nodejs-poolcontroller 7.7.0 → 8.0.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/.eslintrc.json +26 -35
- package/Changelog +22 -0
- package/README.md +7 -3
- package/anslq25/MessagesMock.ts +218 -0
- package/anslq25/boards/MockBoardFactory.ts +50 -0
- package/anslq25/boards/MockEasyTouchBoard.ts +696 -0
- package/anslq25/boards/MockSystemBoard.ts +217 -0
- package/anslq25/chemistry/MockChlorinator.ts +75 -0
- package/anslq25/pumps/MockPump.ts +84 -0
- package/app.ts +10 -14
- package/config/Config.ts +13 -9
- package/config/VersionCheck.ts +6 -2
- package/controller/Constants.ts +58 -25
- package/controller/Equipment.ts +225 -41
- package/controller/Errors.ts +2 -1
- package/controller/Lockouts.ts +34 -2
- package/controller/State.ts +491 -48
- package/controller/boards/AquaLinkBoard.ts +6 -3
- package/controller/boards/BoardFactory.ts +5 -1
- package/controller/boards/EasyTouchBoard.ts +1971 -1751
- package/controller/boards/IntelliCenterBoard.ts +1311 -1688
- package/controller/boards/IntelliComBoard.ts +7 -1
- package/controller/boards/IntelliTouchBoard.ts +153 -42
- package/controller/boards/NixieBoard.ts +209 -66
- package/controller/boards/SunTouchBoard.ts +393 -0
- package/controller/boards/SystemBoard.ts +1862 -1543
- package/controller/comms/Comms.ts +539 -138
- package/controller/comms/ScreenLogic.ts +1663 -0
- package/controller/comms/messages/Messages.ts +242 -60
- package/controller/comms/messages/config/ChlorinatorMessage.ts +4 -3
- package/controller/comms/messages/config/CircuitGroupMessage.ts +5 -2
- package/controller/comms/messages/config/CircuitMessage.ts +81 -13
- package/controller/comms/messages/config/ConfigMessage.ts +3 -1
- package/controller/comms/messages/config/CoverMessage.ts +2 -1
- package/controller/comms/messages/config/CustomNameMessage.ts +2 -1
- package/controller/comms/messages/config/EquipmentMessage.ts +5 -1
- package/controller/comms/messages/config/ExternalMessage.ts +33 -3
- package/controller/comms/messages/config/FeatureMessage.ts +2 -1
- package/controller/comms/messages/config/GeneralMessage.ts +2 -1
- package/controller/comms/messages/config/HeaterMessage.ts +3 -1
- package/controller/comms/messages/config/IntellichemMessage.ts +2 -1
- package/controller/comms/messages/config/OptionsMessage.ts +12 -6
- package/controller/comms/messages/config/PumpMessage.ts +9 -12
- package/controller/comms/messages/config/RemoteMessage.ts +80 -13
- package/controller/comms/messages/config/ScheduleMessage.ts +43 -3
- package/controller/comms/messages/config/SecurityMessage.ts +2 -1
- package/controller/comms/messages/config/ValveMessage.ts +43 -26
- package/controller/comms/messages/status/ChlorinatorStateMessage.ts +8 -7
- package/controller/comms/messages/status/EquipmentStateMessage.ts +93 -20
- package/controller/comms/messages/status/HeaterStateMessage.ts +24 -5
- package/controller/comms/messages/status/IntelliChemStateMessage.ts +7 -4
- package/controller/comms/messages/status/IntelliValveStateMessage.ts +2 -1
- package/controller/comms/messages/status/PumpStateMessage.ts +72 -4
- package/controller/comms/messages/status/VersionMessage.ts +2 -1
- package/controller/nixie/Nixie.ts +15 -4
- package/controller/nixie/NixieEquipment.ts +1 -0
- package/controller/nixie/chemistry/ChemController.ts +300 -129
- package/controller/nixie/chemistry/ChemDoser.ts +806 -0
- package/controller/nixie/chemistry/Chlorinator.ts +133 -129
- package/controller/nixie/circuits/Circuit.ts +171 -30
- package/controller/nixie/heaters/Heater.ts +337 -173
- package/controller/nixie/pumps/Pump.ts +264 -236
- package/controller/nixie/schedules/Schedule.ts +9 -3
- package/defaultConfig.json +46 -5
- package/logger/Logger.ts +38 -9
- package/package.json +13 -9
- package/web/Server.ts +235 -122
- package/web/bindings/aqualinkD.json +114 -59
- package/web/bindings/homeassistant.json +437 -0
- package/web/bindings/influxDB.json +15 -0
- package/web/bindings/mqtt.json +28 -9
- package/web/bindings/mqttAlt.json +15 -0
- package/web/interfaces/baseInterface.ts +58 -7
- package/web/interfaces/httpInterface.ts +5 -2
- package/web/interfaces/influxInterface.ts +9 -2
- package/web/interfaces/mqttInterface.ts +234 -74
- package/web/interfaces/ruleInterface.ts +87 -0
- package/web/services/config/Config.ts +140 -33
- package/web/services/config/ConfigSocket.ts +2 -1
- package/web/services/state/State.ts +144 -3
- package/web/services/state/StateSocket.ts +65 -14
- package/web/services/utilities/Utilities.ts +189 -1
|
@@ -1,17 +1,26 @@
|
|
|
1
1
|
import { clearTimeout, setTimeout } from 'timers';
|
|
2
2
|
import { conn } from '../../../controller/comms/Comms';
|
|
3
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";
|
|
4
|
+
import { IChemical, IChemController, ChemController, ChemControllerCollection, ChemFlowSensor, Chemical, ChemicalChlor, ChemicalORP, ChemicalORPProbe, ChemicalPh, ChemicalPhProbe, ChemicalProbe, ChemicalPump, ChemicalTank, sys } from "../../../controller/Equipment";
|
|
5
5
|
import { logger } from '../../../logger/Logger';
|
|
6
6
|
import { InterfaceServerResponse, webApp } from "../../../web/Server";
|
|
7
7
|
import { Timestamp, utils } from '../../Constants';
|
|
8
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";
|
|
9
|
+
import { IChemicalState, ChemControllerState, ChemicalChlorState, ChemicalDoseState, ChemicalORPState, ChemicalPhState, ChemicalProbeORPState, ChemicalProbePHState, ChemicalProbeState, ChemicalPumpState, ChemicalState, ChemicalTankState, ChlorinatorState, state } from "../../State";
|
|
10
10
|
import { ncp } from '../Nixie';
|
|
11
11
|
import { INixieControlPanel, NixieChildEquipment, NixieEquipment, NixieEquipmentCollection } from "../NixieEquipment";
|
|
12
12
|
import { NixieChlorinator } from './Chlorinator';
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
export interface INixieChemController {
|
|
14
|
+
bodyOnTime: number;
|
|
15
|
+
processAlarms: (schem: any) => void;
|
|
16
|
+
isBodyOn: () => boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface INixieChemical extends NixieEquipment {
|
|
19
|
+
cancelDosing: (schem: IChemicalState, reason: string) => Promise<void>;
|
|
20
|
+
initDose: (schem: any) => Promise<void>;
|
|
21
|
+
chemController: INixieChemController;
|
|
22
|
+
chemical: IChemical;
|
|
23
|
+
}
|
|
15
24
|
export class NixieChemControllerCollection extends NixieEquipmentCollection<NixieChemControllerBase> {
|
|
16
25
|
public async manualDoseAsync(id: number, data: any) {
|
|
17
26
|
try {
|
|
@@ -20,6 +29,14 @@ export class NixieChemControllerCollection extends NixieEquipmentCollection<Nixi
|
|
|
20
29
|
await c.manualDoseAsync(data);
|
|
21
30
|
} catch (err) { logger.error(`manualDoseAysnc: ${err.message}`); return Promise.reject(err); }
|
|
22
31
|
}
|
|
32
|
+
public async calibrateDoseAsync(id: number, data: any) {
|
|
33
|
+
try {
|
|
34
|
+
let c: NixieChemController = this.find(elem => elem.id === id) as NixieChemController;
|
|
35
|
+
if (typeof c === 'undefined') return Promise.reject(new InvalidEquipmentIdError(`Nixie could not find a chem controller at id ${id}`, id, 'chemController'));
|
|
36
|
+
await c.calibrateDoseAsync(data);
|
|
37
|
+
} catch (err) { logger.error(`calibrateDoseAysnc: ${err.message}`); return Promise.reject(err); }
|
|
38
|
+
}
|
|
39
|
+
|
|
23
40
|
public async cancelDoseAsync(id: number, data: any) {
|
|
24
41
|
try {
|
|
25
42
|
let c: NixieChemController = this.find(elem => elem.id === id) as NixieChemController;
|
|
@@ -100,6 +117,17 @@ export class NixieChemControllerCollection extends NixieEquipmentCollection<Nixi
|
|
|
100
117
|
|
|
101
118
|
} catch (err) { } // Don't bail if we have an error
|
|
102
119
|
}
|
|
120
|
+
public async setServiceModeAsync() {
|
|
121
|
+
try {
|
|
122
|
+
for (let i = this.length - 1; i >= 0; i--) {
|
|
123
|
+
try {
|
|
124
|
+
let cc = this[i] as NixieChemControllerBase;
|
|
125
|
+
await cc.setServiceModeAsync();
|
|
126
|
+
} catch (err) { logger.error(`Error setting Chem Controller to service mode ${err}`); return Promise.reject(err); }
|
|
127
|
+
}
|
|
128
|
+
} catch (err) { } // Don't bail if we have an error
|
|
129
|
+
}
|
|
130
|
+
|
|
103
131
|
public async deleteChlorAsync(chlor: NixieChlorinator) {
|
|
104
132
|
// if we delete the chlor, make sure it is removed from all REM Chem Controllers
|
|
105
133
|
try {
|
|
@@ -139,7 +167,7 @@ export class NixieChemControllerCollection extends NixieEquipmentCollection<Nixi
|
|
|
139
167
|
} catch (err) { return arr; }
|
|
140
168
|
} */
|
|
141
169
|
}
|
|
142
|
-
export class NixieChemControllerBase extends NixieEquipment {
|
|
170
|
+
export class NixieChemControllerBase extends NixieEquipment implements INixieChemController {
|
|
143
171
|
public pollingInterval: number = 10000;
|
|
144
172
|
protected _suspendPolling: number = 0;
|
|
145
173
|
public get suspendPolling(): boolean { return this._suspendPolling > 0; }
|
|
@@ -159,6 +187,7 @@ export class NixieChemControllerBase extends NixieEquipment {
|
|
|
159
187
|
}
|
|
160
188
|
public chem: ChemController;
|
|
161
189
|
public syncRemoteREMFeeds(servers) { }
|
|
190
|
+
public async setServiceModeAsync() {}
|
|
162
191
|
public static create(ncp: INixieControlPanel, chem: ChemController): NixieChemControllerBase {
|
|
163
192
|
let type = sys.board.valueMaps.chemControllerTypes.transform(chem.type);
|
|
164
193
|
switch (type.name) {
|
|
@@ -180,7 +209,10 @@ export class NixieChemControllerBase extends NixieEquipment {
|
|
|
180
209
|
return isOn;
|
|
181
210
|
}
|
|
182
211
|
public async setControllerAsync(data: any) { } // This is meant to be abstract override this value
|
|
212
|
+
public processAlarms(schem: any) { }
|
|
213
|
+
|
|
183
214
|
}
|
|
215
|
+
|
|
184
216
|
export class NixieIntelliChemController extends NixieChemControllerBase {
|
|
185
217
|
public configSent: boolean = false;
|
|
186
218
|
constructor(ncp: INixieControlPanel, chem: ChemController) {
|
|
@@ -209,6 +241,7 @@ export class NixieIntelliChemController extends NixieChemControllerBase {
|
|
|
209
241
|
catch (err) { logger.error(`Error polling IntelliChem Controller - ${err}`); return Promise.reject(err); }
|
|
210
242
|
finally { this.suspendPolling = false; if (!this.closing) this._pollTimer = setTimeout(() => { self.pollEquipmentAsync(); }, this.pollingInterval || 10000); }
|
|
211
243
|
}
|
|
244
|
+
public async setServiceModeAsync() {}
|
|
212
245
|
public async setControllerAsync(data: any) {
|
|
213
246
|
try {
|
|
214
247
|
this.suspendPolling = true;
|
|
@@ -312,7 +345,6 @@ export class NixieIntelliChemController extends NixieChemControllerBase {
|
|
|
312
345
|
}
|
|
313
346
|
public async sendConfig(schem: ChemControllerState): Promise<boolean> {
|
|
314
347
|
try {
|
|
315
|
-
return await new Promise<boolean>((resolve, reject) => {
|
|
316
348
|
this.configSent = false;
|
|
317
349
|
let out = Outbound.create({
|
|
318
350
|
protocol: Protocol.IntelliChem,
|
|
@@ -322,16 +354,7 @@ export class NixieIntelliChemController extends NixieChemControllerBase {
|
|
|
322
354
|
payload: [],
|
|
323
355
|
retries: 3, // We are going to try 4 times.
|
|
324
356
|
response: Response.create({ protocol: Protocol.IntelliChem, action: 1 }),
|
|
325
|
-
onAbort: () => { }
|
|
326
|
-
onComplete: (err) => {
|
|
327
|
-
if (err) {
|
|
328
|
-
resolve(false);
|
|
329
|
-
}
|
|
330
|
-
else {
|
|
331
|
-
this.configSent = true;
|
|
332
|
-
resolve(true);
|
|
333
|
-
}
|
|
334
|
-
}
|
|
357
|
+
onAbort: () => { }
|
|
335
358
|
});
|
|
336
359
|
out.insertPayloadBytes(0, 0, 21);
|
|
337
360
|
out.setPayloadByte(0, Math.floor((this.chem.ph.setpoint * 100) / 256) || 0);
|
|
@@ -346,15 +369,15 @@ export class NixieIntelliChemController extends NixieChemControllerBase {
|
|
|
346
369
|
out.setPayloadByte(10, Math.floor(this.chem.alkalinity / 256) || 0);
|
|
347
370
|
out.setPayloadByte(12, Math.round(this.chem.alkalinity % 256) || 0);
|
|
348
371
|
logger.verbose(`Nixie: ${this.chem.name} sending IntelliChem settings action 146`);
|
|
349
|
-
|
|
350
|
-
|
|
372
|
+
out.sendAsync();
|
|
373
|
+
this.configSent = true;
|
|
374
|
+
return true;
|
|
351
375
|
}
|
|
352
376
|
catch (err) { logger.error(`Error updating IntelliChem: ${err.message}`); }
|
|
353
377
|
}
|
|
354
378
|
public async requestStatus(schem: ChemControllerState): Promise<boolean> {
|
|
355
379
|
try {
|
|
356
380
|
schem.type = 2;
|
|
357
|
-
let success = await new Promise<boolean>((resolve, reject) => {
|
|
358
381
|
let out = Outbound.create({
|
|
359
382
|
protocol: Protocol.IntelliChem,
|
|
360
383
|
source: 16,
|
|
@@ -363,21 +386,17 @@ export class NixieIntelliChemController extends NixieChemControllerBase {
|
|
|
363
386
|
payload: [210],
|
|
364
387
|
retries: 3, // We are going to try 4 times.
|
|
365
388
|
response: Response.create({ protocol: Protocol.IntelliChem, action: 18 }),
|
|
366
|
-
onAbort: () => { }
|
|
367
|
-
onComplete: (err) => {
|
|
368
|
-
if (err) {
|
|
369
|
-
// If the IntelliChem is not responding we need to store that off. If an 18 does
|
|
370
|
-
// come across this will be cleared by the processing of that message.
|
|
371
|
-
schem.alarms.comms = sys.board.valueMaps.chemControllerStatus.encode('nocomms');
|
|
372
|
-
resolve(false);
|
|
373
|
-
}
|
|
374
|
-
else { resolve(true); }
|
|
375
|
-
}
|
|
389
|
+
onAbort: () => { }
|
|
376
390
|
});
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
391
|
+
await out.sendAsync();
|
|
392
|
+
return true;
|
|
393
|
+
} catch (err) {
|
|
394
|
+
// If the IntelliChem is not responding we need to store that off. If an 18 does
|
|
395
|
+
// come across this will be cleared by the processing of that message.
|
|
396
|
+
schem.alarms.comms = sys.board.valueMaps.chemControllerStatus.encode('nocomms');
|
|
397
|
+
logger.error(`Communication error with IntelliChem : ${err.message}`);
|
|
398
|
+
return false;
|
|
399
|
+
}
|
|
381
400
|
}
|
|
382
401
|
public async closeAsync() {
|
|
383
402
|
try {
|
|
@@ -419,6 +438,36 @@ export class NixieChemController extends NixieChemControllerBase {
|
|
|
419
438
|
}
|
|
420
439
|
}
|
|
421
440
|
}
|
|
441
|
+
public async setServiceModeAsync() {
|
|
442
|
+
let schem = state.chemControllers.getItemById(this.chem.id);
|
|
443
|
+
if(this.chem.ph.enabled) await this.ph.cancelDosing(schem.ph, 'service mode');
|
|
444
|
+
if(this.chem.orp.enabled) await this.orp.cancelDosing(schem.orp, 'service mode');
|
|
445
|
+
}
|
|
446
|
+
public async calibrateDoseAsync(data: any) {
|
|
447
|
+
try {
|
|
448
|
+
this.suspendPolling = true;
|
|
449
|
+
// Check to see that we are a rem chem.
|
|
450
|
+
let time = parseInt(data.time, 10);
|
|
451
|
+
if (isNaN(time)) return Promise.reject(new InvalidEquipmentDataError(`Time was not supplied for the calibration chem dose`, 'chemController', data.time));
|
|
452
|
+
// Determine which chemical we are dosing. This will be ph or orp.
|
|
453
|
+
let chemType = typeof data.chemType === 'string' ? data.chemType.toLowerCase() : '';
|
|
454
|
+
if (typeof this[chemType] === 'undefined') return Promise.reject(new InvalidEquipmentDataError(`A valid Chem type was not supplied for the calibration chem dose ${data.chemType}`, 'chemController', data.chemType));
|
|
455
|
+
let chem = this.chem[chemType];
|
|
456
|
+
if (typeof chem === 'undefined') return Promise.reject(new InvalidEquipmentDataError(`Could not initiate ${data.chemType} manual dose config not found.`, 'chemController', data.chemType));
|
|
457
|
+
let schem = state.chemControllers.getItemById(this.chem.id, true)[chemType];
|
|
458
|
+
if (typeof schem === 'undefined') return Promise.reject(new InvalidEquipmentDataError(`Could not initiate ${data.chemType} manual dose state not found.`, 'chemController', data.chemType));
|
|
459
|
+
// Now we can tell the chemical to dose.
|
|
460
|
+
if (chemType === 'ph') {
|
|
461
|
+
await this.ph.calibrateDoseAsync(schem, time);
|
|
462
|
+
}
|
|
463
|
+
else if (chemType === 'orp') {
|
|
464
|
+
await this.orp.calibrateDoseAsync(schem, time);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
catch (err) { logger.error(`calibrateDoseAsync: ${err.message}`); return Promise.reject(err); }
|
|
468
|
+
finally { this.suspendPolling = false; }
|
|
469
|
+
|
|
470
|
+
}
|
|
422
471
|
public async manualDoseAsync(data: any) {
|
|
423
472
|
try {
|
|
424
473
|
this.suspendPolling = true;
|
|
@@ -433,8 +482,12 @@ export class NixieChemController extends NixieChemControllerBase {
|
|
|
433
482
|
let schem = state.chemControllers.getItemById(this.chem.id, true)[chemType];
|
|
434
483
|
if (typeof schem === 'undefined') return Promise.reject(new InvalidEquipmentDataError(`Could not initiate ${data.chemType} manual dose state not found.`, 'chemController', data.chemType));
|
|
435
484
|
// Now we can tell the chemical to dose.
|
|
436
|
-
if (chemType === 'ph')
|
|
437
|
-
|
|
485
|
+
if (chemType === 'ph') {
|
|
486
|
+
await this.ph.manualDoseVolumeAsync(schem, vol);
|
|
487
|
+
}
|
|
488
|
+
else if (chemType === 'orp') {
|
|
489
|
+
await this.orp.manualDoseVolumeAsync(schem, vol);
|
|
490
|
+
}
|
|
438
491
|
}
|
|
439
492
|
catch (err) { logger.error(`manualDoseAsync: ${err.message}`); return Promise.reject(err); }
|
|
440
493
|
finally { this.suspendPolling = false; }
|
|
@@ -529,7 +582,7 @@ export class NixieChemController extends NixieChemControllerBase {
|
|
|
529
582
|
await this.orp.setORPAsync(schem.orp, data.orp);
|
|
530
583
|
// Ph Settings
|
|
531
584
|
await this.ph.setPhAsync(schem.ph, data.ph);
|
|
532
|
-
|
|
585
|
+
this.processAlarms(schem);
|
|
533
586
|
}
|
|
534
587
|
catch (err) { logger.error(`setControllerAsync: ${err.message}`); return Promise.reject(err); }
|
|
535
588
|
finally { this.suspendPolling = false; }
|
|
@@ -608,8 +661,10 @@ export class NixieChemController extends NixieChemControllerBase {
|
|
|
608
661
|
// Check each piece of equipment to make sure it is doing its thing.
|
|
609
662
|
schem.calculateSaturationIndex();
|
|
610
663
|
this.processAlarms(schem);
|
|
611
|
-
if (
|
|
612
|
-
|
|
664
|
+
if (state.mode === 0) {
|
|
665
|
+
if (this.chem.ph.enabled) await this.ph.checkDosing(this.chem, schem.ph);
|
|
666
|
+
if (this.chem.orp.enabled) await this.orp.checkDosing(this.chem, schem.orp);
|
|
667
|
+
}
|
|
613
668
|
}
|
|
614
669
|
else
|
|
615
670
|
logger.warn('REM Server not Connected');
|
|
@@ -642,11 +697,11 @@ export class NixieChemController extends NixieChemControllerBase {
|
|
|
642
697
|
let chem = this.chem;
|
|
643
698
|
schem.orp.enabled = this.chem.orp.enabled;
|
|
644
699
|
schem.ph.enabled = this.chem.ph.enabled;
|
|
700
|
+
let probeType = chem.orp.probe.type;
|
|
645
701
|
if (this.chem.orp.enabled) {
|
|
646
702
|
|
|
647
703
|
let useChlorinator = chem.orp.useChlorinator;
|
|
648
704
|
let pumpType = chem.orp.pump.type;
|
|
649
|
-
let probeType = chem.orp.probe.type;
|
|
650
705
|
let currLevelPercent = schem.orp.tank.level / schem.orp.tank.capacity * 100;
|
|
651
706
|
if (pumpType !== 0) {
|
|
652
707
|
if (currLevelPercent <= 0) schem.alarms.orpTank = 64;
|
|
@@ -681,13 +736,17 @@ export class NixieChemController extends NixieChemControllerBase {
|
|
|
681
736
|
schem.warnings.chlorinatorCommError = 0;
|
|
682
737
|
schem.alarms.orpTank = 0;
|
|
683
738
|
schem.warnings.orpDailyLimitReached = 0;
|
|
684
|
-
|
|
739
|
+
// RSG 5-22-22 below block will allow a user to have an orp probe without enabling orp output
|
|
740
|
+
if (probeType !== 0 && chem.orp.tolerance.enabled){
|
|
741
|
+
schem.alarms.orp = schem.orp.level < chem.orp.tolerance.low ? 16 : schem.orp.level > chem.orp.tolerance.high ? 8 : 0;
|
|
742
|
+
}
|
|
743
|
+
else schem.alarms.orp = 0;
|
|
685
744
|
schem.warnings.pHLockout = 0;
|
|
686
745
|
schem.orp.freezeProtect = false;
|
|
687
746
|
}
|
|
747
|
+
probeType = chem.ph.probe.type;
|
|
688
748
|
if (this.chem.ph.enabled) {
|
|
689
749
|
let pumpType = chem.ph.pump.type;
|
|
690
|
-
let probeType = chem.ph.probe.type;
|
|
691
750
|
let currLevelPercent = schem.ph.tank.level / schem.ph.tank.capacity * 100;
|
|
692
751
|
if (pumpType !== 0) {
|
|
693
752
|
if (currLevelPercent <= 0) schem.alarms.pHTank = 32;
|
|
@@ -714,10 +773,15 @@ export class NixieChemController extends NixieChemControllerBase {
|
|
|
714
773
|
else schem.alarms.pH = 0;
|
|
715
774
|
schem.ph.freezeProtect = (state.freeze && chem.ph.disableOnFreeze && schem.isBodyOn);
|
|
716
775
|
}
|
|
776
|
+
|
|
717
777
|
else {
|
|
718
778
|
schem.alarms.pHTank = 0;
|
|
719
779
|
schem.warnings.pHDailyLimitReached = 0;
|
|
720
|
-
|
|
780
|
+
// RSG 5-22-22 Below block will allow user to have a pH probe without enabling pH control
|
|
781
|
+
if (probeType !== 0 && chem.ph.tolerance.enabled) {
|
|
782
|
+
schem.alarms.pH = schem.ph.level < chem.ph.tolerance.low ? 4 : schem.ph.level > chem.ph.tolerance.high ? 2 : 0;
|
|
783
|
+
}
|
|
784
|
+
else schem.alarms.pH = 0;
|
|
721
785
|
schem.ph.freezeProtect = false;
|
|
722
786
|
}
|
|
723
787
|
|
|
@@ -779,7 +843,7 @@ export class NixieChemController extends NixieChemControllerBase {
|
|
|
779
843
|
}
|
|
780
844
|
else schem.alarms.pHProbeFault = 0;
|
|
781
845
|
if (chem.ph.pump.type !== 0) {
|
|
782
|
-
let type = sys.board.valueMaps.chemPumpTypes.transform(chem.ph.
|
|
846
|
+
let type = sys.board.valueMaps.chemPumpTypes.transform(chem.ph.pump.type);
|
|
783
847
|
if (type.remAddress) {
|
|
784
848
|
let dev = await this.checkHardwareStatusAsync(chem.ph.pump.connectionId, chem.ph.pump.deviceBinding);
|
|
785
849
|
schem.alarms.pHPumpFault = dev.hasFault ? 2 : 0;
|
|
@@ -788,7 +852,7 @@ export class NixieChemController extends NixieChemControllerBase {
|
|
|
788
852
|
}
|
|
789
853
|
else schem.alarms.pHPumpFault = 0;
|
|
790
854
|
}
|
|
791
|
-
else schem.alarms.pHPumpFault = schem.alarms.
|
|
855
|
+
else schem.alarms.pHPumpFault = schem.alarms.pHPumpFault = 0;
|
|
792
856
|
if (!chem.isActive) {
|
|
793
857
|
// We need to shut down the pumps.
|
|
794
858
|
}
|
|
@@ -826,7 +890,7 @@ export class NixieChemController extends NixieChemControllerBase {
|
|
|
826
890
|
this.orp.probe.syncRemoteREMFeeds(this.chem, servers);
|
|
827
891
|
}
|
|
828
892
|
}
|
|
829
|
-
class NixieChemical extends NixieChildEquipment {
|
|
893
|
+
class NixieChemical extends NixieChildEquipment implements INixieChemical {
|
|
830
894
|
public chemical: Chemical;
|
|
831
895
|
public pump: NixieChemPump;
|
|
832
896
|
public chlor: NixieChemChlor;
|
|
@@ -858,7 +922,7 @@ class NixieChemical extends NixieChildEquipment {
|
|
|
858
922
|
this.tank = new NixieChemTank(this, chemical.tank);
|
|
859
923
|
logger.info(`Nixie Chemical ${chemical.chemType} object created`);
|
|
860
924
|
}
|
|
861
|
-
public async cancelMixing(schem:
|
|
925
|
+
public async cancelMixing(schem: IChemicalState): Promise<void> {
|
|
862
926
|
try {
|
|
863
927
|
logger.verbose(`Cancelling ${this.chemType} Mix`);
|
|
864
928
|
await this.stopMixing(schem);
|
|
@@ -902,7 +966,7 @@ class NixieChemical extends NixieChildEquipment {
|
|
|
902
966
|
} catch (err) { logger.error(`setMixing: ${err.message}`); return Promise.reject(err); }
|
|
903
967
|
finally { this.suspendPolling = false; }
|
|
904
968
|
}
|
|
905
|
-
protected async stopMixing(schem:
|
|
969
|
+
protected async stopMixing(schem: IChemicalState): Promise<void> {
|
|
906
970
|
try {
|
|
907
971
|
this._stoppingMix = true;
|
|
908
972
|
this.suspendPolling = true;
|
|
@@ -934,7 +998,7 @@ class NixieChemical extends NixieChildEquipment {
|
|
|
934
998
|
} catch (err) { logger.error(`Error stopping chemical mix`); return Promise.reject(err); }
|
|
935
999
|
finally { this._stoppingMix = false; this.suspendPolling = false; }
|
|
936
1000
|
}
|
|
937
|
-
protected async initMixChemicals(schem:
|
|
1001
|
+
protected async initMixChemicals(schem: IChemicalState, mixingTime?: number): Promise<void> {
|
|
938
1002
|
try {
|
|
939
1003
|
if (this._stoppingMix) return;
|
|
940
1004
|
if (typeof this.currentMix === 'undefined') {
|
|
@@ -970,7 +1034,7 @@ class NixieChemical extends NixieChildEquipment {
|
|
|
970
1034
|
}
|
|
971
1035
|
} catch (err) { logger.error(`Error initializing ${schem.chemType} mix: ${err.message}`); }
|
|
972
1036
|
}
|
|
973
|
-
public async mixChemicals(schem:
|
|
1037
|
+
public async mixChemicals(schem: IChemicalState, mixingTime?: number): Promise<void> {
|
|
974
1038
|
try {
|
|
975
1039
|
if (this._stoppingMix) {
|
|
976
1040
|
logger.verbose(`${schem.chemType} is currently stopping mixChemicals ignored.`)
|
|
@@ -985,8 +1049,6 @@ class NixieChemical extends NixieChildEquipment {
|
|
|
985
1049
|
// The chemical is not enabled so we need to ditch the mixing if it is currently underway.
|
|
986
1050
|
await this.stopMixing(schem);
|
|
987
1051
|
return;
|
|
988
|
-
|
|
989
|
-
|
|
990
1052
|
}
|
|
991
1053
|
|
|
992
1054
|
let dt = new Date().getTime();
|
|
@@ -1024,7 +1086,7 @@ class NixieChemical extends NixieChildEquipment {
|
|
|
1024
1086
|
});
|
|
1025
1087
|
}
|
|
1026
1088
|
}
|
|
1027
|
-
public async initDose(schem:
|
|
1089
|
+
public async initDose(schem: IChemicalState) { }
|
|
1028
1090
|
public async closeAsync() {
|
|
1029
1091
|
try {
|
|
1030
1092
|
// We are only killing the mix timer here so when njsPC is restarted it picks up where
|
|
@@ -1035,7 +1097,7 @@ class NixieChemical extends NixieChildEquipment {
|
|
|
1035
1097
|
}
|
|
1036
1098
|
catch (err) { logger.error(`chemController closeAsync ${err.message}`); return Promise.reject(err); }
|
|
1037
1099
|
}
|
|
1038
|
-
public async cancelDosing(schem:
|
|
1100
|
+
public async cancelDosing(schem: IChemicalState, reason: string): Promise<void> {
|
|
1039
1101
|
try {
|
|
1040
1102
|
if (typeof this.chemController.orp.orp.useChlorinator !== 'undefined' && this.chemController.orp.orp.useChlorinator && this.chemController.orp.orp.chlorDosingMethod > 0) {
|
|
1041
1103
|
if (!this.chlor.chlor.superChlor) await this.chlor.stopDosing(schem, reason);
|
|
@@ -1059,7 +1121,7 @@ class NixieChemical extends NixieChildEquipment {
|
|
|
1059
1121
|
}
|
|
1060
1122
|
export class NixieChemTank extends NixieChildEquipment {
|
|
1061
1123
|
public tank: ChemicalTank;
|
|
1062
|
-
constructor(chemical:
|
|
1124
|
+
constructor(chemical: INixieChemical, tank: ChemicalTank) {
|
|
1063
1125
|
super(chemical);
|
|
1064
1126
|
this.tank = tank;
|
|
1065
1127
|
tank.master = 1;
|
|
@@ -1097,8 +1159,8 @@ export class NixieChemPump extends NixieChildEquipment {
|
|
|
1097
1159
|
public _lastOnStatus: number;
|
|
1098
1160
|
protected _dosingTimer: NodeJS.Timeout;
|
|
1099
1161
|
private _isStopping = false;
|
|
1100
|
-
constructor(chemical:
|
|
1101
|
-
public get chemical():
|
|
1162
|
+
constructor(chemical: INixieChemical, pump: ChemicalPump) { super(chemical); this.pump = pump; }
|
|
1163
|
+
public get chemical(): INixieChemical { return this.getParent() as INixieChemical; }
|
|
1102
1164
|
public async setPumpAsync(spump: ChemicalPumpState, data: any): Promise<void> {
|
|
1103
1165
|
try {
|
|
1104
1166
|
if (typeof data !== 'undefined') {
|
|
@@ -1108,11 +1170,17 @@ export class NixieChemPump extends NixieChildEquipment {
|
|
|
1108
1170
|
this.pump.ratedFlow = typeof data.ratedFlow !== 'undefined' ? data.ratedFlow : this.pump.ratedFlow;
|
|
1109
1171
|
this.pump.connectionId = typeof data.connectionId !== 'undefined' ? data.connectionId : this.pump.connectionId;
|
|
1110
1172
|
this.pump.deviceBinding = typeof data.deviceBinding !== 'undefined' ? data.deviceBinding : this.pump.deviceBinding;
|
|
1173
|
+
let type = sys.board.valueMaps.chemPumpTypes.getName(this.pump.type);
|
|
1174
|
+
if (this.pump.type === 'ezo-pmp') {
|
|
1175
|
+
// Now we need to ask for the pump attributes
|
|
1176
|
+
|
|
1177
|
+
|
|
1178
|
+
}
|
|
1111
1179
|
}
|
|
1112
1180
|
} catch (err) { logger.error(`setPumpAsync: ${err.message}`); return Promise.reject(err); }
|
|
1113
1181
|
|
|
1114
1182
|
}
|
|
1115
|
-
public async stopDosing(schem:
|
|
1183
|
+
public async stopDosing(schem: IChemicalState, reason: string): Promise<void> {
|
|
1116
1184
|
try {
|
|
1117
1185
|
logger.debug(`Stopping ${schem.chemType} pump: ${reason}`);
|
|
1118
1186
|
if (this._dosingTimer) {
|
|
@@ -1137,7 +1205,7 @@ export class NixieChemPump extends NixieChildEquipment {
|
|
|
1137
1205
|
} catch (err) { logger.error(`Error stopping ${schem.chemType} dosing: ${err.message}`); return Promise.reject(err); }
|
|
1138
1206
|
finally { this._isStopping = false; }
|
|
1139
1207
|
}
|
|
1140
|
-
public async dose(schem:
|
|
1208
|
+
public async dose(schem: IChemicalState): Promise<void> {
|
|
1141
1209
|
let self = this;
|
|
1142
1210
|
let dose: ChemicalDoseState = schem.currentDose;
|
|
1143
1211
|
try {
|
|
@@ -1155,60 +1223,15 @@ export class NixieChemPump extends NixieChildEquipment {
|
|
|
1155
1223
|
// We aren't going to do anything.
|
|
1156
1224
|
logger.verbose(`Chem pump dose ignore pump ${type}`);
|
|
1157
1225
|
}
|
|
1158
|
-
else if (type === 'relay') {
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
await this.chemical.chemController.processAlarms(schem.chemController);
|
|
1163
|
-
let isBodyOn = schem.chemController.flowDetected;
|
|
1164
|
-
await this.chemical.initDose(schem);
|
|
1165
|
-
let delay = 0;
|
|
1166
|
-
// Check to see if we are in delay. The start delay for the configuration is in minutes.
|
|
1167
|
-
if (isBodyOn && !schem.freezeProtect) {
|
|
1168
|
-
// The remaining delay = delay time - (current time - on time).
|
|
1169
|
-
let timeElapsed = new Date().getTime() - this.chemical.chemController.bodyOnTime;
|
|
1170
|
-
delay = Math.max(0, ((this.chemical.chemical.startDelay * 60) * 1000) - timeElapsed);
|
|
1171
|
-
schem.delayTimeRemaining = Math.max(0, Math.round(delay / 1000));
|
|
1172
|
-
if (delay > 0) {
|
|
1173
|
-
if (!schem.flowDelay) logger.info(`Chem Controller delay dosing for ${utils.formatDuration(delay / 1000)}`)
|
|
1174
|
-
else logger.verbose(`Chem pump delay dosing for ${utils.formatDuration(delay / 1000)}`);
|
|
1175
|
-
schem.flowDelay = true;
|
|
1176
|
-
}
|
|
1177
|
-
else {
|
|
1178
|
-
schem.flowDelay = false;
|
|
1179
|
-
}
|
|
1180
|
-
}
|
|
1181
|
-
// Send a request to latch the relay for 3 seconds. If we don't send another request within 3 seconds of the latch
|
|
1182
|
-
// expiring it will turn the relay back off again. This makes sure we don't leave the pump running on failure.
|
|
1183
|
-
//console.log({ status: dosage.schem.dosingStatus, time: dosage.time, timeDosed: dosage.timeDosed / 1000, volume: dosage.volume, volumeDosed: dosage.volumeDosed });
|
|
1184
|
-
if (!isBodyOn) {
|
|
1185
|
-
// Make sure the pump is off.
|
|
1186
|
-
logger.info(`Chem pump flow not detected. Body is not running.`);
|
|
1187
|
-
// We originally thought that we could wait to turn the dosing on but instead we will cancel the dose. This will allow
|
|
1188
|
-
// the chlorinator to work more smoothly.
|
|
1189
|
-
await this.chemical.cancelDosing(schem, 'no flow');
|
|
1190
|
-
}
|
|
1191
|
-
else if (schem.freezeProtect) {
|
|
1192
|
-
logger.info(`Chem pump freeze protection`);
|
|
1193
|
-
// We originally thought that we could wait to turn the dosing on but instead we will cancel the dose. This will allow
|
|
1194
|
-
// the chlorinator to work more smoothly.
|
|
1195
|
-
await this.chemical.cancelDosing(schem, 'freeze');
|
|
1196
|
-
}
|
|
1197
|
-
else if (schem.tank.level <= 0) {
|
|
1198
|
-
logger.info(`Chem tank ran dry with ${schem.currentDose.volumeRemaining}mL remaining`);
|
|
1199
|
-
await this.chemical.cancelDosing(schem, 'empty tank');
|
|
1200
|
-
}
|
|
1201
|
-
else if (dose.timeRemaining <= 0 || dose.volumeRemaining <= 0) {
|
|
1202
|
-
logger.info(`Dose completed ${dose.volumeDosed}mL ${dose.timeRemaining} ${dose.volumeRemaining}`);
|
|
1203
|
-
await this.chemical.cancelDosing(schem, 'completed');
|
|
1204
|
-
}
|
|
1205
|
-
else if (dose.timeRemaining > 0 && dose.volumeRemaining > 0) { // We are actually dosing here
|
|
1206
|
-
if (delay <= 0) {
|
|
1226
|
+
else if (type === 'relay' || type == 'ezo-pmp') {
|
|
1227
|
+
if (dose.method === 'calibration') {
|
|
1228
|
+
//if (dose.time - (dose._timeDosed / 1000) > 0) {
|
|
1229
|
+
if (dose.timeRemaining > 0) {
|
|
1207
1230
|
logger.verbose(`Sending command to activate chem pump...`);
|
|
1208
1231
|
let res = await this.turnOn(schem, 3000);
|
|
1209
1232
|
if (typeof res.status === 'undefined' || res.status.code !== 200) {
|
|
1210
1233
|
let status = res.status || { code: res.status.code, message: res.status.message };
|
|
1211
|
-
logger.error(`Chem pump could not activate
|
|
1234
|
+
logger.error(`Chem pump could not activate pump ${status.code}: ${status.message}`);
|
|
1212
1235
|
}
|
|
1213
1236
|
let relay = res.obj;
|
|
1214
1237
|
try {
|
|
@@ -1218,32 +1241,109 @@ export class NixieChemPump extends NixieChildEquipment {
|
|
|
1218
1241
|
let time = new Date().getTime() - (dose._lastLatch || new Date().getTime());
|
|
1219
1242
|
// Run our math out to 7 sig figs to keep in the ballpark for very slow pumps.
|
|
1220
1243
|
let vol = Math.round((this.pump.ratedFlow * (time / 1000) / 60) * 1000000) / 1000000;
|
|
1221
|
-
schem.appendDose(
|
|
1244
|
+
schem.appendDose(0, time);
|
|
1222
1245
|
if (schem.tank.units > 0) {
|
|
1223
1246
|
let lvl = schem.tank.level - utils.convert.volume.convertUnits(vol, 'mL', sys.board.valueMaps.volumeUnits.getName(schem.tank.units));
|
|
1224
1247
|
schem.tank.level = Math.max(0, lvl);
|
|
1225
1248
|
}
|
|
1226
1249
|
}
|
|
1227
|
-
logger.info(`Chem Controller
|
|
1250
|
+
logger.info(`Chem Controller calibrating ${dose.chem} ${utils.formatDuration(dose.timeRemaining)} remaining`);
|
|
1228
1251
|
dose._lastLatch = new Date().getTime();
|
|
1229
1252
|
schem.pump.isDosing = this.isOn = relay.state;
|
|
1253
|
+
schem.dosingStatus = 0;
|
|
1230
1254
|
}
|
|
1231
1255
|
else {
|
|
1256
|
+
dose._timeDosed = dose.time * 1000;
|
|
1232
1257
|
await this.turnOff(schem);
|
|
1258
|
+
await this.chemical.cancelDosing(schem, 'complete');
|
|
1233
1259
|
}
|
|
1234
|
-
schem.dosingStatus = 0;
|
|
1235
1260
|
}
|
|
1236
1261
|
else {
|
|
1237
|
-
|
|
1262
|
+
// We are a relay pump so we need to turn on the pump for a timed interval
|
|
1263
|
+
// then check it on each iteration. If the pump does not receive a request
|
|
1264
|
+
// from us then the relay will turn off.
|
|
1265
|
+
await this.chemical.chemController.processAlarms(schem.chemController);
|
|
1266
|
+
let isBodyOn = schem.chemController.flowDetected;
|
|
1267
|
+
await this.chemical.initDose(schem);
|
|
1268
|
+
let delay = 0;
|
|
1269
|
+
// Check to see if we are in delay. The start delay for the configuration is in minutes.
|
|
1270
|
+
if (isBodyOn && !schem.freezeProtect) {
|
|
1271
|
+
// The remaining delay = delay time - (current time - on time).
|
|
1272
|
+
let timeElapsed = new Date().getTime() - this.chemical.chemController.bodyOnTime;
|
|
1273
|
+
delay = Math.max(0, ((this.chemical.chemical.startDelay * 60) * 1000) - timeElapsed);
|
|
1274
|
+
schem.delayTimeRemaining = Math.max(0, Math.round(delay / 1000));
|
|
1275
|
+
if (delay > 0) {
|
|
1276
|
+
if (!schem.flowDelay) logger.info(`Chem Controller delay dosing for ${utils.formatDuration(delay / 1000)}`)
|
|
1277
|
+
else logger.verbose(`Chem pump delay dosing for ${utils.formatDuration(delay / 1000)}`);
|
|
1278
|
+
schem.flowDelay = true;
|
|
1279
|
+
}
|
|
1280
|
+
else {
|
|
1281
|
+
schem.flowDelay = false;
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
// Send a request to latch the relay for 3 seconds. If we don't send another request within 3 seconds of the latch
|
|
1285
|
+
// expiring it will turn the relay back off again. This makes sure we don't leave the pump running on failure.
|
|
1286
|
+
//console.log({ status: dosage.schem.dosingStatus, time: dosage.time, timeDosed: dosage.timeDosed / 1000, volume: dosage.volume, volumeDosed: dosage.volumeDosed });
|
|
1287
|
+
if (!isBodyOn) {
|
|
1288
|
+
// Make sure the pump is off.
|
|
1289
|
+
logger.info(`Chem pump flow not detected. Body is not running.`);
|
|
1290
|
+
// We originally thought that we could wait to turn the dosing on but instead we will cancel the dose. This will allow
|
|
1291
|
+
// the chlorinator to work more smoothly.
|
|
1292
|
+
await this.chemical.cancelDosing(schem, 'no flow');
|
|
1293
|
+
}
|
|
1294
|
+
else if (schem.freezeProtect) {
|
|
1295
|
+
logger.info(`Chem pump freeze protection`);
|
|
1296
|
+
// We originally thought that we could wait to turn the dosing on but instead we will cancel the dose. This will allow
|
|
1297
|
+
// the chlorinator to work more smoothly.
|
|
1298
|
+
await this.chemical.cancelDosing(schem, 'freeze');
|
|
1299
|
+
}
|
|
1300
|
+
else if (schem.tank.level <= 0) {
|
|
1301
|
+
logger.info(`Chem tank ran dry with ${schem.currentDose.volumeRemaining}mL remaining`);
|
|
1302
|
+
await this.chemical.cancelDosing(schem, 'empty tank');
|
|
1303
|
+
}
|
|
1304
|
+
else if (dose.timeRemaining <= 0 || dose.volumeRemaining <= 0) {
|
|
1305
|
+
logger.info(`Dose completed ${dose.volumeDosed}mL ${dose.timeRemaining} ${dose.volumeRemaining}`);
|
|
1306
|
+
await this.chemical.cancelDosing(schem, 'completed');
|
|
1307
|
+
}
|
|
1308
|
+
else if (dose.timeRemaining > 0 && dose.volumeRemaining > 0) { // We are actually dosing here
|
|
1309
|
+
if (delay <= 0) {
|
|
1310
|
+
logger.verbose(`Sending command to activate chem pump...`);
|
|
1311
|
+
let res = await this.turnOn(schem, 3000);
|
|
1312
|
+
if (typeof res.status === 'undefined' || res.status.code !== 200) {
|
|
1313
|
+
let status = res.status || { code: res.status.code, message: res.status.message };
|
|
1314
|
+
logger.error(`Chem pump could not activate relay ${status.code}: ${status.message}`);
|
|
1315
|
+
}
|
|
1316
|
+
let relay = res.obj;
|
|
1317
|
+
try {
|
|
1318
|
+
logger.verbose(`Chem pump response ${JSON.stringify(relay)}`);
|
|
1319
|
+
} catch (err) { logger.error(`Invalid chem pump response`); }
|
|
1320
|
+
if (typeof dose._lastLatch !== 'undefined') {
|
|
1321
|
+
let time = new Date().getTime() - (dose._lastLatch || new Date().getTime());
|
|
1322
|
+
// Run our math out to 7 sig figs to keep in the ballpark for very slow pumps.
|
|
1323
|
+
let vol = Math.round((this.pump.ratedFlow * (time / 1000) / 60) * 1000000) / 1000000;
|
|
1324
|
+
schem.appendDose(vol, time);
|
|
1325
|
+
if (schem.tank.units > 0) {
|
|
1326
|
+
let lvl = schem.tank.level - utils.convert.volume.convertUnits(vol, 'mL', sys.board.valueMaps.volumeUnits.getName(schem.tank.units));
|
|
1327
|
+
schem.tank.level = Math.max(0, lvl);
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
logger.info(`Chem Controller dosed ${dose.chem} ${dose.volumeDosed.toFixed(2)}mL of ${dose.volume}mL ${utils.formatDuration(dose.timeRemaining)} remaining`);
|
|
1331
|
+
dose._lastLatch = new Date().getTime();
|
|
1332
|
+
schem.pump.isDosing = this.isOn = relay.state;
|
|
1333
|
+
}
|
|
1334
|
+
else {
|
|
1335
|
+
await this.turnOff(schem);
|
|
1336
|
+
}
|
|
1337
|
+
schem.dosingStatus = 0;
|
|
1338
|
+
}
|
|
1339
|
+
else {
|
|
1340
|
+
await this.chemical.cancelDosing(schem, 'unknown cancel');
|
|
1341
|
+
}
|
|
1238
1342
|
}
|
|
1239
1343
|
}
|
|
1240
|
-
else if (type === 'ezo-pmp') {
|
|
1241
|
-
logger.info(`Attempting to dose ezo pump`);
|
|
1242
|
-
await NixieEquipment.putDeviceService(this.pump.connectionId, `/state/device/${this.pump.deviceBinding}`, { state: true, latch: 5000 });
|
|
1243
|
-
}
|
|
1244
1344
|
// Check to see if we reached our max dosing time or volume or the tank is empty mix it up.
|
|
1245
1345
|
let status = schem.dosingStatus;
|
|
1246
|
-
if (status === 0) {
|
|
1346
|
+
if (status === 0 && dose.method !== 'calibration') {
|
|
1247
1347
|
let chem = this.chemical.chemical;
|
|
1248
1348
|
if (chem.dosingMethod === 1 && chem.maxDosingTime < (dose._timeDosed / 1000))
|
|
1249
1349
|
await this.chemical.cancelDosing(schem, 'completed');
|
|
@@ -1266,7 +1366,9 @@ export class NixieChemPump extends NixieChildEquipment {
|
|
|
1266
1366
|
// Add a check to tell the chem when we are done.
|
|
1267
1367
|
if (schem.dosingStatus === 0) {
|
|
1268
1368
|
this._dosingTimer = setTimeout(async () => {
|
|
1269
|
-
try {
|
|
1369
|
+
try {
|
|
1370
|
+
await self.dose(schem);
|
|
1371
|
+
}
|
|
1270
1372
|
catch (err) {
|
|
1271
1373
|
logger.error(`self.dose error in finally:`);
|
|
1272
1374
|
logger.error(err);
|
|
@@ -1290,7 +1392,7 @@ export class NixieChemPump extends NixieChildEquipment {
|
|
|
1290
1392
|
}
|
|
1291
1393
|
}
|
|
1292
1394
|
}
|
|
1293
|
-
public async turnOff(schem:
|
|
1395
|
+
public async turnOff(schem: IChemicalState): Promise<InterfaceServerResponse> {
|
|
1294
1396
|
try {
|
|
1295
1397
|
// We need to be turning this pig off. If the REM service has been interrupted
|
|
1296
1398
|
// then we will assume that the relay is off since any request to turn it on will be based upon
|
|
@@ -1302,7 +1404,7 @@ export class NixieChemPump extends NixieChildEquipment {
|
|
|
1302
1404
|
}
|
|
1303
1405
|
catch (err) { logger.error(`chemController.pump.turnOff: ${err.message}`); return Promise.reject(err); }
|
|
1304
1406
|
}
|
|
1305
|
-
public async turnOn(schem:
|
|
1407
|
+
public async turnOn(schem: IChemicalState, latchTimeout?: number): Promise<InterfaceServerResponse> {
|
|
1306
1408
|
try {
|
|
1307
1409
|
let res = await NixieEquipment.putDeviceService(this.pump.connectionId, `/state/device/${this.pump.deviceBinding}`, typeof latchTimeout !== 'undefined' ? { isOn: true, latch: latchTimeout } : { isOn: true });
|
|
1308
1410
|
this.isOn = schem.pump.isDosing = true;
|
|
@@ -1331,7 +1433,7 @@ export class NixieChemChlor extends NixieChildEquipment {
|
|
|
1331
1433
|
}
|
|
1332
1434
|
} catch (err) { logger.error(`setChlorAsync: ${err.message}`); return Promise.reject(err); }
|
|
1333
1435
|
}
|
|
1334
|
-
public async stopDosing(schem:
|
|
1436
|
+
public async stopDosing(schem: IChemicalState, reason: string): Promise<void> {
|
|
1335
1437
|
try {
|
|
1336
1438
|
if (this._dosingTimer) {
|
|
1337
1439
|
clearTimeout(this._dosingTimer);
|
|
@@ -1451,7 +1553,7 @@ export class NixieChemChlor extends NixieChildEquipment {
|
|
|
1451
1553
|
}
|
|
1452
1554
|
}
|
|
1453
1555
|
}
|
|
1454
|
-
public async turnOff(schem:
|
|
1556
|
+
public async turnOff(schem: IChemicalState): Promise<ChlorinatorState> {
|
|
1455
1557
|
try {
|
|
1456
1558
|
//logger.info(`Turning off the chlorinator`);
|
|
1457
1559
|
let chlor = sys.chlorinators.getItemById(1);
|
|
@@ -1634,7 +1736,10 @@ export class NixieChemicalPh extends NixieChemical {
|
|
|
1634
1736
|
await this.stopMixing(sph);
|
|
1635
1737
|
await this.pump.dose(sph);
|
|
1636
1738
|
}
|
|
1637
|
-
else
|
|
1739
|
+
else {
|
|
1740
|
+
if (typeof sph.currentDose !== 'undefined' && sph.currentDose.method === 'calibration') { }
|
|
1741
|
+
else await this.cancelDosing(sph, 'empty tank');
|
|
1742
|
+
}
|
|
1638
1743
|
}
|
|
1639
1744
|
else if (sph.dailyLimitReached) {
|
|
1640
1745
|
await this.cancelDosing(sph, 'daily limit');
|
|
@@ -1736,7 +1841,38 @@ export class NixieChemicalPh extends NixieChemical {
|
|
|
1736
1841
|
if (typeof sph.currentDose !== 'undefined') sph.endDose(new Date(), 'cancelled');
|
|
1737
1842
|
} catch (err) { logger.error(`cancelDosing pH: ${err.message}`); return Promise.reject(err); }
|
|
1738
1843
|
}
|
|
1739
|
-
public async
|
|
1844
|
+
public async calibrateDoseAsync(sph: ChemicalPhState, time: number) {
|
|
1845
|
+
try {
|
|
1846
|
+
logger.debug(`Starting manual ${sph.chemType} dose for ${time}seconds`);
|
|
1847
|
+
let status = sys.board.valueMaps.chemControllerDosingStatus.getName(sph.dosingStatus);
|
|
1848
|
+
if (status === 'monitoring') {
|
|
1849
|
+
// Alright our mixing and dosing have either been cancelled or we fininsed a mixing cycle. Either way
|
|
1850
|
+
// let the system clean these up.
|
|
1851
|
+
if (typeof sph.currentDose !== 'undefined') await this.cancelDosing(sph, 'manual cancel');
|
|
1852
|
+
if (typeof this.currentMix !== 'undefined') await this.stopMixing(sph);
|
|
1853
|
+
}
|
|
1854
|
+
if (status === 'mixing') {
|
|
1855
|
+
// We are mixing so we need to stop that.
|
|
1856
|
+
await this.stopMixing(sph);
|
|
1857
|
+
}
|
|
1858
|
+
else if (status === 'dosing') {
|
|
1859
|
+
// We are dosing so we need to stop that.
|
|
1860
|
+
await this.cancelDosing(sph, 'manual cancel');
|
|
1861
|
+
}
|
|
1862
|
+
//if (sph.tank.level <= 0) return Promise.reject(new InvalidEquipmentDataError(`The ${sph.chemType} tank is empty`, 'chemical', sph));
|
|
1863
|
+
let pump = this.pump.pump;
|
|
1864
|
+
let volume = typeof pump.ratedFlow === 'undefined' || pump.ratedFlow <= 0 ? 0 : time * (pump.ratedFlow / 60);
|
|
1865
|
+
// We should now be monitoring.
|
|
1866
|
+
logger.verbose(`Chem begin calculating manual dose current: ${sph.level} setpoint: ${this.ph.setpoint} time:${time} seconds`);
|
|
1867
|
+
sph.demand = sph.calcDemand(this.chemController.chem);
|
|
1868
|
+
sph.manualDosing = true;
|
|
1869
|
+
sph.startDose(new Date(), 'calibration', -1, 0, time);
|
|
1870
|
+
logger.verbose(`Chem acid manual calibration dose activate pump`);
|
|
1871
|
+
await this.pump.dose(sph);
|
|
1872
|
+
}
|
|
1873
|
+
catch (err) { logger.error(`calibrateDoseAsync: ${err.message}`); logger.error(err); return Promise.reject(err); }
|
|
1874
|
+
}
|
|
1875
|
+
public async manualDoseVolumeAsync(sph: ChemicalPhState, volume: number) {
|
|
1740
1876
|
try {
|
|
1741
1877
|
logger.debug(`Starting manual ${sph.chemType} dose of ${volume}mL`);
|
|
1742
1878
|
let status = sys.board.valueMaps.chemControllerDosingStatus.getName(sph.dosingStatus);
|
|
@@ -1767,7 +1903,7 @@ export class NixieChemicalPh extends NixieChemical {
|
|
|
1767
1903
|
await this.pump.dose(sph);
|
|
1768
1904
|
}
|
|
1769
1905
|
}
|
|
1770
|
-
catch (err) { logger.error(`
|
|
1906
|
+
catch (err) { logger.error(`manualDoseVolumeAsync: ${err.message}`); logger.error(err); return Promise.reject(err); }
|
|
1771
1907
|
}
|
|
1772
1908
|
public async initDose(sph: ChemicalPhState) {
|
|
1773
1909
|
try {
|
|
@@ -1858,7 +1994,7 @@ export class NixieChemicalORP extends NixieChemical {
|
|
|
1858
1994
|
}
|
|
1859
1995
|
catch (err) { logger.error(`setORPAsync: ${err.message}`); return Promise.reject(err); }
|
|
1860
1996
|
}
|
|
1861
|
-
public async
|
|
1997
|
+
public async manualDoseVolumeAsync(sorp: ChemicalORPState, volume: number) {
|
|
1862
1998
|
try {
|
|
1863
1999
|
let status = sys.board.valueMaps.chemControllerDosingStatus.getName(sorp.dosingStatus);
|
|
1864
2000
|
if (status === 'monitoring') {
|
|
@@ -1888,8 +2024,40 @@ export class NixieChemicalORP extends NixieChemical {
|
|
|
1888
2024
|
await this.pump.dose(sorp);
|
|
1889
2025
|
}
|
|
1890
2026
|
}
|
|
1891
|
-
catch (err) { logger.error(`
|
|
2027
|
+
catch (err) { logger.error(`manualDoseVolumeAsync ORP: ${err.message}`); logger.error(err); return Promise.reject(err); }
|
|
1892
2028
|
}
|
|
2029
|
+
public async calibrateDoseAsync(sorp: ChemicalORPState, time: number) {
|
|
2030
|
+
try {
|
|
2031
|
+
logger.debug(`Starting manual ${sorp.chemType} dose for ${time}seconds`);
|
|
2032
|
+
let status = sys.board.valueMaps.chemControllerDosingStatus.getName(sorp.dosingStatus);
|
|
2033
|
+
if (status === 'monitoring') {
|
|
2034
|
+
// Alright our mixing and dosing have either been cancelled or we fininsed a mixing cycle. Either way
|
|
2035
|
+
// let the system clean these up.
|
|
2036
|
+
if (typeof sorp.currentDose !== 'undefined') await this.cancelDosing(sorp, 'manual cancel');
|
|
2037
|
+
if (typeof this.currentMix !== 'undefined') await this.stopMixing(sorp);
|
|
2038
|
+
}
|
|
2039
|
+
if (status === 'mixing') {
|
|
2040
|
+
// We are mixing so we need to stop that.
|
|
2041
|
+
await this.stopMixing(sorp);
|
|
2042
|
+
}
|
|
2043
|
+
else if (status === 'dosing') {
|
|
2044
|
+
// We are dosing so we need to stop that.
|
|
2045
|
+
await this.cancelDosing(sorp, 'manual cancel');
|
|
2046
|
+
}
|
|
2047
|
+
//if (sorp.tank.level <= 0) return Promise.reject(new InvalidEquipmentDataError(`The ${sorp.chemType} tank is empty`, 'chemical', sorp));
|
|
2048
|
+
let pump = this.pump.pump;
|
|
2049
|
+
let volume = typeof pump.ratedFlow === 'undefined' || pump.ratedFlow <= 0 ? 0 : time * (pump.ratedFlow / 60);
|
|
2050
|
+
// We should now be monitoring.
|
|
2051
|
+
logger.verbose(`Chem begin calculating manual dose current: ${sorp.level} setpoint: ${this.orp.setpoint} time:${time} seconds`);
|
|
2052
|
+
sorp.demand = sorp.calcDemand(this.chemController.chem);
|
|
2053
|
+
sorp.manualDosing = true;
|
|
2054
|
+
sorp.startDose(new Date(), 'calibration', -1, 0, time);
|
|
2055
|
+
logger.verbose(`Chem acid manual dose activate pump ${this.pump.pump.ratedFlow}mL/min`);
|
|
2056
|
+
await this.pump.dose(sorp);
|
|
2057
|
+
}
|
|
2058
|
+
catch (err) { logger.error(`calibrateDoseAsync: ${err.message}`); logger.error(err); return Promise.reject(err); }
|
|
2059
|
+
}
|
|
2060
|
+
|
|
1893
2061
|
public async cancelDosing(sorp: ChemicalORPState, reason: string): Promise<void> {
|
|
1894
2062
|
try {
|
|
1895
2063
|
if (typeof sorp.useChlorinator !== 'undefined' && sorp.useChlorinator && this.chemController.orp.orp.chlorDosingMethod > 0) {
|
|
@@ -2029,7 +2197,10 @@ export class NixieChemicalORP extends NixieChemical {
|
|
|
2029
2197
|
await this.stopMixing(sorp);
|
|
2030
2198
|
await this.pump.dose(sorp);
|
|
2031
2199
|
}
|
|
2032
|
-
else
|
|
2200
|
+
else {
|
|
2201
|
+
if (typeof sorp.currentDose !== 'undefined' && sorp.currentDose.method === 'calibration') { }
|
|
2202
|
+
else await this.cancelDosing(sorp, 'empty tank');
|
|
2203
|
+
}
|
|
2033
2204
|
}
|
|
2034
2205
|
else if (sorp.freezeProtect) {
|
|
2035
2206
|
await this.cancelDosing(sorp, 'freeze');
|
|
@@ -2481,7 +2652,7 @@ export class NixieChemProbeORP extends NixieChemProbe {
|
|
|
2481
2652
|
}
|
|
2482
2653
|
export class NixieChemFlowSensor extends NixieChildEquipment {
|
|
2483
2654
|
public sensor: ChemFlowSensor;
|
|
2484
|
-
constructor(parent:
|
|
2655
|
+
constructor(parent: NixieEquipment, sensor: ChemFlowSensor) {
|
|
2485
2656
|
super(parent);
|
|
2486
2657
|
this.sensor = sensor;
|
|
2487
2658
|
sensor.master = 1;
|