From fc19721a3a70635460b051782aa35c7a332c4d74 Mon Sep 17 00:00:00 2001 From: elsoazemelet Date: Wed, 4 Sep 2024 16:33:20 +0200 Subject: [PATCH 01/42] Daily push --- .../main/grid-layout/GridLayout.svelte | 2 +- .../configuration/Configuration.store.js | 46 ++++++------------- .../configuration/configuration-actions.ts | 4 +- src/renderer/runtime/runtime.store.js | 11 +++-- 4 files changed, 23 insertions(+), 40 deletions(-) diff --git a/src/renderer/main/grid-layout/GridLayout.svelte b/src/renderer/main/grid-layout/GridLayout.svelte index d02f29ce1..8d0ea3ac0 100644 --- a/src/renderer/main/grid-layout/GridLayout.svelte +++ b/src/renderer/main/grid-layout/GridLayout.svelte @@ -122,7 +122,7 @@ Math.abs(device.dy - (max_y > 0 ? max_y : 0)), ]; - const obj = structuredClone(device); + const obj = device; obj.fly_x_direction = connection_right - connection_left; obj.fly_y_direction = connection_top - connection_bottom; obj.type = rt[i].id.substr(0, 4); diff --git a/src/renderer/main/panels/configuration/Configuration.store.js b/src/renderer/main/panels/configuration/Configuration.store.js index aff100957..82980688e 100644 --- a/src/renderer/main/panels/configuration/Configuration.store.js +++ b/src/renderer/main/panels/configuration/Configuration.store.js @@ -171,19 +171,19 @@ export class ConfigList extends Array { } target - .getConfig() - .then((script) => { + .getActions() + .then((actions) => { //LOADED and SYNCED - const list = ConfigList.createFromActionString(script); + const list = ConfigList.createFromActions(actions); resolve(list); }) .catch((e) => reject(e)); }); } - static createFromActionString(string) { + static createFromActions(actions) { const config = new ConfigList(); - config.#Init(string); + config.#Init(actions); return config; } @@ -228,34 +228,13 @@ export class ConfigList extends Array { }); } - #Init(actionString) { - //Parse actionString - //TODO: do rawLuas format checking during parsing - - let configList = []; - // get rid of new line, enter - actionString = actionString.replace(/[\n\r]+/g, ""); - // get rid of more than 2 spaces - actionString = actionString.replace(/\s{2,10}/g, " "); - // remove lua opening and closing characters - // this function is used for both parsing full config (long complete lua) and individiual actions lua - if (actionString.startsWith("")[0]; - } - // split by meta comments - configList = actionString.split(/(--\[\[@\w+(?:#|\w|\s)*\]\])/); - - configList = configList.slice(1); - for (var i = 0; i < configList.length; i += 2) { - const split = configList[i] - .match(/--\[\[@(.*)\]\]/) - ?.at(1) - .split(/#(.*)/); + #Init(actions) { + console.log(actions); + for (const action of actions) { const obj = new ConfigObject({ - //Extract short + name, e.g.: '--[[@gms#name]]' => 'gms' - short: split[0], - script: configList[i + 1].trim(), - name: split.length > 1 ? split[1] : undefined, + short: action.short, + script: action.script, + name: action.name, }); super.push(obj); } @@ -380,6 +359,7 @@ export class ConfigTarget { getEvent() { const element = this.getElement(); const event = element.events.find((e) => e.type == this.eventType); + console.log(event); return event; } @@ -405,7 +385,7 @@ export class ConfigTarget { return device; } - getConfig() { + getActions() { return new Promise((resolve, reject) => { const event = this.getEvent(); diff --git a/src/renderer/main/panels/configuration/configuration-actions.ts b/src/renderer/main/panels/configuration/configuration-actions.ts index 4a2a1211e..11b552c21 100644 --- a/src/renderer/main/panels/configuration/configuration-actions.ts +++ b/src/renderer/main/panels/configuration/configuration-actions.ts @@ -204,7 +204,7 @@ export async function discardElement({ dx, dy, page, element }) { eventType: eventtype, page: current.page, }); - const list = ConfigList.createFromActionString(stored); + const list = ConfigList.createFromActions(stored); promises.push(list.sendTo({ target })); } @@ -427,7 +427,7 @@ export function clearElement( const defaultConfig = events.find( (e: any) => e.value === eventtype )?.config; - const list = ConfigList.createFromActionString(defaultConfig); + const list = ConfigList.createFromActions(defaultConfig); promises.push(list.sendTo({ target: target })); } diff --git a/src/renderer/runtime/runtime.store.js b/src/renderer/runtime/runtime.store.js index 85e6bf4a9..57b3cf727 100644 --- a/src/renderer/runtime/runtime.store.js +++ b/src/renderer/runtime/runtime.store.js @@ -14,6 +14,7 @@ import { appSettings } from "./app-helper.store"; import { add_datapoint } from "../serialport/message-stream.store.js"; import { modal } from "../main/modals/modal.store"; import { ProtectedStore } from "./smart-store.store.ts"; +import { createActionsFromString } from "./runtime"; const setIntervalAsync = (fn, ms) => { fn().then(() => { @@ -340,7 +341,7 @@ function create_user_input() { export const user_input = create_user_input(); function create_runtime() { - const _runtime = new ProtectedStore([]); + const _runtime = writable([]); const findUpdateDestEvent = (_runtime, dx, dy, page, element, event) => { let _event = undefined; @@ -694,9 +695,10 @@ function create_runtime() { _runtime.update((_runtime) => { let dest = findUpdateDestEvent(_runtime, dx, dy, page, element, event); if (dest) { - dest.config = actionString; + dest.config = createActionsFromString(actionString); + if (typeof dest.stored === "undefined") { - dest.stored = actionString; + dest.stored = dest.config; } } return _runtime; @@ -709,8 +711,9 @@ function create_runtime() { let dest = findUpdateDestEvent(rt, dx, dy, page, element, event); if (dest) { + const script = ` e.toLua()).join("")} ?>`; instructions - .sendConfigToGrid(dx, dy, page, element, event, dest.config) + .sendConfigToGrid(dx, dy, page, element, event, script) .then((desc) => { resolve(); }) From 799516fbb022ceef4fbf10d16c91cfadab211cf8 Mon Sep 17 00:00:00 2001 From: elsoazemelet Date: Thu, 5 Sep 2024 11:03:29 +0200 Subject: [PATCH 02/42] Daily push --- .../main/user-interface/ActiveChanges.svelte | 2 + src/renderer/runtime/runtime.ts | 255 ++++++++++++++++++ 2 files changed, 257 insertions(+) create mode 100644 src/renderer/runtime/runtime.ts diff --git a/src/renderer/main/user-interface/ActiveChanges.svelte b/src/renderer/main/user-interface/ActiveChanges.svelte index 8df16e14b..d6e551417 100644 --- a/src/renderer/main/user-interface/ActiveChanges.svelte +++ b/src/renderer/main/user-interface/ActiveChanges.svelte @@ -145,6 +145,8 @@ }); } } + + $: console.log($runtime) implements Writable { + protected _internal: Writable; + private _parent: RuntimeNode; + + subscribe( + run: Subscriber, + invalidate?: (value?: T) => void + ): Unsubscriber { + return this._internal.subscribe(run, invalidate); + } + + set(value: T) { + this._internal.set(value); + this.notifyParent(); + } + + update(updater: Updater) { + this._internal.update(updater); + this.notifyParent(); + } + + constructor(parent: RuntimeNode, value?: T) { + this._parent = parent; + this._internal = writable(value); + } + + get parent(): RuntimeNode { + return this._parent; + } + + protected notify() { + //console.log("ME NOTIFIED:", this); + this._internal.update((s) => s); + this.notifyParent(); + } + + protected syncWithGrid() { + console.log("SYNCING"); + } + + protected notifyParent() { + if (!this._parent) { + return; + } + + console.log("NOTIFY", this._parent); + this._parent.notify(); + } + + // Generalized getter + protected getField(key: K): T[K] { + const data = get(this._internal); + return data[key]; + } + + // Generalized setter + protected setField(key: K, value: T[K]) { + this.update((store) => { + store[key] = value; + return store; + }); + this.notifyParent(); + } +} + +export type ActionData = { + short: string; + script: string; + name: string; +}; + +export class GridAction extends RuntimeNode { + constructor(parent: GridEvent, data?: ActionData) { + super(parent, data); + } + + // Getters + get script() { + return this.getField("script"); + } + + get short() { + return this.getField("short"); + } + + get name() { + return this.getField("name"); + } + + // Setters + set script(value: string) { + this.setField("script", value); + } + + set short(value: string) { + this.setField("short", value); + } + + set name(value: string) { + this.setField("name", value); + } + + toLua() { + const namePostfix = typeof this.name !== "undefined" ? `#${this.name}` : ""; + return `--[[@${this.short}${namePostfix}]] ${this.script}`; + } +} + +export type EventData = { + config: Array; + stored: Array; + type: number; +}; + +export class GridEvent extends RuntimeNode { + constructor(parent: GridEvent, data?: ActionData) { + super(parent, data); + } + + // Getters + + // Setters +} + +export type ElementData = { + elementIndex: number; + events: Array; + name: string; + type: ElementType; +}; + +export class GridElement extends RuntimeNode { + constructor(parent: GridEvent, data?: ElementData) { + super(parent, data); + } + + // Getters + + // Setters +} + +export type PageData = { + pageNumber: number; + control_elements: Array; +}; + +export class GridPage extends RuntimeNode { + constructor(parent: GridModule, data?: PageData) { + super(parent, data); + } + + // Getters + + // Setters +} + +type FirmwareVersion = { + major: number | undefined; + minor: number | undefined; + patch: number | undefined; +}; + +type Direction = { + dx: number; + dy: number; +}; + +type DirectionMap = { + bot: Direction; + left: Direction; + right: Direction; + top: Direction; +}; + +export type ModuleData = { + alive: number; + architecture: Architecture; + dx: number; + dy: number; + fwMismatch: boolean; + fwVersion: FirmwareVersion; + gridX: number; + gridY: number; + id: string; + map: DirectionMap; + portstate: any; + rot: number; + type: string; + pages: Array; +}; + +export class GridModule extends RuntimeNode { + constructor(parent: GridRuntime, data?: ModuleData) { + super(parent, data); + } + + // Getters + + // Setters +} + +export class GridRuntime extends RuntimeNode { + constructor() { + super(undefined, undefined); + } + + // Getters + + // Setters +} + +//TODO: helper function to refactor out +export function createActionsFromString(script: string) { + const result: GridAction[] = []; + let configList: string[] = []; + let actionString = script; + // get rid of new line, enter + actionString = actionString.replace(/[\n\r]+/g, ""); + // get rid of more than 2 spaces + actionString = actionString.replace(/\s{2,10}/g, " "); + // remove lua opening and closing characters + // this function is used for both parsing full config (long complete lua) and individiual actions lua + if (actionString.startsWith("")[0]; + } + // split by meta comments + configList = actionString.split(/(--\[\[@\w+(?:#|\w|\s)*\]\])/); + + configList = configList.slice(1); + for (var i = 0; i < configList.length; i += 2) { + const split = configList[i] + .match(/--\[\[@(.*)\]\]/) + ?.at(1) + .split(/#(.*)/); + const obj = new GridAction(undefined, { + //Extract short + name, e.g.: '--[[@gms#name]]' => 'gms' + short: split[0], + script: configList[i + 1].trim(), + name: split.length > 1 ? split[1] : undefined, + }); + result.push(obj); + } + + return result; +} From 4b302e09d149415a4f215b470d5ff7064d2fcf0a Mon Sep 17 00:00:00 2001 From: elsoazemelet Date: Fri, 6 Sep 2024 17:38:19 +0200 Subject: [PATCH 03/42] Daily push --- src/renderer/config-blocks/CodeBlock.svelte | 1 + src/renderer/config-blocks/GamePadAxis.svelte | 2 +- src/renderer/config-blocks/MidiNRPN.svelte | 2 +- src/renderer/config-blocks/RawCode.svelte | 2 + src/renderer/main/FirmwareCheck.svelte | 5 +- src/renderer/main/MiddlePanelContainer.svelte | 7 +- .../main/grid-layout/GridLayout.svelte | 19 +- .../grid-layout/grid-modules/Device.svelte | 2 +- .../grid-modules/devices/TEK1.svelte | 4 +- .../grid-modules/devices/TEK2.svelte | 4 +- .../overlays/ControlNameOverlay.svelte | 2 +- .../overlays/PresetLoadOverlay.svelte | 7 +- .../overlays/ProfileLoadOverlay.svelte | 2 +- .../underlays/ActiveChanges.svelte | 7 +- .../underlays/ElementSelection.svelte | 2 +- .../underlays/ModuleBorder.svelte | 2 +- .../main/modals/AddVirtualModule.svelte | 6 +- .../configuration/Configuration.store.js | 16 +- .../panels/configuration/Configuration.svelte | 10 +- .../ElementSelectionPanel.svelte | 4 +- .../panels/configuration/EventPanel.svelte | 105 +- .../components/ActionPicker.svelte | 2 +- .../configuration/components/Pages.svelte | 2 +- .../configuration/components/Toolbar.svelte | 2 +- .../configuration/configuration-actions.ts | 23 +- .../main/panels/packages/Packages.svelte | 2 +- .../panels/preferences/Preferences.svelte | 4 +- .../panels/profileCloud/ProfileCloud.svelte | 13 +- .../main/user-interface/ActiveChanges.svelte | 4 +- .../user-interface/StickyContainer.svelte | 9 +- src/renderer/runtime/engine.store.ts | 2 +- src/renderer/runtime/runtime.store.js | 1289 ----------------- src/renderer/runtime/runtime.store.ts | 460 ++++++ src/renderer/runtime/runtime.ts | 1009 ++++++++++++- src/renderer/serialport/instructions.ts | 2 +- 35 files changed, 1565 insertions(+), 1469 deletions(-) delete mode 100644 src/renderer/runtime/runtime.store.js create mode 100644 src/renderer/runtime/runtime.store.ts diff --git a/src/renderer/config-blocks/CodeBlock.svelte b/src/renderer/config-blocks/CodeBlock.svelte index e49d97633..71d56bd9b 100644 --- a/src/renderer/config-blocks/CodeBlock.svelte +++ b/src/renderer/config-blocks/CodeBlock.svelte @@ -132,6 +132,7 @@ } function open_monaco() { + console.log(config); $monaco_store = { config: config.makeCopy(), index: index }; $monaco_elementtype = access_tree.elementtype; modal.show({ diff --git a/src/renderer/config-blocks/GamePadAxis.svelte b/src/renderer/config-blocks/GamePadAxis.svelte index ac7727e3c..7a35ddaff 100644 --- a/src/renderer/config-blocks/GamePadAxis.svelte +++ b/src/renderer/config-blocks/GamePadAxis.svelte @@ -57,7 +57,7 @@ import { GridScript } from "@intechstudio/grid-protocol"; import { Script } from "./_script_parsers.js"; - import { LocalDefinitions } from "../runtime/runtime.store.js"; + import { LocalDefinitions } from "../runtime/runtime.store"; import { AtomicSuggestions } from "@intechstudio/grid-uikit"; import { configManager } from "../main/panels/configuration/Configuration.store.js"; diff --git a/src/renderer/config-blocks/MidiNRPN.svelte b/src/renderer/config-blocks/MidiNRPN.svelte index c34ae5424..7bfc27b0e 100644 --- a/src/renderer/config-blocks/MidiNRPN.svelte +++ b/src/renderer/config-blocks/MidiNRPN.svelte @@ -49,7 +49,7 @@ import { GridScript } from "@intechstudio/grid-protocol"; import { AtomicSuggestions } from "@intechstudio/grid-uikit"; import { configManager } from "../main/panels/configuration/Configuration.store.js"; - import { LocalDefinitions } from "../runtime/runtime.store.js"; + import { LocalDefinitions } from "../runtime/runtime.store"; import { Validator } from "./_validators.js"; diff --git a/src/renderer/config-blocks/RawCode.svelte b/src/renderer/config-blocks/RawCode.svelte index 2138a89d0..519e5640d 100644 --- a/src/renderer/config-blocks/RawCode.svelte +++ b/src/renderer/config-blocks/RawCode.svelte @@ -93,6 +93,8 @@ script: config.script, }); } + + $: console.log(config); STATE 0 (Close notification) // check for parsed modules $: { - const store = $runtime; let firmwareMismatchFound = false; - if (store.length === 0) { + if ($runtime.modules.length === 0) { startBootloaderCheck(); if ($appSettings.firmwareNotificationState == 1) { $appSettings.firmwareNotificationState = 2; @@ -61,7 +60,7 @@ STATE 6 | Error | Button -> STATE 0 (Close notification) } // check modules for firmware mismatch - store.forEach((device) => { + runtime.modules.forEach((device) => { if ($appSettings.firmwareNotificationState == 6) { $appSettings.firmwareNotificationState = 0; uploadProgressText = ""; diff --git a/src/renderer/main/MiddlePanelContainer.svelte b/src/renderer/main/MiddlePanelContainer.svelte index c41225f20..e9ed5e405 100644 --- a/src/renderer/main/MiddlePanelContainer.svelte +++ b/src/renderer/main/MiddlePanelContainer.svelte @@ -71,7 +71,7 @@ $: { if ( $pendingActions.length > 0 && - $runtime.length > 0 && + $runtime.modules.length > 0 && typeof moduleHangingTimeout === "undefined" ) { moduleHangingTimeout = setTimeout(() => { @@ -115,13 +115,14 @@
- {#if $runtime.length == 0 && $appSettings.firmwareNotificationState === 0} + {#if $runtime.modules.length == 0 && $appSettings.firmwareNotificationState === 0}
e.dx)); - const min_y = Math.min(...rt.map((e) => e.dy)); - const max_x = Math.max(...rt.map((e) => e.dx)); - const max_y = Math.max(...rt.map((e) => e.dy)); + const min_x = Math.min(...rt.modules.map((e) => e.dx)); + const min_y = Math.min(...rt.modules.map((e) => e.dy)); + const max_x = Math.max(...rt.modules.map((e) => e.dx)); + const max_y = Math.max(...rt.modules.map((e) => e.dy)); return { min_x: min_x, min_y: min_y, max_x: max_x, max_y: max_y, - rows: rt.length > 0 ? Math.abs(min_y - max_y) + 1 : 0, - columns: rt.length > 0 ? Math.abs(min_x - max_x) + 1 : 0, + rows: rt.modules.length > 0 ? Math.abs(min_y - max_y) + 1 : 0, + columns: rt.modules.length > 0 ? Math.abs(min_x - max_x) + 1 : 0, }; } @@ -99,13 +99,13 @@ const { min_x, min_y, max_y, max_x } = dim; s = []; - rt.forEach((device, i) => { + rt.modules.forEach((device, i) => { let connection_top = 0; let connection_bottom = 0; let connection_left = 0; let connection_right = 0; - rt.forEach((neighbor) => { + rt.modules.forEach((neighbor) => { if (!(device.dx == neighbor.dx && device.dy == neighbor.dy)) { const dxDiff = device.dx - neighbor.dx; const dyDiff = device.dy - neighbor.dy; @@ -125,7 +125,6 @@ const obj = device; obj.fly_x_direction = connection_right - connection_left; obj.fly_y_direction = connection_top - connection_bottom; - obj.type = rt[i].id.substr(0, 4); obj.gridX = x + 1; obj.gridY = y + 1; s.push(obj); diff --git a/src/renderer/main/grid-layout/grid-modules/Device.svelte b/src/renderer/main/grid-layout/grid-modules/Device.svelte index 9d0b2cb17..d89953ba3 100644 --- a/src/renderer/main/grid-layout/grid-modules/Device.svelte +++ b/src/renderer/main/grid-layout/grid-modules/Device.svelte @@ -23,7 +23,7 @@ import { appSettings } from "../../../runtime/app-helper.store"; import { moduleOverlay } from "../../../runtime/moduleOverlay"; import { selectedConfigStore } from "../../../runtime/config-helper.store"; - import { user_input } from "../../../runtime/runtime.store.js"; + import { user_input } from "../../../runtime/runtime.store"; import { onMount } from "svelte"; import ModuleSelection from "./underlays/ModuleBorder.svelte"; import { ConfigTarget } from "../../panels/configuration/Configuration.store"; diff --git a/src/renderer/main/grid-layout/grid-modules/devices/TEK1.svelte b/src/renderer/main/grid-layout/grid-modules/devices/TEK1.svelte index 039b1cdce..587bf6233 100644 --- a/src/renderer/main/grid-layout/grid-modules/devices/TEK1.svelte +++ b/src/renderer/main/grid-layout/grid-modules/devices/TEK1.svelte @@ -5,8 +5,8 @@ import EndlessPot from "../elements/EndlessPot.svelte"; import Led from "../elements/Led.svelte"; - import { elementPositionStore } from "../../../../runtime/runtime.store.js"; - import { ledColorStore } from "../../../../runtime/runtime.store.js"; + import { elementPositionStore } from "../../../../runtime/runtime.store"; + import { ledColorStore } from "../../../../runtime/runtime.store"; export let moduleWidth; export let id = "TEK1"; diff --git a/src/renderer/main/grid-layout/grid-modules/devices/TEK2.svelte b/src/renderer/main/grid-layout/grid-modules/devices/TEK2.svelte index 341b71b23..e130df7de 100644 --- a/src/renderer/main/grid-layout/grid-modules/devices/TEK2.svelte +++ b/src/renderer/main/grid-layout/grid-modules/devices/TEK2.svelte @@ -5,8 +5,8 @@ import EndlessPot from "../elements/EndlessPot.svelte"; import Led from "../elements/Led.svelte"; - import { elementPositionStore } from "../../../../runtime/runtime.store.js"; - import { ledColorStore } from "../../../../runtime/runtime.store.js"; + import { elementPositionStore } from "../../../../runtime/runtime.store"; + import { ledColorStore } from "../../../../runtime/runtime.store"; export let moduleWidth; export let id = "TEK2"; diff --git a/src/renderer/main/grid-layout/grid-modules/overlays/ControlNameOverlay.svelte b/src/renderer/main/grid-layout/grid-modules/overlays/ControlNameOverlay.svelte index 60bc16968..54ba3029c 100644 --- a/src/renderer/main/grid-layout/grid-modules/overlays/ControlNameOverlay.svelte +++ b/src/renderer/main/grid-layout/grid-modules/overlays/ControlNameOverlay.svelte @@ -1,6 +1,6 @@ diff --git a/src/renderer/main/grid-layout/grid-modules/underlays/ElementSelection.svelte b/src/renderer/main/grid-layout/grid-modules/underlays/ElementSelection.svelte index 3878d3530..7a5eb0f48 100644 --- a/src/renderer/main/grid-layout/grid-modules/underlays/ElementSelection.svelte +++ b/src/renderer/main/grid-layout/grid-modules/underlays/ElementSelection.svelte @@ -1,5 +1,5 @@ @@ -94,22 +68,13 @@ {options} > - {@const event = events[value]} - {#if typeof event !== "undefined"} - {@const eventData = $runtime - .find((e) => e.dx == event.dx && e.dy == event.dy) - ?.pages[event.page].control_elements.find( - (element) => element.elementIndex == event.element - ) - ?.events.find((e) => e.type == event.type)} - {@const stored = eventData?.stored} - {@const config = eventData?.config} - {#if stored !== config && typeof stored !== "undefined"} - - {/if} - {/if} + {@const event = target?.events.find((e) => e.type === Number(value))} + {#key $runtime} + + {/key}
diff --git a/src/renderer/main/panels/configuration/components/ActionPicker.svelte b/src/renderer/main/panels/configuration/components/ActionPicker.svelte index f89426c73..30847f171 100644 --- a/src/renderer/main/panels/configuration/components/ActionPicker.svelte +++ b/src/renderer/main/panels/configuration/components/ActionPicker.svelte @@ -1,6 +1,6 @@ import { Architecture } from "@intechstudio/grid-protocol"; - import { user_input } from "./../../runtime/runtime.store.js"; + import { user_input } from "./../../runtime/runtime.store"; import { get } from "svelte/store"; import { modal } from "./../modals/modal.store"; import Pages from "../panels/configuration/components/Pages.svelte"; import { selectedConfigStore } from "../../runtime/config-helper.store"; import { moduleOverlay } from "../../runtime/moduleOverlay"; import { MoltenPushButton } from "@intechstudio/grid-uikit"; - import { virtual_runtime } from "../../runtime/virtual-engine"; import AddVirtualModule from "../modals/AddVirtualModule.svelte"; - import { runtime } from "../../runtime/runtime.store.js"; + import { runtime } from "../../runtime/runtime.store"; let selectedModule: any = undefined; $: handleSelecteModuleChange($runtime, $user_input); function handleSelecteModuleChange(rt: any, ui: any) { - selectedModule = rt.find((e: any) => e.dx == ui.dx && e.dy == ui.dy); + selectedModule = rt.modules.find( + (e: any) => e.dx == ui.dx && e.dy == ui.dy + ); } function handleChangeModuleClicked() { diff --git a/src/renderer/runtime/engine.store.ts b/src/renderer/runtime/engine.store.ts index 07f3f59b3..e9e2e053d 100644 --- a/src/renderer/runtime/engine.store.ts +++ b/src/renderer/runtime/engine.store.ts @@ -345,7 +345,7 @@ function createWriteBuffer() { obj.descr.brc_parameters.DX, obj.descr.brc_parameters.DY, ]; - const sender: any = get(runtime).find( + const sender: any = runtime.modules.find( (e: any) => e.dx === dx && e.dy === dy )!; diff --git a/src/renderer/runtime/runtime.store.js b/src/renderer/runtime/runtime.store.js deleted file mode 100644 index 57b3cf727..000000000 --- a/src/renderer/runtime/runtime.store.js +++ /dev/null @@ -1,1289 +0,0 @@ -import { writable, get } from "svelte/store"; - -import { grid, EventType } from "@intechstudio/grid-protocol"; -import { instructions } from "../serialport/instructions"; -import { writeBuffer, sendHeartbeat } from "./engine.store"; -import { createVirtualModule } from "./virtual-engine.ts"; -import { VirtualModuleHWCFG } from "./virtual-engine.ts"; -import { virtual_runtime } from "./virtual-engine.ts"; - -import { Analytics } from "./analytics.js"; - -import { appSettings } from "./app-helper.store"; - -import { add_datapoint } from "../serialport/message-stream.store.js"; -import { modal } from "../main/modals/modal.store"; -import { ProtectedStore } from "./smart-store.store.ts"; -import { createActionsFromString } from "./runtime"; - -const setIntervalAsync = (fn, ms) => { - fn().then(() => { - setTimeout(() => setIntervalAsync(fn, ms), ms); - }); -}; - -// The controller which is added to runtime first, load a default config! - -let selection_changed_timestamp = 0; - -export const elementPositionStore = writable({}); -export const elementNameStore = writable({}); -export const ledColorStore = writable({}); - -export function update_elementPositionStore(descr) { - if (descr.class_parameters.EVENTTYPE == 3) { - // button change must not be registered - - return; - } - - let eps = get(elementPositionStore); - - if (eps[descr.brc_parameters.SX] === undefined) { - eps[descr.brc_parameters.SX] = {}; - } - if (eps[descr.brc_parameters.SX][descr.brc_parameters.SY] === undefined) { - eps[descr.brc_parameters.SX][descr.brc_parameters.SY] = {}; - } - if ( - eps[descr.brc_parameters.SX][descr.brc_parameters.SY][ - descr.class_parameters.ELEMENTNUMBER - ] === undefined - ) { - eps[descr.brc_parameters.SX][descr.brc_parameters.SY][ - descr.class_parameters.ELEMENTNUMBER - ] = -1; - } - - eps[descr.brc_parameters.SX][descr.brc_parameters.SY][ - descr.class_parameters.ELEMENTNUMBER - ] = [descr.class_parameters.EVENTPARAM1, descr.class_parameters.EVENTPARAM2]; - - //console.log("Pos", descr.class_parameters.EVENTPARAM) - - elementPositionStore.set(eps); -} - -export function update_elementNameStore(descr) { - let ens = get(elementNameStore); - - if (ens[descr.brc_parameters.SX] === undefined) { - ens[descr.brc_parameters.SX] = {}; - } - if (ens[descr.brc_parameters.SX][descr.brc_parameters.SY] === undefined) { - ens[descr.brc_parameters.SX][descr.brc_parameters.SY] = {}; - } - if ( - ens[descr.brc_parameters.SX][descr.brc_parameters.SY][ - descr.class_parameters.NUM - ] === undefined - ) { - ens[descr.brc_parameters.SX][descr.brc_parameters.SY][ - descr.class_parameters.NUM - ] = -1; - } - - ens[descr.brc_parameters.SX][descr.brc_parameters.SY][ - descr.class_parameters.NUM - ] = descr.class_parameters.NAME; - - elementNameStore.set(ens); -} - -export function update_elementPositionStore_fromPreview(descr) { - let eps = get(elementPositionStore); - - if (eps[descr.brc_parameters.SX] === undefined) { - eps[descr.brc_parameters.SX] = {}; - } - if (eps[descr.brc_parameters.SX][descr.brc_parameters.SY] === undefined) { - eps[descr.brc_parameters.SX][descr.brc_parameters.SY] = {}; - } - - for (let i = 1; i < descr.class_parameters.LENGTH / 4; i++) { - const num = parseInt( - "0x" + - String.fromCharCode(descr.raw[4 + i * 4 + 0]) + - String.fromCharCode(descr.raw[4 + i * 4 + 1]) - ); - const val = parseInt( - "0x" + - String.fromCharCode(descr.raw[4 + i * 4 + 2]) + - String.fromCharCode(descr.raw[4 + i * 4 + 3]) - ); - //console.log(num, val) - - if ( - eps[descr.brc_parameters.SX][descr.brc_parameters.SY][num] === undefined - ) { - eps[descr.brc_parameters.SX][descr.brc_parameters.SY][num] = -1; - } - - eps[descr.brc_parameters.SX][descr.brc_parameters.SY][num] = val; - - elementPositionStore.set(eps); - } -} - -export function update_ledColorStore(descr) { - for (let i = 0; i < descr.class_parameters.LENGTH / 8; i++) { - const num = parseInt( - "0x" + - String.fromCharCode(descr.raw[8 + i * 8 + 0]) + - String.fromCharCode(descr.raw[8 + i * 8 + 1]) - ); - const red = parseInt( - "0x" + - String.fromCharCode(descr.raw[8 + i * 8 + 2]) + - String.fromCharCode(descr.raw[8 + i * 8 + 3]) - ); - const gre = parseInt( - "0x" + - String.fromCharCode(descr.raw[8 + i * 8 + 4]) + - String.fromCharCode(descr.raw[8 + i * 8 + 5]) - ); - const blu = parseInt( - "0x" + - String.fromCharCode(descr.raw[8 + i * 8 + 6]) + - String.fromCharCode(descr.raw[8 + i * 8 + 7]) - ); - - //console.log(num, red, gre, blu) - - let lcs = get(ledColorStore); - - if (lcs[descr.brc_parameters.SX] === undefined) { - lcs[descr.brc_parameters.SX] = {}; - } - if (lcs[descr.brc_parameters.SX][descr.brc_parameters.SY] === undefined) { - lcs[descr.brc_parameters.SX][descr.brc_parameters.SY] = {}; - } - if ( - lcs[descr.brc_parameters.SX][descr.brc_parameters.SY][num] === undefined - ) { - lcs[descr.brc_parameters.SX][descr.brc_parameters.SY][num] = [0, 0, 0]; - } - - lcs[descr.brc_parameters.SX][descr.brc_parameters.SY][num][0] = red * 4; - lcs[descr.brc_parameters.SX][descr.brc_parameters.SY][num][1] = gre * 4; - lcs[descr.brc_parameters.SX][descr.brc_parameters.SY][num][2] = blu * 4; - - ledColorStore.set(lcs); - } -} - -//Template logger object: { type: "", message: "", classname: "" } -export const logger = writable(); - -function create_user_input() { - const defaultValues = { - dx: undefined, - dy: undefined, - pagenumber: undefined, - elementnumber: undefined, - eventtype: undefined, - }; - - const store = new ProtectedStore(defaultValues); - - function setOverride({ dx, dy, pagenumber, elementnumber, eventtype }) { - for (const [key, value] of Object.entries({ - dx, - dy, - pagenumber, - elementnumber, - eventtype, - })) { - if (typeof value === "undefined") { - store.set({ - ...defaultValues, - }); - return; - } - if (typeof value !== "number") { - throw `($user_input store): ${key} (${value}) is not the type of Number.`; - } - } - - const events = getElementEventTypes(dx, dy, elementnumber); - const closestEvent = get_closest_event(events, eventtype); - - store.set({ - dx: dx, - dy: dy, - pagenumber: pagenumber, - elementnumber: elementnumber, - eventtype: closestEvent, - }); - } - - function get_closest_event(events, event) { - if (events.map((e) => Number(e)).includes(Number(event))) { - return event; - } - - //Select closest event type if incoming device does not have the corrently selected event type - const closestEvent = Math.min( - ...events.map((e) => Number(e)).filter((e) => e > 0) - ); - return closestEvent; - } - - function process_incoming_event_from_grid(descr) { - // engine is disabled - if (get(writeBuffer).length > 0) { - return; - } - - // modal block track physical interaction setting - if (typeof get(modal) !== "undefined") { - return; - } - - // event is init, mapmode, midirx, timer - if ( - descr.class_parameters.EVENTTYPE == 0 || - descr.class_parameters.EVENTTYPE == 4 || - descr.class_parameters.EVENTTYPE == 5 || - descr.class_parameters.EVENTTYPE == 6 - ) { - return; - } - - // system element - if (descr.class_parameters.ELEMENTNUMBER == 255) { - return; - } - - const ui = get(store); - - // filter same control element had multiple interactions - let elementDifferent = - ui.elementnumber != descr.class_parameters.ELEMENTNUMBER; - let eventDifferent = ui.eventtype != descr.class_parameters.EVENTTYPE; - let sxDifferent = ui.dx != descr.brc_parameters.SX; - let syDifferent = ui.dy != descr.brc_parameters.SY; - - if (eventDifferent || elementDifferent || sxDifferent || syDifferent) { - //Filtering out clashing events of control elements - //Example: multiple fader movenet at the same time - if (Date.now() < selection_changed_timestamp + 100) { - return; - } - - let eventtype; - switch (get(appSettings).persistent.changeOnEvent) { - case "element": { - const incomingEventTypes = getElementEventTypes( - descr.brc_parameters.SX, - descr.brc_parameters.SY, - descr.class_parameters.ELEMENTNUMBER - ); - const current = ui.eventtype; - eventtype = get_closest_event(incomingEventTypes, current); - if (!elementDifferent) { - return; - } - break; - } - case "none": { - return; - } - case "event": - default: { - eventtype = descr.class_parameters.EVENTTYPE; - } - } - store.set({ - dx: descr.brc_parameters.SX, - dy: descr.brc_parameters.SY, - pagenumber: ui.pagenumber, - elementnumber: descr.class_parameters.ELEMENTNUMBER, - eventtype: eventtype, - }); - } - selection_changed_timestamp = Date.now(); - } - - function module_destroy_handler(dx, dy) { - // This is used to re-init local settings panel if a module is removed which values have been displayed - const ui = get(store); - const rt = get(runtime); - - if (dx == ui.dx && dy == ui.dy) { - if (get(runtime).length > 0) { - //Set user input to an EXISTING module - //(0,0) module is not guaranteed to exist when unplugging all modules once - //NOTE: Hardcoding (0,0) may break user_input - setOverride({ - dx: rt[0].dx, - dy: rt[0].dy, - pagenumber: ui.pagenumber, - elementnumber: 0, - eventtype: ui.eventtype, - }); - } else { - setOverride({ - ...defaultValues, - }); - } - } - } - - return { - ...store, - set: setOverride, - process_incoming_event_from_grid: process_incoming_event_from_grid, - module_destroy_handler: module_destroy_handler, - }; -} - -export const user_input = create_user_input(); - -function create_runtime() { - const _runtime = writable([]); - - const findUpdateDestEvent = (_runtime, dx, dy, page, element, event) => { - let _event = undefined; - // this elementnumber check refers to uninitialized UI... - if (element !== -1) { - _runtime.forEach((device) => { - if (device.dx == dx && device.dy == dy) { - const pageIndex = device.pages.findIndex((x) => x.pageNumber == page); - const elementIndex = device.pages[ - pageIndex - ].control_elements.findIndex((x) => x.elementIndex == element); - _event = device.pages[pageIndex].control_elements[ - elementIndex - ].events.find((e) => e.type == event); - } - }); - } - return _event; - }; - - function fetchOrLoadConfig({ dx, dy, page, element, event }) { - return new Promise((resolve, reject) => { - const _device = get(runtime).find( - (device) => device.dx == dx && device.dy == dy - ); - const _page = _device.pages.find((e) => e.pageNumber == page); - const _element = _page.control_elements.find( - (e) => e.elementIndex == element - ); - const _event = _element.events.find((e) => e.type == event); - - if (typeof _event.config !== "undefined") { - resolve(); - } else { - instructions - .fetchConfigFromGrid(dx, dy, page, element, event) - .then((descr) => { - const dx = descr.brc_parameters.SX; - const dy = descr.brc_parameters.SY; - const page = descr.class_parameters.PAGENUMBER; - const element = descr.class_parameters.ELEMENTNUMBER; - const event = descr.class_parameters.EVENTTYPE; - const actionstring = descr.class_parameters.ACTIONSTRING; - - update_event_configuration( - dx, - dy, - page, - element, - event, - actionstring - ); - - resolve(); - }) - .catch((e) => reject(e)); - } - }); - } - - function isFirmwareMismatch(currentFirmware, requiredFirmware) { - // Extract major, minor, and patch versions from current and required firmware - const { - major: currentMajor, - minor: currentMinor, - patch: currentPatch, - } = currentFirmware; - const { - major: requiredMajor, - minor: requiredMinor, - patch: requiredPatch, - } = requiredFirmware; - - // Compare major versions - if (currentMajor < requiredMajor) { - return true; // Firmware mismatch if current major version is lower - } else if (currentMajor > requiredMajor) { - return false; // No firmware mismatch if current major version is higher - } - - // Compare minor versions - if (currentMinor < requiredMinor) { - return true; // Firmware mismatch if current minor version is lower - } else if (currentMinor > requiredMinor) { - return false; // No firmware mismatch if current minor version is higher - } - - // Compare patch versions - if (currentPatch < requiredPatch) { - return true; // Firmware mismatch if current patch version is lower - } else { - return false; // No firmware mismatch if current patch version is equal or higher - } - } - - function incoming_heartbeat_handler(descr) { - try { - for (const device of get(_runtime)) { - if (device.architecture === "virtual") { - destroy_module(device.dx, device.dy); - } - } - const controller = this.create_module( - descr.brc_parameters, - descr.class_parameters, - false - ); - - let firstConnection = false; - const device = get(_runtime).find((device) => device.id == controller.id); - if (device) { - if (device.rot != controller.rot) { - _runtime.update((rt) => { - const index = rt.findIndex((device) => device.id == controller.id); - rt[index].rot = controller.rot; - return rt; - }); - } - - if (device.portstate != controller.portstate) { - _runtime.update((rt) => { - const index = rt.findIndex((device) => device.id == controller.id); - rt[index].portstate = controller.portstate; - return rt; - }); - } - - const lastDate = get(heartbeat).find( - (device) => device.id == controller.id - )?.alive; - if (lastDate) { - let newDate = Date.now(); - - heartbeat.update((store) => { - const device = store.find((device) => device.id == controller.id); - if (device) { - device.alive = newDate; - } - return store; - }); - - //console.log(newDate - lastDate) - if (get(appSettings).persistent.heartbeatDebugEnabled) { - const key1 = `Hearbeat (${controller.dx}, ${controller.dy})`; - - add_datapoint(key1, newDate - lastDate); - } - } - } - // device not found, add it to runtime and get page count from grid - else { - // check if the firmware version of the newly connected device is acceptable - console.log("Incoming Device"); - console.log("Architecture", controller.architecture); - - const as = get(appSettings); - const firmware_required = - controller.architecture === "esp32" - ? as.firmware_esp32_required - : as.firmware_d51_required; - controller.fwMismatch = isFirmwareMismatch( - controller.fwVersion, - firmware_required - ); - - console.log( - "Mismatch: ", - controller.fwMismatch, - "Firmware Version: ", - controller.fwVersion - ); - - _runtime.update((devices) => { - return [...devices, controller]; - }); - heartbeat.update((devices) => { - return [...devices, { id: controller.id, alive: Date.now() }]; - }); - - firstConnection = get(_runtime).length === 1; - - Analytics.track({ - event: "Connect Module", - payload: { - action: "Connect", - controller: controller, - moduleCount: get(runtime).length, - }, - mandatory: false, - }); - } - - if (firstConnection) { - setDefaultSelectedElement(); - } - } catch (error) { - console.warn(error); - } - } - - function setDefaultSelectedElement() { - const rt = get(runtime); - user_input.set({ - dx: rt[0].dx, - dy: rt[0].dy, - pagenumber: 0, - elementnumber: 0, - eventtype: 2, - }); - } - - function erase_all() { - _runtime.update((rt) => { - rt.forEach((device) => { - device.pages.forEach((page) => { - page.control_elements.forEach((events) => { - events.events.forEach((event) => { - event.config = undefined; - event.stored = undefined; - }); - }); - }); - }); - return rt; - }); - } - - function element_preset_load(x, y, element, preset) { - return new Promise((resolve, reject) => { - const ui = get(user_input); - let events = preset.configs.events; - const promises = []; - events.forEach((e) => { - const page = ui.pagenumber; - const event = e.event; - - _runtime.update((_runtime) => { - let dest = findUpdateDestEvent(_runtime, x, y, page, element, event); - if (typeof dest !== "undefined") { - dest.config = e.config; - const promise = instructions.sendConfigToGrid( - x, - y, - page, - element, - event, - e.config - ); - - promises.push(promise); - } - return _runtime; - }); - }); - Promise.all(promises) - .then(() => { - resolve(); - logger.set({ - type: "success", - mode: 0, - classname: "elementoverwrite", - message: `Overwrite done!`, - }); - }) - .catch((e) => reject(e)); - }); - } - - function whole_page_overwrite(x, y, array) { - logger.set({ - type: "progress", - mode: 0, - classname: "profileload", - message: `Profile load started...`, - }); - - return new Promise((resolve, reject) => { - // Reorder array to send system element first - const index = array.findIndex((obj) => obj.elementIndex === 255); - - // Check if the object with id === 255 was found - if (index !== -1) { - // Remove the object at the found index - const objectToMove = array.splice(index, 1)[0]; - - // Add the object to the front of the array - array.unshift(objectToMove); - } - - let ui = structuredClone(get(user_input)); - const promises = []; - array.forEach((elem) => { - elem.events.forEach((ev) => { - ui.elementnumber = elem.controlElementNumber; - ui.eventtype = ev.event; - - const page = ui.pagenumber; - const element = ui.elementnumber; - const event = ui.eventtype; - - _runtime.update((_runtime) => { - let dest = findUpdateDestEvent( - _runtime, - x, - y, - page, - element, - event - ); - if (dest) { - dest.config = ev.config.trim(); - } - return _runtime; - }); - - const promise = instructions.sendConfigToGrid( - x, - y, - page, - element, - event, - ev.config - ); - - promises.push(promise); - }); - }); - Promise.all(promises) - .then((desc) => { - logger.set({ - type: "success", - mode: 0, - classname: "profileload", - message: `Profile load complete!`, - }); - resolve(); - }) - .catch((e) => reject(e)); - }); - } - - function update_event_configuration( - dx, - dy, - page, - element, - event, - actionString - ) { - // config - _runtime.update((_runtime) => { - let dest = findUpdateDestEvent(_runtime, dx, dy, page, element, event); - if (dest) { - dest.config = createActionsFromString(actionString); - - if (typeof dest.stored === "undefined") { - dest.stored = dest.config; - } - } - return _runtime; - }); - } - - function send_event_configuration_to_grid(dx, dy, page, element, event) { - return new Promise((resolve, reject) => { - let rt = get(_runtime); - - let dest = findUpdateDestEvent(rt, dx, dy, page, element, event); - if (dest) { - const script = ` e.toLua()).join("")} ?>`; - instructions - .sendConfigToGrid(dx, dy, page, element, event, script) - .then((desc) => { - resolve(); - }) - .catch((e) => reject(e)); - } else { - reject("DEST not found!"); - } - }); - } - - // whole element copy: fetches all event configs from a control element - function fetch_element_configuration_from_grid( - dx, - dy, - pageNumber, - elementNumber - ) { - const rt = get(runtime); - const device = rt.find((device) => device.dx == dx && device.dy == dy); - const page = device.pages.find((x) => x.pageNumber == pageNumber); - const element = page.control_elements.find( - (x) => x.elementIndex == elementNumber - ); - const events = element.events; - - const promises = events.map((e) => { - const eventType = e.type; - const dest = { - dx: dx, - dy: dy, - page: pageNumber, - element: elementNumber, - event: eventType, - }; - const promise = fetchOrLoadConfig(dest); - return promise; - }); - - return Promise.all(promises); - } - - function fetch_page_configuration_from_grid({ dx, dy, page }) { - logger.set({ - type: "progress", - mode: 0, - classname: "profilesave", - message: `Preparing configs...`, - }); - - const rt = get(runtime); - - let device = rt.find((device) => device.dx == dx && device.dy == dy); - - if (typeof device === "undefined") { - logger.set({ - type: "fail", - mode: 0, - classname: "profilesave", - message: `No module selected`, - }); - - return Promise.reject(`No module selected`); - } - - const pageIndex = device.pages.findIndex((x) => x.pageNumber == page); - const controlElements = device.pages[pageIndex].control_elements; - - const fetchArray = []; - - controlElements.forEach((controlElement) => { - controlElement.events.forEach((e) => { - if (typeof e.config === "undefined") { - // put it into the fetchArray - fetchArray.push({ - event: e.type, - elementIndex: controlElement.elementIndex, - }); - } - }); - }); - - // clear the writeBuffer to make sure that there are no fetch operations that may interfere with the callback - // writeBuffer.clear(); - - if (fetchArray.length === 0) { - //nothing to do, let's do calback - return Promise.resolve(); - } - - const promises = fetchArray.map((e) => { - const promise = fetchOrLoadConfig({ - dx: dx, - dy: dy, - page: page, - element: e.elementIndex, - event: e.event, - }); - return promise; - }); - - return Promise.all(promises); - } - - function clear_page_configuration(index) { - _runtime.update((_runtime) => { - _runtime.forEach((device) => { - device.pages[index].control_elements.forEach((control_element) => { - control_element.events.forEach((event) => { - event.config = undefined; - event.stored = undefined; - }); - }); - }); - return _runtime; - }); - } - - function create_page(moduleType, pageNumber) { - moduleType = moduleType.substr(0, 4); - - let control_elements = []; - - let status = "INIT"; - - try { - const moduleElements = grid.get_module_element_list(moduleType); - - for (const [index, element] of moduleElements.entries()) { - if (typeof element === "undefined") { - continue; - } - let events = []; - const elementEvents = grid.get_element_events(element); - for (const event of elementEvents) { - events.push({ - type: Number(event.value), - config: undefined, - stored: undefined, - }); - } - control_elements.push({ - events: events, - elementIndex: index, - type: element, - name: "", - }); - } - - control_elements = control_elements.filter((x) => x); // filter null or invalid items! - - return { status, pageNumber: pageNumber, control_elements }; - } catch (error) { - console.warn("Error while creating page for ", moduleType, error); - } - } - - function create_module(header_param, heartbeat_class_param, virtual = false) { - let moduleType = grid.module_type_from_hwcfg( - Number(heartbeat_class_param.HWCFG) - ); - - // generic check, code below if works only if all parameters are provided - if ( - header_param === undefined || - moduleType === undefined || - heartbeat_class_param === undefined - ) { - console.log( - heartbeat_class_param.HWCFG, - "ERROR", - header_param, - moduleType, - heartbeat_class_param - ); - throw "Error creating new module."; - } - moduleType = moduleType.substr(0, 4); - - return { - // implement the module id rep / req - architecture: virtual - ? "virtual" - : grid.module_architecture_from_hwcfg(heartbeat_class_param.HWCFG), - portstate: heartbeat_class_param.PORTSTATE, - id: moduleType + "_" + "dx:" + header_param.SX + ";dy:" + header_param.SY, - dx: header_param.SX, - dy: header_param.SY, - rot: header_param.ROT, - fwVersion: { - major: heartbeat_class_param.VMAJOR, - minor: heartbeat_class_param.VMINOR, - patch: heartbeat_class_param.VPATCH, - }, - fwMismatch: false, - alive: Date.now(), - map: { - top: { dx: header_param.SX, dy: header_param.SY + 1 }, - right: { dx: header_param.SX + 1, dy: header_param.SY }, - bot: { dx: header_param.SX, dy: header_param.SY - 1 }, - left: { dx: header_param.SX - 1, dy: header_param.SY }, - }, - pages: [ - this.create_page(moduleType, 0), - this.create_page(moduleType, 1), - this.create_page(moduleType, 2), - this.create_page(moduleType, 3), - ], - }; - } - - function destroy_module(dx, dy) { - // remove the destroyed device from runtime - const removed = get(_runtime).find((e) => e.dx == dx && e.dy == dy); - - _runtime.update((rt) => { - const index = rt.findIndex((e) => e.dx === dx && e.dy === dy); - rt.splice(index, 1); - return rt; - }); - - if (get(_runtime).length === 0) { - appSettings.update((s) => { - s.gridLayoutShift = { x: 0, y: 0 }; - return s; - }); - } - - user_input.module_destroy_handler(dx, dy); - if (removed.architecture === "virtual") { - virtual_runtime.destroyModule(dx, dy); - } else { - writeBuffer.module_destroy_handler(dx, dy); - } - - // reset rendering helper stores - - try { - elementPositionStore.update((eps) => { - eps[dx][dy] = undefined; - return eps; - }); - - elementNameStore.update((ens) => { - ens[dx][dy] = undefined; - return ens; - }); - - ledColorStore.update((lcs) => { - lcs[dx][dy] = undefined; - return lcs; - }); - } catch (error) {} - - Analytics.track({ - event: "Disconnect Module", - payload: { - action: "Disconnect", - moduleCount: get(runtime).length, - }, - mandatory: false, - }); - } - - function change_page(new_page_number) { - return new Promise((resolve, reject) => { - if (get(writeBuffer).length > 0) { - reject("Wait before all operations are finished."); - return; - } - - if (unsavedChangesCount() != 0) { - reject("Store your changes before changin pages!"); - return; - } - - let ui = get(user_input); - - // only update pagenumber if it differs from the runtime pagenumber - if (ui.pagenumber === new_page_number) { - resolve(); - return; - } - // clean up the writebuffer if pagenumber changes! - // writeBuffer.clear(); - - instructions - .changeActivePage(new_page_number) - .then(() => { - const ui = get(user_input); - user_input.set({ - dx: ui.dx, - dy: ui.dy, - pagenumber: new_page_number, - elementnumber: ui.elementnumber, - eventtype: ui.eventtype, - }); - resolve(); - }) - .catch((e) => { - reject(e); - }); - }); - } - - function unsavedChangesCount() { - let count = 0; - get(_runtime).forEach((e) => { - e.pages.forEach((e) => { - e.control_elements.forEach((e) => { - e.events.forEach((e) => { - if (typeof e.stored !== "undefined" && e.stored !== e.config) { - count += 1; - } - }); - }); - }); - }); - return count; - } - - async function storePage(index) { - return new Promise((resolve, reject) => { - instructions - .sendPageStoreToGrid() - .then((res) => { - _runtime.update((store) => { - store.forEach((device) => { - device.pages - .find((e) => e.pageNumber == index) - ?.control_elements.forEach((element) => { - element.events.forEach((event) => { - if (event.stored !== event.config) { - event.stored = event.config; - } - }); - }); - }); - return store; - }); - resolve(res); - }) - .catch((e) => { - reject(e); - }); - }); - } - - function clearPage(index) { - logger.set({ - type: "progress", - mode: 0, - classname: "pageclear", - message: `Clearing configurations from page...`, - }); - return new Promise((resolve, reject) => { - instructions - .sendPageClearToGrid() - .then(() => { - clear_page_configuration(index); - resolve(); - }) - .catch((e) => { - console.warn(e); - reject(e); - }); - }); - } - - function discardPage(index) { - logger.set({ - type: "progress", - mode: 0, - classname: "pagediscard", - message: `Discarding configurations...`, - }); - return new Promise((resolve, reject) => { - instructions - .sendPageDiscardToGrid(index) - .then(() => { - clear_page_configuration(index); - resolve(); - }) - .catch((e) => { - console.warn(e); - reject(e); - }); - }); - } - - function addVirtualModule({ dx, dy, type }) { - const module = VirtualModuleHWCFG[type]; - const controller = this.create_module( - { - DX: dx, - DY: dy, - SX: dx, - SY: dy, - }, - { - HWCFG: module.hwcfg, - }, - true - ); - - createVirtualModule(dx, dy, module.type); - - _runtime.update((devices) => { - return [...devices, controller]; - }); - setDefaultSelectedElement(); - } - - return { - subscribe: _runtime.subscribe, - - element_preset_load: element_preset_load, - whole_page_overwrite: whole_page_overwrite, - - update_event_configuration: update_event_configuration, - send_event_configuration_to_grid: send_event_configuration_to_grid, - - fetch_element_configuration_from_grid: - fetch_element_configuration_from_grid, - fetch_page_configuration_from_grid: fetch_page_configuration_from_grid, - - incoming_heartbeat_handler: incoming_heartbeat_handler, - - create_page: create_page, - create_module: create_module, - destroy_module: destroy_module, - - change_page: change_page, - - erase: erase_all, - fetchOrLoadConfig: fetchOrLoadConfig, - unsavedChangesCount: unsavedChangesCount, - storePage: storePage, - discardPage: discardPage, - clearPage: clearPage, - addVirtualModule: addVirtualModule, - }; -} - -export const runtime = create_runtime(); - -//Retrieves device name from coordinates of the device -export function getDeviceName(x, y) { - const rt = get(runtime); - const currentModule = rt.find((device) => device.dx == x && device.dy == y); - return currentModule?.id.slice(0, 4); -} - -export function getElementEventTypes(x, y, elementNumber) { - const rt = get(runtime); - const currentModule = rt.find((device) => device.dx == x && device.dy == y); - - if (typeof currentModule === "undefined") { - console.warn(`Module does not exist on (${x}, ${y})`); - return undefined; - } - const element = currentModule.pages[0].control_elements.find( - (e) => e.elementIndex == elementNumber - ); - - if (typeof element === "undefined") { - console.warn( - `Control element ${elementNumber} does not exist on (${x}, ${y})` - ); - return undefined; - } - - return element.events.map((e) => e.type); -} - -function createEngine() { - const _engine = writable("ENABLED"); - - return { - ..._engine, - }; -} - -export const engine = createEngine(); - -export const heartbeat = writable([]); - -const heartbeat_editor_ms = 300; -const heartbeat_grid_ms = 250; - -const grid_heartbeat_interval_handler = async function () { - let rt = get(runtime); - - rt.forEach((device, i) => { - if (device.architecture === "virtual") { - return; - } - - const alive = get(heartbeat).find((e) => e.id == device.id)?.alive; - - // Allow less strict elapsedTimeLimit while writeBuffer is busy! - const elapsedTimeLimit = - get(writeBuffer).length > 0 - ? heartbeat_grid_ms * 6 - : heartbeat_grid_ms * 3; - const elapsedTime = Date.now() - alive; - - if (!alive || elapsedTime > elapsedTimeLimit) { - // TIMEOUT! let's remove the device - runtime.destroy_module(device.dx, device.dy); - heartbeat.update((heartbeat) => { - return heartbeat.filter((e) => e.id !== device.id); - }); - } - }); -}; - -setIntervalAsync(grid_heartbeat_interval_handler, heartbeat_grid_ms); - -const editor_heartbeat_interval_handler = async function () { - let type = 255; - - if (runtime.unsavedChangesCount() != 0 || typeof get(modal) !== "undefined") { - type = 254; - } - - if ( - get(runtime).length > 0 && - get(runtime).filter((e) => e.architecture === "virtual").length === 0 - ) { - sendHeartbeat(type); - } else { - //writeBuffer.clear(); - } -}; - -setIntervalAsync(editor_heartbeat_interval_handler, heartbeat_editor_ms); - -export class LocalDefinitions { - static getFrom({ configs, index }) { - const config = configs[index]; - let n = index - 1; - let list = []; - let indentation = config?.indentation; - while (n >= 0) { - if (configs[n].indentation <= indentation) { - list.push(configs[n]); - if (configs[n].indentation != indentation) { - indentation = configs[n].indentation; - } - } - --n; - } - - let arr = []; - list.forEach((c) => { - if (c.short == "l" && c.script !== "") { - let _variable_array = c.script.split("=")[0]; - _variable_array = _variable_array.split("local")[1]; - _variable_array = _variable_array.split(","); - _variable_array.forEach((val, i) => { - arr.push({ info: `local - ${val.trim()}`, value: val.trim() }); - }); - } - }); - return arr; - } -} - -export async function wss_send_message(message) { - window.electron.websocket.transmit({ event: "message", data: message }); -} - -console.log("reached end of runtime"); diff --git a/src/renderer/runtime/runtime.store.ts b/src/renderer/runtime/runtime.store.ts new file mode 100644 index 000000000..95802cafe --- /dev/null +++ b/src/renderer/runtime/runtime.store.ts @@ -0,0 +1,460 @@ +import { writable, get } from "svelte/store"; +import { writeBuffer, sendHeartbeat } from "./engine.store"; +import { appSettings } from "./app-helper.store"; +import { modal } from "../main/modals/modal.store"; +import { ProtectedStore } from "./smart-store.store"; +import { GridRuntime } from "./runtime"; + +const setIntervalAsync = (fn, ms) => { + fn().then(() => { + setTimeout(() => setIntervalAsync(fn, ms), ms); + }); +}; + +// The controller which is added to runtime first, load a default config! + +let selection_changed_timestamp = 0; + +export const elementPositionStore = writable({}); +export const elementNameStore = writable({}); +export const ledColorStore = writable({}); + +export function update_elementPositionStore(descr) { + if (descr.class_parameters.EVENTTYPE == 3) { + // button change must not be registered + + return; + } + + let eps = get(elementPositionStore); + + if (eps[descr.brc_parameters.SX] === undefined) { + eps[descr.brc_parameters.SX] = {}; + } + if (eps[descr.brc_parameters.SX][descr.brc_parameters.SY] === undefined) { + eps[descr.brc_parameters.SX][descr.brc_parameters.SY] = {}; + } + if ( + eps[descr.brc_parameters.SX][descr.brc_parameters.SY][ + descr.class_parameters.ELEMENTNUMBER + ] === undefined + ) { + eps[descr.brc_parameters.SX][descr.brc_parameters.SY][ + descr.class_parameters.ELEMENTNUMBER + ] = -1; + } + + eps[descr.brc_parameters.SX][descr.brc_parameters.SY][ + descr.class_parameters.ELEMENTNUMBER + ] = [descr.class_parameters.EVENTPARAM1, descr.class_parameters.EVENTPARAM2]; + + //console.log("Pos", descr.class_parameters.EVENTPARAM) + + elementPositionStore.set(eps); +} + +export function update_elementNameStore(descr) { + let ens = get(elementNameStore); + + if (ens[descr.brc_parameters.SX] === undefined) { + ens[descr.brc_parameters.SX] = {}; + } + if (ens[descr.brc_parameters.SX][descr.brc_parameters.SY] === undefined) { + ens[descr.brc_parameters.SX][descr.brc_parameters.SY] = {}; + } + if ( + ens[descr.brc_parameters.SX][descr.brc_parameters.SY][ + descr.class_parameters.NUM + ] === undefined + ) { + ens[descr.brc_parameters.SX][descr.brc_parameters.SY][ + descr.class_parameters.NUM + ] = -1; + } + + ens[descr.brc_parameters.SX][descr.brc_parameters.SY][ + descr.class_parameters.NUM + ] = descr.class_parameters.NAME; + + elementNameStore.set(ens); +} + +export function update_elementPositionStore_fromPreview(descr) { + let eps = get(elementPositionStore); + + if (eps[descr.brc_parameters.SX] === undefined) { + eps[descr.brc_parameters.SX] = {}; + } + if (eps[descr.brc_parameters.SX][descr.brc_parameters.SY] === undefined) { + eps[descr.brc_parameters.SX][descr.brc_parameters.SY] = {}; + } + + for (let i = 1; i < descr.class_parameters.LENGTH / 4; i++) { + const num = parseInt( + "0x" + + String.fromCharCode(descr.raw[4 + i * 4 + 0]) + + String.fromCharCode(descr.raw[4 + i * 4 + 1]) + ); + const val = parseInt( + "0x" + + String.fromCharCode(descr.raw[4 + i * 4 + 2]) + + String.fromCharCode(descr.raw[4 + i * 4 + 3]) + ); + //console.log(num, val) + + if ( + eps[descr.brc_parameters.SX][descr.brc_parameters.SY][num] === undefined + ) { + eps[descr.brc_parameters.SX][descr.brc_parameters.SY][num] = -1; + } + + eps[descr.brc_parameters.SX][descr.brc_parameters.SY][num] = val; + + elementPositionStore.set(eps); + } +} + +export function update_ledColorStore(descr) { + for (let i = 0; i < descr.class_parameters.LENGTH / 8; i++) { + const num = parseInt( + "0x" + + String.fromCharCode(descr.raw[8 + i * 8 + 0]) + + String.fromCharCode(descr.raw[8 + i * 8 + 1]) + ); + const red = parseInt( + "0x" + + String.fromCharCode(descr.raw[8 + i * 8 + 2]) + + String.fromCharCode(descr.raw[8 + i * 8 + 3]) + ); + const gre = parseInt( + "0x" + + String.fromCharCode(descr.raw[8 + i * 8 + 4]) + + String.fromCharCode(descr.raw[8 + i * 8 + 5]) + ); + const blu = parseInt( + "0x" + + String.fromCharCode(descr.raw[8 + i * 8 + 6]) + + String.fromCharCode(descr.raw[8 + i * 8 + 7]) + ); + + //console.log(num, red, gre, blu) + + let lcs = get(ledColorStore); + + if (lcs[descr.brc_parameters.SX] === undefined) { + lcs[descr.brc_parameters.SX] = {}; + } + if (lcs[descr.brc_parameters.SX][descr.brc_parameters.SY] === undefined) { + lcs[descr.brc_parameters.SX][descr.brc_parameters.SY] = {}; + } + if ( + lcs[descr.brc_parameters.SX][descr.brc_parameters.SY][num] === undefined + ) { + lcs[descr.brc_parameters.SX][descr.brc_parameters.SY][num] = [0, 0, 0]; + } + + lcs[descr.brc_parameters.SX][descr.brc_parameters.SY][num][0] = red * 4; + lcs[descr.brc_parameters.SX][descr.brc_parameters.SY][num][1] = gre * 4; + lcs[descr.brc_parameters.SX][descr.brc_parameters.SY][num][2] = blu * 4; + + ledColorStore.set(lcs); + } +} + +//Template logger object: { type: "", message: "", classname: "" } +export const logger = writable(); + +function create_user_input() { + const defaultValues = { + dx: undefined, + dy: undefined, + pagenumber: undefined, + elementnumber: undefined, + eventtype: undefined, + }; + + const store = new ProtectedStore(defaultValues); + + function setOverride({ dx, dy, pagenumber, elementnumber, eventtype }) { + for (const [key, value] of Object.entries({ + dx, + dy, + pagenumber, + elementnumber, + eventtype, + })) { + if (typeof value === "undefined") { + store.set({ + ...defaultValues, + }); + return; + } + if (typeof value !== "number") { + throw `($user_input store): ${key} (${value}) is not the type of Number.`; + } + } + + const events = getElementEventTypes(dx, dy, elementnumber); + const closestEvent = get_closest_event(events, eventtype); + + store.set({ + dx: dx, + dy: dy, + pagenumber: pagenumber, + elementnumber: elementnumber, + eventtype: closestEvent, + }); + } + + function get_closest_event(events, event) { + if (events.map((e) => Number(e)).includes(Number(event))) { + return event; + } + + //Select closest event type if incoming device does not have the corrently selected event type + const closestEvent = Math.min( + ...events.map((e) => Number(e)).filter((e) => e > 0) + ); + return closestEvent; + } + + function process_incoming_event_from_grid(descr) { + // engine is disabled + if (get(writeBuffer).length > 0) { + return; + } + + // modal block track physical interaction setting + if (typeof get(modal) !== "undefined") { + return; + } + + // event is init, mapmode, midirx, timer + if ( + descr.class_parameters.EVENTTYPE == 0 || + descr.class_parameters.EVENTTYPE == 4 || + descr.class_parameters.EVENTTYPE == 5 || + descr.class_parameters.EVENTTYPE == 6 + ) { + return; + } + + // system element + if (descr.class_parameters.ELEMENTNUMBER == 255) { + return; + } + + const ui = get(store); + + // filter same control element had multiple interactions + let elementDifferent = + ui.elementnumber != descr.class_parameters.ELEMENTNUMBER; + let eventDifferent = ui.eventtype != descr.class_parameters.EVENTTYPE; + let sxDifferent = ui.dx != descr.brc_parameters.SX; + let syDifferent = ui.dy != descr.brc_parameters.SY; + + if (eventDifferent || elementDifferent || sxDifferent || syDifferent) { + //Filtering out clashing events of control elements + //Example: multiple fader movenet at the same time + if (Date.now() < selection_changed_timestamp + 100) { + return; + } + + let eventtype; + switch (get(appSettings).persistent.changeOnEvent) { + case "element": { + const incomingEventTypes = getElementEventTypes( + descr.brc_parameters.SX, + descr.brc_parameters.SY, + descr.class_parameters.ELEMENTNUMBER + ); + const current = ui.eventtype; + eventtype = get_closest_event(incomingEventTypes, current); + if (!elementDifferent) { + return; + } + break; + } + case "none": { + return; + } + case "event": + default: { + eventtype = descr.class_parameters.EVENTTYPE; + } + } + store.set({ + dx: descr.brc_parameters.SX, + dy: descr.brc_parameters.SY, + pagenumber: ui.pagenumber, + elementnumber: descr.class_parameters.ELEMENTNUMBER, + eventtype: eventtype, + }); + } + selection_changed_timestamp = Date.now(); + } + + function module_destroy_handler(dx, dy) { + // This is used to re-init local settings panel if a module is removed which values have been displayed + const ui = get(store); + + if (dx == ui.dx && dy == ui.dy) { + if (runtime.modules.length > 0) { + //Set user input to an EXISTING module + //(0,0) module is not guaranteed to exist when unplugging all modules once + //NOTE: Hardcoding (0,0) may break user_input + setOverride({ + dx: runtime.modules[0].dx, + dy: runtime.modules[0].dy, + pagenumber: ui.pagenumber, + elementnumber: 0, + eventtype: ui.eventtype, + }); + } else { + setOverride({ + ...defaultValues, + }); + } + } + } + + return { + ...store, + set: setOverride, + process_incoming_event_from_grid: process_incoming_event_from_grid, + module_destroy_handler: module_destroy_handler, + }; +} + +export const user_input = create_user_input(); + +export const runtime = new GridRuntime(); + +//Retrieves device name from coordinates of the device +export function getDeviceName(x, y) { + const currentModule = runtime.modules.find( + (device) => device.dx == x && device.dy == y + ); + return currentModule?.id.slice(0, 4); +} + +export function getElementEventTypes(x, y, elementNumber) { + const currentModule = runtime.modules.find( + (device) => device.dx == x && device.dy == y + ); + + if (typeof currentModule === "undefined") { + console.warn(`Module does not exist on (${x}, ${y})`); + return undefined; + } + const element = currentModule.pages[0].control_elements.find( + (e) => e.elementIndex == elementNumber + ); + + if (typeof element === "undefined") { + console.warn( + `Control element ${elementNumber} does not exist on (${x}, ${y})` + ); + return undefined; + } + + return element.events.map((e) => e.type); +} + +function createEngine() { + const _engine = writable("ENABLED"); + + return { + ..._engine, + }; +} + +export const engine = createEngine(); + +export const heartbeat = writable([]); + +const heartbeat_editor_ms = 300; +const heartbeat_grid_ms = 250; + +const grid_heartbeat_interval_handler = async function () { + runtime.modules.forEach((device, i) => { + if (device.architecture === "virtual") { + return; + } + + const alive = get(heartbeat).find((e) => e.id == device.id)?.alive; + + // Allow less strict elapsedTimeLimit while writeBuffer is busy! + const elapsedTimeLimit = + get(writeBuffer).length > 0 + ? heartbeat_grid_ms * 6 + : heartbeat_grid_ms * 3; + const elapsedTime = Date.now() - alive; + + if (!alive || elapsedTime > elapsedTimeLimit) { + // TIMEOUT! let's remove the device + runtime.destroy_module(device.dx, device.dy); + heartbeat.update((heartbeat) => { + return heartbeat.filter((e) => e.id !== device.id); + }); + } + }); +}; + +setIntervalAsync(grid_heartbeat_interval_handler, heartbeat_grid_ms); + +const editor_heartbeat_interval_handler = async function () { + let type = 255; + + if (runtime.unsavedChangesCount() != 0 || typeof get(modal) !== "undefined") { + type = 254; + } + + if ( + runtime.modules.length > 0 && + runtime.modules.filter((e) => e.architecture === "virtual").length === 0 + ) { + sendHeartbeat(type); + } else { + //writeBuffer.clear(); + } +}; + +setIntervalAsync(editor_heartbeat_interval_handler, heartbeat_editor_ms); + +export class LocalDefinitions { + static getFrom({ configs, index }) { + const config = configs[index]; + let n = index - 1; + let list = []; + let indentation = config?.indentation; + while (n >= 0) { + if (configs[n].indentation <= indentation) { + list.push(configs[n]); + if (configs[n].indentation != indentation) { + indentation = configs[n].indentation; + } + } + --n; + } + + let arr = []; + list.forEach((c) => { + if (c.short == "l" && c.script !== "") { + let _variable_array = c.script.split("=")[0]; + _variable_array = _variable_array.split("local")[1]; + _variable_array = _variable_array.split(","); + _variable_array.forEach((val, i) => { + arr.push({ info: `local - ${val.trim()}`, value: val.trim() }); + }); + } + }); + return arr; + } +} + +export async function wss_send_message(message) { + window.electron.websocket.transmit({ event: "message", data: message }); +} + +console.log("reached end of runtime"); diff --git a/src/renderer/runtime/runtime.ts b/src/renderer/runtime/runtime.ts index 7772e75c4..074b2f7bb 100644 --- a/src/renderer/runtime/runtime.ts +++ b/src/renderer/runtime/runtime.ts @@ -1,4 +1,9 @@ -import { Architecture, ElementType } from "@intechstudio/grid-protocol"; +import { + grid, + Architecture, + ElementType, + ModuleType, +} from "@intechstudio/grid-protocol"; import { writable, get, @@ -7,6 +12,22 @@ import { Unsubscriber, Updater, } from "svelte/store"; +import { instructions } from "../serialport/instructions"; +import { writeBuffer } from "./engine.store"; +import { createVirtualModule } from "./virtual-engine"; +import { VirtualModuleHWCFG } from "./virtual-engine"; +import { virtual_runtime } from "./virtual-engine"; +import { Analytics } from "./analytics.js"; +import { appSettings } from "./app-helper.store"; +import { add_datapoint } from "../serialport/message-stream.store.js"; +import { + elementNameStore, + elementPositionStore, + heartbeat, + ledColorStore, + logger, + user_input, +} from "./runtime.store"; abstract class RuntimeNode implements Writable { protected _internal: Writable; @@ -39,21 +60,15 @@ abstract class RuntimeNode implements Writable { } protected notify() { - //console.log("ME NOTIFIED:", this); this._internal.update((s) => s); this.notifyParent(); } - protected syncWithGrid() { - console.log("SYNCING"); - } - protected notifyParent() { if (!this._parent) { return; } - console.log("NOTIFY", this._parent); this._parent.notify(); } @@ -114,54 +129,189 @@ export class GridAction extends RuntimeNode { const namePostfix = typeof this.name !== "undefined" ? `#${this.name}` : ""; return `--[[@${this.short}${namePostfix}]] ${this.script}`; } + + isEqual(other: GridAction) { + return ( + this.name === other.name && + this.script === other.script && + this.short === other.short + ); + } } export type EventData = { - config: Array; - stored: Array; + config?: Array | undefined; + stored?: Array | undefined; type: number; }; -export class GridEvent extends RuntimeNode { - constructor(parent: GridEvent, data?: ActionData) { +export class GridEvent extends RuntimeNode { + constructor(parent: GridElement, data?: EventData) { super(parent, data); } // Getters + get config() { + return this.getField("config"); + } + + get stored() { + return this.getField("stored"); + } + + get type() { + return this.getField("type"); + } // Setters + set config(value: Array) { + this.setField("config", value); + } + + set stored(value: Array) { + this.setField("stored", value); + } + + set type(value: number) { + this.setField("type", value); + } + + //Methods + toLua() { + return ` e.toLua()).join("")} ?>`; + } + + hasChanges(): boolean { + if (this.stored === undefined) { + return false; + } + + if (this.config.length !== this.stored.length) { + return true; + } + + for (let i = 0; i < this.config.length; ++i) { + if (!this.config[i].isEqual(this.stored[i])) { + return true; + } + } + return false; + } + + store(value: Array) { + this.stored = value.map( + (e) => + new GridAction(this, { script: e.script, short: e.short, name: e.name }) + ); + } + + clear() { + this.config = undefined; + this.stored = undefined; + } } export type ElementData = { elementIndex: number; - events: Array; + events?: Array; name: string; type: ElementType; }; export class GridElement extends RuntimeNode { - constructor(parent: GridEvent, data?: ElementData) { + constructor(parent: GridPage, data?: ElementData) { super(parent, data); + + this.events = []; + const elementEvents = grid.get_element_events(data.type); + for (const event of elementEvents) { + this.events.push( + new GridEvent(this, { + type: Number(event.value), + config: undefined, + stored: undefined, + }) + ); + } } // Getters + get elementIndex() { + return this.getField("elementIndex"); + } + + get events() { + return this.getField("events"); + } + + get name() { + return this.getField("name"); + } + + get type() { + return this.getField("type"); + } // Setters + set elementIndex(value: number) { + this.setField("elementIndex", value); + } + + set events(value: Array) { + this.setField("events", value); + } + + set name(value: string) { + this.setField("name", value); + } + + set type(value: ElementType) { + this.setField("type", value); + } } export type PageData = { pageNumber: number; - control_elements: Array; + control_elements?: Array; }; export class GridPage extends RuntimeNode { constructor(parent: GridModule, data?: PageData) { super(parent, data); + this.control_elements = []; + const moduleElements = grid.get_module_element_list(parent.type); + for (const [index, element] of Object.entries(moduleElements)) { + if (typeof element === "undefined") { + continue; + } + + this.control_elements.push( + new GridElement(this, { + elementIndex: Number(index), + type: element, + name: "", + }) + ); + } } // Getters + get pageNumber() { + return this.getField("pageNumber"); + } + + get control_elements() { + return this.getField("control_elements"); + } // Setters + set pageNumber(value: number) { + this.setField("pageNumber", value); + } + + set control_elements(value: Array) { + this.setField("control_elements", value); + } } type FirmwareVersion = { @@ -189,38 +339,851 @@ export type ModuleData = { dy: number; fwMismatch: boolean; fwVersion: FirmwareVersion; - gridX: number; - gridY: number; id: string; map: DirectionMap; portstate: any; rot: number; - type: string; - pages: Array; + type: ModuleType; + pages?: Array; }; export class GridModule extends RuntimeNode { constructor(parent: GridRuntime, data?: ModuleData) { super(parent, data); + this.pages = [ + new GridPage(this, { pageNumber: 0 }), + new GridPage(this, { pageNumber: 1 }), + new GridPage(this, { pageNumber: 2 }), + new GridPage(this, { pageNumber: 3 }), + ]; } // Getters + get alive() { + return this.getField("alive"); + } + + get architecture() { + return this.getField("architecture"); + } + + get dx() { + return this.getField("dx"); + } + + get dy() { + return this.getField("dy"); + } + + get fwMismatch() { + return this.getField("fwMismatch"); + } + + get fwVersion() { + return this.getField("fwVersion"); + } + + get id() { + return this.getField("id"); + } + + get map() { + return this.getField("map"); + } + + get portstate() { + return this.getField("portstate"); + } + + get rot() { + return this.getField("rot"); + } + + get type() { + return this.getField("type"); + } + + get pages() { + return this.getField("pages"); + } // Setters + set alive(value: number) { + this.setField("alive", value); + } + + set architecture(value: Architecture) { + this.setField("architecture", value); + } + + set dx(value: number) { + this.setField("dx", value); + } + + set dy(value: number) { + this.setField("dy", value); + } + + set fwMismatch(value: boolean) { + this.setField("fwMismatch", value); + } + + set fwVersion(value: FirmwareVersion) { + this.setField("fwVersion", value); + } + + set id(value: string) { + this.setField("id", value); + } + + set map(value: DirectionMap) { + this.setField("map", value); + } + + set portstate(value: any) { + this.setField("portstate", value); + } + + set rot(value: number) { + this.setField("rot", value); + } + + set type(value: ModuleType) { + this.setField("type", value); + } + + set pages(value: Array) { + this.setField("pages", value); + } } -export class GridRuntime extends RuntimeNode { +type RuntimeData = { + modules: Array; +}; + +export class GridRuntime extends RuntimeNode { constructor() { - super(undefined, undefined); + super(undefined, { modules: [] }); } - // Getters + get modules() { + return this.getField("modules"); + } // Setters + set modules(value: Array) { + this.setField("modules", value); + } + + findUpdateDestEvent(_runtime, dx, dy, page, element, event) { + let _event = undefined; + // this elementnumber check refers to uninitialized UI... + if (element !== -1) { + _runtime.forEach((device) => { + if (device.dx == dx && device.dy == dy) { + const pageIndex = device.pages.findIndex((x) => x.pageNumber == page); + const elementIndex = device.pages[ + pageIndex + ].control_elements.findIndex((x) => x.elementIndex == element); + _event = device.pages[pageIndex].control_elements[ + elementIndex + ].events.find((e) => e.type == event); + } + }); + } + return _event; + } + + fetchOrLoadConfig({ dx, dy, page, element, event }): Promise { + return new Promise((resolve, reject) => { + const _device = this.modules.find( + (device) => device.dx == dx && device.dy == dy + ); + const _page = _device.pages.find((e) => e.pageNumber == page); + const _element = _page.control_elements.find( + (e) => e.elementIndex == element + ); + const _event = _element.events.find((e) => e.type == event); + + if (typeof _event.config !== "undefined") { + resolve(); + } else { + instructions + .fetchConfigFromGrid(dx, dy, page, element, event) + .then((descr) => { + const dx = descr.brc_parameters.SX; + const dy = descr.brc_parameters.SY; + const page = descr.class_parameters.PAGENUMBER; + const element = descr.class_parameters.ELEMENTNUMBER; + const event = descr.class_parameters.EVENTTYPE; + const actionstring = descr.class_parameters.ACTIONSTRING; + + this.update_event_configuration( + dx, + dy, + page, + element, + event, + actionstring + ); + + resolve(); + }) + .catch((e) => reject(e)); + } + }); + } + + isFirmwareMismatch(currentFirmware, requiredFirmware) { + // Extract major, minor, and patch versions from current and required firmware + const { + major: currentMajor, + minor: currentMinor, + patch: currentPatch, + } = currentFirmware; + const { + major: requiredMajor, + minor: requiredMinor, + patch: requiredPatch, + } = requiredFirmware; + + // Compare major versions + if (currentMajor < requiredMajor) { + return true; // Firmware mismatch if current major version is lower + } else if (currentMajor > requiredMajor) { + return false; // No firmware mismatch if current major version is higher + } + + // Compare minor versions + if (currentMinor < requiredMinor) { + return true; // Firmware mismatch if current minor version is lower + } else if (currentMinor > requiredMinor) { + return false; // No firmware mismatch if current minor version is higher + } + + // Compare patch versions + if (currentPatch < requiredPatch) { + return true; // Firmware mismatch if current patch version is lower + } else { + return false; // No firmware mismatch if current patch version is equal or higher + } + } + + incoming_heartbeat_handler(descr) { + try { + for (const module of this.modules) { + if (module.architecture === "virtual") { + this.destroy_module(module.dx, module.dy); + } + } + const controller = this.create_module( + descr.brc_parameters, + descr.class_parameters, + false + ); + + let firstConnection = false; + const module = this.modules.find((device) => device.id == controller.id); + if (module) { + if (module.rot != controller.rot) { + module.rot = controller.rot; + } + + if (module.portstate != controller.portstate) { + module.portstate = controller.portstate; + } + + const lastDate = get(heartbeat).find( + (device) => device.id == controller.id + )?.alive; + if (lastDate) { + let newDate = Date.now(); + + heartbeat.update((store) => { + const device = store.find((device) => device.id == controller.id); + if (device) { + device.alive = newDate; + } + return store; + }); + + //console.log(newDate - lastDate) + if (get(appSettings).persistent.heartbeatDebugEnabled) { + const key1 = `Hearbeat (${controller.dx}, ${controller.dy})`; + + add_datapoint(key1, newDate - lastDate); + } + } + } + // device not found, add it to runtime and get page count from grid + else { + // check if the firmware version of the newly connected device is acceptable + console.log("Incoming Device"); + console.log("Architecture", controller.architecture); + + const as = get(appSettings); + const firmware_required = + controller.architecture === "esp32" + ? as.firmware_esp32_required + : as.firmware_d51_required; + controller.fwMismatch = this.isFirmwareMismatch( + controller.fwVersion, + firmware_required + ); + + console.log( + "Mismatch: ", + controller.fwMismatch, + "Firmware Version: ", + controller.fwVersion + ); + + this.modules = [...this.modules, controller]; + heartbeat.update((devices) => { + return [...devices, { id: controller.id, alive: Date.now() }]; + }); + + firstConnection = this.modules.length === 1; + + Analytics.track({ + event: "Connect Module", + payload: { + action: "Connect", + controller: controller, + moduleCount: this.modules.length, + }, + mandatory: false, + }); + } + + if (firstConnection) { + this.setDefaultSelectedElement(); + } + } catch (error) { + console.warn(error); + } + } + + setDefaultSelectedElement() { + user_input.set({ + dx: this.modules[0].dx, + dy: this.modules[0].dy, + pagenumber: 0, + elementnumber: 0, + eventtype: 2, + }); + } + + element_preset_load(x, y, element, preset): Promise { + return new Promise((resolve, reject) => { + const ui = get(user_input); + let events = preset.configs.events; + const promises = []; + events.forEach((e) => { + const page = ui.pagenumber; + const event = e.event; + + let dest = this.findUpdateDestEvent( + this.modules, + x, + y, + page, + element, + event + ); + if (typeof dest !== "undefined") { + dest.config = createActionsFromString(dest, e.config); + const promise = instructions.sendConfigToGrid( + x, + y, + page, + element, + event, + e.config + ); + + promises.push(promise); + } + }); + Promise.all(promises) + .then(() => { + resolve(); + logger.set({ + type: "success", + mode: 0, + classname: "elementoverwrite", + message: `Overwrite done!`, + }); + }) + .catch((e) => reject(e)); + }); + } + + whole_page_overwrite(x, y, array): Promise { + logger.set({ + type: "progress", + mode: 0, + classname: "profileload", + message: `Profile load started...`, + }); + + return new Promise((resolve, reject) => { + // Reorder array to send system element first + const index = array.findIndex((obj) => obj.elementIndex === 255); + + // Check if the object with id === 255 was found + if (index !== -1) { + // Remove the object at the found index + const objectToMove = array.splice(index, 1)[0]; + + // Add the object to the front of the array + array.unshift(objectToMove); + } + + let ui = structuredClone(get(user_input)); + const promises = []; + array.forEach((elem) => { + elem.events.forEach((ev) => { + ui.elementnumber = elem.controlElementNumber; + ui.eventtype = ev.event; + + const page = ui.pagenumber; + const element = ui.elementnumber; + const event = ui.eventtype; + + let dest = this.findUpdateDestEvent( + this.modules, + x, + y, + page, + element, + event + ); + if (dest) { + dest.config = createActionsFromString(dest, ev.config); + } + + const promise = instructions.sendConfigToGrid( + x, + y, + page, + element, + event, + ev.config + ); + + promises.push(promise); + }); + }); + Promise.all(promises) + .then((desc) => { + logger.set({ + type: "success", + mode: 0, + classname: "profileload", + message: `Profile load complete!`, + }); + resolve(); + }) + .catch((e) => reject(e)); + }); + } + + update_event_configuration(dx, dy, page, element, event, actionString) { + // config + + let dest = this.findUpdateDestEvent( + this.modules, + dx, + dy, + page, + element, + event + ); + if (dest) { + dest.config = createActionsFromString(dest, actionString); + + if (typeof dest.stored === "undefined") { + dest.store(dest.config); + } + } + } + + send_event_configuration_to_grid( + dx, + dy, + page, + element, + event + ): Promise { + return new Promise((resolve, reject) => { + let dest = this.findUpdateDestEvent( + this.modules, + dx, + dy, + page, + element, + event + ); + if (dest) { + const script = ` e.toLua()).join("")} ?>`; + instructions + .sendConfigToGrid(dx, dy, page, element, event, script) + .then((desc) => { + resolve(); + }) + .catch((e) => reject(e)); + } else { + reject("DEST not found!"); + } + }); + } + + // whole element copy: fetches all event configs from a control element + fetch_element_configuration_from_grid(dx, dy, pageNumber, elementNumber) { + const device = this.modules.find( + (device) => device.dx == dx && device.dy == dy + ); + const page = device.pages.find((x) => x.pageNumber == pageNumber); + const element = page.control_elements.find( + (x) => x.elementIndex == elementNumber + ); + const events = element.events; + + const promises = events.map((e) => { + const eventType = e.type; + const dest = { + dx: dx, + dy: dy, + page: pageNumber, + element: elementNumber, + event: eventType, + }; + const promise = this.fetchOrLoadConfig(dest); + return promise; + }); + + return Promise.all(promises); + } + + fetch_page_configuration_from_grid({ dx, dy, page }) { + logger.set({ + type: "progress", + mode: 0, + classname: "profilesave", + message: `Preparing configs...`, + }); + + let device = this.modules.find( + (device) => device.dx == dx && device.dy == dy + ); + + if (typeof device === "undefined") { + logger.set({ + type: "fail", + mode: 0, + classname: "profilesave", + message: `No module selected`, + }); + + return Promise.reject(`No module selected`); + } + + const pageIndex = device.pages.findIndex((x) => x.pageNumber == page); + const controlElements = device.pages[pageIndex].control_elements; + + const fetchArray = []; + + controlElements.forEach((controlElement) => { + controlElement.events.forEach((e) => { + if (typeof e.config === "undefined") { + // put it into the fetchArray + fetchArray.push({ + event: e.type, + elementIndex: controlElement.elementIndex, + }); + } + }); + }); + + // clear the writeBuffer to make sure that there are no fetch operations that may interfere with the callback + // writeBuffer.clear(); + + if (fetchArray.length === 0) { + //nothing to do, let's do calback + return Promise.resolve(); + } + + const promises = fetchArray.map((e) => { + const promise = this.fetchOrLoadConfig({ + dx: dx, + dy: dy, + page: page, + element: e.elementIndex, + event: e.event, + }); + return promise; + }); + + return Promise.all(promises); + } + + clear_page_configuration(index) { + this.modules.forEach((module) => { + module.pages[index].control_elements.forEach((control_element) => { + control_element.events.forEach((event) => { + event.clear(); + }); + }); + }); + } + + create_module(header_param, heartbeat_class_param, virtual = false) { + const moduleType = grid + .module_type_from_hwcfg(Number(heartbeat_class_param.HWCFG)) + ?.substring(0, 4); + + // generic check, code below if works only if all parameters are provided + if ( + header_param === undefined || + moduleType === undefined || + heartbeat_class_param === undefined + ) { + console.log( + heartbeat_class_param.HWCFG, + "ERROR", + header_param, + moduleType, + heartbeat_class_param + ); + throw "Error creating new module."; + } + + return new GridModule(this, { + // implement the module id rep / req + architecture: virtual + ? Architecture.VIRTUAL + : grid.module_architecture_from_hwcfg(heartbeat_class_param.HWCFG), + portstate: heartbeat_class_param.PORTSTATE, + id: moduleType + "_" + "dx:" + header_param.SX + ";dy:" + header_param.SY, + dx: header_param.SX, + dy: header_param.SY, + rot: header_param.ROT, + fwVersion: { + major: heartbeat_class_param.VMAJOR, + minor: heartbeat_class_param.VMINOR, + patch: heartbeat_class_param.VPATCH, + }, + type: ModuleType[moduleType as keyof typeof ModuleType], + fwMismatch: false, + alive: Date.now(), + map: { + top: { dx: header_param.SX, dy: header_param.SY + 1 }, + right: { dx: header_param.SX + 1, dy: header_param.SY }, + bot: { dx: header_param.SX, dy: header_param.SY - 1 }, + left: { dx: header_param.SX - 1, dy: header_param.SY }, + }, + }); + } + + destroy_module(dx, dy) { + // remove the destroyed device from runtime + const removed = this.modules.find((e) => e.dx == dx && e.dy == dy); + + //TODO + const index = this.modules.findIndex((e) => e.dx === dx && e.dy === dy); + this.modules.splice(index, 1); + + if (this.modules.length === 0) { + appSettings.update((s) => { + s.gridLayoutShift = { x: 0, y: 0 }; + return s; + }); + } + + user_input.module_destroy_handler(dx, dy); + if (removed.architecture === "virtual") { + virtual_runtime.destroyModule(dx, dy); + } else { + writeBuffer.module_destroy_handler(dx, dy); + } + + // reset rendering helper stores + + try { + elementPositionStore.update((eps) => { + eps[dx][dy] = undefined; + return eps; + }); + + elementNameStore.update((ens) => { + ens[dx][dy] = undefined; + return ens; + }); + + ledColorStore.update((lcs) => { + lcs[dx][dy] = undefined; + return lcs; + }); + } catch (error) {} + + Analytics.track({ + event: "Disconnect Module", + payload: { + action: "Disconnect", + moduleCount: this.modules.length, + }, + mandatory: false, + }); + } + + change_page(new_page_number): Promise { + return new Promise((resolve, reject) => { + if (get(writeBuffer).length > 0) { + reject("Wait before all operations are finished."); + return; + } + + if (this.unsavedChangesCount() != 0) { + reject("Store your changes before changin pages!"); + return; + } + + let ui = get(user_input); + + // only update pagenumber if it differs from the runtime pagenumber + if (ui.pagenumber === new_page_number) { + resolve(); + return; + } + // clean up the writebuffer if pagenumber changes! + // writeBuffer.clear(); + + instructions + .changeActivePage(new_page_number) + .then(() => { + const ui = get(user_input); + user_input.set({ + dx: ui.dx, + dy: ui.dy, + pagenumber: new_page_number, + elementnumber: ui.elementnumber, + eventtype: ui.eventtype, + }); + resolve(); + }) + .catch((e) => { + reject(e); + }); + }); + } + + unsavedChangesCount() { + let count = 0; + this.modules.forEach((e) => { + e.pages.forEach((e) => { + e.control_elements.forEach((e) => { + e.events.forEach((e) => { + if (e.hasChanges()) { + count += 1; + } + }); + }); + }); + }); + return count; + } + + async storePage(index) { + return new Promise((resolve, reject) => { + instructions + .sendPageStoreToGrid() + .then((res) => { + this.modules.forEach((module) => { + module.pages + .find((e) => e.pageNumber == index) + ?.control_elements.forEach((element) => { + element.events.forEach((event) => { + if (event.stored !== event.config) { + event.store(event.config); + } + }); + }); + }); + resolve(res); + }) + .catch((e) => { + reject(e); + }); + }); + } + + clearPage(index): Promise { + logger.set({ + type: "progress", + mode: 0, + classname: "pageclear", + message: `Clearing configurations from page...`, + }); + return new Promise((resolve, reject) => { + instructions + .sendPageClearToGrid() + .then(() => { + this.clear_page_configuration(index); + resolve(); + }) + .catch((e) => { + console.warn(e); + reject(e); + }); + }); + } + + discardPage(index): Promise { + logger.set({ + type: "progress", + mode: 0, + classname: "pagediscard", + message: `Discarding configurations...`, + }); + return new Promise((resolve, reject) => { + instructions + .sendPageDiscardToGrid() + .then(() => { + this.clear_page_configuration(index); + resolve(); + }) + .catch((e) => { + console.warn(e); + reject(e); + }); + }); + } + + addVirtualModule({ dx, dy, type }) { + const module = VirtualModuleHWCFG[type]; + const controller = this.create_module( + { + DX: dx, + DY: dy, + SX: dx, + SY: dy, + }, + { + HWCFG: module.hwcfg, + }, + true + ); + + createVirtualModule(dx, dy, module.type); + + this.modules = [...this.modules, controller]; + this.setDefaultSelectedElement(); + } } //TODO: helper function to refactor out -export function createActionsFromString(script: string) { +export function createActionsFromString(parent: GridEvent, script: string) { const result: GridAction[] = []; let configList: string[] = []; let actionString = script; @@ -242,7 +1205,7 @@ export function createActionsFromString(script: string) { .match(/--\[\[@(.*)\]\]/) ?.at(1) .split(/#(.*)/); - const obj = new GridAction(undefined, { + const obj = new GridAction(parent, { //Extract short + name, e.g.: '--[[@gms#name]]' => 'gms' short: split[0], script: configList[i + 1].trim(), diff --git a/src/renderer/serialport/instructions.ts b/src/renderer/serialport/instructions.ts index f852f04e8..ab768f85b 100644 --- a/src/renderer/serialport/instructions.ts +++ b/src/renderer/serialport/instructions.ts @@ -9,7 +9,7 @@ import { InstructionClass, InstructionClassName, } from "../runtime/engine.store.ts"; -import { logger } from "../runtime/runtime.store.js"; +import { logger } from "../runtime/runtime.store"; import { v4 as uuidv4 } from "uuid"; export interface Instructions { From 359437ff3c81dd0fb764c76b39ce2c58200b0e0c Mon Sep 17 00:00:00 2001 From: elsoazemelet Date: Fri, 6 Sep 2024 18:22:49 +0200 Subject: [PATCH 04/42] Daily push --- src/renderer/config-blocks/CodeBlock.svelte | 4 +- src/renderer/lib/_utils.js | 5 -- src/renderer/main/modals/Monaco.store.js | 2 - src/renderer/main/modals/Monaco.svelte | 51 +++++++++++++++++++-- src/renderer/runtime/runtime.ts | 7 +++ 5 files changed, 56 insertions(+), 13 deletions(-) delete mode 100644 src/renderer/lib/_utils.js delete mode 100644 src/renderer/main/modals/Monaco.store.js diff --git a/src/renderer/config-blocks/CodeBlock.svelte b/src/renderer/config-blocks/CodeBlock.svelte index 71d56bd9b..6b90fc073 100644 --- a/src/renderer/config-blocks/CodeBlock.svelte +++ b/src/renderer/config-blocks/CodeBlock.svelte @@ -48,7 +48,7 @@ import { MoltenPushButton } from "@intechstudio/grid-uikit"; - import { monaco_store } from "../main/modals/Monaco.store"; + import { monaco_store } from "../main/modals/monaco.store"; import { monaco_elementtype } from "../lib/CustomMonaco"; import { monaco_editor } from "$lib/CustomMonaco"; @@ -133,7 +133,7 @@ function open_monaco() { console.log(config); - $monaco_store = { config: config.makeCopy(), index: index }; + monaco_store.set({ config: config, index: index }); $monaco_elementtype = access_tree.elementtype; modal.show({ component: Monaco, diff --git a/src/renderer/lib/_utils.js b/src/renderer/lib/_utils.js deleted file mode 100644 index 025657cd3..000000000 --- a/src/renderer/lib/_utils.js +++ /dev/null @@ -1,5 +0,0 @@ -export class Grid { - static round(num) { - return Math.round((num + Number.EPSILON) * 100) / 100; - } -} diff --git a/src/renderer/main/modals/Monaco.store.js b/src/renderer/main/modals/Monaco.store.js deleted file mode 100644 index c320627fd..000000000 --- a/src/renderer/main/modals/Monaco.store.js +++ /dev/null @@ -1,2 +0,0 @@ -import { writable } from "svelte/store"; -export const monaco_store = writable({}); diff --git a/src/renderer/main/modals/Monaco.svelte b/src/renderer/main/modals/Monaco.svelte index 2f0d73422..ae7810f1b 100644 --- a/src/renderer/main/modals/Monaco.svelte +++ b/src/renderer/main/modals/Monaco.svelte @@ -1,8 +1,17 @@ - {#if visible && elementNumber !== 255} -
+

- {elementSettings[elementNumber]?.name} + {typeof $element.name === "undefined" ? "" : $element.name}

diff --git a/src/renderer/main/panels/configuration/ElementSelectionPanel.svelte b/src/renderer/main/panels/configuration/ElementSelectionPanel.svelte index 7ab1b1661..90f58a808 100644 --- a/src/renderer/main/panels/configuration/ElementSelectionPanel.svelte +++ b/src/renderer/main/panels/configuration/ElementSelectionPanel.svelte @@ -1,38 +1,24 @@ - diff --git a/src/renderer/main/panels/configuration/configuration-actions.ts b/src/renderer/main/panels/configuration/configuration-actions.ts index 5c8126941..20f2798ee 100644 --- a/src/renderer/main/panels/configuration/configuration-actions.ts +++ b/src/renderer/main/panels/configuration/configuration-actions.ts @@ -298,6 +298,7 @@ export function updateAction(index: number, newConfig: ConfigObject) { const cm = get(configManager); const tempScript = cm[index].script; const tempName = cm[index].name; + try { configManager.update((s: ConfigList) => { const config = s[index]; diff --git a/src/renderer/runtime/moduleOverlay.ts b/src/renderer/runtime/moduleOverlay.ts index 1fb686853..306ea7597 100644 --- a/src/renderer/runtime/moduleOverlay.ts +++ b/src/renderer/runtime/moduleOverlay.ts @@ -1,6 +1,6 @@ import { writable, Writable } from "svelte/store"; -enum ModuleOverlayType { +export enum ModuleOverlayType { CONFIGURATION_LOAD = "configuration-load-overlay", CONTROL_NAME = "control-name-overlay", } diff --git a/src/renderer/runtime/runtime.store.ts b/src/renderer/runtime/runtime.store.ts index 95802cafe..7c9328704 100644 --- a/src/renderer/runtime/runtime.store.ts +++ b/src/renderer/runtime/runtime.store.ts @@ -16,7 +16,6 @@ const setIntervalAsync = (fn, ms) => { let selection_changed_timestamp = 0; export const elementPositionStore = writable({}); -export const elementNameStore = writable({}); export const ledColorStore = writable({}); export function update_elementPositionStore(descr) { @@ -53,30 +52,22 @@ export function update_elementPositionStore(descr) { elementPositionStore.set(eps); } -export function update_elementNameStore(descr) { - let ens = get(elementNameStore); - - if (ens[descr.brc_parameters.SX] === undefined) { - ens[descr.brc_parameters.SX] = {}; - } - if (ens[descr.brc_parameters.SX][descr.brc_parameters.SY] === undefined) { - ens[descr.brc_parameters.SX][descr.brc_parameters.SY] = {}; - } - if ( - ens[descr.brc_parameters.SX][descr.brc_parameters.SY][ - descr.class_parameters.NUM - ] === undefined - ) { - ens[descr.brc_parameters.SX][descr.brc_parameters.SY][ - descr.class_parameters.NUM - ] = -1; - } - - ens[descr.brc_parameters.SX][descr.brc_parameters.SY][ - descr.class_parameters.NUM - ] = descr.class_parameters.NAME; - - elementNameStore.set(ens); +export function update_element_name(descr) { + const [dx, dy, element, name] = [ + Number(descr.brc_parameters.SX), + Number(descr.brc_parameters.SY), + Number(descr.class_parameters.NUM), + String(descr.class_parameters.NAME), + ]; + + console.log(dx, dy, element, name); + runtime.modules + .find((e) => e.dx === dx && e.dy === dy) + .pages.forEach( + (e) => + (e.control_elements.find((e) => e.elementIndex === element).name = + name.length > 0 ? name : undefined) + ); } export function update_elementPositionStore_fromPreview(descr) { @@ -371,8 +362,6 @@ function createEngine() { export const engine = createEngine(); -export const heartbeat = writable([]); - const heartbeat_editor_ms = 300; const heartbeat_grid_ms = 250; @@ -382,7 +371,7 @@ const grid_heartbeat_interval_handler = async function () { return; } - const alive = get(heartbeat).find((e) => e.id == device.id)?.alive; + const alive = device.alive; // Allow less strict elapsedTimeLimit while writeBuffer is busy! const elapsedTimeLimit = @@ -394,9 +383,6 @@ const grid_heartbeat_interval_handler = async function () { if (!alive || elapsedTime > elapsedTimeLimit) { // TIMEOUT! let's remove the device runtime.destroy_module(device.dx, device.dy); - heartbeat.update((heartbeat) => { - return heartbeat.filter((e) => e.id !== device.id); - }); } }); }; diff --git a/src/renderer/runtime/runtime.ts b/src/renderer/runtime/runtime.ts index 14ef2e224..67b95ea0c 100644 --- a/src/renderer/runtime/runtime.ts +++ b/src/renderer/runtime/runtime.ts @@ -21,9 +21,7 @@ import { Analytics } from "./analytics.js"; import { appSettings } from "./app-helper.store"; import { add_datapoint } from "../serialport/message-stream.store.js"; import { - elementNameStore, elementPositionStore, - heartbeat, ledColorStore, logger, user_input, @@ -35,19 +33,19 @@ abstract class RuntimeNode implements Writable { private _parent: RuntimeNode; private _id: string; - subscribe( + public subscribe( run: Subscriber, invalidate?: (value?: T) => void ): Unsubscriber { return this._internal.subscribe(run, invalidate); } - set(value: T) { + public set(value: T) { this._internal.set(value); this.notifyParent(); } - update(updater: Updater) { + public update(updater: Updater) { this._internal.update(updater); this.notifyParent(); } @@ -229,7 +227,7 @@ export class GridEvent extends RuntimeNode { export type ElementData = { elementIndex: number; events?: Array; - name: string; + name: string | undefined; type: ElementType; }; @@ -276,7 +274,7 @@ export class GridElement extends RuntimeNode { this.setField("events", value); } - private set name(value: string) { + set name(value: string) { this.setField("name", value); } @@ -304,7 +302,7 @@ export class GridPage extends RuntimeNode { new GridElement(this, { elementIndex: Number(index), type: element, - name: "", + name: undefined, }) ); } @@ -424,7 +422,8 @@ export class GridModule extends RuntimeNode { // Setters set alive(value: number) { - this.setField("alive", value); + get(this._internal).alive = value; + //this.setField("alive", value); } set architecture(value: Architecture) { @@ -611,47 +610,36 @@ export class GridRuntime extends RuntimeNode { this.destroy_module(module.dx, module.dy); } } - const controller = this.create_module( - descr.brc_parameters, - descr.class_parameters, - false - ); + + const [sx, sy] = [descr.brc_parameters.SX, descr.brc_parameters.SY]; let firstConnection = false; - const module = this.modules.find((device) => device.id == controller.id); + const module = this.modules.find((e) => e.dx == sx && e.dy == sy); if (module) { - if (module.rot != controller.rot) { - module.rot = controller.rot; + if (module.rot != descr.brc_parameters.ROT) { + module.rot = descr.brc_parameters.ROT; } - if (module.portstate != controller.portstate) { - module.portstate = controller.portstate; + if (module.portstate != descr.class_parameters.PORTSTATE) { + module.portstate = descr.class_parameters.PORTSTATE; } - const lastDate = get(heartbeat).find( - (device) => device.id == controller.id - )?.alive; - if (lastDate) { - let newDate = Date.now(); - - heartbeat.update((store) => { - const device = store.find((device) => device.id == controller.id); - if (device) { - device.alive = newDate; - } - return store; - }); + const lastDate = module.alive; + const newDate = Date.now(); + module.alive = newDate; - //console.log(newDate - lastDate) - if (get(appSettings).persistent.heartbeatDebugEnabled) { - const key1 = `Hearbeat (${controller.dx}, ${controller.dy})`; - - add_datapoint(key1, newDate - lastDate); - } + if (get(appSettings).persistent.heartbeatDebugEnabled) { + const key1 = `Hearbeat (${module.dx}, ${module.dy})`; + add_datapoint(key1, newDate - lastDate); } } // device not found, add it to runtime and get page count from grid else { + const controller = this.create_module( + descr.brc_parameters, + descr.class_parameters, + false + ); // check if the firmware version of the newly connected device is acceptable console.log("Incoming Device"); console.log("Architecture", controller.architecture); @@ -674,9 +662,7 @@ export class GridRuntime extends RuntimeNode { ); this.modules = [...this.modules, controller]; - heartbeat.update((devices) => { - return [...devices, { id: controller.id, alive: Date.now() }]; - }); + controller.alive = Date.now(); firstConnection = this.modules.length === 1; @@ -1059,11 +1045,6 @@ export class GridRuntime extends RuntimeNode { return eps; }); - elementNameStore.update((ens) => { - ens[dx][dy] = undefined; - return ens; - }); - ledColorStore.update((lcs) => { lcs[dx][dy] = undefined; return lcs; diff --git a/src/renderer/serialport/message-stream.store.js b/src/renderer/serialport/message-stream.store.js index 5e0d4b288..3988ab4d3 100644 --- a/src/renderer/serialport/message-stream.store.js +++ b/src/renderer/serialport/message-stream.store.js @@ -6,7 +6,7 @@ import { runtime, user_input, wss_send_message, - update_elementNameStore, + update_element_name, update_elementPositionStore, update_elementPositionStore_fromPreview, update_ledColorStore, @@ -183,7 +183,7 @@ function createMessageStream() { class_descr.class_name === "ELEMENTNAME" && class_descr.class_instr === "EXECUTE" ) { - update_elementNameStore(class_descr); + update_element_name(class_descr); } if ( From 1bcc1ff2f47bf2163d272e342de4dbb5cf0c739c Mon Sep 17 00:00:00 2001 From: elsoazemelet Date: Wed, 11 Sep 2024 15:33:28 +0200 Subject: [PATCH 08/42] Daily push --- src/renderer/config-blocks/CodeBlock.svelte | 9 +- .../underlays/ActiveChanges.svelte | 1 + src/renderer/main/modals/Monaco.svelte | 101 ++++++++++-------- src/renderer/main/modals/modal.store.ts | 7 +- src/renderer/main/modals/monaco.store.ts | 5 +- .../panels/configuration/EventPanel.svelte | 8 +- .../configuration/configuration-actions.ts | 18 +++- src/renderer/runtime/runtime.store.ts | 1 - src/renderer/runtime/runtime.ts | 62 ++++++----- 9 files changed, 124 insertions(+), 88 deletions(-) diff --git a/src/renderer/config-blocks/CodeBlock.svelte b/src/renderer/config-blocks/CodeBlock.svelte index fb20698ba..6026546a0 100644 --- a/src/renderer/config-blocks/CodeBlock.svelte +++ b/src/renderer/config-blocks/CodeBlock.svelte @@ -42,7 +42,7 @@ -