server.rst 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. .. _server:
  2. ======
  3. Server
  4. ======
  5. If you are implementing an OpenADR Server ("Virtual Top Node") using pyOpenADR, read this page.
  6. .. _server_registration:
  7. Registration
  8. ============
  9. If a client (VEN) wants to register for the first time, it will go through a Registration procedure.
  10. .. admonition:: Implementation Checklist
  11. 1. Create a handler that decides what to do with new registrations, based on their ``venID``.
  12. The client will send a :ref:`oadrQueryRegistration` message. The server will respond with a :ref:`oadrCreatedPartyRegistration` message containing a list of its capabilities, notably the implemented OpenADR protocol versions and the available Transport Mechanisms (HTTP and/or XMPP).
  13. The client will then usually send a :ref:`oadrCreatePartyRegistration` message, in which it registers to a specific OpenADR version and Transport Method. The server must then decide what it wants to do with this registration.
  14. In the case that the registration is accepted, the VTN will generate a RegistrationID for this VEN and respond with a :ref:`oadrCreatedPartyRegistration` message.
  15. In your application, when a VEN sends a :ref:`oadrCreatePartyRegistration` request, it will call your ``on_register_party`` handler. This handler must somehow look up what to do with this request, and respond with a ``registration_id``.
  16. Example implementation:
  17. .. code-block:: python3
  18. from pyopenadr.utils import generate_id
  19. async def on_create_party_registration(payload):
  20. ven_id = payload['ven_id']
  21. # Check whether or not this VEN is allowed to register
  22. result = await database.query("""SELECT COUNT(*)
  23. FROM vens
  24. WHERE ven_id = ?""",
  25. (payload['ven_id'],))
  26. if result == 1:
  27. # Generate an ID for this registration
  28. registration_id = generate_id()
  29. # Store the registration in a database (pseudo-code)
  30. await database.query("""UPDATE vens
  31. SET registration_id = ?
  32. WHERE ven_id = ?""",
  33. (registration_id, ven_id))
  34. # Return the registration ID.
  35. # This will be put into the correct form by the OpenADRServer.
  36. return registration_id
  37. .. _server_events:
  38. Events
  39. ======
  40. The server (VTN) is expected to know when it needs to inform the clients (VENs) of certain events that they must respond to. This could be a predicted shortage or overage of available power in a certain electricity grid area, for example.
  41. The VTN must determine when VENs are relevant and which Events to send to them. The next time the VEN polls for new messages (using a :ref:`oadrPoll` or :ref:`oadrRequestEvent` message), it will send the Event in a :ref:`oadrDistributeEvent` message to the client. The client will then evaluate whether or not it indends to comply with the request, and respond with an :ref:`oadrCreatedEvent` message containing an optStatus of ``'optIn'`` or ``'optOut'``.
  42. .. admonition:: Implementation Checklist
  43. In your application, the creation of Events is completely up to you. PyOpenADR will only call your ``on_poll`` handler with a ``ven_id``. This handler must be able to either retrieve the next event for this VEN out of some storage or queue, or make up the Event in real time.
  44. - ``on_created_event(payload)`` handler is called whenever the VEN sends an :ref:`oadrCreatedEvent` message, probably informing you of what they intend to do with the event you gave them.
  45. - ``on_request_event(ven_id)``: this should return the next event (if any) that you have for the VEN. If you return ``None``, a blank :ref:`oadrResponse` will be returned to the VEN.
  46. - ``on_request_report(ven_id)``: this should return then next report (if any) that you have for the VEN. If you return None, a blank :ref:`oadrResponse` will be returned to the VEN.
  47. - ``on_poll(ven_id)``: this should return the next message in line, which is usually either a new :ref:`oadrUpdatedReport` or a :ref:`oadrDistributeEvent` message.
  48. The Event consists of three main sections:
  49. 1. A time period for when this event is supposed to be active
  50. 2. A list of Targets to which the Event applies. This can be the VEN as a whole, or specific groups, assets, geographic areas, et cetera that this VEN represents.
  51. 3. A list of Signals, which form the content of the Event. This can be price signals, load reduction signals, et cetera. Each signal has a name, a type, multiple Intervals that contain the relative start times, and some payload value for the client to interpret.
  52. .. _server_reports:
  53. Reports
  54. =======
  55. Reporting is probably the most complicated of interactions within OpenADR. It involves the following steps:
  56. 1. Party A makes its reporting capabilities known to party B using a :ref:`oadrRegisterReport` message.
  57. 2. Party B responds with an :ref:`oadrRegisteredReport` message, optionally including an :ref:`oadrReportRequest` section that tells party A which party B is interested in.
  58. 3. Party A reponds with an oadrCreatedReport message telling party B that it will periodically generate the reports.
  59. This ceremony is performed once with the VTN as party A and once with the VEN as party A.
  60. The VEN party can collect the reports it requested from the VTN using either the :ref:`oadrPoll` or :ref:`oadrRequestReport` messages. The VTN will respond with an :ref:`oadrUpdateReport` message containing the actual report. The VEN should then respond with a :ref:`oadrUpdatedReport` message.
  61. The VEN should actively supply the reports to the VTN using :ref:`oadrUpdateReport` messages, to which the VTN will respond with :ref:`oadrUpdatedReport` messages.
  62. .. admonition:: Implementation Checklist
  63. To benefit from the automatic reporting engine in pyOpenADR, you should implement the following items yourself:
  64. 1. Configure the OpenADRServer() instance with your reporting capabilities and requirements
  65. 2. Implement a handlers that can retrieve the reports from your backend system
  66. 3. Implement a handler that deal with reports that come in from the clients
  67. .. _server_implement:
  68. Things you should implement
  69. ===========================
  70. You should implement the following handlers:
  71. - ``on_poll(ven_id)``
  72. - ``on_request_event(ven_id)``
  73. - ``on_request_report(payload)``
  74. - ``on_create_party_registration(payload)``
  75. .. _server_meta:
  76. Non-OpenADR signals from the server
  77. ===================================
  78. The pyOpenADR Server can call the following handlers, which are not part of the regular openADR communication flow, but can help you develop a more robust event-driven system:
  79. - ``on_ven_online(ven_id)``: called when a VEN sends an :ref:`oadrPoll`, :ref:`oadrRequestEvent` or :ref:`oadrRequestReport` message after it had been offline before.
  80. - ``on_ven_offline(ven_id)``: called when a VEN misses 3 consecutive poll intervals (configurable).
  81. Example implementation:
  82. .. code-block:: python3
  83. from pyopenadr import OpenADRServer
  84. server = OpenADRServer(vtn_id='MyVTN')
  85. server.add_handler('on_ven_online', on_ven_online)
  86. server.add_handler('on_ven_offline', on_ven_offline)
  87. async def on_ven_online(ven_id):
  88. print(f"VEN {ven_id} is now online again!")
  89. async def on_ven_offline(ven_id):
  90. print(f"VEN {ven_id} has gone AWOL")