Coder Social home page Coder Social logo

oglimmer / scriptable-ocpp-chargepoint-simulator Goto Github PK

View Code? Open in Web Editor NEW
63.0 6.0 28.0 663 KB

A Scriptable OCPP Chargepoint Simulator for OCPP 1.6J. Client and Server.

License: Apache License 2.0

TypeScript 70.49% JavaScript 26.45% Shell 2.66% EJS 0.40%
ocpp16j ocpp node vue websockets

scriptable-ocpp-chargepoint-simulator's Introduction

Scriptable OCPP Chargepoint Simulator

Node.js CI

Intro

This simulator supports:

  • OCPP 1.6 (and security) with JSON
  • REST API with HTML Frontend
  • File based batch mode
  • Fully scriptable in JavaScript
  • ftp and csr operations
  • ws:// and wss:// (with client certificates)

Key development considerations:

license

This software is licensed under Apache License, Version 2.0. See LICENSE.

install & build

Make sure you've npm installed.

batch operation

Put the logic using JavaScript into a file (e.g. custom.js). Your file needs to export an async function with one parameter. This parameter will pass the connect(url: string): Chargepoint function to obtain a Chargepoint class object.

Example:

let cp;
try {
  // WebSocket Connect (no OCPP)
  cp = await connect('ws://localhost:8100/xyz');
  // typical startup OCPP
  await cp.sendBootnotification({chargePointVendor: "vendor", chargePointModel: "1"});
  await cp.sendHeartbeat();
  await cp.sendStatusNotification({connectorId: 0, errorCode: "NoError", status: "Available"});
  await cp.sendStatusNotification({connectorId: 1, errorCode: "NoError", status: "Available"});
  // register code for GetDiagnostics, UpdateFirmware, Reset, ...
  cp.answerGetDiagnostics( async (request) => {
    const fileName = "foo." + new Date().toISOString() + ".txt";
    cp.sendResponse(request.uniqueId, {fileName});
    await cp.sendDiagnosticsStatusNotification({status: "Idle"});
    await cp.sleep(5000);
    await cp.sendDiagnosticsStatusNotification({status: "Uploading"});
    await cp.ftpUploadDummyFile(request.payload.location, fileName);
    await cp.sendDiagnosticsStatusNotification({status: "Uploaded"});
  });
  // Typical charging session
  await cp.sendAuthorize({idTag: "ccc"});
  await cp.sendStatusNotification({connectorId: 1, errorCode: "NoError", status: "Preparing"});
  cp.transaction = await cp.startTransaction({connectorId: 1, idTag: "ccc", meterStart: 1377, timestamp: "2020-06-11T10:50:58.333Z"});
  await cp.sendStatusNotification({connectorId: 1, errorCode: "NoError", status: "Charging"});
  await cp.meterValues({connectorId: 1, transactionId: cp.transaction.transactionId, meterValue: [{ timestamp: "2020-06-11T10:50:58.765Z", sampledValue: [{value: 1387}] }]});
  await cp.stopTransaction({transactionId: cp.transaction.transactionId, meterStop: 1399, timestamp: "2020-06-11T10:50:59.148Z"});
  await cp.sendStatusNotification({connectorId: 1, errorCode: "NoError", status: "Finishing"});
  await cp.sendStatusNotification({connectorId: 1, errorCode: "NoError", status: "Available"});
} catch (err) {
  console.log(err);
} finally {
  cp.close();
}

Start it:

./start.sh --v ./custom.js
# or
cat custom.js | ./start.sh --v --stdin

Remote manipulation for the batch mode

You can start a web server within the batch operation script to allow the manipulation or observation of the script.

const webserver = cp.startListener(8080, '0.0.0.0', {'admin': 'secret'});
webserver.get('/stop', (req, res) => {
    res.send('stopped.');
    webserver.terminate();
});
webserver.get('/availability', (req, res) => {
    // this assumes we're storing the current availability in a variable called 'availability'
    res.send(availability);
});

This example starts a web server on 0.0.0.0:8080 using basic authentication. A user 'admin' with the password 'secret' allows under /stop to stop the webserver and under /availability to retrieve the charge points availability.

server operation

Default port for HTML is 3000. Change via env variable PORT. The WebSocket based Server to Client communication is using PORT+1.

./start.sh --v

Open http://localhost:3000/?connectTemplate=$connectUrl&cp=$chargePointName where chargePointName defines the ID of your chargepoint and connectUrl the connect string without the chargepoint-id at the end.

Example: http://localhost:3000/?connectTemplate=ws://foobar:8088/charging&cp=chargepoint001.

Create API docs

npm run docs

Open the docs in ./public/docs or access them via ./start.sh and http://localhost:3000/docs

TLS options

