Merge branch 'dev'

This commit is contained in:
GHOSCHT 2021-08-27 16:45:08 +02:00
commit 1bcff410e2
18 changed files with 31956 additions and 1165 deletions

View file

@ -22,6 +22,11 @@ module.exports = {
quotes: ["error", "double"], quotes: ["error", "double"],
"import/no-extraneous-dependencies": ["error", { devDependencies: true }], "import/no-extraneous-dependencies": ["error", { devDependencies: true }],
"no-console": "off", "no-console": "off",
"max-len": ["error", { code: 300 }],
"no-shadow": "off",
"@typescript-eslint/no-shadow": ["error"],
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": "error",
"import/extensions": [ "import/extensions": [
"error", "error",
"ignorePackages", "ignorePackages",

28642
Dashboard/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -46,7 +46,8 @@
"package": "yarn build && electron-builder --publish never", "package": "yarn build && electron-builder --publish never",
"postinstall": "electron-builder install-app-deps", "postinstall": "electron-builder install-app-deps",
"lint": "cross-env NODE_ENV=development eslint . --cache --ext .js,.jsx,.ts,.tsx", "lint": "cross-env NODE_ENV=development eslint . --cache --ext .js,.jsx,.ts,.tsx",
"lint:fix": "cross-env NODE_ENV=development eslint . --cache --fix --ext .js,.jsx,.ts,.tsx" "lint:fix": "cross-env NODE_ENV=development eslint . --cache --fix --ext .js,.jsx,.ts,.tsx",
"remotedev": "remotedev --hostname=localhost --port=8000"
}, },
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
@ -54,6 +55,7 @@
"@types/node": "^15.12.5", "@types/node": "^15.12.5",
"@types/react": "^17.0.11", "@types/react": "^17.0.11",
"@types/react-dom": "^17.0.8", "@types/react-dom": "^17.0.8",
"@types/remote-redux-devtools": "^0.5.5",
"@types/styled-components": "^5.1.11", "@types/styled-components": "^5.1.11",
"electron-acrylic-window": "^0.5.5", "electron-acrylic-window": "^0.5.5",
"electron-devtools-installer": "^3.2.0", "electron-devtools-installer": "^3.2.0",
@ -63,15 +65,24 @@
"fs-extra": "^9.0.1", "fs-extra": "^9.0.1",
"react": "^16.13.1", "react": "^16.13.1",
"react-dom": "^16.13.1", "react-dom": "^16.13.1",
"react-redux": "^7.2.4",
"react-select": "^4.3.1",
"redux": "^4.1.1",
"redux-thunk": "^2.3.0",
"remote-redux-devtools": "^0.5.16",
"screenz": "^1.0.0", "screenz": "^1.0.0",
"serialport": "^9.2.0", "serialport": "^9.2.0",
"sqlite3": "^5.0.2",
"styled-components": "^5.3.0", "styled-components": "^5.3.0",
"typescript": "^4.3.4" "typesafe-actions": "^5.1.0",
"typescript": "^4.3.4",
"chalk": "^4.1.2"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.11.0", "@babel/core": "^7.11.0",
"@babel/preset-env": "^7.11.0", "@babel/preset-env": "^7.11.0",
"@babel/preset-react": "^7.10.4", "@babel/preset-react": "^7.10.4",
"@types/react-select": "^4.0.17",
"@types/serialport": "^8.0.1", "@types/serialport": "^8.0.1",
"@typescript-eslint/eslint-plugin": "^4.28.1", "@typescript-eslint/eslint-plugin": "^4.28.1",
"@typescript-eslint/parser": "^4.28.1", "@typescript-eslint/parser": "^4.28.1",
@ -90,6 +101,7 @@
"file-loader": "^6.2.0", "file-loader": "^6.2.0",
"fork-ts-checker-webpack-plugin": "^6.2.12", "fork-ts-checker-webpack-plugin": "^6.2.12",
"html-webpack-plugin": "^4.3.0", "html-webpack-plugin": "^4.3.0",
"remotedev-server": "^0.3.1",
"style-loader": "^1.2.1", "style-loader": "^1.2.1",
"ts-loader": "^9.2.3", "ts-loader": "^9.2.3",
"wait-on": "^6.0.0", "wait-on": "^6.0.0",

View file

@ -1,8 +1,12 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { createGlobalStyle } from "styled-components"; import { createGlobalStyle } from "styled-components";
import { useDispatch, useSelector } from "react-redux";
import { RootState } from "typesafe-actions";
import Select from "react-select";
import SerialPort from "serialport"; import SerialPort from "serialport";
import Knob from "./Components/Knob"; 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` const GlobalStyle = createGlobalStyle`
html { html {
@ -16,33 +20,61 @@ const GlobalStyle = createGlobalStyle`
} }
`; `;
const options: [{value: string, label:string}] = [{ value: "placeholder", label: "Select Port" }];
const fetchPorts = async () => {
const data = await SerialPort.list();
options.shift();
data.forEach((comPort) => { options.push({ value: comPort.path, label: comPort.path }); });
};
const App = () => { const App = () => {
const [status, setStatus] = useState(0); const [status, setStatus] = useState(0);
const dispatch = useDispatch();
const serialConnection = useSelector<RootState, RootState["serialConnection"]>((state) => state.serialConnection);
const SerialDataListener = (data: string) => { const SerialDataListener = (data: string) => {
const parsedData = data.split(","); const parsedData = data.split(",");
console.log(parsedData); console.log(parsedData);
setStatus(parseInt(parsedData[2], 10)); setStatus(parseInt(parsedData[2], 10));
}; };
useEffect(() => { fetchPorts();
parser.on("data", SerialDataListener);
port.open(); const [portSelectionValue, setPortSelectionValue] = useState(options[0]);
return () => { const handlePortChange = async (selectedOption: any) => {
parser.removeListener("data", SerialDataListener); setPortSelectionValue(selectedOption);
port.close(); dispatch(setSerialPort(selectedOption.value));
}; };
}, []);
useEffect(() => {
if (serialConnection.portController !== null) {
serialConnection.portController.parser.on("data", SerialDataListener);
}
return () => {
if (serialConnection.portController !== null) {
serialConnection.portController.parser.removeListener("data", SerialDataListener);
}
};
}, [serialConnection]);
const sendIncreaseHandler = () => { const sendIncreaseHandler = () => {
if (serialConnection.portController !== null && serialConnection.portController.port !== null) {
setStatus(status + 1); setStatus(status + 1);
port.write("2i"); serialConnection.portController.port.write("2i");
}
}; };
const sendDecreaseHandler = () => { const sendDecreaseHandler = () => {
if (serialConnection.portController !== null && serialConnection.portController.port !== null) {
setStatus(status - 1); setStatus(status - 1);
port.write("2d"); serialConnection.portController.port.write("2d");
}
}; };
const sendToggleHandler = () => { const sendToggleHandler = () => {
port.write("2t"); if (serialConnection.portController !== null && serialConnection.portController.port !== null) {
setStatus(status - 1);
serialConnection.portController.port.write("2t");
}
}; };
return ( return (
@ -57,30 +89,24 @@ const App = () => {
<button <button
type="button" type="button"
onClick={() => { onClick={() => {
port.open(); dispatch(connect());
}} }}
> >
open connect
</button> </button>
<button <button
type="button" type="button"
onClick={() => { onClick={() => {
port.close(); dispatch(disconnect());
}} }}
> >
close disconnect
</button>
<button
type="button"
onClick={() => {
const list = SerialPort.list();
list.then((arg) => {
console.log(arg);
});
}}
>
list
</button> </button>
<Select
value={portSelectionValue}
onChange={handlePortChange}
options={options}
/>
</div> </div>
); );
}; };

View file

@ -1,10 +1,51 @@
import SerialPort, { parsers } from "serialport"; import SerialPort, { parsers } from "serialport";
const port = new SerialPort("COM5", { class PortController {
path: null | string;
port: null | SerialPort;
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, baudRate: 9600,
autoOpen: false, autoOpen: false,
}); });
const parser = port.pipe(new parsers.Readline({ delimiter: "\r\n" })); this.parser = this.port.pipe(new parsers.Readline({ delimiter: "\r\n" }));
}
export { port, parser }; 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;

View file

@ -7,6 +7,7 @@ const isDev = require("electron-is-dev");
const screenz = require("screenz"); const screenz = require("screenz");
const els = require("electron-localshortcut"); const els = require("electron-localshortcut");
const devTools = require("electron-devtools-installer"); const devTools = require("electron-devtools-installer");
const chalk = require("chalk");
let tray = null; let tray = null;
let mainWindow = null; let mainWindow = null;
@ -60,9 +61,13 @@ function createWindow() {
: `file://${path.join(__dirname, "../build/index.html")}`, : `file://${path.join(__dirname, "../build/index.html")}`,
); );
// Open the DevTools.
if (isDev) { if (isDev) {
win.webContents.openDevTools({ mode: "detach" }); win.webContents.openDevTools({ mode: "detach" });
const remotedev = require("remotedev-server");
remotedev({ hostname: "localhost", port: 8000 }).then(() => {
console.log(chalk.blue("RemoteDev server running at http://localhost:8000/"));
});
} }
// Register shortcut to open devtools // Register shortcut to open devtools

View file

@ -1,6 +1,13 @@
import React from "react"; import React from "react";
import { render } from "react-dom"; import { render } from "react-dom";
import { Provider } from "react-redux";
import store from "./redux/store";
import App from "./App"; import App from "./App";
render(<App />, document.getElementById("root")); render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root"),
);

