npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

ocpp-task-manager

v1.1.3

Published

A general purpose OCPP framework that can be used to build anything related to OCPP

Downloads

78

Readme

OCPP Task manager

Gitpod ready-to-code

A general purpose Node.js framework that can be used to build anything related to OCPP (Open Charge Point Protocol).

You can build software for a physical chargepoint or a virtual simulator, you can also use it to build Backends or Charging Station Management Systems (CSMS).

It is transport layer agnostic, so that you have the full power to define how the messages are sent and received.

Installation

NPM

Usage

OCPP Task manager being transport layer agnostic, you have to define how to send a message to the other connected entity, i.e. either a charge point or a central system, depending on what you are building.

Also, regardless of you are building a Charge point or a central system, your device will have to respond to CALL messages, (if you don't know what it is, I highly recommend you to read official OCPP documentation).

const OCPPTaskManager = require('ocpp-task-manager');

const device = OCPPTaskManager({
  sender: (message, version) => {
    // define how to send the message for the given OCPP version
  },
  // Define the call handlers for all the CALLs you wan't to support
  callHandlers: {
    // Showing just BootNotification for example
    BootNotification: (payload, { callResult, callError }) => {
      // `payload` hold the payload received with the CALL
      // do anything you want with it, although you might want to first sit and plan

      // You may either respond with a CALLRESULT
      callResult(responsePayload);
      // or with CALLERROR
      callError(errorCode, errorDesciption, errorDetails);
    },
    // This is a catch-all handler, it is executed for any undefined action
    '*': (payload, { callResult, callError }) => {
      callError('NotImplemented');
    },
  },
});

Here, we define a sender function which will receive a message parameter, which is a String, whose contents you shouldn't care about. Just send it! The version parameter stores the current active OCPP version, as defined in device.connected.

Similarly, you will also have to let to know OCPP Task Manager when a message is received from the other entity. You can do it in the following way.

// Assuming `message` variable holds the received message in string format
device.received(message);

Before actually receiving any messages, you will also have to notify OCPP Task Manager that a connection has been established, because, as I already said, the framework doesn't know or care anything about how the connection part works, its concern is just handling OCPP messages.

You must also pass the version of OCPP which is to be used for the current connection.

// Assuming `ocppVersion` variable holds the OCPP version in string format
device.connected(ocppVersion);

If a connection was disconnected, notify using

device.disconnected();

Sometimes, you may want to send a CALL message to the other entity, here's how you will do it,

device
  .sendCall(action, payload)
  .then(response => {
    // `response.payload` will contain the Payload received
    if (response.ok) {
      // You received a CALLRESULT
    } else {
      // You received a CALLERROR
    }
  })
  .catch(error => {
    // Handle error, this was thrown by your sender
  });

Hooks

You can use hooks functionality to run custom code at different life cycles.

device.hooks.before('messageReceived', ({ rawMsg }) => {
  console.log('Message has been received', rawMsg);
});

device.hooks.after('sendWsMsg', ({ rawMsg }) => {
  console.log('Message has been sent', rawMsg);
});

The following hooks are available

  • sendWsMsg
  • sendCallRespond
  • sendCallError
  • executeCallHandler
  • executeCallResultHandler
  • executeCallErrorHandler
  • messageReceived
  • sendCall

Examples

Charge point simulator

const OCPPTaskManager = require('ocpp-task-manager');
const WebSocket = require('ws'); // npm install ws

// Establishing a connection
const ws = new WebSocket('ws://example.com/ocpp/CP001'); // Where CP001 is the chargepoint unique identifier

// Instantiate your device
const device = OCPPTaskManager({
  sender: (message, version) => {
    ws.send(message);
  },
  // Define what to do when calls are received
  callHandlers: {
    Reset: (payload, { callResult, callError }) => {
      if (payload.type === 'Hard') {
        // Some how do a Hard reset, depends on your implementation
        callResult({ status: 'Accepted' });
      } else if (payload.type === 'Soft') {
        // Some how do a Soft reset, depends on your implementation
        callResult({ status: 'Rejected' });
      } else {
        callError('FormationViolation');
      }
    },
    // Similarly define handlers for all the CALLs you want to support
  },
});

// Notify your device on connection open
ws.on('open', () => {
  device.connected('ocpp1.6j');

  // Send boot notification
  device
    .sendCall('BootNotification', {
      /* provide all the necessary payload items */
    })
    .then(response => {
      if (response.ok) {
        if (response.payload.status === 'Accepted') {
          // Start heartbeat loop
          setInterval(
            () => device.sendCall('Heartbeat'),
            response.payload.interval * 1000,
          );
        }
      } else {
        // You received a CALLERROR
        console.log(
          'CALLERROR received',
          response.payload.errorCode,
          response.payload.errorDescription,
          response.payload.errorDetails,
        );
      }
    })
    .catch(error => {
      console.error(error);
    });
});

// Notify your device about disconnection
ws.on('close', () => device.disconnected());

// Pass the received message to you device
ws.on('message', data => {
  device.received(data);
});

OCPP server


// Import the 'http' module
const http = require('http');

// Define the port number to listen on
const port = 3000;

// Create an HTTP server that listens on the specified port
const server = http.createServer((req, res) => {
  // Your buisness logic or http route handlers goes here
  // Express handlers can be integrated here
});

// Start the server and listen on the specified port
server.listen(port, () => {
  console.log(`Server running at http://localhost:${port}/`);
});

// The websocket server that will handle wesocket connections
const wss = new WebSocket.Server({ noServer: true });

server.on('upgrade', async (request, socket, head) => {
  try {
    // handle your websocket's upgrade process with your buisness logic
    wss.handleUpgrade(request, socket, head, async ws => {
      // Start the connection upgrade
      // your buisness logic

      // Emit connection event with your charger object
      // Charger object may include various properties like chargerId, name etc
      // Charger object is completely flexible to implement based on your buisness needs
      wss.emit('connection', ws, request, { charger });
    })
  } catch (err) {
    console.log('error', err);
  }
})

wss.on("connection", async (ws, request, charger) => {
  // Some other buisness logic

  // create new OCPP task manager
  const device = OCPPTaskManager({
    sender: message => {
      // send message through WebSocket
      ws.send(message);
    },
    callHandlers: {
      BootNotification: (payload, { callResult, callError }) => {
        // `payload` hold the payload received with the CALL
        // do anything you want with it, although you might want to first sit and plan

        // You may either respond with a CALLRESULT
        callResult({
          currentTime: new Date().toISOString(),
          interval: 30,
          status: 'Accepted',
        });
        // or with CALLERROR
        callError('InternalError');
      },
      // This is a catch-all handler, it is executed for any undefined action
      '*': (payload, { callResult, callError }) => {
        callError('NotImplemented');
      },
    }
  });

  // device connected with selected OCPP version
  device.connected(selectedOcppVersionOnBothSide);

  ws.on("close", (code, reason) => {
    // Any other buisness logic
    // ...
    // Inform OCPP Task manager that the connection has been closed
    device.disconnected();
    // close WebSocket connection
    ws.close();
  })

  ws.on('message', data => {
    // when message is received through WebSocket
    // pass message to device
    device.received(data);
  });
})