Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions examples/v16/central_system.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

sys.exit(1)

from ocpp.charge_point import create_and_start_charge_point
from ocpp.charge_point import extract_charge_point_id
from ocpp.routing import on
from ocpp.v16 import ChargePoint as cp
from ocpp.v16 import call_result
Expand Down Expand Up @@ -57,7 +57,14 @@ async def on_connect(websocket):
)
return await websocket.close()

await create_and_start_charge_point(websocket, ChargePoint)
charge_point_id = extract_charge_point_id(websocket.request.path)
if not charge_point_id:
logging.error(f"No charge point ID in request path: {websocket.request.path}")
return await websocket.close()

logging.info(f"Charge point {charge_point_id} connected")
cp = ChargePoint(charge_point_id, websocket)
await cp.start()


async def main():
Expand Down
11 changes: 9 additions & 2 deletions examples/v201/central_system.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

sys.exit(1)

from ocpp.charge_point import create_and_start_charge_point
from ocpp.charge_point import extract_charge_point_id
from ocpp.routing import on
from ocpp.v201 import ChargePoint as cp
from ocpp.v201 import call_result
Expand Down Expand Up @@ -62,7 +62,14 @@ async def on_connect(websocket):
)
return await websocket.close()

await create_and_start_charge_point(websocket, ChargePoint)
charge_point_id = extract_charge_point_id(websocket.request.path)
if not charge_point_id:
logging.error(f"No charge point ID in request path: {websocket.request.path}")
return await websocket.close()

logging.info(f"Charge point {charge_point_id} connected")
cp = ChargePoint(charge_point_id, websocket)
await cp.start()


async def main():
Expand Down
11 changes: 9 additions & 2 deletions examples/v21/central_system.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

sys.exit(1)

from ocpp.charge_point import create_and_start_charge_point
from ocpp.charge_point import extract_charge_point_id
from ocpp.routing import on
from ocpp.v21 import ChargePoint as cp
from ocpp.v21 import call_result
Expand Down Expand Up @@ -62,7 +62,14 @@ async def on_connect(websocket):
)
return await websocket.close()

await create_and_start_charge_point(websocket, ChargePoint)
charge_point_id = extract_charge_point_id(websocket.request.path)
if not charge_point_id:
logging.error(f"No charge point ID in request path: {websocket.request.path}")
return await websocket.close()

logging.info(f"Charge point {charge_point_id} connected")
cp = ChargePoint(charge_point_id, websocket)
await cp.start()


async def main():
Expand Down
33 changes: 0 additions & 33 deletions ocpp/charge_point.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,6 @@ def extract_charge_point_id(path: Optional[str]) -> Optional[str]:
Returns:
The charge point ID string, or ``None`` if the path does not
contain a valid identifier.

See Also:
:func:`create_and_start_charge_point` for a higher-level helper
that extracts the ID, validates it, and starts a ``ChargePoint``.
"""
if not path:
return None
Expand All @@ -64,35 +60,6 @@ def extract_charge_point_id(path: Optional[str]) -> Optional[str]:
return charge_point_id


async def create_and_start_charge_point(websocket, charge_point_cls):
"""Extract the charge point ID from a WebSocket and start a ChargePoint.

This is a convenience coroutine for central system implementations.
It extracts the charge point ID from the WebSocket request path,
creates an instance of ``charge_point_cls``, and starts it.

If the path does not contain a valid charge point ID, the WebSocket
connection is closed and ``None`` is returned.

Args:
websocket: The WebSocket connection from the ``websockets`` library.
charge_point_cls: A ``ChargePoint`` subclass to instantiate.

Returns:
The ``ChargePoint`` instance, or ``None`` if the path was invalid.
"""
charge_point_id = extract_charge_point_id(websocket.request.path)
if not charge_point_id:
LOGGER.error("No charge point ID in path: %s", websocket.request.path)
await websocket.close()
return None

LOGGER.info("Charge point %s connected", charge_point_id)
cp = charge_point_cls(charge_point_id, websocket)
await cp.start()
return cp


def camel_to_snake_case(data):
"""
Convert all keys of all dictionaries inside the given argument from
Expand Down
41 changes: 1 addition & 40 deletions tests/test_charge_point_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

import pytest

from ocpp.charge_point import create_and_start_charge_point, extract_charge_point_id
from ocpp.charge_point import extract_charge_point_id
from ocpp.routing import on
from ocpp.v16 import ChargePoint as cp_16
from ocpp.v16 import call_result
Expand Down Expand Up @@ -156,42 +156,3 @@ async def test_reconnection_with_new_instance(self, connection):
cp2 = cp_16("CP001", connection2)
with pytest.raises(Exception):
await cp2.start()


# ---------------------------------------------------------------------------
# Tests for create_and_start_charge_point
# ---------------------------------------------------------------------------


class TestCreateAndStartChargePoint:
"""Test the create_and_start_charge_point helper."""

@pytest.mark.asyncio
async def test_valid_path_creates_and_starts(self):
"""Should create a ChargePoint and call start() for a valid path."""
ws = MagicMock()
ws.request.path = "/CP001"
ws.close = AsyncMock()

started = []

class MyCP(cp_16):
async def start(self):
started.append(self.id)

result = await create_and_start_charge_point(ws, MyCP)
assert result is not None
assert result.id == "CP001"
assert started == ["CP001"]
ws.close.assert_not_called()

@pytest.mark.asyncio
async def test_invalid_path_closes_connection(self):
"""Should close the websocket and return None for an invalid path."""
ws = MagicMock()
ws.request.path = "/"
ws.close = AsyncMock()

result = await create_and_start_charge_point(ws, cp_16)
assert result is None
ws.close.assert_awaited_once()
Loading