/* nodejs-poolController. An application to control pool equipment. Copyright (C) 2016, 2017, 2018, 2019, 2020, 2021, 2022. Russell Goldin, tagyoureit. russ.goldin@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ import { logger } from "../../logger/Logger"; import { setTimeout as setTimeoutSync } from 'timers'; import { Inbound, Outbound, Protocol } from "../../controller/comms/messages/Messages"; import { byteValueMap, byteValueMaps, SystemBoard } from "../../controller/boards/SystemBoard"; import { Anslq25, PoolSystem, sys } from "../../controller/Equipment"; import { ControllerType, utils } from "../../controller/Constants"; import { conn } from "../../controller/comms/Comms"; import { MockEasyTouch } from "./MockEasyTouchBoard"; export class MockSystemBoard { public valueMaps: byteValueMaps = new byteValueMaps(); protected _statusTimer: NodeJS.Timeout; protected _statusCheckRef: number = 0; protected _statusInterval: number = 5000; constructor(system: PoolSystem) { // sys.anslq25.portId = 0; // pass this in. setTimeout(() => { this.processStatusAsync().then(() => { }); }, 5000); } public expansionBoards: byteValueMap = new byteValueMap(); public get statusInterval(): number { return this._statusInterval }; public system: MockSystemCommands = new MockSystemCommands(this); public circuits: MockCircuitCommands = new MockCircuitCommands(this); public schedules: MockScheduleCommands = new MockScheduleCommands(this); public heaters: MockHeaterCommands = new MockHeaterCommands(this); public valves: MockValveCommands = new MockValveCommands(this); public remotes: MockRemoteCommands = new MockRemoteCommands(this); public pumps: MockPumpCommands = new MockPumpCommands(this); public static convertOutbound(outboundMsg: Outbound) { }; public async sendAsync(msg: Outbound){ return await msg.sendAsync(); // is the controller on a real/physical port or a mock port? /* let port = conn.findPortById(sys.anslq25.portId); if (port.mock) { let inbound = new Inbound(); inbound.protocol = msg.protocol; inbound.header = msg.header; inbound.payload = msg.payload; inbound.term = msg.term; inbound.portId = msg.portId; // don't need to wait for packet to process setTimeout(()=>{conn.sendMockPacket(inbound)}, 10); return Promise.resolve(); } else { return await msg.sendAsync(); } */ } public process(msg: Inbound): Outbound { return new Outbound(Protocol.Broadcast,0,0,0,[]); } protected killStatusCheck() { if (typeof this._statusTimer !== 'undefined' && this._statusTimer) clearTimeout(this._statusTimer); this._statusTimer = undefined; this._statusCheckRef = 0; } public suspendStatus(bSuspend: boolean) { // The way status suspension works is by using a reference value that is incremented and decremented // the status check is only performed when the reference value is 0. So suspending the status check 3 times and un-suspending // it 2 times will still result in the status check being suspended. This method also ensures the reference never falls below 0. if (bSuspend) this._statusCheckRef++; else this._statusCheckRef = Math.max(0, this._statusCheckRef - 1); if (this._statusCheckRef > 1) logger.verbose(`Suspending ANSLQ25 status check: ${bSuspend} -- ${this._statusCheckRef}`); } public async setAnslq25Async(data: any): Promise { let self = this; try { this.suspendStatus(true); // if (typeof data.isActive === 'undefined') return Promise.reject(`Mock System Board: No isActive flag provided.`); if (typeof data.anslq25portId === 'undefined') return Promise.reject(new Error(`Mock System Board: No portId provided.`)); if (typeof data.anslq25ControllerType === 'undefined') return Promise.reject(new Error(`Mock System Board: No controller type provided.`)); if (typeof data.anslq25model === 'undefined') return Promise.reject(new Error(`Mock System Board: No model provided.`)); //for (let i = 1; i <= ) let isActive = true; // utils.makeBool(data.isActive); let portId = parseInt(data.anslq25portId, 10); let port = conn.findPortById(portId); if (typeof port === 'undefined') return Promise.reject(new Error(`Mock System Board: Invalid portId provided.`)); if (portId === 0) return Promise.reject(new Error(`Please choose a port other than the primary port.`)); let mockControllerType = data.anslq25ControllerType; let model = parseInt(data.anslq25model, 10); let broadcastComms = data.broadcastComms; if (typeof broadcastComms === 'undefined') return Promise.reject(new Error(`A value for broadcast comms must be provided.`)); sys.anslq25.portId = portId; sys.anslq25.broadcastComms = broadcastComms; switch (mockControllerType) { case ControllerType.EasyTouch:{ sys.anslq25ControllerType = ControllerType.EasyTouch; // (sys.anslq25Board as MockEasyTouch).initExpansionModules(model); break; } default: { logger.warn(`No ANSLQ25 Mock Board definiton yet for: ${mockControllerType}`); return Promise.reject(new Error(`No ANSLQ25 Mock Board definiton yet for: ${mockControllerType}`)); } } sys.anslq25.isActive = isActive; sys.anslq25.model = model; } catch (err) { logger.error(`Error changing port id: ${err.message}`); } finally { this.suspendStatus(false); this._statusTimer = setTimeoutSync(async () => await self.processStatusAsync(), this.statusInterval); } } public async deleteAnslq25Async(data: any) { try { this.killStatusCheck(); this.closeAsync(); sys.anslq25.isActive = false; sys.anslq25.portId = undefined; sys.anslq25.model = undefined; sys.anslq25ControllerType = ControllerType.None; } catch (err){ } finally { this.suspendStatus(false); } } public async processStatusAsync() { let self = this; try { if (this._statusCheckRef > 0) return; this.suspendStatus(true); await sys.anslq25Board.system.sendStatusAsync(); } catch (err) { logger.error(`Error running mock processStatusAsync: ${err}`); } finally { this.suspendStatus(false); if (sys.anslq25.isActive){ if (this.statusInterval > 0) this._statusTimer = setTimeoutSync(async () => await self.processStatusAsync(), this.statusInterval); } } } // public async setPortId(portId: number) { // let self = this; // try { // this.suspendStatus(true); // sys.anslq25.portId = portId; // } catch (err) { // logger.error(`Error changing port id: ${err.message}`); // } // finally { // this.suspendStatus(false); // this._statusTimer = setTimeoutSync(async () => await self.processStatusAsync(), this.statusInterval); // } // } public async closeAsync() { try { } catch (err) { logger.error(err); } } } export class MockBoardCommands { protected mockBoard: MockSystemBoard = null; constructor(parent: MockSystemBoard) { this.mockBoard = parent; } } export class MockSystemCommands extends MockBoardCommands { public sendAck(msg:Inbound) { }; public async processDateTimeAsync(msg: Inbound){ }; public async processCustomNameAsync(msg: Inbound){ }; public async processSettingsAsync(msg: Inbound){ }; public async sendStatusAsync() { }; } export class MockCircuitCommands extends MockBoardCommands { public async processCircuitAsync( msg: Inbound) { }; public async processLightGroupAsync( msg: Inbound) { }; } export class MockScheduleCommands extends MockBoardCommands { public async processScheduleAsync( msg: Inbound) { }; } export class MockHeaterCommands extends MockBoardCommands { public async processHeatModesAsync(msg: Inbound) { }; public async processHeaterConfigAsync(msg: Inbound) { }; } export class MockValveCommands extends MockBoardCommands { public async processValveOptionsAsync(msg: Inbound) { }; public async processValveAssignmentsAsync(msg: Inbound) { }; } export class MockRemoteCommands extends MockBoardCommands { public async processIS4IS10RemoteAsync(msg: Inbound) { }; public async processQuickTouchRemoteAsync(msg: Inbound) { }; public async processSpaCommandRemoteAsync(msg: Inbound) { }; } export class MockPumpCommands extends MockBoardCommands { public async processPumpConfigAsync(msg: Inbound) { }; public async processHighSpeedCircuitsAsync(msg: Inbound) { }; }