Skip to content

Commit

Permalink
Fixed LSI calc formula for IntelliChem. Added defaults for ph and orp…
Browse files Browse the repository at this point in the history
… objects when initializing IntelliChem.
  • Loading branch information
rstrouse committed Mar 23, 2021
1 parent 58e494a commit 269caca
Show file tree
Hide file tree
Showing 8 changed files with 56 additions and 122 deletions.
5 changes: 5 additions & 0 deletions controller/Equipment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ export class PoolSystem implements IPoolSystem {
}
return cfg;
}

public resetSystem() {
conn.pause();
this.resetData();
Expand Down Expand Up @@ -143,6 +144,7 @@ export class PoolSystem implements IPoolSystem {
public resetData() {
if (sys.controllerType !== ControllerType.Virtual && sys.controllerType !== ControllerType.Nixie) {
// Do not clear this out if it is a virtual controller this causes problems.
this.equipment.reset();
this.circuitGroups.clear(0);
this.lightGroups.clear(0);
this.circuits.clear(0);
Expand Down Expand Up @@ -759,6 +761,9 @@ export class Equipment extends EqItem {
public get controllerFirmware(): string { return this.data.softwareVersion; }
public set bootloaderVersion(val: string) { this.setDataVal('bootloaderVersion', val); }
public get bootloaderVersion(): string { return this.data.bootloaderVersion; }
public reset() {

}
setEquipmentIds() {
this.data.equipmentIds = {
circuits: { start: sys.board.equipmentIds.circuits.start, end: sys.board.equipmentIds.circuits.end },
Expand Down
2 changes: 2 additions & 0 deletions controller/State.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1425,6 +1425,7 @@ export class ChemControllerStateCollection extends EqStateCollection<ChemControl

export class ChemControllerState extends EqState {
public initData() {
if (typeof this.data.saturationIndex === 'undefined') this.data.saturationIndex = 0;
if (typeof this.data.flowDetected === 'undefined') this.data.flowDetected = false;
if (typeof this.data.orp === 'undefined') this.data.orp = {};
if (typeof this.data.ph === 'undefined') this.data.ph = {};
Expand Down Expand Up @@ -1574,6 +1575,7 @@ export class ChemControllerState extends EqState {
}
export class ChemicalState extends ChildEqState {
public initData() {
if (typeof this.data.probe === 'undefined') this.data.probe = {};
if (typeof this.data.tank == 'undefined') this.data.tank = { capacity: 0, level: 0, units: 0 };
if (typeof this.data.dosingTimeRemaining === 'undefined') this.data.dosingTimeRemaining = 0;
if (typeof this.data.delayTimeRemaining === 'undefined') this.data.delayTimeRemaining = 0;
Expand Down
3 changes: 3 additions & 0 deletions controller/boards/BoardFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { EasyTouchBoard } from './EasyTouchBoard';
import { SystemBoard } from './SystemBoard';
import { ControllerType } from '../Constants';
import { PoolSystem } from '../Equipment';
import { NixieBoard } from './NixieBoard';

export class BoardFactory {
// Factory create the system board from the controller type. Resist storing
Expand All @@ -35,6 +36,8 @@ export class BoardFactory {
return new IntelliComBoard(system);
case ControllerType.EasyTouch:
return new EasyTouchBoard(system);
case ControllerType.Nixie:
return new NixieBoard(system);
}
return new SystemBoard(system);
}
Expand Down
2 changes: 1 addition & 1 deletion controller/boards/NixieBoard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ export class NixieBoard extends SystemBoard {
//[0, { name: 'nxp', part: 'NXP', desc: 'Nixie Single Body', bodies: 1, valves: 2, shared: false, dual: false }],
//[1, { name: 'nxps', part: 'NXPS', desc: 'Nixie Shared Body', bodies: 2, valves: 4, shared: true, dual: false, chlorinators: 1, chemControllers: 1 }],
//[2, { name: 'nxpd', part: 'NXPD', desc: 'Nixe Dual Body', bodies: 2, valves: 2, shared: false, dual: true, chlorinators: 2, chemControllers: 2 }],
//[255, { name: 'nxu', part: 'Unspecified', desc: 'Unspecified Nixie Controller', bodies: 0, valves: 0, shared: false, dual: false, chlorinators: 0, chemControllers: 0 }]
//[255, { name: 'nxu', part: 'Unspecified', desc: 'Nixie No Body', bodies: 0, valves: 0, shared: false, dual: false, chlorinators: 0, chemControllers: 0 }]
let type = typeof mod.type !== 'undefined' ? this.valueMaps.expansionBoards.get(mod.type) : this.valueMaps.expansionBoards.get(255);
sys.equipment.shared = type.shared;
sys.equipment.dual = type.dual;
Expand Down
31 changes: 17 additions & 14 deletions controller/boards/SystemBoard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2824,18 +2824,20 @@ export class ValveCommands extends BoardCommands {
}
export class ChemControllerCommands extends BoardCommands {
public async deleteChemControllerAsync(data: any): Promise<ChemController> {
let id = typeof data.id !== 'undefined' ? parseInt(data.id, 10) : -1;
if (typeof id === 'undefined' || isNaN(id)) return Promise.reject(new InvalidEquipmentIdError(`Invalid Chem Controller Id`, id, 'chemController'));
let chem = sys.chemControllers.getItemById(id);
if (chem.type === sys.board.valueMaps.chemControllerTypes.getValue('intellichem') && !chem.isVirtual)
sys.board.virtualChemControllers.stop();
let schem = state.chemControllers.getItemById(chem.id);
schem.isActive = chem.isActive = false;
await ncp.chemControllers.removeById(chem.id);
sys.chemControllers.removeItemById(chem.id);
state.chemControllers.removeItemById(chem.id);
sys.emitEquipmentChange();
return Promise.resolve(chem);
try {
let id = typeof data.id !== 'undefined' ? parseInt(data.id, 10) : -1;
if (typeof id === 'undefined' || isNaN(id)) return Promise.reject(new InvalidEquipmentIdError(`Invalid Chem Controller Id`, id, 'chemController'));
let chem = sys.chemControllers.getItemById(id);
if (chem.type === sys.board.valueMaps.chemControllerTypes.getValue('intellichem') && !chem.isVirtual)
sys.board.virtualChemControllers.stop();
let schem = state.chemControllers.getItemById(id);
schem.isActive = chem.isActive = false;
await ncp.chemControllers.removeById(id);
sys.chemControllers.removeItemById(id);
state.chemControllers.removeItemById(id);
sys.emitEquipmentChange();
return Promise.resolve(chem);
} catch (err) { logger.error(`Error deleting chem controller ${err.message}`); }
}
public async manualDoseAsync(data: any): Promise<ChemControllerState> {
try {
Expand Down Expand Up @@ -3409,10 +3411,11 @@ export class ChemControllerCommands extends BoardCommands {
try {
let chem = sys.chemControllers.find(elem => elem.id === data.id);
if (typeof chem === 'undefined') return Promise.reject(`A valid IntelliChem controller could not be found at id ${data.id}`);
chem = sys.chemControllers.getItemById(chem.id, true); // Make sure we persist it.
let address = typeof data.address !== 'undefined' ? parseInt(data.address, 10) : chem.address;
if (typeof address === 'undefined' || isNaN(address) || (address < 144 || address > 158)) return Promise.reject(new InvalidEquipmentDataError(`Invalid IntelliChem address`, 'chemController', address));
let pHSetpoint = typeof data.ph.setpoint !== 'undefined' ? parseFloat(data.ph.setpoint) : chem.ph.setpoint;
let orpSetpoint = typeof data.orp.setpoint !== 'undefined' ? parseInt(data.orp.setpoint, 10) : chem.orp.setpoint;
let pHSetpoint = typeof data.ph !== 'undefined' && typeof data.ph.setpoint !== 'undefined' ? parseFloat(data.ph.setpoint) : chem.ph.setpoint;
let orpSetpoint = typeof data.orp !== 'undefined' && typeof data.orp.setpoint !== 'undefined' ? parseInt(data.orp.setpoint, 10) : chem.orp.setpoint;
let calciumHardness = typeof data.calciumHardness !== 'undefined' ? parseInt(data.calciumHardness, 10) : chem.calciumHardness;
let cyanuricAcid = typeof data.cyanuricAcid !== 'undefined' ? parseInt(data.cyanuricAcid, 10) : chem.cyanuricAcid;
let alkalinity = typeof data.alkalinity !== 'undefined' ? parseInt(data.alkalinity, 10) : chem.alkalinity;
Expand Down
86 changes: 3 additions & 83 deletions controller/comms/Comms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export class Connection {
}
public isRTS: boolean=true;
public emitter: EventEmitter;
public get enabled(): boolean { return typeof this._cfg !== 'undefined' && this._cfg.enabled; }
public async openAsync(): Promise<boolean> {
if (typeof (this.buffer) === 'undefined') {
this.buffer = new SendRecieveBuffer();
Expand Down Expand Up @@ -149,87 +150,6 @@ export class Connection {
});
}
}
//public open(timeOut?: string) {
// if (conn._cfg.netConnect && !conn._cfg.mockPort) {
// if (typeof conn._port !== 'undefined' !&& conn._port.destroyed) conn._port.destroy();
// let nc = conn._port = new net.Socket();
// nc.connect(conn._cfg.netPort, conn._cfg.netHost, function () {
// if (timeOut === 'retry_timeout' || timeOut === 'timeout')
// logger.warn('Net connect (socat) trying to recover from lost connection.');
// });
// nc.on('data', function (data) {
// conn.isOpen = true;
// if (timeOut === 'retry_timeout' || timeOut === 'timeout') {
// logger.info(`Net connect (socat) connected to: ${conn._cfg.netHost}:${conn._cfg.netPort}`);
// timeOut = undefined;
// }
// if (data.length > 0 && !conn.isPaused) conn.emitter.emit('packetread', data);
// conn.resetConnTimer('timeout');
// });
// }
// else {
// var sp: SerialPort = null;
// if (conn._cfg.mockPort) {
// this.mockPort = true;
// SerialPort.Binding = MockBinding;
// let portPath = 'FAKE_PORT';
// MockBinding.createPort(portPath, { echo: false, record: true });
// sp = new SerialPort(portPath, { autoOpen: false });
// }
// else {
// this.mockPort = false;
// sp = new SerialPort(conn._cfg.rs485Port, conn._cfg.portSettings);
// }

// conn._port = sp;
// sp.open(function(err) {
// if (err) {
// conn.resetConnTimer();
// conn.isOpen = false;
// logger.error(`Error opening port: ${err.message}. ${conn._cfg.inactivityRetry > 0 ? `Retry in ${conn._cfg.inactivityRetry} seconds` : `Never retrying; inactivityRetry set to ${conn._cfg.inactivityRetry}`}`);
// }
// else
// logger.info(`Serial port: ${ this.path } request to open succeeded without error`);
// });
// sp.on('open', function() {
// if (timeOut === 'retry_timeout' || timeOut === 'timeout')
// logger.error('Serial port %s recovering from lost connection', conn._cfg.rs485Port);
// else
// logger.info(`Serial port: ${ this.path } opened`);
// conn.isOpen = true;
// });
// // RKS: 06-16-20 -- Unsure why we are using a stream event here. The data
// // is being sent via the data event and I can find no reference to the readable event.
// sp.on('data', function (data) {
// if (!this.mockPort) {
// if (!conn.isPaused) conn.emitter.emit('packetread', data);
// }
// conn.resetConnTimer();
// });
// //sp.on('readable', function () {
// // if (!this.mockPort) {
// // // If we are paused just read the port and do nothing with it.
// // if (conn.isPaused)
// // sp.read();
// // else
// // conn.emitter.emit('packetread', sp.read());
// // conn.resetConnTimer();
// // }
// //});

// }
// if (typeof (conn.buffer) === 'undefined') {
// conn.buffer = new SendRecieveBuffer();
// conn.emitter.on('packetread', function(pkt) { conn.buffer.pushIn(pkt); });
// conn.emitter.on('messagewrite', function(msg) { conn.buffer.pushOut(msg); });
// }
// conn.resetConnTimer('retry_timeout');
// conn._port.on('error', function(err) {
// logger.error(`Error opening port: ${err.message}. ${conn._cfg.inactivityRetry > 0 ? `Retry in ${conn._cfg.inactivityRetry} seconds` : `Never retrying; inactivityRetry set to ${conn._cfg.inactivityRetry}`}`);
// conn.resetConnTimer();
// conn.isOpen = false;
// });
//}
public closeAsync() {
try {
if (conn.connTimer) clearTimeout(conn.connTimer);
Expand Down Expand Up @@ -275,7 +195,7 @@ export class Connection {
netPort: 9801,
inactivityRetry: 10
});
conn.openAsync();
if (conn._cfg.enabled) conn.openAsync();
config.emitter.on('reloaded', () => {
console.log('Config reloaded');
this.reloadConfig(config.getSection('controller.comms', {
Expand All @@ -302,7 +222,7 @@ export class Connection {
if (JSON.stringify(c) !== JSON.stringify(this._cfg)) {
this.closeAsync();
this._cfg = c;
this.openAsync();
if(this._cfg.enabled) this.openAsync();
}
}
public queueSendMessage(msg: Outbound) { conn.emitter.emit('messagewrite', msg); }
Expand Down
48 changes: 24 additions & 24 deletions controller/comms/messages/status/IntelliChemStateMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,34 +80,34 @@ export class IntelliChemStateMessage {
// This is an action 18 that comes from IntelliChem. There is also a 147 that comes from an OCP but this is the raw data.
//[165, 0, 16, 144, 18, 41][2,228,3,2,2,228,2,188,0,0,0,16,0,0,0,0,0,35,0,0,6,6,3,0,250,0,44,0,160,20,0,81,8,0,149,0,80,1,0,0,0]
// Bytes - Descrption
// 0-1 : pH 2x256 + 228 / 100 = 7.4
// 2-3 : ORP 3x256 + 2 = 770
// 4-5 : pH Setpoint : 2x256 + 228 / 100 = 7.4
// 6-7 : ORP Setpoint : 2x256 + 188 = 700
// 0-1 : pH byte(0) x 256 + byte(1) / 100
// 2-3 : ORP byte(2) x 256 + byte(3)
// 4-5 : pH Setpoint : byte(4) x 256 + byte(5) / 100
// 6-7 : ORP Setpoint : byte(6) x 256 + byte(7)
// 8 : Unknown = 0
// 9 : Unknown = 0
// 10 : Unknown = 0
// 11-12 : pH Dose time
// 11-12 : pH Dose time seconds. The number of seconds since the dose started. byte(11) x 256 + byte(12)
// 13 : Unknown
// 14-15 : ORP Dose time seconds. The number of seconds since the dose started.
// 16-17 : pH Dose volume (unknown units) = 35
// 18-19 : ORP Dose volume (unknown units) = 0
// 20 : pH tank level 1-7 = 6
// 21 : ORP tank level 1-7 = 6
// 22 : LSI = 3 & 0x80 === 0x80 ? 256 - 3 / -100 : 3/100 = .03
// 23-24 : Calcium Hardness = 0x256+250 = 250
// 25 : Unknown = 0
// 26 : CYA = 44
// 27-28 : Alkalinity = 0x256+160
// 29 : Salt level = 20
// 14-15 : ORP Dose time seconds. The number of seconds since the dose started. byte(14) x 256 + byte(15)
// 16-17 : pH Dose volume (unknown units) - These appear to be mL.
// 18-19 : ORP Dose volume (unknown units) - These appear to be mL
// 20 : pH tank level 1-7
// 21 : ORP tank level 1-7
// 22 : LSI. (byte(22) & 0x80) === 0x80 ? (256 - byte(22) / -100 : byte(22)/100
// 23-24 : Calcium Hardness = byte(23) x 256 + byte(24) = 250
// 25 : Unknown = 0 (probably reserved CYA byte so the controller is always dealing with integers)
// 26 : CYA Max value = 210.
// 27-28 : Alkalinity = byte(27) x 256 + byte(28)
// 29 : Salt level = byte(29) x 50
// 30 : Unknown
// 31 : Temperature = 81
// 32 : Alarms = 8 = (no alarm)
// 33 : pH Lockout, Daily Limit Reached, Invalid Setup, Chlorinator Comm error
// 34 : Dosing Status = 149 = (pH Monitoring, ORP Mixing)
// 35 : Delays = 0
// 31 : Temperature
// 32 : Alarms
// 33 : Warnings pH Lockout, Daily Limit Reached, Invalid Setup, Chlorinator Comm error
// 34 : Dosing Status (pH Monitoring, ORP Mixing)
// 35 : Delays
// 36-37 : Firmware = 80,1 = 1.080
// 38 : Water Chemistry Warning
// 38 : Water Chemistry Warning (Corrosion...)
// 39 : Unknown
// 40 : Unknown
let address = msg.source;
Expand Down Expand Up @@ -143,9 +143,9 @@ export class IntelliChemStateMessage {
scontroller.ph.tank.level = Math.max(msg.extractPayloadByte(20) > 0 ? msg.extractPayloadByte(20) - 1 : msg.extractPayloadByte(20), 0); // values reported as 1-7; possibly 0 if no tank present
// 21 : ORP tank level 1-7 = 6
scontroller.orp.tank.level = Math.max(msg.extractPayloadByte(21) > 0 ? msg.extractPayloadByte(21) - 1 : msg.extractPayloadByte(21), 0); // values reported as 1-7; possibly 0 if no tank present
// 22 : LSI = 3 & 0x80 === 0x80 ? 256 - 3 / -100 : 3/100 = .03
// 22 : LSI = 3 & 0x80 === 0x80 ? (256 - 3) / -100 : 3/100 = .03
let lsi = msg.extractPayloadByte(22);
scontroller.saturationIndex = (lsi & 0x80) ? 256 - lsi / -100 : lsi / 100;
scontroller.saturationIndex = (lsi & 0x80) === 0x80 ? (256 - lsi) / -100 : lsi / 100;
// 23-24 : Calcium Hardness = 0x256+250 = 250
controller.calciumHardness = (msg.extractPayloadByte(23) * 256) + msg.extractPayloadByte(24);
// 26 : CYA = 44
Expand Down
1 change: 1 addition & 0 deletions defaultConfig.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"controller": {
"comms": {
"enabled": true,
"rs485Port": "/dev/ttyUSB0",
"mockPort": false,
"netConnect": false,
Expand Down

0 comments on commit 269caca

Please sign in to comment.