Source code for shapeshifter_uftp.service.dso_service

from abc import ABC, abstractmethod
from .base_service import ShapeshifterService
from ..client import ShapeshifterDsoAgrClient, ShapeshifterDsoCroClient
from ..uftp import (
    AcceptedRejected,
    DPrognosis,
    DsoPortfolioQueryResponse,
    DsoPortfolioUpdateResponse,
    FlexOffer,
    FlexOfferRevocation,
    FlexOrderResponse,
    FlexRequestResponse,
    FlexSettlementResponse,
    FlexReservationUpdateResponse,
    Metering,
    PayloadMessageResponse,
)


[docs] class ShapeshifterDsoService(ShapeshifterService, ABC): """ Service that represents the Distribution System Operator in the UFTP communication. It can receive requests from the Aggregator. You should subclass this class and implement your own message handling methods. """ sender_role = "DSO" acceptable_messages = [ DPrognosis, DsoPortfolioQueryResponse, DsoPortfolioUpdateResponse, FlexOffer, FlexOfferRevocation, FlexOrderResponse, FlexRequestResponse, FlexReservationUpdateResponse, FlexSettlementResponse, Metering, ] # ------------------------------------------------------------ # # Methods related to processing D Prognosis messages # # ------------------------------------------------------------ #
[docs] def pre_process_d_prognosis(self, message: DPrognosis) -> PayloadMessageResponse: """ D-Prognosis messages are used to communicate D-prognoses between AGRs and DSOs. D-Prognosis messages always contain data for all ISPs for the period they apply to, even if a prognosis is sent after the start of the period, when one or more ISPs are already in the operate or settlement phase. Receiving implementations should ignore the information supplied for those ISPs. """ return PayloadMessageResponse(result=AcceptedRejected.ACCEPTED)
[docs] @abstractmethod def process_d_prognosis(self, message: DPrognosis): """ This method is called after the pre_process_d_prognosis method is completed. It gives you the chance to perform longer-running operations outside of the request context. """
# ------------------------------------------------------------ # # Methods related to processing Flex Request Response # # messages # # ------------------------------------------------------------ #
[docs] def pre_process_flex_request_response(self, message: FlexRequestResponse) -> PayloadMessageResponse: """ FlexOffer messages are used by AGRs to make DSOs an offer for provision of flexibility. A FlexOffer message contains a list of ISPs and, for each ISP, the change in consumption or production offered and the price for the total amount of flexibility offered. FlexOffer messages can be sent once a FlexRequest message has been received but can also be sent unsolicited. Note that multiple FlexOffer messages may be sent based on a single FlexRequest, e.g. to increase the chance that the DSO will order at least part of its available flexibility. The AGR must make sure that it can actually provide the flexibility offered across all of its FlexOffers. """ return PayloadMessageResponse(result=AcceptedRejected.ACCEPTED)
[docs] @abstractmethod def process_flex_request_response(self, message: FlexRequestResponse): """ This method is called after the pre_process_flex_offer method is completed. It gives you the chance to perform longer-running operations outside of the request context. """
# ------------------------------------------------------------ # # Methods related to processing Flex Offer messages # # ------------------------------------------------------------ #
[docs] def pre_process_flex_offer(self, message: FlexOffer) -> PayloadMessageResponse: """ FlexOffer messages are used by AGRs to make DSOs an offer for provision of flexibility. A FlexOffer message contains a list of ISPs and, for each ISP, the change in consumption or production offered and the price for the total amount of flexibility offered. FlexOffer messages can be sent once a FlexRequest message has been received but can also be sent unsolicited. Note that multiple FlexOffer messages may be sent based on a single FlexRequest, e.g. to increase the chance that the DSO will order at least part of its available flexibility. The AGR must make sure that it can actually provide the flexibility offered across all of its FlexOffers. """ return PayloadMessageResponse(result=AcceptedRejected.ACCEPTED)
[docs] @abstractmethod def process_flex_offer(self, message: FlexOffer): """ This method is called after the pre_process_flex_offer method is completed. It gives you the chance to perform longer-running operations outside of the request context. """
# ------------------------------------------------------------ # # Methods related to processing Flex Order Response messages # # ------------------------------------------------------------ #
[docs] def pre_process_flex_order_response(self, message: FlexOrderResponse) -> PayloadMessageResponse: """ Upon receiving and processing a FlexOrder message, the receiving implementation must reply with a FlexOrderResponse, indicating whether the update was handled successfully. """ return PayloadMessageResponse(result=AcceptedRejected.ACCEPTED)
[docs] @abstractmethod def process_flex_order_response(self, message: FlexOrderResponse): """ This method is called after the pre_process_flex_order_response method is completed. It gives you the chance to perform longer-running operations outside of the request context. """
# ------------------------------------------------------------ # # Methods related to processing Flex Offer Revocation # # messages # # ------------------------------------------------------------ #
[docs] def pre_process_flex_offer_revocation(self, message: FlexOfferRevocation) -> PayloadMessageResponse: """ The FlexOfferRevocation message is used by the AGR to revoke a FlexOffer previously sent to a DSO. It voids the FlexOffer, even if its validity time has not yet expired. Revocation is not allowed for FlexOffers that already have associated accepted FlexOrders. """ return PayloadMessageResponse(result=AcceptedRejected.ACCEPTED)
[docs] @abstractmethod def process_flex_offer_revocation(self, message: FlexOfferRevocation): """ This method runs separately from the pre_process_flex_offer_revocation function. It gives you the chance to perform longer-running operations outside of the request context. """
# ------------------------------------------------------------ # # Methods related to processing Flex Reservation Update # # Response messages # # ------------------------------------------------------------ #
[docs] def pre_process_flex_reservation_update_response(self, message: FlexReservationUpdateResponse) -> PayloadMessageResponse: """ The FlexOfferRevocation message is used by the AGR to revoke a FlexOffer previously sent to a DSO. It voids the FlexOffer, even if its validity time has not yet expired. Revocation is not allowed for FlexOffers that already have associated accepted FlexOrders. """ return PayloadMessageResponse(result=AcceptedRejected.ACCEPTED)
[docs] @abstractmethod def process_flex_reservation_update_response(self, message: FlexReservationUpdateResponse): """ This method runs separately from the pre_process_flex_offer_revocation function. It gives you the chance to perform longer-running operations outside of the request context. """
# ------------------------------------------------------------ # # Methods related to processing Flex Settlement Response # # messages # # ------------------------------------------------------------ #
[docs] def pre_process_flex_settlement_response(self, message: FlexSettlementResponse) -> PayloadMessageResponse: """ Upon receiving and processing a FlexSettlement message, the AGR must reply with a FlexSettlementResponse, indicating whether the initial message was handled successfully. When a FlexSettlement message is rejected, the DSO should consider all FlexOrderSettlement elements of that message related to potential dispute. """ return PayloadMessageResponse(result=AcceptedRejected.ACCEPTED)
[docs] @abstractmethod def process_flex_settlement_response(self, message: FlexSettlementResponse): """ This method runs separately from the pre_process_flex_settlement_response function. It gives you the chance to perform longer-running operations outside of the request context. """
# ------------------------------------------------------------ # # Methods related to processing DSO Portfolio Query Response # # messages # # ------------------------------------------------------------ #
[docs] def pre_process_dso_portfolio_query_response(self, message: DsoPortfolioQueryResponse) -> PayloadMessageResponse: """ Upon receiving and processing a DSOPortfolioQuery message, the receiving implementation must reply with a DSOPortfolioQueryResponse, indicating whether the query executed successfully, and if it did, including the query results. Most queries will return zero or more congestion points """ return PayloadMessageResponse(result=AcceptedRejected.ACCEPTED)
[docs] @abstractmethod def process_dso_portfolio_query_response(self, message: DsoPortfolioQueryResponse): """ This method runs after the pre_process_dso_portfolio_query_response has finised. """
# ------------------------------------------------------------ # # Methods related to processing DSO Portfolio Update # # Response messages # # ------------------------------------------------------------ #
[docs] def pre_process_dso_portfolio_update_response(self, message: DsoPortfolioUpdateResponse) -> PayloadMessageResponse: """ Upon receiving and processing a DSOPortfolioUpdate message, the receiving implementation must reply with a DSOPortfolioUpdateResponse, indicating whether the update was handled successfully. """ return PayloadMessageResponse(result=AcceptedRejected.ACCEPTED)
[docs] @abstractmethod def process_dso_portfolio_update_response(self, message: DsoPortfolioUpdateResponse): """ This method runs after the pre_process_portfolio_update_response method has finished. """
# ------------------------------------------------------------ # # Methods related to processing Metering messages # # ------------------------------------------------------------ #
[docs] def pre_process_metering(self, message: Metering) -> PayloadMessageResponse: """ The Metering message is an optional message. The DSO will specify whether metering messages are required for a given program. If metering messages are used then the AGR must send metering messages, with one message sent per connection point per day. The metering messages must all be sent before the settlement can be performed. It is recommend to send the metering messages daily, once the metering data has been collected for the day. """ return PayloadMessageResponse(result=AcceptedRejected.ACCEPTED)
[docs] @abstractmethod def process_metering(self, message: Metering): """ This method runs after the pre_process_metering method has finished. """
# ------------------------------------------------------------ # # Convenience methods for getting a client to the designated # # participant. # # ------------------------------------------------------------ #
[docs] def agr_client(self, recipient_domain) -> ShapeshifterDsoAgrClient: """ Retrieve a client object for sending messages to the AGR. """ return self._get_client(recipient_domain, 'AGR')
[docs] def cro_client(self, recipient_domain) -> ShapeshifterDsoCroClient: """ Retrieve a client object for sending messages to the CRO. """ return self._get_client(recipient_domain, 'CRO')