View file

@ -0,0 +1,12 @@
import PortController from "../SerialConnection";
export default interface ISerialConnectionState {
port: string | null;
portController: PortController | null;
status: {
connecting: boolean;
connected: boolean;
error: string | null;
};
// eslint-disable-next-line semi
}

View file

@ -0,0 +1,36 @@
import { ThunkResult } from "typesafe-actions";
import {
connectionStart, connectionSuccess, connectionFailure, setPortController, connectionEnd,
} from "./serialConnectionActions";
import PortController from "../../SerialConnection";
const connect = (): ThunkResult<void> => 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<void> => async (dispatch, getState) => {
const state = getState();
state.serialConnection.portController?.close();
dispatch(connectionEnd());
};
export { connect, disconnect };

View file

@ -0,0 +1,23 @@
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",
CONNECTION_END = "CONNECTION_END",
SET_PORT_CONTROLLER = "SET_PORT_CONTROLLER"
}
export const setSerialPort = (port: string) => action(SerialConnectionActionTypes.SET_SERIAL_PORT, port);
export const connectionStart = () => action(SerialConnectionActionTypes.CONNECTION_START);
export const connectionSuccess = () => action(SerialConnectionActionTypes.CONNECTION_SUCCESS);
export const connectionFailure = (error: Error) => action(SerialConnectionActionTypes.CONNECTION_FAILURE, error);
export const connectionEnd = () => action(SerialConnectionActionTypes.CONNECTION_END);
export const setPortController = (controller: PortController) => action(SerialConnectionActionTypes.SET_PORT_CONTROLLER, controller);

