From c3ee95af4edd541595e3873c8aa4c93d38e59474 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Fri, 4 Feb 2022 16:19:26 +0100 Subject: [PATCH] Introduce JSON type and use it for OCPP and internal message structure MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jérôme Benoit --- src/charging-station/Bootstrap.ts | 9 +++++---- src/types/ChargingStationWorker.ts | 2 +- src/types/JsonType.ts | 5 +++++ src/types/Worker.ts | 8 ++++---- src/types/ocpp/1.6/ChargingProfile.ts | 8 +++++--- src/types/ocpp/1.6/MeterValues.ts | 7 ++++--- src/types/ocpp/1.6/Requests.ts | 29 ++++++++++++++------------- src/types/ocpp/1.6/Responses.ts | 21 ++++++++++--------- src/types/ocpp/1.6/Transaction.ts | 15 +++++++------- src/types/ocpp/Configuration.ts | 4 +++- src/worker/WorkerAbstract.ts | 4 ++-- src/worker/WorkerDynamicPool.ts | 4 ++-- src/worker/WorkerFactory.ts | 12 +++++------ src/worker/WorkerSet.ts | 6 +++--- src/worker/WorkerStaticPool.ts | 4 ++-- 15 files changed, 76 insertions(+), 62 deletions(-) create mode 100644 src/types/JsonType.ts diff --git a/src/charging-station/Bootstrap.ts b/src/charging-station/Bootstrap.ts index ce5286ac..143bb5c4 100644 --- a/src/charging-station/Bootstrap.ts +++ b/src/charging-station/Bootstrap.ts @@ -3,6 +3,7 @@ import { ChargingStationWorkerData, ChargingStationWorkerMessage, ChargingStationWorkerMessageEvents } from '../types/ChargingStationWorker'; import Configuration from '../utils/Configuration'; +import Statistics from '../types/Statistics'; import { Storage } from '../performance/storage/Storage'; import { StorageFactory } from '../performance/storage/StorageFactory'; import { UIServiceUtils } from './ui-websocket-services/UIServiceUtils'; @@ -17,7 +18,7 @@ import { version } from '../../package.json'; export default class Bootstrap { private static instance: Bootstrap | null = null; - private workerImplementation: WorkerAbstract | null = null; + private workerImplementation: WorkerAbstract | null = null; private readonly uiWebSocketServer!: UIWebSocketServer; private readonly storage!: Storage; private numberOfChargingStations: number; @@ -118,11 +119,11 @@ export default class Bootstrap { }, messageHandler: async (msg: ChargingStationWorkerMessage) => { if (msg.id === ChargingStationWorkerMessageEvents.STARTED) { - this.uiWebSocketServer.chargingStations.add(msg.data.id); + this.uiWebSocketServer.chargingStations.add(msg.data.id as string); } else if (msg.id === ChargingStationWorkerMessageEvents.STOPPED) { - this.uiWebSocketServer.chargingStations.delete(msg.data.id); + this.uiWebSocketServer.chargingStations.delete(msg.data.id as string); } else if (msg.id === ChargingStationWorkerMessageEvents.PERFORMANCE_STATISTICS) { - await this.storage.storePerformanceStatistics(msg.data); + await this.storage.storePerformanceStatistics(msg.data as unknown as Statistics); } } }); diff --git a/src/types/ChargingStationWorker.ts b/src/types/ChargingStationWorker.ts index a48e0768..8bdc9af0 100644 --- a/src/types/ChargingStationWorker.ts +++ b/src/types/ChargingStationWorker.ts @@ -19,6 +19,6 @@ export const ChargingStationWorkerMessageEvents = { }; -export interface ChargingStationWorkerMessage extends Omit { +export interface ChargingStationWorkerMessage extends Omit, 'id'> { id: ChargingStationWorkerMessageEvents; } diff --git a/src/types/JsonType.ts b/src/types/JsonType.ts new file mode 100644 index 00000000..2bd219f4 --- /dev/null +++ b/src/types/JsonType.ts @@ -0,0 +1,5 @@ +export interface JsonType { + [x: string]: string | number | boolean | Date | JsonType | JsonArray; +} + +type JsonArray = Array; diff --git a/src/types/Worker.ts b/src/types/Worker.ts index 8f47f578..7a963355 100644 --- a/src/types/Worker.ts +++ b/src/types/Worker.ts @@ -1,3 +1,4 @@ +import { JsonType } from './JsonType'; import { PoolOptions } from 'poolifier'; import { Worker } from 'worker_threads'; @@ -16,17 +17,16 @@ export interface WorkerOptions { messageHandler?: (message: unknown) => void | Promise; } -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface WorkerData {} +export type WorkerData = JsonType; export interface WorkerSetElement { worker: Worker; numberOfWorkerElements: number; } -export interface WorkerMessage { +export interface WorkerMessage { id: WorkerMessageEvents; - data: any; + data: T; } export enum WorkerMessageEvents { diff --git a/src/types/ocpp/1.6/ChargingProfile.ts b/src/types/ocpp/1.6/ChargingProfile.ts index 5d72a7ac..6416bf5d 100644 --- a/src/types/ocpp/1.6/ChargingProfile.ts +++ b/src/types/ocpp/1.6/ChargingProfile.ts @@ -1,4 +1,6 @@ -export interface OCPP16ChargingProfile { +import { JsonType } from '../../JsonType'; + +export interface OCPP16ChargingProfile extends JsonType { chargingProfileId: number; transactionId?: number; stackLevel: number; @@ -10,7 +12,7 @@ export interface OCPP16ChargingProfile { chargingSchedule: ChargingSchedule; } -export interface ChargingSchedule { +export interface ChargingSchedule extends JsonType { duration?: number; startSchedule?: Date; chargingRateUnit: ChargingRateUnitType; @@ -18,7 +20,7 @@ export interface ChargingSchedule { minChargeRate?: number; } -export interface ChargingSchedulePeriod { +export interface ChargingSchedulePeriod extends JsonType { startPeriod: number; limit: number; numberPhases?: number; diff --git a/src/types/ocpp/1.6/MeterValues.ts b/src/types/ocpp/1.6/MeterValues.ts index d3038e63..97a7d24c 100644 --- a/src/types/ocpp/1.6/MeterValues.ts +++ b/src/types/ocpp/1.6/MeterValues.ts @@ -1,4 +1,5 @@ import { EmptyObject } from '../../EmptyObject'; +import { JsonType } from '../../JsonType'; export enum MeterValueUnit { WATT_HOUR = 'Wh', @@ -81,7 +82,7 @@ export enum MeterValueFormat { SIGNED_DATA = 'SignedData', } -export interface OCPP16SampledValue { +export interface OCPP16SampledValue extends JsonType { value?: string; unit?: MeterValueUnit; context?: MeterValueContext; @@ -91,12 +92,12 @@ export interface OCPP16SampledValue { format?: MeterValueFormat; } -export interface OCPP16MeterValue { +export interface OCPP16MeterValue extends JsonType { timestamp: string; sampledValue: OCPP16SampledValue[]; } -export interface MeterValuesRequest { +export interface MeterValuesRequest extends JsonType { connectorId: number; transactionId?: number; meterValue: OCPP16MeterValue[]; diff --git a/src/types/ocpp/1.6/Requests.ts b/src/types/ocpp/1.6/Requests.ts index d83417b0..97b5d756 100644 --- a/src/types/ocpp/1.6/Requests.ts +++ b/src/types/ocpp/1.6/Requests.ts @@ -1,6 +1,7 @@ import { ChargingProfilePurposeType, OCPP16ChargingProfile } from './ChargingProfile'; import { EmptyObject } from '../../EmptyObject'; +import { JsonType } from '../../JsonType'; import { OCPP16ChargePointErrorCode } from './ChargePointErrorCode'; import { OCPP16ChargePointStatus } from './ChargePointStatus'; import { OCPP16DiagnosticsStatus } from './DiagnosticsStatus'; @@ -35,7 +36,7 @@ export enum OCPP16IncomingRequestCommand { export type HeartbeatRequest = EmptyObject; -export interface OCPP16BootNotificationRequest { +export interface OCPP16BootNotificationRequest extends JsonType { chargeBoxSerialNumber?: string; chargePointModel: string; chargePointSerialNumber?: string; @@ -47,7 +48,7 @@ export interface OCPP16BootNotificationRequest { meterType?: string; } -export interface StatusNotificationRequest { +export interface StatusNotificationRequest extends JsonType { connectorId: number; errorCode: OCPP16ChargePointErrorCode; info?: string; @@ -57,26 +58,26 @@ export interface StatusNotificationRequest { vendorErrorCode?: string; } -export interface ChangeConfigurationRequest { +export interface ChangeConfigurationRequest extends JsonType { key: string | OCPP16StandardParametersKey; value: string; } -export interface RemoteStartTransactionRequest { +export interface RemoteStartTransactionRequest extends JsonType { connectorId: number; idTag: string; chargingProfile?: OCPP16ChargingProfile; } -export interface RemoteStopTransactionRequest { +export interface RemoteStopTransactionRequest extends JsonType { transactionId: number; } -export interface UnlockConnectorRequest { +export interface UnlockConnectorRequest extends JsonType { connectorId: number; } -export interface GetConfigurationRequest { +export interface GetConfigurationRequest extends JsonType { key?: string | OCPP16StandardParametersKey[]; } @@ -85,11 +86,11 @@ export enum ResetType { SOFT = 'Soft' } -export interface ResetRequest { +export interface ResetRequest extends JsonType { type: ResetType; } -export interface SetChargingProfileRequest { +export interface SetChargingProfileRequest extends JsonType { connectorId: number; csChargingProfiles: OCPP16ChargingProfile; } @@ -99,19 +100,19 @@ export enum OCPP16AvailabilityType { OPERATIVE = 'Operative' } -export interface ChangeAvailabilityRequest { +export interface ChangeAvailabilityRequest extends JsonType { connectorId: number; type: OCPP16AvailabilityType; } -export interface ClearChargingProfileRequest { +export interface ClearChargingProfileRequest extends JsonType { id?: number; connectorId?: number; chargingProfilePurpose?: ChargingProfilePurposeType; stackLevel?: number; } -export interface GetDiagnosticsRequest { +export interface GetDiagnosticsRequest extends JsonType { location: string; retries?: number; retryInterval?: number; @@ -119,7 +120,7 @@ export interface GetDiagnosticsRequest { stopTime?: Date; } -export interface DiagnosticsStatusNotificationRequest { +export interface DiagnosticsStatusNotificationRequest extends JsonType { status: OCPP16DiagnosticsStatus } @@ -132,7 +133,7 @@ export enum MessageTrigger { StatusNotification = 'StatusNotification' } -export interface OCPP16TriggerMessageRequest { +export interface OCPP16TriggerMessageRequest extends JsonType { requestedMessage: MessageTrigger; connectorId?: number } diff --git a/src/types/ocpp/1.6/Responses.ts b/src/types/ocpp/1.6/Responses.ts index 4f526b94..7d3487ba 100644 --- a/src/types/ocpp/1.6/Responses.ts +++ b/src/types/ocpp/1.6/Responses.ts @@ -1,7 +1,8 @@ import { EmptyObject } from '../../EmptyObject'; +import { JsonType } from '../../JsonType'; import { OCPPConfigurationKey } from '../Configuration'; -export interface HeartbeatResponse { +export interface HeartbeatResponse extends JsonType { currentTime: string; } @@ -11,7 +12,7 @@ export enum OCPP16UnlockStatus { NOT_SUPPORTED = 'NotSupported' } -export interface UnlockConnectorResponse { +export interface UnlockConnectorResponse extends JsonType { status: OCPP16UnlockStatus; } @@ -22,7 +23,7 @@ export enum OCPP16ConfigurationStatus { NOT_SUPPORTED = 'NotSupported' } -export interface ChangeConfigurationResponse { +export interface ChangeConfigurationResponse extends JsonType { status: OCPP16ConfigurationStatus; } @@ -32,7 +33,7 @@ export enum OCPP16RegistrationStatus { REJECTED = 'Rejected' } -export interface OCPP16BootNotificationResponse { +export interface OCPP16BootNotificationResponse extends JsonType { status: OCPP16RegistrationStatus; currentTime: string; interval: number; @@ -40,7 +41,7 @@ export interface OCPP16BootNotificationResponse { export type StatusNotificationResponse = EmptyObject; -export interface GetConfigurationResponse { +export interface GetConfigurationResponse extends JsonType { configurationKey: OCPPConfigurationKey[]; unknownKey: string[]; } @@ -51,7 +52,7 @@ export enum OCPP16ChargingProfileStatus { NOT_SUPPORTED = 'NotSupported', } -export interface SetChargingProfileResponse { +export interface SetChargingProfileResponse extends JsonType { status: OCPP16ChargingProfileStatus; } @@ -61,7 +62,7 @@ export enum OCPP16AvailabilityStatus { SCHEDULED = 'Scheduled' } -export interface ChangeAvailabilityResponse { +export interface ChangeAvailabilityResponse extends JsonType { status: OCPP16AvailabilityStatus; } @@ -70,11 +71,11 @@ export enum OCPP16ClearChargingProfileStatus { UNKNOWN = 'Unknown' } -export interface ClearChargingProfileResponse { +export interface ClearChargingProfileResponse extends JsonType { status: OCPP16ClearChargingProfileStatus; } -export interface GetDiagnosticsResponse { +export interface GetDiagnosticsResponse extends JsonType { fileName?: string; } @@ -86,6 +87,6 @@ export enum OCPP16TriggerMessageStatus { NOT_IMPLEMENTED = 'NotImplemented' } -export interface OCPP16TriggerMessageResponse { +export interface OCPP16TriggerMessageResponse extends JsonType { status: OCPP16TriggerMessageStatus } diff --git a/src/types/ocpp/1.6/Transaction.ts b/src/types/ocpp/1.6/Transaction.ts index 41bd344e..bf237777 100644 --- a/src/types/ocpp/1.6/Transaction.ts +++ b/src/types/ocpp/1.6/Transaction.ts @@ -1,3 +1,4 @@ +import { JsonType } from '../../JsonType'; import { OCPP16MeterValue } from './MeterValues'; export enum OCPP16StopTransactionReason { @@ -23,21 +24,21 @@ export enum OCPP16AuthorizationStatus { CONCURRENT_TX = 'ConcurrentTx' } -export interface IdTagInfo { +export interface IdTagInfo extends JsonType { status: OCPP16AuthorizationStatus; parentIdTag?: string; expiryDate?: Date; } -export interface AuthorizeRequest { +export interface AuthorizeRequest extends JsonType { idTag: string; } -export interface OCPP16AuthorizeResponse { +export interface OCPP16AuthorizeResponse extends JsonType { idTagInfo: IdTagInfo; } -export interface StartTransactionRequest { +export interface StartTransactionRequest extends JsonType { connectorId: number; idTag: string; meterStart: number; @@ -45,12 +46,12 @@ export interface StartTransactionRequest { timestamp: string; } -export interface OCPP16StartTransactionResponse { +export interface OCPP16StartTransactionResponse extends JsonType { idTagInfo: IdTagInfo; transactionId: number; } -export interface StopTransactionRequest { +export interface StopTransactionRequest extends JsonType { idTag?: string; meterStop: number; timestamp: string; @@ -59,6 +60,6 @@ export interface StopTransactionRequest { transactionData?: OCPP16MeterValue[]; } -export interface OCPP16StopTransactionResponse { +export interface OCPP16StopTransactionResponse extends JsonType { idTagInfo?: IdTagInfo; } diff --git a/src/types/ocpp/Configuration.ts b/src/types/ocpp/Configuration.ts index b55c2121..b13a24ba 100644 --- a/src/types/ocpp/Configuration.ts +++ b/src/types/ocpp/Configuration.ts @@ -1,5 +1,7 @@ import { OCPP16StandardParametersKey, OCPP16SupportedFeatureProfiles, OCPP16VendorDefaultParametersKey } from './1.6/Configuration'; +import { JsonType } from '../JsonType'; + export type StandardParametersKey = OCPP16StandardParametersKey; export const StandardParametersKey = { @@ -29,7 +31,7 @@ export enum ConnectorPhaseRotation { TSR = 'TSR' } -export interface OCPPConfigurationKey { +export interface OCPPConfigurationKey extends JsonType { key: string | StandardParametersKey; readonly: boolean; value?: string; diff --git a/src/worker/WorkerAbstract.ts b/src/worker/WorkerAbstract.ts index fa28597c..7c662d9d 100644 --- a/src/worker/WorkerAbstract.ts +++ b/src/worker/WorkerAbstract.ts @@ -1,7 +1,7 @@ import Constants from '../utils/Constants'; import { WorkerData } from '../types/Worker'; -export default abstract class WorkerAbstract { +export default abstract class WorkerAbstract { protected readonly workerScript: string; protected readonly workerStartDelay: number; public abstract readonly size: number; @@ -20,5 +20,5 @@ export default abstract class WorkerAbstract { public abstract start(): Promise; public abstract stop(): Promise; - public abstract addElement(elementData: WorkerData): Promise; + public abstract addElement(elementData: T): Promise; } diff --git a/src/worker/WorkerDynamicPool.ts b/src/worker/WorkerDynamicPool.ts index e2dd3b1e..37b6ddd1 100644 --- a/src/worker/WorkerDynamicPool.ts +++ b/src/worker/WorkerDynamicPool.ts @@ -6,7 +6,7 @@ import WorkerAbstract from './WorkerAbstract'; import { WorkerData } from '../types/Worker'; import { WorkerUtils } from './WorkerUtils'; -export default class WorkerDynamicPool extends WorkerAbstract { +export default class WorkerDynamicPool extends WorkerAbstract { private readonly pool: DynamicThreadPool; /** @@ -56,7 +56,7 @@ export default class WorkerDynamicPool extends WorkerAbstract { * @returns * @public */ - public async addElement(elementData: T): Promise { + public async addElement(elementData: WorkerData): Promise { await this.pool.execute(elementData); // Start worker sequentially to optimize memory at startup await Utils.sleep(this.workerStartDelay); diff --git a/src/worker/WorkerFactory.ts b/src/worker/WorkerFactory.ts index 8b979d06..76da7a20 100644 --- a/src/worker/WorkerFactory.ts +++ b/src/worker/WorkerFactory.ts @@ -1,5 +1,5 @@ import { Worker, isMainThread } from 'worker_threads'; -import { WorkerOptions, WorkerProcessType } from '../types/Worker'; +import { WorkerData, WorkerOptions, WorkerProcessType } from '../types/Worker'; import Constants from '../utils/Constants'; import { PoolOptions } from 'poolifier'; @@ -13,7 +13,7 @@ export default class WorkerFactory { // This is intentional } - public static getWorkerImplementation(workerScript: string, workerProcessType: WorkerProcessType, options?: WorkerOptions): WorkerAbstract | null { + public static getWorkerImplementation(workerScript: string, workerProcessType: WorkerProcessType, options?: WorkerOptions): WorkerAbstract | null { if (!isMainThread) { throw new Error('Trying to get a worker implementation outside the main thread'); } @@ -21,20 +21,20 @@ export default class WorkerFactory { options.startDelay = options?.startDelay ?? Constants.WORKER_START_DELAY; options.poolOptions = options?.poolOptions ?? {} as PoolOptions; options?.messageHandler && (options.poolOptions.messageHandler = options.messageHandler); - let workerImplementation: WorkerAbstract = null; + let workerImplementation: WorkerAbstract = null; switch (workerProcessType) { case WorkerProcessType.WORKER_SET: options.elementsPerWorker = options.elementsPerWorker ?? Constants.DEFAULT_CHARGING_STATIONS_PER_WORKER; - workerImplementation = new WorkerSet(workerScript, options.elementsPerWorker, options.startDelay, options); + workerImplementation = new WorkerSet(workerScript, options.elementsPerWorker, options.startDelay, options); break; case WorkerProcessType.STATIC_POOL: options.poolMaxSize = options.poolMaxSize ?? Constants.DEFAULT_WORKER_POOL_MAX_SIZE; - workerImplementation = new WorkerStaticPool(workerScript, options.poolMaxSize, options.startDelay, options.poolOptions); + workerImplementation = new WorkerStaticPool(workerScript, options.poolMaxSize, options.startDelay, options.poolOptions); break; case WorkerProcessType.DYNAMIC_POOL: options.poolMinSize = options.poolMinSize ?? Constants.DEFAULT_WORKER_POOL_MIN_SIZE; options.poolMaxSize = options.poolMaxSize ?? Constants.DEFAULT_WORKER_POOL_MAX_SIZE; - workerImplementation = new WorkerDynamicPool(workerScript, options.poolMinSize, options.poolMaxSize, options.startDelay, options.poolOptions); + workerImplementation = new WorkerDynamicPool(workerScript, options.poolMinSize, options.poolMaxSize, options.startDelay, options.poolOptions); break; default: throw new Error(`Worker implementation type '${workerProcessType}' not found`); diff --git a/src/worker/WorkerSet.ts b/src/worker/WorkerSet.ts index d179608d..64399c87 100644 --- a/src/worker/WorkerSet.ts +++ b/src/worker/WorkerSet.ts @@ -1,13 +1,13 @@ // Partial Copyright Jerome Benoit. 2021. All Rights Reserved. -import { WorkerMessageEvents, WorkerOptions, WorkerSetElement } from '../types/Worker'; +import { WorkerData, WorkerMessageEvents, WorkerOptions, WorkerSetElement } from '../types/Worker'; import Utils from '../utils/Utils'; import { Worker } from 'worker_threads'; import WorkerAbstract from './WorkerAbstract'; import { WorkerUtils } from './WorkerUtils'; -export default class WorkerSet extends WorkerAbstract { +export default class WorkerSet extends WorkerAbstract { public readonly maxElementsPerWorker: number; private readonly messageHandler: (message: unknown) => void | Promise; private readonly workerSet: Set; @@ -37,7 +37,7 @@ export default class WorkerSet extends WorkerAbstract { * @returns * @public */ - public async addElement(elementData: T): Promise { + public async addElement(elementData: WorkerData): Promise { if (!this.workerSet) { throw new Error('Cannot add a WorkerSet element: workers\' set does not exist'); } diff --git a/src/worker/WorkerStaticPool.ts b/src/worker/WorkerStaticPool.ts index d5d6390c..1cbd18e8 100644 --- a/src/worker/WorkerStaticPool.ts +++ b/src/worker/WorkerStaticPool.ts @@ -6,7 +6,7 @@ import WorkerAbstract from './WorkerAbstract'; import { WorkerData } from '../types/Worker'; import { WorkerUtils } from './WorkerUtils'; -export default class WorkerStaticPool extends WorkerAbstract { +export default class WorkerStaticPool extends WorkerAbstract { private readonly pool: FixedThreadPool; /** @@ -55,7 +55,7 @@ export default class WorkerStaticPool extends WorkerAbstract { * @returns * @public */ - public async addElement(elementData: T): Promise { + public async addElement(elementData: WorkerData): Promise { await this.pool.execute(elementData); // Start worker sequentially to optimize memory at startup await Utils.sleep(this.workerStartDelay); -- 2.34.1