Add extracted tools: CitrineOS, OpenOCPP, ShapeShifter

- 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
This commit is contained in:
Eric F
2026-06-08 00:38:27 -04:00
parent 468cfeaa50
commit d398a6ced2
7326 changed files with 1177561 additions and 7 deletions

View File

@@ -0,0 +1,363 @@
from datetime import datetime, timezone
from uuid import uuid4
from xsdata.models.datatype import XmlDate
from shapeshifter_uftp.uftp import *
default_args = {
"version": "3.1.0",
"sender_domain": "agr.dev",
"recipient_domain": "cro.dev",
"time_stamp": datetime.now(timezone.utc).isoformat(),
"message_id": str(uuid4()),
"conversation_id": str(uuid4())
}
messages = [
AgrPortfolioQuery(period=XmlDate(2023, 1, 1), **default_args),
AgrPortfolioQueryResponse(
dso_views=[
AgrPortfolioQueryResponseDSOView(
dso_portfolios=[
AgrPortfolioQueryResponseDSOPortfolio(
congestion_points=[
AgrPortfolioQueryResponseCongestionPoint(
connections=[
AgrPortfolioQueryResponseConnection(
entity_address="ean.210987654321"
)
],
entity_address="ean.123456789012",
mutex_offers_supported=True,
day_ahead_redispatch_by=RedispatchBy.AGR,
intraday_redispatch_by=RedispatchBy.AGR,
)
],
dso_domain="dso.dev"
)
]
)
],
period=XmlDate(2023, 1, 1),
agr_portfolio_query_message_id=str(uuid4()),
**default_args
),
AgrPortfolioUpdate(
connections=[
AgrPortfolioUpdateConnection(
entity_address="ean.123456789012",
start_period=XmlDate(2023, 1, 1)
)
],
**default_args
),
AgrPortfolioUpdateResponse(agr_portfolio_update_message_id=str(uuid4()), **default_args),
DPrognosis(
isp_duration="PT15M",
period=XmlDate(2023, 1, 1),
congestion_point="ean.123456789012",
isps=[
DPrognosisISP(
power=2,
start=1,
duration=1
)
],
revision=1,
**default_args
),
DPrognosisResponse(
d_prognosis_message_id=str(uuid4()),
**default_args,
),
DsoPortfolioQuery(
entity_address="ean.123456789012", period=XmlDate(2023, 5, 1), **default_args
),
DsoPortfolioQueryResponse(
congestion_point=DsoPortfolioQueryCongestionPoint(
connections=[
DsoPortfolioQueryConnection(
entity_address="ean.123456789012",
agr_domain="agr.dev"
)
],
entity_address="ean.123456789012"
),
period=XmlDate(2023, 5, 1),
dso_portfolio_query_message_id=str(uuid4()),
**default_args,
),
DsoPortfolioUpdate(
congestion_points=[
DsoPortfolioUpdateCongestionPoint(
connections=[
DsoPortfolioUpdateConnection(
entity_address="ean.123456789012",
start_period=XmlDate(2023, 1, 1),
end_period=XmlDate(2023, 1, 1),
)
],
entity_address="ean.123456789012",
start_period=XmlDate(2023, 1, 1),
end_period=XmlDate(2023, 1, 1),
mutex_offers_supported=True,
day_ahead_redispatch_by=RedispatchBy.AGR,
intraday_redispatch_by=RedispatchBy.AGR,
)
],
**default_args
),
DsoPortfolioUpdateResponse(
dso_portfolio_update_message_id=str(uuid4()), **default_args
),
FlexOffer(
isp_duration="PT15M",
period=XmlDate(2023, 1, 1),
congestion_point="ean.123456789012",
expiration_date_time=datetime.now(timezone.utc).isoformat(),
offer_options=[
FlexOfferOption(
isps=[
FlexOfferOptionISP(
power=1,
start=1,
duration=1
)
],
option_reference="MyOption",
price=2.30,
min_activation_factor=0.5
)
],
flex_request_message_id=str(uuid4()),
**default_args
),
FlexOffer(
isp_duration="PT15M",
period=XmlDate(2023, 1, 1),
congestion_point="ean.123456789012",
expiration_date_time=datetime.now(timezone.utc).isoformat(),
offer_options=[
FlexOfferOption(
isps=[
FlexOfferOptionISP(
power=1,
start=1,
duration=1
)
],
option_reference="MyOption",
price=2.30,
min_activation_factor=0.5
)
],
flex_request_message_id=str(uuid4()),
**default_args
),
FlexOffer(
isp_duration="PT15M",
period=XmlDate(2023, 1, 1),
congestion_point="ean.123456789012",
expiration_date_time=datetime.now(timezone.utc).isoformat(),
offer_options=[
FlexOfferOption(
isps=[
FlexOfferOptionISP(
power=1,
start=1,
duration=1
)
],
option_reference="MyOption",
price=2.30,
min_activation_factor=0.5
)
],
unsolicited=True,
flex_request_message_id=None,
**default_args
),
FlexOfferResponse(flex_offer_message_id=str(uuid4()), **default_args),
FlexOfferRevocation(flex_offer_message_id=str(uuid4()), **default_args),
FlexOfferRevocationResponse(flex_offer_revocation_message_id=str(uuid4()), **default_args),
FlexOrder(
isps=[FlexOrderISP(
power=1,
duration=1,
start=1
)],
isp_duration="PT15M",
period=XmlDate(2023, 1, 1),
congestion_point="ean.123456789012",
flex_offer_message_id=str(uuid4()),
contract_id=str(uuid4()),
d_prognosis_message_id=str(uuid4()),
baseline_reference=str(uuid4()),
price=2.00,
currency="EUR",
order_reference=str(uuid4()),
option_reference=str(uuid4()),
activation_factor=0.5,
**default_args
),
FlexOrder(
isps=[FlexOrderISP(
power=1,
duration=1,
start=1
)],
isp_duration="PT15M",
period=XmlDate(2023, 1, 1),
congestion_point="ean.123456789012",
unsolicited=True,
flex_offer_message_id=None,
contract_id=str(uuid4()),
d_prognosis_message_id=str(uuid4()),
baseline_reference=str(uuid4()),
price=2.00,
currency="EUR",
order_reference=str(uuid4()),
option_reference=str(uuid4()),
activation_factor=0.5,
**default_args
),
FlexOrderResponse(
flex_order_message_id=str(uuid4()),
**default_args
),
FlexRequest(
isp_duration="PT15M",
period=XmlDate(2023, 1, 1),
congestion_point="ean.123456789012",
isps=[
FlexRequestISP(
disposition=AvailableRequested.REQUESTED,
min_power=0,
max_power=10,
start=1,
duration=1,
)
],
revision=1,
expiration_date_time=datetime.now(timezone.utc).isoformat(),
contract_id=str(uuid4()),
service_type="MyService",
**default_args
),
FlexReservationUpdate(
isp_duration="PT15M",
period=XmlDate(2023, 1, 1),
congestion_point="ean.123456789012",
isps=[
FlexReservationUpdateISP(
power=1,
start=1,
duration=1
)
],
contract_id=str(uuid4()),
reference="MyReference",
**default_args,
),
FlexReservationUpdateResponse(
flex_reservation_update_message_id=str(uuid4()),
**default_args
),
FlexSettlement(
flex_order_settlements=[
FlexOrderSettlement(
isps=[
FlexOrderSettlementISP(
start=1,
duration=1,
baseline_power=1,
ordered_flex_power=1,
actual_power=1,
delivered_flex_power=1,
power_deficiency=1,
)
],
period=XmlDate(2023, 1, 1),
congestion_point="ean.123456789012",
order_reference=str(uuid4()),
contract_id=str(uuid4()),
d_prognosis_message_id=str(uuid4()),
baseline_reference=str(uuid4()),
price=1.0,
penalty=1.0,
net_settlement=2.0,
)
],
contract_settlements=[
ContractSettlement(
periods=[
ContractSettlementPeriod(
isps=[
ContractSettlementISP(
start=1,
duration=1,
reserved_power=1,
requested_power=1,
available_power=1,
offered_power=1,
ordered_power=1,
)
],
period=XmlDate(2023, 1, 1)
)
],
contract_id=str(uuid4())
)
],
period_start=XmlDate(2023, 1, 1),
period_end=XmlDate(2023, 5, 1),
currency="EUR",
**default_args,
),
FlexSettlementResponse(
flex_order_settlement_statuses=[
FlexOrderSettlementStatus(
order_reference=str(uuid4()),
disposition=AcceptedDisputed.ACCEPTED,
dispute_reason="My Reason",
)
],
flex_settlement_message_id=str(uuid4()),
**default_args,
),
FlexRequestResponse(
flex_request_message_id=str(uuid4()),
**default_args,
),
Metering(
profiles=[
MeteringProfile(
isps=[
MeteringISP(
start=1,
value=1
)
],
profile_type=MeteringProfileEnum.POWER,
unit=MeteringUnit.K_W,
)
],
revision=1,
isp_duration="PT15M",
time_zone="Europe/Amsterdam",
currency="EUR",
period=XmlDate(2023, 1, 1),
ean="E1234567890123456",
**default_args
),
MeteringResponse(
metering_message_id=str(uuid4()),
**default_args
),
]
messages_by_type = {
type(message): message for message in messages
}

