mobilityhouse / ocpp

Python implementation of the Open Charge Point Protocol (OCPP).
MIT License
788 stars 310 forks source link

RemoteStartTransaction: TimeoutErrore #196

Closed Sara-H07 closed 3 years ago

Sara-H07 commented 3 years ago

I'm trying to run the code in https://github.com/mobilityhouse/ocpp/issues/81#issue-592479514 after making the corrections in on_connect presented in https://github.com/mobilityhouse/ocpp/issues/81#issuecomment-607877799 , but I'm getting TimeoutErrore on the central system side. Can you help, please?

Central_system.py

import asyncio
from datetime import datetime
#from simple_charge.config.settings import *
#from simple_charge.config.handlers import *
from pymongo import MongoClient
from bson import ObjectId
import json
from ocpp.routing import on
from ocpp.v16 import ChargePoint as cp
from ocpp.v16.enums import *
from ocpp.v16 import call_result, call

try:
    import websockets
except ModuleNotFoundError:
    print("This example relies on the 'websockets' package.")
    print("Please install it by running: ")
    print()
    print(" $ pip install websockets")
    import sys
    sys.exit(1)

class ChargePoint(cp):
    @on(Action.BootNotification)
    def on_boot_notification(self, charge_point_vendor, charge_point_model, **kwargs):
        return call_result.BootNotificationPayload(
            current_time=datetime.utcnow().isoformat(),
            interval=10,
            status=RegistrationStatus.accepted
        )

    @on(Action.Heartbeat)
    def on_heartbeat(self):
        return call_result.HeartbeatPayload(
            current_time=datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S') + "Z"
        )

    @on(Action.Authorize)
    def on_authorize(self, id_tag):
        return call_result.AuthorizePayload(
            id_tag_info={
                "status": 'Accepted'
            }
        )

    @on(Action.StartTransaction)
    def on_start_transaction(self, connector_id, id_tag, timestamp, meter_start, reservation_id):
        return call_result.StartTransactionPayload(
            id_tag_info={
                "status": 'Accepted'
            },
            transaction_id=int(1)
        )

    @on(Action.StopTransaction)
    def on_stop_transaction(self, transaction_id, id_tag, timestamp, meter_stop):
        return call_result.StopTransactionPayload()

    @on(Action.MeterValues)
    def on_meter_value(self):
        return call_result.MeterValuesPayload()

    @on(Action.StatusNotification)
    def on_status_notification(self, connector_id, error_code, status):
        return call_result.StatusNotificationPayload()

    @on(Action.DataTransfer)
    def on_data_transfer(self, vendor_id, message_id, data):
        return call_result.DataTransferPayload(
            status='Accepted'
        )

    async def remote_start_transaction(self):
        request = call.RemoteStartTransactionPayload(
            id_tag='1'
        )
        response = await self.call(request)
        if response.status == RemoteStartStopStatus.accepted:
            print("Transaction Started!!!")

    async def remote_stop_transaction(self):
        request = call.RemoteStopTransactionPayload(
            transaction_id=1
        )
        response = await self.call(request)

        if response.status == RemoteStartStopStatus.accepted:
            print("Stopping transaction")

connected = set()
clients = dict()
ping_counter = 0
clients_couter = 0

async def on_connect(websocket, path):
    charge_point_id = path.strip('/')
    cp = ChargePoint(charge_point_id, websocket)
    try:
        await asyncio.gather(cp.start(), cp.remote_start_transaction())
    except websockets.exceptions.ConnectionClosed:
        connected.remove(websocket)
        print("Charge Point disconnected")

async def actions(websocket, path):
    charge_point_id = path.strip('/')
    cp = ChargePoint(charge_point_id, websocket)
    if websocket:
        print("B action", websocket)
        await cp.remote_start_transaction()

async def register(websocket, path):
    await asyncio.sleep(2)
    connected.add(websocket)
    await actions(websocket, path)
OrangeTux commented 3 years ago

Please provide a strack trace of the error.

Sara-H07 commented 3 years ago

thank you for your reply

