Skip to content

Commit

Permalink
Feat: Implement websocketOptions as arg to CS connection (#58)
Browse files Browse the repository at this point in the history
Feat: Implement websocketOptions as arg to CS connection
  • Loading branch information
carloslibardo authored Dec 18, 2024
1 parent 7672b1a commit b9ee039
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 60 deletions.
2 changes: 1 addition & 1 deletion docs/modules/cp/index.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ const chargePoint = new ChargePoint(
**Signature**

```ts
async connect(): Promise<Connection<CentralSystemAction<'v1.6-json'>>>
async connect(websocketOptions: WebsocketOptions): Promise<Connection<CentralSystemAction<'v1.6-json'>>>
```

### sendRequest (method)
Expand Down
11 changes: 9 additions & 2 deletions src/cp/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import { ChargePointAction, chargePointActions } from '../messages/cp';
import { EitherAsync, Left } from 'purify-ts';
import { OCPPRequestError, ValidationError } from '../errors';
import { OCPPVersion } from '../types';
import * as http from 'http';

export type ConnectArgs = http.ClientRequestArgs & WebSocket.ClientOptions;

export type CPSendRequestArgs<T extends ChargePointAction<V>, V extends OCPPVersion> = {
ocppVersion: 'v1.6-json',
Expand Down Expand Up @@ -45,6 +48,7 @@ export type CPSendRequestArgs<T extends ChargePointAction<V>, V extends OCPPVers
*
* @category Charge Point
*/

export default class ChargePoint {
private connection?: Connection<CentralSystemAction<'v1.6-json'>>;

Expand All @@ -54,9 +58,12 @@ export default class ChargePoint {
private readonly csUrl: string
) { }

async connect(): Promise<Connection<CentralSystemAction<'v1.6-json'>>> {
async connect(connectArgs?: ConnectArgs): Promise<Connection<CentralSystemAction<'v1.6-json'>>> {
const url = `${this.csUrl}/${this.id}`;
const socket = new WebSocket(url, SUPPORTED_PROTOCOLS);
const socket = new WebSocket(url, SUPPORTED_PROTOCOLS, {
auth: `${this.id}:${connectArgs?.auth}`,
...connectArgs,
});

const connection = new Connection(
socket,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ export interface IBootNotificationInput {

export interface IBootNotificationOutput {
/** urn://Ocpp/Cs/2012/06/#RegistrationStatus(Accepted,Rejected) */
status: "Accepted" | "Rejected" | 'Pending';
status: "Accepted" | "Rejected" | "Pending";
/** urn://Ocpp/Cs/2012/06/#s:dateTime(undefined) */
currentTime: string;
/** urn://Ocpp/Cs/2012/06/#s:int(undefined) */
Expand Down
115 changes: 75 additions & 40 deletions src/soap/connection.ts
Original file line number Diff line number Diff line change
@@ -1,57 +1,92 @@
import { ActionName, Request, Response } from '../messages';
import { OCPPApplicationError, OCPPRequestError } from '../errors/index';
import { validateMessageRequest } from '../messages/validation';
import { EitherAsync, Left, Right } from 'purify-ts';
import * as soap from 'soap';
import * as path from 'path';
import { chargePointActions } from '../messages/cp';
import { soapCentralSystemActions } from '../messages/cs';
import * as uuid from 'uuid';
import { ActionName, Request, Response } from "../messages";
import { OCPPApplicationError, OCPPRequestError } from "../errors/index";
import { validateMessageRequest } from "../messages/validation";
import { EitherAsync, Left, Right } from "purify-ts";
import * as soap from "soap";
import * as path from "path";
import { chargePointActions } from "../messages/cp";
import { soapCentralSystemActions } from "../messages/cs";
import * as uuid from "uuid";

type SOAPResponse = [Error | null, any, any, any, any];

export default class SOAPConnection {
private respondedActions: ActionName<'v1.5-soap'>[];
private respondedActions: ActionName<"v1.5-soap">[];
constructor(
private readonly soapClient: soap.Client,
private readonly connectedTo: 'cp' | 'cs',
private readonly connectedTo: "cp" | "cs",
private readonly chargePointId: string,
private readonly endpoint: string,
private readonly endpoint: string
) {
this.respondedActions = connectedTo === 'cp' ? soapCentralSystemActions : chargePointActions;
this.respondedActions =
connectedTo === "cp" ? soapCentralSystemActions : chargePointActions;
}

static async connect(endpoint: string, connectedTo: 'cp' | 'cs', chargePointId: string): Promise<SOAPConnection> {
const wsdlPath = path.resolve(__dirname, (
connectedTo === 'cs'
? '../messages/soap/ocpp_centralsystemservice_1.5_final.wsdl'
: '../messages/soap/ocpp_chargepointservice_1.5_final.wsdl'
));
const soapClient = await soap.createClientAsync(wsdlPath, { endpoint, forceSoap12Headers: true });
static async connect(
endpoint: string,
connectedTo: "cp" | "cs",
chargePointId: string
): Promise<SOAPConnection> {
const wsdlPath = path.resolve(
__dirname,
connectedTo === "cs"
? "../messages/soap/ocpp_centralsystemservice_1.5_final.wsdl"
: "../messages/soap/ocpp_chargepointservice_1.5_final.wsdl"
);
const soapClient = await soap.createClientAsync(wsdlPath, {
endpoint,
forceSoap12Headers: true,
});
return new SOAPConnection(soapClient, connectedTo, chargePointId, endpoint);
}

public sendRequest<T extends ActionName<'v1.5-soap'>>(action: T, { action: _, ocppVersion: __, ...payload }: Request<T, 'v1.5-soap'>): EitherAsync<OCPPRequestError, Response<T, 'v1.5-soap'>> {
public sendRequest<T extends ActionName<"v1.5-soap">>(
action: T,
{ action: _, ocppVersion: __, ...payload }: Request<T, "v1.5-soap">
): EitherAsync<OCPPRequestError, Response<T, "v1.5-soap">> {
return EitherAsync.fromPromise(async () => {
const validateResult = validateMessageRequest(action, payload, this.respondedActions);
const validateResult = validateMessageRequest(
action,
payload,
this.respondedActions
);
if (validateResult.isLeft())
return Left(new OCPPApplicationError(validateResult.extract().toString()))

const xmlNs = this.connectedTo === 'cp' ? 'urn://Ocpp/Cp/2012/06/' : 'urn://Ocpp/Cs/2012/06/';
this.soapClient.addSoapHeader({ chargeBoxIdentity: this.chargePointId }, '', 'ocpp', xmlNs);
this.soapClient.addSoapHeader({
'Action': '/' + action,
'MessageID': uuid.v4(),
'To': this.endpoint,
}, '', 'wsa5', 'http://www.w3.org/2005/08/addressing');
return Left(
new OCPPApplicationError(validateResult.extract().toString())
);

const [err, result, _rawResponse, _soapHeader, _rawRequest] = await new Promise(resolve => {
const [serviceKey, portKey] = this.connectedTo === 'cp'
? ['ChargePointService', 'ChargePointServiceSoap12']
: ['CentralSystemService', 'CentralSystemServiceSoap12'];
this.soapClient[serviceKey][portKey][action](payload, (...args: any) => resolve(args))
const xmlNs =
this.connectedTo === "cp"
? "urn://Ocpp/Cp/2012/06/"
: "urn://Ocpp/Cs/2012/06/";
this.soapClient.addSoapHeader(
{ chargeBoxIdentity: this.chargePointId },
"",
"ocpp",
xmlNs
);
this.soapClient.addSoapHeader(
{
Action: "/" + action,
MessageID: uuid.v4(),
To: this.endpoint,
},
"",
"wsa5",
"http://www.w3.org/2005/08/addressing"
);

const [err, result, _rawResponse, _soapHeader, _rawRequest] = await new Promise<SOAPResponse>((resolve) => {
const [serviceKey, portKey] =
this.connectedTo === "cp"
? ["ChargePointService", "ChargePointServiceSoap12"]
: ["CentralSystemService", "CentralSystemServiceSoap12"];
this.soapClient[serviceKey][portKey][action](payload, (...args: any[]) =>
resolve(args as SOAPResponse)
);
});
if (err)
return Left(new OCPPRequestError(err.toString()))
if (err) return Left(new OCPPRequestError(err.toString()));
return Right(result);
})
});
}
}
}
5 changes: 4 additions & 1 deletion src/ws/format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ export const parseOCPPMessage = (raw: OCPPJRawMessage): Either<ValidationError,
default: return Left(new ValidationError(`Not supported message type: ${type}`));
}
} catch (err) {
return Left(new ValidationError(`An error occurred when trying to parse message: "${raw}"`).wrap(err))
if (err instanceof Error) {
return Left(new ValidationError(`An error occurred when trying to parse message: "${raw}"`).wrap(err))
}
return Left(new ValidationError(`An error occurred when trying to parse message: "${raw}"`))
}
};

Expand Down
16 changes: 4 additions & 12 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
"compilerOptions": {
"outDir": "dist",
"module": "commonjs",
"lib": [
"es2019"
],
"lib": ["es2019"],
"declaration": true,
"target": "es2018",
"sourceMap": true,
Expand All @@ -13,12 +11,6 @@
"skipLibCheck": true,
"stripInternal": true
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
"**/*.spec.ts",
"examples"
]
}
"include": ["src/**/*"],
"exclude": ["node_modules", "**/*.spec.ts", "examples"]
}
6 changes: 3 additions & 3 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4438,9 +4438,9 @@ typedarray@^0.0.6:
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=

typescript@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.3.tgz#39062d8019912d43726298f09493d598048c1ce3"
integrity sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==
version "4.9.5"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a"
integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==

typescript@~4.1.3:
version "4.1.5"
Expand Down

0 comments on commit b9ee039

Please sign in to comment.