Skip to content

Commit

Permalink
Merge pull request #24 from Nasar165/bootnotification
Browse files Browse the repository at this point in the history
Bootnotification
  • Loading branch information
Nasar165 authored May 5, 2024
2 parents 6ec1dbe + bb9cb84 commit 7681439
Show file tree
Hide file tree
Showing 15 changed files with 383 additions and 31 deletions.
9 changes: 5 additions & 4 deletions app/components/evse.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,26 @@ import WebSocket from './WebSocket';
import { ChargingSocket, IChargingSocket } from '../service/ocpp/connector';
import { StatusNotification } from '../service/ocpp/status.notificiation';
import { HandleOcpp } from '../service/ocpp/ocpp.handler';
import { SendBootNotification } from '../service/ocpp/command/boot-notification/bootnotification';

const defaultValue = 'ws://localhost:8080/ocpp/JwNpTpPxPm/CHR202305102';

export default function Evse() {
const [url, setUrl] = useState(defaultValue);
const [online, setOnline] = useState(false);
const writer = useRef<IWriter[]>([]);
const writer = useRef<Array<IWriter>>([]);

const chargingSocket = useRef<IChargingSocket>(
new ChargingSocket(StatusNotification.UNAVAILABLE)
);

const onlineChange: ConState = (connected: boolean, w?: IWriter) => {
console.log(chargingSocket);

setOnline(connected);
if (w != null) {
writer.current.push(w);
// send BootNotification once during Boot up
w?.Write('hello');
console.log(chargingSocket);
SendBootNotification(w);
}
};

Expand Down
15 changes: 15 additions & 0 deletions app/helper/validation.helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { plainToClass } from 'class-transformer';
import { validateSync, ValidationError } from 'class-validator';

type ValidationResult<T> = [T, Array<ValidationError>];

const defaultValue = {
enableDebugMessages: false,
validationError: { target: false },
};

export default function Validate<T>(c: any, obj: unknown): ValidationResult<T> {
const result: object = plainToClass(c, obj);
const validation = validateSync(result, defaultValue);
return [result as T, validation];
}
2 changes: 0 additions & 2 deletions app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
'use client';

import Evse from './components/evse';

export default function Home() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import {
IsNumber,
IsEnum,
Min,
Max,
MinLength,
MaxLength,
IsOptional,
IsString,
} from 'class-validator';

enum Status {
ACCEPTED = 'Accepted',
REJECTED = 'Rejected',
PENDING = 'Pending',
}

interface IBootNotification {
chargePointVendor: string;
chargePointModel: string;
chargeBoxSerialNumber: string;
chargePointSerialNumber: string;
firmwareVersion: string;
iccid: string;
imsi: string;
meterSerialNumber: string;
meterType: string;
}
interface IBootNotificationRes {
currentTime: Date;
interval: number;
status: Status;
}

class BootNotification implements IBootNotification {
@MinLength(1)
@MaxLength(20)
chargePointVendor = '';

@MinLength(1)
@MaxLength(20)
chargePointModel = '';

@MinLength(1)
@MaxLength(25)
@IsOptional()
chargeBoxSerialNumber = '';

@MinLength(1)
@MaxLength(25)
@IsOptional()
chargePointSerialNumber = '';

@MinLength(1)
@MaxLength(50)
@IsOptional()
firmwareVersion = '';

@MinLength(1)
@MaxLength(20)
@IsOptional()
iccid = '';

@MinLength(1)
@MaxLength(20)
@IsOptional()
imsi = '';

@MinLength(1)
@MaxLength(25)
@IsOptional()
meterSerialNumber = '';

@MinLength(1)
@MaxLength(25)
@IsOptional()
meterType = '';
}

class BootNotificationRes implements IBootNotificationRes {
@IsString()
currentTime: Date = new Date();

@IsNumber()
@Min(3600)
@Max(84000)
interval = 0;

@IsEnum(Status)
status = Status.PENDING;
}

export type { IBootNotification };
export { Status, BootNotification, BootNotificationRes };
81 changes: 81 additions & 0 deletions app/service/ocpp/command/boot-notification/bootnotification.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { DEFAULT_TIMER, IWriter } from '../../../websocket/websocket.model';
import { Action, CreateRequestFrame, GetRequestFrame } from '../../ocpp.action';
import { IResponse } from '../../ocpp.frame';
import { NewTransaction } from '../../transaction/transaction.handler';
import {
BootNotificationRes,
IBootNotification,
Status,
} from './bootnotification.model';
import { CreateError, ErrorCode } from '../../ocpp.error';
import Validate from '@/app/helper/validation.helper';

const defaultValue: IBootNotification = {
chargePointVendor: 'EW',
chargePointModel: 'CHR22',
chargeBoxSerialNumber: 'CR-00',
chargePointSerialNumber: 'CR-00',
firmwareVersion: '',
iccid: '',
imsi: '',
meterSerialNumber: '',
meterType: 'power',
};

let id: ReturnType<typeof setTimeout>;
let success = false;

function retry(w: IWriter): void {
id = setTimeout(() => {
try {
SendBootNotification(w);
} catch (error) {
clearTimeout(id);
}
}, DEFAULT_TIMER);
}

function SendBootNotification(w: IWriter): void {
try {
clearTimeout(id);
if (success) return;
const frame = CreateRequestFrame(Action.BOOT_NOTIFICATION, defaultValue);
NewTransaction(GetRequestFrame(frame), BootNotification);
w.Write(frame);
} catch (error) {
retry(w);
}
}

function BootNotification(w: IWriter, frame: IResponse): void {
clearTimeout(id);
const [result, validation] = Validate<BootNotificationRes>(
BootNotificationRes,
frame.payload
);

console.log(validation);

if (validation.length > 0) {
w.Write(CreateError(ErrorCode.PropertyConstraintViolation, validation));
return retry(w);
}

if (result.status == Status.PENDING) {
w.Write(
CreateError(ErrorCode.NotSupported, {
err: 'Pending functionality is not supported',
})
);

return retry(w);
}

if (result.status == Status.REJECTED) {
return retry(w);
}

success = true;
}

export { SendBootNotification, BootNotification };
1 change: 1 addition & 0 deletions app/service/ocpp/connector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ interface IChargingSocket {
}

class ChargingSocket implements IChargingSocket {
booted = false;
constructor(private state: StatusNotification) {}

ChangeState(state: StatusNotification): void {
Expand Down
44 changes: 35 additions & 9 deletions app/service/ocpp/ocpp.action.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
import { IRequest, IResponse } from './ocpp.frame';
import { v4 } from 'uuid';
import {
BaseTuple,
CallType,
IRequest,
IResponse,
RequestTuple,
ResponseTuple,
UniqueID,
} from './ocpp.frame';
import { ErrorCode } from './ocpp.error';

enum Action {
BOOT_NOTIFICATION = 'BootNotification',
Expand All @@ -10,20 +20,36 @@ enum Action {
REMOTE_STOP_TRANSACTION = 'RemoteStopTransaction',
}

function CreateRequestFrame(): void {
throw new Error('Not implemented');
function CreateRequestFrame<T>(action: Action, payload: T): RequestTuple {
return [CallType.CALL, v4(), action, payload];
}

function GetRequestFrame(): IRequest {
throw new Error('Not implemented');
function GetRequestFrame(payload: RequestTuple | BaseTuple): IRequest {
if (payload.length < 4) throw new Error(ErrorCode.FormationViolation);
if (payload[0] != CallType.CALL) throw new Error(ErrorCode.ProtocolError);

return {
messageTypeID: payload[0],
uuid: payload[1],
action: payload[2]!,
payload: payload[3],
};
}

function CreateResponseFrame(): void {
throw new Error('Not implemented');
function CreateResponseFrame<T>(uuid: UniqueID, payload: T): ResponseTuple {
return [CallType.CALL_RESULT, uuid, payload];
}

function GetResponseFrame(): IResponse {
throw new Error('Not implemented');
function GetResponseFrame(payload: ResponseTuple | BaseTuple): IResponse {
if (payload.length < 3) throw new Error(ErrorCode.FormationViolation);
if (payload[0] != CallType.CALL_RESULT)
throw new Error(ErrorCode.ProtocolError);

return {
messageTypeID: payload[0],
uuid: payload[1],
payload: payload[2],
};
}

export {
Expand Down
4 changes: 2 additions & 2 deletions app/service/ocpp/ocpp.error.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BaseTuple, CallType, ErrorFrame, ErrorTuple } from './ocpp.frame';
import { BaseTuple, CallType, IErrorFrame, ErrorTuple } from './ocpp.frame';
import { v4 } from 'uuid';

type ErrorDescription = string;
Expand Down Expand Up @@ -32,7 +32,7 @@ function CreateError(errCode: ErrorCode, details: ErrorDetails): ErrorTuple {
return [CallType.CALL_ERROR, v4(), code, errCode, details];
}

function GetError(payload: ErrorTuple | BaseTuple): ErrorFrame {
function GetError(payload: ErrorTuple | BaseTuple): IErrorFrame {
if (payload.length != 5) throw new Error(ErrorCode.FormationViolation);
if (payload[0] != CallType.CALL_ERROR)
throw new Error(ErrorCode.FormationViolation);
Expand Down
4 changes: 2 additions & 2 deletions app/service/ocpp/ocpp.frame.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ interface IPayload {
payload: Payload;
}

interface ErrorFrame extends IFrame {
interface IErrorFrame extends IFrame {
errorCode: ErrorCode;
errorDescription: ErrorDescription;
errorDetails: unknown;
Expand All @@ -40,7 +40,7 @@ interface IFrame {
export type {
IRequest,
IResponse,
ErrorFrame,
IErrorFrame,
Action,
BaseTuple,
UniqueID,
Expand Down
Loading

0 comments on commit 7681439

Please sign in to comment.