import { Storage } from '../performance/storage/Storage';
import { StorageFactory } from '../performance/storage/StorageFactory';
import Utils from '../utils/Utils';
+import WebSocketServer from './WebSocketServer';
import WorkerAbstract from '../worker/WorkerAbstract';
import WorkerFactory from '../worker/WorkerFactory';
import chalk from 'chalk';
export default class Bootstrap {
private static instance: Bootstrap | null = null;
private workerImplementation: WorkerAbstract | null = null;
+ private readonly webSocketServer: WebSocketServer;
private readonly storage: Storage;
private numberOfChargingStations: number;
private readonly version: string = version;
this.started = false;
this.workerScript = path.join(path.resolve(__dirname, '../'), 'charging-station', 'ChargingStationWorker.js');
this.initWorkerImplementation();
+ this.webSocketServer = new WebSocketServer();
this.storage = StorageFactory.getStorage(Configuration.getPerformanceStorage().type, Configuration.getPerformanceStorage().URI, this.logPrefix());
Configuration.setConfigurationChangeCallback(async () => Bootstrap.getInstance().restart());
}
this.numberOfChargingStations = 0;
await this.storage.open();
await this.workerImplementation.start();
+ this.webSocketServer.start();
// Start ChargingStation object in worker thread
if (Configuration.getStationTemplateURLs()) {
for (const stationURL of Configuration.getStationTemplateURLs()) {
public async stop(): Promise<void> {
if (isMainThread && this.started) {
await this.workerImplementation.stop();
+ this.webSocketServer.stop();
await this.storage.close();
} else {
console.error(chalk.red('Trying to stop the charging stations simulator while not started'));
+import { ProtocolCommand, ProtocolRequest, ProtocolVersion } from '../types/UIProtocol';
+
+import AbstractUIService from './WebSocketServices/ui/AbstractUIService';
import { IncomingMessage } from 'http';
+import UIService from './WebSocketServices/ui/0.0.1/UIService';
+import Utils from '../utils/Utils';
import WebSocket from 'ws';
import logger from '../utils/Logger';
-enum WebSocketServerCommand {
- START_TRANSACTION = 'startTransaction',
- STOP_TRANSACTION = 'stopTransaction',
- UNKNOWN = 'unknown',
-}
-
-type WebSocketServerRequest = [WebSocketServerCommand, Record<string, unknown>];
-
export default class WebSocketServer extends WebSocket.Server {
+ private webSocketServerService: AbstractUIService;
+
public constructor(options?: WebSocket.ServerOptions, callback?: () => void) {
// Create the WebSocket Server
super(options, callback);
+ // FIXME: version the instantiation
+ this.webSocketServerService = new UIService(this);
}
public broadcastToClients(message: Record<string, unknown>): void {
}
public start(): void {
- // this.on('connection', (socket: WebSocket, request: IncomingMessage): void => {
- // // Check connection validity
- // });
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
+ const self = this;
+ this.on('connection', (socket: WebSocket, request: IncomingMessage): void => {
+ // Check connection validity
+ });
this.on('message', (messageData) => {
- let [command, payload]: WebSocketServerRequest = [WebSocketServerCommand.UNKNOWN, {}];
+ let [version, command, payload]: ProtocolRequest = [ProtocolVersion['0.0.1'], ProtocolCommand.UNKNOWN, {}];
// FIXME: check for iterable object
- [command, payload] = JSON.parse(messageData.toString()) as WebSocketServerRequest;
- switch (command) {
- case WebSocketServerCommand.START_TRANSACTION:
- break;
- case WebSocketServerCommand.STOP_TRANSACTION:
+ [version, command, payload] = JSON.parse(messageData.toString()) as ProtocolRequest;
+ switch (version) {
+ case ProtocolVersion['0.0.1']:
+ self.webSocketServerService.handleMessage(command, payload).catch(() => { });
break;
default:
- logger.warn(`Unknown command: ${command}`);
+ logger.error(`${this.logPrefix()} Unknown protocol version: ${version}`);
}
});
}
+
+ public stop(): void {
+ this.close();
+ }
+
+ public logPrefix(): string {
+ return Utils.logPrefix('WebSocket Server:');
+ }
}
--- /dev/null
+import { ProtocolCommand, ProtocolRequestHandler } from '../../../../types/UIProtocol';
+
+import AbstractUIService from '../AbstractUIService';
+import BaseError from '../../../../exception/BaseError';
+import WebSocketServer from '../../../WebSocketServer';
+import logger from '../../../../utils/Logger';
+
+export default class UIService extends AbstractUIService {
+ private readonly messageHandlers: Map<ProtocolCommand, ProtocolRequestHandler>;
+
+ constructor(webSocketServer: WebSocketServer) {
+ super(webSocketServer);
+ this.messageHandlers = new Map<ProtocolCommand, ProtocolRequestHandler>([
+ [ProtocolCommand.START_TRANSACTION, this.handleStartTransaction.bind(this)],
+ [ProtocolCommand.STOP_TRANSACTION, this.handleStopTransaction.bind(this)],
+ ]);
+ }
+
+ async handleMessage(command: ProtocolCommand, payload: Record<string, unknown>): Promise<void> {
+ let messageResponse: Record<string, unknown>;
+ if (this.messageHandlers.has(command)) {
+ try {
+ // Call the method to build the response
+ messageResponse = await this.messageHandlers.get(command)(payload);
+ } catch (error) {
+ // Log
+ logger.error(this.webSocketServer.logPrefix() + ' Handle request error: %j', error);
+ throw error;
+ }
+ } else {
+ // Throw exception
+ throw new BaseError(`${command} is not implemented to handle message payload ${JSON.stringify(payload, null, 2)}`);
+ }
+ // Send the built response
+ this.webSocketServer.broadcastToClients(messageResponse);
+ }
+
+ private handleStartTransaction(payload: Record<string, unknown>) { }
+ private handleStopTransaction(payload: Record<string, unknown>) { }
+}
--- /dev/null
+import { ProtocolCommand } from '../../../types/UIProtocol';
+import WebSocketServer from '../../WebSocketServer';
+
+export default abstract class AbstractUIService {
+ protected readonly webSocketServer: WebSocketServer;
+
+ constructor(webSocketServer: WebSocketServer) {
+ this.webSocketServer = webSocketServer;
+ }
+
+ abstract handleMessage(command: ProtocolCommand, payload: Record<string, unknown>): Promise<void>;
+}
import { DefaultResponse } from '../../../types/ocpp/Responses';
import { ErrorType } from '../../../types/ocpp/ErrorType';
import { IncomingRequestHandler } from '../../../types/ocpp/Requests';
-import { MessageType } from '../../../types/ocpp/MessageType';
import { OCPP16ChargePointStatus } from '../../../types/ocpp/1.6/ChargePointStatus';
import { OCPP16DiagnosticsStatus } from '../../../types/ocpp/1.6/DiagnosticsStatus';
import { OCPP16StandardParametersKey } from '../../../types/ocpp/1.6/Configuration';
--- /dev/null
+
+export enum ProtocolVersion {
+ '0.0.1' = '0.0.1',
+ '0.0.2' = '0.0.2',
+}
+
+export enum ProtocolCommand {
+ START_TRANSACTION = 'startTransaction',
+ STOP_TRANSACTION = 'stopTransaction',
+ UNKNOWN = 'unknown',
+}
+
+export type ProtocolRequest = [ProtocolVersion, ProtocolCommand, Record<string, unknown>];
+
+export type ProtocolRequestHandler = (payload: Record<string, unknown>) => Record<string, unknown> | Promise<Record<string, unknown>>;