View file

@ -0,0 +1,58 @@
import { createReducer } from "typesafe-actions";
import ISerialConnectionState from "../../interfaces/ISerialConnectionState";
import { SerialConnectionActionTypes } from "../actions/serialConnectionActions";
const initialState: ISerialConnectionState = {
port: null,
portController: null,
status: {
connecting: false,
connected: false,
error: null,
},
};
const SerialConnectionReducer = createReducer(initialState)
.handleType(SerialConnectionActionTypes.SET_SERIAL_PORT, (state, action) => ({
...state,
port: action.payload,
}))
.handleType(SerialConnectionActionTypes.CONNECTION_START, (state) => ({
...state,
status: {
connecting: true,
connected: false,
error: null,
},
}))
.handleType(SerialConnectionActionTypes.CONNECTION_FAILURE, (state, action) => ({
...state,
status: {
connecting: false,
connected: false,
error: action.payload.message,
},
}))
.handleType(SerialConnectionActionTypes.CONNECTION_SUCCESS, (state) => ({
...state,
status: {
connecting: false,
connected: true,
error: null,
},
}))
.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;

View file

@ -0,0 +1,19 @@
import thunk from "redux-thunk";
import { createStore, applyMiddleware } from "redux";
import { RootAction, RootState } from "typesafe-actions";
import composeEnhancers from "./utils";
import rootReducer from "./root-reducer";
const middlewares = [thunk];
const enhancer = composeEnhancers(applyMiddleware(...middlewares));
const initialState = {};
const store = createStore<RootState, RootAction, any, any>(
rootReducer,
initialState,
enhancer,
);
export default store;

View file

@ -0,0 +1,7 @@
import * as serialConnectionActions from "../actions/serialConnectionActions";
const rootAction = {
serialConnection: serialConnectionActions,
};
export default rootAction;

View file

@ -0,0 +1,8 @@
import { combineReducers } from "redux";
import SerialConnectionReducer from "../reducers/serialConnectionReducer";
const rootReducer = combineReducers({
serialConnection: SerialConnectionReducer,
});
export default rootReducer;

19
Dashboard/src/redux/store/types.d.ts vendored Normal file
View file

@ -0,0 +1,19 @@
import { StateType, ActionType } from "typesafe-actions";
import { ThunkAction } from "redux-thunk";
import rootReducer from "./root-reducer";
import rootAction from "./root-action";
import store from "./index";
declare module "typesafe-actions" {
export type Store = StateType<typeof store>;
export type RootState = StateType<typeof rootReducer>;
export type RootAction = ActionType<typeof rootAction>;
export type ThunkResult<R> = ThunkAction<R, RootState, unknown, RootAction>;
interface Types {
RootAction: RootAction;
}
}

View file

@ -0,0 +1,12 @@
import { composeWithDevTools } from "remote-redux-devtools";
// Access Redux devtools via http://localhost:8000
const composeEnhancers = composeWithDevTools({
name: "Light-Control",
realtime: true,
secure: false,
hostname: "localhost",
port: 8000,
});
export default composeEnhancers;

File diff suppressed because it is too large Load diff

View file

@ -7,3 +7,12 @@
</a> </a>
Uses: https://github.com/GHOSCHT/simple-electron-react-boilerplate Uses: https://github.com/GHOSCHT/simple-electron-react-boilerplate
Dashboard: node-gyp fails on sqlite3 build -> set default pyhton version to python 2
```
node-gyp --python /path/to/python2.7/python.exe
```
or
```
npm config set python /path/to/executable/python2.7
```