Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,11 @@
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.type.SqlTypes;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

/**
* JPA entity for AggregatedNodeRef (Aggregated Node Reference).
Expand All @@ -48,12 +47,12 @@ public class AggregatedNodeRefEntity {

/**
* Primary key identifier.
* AggregatedNodeRef extends Object (not IdentifiedObject), so uses Long ID.
*/
@Id
@GeneratedValue(strategy = GenerationType.UUID)
@JdbcTypeCode(SqlTypes.CHAR)
@Column(length = 36, columnDefinition = "char(36)", updatable = false, nullable = false)
private UUID id;
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(updatable = false, nullable = false)
private Long id;

/**
* Type of the aggregated node.
Expand Down Expand Up @@ -84,12 +83,16 @@ public class AggregatedNodeRefEntity {
private Long endEffectiveDate;

/**
* Associated pricing node reference for this aggregated node.
* Each aggregated node references an underlying pricing node.
* Associated pricing node references for this aggregated node.
* Per ESPI 4.0 XSD (espi.xsd:1597), each aggregated node can reference 0 to many pricing nodes.
*/
@ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JoinColumn(name = "pnode_ref_id")
private PnodeRefEntity pnodeRef;
@ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JoinTable(
name = "aggregated_node_ref_pnode_refs",
joinColumns = @JoinColumn(name = "aggregated_node_ref_id"),
inverseJoinColumns = @JoinColumn(name = "pnode_ref_id")
)
private List<PnodeRefEntity> pnodeRefs = new ArrayList<>();

