mikuso / ocpp-rpc

A Node.js client & server implementation of the WAMP-like RPC-over-websocket system defined in the OCPP-J protocols.
MIT License
94 stars 28 forks source link

Facing Issue While calling custom method from Server and shows ProtocolError #15

Closed kvext closed 2 years ago

kvext commented 2 years ago

Hi @mikuso,

I can not call custom method defined client method from server and while calling it will give me ProtocolError.

I have two separate apps once is server and another is client on both I have installed ocpp-rpc package and also using NodeJS version 18.1.0 Now, I want to call customer method as below:

On the Client end Say method is defined:

const cli = new RPCClient({
    endpoint: "ws://localhost:3000",
    identity: "EXAMPLE123",
    protocols: ["ocpp1.6"],
    strictMode: true,
});

cli.handle("Say", ({ params }) => {
    console.log("Server said:", params);
});

And when I called "Say" method from Server as below, It is returning me an error and aborted both server and client.

const server = new RPCServer({
    protocols: ['ocpp1.6'],
    strictMode: true,
});

server.on('client', async (client) => {
     await client.call("Say", `Hello, ${client.identity}!`);
});

Error as below:

server_app\node_modules\ocpp-rpc\lib\util.js:44
    const err = new E(message ?? '');
                ^

RPCProtocolError: Schema 'urn:Say.req' is missing from subprotocol schema 'ocpp1.6'
    at createRPCError (\server_app\node_modules\ocpp-rpc\lib\util.js:44:17)
    at Validator.validate (\server_app\node_modules\ocpp-rpc\lib\validator.js:39:19)
    at RPCServerClient._call (\server_app\node_modules\ocpp-rpc\lib\client.js:287:27)
    at Queue._next (\server_app\node_modules\ocpp-rpc\lib\queue.js:39:35)
    at \server_app\node_modules\ocpp-rpc\lib\queue.js:22:18
    at new Promise (<anonymous>)
    at Queue.push (\server_app\node_modules\ocpp-rpc\lib\queue.js:15:16)
    at RPCServerClient.call (\server_app\node_modules\ocpp-rpc\lib\client.js:270:38)
    at RPCServer.<anonymous> (\server_app\app.js:50:18)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
  rpcErrorMessage: 'Payload for method is incomplete',
  rpcErrorCode: 'ProtocolError',
  details: {}
}

Thanks

mikuso commented 2 years ago

Hi,

If you are using strictMode: true with ocpp1.6 subprotocol, then you cannot use non-ocpp1.6 methods as it violates the princples of strict mode.

If you pass strictMode: false (for both client and server), then you can use custom methods.

Alternatively, if you want to use custom methods in a way that is compatible with OCPP1.6, you can use the DataTransfer call for this purpose. Please see the OCPP1.6 documentation for information on how this works.

kvext commented 2 years ago

Hi @mikuso ,

Thanks for your suggestion.

I have tried by changing strictMode: false and It works for custom methods, but when I call "MeterValues" from client, It returns me error on client end as below:

Call failed because: Requested method is not known
RPC Error code: NotImplemented

Following is the way I am trying to implement to communicate both ways:

On the Server end:

const server = new RPCServer({
    protocols: ['ocpp1.6'],
    strictMode: false,
});

server.on('client', async (client) => {

     await client.handle(({method, params}) => {
        console.log(`Server got ${method} from ${client.identity}:`, params);
        throw createRPCError("NotImplemented");
    });

    await client.call("Say", `Hello, ${client.identity}!`);

    await client.handle('MeterValues', ({params}) => {
            console.log(`Server got MeterValues from ${client.identity}:`, params);
            return {};
        });
    });
 }); 

server.listen(3000);

On the Client End:

async function main() {
    const cli = new RPCClient({
        endpoint: "ws://localhost:3000",
        identity: "EXAMPLE123",
        protocols: ["ocpp1.6"],
        strictMode: false,
    });

    cli.handle("Say", ({ params }) => {
        console.log("Server said:", params);
    });

    cli.connect();

    try {

         let respMeterValues = await cli.call("MeterValues", {
            connectorId: 0,
            transactionId: 12345,
            meterValue: [
                {
                    sampledValue: [
                        {
                            unit: "Celsius",
                            context: "Sample.Clock",
                            measurand: "Temperature",
                            location: "Body",
                            value: "33.8,18.6",
                        },
                    ],
                    timestamp: new Date().toISOString(),
                },
            ],
        });

        console.log("Response from Server (MeterValues) => ", respMeterValues);

    } catch (err) {
        // Call failed
        console.error("Call failed because:", err.message);
        console.error("RPC Error code:", err.rpcErrorCode);
        console.error("Error details:", err.details);
        throw err;
    }
}

main().catch(console.error);  

Why It is returning me an error as below?

Server said: Hello, EXAMPLE123!
Call failed because: Unable to handle 'MeterValues' calls
RPC Error code: NotImplemented
Error details: {}
RPCNotImplementedError: Unable to handle 'MeterValues' calls
    at createRPCError (\client_app\node_modules\ocpp-rpc\lib\util.js:44:17)
    at RPCClient._onCallError (\client_app\node_modules\ocpp-rpc\lib\client.js:841:25)
    at RPCClient._onMessage (\client_app\node_modules\ocpp-rpc\lib\client.js:649:26)
    at WebSocket.<anonymous> (\client_app\node_modules\ocpp-rpc\lib\client.js:369:42)
    at WebSocket.emit (node:events:527:28)
    at Receiver.receiverOnMessage (\client_app\node_modules\ws\lib\websocket.js:1169:20)
    at Receiver.emit (node:events:527:28)
    at Receiver.dataMessage (\client_app\node_modules\ws\lib\receiver.js:528:14)
    at Receiver.getData (\client_app\node_modules\ws\lib\receiver.js:446:17)
    at Receiver.startLoop (\client_app\node_modules\ws\lib\receiver.js:148:22) {
  rpcErrorMessage: 'Requested method is not known',
  rpcErrorCode: 'NotImplemented',
  details: {}
}

can you please provide more detail if anything is missed or required to do changes for the same.

Thanks,

mikuso commented 2 years ago

You have a race condition in your code.

You awaited for the response from your "Say" call before you registered your handler for the "MeterValues" call. Because of this, the MeterValues call was received by your client before you registered a handler for it. The MeterValues call was handled by the first (wildcard) handler instead, throwing a NotImplemented RPC error.

Try re-ordering your code as follows and try again:

server.on('client', async (client) => {

    client.handle(({method, params}) => {
        console.log(`Server got ${method} from ${client.identity}:`, params);
        throw createRPCError("NotImplemented");
    });

    client.handle('MeterValues', ({params}) => {
        console.log(`Server got MeterValues from ${client.identity}:`, params);
        return {};
    });

    await client.call("Say", `Hello, ${client.identity}!`);

});

Note: You don't need to await on client.handle() as it does not return a Promise.

kvext commented 2 years ago

Hi @mikuso,

Awesome! Thank you very much! It works now.

mikuso commented 2 years ago

Great! You're welcome.