mobilityhouse / ocpp

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

How should RemoteStartTransaction be implemented? #62

Closed lucianosilvi closed 4 years ago

lucianosilvi commented 4 years ago

Hello, I'm trying to trigger remote start transactions, for that, using the example on the docs, I have: `class MyChargePoint(cp):

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

    # @on(Action.RemoteStartTransaction)
    def remote_start(self, id_tag, **kwargs):
        """
            Send remote start transaction to CP
        """
        return call.RemoteStartTransactionPayload(
            id_tag=id_tag
        )

I implemented other method that calls remote_start under certain conditions, however when this method is called (remote_start) nothing is sent to the CP websocket. Is this normal? How can I check if the remote start action was received by the Charger? I'm running both ends to simulate the behaviour (Central System and CP) to check everything works fine.

Thanks

OrangeTux commented 4 years ago

I'm sorrry for missing this completely. But you seem to return the wrong payload. Instead of using call.RemoteStartTransactionPayload you should use call_result.RemoteStartTransactionPayload.

MrMika96 commented 4 years ago

Can you make an example of how to create remote_start_transaction on both central system and charge point? Because i have this on central system part:

    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!!!")

and this on charge point part:

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

And charge point is receiving id_tag, but sending call_result somewhere else, i think

OrangeTux commented 4 years ago

Closed due inactivity. Feel free to reopen if needed.

TehEckoz commented 10 months ago

Hello, I have a question, when making a remote transaction, it asks me to pass 3 parameters, two of them optional.

The id_tag, connector_id and the charging_profile.

Indeed I see that by passing the id_tag ​​and the connector_id, it connects to the charger.

But if I have different charger points, how do I identify which charger is correct to select the connector_id?

OrangeTux commented 9 months ago

When you use this library to implement a CSMS, an instance of ChargePoint is created for OCPP that is opened at your CSMS.

If you want to set a RemoteStartTransaction to a specific charger, you need to find the corresponding instance ChargePoint. Use that instance to send a message to the charger.

See also this thread: https://github.com/mobilityhouse/ocpp/issues/263. That contains some code to send a Reset payload to a specific charger.

TehEckoz commented 9 months ago

Thanks for answering,

My question is the following, I understand that before calling remote start transaction we should identify the loading point where we will start the transaction.

How do I identify one charging point or another. Right now, having two charging points makes me turn on both.

OrangeTux commented 9 months ago

You need to find the right ChargePoint instance based on the id of the charger. For example, by using ChargePoint.id.

TehEckoz commented 9 months ago

Hello again,

This is what I am passing in my central_system, and I am running it by cmd with py central_system.py

But I don't really understand how to get the charging point

request = call.RemoteStartTransactionPayload(
                id_tag=id_tag,
                # connector_id=1,
                # charging_profile= 
                charging_profile={  
                                    "connector_id": 1,
                                    "csChargingProfiles": {
                                        "chargingProfileId": 1,
                                        "chargingProfileKind": "Absolute",
                                        "chargingProfilePurpose": "TxDefaultProfile",
                                        "chargingSchedule": {
                                            "chargingRateUnit": "W",
                                            "chargingSchedulePeriod": [
                                                {
                                                "limit": 2,
                                                "startPeriod": 0,
                                                }
                                                ],
                                                "duration":86400
                                        },        
                                        'stackLevel': 1

                                    },
                }

            )``

This is correct? If so, how do I have to call the charging point, or how should I obtain it so that I can execute the remote start transaction to a single specific charging point.

Thank you and sorry for the inconvenience.

OrangeTux commented 9 months ago

You must have some logic in your CSMS that creates a ChargePoint instance when a charger connects. Store that instance somewhere.

Next, in your code concerned with sending the request, retrieve the ChargePoint you stored earlier, build the request and execute await your_charger.call(...).

This comment might help you.

TehEckoz commented 9 months ago

I attach an image with what the cmd returns and with what I have put in code in start remote transaction and on_connect in central_system.py

CENTRAL_SYSTEM

OrangeTux commented 9 months ago

I'm lost. What is your question?

TehEckoz commented 9 months ago

It returns Rejected, I cannot identify a single load point before the remote start transaction