/**
* Usage point that owns this aggregated node reference.
Expand All @@ -102,21 +105,21 @@ public class AggregatedNodeRefEntity {
/**
* Constructor with all fields.
*/
public AggregatedNodeRefEntity(String anodeType, String ref, Long startEffectiveDate, Long endEffectiveDate,
PnodeRefEntity pnodeRef, UsagePointEntity usagePoint) {
public AggregatedNodeRefEntity(String anodeType, String ref, Long startEffectiveDate, Long endEffectiveDate,
List<PnodeRefEntity> pnodeRefs, UsagePointEntity usagePoint) {
this.anodeType = anodeType;
this.ref = ref;
this.startEffectiveDate = startEffectiveDate;
this.endEffectiveDate = endEffectiveDate;
this.pnodeRef = pnodeRef;
this.pnodeRefs = pnodeRefs != null ? pnodeRefs : new ArrayList<>();
this.usagePoint = usagePoint;
}

/**
* Constructor with basic fields.
*/
public AggregatedNodeRefEntity(String anodeType, String ref, PnodeRefEntity pnodeRef, UsagePointEntity usagePoint) {
this(anodeType, ref, null, null, pnodeRef, usagePoint);
public AggregatedNodeRefEntity(String anodeType, String ref, List<PnodeRefEntity> pnodeRefs, UsagePointEntity usagePoint) {
this(anodeType, ref, null, null, pnodeRefs, usagePoint);
}

/**
Expand All @@ -143,14 +146,18 @@ public String getDisplayName() {
}

/**
* Gets display name including the associated pricing node.
*
* @return formatted display name with pricing node
* Gets display name including the associated pricing nodes.
*
* @return formatted display name with pricing nodes
*/
public String getFullDisplayName() {
String aggregatedDisplay = getDisplayName();
if (pnodeRef != null) {
return aggregatedDisplay + " -> " + pnodeRef.getDisplayName();
if (pnodeRefs != null && !pnodeRefs.isEmpty()) {
String pnodeNames = pnodeRefs.stream()
.map(PnodeRefEntity::getDisplayName)
.reduce((a, b) -> a + ", " + b)
.orElse("");
return aggregatedDisplay + " -> [" + pnodeNames + "]";
}
return aggregatedDisplay;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,25 +21,30 @@

import jakarta.xml.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;

/**
* AggregatedNodeRef DTO record for JAXB XML marshalling/unmarshalling.
*
*
* Represents a reference to an aggregated node in the electrical grid.
* Used within UsagePoint to specify aggregated pricing/load zones.
*
*
* Per ESPI 4.0 XSD (espi.xsd:1597), pnodeRef has minOccurs="0" maxOccurs="unbounded"
*
* Part of the NAESB ESPI UsagePoint structure for aggregated node references.
*/
@XmlAccessorType(XmlAccessType.PROPERTY)
@XmlType(name = "AggregatedNodeRef", namespace = "http://naesb.org/espi", propOrder = {
"anodeType", "ref", "startEffectiveDate", "endEffectiveDate", "pnodeRef"
})
public record AggregatedNodeRefDto(

String anodeType,
String ref,
Long startEffectiveDate,
Long endEffectiveDate,
PnodeRefDto pnodeRef
List<PnodeRefDto> pnodeRef
) {

/**
Expand Down Expand Up @@ -78,32 +83,33 @@ public Long getEndEffectiveDate() {
}

/**
* Pricing node reference associated with this aggregated node.
* Contains the underlying pricing node that contributes to the aggregated node.
* Pricing node references associated with this aggregated node.
* Contains the underlying pricing nodes that contribute to the aggregated node.
* Per ESPI 4.0 XSD (espi.xsd:1597), supports 0 to many pricing node references.
*/
@XmlElement(name = "pnodeRef")
public PnodeRefDto getPnodeRef() {
return pnodeRef;
public List<PnodeRefDto> getPnodeRef() {
return pnodeRef != null ? pnodeRef : new ArrayList<>();
}

/**
* Default constructor for JAXB.
*/
public AggregatedNodeRefDto() {
this(null, null, null, null, null);
this(null, null, null, null, new ArrayList<>());
}

/**
* Constructor with aggregated node reference and type.
*/
public AggregatedNodeRefDto(String anodeType, String ref) {
this(anodeType, ref, null, null, null);
this(anodeType, ref, null, null, new ArrayList<>());
}

/**
* Constructor with aggregated node reference, type, and pricing node.
* Constructor with aggregated node reference, type, and pricing nodes.
*/
public AggregatedNodeRefDto(String anodeType, String ref, PnodeRefDto pnodeRef) {
public AggregatedNodeRefDto(String anodeType, String ref, List<PnodeRefDto> pnodeRef) {
this(anodeType, ref, null, null, pnodeRef);
}

Expand All @@ -120,40 +126,40 @@ public boolean isValid() {

/**
* Creates an AggregatedNodeRef with current validity period.
*
*
* @param anodeType the type of aggregated node
* @param ref aggregated node reference
* @return AggregatedNodeRef valid from now
*/
public static AggregatedNodeRefDto createCurrent(String anodeType, String ref) {
long currentTime = System.currentTimeMillis() / 1000;
return new AggregatedNodeRefDto(anodeType, ref, currentTime, null, null);
return new AggregatedNodeRefDto(anodeType, ref, currentTime, null, new ArrayList<>());
}

/**
* Creates an AggregatedNodeRef with current validity period and pricing node reference.
*
* Creates an AggregatedNodeRef with current validity period and pricing node references.
*
* @param anodeType the type of aggregated node
* @param ref aggregated node reference
* @param pnodeRef associated pricing node reference
* @param pnodeRef associated pricing node references
* @return AggregatedNodeRef valid from now
*/
public static AggregatedNodeRefDto createCurrent(String anodeType, String ref, PnodeRefDto pnodeRef) {
public static AggregatedNodeRefDto createCurrent(String anodeType, String ref, List<PnodeRefDto> pnodeRef) {
long currentTime = System.currentTimeMillis() / 1000;
return new AggregatedNodeRefDto(anodeType, ref, currentTime, null, pnodeRef);
}

/**
* Creates an AggregatedNodeRef with specified validity period.
*
*
* @param anodeType the type of aggregated node
* @param ref aggregated node reference
* @param startEffectiveDate start of validity period (epoch seconds)
* @param endEffectiveDate end of validity period (epoch seconds, null for indefinite)
* @param pnodeRef associated pricing node reference
* @param pnodeRef associated pricing node references
* @return AggregatedNodeRef with specified validity
*/
public static AggregatedNodeRefDto create(String anodeType, String ref, Long startEffectiveDate, Long endEffectiveDate, PnodeRefDto pnodeRef) {
public static AggregatedNodeRefDto create(String anodeType, String ref, Long startEffectiveDate, Long endEffectiveDate, List<PnodeRefDto> pnodeRef) {
return new AggregatedNodeRefDto(anodeType, ref, startEffectiveDate, endEffectiveDate, pnodeRef);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -147,10 +147,10 @@ public static AggregatedNodeRefsDto createLoadZoneRefs() {
// Create pricing node references for the load zones
PnodeRefDto northPnode = PnodeRefDto.createCurrent("HUB", "NORTH_HUB_PNODE");
PnodeRefDto southPnode = PnodeRefDto.createCurrent("HUB", "SOUTH_HUB_PNODE");

return new AggregatedNodeRefsDto(
AggregatedNodeRefDto.createCurrent("LOAD_ZONE", "LOAD_ZONE_NORTH", northPnode),
AggregatedNodeRefDto.createCurrent("LOAD_ZONE", "LOAD_ZONE_SOUTH", southPnode)
AggregatedNodeRefDto.createCurrent("LOAD_ZONE", "LOAD_ZONE_NORTH", List.of(northPnode)),
AggregatedNodeRefDto.createCurrent("LOAD_ZONE", "LOAD_ZONE_SOUTH", List.of(southPnode))
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import org.greenbuttonalliance.espi.common.dto.usage.AggregatedNodeRefDto;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;

/**
* MapStruct mapper for converting between AggregatedNodeRefEntity and AggregatedNodeRefDto.
Expand All @@ -41,7 +40,7 @@ public interface AggregatedNodeRefMapper {
@Mapping(target = "ref", source = "ref")
@Mapping(target = "startEffectiveDate", source = "startEffectiveDate")
@Mapping(target = "endEffectiveDate", source = "endEffectiveDate")
@Mapping(target = "pnodeRef", source = "pnodeRef")
@Mapping(target = "pnodeRef", source = "pnodeRefs")
AggregatedNodeRefDto toDto(AggregatedNodeRefEntity entity);

/**
Expand All @@ -56,16 +55,7 @@ public interface AggregatedNodeRefMapper {
@Mapping(target = "ref", source = "ref")
@Mapping(target = "startEffectiveDate", source = "startEffectiveDate")
@Mapping(target = "endEffectiveDate", source = "endEffectiveDate")
@Mapping(target = "pnodeRef", source = "pnodeRef")
@Mapping(target = "pnodeRefs", source = "pnodeRef")
AggregatedNodeRefEntity toEntity(AggregatedNodeRefDto dto);

/**
* Updates an existing AggregatedNodeRefEntity with data from an AggregatedNodeRefDto.
*
* @param dto the source DTO
* @param entity the target entity to update
*/
@Mapping(target = "id", ignore = true)
@Mapping(target = "usagePoint", ignore = true)
void updateEntity(AggregatedNodeRefDto dto, @MappingTarget AggregatedNodeRefEntity entity);
}
Loading
Loading