voltbras / ts-ocpp Goto Github PK
View Code? Open in Web Editor NEW:zap: OCPP (Open Charge Point Protocol) implemented in Typescript.
Home Page: https://voltbras.github.io/ts-ocpp
License: MIT License
:zap: OCPP (Open Charge Point Protocol) implemented in Typescript.
Home Page: https://voltbras.github.io/ts-ocpp
License: MIT License
The ChargePoint has a method connection(), the problem that I have is that if there is no connection problem and the connection, closes, I can't get this information and create proper logging trace, also the connect method binds console.log to error socket event, the CentralSistem has the same problem but has other implications:
One partial solution is this;
async connect(): Promise<WebSocket> {
const url = `${this.csUrl}/${this.cpId}`;
const socket = new WebSocket(url, SUPPORTED_PROTOCOLS);
this.connection = new Connection(
socket,
this.requestHandler,
centralSystemActions,
chargePointActions,
);
socket.on('message', (data) => this.connection?.handleWebsocketData(data));
return new Promise((resolve) => {
socket?.on('open', (socket: WebSocket) => resolve(socket));
});
}
This doesn't break actual use, and provide the socket on open to allow binding to events an most important for me close event, that carry valuable information. I mean partially because I'm not happy exposing the socket and has a race condition in case that a error is raised after a chance to bind the events.
This also is valid, not expose socket, and doesn't break current API:
async connect(
errorHandler?: (err: any) => void,
closeHandler?: (code: number, reason: string) => void,
): Promise<void> {
const url = `${this.csUrl}/${this.cpId}`;
const socket = new WebSocket(url, SUPPORTED_PROTOCOLS);
if (errorHandler !== undefined) {
socket.on('error', errorHandler);
}
if (closeHandler !== undefined) {
socket.on('close', closeHandler);
}
this.connection = new Connection(
socket,
this.requestHandler,
centralSystemActions,
chargePointActions,
);
socket.on('message', (data) => this.connection?.handleWebsocketData(data));
return new Promise((resolve) => {
socket?.on('open', () => resolve());
});
}
I can make a PR with a solution adding a method on(), that can subscribe to this events or the solution that is more suited for project. I didn't make use of the CentralSistem code, but after reviewing it, I also find this problem to bind to this close/error events.
For example the StartTransaction
response, contains an idTagInfo
field of IdTagInfo
type.
As per the OCPP v1.5 SOAP specification, the fields parentIdTag
and expiryDate
of the IdTagInfo
type are optional.
However they currently are mandatory via the types, but they should be optional. This occurs in various types that were generated per the WSDL files.
It would provide a more correct typing system, if either:
Im trying to send BootNotificationResponse, as it is shown in examples:
defining the centralSystem:
const centralSystem = new OCPP.CentralSystem(3000, (req, metadata) => {
case 'BootNotification':
return {
action: req.action,
ocppVersion,
currentTime: new Date().toISOString(),
payload: {
status: 'Accepted',
currentTime: new Date().toISOString(),
interval: 120
}
};
but when trying to run I get the following erro:
error TS2345: Argument of type '(req: (AuthorizeRequest & { action: "Authorize"; ocppVersion: "v1.6-json"; }) | (BootNotificationRequest & { action: "BootNotification"; ocppVersion: "v1.6-json"; }) | ... 17 more ... | (IStopTransactionInput & { ...; }), metadata: RequestMetadata) => { ...; } | ... 1 more ... | { ...; }' is not assignable to parameter of type 'RequestHandler<"Authorize" | "BootNotification" | "DataTransfer" | "DiagnosticsStatusNotification" | "FirmwareStatusNotification" | "Heartbeat" | "MeterValues" | "StartTransaction" | "StatusNotification" | "StopTransaction", RequestMetadata, OCPPVersion>'.
Type '{ action: "Heartbeat"; ocppVersion: "v1.6-json" | "v1.5-soap"; currentTime: string; payload?: undefined; } | { action: "StatusNotification"; ocppVersion: "v1.6-json" | "v1.5-soap"; currentTime?: undefined; payload?: undefined; } | { ...; }' is not assignable to type 'Result<(AuthorizeResponse & { action: "Authorize"; ocppVersion: "v1.6-json"; }) | (BootNotificationResponse & { action: "BootNotification"; ocppVersion: "v1.6-json"; }) | ... 17 more ... | (IStopTransactionOutput & { ...; })>'.
Type '{ action: "BootNotification"; ocppVersion: "v1.6-json" | "v1.5-soap"; currentTime: string; payload: { status: string; currentTime: string; interval: number; }; }' is not assignable to type 'Result<(AuthorizeResponse & { action: "Authorize"; ocppVersion: "v1.6-json"; }) | (BootNotificationResponse & { action: "BootNotification"; ocppVersion: "v1.6-json"; }) | ... 17 more ... | (IStopTransactionOutput & { ...; })>'.
Type '{ action: "BootNotification"; ocppVersion: "v1.6-json" | "v1.5-soap"; currentTime: string; payload: { status: string; currentTime: string; interval: number; }; }' is not assignable to type 'IHeartbeatOutput & { action: "Heartbeat"; ocppVersion: "v1.5-soap"; }'.
Type '{ action: "BootNotification"; ocppVersion: "v1.6-json" | "v1.5-soap"; currentTime: string; payload: { status: string; currentTime: string; interval: number; }; }' is not assignable to type '{ action: "Heartbeat"; ocppVersion: "v1.5-soap"; }'.
Types of property 'action' are incompatible.
Type '"BootNotification"' is not assignable to type '"Heartbeat"'.
15 const centralSystem = new OCPP.CentralSystem(3000, (req, metadata) => {
What yould be the right way to define BootNotificationResponse?
About the public/private methods, how about adding a linter to this, so this restriction is enforced automatically?
Originally posted by @eduhenke in #7 (comment)
Hello,
I have some issues when try to StartTransaction, becuase StopTransaction Send me "DeAuthorized"
How can I resolve it?
This are my requests, responses and Code:
req = {
action: 'BootNotification',
ocppVersion: 'v1.6-json',
chargePointModel: 'CDT_TACW7::NET_WIFI',
chargePointVendor: 'ABB',
chargeBoxSerialNumber: 'TACW74',
firmwareVersion: 'TAC1Z9120',
meterType: 'V1'
}
chargePointId = TACW74
ret = {
action: 'BootNotification',
ocppVersion: 'v1.6-json',
status: 'Accepted',
interval: 1440,
currentTime: '2024-05-15T17:04:52.021Z'
}
req = {
action: 'StatusNotification',
ocppVersion: 'v1.6-json',
connectorId: 0,
errorCode: 'NoError',
info: 'null',
status: 'Available',
vendorErrorCode: '0x0000'
}
chargePointId = TACW74
ret = { action: 'StatusNotification', ocppVersion: 'v1.6-json' }
req = {
action: 'StatusNotification',
ocppVersion: 'v1.6-json',
connectorId: 1,
errorCode: 'NoError',
info: 'null',
status: 'Preparing',
vendorErrorCode: '0x0000'
}
chargePointId = TACW74
ret = { action: 'StatusNotification', ocppVersion: 'v1.6-json' }
req = { action: 'Authorize', ocppVersion: 'v1.6-json', idTag: '6CD98725' }
chargePointId = TACW74
ret = { idTagInfo: { status: 'Accepted' } }
req = {
action: 'StatusNotification',
ocppVersion: 'v1.6-json',
connectorId: 1,
errorCode: 'NoError',
info: 'null',
status: 'SuspendedEV',
vendorErrorCode: '0x0000'
}
chargePointId = TACW74
ret = { action: 'StatusNotification', ocppVersion: 'v1.6-json' }
req = {
action: 'StatusNotification',
ocppVersion: 'v1.6-json',
connectorId: 1,
errorCode: 'NoError',
info: 'null',
status: 'Charging',
vendorErrorCode: '0x0000'
}
chargePointId = TACW74
ret = { action: 'StatusNotification', ocppVersion: 'v1.6-json' }
req = {
action: 'StartTransaction',
ocppVersion: 'v1.6-json',
connectorId: 1,
idTag: '6CD98725',
meterStart: 0,
timestamp: '2024-05-15T17:05:12.000Z'
}
chargePointId = TACW74
ret = {
action: 'StartTransaction',
ocppVersion: 'v1.6-json',
idTagInfo: { satus: 'Accepted' },
transactionId: 739831
}
req = {
action: 'StatusNotification',
ocppVersion: 'v1.6-json',
connectorId: 1,
errorCode: 'NoError',
info: 'null',
status: 'SuspendedEVSE',
vendorErrorCode: '0x0000'
}
chargePointId = TACW74
ret = { action: 'StatusNotification', ocppVersion: 'v1.6-json' }
req = {
action: 'StatusNotification',
ocppVersion: 'v1.6-json',
connectorId: 1,
errorCode: 'NoError',
info: 'null',
status: 'Finishing',
vendorErrorCode: '0x0000'
}
chargePointId = TACW74
ret = { action: 'StatusNotification', ocppVersion: 'v1.6-json' }
req = {
action: 'StopTransaction',
ocppVersion: 'v1.6-json',
meterStop: 0,
idTag: '6CD98725',
timestamp: '2024-05-15T17:05:14.000Z',
transactionId: 739831,
reason: 'DeAuthorized'
}
chargePointId = TACW74
ret = {
action: 'StopTransaction',
ocppVersion: 'v1.6-json',
idTagInfo: { satus: 'Accepted' }
}
req = {
action: 'StatusNotification',
ocppVersion: 'v1.6-json',
connectorId: 1,
errorCode: 'NoError',
info: 'null',
status: 'Preparing',
vendorErrorCode: '0x0000'
}
chargePointId = TACW74
ret = { action: 'StatusNotification', ocppVersion: 'v1.6-json' }
This is my code:
this.centralSystem = new CentralSystem(8100, (req, { chargePointId }) => {
console.log('\n\n');
console.log('req =', req);
console.log('chargePointId =', chargePointId);
let ret;
switch (req.action) {
case 'Heartbeat':
ret = {
action: req.action,
ocppVersion: req.ocppVersion,
currentTime: new Date().toISOString(),
};
break;
case 'BootNotification':
ret = {
action: req.action,
ocppVersion: req.ocppVersion,
status: 'Accepted',
interval: 1440,
currentTime: new Date().toISOString(),
};
break;
case 'StatusNotification':
ret = {
action: req.action,
ocppVersion: req.ocppVersion,
};
break;
case 'Authorize':
ret = {
idTagInfo: {
status: 'Accepted',
},
};
break;
case 'StartTransaction':
this.gTransactionId = this.getRandomNumber();
ret = {
action: req.action,
ocppVersion: req.ocppVersion,
idTagInfo: {
satus: 'Accepted',
},
transactionId: this.gTransactionId,
};
break;
case 'StopTransaction':
ret = {
action: req.action,
ocppVersion: req.ocppVersion,
idTagInfo: {
satus: 'Accepted',
},
};
break;
case 'MeterValues':
ret = {
action: req.action,
ocppVersion: req.ocppVersion,
};
break;
default:
throw new Error('message not supported');
}
console.log('ret =', ret);
return ret;
});
Provide an automated pipeline mainly for testing on pull requests.
I think it should have a way to know about connecting ChargePoints from Central System
class.
When you run the Central System
as container and connect it with other servers like this,
Charge Point <-> Central System <-> Your backend Server
and you want send request to Charge Point, you don't know which Central System is connecting to this specific Charge Point.
Its better if Central System
can let us know about connecting ChargePointIds.
Here is my idea,
const cs = new CentralSystem();
const chargePoints = cs.getChargePointIds();
const targetChargePointId = 111;
const connecting = !!chargePoints.filter(c => c === targetChargePointId).length;
if (connecting) {
// has connection with target chargePoint.
const response = await cs.sendRequest();
}
// no connection
First of all, congratulations for this great library, I'm using it to emulate real CPs, I create a really simple project that creates a ChargePoint and connect to a real Central Server, when I ran it with ts-node it executes without problems, but when I try to compile with tsc i get this errors:
$ npm run build
> [email protected] build
> rimraf ./dist && tsc
node_modules/@voltbras/ts-ocpp/dist/messages/index.d.ts:6:93 - error TS2536: Type '"request"' cannot be used to index type 'ReqRes<T, V>'.
6 export declare type Request<T extends ActionName<V>, V extends OCPPVersion = OCPPVersion> = ReqRes<T, V>['request'];
~~~~~~~~~~~~~~~~~~~~~~~
node_modules/@voltbras/ts-ocpp/dist/messages/index.d.ts:7:94 - error TS2536: Type '"response"' cannot be used to index type 'ReqRes<T, V>'.
7 export declare type Response<T extends ActionName<V>, V extends OCPPVersion = OCPPVersion> = ReqRes<T, V>['response'];
~~~~~~~~~~~~~~~~~~~~~~~~
Found 2 errors.
I want to contribute to the library, I see that have some missing things, CI, lint, other ocpp-j and ocpp-s protocols, npm package, etc. I thing that could be great if this tasks will be created so people that want to help, have some clue on priority things.
set TriggerMessage
in sendRequest
function of Central System will throw type error.
TriggerMessage
in action property of sendRequest
functionimport { CentralSystem } from '@voltbras/ts-ocpp';
const cs = new CentralSystem(8080, (req, metadata) => {
const { ocppVersion } = req;
switch (req.action) {
case 'Heartbeat':
return { action: req.action, ocppVersion, currentTime: new Date().toISOString() };
case 'StatusNotification':
return { action: req.action, ocppVersion };
}
throw new Error('not supported');
});
console.log('server started');
if (process.env.SEND_COMMAND) {
setTimeout(async () => {
console.log('sending request')
const response = await cs.sendRequest({
ocppVersion: 'v1.6-json',
//(property) action: "TriggerMessage"
// Type '"TriggerMessage"' is not assignable to type '"DataTransfer" | "CancelReservation" | "ChangeAvailability" | // "ChangeConfiguration" | "ClearCache" | "GetConfiguration" | "GetDiagnostics" | "GetLocalListVersion" | "RemoteStartTransaction" | ... 5 more ... | "UpdateFirmware"'.ts(2322)
action: 'TriggerMessage', // type error
chargePointId: '123',
payload: {
connectorId: 2,
idTag: '123',
}
});
console.log({ response })
}, 3000);
}
No type error occur
Nowadays, when a request comes from the ChargePoint to the CentralSystem, the WS connection class, validates if the requests follows the jsonschema, if it doesn't it rejects the request and returns the error to the caller.
However, we interface with several different charge point models/vendors and some of them do not strictly follow the jsonschema(unfortunately).
It would be a nice addition if this library would allow these validation errors to be passed to the handler, and let the handler decide if that error is ok to ignore or not.
Hello, I could not install the npm package because it is not found anymore on the npm. Can you help me regarding this problem please?
npm install @voltbras/ts-ocpp --save
npm ERR! code E404
npm ERR! 404 Not Found - GET https://registry.npmjs.org/@voltbras%2fts-ocpp - Not found
npm ERR! 404
npm ERR! 404 '@voltbras/ts-ocpp@latest' is not in the npm registry.
npm ERR! 404 You should bug the author to publish it (or use the name yourself!)
npm ERR! 404
npm ERR! 404 Note that you can also install from a
npm ERR! 404 tarball, folder, http url, or git url.
Thank you!
I create a charge point like so:
export const cpHandlers: RequestHandler<
CentralSystemAction<"v1.6-json">,
ValidationError | undefined,
"v1.6-json"
> = (req) => {
switch (req.action) {
case "TriggerMessage":
return {
status: "Accepted",
action: "TriggerMessage",
ocppVersion: req.ocppVersion,
};
}
throw new Error("message not supported");
};
export const cp = new ChargePoint(
chargerId,
cpHandlers,
`${wsUrl}/${chargerId}`
According to the specs, immediately after confirming TriggerMessage, I need to send the message being triggered.
What is the intended way to do this?
I can't seem to find a way to guarantee that it will be sent AFTER returning the confirmation for TriggerMessage.
I thought about adding a setTimeOut
with the requested message before returning the confirmation, but it seems wrong.
As the title says - there's no BootNotification
example for the server nor the client, and the BootNotification
is most important message, as it starts whole communication between the server and the charger.
npm is throwing error while installing the package:
npm ERR! prepareGitDep 1>
npm ERR! prepareGitDep > @voltbras/[email protected] prepare C:\Users\Saurabh\AppData\Roaming\npm-cache_cacache\tmp\git-clone-2a1ddc4a
npm ERR! prepareGitDep > npm run build
npm ERR! prepareGitDep
npm ERR! prepareGitDep
npm ERR! prepareGitDep > @voltbras/[email protected] build C:\Users\Saurabh\AppData\Roaming\npm-cache_cacache\tmp\git-clone-2a1ddc4a
npm ERR! prepareGitDep > tsc && npm run copy-assets
npm ERR! prepareGitDep
npm ERR! prepareGitDep
npm ERR! prepareGitDep > @voltbras/[email protected] copy-assets C:\Users\Saurabh\AppData\Roaming\npm-cache_cacache\tmp\git-clone-2a1ddc4a
npm ERR! prepareGitDep > npm run copy-assets-wsdl && npm run copy-assets-jsonschema
npm ERR! prepareGitDep
npm ERR! prepareGitDep
npm ERR! prepareGitDep > @voltbras/[email protected] copy-assets-wsdl C:\Users\Saurabh\AppData\Roaming\npm-cache_cacache\tmp\git-clone-2a1ddc4a
npm ERR! prepareGitDep > cp ./src/messages/soap/*.wsdl ./dist/messages/soap
npm ERR! prepareGitDep
npm ERR! prepareGitDep
npm ERR! prepareGitDep 2> npm WARN install Usage of the --dev
option is deprecated. Use --also=dev
instead.
npm ERR! prepareGitDep npm WARN deprecated [email protected]: request has been deprecated, see request/request#3142
npm ERR! prepareGitDep npm WARN deprecated [email protected]: some dependency vulnerabilities fixed, support for node < 10 dropped, and newer ECMAScript syntax/features added
npm ERR! prepareGitDep npm WARN deprecated [email protected]: this library is no longer supported
npm ERR! prepareGitDep npm WARN deprecated [email protected]: CoffeeScript on NPM has moved to "coffeescript" (no hyphen)
npm ERR! prepareGitDep npm WARN deprecated [email protected]: Removed event-stream from gulp-header
npm ERR! prepareGitDep npm WARN deprecated [email protected]: https://github.com/lydell/resolve-url#deprecated
npm ERR! prepareGitDep npm WARN deprecated [email protected]: Please see https://github.com/lydell/urix#deprecated
npm ERR! prepareGitDep 'cp' is not recognized as an internal or external command,
npm ERR! prepareGitDep operable program or batch file.
npm ERR! prepareGitDep npm ERR! code ELIFECYCLE
npm ERR! prepareGitDep npm ERR! errno 1
npm ERR! prepareGitDep npm ERR! @voltbras/[email protected] copy-assets-wsdl: cp ./src/messages/soap/*.wsdl ./dist/messages/soap
npm ERR! prepareGitDep npm ERR! Exit status 1
npm ERR! prepareGitDep npm ERR!
npm ERR! prepareGitDep npm ERR! Failed at the @voltbras/[email protected] copy-assets-wsdl script.
npm ERR! prepareGitDep npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
npm ERR! prepareGitDep
npm ERR! prepareGitDep npm ERR! A complete log of this run can be found in:
npm ERR! prepareGitDep npm ERR! C:\Users\Saurabh\AppData\Roaming\npm-cache_logs\2021-08-26T07_29_14_464Z-debug.log
npm ERR! prepareGitDep npm ERR! code ELIFECYCLE
npm ERR! prepareGitDep npm ERR! errno 1
npm ERR! prepareGitDep npm ERR! @voltbras/[email protected] copy-assets: npm run copy-assets-wsdl && npm run copy-assets-jsonschema
npm ERR! prepareGitDep npm ERR! Exit status 1
npm ERR! prepareGitDep npm ERR!
npm ERR! prepareGitDep npm ERR! Failed at the @voltbras/[email protected] copy-assets script.
npm ERR! prepareGitDep npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
npm ERR! prepareGitDep
npm ERR! prepareGitDep npm ERR! A complete log of this run can be found in:
npm ERR! prepareGitDep npm ERR! C:\Users\Saurabh\AppData\Roaming\npm-cache_logs\2021-08-26T07_29_14_500Z-debug.log
npm ERR! prepareGitDep npm ERR! code ELIFECYCLE
npm ERR! prepareGitDep npm ERR! errno 1
npm ERR! prepareGitDep npm ERR! @voltbras/[email protected] build: tsc && npm run copy-assets
npm ERR! prepareGitDep npm ERR! Exit status 1
npm ERR! prepareGitDep npm ERR!
npm ERR! prepareGitDep npm ERR! Failed at the @voltbras/[email protected] build script.
npm ERR! prepareGitDep npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
npm ERR! prepareGitDep
npm ERR! prepareGitDep npm ERR! A complete log of this run can be found in:
npm ERR! prepareGitDep npm ERR! C:\Users\Saurabh\AppData\Roaming\npm-cache_logs\2021-08-26T07_29_14_540Z-debug.log
npm ERR! prepareGitDep npm ERR! code ELIFECYCLE
npm ERR! prepareGitDep npm ERR! errno 1
npm ERR! prepareGitDep npm ERR! @voltbras/[email protected] prepare: npm run build
npm ERR! prepareGitDep npm ERR! Exit status 1
npm ERR! prepareGitDep npm ERR!
npm ERR! prepareGitDep npm ERR! Failed at the @voltbras/[email protected] prepare script.
npm ERR! prepareGitDep npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
npm ERR! prepareGitDep
npm ERR! prepareGitDep npm ERR! A complete log of this run can be found in:
npm ERR! prepareGitDep npm ERR! C:\Users\Saurabh\AppData\Roaming\npm-cache_logs\2021-08-26T07_29_14_631Z-debug.log
npm ERR! prepareGitDep
npm ERR! premature close
Improve project documentation including:
In most cases, it doesn't change anything, though the problem I saw was with the MeterValues - this module uses outdated schemas, without Celsius
as MeterValues unit, whereas newer MeterValues JSON schema includes it
there is an issue with some chargers that do not fully close the previous websocket connection after creating a new websocket connection. when the first socket closes, it clears all existing connections, not only the one which closed.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.