diff --git a/apps/app/src/main/index.ts b/apps/app/src/main/index.ts index b40f87bea0..1cdd5d8567 100644 --- a/apps/app/src/main/index.ts +++ b/apps/app/src/main/index.ts @@ -32,10 +32,10 @@ function createWindow(): void { mainWindow.webContents.openDevTools() mainWindow.on("ready-to-show", () => { + mainWindow.show() + initSerialPort(ipcMain, mainWindow.webContents) initSql(ipcMain) - - mainWindow.show() }) mainWindow.webContents.setWindowOpenHandler((details) => { @@ -66,9 +66,6 @@ app.whenReady().then(() => { optimizer.watchWindowShortcuts(window) }) - // IPC test - ipcMain.on("ping", () => console.log("pong")) - createWindow() app.on("activate", function () { diff --git a/apps/web/src/app/app.spec.tsx b/apps/web/src/app/app.spec.tsx index fde9974553..a8ca138347 100644 --- a/apps/web/src/app/app.spec.tsx +++ b/apps/web/src/app/app.spec.tsx @@ -5,19 +5,19 @@ import { render } from "@testing-library/react" import App from "./app" -import { Device } from "app-serialport/models" +import { SerialPortDeviceInfo } from "app-serialport/models" jest.mock("app-serialport/renderer", () => { return { AppSerialPort: { - onChange: jest.fn().mockResolvedValue([ + onDevicesChanged: jest.fn().mockResolvedValue([ { vendorId: "0e8d", productId: "2006", path: "/dev/ttyUSB0.KOM123456789", }, - ] as Device[]), - write: jest.fn(), + ] as SerialPortDeviceInfo[]), + request: jest.fn(), }, } }) diff --git a/apps/web/src/app/serialport-demo.ts b/apps/web/src/app/serialport-demo.ts index 224d1cbc70..1808d800c0 100644 --- a/apps/web/src/app/serialport-demo.ts +++ b/apps/web/src/app/serialport-demo.ts @@ -3,153 +3,58 @@ * For licensing, see https://github.com/mudita/mudita-center/blob/master/LICENSE.md */ -import { useCallback, useEffect, useRef } from "react" -import { AppSerialPort } from "app-serialport/renderer" -import { ChangedDevices } from "app-serialport/models" +import { useCallback, useEffect } from "react" import { isEmpty } from "lodash" +import { SerialPortDeviceInfo } from "app-serialport/models" +import { AppSerialPort } from "app-serialport/renderer" export const useSerialPortListener = () => { - // const interval = useRef() - const ref = useRef(false) - - const listenPorts = useCallback(async () => { - if (!ref.current) { - ref.current = true - const onAttach = async (added: NonNullable) => { - const apiConfigurationResponse = await AppSerialPort.write(added.path, { - endpoint: "API_CONFIGURATION", - method: "GET", - body: {}, - options: { - connectionTimeOut: 30000, - }, - }) - const entitiesConfigurationResponse = await AppSerialPort.write( - added.path, - { - endpoint: "ENTITIES_CONFIGURATION", - method: "GET", - body: { - entityType: "contacts", - }, - options: { - connectionTimeOut: 30000, - }, - } - ) - console.log({ apiConfigurationResponse, entitiesConfigurationResponse }) - } - AppSerialPort.onChange((changes) => { - console.log(changes) - if (!isEmpty(changes.added)) { - void onAttach(changes.added) - } + const listenForDevicesChange = useCallback(async () => { + const onAttach = async (device: SerialPortDeviceInfo) => { + const resp1 = await AppSerialPort.request(device.path, { + endpoint: "API_CONFIGURATION", + method: "GET", + body: {}, + options: { + connectionTimeOut: 30000, + }, + }) + console.log(resp1) + + const resp2 = await AppSerialPort.request(device.path, { + endpoint: "ENTITIES_CONFIGURATION", + method: "GET", + body: { + entityType: "contacts", + }, + options: { + connectionTimeOut: 30000, + }, + }) + console.log(resp2) + + const resp3 = await AppSerialPort.request(device.path, { + endpoint: "ENTITIES_DATA", + method: "GET", + body: { + entityType: "contacts", + responseType: "json", + }, + options: { + connectionTimeOut: 30000, + }, }) + console.log(resp3) } - - // const req1 = () => - // AppSerialPort.write(ports[0].path, { - // endpoint: "API_CONFIGURATION", - // method: "GET", - // body: {}, - // options: { - // connectionTimeOut: 30000, - // }, - // }) - - // const req2 = () => - // AppSerialPort.write(ports[0].path, { - // endpoint: "ENTITIES_CONFIGURATION", - // method: "GET", - // body: { - // entityType: "contacts", - // }, - // options: { - // connectionTimeOut: 30000, - // }, - // }) - - // await AppSerialPort.write(ports[0].path, { - // endpoint: "API_CONFIGURATION", - // method: "GET", - // body: {}, - // options: { - // connectionTimeOut: 30000, - // }, - // }) - // await new Promise((resolve) => setTimeout(resolve, 100)) - // await AppSerialPort.write(ports[0].path, { - // endpoint: "ENTITIES_CONFIGURATION", - // method: "GET", - // body: { - // entityType: "contacts", - // }, - // options: { - // connectionTimeOut: 30000, - // }, - // }) - // await new Promise((resolve) => setTimeout(resolve, 100)) - // void AppSerialPort.write(ports[0].path, { - // endpoint: "ENTITIES_DATA", - // method: "GET", - // body: { - // entityType: "contacts", - // responseType: "json", - // }, - // options: { - // connectionTimeOut: 30000, - // }, - // }) - // await new Promise((resolve) => setTimeout(resolve, 100)) - // await AppSerialPort.write(ports[0].path, { - // endpoint: "ENTITIES_CONFIGURATION", - // method: "GET", - // body: { - // entityType: "contacts", - // }, - // options: { - // connectionTimeOut: 30000, - // }, - // }) - - Promise.all([ - // AppSerialPort.write(ports[0].path, { - // endpoint: "API_CONFIGURATION", - // method: "GET", - // body: {}, - // options: { - // connectionTimeOut: 30000, - // }, - // }), - // AppSerialPort.write(ports[0].path, { - // endpoint: "ENTITIES_CONFIGURATION", - // method: "GET", - // body: { - // entityType: "contacts", - // }, - // options: { - // connectionTimeOut: 30000, - // }, - // }), - // AppSerialPort.write(ports[0].path, { - // endpoint: "ENTITIES_DATA", - // method: "GET", - // body: { - // entityType: "contacts", - // responseType: "json", - // }, - // options: { - // connectionTimeOut: 30000, - // }, - // }), - ]).then((resp) => { - console.log(resp) + AppSerialPort.onDevicesChanged((changes) => { + console.log(changes) + if (!isEmpty(changes.added)) { + void onAttach(changes.added[0]) + } }) }, []) useEffect(() => { - void listenPorts() - // interval.current = setInterval(listenPorts, 2000) - // return () => clearInterval(interval.current) - }, [listenPorts]) + void listenForDevicesChange() + }, [listenForDevicesChange]) } diff --git a/apps/web/src/main.tsx b/apps/web/src/main.tsx index fa4b9586b2..be9c8511e7 100644 --- a/apps/web/src/main.tsx +++ b/apps/web/src/main.tsx @@ -3,13 +3,8 @@ * For licensing, see https://github.com/mudita/mudita-center/blob/master/LICENSE.md */ -import { StrictMode } from "react" import * as ReactDOM from "react-dom/client" import App from "./app/app" const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement) -root.render( - - - -) +root.render() diff --git a/jest.config.ts b/jest.config.ts index 57d1b455af..310c37fdbf 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -5,11 +5,6 @@ import { getJestProjectsAsync } from "@nx/jest" -void (async () => { - const p = await getJestProjectsAsync() - console.log(p) -})() - export default async () => ({ projects: await getJestProjectsAsync(), }) diff --git a/libs/app-serialport/devices/.babelrc b/libs/app-serialport/devices/.babelrc new file mode 100644 index 0000000000..cb5fe9ef74 --- /dev/null +++ b/libs/app-serialport/devices/.babelrc @@ -0,0 +1,11 @@ +{ + "presets": [ + [ + "@nx/react/babel", + { + "runtime": "automatic", + "useBuiltIns": "usage" + } + ] + ] +} diff --git a/libs/app-serialport/devices/README.md b/libs/app-serialport/devices/README.md new file mode 100644 index 0000000000..370f2bd245 --- /dev/null +++ b/libs/app-serialport/devices/README.md @@ -0,0 +1,7 @@ +# app-serialport-devices + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test app-serialport-devices` to execute the unit tests via [Vitest](https://vitest.dev/). diff --git a/libs/app-serialport/devices/eslint.config.js b/libs/app-serialport/devices/eslint.config.js new file mode 100644 index 0000000000..7af884cbd1 --- /dev/null +++ b/libs/app-serialport/devices/eslint.config.js @@ -0,0 +1,17 @@ +/** + * Copyright (c) Mudita sp. z o.o. All rights reserved. + * For licensing, see https://github.com/mudita/mudita-center/blob/master/LICENSE.md + */ + +const nx = require("@nx/eslint-plugin") +const baseConfig = require("../../../eslint.config.js") + +module.exports = [ + ...baseConfig, + ...nx.configs["flat/react"], + { + files: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"], + // Override or add rules here + rules: {}, + }, +] diff --git a/libs/app-serialport/devices/jest.config.ts b/libs/app-serialport/devices/jest.config.ts new file mode 100644 index 0000000000..61e2f7e33d --- /dev/null +++ b/libs/app-serialport/devices/jest.config.ts @@ -0,0 +1,15 @@ +/** + * Copyright (c) Mudita sp. z o.o. All rights reserved. + * For licensing, see https://github.com/mudita/mudita-center/blob/master/LICENSE.md + */ + +export default { + displayName: "app-serialport-devices", + preset: "../../../jest.preset.js", + transform: { + "^(?!.*\\.(js|jsx|ts|tsx|css|json)$)": "@nx/react/plugins/jest", + "^.+\\.[tj]sx?$": ["babel-jest", { presets: ["@nx/react/babel"] }], + }, + moduleFileExtensions: ["ts", "tsx", "js", "jsx"], + coverageDirectory: "../../../coverage/libs/app-serialport/devices", +} diff --git a/libs/app-serialport/devices/package.json b/libs/app-serialport/devices/package.json new file mode 100644 index 0000000000..eaabbab2f4 --- /dev/null +++ b/libs/app-serialport/devices/package.json @@ -0,0 +1,12 @@ +{ + "name": "app-serialport/devices", + "version": "0.0.1", + "main": "./index.js", + "types": "./index.d.ts", + "exports": { + ".": { + "import": "./index.mjs", + "require": "./index.js" + } + } +} diff --git a/libs/app-serialport/devices/project.json b/libs/app-serialport/devices/project.json new file mode 100644 index 0000000000..45f6586861 --- /dev/null +++ b/libs/app-serialport/devices/project.json @@ -0,0 +1,9 @@ +{ + "name": "app-serialport-devices", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/app-serialport/devices/src", + "projectType": "library", + "tags": ["scope:main"], + "// targets": "to see all targets run: nx show project app-serialport-devices --web", + "targets": {} +} diff --git a/libs/app-serialport/models/src/lib/serialport-ipc-events.ts b/libs/app-serialport/devices/src/index.ts similarity index 60% rename from libs/app-serialport/models/src/lib/serialport-ipc-events.ts rename to libs/app-serialport/devices/src/index.ts index 45855b8ab5..566886f647 100644 --- a/libs/app-serialport/models/src/lib/serialport-ipc-events.ts +++ b/libs/app-serialport/devices/src/index.ts @@ -3,7 +3,5 @@ * For licensing, see https://github.com/mudita/mudita-center/blob/master/LICENSE.md */ -export enum SerialportIpcEvents { - Change = "serialport:change", - Write = "serialport:write", -} \ No newline at end of file +export * from "./lib/serial-port-device" +export * from "./lib/devices" diff --git a/libs/app-serialport/devices/src/lib/api-device/api-device-request-parser.test.ts b/libs/app-serialport/devices/src/lib/api-device/api-device-request-parser.test.ts new file mode 100644 index 0000000000..6162ccc93e --- /dev/null +++ b/libs/app-serialport/devices/src/lib/api-device/api-device-request-parser.test.ts @@ -0,0 +1,32 @@ +/** + * Copyright (c) Mudita sp. z o.o. All rights reserved. + * For licensing, see https://github.com/mudita/mudita-center/blob/master/LICENSE.md + */ + +import { + apiDeviceRequestParser, + ApiDeviceRequest, +} from "./api-device-request-parser" + +describe("apiDeviceRequestParser", () => { + const request: ApiDeviceRequest = { + rid: 1, + endpoint: "API_CONFIGURATION", + method: "GET", + } + + it("returns a proper starting with #", () => { + const result = apiDeviceRequestParser(request) + expect(result[0]).toBe("#") + }) + + it("properly calculates the length of the payload", () => { + const result = apiDeviceRequestParser(request) + expect(result.slice(1, 10)).toBe("000000055") + }) + + it("properly attaches the payload", () => { + const result = apiDeviceRequestParser(request) + expect(result.slice(10)).toBe(JSON.stringify(request)) + }) +}) diff --git a/libs/app-serialport/devices/src/lib/api-device/api-device-request-parser.ts b/libs/app-serialport/devices/src/lib/api-device/api-device-request-parser.ts new file mode 100644 index 0000000000..d370287406 --- /dev/null +++ b/libs/app-serialport/devices/src/lib/api-device/api-device-request-parser.ts @@ -0,0 +1,18 @@ +/** + * Copyright (c) Mudita sp. z o.o. All rights reserved. + * For licensing, see https://github.com/mudita/mudita-center/blob/master/LICENSE.md + */ + +import { TextEncoder } from "util" +import { APIRequestData } from "app-serialport/models" + +export interface ApiDeviceRequest extends APIRequestData { + rid?: number +} + +export const apiDeviceRequestParser = (data: ApiDeviceRequest): string => { + const encoder = new TextEncoder() + const payload = JSON.stringify(data) + const header = String(encoder.encode(payload).length).padStart(9, "0") + return `#${header}${payload}` +} diff --git a/libs/app-serialport/main/src/lib/devices/api-device/api-device-response-parser.test.ts b/libs/app-serialport/devices/src/lib/api-device/api-device-response-parser.test.ts similarity index 100% rename from libs/app-serialport/main/src/lib/devices/api-device/api-device-response-parser.test.ts rename to libs/app-serialport/devices/src/lib/api-device/api-device-response-parser.test.ts diff --git a/libs/app-serialport/main/src/lib/devices/api-device/api-device-response-parser.ts b/libs/app-serialport/devices/src/lib/api-device/api-device-response-parser.ts similarity index 100% rename from libs/app-serialport/main/src/lib/devices/api-device/api-device-response-parser.ts rename to libs/app-serialport/devices/src/lib/api-device/api-device-response-parser.ts diff --git a/libs/app-serialport/devices/src/lib/api-device/serial-port-api-device.ts b/libs/app-serialport/devices/src/lib/api-device/serial-port-api-device.ts new file mode 100644 index 0000000000..5341c39711 --- /dev/null +++ b/libs/app-serialport/devices/src/lib/api-device/serial-port-api-device.ts @@ -0,0 +1,31 @@ +/** + * Copyright (c) Mudita sp. z o.o. All rights reserved. + * For licensing, see https://github.com/mudita/mudita-center/blob/master/LICENSE.md + */ + +import { ApiDeviceResponseParser } from "./api-device-response-parser" +import { + ApiDeviceRequest, + apiDeviceRequestParser, +} from "./api-device-request-parser" +import { + SerialPortDevice, + SerialPortDeviceOptions, +} from "../serial-port-device" + +export class SerialPortApiDevice extends SerialPortDevice { + static matchingVendorIds = ["0e8d", "3725"] + static matchingProductIds = ["200a", "2006", "2012", "8198", "8202", "8210"] + + constructor({ baudRate = 9600, ...options }: SerialPortDeviceOptions) { + super( + { baudRate, ...options }, + new ApiDeviceResponseParser({ matcher: /#\d{9}/g }) + ) + this.idKey = "rid" + } + + parseRequest(data: ApiDeviceRequest) { + return apiDeviceRequestParser(data) + } +} diff --git a/libs/app-serialport/devices/src/lib/devices.ts b/libs/app-serialport/devices/src/lib/devices.ts new file mode 100644 index 0000000000..c95d5453c9 --- /dev/null +++ b/libs/app-serialport/devices/src/lib/devices.ts @@ -0,0 +1,8 @@ +/** + * Copyright (c) Mudita sp. z o.o. All rights reserved. + * For licensing, see https://github.com/mudita/mudita-center/blob/master/LICENSE.md + */ + +import { SerialPortApiDevice } from "./api-device/serial-port-api-device" + +export const devices = [SerialPortApiDevice] diff --git a/libs/app-serialport/main/src/lib/devices/request-parser.interface.ts b/libs/app-serialport/devices/src/lib/serial-port-device.ts similarity index 86% rename from libs/app-serialport/main/src/lib/devices/request-parser.interface.ts rename to libs/app-serialport/devices/src/lib/serial-port-device.ts index b8dc6d60fd..aa6907a9c1 100644 --- a/libs/app-serialport/main/src/lib/devices/request-parser.interface.ts +++ b/libs/app-serialport/devices/src/lib/serial-port-device.ts @@ -11,11 +11,18 @@ import { uniqueId } from "lodash" import EventEmitter from "events" import PQueue from "p-queue" -export type SerialPortDeviceOptions = SerialPortOpenOptions & { +type BaseSerialPortDeviceOptions = SerialPortOpenOptions & { queueInterval?: number queueConcurrency?: number } +export type SerialPortDeviceOptions = Omit< + BaseSerialPortDeviceOptions, + "baudRate" +> & { + baudRate?: number +} + export class SerialPortDevice extends SerialPort { private responseEmitter = new EventEmitter() private queue: PQueue @@ -28,7 +35,7 @@ export class SerialPortDevice extends SerialPort { queueInterval = 1, queueConcurrency = 1, ...options - }: SerialPortDeviceOptions, + }: BaseSerialPortDeviceOptions, parser: Transform ) { super(options) @@ -74,4 +81,8 @@ export class SerialPortDevice extends SerialPort { }) }) } + + destroy(error?: Error): this { + return super.destroy(error) + } } diff --git a/libs/app-serialport/devices/tsconfig.json b/libs/app-serialport/devices/tsconfig.json new file mode 100644 index 0000000000..acc38d499c --- /dev/null +++ b/libs/app-serialport/devices/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "jsx": "react-jsx", + "allowJs": false, + "esModuleInterop": false, + "allowSyntheticDefaultImports": true, + "strict": true, + "types": ["vite/client"] + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ], + "extends": "../../../tsconfig.base.json" +} diff --git a/libs/app-serialport/devices/tsconfig.lib.json b/libs/app-serialport/devices/tsconfig.lib.json new file mode 100644 index 0000000000..7f6a702fa2 --- /dev/null +++ b/libs/app-serialport/devices/tsconfig.lib.json @@ -0,0 +1,24 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "types": [ + "node", + "vite/client" + ] + }, + "exclude": [ + "**/*.spec.ts", + "**/*.test.ts", + "**/*.spec.tsx", + "**/*.test.tsx", + "**/*.spec.js", + "**/*.test.js", + "**/*.spec.jsx", + "**/*.test.jsx", + "jest.config.ts", + "src/**/*.spec.ts", + "src/**/*.test.ts" + ], + "include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"] +} diff --git a/libs/app-serialport/devices/tsconfig.spec.json b/libs/app-serialport/devices/tsconfig.spec.json new file mode 100644 index 0000000000..9cd6164b51 --- /dev/null +++ b/libs/app-serialport/devices/tsconfig.spec.json @@ -0,0 +1,22 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "module": "commonjs", + "moduleResolution": "node10", + "jsx": "react-jsx", + "types": ["jest", "node"] + }, + "include": [ + "jest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.test.tsx", + "src/**/*.spec.tsx", + "src/**/*.test.js", + "src/**/*.spec.js", + "src/**/*.test.jsx", + "src/**/*.spec.jsx", + "src/**/*.d.ts" + ] +} diff --git a/libs/app-serialport/devices/vite.config.ts b/libs/app-serialport/devices/vite.config.ts new file mode 100644 index 0000000000..08dd18b4a9 --- /dev/null +++ b/libs/app-serialport/devices/vite.config.ts @@ -0,0 +1,53 @@ +/** + * Copyright (c) Mudita sp. z o.o. All rights reserved. + * For licensing, see https://github.com/mudita/mudita-center/blob/master/LICENSE.md + */ + +/// +import { defineConfig } from "vite" +import react from "@vitejs/plugin-react" +import dts from "vite-plugin-dts" +import * as path from "path" +import { nxViteTsPaths } from "@nx/vite/plugins/nx-tsconfig-paths.plugin" +import { nxCopyAssetsPlugin } from "@nx/vite/plugins/nx-copy-assets.plugin" + +export default defineConfig({ + root: __dirname, + cacheDir: "../../../node_modules/.vite/libs/app-serialport/devices", + plugins: [ + react(), + nxViteTsPaths(), + nxCopyAssetsPlugin(["*.md"]), + dts({ + entryRoot: "src", + tsconfigPath: path.join(__dirname, "tsconfig.lib.json"), + }), + ], + // Uncomment this if you are using workers. + // worker: { + // plugins: [ nxViteTsPaths() ], + // }, + // Configuration for building your library. + // See: https://vitejs.dev/guide/build.html#library-mode + build: { + outDir: "../../../dist/libs/app-serialport/devices", + emptyOutDir: true, + reportCompressedSize: true, + commonjsOptions: { + transformMixedEsModules: true, + }, + lib: { + // Could also be a dictionary or array of multiple entry points. + entry: "src/index.ts", + name: "app-serialport-devices", + fileName: "index", + // Change this to the formats you want to support. + // Don't forget to update your package.json as well. + formats: ["es", "cjs"], + }, + rollupOptions: { + // External packages that should not be bundled into your library. + external: ["react", "react-dom", "react/jsx-runtime"], + }, + }, +}) diff --git a/libs/app-serialport/main/.babelrc b/libs/app-serialport/main/.babelrc index ef4889c1ab..cb5fe9ef74 100644 --- a/libs/app-serialport/main/.babelrc +++ b/libs/app-serialport/main/.babelrc @@ -7,14 +7,5 @@ "useBuiltIns": "usage" } ] - ], - "plugins": [ - [ - "styled-components", - { - "pure": true, - "ssr": true - } - ] ] } diff --git a/libs/app-serialport/main/src/lib/app-serial-port.ts b/libs/app-serialport/main/src/lib/app-serial-port.ts index 9dd0e0a2f1..ae2d43af7d 100644 --- a/libs/app-serialport/main/src/lib/app-serial-port.ts +++ b/libs/app-serialport/main/src/lib/app-serial-port.ts @@ -5,31 +5,40 @@ import { SerialPort } from "serialport" import { PortInfo } from "@serialport/bindings-interface" -import { SerialPortApiDevice } from "./devices/api-device/serial-port-api-device" -import { SerialPortDevice } from "./devices/request-parser.interface" -import { APIRequestData, ChangedDevices, Device } from "app-serialport/models" import EventEmitter from "events" +import { + APIRequestData, + SerialPortChangedDevices, + SerialPortDeviceInfo, +} from "app-serialport/models" +import { devices, SerialPortDevice } from "app-serialport/devices" + +type DevicesChangeCallback = (data: SerialPortChangedDevices) => void +type Path = string +enum SerialPortEvents { + DevicesChanged = "devicesChanged", +} -type DevicesChangeCallback = (data: ChangedDevices) => void - -const isKnownDevice = (port: PortInfo): port is Device => { +const isKnownDevice = (port: PortInfo): port is SerialPortDeviceInfo => { return port.productId !== undefined && port.vendorId !== undefined } export class AppSerialPort { - private readonly instances = new Map() - supportedDevices = [SerialPortApiDevice] - attachedDevices: Device[] = [] - eventEmitter = new EventEmitter() + private readonly instances = new Map() + private readonly supportedDevices = devices + private readonly eventEmitter = new EventEmitter() + private currentDevices: SerialPortDeviceInfo[] = [] + private addedDevices: SerialPortDeviceInfo[] = [] + private removedDevices: SerialPortDeviceInfo[] = [] constructor() { - void this.checkForPortChanges() + void this.detectChanges() setInterval(() => { - void this.checkForPortChanges() - }, 2000) + void this.detectChanges() + }, 3000) } - private async checkForPortChanges() { + private async detectChanges() { const currentDevices = (await SerialPort.list()).filter((port) => { if (!isKnownDevice(port)) { return false @@ -40,63 +49,80 @@ export class AppSerialPort { device.matchingProductIds.includes(port.productId) ) }) - }) as Device[] + }) as SerialPortDeviceInfo[] + + this.currentDevices + .filter((device) => { + return !currentDevices.find( + (newDevice) => newDevice.path === device.path + ) + }) + .forEach((device) => { + this.removedDevices.push(device) + }) - const removedDevices = this.attachedDevices.filter((device) => { - return !currentDevices.find((newDevice) => newDevice.path === device.path) + currentDevices.forEach((device) => { + if (!this.instances.has(device.path)) { + this.addedDevices.push(device) + } }) - this.attachedDevices = currentDevices + this.currentDevices = currentDevices + this.applyChanges() + } - removedDevices.forEach((device) => { - this.removeInstance(device) + private applyChanges() { + this.removedDevices.forEach((device) => { + this.removeInstance(device.path) }) - - currentDevices.forEach((device) => { + this.currentDevices.forEach((device) => { this.ensureInstance(device.path) }) + + if (this.addedDevices.length > 0 || this.removedDevices.length > 0) { + this.eventEmitter.emit(SerialPortEvents.DevicesChanged, { + removed: this.removedDevices, + added: this.addedDevices, + all: this.currentDevices, + }) + } + + this.addedDevices = [] + this.removedDevices = [] } - private instanceExists(path: string) { + private instanceExists(path: Path) { return this.instances.has(path) } - private createInstance(path: string) { - const instance = this.getInstanceForDevice(path) - if (instance) { - const serialPort = new instance({ path, baudRate: 9600 }) + private createInstance(path: Path) { + const SerialPortInstance = this.getDeviceSerialPortInstance(path) + if (SerialPortInstance) { + const serialPort = new SerialPortInstance({ path }) this.instances.set(path, serialPort) - const change: Pick = { - added: this.getDeviceByPath(path), - } - this.eventEmitter.emit("devicesChanged", change) } } - private removeInstance(device: Device) { - const serialPort = this.instances.get(device.path) - if (serialPort) { - serialPort.destroy() - this.instances.delete(device.path) - const change: Pick = { - removed: device, - } - this.eventEmitter.emit("devicesChanged", change) + private ensureInstance(path: Path) { + if (!this.instanceExists(path)) { + this.createInstance(path) } + return this.instances.get(path) as SerialPortDevice } - private ensureInstance(path: string) { - if (!this.instanceExists(path)) { - this.createInstance(path) + private removeInstance(path: Path) { + const serialPort = this.instances.get(path) + if (serialPort) { + serialPort.destroy() + this.instances.delete(path) } - return this.instances.get(path) } - private getDeviceByPath(path: string) { - return this.attachedDevices.find((device) => device.path === path) + private getDeviceByPath(path: Path) { + return this.currentDevices.find((device) => device.path === path) } - private getInstanceForDevice(path: string) { + private getDeviceSerialPortInstance(path: Path) { const port = this.getDeviceByPath(path) if (!port) { return @@ -109,24 +135,21 @@ export class AppSerialPort { }) } - changeBaudRate(path: string, baudRate: number) { + changeBaudRate(path: Path, baudRate: number) { const serialPort = this.ensureInstance(path) serialPort?.update({ baudRate }) } - async request(path: string, data: APIRequestData) { - return this.ensureInstance(path)?.request(data) - } - onDevicesChange(callback: DevicesChangeCallback) { this.eventEmitter.on( - "devicesChanged", - (changes: Omit) => { - callback({ - all: this.attachedDevices, - ...changes, - }) + SerialPortEvents.DevicesChanged, + (changes: SerialPortChangedDevices) => { + callback(changes) } ) } + + async request(path: Path, data: APIRequestData) { + return this.ensureInstance(path)?.request(data) + } } diff --git a/libs/app-serialport/main/src/lib/devices/api-device/serial-port-api-device.ts b/libs/app-serialport/main/src/lib/devices/api-device/serial-port-api-device.ts deleted file mode 100644 index 1504e9c0e1..0000000000 --- a/libs/app-serialport/main/src/lib/devices/api-device/serial-port-api-device.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright (c) Mudita sp. z o.o. All rights reserved. - * For licensing, see https://github.com/mudita/mudita-center/blob/master/LICENSE.md - */ - -import { APIRequestData } from "app-serialport/models" -import { TextEncoder } from "util" -import { ApiDeviceResponseParser } from "./api-device-response-parser" -import { - SerialPortDevice, - SerialPortDeviceOptions, -} from "../request-parser.interface" - -interface Data extends APIRequestData { - rid?: number -} - -export class SerialPortApiDevice extends SerialPortDevice { - static matchingVendorIds = ["0e8d"] - static matchingProductIds = ["200a", "2006"] - - constructor(options: SerialPortDeviceOptions) { - super(options, new ApiDeviceResponseParser({ matcher: /#\d{9}/g })) - this.idKey = "rid" - } - - parseRequest(data: Data) { - const encoder = new TextEncoder() - const payload = JSON.stringify(data) - const header = String(encoder.encode(payload).length).padStart(9, "0") - return `#${header}${payload}` - } -} diff --git a/libs/app-serialport/main/src/lib/init-serialport.ts b/libs/app-serialport/main/src/lib/init-serialport.ts index 143182a19f..b8d43f1d1c 100644 --- a/libs/app-serialport/main/src/lib/init-serialport.ts +++ b/libs/app-serialport/main/src/lib/init-serialport.ts @@ -4,19 +4,26 @@ */ import { IpcMain, WebContents } from "electron" -import { APIRequestData, SerialportIpcEvents } from "app-serialport/models" +import { APIRequestData, SerialPortIpcEvents } from "app-serialport/models" import { AppSerialPort } from "./app-serial-port" +let serialport: AppSerialPort | null = null + export const initSerialPort = (ipcMain: IpcMain, webContents: WebContents) => { - const serialport = new AppSerialPort() + if (!serialport) { + serialport = new AppSerialPort() - serialport.onDevicesChange((data) => { - webContents.send(SerialportIpcEvents.Change, data) - }) - ipcMain.handle( - SerialportIpcEvents.Write, - (_, path: string, data: APIRequestData) => { - return serialport.request(path, data) + if (serialport) { + serialport.onDevicesChange((data) => { + webContents.send(SerialPortIpcEvents.DevicesChanged, data) + }) + ipcMain.removeHandler(SerialPortIpcEvents.Request) + ipcMain.handle( + SerialPortIpcEvents.Request, + (_, path: string, data: APIRequestData) => { + return (serialport as AppSerialPort).request(path, data) + } + ) } - ) + } } diff --git a/libs/app-serialport/main/src/lib/serialport-ipc.types.ts b/libs/app-serialport/main/src/lib/serialport-ipc.types.ts index 590e09d259..299b877c2f 100644 --- a/libs/app-serialport/main/src/lib/serialport-ipc.types.ts +++ b/libs/app-serialport/main/src/lib/serialport-ipc.types.ts @@ -3,16 +3,16 @@ * For licensing, see https://github.com/mudita/mudita-center/blob/master/LICENSE.md */ -import { ChangedDevices, SerialportIpcEvents } from "app-serialport/models" +import { SerialPortChangedDevices, SerialPortIpcEvents } from "app-serialport/models" import { IpcRenderer, IpcRendererEvent } from "electron" export interface IpcAppSerialport extends IpcRenderer { on( - channel: SerialportIpcEvents.Change, - callback: (event: IpcRendererEvent, data: ChangedDevices) => void + channel: SerialPortIpcEvents.DevicesChanged, + callback: (event: IpcRendererEvent, data: SerialPortChangedDevices) => void ): this invoke( - channel: SerialportIpcEvents.Write, + channel: SerialPortIpcEvents.Request, path: string, data: string ): Promise diff --git a/libs/app-serialport/main/tsconfig.lib.json b/libs/app-serialport/main/tsconfig.lib.json index f1d53c724e..7f6a702fa2 100644 --- a/libs/app-serialport/main/tsconfig.lib.json +++ b/libs/app-serialport/main/tsconfig.lib.json @@ -4,8 +4,6 @@ "outDir": "../../../dist/out-tsc", "types": [ "node", - "@nx/react/typings/cssmodule.d.ts", - "@nx/react/typings/image.d.ts", "vite/client" ] }, diff --git a/libs/app-serialport/models/.babelrc b/libs/app-serialport/models/.babelrc index ef4889c1ab..cb5fe9ef74 100644 --- a/libs/app-serialport/models/.babelrc +++ b/libs/app-serialport/models/.babelrc @@ -7,14 +7,5 @@ "useBuiltIns": "usage" } ] - ], - "plugins": [ - [ - "styled-components", - { - "pure": true, - "ssr": true - } - ] ] } diff --git a/libs/app-serialport/models/src/index.ts b/libs/app-serialport/models/src/index.ts index 58b836e508..3b2757b8d2 100644 --- a/libs/app-serialport/models/src/index.ts +++ b/libs/app-serialport/models/src/index.ts @@ -3,6 +3,6 @@ * For licensing, see https://github.com/mudita/mudita-center/blob/master/LICENSE.md */ -export * from "./lib/serialport-ipc-events" -export * from "./lib/serialport-requests" -export * from "./lib/serial-port-device" \ No newline at end of file +export * from "./lib/serial-port-ipc-events" +export * from "./lib/serial-port-requests" +export * from "./lib/serial-port-device-info" \ No newline at end of file diff --git a/libs/app-serialport/models/src/lib/serial-port-device.ts b/libs/app-serialport/models/src/lib/serial-port-device-info.ts similarity index 55% rename from libs/app-serialport/models/src/lib/serial-port-device.ts rename to libs/app-serialport/models/src/lib/serial-port-device-info.ts index 8357fdccf1..4ea3de5772 100644 --- a/libs/app-serialport/models/src/lib/serial-port-device.ts +++ b/libs/app-serialport/models/src/lib/serial-port-device-info.ts @@ -5,13 +5,13 @@ import { PortInfo } from "@serialport/bindings-interface" -export interface Device extends PortInfo { +export interface SerialPortDeviceInfo extends PortInfo { productId: string vendorId: string } -export interface ChangedDevices { - all: Device[] - added?: Device - removed?: Device -} \ No newline at end of file +export interface SerialPortChangedDevices { + all: SerialPortDeviceInfo[] + added: SerialPortDeviceInfo[] + removed: SerialPortDeviceInfo[] +} diff --git a/libs/app-serialport/models/src/lib/serial-port-ipc-events.ts b/libs/app-serialport/models/src/lib/serial-port-ipc-events.ts new file mode 100644 index 0000000000..2c4158e65b --- /dev/null +++ b/libs/app-serialport/models/src/lib/serial-port-ipc-events.ts @@ -0,0 +1,9 @@ +/** + * Copyright (c) Mudita sp. z o.o. All rights reserved. + * For licensing, see https://github.com/mudita/mudita-center/blob/master/LICENSE.md + */ + +export enum SerialPortIpcEvents { + DevicesChanged = "serialport:devices-changed", + Request = "serialport:request", +} \ No newline at end of file diff --git a/libs/app-serialport/models/src/lib/serialport-requests.ts b/libs/app-serialport/models/src/lib/serial-port-requests.ts similarity index 100% rename from libs/app-serialport/models/src/lib/serialport-requests.ts rename to libs/app-serialport/models/src/lib/serial-port-requests.ts diff --git a/libs/app-serialport/models/tsconfig.lib.json b/libs/app-serialport/models/tsconfig.lib.json index f1d53c724e..7f6a702fa2 100644 --- a/libs/app-serialport/models/tsconfig.lib.json +++ b/libs/app-serialport/models/tsconfig.lib.json @@ -4,8 +4,6 @@ "outDir": "../../../dist/out-tsc", "types": [ "node", - "@nx/react/typings/cssmodule.d.ts", - "@nx/react/typings/image.d.ts", "vite/client" ] }, diff --git a/libs/app-serialport/renderer/src/lib/app-serial-port.ts b/libs/app-serialport/renderer/src/lib/app-serial-port.ts index 9266638d5d..ceb7c0997a 100644 --- a/libs/app-serialport/renderer/src/lib/app-serial-port.ts +++ b/libs/app-serialport/renderer/src/lib/app-serial-port.ts @@ -5,22 +5,22 @@ import { APIRequestData, - ChangedDevices, - SerialportIpcEvents, + SerialPortChangedDevices, + SerialPortIpcEvents, } from "app-serialport/models" export const AppSerialPort = { - onChange: (callback: (changes: ChangedDevices) => void) => { + onDevicesChanged: (callback: (changes: SerialPortChangedDevices) => void) => { return window.electron.ipcRenderer.on( - SerialportIpcEvents.Change, + SerialPortIpcEvents.DevicesChanged, (_, changes) => { callback(changes) } ) }, - write: (path: string, data: APIRequestData) => { + request: (path: string, data: APIRequestData) => { return window.electron.ipcRenderer.invoke( - SerialportIpcEvents.Write, + SerialPortIpcEvents.Request, path, data ) diff --git a/libs/app-sql/main/src/lib/contacts.db b/libs/app-sql/main/src/lib/contacts.db deleted file mode 100644 index 84295c6f02..0000000000 Binary files a/libs/app-sql/main/src/lib/contacts.db and /dev/null differ diff --git a/libs/app-sql/main/src/lib/init-sql.ts b/libs/app-sql/main/src/lib/init-sql.ts index ebc18914b3..ee41613386 100644 --- a/libs/app-sql/main/src/lib/init-sql.ts +++ b/libs/app-sql/main/src/lib/init-sql.ts @@ -7,19 +7,24 @@ import { IpcMain } from "electron" import { SqlIpcEvents } from "app-sql/models" import { AppSql } from "./app-sql" -export const initSql = (ipcMain: IpcMain) => { - const sql = new AppSql() +let sql: AppSql | null = null - ipcMain.handle( - SqlIpcEvents.RunQuery, - async (_, name: string, query: string) => { - await sql.runQuery(name, query) - } - ) - ipcMain.handle( - SqlIpcEvents.ExecuteQuery, - (_, name: string, query: string) => { - return sql.executeQuery(name, query) - } - ) +export const initSql = (ipcMain: IpcMain) => { + if (!sql) { + sql = new AppSql() + ipcMain.removeHandler(SqlIpcEvents.RunQuery) + ipcMain.handle( + SqlIpcEvents.RunQuery, + async (_, name: string, query: string) => { + await (sql as AppSql).runQuery(name, query) + } + ) + ipcMain.removeHandler(SqlIpcEvents.ExecuteQuery) + ipcMain.handle( + SqlIpcEvents.ExecuteQuery, + (_, name: string, query: string) => { + return (sql as AppSql).executeQuery(name, query) + } + ) + } } diff --git a/tsconfig.base.json b/tsconfig.base.json index ac2b05520d..2af8d84987 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -16,6 +16,7 @@ "skipDefaultLibCheck": true, "baseUrl": ".", "paths": { + "app-serialport/devices": ["libs/app-serialport/devices/src/index.ts"], "app-serialport/main": ["libs/app-serialport/main/src/index.ts"], "app-serialport/models": ["libs/app-serialport/models/src/index.ts"], "app-serialport/renderer": ["libs/app-serialport/renderer/src/index.ts"],