diff --git a/Dashboard/src/App.tsx b/Dashboard/src/App.tsx index 4b5c392..5af9d07 100644 --- a/Dashboard/src/App.tsx +++ b/Dashboard/src/App.tsx @@ -1,8 +1,10 @@ import React, { useEffect, useState } from "react"; import { createGlobalStyle } from "styled-components"; -import SerialPort from "serialport"; +import { useDispatch, useSelector } from "react-redux"; +import { RootState } from "typesafe-actions"; import Knob from "./Components/Knob"; -import { port, parser } from "./SerialConnection"; +import { connect, disconnect } from "./redux/actions/asyncSerialConnectionActions"; +import { setSerialPort } from "./redux/actions/serialConnectionActions"; const GlobalStyle = createGlobalStyle` html { @@ -18,6 +20,9 @@ const GlobalStyle = createGlobalStyle` const App = () => { const [status, setStatus] = useState(0); + const dispatch = useDispatch(); + const serialConnection = useSelector((state) => state.serialConnection); + const SerialDataListener = (data: string) => { const parsedData = data.split(","); console.log(parsedData); @@ -25,24 +30,34 @@ const App = () => { }; useEffect(() => { - parser.on("data", SerialDataListener); - port.open(); + if (serialConnection.portController !== null) { + serialConnection.portController.parser.on("data", SerialDataListener); + } + return () => { - parser.removeListener("data", SerialDataListener); - port.close(); + if (serialConnection.portController !== null) { + serialConnection.portController.parser.removeListener("data", SerialDataListener); + } }; - }, []); + }, [serialConnection]); const sendIncreaseHandler = () => { - setStatus(status + 1); - port.write("2i"); + if (serialConnection.portController !== null && serialConnection.portController.port !== null) { + setStatus(status + 1); + serialConnection.portController.port.write("2i"); + } }; const sendDecreaseHandler = () => { - setStatus(status - 1); - port.write("2d"); + if (serialConnection.portController !== null && serialConnection.portController.port !== null) { + setStatus(status - 1); + serialConnection.portController.port.write("2d"); + } }; const sendToggleHandler = () => { - port.write("2t"); + if (serialConnection.portController !== null && serialConnection.portController.port !== null) { + setStatus(status - 1); + serialConnection.portController.port.write("2t"); + } }; return ( @@ -57,29 +72,19 @@ const App = () => { - ); diff --git a/Dashboard/src/SerialConnection.ts b/Dashboard/src/SerialConnection.ts index e8fa71b..449ff1d 100644 --- a/Dashboard/src/SerialConnection.ts +++ b/Dashboard/src/SerialConnection.ts @@ -1,10 +1,51 @@ import SerialPort, { parsers } from "serialport"; -const port = new SerialPort("COM5", { - baudRate: 9600, - autoOpen: false, -}); +class PortController { + path: null | string; -const parser = port.pipe(new parsers.Readline({ delimiter: "\r\n" })); + port: null | SerialPort; -export { port, parser }; + parser: any; + + constructor() { + this.path = null; + this.port = null; + } + + get isOpen() { + if (!this.port) { + return false; + } + return this.port.isOpen; + } + + create(path: string) { + this.path = path; + this.port = new SerialPort(path, { + baudRate: 9600, + autoOpen: false, + }); + + this.parser = this.port.pipe(new parsers.Readline({ delimiter: "\r\n" })); + } + + open(callback: (error:Error | null | undefined)=>void) { + if (this.isOpen) { + throw new Error("Port already open"); + } + + if (this.port === null) { + throw new Error("Port must be created first"); + } + + this.port.open((error) => callback(error)); + } + + close() { + if (this.port) { + this.port.close(); + } + } +} + +export default PortController; diff --git a/Dashboard/src/interfaces/ISerialConnectionState.ts b/Dashboard/src/interfaces/ISerialConnectionState.ts index aa408e0..a425c03 100644 --- a/Dashboard/src/interfaces/ISerialConnectionState.ts +++ b/Dashboard/src/interfaces/ISerialConnectionState.ts @@ -1,5 +1,8 @@ +import PortController from "../SerialConnection"; + export default interface ISerialConnectionState { - port: null | string; + port: string | null; + portController: PortController | null; status: { connecting: boolean; connected: boolean; diff --git a/Dashboard/src/redux/actions/asyncSerialConnectionActions.ts b/Dashboard/src/redux/actions/asyncSerialConnectionActions.ts new file mode 100644 index 0000000..0ce74cd --- /dev/null +++ b/Dashboard/src/redux/actions/asyncSerialConnectionActions.ts @@ -0,0 +1,36 @@ +import { ThunkResult } from "typesafe-actions"; +import { + connectionStart, connectionSuccess, connectionFailure, setPortController, connectionEnd, +} from "./serialConnectionActions"; +import PortController from "../../SerialConnection"; + +const connect = (): ThunkResult => async (dispatch, getState) => { + const state = getState(); + dispatch(connectionStart()); + + if (state.serialConnection.port === null) { + dispatch(connectionFailure(new Error("No Serial Port set"))); + return; + } + + const controller = new PortController(); + controller.create(state.serialConnection.port); + + dispatch(setPortController(controller)); + + controller.open((error) => { + if (error === null || error === undefined) { + dispatch(connectionSuccess()); + } else { + dispatch(connectionFailure(error)); + } + }); +}; + +const disconnect = (): ThunkResult => async (dispatch, getState) => { + const state = getState(); + state.serialConnection.portController?.close(); + dispatch(connectionEnd()); +}; + +export { connect, disconnect }; diff --git a/Dashboard/src/redux/actions/serialConnectionActions.ts b/Dashboard/src/redux/actions/serialConnectionActions.ts index 650eee1..7670ea6 100644 --- a/Dashboard/src/redux/actions/serialConnectionActions.ts +++ b/Dashboard/src/redux/actions/serialConnectionActions.ts @@ -1,11 +1,13 @@ import { action } from "typesafe-actions"; +import PortController from "../../SerialConnection"; export enum SerialConnectionActionTypes { SET_SERIAL_PORT = "SET_SERIAL_PORT", CONNECTION_START = "CONNECTION_START", CONNECTION_SUCCESS = "CONNECTION_SUCCESS", CONNECTION_FAILURE = "CONNECTION_FAILURE", - DISCONNECT = "DISCONNECT", + CONNECTION_END = "CONNECTION_END", + SET_PORT_CONTROLLER = "SET_PORT_CONTROLLER" } export const setSerialPort = (port: string) => action(SerialConnectionActionTypes.SET_SERIAL_PORT, port); @@ -16,4 +18,6 @@ export const connectionSuccess = () => action(SerialConnectionActionTypes.CONNEC export const connectionFailure = (error: Error) => action(SerialConnectionActionTypes.CONNECTION_FAILURE, error); -export const disconnect = () => action(SerialConnectionActionTypes.DISCONNECT); +export const connectionEnd = () => action(SerialConnectionActionTypes.CONNECTION_END); + +export const setPortController = (controller: PortController) => action(SerialConnectionActionTypes.SET_PORT_CONTROLLER, controller); diff --git a/Dashboard/src/redux/reducers/serialConnectionReducer.ts b/Dashboard/src/redux/reducers/serialConnectionReducer.ts index 9cde48e..dcc11a3 100644 --- a/Dashboard/src/redux/reducers/serialConnectionReducer.ts +++ b/Dashboard/src/redux/reducers/serialConnectionReducer.ts @@ -4,6 +4,7 @@ import { SerialConnectionActionTypes } from "../actions/serialConnectionActions" const initialState: ISerialConnectionState = { port: null, + portController: null, status: { connecting: false, connected: false, @@ -40,13 +41,18 @@ const SerialConnectionReducer = createReducer(initialState) error: null, }, })) - .handleType(SerialConnectionActionTypes.DISCONNECT, (state) => ({ + .handleType(SerialConnectionActionTypes.CONNECTION_END, (state) => ({ ...state, + portController: null, status: { connecting: false, connected: false, error: null, }, + })) + .handleType(SerialConnectionActionTypes.SET_PORT_CONTROLLER, (state, action) => ({ + ...state, + portController: action.payload, })); export default SerialConnectionReducer;