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
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { utils, Timestamp } from '../../Constants';
|
|
3
|
-
import { logger } from '../../../logger/Logger';
|
|
4
|
-
|
|
5
|
-
import { NixieEquipment, NixieChildEquipment, NixieEquipmentCollection, INixieControlPanel } from "../NixieEquipment";
|
|
6
|
-
import { ChemController, Chemical, ChemicalPh, ChemicalORP, ChemicalPhProbe, ChemicalORPProbe, ChemicalTank, ChemicalPump, sys, ChemicalProbe, ChemControllerCollection, ChemFlowSensor } from "../../../controller/Equipment";
|
|
7
|
-
import { ChemControllerState, ChemicalState, ChemicalORPState, ChemicalPhState, state, ChemicalProbeState, ChemicalProbePHState, ChemicalProbeORPState, ChemicalTankState, ChemicalPumpState, ChemicalDoseState } from "../../State";
|
|
8
|
-
import { setTimeout, clearTimeout } from 'timers';
|
|
9
|
-
import { webApp, InterfaceServerResponse } from "../../../web/Server";
|
|
1
|
+
import { clearTimeout, setTimeout } from 'timers';
|
|
10
2
|
import { conn } from '../../../controller/comms/Comms';
|
|
11
3
|
import { Outbound, Protocol, Response } from '../../../controller/comms/messages/Messages';
|
|
4
|
+
import { ChemController, ChemControllerCollection, ChemFlowSensor, Chemical, ChemicalChlor, ChemicalORP, ChemicalORPProbe, ChemicalPh, ChemicalPhProbe, ChemicalProbe, ChemicalPump, ChemicalTank, sys } from "../../../controller/Equipment";
|
|
5
|
+
import { logger } from '../../../logger/Logger';
|
|
6
|
+
import { InterfaceServerResponse, webApp } from "../../../web/Server";
|
|
7
|
+
import { Timestamp, utils } from '../../Constants';
|
|
8
|
+
import { EquipmentNotFoundError, EquipmentTimeoutError, InvalidEquipmentDataError, InvalidEquipmentIdError, InvalidOperationError } from '../../Errors';
|
|
9
|
+
import { ChemControllerState, ChemicalChlorState, ChemicalDoseState, ChemicalORPState, ChemicalPhState, ChemicalProbeORPState, ChemicalProbePHState, ChemicalProbeState, ChemicalPumpState, ChemicalState, ChemicalTankState, ChlorinatorState, state } from "../../State";
|
|
10
|
+
import { ncp } from '../Nixie';
|
|
11
|
+
import { INixieControlPanel, NixieChildEquipment, NixieEquipment, NixieEquipmentCollection } from "../NixieEquipment";
|
|
12
|
+
import { NixieChlorinator } from './Chlorinator';
|
|
13
|
+
|
|
12
14
|
|
|
13
15
|
export class NixieChemControllerCollection extends NixieEquipmentCollection<NixieChemControllerBase> {
|
|
14
16
|
public async manualDoseAsync(id: number, data: any) {
|
|
@@ -41,7 +43,7 @@ export class NixieChemControllerCollection extends NixieEquipmentCollection<Nixi
|
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
public async setControllerAsync(chem: ChemController, data: any) {
|
|
44
|
-
// By the time we get here we know that we are in control and this
|
|
46
|
+
// By the time we get here we know that we are in control and this REM Chem or IntelliChem.
|
|
45
47
|
try {
|
|
46
48
|
let ncc: NixieChemControllerBase = this.find(elem => elem.id === chem.id) as NixieChemControllerBase;
|
|
47
49
|
if (typeof ncc === 'undefined') {
|
|
@@ -49,12 +51,13 @@ export class NixieChemControllerCollection extends NixieEquipmentCollection<Nixi
|
|
|
49
51
|
ncc = NixieChemControllerBase.create(this.controlPanel, chem);
|
|
50
52
|
this.push(ncc);
|
|
51
53
|
let ctype = sys.board.valueMaps.chemControllerTypes.transform(chem.type);
|
|
52
|
-
logger.info(`
|
|
54
|
+
logger.info(`Nixie Chem Controller was created at id #${chem.id} for type ${ctype.desc}`);
|
|
53
55
|
await ncc.setControllerAsync(data);
|
|
54
56
|
}
|
|
55
57
|
else {
|
|
56
58
|
await ncc.setControllerAsync(data);
|
|
57
59
|
}
|
|
60
|
+
// Now go back through the array and undo anything that is in need of pruning.
|
|
58
61
|
}
|
|
59
62
|
catch (err) { logger.error(`setControllerAsync: ${err.message}`); return Promise.reject(err); }
|
|
60
63
|
}
|
|
@@ -67,13 +70,20 @@ export class NixieChemControllerCollection extends NixieEquipmentCollection<Nixi
|
|
|
67
70
|
}
|
|
68
71
|
public async initAsync(controllers: ChemControllerCollection) {
|
|
69
72
|
try {
|
|
70
|
-
this.length = 0;
|
|
71
73
|
for (let i = 0; i < controllers.length; i++) {
|
|
72
74
|
let cc = controllers.getItemByIndex(i);
|
|
73
75
|
if (cc.master === 1) {
|
|
76
|
+
let type = sys.board.valueMaps.chemControllerTypes.transform(cc.type);
|
|
74
77
|
logger.info(`Initializing chemController ${cc.name}`);
|
|
75
|
-
|
|
76
|
-
this.
|
|
78
|
+
// First check to make sure it isnt already there.
|
|
79
|
+
if (typeof this.find(elem => elem.id === cc.id) === 'undefined') {
|
|
80
|
+
|
|
81
|
+
let ncc = NixieChemControllerBase.create(this.controlPanel, cc);
|
|
82
|
+
this.push(ncc);
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
logger.info(`chemController ${cc.name} has already been initialized`);
|
|
86
|
+
}
|
|
77
87
|
}
|
|
78
88
|
}
|
|
79
89
|
}
|
|
@@ -86,39 +96,56 @@ export class NixieChemControllerCollection extends NixieEquipmentCollection<Nixi
|
|
|
86
96
|
logger.info(`Closing chemController ${this[i].id}`);
|
|
87
97
|
await this[i].closeAsync();
|
|
88
98
|
this.splice(i, 1);
|
|
89
|
-
} catch (err) { logger.error(`Error stopping Nixie Chem Controller ${err}`); return Promise.reject(err);}
|
|
99
|
+
} catch (err) { logger.error(`Error stopping Nixie Chem Controller ${err}`); return Promise.reject(err); }
|
|
90
100
|
}
|
|
91
101
|
|
|
92
102
|
} catch (err) { } // Don't bail if we have an error
|
|
93
103
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
dest: addr,
|
|
103
|
-
action: 210,
|
|
104
|
-
payload: [210],
|
|
105
|
-
retries: 1, // We are going to try 2 times.
|
|
106
|
-
response: Response.create({ protocol: Protocol.IntelliChem, action: 18 }),
|
|
107
|
-
onAbort: () => { },
|
|
108
|
-
onComplete: (err) => {
|
|
109
|
-
if (err) resolve(false);
|
|
110
|
-
else resolve(true);
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
conn.queueSendMessage(out);
|
|
114
|
-
});
|
|
115
|
-
if (success) arr.push(addr)
|
|
104
|
+
public async deleteChlorAsync(chlor: NixieChlorinator) {
|
|
105
|
+
// if we delete the chlor, make sure it is removed from all REM Chem Controllers
|
|
106
|
+
try {
|
|
107
|
+
for (let i = this.length - 1; i >= 0; i--) {
|
|
108
|
+
try {
|
|
109
|
+
let ncc = this[i] as NixieChemControllerBase;;
|
|
110
|
+
ncc.orp.deleteChlorAsync(chlor);
|
|
111
|
+
} catch (err) { logger.error(`Error deleting chlor from Nixie Chem Controller ${err}`); return Promise.reject(err); }
|
|
116
112
|
}
|
|
117
|
-
|
|
113
|
+
|
|
114
|
+
}
|
|
115
|
+
catch (err) { logger.error(`ncp.deleteChlorAsync: ${err.message}`); return Promise.reject(err); }
|
|
118
116
|
}
|
|
117
|
+
// This is currently not used for anything.
|
|
118
|
+
/* public async searchIntelliChem(): Promise<number[]> {
|
|
119
|
+
let arr = [];
|
|
120
|
+
try {
|
|
121
|
+
for (let addr = 144; addr <= 152; addr++) {
|
|
122
|
+
let success = await new Promise<void>((resolve, reject) => {
|
|
123
|
+
let out = Outbound.create({
|
|
124
|
+
protocol: Protocol.IntelliChem,
|
|
125
|
+
dest: addr,
|
|
126
|
+
action: 210,
|
|
127
|
+
payload: [210],
|
|
128
|
+
retries: 1, // We are going to try 2 times.
|
|
129
|
+
response: Response.create({ protocol: Protocol.IntelliChem, action: 18 }),
|
|
130
|
+
onAbort: () => { },
|
|
131
|
+
onComplete: (err) => {
|
|
132
|
+
if (err) resolve(false);
|
|
133
|
+
else resolve(true);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
conn.queueSendMessage(out);
|
|
137
|
+
});
|
|
138
|
+
if (success) arr.push(addr)
|
|
139
|
+
}
|
|
140
|
+
} catch (err) { return arr; }
|
|
141
|
+
} */
|
|
119
142
|
}
|
|
120
143
|
export class NixieChemControllerBase extends NixieEquipment {
|
|
121
144
|
public pollingInterval: number = 10000;
|
|
145
|
+
protected _suspendPolling: number = 0;
|
|
146
|
+
public get suspendPolling(): boolean { return this._suspendPolling > 0; }
|
|
147
|
+
public set suspendPolling(val: boolean) { this._suspendPolling = Math.max(0, this._suspendPolling + (val ? 1 : -1)); }
|
|
148
|
+
public _ispolling = false;
|
|
122
149
|
protected _pollTimer: NodeJS.Timeout = null;
|
|
123
150
|
protected closing = false;
|
|
124
151
|
public orp: NixieChemicalORP;
|
|
@@ -132,14 +159,12 @@ export class NixieChemControllerBase extends NixieEquipment {
|
|
|
132
159
|
this.chem = chem;
|
|
133
160
|
}
|
|
134
161
|
public chem: ChemController;
|
|
135
|
-
public syncRemoteREMFeeds(servers) {}
|
|
136
|
-
public static create(ncp: INixieControlPanel, chem: ChemController): NixieChemControllerBase
|
|
137
|
-
// RKS: 06-25-21 - Keeping the homegrown around for now but I don't really know why we care.
|
|
162
|
+
public syncRemoteREMFeeds(servers) { }
|
|
163
|
+
public static create(ncp: INixieControlPanel, chem: ChemController): NixieChemControllerBase {
|
|
138
164
|
let type = sys.board.valueMaps.chemControllerTypes.transform(chem.type);
|
|
139
165
|
switch (type.name) {
|
|
140
166
|
case 'intellichem':
|
|
141
167
|
return new NixieIntelliChemController(ncp, chem);
|
|
142
|
-
case 'homegrown':
|
|
143
168
|
case 'rem':
|
|
144
169
|
return new NixieChemController(ncp, chem);
|
|
145
170
|
default:
|
|
@@ -155,12 +180,9 @@ export class NixieChemControllerBase extends NixieEquipment {
|
|
|
155
180
|
else if (!isOn) this.bodyOnTime = undefined;
|
|
156
181
|
return isOn;
|
|
157
182
|
}
|
|
158
|
-
public async setControllerAsync(data: any) {} // This is meant to be abstract override this value
|
|
183
|
+
public async setControllerAsync(data: any) { } // This is meant to be abstract override this value
|
|
159
184
|
}
|
|
160
185
|
export class NixieIntelliChemController extends NixieChemControllerBase {
|
|
161
|
-
protected _suspendPolling: number = 0;
|
|
162
|
-
public get suspendPolling(): boolean { return this._suspendPolling > 0; }
|
|
163
|
-
public set suspendPolling(val: boolean) { this._suspendPolling = Math.max(0, this._suspendPolling + (val ? 1 : -1)); }
|
|
164
186
|
public configSent: boolean = false;
|
|
165
187
|
constructor(ncp: INixieControlPanel, chem: ChemController) {
|
|
166
188
|
super(ncp, chem);
|
|
@@ -169,6 +191,7 @@ export class NixieIntelliChemController extends NixieChemControllerBase {
|
|
|
169
191
|
this.pollEquipmentAsync();
|
|
170
192
|
}
|
|
171
193
|
public async pollEquipmentAsync() {
|
|
194
|
+
let self = this;
|
|
172
195
|
try {
|
|
173
196
|
this.suspendPolling = true;
|
|
174
197
|
if (typeof this._pollTimer !== 'undefined' || this._pollTimer) clearTimeout(this._pollTimer);
|
|
@@ -181,11 +204,11 @@ export class NixieIntelliChemController extends NixieChemControllerBase {
|
|
|
181
204
|
// since most of the time these are attached to the filter relay.
|
|
182
205
|
if (this.isBodyOn() && !this.closing) {
|
|
183
206
|
if (!this.configSent) await this.sendConfig(schem);
|
|
184
|
-
if(!this.closing) await this.requestStatus(schem);
|
|
207
|
+
if (!this.closing) await this.requestStatus(schem);
|
|
185
208
|
}
|
|
186
209
|
}
|
|
187
210
|
catch (err) { logger.error(`Error polling IntelliChem Controller - ${err}`); return Promise.reject(err); }
|
|
188
|
-
finally { this.suspendPolling = false; if (!this.closing) this._pollTimer = setTimeout(
|
|
211
|
+
finally { this.suspendPolling = false; if (!this.closing) this._pollTimer = setTimeout(() => { self.pollEquipmentAsync(); }, this.pollingInterval || 10000); }
|
|
189
212
|
}
|
|
190
213
|
public async setControllerAsync(data: any) {
|
|
191
214
|
try {
|
|
@@ -194,7 +217,7 @@ export class NixieIntelliChemController extends NixieChemControllerBase {
|
|
|
194
217
|
let address = typeof data.address !== 'undefined' ? parseInt(data.address) : chem.address;
|
|
195
218
|
let name = typeof data.name !== 'undefined' ? data.name : chem.name || `IntelliChem - ${address - 143}`;
|
|
196
219
|
let type = sys.board.valueMaps.chemControllerTypes.transformByName('intellichem');
|
|
197
|
-
// So now we are down to the nitty gritty setting the data for the REM
|
|
220
|
+
// So now we are down to the nitty gritty setting the data for the REM Chem controller.
|
|
198
221
|
let calciumHardness = typeof data.calciumHardness !== 'undefined' ? parseInt(data.calciumHardness, 10) : chem.calciumHardness;
|
|
199
222
|
let cyanuricAcid = typeof data.cyanuricAcid !== 'undefined' ? parseInt(data.cyanuricAcid, 10) : chem.cyanuricAcid;
|
|
200
223
|
let alkalinity = typeof data.alkalinity !== 'undefined' ? parseInt(data.alkalinity, 10) : chem.alkalinity;
|
|
@@ -247,7 +270,7 @@ export class NixieIntelliChemController extends NixieChemControllerBase {
|
|
|
247
270
|
chem.borates = borates;
|
|
248
271
|
chem.body = schem.body = body;
|
|
249
272
|
chem.type = schem.type = type.val;
|
|
250
|
-
|
|
273
|
+
|
|
251
274
|
let acidTankLevel = typeof data.ph !== 'undefined' && typeof data.ph.tank !== 'undefined' && typeof data.ph.tank.level !== 'undefined' ? parseInt(data.ph.tank.level, 10) : schem.ph.tank.level;
|
|
252
275
|
let orpTankLevel = typeof data.orp !== 'undefined' && typeof data.orp.tank !== 'undefined' && typeof data.orp.tank.level !== 'undefined' ? parseInt(data.orp.tank.level, 10) : schem.orp.tank.level;
|
|
253
276
|
// Copy the data back to the chem object.
|
|
@@ -270,7 +293,7 @@ export class NixieIntelliChemController extends NixieChemControllerBase {
|
|
|
270
293
|
chem.orp.tolerance.high = orpTolerance.high;
|
|
271
294
|
chem.ph.setpoint = pHSetpoint;
|
|
272
295
|
chem.orp.setpoint = orpSetpoint;
|
|
273
|
-
chem.siCalcType = siCalcType;
|
|
296
|
+
schem.siCalcType = chem.siCalcType = siCalcType;
|
|
274
297
|
chem.address = schem.address = address;
|
|
275
298
|
chem.name = schem.name = name;
|
|
276
299
|
chem.flowSensor.enabled = false;
|
|
@@ -357,10 +380,10 @@ export class NixieIntelliChemController extends NixieChemControllerBase {
|
|
|
357
380
|
if (typeof this._pollTimer !== 'undefined' || this._pollTimer) clearTimeout(this._pollTimer);
|
|
358
381
|
this._pollTimer = null;
|
|
359
382
|
let schem = state.chemControllers.getItemById(this.chem.id);
|
|
360
|
-
if(typeof this.ph !== 'undefined') await this.ph.closeAsync();
|
|
383
|
+
if (typeof this.ph !== 'undefined') await this.ph.closeAsync();
|
|
361
384
|
if (typeof this.orp !== 'undefined') await this.orp.closeAsync();
|
|
362
385
|
logger.info(`Closing Chem Controller ${this.chem.name}`);
|
|
363
|
-
|
|
386
|
+
|
|
364
387
|
}
|
|
365
388
|
catch (err) { logger.error(`ChemController closeAsync: ${err.message}`); return Promise.reject(err); }
|
|
366
389
|
}
|
|
@@ -382,6 +405,7 @@ export class NixieChemController extends NixieChemControllerBase {
|
|
|
382
405
|
// not been set and we have a state obect defined.
|
|
383
406
|
let cstate = state.chemControllers.find(x => x.id === chem.id);
|
|
384
407
|
if (cstate && typeof cstate !== 'undefined') {
|
|
408
|
+
console.log(`Converting to v2.x data structure`);
|
|
385
409
|
if (cstate.ph.dosingStatus === 1) cstate.ph.dosingStatus = 2;
|
|
386
410
|
else if (cstate.ph.dosingStatus === 2) cstate.ph.dosingStatus = 1;
|
|
387
411
|
if (cstate.orp.dosingStatus === 1) cstate.orp.dosingStatus = 2;
|
|
@@ -391,6 +415,7 @@ export class NixieChemController extends NixieChemControllerBase {
|
|
|
391
415
|
}
|
|
392
416
|
public async manualDoseAsync(data: any) {
|
|
393
417
|
try {
|
|
418
|
+
this.suspendPolling = true;
|
|
394
419
|
// Check to see that we are a rem chem.
|
|
395
420
|
let vol = parseInt(data.volume, 10);
|
|
396
421
|
if (isNaN(vol)) return Promise.reject(new InvalidEquipmentDataError(`Volume was not supplied for the manual chem dose`, 'chemController', data.volume));
|
|
@@ -406,9 +431,11 @@ export class NixieChemController extends NixieChemControllerBase {
|
|
|
406
431
|
else if (chemType === 'orp') await this.orp.manualDoseAsync(schem, vol);
|
|
407
432
|
}
|
|
408
433
|
catch (err) { logger.error(`manualDoseAsync: ${err.message}`); return Promise.reject(err); }
|
|
434
|
+
finally { this.suspendPolling = false; }
|
|
409
435
|
}
|
|
410
436
|
public async manualMixAsync(data: any) {
|
|
411
437
|
try {
|
|
438
|
+
this.suspendPolling = true;
|
|
412
439
|
// Check to see that we are a rem chem.
|
|
413
440
|
let time = 0;
|
|
414
441
|
if (typeof data.hours !== 'undefined') time += parseInt(data.hours, 10) * 3600;
|
|
@@ -427,9 +454,11 @@ export class NixieChemController extends NixieChemControllerBase {
|
|
|
427
454
|
else if (chemType === 'orp') await this.orp.mixChemicals(schem, time);
|
|
428
455
|
}
|
|
429
456
|
catch (err) { logger.error(`manualMixAsync: ${err.message}`); return Promise.reject(err); }
|
|
457
|
+
finally { this.suspendPolling = false; }
|
|
430
458
|
}
|
|
431
459
|
public async cancelDosingAsync(data: any) {
|
|
432
460
|
try {
|
|
461
|
+
this.suspendPolling = true;
|
|
433
462
|
// Determine which chemical we are cancelling. This will be ph or orp.
|
|
434
463
|
let chemType = typeof data.chemType === 'string' ? data.chemType.toLowerCase() : '';
|
|
435
464
|
if (typeof this[chemType] === 'undefined') return Promise.reject(new InvalidEquipmentDataError(`A valid Chem type was not supplied for the manual chem dose ${data.chemType}`, 'chemController', data.chemType));
|
|
@@ -442,25 +471,24 @@ export class NixieChemController extends NixieChemControllerBase {
|
|
|
442
471
|
else if (chemType === 'orp') await this.orp.cancelDosing(schem, 'cancelled');
|
|
443
472
|
}
|
|
444
473
|
catch (err) { logger.error(`cancelDosingAsync: ${err.message}`); return Promise.reject(err); }
|
|
474
|
+
finally { this.suspendPolling = false; }
|
|
445
475
|
}
|
|
446
476
|
public async cancelMixingAsync(data: any) {
|
|
447
477
|
try {
|
|
478
|
+
this.suspendPolling = true;
|
|
448
479
|
// Determine which chemical we are cancelling. This will be ph or orp.
|
|
449
480
|
let chemType = typeof data.chemType === 'string' ? data.chemType.toLowerCase() : '';
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
if (
|
|
453
|
-
|
|
454
|
-
if (typeof schem === 'undefined') return Promise.reject(new InvalidEquipmentDataError(`Could not cancel ${data.chemType} mix state not found.`, 'chemController', data.chemType));
|
|
455
|
-
// Now we can tell the chemical to dose.
|
|
456
|
-
if (chemType === 'ph') await this.ph.cancelMixing(schem);
|
|
457
|
-
else if (chemType === 'orp') await this.orp.cancelMixing(schem);
|
|
458
|
-
schem.dosingStatus = sys.board.valueMaps.chemControllerDosingStatus.getValue('monitoring');
|
|
481
|
+
let schem = state.chemControllers.getItemById(this.chem.id);
|
|
482
|
+
if (chemType === 'ph') await this.ph.cancelMixing(schem.ph);
|
|
483
|
+
else if (chemType === 'orp') await this.orp.cancelMixing(schem.orp);
|
|
484
|
+
else return Promise.reject(new InvalidEquipmentDataError(`A valid Chem type was not supplied for chemical ${data.chemType}`, 'chemController', data.chemType));
|
|
459
485
|
}
|
|
460
|
-
catch (err) { logger.error(`
|
|
486
|
+
catch (err) { logger.error(`cancelMixingAsync: ${err.message}`); return Promise.reject(err); }
|
|
487
|
+
finally { this.suspendPolling = false; }
|
|
461
488
|
}
|
|
462
489
|
public async setControllerAsync(data: any) {
|
|
463
490
|
try {
|
|
491
|
+
this.suspendPolling = true;
|
|
464
492
|
let chem = this.chem;
|
|
465
493
|
// So now we are down to the nitty gritty setting the data for the REM or Homegrown Chem controller.
|
|
466
494
|
let calciumHardness = typeof data.calciumHardness !== 'undefined' ? parseInt(data.calciumHardness, 10) : chem.calciumHardness;
|
|
@@ -488,7 +516,7 @@ export class NixieChemController extends NixieChemControllerBase {
|
|
|
488
516
|
if (typeof data.lsiRange.low === 'number') chem.lsiRange.low = data.lsiRange.low;
|
|
489
517
|
if (typeof data.lsiRange.high === 'number') chem.lsiRange.high = data.lsiRange.high;
|
|
490
518
|
}
|
|
491
|
-
if (typeof data.siCalcType !== 'undefined') chem.siCalcType = data.siCalcType;
|
|
519
|
+
if (typeof data.siCalcType !== 'undefined') schem.siCalcType = chem.siCalcType = data.siCalcType;
|
|
492
520
|
await this.flowSensor.setSensorAsync(data.flowSensor);
|
|
493
521
|
// Alright we are down to the equipment items all validation should have been completed by now.
|
|
494
522
|
// ORP Settings
|
|
@@ -498,9 +526,11 @@ export class NixieChemController extends NixieChemControllerBase {
|
|
|
498
526
|
await this.processAlarms(schem);
|
|
499
527
|
}
|
|
500
528
|
catch (err) { logger.error(`setControllerAsync: ${err.message}`); return Promise.reject(err); }
|
|
529
|
+
finally { this.suspendPolling = false; }
|
|
501
530
|
}
|
|
502
531
|
public async checkFlowAsync(schem: ChemControllerState): Promise<boolean> {
|
|
503
532
|
try {
|
|
533
|
+
this.suspendPolling = true;
|
|
504
534
|
schem.isBodyOn = this.isBodyOn();
|
|
505
535
|
// rsg - we were not returning the flow sensor state when the body was off.
|
|
506
536
|
// first, this would not allow us to retrieve a pressure of 0 to update flowSensor.state
|
|
@@ -510,6 +540,7 @@ export class NixieChemController extends NixieChemControllerBase {
|
|
|
510
540
|
schem.alarms.flowSensorFault = 0;
|
|
511
541
|
}
|
|
512
542
|
else {
|
|
543
|
+
logger.verbose(`Begin getting flow sensor state`);
|
|
513
544
|
let ret = await this.flowSensor.getState();
|
|
514
545
|
schem.flowSensor.state = ret.obj.state;
|
|
515
546
|
// Call out to REM to see if we have flow.
|
|
@@ -526,6 +557,7 @@ export class NixieChemController extends NixieChemControllerBase {
|
|
|
526
557
|
else if (typeof ret.obj.state === 'boolean') v = ret.obj.state;
|
|
527
558
|
else if (typeof ret.obj.state === 'number') v = utils.makeBool(ret.obj.state);
|
|
528
559
|
else if (typeof ret.obj.state.val === 'number') v = utils.makeBool(ret.obj.state.val);
|
|
560
|
+
else if (typeof ret.obj.state.value === 'number') v = utils.makeBool(ret.obj.state.value);
|
|
529
561
|
else v = false;
|
|
530
562
|
this.flowDetected = schem.flowDetected = v;
|
|
531
563
|
}
|
|
@@ -541,15 +573,22 @@ export class NixieChemController extends NixieChemControllerBase {
|
|
|
541
573
|
}
|
|
542
574
|
if (!schem.flowDetected) this.bodyOnTime = undefined;
|
|
543
575
|
else if (typeof this.bodyOnTime === 'undefined') this.bodyOnTime = new Date().getTime();
|
|
576
|
+
logger.verbose(`End getting flow sensor state`);
|
|
544
577
|
return schem.flowDetected;
|
|
545
578
|
}
|
|
546
|
-
catch (err) { logger.error(`checkFlowAsync: ${err.message}`); schem.alarms.flowSensorFault = 7; this.flowDetected = schem.flowDetected = false; return Promise.reject(err);}
|
|
579
|
+
catch (err) { logger.error(`checkFlowAsync: ${err.message}`); schem.alarms.flowSensorFault = 7; this.flowDetected = schem.flowDetected = false; return Promise.reject(err); }
|
|
580
|
+
finally { this.suspendPolling = false; }
|
|
547
581
|
}
|
|
548
582
|
public async pollEquipmentAsync() {
|
|
583
|
+
let self = this;
|
|
549
584
|
try {
|
|
585
|
+
logger.verbose(`Begin polling Chem Controller ${this.id}`);
|
|
586
|
+
if (this._suspendPolling > 0) logger.warn(`Suspend polling for ${this.chem.name} -> ${this._suspendPolling}`);
|
|
587
|
+
if (this.suspendPolling) return;
|
|
588
|
+
if (this._ispolling) return;
|
|
589
|
+
this._ispolling = true;
|
|
550
590
|
if (typeof this._pollTimer !== 'undefined' || this._pollTimer) clearTimeout(this._pollTimer);
|
|
551
591
|
this._pollTimer = null;
|
|
552
|
-
let success = false;
|
|
553
592
|
let schem = state.chemControllers.getItemById(this.chem.id, !this.closing);
|
|
554
593
|
// We need to check on the equipment to make sure it is solid.
|
|
555
594
|
if (sys.board.valueMaps.chemControllerTypes.getName(this.chem.type) === 'rem') {
|
|
@@ -560,10 +599,8 @@ export class NixieChemController extends NixieChemControllerBase {
|
|
|
560
599
|
await this.checkFlowAsync(schem);
|
|
561
600
|
await this.validateSetupAsync(this.chem, schem);
|
|
562
601
|
if (this.chem.ph.enabled) await this.ph.probe.setTempCompensationAsync(schem.ph.probe);
|
|
563
|
-
// We are not processing Homegrown at this point.
|
|
564
602
|
// Check each piece of equipment to make sure it is doing its thing.
|
|
565
603
|
schem.calculateSaturationIndex();
|
|
566
|
-
//this.calculateSaturationIndex();
|
|
567
604
|
this.processAlarms(schem);
|
|
568
605
|
if (this.chem.ph.enabled) await this.ph.checkDosing(this.chem, schem.ph);
|
|
569
606
|
if (this.chem.orp.enabled) await this.orp.checkDosing(this.chem, schem.orp);
|
|
@@ -571,9 +608,14 @@ export class NixieChemController extends NixieChemControllerBase {
|
|
|
571
608
|
else
|
|
572
609
|
logger.warn('REM Server not Connected');
|
|
573
610
|
}
|
|
611
|
+
this._ispolling = false;
|
|
612
|
+
}
|
|
613
|
+
catch (err) { this._ispolling = false; logger.error(`Error polling Chem Controller - ${err}`); }
|
|
614
|
+
finally {
|
|
615
|
+
if (!this.closing && !this._ispolling)
|
|
616
|
+
this._pollTimer = setTimeout(() => { self.pollEquipmentAsync(); }, this.pollingInterval || 10000);
|
|
617
|
+
logger.verbose(`End polling Chem Controller ${this.id}`);
|
|
574
618
|
}
|
|
575
|
-
catch (err) { logger.error(`Error polling Chem Controller - ${err}`); return Promise.reject(err);}
|
|
576
|
-
finally { if(!this.closing) this._pollTimer = setTimeout(async () => {try {await this.pollEquipmentAsync()} catch (err){return Promise.reject(err);}}, this.pollingInterval || 10000); }
|
|
577
619
|
}
|
|
578
620
|
public processAlarms(schem: ChemControllerState) {
|
|
579
621
|
try {
|
|
@@ -588,7 +630,7 @@ export class NixieChemController extends NixieChemControllerBase {
|
|
|
588
630
|
}
|
|
589
631
|
else {
|
|
590
632
|
// both flow and pressure sensors (type 2 & 4)
|
|
591
|
-
if (schem.isBodyOn && schem.
|
|
633
|
+
if (schem.isBodyOn && !schem.flowDetected || !schem.isBodyOn && schem.flowDetected) {
|
|
592
634
|
schem.alarms.flow = 1;
|
|
593
635
|
}
|
|
594
636
|
else schem.alarms.flow = 0;
|
|
@@ -612,7 +654,7 @@ export class NixieChemController extends NixieChemControllerBase {
|
|
|
612
654
|
else schem.alarms.orpTank = 0;
|
|
613
655
|
// Alright we need to determine whether we need to adjust the volume any so that we get at least 3 seconds out of the pump.
|
|
614
656
|
let padj = this.chem.orp.pump.type > 0 && !this.chem.orp.useChlorinator ? (this.chem.orp.pump.ratedFlow / 60) * 3 : 0;
|
|
615
|
-
if (this.chem.orp.maxDailyVolume <= schem.orp.dailyVolumeDosed) {
|
|
657
|
+
if (this.chem.orp.maxDailyVolume <= schem.orp.dailyVolumeDosed && !this.chem.orp.useChlorinator) {
|
|
616
658
|
schem.warnings.orpDailyLimitReached = 4;
|
|
617
659
|
schem.orp.dailyLimitReached = true;
|
|
618
660
|
}
|
|
@@ -671,6 +713,7 @@ export class NixieChemController extends NixieChemControllerBase {
|
|
|
671
713
|
if (chem.lsiRange.enabled) {
|
|
672
714
|
schem.warnings.waterChemistry = schem.saturationIndex < chem.lsiRange.low ? 1 : schem.saturationIndex > chem.lsiRange.high ? 2 : 0;
|
|
673
715
|
}
|
|
716
|
+
else schem.warnings.waterChemistry = 0;
|
|
674
717
|
} catch (err) { logger.error(`Error processing chem controller ${this.chem.name} alarms: ${err.message}`); }
|
|
675
718
|
}
|
|
676
719
|
private async checkHardwareStatusAsync(connectionId: string, deviceBinding: string) {
|
|
@@ -682,6 +725,7 @@ export class NixieChemController extends NixieChemControllerBase {
|
|
|
682
725
|
public async validateSetupAsync(chem: ChemController, schem: ChemControllerState) {
|
|
683
726
|
try {
|
|
684
727
|
// The validation will be different if the body is on or not. So lets get that information.
|
|
728
|
+
logger.verbose(`Begin validating ${chem.id} - ${chem.name} setup`);
|
|
685
729
|
if (chem.orp.enabled) {
|
|
686
730
|
if (chem.orp.probe.type !== 0) {
|
|
687
731
|
let type = sys.board.valueMaps.chemORPProbeTypes.transform(chem.orp.probe.type);
|
|
@@ -741,8 +785,11 @@ export class NixieChemController extends NixieChemControllerBase {
|
|
|
741
785
|
if (chem.body === 2) totalGallons += sys.bodies.getItemById(3).capacity;
|
|
742
786
|
if (chem.body === 3) totalGallons += sys.bodies.getItemById(4).capacity;
|
|
743
787
|
schem.alarms.bodyFault = (isNaN(totalGallons) || totalGallons === 0) ? 6 : 0;
|
|
788
|
+
if (schem.alarms.bodyFault !== 0) logger.warn(`Chem controller body calculation invalid ${totalGallons} -> ${chem.body}`);
|
|
744
789
|
}
|
|
745
790
|
schem.alarms.comms = 0;
|
|
791
|
+
logger.verbose(`End validating ${chem.id} - ${chem.name} setup`);
|
|
792
|
+
|
|
746
793
|
} catch (err) { logger.error(`Error checking Chem Controller Hardware ${this.chem.name}: ${err.message}`); schem.alarms.comms = 2; return Promise.reject(err); }
|
|
747
794
|
}
|
|
748
795
|
public async closeAsync() {
|
|
@@ -768,44 +815,40 @@ export class NixieChemController extends NixieChemControllerBase {
|
|
|
768
815
|
class NixieChemical extends NixieChildEquipment {
|
|
769
816
|
public chemical: Chemical;
|
|
770
817
|
public pump: NixieChemPump;
|
|
818
|
+
public chlor: NixieChemChlor;
|
|
771
819
|
public tank: NixieChemTank;
|
|
772
820
|
public _lastOnStatus: number;
|
|
821
|
+
protected _stoppingMix = false;
|
|
822
|
+
protected _suspendPolling: number = 0;
|
|
823
|
+
public get suspendPolling(): boolean { return this._suspendPolling > 0; }
|
|
824
|
+
public set suspendPolling(val: boolean) { this._suspendPolling = Math.max(0, this._suspendPolling + (val ? 1 : -1)); }
|
|
825
|
+
protected _processingMix = false;
|
|
773
826
|
//public currentDose: ChemicalDoseState;
|
|
774
827
|
public chemType: string;
|
|
775
|
-
public
|
|
828
|
+
public _currentMix: NixieChemMix;
|
|
776
829
|
//public doseHistory: NixieChemDoseLog[] = [];
|
|
777
830
|
protected _mixTimer: NodeJS.Timeout;
|
|
778
831
|
//public get logFilename() { return `chemDosage_unknown.log`; }
|
|
779
832
|
public get chemController(): NixieChemController { return this.getParent() as NixieChemController; }
|
|
833
|
+
public get currentMix(): NixieChemMix { return this._currentMix; }
|
|
834
|
+
public set currentMix(val: NixieChemMix) {
|
|
835
|
+
if (typeof val === 'undefined' && typeof this._currentMix !== 'undefined') logger.debug(`${this.chemical.chemType} mix set to undefined`);
|
|
836
|
+
else logger.debug(`Set new current mix ${this.chemical.chemType}`)
|
|
837
|
+
this._currentMix = val;
|
|
838
|
+
}
|
|
780
839
|
constructor(controller: NixieChemController, chemical: Chemical) {
|
|
781
840
|
super(controller);
|
|
782
841
|
chemical.master = 1;
|
|
783
842
|
this.chemical = chemical;
|
|
784
843
|
this.pump = new NixieChemPump(this, chemical.pump);
|
|
785
844
|
this.tank = new NixieChemTank(this, chemical.tank);
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
// let lines = await this.chemController.controlPanel.readLogFile(this.logFilename);
|
|
790
|
-
// let dt = new Date().getTime() - 86400000;
|
|
791
|
-
// let total = 0;
|
|
792
|
-
// for (let i = 0; i < lines.length; i++) {
|
|
793
|
-
// try {
|
|
794
|
-
// let log = NixieChemDoseLog.fromLog(lines[i]);
|
|
795
|
-
// if (log.end.getTime() > dt) {
|
|
796
|
-
// this.doseHistory.push(log);
|
|
797
|
-
// }
|
|
798
|
-
// else break; // The file should be ordered where the latest dose is at the top.
|
|
799
|
-
// } catch (err) { logger.error(`read chemController Dose History: ${err.message}`); }
|
|
800
|
-
// }
|
|
801
|
-
//})();
|
|
802
|
-
}
|
|
803
|
-
public async cancelMixing(schem: ChemicalState) {
|
|
845
|
+
logger.info(`Nixie Chemical ${chemical.chemType} object created`);
|
|
846
|
+
}
|
|
847
|
+
public async cancelMixing(schem: ChemicalState): Promise<void> {
|
|
804
848
|
try {
|
|
805
|
-
|
|
849
|
+
logger.verbose(`Cancelling ${this.chemType} Mix`);
|
|
806
850
|
await this.stopMixing(schem);
|
|
807
|
-
|
|
808
|
-
} catch (err) { logger.error(`cancelMixing pH: ${err.message}`); return Promise.reject(err); }
|
|
851
|
+
} catch (err) { logger.error(`cancelMixing ${this.chemType}: ${err.message}`); return Promise.reject(err); }
|
|
809
852
|
}
|
|
810
853
|
protected async setHardware(chemical: Chemical, data: any) {
|
|
811
854
|
try {
|
|
@@ -813,7 +856,7 @@ class NixieChemical extends NixieChildEquipment {
|
|
|
813
856
|
}
|
|
814
857
|
catch (err) { return Promise.reject(err); }
|
|
815
858
|
}
|
|
816
|
-
protected async setDosing(chemical: Chemical, data: any) {
|
|
859
|
+
protected async setDosing(chemical: Chemical, data: any): Promise<void> {
|
|
817
860
|
try {
|
|
818
861
|
if (typeof data !== 'undefined') {
|
|
819
862
|
chemical.enabled = typeof data.enabled !== 'undefined' ? utils.makeBool(data.enabled) : chemical.enabled;
|
|
@@ -830,8 +873,9 @@ class NixieChemical extends NixieChildEquipment {
|
|
|
830
873
|
}
|
|
831
874
|
} catch (err) { logger.error(`setDosing: ${err.message}`); return Promise.reject(err); }
|
|
832
875
|
}
|
|
833
|
-
protected async setMixing(chemical: Chemical, data: any) {
|
|
876
|
+
protected async setMixing(chemical: Chemical, data: any): Promise<void> {
|
|
834
877
|
try {
|
|
878
|
+
this.suspendPolling = true;
|
|
835
879
|
if (typeof data !== 'undefined') {
|
|
836
880
|
if (typeof data.mixingTimeHours !== 'undefined' || typeof data.mixingTimeMinutes !== 'undefined') {
|
|
837
881
|
data.mixingTime = (typeof data.mixingTimeHours !== 'undefined' ? parseInt(data.mixingTimeHours, 10) * 3600 : 0) +
|
|
@@ -842,103 +886,154 @@ class NixieChemical extends NixieChildEquipment {
|
|
|
842
886
|
chemical.flowOnlyMixing = typeof data.flowOnlyMixing !== 'undefined' ? utils.makeBool(data.flowOnlyMixing) : chemical.flowOnlyMixing;
|
|
843
887
|
}
|
|
844
888
|
} catch (err) { logger.error(`setMixing: ${err.message}`); return Promise.reject(err); }
|
|
889
|
+
finally { this.suspendPolling = false; }
|
|
845
890
|
}
|
|
846
|
-
protected async stopMixing(schem: ChemicalState) {
|
|
891
|
+
protected async stopMixing(schem: ChemicalState): Promise<void> {
|
|
847
892
|
try {
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
if (typeof this.
|
|
851
|
-
|
|
852
|
-
|
|
893
|
+
this._stoppingMix = true;
|
|
894
|
+
this.suspendPolling = true;
|
|
895
|
+
if (typeof this.currentMix !== 'undefined') logger.debug(`Stopping ${schem.chemType} mix and clearing the current mix object.`);
|
|
896
|
+
if (typeof this.chemController.orp.orp.useChlorinator !== 'undefined' && this.chemController.orp.orp.useChlorinator && this.chemController.orp.orp.dosingMethod > 0)
|
|
897
|
+
schem.chlor.isDosing = false;
|
|
898
|
+
else
|
|
899
|
+
schem.pump.isDosing = false;
|
|
900
|
+
|
|
901
|
+
if (typeof this.currentMix !== 'undefined' || typeof this._mixTimer !== 'undefined' || this._mixTimer) {
|
|
902
|
+
if (this._mixTimer || typeof this._mixTimer !== 'undefined') {
|
|
903
|
+
clearInterval(this._mixTimer);
|
|
904
|
+
this._mixTimer = undefined;
|
|
905
|
+
logger.verbose(`Cleared ${schem.chemType} mix timer`);
|
|
906
|
+
}
|
|
907
|
+
else
|
|
908
|
+
logger.warn(`${schem.chemType} did not have a mix timer set when cancelling.`);
|
|
909
|
+
|
|
910
|
+
if (typeof this.currentMix !== 'undefined') {
|
|
911
|
+
this.currentMix = undefined;
|
|
912
|
+
logger.verbose(`Cleared ${schem.chemType} mix object`);
|
|
913
|
+
}
|
|
914
|
+
else
|
|
915
|
+
logger.warn(`${schem.chemType} did not have a currentMix object set when cancelling.`);
|
|
916
|
+
schem.dosingStatus = sys.board.valueMaps.chemControllerDosingStatus.getValue('monitoring');
|
|
917
|
+
schem.mixTimeRemaining = 0;
|
|
918
|
+
schem.manualMixing = false;
|
|
853
919
|
}
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
} catch (err) { logger.error(`Error stopping chemical mix`); return Promise.reject(err);}
|
|
920
|
+
} catch (err) { logger.error(`Error stopping chemical mix`); return Promise.reject(err); }
|
|
921
|
+
finally { this._stoppingMix = false; this.suspendPolling = false; }
|
|
857
922
|
}
|
|
858
|
-
|
|
923
|
+
protected async initMixChemicals(schem: ChemicalState, mixingTime?: number): Promise<void> {
|
|
859
924
|
try {
|
|
860
|
-
|
|
861
|
-
let flowDetected = this.chemController.flowDetected;
|
|
862
|
-
if (typeof this._mixTimer !== 'undefined') {
|
|
863
|
-
clearTimeout(this._mixTimer);
|
|
864
|
-
this._mixTimer = undefined;
|
|
865
|
-
}
|
|
866
|
-
let dt = new Date().getTime();
|
|
867
|
-
if (typeof mixingTime !== 'undefined') {
|
|
868
|
-
// This is a manual mix so we need to make sure the pump is not dosing.
|
|
869
|
-
await this.pump.stopDosing(schem, 'completed');
|
|
870
|
-
await this.stopMixing(schem);
|
|
871
|
-
}
|
|
872
|
-
schem.pump.isDosing = false;
|
|
925
|
+
if (this._stoppingMix) return;
|
|
873
926
|
if (typeof this.currentMix === 'undefined') {
|
|
927
|
+
if (typeof mixingTime !== 'undefined') {
|
|
928
|
+
// This is a manual mix so we need to make sure the pump is not dosing.
|
|
929
|
+
logger.info(`Clearing any possible ${schem.chemType} dosing or existing mix for mixingTime: ${mixingTime}`);
|
|
930
|
+
await this.pump.stopDosing(schem, 'mix override');
|
|
931
|
+
await this.stopMixing(schem);
|
|
932
|
+
}
|
|
874
933
|
this.currentMix = new NixieChemMix();
|
|
875
934
|
if (typeof mixingTime !== 'undefined' && !isNaN(mixingTime)) {
|
|
876
935
|
this.currentMix.set({ time: mixingTime, timeMixed: 0, isManual: true });
|
|
936
|
+
schem.manualMixing = true;
|
|
877
937
|
}
|
|
878
938
|
else if (schem.mixTimeRemaining > 0) {
|
|
879
|
-
|
|
939
|
+
if (schem.manualMixing) {
|
|
940
|
+
this.currentMix.set({ time: schem.mixTimeRemaining, timeMixed: 0, isManual: true });
|
|
941
|
+
}
|
|
942
|
+
else
|
|
943
|
+
|
|
944
|
+
this.currentMix.set({ time: this.chemical.mixingTime, timeMixed: Math.max(0, this.chemical.mixingTime - schem.mixTimeRemaining) });
|
|
880
945
|
}
|
|
946
|
+
|
|
881
947
|
else
|
|
882
948
|
this.currentMix.set({ time: this.chemical.mixingTime, timeMixed: 0 });
|
|
883
949
|
logger.info(`Chem Controller begin mixing ${schem.chemType} for ${utils.formatDuration(this.currentMix.timeRemaining)} of ${utils.formatDuration(this.currentMix.time)}`)
|
|
884
|
-
schem.
|
|
885
|
-
|
|
950
|
+
schem.mixTimeRemaining = this.currentMix.timeRemaining;
|
|
951
|
+
}
|
|
952
|
+
if (typeof this._mixTimer === 'undefined' || !this._mixTimer) {
|
|
953
|
+
let self = this;
|
|
954
|
+
this._mixTimer = setInterval(async () => { await self.mixChemicals(schem); }, 1000);
|
|
955
|
+
logger.verbose(`Set ${schem.chemType} mix timer`);
|
|
956
|
+
}
|
|
957
|
+
} catch (err) { logger.error(`Error initializing ${schem.chemType} mix: ${err.message}`); }
|
|
958
|
+
}
|
|
959
|
+
public async mixChemicals(schem: ChemicalState, mixingTime?: number): Promise<void> {
|
|
960
|
+
try {
|
|
961
|
+
if (this._stoppingMix) {
|
|
962
|
+
logger.verbose(`${schem.chemType} is currently stopping mixChemicals ignored.`)
|
|
963
|
+
return;
|
|
964
|
+
}
|
|
965
|
+
if (this._processingMix) {
|
|
966
|
+
logger.verbose(`${schem.chemType} is already processing mixChemicals ignored.`);
|
|
967
|
+
return;
|
|
886
968
|
}
|
|
887
|
-
|
|
888
|
-
|
|
969
|
+
this._processingMix = true;
|
|
970
|
+
let dt = new Date().getTime();
|
|
971
|
+
await this.initMixChemicals(schem, mixingTime);
|
|
972
|
+
if (this._stoppingMix) return;
|
|
973
|
+
schem.chlor.isDosing = schem.pump.isDosing = false;
|
|
974
|
+
if (!this.chemical.flowOnlyMixing || (schem.chemController.isBodyOn && this.chemController.flowDetected)) {
|
|
975
|
+
if (this.chemType === 'orp' && typeof this.chemController.orp.orp.useChlorinator !== 'undefined' && this.chemController.orp.orp.useChlorinator && this.chemController.orp.orp.dosingMethod > 0) {
|
|
976
|
+
if (state.chlorinators.getItemById(1).currentOutput !== 0) {
|
|
977
|
+
logger.debug(`Chem mixing ORP (chlorinator) paused waiting for chlor current output to be 0%. Mix time remaining: ${utils.formatDuration(schem.mixTimeRemaining)} `);
|
|
978
|
+
return;
|
|
979
|
+
}
|
|
980
|
+
}
|
|
889
981
|
this.currentMix.timeMixed += Math.round((dt - this.currentMix.lastChecked) / 1000);
|
|
890
982
|
// Reflect any changes to the configuration.
|
|
891
|
-
if (!this.currentMix.isManual) this.currentMix.time = this.chemical.mixingTime;
|
|
892
|
-
schem.mixTimeRemaining = this.currentMix.timeRemaining;
|
|
983
|
+
if (!this.currentMix.isManual) { this.currentMix.time = this.chemical.mixingTime; }
|
|
984
|
+
schem.mixTimeRemaining = Math.round(this.currentMix.timeRemaining);
|
|
893
985
|
logger.verbose(`Chem mixing ${schem.chemType} remaining: ${utils.formatDuration(schem.mixTimeRemaining)}`);
|
|
894
986
|
}
|
|
895
|
-
else
|
|
896
|
-
logger.verbose(`Chem mixing paused because body is not on.`);
|
|
897
|
-
}
|
|
987
|
+
else
|
|
988
|
+
logger.verbose(`Chem ${schem.chemType} mixing paused because body is not on.`);
|
|
898
989
|
this.currentMix.lastChecked = dt;
|
|
899
|
-
if (schem.mixTimeRemaining
|
|
900
|
-
logger.info(`Chem Controller ${schem.chemType} mixing Complete after ${utils.formatDuration(this.currentMix.timeMixed)}`)
|
|
901
|
-
|
|
902
|
-
this.currentMix = undefined;
|
|
990
|
+
if (schem.mixTimeRemaining <= 0) {
|
|
991
|
+
logger.info(`Chem Controller ${schem.chemType} mixing Complete after ${utils.formatDuration(this.currentMix.timeMixed)}`);
|
|
992
|
+
await this.stopMixing(schem);
|
|
903
993
|
}
|
|
904
|
-
else {
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
} catch (err) { logger.error(`Error mixing chemicals
|
|
908
|
-
finally {
|
|
994
|
+
else {
|
|
995
|
+
schem.dosingStatus = sys.board.valueMaps.chemControllerDosingStatus.getValue('mixing');
|
|
996
|
+
}
|
|
997
|
+
} catch (err) { logger.error(`Error mixing chemicals: ${err.message}`); }
|
|
998
|
+
finally {
|
|
999
|
+
this._processingMix = false;
|
|
1000
|
+
setImmediate(() => {
|
|
1001
|
+
schem.chemController.emitEquipmentChange();
|
|
1002
|
+
});
|
|
1003
|
+
}
|
|
909
1004
|
}
|
|
910
1005
|
public async initDose(schem: ChemicalState) { }
|
|
911
1006
|
public async closeAsync() {
|
|
912
1007
|
try {
|
|
913
|
-
|
|
1008
|
+
// We are only killing the mix timer here so when njsPC is restarted it picks up where
|
|
1009
|
+
// it left off with mixing.
|
|
1010
|
+
if (typeof this._mixTimer !== 'undefined') clearInterval(this._mixTimer);
|
|
914
1011
|
this._mixTimer = undefined;
|
|
915
1012
|
await super.closeAsync();
|
|
916
1013
|
}
|
|
917
|
-
catch (err) { logger.error(`chemController closeAsync ${err.message}`); return Promise.reject(err);}
|
|
1014
|
+
catch (err) { logger.error(`chemController closeAsync ${err.message}`); return Promise.reject(err); }
|
|
918
1015
|
}
|
|
919
|
-
public async cancelDosing(schem: ChemicalState, reason: string) {
|
|
1016
|
+
public async cancelDosing(schem: ChemicalState, reason: string): Promise<void> {
|
|
920
1017
|
try {
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
1018
|
+
if (typeof this.chemController.orp.orp.useChlorinator !== 'undefined' && this.chemController.orp.orp.useChlorinator && this.chemController.orp.orp.dosingMethod > 0) {
|
|
1019
|
+
if (!this.chlor.chlor.superChlor) await this.chlor.stopDosing(schem, reason);
|
|
1020
|
+
// for chlor, we want 15 minute intervals
|
|
1021
|
+
if (schem.doseHistory.length) {
|
|
1022
|
+
// if last dose was within 15 minutes, set mix time to 15 mins-lastdose
|
|
1023
|
+
// if no dose in last 15, then we should be monitoring
|
|
1024
|
+
let lastDoseTime = schem.doseHistory[0].timeDosed;
|
|
1025
|
+
let mixTime = Math.min(Math.max(this.chlor.chlorInterval * 60 - lastDoseTime, 0), this.chlor.chlorInterval * 60);
|
|
1026
|
+
if (schem.dosingStatus === 0) await this.mixChemicals(schem, mixTime);
|
|
1027
|
+
}
|
|
1028
|
+
else
|
|
1029
|
+
if (schem.dosingStatus === 0) await this.mixChemicals(schem);
|
|
1030
|
+
}
|
|
1031
|
+
else {
|
|
1032
|
+
// Just stop the pump for now but we will do some logging later.
|
|
1033
|
+
if (schem.dosingStatus === 0) await this.mixChemicals(schem);
|
|
1034
|
+
}
|
|
925
1035
|
} catch (err) { logger.error(`cancelDosing: ${err.message}`); return Promise.reject(err); }
|
|
926
1036
|
}
|
|
927
|
-
//public calcTotalDosed(hours: number, trim: boolean = false): number {
|
|
928
|
-
// let total = 0;
|
|
929
|
-
// let dt = new Date().getTime() - (hours * 3600000);
|
|
930
|
-
// for (let i = this.doseHistory.length - 1; i >= 0; i--) {
|
|
931
|
-
// let log = this.doseHistory[i];
|
|
932
|
-
// if (log.end.getTime() > dt) total += log.volumeDosed;
|
|
933
|
-
// else if (trim) {
|
|
934
|
-
// this.doseHistory.splice(i, 1);
|
|
935
|
-
// }
|
|
936
|
-
// }
|
|
937
|
-
// if (typeof this.currentDose !== 'undefined' && this.currentDose.volumeRemaining > 0 && this.currentDose.timeRemaining > 0) {
|
|
938
|
-
// total += this.currentDose.volumeDosed;
|
|
939
|
-
// }
|
|
940
|
-
// return Math.round(total);
|
|
941
|
-
//}
|
|
942
1037
|
}
|
|
943
1038
|
export class NixieChemTank extends NixieChildEquipment {
|
|
944
1039
|
public tank: ChemicalTank;
|
|
@@ -982,7 +1077,7 @@ export class NixieChemPump extends NixieChildEquipment {
|
|
|
982
1077
|
private _isStopping = false;
|
|
983
1078
|
constructor(chemical: NixieChemical, pump: ChemicalPump) { super(chemical); this.pump = pump; }
|
|
984
1079
|
public get chemical(): NixieChemical { return this.getParent() as NixieChemical; }
|
|
985
|
-
public async setPumpAsync(spump: ChemicalPumpState, data: any) {
|
|
1080
|
+
public async setPumpAsync(spump: ChemicalPumpState, data: any): Promise<void> {
|
|
986
1081
|
try {
|
|
987
1082
|
if (typeof data !== 'undefined') {
|
|
988
1083
|
this.pump.enabled = typeof data.enabled !== 'undefined' ? data.enabled : this.pump.enabled;
|
|
@@ -994,8 +1089,9 @@ export class NixieChemPump extends NixieChildEquipment {
|
|
|
994
1089
|
} catch (err) { logger.error(`setPumpAsync: ${err.message}`); return Promise.reject(err); }
|
|
995
1090
|
|
|
996
1091
|
}
|
|
997
|
-
public async stopDosing(schem: ChemicalState, reason: string) {
|
|
1092
|
+
public async stopDosing(schem: ChemicalState, reason: string): Promise<void> {
|
|
998
1093
|
try {
|
|
1094
|
+
logger.debug(`Stopping ${schem.chemType} pump: ${reason}`);
|
|
999
1095
|
if (this._dosingTimer) {
|
|
1000
1096
|
clearTimeout(this._dosingTimer);
|
|
1001
1097
|
this._dosingTimer = undefined;
|
|
@@ -1006,20 +1102,20 @@ export class NixieChemPump extends NixieChildEquipment {
|
|
|
1006
1102
|
}
|
|
1007
1103
|
this._isStopping = true;
|
|
1008
1104
|
let dose = schem.currentDose;
|
|
1009
|
-
//let dose = this.chemical.currentDose;
|
|
1010
1105
|
if (this.pump.type !== 0) await this.turnOff(schem);
|
|
1011
1106
|
if (typeof dose !== 'undefined') {
|
|
1012
|
-
|
|
1013
|
-
schem.endDose();
|
|
1107
|
+
schem.endDose(new Date(), reason);
|
|
1014
1108
|
schem.manualDosing = false;
|
|
1015
1109
|
schem.dosingTimeRemaining = 0;
|
|
1016
1110
|
schem.dosingVolumeRemaining = 0;
|
|
1017
1111
|
schem.volumeDosed = 0;
|
|
1112
|
+
schem.timeDosed = 0;
|
|
1018
1113
|
}
|
|
1019
1114
|
} catch (err) { logger.error(`Error stopping ${schem.chemType} dosing: ${err.message}`); return Promise.reject(err); }
|
|
1020
1115
|
finally { this._isStopping = false; }
|
|
1021
1116
|
}
|
|
1022
|
-
public async dose(schem: ChemicalState) {
|
|
1117
|
+
public async dose(schem: ChemicalState): Promise<void> {
|
|
1118
|
+
let self = this;
|
|
1023
1119
|
let dose: ChemicalDoseState = schem.currentDose;
|
|
1024
1120
|
try {
|
|
1025
1121
|
if (this._dosingTimer) {
|
|
@@ -1128,10 +1224,11 @@ export class NixieChemPump extends NixieChildEquipment {
|
|
|
1128
1224
|
await this.chemical.cancelDosing(schem, 'empty tank');
|
|
1129
1225
|
}
|
|
1130
1226
|
//dosage.schem.dosingStatus = status;
|
|
1227
|
+
return;
|
|
1131
1228
|
} catch (err) {
|
|
1132
1229
|
// If we have an error then we want to clear the latch time. Theoretically we could add 3 seconds of latch time but who knows when the failure
|
|
1133
1230
|
// actually occurred.
|
|
1134
|
-
if(typeof dose !== 'undefined') dose._lastLatch = undefined;
|
|
1231
|
+
if (typeof dose !== 'undefined') dose._lastLatch = undefined;
|
|
1135
1232
|
logger.error(`chemController.pump dose: ${err.message}`);
|
|
1136
1233
|
return Promise.reject(err);
|
|
1137
1234
|
}
|
|
@@ -1140,14 +1237,24 @@ export class NixieChemPump extends NixieChildEquipment {
|
|
|
1140
1237
|
// Add a check to tell the chem when we are done.
|
|
1141
1238
|
if (schem.dosingStatus === 0) {
|
|
1142
1239
|
this._dosingTimer = setTimeout(async () => {
|
|
1143
|
-
try { await
|
|
1144
|
-
catch (err) {
|
|
1240
|
+
try { await self.dose(schem); }
|
|
1241
|
+
catch (err) {
|
|
1242
|
+
logger.error(`self.dose error in finally:`);
|
|
1243
|
+
logger.error(err);
|
|
1244
|
+
//return Promise.reject(err); // this isn't a promise we should be returning
|
|
1245
|
+
}
|
|
1145
1246
|
}, 1000);
|
|
1146
1247
|
}
|
|
1147
1248
|
else {
|
|
1148
1249
|
// Tell whichever chemical we are dealing with to begin mixing.
|
|
1149
1250
|
if (typeof dose !== 'undefined') {
|
|
1150
|
-
|
|
1251
|
+
try {
|
|
1252
|
+
await this.chemical.cancelDosing(schem, 'completed');
|
|
1253
|
+
}
|
|
1254
|
+
catch (err) {
|
|
1255
|
+
logger.error(`this.chemical.cancelDosing error in finally:`);
|
|
1256
|
+
logger.error(err);
|
|
1257
|
+
}
|
|
1151
1258
|
schem.pump.isDosing = this.isOn = false;
|
|
1152
1259
|
schem.manualDosing = false;
|
|
1153
1260
|
}
|
|
@@ -1169,12 +1276,190 @@ export class NixieChemPump extends NixieChildEquipment {
|
|
|
1169
1276
|
public async turnOn(schem: ChemicalState, latchTimeout?: number): Promise<InterfaceServerResponse> {
|
|
1170
1277
|
try {
|
|
1171
1278
|
let res = await NixieEquipment.putDeviceService(this.pump.connectionId, `/state/device/${this.pump.deviceBinding}`, typeof latchTimeout !== 'undefined' ? { isOn: true, latch: latchTimeout } : { isOn: true });
|
|
1172
|
-
this.isOn = schem.pump.isDosing =
|
|
1279
|
+
this.isOn = schem.pump.isDosing = true;
|
|
1173
1280
|
return res;
|
|
1174
1281
|
}
|
|
1175
1282
|
catch (err) { logger.error(`chemController.pump.turnOn: ${err.message}`); return Promise.reject(err); }
|
|
1176
1283
|
}
|
|
1177
1284
|
}
|
|
1285
|
+
export class NixieChemChlor extends NixieChildEquipment {
|
|
1286
|
+
public chlor: ChemicalChlor;
|
|
1287
|
+
public isOn: boolean;
|
|
1288
|
+
public _lastOnStatus: number;
|
|
1289
|
+
protected _dosingTimer: NodeJS.Timeout;
|
|
1290
|
+
private _isStopping = false;
|
|
1291
|
+
public chlorInterval = 15;
|
|
1292
|
+
constructor(chemical: NixieChemical, chlor: ChemicalChlor) { super(chemical); this.chlor = chlor; }
|
|
1293
|
+
public get chemical(): NixieChemical { return this.getParent() as NixieChemical; }
|
|
1294
|
+
public async setChlorAsync(schlor: ChemicalChlorState, data: any) {
|
|
1295
|
+
try {
|
|
1296
|
+
if (typeof data.chlorDosingMethod !== 'undefined' && data.chlorDosingMethod === 0) {
|
|
1297
|
+
if (schlor.chemical.dosingStatus === 0) { await this.chemical.cancelDosing(schlor.chemController.orp, 'dosing method changed'); }
|
|
1298
|
+
if (schlor.chemical.dosingStatus === 1) { await this.chemical.cancelMixing(schlor.chemController.orp); }
|
|
1299
|
+
let chlor = sys.chlorinators.getItemById(1);
|
|
1300
|
+
chlor.disabled = false;
|
|
1301
|
+
chlor.isDosing = false;
|
|
1302
|
+
}
|
|
1303
|
+
} catch (err) { logger.error(`setChlorAsync: ${err.message}`); return Promise.reject(err); }
|
|
1304
|
+
}
|
|
1305
|
+
public async stopDosing(schem: ChemicalState, reason: string): Promise<void> {
|
|
1306
|
+
try {
|
|
1307
|
+
if (this._dosingTimer) {
|
|
1308
|
+
clearTimeout(this._dosingTimer);
|
|
1309
|
+
this._dosingTimer = undefined;
|
|
1310
|
+
}
|
|
1311
|
+
if (this._isStopping) {
|
|
1312
|
+
logger.warn('Trying to stop dosing chlor but it has not yet responded.');
|
|
1313
|
+
return Promise.reject(new EquipmentTimeoutError(`Already trying to stop chlor dosing.`, `chlorStopDosingInProgress`));
|
|
1314
|
+
// return false; // We have to semaphore here just in case the chlor is not stopping as we would like.
|
|
1315
|
+
}
|
|
1316
|
+
logger.debug(`Stopping chlorinating: ${reason}`);
|
|
1317
|
+
this._isStopping = true;
|
|
1318
|
+
let dose = schem.currentDose;
|
|
1319
|
+
await this.turnOff(schem);
|
|
1320
|
+
if (typeof dose !== 'undefined') {
|
|
1321
|
+
schem.endDose();
|
|
1322
|
+
schem.manualDosing = false;
|
|
1323
|
+
schem.dosingTimeRemaining = 0;
|
|
1324
|
+
schem.dosingVolumeRemaining = 0;
|
|
1325
|
+
schem.volumeDosed = 0;
|
|
1326
|
+
}
|
|
1327
|
+
} catch (err) { logger.error(`Error stopping ${schem.chemType} dosing: ${err.message}`); return Promise.reject(err); }
|
|
1328
|
+
finally { this._isStopping = false; }
|
|
1329
|
+
}
|
|
1330
|
+
public async dose(schem: ChemicalState): Promise<void> {
|
|
1331
|
+
let self = this;
|
|
1332
|
+
let dose: ChemicalDoseState = schem.currentDose;
|
|
1333
|
+
try {
|
|
1334
|
+
if (this._dosingTimer) {
|
|
1335
|
+
clearTimeout(this._dosingTimer);
|
|
1336
|
+
this._dosingTimer = undefined;
|
|
1337
|
+
}
|
|
1338
|
+
if (typeof dose === 'undefined') {
|
|
1339
|
+
await this.chemical.cancelDosing(schem, 'undefined dose');
|
|
1340
|
+
return;
|
|
1341
|
+
}
|
|
1342
|
+
if (this.chlor.ratedLbs === 0) {
|
|
1343
|
+
// We aren't going to do anything.
|
|
1344
|
+
logger.verbose(`Chem dose ignore chlor because it doesn't have a dosing rating.`);
|
|
1345
|
+
}
|
|
1346
|
+
else {
|
|
1347
|
+
await this.chemical.chemController.processAlarms(schem.chemController);
|
|
1348
|
+
let isBodyOn = schem.chemController.flowDetected;
|
|
1349
|
+
await this.chemical.initDose(schem);
|
|
1350
|
+
let chemController = schem.getParent()
|
|
1351
|
+
let schlor = state.chlorinators.getItemById(1);
|
|
1352
|
+
if (!isBodyOn) {
|
|
1353
|
+
// Make sure the chlor is off.
|
|
1354
|
+
logger.info(`Chem chlor flow not detected. Body is not running.`);
|
|
1355
|
+
await this.chemical.cancelDosing(schem, 'no flow');
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
else if (chemController.ph.enabled && chemController.ph.pump.isDosing && chemController.ph.dosePriority) {
|
|
1359
|
+
// If ph has dose priority and is dosing, we shouldn't be continuing here
|
|
1360
|
+
let chem = sys.chemControllers.getItemById(chemController.id, false);
|
|
1361
|
+
if (chem.ph.dosePriority)
|
|
1362
|
+
await this.chemical.cancelDosing(schem, 'ph dose priority');
|
|
1363
|
+
}
|
|
1364
|
+
else if (this.chlor.superChlor) {
|
|
1365
|
+
// if superchlor is active, it may be to boost the ORP and we should respect that
|
|
1366
|
+
await this.chemical.cancelDosing(schem, 'superchlor');
|
|
1367
|
+
}
|
|
1368
|
+
else if (dose.timeRemaining <= 0 || dose.volumeRemaining <= 0) {
|
|
1369
|
+
logger.info(`Dose completed ${dose.volumeDosed}lbs ${dose.timeRemaining} ${dose.volumeRemaining}`);
|
|
1370
|
+
await this.chemical.cancelDosing(schem, 'completed');
|
|
1371
|
+
}
|
|
1372
|
+
else if (dose.timeRemaining > 0 && dose.volumeRemaining > 0) { // We are actually dosing here
|
|
1373
|
+
try {
|
|
1374
|
+
await this.turnOn(schem);
|
|
1375
|
+
if (schlor.currentOutput !== 100) {
|
|
1376
|
+
logger.warn(`Chlor dose not added because current output is not 100%`);
|
|
1377
|
+
}
|
|
1378
|
+
else {
|
|
1379
|
+
if (typeof dose._lastLatch !== 'undefined') {
|
|
1380
|
+
let time = new Date().getTime() - (dose._lastLatch || new Date().getTime());
|
|
1381
|
+
let vol = this.chlor.ratedLbs * time / 1000;
|
|
1382
|
+
schem.appendDose(vol, time);
|
|
1383
|
+
}
|
|
1384
|
+
logger.info(`Chem Controller ${dose.chem} chlorinated ${Math.round(dose.volumeDosed * 1000000) / 1000000}lbs of ${Math.round(dose.volume * 1000000) / 1000000}lbs - ${utils.formatDuration(dose.timeRemaining)} remaining`);
|
|
1385
|
+
dose._lastLatch = new Date().getTime();
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
catch (err) {
|
|
1389
|
+
logger.error(`Error starting chlorination: ${err}.`)
|
|
1390
|
+
}
|
|
1391
|
+
// if we don't reach the chlorinator, we still want to be in dosing status
|
|
1392
|
+
schem.dosingStatus = 0;
|
|
1393
|
+
}
|
|
1394
|
+
else {
|
|
1395
|
+
await this.chemical.cancelDosing(schem, 'unknown cancel');
|
|
1396
|
+
}
|
|
1397
|
+
return;
|
|
1398
|
+
}
|
|
1399
|
+
} catch (err) {
|
|
1400
|
+
logger.error(`chemController.chlor dose: ${err.message}`);
|
|
1401
|
+
return Promise.reject(err);
|
|
1402
|
+
}
|
|
1403
|
+
finally {
|
|
1404
|
+
schem.chemController.emitEquipmentChange();
|
|
1405
|
+
// Add a check to tell the chem when we are done.
|
|
1406
|
+
if (schem.dosingStatus === 0) {
|
|
1407
|
+
this._dosingTimer = setTimeout(async () => {
|
|
1408
|
+
try { await self.dose(schem); }
|
|
1409
|
+
catch (err) {
|
|
1410
|
+
logger.error(err);
|
|
1411
|
+
// return Promise.reject(err); // should not be returning a promise in a finally
|
|
1412
|
+
}
|
|
1413
|
+
}, 1000);
|
|
1414
|
+
}
|
|
1415
|
+
else {
|
|
1416
|
+
// Tell whichever chemical we are dealing with to begin mixing.
|
|
1417
|
+
if (typeof dose !== 'undefined') {
|
|
1418
|
+
await this.chemical.cancelDosing(schem, 'completed');
|
|
1419
|
+
schem.chlor.isDosing = this.isOn = false;
|
|
1420
|
+
// schem.manualDosing = false;
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
public async turnOff(schem: ChemicalState): Promise<ChlorinatorState> {
|
|
1426
|
+
try {
|
|
1427
|
+
logger.info(`Turning off the chlorinator`);
|
|
1428
|
+
let chlor = sys.chlorinators.getItemById(1);
|
|
1429
|
+
let schlor = state.chlorinators.getItemById(1);
|
|
1430
|
+
if (schlor.currentOutput === 0 && schlor.targetOutput === 0 && !schlor.superChlor && chlor.disabled && !chlor.isDosing) {
|
|
1431
|
+
this.isOn = schem.chlor.isDosing = false;
|
|
1432
|
+
return schlor;
|
|
1433
|
+
}
|
|
1434
|
+
let cstate = await sys.board.chlorinator.setChlorAsync({
|
|
1435
|
+
id: 1,
|
|
1436
|
+
disabled: true,
|
|
1437
|
+
isDosing: false
|
|
1438
|
+
})
|
|
1439
|
+
this.isOn = schem.chlor.isDosing = false;
|
|
1440
|
+
return cstate;
|
|
1441
|
+
}
|
|
1442
|
+
catch (err) { logger.error(`chemController.chlor.turnOff: ${err.message}`); return Promise.reject(err); }
|
|
1443
|
+
}
|
|
1444
|
+
public async turnOn(schem: ChemicalState, latchTimeout?: number): Promise<ChlorinatorState> {
|
|
1445
|
+
try {
|
|
1446
|
+
let chlor = sys.chlorinators.getItemById(1);
|
|
1447
|
+
let schlor = state.chlorinators.getItemById(1);
|
|
1448
|
+
if (schlor.currentOutput === 100 && schlor.targetOutput === 100 && !schlor.superChlor && !chlor.disabled && chlor.isDosing) {
|
|
1449
|
+
this.isOn = schem.chlor.isDosing = true;
|
|
1450
|
+
return schlor;
|
|
1451
|
+
}
|
|
1452
|
+
let cstate = await sys.board.chlorinator.setChlorAsync({
|
|
1453
|
+
id: 1,
|
|
1454
|
+
disabled: false,
|
|
1455
|
+
isDosing: true
|
|
1456
|
+
})
|
|
1457
|
+
this.isOn = schem.chlor.isDosing = true;
|
|
1458
|
+
return cstate;
|
|
1459
|
+
}
|
|
1460
|
+
catch (err) { logger.error(`chemController.chlor.turnOn: ${err.message}`); return Promise.reject(err); }
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1178
1463
|
export class NixieChemicalPh extends NixieChemical {
|
|
1179
1464
|
public get ph(): ChemicalPh { return this.chemical as ChemicalPh; }
|
|
1180
1465
|
public probe: NixieChemProbePh;
|
|
@@ -1221,10 +1506,10 @@ export class NixieChemicalPh extends NixieChemical {
|
|
|
1221
1506
|
let sorp = sph.chemController.orp;
|
|
1222
1507
|
for (let i = 0; i < chlors.length; i++) {
|
|
1223
1508
|
let chlor = chlors.getItemByIndex(i);
|
|
1224
|
-
if (!chlor.disabled) await sys.board.chlorinator.setChlorAsync({ id: chlor.id, disabled: true });
|
|
1509
|
+
if (!chlor.disabled) await sys.board.chlorinator.setChlorAsync({ id: chlor.id, disabled: true, isDosing: false });
|
|
1225
1510
|
}
|
|
1226
1511
|
// If we are currently dosing ORP then we need to stop that because pH is currently dosing.
|
|
1227
|
-
if (sorp.pump.isDosing) await this.chemController.orp.cancelDosing(sorp, 'pH priority');
|
|
1512
|
+
if (sorp.pump.isDosing || sorp.chlor.isDosing) await this.chemController.orp.cancelDosing(sorp, 'pH priority');
|
|
1228
1513
|
}
|
|
1229
1514
|
this.ph.dosePriority = b;
|
|
1230
1515
|
}
|
|
@@ -1234,45 +1519,12 @@ export class NixieChemicalPh extends NixieChemical {
|
|
|
1234
1519
|
}
|
|
1235
1520
|
catch (err) { logger.error(`chemController setPhAysnc.: ${err.message}`); return Promise.reject(err); }
|
|
1236
1521
|
}
|
|
1237
|
-
//public calcDemand(sph: ChemicalPhState): number {
|
|
1238
|
-
// let chem = this.chemController.chem;
|
|
1239
|
-
// // Calculate how many mL are required to raise to our pH level.
|
|
1240
|
-
// // 1. Get the total gallons of water that the chem controller is in
|
|
1241
|
-
// // control of.
|
|
1242
|
-
// let totalGallons = 0;
|
|
1243
|
-
|
|
1244
|
-
// if (chem.body === 0 || chem.body === 32 || sys.equipment.shared) totalGallons += sys.bodies.getItemById(1).capacity;
|
|
1245
|
-
// if (chem.body === 1 || chem.body === 32 || sys.equipment.shared) totalGallons += sys.bodies.getItemById(2).capacity;
|
|
1246
|
-
// if (chem.body === 2) totalGallons += sys.bodies.getItemById(3).capacity;
|
|
1247
|
-
// if (chem.body === 3) totalGallons += sys.bodies.getItemById(4).capacity;
|
|
1248
|
-
// logger.verbose(`Chem begin calculating demand: ${sph.level} setpoint: ${this.ph.setpoint} body: ${totalGallons}`);
|
|
1249
|
-
// let chg = this.ph.setpoint - sph.level;
|
|
1250
|
-
// let delta = chg * totalGallons;
|
|
1251
|
-
// let temp = (sph.level + this.ph.setpoint) / 2;
|
|
1252
|
-
// let adj = (192.1626 + -60.1221 * temp + 6.0752 * temp * temp + -0.1943 * temp * temp * temp) * (chem.alkalinity + 13.91) / 114.6;
|
|
1253
|
-
// let extra = (-5.476259 + 2.414292 * temp + -0.355882 * temp * temp + 0.01755 * temp * temp * temp) * (chem.borates || 0);
|
|
1254
|
-
// extra *= delta;
|
|
1255
|
-
// delta *= adj;
|
|
1256
|
-
// let dose = 0;
|
|
1257
|
-
// if (this.ph.phSupply === 0) { // We are dispensing base so we need to calculate the demand here.
|
|
1258
|
-
// if (chg > 0) {
|
|
1259
|
-
|
|
1260
|
-
// }
|
|
1261
|
-
// }
|
|
1262
|
-
// else {
|
|
1263
|
-
// if (chg < 0) {
|
|
1264
|
-
// let at = sys.board.valueMaps.acidTypes.transform(this.ph.acidType);
|
|
1265
|
-
// dose = Math.round(utils.convert.volume.convertUnits((delta / -240.15 * at.dosingFactor) + (extra / -240.15 * at.dosingFactor), 'oz', 'mL'));
|
|
1266
|
-
// }
|
|
1267
|
-
// }
|
|
1268
|
-
// sph.demand = dose;
|
|
1269
|
-
// return dose;
|
|
1270
|
-
//}
|
|
1271
1522
|
public async checkDosing(chem: ChemController, sph: ChemicalPhState) {
|
|
1272
1523
|
try {
|
|
1273
1524
|
let status = sys.board.valueMaps.chemControllerDosingStatus.getName(sph.dosingStatus);
|
|
1525
|
+
logger.debug(`Begin check ${sph.chemType} dosing status = ${status}`);
|
|
1274
1526
|
let demand = sph.calcDemand(chem);
|
|
1275
|
-
|
|
1527
|
+
sph.demand = Math.max(demand, 0);
|
|
1276
1528
|
if (sph.suspendDosing) {
|
|
1277
1529
|
// Kill off the dosing and make sure the pump isn't running. Let's force the issue here.
|
|
1278
1530
|
await this.cancelDosing(sph, 'suspended');
|
|
@@ -1283,17 +1535,37 @@ export class NixieChemicalPh extends NixieChemical {
|
|
|
1283
1535
|
// let the system clean these up.
|
|
1284
1536
|
if (typeof sph.currentDose !== 'undefined') logger.error('Somehow we made it to monitoring and still have a current dose');
|
|
1285
1537
|
sph.currentDose = undefined;
|
|
1286
|
-
this.currentMix = undefined;
|
|
1287
1538
|
sph.manualDosing = false;
|
|
1288
|
-
sph.mixTimeRemaining = 0;
|
|
1289
1539
|
sph.dosingVolumeRemaining = 0;
|
|
1290
1540
|
sph.dosingTimeRemaining = 0;
|
|
1291
|
-
|
|
1292
|
-
|
|
1541
|
+
if (typeof this.currentMix !== 'undefined') {
|
|
1542
|
+
if (ncp.chemControllers.length > 1) {
|
|
1543
|
+
let arrIds = [];
|
|
1544
|
+
for (let i = 0; i < ncp.chemControllers.length; i++) {
|
|
1545
|
+
arrIds.push(ncp[i].id);
|
|
1546
|
+
}
|
|
1547
|
+
logger.info(`More than one NixieChemController object was found ${JSON.stringify(arrIds)}`);
|
|
1548
|
+
}
|
|
1549
|
+
logger.debug(`We are now monitoring and have a mixing object`);
|
|
1550
|
+
await this.stopMixing(sph);
|
|
1551
|
+
}
|
|
1552
|
+
await this.cancelDosing(sph, 'monitoring');
|
|
1293
1553
|
}
|
|
1294
1554
|
if (status === 'mixing') {
|
|
1295
|
-
await this.cancelDosing(sph, '
|
|
1296
|
-
|
|
1555
|
+
await this.cancelDosing(sph, 'mixing');
|
|
1556
|
+
if (typeof this.currentMix === 'undefined') {
|
|
1557
|
+
// First lets check to see how many chem controllers we have.
|
|
1558
|
+
// RKS: Keep this case around in case there is another Moby Dick and Nixie has an orphan out there.
|
|
1559
|
+
//if (ncp.chemControllers.length > 1) {
|
|
1560
|
+
// let arrIds = [];
|
|
1561
|
+
// for (let i = 0; i < ncp.chemControllers.length; i++) {
|
|
1562
|
+
// arrIds.push(ncp[i].id);
|
|
1563
|
+
// }
|
|
1564
|
+
// logger.info(`More than one NixieChemController object was found ${JSON.stringify(arrIds)}`);
|
|
1565
|
+
//}
|
|
1566
|
+
logger.info(`Current ${sph.chemType} mix object not defined initializing mix`);
|
|
1567
|
+
await this.mixChemicals(sph);
|
|
1568
|
+
}
|
|
1297
1569
|
}
|
|
1298
1570
|
else if (sph.manualDosing) {
|
|
1299
1571
|
// We are manually dosing. We are not going to dynamically change the dose.
|
|
@@ -1302,7 +1574,6 @@ export class NixieChemicalPh extends NixieChemical {
|
|
|
1302
1574
|
// Unfortunately we will lose the original start date but who cares as the volumes should remain the same.
|
|
1303
1575
|
let volume = sph.volumeDosed + sph.dosingVolumeRemaining;
|
|
1304
1576
|
let time = sph.timeDosed + sph.dosingTimeRemaining;
|
|
1305
|
-
sph.demand = sph.calcDemand(this.chemController.chem);
|
|
1306
1577
|
sph.startDose(new Timestamp().addSeconds(-sph.doseTime).toDate(), 'manual', volume, sph.dosingVolumeRemaining, time * 1000, sph.doseTime * 1000);
|
|
1307
1578
|
}
|
|
1308
1579
|
if (sph.tank.level > 0) {
|
|
@@ -1360,7 +1631,6 @@ export class NixieChemicalPh extends NixieChemical {
|
|
|
1360
1631
|
break;
|
|
1361
1632
|
}
|
|
1362
1633
|
logger.verbose(`Chem acid dosing maximums applied ${dose}mL for ${utils.formatDuration(time)}`);
|
|
1363
|
-
sph.demand = demand;
|
|
1364
1634
|
if (typeof sph.currentDose === 'undefined' && sph.tank.level > 0) {
|
|
1365
1635
|
// We will include this with the dose demand because our limits may reduce it.
|
|
1366
1636
|
//dosage.demand = demand;
|
|
@@ -1386,10 +1656,12 @@ export class NixieChemicalPh extends NixieChemical {
|
|
|
1386
1656
|
await this.cancelDosing(sph, 'empty tank');
|
|
1387
1657
|
}
|
|
1388
1658
|
}
|
|
1389
|
-
return true;
|
|
1390
1659
|
}
|
|
1391
1660
|
}
|
|
1392
|
-
catch (err) { logger.error(err); return Promise.reject(err);}
|
|
1661
|
+
catch (err) { logger.error(err); return Promise.reject(err); }
|
|
1662
|
+
finally {
|
|
1663
|
+
logger.debug(`End check ${sph.chemType} dosing status = ${sys.board.valueMaps.chemControllerDosingStatus.getName(sph.dosingStatus)}`);
|
|
1664
|
+
}
|
|
1393
1665
|
}
|
|
1394
1666
|
public async cancelDosing(sph: ChemicalPhState, reason: string) {
|
|
1395
1667
|
try {
|
|
@@ -1406,11 +1678,12 @@ export class NixieChemicalPh extends NixieChemical {
|
|
|
1406
1678
|
}
|
|
1407
1679
|
}
|
|
1408
1680
|
}
|
|
1409
|
-
if(typeof sph.currentDose !== 'undefined') sph.endDose(new Date(), 'cancelled');
|
|
1681
|
+
if (typeof sph.currentDose !== 'undefined') sph.endDose(new Date(), 'cancelled');
|
|
1410
1682
|
} catch (err) { logger.error(`cancelDosing pH: ${err.message}`); return Promise.reject(err); }
|
|
1411
1683
|
}
|
|
1412
1684
|
public async manualDoseAsync(sph: ChemicalPhState, volume: number) {
|
|
1413
1685
|
try {
|
|
1686
|
+
logger.debug(`Starting manual ${sph.chemType} dose of ${volume}mL`);
|
|
1414
1687
|
let status = sys.board.valueMaps.chemControllerDosingStatus.getName(sph.dosingStatus);
|
|
1415
1688
|
if (status === 'monitoring') {
|
|
1416
1689
|
// Alright our mixing and dosing have either been cancelled or we fininsed a mixing cycle. Either way
|
|
@@ -1439,7 +1712,7 @@ export class NixieChemicalPh extends NixieChemical {
|
|
|
1439
1712
|
await this.pump.dose(sph);
|
|
1440
1713
|
}
|
|
1441
1714
|
}
|
|
1442
|
-
catch (err) { logger.error(`manualDoseAsync: ${err.message}`); logger.error(err); return Promise.reject(err);}
|
|
1715
|
+
catch (err) { logger.error(`manualDoseAsync: ${err.message}`); logger.error(err); return Promise.reject(err); }
|
|
1443
1716
|
}
|
|
1444
1717
|
public async initDose(sph: ChemicalPhState) {
|
|
1445
1718
|
try {
|
|
@@ -1466,21 +1739,24 @@ export class NixieChemicalORP extends NixieChemical {
|
|
|
1466
1739
|
this.chemType = 'orp';
|
|
1467
1740
|
this.orp = chemical;
|
|
1468
1741
|
this.probe = new NixieChemProbeORP(this, chemical.probe);
|
|
1742
|
+
this.chlor = new NixieChemChlor(this, chemical.chlor);
|
|
1469
1743
|
}
|
|
1470
1744
|
public get logFilename() { return `chemDosage_orp.log`; }
|
|
1471
1745
|
public async setORPAsync(sorp: ChemicalORPState, data: any) {
|
|
1472
1746
|
try {
|
|
1473
1747
|
if (typeof data !== 'undefined') {
|
|
1474
|
-
this.orp.useChlorinator = typeof data.useChlorinator !== 'undefined' ? utils.makeBool(data.useChlorinator) : this.orp.useChlorinator;
|
|
1748
|
+
sorp.useChlorinator = this.orp.useChlorinator = typeof data.useChlorinator !== 'undefined' ? utils.makeBool(data.useChlorinator) : this.orp.useChlorinator;
|
|
1475
1749
|
sorp.enabled = this.orp.enabled = typeof data.enabled !== 'undefined' ? utils.makeBool(data.enabled) : this.orp.enabled;
|
|
1476
1750
|
sorp.level = typeof data.level !== 'undefined' && !isNaN(parseFloat(data.level)) ? parseFloat(data.level) : sorp.level;
|
|
1477
1751
|
this.orp.phLockout = typeof data.phLockout !== 'undefined' && !isNaN(parseFloat(data.phLockout)) ? parseFloat(data.phLockout) : this.orp.phLockout;
|
|
1478
1752
|
this.orp.flowReadingsOnly = typeof data.flowReadingsOnly !== 'undefined' ? utils.makeBool(data.flowReadingsOnly) : this.orp.flowReadingsOnly;
|
|
1753
|
+
if (typeof data.chlorDosingMethod !== 'undefined') { this.orp.chlorDosingMethod = data.chlorDosingMethod; }
|
|
1479
1754
|
await this.setDosing(this.orp, data);
|
|
1480
1755
|
await this.setMixing(this.orp, data);
|
|
1481
1756
|
await this.probe.setProbeORPAsync(sorp.probe, data.probe);
|
|
1482
1757
|
await this.tank.setTankAsync(sorp.tank, data.tank);
|
|
1483
1758
|
await this.pump.setPumpAsync(sorp.pump, data.pump);
|
|
1759
|
+
await this.chlor.setChlorAsync(sorp.chlor, data);
|
|
1484
1760
|
this.orp.setpoint = sorp.setpoint = typeof data.setpoint !== 'undefined' ? parseInt(data.setpoint, 10) : this.orp.setpoint;
|
|
1485
1761
|
if (typeof data.tolerance !== 'undefined') {
|
|
1486
1762
|
if (typeof data.tolerance.enabled !== 'undefined') this.orp.tolerance.enabled = utils.makeBool(data.tolerance.enabled);
|
|
@@ -1521,21 +1797,106 @@ export class NixieChemicalORP extends NixieChemical {
|
|
|
1521
1797
|
await this.pump.dose(sorp);
|
|
1522
1798
|
}
|
|
1523
1799
|
}
|
|
1524
|
-
catch (err) { logger.error(`manualDoseAsync ORP: ${err.message}`); logger.error(err); return Promise.reject(err);}
|
|
1800
|
+
catch (err) { logger.error(`manualDoseAsync ORP: ${err.message}`); logger.error(err); return Promise.reject(err); }
|
|
1525
1801
|
}
|
|
1526
|
-
public async cancelDosing(sorp: ChemicalORPState, reason: string) {
|
|
1802
|
+
public async cancelDosing(sorp: ChemicalORPState, reason: string): Promise<void> {
|
|
1527
1803
|
try {
|
|
1528
|
-
|
|
1529
|
-
|
|
1804
|
+
if (typeof sorp.useChlorinator !== 'undefined' && sorp.useChlorinator && this.chemController.orp.orp.dosingMethod > 0) {
|
|
1805
|
+
await this.chlor.stopDosing(sorp, reason);
|
|
1806
|
+
// for chlor, we want 15 minute intervals
|
|
1807
|
+
if (sorp.doseHistory.length) {
|
|
1808
|
+
// if last dose was within 15 minutes, set mix time to 15 mins-lastdose
|
|
1809
|
+
// if no dose in last 15, then we should be monitoring
|
|
1810
|
+
if (new Date().getTime() - sorp.doseHistory[0].end.getTime() < this.chlor.chlorInterval * 60 * 1000){
|
|
1811
|
+
let lastDoseTime = sorp.doseHistory[0].timeDosed;
|
|
1812
|
+
let mixTime = Math.min(Math.max(this.chlor.chlorInterval * 60 - lastDoseTime, 0), this.chlor.chlorInterval * 60);
|
|
1813
|
+
// if (mixTime === 0) return; // due to delays with setting chlor, let the checkDosing pick up the cycle again with the chlor already on.
|
|
1814
|
+
if (sorp.dosingStatus === 0) await this.mixChemicals(sorp, mixTime);
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
else{
|
|
1818
|
+
if (sorp.dosingStatus === 0) await this.mixChemicals(sorp);
|
|
1819
|
+
}
|
|
1820
|
+
return;
|
|
1821
|
+
}
|
|
1822
|
+
else {
|
|
1823
|
+
// Just stop the pump for now but we will do some logging later.
|
|
1824
|
+
await this.pump.stopDosing(sorp, reason);
|
|
1825
|
+
}
|
|
1530
1826
|
if (sorp.dosingStatus === 0) {
|
|
1531
1827
|
await this.mixChemicals(sorp);
|
|
1532
1828
|
sorp.endDose(new Date(), 'cancelled');
|
|
1533
1829
|
}
|
|
1534
1830
|
} catch (err) { logger.error(`cancelDosing ORP: ${err.message}`); return Promise.reject(err); }
|
|
1535
1831
|
}
|
|
1536
|
-
|
|
1832
|
+
protected async initMixChemicals(schem: ChemicalState, mixingTime?: number): Promise<void> {
|
|
1833
|
+
try {
|
|
1834
|
+
if (this._stoppingMix) return;
|
|
1835
|
+
if (typeof this.currentMix === 'undefined' || (typeof this.currentMix !== 'undefined' && isNaN(this.currentMix.timeMixed))) {
|
|
1836
|
+
if (typeof mixingTime !== 'undefined') {
|
|
1837
|
+
// This is a manual mix so we need to make sure the pump is not dosing.
|
|
1838
|
+
logger.info(`Clearing any possible ${schem.chemType} dosing or existing mix for mixingTime: ${mixingTime}`);
|
|
1839
|
+
if (schem.chemController.orp.useChlorinator) await this.chlor.stopDosing(schem, 'mix override');
|
|
1840
|
+
else await this.pump.stopDosing(schem, 'mix override');
|
|
1841
|
+
await this.stopMixing(schem);
|
|
1842
|
+
}
|
|
1843
|
+
this.currentMix = new NixieChemMix();
|
|
1844
|
+
if (typeof mixingTime !== 'undefined' && !isNaN(mixingTime)) {
|
|
1845
|
+
this.currentMix.set({ time: Math.round(mixingTime), timeMixed: 0, isManual: true });
|
|
1846
|
+
schem.manualMixing = true;
|
|
1847
|
+
}
|
|
1848
|
+
else if (schem.mixTimeRemaining > 0) {
|
|
1849
|
+
if (schem.manualMixing) {
|
|
1850
|
+
this.currentMix.set({ time: schem.mixTimeRemaining, timeMixed: 0, isManual: true });
|
|
1851
|
+
}
|
|
1852
|
+
else
|
|
1853
|
+
if (typeof this.chemController.orp.orp.useChlorinator !== 'undefined' && this.chemController.orp.orp.useChlorinator && this.chemController.orp.orp.dosingMethod > 0) {
|
|
1854
|
+
// if last dose was within 15 minutes, set mix time to 15 mins-(now-lastdose)
|
|
1855
|
+
// if no dose in last 15, then we should be monitoring
|
|
1856
|
+
await this.chlor.stopDosing(schem, 'mix override'); // ensure chlor has stopped
|
|
1857
|
+
if (schem.doseHistory.length) {
|
|
1858
|
+
// if last dose was within 15 minutes, set mix time to 15 mins-lastdose
|
|
1859
|
+
// if no dose in last 15, then we should be monitoring
|
|
1860
|
+
let lastDoseTime = schem.doseHistory[0].timeDosed;
|
|
1861
|
+
let mixTime = Math.min(Math.max(this.chlor.chlorInterval * 60 - lastDoseTime, 0), this.chlor.chlorInterval * 60);
|
|
1862
|
+
// if (mixTime === 0) return; // due to delays in the setting the chlor, if we had a full dose last time let the chlor continue
|
|
1863
|
+
this.currentMix.set({ time: this.chlor.chlorInterval, timeMixed: Math.max(0, mixTime - schem.mixTimeRemaining) });
|
|
1864
|
+
}
|
|
1865
|
+
else
|
|
1866
|
+
// if no dose history, mix for 0s
|
|
1867
|
+
// this.currentMix.set({ time: this.chemical.mixingTime, timeMixed: Math.max(0, (this.chlor.chlorInterval * 60) - schem.mixTimeRemaining) });
|
|
1868
|
+
this.currentMix.set({ time: 0, timeMixed: 0 });
|
|
1869
|
+
}
|
|
1870
|
+
else {
|
|
1871
|
+
this.currentMix.set({ time: this.chemical.mixingTime, timeMixed: Math.max(0, this.chemical.mixingTime - schem.mixTimeRemaining) });
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1874
|
+
else
|
|
1875
|
+
if (typeof this.chemController.orp.orp.useChlorinator !== 'undefined' && this.chemController.orp.orp.useChlorinator && this.chemController.orp.orp.dosingMethod > 0)
|
|
1876
|
+
this.currentMix.set({ time: this.chlor.chlorInterval * 60, timeMixed: 0 });
|
|
1877
|
+
else
|
|
1878
|
+
this.currentMix.set({ time: this.chemical.mixingTime, timeMixed: 0 });
|
|
1879
|
+
logger.info(`Chem Controller begin mixing ${schem.chemType} for ${utils.formatDuration(this.currentMix.timeRemaining)} of ${utils.formatDuration(this.currentMix.time)}`)
|
|
1880
|
+
schem.mixTimeRemaining = this.currentMix.timeRemaining;
|
|
1881
|
+
}
|
|
1882
|
+
if (typeof this._mixTimer === 'undefined' || !this._mixTimer) {
|
|
1883
|
+
let self = this;
|
|
1884
|
+
this._mixTimer = setInterval(async () => { await self.mixChemicals(schem); }, 1000);
|
|
1885
|
+
logger.verbose(`Set ${schem.chemType} mix timer`);
|
|
1886
|
+
}
|
|
1887
|
+
} catch (err) { logger.error(`Error initializing ${schem.chemType} mix: ${err.message}`); }
|
|
1888
|
+
}
|
|
1889
|
+
public async checkDosing(chem: ChemController, sorp: ChemicalORPState): Promise<void> {
|
|
1537
1890
|
try {
|
|
1538
1891
|
let status = sys.board.valueMaps.chemControllerDosingStatus.getName(sorp.dosingStatus);
|
|
1892
|
+
if (!chem.orp.flowReadingsOnly || (chem.orp.flowReadingsOnly && sorp.chemController.flowDetected)) {
|
|
1893
|
+
// demand in raw mV
|
|
1894
|
+
sorp.demand = this.orp.setpoint - sorp.level;
|
|
1895
|
+
// log the demand. We'll store the last 100 data points.
|
|
1896
|
+
// With 1s intervals, this will only be 1m 40s. Likely should consider more... and def time to move this to an external file.
|
|
1897
|
+
sorp.appendDemand(new Date().valueOf(), sorp.demand);
|
|
1898
|
+
}
|
|
1899
|
+
if (chem.orp.useChlorinator && chem.orp.chlorDosingMethod === 0) return; // if chlor is managing itself, don't even cancel/stop as it will set the flags on the chlor
|
|
1539
1900
|
if (sorp.suspendDosing) {
|
|
1540
1901
|
// Kill off the dosing and make sure the pump isn't running. Let's force the issue here.
|
|
1541
1902
|
await this.cancelDosing(sorp, 'suspended');
|
|
@@ -1550,10 +1911,10 @@ export class NixieChemicalORP extends NixieChemical {
|
|
|
1550
1911
|
sorp.dosingVolumeRemaining = 0;
|
|
1551
1912
|
sorp.dosingTimeRemaining = 0;
|
|
1552
1913
|
await this.stopMixing(sorp);
|
|
1553
|
-
await this.cancelDosing(sorp, '
|
|
1914
|
+
await this.cancelDosing(sorp, 'monitoring');
|
|
1554
1915
|
}
|
|
1555
1916
|
if (status === 'mixing') {
|
|
1556
|
-
await this.cancelDosing(sorp, '
|
|
1917
|
+
await this.cancelDosing(sorp, 'mixing');
|
|
1557
1918
|
await this.mixChemicals(sorp);
|
|
1558
1919
|
}
|
|
1559
1920
|
else if (sorp.manualDosing) {
|
|
@@ -1573,74 +1934,224 @@ export class NixieChemicalORP extends NixieChemical {
|
|
|
1573
1934
|
}
|
|
1574
1935
|
else await this.cancelDosing(sorp, 'empty tank');
|
|
1575
1936
|
}
|
|
1576
|
-
else if (sorp.dailyLimitReached) {
|
|
1937
|
+
else if (sorp.dailyLimitReached && !chem.orp.useChlorinator) {
|
|
1577
1938
|
await this.cancelDosing(sorp, 'daily limit');
|
|
1578
1939
|
}
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1940
|
+
// if the ph pump is dosing and dosePriority is enabled, do not dose
|
|
1941
|
+
else if (sorp.chemController.ph.pump.isDosing && chem.ph.dosePriority) {
|
|
1942
|
+
await this.cancelDosing(sorp, 'ph pump dosing + dose priority');
|
|
1943
|
+
return;
|
|
1944
|
+
}
|
|
1945
|
+
else if (status === 'monitoring' || status === 'dosing') {
|
|
1946
|
+
// let _doseCalculatedSec = 0;
|
|
1947
|
+
if (!sorp.lockout) {
|
|
1948
|
+
|
|
1583
1949
|
// 1. Get the total gallons of water that the chem controller is in control of.
|
|
1584
1950
|
let totalGallons = 0;
|
|
1585
|
-
|
|
1586
|
-
|
|
1951
|
+
let body1 = sys.bodies.getItemById(1);
|
|
1952
|
+
let body2 = sys.bodies.getItemById(2);
|
|
1953
|
+
if (chem.body === 0 || chem.body === 32) totalGallons += body1.capacity;
|
|
1954
|
+
if (chem.body === 1 || chem.body === 32) totalGallons += body2.capacity;
|
|
1587
1955
|
if (chem.body === 2) totalGallons += sys.bodies.getItemById(3).capacity;
|
|
1588
1956
|
if (chem.body === 3) totalGallons += sys.bodies.getItemById(4).capacity;
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1957
|
+
|
|
1958
|
+
if (chem.orp.useChlorinator) {
|
|
1959
|
+
/*
|
|
1960
|
+
Alright, here's the current thinking.
|
|
1961
|
+
1. If the orp setpoint is > 50mV below the current orp, the chlor will
|
|
1962
|
+
be run at 100%.
|
|
1963
|
+
2. At the other end, if the demand is < -20mV above the setpoint, chlor
|
|
1964
|
+
will be run at 0%.
|
|
1965
|
+
3. This assumes a sliding scale where we will have an equilibrium point when
|
|
1966
|
+
setpoint = current orp and hopefully this will be somewhere near (50-20 / 100) = ~30% of time the chlor is on
|
|
1967
|
+
|
|
1968
|
+
Thoughts from @rstrouste
|
|
1969
|
+
Volume -- Check
|
|
1970
|
+
Delivery Rate -- IC40, IC60, IC20 and IC30 all have different production rates in pounds/day. The pounds are Sodium Hypochlorite which translates into Hypochlorous acid (HOCl) + Hypochlorite (OCI-). The former is stronger and the amount of this that is produced is based upon, temperature, pH, and CYA with pH within range being irrelevant (hence the reason for pH lockout).
|
|
1971
|
+
|
|
1972
|
+
|
|
1973
|
+
Additional future factors to consider-
|
|
1974
|
+
* If temp is below 65(?), the chlor won't be producing any chlorine. Throw a warning/error?
|
|
1975
|
+
* If salt level is too low/high it will cause issues. Warning/error?
|
|
1976
|
+
* Adjust chlor output if it is under/oversized for the total gallons
|
|
1977
|
+
*/
|
|
1978
|
+
// if we are still mixing, return
|
|
1979
|
+
if (typeof sorp.mixTimeRemaining !== 'undefined' && sorp.mixTimeRemaining > 0) {
|
|
1980
|
+
await this.cancelDosing(sorp, 'still mixing');
|
|
1981
|
+
return;
|
|
1982
|
+
}
|
|
1983
|
+
// Old fashion method; let the setpoints on chlor be the master
|
|
1984
|
+
// if (chem.orp.chlorDosingMethod === 0) return;
|
|
1985
|
+
// if there is a current pending dose, finish it out
|
|
1986
|
+
if (typeof sorp.currentDose === 'undefined') {
|
|
1987
|
+
if (sorp.dosingStatus === 0) { // 0 is dosing
|
|
1988
|
+
// We need to finish off a dose that was interrupted by regular programming. This occurs
|
|
1989
|
+
// when for instance njspc is interrupted and restarted in the middle of a dose. If we were
|
|
1990
|
+
// mixing before we will never get here.
|
|
1991
|
+
if (typeof sorp.currentDose === 'undefined')
|
|
1992
|
+
sorp.startDose(new Timestamp().addSeconds(-sorp.doseTime).toDate(), 'auto', sorp.doseVolume + sorp.dosingVolumeRemaining, sorp.doseVolume, (sorp.doseTime + sorp.dosingTimeRemaining) * 1000, sorp.doseTime * 1000);
|
|
1993
|
+
await this.chlor.dose(sorp);
|
|
1994
|
+
return;
|
|
1610
1995
|
}
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1996
|
+
}
|
|
1997
|
+
|
|
1998
|
+
|
|
1999
|
+
let chlor = sys.chlorinators.getItemById(1); // Still haven't seen any systems with 2+ chlors
|
|
2000
|
+
let schlor = state.chlorinators.getItemById(1);
|
|
2001
|
+
// If someone or something is superchloring the pool, let it be
|
|
2002
|
+
if (schlor.superChlor) return;
|
|
2003
|
+
// Let's have some fun trying to figure out a dynamic approach to chlor management
|
|
2004
|
+
let body = sys.board.bodies.getBodyState(this.chemController.chem.body);
|
|
2005
|
+
let adj = 1;
|
|
2006
|
+
if (typeof body !== 'undefined' && totalGallons > 0 && sys.bodies.length > 1) {
|
|
2007
|
+
// Intellichem scales down dosing based on the spa being on
|
|
2008
|
+
// vs the pool. May be interesting to experiment with.
|
|
2009
|
+
let type = sys.board.valueMaps.bodyTypes.getName(body.type);
|
|
2010
|
+
switch (type) {
|
|
2011
|
+
case "pool":
|
|
2012
|
+
case "pool/spa":
|
|
2013
|
+
// normal dosing
|
|
2014
|
+
break;
|
|
2015
|
+
default:
|
|
2016
|
+
// case "spa":
|
|
2017
|
+
// adjust dosing down to the amount of the smaller body
|
|
2018
|
+
adj -= Math.abs((body1.capacity - body2.capacity) / Math.max(body1.capacity, body2.capacity));
|
|
2019
|
+
break;
|
|
1614
2020
|
}
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
2021
|
+
}
|
|
2022
|
+
let model = sys.board.valueMaps.chlorinatorModel.findItem(chlor.model);
|
|
2023
|
+
if (typeof model === 'undefined' || model === 0) return Promise.reject(new EquipmentNotFoundError(`Please specify a chlorinator model to allow Nixie to calculate chlorine demand`, `chlorinator`));
|
|
2024
|
+
// if we want to adjust for over/under sized chlorinator we can do so here
|
|
2025
|
+
// if (typeof model !== 'undefined' && model.capacity > 0) {
|
|
2026
|
+
// adj *= totalGallons / model.capacity;
|
|
2027
|
+
// }
|
|
1618
2028
|
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
2029
|
+
// unlike ph/orp tank dosing, we are using 15 min intervals so if there is an existing dose then continue
|
|
2030
|
+
if (typeof sorp.currentDose !== 'undefined' && sorp.currentDose.volumeRemaining > 0) {
|
|
2031
|
+
await this.chlor.dose(sorp);
|
|
2032
|
+
return;
|
|
2033
|
+
}
|
|
2034
|
+
|
|
2035
|
+
// We could store these data points in a separate file like the dosing logs.
|
|
2036
|
+
let percentOfTime = 0;
|
|
2037
|
+
if (sorp.demand > 50) {
|
|
2038
|
+
logger.info(`Chlor demand ${sorp.demand} > 50; % of time set to 100%.`);
|
|
2039
|
+
percentOfTime = 1;
|
|
2040
|
+
}
|
|
2041
|
+
else if (sorp.demand < -20) {
|
|
2042
|
+
await this.cancelDosing(sorp, 'demand < -20');
|
|
2043
|
+
}
|
|
2044
|
+
else {
|
|
2045
|
+
// y=mx+b; m = 100/70; b = 100-(50*100/70) = 28.57
|
|
2046
|
+
// let's start with a straight line
|
|
2047
|
+
let b = 100 - (50 * 100 / 70);
|
|
2048
|
+
percentOfTime = ((100 / 70) * sorp.demand * adj + b) / 100;
|
|
2049
|
+
logger.info(`Chlor trend line is ${sorp.demandHistory.slope}.`);
|
|
2050
|
+
if (sorp.demandHistory.slope > 5 && sorp.demand < 0) {
|
|
2051
|
+
// need less chlorine, but we're getting there too fast
|
|
2052
|
+
// slope is high; turn down dose
|
|
2053
|
+
percentOfTime *= .5;
|
|
2054
|
+
}
|
|
2055
|
+
else if (sorp.demandHistory.slope < 5 && sorp.demand > 0) {
|
|
2056
|
+
// need more chlorine, but we aren't getting there fast enough
|
|
2057
|
+
// slope is too low, turn up dose
|
|
2058
|
+
percentOfTime *= 1.1;
|
|
2059
|
+
}
|
|
2060
|
+
else if (sorp.demandHistory.slope > 0 && sorp.demand > 0) {
|
|
2061
|
+
// chlorine is increasing, but we need less of it
|
|
2062
|
+
percentOfTime *= .5;
|
|
2063
|
+
}
|
|
2064
|
+
else if (sorp.demandHistory.slope < 0 && sorp.demand > 0) {
|
|
2065
|
+
// chlorine is decreasing, but we need more
|
|
2066
|
+
percentOfTime *= 1.1;
|
|
2067
|
+
}
|
|
2068
|
+
percentOfTime = Math.min(1, Math.max(0, percentOfTime));
|
|
2069
|
+
logger.info(`Chlor dosing % of time is ${Math.round(percentOfTime * 10000) / 100}%`)
|
|
1629
2070
|
}
|
|
1630
|
-
|
|
2071
|
+
|
|
2072
|
+
// convert the % of time back to an amount of chlorine over 15 minutes;
|
|
2073
|
+
let time = this.chlor.chlorInterval * 60 * percentOfTime;
|
|
2074
|
+
let dose = model.chlorinePerSec * time;
|
|
2075
|
+
|
|
2076
|
+
if (dose > 0) {
|
|
2077
|
+
logger.info(`Chem chlor calculated dosing at ${Math.round(percentOfTime * 10000) / 100}% and will dose ${Math.round(dose * 1000000) / 1000000}Lbs of chlorine over the next ${utils.formatDuration(time)}.`)
|
|
1631
2078
|
sorp.startDose(new Date(), 'auto', dose, 0, time, 0);
|
|
2079
|
+
await this.chlor.dose(sorp);
|
|
2080
|
+
return;
|
|
2081
|
+
}
|
|
2082
|
+
|
|
2083
|
+
// if none of the other conditions are true, mix
|
|
2084
|
+
// await this.mixChemicals(sorp, this.chlor.chlorInterval * 60);
|
|
2085
|
+
|
|
1632
2086
|
}
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
2087
|
+
else if (this.orp.setpoint > sorp.level) {
|
|
2088
|
+
let pump = this.pump.pump;
|
|
2089
|
+
// Calculate how many mL are required to raise to our ORP level.
|
|
2090
|
+
let demand = Math.round(utils.convert.volume.convertUnits(0, 'oz', 'mL'));
|
|
2091
|
+
let time = typeof pump.ratedFlow === 'undefined' || pump.ratedFlow <= 0 ? 0 : Math.round(demand / (pump.ratedFlow / 60));
|
|
2092
|
+
let meth = sys.board.valueMaps.chemDosingMethods.getName(this.orp.dosingMethod);
|
|
2093
|
+
// Now that we know our chlorine demand we need to adjust this dose based upon the limits provided in the setup.
|
|
2094
|
+
switch (meth) {
|
|
2095
|
+
case 'time':
|
|
2096
|
+
time = this.orp.maxDosingTime;
|
|
2097
|
+
demand = typeof pump.ratedFlow === 'undefined' ? 0 : Math.round(time * (this.pump.pump.ratedFlow / 60));
|
|
2098
|
+
break;
|
|
2099
|
+
case 'volume':
|
|
2100
|
+
demand = this.orp.maxDosingVolume;
|
|
2101
|
+
time = typeof pump.ratedFlow === 'undefined' || pump.ratedFlow <= 0 ? 0 : Math.round(demand / (pump.ratedFlow / 60));
|
|
2102
|
+
break;
|
|
2103
|
+
case 'volumeTime':
|
|
2104
|
+
default:
|
|
2105
|
+
// This is maybe a bit dumb as the volume and time should equal out for the rated flow. In other words
|
|
2106
|
+
// you will never get to the volume limit if the rated flow can't keep up to the time.
|
|
2107
|
+
if (demand > this.orp.maxDosingVolume) {
|
|
2108
|
+
demand = this.orp.maxDosingVolume;
|
|
2109
|
+
time = typeof pump.ratedFlow === 'undefined' || pump.ratedFlow <= 0 ? 0 : Math.round(demand / (pump.ratedFlow / 60));
|
|
2110
|
+
}
|
|
2111
|
+
if (time > this.orp.maxDosingTime) {
|
|
2112
|
+
time = this.orp.maxDosingTime;
|
|
2113
|
+
demand = typeof pump.ratedFlow === 'undefined' ? 0 : Math.round(time * (this.pump.pump.ratedFlow / 60));
|
|
2114
|
+
}
|
|
2115
|
+
break;
|
|
2116
|
+
}
|
|
2117
|
+
logger.info(`Chem orp dose calculated ${demand}mL for ${utils.formatDuration(time)} Tank Level: ${sorp.tank.level} using ${meth}`);
|
|
2118
|
+
|
|
2119
|
+
sorp.demand = sorp.calcDemand(chem);
|
|
2120
|
+
if (sorp.demand > 0) logger.info(`Chem orp dose calculated ${demand}mL for ${utils.formatDuration(time)} Tank Level: ${sorp.tank.level} using ${meth}`);
|
|
2121
|
+
|
|
2122
|
+
if (typeof sorp.currentDose === 'undefined') {
|
|
2123
|
+
// We will include this with the dose demand because our limits may reduce it.
|
|
2124
|
+
//dosage.demand = demand;
|
|
2125
|
+
if (sorp.dosingStatus === 0) { // 0 is dosing.
|
|
2126
|
+
// We need to finish off a dose that was interrupted by regular programming. This occurs
|
|
2127
|
+
// when for instance njspc is interrupted and restarted in the middle of a dose. If we were
|
|
2128
|
+
// mixing before we will never get here.
|
|
2129
|
+
if (typeof sorp.currentDose === 'undefined')
|
|
2130
|
+
sorp.startDose(new Timestamp().addSeconds(-sorp.doseTime).toDate(), 'auto', sorp.doseVolume + sorp.dosingVolumeRemaining, sorp.doseVolume, (sorp.doseTime + sorp.dosingTimeRemaining) * 1000, sorp.doseTime * 1000);
|
|
2131
|
+
}
|
|
2132
|
+
else
|
|
2133
|
+
sorp.startDose(new Date(), 'auto', demand, 0, time, 0);
|
|
2134
|
+
}
|
|
2135
|
+
// Now let's determine what we need to do with our pump to satisfy our acid demand.
|
|
2136
|
+
if (sorp.tank.level > 0) {
|
|
2137
|
+
await this.pump.dose(sorp);
|
|
2138
|
+
}
|
|
2139
|
+
else await this.cancelDosing(sorp, 'empty tank');
|
|
1636
2140
|
}
|
|
1637
|
-
else await this.cancelDosing(sorp, 'empty tank');
|
|
1638
2141
|
}
|
|
1639
2142
|
else
|
|
1640
2143
|
await this.cancelDosing(sorp, 'unknown cancel');
|
|
1641
2144
|
}
|
|
1642
2145
|
}
|
|
1643
|
-
catch (err) { logger.error(`checkDosing ORP: ${err.message}`); return Promise.reject(err);}
|
|
2146
|
+
catch (err) { logger.error(`checkDosing ORP: ${err.message}`); return Promise.reject(err); }
|
|
2147
|
+
}
|
|
2148
|
+
public async deleteChlorAsync(chlor: NixieChlorinator) {
|
|
2149
|
+
logger.info(`Removing chlor ${chlor.id} from Chem Controller ${this.getParent().id}`);
|
|
2150
|
+
let schem = state.chemControllers.getItemById(this.getParent().id);
|
|
2151
|
+
this.orp.useChlorinator = false;
|
|
2152
|
+
schem.orp.useChlorinator = false;
|
|
2153
|
+
if (schem.orp.dosingStatus === 0) { await this.cancelDosing(schem.orp, 'deleting chlorinator'); }
|
|
2154
|
+
if (schem.orp.dosingStatus === 1) { await this.cancelMixing(schem.orp); }
|
|
1644
2155
|
}
|
|
1645
2156
|
}
|
|
1646
2157
|
class NixieChemProbe extends NixieChildEquipment {
|
|
@@ -1695,7 +2206,7 @@ export class NixieChemProbePh extends NixieChemProbe {
|
|
|
1695
2206
|
// Set the current body so that it references the temperature of the current running body.
|
|
1696
2207
|
let body = sys.board.bodies.getBodyState(this.chemical.chemController.chem.body);
|
|
1697
2208
|
if (typeof body !== 'undefined' && body.isOn) {
|
|
1698
|
-
let units = sys.board.valueMaps.tempUnits.transform(
|
|
2209
|
+
let units = sys.board.valueMaps.tempUnits.transform(state.temps.units);
|
|
1699
2210
|
let obj = {};
|
|
1700
2211
|
obj[`temp${units.name.toUpperCase()}`] = body.temp;
|
|
1701
2212
|
sprobe.tempUnits = units.val;
|
|
@@ -1721,7 +2232,7 @@ export class NixieChemProbePh extends NixieChemProbe {
|
|
|
1721
2232
|
deviceBinding: this.probe.deviceBinding,
|
|
1722
2233
|
eventName: "chemController",
|
|
1723
2234
|
property: "pHLevel",
|
|
1724
|
-
sendValue: '
|
|
2235
|
+
sendValue: 'all',
|
|
1725
2236
|
isActive: data.remFeedEnabled,
|
|
1726
2237
|
sampling: 1,
|
|
1727
2238
|
changesOnly: false,
|
|
@@ -1729,7 +2240,7 @@ export class NixieChemProbePh extends NixieChemProbe {
|
|
|
1729
2240
|
}
|
|
1730
2241
|
let res = await NixieChemController.putDeviceService(this.probe.connectionId, '/config/feed', d);
|
|
1731
2242
|
if (res.status.code === 200) { this.probe.remFeedEnabled = data.remFeedEnabled; }
|
|
1732
|
-
else { logger.warn(`setRemoteREMFeed: Cannot set remote feed. Message:${JSON.stringify(res.status)} for feed: ${JSON.stringify(d)}.`);
|
|
2243
|
+
else { logger.warn(`setRemoteREMFeed: Cannot set remote feed. Message:${JSON.stringify(res.status)} for feed: ${JSON.stringify(d)}.`); }
|
|
1733
2244
|
}
|
|
1734
2245
|
catch (err) { logger.error(`setRemoteREMFeed: ${err.message}`); return Promise.reject(err); }
|
|
1735
2246
|
}
|
|
@@ -1812,9 +2323,15 @@ export class NixieChemProbeORP extends NixieChemProbe {
|
|
|
1812
2323
|
}
|
|
1813
2324
|
let res = await NixieChemController.putDeviceService(this.probe.connectionId, '/config/feed', d);
|
|
1814
2325
|
if (res.status.code === 200) { this.probe.remFeedEnabled = data.remFeedEnabled; }
|
|
1815
|
-
else {
|
|
2326
|
+
else {
|
|
2327
|
+
logger.warn(`setRemoteREMFeed: Cannot set remote feed. Message:${JSON.stringify(res.status)} for feed: ${JSON.stringify(d)}.`);
|
|
2328
|
+
// return Promise.reject(new InvalidOperationError(`Nixie could not set remote REM feed for the ORP probe.`, this.probe.dataName));
|
|
2329
|
+
}
|
|
2330
|
+
}
|
|
2331
|
+
catch (err) {
|
|
2332
|
+
logger.error(`setRemoteREMFeed: ${err.message}`);
|
|
2333
|
+
//return Promise.reject(err); // don't muck up chem controller if we can't set the feeds.
|
|
1816
2334
|
}
|
|
1817
|
-
catch (err) { logger.error(`setRemoteREMFeed: ${err.message}`); return Promise.reject(err); }
|
|
1818
2335
|
}
|
|
1819
2336
|
public syncRemoteREMFeeds(chem: ChemController, servers) {
|
|
1820
2337
|
// match any feeds and store the id/statusf
|