Bez popisu

cs1_6.py 6.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. import asyncio
  2. import logging
  3. from datetime import datetime
  4. from pprint import pprint
  5. try:
  6. import websockets
  7. except ModuleNotFoundError:
  8. print("This example relies on the 'websockets' package.")
  9. print("Please install it by running: ")
  10. print()
  11. print(" $ pip install websockets")
  12. import sys
  13. sys.exit(1)
  14. from ocpp.routing import on, after
  15. from ocpp.v16 import ChargePoint as cp
  16. from ocpp.v16 import call_result
  17. from ocpp.v16.datatypes import IdTagInfo
  18. from ocpp.v16.enums import Action, RegistrationStatus, AuthorizationStatus
  19. from ocpp.messages import Call, MessageType, unpack, validate_payload
  20. logging.basicConfig(level=logging.INFO)
  21. class CustomCP(cp):
  22. def __init__(self, id, connection, response_timeout=30):
  23. super().__init__(id, connection, response_timeout)
  24. async def _handle_call(self, msg):
  25. """
  26. Execute all hooks installed for based on the Action of the message.
  27. First the '_on_action' hook is executed and its response is returned to
  28. the client. If there is no '_on_action' hook for Action in the message
  29. a CallError with a NotImplementedError is returned. If the Action is
  30. not supported by the OCPP version a NotSupportedError is returned.
  31. Next the '_after_action' hook is executed.
  32. """
  33. try:
  34. handlers = self.route_map[msg.action]
  35. except KeyError:
  36. _raise_key_error(msg.action, self._ocpp_version)
  37. return
  38. if not handlers.get("_skip_schema_validation", False):
  39. validate_payload(msg, self._ocpp_version)
  40. # OCPP uses camelCase for the keys in the payload. It's more pythonic
  41. # to use snake_case for keyword arguments. Therefore the keys must be
  42. # 'translated'. Some examples:
  43. #
  44. # * chargePointVendor becomes charge_point_vendor
  45. # * firmwareVersion becomes firmwareVersion
  46. if msg.payload is None:
  47. msg.payload = {}
  48. snake_case_payload = camel_to_snake_case(msg.payload)
  49. try:
  50. handler = handlers["_on_action"]
  51. except KeyError:
  52. _raise_key_error(msg.action, self._ocpp_version)
  53. try:
  54. response = handler(**snake_case_payload)
  55. if inspect.isawaitable(response):
  56. response = await response
  57. except Exception as e:
  58. LOGGER.exception("Error while handling request '%s'", msg)
  59. response = msg.create_call_error(e).to_json()
  60. await self._send(response)
  61. return
  62. temp_response_payload = asdict(response)
  63. # Remove nones ensures that we strip out optional arguments
  64. # which were not set and have a default value of None
  65. response_payload = remove_nones(temp_response_payload)
  66. # The response payload must be 'translated' from snake_case to
  67. # camelCase. So:
  68. #
  69. # * charge_point_vendor becomes chargePointVendor
  70. # * firmware_version becomes firmwareVersion
  71. camel_case_payload = snake_to_camel_case(response_payload)
  72. response = msg.create_call_result(camel_case_payload)
  73. if not handlers.get("_skip_schema_validation", False):
  74. validate_payload(response, self._ocpp_version)
  75. await self._send(response.to_json())
  76. try:
  77. handler = handlers["_after_action"]
  78. # Create task to avoid blocking when making a call inside the
  79. # after handler
  80. response = handler(**snake_case_payload)
  81. if inspect.isawaitable(response):
  82. asyncio.ensure_future(response)
  83. except KeyError:
  84. # '_on_after' hooks are not required. Therefore ignore exception
  85. # when no '_on_after' hook is installed.
  86. pass
  87. return response
  88. class ChargePoint(cp):
  89. @on(Action.BootNotification)
  90. def on_boot_notification(
  91. self, charge_point_vendor: str, charge_point_model: str, **kwargs):
  92. pprint(kwargs)
  93. return call_result.BootNotificationPayload(
  94. current_time=datetime.utcnow().isoformat(),
  95. interval=10,
  96. status=RegistrationStatus.accepted,
  97. )
  98. @on(Action.Authorize)
  99. def on_authorization(self, id_tag):
  100. # pprint(kwargs)
  101. idti = IdTagInfo(status=AuthorizationStatus.accepted)
  102. # idti.status = AuthorizationStatus.accepted
  103. return call_result.AuthorizePayload(
  104. id_tag_info=idti
  105. )
  106. @on(Action.StatusNotification)
  107. def on_status_notification(self, id_tag):
  108. # pprint(kwargs)
  109. # idti = IdTagInfo(status=AuthorizationStatus.accepted)
  110. # idti.status = AuthorizationStatus.accepted
  111. return call_result.StatusNotificationPayload()
  112. @on(Action.Heartbeat, skip_schema_validation=True)
  113. def on_heartbeat(self, **kwargs): # receives empty payload from CP
  114. print("--HeartBeat--")
  115. print(kwargs)
  116. return call_result.HeartbeatPayload(
  117. current_time=datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")
  118. )
  119. async def on_connect(websocket, path):
  120. """For every new charge point that connects, create a ChargePoint
  121. instance and start listening for messages.
  122. """
  123. try:
  124. requested_protocols = websocket.request_headers["Sec-WebSocket-Protocol"]
  125. except KeyError:
  126. logging.error("Client hasn't requested any Subprotocol. Closing Connection")
  127. return await websocket.close()
  128. if websocket.subprotocol:
  129. logging.info("Protocols Matched: %s", websocket.subprotocol)
  130. else:
  131. # In the websockets lib if no subprotocols are supported by the
  132. # client and the server, it proceeds without a subprotocol,
  133. # so we have to manually close the connection.
  134. logging.warning(
  135. "Protocols Mismatched | Expected Subprotocols: %s,"
  136. " but client supports %s | Closing connection",
  137. websocket.available_subprotocols,
  138. requested_protocols,
  139. )
  140. return await websocket.close()
  141. charge_point_id = path.strip("/")
  142. cp = ChargePoint(charge_point_id, websocket)
  143. await cp.start()
  144. async def main():
  145. server = await websockets.serve(
  146. on_connect, "0.0.0.0", 9000, subprotocols=["ocpp1.6"]
  147. )
  148. logging.info("Server Started listening to new connections...")
  149. await server.wait_closed()
  150. if __name__ == "__main__":
  151. # asyncio.run() is used when running this example with Python >= 3.7v
  152. asyncio.run(main())