Error in connection handler
Traceback (most recent call last):
  File "/home/sara/.local/lib/python3.7/site-packages/websockets/server.py", line 191, in handler
    await self.ws_handler(self, path)
  File "/home/sara/ocpp/remote/central_system.py", line 102, in on_connect
    await asyncio.gather(cp.start(), cp.remote_start_transaction())
  File "/home/sara/ocpp/remote/central_system.py", line 78, in remote_start_transaction
    response = await self.call(request)
  File "/home/sara/.local/lib/python3.7/site-packages/ocpp/charge_point.py", line 265, in call
    self._response_timeout)
  File "/home/sara/.local/lib/python3.7/site-packages/ocpp/charge_point.py", line 293, in _get_specific_response
    timeout)
  File "/usr/lib/python3.7/asyncio/tasks.py", line 449, in wait_for
    raise futures.TimeoutError()
concurrent.futures._base.TimeoutError
OrangeTux commented 3 years ago

From what I understand is the the CSMS sending a RemoteStartTransaction to the charger. But the charger is not answering and therefore the TimeoutError is raised.

Sara-H07 commented 3 years ago

exactly, but there is a function call_result.RemoteStartTransactionPayload in charge_point.py that allows sending a response to the central system. I don't know what the problem is.

charge_point.py

import asyncio
from datetime import datetime
from central_system import actions, on_connect

try:
    import websockets
except ModuleNotFoundError:
    print("This example relies on the 'websockets' package.")
    print("Please install it by running: ")
    print()
    print(" $ pip install websockets")
    import sys
    sys.exit(1)

from ocpp.v16 import call, call_result
from ocpp.v16 import ChargePoint as cp
from ocpp.v16.enums import *
from ocpp.routing import on, after

class ChargePoint(cp):
    async def send_boot_notification(self):
        request = call.BootNotificationPayload(
            charge_point_model="Optimus",
            charge_point_vendor="The Mobility House"
        )
        response = await self.call(request)
        if response.status == RegistrationStatus.accepted:
            print("Connected to central system.")

    @on(Action.RemoteStartTransaction)
    def remote_start_transaction(self, id_tag):
        return call_result.RemoteStartTransactionPayload(status=RemoteStartStopStatus.accepted)
        print("sent")

    @on(Action.RemoteStopTransaction)
    def remote_stop_transaction(self, transaction_id):
        return call_result.RemoteStopTransactionPayload(status=RemoteStartStopStatus.accepted)

async def main():
    async with websockets.connect(
        'ws://localhost:9000/CP_1',
            subprotocols=['ocpp1.6']
    ) as ws:

        cp = ChargePoint('CP_1', ws)
        await asyncio.gather(cp.start(), cp.send_boot_notification())

if __name__ == '__main__':
    try:
        # asyncio.run() is used when running this example with Python 3.7 and
        # higher.
        asyncio.run(main())
    except AttributeError:
        # For Python 3.6 a bit more code is required to run the main() task on
        # an event loop.
        loop = asyncio.get_event_loop()
        loop.run_until_complete(main())
        loop.close()
tropxy commented 3 years ago

Hi @Sara-H07 by experience, it can be the case that, if you are using a real Charging Station, the station will only correctly process OCPP messages, after the BootNotification has been sent and responded to. As stated in the standard, no OCPP messages shall be sent before that. I am not sure if that is the case here, but wanted to bring that up..

Sara-H07 commented 3 years ago

no, I use a simple ChargePoint-simulation script

Sara-H07 commented 3 years ago

after updating the OCPP package, I got this new error

Error in connection handler
Traceback (most recent call last):
  File "/home/sara/.local/lib/python3.7/site-packages/ocpp/charge_point.py", line 266, in call
    self._response_timeout)
  File "/home/sara/.local/lib/python3.7/site-packages/ocpp/charge_point.py", line 299, in _get_specific_response
    timeout)
  File "/usr/lib/python3.7/asyncio/tasks.py", line 449, in wait_for
    raise futures.TimeoutError()
concurrent.futures._base.TimeoutError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/sara/.local/lib/python3.7/site-packages/websockets/server.py", line 191, in handler
    await self.ws_handler(self, path)
  File "/home/sara/ocpp/remote/central_system.py", line 102, in on_connect
    await asyncio.gather(cp.start(), cp.remote_start_transaction())
  File "/home/sara/ocpp/remote/central_system.py", line 78, in remote_start_transaction
    response = await self.call(request)
  File "/home/sara/.local/lib/python3.7/site-packages/ocpp/charge_point.py", line 269, in call
    f"Waited {self._response_timeout}s for response on "