TehEckoz commented 9 months ago

In the CMD it returns the same thing twice, because it is collecting both charging points, I need to be able to visit only the one I choose or decide

OrangeTux commented 9 months ago

You connection handler contains

await asyncio.gather(cp.start(), cp.remote_start_transaction())

That instructs the handler to remotely start a transaction as a charger connects. If you don't want that, remove the cp.remote_start_transaction().

In that same connection handler, a ChargePoint instance is created. You should store that instance somewhere.

Next, in your code that triggers a remote start transaction you should retrieve relevant ChargePoint instance and call cp.remote_start_transaction() on it.

In this example that I linked earlier, the ChargePoint instances are stored in CentralSystem._chargers. The CentralSystem.reset_fun() iterates of the connected chargers and calls cp.reset() on the charger that matches a filter.

TehEckoz commented 9 months ago

Until now, when I pass only a single charging point (charging station). I execute remote start correctly.

But if I have more than one charging point (charging station), it makes me rejected from the second one.

This is what I have done, I don't know if it is correct. or it could be done another way.


async def remote_start_transaction(self):
        print('FUNC. REMOTE')
        results = []

        try:

            with pyodbc.connect('DRIVER={SQL Server};SERVER=X;DATABASE=X;UID=X) as connection:
                while True:
                    row = connection.execute("select top 1 id_tag, connector_id, charged_point_id from RemoteStart where valido = 1").fetchone()

                    if not row:
                        break  

                    id_tag, connector_id, charged_point_id = row

                    print('User: ', id_tag)
                    print('Connector: ', connector_id)
                    print('Charged Point:', charged_point_id)

                    request = call.RemoteStartTransactionPayload(
                        id_tag=id_tag,
                        connector_id=connector_id
                    )

                    print('RemoteStartTransactionPayload Request: ', request)

                    try:
                        response = await self.call(request)
                        print('Request Response:', response)

                        if response.status == RemoteStartStopStatus.rejected:
                            print('Rejected')

                        if response.status == RemoteStartStopStatus.accepted:
                            print("TRANSACTION START")
                            connection.execute("UPDATE RemoteStart SET valido = 0 where id_tag = ? and charged_point_id = ? and connector_id = ?", id_tag, charged_point_id, connector_id)
                            connection.commit()
                            print("¡COMPLETED!")
                            results.append(call_result.RemoteStartTransactionPayload(status=RemoteStartStopStatus.accepted))

                    except Exception as e:
                        print(f"Error in the call a self.call: {e}")

        except Exception as e:
            print(f"Error in the connex BBDD: {e}")

        if not results:
            print('NO REMOTES TRANSACCTION')

        return results
TehEckoz commented 9 months ago

In On_Connect I do this:


async def on_connect(websocket, path):
    charge_point_id = path.strip('/')
    cp = ChargePoint(charge_point_id, websocket)

    while True:
        print('CONSULT. REMOTE')
        cursor = pyodbc.connect('DRIVER={SQL Server};SERVER=X;DATABASE=X;UID=X')
        valido = cursor.execute("select count(valido) from remotestart where valido = 1").fetchone()[0]
        cursor.close()

        if valido > 0:
            cursor = pyodbc.connect('DRIVER={SQL Server};SERVER=X;DATABASE=X;UID=X')
            id_tag = cursor.execute("select top 1 id_tag from RemoteStart where valido = 1").fetchone()[0]    
            connector_id = cursor.execute("select top 1 connector_id from RemoteStart where valido = 1").fetchone()[0]
            chrg_point_id = cursor.execute("select top 1 charged_point_id from RemoteStart where valido = 1").fetchone()[0]
            cursor.close()

            if charge_point_id == chrg_point_id:
                await asyncio.gather(cp.start(), cp.remote_start_transaction())
            else:
                print('')
        else:
            print('0 TRANSACTIONS REMOTES')
            await asyncio.sleep(5) 
            continue  

        try:
            await asyncio.gather(cp.start())
        except websockets.exceptions.ConnectionClosed:
            connected.remove(websocket)
            print("Charge Point disconnected")

        await asyncio.sleep(5)