Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Statusnotification #25

Merged
merged 4 commits into from
May 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 23 additions & 8 deletions app/components/evse.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,24 @@ 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 {
ChargePointErrorCodes,
StatusNotification,
} from '../service/ocpp/command/status-notification/status.notificiation';
import { SendStatusNotification } from '../service/ocpp/command/status-notification/statusnotification';

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<Array<IWriter>>([]);

const chargingSocket = useRef<IChargingSocket>(
new ChargingSocket(StatusNotification.UNAVAILABLE)
);
const [socket, setSocket] = useState<IChargingSocket>(new ChargingSocket());

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

setOnline(connected);
if (w != null) {
writer.current.push(w);
Expand All @@ -36,7 +36,21 @@ export default function Evse() {

const onMessage = (ev: MessageEvent) => {
if (writer == null) return;
HandleOcpp(writer.current[0], ev.data);
HandleOcpp(writer.current[0], ev.data, changeState);
};

const changeState = (
state: StatusNotification,
error?: ChargePointErrorCodes
) => {
setSocket({ ...socket, State: state });
if (writer.current[0] == null) return;
SendStatusNotification(
writer.current[0],
0,
error ?? ChargePointErrorCodes.NoError,
state
);
};

return (
Expand All @@ -48,6 +62,7 @@ export default function Evse() {
onMessage={onMessage}
online={online}
/>
<StatusNotificationUI state={socket.State} changeState={changeState} />
</div>
);
}
100 changes: 100 additions & 0 deletions app/components/select.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { useEffect, useState } from 'react';

type ReturnValue = string | number;

type Item = {
name: string;
value: ReturnValue;
};

type Select = {
items: Array<Item>;
defaultItem: Item;
onChange: (value: ReturnValue) => void;
};

function Selected(item: Item, selected?: Item) {
if (selected == null || item.name != selected.name) return null;
return (
<span className='text-indigo-600 absolute inset-y-0 right-0 flex items-center pr-4'>
<svg
className='h-5 w-5'
viewBox='0 0 20 20'
fill='currentColor'
aria-hidden='true'
>
<path d='M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z' />
</svg>
</span>
);
}

export default function Select({
items,
defaultItem,
onChange,
}: Select): React.JSX.Element {
const [selected, setSelected] = useState<Item>();
const [drop, setDrop] = useState(false);
const show = drop ? '' : 'hidden';

const onClick = (item: Item) => {
setSelected(item);
onChange(item.value);
onDrop();
};

const onDrop = () => {
setDrop((v) => !v);
};

useEffect(() => {
setSelected(defaultItem);
}, [defaultItem]);

return (
<div>
<div className='relative mt-2'>
<button
onClick={onDrop}
type='button'
className=' relative w-full cursor-default rounded-md bg-white py-1.5 pl-3 pr-10 text-left text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500 sm:text-sm sm:leading-6'
>
<span className='flex items-center'>
<span className='ml-3 block truncate'>
{selected == null ? null : selected.name}
</span>
</span>
<span className='pointer-events-none absolute inset-y-0 right-0 ml-3 flex items-center pr-2'>
<svg className='h-5 w-5 text-gray-400' viewBox='0 0 20 20'>
<path d='M10 3a.75.75 0 01.55.24l3.25 3.5a.75.75 0 11-1.1 1.02L10 4.852 7.3 7.76a.75.75 0 01-1.1-1.02l3.25-3.5A.75.75 0 0110 3zm-3.76 9.2a.75.75 0 011.06.04l2.7 2.908 2.7-2.908a.75.75 0 111.1 1.02l-3.25 3.5a.75.75 0 01-1.1 0l-3.25-3.5a.75.75 0 01.04-1.06z' />
</svg>
</span>
</button>
<ul
className={`${show} absolute z-10 mt-1 max-h-56 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm`}
role='listbox'
aria-labelledby='listbox-label'
aria-activedescendant='listbox-option-3'
>
{items.map((item) => (
<li
onClick={() => onClick(item)}
key={item.name}
className='text-gray-900 relative cursor-default select-none py-2 pl-3 pr-9'
>
<div className='flex items-center'>
<span className='font-normal ml-3 block truncate'>
{item.name}
</span>
</div>
{Selected(item, selected)}
</li>
))}
</ul>
</div>
</div>
);
}

export type { Item, ReturnValue };
31 changes: 31 additions & 0 deletions app/components/status.notification.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from 'react';
import { StatusNotification } from '../service/ocpp/command/status-notification/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 {
const items: Array<Item> = Object.values(StatusNotification).map((v) => {
return { name: v, value: v };
});
const defaultState = { name: state, value: state };

const onChange = (value: ReturnValue) =>
changeState(value as StatusNotification);

console.log(state);

return (
<div>
state: {state}
<Select items={items} onChange={onChange} defaultItem={defaultState} />
</div>
);
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,4 @@
import {
IsNumber,
IsEnum,
Min,
Max,
MinLength,
MaxLength,
IsOptional,
IsString,
} from 'class-validator';
import { IsNumber, IsEnum, Min, Max, IsString } from 'class-validator';

enum Status {
ACCEPTED = 'Accepted',
Expand All @@ -32,51 +23,6 @@ interface IBootNotificationRes {
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();
Expand All @@ -91,4 +37,4 @@ class BootNotificationRes implements IBootNotificationRes {
}

export type { IBootNotification };
export { Status, BootNotification, BootNotificationRes };
export { Status, BootNotificationRes };
14 changes: 10 additions & 4 deletions app/service/ocpp/command/boot-notification/bootnotification.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
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 { CreateTransaction } from '../../transaction/transaction.handler';
import {
BootNotificationRes,
IBootNotification,
Status,
} from './bootnotification.model';
import { CreateError, ErrorCode } from '../../ocpp.error';
import Validate from '@/app/helper/validation.helper';
import { StatusNotification } from '../status-notification/status.notificiation';
import { ChangeState } from '../../ocpp.handler';

const defaultValue: IBootNotification = {
chargePointVendor: 'EW',
Expand Down Expand Up @@ -40,14 +42,18 @@ function SendBootNotification(w: IWriter): void {
clearTimeout(id);
if (success) return;
const frame = CreateRequestFrame(Action.BOOT_NOTIFICATION, defaultValue);
NewTransaction(GetRequestFrame(frame), BootNotification);
CreateTransaction(GetRequestFrame(frame), BootNotification);
w.Write(frame);
} catch (error) {
retry(w);
}
}

function BootNotification(w: IWriter, frame: IResponse): void {
function BootNotification(
w: IWriter,
frame: IResponse,
changeState: ChangeState
): void {
clearTimeout(id);
const [result, validation] = Validate<BootNotificationRes>(
BootNotificationRes,
Expand All @@ -74,7 +80,7 @@ function BootNotification(w: IWriter, frame: IResponse): void {
if (result.status == Status.REJECTED) {
return retry(w);
}

changeState(StatusNotification.AVAILABLE);
success = true;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
enum StatusNotification {
AVAILABLE = 'Available',
PREPARING = 'Preparing',
CHARGING = 'Charging',
SUSPENDED_EVSE = 'SuspendedEVSE',
SUSPENDED_EV = 'SuspendedEV',
FINISHING = 'Finishing',
RESERVED = 'Reserved',
UNAVAILABLE = 'Unavailable',
FAULTED = 'Faulted',
}

enum ChargePointErrorCodes {
ConnectorLockFailure = 'NoError',
EVCommunicationError = 'EVCommunicationError',
GroundFailure = 'GroundFailure',
HighTemperature = 'HighTemperature',
InternalError = 'InternalError',
LocalListConflict = 'LocalListConflict',
NoError = 'NoError',
OtherError = 'OtherError',
OverCurrentFailure = 'OverCurrentFailure',
OverVoltage = 'OverVoltage',
PowerMeterFailure = 'PowerMeterFailure',
PowerSwitchFailure = 'PowerSwitchFailure',
ReaderFailure = 'ReaderFailure',
ResetFailure = 'ResetFailure',
UnderVoltage = 'UnderVoltage',
WeakSignal = 'WeakSignal',
}

interface IStatusNotification {
connectorId: number;
status: StatusNotification;
errorCode: ChargePointErrorCodes;
}

export type { IStatusNotification };
export { ChargePointErrorCodes, StatusNotification };
30 changes: 30 additions & 0 deletions app/service/ocpp/command/status-notification/statusnotification.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { IWriter } from '../../../websocket/websocket.model';
import { Action, CreateRequestFrame, GetRequestFrame } from '../../ocpp.action';
import { CreateTransaction } from '../../transaction/transaction.handler';
import {
ChargePointErrorCodes,
IStatusNotification,
StatusNotification,
} from './status.notificiation';

function SendStatusNotification(
w: IWriter,
connectorId: number,
errorCode: ChargePointErrorCodes,
status: StatusNotification
): void {
const req: IStatusNotification = {
connectorId,
errorCode,
status,
};
const frame = CreateRequestFrame(Action.STATUS_NOTIFICATION, req);
w.Write(frame);
CreateTransaction(GetRequestFrame(frame), StatusNotificationRes);
}

function StatusNotificationRes(): void {
console.log('status notification res received');
}

export { SendStatusNotification };
Loading
Loading