View File

@@ -0,0 +1,351 @@
import itertools
from base64 import b64encode
from concurrent.futures import Future
from nacl.bindings import crypto_sign_keypair
from shapeshifter_uftp import (
ShapeshifterAgrService,
ShapeshifterCroService,
ShapeshifterDsoService,
)
from shapeshifter_uftp.service.base_service import snake_case
AGR_DOMAIN = "agr.dev"
CRO_DOMAIN = "cro.dev"
DSO_DOMAIN = "dso.dev"
AGR_TEST_PORT = 9001
CRO_TEST_PORT = 9002
DSO_TEST_PORT = 9003
AGR_PUBLIC_KEY, AGR_PRIVATE_KEY = [b64encode(key).decode() for key in crypto_sign_keypair()]
CRO_PUBLIC_KEY, CRO_PRIVATE_KEY = [b64encode(key).decode() for key in crypto_sign_keypair()]
DSO_PUBLIC_KEY, DSO_PRIVATE_KEY = [b64encode(key).decode() for key in crypto_sign_keypair()]
def endpoint_lookup_function(domain, role):
if domain == "agr.dev":
return f"http://localhost:{AGR_TEST_PORT}/shapeshifter/api/v3/message"
elif domain == "cro.dev":
return f"http://localhost:{CRO_TEST_PORT}/shapeshifter/api/v3/message"
elif domain == "dso.dev":
return f"http://localhost:{DSO_TEST_PORT}/shapeshifter/api/v3/message"
def key_lookup_function(domain, role):
if domain == "agr.dev":
return AGR_PUBLIC_KEY
elif domain == "cro.dev":
return CRO_PUBLIC_KEY
elif domain == "dso.dev":
return DSO_PUBLIC_KEY
class DummyAgrService(ShapeshifterAgrService):
def __init__(self, oauth_lookup_function=None):
super().__init__(
sender_domain=AGR_DOMAIN,
signing_key=AGR_PRIVATE_KEY,
key_lookup_function=key_lookup_function,
endpoint_lookup_function=endpoint_lookup_function,
oauth_lookup_function=oauth_lookup_function,
port=AGR_TEST_PORT
)
self.request_futures = {
f"{stage}_{name}": Future()
for stage, name in itertools.product(
["process"],
[
name
for name in [
snake_case(message.__name__)
for message in self.acceptable_messages
]
],
)
}
def reset_futures(self, name):
self.request_futures[f"pre_process_{name}"] = Future()
self.request_futures[f"process_{name}"] = Future()
def process_flex_request(self, message):
self.request_futures["process_flex_request"].set_result(message)
def process_flex_order(self, message):
self.request_futures["process_flex_order"].set_result(message)
def process_flex_reservation_update(self, message):
self.request_futures["process_flex_reservation_update"].set_result(message)
def process_flex_settlement(self, message):
self.request_futures["process_flex_settlement"].set_result(message)
def process_flex_offer_revocation_response(self, message):
self.request_futures["process_flex_offer_revocation_response"].set_result(message)
def process_agr_portfolio_query_response(self, message):
self.request_futures["process_agr_portfolio_query_response"].set_result(message)
def process_agr_portfolio_update_response(self, message):
self.request_futures["process_agr_portfolio_update_response"].set_result(message)
def process_d_prognosis_response(self, message):
self.request_futures["process_d_prognosis_response"].set_result(message)
def process_flex_offer_response(self, message):
self.request_futures["process_flex_offer_response"].set_result(message)
def process_metering_response(self, message):
self.request_futures["process_metering_response"].set_result(message)
def process_test_message(self, message, sender_role):
self.request_futures["process_test_message"].set_result(message)
super().process_test_message(message, sender_role)
def process_test_message_response(self, message):
self.request_futures["process_test_message_response"].set_result(message)
super().process_test_message_response(message)
class DummyCroService(ShapeshifterCroService):
def __init__(self):
super().__init__(
sender_domain=CRO_DOMAIN,
signing_key=CRO_PRIVATE_KEY,
key_lookup_function=key_lookup_function,
endpoint_lookup_function=endpoint_lookup_function,
port=CRO_TEST_PORT
)
self.request_futures = {
f"{stage}_{name}": Future()
for stage, name in itertools.product(
["pre_process", "process"],
[
name
for name in [
snake_case(message.__name__)
for message in self.acceptable_messages
]
],
)
}
self.response_futures = {
name: Future()
for name in [
f"pre_process_{snake_case(message.__name__)}"
for message in self.acceptable_messages
]
}
def reset_futures(self, name):
self.request_futures[f"pre_process_{name}"] = Future()
self.request_futures[f"process_{name}"] = Future()
self.response_futures[f"pre_process_{name}"] = Future()
def process_agr_portfolio_query(self, message):
self.request_futures["process_agr_portfolio_query"].set_result(message)
def process_agr_portfolio_update(self, message):
self.request_futures["process_agr_portfolio_update"].set_result(message)
def process_dso_portfolio_query(self, message):
self.request_futures["process_dso_portfolio_query"].set_result(message)
def process_dso_portfolio_update(self, message):
self.request_futures["process_dso_portfolio_update"].set_result(message)
def process_test_message(self, message, sender_role):
self.request_futures["process_test_message"].set_result(message)
super().process_test_message(message, sender_role)
def process_test_message_response(self, message):
self.request_futures["process_test_message_response"].set_result(message)
super().process_test_message_response(message)
class DummyDsoService(ShapeshifterDsoService):
def __init__(self):
super().__init__(
sender_domain=DSO_DOMAIN,
signing_key=DSO_PRIVATE_KEY,
key_lookup_function=key_lookup_function,
endpoint_lookup_function=endpoint_lookup_function,
port=DSO_TEST_PORT
)
self.request_futures = {
f"{stage}_{name}": Future()
for stage, name in itertools.product(
["pre_process", "process"],
[
name
for name in [
snake_case(message.__name__)
for message in self.acceptable_messages
]
],
)
}
self.response_futures = {
name: Future()
for name in [
f"pre_process_{snake_case(message.__name__)}"
for message in self.acceptable_messages
]
}
def reset_futures(self, name):
self.request_futures[f"pre_process_{name}"] = Future()
self.request_futures[f"process_{name}"] = Future()
self.response_futures[f"pre_process_{name}"] = Future()
def process_flex_offer(self, message):
self.request_futures["process_flex_offer"].set_result(message)
def process_flex_order_response(self, message):
self.request_futures["process_flex_order_response"].set_result(message)
def process_d_prognosis(self, message):
self.request_futures["process_d_prognosis"].set_result(message)
def process_flex_offer_revocation(self, message):
self.request_futures["process_flex_offer_revocation"].set_result(message)
def process_flex_settlement_response(self, message):
self.request_futures["process_flex_settlement_response"].set_result(message)
def process_dso_portfolio_update_response(self, message):
self.request_futures["process_dso_portfolio_update_response"].set_result(message)
def process_dso_portfolio_query_response(self, message):
self.request_futures["process_dso_portfolio_query_response"].set_result(message)
def process_flex_request_response(self, message):
self.request_futures["process_flex_request_response"].set_result(message)
def process_flex_reservation_update_response(self, message):
self.request_futures["process_flex_reservation_update_response"].set_result(message)
def process_metering(self, message):
self.request_futures["process_metering"].set_result(message)
def process_test_message(self, message, sender_role):
self.request_futures["process_test_message"].set_result(message)
super().process_test_message(message, sender_role)
def process_test_message_response(self, message):
if self.request_futures["process_test_message_response"].done() is False:
self.request_futures["process_test_message_response"].set_result(message)
super().process_test_message_response(message)
class DefaultResponseAgrService(ShapeshifterAgrService):
def __init__(self):
super().__init__(
sender_domain=AGR_DOMAIN,
signing_key=AGR_PRIVATE_KEY,
key_lookup_function=key_lookup_function,
endpoint_lookup_function=endpoint_lookup_function,
port=AGR_TEST_PORT
)
def process_flex_request(self, message):
pass
def process_flex_order(self, message):
pass
def process_flex_reservation_update(self, message):
pass
def process_flex_settlement(self, message):
pass
def process_flex_offer_revocation_response(self, message):
pass
def process_agr_portfolio_query_response(self, message):
pass
def process_agr_portfolio_update_response(self, message):
pass
def process_d_prognosis_response(self, message):
pass
def process_flex_offer_response(self, message):
pass
def process_metering_response(self, message):
pass
class DefaultResponseCroService(ShapeshifterCroService):
def __init__(self):
super().__init__(
sender_domain=CRO_DOMAIN,
signing_key=CRO_PRIVATE_KEY,
key_lookup_function=key_lookup_function,
endpoint_lookup_function=endpoint_lookup_function,
port=CRO_TEST_PORT
)
def process_agr_portfolio_query(self, message):
pass
def process_agr_portfolio_update(self, message):
pass
def process_dso_portfolio_query(self, message):
pass
def process_dso_portfolio_update(self, message):
pass
class DefaultResponseDsoService(ShapeshifterDsoService):
def __init__(self):
super().__init__(
sender_domain=DSO_DOMAIN,
signing_key=DSO_PRIVATE_KEY,
key_lookup_function=key_lookup_function,
endpoint_lookup_function=endpoint_lookup_function,
port=DSO_TEST_PORT
)
def process_flex_offer(self, message):
pass
def process_flex_order_response(self, message):
pass
def process_d_prognosis(self, message):
pass
def process_flex_offer_revocation(self, message):
pass
def process_flex_settlement_response(self, message):
pass
def process_dso_portfolio_update_response(self, message):
pass
def process_dso_portfolio_query_response(self, message):
pass
def process_flex_request_response(self, message):
pass
def process_flex_reservation_update_response(self, message):
pass
def process_metering(self, message):
pass