Skip to content

Commit

Permalink
Merge pull request #3 from shiv3/add/experimental-feature
Browse files Browse the repository at this point in the history
Add experimental feature
  • Loading branch information
shiv3 committed Jul 22, 2024
2 parents cf2b5d4 + a455e42 commit e632c2b
Show file tree
Hide file tree
Showing 13 changed files with 611 additions and 282 deletions.
422 changes: 285 additions & 137 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
},
"dependencies": {
"@voltbras/ts-ocpp": "^2.7.3",
"flowbite": "^2.4.1",
"flowbite-react": "^0.10.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^6.24.0",
Expand Down
8 changes: 5 additions & 3 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
// App.tsx
import React from "react";
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
import {BrowserRouter as Router, Route, Routes } from "react-router-dom";
import Navbar from "./components/Navbar.tsx";
import ChargePoint from "./components/ChargePoint.tsx";
import Settings from "./components/Settings.tsx";
import TopPage from "./components/TopPage.tsx";

const App: React.FC = () => {


return (
<Router basename={import.meta.env.VITE_BASE_URL}>
<div className="min-h-screen bg-gray-100">
<Navbar />
<div className="container mx-auto my-auto p-4">
<Routes>
<Route path="/" element={<ChargePoint />} />
<Route path="/" element={<TopPage />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</div>
Expand Down
102 changes: 40 additions & 62 deletions src/components/ChargePoint.tsx
Original file line number Diff line number Diff line change
@@ -1,67 +1,58 @@
import React, { useState, useEffect } from "react";
import { ChargePoint as OCPPChargePoint } from "../cp/ChargePoint";
import React, {useState, useEffect} from "react";
import {ChargePoint as OCPPChargePoint} from "../cp/ChargePoint";
import Connector from "./Connector.tsx";
import { useLocation } from "react-router-dom";
import Logger from "./Logger.tsx";
import * as ocpp from "../cp/OcppTypes";

const ChargePoint: React.FC = () => {
const [cpStatus, setCpStatus] = useState<string>(ocpp.OCPPStatus.Unavailable);
interface ChargePointProps {
cp : OCPPChargePoint;
TagID: string;
}

const ChargePoint: React.FC<ChargePointProps> = (props) => {
const [cp, setCp] = useState<OCPPChargePoint | null>(null);
const [cpStatus, setCpStatus] = useState<string>(ocpp.OCPPStatus.Unavailable);
const [cpError, setCpError] = useState<string>("");

const search = useLocation().search;
const query = new URLSearchParams(search);
const [logs , setLogs] = useState<string[]>([]);

useEffect(() => {
const connectorNumber = parseInt(
query.get("connectors") || localStorage.getItem("CONNECTORS") || "2"
);
const wsURL = query.get("wsurl") || localStorage.getItem("WSURL") || "";
const cpID = query.get("cpid") || localStorage.getItem("CPID") || "CP-001";
const tagID = query.get("tag") || localStorage.getItem("TAG") || "DEADBEEF";

localStorage.setItem("WSURL", wsURL);
localStorage.setItem("CONNECTORS", connectorNumber.toString());
localStorage.setItem("CPID", cpID);
localStorage.setItem("TAG", tagID);

const newCp = new OCPPChargePoint(cpID, connectorNumber, wsURL);
newCp.statusChangeCallback = statusChangeCb;
newCp.loggingCallback = logMsg;
newCp.errorCallback = setCpError;
setCp(newCp);
}, []);
console.log("ChargePointProps", props);
props.cp.statusChangeCallback = statusChangeCb;
props.cp.loggingCallback = logMsg;
props.cp.errorCallback = setCpError;
setCp(props.cp);
}, [props]);

const statusChangeCb = (s: string) => {
setCpStatus(s);
};

const logMsg = (msg: string) => {
console.log(msg);
setLogs((prevLogs) => [...prevLogs, msg]);
};

return (
<div className="bg-white shadow-md rounded px-2 pt-2 pb-1 h-screen">
<SettingsView />
<SettingsView {...props}/>
<div className="flex flex-col md:flex-row">
<ChargePointControls cp={cp} cpStatus={cpStatus} cpError={cpError} />
<ChargePointControls cp={cp} cpStatus={cpStatus} cpError={cpError}/>
<div className="flex-1">
<AuthView cp={cp} cpStatus={cpStatus} />
<AuthView cp={cp} cpStatus={cpStatus} tagID={props.TagID}/>
<div className="flex flex-col md:flex-row mt-4">
{cp?.connectors &&
Array.from(Array(cp.connectors.size).keys()).map((i) => (
<Connector key={i + 1} id={i + 1} cp={cp} />
<Connector key={i + 1} id={i + 1} cp={cp}/>
))}
</div>
</div>
</div>
<Logger />
<Logger logs={logs}/>
</div>
);
};

const CPStatus: React.FC<{ status: string }> = ({ status }) => {
const CPStatus: React.FC<{ status: string }> = ({status}) => {
const statusColor = (s: string) => {
switch (s) {
case ocpp.OCPPStatus.Unavailable:
Expand All @@ -87,19 +78,15 @@ const CPStatus: React.FC<{ status: string }> = ({ status }) => {
interface AuthViewProps {
cp: OCPPChargePoint | null;
cpStatus: string;
tagID: string;
}

const AuthView: React.FC<AuthViewProps> = ({ cp, cpStatus }) => {
const [tagID, setTagID] = useState<string>("");

useEffect(() => {
setTagID(localStorage.getItem("TAG") || "");
}, []);
const AuthView: React.FC<AuthViewProps> = (props) => {
const [tagID, setTagID] = useState<string>(props.tagID);

const handleAuthorize = () => {
if (cp) {
const tagId = localStorage.getItem("TAG") || "DEADBEEF";
cp.authorize(tagId);
if (props.cp) {
props.cp.authorize(tagID);
}
};

Expand All @@ -120,18 +107,18 @@ const AuthView: React.FC<AuthViewProps> = ({ cp, cpStatus }) => {
value={tagID}
onChange={(e) => setTagID(e.target.value)}
placeholder="DEADBEEF"
style={{ maxWidth: "20ch" }}
style={{maxWidth: "20ch"}}
/>
<p className="text-gray-600 text-xs italic mt-1">
The ID of the simulated RFID tag
</p>
</div>
<button
onClick={handleAuthorize}
className="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded w-full
className="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded
disabled:bg-green-300
"
disabled={cpStatus !== ocpp.OCPPStatus.Available}
disabled={props.cpStatus !== ocpp.OCPPStatus.Available}
>
Authorize
</button>
Expand All @@ -146,10 +133,10 @@ interface ChargePointControlsProps {
}

const ChargePointControls: React.FC<ChargePointControlsProps> = ({
cp,
cpStatus,
cpError,
}) => {
cp,
cpStatus,
cpError,
}) => {
const [isHeartbeatEnabled, setIsHeartbeatEnabled] = useState<boolean>(false);

const handleConnect = () => {
Expand Down Expand Up @@ -182,7 +169,7 @@ const ChargePointControls: React.FC<ChargePointControlsProps> = ({
return (
<div className="bg-gray-100 rounded p-4 mr-4">
<div className="bg-gray-100 rounded p-4 mr-4">
<CPStatus status={cpStatus} />
<CPStatus status={cpStatus}/>
</div>
<div>
{cpError !== "" && (
Expand Down Expand Up @@ -236,22 +223,13 @@ const ChargePointControls: React.FC<ChargePointControlsProps> = ({
);
};

const SettingsView: React.FC = () => {
const [wsURL] = useState<string>(localStorage.getItem("WSURL") || "");
const [connectorNumber] = useState<number>(
parseInt(localStorage.getItem("CONNECTORS") || "2")
);
const [cpID] = useState<string>(localStorage.getItem("CPID") || "CP-001");
const [ocppVersion] = useState<string>(
localStorage.getItem("OCPP") || "OCPP-1.6J"
);
const SettingsView: React.FC<ChargePointProps> = (props) => {
return (
<div className="mb-1 bg-gray-100 rounded p-2">
<p className="text-lg font-semibold">settings</p>
<li>WSURL: {wsURL}</li>
<li>CONNECTORS: {connectorNumber}</li>
<li>CPID: {cpID}</li>
<li>OCPP: {ocppVersion}</li>
<li>CPID: {props.cp.id}</li>
<li>CONNECTORS: {props.cp.connectorNumber}</li>
<li>WSURL: {props.cp.wsUrl}</li>
</div>
);
};
Expand Down
21 changes: 5 additions & 16 deletions src/components/Logger.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,10 @@
import React, { useState, useEffect, useRef } from "react";
import React, { useEffect, useRef} from "react";

const Logger: React.FC = () => {
const [logs, setLogs] = useState<string[]>([]);

useEffect(() => {
// You might want to implement a more sophisticated logging system
// This is just a basic example
const oldConsoleLog = console.log;
console.log = (message: string) => {
setLogs((prevLogs) => [...prevLogs, message]);
};

return () => {
console.log = oldConsoleLog;
};
}, []);
interface LoggerProps {
logs: string[];
}

const Logger: React.FC<LoggerProps> = ({logs}) => {
return (
<div className="mt-2 border-0 border-blue-500 h-screen">
<AutoScrollingTextarea
Expand Down
28 changes: 24 additions & 4 deletions src/components/Settings.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import React, { useState, useEffect } from "react";
import React, {useState, useEffect} from "react";

const Settings: React.FC = () => {
const [wsURL, setWsURL] = useState<string>("");
const [connectorNumber, setConnectorNumber] = useState<number>(2);
const [cpID, setCpID] = useState<string>("");
const [tagID, setTagID] = useState<string>("");
const [ocppVersion, setOcppVersion] = useState<string>("OCPP-1.6J");
const [experimental, setExperimental] = useState<string>();

useEffect(() => {
setWsURL(localStorage.getItem("WSURL") || "");
setConnectorNumber(parseInt(localStorage.getItem("CONNECTORS") || "2"));
setCpID(localStorage.getItem("CPID") || "");
setTagID(localStorage.getItem("TAG") || "");
setOcppVersion(localStorage.getItem("OCPP") || "OCPP-1.6J");
setExperimental(localStorage.getItem("EXPERIMENTAL") || "");
}, []);

const handleSubmit = (e: React.FormEvent) => {
Expand All @@ -20,6 +23,8 @@ const Settings: React.FC = () => {
localStorage.setItem("CONNECTORS", connectorNumber.toString());
localStorage.setItem("CPID", cpID);
localStorage.setItem("TAG", tagID);
localStorage.setItem("OCPP", ocppVersion);
localStorage.setItem("EXPERIMENTAL", btoa(experimental ?? "") || "");
alert("Settings saved successfully!");
};

Expand Down Expand Up @@ -60,7 +65,7 @@ const Settings: React.FC = () => {
value={connectorNumber}
onChange={(e) => setConnectorNumber(parseInt(e.target.value))}
placeholder="2"
style={{ maxWidth: "20ch" }}
style={{maxWidth: "20ch"}}
/>
</div>

Expand All @@ -78,7 +83,7 @@ const Settings: React.FC = () => {
value={cpID}
onChange={(e) => setCpID(e.target.value)}
placeholder="CP001"
style={{ maxWidth: "20ch" }}
style={{maxWidth: "20ch"}}
/>
</div>
<div className="mb-4">
Expand All @@ -93,11 +98,26 @@ const Settings: React.FC = () => {
id="OCPP"
value={ocppVersion}
onChange={(e) => setOcppVersion(e.target.value)}
style={{ maxWidth: "20ch" }}
style={{maxWidth: "20ch"}}
>
<option value="OCPP-1.6J">OCPP-1.6J</option>
</select>
</div>
<div className="mb-4">
<label
className="block text-gray-700 text-sm font-bold mb-2"
htmlFor="Experimental"
>
Experimental
</label>
<textarea
className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
id="Experimental"
placeholder="Experimental features"
style={{height: "100px"}}
onChange={(e) => setExperimental(e.target.value)
}></textarea>
</div>
<div className="flex items-center justify-between">
<button
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
Expand Down
1 change: 1 addition & 0 deletions src/components/Tab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Loading

0 comments on commit e632c2b

Please sign in to comment.