357 lines
11 KiB
JavaScript
357 lines
11 KiB
JavaScript
exports.id = 'relay';
|
|
exports.title = 'DI_DO_Controller';
|
|
exports.version = '1.0.0';
|
|
exports.group = 'Worksys';
|
|
exports.color = '#2134B0';
|
|
exports.input = 1;
|
|
exports.output = ["red", "white", "yellow"];
|
|
exports.click = false;
|
|
exports.author = 'Daniel Segeš';
|
|
exports.icon = 'bolt';
|
|
exports.options = { edge: "undefined" };
|
|
|
|
exports.html = `<div class="padding">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div data-jc="textbox" data-jc-path="edge" data-jc-config="placeholder:undefined;required:true" class="m">Edge TB Name</div>
|
|
</div>
|
|
</div>
|
|
</div>`;
|
|
|
|
exports.readme = `# Sets RS232 port and all digital pins on device. Then it starts to receive data from sensors.
|
|
It receives:
|
|
|
|
rotary_switch_state,
|
|
rotary_switch_state,
|
|
door_condition,
|
|
state_of_breaker,
|
|
state_of_contactor,
|
|
twilight_sensor
|
|
`;
|
|
|
|
/*
|
|
we open rsPort "/dev/ttymxc0" and set digital input and output pins with "setRSPortData"
|
|
Currently we are interested in pins no. 1,2,3,6,8,9,10,16
|
|
pins number 11, 12, 13 (we receive 10,11,12 in rsPortReceivedData) are "stykace"
|
|
When port receives data, it must be exactly 4 bytes long. Second byte is pin, that changed its value, fourth byte is value itself.
|
|
After that, we set this value to "previousValues[allPins[whichpin]]" variable
|
|
*/
|
|
|
|
|
|
/*
|
|
RVO objekt:
|
|
state_of_main_switch - sem sa bude reportovať stav hlavného ističa : 0-> off 1-> on (toto nie je na platforme, ale Rado to už do entity type doplnil)
|
|
rotary_switch_state - sem by sa mal reportovať stav vstupov manual a auto podľa nasledovnej logiky:
|
|
Manual = 1 a Auto = 0 -> vyreportuje Manual
|
|
Manual = 0 a Auto = 0 -> vyreportuje Off
|
|
Manual = 0 a Auto = 1 -> vyreportuje Automatic
|
|
|
|
door_condition - tuto ide pin 6, dverový kontakt -> 1 -> vyreportuje Closed, 0 -> vyreportuje Open
|
|
twilight_sensor - hodnotu, ktorú vracia ten analógový vstup (17) treba poslať sem ako float number. Zrejme tu potom pridáme nejaký koeficient prevodu na luxy
|
|
|
|
zjavne nám v jsone chýba stav hlavného ističa. Musíme to potom doplniť
|
|
|
|
Na každú líniu:
|
|
state_of_breaker - podľa indexu ističa sa reportuje jeho stav, teda istič 1 na líniu 1: 0-> off 1-> on
|
|
state_of_contactor - podľa indexu stykača sa reportuje jeho stav, teda stykač 1 na líniu 1: 0-> off 1-> on
|
|
momentálne sa stav zmení len keď vo flow klikneš aby sa zmenil, ale tá zmena by sa mala ukázať aj na platforme
|
|
*/
|
|
|
|
exports.install = function(instance) {
|
|
|
|
let previousValues = {};
|
|
let rsPortReceivedData = [];
|
|
|
|
console.log("DI_DO_Relay_Controller installed");
|
|
|
|
//key is PIN number
|
|
const conversionTable = {
|
|
"1": {tbname: "KjbN4q7JPZmexgdnz2yKQ98YAWwO0Q3BMX6ERLoV", type: "state_of_main_switch"}, //state_of_main_switch pin1
|
|
"2": {tbname: "KjbN4q7JPZmexgdnz2yKQ98YAWwO0Q3BMX6ERLoV", type: "rotary_switch_state"}, //rotary_switch_state - poloha manual = pin2
|
|
"3": {tbname: "KjbN4q7JPZmexgdnz2yKQ98YAWwO0Q3BMX6ERLoV", type: "rotary_switch_state"}, //rotary_switch_state - poloha auto = pin3
|
|
"6": {tbname: "KjbN4q7JPZmexgdnz2yKQ98YAWwO0Q3BMX6ERLoV", type: "door_condition"}, // door_condition = pin6, 1 -> vyreportuje Closed, 0 -> vyreportuje Open
|
|
"8": {tbname: "RMgnK93rkoAazbqdQ4yBG95Z1YXGx6pmwBeVEP2O", type: "state_of_breaker", "line": 1}, // state_of_breaker linia 1 0=off, 1=on
|
|
"9": {tbname: "dlE1VQjYrNx9gZRmb38gG08oLBO4qaAk2M6JPnG7", type: "state_of_breaker", "line": 2}, // state_of_breaker linia 2 0=off, 1=on
|
|
"10": {tbname: "vnmG4kJxaXWNBgMQq0D7Aj5e9oZzOAlr6LdR3w2V", type: "state_of_breaker", "line": 3}, // state_of_breaker linia 3 0=off, 1=on
|
|
"11": {tbname: "RMgnK93rkoAazbqdQ4yBG95Z1YXGx6pmwBeVEP2O", type: "state_of_contactor", "line": 1}, // state_of_contactor linia 1 0=off, 1=on
|
|
"12": {tbname: "dlE1VQjYrNx9gZRmb38gG08oLBO4qaAk2M6JPnG7", type: "state_of_contactor", "line": 2}, // state_of_contactor linia 2 0=off, 1=on
|
|
"13": {tbname: "vnmG4kJxaXWNBgMQq0D7Aj5e9oZzOAlr6LdR3w2V", type: "state_of_contactor", "line": 3}, // state_of_contactor linia 3 0=off, 1=on
|
|
"16": {tbname: "KjbN4q7JPZmexgdnz2yKQ98YAWwO0Q3BMX6ERLoV", type: "state_of_main_switch"}, // twilight_sensor = pin16
|
|
};
|
|
|
|
const dbRelays = TABLE("relays");
|
|
dbRelays.on('change', function(doc, old) {
|
|
instance.send(2, "reload_relays");
|
|
});
|
|
|
|
//modify
|
|
|
|
const SerialPort = require('serialport');
|
|
//const { exec } = require('child_process');
|
|
const { openPort, runSyncExec, writeData } = require('./serialport_helper.js');
|
|
|
|
const setRSPortData = [0xAA,6,6,6,6,0,6,0,6,6,6,1,1,1,1,0,0,10,10,10,10,0,10,0,10,10,10,0,0,0,0,0,0,5,0,0,0,15,15,15,15,0,15,0,15,15,15,0,0,0,0,0,0,30,0,0,0];
|
|
const rsPort = new SerialPort("/dev/ttymxc0", { autoOpen: false });
|
|
|
|
rsPort.on('open', async function() {
|
|
|
|
await runSyncExec("stty -F /dev/ttymxc0 115200 min 1 time 5 ignbrk -brkint -icrnl -imaxbel -opost -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke").then(function (status) {
|
|
instance.send(0, exports.title + " runSyncExec - Promise Resolved:" + status);
|
|
|
|
//set port
|
|
rsPort.write(Buffer.from(setRSPortData), function(err) {
|
|
instance.send(0, exports.title + " Digital in_out has been set");
|
|
|
|
//force turn off relays
|
|
let keys = Object.keys(conversionTable);
|
|
for(let i = 0; i < keys.length; i++)
|
|
{
|
|
let key = keys[i];
|
|
|
|
if(conversionTable[key].type == "state_of_contactor")
|
|
{
|
|
let pin = key - 1;
|
|
let line = conversionTable[key].line;
|
|
|
|
turnOff(line, pin);
|
|
}
|
|
}
|
|
|
|
//dbRelays.modify({ contactor: 0 });
|
|
})
|
|
|
|
}).catch(function (reason) {
|
|
instance.send(0, exports.title + " runSyncExec - promise rejected:" + reason);
|
|
});
|
|
|
|
});
|
|
|
|
rsPort.open();
|
|
|
|
rsPort.on('data', function (data){
|
|
|
|
rsPortReceivedData = [...rsPortReceivedData, ...data];
|
|
|
|
if (rsPortReceivedData[0] != 85) {
|
|
rsPortReceivedData = [];
|
|
return;
|
|
}
|
|
|
|
let l = rsPortReceivedData.length;
|
|
|
|
if (l < 4 ) return;
|
|
|
|
if (l > 4 ) {
|
|
|
|
// if array length is greater than 4, we take first 4 byte and do the logic, second 4 bytes, do the logic and so on
|
|
let i, j, temparray, chunk = 4;
|
|
for ( i = 0, j = l; i < j; i += chunk ) {
|
|
temparray = rsPortReceivedData.slice(i, i + chunk);
|
|
|
|
if ( temparray.length < 4 ){
|
|
rsPortReceivedData = [...temparray];
|
|
return;
|
|
}
|
|
|
|
switchLogic(temparray);
|
|
}
|
|
|
|
rsPortReceivedData = [];
|
|
return;
|
|
}
|
|
|
|
switchLogic(rsPortReceivedData);
|
|
|
|
rsPortReceivedData = [];
|
|
|
|
});
|
|
|
|
rsPort.on('error', function(err) {
|
|
instance.send(0, err.message);
|
|
})
|
|
|
|
rsPort.on("close", () => {
|
|
rsPort.close();
|
|
})
|
|
|
|
instance.on("close", () => {
|
|
rsPort.close();
|
|
})
|
|
|
|
function getPin(line)
|
|
{
|
|
//conversionTable
|
|
let keys = Object.keys(conversionTable);
|
|
for(let i = 0; i < keys.length; i++)
|
|
{
|
|
let key = keys[i];
|
|
|
|
if(conversionTable[key].type == "state_of_contactor" && conversionTable[key].line == line)
|
|
{
|
|
return key - 1;
|
|
}
|
|
}
|
|
|
|
console.log("no pin detected");
|
|
|
|
return null;
|
|
}
|
|
|
|
function turnOn(line, pin)
|
|
{
|
|
if( pin === undefined) pin = getPin(line);
|
|
if( pin === undefined) return;
|
|
|
|
let arr = [0x55];
|
|
arr.push( pin );
|
|
arr.push( 0 );
|
|
arr.push( 1 );
|
|
|
|
if(!rsPort.isOpen)
|
|
{
|
|
console.log("port is not opened");
|
|
return;
|
|
}
|
|
|
|
rsPort.write(Buffer.from(arr), function(err) {
|
|
switchLogic(arr);
|
|
});
|
|
}
|
|
|
|
function turnOff(line, pin)
|
|
{
|
|
if( pin === undefined) pin = getPin(line);
|
|
if( pin === undefined) return;
|
|
|
|
let arr = [0x55];
|
|
arr.push( pin );
|
|
arr.push( 0 );
|
|
arr.push( 0 );
|
|
|
|
if(!rsPort.isOpen)
|
|
{
|
|
console.log("port is not opened");
|
|
return;
|
|
}
|
|
|
|
rsPort.write(Buffer.from(arr), function(err) {
|
|
switchLogic(arr);
|
|
});
|
|
}
|
|
|
|
// we expect array as flowdata.data
|
|
instance.on("data", (flowdata) => {
|
|
|
|
//console.log(flowdata.data);
|
|
|
|
if(flowdata.data instanceof Object)
|
|
{
|
|
let obj = flowdata.data;
|
|
|
|
let line = obj.line;
|
|
|
|
if(obj.command == "turnOn") turnOn(line);
|
|
else if(obj.command == "turnOff") turnOff(line);
|
|
|
|
return;
|
|
}
|
|
|
|
if (Array.isArray(flowdata.data)){
|
|
rsPort.write(Buffer.from(flowdata.data), function(err) {
|
|
switchLogic(flowdata.data);
|
|
|
|
instance.send(0,{"WRITE":flowdata.data});
|
|
});
|
|
}
|
|
})
|
|
|
|
const switchLogic = (rsPortReceivedData) => {
|
|
|
|
let dataToTb;
|
|
let values = {};
|
|
|
|
let pinIndex = rsPortReceivedData[1] + 1;
|
|
if (pinIndex === 17) pinIndex--;
|
|
|
|
let newPinValue = rsPortReceivedData[3];
|
|
|
|
let obj = conversionTable[pinIndex];
|
|
|
|
if(obj == undefined)
|
|
{
|
|
//console.log("undefined pinIndex", pinIndex, rsPortReceivedData);
|
|
return;
|
|
}
|
|
|
|
let type = obj.type;
|
|
let line = obj.line;
|
|
let tbname = obj.tbname;
|
|
|
|
//default value
|
|
let value = "On";
|
|
if(newPinValue === 0) value = "Off";
|
|
|
|
if(type == "rotary_switch_state")
|
|
{
|
|
// combination of these two pins required to get result
|
|
let pin2, pin3;
|
|
if(pinIndex == 2)
|
|
{
|
|
pin2 = newPinValue;
|
|
pin3 = previousValues[pinIndex];
|
|
if (pin3 == undefined) pin3 = 0;
|
|
}
|
|
else if(pinIndex == 3)
|
|
{
|
|
pin3 = newPinValue;
|
|
pin2 = previousValues[1];
|
|
if (pin2 == undefined) pin2 = 0;
|
|
}
|
|
|
|
if (pin2 == 1 && pin3 == 0) value = "Manual";
|
|
if (pin2 == 0 && pin3 == 0) value = "Off";
|
|
if (pin2 == 0 && pin3 == 1) value = "Automatic";
|
|
}
|
|
else if(type == "door_condition")
|
|
{
|
|
newPinValue === 0 ? value = "Open" : value = "Closed";
|
|
}
|
|
else if(type == "twilight_sensor")
|
|
{
|
|
value = parseFloat(newPinValue + (256*rsPortReceivedData[2]));
|
|
}
|
|
else if(type == "state_of_contactor")
|
|
{
|
|
//modify table relays
|
|
dbRelays.modify({ contactor: newPinValue }).where("line", line);
|
|
}
|
|
|
|
values[obj.type] = value;
|
|
|
|
if(conversionTable.hasOwnProperty(pinIndex))
|
|
{
|
|
let insertIntoTb = false;
|
|
if(newPinValue != previousValues[pinIndex]) insertIntoTb = true;
|
|
if(obj.hasOwnProperty("state_of_contactor")) insertIntoTb = true;
|
|
|
|
if(insertIntoTb)
|
|
{
|
|
dataToTb = {
|
|
[tbname]: [
|
|
{
|
|
"ts": Date.now(),
|
|
"values": values
|
|
}
|
|
]
|
|
}
|
|
|
|
instance.send(1, dataToTb);
|
|
}
|
|
}
|
|
|
|
//pin was changed
|
|
previousValues[pinIndex] = newPinValue;
|
|
}
|
|
}
|
|
|
|
|