nodejs-poolcontroller 7.2.0 → 7.5.1
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/.github/ISSUE_TEMPLATE/bug_report.md +1 -1
- package/Changelog +13 -0
- package/Dockerfile +1 -0
- package/README.md +5 -5
- package/app.ts +11 -0
- package/config/Config.ts +3 -0
- package/config/VersionCheck.ts +8 -4
- package/controller/Constants.ts +165 -9
- package/controller/Equipment.ts +186 -65
- package/controller/Errors.ts +22 -1
- package/controller/State.ts +273 -57
- package/controller/boards/EasyTouchBoard.ts +194 -95
- package/controller/boards/IntelliCenterBoard.ts +115 -42
- package/controller/boards/IntelliTouchBoard.ts +104 -30
- package/controller/boards/NixieBoard.ts +155 -53
- package/controller/boards/SystemBoard.ts +1529 -514
- package/controller/comms/Comms.ts +219 -42
- package/controller/comms/messages/Messages.ts +16 -4
- package/controller/comms/messages/config/ChlorinatorMessage.ts +13 -3
- package/controller/comms/messages/config/CircuitGroupMessage.ts +6 -0
- package/controller/comms/messages/config/CircuitMessage.ts +1 -1
- package/controller/comms/messages/config/CoverMessage.ts +1 -0
- package/controller/comms/messages/config/EquipmentMessage.ts +4 -0
- package/controller/comms/messages/config/ExternalMessage.ts +43 -25
- package/controller/comms/messages/config/FeatureMessage.ts +8 -1
- package/controller/comms/messages/config/GeneralMessage.ts +8 -0
- package/controller/comms/messages/config/HeaterMessage.ts +15 -9
- package/controller/comms/messages/config/IntellichemMessage.ts +4 -1
- package/controller/comms/messages/config/OptionsMessage.ts +13 -1
- package/controller/comms/messages/config/PumpMessage.ts +4 -20
- package/controller/comms/messages/config/RemoteMessage.ts +4 -0
- package/controller/comms/messages/config/ScheduleMessage.ts +11 -0
- package/controller/comms/messages/config/SecurityMessage.ts +1 -0
- package/controller/comms/messages/config/ValveMessage.ts +12 -2
- package/controller/comms/messages/status/ChlorinatorStateMessage.ts +14 -6
- package/controller/comms/messages/status/EquipmentStateMessage.ts +78 -24
- package/controller/comms/messages/status/HeaterStateMessage.ts +25 -5
- package/controller/comms/messages/status/IntelliChemStateMessage.ts +55 -26
- package/controller/nixie/Nixie.ts +18 -16
- package/controller/nixie/NixieEquipment.ts +6 -6
- package/controller/nixie/bodies/Body.ts +7 -4
- package/controller/nixie/bodies/Filter.ts +7 -4
- package/controller/nixie/chemistry/ChemController.ts +800 -283
- package/controller/nixie/chemistry/Chlorinator.ts +22 -14
- package/controller/nixie/circuits/Circuit.ts +42 -7
- package/controller/nixie/heaters/Heater.ts +303 -30
- package/controller/nixie/pumps/Pump.ts +57 -30
- package/controller/nixie/schedules/Schedule.ts +10 -7
- package/controller/nixie/valves/Valve.ts +7 -5
- package/defaultConfig.json +32 -1
- package/issue_template.md +1 -1
- package/logger/DataLogger.ts +37 -22
- package/package.json +20 -18
- package/web/Server.ts +529 -31
- package/web/bindings/influxDB.json +157 -5
- package/web/bindings/mqtt.json +112 -13
- package/web/bindings/mqttAlt.json +109 -11
- package/web/interfaces/baseInterface.ts +2 -1
- package/web/interfaces/httpInterface.ts +2 -0
- package/web/interfaces/influxInterface.ts +103 -54
- package/web/interfaces/mqttInterface.ts +16 -5
- package/web/services/config/Config.ts +179 -43
- package/web/services/state/State.ts +51 -5
- package/web/services/state/StateSocket.ts +19 -2
package/controller/State.ts
CHANGED
|
@@ -36,6 +36,7 @@ export class State implements IState {
|
|
|
36
36
|
private _isDirty: boolean;
|
|
37
37
|
private _timerDirty: NodeJS.Timeout;
|
|
38
38
|
protected _dt: Timestamp;
|
|
39
|
+
protected _startTime: Timestamp;
|
|
39
40
|
protected _controllerType: ControllerType;
|
|
40
41
|
protected onchange = (obj, fn) => {
|
|
41
42
|
const handler = {
|
|
@@ -191,6 +192,8 @@ export class State implements IState {
|
|
|
191
192
|
public get controllerState() {
|
|
192
193
|
var self = this;
|
|
193
194
|
return {
|
|
195
|
+
systemUnits: sys.board.valueMaps.systemUnits.transform(sys.general.options.units),
|
|
196
|
+
startTime: self.data.startTime || '',
|
|
194
197
|
time: self.data.time || '',
|
|
195
198
|
// body: self.data.body || {},
|
|
196
199
|
valve: self.data.valve || 0,
|
|
@@ -200,7 +203,6 @@ export class State implements IState {
|
|
|
200
203
|
batteryVoltage: self.data.batteryVoltage || 0,
|
|
201
204
|
status: self.data.status || {},
|
|
202
205
|
mode: self.data.mode || {},
|
|
203
|
-
// freeze: self.data.freeze || false,
|
|
204
206
|
appVersion: sys.appVersion || '',
|
|
205
207
|
appVersionState: self.appVersion.get(true) || {},
|
|
206
208
|
clockMode: sys.board.valueMaps.clockModes.transform(sys.general.options.clockMode) || {},
|
|
@@ -209,7 +211,8 @@ export class State implements IState {
|
|
|
209
211
|
model: sys.equipment.model,
|
|
210
212
|
sunrise: self.data.sunrise || '',
|
|
211
213
|
sunset: self.data.sunset || '',
|
|
212
|
-
alias: sys.general.alias
|
|
214
|
+
alias: sys.general.alias,
|
|
215
|
+
freeze: utils.makeBool(self.data.freeze)
|
|
213
216
|
};
|
|
214
217
|
}
|
|
215
218
|
public emitAllEquipmentChanges() {
|
|
@@ -305,7 +308,6 @@ export class State implements IState {
|
|
|
305
308
|
this.hasChanged = true;
|
|
306
309
|
}
|
|
307
310
|
}
|
|
308
|
-
|
|
309
311
|
}
|
|
310
312
|
public get valve(): number { return this.data.valve; }
|
|
311
313
|
public set valve(val: number) {
|
|
@@ -330,14 +332,30 @@ export class State implements IState {
|
|
|
330
332
|
public get isInitialized(): boolean { return typeof (this.data.status) !== 'undefined' && this.data.status.val !== 0; }
|
|
331
333
|
public init() {
|
|
332
334
|
console.log(`Init state for Pool Controller`);
|
|
333
|
-
var
|
|
334
|
-
|
|
335
|
+
var sdata = this.loadFile(this.statePath, {});
|
|
336
|
+
sdata = extend(true, { mode: { val: -1 }, temps: { units: { val: 0, name: 'F', desc: 'Fahrenheit' } } }, sdata);
|
|
337
|
+
if (typeof sdata.temps !== 'undefined' && typeof sdata.temps.bodies !== 'undefined') {
|
|
338
|
+
EqStateCollection.removeNullIds(sdata.temps.bodies);
|
|
339
|
+
}
|
|
340
|
+
EqStateCollection.removeNullIds(sdata.schedules);
|
|
341
|
+
EqStateCollection.removeNullIds(sdata.features);
|
|
342
|
+
EqStateCollection.removeNullIds(sdata.circuits);
|
|
343
|
+
EqStateCollection.removeNullIds(sdata.pumps);
|
|
344
|
+
EqStateCollection.removeNullIds(sdata.chlorinators);
|
|
345
|
+
EqStateCollection.removeNullIds(sdata.valves);
|
|
346
|
+
EqStateCollection.removeNullIds(sdata.heaters);
|
|
347
|
+
EqStateCollection.removeNullIds(sdata.covers);
|
|
348
|
+
EqStateCollection.removeNullIds(sdata.circuitGroups);
|
|
349
|
+
EqStateCollection.removeNullIds(sdata.lightGroups);
|
|
350
|
+
EqStateCollection.removeNullIds(sdata.remotes);
|
|
351
|
+
EqStateCollection.removeNullIds(sdata.chemControllers);
|
|
352
|
+
EqStateCollection.removeNullIds(sdata.filters);
|
|
335
353
|
var self = this;
|
|
336
|
-
let pnlTime = typeof
|
|
354
|
+
let pnlTime = typeof sdata.time !== 'undefined' ? new Date(sdata.time) : new Date();
|
|
337
355
|
if (isNaN(pnlTime.getTime())) pnlTime = new Date();
|
|
338
356
|
this._dt = new Timestamp(pnlTime);
|
|
339
357
|
this._dt.milliseconds = 0;
|
|
340
|
-
this.data =
|
|
358
|
+
this.data = sdata;
|
|
341
359
|
//this.onchange(state, function () { self.dirty = true; });
|
|
342
360
|
this._dt.emitter.on('change', function () {
|
|
343
361
|
self.data.time = self._dt.format();
|
|
@@ -370,6 +388,7 @@ export class State implements IState {
|
|
|
370
388
|
this.comms = new CommsState();
|
|
371
389
|
this.heliotrope = new Heliotrope();
|
|
372
390
|
this.appVersion = new AppVersionState(this.data, 'appVersion');
|
|
391
|
+
this.data.startTime = Timestamp.toISOLocal(new Date());
|
|
373
392
|
versionCheck.checkGitLocal();
|
|
374
393
|
}
|
|
375
394
|
public resetData() {
|
|
@@ -472,6 +491,7 @@ export interface ICircuitState {
|
|
|
472
491
|
emitEquipmentChange();
|
|
473
492
|
get(bCopy?: boolean);
|
|
474
493
|
showInFeatures?: boolean;
|
|
494
|
+
isActive?: boolean;
|
|
475
495
|
}
|
|
476
496
|
|
|
477
497
|
interface IEqStateCreator<T> { ctor(data: any, name: string, parent?): T; }
|
|
@@ -571,6 +591,20 @@ class EqStateCollection<T> {
|
|
|
571
591
|
if (typeof (data[name]) === 'undefined') data[name] = [];
|
|
572
592
|
this.data = data[name];
|
|
573
593
|
}
|
|
594
|
+
public static removeNullIds(data: any) {
|
|
595
|
+
if (typeof data !== 'undefined' && Array.isArray(data) && typeof data.length === 'number') {
|
|
596
|
+
for (let i = data.length - 1; i >= 0; i--) {
|
|
597
|
+
if (typeof data[i].id !== 'number') {
|
|
598
|
+
console.log(`Removing ${data[i].id}-${data[i].name}`);
|
|
599
|
+
data.splice(i, 1);
|
|
600
|
+
}
|
|
601
|
+
else if (typeof data[i].id === 'undefined' || isNaN(data[i].id)) {
|
|
602
|
+
console.log(`Removing isNaN ${data[i].id}-${data[i].name}`);
|
|
603
|
+
data.splice(i, 1);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
}
|
|
574
608
|
public getItemById(id: number, add?: boolean, data?: any): T {
|
|
575
609
|
for (let i = 0; i < this.data.length; i++)
|
|
576
610
|
if (typeof this.data[i].id !== 'undefined' && this.data[i].id === id) {
|
|
@@ -779,6 +813,12 @@ export class EquipmentMessages extends EqStateCollection<EquipmentMessage> {
|
|
|
779
813
|
}
|
|
780
814
|
return rem;
|
|
781
815
|
}
|
|
816
|
+
public setMessageByCode(code: string, severity: string | number, message: string): EquipmentMessage {
|
|
817
|
+
let msg = this.getItemByCode(code, true);
|
|
818
|
+
msg.severity = sys.board.valueMaps.eqMessageSeverities.encode(severity, 0);
|
|
819
|
+
msg.message = message;
|
|
820
|
+
return msg;
|
|
821
|
+
}
|
|
782
822
|
}
|
|
783
823
|
export class EquipmentMessage extends ChildEqState {
|
|
784
824
|
public initData() {
|
|
@@ -821,7 +861,10 @@ export class PumpStateCollection extends EqStateCollection<PumpState> {
|
|
|
821
861
|
}
|
|
822
862
|
public cleanupState() {
|
|
823
863
|
for (let i = this.data.length - 1; i >= 0; i--) {
|
|
824
|
-
if (
|
|
864
|
+
if (isNaN(this.data[i].id)) this.data.splice(i, 1);
|
|
865
|
+
else {
|
|
866
|
+
if (typeof sys.pumps.find(elem => elem.id === this.data[i].id) === 'undefined') this.removeItemById(this.data[i].id);
|
|
867
|
+
}
|
|
825
868
|
}
|
|
826
869
|
let cfg = sys.pumps.toArray();
|
|
827
870
|
for (let i = 0; i < cfg.length; i++) {
|
|
@@ -836,6 +879,11 @@ export class PumpStateCollection extends EqStateCollection<PumpState> {
|
|
|
836
879
|
}
|
|
837
880
|
export class PumpState extends EqState {
|
|
838
881
|
public dataName: string = 'pump';
|
|
882
|
+
public initData() {
|
|
883
|
+
if (typeof this.data.status === 'undefined') {
|
|
884
|
+
this.data.status = { name: 'ok', desc: 'Ok', val: 0 };
|
|
885
|
+
}
|
|
886
|
+
}
|
|
839
887
|
private _threshold = 0.05;
|
|
840
888
|
private exceedsThreshold(origVal: number, newVal: number) {
|
|
841
889
|
return Math.abs((newVal - origVal) / origVal) > this._threshold;
|
|
@@ -869,7 +917,7 @@ export class PumpState extends EqState {
|
|
|
869
917
|
// quick fix for #172
|
|
870
918
|
if (this.status !== val) {
|
|
871
919
|
if (sys.board.valueMaps.pumpTypes.getName(this.type) === 'vsf' && val === 0) {
|
|
872
|
-
this.data.status = { name: 'ok', desc: 'Ok', val };
|
|
920
|
+
this.data.status = { name: 'ok', desc: 'Ok', val: 0 };
|
|
873
921
|
}
|
|
874
922
|
else this.data.status = sys.board.valueMaps.pumpStatus.transform(val);
|
|
875
923
|
this.hasChanged = true;
|
|
@@ -951,6 +999,7 @@ export class ScheduleState extends EqState {
|
|
|
951
999
|
public get startDate(): Date { return this._startDate; }
|
|
952
1000
|
public set startDate(val: Date) { this._startDate = val; this._saveStartDate(); }
|
|
953
1001
|
private _saveStartDate() {
|
|
1002
|
+
if (typeof this._startDate === 'undefined') this._startDate = new Date();
|
|
954
1003
|
this.startDate.setHours(0, 0, 0, 0);
|
|
955
1004
|
this.setDataVal('startDate', Timestamp.toISOLocal(this.startDate));
|
|
956
1005
|
}
|
|
@@ -1055,7 +1104,10 @@ export class CircuitGroupStateCollection extends EqStateCollection<CircuitGroupS
|
|
|
1055
1104
|
}
|
|
1056
1105
|
public cleanupState() {
|
|
1057
1106
|
for (let i = this.data.length - 1; i >= 0; i--) {
|
|
1058
|
-
if (
|
|
1107
|
+
if (isNaN(this.data[i].id)) this.data.splice(i, 1);
|
|
1108
|
+
else {
|
|
1109
|
+
if (typeof sys.circuitGroups.find(elem => elem.id === this.data[i].id) === 'undefined') this.removeItemById(this.data[i].id);
|
|
1110
|
+
}
|
|
1059
1111
|
}
|
|
1060
1112
|
let cfg = sys.circuitGroups.toArray();
|
|
1061
1113
|
for (let i = 0; i < cfg.length; i++) {
|
|
@@ -1091,7 +1143,7 @@ export class CircuitGroupState extends EqState implements ICircuitGroupState, IC
|
|
|
1091
1143
|
if (typeof this.data.endTime === 'undefined') return undefined;
|
|
1092
1144
|
return new Timestamp(this.data.endTime);
|
|
1093
1145
|
}
|
|
1094
|
-
public set endTime(val: Timestamp) { typeof val !== 'undefined' ? this.setDataVal('endTime', Timestamp.toISOLocal(val.toDate())
|
|
1146
|
+
public set endTime(val: Timestamp) { typeof val !== 'undefined' ? this.setDataVal('endTime', Timestamp.toISOLocal(val.toDate())) : this.setDataVal('endTime', undefined); }
|
|
1095
1147
|
public get isActive(): boolean { return this.data.isActive; }
|
|
1096
1148
|
public set isActive(val: boolean) { this.setDataVal('isActive', val); }
|
|
1097
1149
|
public get showInFeatures(): boolean { return typeof this.data.showInFeatures === 'undefined' ? true : this.data.showInFeatures; }
|
|
@@ -1115,12 +1167,23 @@ export class CircuitGroupState extends EqState implements ICircuitGroupState, IC
|
|
|
1115
1167
|
state._dirtyList.removeEqState(this);
|
|
1116
1168
|
}
|
|
1117
1169
|
}
|
|
1170
|
+
public get(bcopy?: boolean): any {
|
|
1171
|
+
let d = super.get(bcopy);
|
|
1172
|
+
let cg = sys.circuitGroups.getItemById(this.id);
|
|
1173
|
+
if (!cg.isActive) d.isActive = false;
|
|
1174
|
+
else d.isActive = undefined;
|
|
1175
|
+
return d;
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1118
1178
|
}
|
|
1119
1179
|
export class LightGroupStateCollection extends EqStateCollection<LightGroupState> {
|
|
1120
1180
|
public createItem(data: any): LightGroupState { return new LightGroupState(data); }
|
|
1121
1181
|
public cleanupState() {
|
|
1122
1182
|
for (let i = this.data.length - 1; i >= 0; i--) {
|
|
1123
|
-
if (
|
|
1183
|
+
if (isNaN(this.data[i].id)) this.data.splice(i, 1);
|
|
1184
|
+
else {
|
|
1185
|
+
if (typeof sys.lightGroups.find(elem => elem.id === this.data[i].id) === 'undefined') this.removeItemById(this.data[i].id);
|
|
1186
|
+
}
|
|
1124
1187
|
}
|
|
1125
1188
|
let cfg = sys.lightGroups.toArray();
|
|
1126
1189
|
for (let i = 0; i < cfg.length; i++) {
|
|
@@ -1164,7 +1227,7 @@ export class LightGroupState extends EqState implements ICircuitGroupState, ICir
|
|
|
1164
1227
|
if (typeof this.data.endTime === 'undefined') return undefined;
|
|
1165
1228
|
return new Timestamp(this.data.endTime);
|
|
1166
1229
|
}
|
|
1167
|
-
public set endTime(val: Timestamp) { typeof val !== 'undefined' ? this.setDataVal('endTime', Timestamp.toISOLocal(val.toDate())
|
|
1230
|
+
public set endTime(val: Timestamp) { typeof val !== 'undefined' ? this.setDataVal('endTime', Timestamp.toISOLocal(val.toDate())) : this.setDataVal('endTime', undefined); }
|
|
1168
1231
|
public get isOn(): boolean { return this.data.isOn; }
|
|
1169
1232
|
public set isOn(val: boolean) { this.setDataVal('isOn', val); }
|
|
1170
1233
|
public get isActive(): boolean { return this.data.isActive; }
|
|
@@ -1201,8 +1264,12 @@ export class BodyTempStateCollection extends EqStateCollection<BodyTempState> {
|
|
|
1201
1264
|
}
|
|
1202
1265
|
public cleanupState() {
|
|
1203
1266
|
for (let i = this.data.length - 1; i >= 0; i--) {
|
|
1204
|
-
if (
|
|
1267
|
+
if (isNaN(this.data[i].id)) this.data.splice(i, 1);
|
|
1268
|
+
else {
|
|
1269
|
+
if (typeof sys.bodies.find(elem => elem.id === this.data[i].id) === 'undefined') this.removeItemById(this.data[i].id);
|
|
1270
|
+
}
|
|
1205
1271
|
}
|
|
1272
|
+
this.sortById();
|
|
1206
1273
|
}
|
|
1207
1274
|
|
|
1208
1275
|
}
|
|
@@ -1221,6 +1288,7 @@ export class BodyTempState extends EqState {
|
|
|
1221
1288
|
public dataName = 'bodyTempState';
|
|
1222
1289
|
public initData() {
|
|
1223
1290
|
if (typeof this.data.heaterOptions === 'undefined') this.data.heaterOptions = { total: 0 };
|
|
1291
|
+
if (typeof this.data.isCovered === 'undefined') this.data.isCovered = false;
|
|
1224
1292
|
}
|
|
1225
1293
|
public get id(): number { return this.data.id; }
|
|
1226
1294
|
public set id(val: number) { this.setDataVal('id', val); }
|
|
@@ -1259,6 +1327,8 @@ export class BodyTempState extends EqState {
|
|
|
1259
1327
|
public set coolSetpoint(val: number) { this.setDataVal('coolSetpoint', val); }
|
|
1260
1328
|
public get isOn(): boolean { return this.data.isOn; }
|
|
1261
1329
|
public set isOn(val: boolean) { this.setDataVal('isOn', val); }
|
|
1330
|
+
public get isCovered(): boolean { return this.data.isCovered; }
|
|
1331
|
+
public set isCovered(val: boolean) { this.setDataVal('isCovered', val); }
|
|
1262
1332
|
public emitData(name: string, data: any) { webApp.emitToClients('body', this.data); }
|
|
1263
1333
|
// RKS: This is a very interesting object because we have a varied object. Type safety rules should not apply
|
|
1264
1334
|
// here as the heater types are specific to the installed equipment. The reason is because it has no meaning without the body and the calculation of it should
|
|
@@ -1325,7 +1395,13 @@ export class HeaterStateCollection extends EqStateCollection<HeaterState> {
|
|
|
1325
1395
|
public createItem(data: any): HeaterState { return new HeaterState(data); }
|
|
1326
1396
|
public cleanupState() {
|
|
1327
1397
|
for (let i = this.data.length - 1; i >= 0; i--) {
|
|
1328
|
-
if (
|
|
1398
|
+
if (isNaN(this.data[i].id)) {
|
|
1399
|
+
logger.info(`Removed Invalid Heater ${this.data[i].id}-${this.data[i].name}`);
|
|
1400
|
+
this.data.splice(i, 1);
|
|
1401
|
+
}
|
|
1402
|
+
else {
|
|
1403
|
+
if (typeof sys.heaters.find(elem => elem.id === this.data[i].id) === 'undefined') this.removeItemById(this.data[i].id);
|
|
1404
|
+
}
|
|
1329
1405
|
}
|
|
1330
1406
|
let cfg = sys.heaters.toArray();
|
|
1331
1407
|
for (let i = 0; i < cfg.length; i++) {
|
|
@@ -1344,8 +1420,10 @@ export class HeaterState extends EqState {
|
|
|
1344
1420
|
public set name(val: string) { this.setDataVal('name', val); }
|
|
1345
1421
|
public get isOn(): boolean { return this.data.isOn; }
|
|
1346
1422
|
public set isOn(val: boolean) { this.setDataVal('isOn', val); }
|
|
1347
|
-
public get
|
|
1348
|
-
public set
|
|
1423
|
+
public get isCooling(): boolean { return this.data.isCooling; }
|
|
1424
|
+
public set isCooling(val: boolean) { this.setDataVal('isCooling', val); }
|
|
1425
|
+
//public get isVirtual(): boolean { return this.data.isVirtual; }
|
|
1426
|
+
//public set isVirtual(val: boolean) { this.setDataVal('isVirtual', val); }
|
|
1349
1427
|
public get type(): number | any { return typeof this.data.type !== 'undefined' ? this.data.type.val : 0; }
|
|
1350
1428
|
public set type(val: number | any) {
|
|
1351
1429
|
if (this.type !== val) {
|
|
@@ -1367,7 +1445,10 @@ export class FeatureStateCollection extends EqStateCollection<FeatureState> {
|
|
|
1367
1445
|
public async toggleFeatureStateAsync(id: number) { return sys.board.features.toggleFeatureStateAsync(id); }
|
|
1368
1446
|
public cleanupState() {
|
|
1369
1447
|
for (let i = this.data.length - 1; i >= 0; i--) {
|
|
1370
|
-
if (
|
|
1448
|
+
if (isNaN(this.data[i].id)) this.data.splice(i, 1);
|
|
1449
|
+
else {
|
|
1450
|
+
if (typeof sys.features.find(elem => elem.id === this.data[i].id) === 'undefined') this.removeItemById(this.data[i].id);
|
|
1451
|
+
}
|
|
1371
1452
|
}
|
|
1372
1453
|
let cfg = sys.features.toArray();
|
|
1373
1454
|
for (let i = 0; i < cfg.length; i++) {
|
|
@@ -1383,6 +1464,9 @@ export class FeatureStateCollection extends EqStateCollection<FeatureState> {
|
|
|
1383
1464
|
|
|
1384
1465
|
export class FeatureState extends EqState implements ICircuitState {
|
|
1385
1466
|
public dataName: string = 'feature';
|
|
1467
|
+
public initData() {
|
|
1468
|
+
if (typeof this.data.freezeProtect === 'undefined') this.data.freezeProtect = false;
|
|
1469
|
+
}
|
|
1386
1470
|
public get id(): number { return this.data.id; }
|
|
1387
1471
|
public set id(val: number) { this.data.id = val; }
|
|
1388
1472
|
public get name(): string { return this.data.name; }
|
|
@@ -1404,7 +1488,13 @@ export class FeatureState extends EqState implements ICircuitState {
|
|
|
1404
1488
|
if (typeof this.data.endTime === 'undefined') return undefined;
|
|
1405
1489
|
return new Timestamp(this.data.endTime);
|
|
1406
1490
|
}
|
|
1407
|
-
public set endTime(val: Timestamp) { typeof val !== 'undefined' ? this.setDataVal('endTime', Timestamp.toISOLocal(val.toDate())
|
|
1491
|
+
public set endTime(val: Timestamp) { typeof val !== 'undefined' ? this.setDataVal('endTime', Timestamp.toISOLocal(val.toDate())) : this.setDataVal('endTime', undefined); }
|
|
1492
|
+
// This property will be set if the system has turn this feature on for freeze protection reasons. We have no way of knowing when Pentair does this but
|
|
1493
|
+
// need to know (so we can shut it off) if we have done this.
|
|
1494
|
+
public get freezeProtect(): boolean { return this.data.freezeProtect; }
|
|
1495
|
+
public set freezeProtect(val: boolean) { this.setDataVal('freezeProtect', val); }
|
|
1496
|
+
public get isActive(): boolean { return this.data.isActive; }
|
|
1497
|
+
public set isActive(val: boolean) { this.setDataVal('isActive', val); }
|
|
1408
1498
|
}
|
|
1409
1499
|
export class VirtualCircuitState extends EqState implements ICircuitState {
|
|
1410
1500
|
public dataName: string = 'virtualCircuit';
|
|
@@ -1427,7 +1517,7 @@ export class VirtualCircuitState extends EqState implements ICircuitState {
|
|
|
1427
1517
|
if (typeof this.data.endTime === 'undefined') return undefined;
|
|
1428
1518
|
return new Timestamp(this.data.endTime);
|
|
1429
1519
|
}
|
|
1430
|
-
public set endTime(val: Timestamp) { typeof val !== 'undefined' ? this.setDataVal('endTime', Timestamp.toISOLocal(val.toDate())
|
|
1520
|
+
public set endTime(val: Timestamp) { typeof val !== 'undefined' ? this.setDataVal('endTime', Timestamp.toISOLocal(val.toDate())) : this.setDataVal('endTime', undefined); }
|
|
1431
1521
|
}
|
|
1432
1522
|
export class VirtualCircuitStateCollection extends EqStateCollection<VirtualCircuitState> {
|
|
1433
1523
|
public createItem(data: any): VirtualCircuitState { return new VirtualCircuitState(data); }
|
|
@@ -1452,7 +1542,10 @@ export class CircuitStateCollection extends EqStateCollection<CircuitState> {
|
|
|
1452
1542
|
}
|
|
1453
1543
|
public cleanupState() {
|
|
1454
1544
|
for (let i = this.data.length - 1; i >= 0; i--) {
|
|
1455
|
-
if (
|
|
1545
|
+
if (isNaN(this.data[i].id)) this.data.splice(i, 1);
|
|
1546
|
+
else {
|
|
1547
|
+
if (typeof sys.circuits.find(elem => elem.id === this.data[i].id) === 'undefined') this.removeItemById(this.data[i].id);
|
|
1548
|
+
}
|
|
1456
1549
|
}
|
|
1457
1550
|
let cfg = sys.circuits.toArray();
|
|
1458
1551
|
for (let i = 0; i < cfg.length; i++) {
|
|
@@ -1468,6 +1561,9 @@ export class CircuitStateCollection extends EqStateCollection<CircuitState> {
|
|
|
1468
1561
|
}
|
|
1469
1562
|
export class CircuitState extends EqState implements ICircuitState {
|
|
1470
1563
|
public dataName = 'circuit';
|
|
1564
|
+
public initData() {
|
|
1565
|
+
if (typeof this.data.freezeProtect === 'undefined') this.data.freezeProtect = false;
|
|
1566
|
+
}
|
|
1471
1567
|
public get id(): number { return this.data.id; }
|
|
1472
1568
|
public set id(val: number) { this.data.id = val; }
|
|
1473
1569
|
public get name(): string { return this.data.name; }
|
|
@@ -1485,10 +1581,12 @@ export class CircuitState extends EqState implements ICircuitState {
|
|
|
1485
1581
|
this.hasChanged = true;
|
|
1486
1582
|
}
|
|
1487
1583
|
}
|
|
1488
|
-
public get lightingTheme(): number { return typeof
|
|
1584
|
+
public get lightingTheme(): number { return typeof this.data.lightingTheme !== 'undefined' ? this.data.lightingTheme.val : 255; }
|
|
1489
1585
|
public set lightingTheme(val: number) {
|
|
1490
1586
|
if (this.lightingTheme !== val) {
|
|
1491
|
-
this
|
|
1587
|
+
// Force this to undefined when we are a circuit without a theme.
|
|
1588
|
+
if (typeof val === 'undefined') this.data.lightingTheme = undefined;
|
|
1589
|
+
else this.data.lightingTheme = sys.board.valueMaps.lightThemes.transform(val);
|
|
1492
1590
|
this.hasChanged = true;
|
|
1493
1591
|
}
|
|
1494
1592
|
}
|
|
@@ -1505,13 +1603,22 @@ export class CircuitState extends EqState implements ICircuitState {
|
|
|
1505
1603
|
if (typeof this.data.endTime === 'undefined') return undefined;
|
|
1506
1604
|
return new Timestamp(this.data.endTime);
|
|
1507
1605
|
}
|
|
1508
|
-
public set endTime(val: Timestamp) { typeof val !== 'undefined' ? this.setDataVal('endTime', Timestamp.toISOLocal(val.toDate())
|
|
1606
|
+
public set endTime(val: Timestamp) { typeof val !== 'undefined' ? this.setDataVal('endTime', Timestamp.toISOLocal(val.toDate())) : this.setDataVal('endTime', undefined); }
|
|
1607
|
+
// This property will be set if the system has turn this circuit on for freeze protection reasons. We have no way of knowing when Pentair does this but
|
|
1608
|
+
// need to know (so we can shut it off) if we have done this.
|
|
1609
|
+
public get freezeProtect(): boolean { return this.data.freezeProtect; }
|
|
1610
|
+
public set freezeProtect(val: boolean) { this.setDataVal('freezeProtect', val); }
|
|
1611
|
+
public get isActive(): boolean { return this.data.isActive; }
|
|
1612
|
+
public set isActive(val: boolean) { this.setDataVal('isActive', val); }
|
|
1509
1613
|
}
|
|
1510
1614
|
export class ValveStateCollection extends EqStateCollection<ValveState> {
|
|
1511
1615
|
public createItem(data: any): ValveState { return new ValveState(data); }
|
|
1512
1616
|
public cleanupState() {
|
|
1513
1617
|
for (let i = this.data.length - 1; i >= 0; i--) {
|
|
1514
|
-
if (
|
|
1618
|
+
if (isNaN(this.data[i].id)) this.data.splice(i, 1);
|
|
1619
|
+
else {
|
|
1620
|
+
if (typeof sys.valves.find(elem => elem.id === this.data[i].id) === 'undefined') this.removeItemById(this.data[i].id);
|
|
1621
|
+
}
|
|
1515
1622
|
}
|
|
1516
1623
|
let cfg = sys.valves.toArray();
|
|
1517
1624
|
for (let i = 0; i < cfg.length; i++) {
|
|
@@ -1550,7 +1657,7 @@ export class ValveState extends EqState {
|
|
|
1550
1657
|
if (valve.circuit !== 256) vstate.circuit = state.circuits.getInterfaceById(valve.circuit).get(true);
|
|
1551
1658
|
vstate.isIntake = utils.makeBool(valve.isIntake);
|
|
1552
1659
|
vstate.isReturn = utils.makeBool(valve.isReturn);
|
|
1553
|
-
vstate.isVirtual = utils.makeBool(valve.isVirtual);
|
|
1660
|
+
// vstate.isVirtual = utils.makeBool(valve.isVirtual);
|
|
1554
1661
|
vstate.isActive = utils.makeBool(valve.isActive);
|
|
1555
1662
|
vstate.pinId = valve.pinId;
|
|
1556
1663
|
return vstate;
|
|
@@ -1568,12 +1675,15 @@ export class CoverStateCollection extends EqStateCollection<CoverState> {
|
|
|
1568
1675
|
}
|
|
1569
1676
|
export class CoverState extends EqState {
|
|
1570
1677
|
public dataName: string = 'cover';
|
|
1678
|
+
public initData() {
|
|
1679
|
+
if (typeof this.data.isClosed === 'undefined') this.data.isClosed = false;
|
|
1680
|
+
}
|
|
1571
1681
|
public get id(): number { return this.data.id; }
|
|
1572
1682
|
public set id(val: number) { this.data.id = val; }
|
|
1573
1683
|
public get name(): string { return this.data.name; }
|
|
1574
1684
|
public set name(val: string) { this.setDataVal('name', val); }
|
|
1575
|
-
public get
|
|
1576
|
-
public set
|
|
1685
|
+
public get isClosed(): boolean { return this.data.isClosed; }
|
|
1686
|
+
public set isClosed(val: boolean) { this.setDataVal('isClosed', val); }
|
|
1577
1687
|
}
|
|
1578
1688
|
export class ChlorinatorStateCollection extends EqStateCollection<ChlorinatorState> {
|
|
1579
1689
|
public createItem(data: any): ChlorinatorState { return new ChlorinatorState(data); }
|
|
@@ -1581,7 +1691,10 @@ export class ChlorinatorStateCollection extends EqStateCollection<ChlorinatorSta
|
|
|
1581
1691
|
public lastDispatchSuperChlor: number = 0;
|
|
1582
1692
|
public cleanupState() {
|
|
1583
1693
|
for (let i = this.data.length - 1; i >= 0; i--) {
|
|
1584
|
-
if (
|
|
1694
|
+
if (isNaN(this.data[i].id)) this.data.splice(i, 1);
|
|
1695
|
+
else {
|
|
1696
|
+
if (typeof sys.chlorinators.find(elem => elem.id === this.data[i].id) === 'undefined') this.removeItemById(this.data[i].id);
|
|
1697
|
+
}
|
|
1585
1698
|
}
|
|
1586
1699
|
let cfg = sys.chlorinators.toArray();
|
|
1587
1700
|
for (let i = 0; i < cfg.length; i++) {
|
|
@@ -1634,16 +1747,6 @@ export class ChlorinatorState extends EqState {
|
|
|
1634
1747
|
this.hasChanged = true;
|
|
1635
1748
|
}
|
|
1636
1749
|
}
|
|
1637
|
-
//public get virtualControllerStatus(): number {
|
|
1638
|
-
// return typeof (this.data.virtualControllerStatus) !== 'undefined' ? this.data.virtualControllerStatus.val : -1;
|
|
1639
|
-
//}
|
|
1640
|
-
//public set virtualControllerStatus(val: number) {
|
|
1641
|
-
// if (this.virtualControllerStatus !== val) {
|
|
1642
|
-
// this.data.virtualControllerStatus = sys.board.valueMaps.virtualControllerStatus.transform(val);
|
|
1643
|
-
// this.hasChanged = true;
|
|
1644
|
-
|
|
1645
|
-
// }
|
|
1646
|
-
//}
|
|
1647
1750
|
public get type(): number { return typeof (this.data.type) !== 'undefined' ? this.data.type.val : -1; }
|
|
1648
1751
|
public set type(val: number) {
|
|
1649
1752
|
if (this.type !== val) {
|
|
@@ -1730,12 +1833,21 @@ export class ChlorinatorState extends EqState {
|
|
|
1730
1833
|
else
|
|
1731
1834
|
this.setDataVal('superChlor', false);
|
|
1732
1835
|
}
|
|
1836
|
+
public getExtended(): any {
|
|
1837
|
+
let schlor = this.get(true);
|
|
1838
|
+
let chlor = sys.chlorinators.getItemById(this.id, false);
|
|
1839
|
+
schlor.lockSetpoints = chlor.disabled || chlor.isDosing;
|
|
1840
|
+
return schlor;
|
|
1841
|
+
}
|
|
1733
1842
|
}
|
|
1734
1843
|
export class ChemControllerStateCollection extends EqStateCollection<ChemControllerState> {
|
|
1735
1844
|
public createItem(data: any): ChemControllerState { return new ChemControllerState(data); }
|
|
1736
1845
|
public cleanupState() {
|
|
1737
1846
|
for (let i = this.data.length - 1; i >= 0; i--) {
|
|
1738
|
-
if (
|
|
1847
|
+
if (isNaN(this.data[i].id)) this.data.splice(i, 1);
|
|
1848
|
+
else {
|
|
1849
|
+
if (typeof sys.chemControllers.find(elem => elem.id === this.data[i].id) === 'undefined') this.removeItemById(this.data[i].id);
|
|
1850
|
+
}
|
|
1739
1851
|
}
|
|
1740
1852
|
// Make sure we have at least the items that exist in the config.
|
|
1741
1853
|
let cfg = sys.chemControllers.toArray();
|
|
@@ -1775,7 +1887,7 @@ export class ChemControllerState extends EqState {
|
|
|
1775
1887
|
//var chemControllerState = {
|
|
1776
1888
|
// lastComm: 'number', // The unix time the chem controller sent its status.
|
|
1777
1889
|
// id: 'number', // Id of the chemController.
|
|
1778
|
-
// type: 'valueMap', // intellichem,
|
|
1890
|
+
// type: 'valueMap', // intellichem, rem.
|
|
1779
1891
|
// address: 'number', // Assigned address if IntelliChem.
|
|
1780
1892
|
// name: 'string', // Name assigned to the controller.
|
|
1781
1893
|
// status: 'valueMap', // ok, nocomms, setupError
|
|
@@ -1998,21 +2110,22 @@ export class ChemControllerState extends EqState {
|
|
|
1998
2110
|
export class ChemicalState extends ChildEqState {
|
|
1999
2111
|
public initData() {
|
|
2000
2112
|
if (typeof this.data.probe === 'undefined') this.data.probe = {};
|
|
2001
|
-
if (typeof this.data.tank
|
|
2113
|
+
if (typeof this.data.tank === 'undefined') this.data.tank = { capacity: 0, level: 0, units: 0 };
|
|
2114
|
+
if (typeof this.data.pump === 'undefined') this.data.pump = { isDosing: false };
|
|
2002
2115
|
if (typeof this.data.dosingTimeRemaining === 'undefined') this.data.dosingTimeRemaining = 0;
|
|
2003
2116
|
if (typeof this.data.delayTimeRemaining === 'undefined') this.data.delayTimeRemaining = 0;
|
|
2004
2117
|
if (typeof this.data.dosingVolumeRemaining === 'undefined') this.data.dosingVolumeRemaining = 0;
|
|
2005
2118
|
if (typeof this.data.doseVolume === 'undefined') this.data.doseVolume = 0;
|
|
2006
2119
|
if (typeof this.data.doseTime === 'undefined') this.data.doseTime = 0;
|
|
2007
2120
|
if (typeof this.data.lockout === 'undefined') this.data.lockout = false;
|
|
2008
|
-
if (typeof this.data.level
|
|
2121
|
+
if (typeof this.data.level === 'undefined') this.data.level = 0;
|
|
2009
2122
|
if (typeof this.data.mixTimeRemaining === 'undefined') this.data.mixTimeRemaining = 0;
|
|
2010
2123
|
if (typeof this.data.dailyLimitReached === 'undefined') this.data.dailyLimitReached = false;
|
|
2011
2124
|
if (typeof this.data.manualDosing === 'undefined') this.data.manualDosing = false;
|
|
2125
|
+
if (typeof this.data.manualMixing === 'undefined') this.data.manualMixing = false;
|
|
2012
2126
|
if (typeof this.data.flowDelay === 'undefined') this.data.flowDelay = false;
|
|
2013
2127
|
if (typeof this.data.dosingStatus === 'undefined') this.dosingStatus = 2;
|
|
2014
2128
|
if (typeof this.data.enabled === 'undefined') this.data.enabled = true;
|
|
2015
|
-
if (typeof this.data.level === 'undefined') this.data.level = 0;
|
|
2016
2129
|
}
|
|
2017
2130
|
public getConfig(): Chemical { return; }
|
|
2018
2131
|
public calcDoseHistory(): number {
|
|
@@ -2073,7 +2186,7 @@ export class ChemicalState extends ChildEqState {
|
|
|
2073
2186
|
this.doseHistory.unshift(dose);
|
|
2074
2187
|
this.dailyVolumeDosed = this.calcDoseHistory();
|
|
2075
2188
|
DataLogger.writeEnd(`chemDosage_${this.chemType}.log`, dose);
|
|
2076
|
-
webApp.emitToClients(`chemicalDose`, dose);
|
|
2189
|
+
setImmediate(() => { webApp.emitToClients(`chemicalDose`, dose); });
|
|
2077
2190
|
}
|
|
2078
2191
|
this.currentDose = undefined;
|
|
2079
2192
|
}
|
|
@@ -2088,7 +2201,7 @@ export class ChemicalState extends ChildEqState {
|
|
|
2088
2201
|
this.timeDosed = Math.round(dose._timeDosed / 1000);
|
|
2089
2202
|
this.dosingTimeRemaining = dose.timeRemaining;
|
|
2090
2203
|
this.dosingVolumeRemaining = dose.volumeRemaining;
|
|
2091
|
-
if (dose.volumeDosed > 0) webApp.emitToClients(`chemicalDose`, dose);
|
|
2204
|
+
if (dose.volumeDosed > 0) setImmediate(() => { webApp.emitToClients(`chemicalDose`, dose); });
|
|
2092
2205
|
return dose;
|
|
2093
2206
|
}
|
|
2094
2207
|
public get currentDose(): ChemicalDoseState {
|
|
@@ -2112,6 +2225,11 @@ export class ChemicalState extends ChildEqState {
|
|
|
2112
2225
|
return this.data.doseHistory;
|
|
2113
2226
|
}
|
|
2114
2227
|
public set doseHistory(val: ChemicalDoseState[]) { this.setDataVal('doseHistory', val); }
|
|
2228
|
+
public appendDemand(time: number, val: number) {
|
|
2229
|
+
let dH = this.demandHistory;
|
|
2230
|
+
dH.appendDemand(time, val);
|
|
2231
|
+
}
|
|
2232
|
+
public get demandHistory() { return new ChemicalDemandState(this.data, 'demandHistory', this) };
|
|
2115
2233
|
public get enabled(): boolean { return this.data.enabled; }
|
|
2116
2234
|
public set enabled(val: boolean) { this.data.enabled = val; }
|
|
2117
2235
|
public get level(): number { return this.data.level; }
|
|
@@ -2143,6 +2261,7 @@ export class ChemicalState extends ChildEqState {
|
|
|
2143
2261
|
public get dosingStatus(): number { return typeof (this.data.dosingStatus) !== 'undefined' ? this.data.dosingStatus.val : undefined; }
|
|
2144
2262
|
public set dosingStatus(val: number) {
|
|
2145
2263
|
if (this.dosingStatus !== val) {
|
|
2264
|
+
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})`);
|
|
2146
2265
|
this.data.dosingStatus = sys.board.valueMaps.chemControllerDosingStatus.transform(val);
|
|
2147
2266
|
this.hasChanged = true;
|
|
2148
2267
|
}
|
|
@@ -2153,10 +2272,13 @@ export class ChemicalState extends ChildEqState {
|
|
|
2153
2272
|
public set flowDelay(val: boolean) { this.data.flowDelay = val; }
|
|
2154
2273
|
public get manualDosing(): boolean { return utils.makeBool(this.data.manualDosing); }
|
|
2155
2274
|
public set manualDosing(val: boolean) { this.setDataVal('manualDosing', val); }
|
|
2275
|
+
public get manualMixing(): boolean { return utils.makeBool(this.data.manualMixing); }
|
|
2276
|
+
public set manualMixing(val: boolean) { this.setDataVal('manualMixing', val); }
|
|
2156
2277
|
public get dailyLimitReached(): boolean { return utils.makeBool(this.data.dailyLimitReached); }
|
|
2157
2278
|
public set dailyLimitReached(val: boolean) { this.data.dailyLimitReached = val; }
|
|
2158
2279
|
public get tank(): ChemicalTankState { return new ChemicalTankState(this.data, 'tank', this); }
|
|
2159
2280
|
public get pump(): ChemicalPumpState { return new ChemicalPumpState(this.data, 'pump', this); }
|
|
2281
|
+
public get chlor(): ChemicalChlorState { return new ChemicalChlorState(this.data, 'chlor', this); }
|
|
2160
2282
|
public calcDemand(chem?: ChemController): number { return 0; }
|
|
2161
2283
|
public getExtended() {
|
|
2162
2284
|
let chem = this.get(true);
|
|
@@ -2167,7 +2289,7 @@ export class ChemicalState extends ChildEqState {
|
|
|
2167
2289
|
}
|
|
2168
2290
|
export class ChemicalPhState extends ChemicalState {
|
|
2169
2291
|
public initData() {
|
|
2170
|
-
if (typeof this.data.chemType === 'undefined') this.data.chemType === 'acid';
|
|
2292
|
+
// if (typeof this.data.chemType === 'undefined') this.data.chemType === 'acid'; // RSG 10-23-21 - Only a getter; don't need to set this.
|
|
2171
2293
|
super.initData();
|
|
2172
2294
|
}
|
|
2173
2295
|
public getConfig() {
|
|
@@ -2200,7 +2322,7 @@ export class ChemicalPhState extends ChemicalState {
|
|
|
2200
2322
|
if (chem.body === 1 || chem.body === 32 || sys.equipment.shared) totalGallons += sys.bodies.getItemById(2).capacity;
|
|
2201
2323
|
if (chem.body === 2) totalGallons += sys.bodies.getItemById(3).capacity;
|
|
2202
2324
|
if (chem.body === 3) totalGallons += sys.bodies.getItemById(4).capacity;
|
|
2203
|
-
logger.verbose(`Chem begin calculating demand: ${this.level} setpoint: ${this.setpoint} body: ${totalGallons}`);
|
|
2325
|
+
logger.verbose(`Chem begin calculating ${this.chemType} demand: ${this.level} setpoint: ${this.setpoint} body: ${totalGallons}`);
|
|
2204
2326
|
let chg = this.setpoint - this.level;
|
|
2205
2327
|
let delta = chg * totalGallons;
|
|
2206
2328
|
let temp = (this.level + this.setpoint) / 2;
|
|
@@ -2227,6 +2349,7 @@ export class ChemicalORPState extends ChemicalState {
|
|
|
2227
2349
|
public initData() {
|
|
2228
2350
|
if (typeof this.data.probe === 'undefined') this.data.probe = {};
|
|
2229
2351
|
if (typeof this.data.chemType === 'undefined') this.data.chemType === 'orp';
|
|
2352
|
+
if (typeof this.data.useChlorinator === 'undefined') this.data.useChlorinator = false;
|
|
2230
2353
|
super.initData();
|
|
2231
2354
|
// Load up the 24 hours doseHistory.
|
|
2232
2355
|
//this.doseHistory = DataLogger.readFromEnd(`chemDosage_${this.chemType}.log`, ChemicalDoseState, (lineNumber: number, entry: ChemicalDoseState): boolean => {
|
|
@@ -2239,6 +2362,8 @@ export class ChemicalORPState extends ChemicalState {
|
|
|
2239
2362
|
}
|
|
2240
2363
|
public get chemType() { return 'orp'; }
|
|
2241
2364
|
public get probe() { return new ChemicalProbeORPState(this.data, 'probe', this); }
|
|
2365
|
+
public get useChlorinator(): boolean { return utils.makeBool(this.data.useChlorinator); }
|
|
2366
|
+
public set useChlorinator(val: boolean) { this.setDataVal('useChlorinator', val); }
|
|
2242
2367
|
public get suspendDosing(): boolean {
|
|
2243
2368
|
let cc = this.chemController;
|
|
2244
2369
|
return cc.alarms.comms !== 0 || cc.alarms.orpProbeFault !== 0 || cc.alarms.orpPumpFault !== 0 || cc.alarms.bodyFault !== 0;
|
|
@@ -2280,6 +2405,22 @@ export class ChemicalPumpState extends ChildEqState {
|
|
|
2280
2405
|
return pump;
|
|
2281
2406
|
}
|
|
2282
2407
|
}
|
|
2408
|
+
export class ChemicalChlorState extends ChildEqState {
|
|
2409
|
+
public initData() {
|
|
2410
|
+
if (typeof this.data.isDosing === 'undefined') this.data.isDosing = false;
|
|
2411
|
+
}
|
|
2412
|
+
public get chemical(): ChemicalState { return this.getParent() as ChemicalState; }
|
|
2413
|
+
public get chemController(): ChemControllerState {
|
|
2414
|
+
let p = this.chemical;
|
|
2415
|
+
return typeof p !== 'undefined' ? p.getParent() as ChemControllerState : undefined;
|
|
2416
|
+
}
|
|
2417
|
+
public get isDosing(): boolean { return utils.makeBool(this.data.isDosing); }
|
|
2418
|
+
public set isDosing(val: boolean) { this.setDataVal('isDosing', val); }
|
|
2419
|
+
public getExtended() {
|
|
2420
|
+
let chlor = this.get(true);
|
|
2421
|
+
return chlor;
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2283
2424
|
export class ChemicalProbeState extends ChildEqState {
|
|
2284
2425
|
public initData() {
|
|
2285
2426
|
if (typeof this.data.level === 'undefined') this.data.level = null;
|
|
@@ -2353,14 +2494,16 @@ export class ChemicalDoseState extends DataLoggerEntry {
|
|
|
2353
2494
|
public _isManual: boolean;
|
|
2354
2495
|
|
|
2355
2496
|
constructor(entry?: string | object) {
|
|
2356
|
-
super(
|
|
2497
|
+
super();
|
|
2498
|
+
if (typeof entry === 'object') entry = JSON.stringify(entry);
|
|
2499
|
+
if (typeof entry === 'string') this.parse(entry);
|
|
2357
2500
|
// Javascript is idiotic in that the initialization of variables
|
|
2358
2501
|
// do not happen before the assignment so some of the values can be undefined.
|
|
2359
2502
|
if (typeof this.volumeDosed === 'undefined' || !this.volumeDosed) this.volumeDosed = 0;
|
|
2360
2503
|
if (typeof this.volume === 'undefined' || !this.volume) this.volume = 0;
|
|
2361
2504
|
if (typeof this._isManual === 'undefined') this._isManual = this.method === 'manual';
|
|
2362
2505
|
if (typeof this.timeDosed === 'undefined' || !this.timeDosed) this.timeDosed = 0;
|
|
2363
|
-
if (typeof this._timeDosed === 'undefined') this.timeDosed * 1000;
|
|
2506
|
+
if (typeof this._timeDosed === 'undefined') this._timeDosed = this.timeDosed * 1000;
|
|
2364
2507
|
if (typeof this.time === 'undefined' || !this.time) this.time = 0;
|
|
2365
2508
|
}
|
|
2366
2509
|
public id: number;
|
|
@@ -2377,12 +2520,65 @@ export class ChemicalDoseState extends DataLoggerEntry {
|
|
|
2377
2520
|
public time: number;
|
|
2378
2521
|
public timeDosed: number;
|
|
2379
2522
|
|
|
2380
|
-
public createInstance(entry?: string): ChemicalDoseState { return new ChemicalDoseState(entry); }
|
|
2523
|
+
public static createInstance(entry?: string): ChemicalDoseState { return new ChemicalDoseState(entry); }
|
|
2381
2524
|
public save() { DataLogger.writeEnd(`chemDosage_${this.chem}.log`, this); }
|
|
2382
2525
|
public get timeRemaining(): number { return Math.floor(Math.max(0, this.time - (this._timeDosed / 1000))); }
|
|
2383
2526
|
public get volumeRemaining(): number { return Math.max(0, this.volume - this.volumeDosed); }
|
|
2527
|
+
public parse(entry: string) {
|
|
2528
|
+
// let obj = typeof entry !== 'undefined' ? JSON.parse(entry, this.dateParser) : {};
|
|
2529
|
+
let obj = typeof entry !== 'undefined' ? JSON.parse(entry) : {};
|
|
2530
|
+
for (const prop in obj) {obj[prop] = this.dateParser(prop, obj[prop])}
|
|
2531
|
+
if (typeof obj.setpoint !== 'undefined') this.setpoint = obj.setpoint;
|
|
2532
|
+
if (typeof obj.method !== 'undefined') this.method = obj.method;
|
|
2533
|
+
if (typeof obj.start !== 'undefined') this.start = obj.start;
|
|
2534
|
+
if (typeof obj.end !== 'undefined') this.end = obj.end;
|
|
2535
|
+
if (typeof obj.chem !== 'undefined') this.chem = obj.chem;
|
|
2536
|
+
if (typeof obj.demand !== 'undefined') this.demand = obj.demand;
|
|
2537
|
+
if (typeof obj.id !== 'undefined') this.id = obj.id;
|
|
2538
|
+
if (typeof obj.level !== 'undefined') this.level = obj.level;
|
|
2539
|
+
if (typeof obj.volume !== 'undefined') this.volume = obj.volume;
|
|
2540
|
+
if (typeof obj.status !== 'undefined') this.status = obj.status;
|
|
2541
|
+
if (typeof obj.volumeDosed !== 'undefined') this.volumeDosed = obj.volumeDosed;
|
|
2542
|
+
if (typeof obj.time !== 'undefined') this.time = obj.time;
|
|
2543
|
+
if (typeof obj.timeDosed !== 'undefined') this.timeDosed = obj.timeDosed;
|
|
2544
|
+
// this.setProperties(obj);
|
|
2545
|
+
}
|
|
2546
|
+
protected setProperties(data: any) {
|
|
2547
|
+
let op = Object.getOwnPropertyNames(Object.getPrototypeOf(this));
|
|
2548
|
+
for (let i in op) {
|
|
2549
|
+
let prop = op[i];
|
|
2550
|
+
if (typeof this[prop] === 'function') continue;
|
|
2551
|
+
if (typeof data[prop] !== 'undefined') {
|
|
2552
|
+
if (typeof this[prop] === null || typeof data[prop] === null) continue;
|
|
2553
|
+
this[prop] = data[prop];
|
|
2554
|
+
}
|
|
2555
|
+
}
|
|
2556
|
+
}
|
|
2384
2557
|
}
|
|
2385
2558
|
|
|
2559
|
+
export class ChemicalDemandState extends ChildEqState {
|
|
2560
|
+
public initData() {
|
|
2561
|
+
if (typeof this.data.time === 'undefined') this.data.time = [];
|
|
2562
|
+
if (typeof this.data.value === 'undefined') this.data.value = [];
|
|
2563
|
+
}
|
|
2564
|
+
|
|
2565
|
+
public appendDemand(time: number, val: number) {
|
|
2566
|
+
while (this.data.time.length > 99) {
|
|
2567
|
+
this.data.time.pop();
|
|
2568
|
+
this.data.value.pop();
|
|
2569
|
+
}
|
|
2570
|
+
this.data.time.unshift(Math.round(time / 1000));
|
|
2571
|
+
this.data.value.unshift(val);
|
|
2572
|
+
// calculate the slope with each save
|
|
2573
|
+
let slope = utils.slopeOfLeastSquares(this.data.time, this.data.value);
|
|
2574
|
+
this.setDataVal('slope', slope); // will act as hasChanged=true;
|
|
2575
|
+
}
|
|
2576
|
+
public get demandHistory(): {} { return [this.data.time, this.data.value]; }
|
|
2577
|
+
public get times(): number[] { return this.data.time; }
|
|
2578
|
+
public get values(): number[] { return this.data.value; }
|
|
2579
|
+
public set slope(val: number) { this.setDataVal('slope', val); }
|
|
2580
|
+
public get slope():number { return this.data.slope; }
|
|
2581
|
+
}
|
|
2386
2582
|
|
|
2387
2583
|
export class ChemControllerStateWarnings extends ChildEqState {
|
|
2388
2584
|
///ctor(data): ChemControllerStateWarnings { return new ChemControllerStateWarnings(data, name || 'warnings'); }
|
|
@@ -2605,15 +2801,35 @@ export class FilterState extends EqState {
|
|
|
2605
2801
|
this.hasChanged = true;
|
|
2606
2802
|
}
|
|
2607
2803
|
}
|
|
2608
|
-
public get
|
|
2609
|
-
public set
|
|
2610
|
-
|
|
2611
|
-
|
|
2804
|
+
public get pressureUnits(): number { return this.data.pressureUnits; }
|
|
2805
|
+
public set pressureUnits(val: number) {
|
|
2806
|
+
if (this.pressureUnits !== val) {
|
|
2807
|
+
this.setDataVal('pressureUnits', sys.board.valueMaps.pressureUnits.transform(val));
|
|
2808
|
+
}
|
|
2809
|
+
}
|
|
2810
|
+
public get pressure(): number { return this.data.pressure; }
|
|
2811
|
+
public set pressure(val: number) { this.setDataVal('pressure', val); }
|
|
2812
|
+
public get refPressure(): number { return this.data.refPressure; }
|
|
2813
|
+
public set refPressure(val: number) {
|
|
2814
|
+
if (val !== this.refPressure) {
|
|
2815
|
+
this.setDataVal('refPressure', val);
|
|
2816
|
+
this.calcCleanPercentage();
|
|
2817
|
+
}
|
|
2818
|
+
else { this.setDataVal('refPressure', val); }
|
|
2819
|
+
}
|
|
2820
|
+
public get cleanPercentage(): number { return this.data.cleanPercentage; }
|
|
2821
|
+
public set cleanPercentage(val: number) { this.setDataVal('cleanPercentage', val); }
|
|
2612
2822
|
public get lastCleanDate(): Timestamp { return this.data.lastCleanDate; }
|
|
2613
2823
|
public set lastCleanDate(val: Timestamp) { this.setDataVal('lastCleanDate', val); }
|
|
2614
|
-
public get needsCleaning(): number { return this.data.needsCleaning; }
|
|
2615
|
-
public set needsCleaning(val: number) { this.setDataVal('needsCleaning', val); }
|
|
2616
2824
|
public get isOn(): boolean { return utils.makeBool(this.data.isOn); }
|
|
2617
2825
|
public set isOn(val: boolean) { this.setDataVal('isOn', val); }
|
|
2826
|
+
public calcCleanPercentage() {
|
|
2827
|
+
if (typeof this.refPressure === 'undefined') return;
|
|
2828
|
+
let filter = sys.filters.find(elem => elem.id == this.id);
|
|
2829
|
+
// 8 to 10
|
|
2830
|
+
let cp = filter.cleanPressure || 0;
|
|
2831
|
+
let dp = filter.dirtyPressure || 1;
|
|
2832
|
+
this.cleanPercentage = (cp - dp != 0) ? Math.round(Math.max(0, (1 - (this.refPressure - cp) / (dp - cp)) * 100) * 100)/100 : 0;
|
|
2833
|
+
}
|
|
2618
2834
|
}
|
|
2619
2835
|
export var state = new State();
|