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.
Files changed (82) hide show
  1. package/.eslintrc.json +26 -35
  2. package/Changelog +22 -0
  3. package/README.md +7 -3
  4. package/anslq25/MessagesMock.ts +218 -0
  5. package/anslq25/boards/MockBoardFactory.ts +50 -0
  6. package/anslq25/boards/MockEasyTouchBoard.ts +696 -0
  7. package/anslq25/boards/MockSystemBoard.ts +217 -0
  8. package/anslq25/chemistry/MockChlorinator.ts +75 -0
  9. package/anslq25/pumps/MockPump.ts +84 -0
  10. package/app.ts +10 -14
  11. package/config/Config.ts +13 -9
  12. package/config/VersionCheck.ts +6 -2
  13. package/controller/Constants.ts +58 -25
  14. package/controller/Equipment.ts +225 -41
  15. package/controller/Errors.ts +2 -1
  16. package/controller/Lockouts.ts +34 -2
  17. package/controller/State.ts +491 -48
  18. package/controller/boards/AquaLinkBoard.ts +6 -3
  19. package/controller/boards/BoardFactory.ts +5 -1
  20. package/controller/boards/EasyTouchBoard.ts +1971 -1751
  21. package/controller/boards/IntelliCenterBoard.ts +1311 -1688
  22. package/controller/boards/IntelliComBoard.ts +7 -1
  23. package/controller/boards/IntelliTouchBoard.ts +153 -42
  24. package/controller/boards/NixieBoard.ts +209 -66
  25. package/controller/boards/SunTouchBoard.ts +393 -0
  26. package/controller/boards/SystemBoard.ts +1862 -1543
  27. package/controller/comms/Comms.ts +539 -138
  28. package/controller/comms/ScreenLogic.ts +1663 -0
  29. package/controller/comms/messages/Messages.ts +242 -60
  30. package/controller/comms/messages/config/ChlorinatorMessage.ts +4 -3
  31. package/controller/comms/messages/config/CircuitGroupMessage.ts +5 -2
  32. package/controller/comms/messages/config/CircuitMessage.ts +81 -13
  33. package/controller/comms/messages/config/ConfigMessage.ts +3 -1
  34. package/controller/comms/messages/config/CoverMessage.ts +2 -1
  35. package/controller/comms/messages/config/CustomNameMessage.ts +2 -1
  36. package/controller/comms/messages/config/EquipmentMessage.ts +5 -1
  37. package/controller/comms/messages/config/ExternalMessage.ts +33 -3
  38. package/controller/comms/messages/config/FeatureMessage.ts +2 -1
  39. package/controller/comms/messages/config/GeneralMessage.ts +2 -1
  40. package/controller/comms/messages/config/HeaterMessage.ts +3 -1
  41. package/controller/comms/messages/config/IntellichemMessage.ts +2 -1
  42. package/controller/comms/messages/config/OptionsMessage.ts +12 -6
  43. package/controller/comms/messages/config/PumpMessage.ts +9 -12
  44. package/controller/comms/messages/config/RemoteMessage.ts +80 -13
  45. package/controller/comms/messages/config/ScheduleMessage.ts +43 -3
  46. package/controller/comms/messages/config/SecurityMessage.ts +2 -1
  47. package/controller/comms/messages/config/ValveMessage.ts +43 -26
  48. package/controller/comms/messages/status/ChlorinatorStateMessage.ts +8 -7
  49. package/controller/comms/messages/status/EquipmentStateMessage.ts +93 -20
  50. package/controller/comms/messages/status/HeaterStateMessage.ts +24 -5
  51. package/controller/comms/messages/status/IntelliChemStateMessage.ts +7 -4
  52. package/controller/comms/messages/status/IntelliValveStateMessage.ts +2 -1
  53. package/controller/comms/messages/status/PumpStateMessage.ts +72 -4
  54. package/controller/comms/messages/status/VersionMessage.ts +2 -1
  55. package/controller/nixie/Nixie.ts +15 -4
  56. package/controller/nixie/NixieEquipment.ts +1 -0
  57. package/controller/nixie/chemistry/ChemController.ts +300 -129
  58. package/controller/nixie/chemistry/ChemDoser.ts +806 -0
  59. package/controller/nixie/chemistry/Chlorinator.ts +133 -129
  60. package/controller/nixie/circuits/Circuit.ts +171 -30
  61. package/controller/nixie/heaters/Heater.ts +337 -173
  62. package/controller/nixie/pumps/Pump.ts +264 -236
  63. package/controller/nixie/schedules/Schedule.ts +9 -3
  64. package/defaultConfig.json +46 -5
  65. package/logger/Logger.ts +38 -9
  66. package/package.json +13 -9
  67. package/web/Server.ts +235 -122
  68. package/web/bindings/aqualinkD.json +114 -59
  69. package/web/bindings/homeassistant.json +437 -0
  70. package/web/bindings/influxDB.json +15 -0
  71. package/web/bindings/mqtt.json +28 -9
  72. package/web/bindings/mqttAlt.json +15 -0
  73. package/web/interfaces/baseInterface.ts +58 -7
  74. package/web/interfaces/httpInterface.ts +5 -2
  75. package/web/interfaces/influxInterface.ts +9 -2
  76. package/web/interfaces/mqttInterface.ts +234 -74
  77. package/web/interfaces/ruleInterface.ts +87 -0
  78. package/web/services/config/Config.ts +140 -33
  79. package/web/services/config/ConfigSocket.ts +2 -1
  80. package/web/services/state/State.ts +144 -3
  81. package/web/services/state/StateSocket.ts +65 -14
  82. 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
