nodejs-poolcontroller 7.6.1 → 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +36 -45
- package/.github/ISSUE_TEMPLATE/1-bug-report.yml +84 -0
- package/.github/ISSUE_TEMPLATE/2-docs.md +12 -0
- package/.github/ISSUE_TEMPLATE/3-proposal.md +28 -0
- package/.github/ISSUE_TEMPLATE/config.yml +8 -0
- package/CONTRIBUTING.md +74 -74
- package/Changelog +242 -215
- package/Dockerfile +17 -17
- package/Gruntfile.js +40 -40
- package/LICENSE +661 -661
- package/README.md +195 -191
- package/anslq25/MessagesMock.ts +218 -0
- package/anslq25/boards/MockBoardFactory.ts +50 -0
- package/anslq25/boards/MockEasyTouchBoard.ts +696 -0
- package/anslq25/boards/MockSystemBoard.ts +217 -0
- package/anslq25/chemistry/MockChlorinator.ts +75 -0
- package/anslq25/pumps/MockPump.ts +84 -0
- package/app.ts +10 -14
- package/config/Config.ts +26 -8
- package/config/VersionCheck.ts +8 -4
- package/controller/Constants.ts +59 -25
- package/controller/Equipment.ts +2667 -2459
- package/controller/Errors.ts +181 -180
- package/controller/Lockouts.ts +534 -436
- package/controller/State.ts +596 -77
- package/controller/boards/AquaLinkBoard.ts +1003 -0
- package/controller/boards/BoardFactory.ts +53 -45
- package/controller/boards/EasyTouchBoard.ts +3079 -2653
- package/controller/boards/IntelliCenterBoard.ts +3821 -4230
- package/controller/boards/IntelliComBoard.ts +69 -63
- package/controller/boards/IntelliTouchBoard.ts +384 -241
- package/controller/boards/NixieBoard.ts +1871 -1675
- package/controller/boards/SunTouchBoard.ts +393 -0
- package/controller/boards/SystemBoard.ts +5244 -4697
- package/controller/comms/Comms.ts +905 -541
- package/controller/comms/ScreenLogic.ts +1663 -0
- package/controller/comms/messages/Messages.ts +382 -54
- package/controller/comms/messages/config/ChlorinatorMessage.ts +8 -4
- package/controller/comms/messages/config/CircuitGroupMessage.ts +5 -2
- package/controller/comms/messages/config/CircuitMessage.ts +82 -13
- package/controller/comms/messages/config/ConfigMessage.ts +3 -1
- package/controller/comms/messages/config/CoverMessage.ts +2 -1
- package/controller/comms/messages/config/CustomNameMessage.ts +31 -30
- package/controller/comms/messages/config/EquipmentMessage.ts +5 -1
- package/controller/comms/messages/config/ExternalMessage.ts +33 -3
- package/controller/comms/messages/config/FeatureMessage.ts +2 -1
- package/controller/comms/messages/config/GeneralMessage.ts +2 -1
- package/controller/comms/messages/config/HeaterMessage.ts +145 -11
- package/controller/comms/messages/config/IntellichemMessage.ts +2 -1
- package/controller/comms/messages/config/OptionsMessage.ts +16 -27
- package/controller/comms/messages/config/PumpMessage.ts +62 -47
- package/controller/comms/messages/config/RemoteMessage.ts +80 -13
- package/controller/comms/messages/config/ScheduleMessage.ts +390 -347
- package/controller/comms/messages/config/SecurityMessage.ts +2 -1
- package/controller/comms/messages/config/ValveMessage.ts +44 -27
- package/controller/comms/messages/status/ChlorinatorStateMessage.ts +44 -91
- package/controller/comms/messages/status/EquipmentStateMessage.ts +139 -30
- package/controller/comms/messages/status/HeaterStateMessage.ts +135 -86
- package/controller/comms/messages/status/IntelliChemStateMessage.ts +448 -445
- package/controller/comms/messages/status/IntelliValveStateMessage.ts +36 -35
- package/controller/comms/messages/status/PumpStateMessage.ts +92 -2
- package/controller/comms/messages/status/VersionMessage.ts +2 -1
- package/controller/nixie/Nixie.ts +173 -162
- package/controller/nixie/NixieEquipment.ts +104 -103
- package/controller/nixie/bodies/Body.ts +120 -120
- package/controller/nixie/bodies/Filter.ts +135 -135
- package/controller/nixie/chemistry/ChemController.ts +2682 -2498
- package/controller/nixie/chemistry/ChemDoser.ts +806 -0
- package/controller/nixie/chemistry/Chlorinator.ts +367 -314
- package/controller/nixie/circuits/Circuit.ts +402 -248
- package/controller/nixie/heaters/Heater.ts +815 -649
- package/controller/nixie/pumps/Pump.ts +934 -661
- package/controller/nixie/schedules/Schedule.ts +319 -257
- package/controller/nixie/valves/Valve.ts +170 -170
- package/defaultConfig.json +346 -286
- package/logger/DataLogger.ts +448 -448
- package/logger/Logger.ts +38 -9
- package/package.json +60 -56
- package/tsconfig.json +25 -25
- package/web/Server.ts +275 -117
- package/web/bindings/aqualinkD.json +560 -0
- package/web/bindings/homeassistant.json +437 -0
- package/web/bindings/influxDB.json +1066 -1021
- package/web/bindings/mqtt.json +721 -654
- package/web/bindings/mqttAlt.json +746 -684
- package/web/bindings/rulesManager.json +54 -54
- package/web/bindings/smartThings-Hubitat.json +31 -31
- package/web/bindings/valveRelays.json +20 -20
- package/web/bindings/vera.json +25 -25
- package/web/interfaces/baseInterface.ts +188 -136
- package/web/interfaces/httpInterface.ts +148 -124
- package/web/interfaces/influxInterface.ts +283 -245
- package/web/interfaces/mqttInterface.ts +695 -475
- package/web/interfaces/ruleInterface.ts +87 -0
- package/web/services/config/Config.ts +177 -49
- package/web/services/config/ConfigSocket.ts +2 -1
- package/web/services/state/State.ts +154 -3
- package/web/services/state/StateSocket.ts +69 -18
- package/web/services/utilities/Utilities.ts +232 -42
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -52
- package/config copy.json +0 -300
- package/issue_template.md +0 -52
package/controller/State.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* nodejs-poolController. An application to control pool equipment.
|
|
2
|
-
Copyright (C) 2016, 2017, 2018, 2019, 2020
|
|
2
|
+
Copyright (C) 2016, 2017, 2018, 2019, 2020, 2021, 2022.
|
|
3
|
+
Russell Goldin, tagyoureit. russ.goldin@gmail.com
|
|
3
4
|
|
|
4
5
|
This program is free software: you can redistribute it and/or modify
|
|
5
6
|
it under the terms of the GNU Affero General Public License as
|
|
@@ -22,10 +23,9 @@ import * as util from 'util';
|
|
|
22
23
|
import { logger } from '../logger/Logger';
|
|
23
24
|
import { webApp } from '../web/Server';
|
|
24
25
|
import { ControllerType, Timestamp, utils, Heliotrope } from './Constants';
|
|
25
|
-
import { sys, Chemical, ChemController } from './Equipment';
|
|
26
|
+
import { sys, Chemical, ChemController, ChemicalTank, ChemicalPump } from './Equipment';
|
|
26
27
|
import { versionCheck } from '../config/VersionCheck';
|
|
27
|
-
import {
|
|
28
|
-
import { DataLogger, DataLoggerEntry, IDataLoggerEntry } from '../logger/DataLogger';
|
|
28
|
+
import { DataLogger, DataLoggerEntry } from '../logger/DataLogger';
|
|
29
29
|
import { delayMgr } from './Lockouts';
|
|
30
30
|
|
|
31
31
|
export class State implements IState {
|
|
@@ -99,7 +99,7 @@ export class State implements IState {
|
|
|
99
99
|
lines = buff.toString().split('\n');
|
|
100
100
|
}
|
|
101
101
|
return lines;
|
|
102
|
-
} catch (err) { logger.error(err); }
|
|
102
|
+
} catch (err) { logger.error(`Error reading log file ${logFile}: ${err.message}`); }
|
|
103
103
|
}
|
|
104
104
|
public async logData(logFile: string, data: any) {
|
|
105
105
|
try {
|
|
@@ -116,7 +116,7 @@ export class State implements IState {
|
|
|
116
116
|
else
|
|
117
117
|
lines.unshift(data.toString());
|
|
118
118
|
fs.writeFileSync(logPath, lines.join('\n'));
|
|
119
|
-
} catch (err) { logger.error(err); }
|
|
119
|
+
} catch (err) { logger.error(`Error reading or writing logData ${logFile}: ${err.message}`); }
|
|
120
120
|
}
|
|
121
121
|
public getState(section?: string): any {
|
|
122
122
|
// todo: getState('time') returns an array of chars. Needs no be fixed.
|
|
@@ -140,6 +140,7 @@ export class State implements IState {
|
|
|
140
140
|
_state.filters = this.filters.getExtended();
|
|
141
141
|
_state.schedules = this.schedules.getExtended();
|
|
142
142
|
_state.chemControllers = this.chemControllers.getExtended();
|
|
143
|
+
_state.chemDosers = this.chemDosers.getExtended();
|
|
143
144
|
_state.delays = delayMgr.serialize();
|
|
144
145
|
return _state;
|
|
145
146
|
}
|
|
@@ -160,7 +161,7 @@ export class State implements IState {
|
|
|
160
161
|
try {
|
|
161
162
|
if (this._timerDirty) clearTimeout(this._timerDirty);
|
|
162
163
|
this.persist();
|
|
163
|
-
|
|
164
|
+
/* if (sys.controllerType === ControllerType.Virtual) {
|
|
164
165
|
for (let i = 0; i < state.temps.bodies.length; i++) {
|
|
165
166
|
state.temps.bodies.getItemByIndex(i).isOn = false;
|
|
166
167
|
}
|
|
@@ -170,7 +171,7 @@ export class State implements IState {
|
|
|
170
171
|
for (let i = 0; i < state.features.length; i++) {
|
|
171
172
|
state.features.getItemByIndex(i).isOn = false;
|
|
172
173
|
}
|
|
173
|
-
}
|
|
174
|
+
} */
|
|
174
175
|
logger.info('State process shut down');
|
|
175
176
|
} catch (err) { logger.error(`Error shutting down state process: ${err.message}`); }
|
|
176
177
|
}
|
|
@@ -265,6 +266,9 @@ export class State implements IState {
|
|
|
265
266
|
for (let i = 0; i < state.chemControllers.length; i++) {
|
|
266
267
|
state.chemControllers.getItemByIndex(i).hasChanged = true;
|
|
267
268
|
}
|
|
269
|
+
for (let i = 0; i < state.chemDosers.length; i++) {
|
|
270
|
+
state.chemDosers.getItemByIndex(i).hasChanged = true;
|
|
271
|
+
}
|
|
268
272
|
state.emitEquipmentChanges();
|
|
269
273
|
}
|
|
270
274
|
public emitEquipmentChanges() {
|
|
@@ -341,6 +345,7 @@ export class State implements IState {
|
|
|
341
345
|
public get isInitialized(): boolean { return typeof (this.data.status) !== 'undefined' && this.data.status.val !== 0; }
|
|
342
346
|
public init() {
|
|
343
347
|
console.log(`Init state for Pool Controller`);
|
|
348
|
+
|
|
344
349
|
var sdata = this.loadFile(this.statePath, {});
|
|
345
350
|
sdata = extend(true, { mode: { val: -1 }, temps: { units: { val: 0, name: 'F', desc: 'Fahrenheit' } } }, sdata);
|
|
346
351
|
if (typeof sdata.temps !== 'undefined' && typeof sdata.temps.bodies !== 'undefined') {
|
|
@@ -358,6 +363,7 @@ export class State implements IState {
|
|
|
358
363
|
EqStateCollection.removeNullIds(sdata.lightGroups);
|
|
359
364
|
EqStateCollection.removeNullIds(sdata.remotes);
|
|
360
365
|
EqStateCollection.removeNullIds(sdata.chemControllers);
|
|
366
|
+
EqStateCollection.removeNullIds(sdata.chemDosers);
|
|
361
367
|
EqStateCollection.removeNullIds(sdata.filters);
|
|
362
368
|
var self = this;
|
|
363
369
|
let pnlTime = typeof sdata.time !== 'undefined' ? new Date(sdata.time) : new Date();
|
|
@@ -365,6 +371,9 @@ export class State implements IState {
|
|
|
365
371
|
this._dt = new Timestamp(pnlTime);
|
|
366
372
|
this._dt.milliseconds = 0;
|
|
367
373
|
this.data = sdata;
|
|
374
|
+
this.equipment = new EquipmentState(this.data, 'equipment');
|
|
375
|
+
this.equipment.messages.clear();
|
|
376
|
+
|
|
368
377
|
//this.onchange(state, function () { self.dirty = true; });
|
|
369
378
|
this._dt.emitter.on('change', function () {
|
|
370
379
|
self.data.time = self._dt.format();
|
|
@@ -378,7 +387,6 @@ export class State implements IState {
|
|
|
378
387
|
versionCheck.checkGitRemote();
|
|
379
388
|
});
|
|
380
389
|
this.status = 0; // Initializing
|
|
381
|
-
this.equipment = new EquipmentState(this.data, 'equipment');
|
|
382
390
|
this.equipment.controllerType = this._controllerType;
|
|
383
391
|
this.temps = new TemperatureState(this.data, 'temps');
|
|
384
392
|
this.pumps = new PumpStateCollection(this.data, 'pumps');
|
|
@@ -392,6 +400,7 @@ export class State implements IState {
|
|
|
392
400
|
this.lightGroups = new LightGroupStateCollection(this.data, 'lightGroups');
|
|
393
401
|
this.virtualCircuits = new VirtualCircuitStateCollection(this.data, 'virtualCircuits');
|
|
394
402
|
this.chemControllers = new ChemControllerStateCollection(this.data, 'chemControllers');
|
|
403
|
+
this.chemDosers = new ChemDoserStateCollection(this.data, 'chemDosers');
|
|
395
404
|
this.covers = new CoverStateCollection(this.data, 'covers');
|
|
396
405
|
this.filters = new FilterStateCollection(this.data, 'filters');
|
|
397
406
|
this.comms = new CommsState();
|
|
@@ -433,6 +442,7 @@ export class State implements IState {
|
|
|
433
442
|
public covers: CoverStateCollection;
|
|
434
443
|
public filters: FilterStateCollection;
|
|
435
444
|
public chemControllers: ChemControllerStateCollection;
|
|
445
|
+
public chemDosers: ChemDoserStateCollection;
|
|
436
446
|
public comms: CommsState;
|
|
437
447
|
public appVersion: AppVersionState;
|
|
438
448
|
|
|
@@ -505,6 +515,8 @@ export interface ICircuitState {
|
|
|
505
515
|
isActive?: boolean;
|
|
506
516
|
startDelay?: boolean;
|
|
507
517
|
stopDelay?: boolean;
|
|
518
|
+
manualPriorityActive?: boolean;
|
|
519
|
+
dataName?: string;
|
|
508
520
|
}
|
|
509
521
|
|
|
510
522
|
interface IEqStateCreator<T> { ctor(data: any, name: string, parent?): T; }
|
|
@@ -639,6 +651,10 @@ class EqStateCollection<T> {
|
|
|
639
651
|
}
|
|
640
652
|
return rem;
|
|
641
653
|
}
|
|
654
|
+
public removeItemByIndex(ndx: number) {
|
|
655
|
+
return this.data.splice(ndx, 1);
|
|
656
|
+
}
|
|
657
|
+
|
|
642
658
|
public createItem(data: any): T { return new EqState(data) as unknown as T; }
|
|
643
659
|
public clear() { this.data.length = 0; }
|
|
644
660
|
public get length(): number { return typeof (this.data) !== 'undefined' ? this.data.length : 0; }
|
|
@@ -730,6 +746,8 @@ export class EquipmentState extends EqState {
|
|
|
730
746
|
public set name(val: string) { this.setDataVal('name', val); }
|
|
731
747
|
public get model(): string { return this.data.model; }
|
|
732
748
|
public set model(val: string) { this.setDataVal('model', val); }
|
|
749
|
+
public get single(): boolean { return this.data.single; }
|
|
750
|
+
public set single(val: boolean) { this.setDataVal('single', val); }
|
|
733
751
|
public get shared(): boolean { return this.data.shared; }
|
|
734
752
|
public set shared(val: boolean) { this.setDataVal('shared', val); }
|
|
735
753
|
public get dual(): boolean { return this.data.dual; }
|
|
@@ -805,12 +823,14 @@ export class EquipmentMessages extends EqStateCollection<EquipmentMessage> {
|
|
|
805
823
|
rem = this.data.splice(i, 1);
|
|
806
824
|
}
|
|
807
825
|
}
|
|
826
|
+
if (typeof rem !== 'undefined') webApp.emitToClients('sysmessages', this.get(true));
|
|
808
827
|
return typeof rem !== 'undefined' ? new EquipmentMessage(rem, undefined, undefined) : undefined;
|
|
809
828
|
}
|
|
810
829
|
// For lack of a better term category includes the equipment identifier if supplied.
|
|
811
830
|
public removeItemByCategory(category: string) {
|
|
812
831
|
let rem: EquipmentMessage[] = [];
|
|
813
832
|
let cmr = EquipmentMessage.parseMessageCode(category);
|
|
833
|
+
let hasChanges = false;
|
|
814
834
|
for (let i = this.data.length - 1; i >= 0; i--) {
|
|
815
835
|
if (typeof (this.data[i].code) !== 'undefined') {
|
|
816
836
|
let cm = EquipmentMessage.parseMessageCode(this.data.code);
|
|
@@ -819,17 +839,20 @@ export class EquipmentMessages extends EqStateCollection<EquipmentMessage> {
|
|
|
819
839
|
if (typeof cmr.messageId === 'undefined' || cm.messageId === cmr.messageId) {
|
|
820
840
|
let data = this.data.splice(i, 1);
|
|
821
841
|
rem.push(new EquipmentMessage(data, undefined, undefined));
|
|
842
|
+
hasChanges = true;
|
|
822
843
|
}
|
|
823
844
|
}
|
|
824
845
|
}
|
|
825
846
|
}
|
|
826
847
|
}
|
|
848
|
+
if (hasChanges) webApp.emitToClients('sysmessages', this.get(true));
|
|
827
849
|
return rem;
|
|
828
850
|
}
|
|
829
851
|
public setMessageByCode(code: string, severity: string | number, message: string): EquipmentMessage {
|
|
830
852
|
let msg = this.getItemByCode(code, true);
|
|
831
853
|
msg.severity = sys.board.valueMaps.eqMessageSeverities.encode(severity, 0);
|
|
832
854
|
msg.message = message;
|
|
855
|
+
webApp.emitToClients('sysmessages', this.get(true));
|
|
833
856
|
return msg;
|
|
834
857
|
}
|
|
835
858
|
}
|
|
@@ -910,13 +933,18 @@ export class PumpState extends EqState {
|
|
|
910
933
|
public get name(): string { return this.data.name; }
|
|
911
934
|
public set name(val: string) { this.setDataVal('name', val); }
|
|
912
935
|
public get rpm(): number { return this.data.rpm; }
|
|
913
|
-
public set rpm(val: number) { this.setDataVal('rpm', val
|
|
936
|
+
public set rpm(val: number) { this.setDataVal('rpm', val); }
|
|
937
|
+
//public set rpm(val: number) { this.setDataVal('rpm', val, this.exceedsThreshold(this.data.rpm, val)); }
|
|
914
938
|
public get relay(): number { return this.data.relay; }
|
|
915
939
|
public set relay(val: number) { this.setDataVal('relay', val); }
|
|
940
|
+
public get program(): number { return this.data.program; }
|
|
941
|
+
public set program(val: number) { this.setDataVal('program', val); }
|
|
916
942
|
public get watts(): number { return this.data.watts; }
|
|
917
|
-
public set watts(val: number) { this.setDataVal('watts', val
|
|
943
|
+
public set watts(val: number) { this.setDataVal('watts', val); }
|
|
944
|
+
//public set watts(val: number) { this.setDataVal('watts', val, this.exceedsThreshold(this.data.watts, val)); }
|
|
918
945
|
public get flow(): number { return this.data.flow; }
|
|
919
|
-
public set flow(val: number) { this.setDataVal('flow', val
|
|
946
|
+
public set flow(val: number) { this.setDataVal('flow', val); }
|
|
947
|
+
//public set flow(val: number) { this.setDataVal('flow', val, this.exceedsThreshold(this.data.flow, val)); }
|
|
920
948
|
public get mode(): number { return this.data.mode; }
|
|
921
949
|
public set mode(val: number) { this.setDataVal('mode', val); }
|
|
922
950
|
public get driveState(): number { return this.data.driveState; }
|
|
@@ -938,7 +966,7 @@ export class PumpState extends EqState {
|
|
|
938
966
|
this.hasChanged = true;
|
|
939
967
|
}
|
|
940
968
|
}
|
|
941
|
-
|
|
969
|
+
/* public get virtualControllerStatus(): number {
|
|
942
970
|
return typeof (this.data.virtualControllerStatus) !== 'undefined' ? this.data.virtualControllerStatus.val : -1;
|
|
943
971
|
}
|
|
944
972
|
public set virtualControllerStatus(val: number) {
|
|
@@ -946,7 +974,7 @@ export class PumpState extends EqState {
|
|
|
946
974
|
this.data.virtualControllerStatus = sys.board.valueMaps.virtualControllerStatus.transform(val);
|
|
947
975
|
this.hasChanged = true;
|
|
948
976
|
}
|
|
949
|
-
}
|
|
977
|
+
} */
|
|
950
978
|
public get targetSpeed(): number { return this.data.targetSpeed; } // used for virtual controller
|
|
951
979
|
public set targetSpeed(val: number) { this.setDataVal('targetSpeed', val); }
|
|
952
980
|
public get type() { return typeof (this.data.type) !== 'undefined' ? this.data.type.val : -1; }
|
|
@@ -985,23 +1013,43 @@ export class PumpState extends EqState {
|
|
|
985
1013
|
pump.speedStepSize = cpump.speedStepSize;
|
|
986
1014
|
pump.flowStepSize = cpump.flowStepSize;
|
|
987
1015
|
pump.circuits = [];
|
|
988
|
-
for (let i = 0; i <
|
|
1016
|
+
for (let i = 0; i < cpump.circuits.length; i++) {
|
|
989
1017
|
let c = cpump.circuits.getItemByIndex(i).get(true);
|
|
990
1018
|
c.circuit = state.circuits.getInterfaceById(c.circuit).get(true);
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1019
|
+
switch (pump.type.name) {
|
|
1020
|
+
case 'vf':
|
|
1021
|
+
c.units = sys.board.valueMaps.pumpUnits.transformByName('gpm');
|
|
1022
|
+
break;
|
|
1023
|
+
case 'hwvs':
|
|
1024
|
+
case 'vssvrs':
|
|
1025
|
+
case 'vs':
|
|
1026
|
+
c.units = sys.board.valueMaps.pumpUnits.transformByName('rpm');
|
|
1027
|
+
break;
|
|
1028
|
+
case 'ss':
|
|
1029
|
+
case 'ds':
|
|
1030
|
+
case 'sf':
|
|
1031
|
+
case 'hwrly':
|
|
1032
|
+
c.units = 'undefined';
|
|
1033
|
+
break;
|
|
1034
|
+
default:
|
|
1035
|
+
c.units = sys.board.valueMaps.pumpUnits.transform(c.units || 0);
|
|
1036
|
+
break;
|
|
1003
1037
|
}
|
|
1004
|
-
|
|
1038
|
+
// RKS: 04-08-22 - This is just wrong. If the user did not define circuits then they should not be sent down and it creates a whole host of issues.
|
|
1039
|
+
//if (typeof c.circuit.id === 'undefined' || typeof c.circuit.name === 'undefined') {
|
|
1040
|
+
// // return "blank" circuit if none defined
|
|
1041
|
+
// c.circuit.id = 0;
|
|
1042
|
+
// c.circuit.name = 'Not Used';
|
|
1043
|
+
// if (sys.board.valueMaps.pumpTypes.getName(cpump.type) === 'vf') {
|
|
1044
|
+
// c.units = sys.board.valueMaps.pumpUnits.getValue('gpm');
|
|
1045
|
+
// c.circuit.flow = 0;
|
|
1046
|
+
// }
|
|
1047
|
+
// else {
|
|
1048
|
+
// c.units = sys.board.valueMaps.pumpUnits.getValue('rpm');
|
|
1049
|
+
// c.circuit.speed = 0;
|
|
1050
|
+
// }
|
|
1051
|
+
//}
|
|
1052
|
+
//c.units = sys.board.valueMaps.pumpUnits.transform(c.units);
|
|
1005
1053
|
pump.circuits.push(c);
|
|
1006
1054
|
}
|
|
1007
1055
|
pump.circuits.sort((a, b) => { return a.id > b.id ? 1 : -1; });
|
|
@@ -1046,6 +1094,8 @@ export class ScheduleState extends EqState {
|
|
|
1046
1094
|
public set endTime(val: number) { this.setDataVal('endTime', val); }
|
|
1047
1095
|
public get circuit(): number { return this.data.circuit; }
|
|
1048
1096
|
public set circuit(val: number) { this.setDataVal('circuit', val); }
|
|
1097
|
+
public get disabled(): boolean { return this.data.disabled; }
|
|
1098
|
+
public set disabled(val: boolean) { this.setDataVal('disabled', val); }
|
|
1049
1099
|
public get scheduleType(): number { return typeof (this.data.scheduleType) !== 'undefined' ? this.data.scheduleType.val : undefined; }
|
|
1050
1100
|
public set scheduleType(val: number) {
|
|
1051
1101
|
if (this.scheduleType !== val) {
|
|
@@ -1097,6 +1147,8 @@ export class ScheduleState extends EqState {
|
|
|
1097
1147
|
public set coolSetpoint(val: number) { this.setDataVal('coolSetpoint', val); }
|
|
1098
1148
|
public get isOn(): boolean { return this.data.isOn; }
|
|
1099
1149
|
public set isOn(val: boolean) { this.setDataVal('isOn', val); }
|
|
1150
|
+
public get manualPriorityActive(): boolean { return this.data.manualPriorityActive; }
|
|
1151
|
+
public set manualPriorityActive(val: boolean) { this.setDataVal('manualPriorityActive', val); }
|
|
1100
1152
|
public getExtended() {
|
|
1101
1153
|
let sched = this.get(true); // Always operate on a copy.
|
|
1102
1154
|
//if (typeof this.circuit !== 'undefined')
|
|
@@ -1124,6 +1176,7 @@ export interface ICircuitGroupState {
|
|
|
1124
1176
|
dataName: string;
|
|
1125
1177
|
lightingTheme?: number;
|
|
1126
1178
|
showInFeatures?: boolean;
|
|
1179
|
+
manualPriorityActive?: boolean;
|
|
1127
1180
|
get(bCopy?: boolean);
|
|
1128
1181
|
emitEquipmentChange();
|
|
1129
1182
|
}
|
|
@@ -1180,10 +1233,15 @@ export class CircuitGroupState extends EqState implements ICircuitGroupState, IC
|
|
|
1180
1233
|
public set isActive(val: boolean) { this.setDataVal('isActive', val); }
|
|
1181
1234
|
public get showInFeatures(): boolean { return typeof this.data.showInFeatures === 'undefined' ? true : this.data.showInFeatures; }
|
|
1182
1235
|
public set showInFeatures(val: boolean) { this.setDataVal('showInFeatures', val); }
|
|
1236
|
+
public get manualPriorityActive(): boolean { return this.data.manualPriorityActive; }
|
|
1237
|
+
public set manualPriorityActive(val: boolean) { this.setDataVal('manualPriorityActive', val); }
|
|
1183
1238
|
public getExtended() {
|
|
1184
1239
|
let sgrp = this.get(true); // Always operate on a copy.
|
|
1185
1240
|
if (typeof sgrp.showInFeatures === 'undefined') sgrp.showInFeatures = true;
|
|
1241
|
+
|
|
1186
1242
|
let cgrp = sys.circuitGroups.getItemById(this.id);
|
|
1243
|
+
sgrp.showInFeatures = this.showInFeatures = cgrp.showInFeatures;
|
|
1244
|
+
sgrp.isActive = this.isActive = cgrp.isActive;
|
|
1187
1245
|
sgrp.circuits = [];
|
|
1188
1246
|
for (let i = 0; i < cgrp.circuits.length; i++) {
|
|
1189
1247
|
let cgc = cgrp.circuits.getItemByIndex(i).get(true);
|
|
@@ -1264,6 +1322,8 @@ export class LightGroupState extends EqState implements ICircuitGroupState, ICir
|
|
|
1264
1322
|
public set isOn(val: boolean) { this.setDataVal('isOn', val); }
|
|
1265
1323
|
public get isActive(): boolean { return this.data.isActive; }
|
|
1266
1324
|
public set isActive(val: boolean) { this.setDataVal('isActive', val); }
|
|
1325
|
+
public get manualPriorityActive(): boolean { return this.data.manualPriorityActive; }
|
|
1326
|
+
public set manualPriorityActive(val: boolean) { this.setDataVal('manualPriorityActive', val); }
|
|
1267
1327
|
public async setThemeAsync(val: number) { return sys.board.circuits.setLightThemeAsync; }
|
|
1268
1328
|
public getExtended() {
|
|
1269
1329
|
let sgrp = this.get(true); // Always operate on a copy.
|
|
@@ -1334,6 +1394,8 @@ export class BodyTempState extends EqState {
|
|
|
1334
1394
|
if (typeof this.heaterCooldownDelay === 'undefined') this.data.heaterCooldownDelay = false;
|
|
1335
1395
|
if (typeof this.data.startDelay === 'undefined') this.data.startDelay = false;
|
|
1336
1396
|
if (typeof this.data.stopDelay === 'undefined') this.data.stopDelay = false;
|
|
1397
|
+
if (typeof this.data.showInDashboard === 'undefined') this.data.showInDashboard = true;
|
|
1398
|
+
if (typeof this.data.heatMode === 'undefined') this.data.heatMode = sys.board.valueMaps.heatModes.transform(0);
|
|
1337
1399
|
}
|
|
1338
1400
|
public get id(): number { return this.data.id; }
|
|
1339
1401
|
public set id(val: number) { this.setDataVal('id', val); }
|
|
@@ -1376,6 +1438,8 @@ export class BodyTempState extends EqState {
|
|
|
1376
1438
|
public set startDelay(val: boolean) { this.setDataVal('startDelay', val); }
|
|
1377
1439
|
public get stopDelay(): boolean { return this.data.stopDelay; }
|
|
1378
1440
|
public set stopDelay(val: boolean) { this.setDataVal('stopDelay', val); }
|
|
1441
|
+
public get showInDashboard(): boolean { return this.data.showInDashboard; }
|
|
1442
|
+
public set showInDashboard(val: boolean) { this.setDataVal('showInDashboard', val); }
|
|
1379
1443
|
|
|
1380
1444
|
public get isCovered(): boolean { return this.data.isCovered; }
|
|
1381
1445
|
public set isCovered(val: boolean) { this.setDataVal('isCovered', val); }
|
|
@@ -1544,7 +1608,6 @@ export class FeatureStateCollection extends EqStateCollection<FeatureState> {
|
|
|
1544
1608
|
}
|
|
1545
1609
|
}
|
|
1546
1610
|
}
|
|
1547
|
-
|
|
1548
1611
|
export class FeatureState extends EqState implements ICircuitState {
|
|
1549
1612
|
public dataName: string = 'feature';
|
|
1550
1613
|
public initData() {
|
|
@@ -1578,6 +1641,8 @@ export class FeatureState extends EqState implements ICircuitState {
|
|
|
1578
1641
|
public set freezeProtect(val: boolean) { this.setDataVal('freezeProtect', val); }
|
|
1579
1642
|
public get isActive(): boolean { return this.data.isActive; }
|
|
1580
1643
|
public set isActive(val: boolean) { this.setDataVal('isActive', val); }
|
|
1644
|
+
public get manualPriorityActive(): boolean { return this.data.manualPriorityActive; }
|
|
1645
|
+
public set manualPriorityActive(val: boolean) { this.setDataVal('manualPriorityActive', val); }
|
|
1581
1646
|
}
|
|
1582
1647
|
export class VirtualCircuitState extends EqState implements ICircuitState {
|
|
1583
1648
|
public dataName: string = 'virtualCircuit';
|
|
@@ -1724,6 +1789,8 @@ export class CircuitState extends EqState implements ICircuitState {
|
|
|
1724
1789
|
public set lockoutOn(val: boolean) { this.setDataVal('lockoutOn', val); }
|
|
1725
1790
|
public get lockoutOff(): boolean { return this.data.lockoutOff; }
|
|
1726
1791
|
public set lockoutOff(val: boolean) { this.setDataVal('lockoutOff', val); }
|
|
1792
|
+
public get manualPriorityActive(): boolean { return this.data.manualPriorityActive; }
|
|
1793
|
+
public set manualPriorityActive(val: boolean) { this.setDataVal('manualPriorityActive', val); }
|
|
1727
1794
|
}
|
|
1728
1795
|
export class ValveStateCollection extends EqStateCollection<ValveState> {
|
|
1729
1796
|
public createItem(data: any): ValveState { return new ValveState(data); }
|
|
@@ -1800,9 +1867,16 @@ export class CoverState extends EqState {
|
|
|
1800
1867
|
public set isClosed(val: boolean) { this.setDataVal('isClosed', val); }
|
|
1801
1868
|
}
|
|
1802
1869
|
export class ChlorinatorStateCollection extends EqStateCollection<ChlorinatorState> {
|
|
1870
|
+
public superChlor: { id: number, lastDispatch: number, reference: number }[] = [];
|
|
1871
|
+
public getSuperChlor(id: number): { id: number, lastDispatch: number, reference: number } {
|
|
1872
|
+
let sc = this.superChlor.find(elem => id === elem.id);
|
|
1873
|
+
if (typeof sc === 'undefined') {
|
|
1874
|
+
sc = { id: id, lastDispatch: 0, reference: 0 };
|
|
1875
|
+
this.superChlor.push(sc);
|
|
1876
|
+
}
|
|
1877
|
+
return sc;
|
|
1878
|
+
}
|
|
1803
1879
|
public createItem(data: any): ChlorinatorState { return new ChlorinatorState(data); }
|
|
1804
|
-
public superChlorReference: number = 0;
|
|
1805
|
-
public lastDispatchSuperChlor: number = 0;
|
|
1806
1880
|
public cleanupState() {
|
|
1807
1881
|
for (let i = this.data.length - 1; i >= 0; i--) {
|
|
1808
1882
|
if (isNaN(this.data[i].id)) this.data.splice(i, 1);
|
|
@@ -1815,10 +1889,10 @@ export class ChlorinatorStateCollection extends EqStateCollection<ChlorinatorSta
|
|
|
1815
1889
|
let c = cfg[i];
|
|
1816
1890
|
let s = this.getItemById(cfg[i].id, true);
|
|
1817
1891
|
s.type = c.type;
|
|
1892
|
+
s.model = c.model;
|
|
1818
1893
|
s.name = c.name;
|
|
1819
1894
|
s.isActive = c.isActive;
|
|
1820
1895
|
}
|
|
1821
|
-
|
|
1822
1896
|
}
|
|
1823
1897
|
}
|
|
1824
1898
|
export class ChlorinatorState extends EqState {
|
|
@@ -1868,6 +1942,13 @@ export class ChlorinatorState extends EqState {
|
|
|
1868
1942
|
this.hasChanged = true;
|
|
1869
1943
|
}
|
|
1870
1944
|
}
|
|
1945
|
+
public get model(): number { return typeof (this.data.model) !== 'undefined' ? this.data.model.val : 0; }
|
|
1946
|
+
public set model(val: number) {
|
|
1947
|
+
if (this.model !== val) {
|
|
1948
|
+
this.data.model = sys.board.valueMaps.chlorinatorModel.transform(val);
|
|
1949
|
+
this.hasChanged = true;
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1871
1952
|
public get body(): number { return typeof (this.data.body) !== 'undefined' ? this.data.body.val : -1; }
|
|
1872
1953
|
public set body(val: number) {
|
|
1873
1954
|
if (this.body !== val) {
|
|
@@ -1881,32 +1962,15 @@ export class ChlorinatorState extends EqState {
|
|
|
1881
1962
|
public set spaSetpoint(val: number) { this.setDataVal('spaSetpoint', val); }
|
|
1882
1963
|
public get superChlorHours(): number { return this.data.superChlorHours; }
|
|
1883
1964
|
public set superChlorHours(val: number) { this.setDataVal('superChlorHours', val); }
|
|
1965
|
+
public get saltTarget(): number { return this.data.saltTarget; }
|
|
1966
|
+
public set saltTarget(val: number) { this.setDataVal('saltTarget', val); }
|
|
1884
1967
|
public get saltRequired(): number { return this.data.saltRequired; }
|
|
1885
1968
|
public get saltLevel(): number { return this.data.saltLevel; }
|
|
1886
1969
|
public set saltLevel(val: number) {
|
|
1887
|
-
this.
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
let capacity = 0;
|
|
1891
|
-
for (let i = 0; i < sys.bodies.length; i++) {
|
|
1892
|
-
let body = sys.bodies.getItemById(i + 1);
|
|
1893
|
-
if (this.body === 32)
|
|
1894
|
-
capacity = Math.max(body.capacity, capacity);
|
|
1895
|
-
else if (this.body === 0 && body.id === 1)
|
|
1896
|
-
capacity = Math.max(body.capacity, capacity);
|
|
1897
|
-
else if (this.body === 1 && body.id === 2)
|
|
1898
|
-
capacity = Math.max(body.capacity, capacity);
|
|
1899
|
-
}
|
|
1900
|
-
if (capacity > 0 && this.saltLevel < 3100) {
|
|
1901
|
-
// Salt requirements calculation.
|
|
1902
|
-
// Target - SaltLevel = NeededSalt = 3400 - 2900 = 500ppm
|
|
1903
|
-
// So to raise 120ppm you need to add 1lb per 1000 gal.
|
|
1904
|
-
// (NeededSalt/120ppm) * (MaxBody/1000) = (500/120) * (33000/1000) = 137.5lbs of salt required to hit target.
|
|
1905
|
-
let dec = Math.pow(10, 2);
|
|
1906
|
-
this.data.saltRequired = Math.round((((3400 - this.saltLevel) / 120) * (capacity / 1000)) * dec) / dec;
|
|
1970
|
+
if (this.saltLevel !== val) {
|
|
1971
|
+
this.setDataVal('saltLevel', val);
|
|
1972
|
+
this.calcSaltRequired();
|
|
1907
1973
|
}
|
|
1908
|
-
else
|
|
1909
|
-
this.data.saltRequired = 0;
|
|
1910
1974
|
}
|
|
1911
1975
|
public get superChlor(): boolean { return this.data.superChlor; }
|
|
1912
1976
|
public set superChlor(val: boolean) {
|
|
@@ -1915,41 +1979,92 @@ export class ChlorinatorState extends EqState {
|
|
|
1915
1979
|
}
|
|
1916
1980
|
public get superChlorRemaining(): number { return this.data.superChlorRemaining || 0; }
|
|
1917
1981
|
public set superChlorRemaining(val: number) {
|
|
1982
|
+
if (val === this.data.superChlorRemaining) return;
|
|
1918
1983
|
let remaining: number;
|
|
1919
|
-
|
|
1920
|
-
|
|
1984
|
+
let sc = state.chlorinators.getSuperChlor(this.id);
|
|
1985
|
+
let chlor = sys.chlorinators.getItemById(this.id);
|
|
1986
|
+
if (chlor.master === 1) {
|
|
1987
|
+
// If we are 10 seconds different then we need to send it off and save the data.
|
|
1988
|
+
if (Math.floor(val / 10) !== Math.floor(this.superChlorRemaining / 10)) {
|
|
1989
|
+
this.hasChanged = true;
|
|
1990
|
+
remaining = val;
|
|
1991
|
+
sc.reference = Math.floor(new Date().getTime() / 1000);
|
|
1992
|
+
this.setDataVal('superChlorRemaining', remaining);
|
|
1993
|
+
}
|
|
1994
|
+
else if (val <= 0)
|
|
1995
|
+
remaining = 0;
|
|
1996
|
+
else
|
|
1997
|
+
remaining = this.superChlorRemaining;
|
|
1998
|
+
}
|
|
1999
|
+
else if (chlor.master === 2) {
|
|
2000
|
+
// If we are 10 seconds different then we need to send it off and save the data.
|
|
2001
|
+
if (Math.floor(val / 10) !== Math.floor(this.superChlorRemaining / 10)) {
|
|
2002
|
+
this.hasChanged = true;
|
|
2003
|
+
remaining = val;
|
|
2004
|
+
sc.reference = Math.floor(new Date().getTime() / 1000);
|
|
2005
|
+
this.setDataVal('superChlorRemaining', remaining);
|
|
2006
|
+
}
|
|
1921
2007
|
}
|
|
1922
2008
|
else if (sys.controllerType === 'intellicenter') {
|
|
1923
2009
|
// Trim the seconds off both of these as we will be keeping the seconds separately since this
|
|
1924
2010
|
// only reports in minutes. That way our seconds become self healing.
|
|
1925
2011
|
if (Math.ceil(this.superChlorRemaining / 60) * 60 !== val) {
|
|
1926
|
-
|
|
2012
|
+
sc.reference = Math.floor(new Date().getTime() / 1000); // Get the epoc and strip the milliseconds.
|
|
1927
2013
|
this.hasChanged = true;
|
|
1928
2014
|
}
|
|
1929
|
-
let secs = Math.floor(new Date().getTime() / 1000) -
|
|
2015
|
+
let secs = Math.floor(new Date().getTime() / 1000) - sc.reference;
|
|
1930
2016
|
remaining = Math.max(0, val - Math.min(secs, 60));
|
|
2017
|
+
if (sc.lastDispatch - 5 > remaining) this.hasChanged = true;
|
|
2018
|
+
this.data.superChlorRemaining = remaining;
|
|
1931
2019
|
}
|
|
1932
2020
|
else {
|
|
1933
2021
|
// *Touch only reports superchlor hours remaining.
|
|
1934
2022
|
// If we have the same hours as existing, retain the mins + secs
|
|
1935
2023
|
if (Math.ceil(this.superChlorRemaining / 3600) * 60 !== val / 60) {
|
|
1936
|
-
|
|
2024
|
+
sc.reference = Math.floor(new Date().getTime() / 1000); // Get the epoc and strip the milliseconds.
|
|
1937
2025
|
this.hasChanged = true;
|
|
1938
2026
|
}
|
|
1939
|
-
let secs = Math.floor(new Date().getTime() / 1000) -
|
|
2027
|
+
let secs = Math.floor(new Date().getTime() / 1000) - sc.reference;
|
|
1940
2028
|
remaining = Math.max(0, val - Math.min(secs, 3600));
|
|
2029
|
+
if (sc.lastDispatch - 5 > remaining) this.hasChanged = true;
|
|
2030
|
+
this.data.superChlorRemaining = remaining;
|
|
1941
2031
|
}
|
|
1942
|
-
if (
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
if (remaining > 0)
|
|
1946
|
-
this.setDataVal('superChlor', true);
|
|
1947
|
-
else
|
|
1948
|
-
this.setDataVal('superChlor', false);
|
|
2032
|
+
if (this.hasChanged) sc.lastDispatch = remaining;
|
|
2033
|
+
this.setDataVal('superChlor', remaining > 0);
|
|
2034
|
+
chlor.superChlor = remaining > 0;
|
|
1949
2035
|
}
|
|
2036
|
+
public calcSaltRequired(saltTarget?: number) : number {
|
|
2037
|
+
if (typeof saltTarget === 'undefined') saltTarget = sys.chlorinators.getItemById(this.id, false).saltTarget || 0;
|
|
2038
|
+
let saltRequired = 0;
|
|
2039
|
+
//this.data.saltLevel = val;
|
|
2040
|
+
// Calculate the salt required.
|
|
2041
|
+
let capacity = 0;
|
|
2042
|
+
for (let i = 0; i < sys.bodies.length; i++) {
|
|
2043
|
+
let body = sys.bodies.getItemById(i + 1);
|
|
2044
|
+
if (this.body === 32)
|
|
2045
|
+
capacity = Math.max(body.capacity, capacity);
|
|
2046
|
+
else if (this.body === 0 && body.id === 1)
|
|
2047
|
+
capacity = Math.max(body.capacity, capacity);
|
|
2048
|
+
else if (this.body === 1 && body.id === 2)
|
|
2049
|
+
capacity = Math.max(body.capacity, capacity);
|
|
2050
|
+
}
|
|
2051
|
+
if (capacity > 0 && this.saltLevel < saltTarget) {
|
|
2052
|
+
// Salt requirements calculation.
|
|
2053
|
+
// Target - SaltLevel = NeededSalt = 3400 - 2900 = 500ppm
|
|
2054
|
+
// So to raise 120ppm you need to add 1lb per 1000 gal.
|
|
2055
|
+
// (NeededSalt/120ppm) * (MaxBody/1000) = (500/120) * (33000/1000) = 137.5lbs of salt required to hit target.
|
|
2056
|
+
let dec = Math.pow(10, 2);
|
|
2057
|
+
saltRequired = Math.round((((saltTarget - this.saltLevel) / 120) * (capacity / 1000)) * dec) / dec;
|
|
2058
|
+
if (this.saltRequired < 0) saltRequired = 0;
|
|
2059
|
+
}
|
|
2060
|
+
this.setDataVal('saltRequired', saltRequired);
|
|
2061
|
+
return saltRequired;
|
|
2062
|
+
}
|
|
2063
|
+
public getEmitData() { return this.getExtended(); }
|
|
1950
2064
|
public getExtended(): any {
|
|
1951
2065
|
let schlor = this.get(true);
|
|
1952
2066
|
let chlor = sys.chlorinators.getItemById(this.id, false);
|
|
2067
|
+
schlor.saltTarget = chlor.saltTarget;
|
|
1953
2068
|
schlor.lockSetpoints = chlor.disabled || chlor.isDosing;
|
|
1954
2069
|
return schlor;
|
|
1955
2070
|
}
|
|
@@ -1976,8 +2091,278 @@ export class ChemControllerStateCollection extends EqStateCollection<ChemControl
|
|
|
1976
2091
|
}
|
|
1977
2092
|
}
|
|
1978
2093
|
}
|
|
2094
|
+
export class ChemDoserStateCollection extends EqStateCollection<ChemDoserState> {
|
|
2095
|
+
public createItem(data: any): ChemDoserState { return new ChemDoserState(data); }
|
|
2096
|
+
public cleanupState() {
|
|
2097
|
+
for (let i = this.data.length - 1; i >= 0; i--) {
|
|
2098
|
+
if (isNaN(this.data[i].id)) this.data.splice(i, 1);
|
|
2099
|
+
else {
|
|
2100
|
+
if (typeof sys.chemControllers.find(elem => elem.id === this.data[i].id) === 'undefined') this.removeItemById(this.data[i].id);
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
// Make sure we have at least the items that exist in the config.
|
|
2104
|
+
let cfg = sys.chemControllers.toArray();
|
|
2105
|
+
for (let i = 0; i < cfg.length; i++) {
|
|
2106
|
+
let c = cfg[i];
|
|
2107
|
+
let s = this.getItemById(cfg[i].id, true);
|
|
2108
|
+
s.body = c.body;
|
|
2109
|
+
s.name = c.name;
|
|
2110
|
+
s.type = c.type;
|
|
2111
|
+
s.isActive = c.isActive;
|
|
2112
|
+
}
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
export interface IChemControllerState {
|
|
2116
|
+
flowDetected: boolean;
|
|
2117
|
+
isBodyOn: boolean;
|
|
2118
|
+
emitEquipmentChange: () => void;
|
|
2119
|
+
}
|
|
2120
|
+
export interface IChemicalState {
|
|
2121
|
+
enabled: boolean;
|
|
2122
|
+
currentDose: ChemicalDoseState;
|
|
2123
|
+
manualDosing: boolean;
|
|
2124
|
+
manualMixing: boolean;
|
|
2125
|
+
dosingTimeRemaining: number;
|
|
2126
|
+
dosingVolumeRemaining: number;
|
|
2127
|
+
volumeDosed: number;
|
|
2128
|
+
timeDosed: number;
|
|
2129
|
+
endDose: (dtEnd?: Date, status?: string, volumeDosed?: number, timeDosed?: number) => ChemicalDoseState;
|
|
2130
|
+
appendDose: (volumeDosed: number, timeDosed: number) => ChemicalDoseState;
|
|
2131
|
+
tank: ChemicalTankState;
|
|
2132
|
+
pump: ChemicalPumpState;
|
|
2133
|
+
doseHistory: ChemicalDoseState[];
|
|
2134
|
+
freezeProtect: boolean;
|
|
2135
|
+
mixTimeRemaining: number;
|
|
2136
|
+
delayTimeRemaining: number;
|
|
2137
|
+
flowDelay: boolean;
|
|
2138
|
+
dailyVolumeDosed: number;
|
|
2139
|
+
chemType: string;
|
|
2140
|
+
dosingStatus: number;
|
|
2141
|
+
chemController: IChemControllerState;
|
|
2142
|
+
chlor?: ChemicalChlorState;
|
|
2143
|
+
|
|
2144
|
+
}
|
|
2145
|
+
export class ChemDoserState extends EqState implements IChemicalState, IChemControllerState {
|
|
2146
|
+
public initData() {
|
|
2147
|
+
if (typeof this.data.flowDetected === 'undefined') this.data.flowDetected = false;
|
|
2148
|
+
if (typeof this.data.flowSensor === 'undefined') this.data.flowSensor = {};
|
|
2149
|
+
if (typeof this.data.alarms === 'undefined') { let a = this.alarms; }
|
|
2150
|
+
if (typeof this.data.warnings === 'undefined') { let w = this.warnings; }
|
|
2151
|
+
if (typeof this.data.tank === 'undefined') this.data.tank = { capacity: 0, level: 0, units: 0 };
|
|
2152
|
+
if (typeof this.data.pump === 'undefined') this.data.pump = { isDosing: false };
|
|
2153
|
+
if (typeof this.data.dosingTimeRemaining === 'undefined') this.data.dosingTimeRemaining = 0;
|
|
2154
|
+
if (typeof this.data.delayTimeRemaining === 'undefined') this.data.delayTimeRemaining = 0;
|
|
2155
|
+
if (typeof this.data.dosingVolumeRemaining === 'undefined') this.data.dosingVolumeRemaining = 0;
|
|
2156
|
+
if (typeof this.data.doseVolume === 'undefined') this.data.doseVolume = 0;
|
|
2157
|
+
if (typeof this.data.doseTime === 'undefined') this.data.doseTime = 0;
|
|
2158
|
+
if (typeof this.data.lockout === 'undefined') this.data.lockout = false;
|
|
2159
|
+
if (typeof this.data.level === 'undefined') this.data.level = 0;
|
|
2160
|
+
if (typeof this.data.mixTimeRemaining === 'undefined') this.data.mixTimeRemaining = 0;
|
|
2161
|
+
if (typeof this.data.dailyLimitReached === 'undefined') this.data.dailyLimitReached = false;
|
|
2162
|
+
if (typeof this.data.manualDosing === 'undefined') this.data.manualDosing = false;
|
|
2163
|
+
if (typeof this.data.manualMixing === 'undefined') this.data.manualMixing = false;
|
|
2164
|
+
if (typeof this.data.flowDelay === 'undefined') this.data.flowDelay = false;
|
|
2165
|
+
if (typeof this.data.dosingStatus === 'undefined') this.dosingStatus = 2;
|
|
2166
|
+
if (typeof this.data.enabled === 'undefined') this.data.enabled = true;
|
|
2167
|
+
if (typeof this.data.freezeProtect === 'undefined') this.data.freezeProtect = false;
|
|
2168
|
+
if (typeof this.data.setpoint === 'undefined') this.data.setpoint = 100;
|
|
2169
|
+
if (typeof this.data.suspendDosing === 'undefined') this.data.suspendDosing = false;
|
|
2170
|
+
}
|
|
2171
|
+
public dataName: string = 'chemDoser';
|
|
2172
|
+
public get id(): number { return this.data.id; }
|
|
2173
|
+
public set id(val: number) { this.setDataVal('id', val); }
|
|
2174
|
+
public get isActive(): boolean { return this.data.isActive; }
|
|
2175
|
+
public set isActive(val: boolean) { this.setDataVal('isActive', val); }
|
|
2176
|
+
public get name(): string { return this.data.name; }
|
|
2177
|
+
public set name(val: string) { this.setDataVal('name', val); }
|
|
2178
|
+
public get setpoint(): number { return this.data.setpoint; }
|
|
2179
|
+
public set setpoint(val: number) { this.setDataVal('setpoint', val); }
|
|
1979
2180
|
|
|
1980
|
-
|
|
2181
|
+
public get lastComm(): number { return this.data.lastComm || 0; }
|
|
2182
|
+
public set lastComm(val: number) { this.setDataVal('lastComm', val, false); }
|
|
2183
|
+
public get isBodyOn(): boolean { return this.data.isBodyOn; }
|
|
2184
|
+
public set isBodyOn(val: boolean) { this.data.isBodyOn = val; }
|
|
2185
|
+
public get flowDetected(): boolean { return this.data.flowDetected; }
|
|
2186
|
+
public set flowDetected(val: boolean) { this.data.flowDetected = val; }
|
|
2187
|
+
public get status(): number { return typeof (this.data.status) !== 'undefined' ? this.data.status.val : -1; }
|
|
2188
|
+
public set status(val: number) {
|
|
2189
|
+
if (this.status !== val) {
|
|
2190
|
+
this.data.status = sys.board.valueMaps.chemDoserStatus.transform(val);
|
|
2191
|
+
this.hasChanged = true;
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
2194
|
+
public get body(): number { return typeof (this.data.body) !== 'undefined' ? this.data.body.val : -1; }
|
|
2195
|
+
public set body(val: number) {
|
|
2196
|
+
if (this.body !== val) {
|
|
2197
|
+
this.data.body = sys.board.valueMaps.bodies.transform(val);
|
|
2198
|
+
this.hasChanged = true;
|
|
2199
|
+
}
|
|
2200
|
+
}
|
|
2201
|
+
public get type(): number { return typeof (this.data.type) !== 'undefined' ? this.data.type.val : 0; }
|
|
2202
|
+
public set type(val: number) {
|
|
2203
|
+
if (this.type !== val) {
|
|
2204
|
+
this.data.type = sys.board.valueMaps.chemDoserTypes.transform(val);
|
|
2205
|
+
this.data.chemType = this.data.type.desc;
|
|
2206
|
+
this.hasChanged = true;
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
2209
|
+
public get enabled(): boolean { return this.data.enabled; }
|
|
2210
|
+
public set enabled(val: boolean) { this.data.enabled = val; }
|
|
2211
|
+
public get freezeProtect(): boolean { return this.data.freezeProtect; }
|
|
2212
|
+
public set freezeProtect(val: boolean) { this.data.freezeProtect = val; }
|
|
2213
|
+
public get suspendDosing(): boolean { return this.data.suspendDosing; }
|
|
2214
|
+
public set suspendDosing(val: boolean) { this.data.suspendDosing = val; }
|
|
2215
|
+
public get chemType(): string { return this.data.chemType; }
|
|
2216
|
+
public get delayTimeRemaining(): number { return this.data.delayTimeRemaining; }
|
|
2217
|
+
public set delayTimeRemaining(val: number) { this.setDataVal('delayTimeRemaining', val); }
|
|
2218
|
+
public get doseTime(): number { return this.data.doseTime; }
|
|
2219
|
+
public set doseTime(val: number) { this.setDataVal('doseTime', val); }
|
|
2220
|
+
public get doseVolume(): number { return this.data.doseVolume; }
|
|
2221
|
+
public set doseVolume(val: number) { this.setDataVal('doseVolume', val); }
|
|
2222
|
+
public get dosingTimeRemaining(): number { return this.data.dosingTimeRemaining; }
|
|
2223
|
+
public set dosingTimeRemaining(val: number) { this.setDataVal('dosingTimeRemaining', val); }
|
|
2224
|
+
public get dosingVolumeRemaining(): number { return this.data.dosingVolumeRemaining; }
|
|
2225
|
+
public set dosingVolumeRemaining(val: number) { this.setDataVal('dosingVolumeRemaining', val); }
|
|
2226
|
+
public get volumeDosed(): number { return this.data.volumeDosed; }
|
|
2227
|
+
public set volumeDosed(val: number) { this.setDataVal('volumeDosed', val); }
|
|
2228
|
+
public get timeDosed(): number { return this.data.timeDosed; }
|
|
2229
|
+
public set timeDosed(val: number) { this.setDataVal('timeDosed', val); }
|
|
2230
|
+
public get dailyVolumeDosed(): number { return this.data.dailyVolumeDosed; }
|
|
2231
|
+
public set dailyVolumeDosed(val: number) { this.setDataVal('dailyVolumeDosed', val); }
|
|
2232
|
+
public get mixTimeRemaining(): number { return this.data.mixTimeRemaining; }
|
|
2233
|
+
public set mixTimeRemaining(val: number) { this.setDataVal('mixTimeRemaining', val); }
|
|
2234
|
+
public get dosingStatus(): number { return typeof (this.data.dosingStatus) !== 'undefined' ? this.data.dosingStatus.val : undefined; }
|
|
2235
|
+
public set dosingStatus(val: number) {
|
|
2236
|
+
if (this.dosingStatus !== val) {
|
|
2237
|
+
logger.debug(`${this.chemType} dosing status changed from ${sys.board.valueMaps.chemControllerDosingStatus.getName(this.dosingStatus)} (${this.dosingStatus}) to ${sys.board.valueMaps.chemControllerDosingStatus.getName(val)}(${val})`);
|
|
2238
|
+
this.data.dosingStatus = sys.board.valueMaps.chemControllerDosingStatus.transform(val);
|
|
2239
|
+
this.hasChanged = true;
|
|
2240
|
+
}
|
|
2241
|
+
}
|
|
2242
|
+
public get lockout(): boolean { return utils.makeBool(this.data.lockout); }
|
|
2243
|
+
public set lockout(val: boolean) { this.setDataVal('lockout', val); }
|
|
2244
|
+
public get flowDelay(): boolean { return utils.makeBool(this.data.flowDelay); }
|
|
2245
|
+
public set flowDelay(val: boolean) { this.data.flowDelay = val; }
|
|
2246
|
+
public get manualDosing(): boolean { return utils.makeBool(this.data.manualDosing); }
|
|
2247
|
+
public set manualDosing(val: boolean) { this.setDataVal('manualDosing', val); }
|
|
2248
|
+
public get manualMixing(): boolean { return utils.makeBool(this.data.manualMixing); }
|
|
2249
|
+
public set manualMixing(val: boolean) { this.setDataVal('manualMixing', val); }
|
|
2250
|
+
public get dailyLimitReached(): boolean { return utils.makeBool(this.data.dailyLimitReached); }
|
|
2251
|
+
public set dailyLimitReached(val: boolean) { this.data.dailyLimitReached = val; }
|
|
2252
|
+
public get tank(): ChemicalTankState { return new ChemicalTankState(this.data, 'tank', this); }
|
|
2253
|
+
public get pump(): ChemicalPumpState { return new ChemicalPumpState(this.data, 'pump', this); }
|
|
2254
|
+
public get flowSensor(): ChemicalFlowSensorState { return new ChemicalFlowSensorState(this.data, 'flowSensor', this); }
|
|
2255
|
+
public get warnings(): ChemDoserStateWarnings { return new ChemDoserStateWarnings(this.data, 'warnings', this); }
|
|
2256
|
+
public get alarms(): ChemDoserStateAlarms { return new ChemDoserStateAlarms(this.data, 'alarms', this); }
|
|
2257
|
+
public calcDoseHistory(): number {
|
|
2258
|
+
// The dose history records will already exist when the state is loaded. There are enough records to cover 24 hours in this
|
|
2259
|
+
// instance. We need to prune off any records that are > 24 hours when we calculate.
|
|
2260
|
+
let dailyVolumeDosed = 0;
|
|
2261
|
+
let dt = new Date();
|
|
2262
|
+
let dtMax = dt.setTime(dt.getTime() - (24 * 60 * 60 * 1000));
|
|
2263
|
+
for (let i = this.doseHistory.length - 1; i >= 0; i--) {
|
|
2264
|
+
let dh = this.doseHistory[i];
|
|
2265
|
+
if (typeof dh.end !== 'undefined'
|
|
2266
|
+
&& typeof dh.end.getTime == 'function'
|
|
2267
|
+
&& dh.end.getTime() > dtMax
|
|
2268
|
+
&& dh.volumeDosed > 0) dailyVolumeDosed += dh.volumeDosed;
|
|
2269
|
+
else {
|
|
2270
|
+
logger.info(`Removing dose history ${dh.chem} ${dh.end}`);
|
|
2271
|
+
this.doseHistory.splice(i, 1);
|
|
2272
|
+
}
|
|
2273
|
+
}
|
|
2274
|
+
return dailyVolumeDosed + (typeof this.currentDose !== 'undefined' ? this.currentDose.volumeDosed : 0);
|
|
2275
|
+
}
|
|
2276
|
+
public startDose(dtStart: Date = new Date(), method: string = 'auto', volume: number = 0, volumeDosed: number = 0, time: number = 0, timeDosed: number = 0): ChemicalDoseState {
|
|
2277
|
+
this.currentDose = new ChemicalDoseState();
|
|
2278
|
+
this.currentDose.type = this.chemType;
|
|
2279
|
+
this.currentDose.id = this.id;
|
|
2280
|
+
this.currentDose.start = dtStart;
|
|
2281
|
+
this.currentDose.method = method;
|
|
2282
|
+
this.currentDose.volumeDosed = volumeDosed;
|
|
2283
|
+
this.doseVolume = this.currentDose.volume = volume;
|
|
2284
|
+
this.currentDose.chem = this.chemType;
|
|
2285
|
+
this.currentDose.time = time;
|
|
2286
|
+
this.currentDose._timeDosed = timeDosed;
|
|
2287
|
+
this.volumeDosed = this.currentDose.volumeDosed;
|
|
2288
|
+
this.timeDosed = Math.round(timeDosed / 1000);
|
|
2289
|
+
this.dosingTimeRemaining = this.currentDose.timeRemaining;
|
|
2290
|
+
this.dosingVolumeRemaining = this.currentDose.volumeRemaining;
|
|
2291
|
+
this.doseTime = Math.round(this.currentDose.time / 1000);
|
|
2292
|
+
this.currentDose._isManual = method === 'manual';
|
|
2293
|
+
this.currentDose.status = 'current';
|
|
2294
|
+
//webApp.emitToClients(`chemicalDose`, this.currentDose);
|
|
2295
|
+
return this.currentDose;
|
|
2296
|
+
}
|
|
2297
|
+
public endDose(dtEnd: Date = new Date(), status: string = 'completed', volumeDosed: number = 0, timeDosed: number = 0): ChemicalDoseState {
|
|
2298
|
+
let dose = this.currentDose;
|
|
2299
|
+
if (typeof dose !== 'undefined') {
|
|
2300
|
+
dose.type = 'Peristaltic';
|
|
2301
|
+
dose._timeDosed += timeDosed;
|
|
2302
|
+
dose.volumeDosed += volumeDosed;
|
|
2303
|
+
dose.end = dtEnd;
|
|
2304
|
+
dose.timeDosed = dose._timeDosed / 1000;
|
|
2305
|
+
dose.status = status;
|
|
2306
|
+
this.volumeDosed = dose.volumeDosed;
|
|
2307
|
+
this.timeDosed = Math.round(dose._timeDosed / 1000);
|
|
2308
|
+
this.dosingTimeRemaining = 0;
|
|
2309
|
+
this.dosingVolumeRemaining = 0;
|
|
2310
|
+
if (dose.volumeDosed > 0 || dose.timeDosed > 0) {
|
|
2311
|
+
this.dailyVolumeDosed = this.calcDoseHistory();
|
|
2312
|
+
this.doseHistory.unshift(dose);
|
|
2313
|
+
DataLogger.writeEnd(`chemDosage_${this.chemType}.log`, dose);
|
|
2314
|
+
setImmediate(() => { webApp.emitToClients(`chemicalDose`, dose); });
|
|
2315
|
+
}
|
|
2316
|
+
this.currentDose = undefined;
|
|
2317
|
+
}
|
|
2318
|
+
return dose;
|
|
2319
|
+
}
|
|
2320
|
+
// Appends dose information to the current dose. The time here is in ms and our target will be in sec.
|
|
2321
|
+
public appendDose(volumeDosed: number, timeDosed: number): ChemicalDoseState {
|
|
2322
|
+
let dose = typeof this.currentDose !== 'undefined' ? this.currentDose : this.currentDose = this.startDose();
|
|
2323
|
+
dose._timeDosed += timeDosed;
|
|
2324
|
+
dose.volumeDosed += volumeDosed;
|
|
2325
|
+
dose.timeDosed = dose._timeDosed / 1000;
|
|
2326
|
+
dose.type = 'Peristaltic';
|
|
2327
|
+
this.volumeDosed = dose.volumeDosed;
|
|
2328
|
+
this.timeDosed = Math.round(dose._timeDosed / 1000);
|
|
2329
|
+
this.dosingTimeRemaining = dose.timeRemaining;
|
|
2330
|
+
this.dosingVolumeRemaining = dose.volumeRemaining;
|
|
2331
|
+
|
|
2332
|
+
if (dose.volumeDosed > 0 || timeDosed > 0) setImmediate(() => { webApp.emitToClients(`chemicalDose`, dose); });
|
|
2333
|
+
return dose;
|
|
2334
|
+
}
|
|
2335
|
+
public get currentDose(): ChemicalDoseState {
|
|
2336
|
+
if (typeof this.data.currentDose === 'undefined') return this.data.currentDose;
|
|
2337
|
+
if (typeof this.data.currentDose.save !== 'function') this.data.currentDose = new ChemicalDoseState(this.data.currentDose);
|
|
2338
|
+
return this.data.currentDose;
|
|
2339
|
+
}
|
|
2340
|
+
public set currentDose(val: ChemicalDoseState) {
|
|
2341
|
+
this.setDataVal('currentDose', val);
|
|
2342
|
+
}
|
|
2343
|
+
public get doseHistory(): ChemicalDoseState[] {
|
|
2344
|
+
if (typeof this.data.doseHistory === 'undefined') this.data.doseHistory = [];
|
|
2345
|
+
if (this.data.doseHistory.length === 0) return this.data.doseHistory;
|
|
2346
|
+
if (typeof this.data.doseHistory[0].save !== 'function') {
|
|
2347
|
+
let arr: ChemicalDoseState[] = [];
|
|
2348
|
+
for (let i = 0; i < this.data.doseHistory.length; i++) {
|
|
2349
|
+
arr.push(new ChemicalDoseState(this.data.doseHistory[i]));
|
|
2350
|
+
}
|
|
2351
|
+
this.data.doseHistory = arr;
|
|
2352
|
+
}
|
|
2353
|
+
return this.data.doseHistory;
|
|
2354
|
+
}
|
|
2355
|
+
public set doseHistory(val: ChemicalDoseState[]) { this.setDataVal('doseHistory', val); }
|
|
2356
|
+
public get chemController() { return this; }
|
|
2357
|
+
public getExtended(): any {
|
|
2358
|
+
let chem = sys.chemDosers.getItemById(this.id);
|
|
2359
|
+
let obj = this.get(true);
|
|
2360
|
+
obj = extend(true, obj, chem.getExtended());
|
|
2361
|
+
return obj;
|
|
2362
|
+
}
|
|
2363
|
+
}
|
|
2364
|
+
|
|
2365
|
+
export class ChemControllerState extends EqState implements IChemControllerState {
|
|
1981
2366
|
public initData() {
|
|
1982
2367
|
if (typeof this.data.saturationIndex === 'undefined') this.data.saturationIndex = 0;
|
|
1983
2368
|
if (typeof this.data.flowDetected === 'undefined') this.data.flowDetected = false;
|
|
@@ -2209,10 +2594,22 @@ export class ChemControllerState extends EqState {
|
|
|
2209
2594
|
this.hasChanged = true;
|
|
2210
2595
|
}
|
|
2211
2596
|
}
|
|
2597
|
+
public getEmitData(): any {
|
|
2598
|
+
let chem = sys.chemControllers.getItemById(this.id);
|
|
2599
|
+
let obj = this.get(true);
|
|
2600
|
+
obj.address = chem.address;
|
|
2601
|
+
obj.borates = chem.borates;
|
|
2602
|
+
obj.saturationIndex = this.saturationIndex || 0;
|
|
2603
|
+
obj.alkalinity = chem.alkalinity;
|
|
2604
|
+
obj.calciumHardness = chem.calciumHardness;
|
|
2605
|
+
obj.cyanuricAcid = chem.cyanuricAcid;
|
|
2606
|
+
return obj;
|
|
2607
|
+
}
|
|
2212
2608
|
public getExtended(): any {
|
|
2213
2609
|
let chem = sys.chemControllers.getItemById(this.id);
|
|
2214
2610
|
let obj = this.get(true);
|
|
2215
2611
|
obj.address = chem.address;
|
|
2612
|
+
obj.borates = chem.borates;
|
|
2216
2613
|
obj.saturationIndex = this.saturationIndex || 0;
|
|
2217
2614
|
obj.alkalinity = chem.alkalinity;
|
|
2218
2615
|
obj.calciumHardness = chem.calciumHardness;
|
|
@@ -2223,7 +2620,7 @@ export class ChemControllerState extends EqState {
|
|
|
2223
2620
|
return obj;
|
|
2224
2621
|
}
|
|
2225
2622
|
}
|
|
2226
|
-
export class ChemicalState extends ChildEqState {
|
|
2623
|
+
export class ChemicalState extends ChildEqState implements IChemicalState {
|
|
2227
2624
|
public initData() {
|
|
2228
2625
|
if (typeof this.data.probe === 'undefined') this.data.probe = {};
|
|
2229
2626
|
if (typeof this.data.tank === 'undefined') this.data.tank = { capacity: 0, level: 0, units: 0 };
|
|
@@ -2266,6 +2663,7 @@ export class ChemicalState extends ChildEqState {
|
|
|
2266
2663
|
}
|
|
2267
2664
|
public startDose(dtStart: Date = new Date(), method: string = 'auto', volume: number = 0, volumeDosed: number = 0, time: number = 0, timeDosed: number = 0): ChemicalDoseState {
|
|
2268
2665
|
this.currentDose = new ChemicalDoseState();
|
|
2666
|
+
this.currentDose.type = this.type;
|
|
2269
2667
|
this.currentDose.id = this.chemController.id;
|
|
2270
2668
|
this.currentDose.start = dtStart;
|
|
2271
2669
|
this.currentDose.method = method;
|
|
@@ -2299,9 +2697,9 @@ export class ChemicalState extends ChildEqState {
|
|
|
2299
2697
|
this.timeDosed = Math.round(dose._timeDosed / 1000);
|
|
2300
2698
|
this.dosingTimeRemaining = 0;
|
|
2301
2699
|
this.dosingVolumeRemaining = 0;
|
|
2302
|
-
if (dose.volumeDosed > 0) {
|
|
2303
|
-
this.doseHistory.unshift(dose);
|
|
2700
|
+
if (dose.volumeDosed > 0 || dose.timeDosed > 0) {
|
|
2304
2701
|
this.dailyVolumeDosed = this.calcDoseHistory();
|
|
2702
|
+
this.doseHistory.unshift(dose);
|
|
2305
2703
|
DataLogger.writeEnd(`chemDosage_${this.chemType}.log`, dose);
|
|
2306
2704
|
setImmediate(() => { webApp.emitToClients(`chemicalDose`, dose); });
|
|
2307
2705
|
}
|
|
@@ -2314,11 +2712,12 @@ export class ChemicalState extends ChildEqState {
|
|
|
2314
2712
|
let dose = typeof this.currentDose !== 'undefined' ? this.currentDose : this.currentDose = this.startDose();
|
|
2315
2713
|
dose._timeDosed += timeDosed;
|
|
2316
2714
|
dose.volumeDosed += volumeDosed;
|
|
2715
|
+
dose.timeDosed = dose._timeDosed/1000;
|
|
2317
2716
|
this.volumeDosed = dose.volumeDosed;
|
|
2318
2717
|
this.timeDosed = Math.round(dose._timeDosed / 1000);
|
|
2319
2718
|
this.dosingTimeRemaining = dose.timeRemaining;
|
|
2320
2719
|
this.dosingVolumeRemaining = dose.volumeRemaining;
|
|
2321
|
-
if (dose.volumeDosed > 0) setImmediate(() => { webApp.emitToClients(`chemicalDose`, dose); });
|
|
2720
|
+
if (dose.volumeDosed > 0 || timeDosed > 0) setImmediate(() => { webApp.emitToClients(`chemicalDose`, dose); });
|
|
2322
2721
|
return dose;
|
|
2323
2722
|
}
|
|
2324
2723
|
public get currentDose(): ChemicalDoseState {
|
|
@@ -2346,6 +2745,7 @@ export class ChemicalState extends ChildEqState {
|
|
|
2346
2745
|
let dH = this.demandHistory;
|
|
2347
2746
|
dH.appendDemand(time, val);
|
|
2348
2747
|
}
|
|
2748
|
+
public get type() { return this.data.type; };
|
|
2349
2749
|
public get demandHistory() { return new ChemicalDemandState(this.data, 'demandHistory', this) };
|
|
2350
2750
|
public get enabled(): boolean { return this.data.enabled; }
|
|
2351
2751
|
public set enabled(val: boolean) { this.data.enabled = val; }
|
|
@@ -2410,6 +2810,7 @@ export class ChemicalPhState extends ChemicalState {
|
|
|
2410
2810
|
public initData() {
|
|
2411
2811
|
super.initData();
|
|
2412
2812
|
if (typeof this.data.chemType === 'undefined') this.data.chemType = 'none';
|
|
2813
|
+
if (typeof this.data.type === 'undefined') this.data.type = 'ph';
|
|
2413
2814
|
}
|
|
2414
2815
|
public getConfig() {
|
|
2415
2816
|
let schem = this.chemController;
|
|
@@ -2436,13 +2837,27 @@ export class ChemicalPhState extends ChemicalState {
|
|
|
2436
2837
|
// Calculate how many mL are required to raise to our pH level.
|
|
2437
2838
|
// 1. Get the total gallons of water that the chem controller is in
|
|
2438
2839
|
// control of.
|
|
2840
|
+
// 2. RSG 5-22-22 - If the spa is on, calc demand only based on the spa volume. Otherwise, long periods of spa usage
|
|
2841
|
+
// will result in an overdose if pH is high.
|
|
2439
2842
|
let totalGallons = 0;
|
|
2843
|
+
// The bodyIsOn code was throwing an exception whenver no bodies were on.
|
|
2844
|
+
if (chem.body === 32 && sys.equipment.shared) {
|
|
2845
|
+
// We are shared and when body 2 (spa) is on body 1 (pool) is off.
|
|
2846
|
+
if (state.temps.bodies.getItemById(2).isOn === true) totalGallons = sys.bodies.getItemById(2).capacity;
|
|
2847
|
+
else totalGallons = sys.bodies.getItemById(1).capacity + sys.bodies.getItemById(2).capacity;
|
|
2848
|
+
}
|
|
2849
|
+
else {
|
|
2850
|
+
// These are all single body implementations so we simply match to the body.
|
|
2851
|
+
totalGallons = sys.bodies.getItemById(chem.body + 1).capacity;
|
|
2852
|
+
}
|
|
2440
2853
|
|
|
2441
|
-
if (chem.body === 0 || chem.body === 32 || sys.equipment.shared) totalGallons += sys.bodies.getItemById(1).capacity;
|
|
2442
|
-
|
|
2443
|
-
if (chem.body ===
|
|
2444
|
-
if (chem.body ===
|
|
2445
|
-
|
|
2854
|
+
//if (chem.body === 0 || chem.body === 32 || sys.equipment.shared) totalGallons += sys.bodies.getItemById(1).capacity;
|
|
2855
|
+
//let bodyIsOn = state.temps.bodies.getBodyIsOn();
|
|
2856
|
+
//if (bodyIsOn.circuit === 1 && sys.circuits.getInterfaceById(bodyIsOn.circuit).type === sys.board.valueMaps.circuitFunctions.getValue('spa') && (chem.body === 1 || chem.body === 32 || sys.equipment.shared)) totalGallons = sys.bodies.getItemById(2).capacity;
|
|
2857
|
+
//else if (chem.body === 1 || chem.body === 32 || sys.equipment.shared) totalGallons += sys.bodies.getItemById(2).capacity;
|
|
2858
|
+
//if (chem.body === 2) totalGallons += sys.bodies.getItemById(3).capacity;
|
|
2859
|
+
//if (chem.body === 3) totalGallons += sys.bodies.getItemById(4).capacity;
|
|
2860
|
+
logger.verbose(`Chem begin calculating ${this.chemType} demand: ${this.level} setpoint: ${this.setpoint} total gallons: ${totalGallons}`);
|
|
2446
2861
|
let chg = this.setpoint - this.level;
|
|
2447
2862
|
let delta = chg * totalGallons;
|
|
2448
2863
|
let temp = (this.level + this.setpoint) / 2;
|
|
@@ -2470,6 +2885,8 @@ export class ChemicalORPState extends ChemicalState {
|
|
|
2470
2885
|
if (typeof this.data.probe === 'undefined') this.data.probe = {};
|
|
2471
2886
|
if (typeof this.data.chemType === 'undefined') this.data.chemType = 'none';
|
|
2472
2887
|
if (typeof this.data.useChlorinator === 'undefined') this.data.useChlorinator = false;
|
|
2888
|
+
if (typeof this.data.type === 'undefined') this.data.type = 'orp';
|
|
2889
|
+
|
|
2473
2890
|
super.initData();
|
|
2474
2891
|
// Load up the 24 hours doseHistory.
|
|
2475
2892
|
//this.doseHistory = DataLogger.readFromEnd(`chemDosage_${this.chemType}.log`, ChemicalDoseState, (lineNumber: number, entry: ChemicalDoseState): boolean => {
|
|
@@ -2640,6 +3057,7 @@ export class ChemicalDoseState extends DataLoggerEntry {
|
|
|
2640
3057
|
public volumeDosed: number;
|
|
2641
3058
|
public time: number;
|
|
2642
3059
|
public timeDosed: number;
|
|
3060
|
+
public type: string;
|
|
2643
3061
|
|
|
2644
3062
|
public static createInstance(entry?: string): ChemicalDoseState { return new ChemicalDoseState(entry); }
|
|
2645
3063
|
public save() { DataLogger.writeEnd(`chemDosage_${this.chem}.log`, this); }
|
|
@@ -2755,6 +3173,107 @@ export class ChemControllerStateWarnings extends ChildEqState {
|
|
|
2755
3173
|
}
|
|
2756
3174
|
}
|
|
2757
3175
|
|
|
3176
|
+
}
|
|
3177
|
+
export class ChemDoserStateWarnings extends ChildEqState {
|
|
3178
|
+
///ctor(data): ChemControllerStateWarnings { return new ChemControllerStateWarnings(data, name || 'warnings'); }
|
|
3179
|
+
public dataName = 'chemDoserWarnings';
|
|
3180
|
+
public initData() {
|
|
3181
|
+
if (typeof this.data.lockout === 'undefined') this.lockout = 0;
|
|
3182
|
+
if (typeof this.data.pHDailyLimitReached === 'undefined') this.dailyLimitReached = 0;
|
|
3183
|
+
if (typeof this.data.invalidSetup === 'undefined') this.invalidSetup = 0;
|
|
3184
|
+
if (typeof this.data.chlorinatorCommError === 'undefined') this.chlorinatorCommError = 0;
|
|
3185
|
+
}
|
|
3186
|
+
public get lockout(): number { return this.data.lockout; }
|
|
3187
|
+
public set lockout(val: number) {
|
|
3188
|
+
if (this.lockout !== val) {
|
|
3189
|
+
this.data.lockout = sys.board.valueMaps.chemDoserLimits.transform(val);
|
|
3190
|
+
this.hasChanged = true;
|
|
3191
|
+
}
|
|
3192
|
+
}
|
|
3193
|
+
public get dailyLimitReached(): number { return this.data.dailyLimitReached; }
|
|
3194
|
+
public set dailyLimitReached(val: number) {
|
|
3195
|
+
if (this.dailyLimitReached !== val) {
|
|
3196
|
+
this.data.dailyLimitReached = sys.board.valueMaps.chemDoserLimits.transform(val);
|
|
3197
|
+
this.hasChanged = true;
|
|
3198
|
+
}
|
|
3199
|
+
}
|
|
3200
|
+
public get invalidSetup(): number { return this.data.invalidSetup; }
|
|
3201
|
+
public set invalidSetup(val: number) {
|
|
3202
|
+
if (this.invalidSetup !== val) {
|
|
3203
|
+
this.data.invalidSetup = sys.board.valueMaps.chemDoserLimits.transform(val);
|
|
3204
|
+
this.hasChanged = true;
|
|
3205
|
+
}
|
|
3206
|
+
}
|
|
3207
|
+
public get chlorinatorCommError(): number { return this.data.chlorinatorCommError; }
|
|
3208
|
+
public set chlorinatorCommError(val: number) {
|
|
3209
|
+
if (this.chlorinatorCommError !== val) {
|
|
3210
|
+
this.data.chlorinatorCommError = sys.board.valueMaps.chemDoserWarnings.transform(val);
|
|
3211
|
+
this.hasChanged = true;
|
|
3212
|
+
}
|
|
3213
|
+
}
|
|
3214
|
+
}
|
|
3215
|
+
|
|
3216
|
+
export class ChemDoserStateAlarms extends ChildEqState {
|
|
3217
|
+
public dataName = 'chemControllerAlarms';
|
|
3218
|
+
public initData() {
|
|
3219
|
+
if (typeof this.data.flow === 'undefined') this.data.flow = sys.board.valueMaps.chemDoserAlarms.transform(0);
|
|
3220
|
+
if (typeof this.data.tank === 'undefined') this.data.tank = sys.board.valueMaps.chemDoserAlarms.transform(0);
|
|
3221
|
+
if (typeof this.data.pumpFault === 'undefined') this.data.pumpFault = sys.board.valueMaps.chemDoserHardwareFaults.transform(0);
|
|
3222
|
+
if (typeof this.data.bodyFault === 'undefined') this.data.bodyFault = sys.board.valueMaps.chemDoserHardwareFaults.transform(0);
|
|
3223
|
+
if (typeof this.data.flowSensorFault === 'undefined') this.data.flowSensorFault = sys.board.valueMaps.chemDoserHardwareFaults.transform(0);
|
|
3224
|
+
if (typeof this.data.comms === 'undefined') this.data.comms = sys.board.valueMaps.chemDoserStatus.transform(0);
|
|
3225
|
+
if (typeof this.data.freezeProtect === 'undefined') this.data.freezeProtect = sys.board.valueMaps.chemDoserAlarms.transform(0);
|
|
3226
|
+
}
|
|
3227
|
+
public get flow(): number { return typeof this.data.flow === 'undefined' ? undefined : this.data.flow.val; }
|
|
3228
|
+
public set flow(val: number) {
|
|
3229
|
+
if (this.flow !== val) {
|
|
3230
|
+
this.data.flow = sys.board.valueMaps.chemDoserAlarms.transform(val);
|
|
3231
|
+
this.hasChanged = true;
|
|
3232
|
+
}
|
|
3233
|
+
}
|
|
3234
|
+
public get tank(): number { return typeof this.data.pHTank === 'undefined' ? undefined : this.data.tank.val; }
|
|
3235
|
+
public set tank(val: number) {
|
|
3236
|
+
if (this.tank !== val) {
|
|
3237
|
+
this.data.tank = sys.board.valueMaps.chemDoserAlarms.transform(val);
|
|
3238
|
+
this.hasChanged = true;
|
|
3239
|
+
}
|
|
3240
|
+
}
|
|
3241
|
+
public get pumpFault(): number { return typeof this.data.pumpFault === 'undefined' ? undefined : this.data.pumpFault.val; }
|
|
3242
|
+
public set pumpFault(val: number) {
|
|
3243
|
+
if (this.pumpFault !== val) {
|
|
3244
|
+
this.data.pumpFault = sys.board.valueMaps.chemDoserHardwareFaults.transform(val);
|
|
3245
|
+
this.hasChanged = true;
|
|
3246
|
+
}
|
|
3247
|
+
}
|
|
3248
|
+
public get bodyFault(): number { return typeof this.data.bodyFault === 'undefined' ? undefined : this.data.bodyFault.val; }
|
|
3249
|
+
public set bodyFault(val: number) {
|
|
3250
|
+
if (this.bodyFault !== val) {
|
|
3251
|
+
this.data.bodyFault = sys.board.valueMaps.chemDoserHardwareFaults.transform(val);
|
|
3252
|
+
this.hasChanged = true;
|
|
3253
|
+
}
|
|
3254
|
+
}
|
|
3255
|
+
public get flowSensorFault(): number { return typeof this.data.flowSensorFault === 'undefined' ? undefined : this.data.flowSensorFault.val; }
|
|
3256
|
+
public set flowSensorFault(val: number) {
|
|
3257
|
+
if (this.flowSensorFault !== val) {
|
|
3258
|
+
this.data.flowSensorFault = sys.board.valueMaps.chemDoserHardwareFaults.transform(val);
|
|
3259
|
+
this.hasChanged = true;
|
|
3260
|
+
}
|
|
3261
|
+
}
|
|
3262
|
+
public get comms(): number { return typeof this.data.comms === 'undefined' ? undefined : this.data.comms.val; }
|
|
3263
|
+
public set comms(val: number) {
|
|
3264
|
+
if (this.comms !== val) {
|
|
3265
|
+
this.data.comms = sys.board.valueMaps.chemDoserStatus.transform(val);
|
|
3266
|
+
this.hasChanged = true;
|
|
3267
|
+
}
|
|
3268
|
+
}
|
|
3269
|
+
public get freezeProtect(): number { return typeof this.data.freezeProtect === 'undefined' ? undefined : this.data.freezeProtect.val; }
|
|
3270
|
+
public set freezeProtect(val: number) {
|
|
3271
|
+
if (this.freezeProtect !== val) {
|
|
3272
|
+
this.data.freezeProtect = sys.board.valueMaps.chemDoserAlarms.transform(val);
|
|
3273
|
+
this.hasChanged = true;
|
|
3274
|
+
}
|
|
3275
|
+
}
|
|
3276
|
+
|
|
2758
3277
|
}
|
|
2759
3278
|
export class ChemControllerStateAlarms extends ChildEqState {
|
|
2760
3279
|
//ctor(data): ChemControllerStateWarnings { return new ChemControllerStateWarnings(data, name || 'alarms'); }
|