mobilityhouse / ocpp

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

CentralSystem send remote start transaction when Criteria Fulfilled #507

Closed dalnel closed 12 months ago

dalnel commented 12 months ago

I use await asyncio.gather(cp.start(), cp.send_remote_start_transaction()), and it successfully sends a charging command to the charging station. However, I want to issue the charging command only when a certain condition is met (e.g., when variable 'a' keeps accumulating to 10). It seems that it gets blocked by cp.start() and doesn't allow other code to execute. Does anyone have a solution for this? My expected code is as follows:

import asyncio
from re import L
import websockets
from datetime import datetime
import logging

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

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

class ChargePoint(cp):

    # Operations Initiated by Central System
    async def send_remote_start_transaction(self):
        request = call.RemoteStartTransactionPayload(
            id_tag='test'
        )

        response = await self.call(request)

        if response.status == RemoteStartStopStatus.accepted:
            print("Transaction Started!!!.")

        elif response.status == ChargingProfileStatus.rejected:
            print("Transaction not Started!!!.")

    @on(Action.BootNotification)
    def on_boot_notitication(self, charge_point_vendor, charge_point_model, **kwargs):

        print(f'{charge_point_model} from {charge_point_vendor} booted.')

        return call_result.BootNotificationPayload(
            current_time=datetime.utcnow().isoformat(),
            interval=10,
            status=RegistrationStatus.accepted
        )

    @on(Action.Heartbeat)
    def on_heartbeat(self, **kwargs):
        return call_result.HeartbeatPayload(
            current_time=datetime.utcnow().isoformat()
        )

# Server functions

async def on_connect(websocket, path):
    """ For every new charge point that connects, create a ChargePoint instance
    and start listening for messages.

    """
    #await websocket.send('Connection made succesfully.')
    charge_point_id = path.strip('/')

    cp = ChargePoint(charge_point_id, websocket)  
    await cp.start() #僅接收心跳包
    a = 0
    while True:
        await asyncio.sleep(1)
        a += 1
        print(a)
        if a == 10:
            await cp.send_remote_start_transaction()

async def main():
    server = await websockets.serve(
        on_connect,
        '0.0.0.0',
        8888,
        subprotocols=['ocpp1.6']
    )

    await server.wait_closed()

if __name__ == '__main__':
    asyncio.run(main())
OrangeTux commented 12 months ago

cp.start() is a coroutine that never ends under normal conditions. It only ends with an exception indicating an error.

You want to run several coroutines concurrently. Check out the documentation of asyncio.gather() or asyncio.TaksGroup() to learn how to run several coroutines concurrently.

dalnel commented 12 months ago

cp.start() is a coroutine that never ends under normal conditions. It only ends with an exception indicating an error.

You want to run several coroutines concurrently. Check out the documentation of asyncio.gather() or asyncio.TaksGroup() to learn how to run several coroutines concurrently.

Thank you for your response. After reading the documents you provided, I made some modifications to the program, and it is now running smoothly. Below is the code I modified. If there's a better way to write it, please advise.

import asyncio
from re import L
import websockets
from datetime import datetime
import logging

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

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

class ChargePoint(cp):

    # Operations Initiated by Central System
    async def send_remote_start_transaction(self):
        request = call.RemoteStartTransactionPayload(
            id_tag='test'
        )

        response = await self.call(request)

        if response.status == RemoteStartStopStatus.accepted:
            print("Transaction Started!!!.")

        elif response.status == ChargingProfileStatus.rejected:
            print("Transaction not Started!!!.")

    @on(Action.BootNotification)
    def on_boot_notitication(self, charge_point_vendor, charge_point_model, **kwargs):

        print(f'{charge_point_model} from {charge_point_vendor} booted.')

        return call_result.BootNotificationPayload(
            current_time=datetime.utcnow().isoformat(),
            interval=10,
            status=RegistrationStatus.accepted
        )

    @on(Action.Heartbeat)
    def on_heartbeat(self, **kwargs):
        return call_result.HeartbeatPayload(
            current_time=datetime.utcnow().isoformat()
        )

# Server functions

async def a():
    b = 0
    while True:
        await asyncio.sleep(1)
        b += 1
        print(b)
        if b == 10:
            print("123")

async def on_connect(websocket, path):
    """ For every new charge point that connects, create a ChargePoint instance
    and start listening for messages.

    """
    #await websocket.send('Connection made succesfully.')
    charge_point_id = path.strip('/')

    cp = ChargePoint(charge_point_id, websocket)  
    await asyncio.gather(cp.start(), a()) #僅接收心跳包

async def main():
    server = await websockets.serve(
        on_connect,
        '0.0.0.0',
        8888,
        subprotocols=['ocpp1.6']
    )

    await server.wait_closed()

if __name__ == '__main__':
    asyncio.run(main())
OrangeTux commented 12 months ago

Seems okay to me!