To pass a root CA file (to verify the server's certificate) use the --ca parameter from start.sh or set the env variable SSL_CERT_FILE.

To set client certificates (for mTLS) for a charge point with the id my-chargepoint-id use the following parameters:

./start.sh --v1 --keyStore '[{"id": "my-chargepoint-id", "key": "private.pem", "cert": "cert.pem"}]' --ca ./ca.pem

server operation - DEV mode

Run:

./start.sh --v1 --d

OCPP Operations

Trigger Message

The simulator will respond to all Trigger Message with status=NotImplemented if no answerTriggerMessage have been registered for this requestedMessage.

OCPP 1.6 defines those requestedMessage:

  • "BootNotification"
  • "DiagnosticsStatusNotification"
  • "FirmwareStatusNotification"
  • "Heartbeat"
  • "MeterValues"
  • "StatusNotification"

Example for BootNotification

cp.answerTriggerMessage("BootNotification", async (request) => {
    cp.sendResponse(request.uniqueId, {status: "Accepted"});
    await cp.sendBootnotification({chargePointVendor: "vendor", chargePointModel: "1"});
});

Another example for DiagnosticsStatusNotification

// your code for handling GetDiagnostics will need to update a variable
// currentDiagnosticsStatus with the current state
cp.answerTriggerMessage("DiagnosticsStatusNotification", async (request) => {
    if(currentDiagnosticsStatus) {
        cp.sendResponse(request.uniqueId, {status: "Accepted"});
        await cp.sendDiagnosticsStatusNotification({status: currentDiagnosticsStatus});
    } else {
        cp.sendResponse(request.uniqueId, {status: "Rejected"});
    }
});

ExtendedTriggerMessage

OCPP 1.6 security defines those requestedMessage:

  • "BootNotification"
  • "LogStatusNotification"
  • "FirmwareStatusNotification"
  • "Heartbeat"
  • "MeterValues"
  • "SignChargePointCertificate"
  • "StatusNotification"

Example of SignChargePointCertificate:

let tmpKey;
cp.answerExtendedTriggerMessage("SignChargePointCertificate", async (request) => {
    cp.sendResponse(request.uniqueId, {status: "Accepted"});
    const {key, csr} = await cp.generateCsr('/OU=Ocpp-Simulator/O=Ocpp Simu Inc./L=Paradise City/ST=The State/C=US/CN=the-best-chargepoint');
    tmpKey = key;
    await cp.sendSignCertificate({csr, "typeOfCertificate": "ChargingStationCertificate"});
});
cp.answerCertificateSigned( async (request) => {
    if(!tmpKey) {
        cp.sendResponse(request.uniqueId, {status: "Rejected"});
        return;
    }
    cp.sendResponse(request.uniqueId, {status: "Accepted"});
    const keystore = cp.keystore();
    // this will overwrite the current key/cert files
    const filenames = keystore.save(false, tmpKey, request.payload.cert.join('\n'));
    // alternatively certs/key can be written in a new file
    // keystore.save('-' + new Date().toISOString(), tmpKey, request.payload.cert.join('\n'));
    await cp.reConnect();
    await cp.sendBootnotification({chargePointVendor: "vendor", chargePointModel: "1"});
}, cp.CERTIFICATE_SIGNED_OPTIONS_PEM_ENCODER());

Supported

  • BootNotification
  • HeartBeat
  • StatusNotification
  • Authorize
  • StartTransaction
  • StopTransaction
  • MeterValues
  • Get Diagnostics
  • Diagnostics Status Notification
  • Update Firmware
  • Firmware Status Notification
  • Trigger Message
  • Reset
  • Get Configuration
  • Change Configuration
  • Change Availability
  • Remote Start Transaction
  • ExtendedTriggerMessage (1.6 security)
  • SignCertificate (1.6 security)
  • CertificateSigned (1.6 security)

Not supported (yet)

  • Cancel Reservation
  • Clear Cache
  • Clear Charging Profile
  • Data Transfer
  • Get Composite Schedule
  • Get Local List Version
  • Remote Stop Transaction
  • Reserve Now
  • Send Local List
  • Set Charging Profile
  • Unlock Connector

Architecture

alt text

Disable host name verification

To disable the host name verification when using TLS, apply this patch:

diff --git a/src/websocket-connection-centralsystem.ts b/src/websocket-connection-centralsystem.ts
index ec53fdc..e0323a8 100644
--- a/src/websocket-connection-centralsystem.ts
+++ b/src/websocket-connection-centralsystem.ts
@@ -32,6 +32,7 @@ export class WSConCentralSystem{
           options.key = fs.readFileSync(keyStoreElement.key);
           options.cert = fs.readFileSync(keyStoreElement.cert);
         }
+        options.checkServerIdentity = () => undefined;
       }
       this.ws = new WebSocket(this.url, "ocpp1.6", options);
       let promiseResolved = false;

scriptable-ocpp-chargepoint-simulator's People

Contributors

dependabot[bot] avatar oglimmer avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

scriptable-ocpp-chargepoint-simulator's Issues

Not able to trigger services which flows from central system to charging point.

I am trying to trigger functions such as GetConfiguration, RemoteStartTransaction(basically things that flows from central system to CP). below is a code snippet that i am using :

@app.get("/get-configuration/{charge_point_id}")
async def get_configuration(charge_point_id: str):
    cp_obj=None
    for obj in cs.charge_point_v16_objs:
            if obj.charge_point_id == charge_point_id:
                cp_obj = obj
                break
    await cp_obj.get_configuration(config_list=[ConfigurationKey.get_configuration_max_keys])
    return {}

I am hitting an fastapi endpoint which will trigger this function and so on. I am using "./start.sh --v" command followed by accessing "http://localhost:3000/?connectTemplate=$connectUrl&cp=$chargePointName". When i hit api to trigger function(such as GetConfiguration, RemoteStartTransaction) i can see it returns status=NotImplemented. I am not sure rather i dont understand the steps of adding answerTriggerMessage stuff. Can anybody help with this issue?

how to connect multiple chargepoint to the central system

Hi,
I can init the application now, and I can register different charge points to the application, and I create my own central system.
image

image

I modify some existing code to connect the charge point to my own central system, and it works well, after I did some operation in the web console, my central system can get the message from this charge point.
image

but how I can connect multiple charge points to the central system? thanks for the focus

Can't visit the docs

Hi Team,
I can initiate the application successfully locally, but I can't visit the docs page by http://localhost:3000/docs, could you help look at this issue?

Best Regards

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.