Skip to content

Commit

Permalink
Reduce use of NotSupportedError in favor of NotImplementedError
Browse files Browse the repository at this point in the history
Table 7 of the Open Charge Point Protocol JSON 1.6, OCPP-J 1.6 Specification lists all error codes with a short description.

* NotSupported - Requested Action is not known by receiver
* NotImplemented - Requested Action is recognized but not supported by the receiver charge_point.py:175 can indicate both problems.

This project didn't distinguished between the 2 cases and would raise a NotSupportedError in both cases.

That changes with this commit. A NotImplementedError will be raised for valid OCPP actions that are not implemented by the user.

Fixes: #493
  • Loading branch information
drc38 authored Nov 3, 2023
1 parent 09ab8c6 commit f159cf7
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 15 deletions.
47 changes: 39 additions & 8 deletions ocpp/charge_point.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from dataclasses import asdict
from typing import Dict, List, Union

from ocpp.exceptions import NotSupportedError, OCPPError
from ocpp.exceptions import NotImplementedError, NotSupportedError, OCPPError
from ocpp.messages import Call, MessageType, unpack, validate_payload
from ocpp.routing import create_route_map

Expand Down Expand Up @@ -79,6 +79,39 @@ def remove_nones(data: Union[List, Dict]) -> Union[List, Dict]:
return data


def _raise_key_error(action, version):
"""
Checks whether a keyerror returned by _handle_call
is supported by the OCPP version or is simply
not implemented by the server/client and raises
the appropriate error.
"""

from ocpp.v16.enums import Action as v16_Action
from ocpp.v201.enums import Action as v201_Action

if version == "1.6":
if hasattr(v16_Action, action):
raise NotImplementedError(
details={"cause": f"No handler for {action} registered."}
)
else:
raise NotSupportedError(
details={"cause": f"{action} not supported by OCPP{version}."}
)
elif version in ["2.0", "2.0.1"]:
if hasattr(v201_Action, action):
raise NotImplementedError(
details={"cause": f"No handler for {action} registered."}
)
else:
raise NotSupportedError(
details={"cause": f"{action} not supported by OCPP{version}."}
)

return


class ChargePoint:
"""
Base Element containing all the necessary OCPP1.6J messages for messages
Expand Down Expand Up @@ -165,17 +198,17 @@ async def _handle_call(self, msg):
First the '_on_action' hook is executed and its response is returned to
the client. If there is no '_on_action' hook for Action in the message
a CallError with a NotSupportedError is returned.
a CallError with a NotImplementedError is returned. If the Action is
not supported by the OCPP version a NotSupportedError is returned.
Next the '_after_action' hook is executed.
"""
try:
handlers = self.route_map[msg.action]
except KeyError:
raise NotSupportedError(
details={"cause": f"No handler for {msg.action} registered."}
)
_raise_key_error(msg.action, self._ocpp_version)
return

if not handlers.get("_skip_schema_validation", False):
validate_payload(msg, self._ocpp_version)
Expand All @@ -190,9 +223,7 @@ async def _handle_call(self, msg):
try:
handler = handlers["_on_action"]
except KeyError:
raise NotSupportedError(
details={"cause": f"No handler for {msg.action} registered."}
)
_raise_key_error(msg.action, self._ocpp_version)

try:
response = handler(**snake_case_payload)
Expand Down
8 changes: 4 additions & 4 deletions ocpp/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,14 @@ def __str__(self):

class NotImplementedError(OCPPError):
code = "NotImplemented"
default_description = "Requested Action is not known by receiver"
default_description = (
"Request Action is recognized but not supported by the receiver"
)


class NotSupportedError(OCPPError):
code = "NotSupported"
default_description = (
"Request Action is recognized but not supported by " "the receiver"
)
default_description = "Requested Action is not known by receiver"


class InternalError(OCPPError):
Expand Down
5 changes: 5 additions & 0 deletions tests/v16/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ def heartbeat_call():
return Call(unique_id=1, action=Action.Heartbeat, payload={}).to_json()


@pytest.fixture
def not_supported_call():
return Call(unique_id=1, action="InvalidAction", payload={}).to_json()


@pytest.fixture
def boot_notification_call():
return Call(
Expand Down
39 changes: 38 additions & 1 deletion tests/v16/test_v16_charge_point.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,43 @@ def on_boot_notification(**kwargs): # noqa
)


@pytest.mark.asyncio
async def test_route_message_not_supported(base_central_system, not_supported_call):
"""
Test that a CALLERROR is sent back, reporting that it is
not supported by OCPP version.
"""

@on(Action.BootNotification)
def on_boot_notification(**kwargs): # noqa
assert kwargs["firmware_version"] == "#1:3.4.0-2990#N:217H;1.0-223"

return call_result.BootNotificationPayload(
current_time="2018-05-29T17:37:05.495259",
interval=350,
# 'Yolo' is not a valid value for for field status.
status="Yolo",
)

setattr(base_central_system, "on_boot_notification", on_boot_notification)
base_central_system.route_map = create_route_map(base_central_system)

await base_central_system.route_message(not_supported_call)
base_central_system._connection.send.assert_called_once_with(
json.dumps(
[
4,
1,
"NotSupported",
"Requested Action is not known by receiver",
{"cause": "InvalidAction not supported by OCPP1.6."},
],
separators=(",", ":"),
)
)


@pytest.mark.asyncio
async def test_route_message_with_no_route(base_central_system, heartbeat_call):
"""
Expand All @@ -124,7 +161,7 @@ async def test_route_message_with_no_route(base_central_system, heartbeat_call):
[
4,
1,
"NotSupported",
"NotImplemented",
"Request Action is recognized but not supported by the receiver",
{"cause": "No handler for Heartbeat registered."},
],
Expand Down
2 changes: 1 addition & 1 deletion tests/v20/test_v20_charge_point.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ async def test_route_message_with_no_route(base_central_system, heartbeat_call):
[
4,
1,
"NotSupported",
"NotImplemented",
"Request Action is recognized but not supported by the receiver",
{"cause": "No handler for Heartbeat registered."},
],
Expand Down
2 changes: 1 addition & 1 deletion tests/v201/test_v201_charge_point.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ async def test_route_message_with_no_route(base_central_system, heartbeat_call):
[
4,
1,
"NotSupported",
"NotImplemented",
"Request Action is recognized but not supported by the receiver",
{"cause": "No handler for Heartbeat registered."},
],
Expand Down

0 comments on commit f159cf7

Please sign in to comment.