concurrent.futures._base.TimeoutError: Waited 30s for response on [2,"3f45596e-06c2-446f-bfcb-0ccf2406397d","RemoteStartTransaction",{"idTag":"1"}].

Can you help, please?

OrangeTux commented 3 years ago

I tried to reproduce the problem with the code you provided. But I couldn't. It works on my machine.

sekharnanchari commented 3 years ago

Hi @Sara-H07 can you please help me how can I fix this?

tropxy commented 3 years ago

Hi, I tried the code myself as well and it works on my end as well, as you can check by this screenshot. On top you have the CSMS and on the bottom the CP:

Bildschirmfoto 2021-06-09 um 21 19 19

This is the code I have used.

CSMS

import asyncio
from datetime import datetime
#from simple_charge.config.settings import *
#from simple_charge.config.handlers import *
#from pymongo import MongoClient
# from bson import ObjectId
import json
import logging
from ocpp.routing import on
from ocpp.v16 import ChargePoint as cp
from ocpp.v16.enums import *
from ocpp.v16 import call_result, call

try:
    import websockets
except ModuleNotFoundError:
    print("This example relies on the 'websockets' package.")
    print("Please install it by running: ")
    print()
    print(" $ pip install websockets")
    import sys
    sys.exit(1)

logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger('session')

class ChargePoint(cp):
    @on(Action.BootNotification)
    def on_boot_notification(self, charge_point_vendor, charge_point_model, **kwargs):
        return call_result.BootNotificationPayload(
            current_time=datetime.utcnow().isoformat(),
            interval=10,
            status=RegistrationStatus.accepted
        )

    @on(Action.Heartbeat)
    def on_heartbeat(self):
        return call_result.HeartbeatPayload(
            current_time=datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S') + "Z"
        )

    @on(Action.Authorize)
    def on_authorize(self, id_tag):
        return call_result.AuthorizePayload(
            id_tag_info={
                "status": 'Accepted'
            }
        )

    @on(Action.StartTransaction)
    def on_start_transaction(self, connector_id, id_tag, timestamp, meter_start, reservation_id):
        return call_result.StartTransactionPayload(
            id_tag_info={
                "status": 'Accepted'
            },
            transaction_id=int(1)
        )

    @on(Action.StopTransaction)
    def on_stop_transaction(self, transaction_id, id_tag, timestamp, meter_stop):
        return call_result.StopTransactionPayload()

    @on(Action.MeterValues)
    def on_meter_value(self):
        return call_result.MeterValuesPayload()

    @on(Action.StatusNotification)
    def on_status_notification(self, connector_id, error_code, status):
        return call_result.StatusNotificationPayload()

    @on(Action.DataTransfer)
    def on_data_transfer(self, vendor_id, message_id, data):
        return call_result.DataTransferPayload(
            status='Accepted'
        )

    async def remote_start_transaction(self):
        request = call.RemoteStartTransactionPayload(
            id_tag='1'
        )
        response = await self.call(request)
        if response.status == RemoteStartStopStatus.accepted:
            print("Transaction Started!!!")

    async def remote_stop_transaction(self):
        request = call.RemoteStopTransactionPayload(
            transaction_id=1
        )
        response = await self.call(request)

        if response.status == RemoteStartStopStatus.accepted:
            print("Stopping transaction")

connected = set()
clients = dict()
ping_counter = 0
clients_couter = 0

async def on_connect(websocket, path):
    charge_point_id = path.strip('/')
    cp = ChargePoint(charge_point_id, websocket)
    try:
        await asyncio.gather(cp.start(), cp.remote_start_transaction())
    except websockets.exceptions.ConnectionClosed:
        connected.remove(websocket)
        print("Charge Point disconnected")

async def actions(websocket, path):
    charge_point_id = path.strip('/')
    cp = ChargePoint(charge_point_id, websocket)
    if websocket:
        print("B action", websocket)
        await cp.remote_start_transaction()

async def register(websocket, path):
    await asyncio.sleep(2)
    connected.add(websocket)
    await actions(websocket, path)

async def main():
    server = await websockets.serve(
        on_connect,
        '0.0.0.0',
        9000,
        subprotocols=['ocpp1.6']
    )
    logger.info("WebSocket Server Started")
    await server.wait_closed()

