Skip to content

Commit

Permalink
Add OCPP DataTransfer request support
Browse files Browse the repository at this point in the history
Ref #36

Signed-off-by: Jérôme Benoit <[email protected]>
  • Loading branch information
Jérôme Benoit committed Nov 16, 2022
1 parent 231d1ec commit 91a7d3e
Show file tree
Hide file tree
Showing 13 changed files with 197 additions and 66 deletions.
141 changes: 76 additions & 65 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ make SUBMODULES_INIT=true
- :white_check_mark: ChangeAvailability
- :white_check_mark: ChangeConfiguration
- :white_check_mark: ClearCache
- :x: DataTransfer
- :white_check_mark: DataTransfer
- :white_check_mark: GetConfiguration
- :white_check_mark: Heartbeat
- :white_check_mark: MeterValues
Expand Down Expand Up @@ -468,41 +468,6 @@ Set the Websocket header _Sec-Websocket-Protocol_ to `ui0.0.1`.
`chargingStations`: ChargingStationData[]
}

###### Start Transaction

- Request:
`ProcedureName`: 'startTransaction'
`PDU`: {
`hashIds`: charging station unique identifier strings array (optional, default: all charging stations),
`connectorId`: connector id integer,
`idTag`: RFID tag string
}

- Response:
`PDU`: {
`status`: 'success' | 'failure',
`hashIdsSucceeded`: charging station unique identifier strings array,
`hashIdsFailed`: charging station unique identifier strings array (optional),
`responsesFailed`: failed responses payload array (optional)
}

###### Stop Transaction

- Request:
`ProcedureName`: 'stopTransaction'
`PDU`: {
`hashIds`: charging station unique identifier strings array (optional, default: all charging stations),
`transactionId`: transaction id integer
}

- Response:
`PDU`: {
`status`: 'success' | 'failure',
`hashIdsSucceeded`: charging station unique identifier strings array,
`hashIdsFailed`: charging station unique identifier strings array (optional),
`responsesFailed`: failed responses payload array (optional)
}

###### Start Charging Station

- Request:
Expand Down Expand Up @@ -601,40 +566,86 @@ Set the Websocket header _Sec-Websocket-Protocol_ to `ui0.0.1`.
`responsesFailed`: failed responses payload array (optional)
}

###### Status Notification
###### OCPP commands trigger

- Request:
`ProcedureName`: 'StatusNotification'
`PDU`: {
`hashIds`: charging station unique identifier strings array (optional, default: all charging stations),
`connectorId`: connector id integer,
`errorCode`: connector error code,
`status`: connector status
}
The request PDU is the same as the OCPP command payload with some optional fields added to target the simulated charging stations:

- Response:
`PDU`: {
`status`: 'success' | 'failure',
`hashIdsSucceeded`: charging station unique identifier strings array,
`hashIdsFailed`: charging station unique identifier strings array (optional),
`responsesFailed`: failed responses payload array (optional)
}
`PDU`: {
`hashIds`: charging station unique identifier strings array (optional, default: all charging stations),
...`payload`
}

###### Heartbeat
Examples:

- Request:
`ProcedureName`: 'Heartbeat'
`PDU`: {
`hashIds`: charging station unique identifier strings array (optional, default: all charging stations),
}
- **Start Transaction**

- Response:
`PDU`: {
`status`: 'success' | 'failure',
`hashIdsSucceeded`: charging station unique identifier strings array,
`hashIdsFailed`: charging station unique identifier strings array (optional),
`responsesFailed`: failed responses payload array (optional)
}
- Request:
`ProcedureName`: 'startTransaction'
`PDU`: {
`hashIds`: charging station unique identifier strings array (optional, default: all charging stations),
`connectorId`: connector id integer,
`idTag`: RFID tag string
}

- Response:
`PDU`: {
`status`: 'success' | 'failure',
`hashIdsSucceeded`: charging station unique identifier strings array,
`hashIdsFailed`: charging station unique identifier strings array (optional),
`responsesFailed`: failed responses payload array (optional)
}

- **Stop Transaction**

- Request:
`ProcedureName`: 'stopTransaction'
`PDU`: {
`hashIds`: charging station unique identifier strings array (optional, default: all charging stations),
`transactionId`: transaction id integer
}

- Response:
`PDU`: {
`status`: 'success' | 'failure',
`hashIdsSucceeded`: charging station unique identifier strings array,
`hashIdsFailed`: charging station unique identifier strings array (optional),
`responsesFailed`: failed responses payload array (optional)
}

- **Status Notification**

- Request:
`ProcedureName`: 'StatusNotification'
`PDU`: {
`hashIds`: charging station unique identifier strings array (optional, default: all charging stations),
`connectorId`: connector id integer,
`errorCode`: connector error code,
`status`: connector status
}

- Response:
`PDU`: {
`status`: 'success' | 'failure',
`hashIdsSucceeded`: charging station unique identifier strings array,
`hashIdsFailed`: charging station unique identifier strings array (optional),
`responsesFailed`: failed responses payload array (optional)
}