- conn.queueSendMessage(out);
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
- conn.queueSendMessage(out);
378
- });
379
- return success;
380
- } catch (err) { logger.error(`Communication error with IntelliChem : ${err.message}`); }
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') await this.ph.manualDoseAsync(schem, vol);
437
- else if (chemType === 'orp') await this.orp.manualDoseAsync(schem, vol);
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
- await this.processAlarms(schem);
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 (this.chem.ph.enabled) await this.ph.checkDosing(this.chem, schem.ph);
612
- if (this.chem.orp.enabled) await this.orp.checkDosing(this.chem, schem.orp);
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
- schem.alarms.orp = 0;
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
- schem.alarms.pH = 0;
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.probe.type);
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.pHProbeFault = 0;
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: ChemicalState): Promise<void> {
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: ChemicalState): Promise<void> {
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: ChemicalState, mixingTime?: number): Promise<void> {
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: ChemicalState, mixingTime?: number): Promise<void> {
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: ChemicalState) { }
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: ChemicalState, reason: string): Promise<void> {
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: NixieChemical, tank: ChemicalTank) {
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: NixieChemical, pump: ChemicalPump) { super(chemical); this.pump = pump; }
1101
- public get chemical(): NixieChemical { return this.getParent() as NixieChemical; }
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: ChemicalState, reason: string): Promise<void> {
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: ChemicalState): Promise<void> {
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
- // We are a relay pump so we need to turn on the pump for a timed interval
1160
- // then check it on each iteration. If the pump does not receive a request
1161
- // from us then the relay will turn off.
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 relay ${status.code}: ${status.message}`);
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(vol, time);
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 dosed ${dose.chem} ${dose.volumeDosed.toFixed(2)}mL of ${dose.volume}mL ${utils.formatDuration(dose.timeRemaining)} remaining`);
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
- await this.chemical.cancelDosing(schem, 'unknown cancel');
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 { await self.dose(schem); }
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: ChemicalState): Promise<InterfaceServerResponse> {
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: ChemicalState, latchTimeout?: number): Promise<InterfaceServerResponse> {
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: ChemicalState, reason: string): Promise<void> {
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: ChemicalState): Promise<ChlorinatorState> {
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 await this.cancelDosing(sph, 'empty tank');
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 manualDoseAsync(sph: ChemicalPhState, volume: number) {
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(`manualDoseAsync: ${err.message}`); logger.error(err); return Promise.reject(err); }
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 manualDoseAsync(sorp: ChemicalORPState, volume: number) {
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(`manualDoseAsync ORP: ${err.message}`); logger.error(err); return Promise.reject(err); }
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 await this.cancelDosing(sorp, 'empty tank');
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: NixieChemController, sensor: ChemFlowSensor) {
2655
+ constructor(parent: NixieEquipment, sensor: ChemFlowSensor) {
2485
2656
  super(parent);
2486
2657
  this.sensor = sensor;
2487
2658
  sensor.master = 1;