if __name__ == '__main__':
    asyncio.run(main())

CP

import asyncio
import logging
from datetime import datetime
#from central_system import actions, on_connect

try:
    import websockets
except ModuleNotFoundError:
    print("This example relies on the 'websockets' package.")
    print("Please install it by running: ")
    print()
    print(" $ pip install websockets")
    import sys
    sys.exit(1)

from ocpp.v16 import call, call_result
from ocpp.v16 import ChargePoint as cp
from ocpp.v16.enums import *
from ocpp.routing import on, after

logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger('cp')

class ChargePoint(cp):
    async def send_boot_notification(self):
        request = call.BootNotificationPayload(
            charge_point_model="Optimus",
            charge_point_vendor="The Mobility House"
        )
        response = await self.call(request)
        if response.status == RegistrationStatus.accepted:
            print("Connected to central system.")

    @on(Action.RemoteStartTransaction)
    def remote_start_transaction(self, id_tag):
        return call_result.RemoteStartTransactionPayload(status=RemoteStartStopStatus.accepted)

    @on(Action.RemoteStopTransaction)
    def remote_stop_transaction(self, transaction_id):
        return call_result.RemoteStopTransactionPayload(status=RemoteStartStopStatus.accepted)

async def main():
    async with websockets.connect(
        'ws://localhost:9000/CP_1',
            subprotocols=['ocpp1.6']
    ) as ws:

        cp = ChargePoint('CP_1', ws)
        await asyncio.gather(cp.start(), cp.send_boot_notification())

if __name__ == '__main__':
    try:
        # asyncio.run() is used when running this example with Python 3.7 and
        # higher.
        asyncio.run(main())
    except AttributeError:
        # For Python 3.6 a bit more code is required to run the main() task on
        # an event loop.
        loop = asyncio.get_event_loop()
        loop.run_until_complete(main())
        loop.close()
sekharnanchari commented 3 years ago

@tropxy thank you very much.

NightTalkerMY commented 10 months ago

Hi, I tried the code myself as well and it works on my end as well, as you can check by this screenshot. On top you have the CSMS and on the bottom the CP: Bildschirmfoto 2021-06-09 um 21 19 19

This is the code I have used.

CSMS

import asyncio
from datetime import datetime
#from simple_charge.config.settings import *
#from simple_charge.config.handlers import *
#from pymongo import MongoClient
# from bson import ObjectId
import json
import logging
from ocpp.routing import on
from ocpp.v16 import ChargePoint as cp
from ocpp.v16.enums import *
from ocpp.v16 import call_result, call

try:
    import websockets
except ModuleNotFoundError:
    print("This example relies on the 'websockets' package.")
    print("Please install it by running: ")
    print()
    print(" $ pip install websockets")
    import sys
    sys.exit(1)

logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger('session')

class ChargePoint(cp):
    @on(Action.BootNotification)
    def on_boot_notification(self, charge_point_vendor, charge_point_model, **kwargs):
        return call_result.BootNotificationPayload(
            current_time=datetime.utcnow().isoformat(),
            interval=10,
            status=RegistrationStatus.accepted
        )

    @on(Action.Heartbeat)
    def on_heartbeat(self):
        return call_result.HeartbeatPayload(
            current_time=datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S') + "Z"
        )

    @on(Action.Authorize)
    def on_authorize(self, id_tag):
        return call_result.AuthorizePayload(
            id_tag_info={
                "status": 'Accepted'
            }
        )

    @on(Action.StartTransaction)
    def on_start_transaction(self, connector_id, id_tag, timestamp, meter_start, reservation_id):
        return call_result.StartTransactionPayload(
            id_tag_info={
                "status": 'Accepted'
            },
            transaction_id=int(1)
        )

    @on(Action.StopTransaction)
    def on_stop_transaction(self, transaction_id, id_tag, timestamp, meter_stop):
        return call_result.StopTransactionPayload()

    @on(Action.MeterValues)
    def on_meter_value(self):
        return call_result.MeterValuesPayload()

    @on(Action.StatusNotification)
    def on_status_notification(self, connector_id, error_code, status):
        return call_result.StatusNotificationPayload()

    @on(Action.DataTransfer)
    def on_data_transfer(self, vendor_id, message_id, data):
        return call_result.DataTransferPayload(
            status='Accepted'
        )

    async def remote_start_transaction(self):
        request = call.RemoteStartTransactionPayload(
            id_tag='1'
        )
        response = await self.call(request)
        if response.status == RemoteStartStopStatus.accepted:
            print("Transaction Started!!!")

    async def remote_stop_transaction(self):
        request = call.RemoteStopTransactionPayload(
            transaction_id=1
        )
        response = await self.call(request)

        if response.status == RemoteStartStopStatus.accepted:
            print("Stopping transaction")