- **Heartbeat**

- Request:
`ProcedureName`: 'Heartbeat'
`PDU`: {
`hashIds`: charging station unique identifier strings array (optional, default: all charging stations),
}

- Response:
`PDU`: {
`status`: 'success' | 'failure',
`hashIdsSucceeded`: charging station unique identifier strings array,
`hashIdsFailed`: charging station unique identifier strings array (optional),
`responsesFailed`: failed responses payload array (optional)
}

## Support, Feedback, Contributing

Expand Down
21 changes: 21 additions & 0 deletions src/assets/json-schemas/ocpp/1.6/DataTransfer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"id": "urn:OCPP:1.6:2019:12:DataTransferRequest",
"title": "DataTransferRequest",
"type": "object",
"properties": {
"vendorId": {
"type": "string",
"maxLength": 255
},
"messageId": {
"type": "string",
"maxLength": 50
},
"data": {
"type": "string"
}
},
"additionalProperties": false,
"required": ["vendorId"]
}
18 changes: 18 additions & 0 deletions src/assets/json-schemas/ocpp/1.6/DataTransferResponse.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"id": "urn:OCPP:1.6:2019:12:DataTransferResponse",
"title": "DataTransferResponse",
"type": "object",
"properties": {
"status": {
"type": "string",
"additionalProperties": false,
"enum": ["Accepted", "Rejected", "UnknownMessageId", "UnknownVendorId"]
},
"data": {
"type": "string"
}
},
"additionalProperties": false,
"required": ["status"]
}
19 changes: 18 additions & 1 deletion src/charging-station/ChargingStationWorkerBroadcastChannel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ import type OCPPError from '../exception/OCPPError';
import { StandardParametersKey } from '../types/ocpp/Configuration';
import {
type BootNotificationRequest,
type DataTransferRequest,
type HeartbeatRequest,
type MeterValuesRequest,
RequestCommand,
type StatusNotificationRequest,
} from '../types/ocpp/Requests';
import {
type BootNotificationResponse,
type DataTransferResponse,
DataTransferStatus,
type HeartbeatResponse,
type MeterValuesResponse,
RegistrationStatus,
Expand Down Expand Up @@ -49,7 +52,8 @@ type CommandResponse =
| BootNotificationResponse
| StatusNotificationResponse
| HeartbeatResponse
| MeterValuesResponse;
| MeterValuesResponse
| DataTransferResponse;

type CommandHandler = (
requestPayload?: BroadcastChannelRequestPayload
Expand Down Expand Up @@ -178,6 +182,14 @@ export default class ChargingStationWorkerBroadcastChannel extends WorkerBroadca
});
},
],
[
BroadcastChannelProcedureName.DATA_TRANSFER,
async (requestPayload?: BroadcastChannelRequestPayload) =>
this.chargingStation.ocppRequestService.requestHandler<
DataTransferRequest,
DataTransferResponse
>(this.chargingStation, RequestCommand.DATA_TRANSFER, requestPayload),
],
]);
this.chargingStation = chargingStation;
this.onmessage = this.requestHandler.bind(this) as (message: MessageEvent) => void;
Expand Down Expand Up @@ -316,6 +328,11 @@ export default class ChargingStationWorkerBroadcastChannel extends WorkerBroadca
return ResponseStatus.SUCCESS;
}
return ResponseStatus.FAILURE;
case BroadcastChannelProcedureName.DATA_TRANSFER:
if (commandResponse?.status === DataTransferStatus.ACCEPTED) {
return ResponseStatus.SUCCESS;
}
return ResponseStatus.FAILURE;
case BroadcastChannelProcedureName.STATUS_NOTIFICATION:
case BroadcastChannelProcedureName.METER_VALUES:
if (Utils.isEmptyObject(commandResponse) === true) {
Expand Down
15 changes: 15 additions & 0 deletions src/charging-station/ocpp/1.6/OCPP16RequestService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type { OCPP16MeterValuesRequest } from '../../../types/ocpp/1.6/MeterValu
import {
DiagnosticsStatusNotificationRequest,
OCPP16BootNotificationRequest,
OCPP16DataTransferRequest,
OCPP16HeartbeatRequest,
OCPP16RequestCommand,
OCPP16StatusNotificationRequest,
Expand Down Expand Up @@ -138,6 +139,18 @@ export default class OCPP16RequestService extends OCPPRequestService {
)
) as JSONSchemaType<OCPP16StopTransactionRequest>,
],
[
OCPP16RequestCommand.DATA_TRANSFER,
JSON.parse(
fs.readFileSync(
path.resolve(
path.dirname(fileURLToPath(import.meta.url)),
'../../../assets/json-schemas/ocpp/1.6/DataTransfer.json'
),
'utf8'
)
) as JSONSchemaType<OCPP16DataTransferRequest>,
],
]);
this.buildRequestPayload.bind(this);
this.validatePayload.bind(this);
Expand Down Expand Up @@ -268,6 +281,8 @@ export default class OCPP16RequestService extends OCPPRequestService {
),
}),
} as unknown as Request;
case OCPP16RequestCommand.DATA_TRANSFER:
return commandParams as unknown as Request;
default:
// OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError().
throw new OCPPError(
Expand Down
14 changes: 14 additions & 0 deletions src/charging-station/ocpp/1.6/OCPP16ResponseService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
import {
DiagnosticsStatusNotificationResponse,
OCPP16BootNotificationResponse,
OCPP16DataTransferResponse,
OCPP16HeartbeatResponse,
OCPP16RegistrationStatus,
OCPP16StatusNotificationResponse,
Expand Down Expand Up @@ -66,6 +67,7 @@ export default class OCPP16ResponseService extends OCPPResponseService {
[OCPP16RequestCommand.STATUS_NOTIFICATION, this.emptyResponseHandler.bind(this)],
[OCPP16RequestCommand.METER_VALUES, this.emptyResponseHandler.bind(this)],
[OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, this.emptyResponseHandler.bind(this)],
[OCPP16RequestCommand.DATA_TRANSFER, this.emptyResponseHandler.bind(this)],
]);
this.jsonSchemas = new Map<OCPP16RequestCommand, JSONSchemaType<JsonObject>>([
[
Expand Down Expand Up @@ -164,6 +166,18 @@ export default class OCPP16ResponseService extends OCPPResponseService {
)
) as JSONSchemaType<DiagnosticsStatusNotificationResponse>,
],
[
OCPP16RequestCommand.DATA_TRANSFER,
JSON.parse(
fs.readFileSync(
path.resolve(
path.dirname(fileURLToPath(import.meta.url)),
'../../../assets/json-schemas/ocpp/1.6/DataTransferResponse.json'
),
'utf8'
)
) as JSONSchemaType<OCPP16DataTransferResponse>,
],
]);
this.validatePayload.bind(this);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export default abstract class AbstractUIService {
[ProcedureName.STATUS_NOTIFICATION]: BroadcastChannelProcedureName.STATUS_NOTIFICATION,
[ProcedureName.HEARTBEAT]: BroadcastChannelProcedureName.HEARTBEAT,
[ProcedureName.METER_VALUES]: BroadcastChannelProcedureName.METER_VALUES,
[ProcedureName.DATA_TRANSFER]: BroadcastChannelProcedureName.DATA_TRANSFER,
};

