From b38a831f410d615ff6d893f2d2fec7c998ca0bdb Mon Sep 17 00:00:00 2001 From: Nasar Eddaoui Date: Sun, 5 May 2024 19:44:58 +0700 Subject: [PATCH 1/4] allow change state of evse --- app/components/evse.tsx | 22 +++++++++------ app/components/status.notification.tsx | 12 ++++++++ .../boot-notification/bootnotification.ts | 11 ++++++-- app/service/ocpp/connector.ts | 7 +++-- app/service/ocpp/ocpp.handler.ts | 28 +++++++++++++++---- .../ocpp/transaction/transaction.model.ts | 13 ++++++--- 6 files changed, 69 insertions(+), 24 deletions(-) create mode 100644 app/components/status.notification.tsx diff --git a/app/components/evse.tsx b/app/components/evse.tsx index eda2f82..4053319 100644 --- a/app/components/evse.tsx +++ b/app/components/evse.tsx @@ -1,13 +1,14 @@ 'use client'; -import { SyntheticEvent, useRef, useState } from 'react'; +import { SyntheticEvent, useEffect, useRef, useState } from 'react'; import Input from './input'; import { ConState, IWriter } from '../service/websocket/websocket.model'; 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'; +import StatusNotificationUI from './status.notification'; +import { StatusNotification } from '../service/ocpp/status.notificiation'; const defaultValue = 'ws://localhost:8080/ocpp/JwNpTpPxPm/CHR202305102'; @@ -15,14 +16,9 @@ export default function Evse() { const [url, setUrl] = useState(defaultValue); const [online, setOnline] = useState(false); const writer = useRef>([]); - - const chargingSocket = useRef( - new ChargingSocket(StatusNotification.UNAVAILABLE) - ); + const [socket, setSocket] = useState(new ChargingSocket()); const onlineChange: ConState = (connected: boolean, w?: IWriter) => { - console.log(chargingSocket); - setOnline(connected); if (w != null) { writer.current.push(w); @@ -36,7 +32,14 @@ export default function Evse() { const onMessage = (ev: MessageEvent) => { if (writer == null) return; - HandleOcpp(writer.current[0], ev.data); + console.log(socket); + + HandleOcpp(writer.current[0], ev.data, changeState); + }; + + const changeState = (state: StatusNotification) => { + socket.ChangeState(state); + setSocket({ ...socket, State: state }); }; return ( @@ -48,6 +51,7 @@ export default function Evse() { onMessage={onMessage} online={online} /> + ); } diff --git a/app/components/status.notification.tsx b/app/components/status.notification.tsx new file mode 100644 index 0000000..62ae33f --- /dev/null +++ b/app/components/status.notification.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +import { StatusNotification } from '../service/ocpp/status.notificiation'; + +type State = { + state: StatusNotification; +}; + +export default function StatusNotificationUI({ + state, +}: State): React.JSX.Element { + return
state: {state}
; +} diff --git a/app/service/ocpp/command/boot-notification/bootnotification.ts b/app/service/ocpp/command/boot-notification/bootnotification.ts index 12448bf..0648768 100644 --- a/app/service/ocpp/command/boot-notification/bootnotification.ts +++ b/app/service/ocpp/command/boot-notification/bootnotification.ts @@ -9,6 +9,9 @@ import { } from './bootnotification.model'; import { CreateError, ErrorCode } from '../../ocpp.error'; import Validate from '@/app/helper/validation.helper'; +import { IChargingSocket } from '../../connector'; +import { StatusNotification } from '../../status.notificiation'; +import { ChangeState } from '../../ocpp.handler'; const defaultValue: IBootNotification = { chargePointVendor: 'EW', @@ -47,7 +50,11 @@ function SendBootNotification(w: IWriter): void { } } -function BootNotification(w: IWriter, frame: IResponse): void { +function BootNotification( + w: IWriter, + frame: IResponse, + changeState: ChangeState +): void { clearTimeout(id); const [result, validation] = Validate( BootNotificationRes, @@ -74,7 +81,7 @@ function BootNotification(w: IWriter, frame: IResponse): void { if (result.status == Status.REJECTED) { return retry(w); } - + changeState(StatusNotification.Available); success = true; } diff --git a/app/service/ocpp/connector.ts b/app/service/ocpp/connector.ts index 79cbb4e..f85c7a1 100644 --- a/app/service/ocpp/connector.ts +++ b/app/service/ocpp/connector.ts @@ -1,20 +1,21 @@ import { StatusNotification } from './status.notificiation'; interface IChargingSocket { + State: StatusNotification; ChangeState(state: StatusNotification): void; GetState(): StatusNotification; } class ChargingSocket implements IChargingSocket { + State: StatusNotification = StatusNotification.UNAVAILABLE; booted = false; - constructor(private state: StatusNotification) {} ChangeState(state: StatusNotification): void { - this.state = state; + this.State = state; } GetState(): StatusNotification { - return this.state; + return this.State; } } diff --git a/app/service/ocpp/ocpp.handler.ts b/app/service/ocpp/ocpp.handler.ts index 2a8444d..9426226 100644 --- a/app/service/ocpp/ocpp.handler.ts +++ b/app/service/ocpp/ocpp.handler.ts @@ -8,9 +8,11 @@ import { IRequest, IResponse, } from './ocpp.frame'; +import { StatusNotification } from './status.notificiation'; import { FindTransaction } from './transaction/transaction.handler'; type OCPPData = IRequest | IResponse | IErrorFrame; +type ChangeState = (state: StatusNotification) => void; function getCallType(frame: BaseTuple): CallType { return frame[0]; @@ -40,25 +42,33 @@ function processCall(frame: IRequest) { console.log(frame); } -function processReturn(w: IWriter, frame: IErrorFrame | IResponse): void { +function processReturn( + w: IWriter, + frame: IErrorFrame | IResponse, + changeState: ChangeState +): void { try { const transaction = FindTransaction(frame.uuid); if (frame.messageTypeID == CallType.CALL_ERROR) transaction.AddError(frame as IErrorFrame); else { - transaction.AddResponse(w, frame as IResponse); + transaction.AddResponse(w, frame as IResponse, changeState); } } catch (error) { console.log('Unable to process transaction'); } } -function handleFrame(w: IWriter, frame: BaseTuple): void { +function handleFrame( + w: IWriter, + frame: BaseTuple, + changeState: ChangeState +): void { const [call, result] = getFullFrame(frame); if (call == CallType.CALL) { processCall(result as IRequest); } else { - processReturn(w, result); + processReturn(w, result, changeState); } } @@ -78,13 +88,19 @@ function handlerError(err: Error, w: IWriter): void { w.Write(json); } -export function HandleOcpp(w: IWriter, json: string): void { +export function HandleOcpp( + w: IWriter, + json: string, + changeState: ChangeState +): void { try { const data: unknown = JSON.parse(json); if (!Array.isArray(data)) throw new Error(ErrorCode.ProtocolError); let frame = isValidFrame(data); - handleFrame(w, frame); + handleFrame(w, frame, changeState); } catch (error: unknown) { handlerError(error as Error, w); } } + +export type { ChangeState }; diff --git a/app/service/ocpp/transaction/transaction.model.ts b/app/service/ocpp/transaction/transaction.model.ts index 8689b09..d8cb234 100644 --- a/app/service/ocpp/transaction/transaction.model.ts +++ b/app/service/ocpp/transaction/transaction.model.ts @@ -1,12 +1,17 @@ import { IWriter } from '../../websocket/websocket.model'; import { IErrorFrame, IRequest, IResponse } from '../ocpp.frame'; +import { ChangeState } from '../ocpp.handler'; -type ActionResponse = (w: IWriter, frame: IResponse) => void; +type ActionResponse = ( + w: IWriter, + frame: IResponse, + changeState: ChangeState +) => void; interface ITransaction { GetID(): string; Handler: ActionResponse; - AddResponse(w: IWriter, frame: IResponse): void; + AddResponse(w: IWriter, frame: IResponse, changeState: ChangeState): void; GetResponse(): IResponse | undefined; AddError(frame: IErrorFrame): void; GetError(): IErrorFrame | undefined; @@ -25,9 +30,9 @@ class Transaction implements ITransaction { this.error = frame; } - AddResponse(w: IWriter, frame: IResponse): void { + AddResponse(w: IWriter, frame: IResponse, changeState: ChangeState): void { this.response = frame; - this.Handler(w, frame); + this.Handler(w, frame, changeState); } GetID(): string { From 2debe594b0083926f08995a26b62a72cec5c838c Mon Sep 17 00:00:00 2001 From: Nasar Eddaoui Date: Sun, 5 May 2024 20:23:37 +0700 Subject: [PATCH 2/4] add select box to change states --- app/components/evse.tsx | 3 +- app/components/select.tsx | 96 +++++++++++++++++++ app/components/status.notification.tsx | 22 ++++- .../bootnotification.model.ts | 58 +---------- .../boot-notification/bootnotification.ts | 3 +- app/service/ocpp/status.notificiation.ts | 2 +- 6 files changed, 122 insertions(+), 62 deletions(-) create mode 100644 app/components/select.tsx diff --git a/app/components/evse.tsx b/app/components/evse.tsx index 4053319..7290a47 100644 --- a/app/components/evse.tsx +++ b/app/components/evse.tsx @@ -38,7 +38,6 @@ export default function Evse() { }; const changeState = (state: StatusNotification) => { - socket.ChangeState(state); setSocket({ ...socket, State: state }); }; @@ -51,7 +50,7 @@ export default function Evse() { onMessage={onMessage} online={online} /> - + ); } diff --git a/app/components/select.tsx b/app/components/select.tsx new file mode 100644 index 0000000..9105fde --- /dev/null +++ b/app/components/select.tsx @@ -0,0 +1,96 @@ +import { useState } from 'react'; + +type ReturnValue = string | number; + +type Item = { + name: string; + value: ReturnValue; +}; + +type Select = { + items: Array; + defaultItem: Item; + onChange: (value: ReturnValue) => void; +}; + +function Selected(item: Item, selected?: Item) { + if (selected == null || item.name != selected.name) return null; + return ( + + + + ); +} + +export default function Select({ + items, + defaultItem, + onChange, +}: Select): React.JSX.Element { + const [selected, setSelected] = useState(defaultItem); + const [drop, setDrop] = useState(false); + const show = drop ? '' : 'hidden'; + + const onClick = (item: Item) => { + setSelected(item); + onChange(item.value); + onDrop(); + }; + + const onDrop = () => { + setDrop((v) => !v); + }; + + return ( +
+
+ +
    + {items.map((item) => ( +
  • onClick(item)} + key={item.name} + className='text-gray-900 relative cursor-default select-none py-2 pl-3 pr-9' + > +
    + + {item.name} + +
    + {Selected(item, selected)} +
  • + ))} +
+
+
+ ); +} + +export type { Item, ReturnValue }; diff --git a/app/components/status.notification.tsx b/app/components/status.notification.tsx index 62ae33f..0f3c573 100644 --- a/app/components/status.notification.tsx +++ b/app/components/status.notification.tsx @@ -1,12 +1,32 @@ import React from 'react'; import { StatusNotification } from '../service/ocpp/status.notificiation'; +import { ChangeState } from '../service/ocpp/ocpp.handler'; +import Select, { Item, ReturnValue } from './select'; type State = { state: StatusNotification; + changeState: ChangeState; }; export default function StatusNotificationUI({ state, + changeState, }: State): React.JSX.Element { - return
state: {state}
; + const items: Array = Object.values(StatusNotification).map((v) => { + return { name: v, value: v }; + }); + + const onChange = (value: ReturnValue) => + changeState(value as StatusNotification); + + return ( +
+ state: {state} + +