- CitrineOS core extracted (CSMS OCPP 2.0.1) - OpenOCPP extracted (firmware OCPP 1.6J/2.0.1) - ShapeShifter library installed (pip install -e) - ShapeShifter specification extracted - EVerest extracted TODO updated with progress
452 lines
13 KiB
Python
452 lines
13 KiB
Python
from dataclasses import dataclass, field
|
|
from decimal import Decimal
|
|
from typing import List, Optional
|
|
|
|
from xsdata.models.datatype import XmlDate
|
|
|
|
from ..enums import AcceptedDisputed
|
|
from ..validations import validate_decimal, validate_list
|
|
from .payload_message import PayloadMessageResponse
|
|
|
|
|
|
@dataclass(kw_only=True)
|
|
class ContractSettlementISP:
|
|
"""
|
|
:ivar start: Number of the first ISPs this element refers to. The
|
|
first ISP of a day has number 1.
|
|
:ivar duration: The number of the ISPs this element represents.
|
|
Optional, default value is 1.
|
|
:ivar reserved_power: Amount of flex power that has been reserved
|
|
(and not released using a FlexReservationUpdate message).
|
|
:ivar requested_power: Amount of flex power that has been both
|
|
reserved in advance and has been requested using a FlexRequest
|
|
(i.e. the lowest amount of flex power for this ISP). If there
|
|
was no FlexRequest, this field is omitted.
|
|
:ivar available_power: Amount of flex power that is considered
|
|
available based on the FlexRequest in question. In case
|
|
RequestedPower=0, AvailablePower is defined so that the offered
|
|
power is allowed to be between 0 and AvailablePower in terms of
|
|
compliancy (see Appendix 'Rationale for information exchange in
|
|
flexibility request' for details). In case RequestedPower ≠0,
|
|
AvailablePower is defined so that the offered power is allowed
|
|
to exceed the amount of requested power up to AvailablePower. If
|
|
this is relevant for settlement, the DSO can include this field.
|
|
:ivar offered_power: Amount of flex power that has been reserved in
|
|
advance, requested using a FlexRequest and covered in an offer
|
|
from the AGR. If there was no offer, this field is omitted. If
|
|
there were multiple offers, only the one is considered that is
|
|
most compliant .
|
|
:ivar ordered_power: Amount of flex power that has been ordered
|
|
using a FlexOrder message that was based on a FlexOffer, both
|
|
linked to this contract. If there was no order, this field is
|
|
omitted.
|
|
"""
|
|
class Meta:
|
|
name = "ContractSettlementISP"
|
|
|
|
start: int = field(
|
|
metadata={
|
|
"name": "Start",
|
|
"type": "Attribute",
|
|
"required": True,
|
|
}
|
|
)
|
|
duration: int = field(
|
|
default=1,
|
|
metadata={
|
|
"name": "Duration",
|
|
"type": "Attribute",
|
|
}
|
|
)
|
|
reserved_power: int = field(
|
|
metadata={
|
|
"name": "ReservedPower",
|
|
"type": "Attribute",
|
|
"required": True,
|
|
}
|
|
)
|
|
requested_power: Optional[int] = field(
|
|
default=None,
|
|
metadata={
|
|
"name": "RequestedPower",
|
|
"type": "Attribute",
|
|
}
|
|
)
|
|
available_power: Optional[int] = field(
|
|
default=None,
|
|
metadata={
|
|
"name": "AvailablePower",
|
|
"type": "Attribute",
|
|
}
|
|
)
|
|
offered_power: Optional[int] = field(
|
|
default=None,
|
|
metadata={
|
|
"name": "OfferedPower",
|
|
"type": "Attribute",
|
|
}
|
|
)
|
|
ordered_power: Optional[int] = field(
|
|
default=None,
|
|
metadata={
|
|
"name": "OrderedPower",
|
|
"type": "Attribute",
|
|
}
|
|
)
|
|
|
|
|
|
@dataclass(kw_only=True)
|
|
class ContractSettlementPeriod:
|
|
"""
|
|
:ivar isp:
|
|
:ivar period: Period the being settled.
|
|
"""
|
|
isps: List[ContractSettlementISP] = field(
|
|
default_factory=list,
|
|
metadata={
|
|
"name": "ISP",
|
|
"type": "Element",
|
|
"min_occurs": 1,
|
|
}
|
|
)
|
|
period: XmlDate = field(
|
|
metadata={
|
|
"name": "Period",
|
|
"type": "Attribute",
|
|
"format": "%Y-%m-%d",
|
|
"required": True,
|
|
}
|
|
)
|
|
|
|
|
|
@dataclass(kw_only=True)
|
|
class ContractSettlement:
|
|
"""
|
|
:ivar period:
|
|
:ivar contract_id: Reference to the concerning bilateral contract.
|
|
"""
|
|
periods: List[ContractSettlementPeriod] = field(
|
|
default_factory=list,
|
|
metadata={
|
|
"name": "Period",
|
|
"type": "Element",
|
|
"min_occurs": 1,
|
|
}
|
|
)
|
|
contract_id: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"name": "ContractID",
|
|
"type": "Attribute",
|
|
}
|
|
)
|
|
|
|
def __post_init__(self):
|
|
validate_list('periods', self.periods, ContractSettlementPeriod, 1)
|
|
|
|
|
|
@dataclass(kw_only=True)
|
|
class FlexOrderSettlementStatus:
|
|
"""
|
|
:ivar order_reference: Order reference assigned by the DSO when
|
|
originating the FlexOrder.
|
|
:ivar disposition: Indication whether the AGR accepts the order
|
|
settlement details provided by the DSO (and will invoice
|
|
accordingly), or disputes these details.
|
|
:ivar dispute_reason: In case the order settlement was disputed,
|
|
this attribute must contain a human-readable description of the
|
|
reason.
|
|
"""
|
|
order_reference: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"name": "OrderReference",
|
|
"type": "Attribute",
|
|
}
|
|
)
|
|
disposition: AcceptedDisputed = field(
|
|
metadata={
|
|
"name": "Disposition",
|
|
"type": "Attribute",
|
|
"required": True,
|
|
}
|
|
)
|
|
dispute_reason: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"name": "DisputeReason",
|
|
"type": "Attribute",
|
|
}
|
|
)
|
|
|
|
@dataclass(kw_only=True)
|
|
class FlexOrderSettlementISP:
|
|
"""
|
|
:ivar start: Number of the first ISPs this element refers to. The
|
|
first ISP of a day has number 1.
|
|
:ivar duration: The number of the ISPs this element represents.
|
|
Optional, default value is 1.
|
|
:ivar baseline_power: Power originally forecast (as per the
|
|
referenced baseline) for this ISP in Watts.
|
|
:ivar ordered_flex_power: Amount of flex power ordered (as per the
|
|
referenced FlexOrder message) for this ISP in Watts.
|
|
:ivar actual_power: Actual amount of power for this ISP in Watts, as
|
|
measured/determined by the DSO and allocated to the AGR.
|
|
:ivar delivered_flex_power: Actual amount of flex power delivered
|
|
for this ISP in Watts, as determined by the DSO.
|
|
:ivar power_deficiency: Amount of flex power sold but not delivered
|
|
for this ISP in Watts, as determined by the DSO.
|
|
"""
|
|
class Meta:
|
|
name = "FlexOrderSettlementISP"
|
|
|
|
start: int = field(
|
|
metadata={
|
|
"name": "Start",
|
|
"type": "Attribute",
|
|
"required": True,
|
|
}
|
|
)
|
|
duration: int = field(
|
|
default=1,
|
|
metadata={
|
|
"name": "Duration",
|
|
"type": "Attribute",
|
|
}
|
|
)
|
|
baseline_power: int = field(
|
|
metadata={
|
|
"name": "BaselinePower",
|
|
"type": "Attribute",
|
|
"required": True,
|
|
}
|
|
)
|
|
ordered_flex_power: int = field(
|
|
metadata={
|
|
"name": "OrderedFlexPower",
|
|
"type": "Attribute",
|
|
"required": True,
|
|
}
|
|
)
|
|
actual_power: int = field(
|
|
metadata={
|
|
"name": "ActualPower",
|
|
"type": "Attribute",
|
|
"required": True,
|
|
}
|
|
)
|
|
delivered_flex_power: int = field(
|
|
metadata={
|
|
"name": "DeliveredFlexPower",
|
|
"type": "Attribute",
|
|
"required": True,
|
|
}
|
|
)
|
|
power_deficiency: int = field(
|
|
default=0,
|
|
metadata={
|
|
"name": "PowerDeficiency",
|
|
"type": "Attribute",
|
|
}
|
|
)
|
|
|
|
|
|
@dataclass(kw_only=True)
|
|
class FlexOrderSettlement:
|
|
"""
|
|
:ivar isp:
|
|
:ivar order_reference: Order reference assigned by the DSO when
|
|
originating the FlexOrder.
|
|
:ivar period:
|
|
:ivar contract_id: Reference to the concerning bilateral contract,
|
|
if it is linked to it
|
|
:ivar d_prognosis_message_id: MessageID of the Prognosis message
|
|
(more specifically: the D-Prognosis) the FlexOrder is based on,
|
|
if it has been agreed that the baseline is based on D-prognoses.
|
|
:ivar baseline_reference: Identification of the baseline prognosis,
|
|
if another baseline methodology is used than based on
|
|
D-prognoses.
|
|
:ivar congestion_point: Entity Address of the Congestion Point the
|
|
FlexOrder applies to.
|
|
:ivar price: The price accepted for supplying the ordered amount of
|
|
flexibility as per the referenced FlexOrder messages.
|
|
:ivar penalty: Penalty due a non-zero PowerDeficiency
|
|
:ivar net_settlement: Net settlement amount for this Period: Price
|
|
minus Penalty.
|
|
"""
|
|
isps: List[FlexOrderSettlementISP] = field(
|
|
default_factory=list,
|
|
metadata={
|
|
"name": "ISP",
|
|
"type": "Element",
|
|
"min_occurs": 1,
|
|
}
|
|
)
|
|
order_reference: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"name": "OrderReference",
|
|
"type": "Attribute",
|
|
}
|
|
)
|
|
period: XmlDate = field(
|
|
metadata={
|
|
"name": "Period",
|
|
"type": "Attribute",
|
|
"format": "%Y-%m-%d",
|
|
"required": True,
|
|
}
|
|
)
|
|
contract_id: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"name": "ContractID",
|
|
"type": "Attribute",
|
|
}
|
|
)
|
|
d_prognosis_message_id: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"name": "D-PrognosisMessageID",
|
|
"type": "Attribute",
|
|
"pattern": r"[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}",
|
|
}
|
|
)
|
|
baseline_reference: Optional[str] = field(
|
|
default=None,
|
|
metadata={
|
|
"name": "BaselineReference",
|
|
"type": "Attribute",
|
|
}
|
|
)
|
|
congestion_point: str = field(
|
|
metadata={
|
|
"name": "CongestionPoint",
|
|
"type": "Attribute",
|
|
"required": True,
|
|
"pattern": r"(ea1\.[0-9]{4}-[0-9]{2}\..{1,244}:.{1,244}|ean\.[0-9]{12,34})",
|
|
}
|
|
)
|
|
price: Decimal = field(
|
|
metadata={
|
|
"name": "Price",
|
|
"type": "Attribute",
|
|
"required": True,
|
|
"fraction_digits": 4,
|
|
}
|
|
)
|
|
penalty: Decimal = field(
|
|
default=Decimal("0"),
|
|
metadata={
|
|
"name": "Penalty",
|
|
"type": "Attribute",
|
|
"fraction_digits": 4,
|
|
}
|
|
)
|
|
net_settlement: Decimal = field(
|
|
metadata={
|
|
"name": "NetSettlement",
|
|
"type": "Attribute",
|
|
"required": True,
|
|
"fraction_digits": 4,
|
|
}
|
|
)
|
|
|
|
def __post_init__(self):
|
|
validate_list('isps', self.isps, FlexOrderSettlementISP, 1)
|
|
self.price = validate_decimal('price', self.price, 4)
|
|
self.penalty = validate_decimal('penalty', self.penalty, 4)
|
|
self.net_settlement = validate_decimal('net_settlement', self.net_settlement, 4)
|
|
|
|
|
|
@dataclass(kw_only=True)
|
|
class FlexSettlementResponse(PayloadMessageResponse):
|
|
flex_settlement_message_id: str = field(
|
|
metadata={
|
|
"name": "FlexSettlementMessageID",
|
|
"type": "Attribute",
|
|
"required": True,
|
|
"pattern": r"[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}",
|
|
}
|
|
)
|
|
|
|
flex_order_settlement_statuses: List[FlexOrderSettlementStatus] = field(
|
|
default_factory=list,
|
|
metadata={
|
|
"name": "FlexOrderSettlementStatus",
|
|
"type": "Element",
|
|
"min_occurs": 1,
|
|
}
|
|
)
|
|
|
|
def __post_init__(self):
|
|
validate_list(
|
|
"flex_order_settlement_statuses",
|
|
self.flex_order_settlement_statuses,
|
|
FlexOrderSettlementStatus,
|
|
1,
|
|
)
|
|
|
|
|
|
@dataclass(kw_only=True)
|
|
class FlexSettlement(PayloadMessageResponse):
|
|
"""
|
|
:ivar flex_order_settlement:
|
|
:ivar contract_settlement:
|
|
:ivar period_start: First Period of the settlement period this
|
|
message applies to.
|
|
:ivar period_end: Last Period of the settlement period this message
|
|
applies to.
|
|
:ivar currency: ISO 4217 code indicating the currency that applies
|
|
to all amounts (flex price, penalty and net settlement) in this
|
|
message.
|
|
"""
|
|
flex_order_settlements: List[FlexOrderSettlement] = field(
|
|
default_factory=list,
|
|
metadata={
|
|
"name": "FlexOrderSettlement",
|
|
"type": "Element",
|
|
"min_occurs": 1,
|
|
}
|
|
)
|
|
contract_settlements: List[ContractSettlement] = field(
|
|
default_factory=list,
|
|
metadata={
|
|
"name": "ContractSettlement",
|
|
"type": "Element",
|
|
"min_occurs": 1,
|
|
}
|
|
)
|
|
period_start: XmlDate = field(
|
|
metadata={
|
|
"name": "PeriodStart",
|
|
"type": "Attribute",
|
|
"format": "%Y-%m-%d",
|
|
"required": True,
|
|
}
|
|
)
|
|
period_end: XmlDate = field(
|
|
metadata={
|
|
"name": "PeriodEnd",
|
|
"type": "Attribute",
|
|
"format": "%Y-%m-%d",
|
|
"required": True,
|
|
}
|
|
)
|
|
currency: str = field(
|
|
metadata={
|
|
"name": "Currency",
|
|
"type": "Attribute",
|
|
"required": True,
|
|
"pattern": r"[A-Z]{3}",
|
|
}
|
|
)
|
|
|
|
def __post_init__(self):
|
|
validate_list(
|
|
"flex_order_settlements", self.flex_order_settlements, FlexOrderSettlement, 1
|
|
)
|
|
validate_list(
|
|
"contract_settlements", self.contract_settlements, ContractSettlement, 1
|
|
)
|