protected readonly requestHandlers: Map<ProcedureName, ProtocolRequestHandler>;
Expand Down
1 change: 1 addition & 0 deletions src/types/UIProtocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export enum ProcedureName {
STATUS_NOTIFICATION = 'statusNotification',
HEARTBEAT = 'heartbeat',
METER_VALUES = 'meterValues',
DATA_TRANSFER = 'dataTransfer',
}

export interface RequestPayload extends JsonObject {
Expand Down
1 change: 1 addition & 0 deletions src/types/WorkerBroadcastChannel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export enum BroadcastChannelProcedureName {
STATUS_NOTIFICATION = 'statusNotification',
HEARTBEAT = 'heartbeat',
METER_VALUES = 'meterValues',
DATA_TRANSFER = 'dataTransfer',
}

export interface BroadcastChannelRequestPayload extends RequestPayload {
Expand Down
7 changes: 7 additions & 0 deletions src/types/ocpp/1.6/Requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export enum OCPP16RequestCommand {
STOP_TRANSACTION = 'StopTransaction',
METER_VALUES = 'MeterValues',
DIAGNOSTICS_STATUS_NOTIFICATION = 'DiagnosticsStatusNotification',
DATA_TRANSFER = 'DataTransfer',
}

export type OCPP16HeartbeatRequest = EmptyObject;
Expand Down Expand Up @@ -137,3 +138,9 @@ export interface OCPP16TriggerMessageRequest extends JsonObject {
requestedMessage: OCPP16MessageTrigger;
connectorId?: number;
}

export interface OCPP16DataTransferRequest extends JsonObject {
vendorId: string;
messageId?: string;
data?: string;
}
12 changes: 12 additions & 0 deletions src/types/ocpp/1.6/Responses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,15 @@ export enum OCPP16TriggerMessageStatus {
export interface OCPP16TriggerMessageResponse extends JsonObject {
status: OCPP16TriggerMessageStatus;
}

export enum OCPP16DataTransferStatus {
ACCEPTED = 'Accepted',
REJECTED = 'Rejected',
UNKNOWN_MESSAGE_ID = 'UnknownMessageId',
UNKNOWN_VENDOR_ID = 'UnknownVendorId',
}

export interface OCPP16DataTransferResponse extends JsonObject {
status: OCPP16DataTransferStatus;
data?: string;
}
Loading

0 comments on commit 91a7d3e

Please sign in to comment.