connected = set()
clients = dict()
ping_counter = 0
clients_couter = 0

async def on_connect(websocket, path):
    charge_point_id = path.strip('/')
    cp = ChargePoint(charge_point_id, websocket)
    try:
        await asyncio.gather(cp.start(), cp.remote_start_transaction())
    except websockets.exceptions.ConnectionClosed:
        connected.remove(websocket)
        print("Charge Point disconnected")

async def actions(websocket, path):
    charge_point_id = path.strip('/')
    cp = ChargePoint(charge_point_id, websocket)
    if websocket:
        print("B action", websocket)
        await cp.remote_start_transaction()

async def register(websocket, path):
    await asyncio.sleep(2)
    connected.add(websocket)
    await actions(websocket, path)

async def main():
    server = await websockets.serve(
        on_connect,
        '0.0.0.0',
        9000,
        subprotocols=['ocpp1.6']
    )
    logger.info("WebSocket Server Started")
    await server.wait_closed()

if __name__ == '__main__':
    asyncio.run(main())

CP

import asyncio
import logging
from datetime import datetime
#from central_system import actions, on_connect

try:
    import websockets
except ModuleNotFoundError:
    print("This example relies on the 'websockets' package.")
    print("Please install it by running: ")
    print()
    print(" $ pip install websockets")
    import sys
    sys.exit(1)

from ocpp.v16 import call, call_result
from ocpp.v16 import ChargePoint as cp
from ocpp.v16.enums import *
from ocpp.routing import on, after

logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger('cp')

class ChargePoint(cp):
    async def send_boot_notification(self):
        request = call.BootNotificationPayload(
            charge_point_model="Optimus",
            charge_point_vendor="The Mobility House"
        )
        response = await self.call(request)
        if response.status == RegistrationStatus.accepted:
            print("Connected to central system.")

    @on(Action.RemoteStartTransaction)
    def remote_start_transaction(self, id_tag):
        return call_result.RemoteStartTransactionPayload(status=RemoteStartStopStatus.accepted)

    @on(Action.RemoteStopTransaction)
    def remote_stop_transaction(self, transaction_id):
        return call_result.RemoteStopTransactionPayload(status=RemoteStartStopStatus.accepted)

async def main():
    async with websockets.connect(
        'ws://localhost:9000/CP_1',
            subprotocols=['ocpp1.6']
    ) as ws:

        cp = ChargePoint('CP_1', ws)
        await asyncio.gather(cp.start(), cp.send_boot_notification())

if __name__ == '__main__':
    try:
        # asyncio.run() is used when running this example with Python 3.7 and
        # higher.
        asyncio.run(main())
    except AttributeError:
        # For Python 3.6 a bit more code is required to run the main() task on
        # an event loop.
        loop = asyncio.get_event_loop()
        loop.run_until_complete(main())
        loop.close()

Hi, I have a question, supposed that I wanted to send JSON via websocket to the CS in remote charging, I will have my message as

[2,"34RWFMW","RemoteStartTransaction",{ "idTag":"1", "connectorId":1 }]

But when it comes to the code, it gives an error, I have also realizes that is because there is not @(Action.) written under the class chargepoint, therefore the error message i got is

[ 4, "34RWFMW", "NotSupported", "Requested Action is not known by receiver", { "cause": "RemoteStartTransaction not supported by OCPP1.6." } ]

But i if we include @(Action.), then we will have to code it as "call_result" in the central system. However, this will be weird because under the central system, we should let the central system requesting the charge point, but not central system configurate the charge point.

The suggested method only works from the central system side, and is sort of "hard calling" the remote start transaction inside the central system, even without any request being sent from the JSON via websocket to initiate remote charge.

So, is there a way to communicate with the central system, and therefore perform remote charge using JSON via websocket? Any idea for this? Thanks