Skip to content

Commit 23dbb05

Browse files
dfcoffinclaude
andcommitted
feat: ESPI 4.0 Schema Compliance - Phase 16b: UsagePoint Add Enum Fields & Reorder
This PR implements Phase 16b of the UsagePoint schema compliance work, adding 3 missing enum fields and reordering all entity fields to match exact ESPI 4.0 XSD sequence. Builds on Phase 16a (PR #83) which added Boolean/String fields. ## Changes in Phase 16b ### New Enum Classes (domain/common) - `AmiBillingReadyKind` - AMI billing readiness lifecycle states (7 values) - `UsagePointConnectedKind` - Network connection states (3 values) - `PhaseCodeKind` - Phase identifiers for electrical systems (27 values) ### Entity Updates (UsagePointEntity) - Added 3 enum fields in correct XSD positions: - `amiBillingReady` (XSD position 5) - `connectionState` (XSD position 7) - `phaseCode` (XSD position 15) - **Reordered ALL fields to match exact ESPI 4.0 XSD element sequence** - Added XSD position comments for each field (positions 1-21) - Added section headers for clarity (XSD fields, legacy fields, relationships) - Documented legacy fields (`uri`, `kind`) as NOT in XSD for Phase 16c review ### Database Migrations Updated all three vendor-specific V2 migrations: - **H2**: Added 3 enum columns (VARCHAR 32) in XSD sequence - **MySQL**: Added 3 enum columns (VARCHAR 32) in XSD sequence - **PostgreSQL**: Added 3 enum columns (VARCHAR 32) in XSD sequence ### Testing - Added comprehensive repository test: `shouldPersistAndRetrievePhase16bEnumFields()` - Verifies all 3 enum fields persist and retrieve correctly - All 584 tests passing (was 583, added 1 new test) ## XSD Compliance Achieved Fields now match exact ESPI 4.0 XSD sequence (espi.xsd:486-613): 1. roleFlags 2. ServiceCategory 3. status 4. serviceDeliveryPoint (relationship) 5. **amiBillingReady** ✅ NEW (Phase 16b) 6. checkBilling (Phase 16a) 7. **connectionState** ✅ NEW (Phase 16b) 8. estimatedLoad 9. grounded (Phase 16a) 10. isSdp (Phase 16a) 11. isVirtual (Phase 16a) 12. minimalUsageExpected (Phase 16a) 13. nominalServiceVoltage 14. outageRegion (Phase 16a) 15. **phaseCode** ✅ NEW (Phase 16b) 16. ratedCurrent 17. ratedPower 18. readCycle (Phase 16a) 19. readRoute (Phase 16a) 20. serviceDeliveryRemark (Phase 16a) 21. servicePriority (Phase 16a) 22-23. pnodeRefs, aggregateNodeRefs (relationships) ## Technical Details ### Enum Design - All enums follow existing ServiceCategory pattern - String-based values for AmiBillingReadyKind, UsagePointConnectedKind - Integer-based values for PhaseCodeKind (per XSD UInt16) - Each enum has `fromValue()` method for deserialization - Stored as VARCHAR(32) in database using `@Enumerated(EnumType.STRING)` ### Field Ordering - Entity fields now match exact XSD element sequence - Improves maintainability and XSD compliance verification - Inline comments mark XSD positions for each field - Legacy fields clearly separated with TODO for Phase 16c ### Additive-Only Changes This PR is **100% additive** with zero breaking changes: - All new fields are nullable/optional - No field deletions or renames - No changes to existing relationships - Backward compatible database migrations ## Test Results ``` Tests run: 584, Failures: 0, Errors: 0, Skipped: 0 BUILD SUCCESS ``` ## Related - Issue #28 - Phase 16: UsagePoint - PR #83 - Phase 16a: Add Missing Boolean/String Fields - Part 2 of 5 sub-phases for complete UsagePoint compliance ## Next Steps (Phase 16c) - Repository cleanup (remove non-indexed queries) - Review legacy fields (`kind`, `uri`) - Convert @query to Spring Data JPA derived queries Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 25796a7 commit 23dbb05

8 files changed

Lines changed: 518 additions & 64 deletions

File tree

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*
2+
*
3+
* Copyright (c) 2025 Green Button Alliance, Inc.
4+
*
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*
18+
*/
19+
20+
package org.greenbuttonalliance.espi.common.domain.common;
21+
22+
/**
23+
* Lifecycle states of the metering installation at a usage point with respect to
24+
* readiness for billing via advanced metering infrastructure reads.
25+
* Per ESPI 4.0 XSD: AmiBillingReadyKind enumeration.
26+
*/
27+
public enum AmiBillingReadyKind {
28+
/**
29+
* Usage point is equipped with an AMI capable meter that is not yet currently
30+
* equipped with a communications module.
31+
*/
32+
AMI_CAPABLE("amiCapable"),
33+
34+
/**
35+
* Usage point is equipped with an AMI capable meter; however, the AMI functionality
36+
* has been disabled or is not being used.
37+
*/
38+
AMI_DISABLED("amiDisabled"),
39+
40+
/**
41+
* Usage point is equipped with an operating AMI capable meter and accuracy has been
42+
* certified for billing purposes.
43+
*/
44+
BILLING_APPROVED("billingApproved"),
45+
46+
/**
47+
* Usage point is equipped with an AMI capable meter having communications capability.
48+
*/
49+
ENABLED("enabled"),
50+
51+
/**
52+
* Usage point is equipped with a non AMI capable meter.
53+
*/
54+
NON_AMI("nonAmi"),
55+
56+
/**
57+
* Usage point is not currently equipped with a meter.
58+
*/
59+
NON_METERED("nonMetered"),
60+
61+
/**
62+
* Usage point is equipped with an AMI capable meter that is functioning and
63+
* communicating with the AMI network.
64+
*/
65+
OPERABLE("operable");
66+
67+
private final String value;
68+
69+
AmiBillingReadyKind(String value) {
70+
this.value = value;
71+
}
72+
73+
public String getValue() {
74+
return value;
75+
}
76+
77+
/**
78+
* Converts a string value to the corresponding AmiBillingReadyKind enum constant.
79+
*
80+
* @param value the string value from XML/XSD
81+
* @return the matching enum constant, or null if not found
82+
*/
83+
public static AmiBillingReadyKind fromValue(String value) {
84+
if (value == null) {
85+
return null;
86+
}
87+
for (AmiBillingReadyKind kind : AmiBillingReadyKind.values()) {
88+
if (kind.value.equals(value)) {
89+
return kind;
90+
}
91+
}
92+
return null;
93+
}
94+
}
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
/*
2+
*
3+
* Copyright (c) 2025 Green Button Alliance, Inc.
4+
*
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*
18+
*/
19+
20+
package org.greenbuttonalliance.espi.common.domain.common;
21+
22+
/**
23+
* Enumeration of phase identifiers. Allows designation of phases for both transmission
24+
* and distribution equipment, circuits and loads. Residential and small commercial loads
25+
* are often served from single-phase, or split-phase, secondary circuits. Phases 1 and 2
26+
* refer to hot wires that are 180 degrees out of phase, while N refers to the neutral wire.
27+
* Through single-phase transformer connections, these secondary circuits may be served from
28+
* one or two of the primary phases A, B, and C. For three-phase loads, use the A, B, C
29+
* phase codes instead of s12N.
30+
* <p>
31+
* Per ESPI 4.0 XSD: PhaseCodeKind enumeration (UInt16 values).
32+
*/
33+
public enum PhaseCodeKind {
34+
/**
35+
* ABC to Neutral (three-phase, four-wire).
36+
*/
37+
ABCN(225),
38+
39+
/**
40+
* Involving all phases (three-phase).
41+
*/
42+
ABC(224),
43+
44+
/**
45+
* AB to Neutral.
46+
*/
47+
ABN(193),
48+
49+
/**
50+
* Phases A, C and neutral.
51+
*/
52+
ACN(41),
53+
54+
/**
55+
* BC to neutral.
56+
*/
57+
BCN(97),
58+
59+
/**
60+
* Phases A to B.
61+
*/
62+
AB(132),
63+
64+
/**
65+
* Phases A and C.
66+
*/
67+
AC(96),
68+
69+
/**
70+
* Phases B to C.
71+
*/
72+
BC(66),
73+
74+
/**
75+
* Phases A to neutral.
76+
*/
77+
AN(129),
78+
79+
/**
80+
* Phases B to neutral.
81+
*/
82+
BN(65),
83+
84+
/**
85+
* Phases C to neutral.
86+
*/
87+
CN(33),
88+
89+
/**
90+
* Phase A.
91+
*/
92+
A(128),
93+
94+
/**
95+
* Phase B.
96+
*/
97+
B(64),
98+
99+
/**
100+
* Phase C.
101+
*/
102+
C(32),
103+
104+
/**
105+
* Neutral.
106+
*/
107+
N(16),
108+
109+
/**
110+
* Phase S2 to neutral.
111+
*/
112+
S2N(272),
113+
114+
/**
115+
* Phase S1, S2 to neutral (split-phase secondary).
116+
*/
117+
S12N(784),
118+
119+
/**
120+
* Phase S1 to Neutral.
121+
*/
122+
S1N(528),
123+
124+
/**
125+
* Phase S2.
126+
*/
127+
S2(256),
128+
129+
/**
130+
* Phase S1 to S2.
131+
*/
132+
S12(768),
133+
134+
/**
135+
* Phase S1.
136+
*/
137+
S1(512),
138+
139+
/**
140+
* Not applicable to any phase.
141+
*/
142+
NONE(0),
143+
144+
/**
145+
* Phase A current relative to Phase A voltage.
146+
*/
147+
A_TO_AV(136),
148+
149+
/**
150+
* Phase B current or voltage relative to Phase A voltage.
151+
*/
152+
B_AV(72),
153+
154+
/**
155+
* Phase C current or voltage relative to Phase A voltage.
156+
*/
157+
C_AV(40),
158+
159+
/**
160+
* Neutral to ground.
161+
*/
162+
NG(17);
163+
164+
private final Integer value;
165+
166+
PhaseCodeKind(Integer value) {
167+
this.value = value;
168+
}
169+
170+
public Integer getValue() {
171+
return value;
172+
}
173+
174+
/**
175+
* Converts an integer value to the corresponding PhaseCodeKind enum constant.
176+
*
177+
* @param value the integer value from XML/XSD
178+
* @return the matching enum constant, or null if not found
179+
*/
180+
public static PhaseCodeKind fromValue(Integer value) {
181+
if (value == null) {
182+
return null;
183+
}
184+
for (PhaseCodeKind kind : PhaseCodeKind.values()) {
185+
if (kind.value.equals(value)) {
186+
return kind;
187+
}
188+
}
189+
return null;
190+
}
191+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
*
3+
* Copyright (c) 2025 Green Button Alliance, Inc.
4+
*
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*
18+
*/
19+
20+
package org.greenbuttonalliance.espi.common.domain.common;
21+
22+
/**
23+
* State of the usage point with respect to connection to the network.
24+
* Per ESPI 4.0 XSD: UsagePointConnectedKind enumeration.
25+
*/
26+
public enum UsagePointConnectedKind {
27+
/**
28+
* The usage point is connected to the network and able to receive or send
29+
* the applicable commodity (electricity, gas, water, etc.).
30+
*/
31+
CONNECTED("connected"),
32+
33+
/**
34+
* The usage point has been disconnected through operation of a disconnect function
35+
* within the meter present at the usage point. The usage point is unable to receive
36+
* or send the applicable commodity (electricity, gas, water, etc.). A logical
37+
* disconnect can often be achieved without utilising a field crew.
38+
*/
39+
LOGICALLY_DISCONNECTED("logicallyDisconnected"),
40+
41+
/**
42+
* The usage point has been disconnected from the network at a point upstream of the meter.
43+
* The usage point is unable to receive or send the applicable commodity (electricity,
44+
* gas, water, etc.). A physical disconnect is often achieved by utilising a field crew.
45+
*/
46+
PHYSICALLY_DISCONNECTED("physicallyDisconnected");
47+
48+
private final String value;
49+
50+
UsagePointConnectedKind(String value) {
51+
this.value = value;
52+
}
53+
54+
public String getValue() {
55+
return value;
56+
}
57+
58+
/**
59+
* Converts a string value to the corresponding UsagePointConnectedKind enum constant.
60+
*
61+
* @param value the string value from XML/XSD
62+
* @return the matching enum constant, or null if not found
63+
*/
64+
public static UsagePointConnectedKind fromValue(String value) {
65+
if (value == null) {
66+
return null;
67+
}
68+
for (UsagePointConnectedKind kind : UsagePointConnectedKind.values()) {
69+
if (kind.value.equals(value)) {
70+
return kind;
71+
}
72+
}
73+
return null;
74+
}
75+
}

0 commit comments

Comments
 (0)