Skip to content

Commit

Permalink
Merge pull request #16 from Nasar165/reconnect
Browse files Browse the repository at this point in the history
Reconnect
  • Loading branch information
Nasar165 authored May 4, 2024
2 parents 48a0260 + c066bfa commit 40ef10c
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 39 deletions.
9 changes: 8 additions & 1 deletion app/components/input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,16 @@ type InputEvent = (event: SyntheticEvent<HTMLInputElement>) => void;
type input = {
name: string;
value: string;
disabled: boolean;
onChange: InputEvent;
};

export default function Input({ name, value, onChange }: input): JSX.Element {
export default function Input({
name,
value,
disabled,
onChange,
}: input): JSX.Element {
return (
<div className='px-4'>
<label className='block text-sm font-medium leading-6 text-gray-900'>
Expand All @@ -22,6 +28,7 @@ export default function Input({ name, value, onChange }: input): JSX.Element {
onChange={onChange}
className='block w-full rounded-md border-0 py-1.5 pl-7 pr-20 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6'
placeholder='wss://'
disabled={disabled}
/>
</div>
</div>
Expand Down
22 changes: 9 additions & 13 deletions app/components/socket.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,31 @@
'use client';

import React, { useEffect, useState } from 'react';
import { Websocket } from '../lib/websocket/socket';
import React from 'react';
import { CloseEvent, ConState } from '../model/websocket';
import WebSocketHook from '../hook/socketHook';

type Url = { url: string };
type Url = { url: string; online: boolean; state: ConState };

export default function Socket({ url }: Url): React.JSX.Element {
const [socket, setSocket] = useState<Websocket>();
export default function Socket({ url, online, state }: Url): React.JSX.Element {
const [socket] = WebSocketHook({ state: state });

const start = () => {
try {
if (!socket?.Alive()) {
socket?.Start(url);
setSocket(socket);
} else socket?.Stop(1000);
socket?.Connect(url, state);
} else socket?.Disconnect(CloseEvent.NORMAL);
} catch (err) {
console.error(err);
}
};

useEffect(() => {
setSocket(new Websocket());
}, []);

return (
<div>
<button
className='border border-blue-600 rounded-md px-4 py-2 my-4'
onClick={() => start()}
>
{socket?.Alive() ? 'Disconnect' : 'Connect'}
{online ? 'Disconnect' : 'Connect'}
</button>
</div>
);
Expand Down
16 changes: 16 additions & 0 deletions app/hook/socketHook.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { useEffect, useRef, useState } from 'react';
import { ConState, IWebSocket } from '../model/websocket';
import { Websocket } from '../lib/websocket/websocket';

type Url = { state: ConState };

export default function WebSocketHook({ state }: Url): [IWebSocket] {
const [socket, setSocket] = useState<IWebSocket>();
const ref = useRef(state);

useEffect(() => {
setSocket(new Websocket(ref.current));
}, []);

return [socket!];
}
4 changes: 2 additions & 2 deletions app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import type { Metadata } from 'next';
import './globals.css';

export const metadata: Metadata = {
title: 'Create Next App',
description: 'Generated by create next app',
title: 'EVSE OCPP test tool',
description: 'A simple OCPP 1.6-J test tool',
};

export default function RootLayout({
Expand Down
18 changes: 8 additions & 10 deletions app/lib/websocket/socket.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,26 @@
import { DEAD, ISocket, LIVE } from '@/app/model/websocket';
export class Websocket implements ISocket {
private socket?: WebSocket;
import { CloseEvent, DEAD, ISocket, LIVE } from '@/app/model/websocket';

export class Socket implements ISocket {
protected socket?: WebSocket;

Alive(): boolean {
return this.socket != null && this.socket.readyState == this.socket.OPEN;
}

Start(url: string): void {
if (this.Alive()) throw Error(LIVE);

this.socket = new WebSocket(url, ['ocpp1.6']);
console.log('Websocket connection was successful');
console.info('Websocket connection was successful');
}

Stop(code?: number): void {
if (!this.Alive()) throw new Error(DEAD);

console.log('closing socket');
this.socket!.close(code ?? 1000);
if (this.socket == null) throw new Error(DEAD);
this.socket!.close(code ?? CloseEvent.NORMAL);
console.info('socket closed');
}

Send(message: string) {
if (!this.Alive()) throw new Error(DEAD);

this.socket!.send(message);
}
}
75 changes: 75 additions & 0 deletions app/lib/websocket/websocket.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { CloseEvent, ConState, DEFAULT_TIMER } from '@/app/model/websocket';
import { Socket } from './socket';

let id: ReturnType<typeof setTimeout>;

export class Websocket extends Socket {
private url: string = '';
private timer = 0;
private stateChange: ConState;

constructor(event: ConState) {
super();
this.timer = DEFAULT_TIMER;
this.stateChange = event;
}

Connect(url: string): void {
this.stateChange(true);
this.url = url;
this.Start(url);
this.setEventListeners();
}

Disconnect(code: number): void {
try {
this.stateChange(false);
this.Stop(code);
} catch (error) {
throw error;
} finally {
this.removeEventListeners();
}
}

protected setEventListeners(): void {
this.socket!.addEventListener('close', this.close.bind(this));
this.socket!.addEventListener('error', this.error.bind(this));
this.socket!.addEventListener('open', this.open.bind(this));
}

protected removeEventListeners(): void {
this.socket!.removeEventListener('close', this.close.bind(this));
this.socket!.removeEventListener('error', this.error.bind(this));
this.socket!.addEventListener('open', this.open.bind(this));
}

protected retry(): void {
clearTimeout(id);
console.log('connection lost retrying');
id = setTimeout(() => {
this.Start(this.url);
}, this.timer);
}

protected error(ev: Event): void {
console.error('websocket an error has occurred', ev);
const socket = ev.currentTarget as WebSocket;

if (socket.readyState == socket.CLOSED) {
this.stateChange(false);
this.removeEventListeners();
this.retry();
}
}

protected close(reason: CloseEvent): void {
if (this.socket?.readyState != this.socket?.OPEN) return;
console.info(reason);
this.Stop(CloseEvent.NORMAL);
}

protected open(): void {
this.stateChange(true);
}
}
26 changes: 21 additions & 5 deletions app/model/websocket.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,27 @@
const CloseEvent = Object.freeze({
NORMAL: 1000,
INTERNALERROR: 1011,
});

const DEFAULT_TIMER = 3600;
const LIVE = 'web socket client is already initialized';
const DEAD = 'web socket client is dead, open a new connection';

interface ISocket {
Alive(): boolean;
Start(url: string, event: CloseEvent): void;
type ConState = (connected: boolean) => void;

interface ISocket extends IAlive {
Start(url: string, event: ConState): void;
Stop(code: number): void;
}

export type { ISocket };
export { LIVE, DEAD };
interface IWebSocket extends IAlive {
Connect(url: string, event: ConState): void;
Disconnect(code: number): void;
}

interface IAlive {
Alive(): boolean;
}

export type { IWebSocket, ISocket, ConState };
export { LIVE, DEAD, DEFAULT_TIMER, CloseEvent };
18 changes: 10 additions & 8 deletions app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,26 @@
import { SyntheticEvent, useState } from 'react';
import Websocket from './components/socket';
import Input from './components/input';
import { ConState } from './model/websocket';

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

export default function Home() {
const [url, setUrl] = useState(
'ws://localhost:8080/ocpp/JwNpTpPxPm/CHR202305102'
);
const [url, setUrl] = useState(defaultValue);
const [online, setOnline] = useState(false);

const onChange = (event: SyntheticEvent<HTMLInputElement>) => {
console.log(event.currentTarget.value);
const state: ConState = (connected: boolean) => setOnline(connected);

const onChange = (event: SyntheticEvent<HTMLInputElement>) => {
setUrl(event.currentTarget.value);
};

return (
<div className='pt-6 text-center'>
<h1 className='text-4xl'>OCPP 1.6-J EVSE Test tool</h1>
<div className='w-1/3 mx-auto mt-8'>
<Input name='url' value={url} onChange={onChange} />
<Websocket url={url} />
<div className='w-screen mx-auto mt-8 md:w-2/3 xl:w-1/3'>
<Input name='url' value={url} onChange={onChange} disabled={online} />
<Websocket url={url} state={state} online={online} />
</div>
</div>
);
Expand Down

0 comments on commit 40ef10c

Please sign in to comment.