Скрипт для телеуправления с ключом блокировки

Сделал скрипт для телеуправления с ключом блокировки:

telecontrol.schema.json

{
    "type":"object",
    "title": "Телеуправление",
    "description": "Настройка телеуправления выключателями",
    "configFile": {
        "path": "/etc/telecontrol.conf",
        "service": "wb-rules"
    },
    "definitions": {
        "tc_point":{
            "type":"object",
            "title":"Точка управления",
            "headerTemplate": "{{self.name}}",
            "properties": {
                "enabled": {
                    "type": "boolean",
                    "title": "Акт.",
                    "propertyOrder": 1,
                    "_format": "checkbox"
                },
                "name":{
                    "type":"string",
                    "title":"Наименование",
                    "propertyOrder": 2,
                    "default":"ВВ 10 кВ ф.1"
                },
                "ts":{
                    "type":"string",
                    "title":"ТС состояния выключателя",
                    "propertyOrder":3,
                    "default":"wb-gpio/EXT2_IN1"
                },
                "time":{
                    "type": "integer",
                    "title": "Длительность, мс",
                    "propertyOrder": 4,
                    "min":100,
                    "max":10000,
                    "default":1000
                },
                "on":{
                    "type":"string",
                    "title":"Реле на Включение",
                    "propertyOrder": 5,
                    "default":"wb-gpio/EXT1_R3A1"
                },
                "off":{
                    "type":"string",
                    "title":"Реле на Отключение",
                    "propertyOrder": 6,
                    "default":"wb-gpio/EXT1_R3A2"
                }
            }
        }
    },
    "properties":{
        "use_key":{
            "type":"boolean",
            "title":"Использовать ключ блокировки телеуправления",
            "propertyOrder": 1,
            "default":false,
            "_format": "checkbox"
        },
        "key":{
            "type": "string",
            "title": "Ключ блокировки ТУ",
            "propertyOrder": 2,
            "default":"Вход 14",
            "enum":["wb-gpio/EXT2_IN12","wb-gpio/EXT2_IN13","wb-gpio/EXT2_IN14"],
            "options": {"enum_titles": ["Вход 12","Вход 13","Вход 14"]}
        },
        "points": {
            "type": "array",
            "title": "Точки телеуправления",
            "description": "Список точек телеуправления и назначенных им выходов",
            "propertyOrder": 3,
            "_format": "table",
            "items": {
                "$ref": "#/definitions/tc_point"
            },
            "options": {
                "disable_collapse": true
            }
        }
    }
}

telecontrol-rule.js

log("Модуль телеуправление загружен");  
var config = readConfig("/etc/telecontrol.conf");
var tu_points = {};
config.points.forEach(function(point,n){
  if(point.enabled){
    tu_points[point.name]={type: "switch",value: dev[point.ts]};
    defineRule("wb-telecontrol"+n, {
      whenChanged: "wb-telecontrol/"+point.name,
      then: function(newValue, devName, cellName) {
        if((config.use_key&&!dev[config.key])||!config.use_key){
          //if(newValue!=dev[point.ts])
            (function(x){log("Телеуправление {} выполнено переведен в [{}]",point.name,newValue?"Включено":"Отключено");dev[x]=true;setTimeout(function(){dev[x]=false;},point.time);})(newValue?point.on:point.off);
        }else{
          //if(newValue!=dev[point.ts])dev[devName][cellName] = dev[point.ts];
          log("Телеуправление {} не выполнено, активна блокировка.",point.name);
        }
      }
	});
  }
});
defineVirtualDevice("wb-telecontrol", {title: "Телеуправление",cells: tu_points});

Но правило whenChanged имеет особенность - нельзя послать 2 команды одного типа подряд.
какое еще можно использовать правило чтобы обработать данные пришедшие по МЭК104?

Нужно обрабатывать все команды пришедшие по МЭК104, в том числе и повторные - запись 1 - должна активировать реле включения столько раз, сколько будет послана комманда записи. Если по изменениям - то только чередовать команды записи 0 и 1.

Немного непонятно. Правило отрабатывает каждый раз при изменении топика, на который оно подписано.
Если нужно выполнить “последовательность” команд - то логично весь объект (список команд) строкой записать в MQTT, в правиле распарсить и обработать.

По МЭК 104 - два раза приходит запись “1” в канал - с периодичностью 2 минуты(первый раз команда не отработала, пробуем еще раз ту же комманду).
Отработает только первая, потому что вотрой раз канал уже равен “1” (ничего не изменилось), а на самом деле нужно еще раз активировать реле как и при изменении.

Ну так как в MQTT пишет драйвер шлюза МЭК104, а там только 1 топик на канал МЭК104 - поэтому вопрос как реализовать? Если бы было 2 топика - включения и отключения или можно было обработать появление сообщения в топике кроме whenChanged, а например newMessage - который бы активировался каждым сообщением в топике.

Как раз whenChanged вызывается каждый раз при изменении топика. Попробудет добавить в функцию запись в лог - увидите

log.info("Правило для",devName, cellName, "сработало, значение", newValue )

и параллельно подпишитесь на топик в брокере с помощью
https://wirenboard.com/wiki/MQTT#.D0.A1.D0.BB.D0.B5.D0.B6.D0.B5.D0.BD.D0.B8.D0.B5_.D0.B7.D0.B0_.D1.81.D0.BE.D1.81.D1.82.D0.BE.D1.8F.D0.BD.D0.B8.D0.B5.D0.BC_.D1.83.D1.81.D1.82.D1.80.D0.BE.D0.B9.D1.81.D1.82.D0.B2.D0.B0_.2F_.D0.BF.D0.BE.D0.B4.D0.BF.D0.B8.D1.81.D0.BA.D0.B0_.D0.BD.D0.B0_.D1.82.D0.BE.D0.BF.D0.B8.D0.BA
Найдем “источник” - либо wb-mqtt-iec104 публикует только один раз (скорее всего) либо wb-rules не отрабатывает.

И да, кончно есть:

whenChanged вызывается только при изменении, проверено.
Но trackMqtt - вызывается даже 2 раза на одну операцию записи, потому что драйвер МЭК104 пишет так. Еще он вызывается сразу после подписки, что не совсем корректно и приходися использовать флаг блокировки.

Как драйвер отрабатывает с последовательностью select->execute из протокола МЭК101?
У меня убеждение что он пишет как на select(предварительный выбор) так и на execute(исполнение) - отсюда и 2 сообщения в MQTT,
но назначение Select немного другое.


Предварительный выбор - это блокировка от записи из других источников, когда нужно записать несколько параметров одним моментом времени.

В таком виде вроде работает:

log("Модуль телеуправление загружен");  
var config = readConfig("/etc/telecontrol.conf");
var tu_points = {};
var busy = {};
config.points.forEach(function(point,n){
  if(point.enabled){
    busy[point.name] = true;
    tu_points[point.name]={type: "switch",value: dev[point.ts]};
    trackMqtt("/devices/wb-telecontrol/controls/"+point.name, function(info) {
      if(!busy[point.name]){
        busy[point.name]=true;
        if((config.use_key&&!dev[config.key])||!config.use_key){
          //if(newValue!=dev[point.ts])
            (function(x){log("Телеуправление {} выполнено переведен в [{}]",point.name,info.value=="1"?"Включено":"Отключено");dev[x]=true;setTimeout(function(){dev[x]=false;},point.time);})(info.value=="1"?point.on:point.off);
        }else{
          //if(newValue!=dev[point.ts])dev[devName][cellName] = dev[point.ts];
          log("Телеуправление {} не выполнено, активна блокировка.",point.name);
        }
        setTimeout(function(){busy[point.name]=false;},1000+point.time);
      }
    });
  }
});
setTimeout(function(){config.points.forEach(function(point,n){busy[point.name]=false;})},3000);
defineVirtualDevice("wb-telecontrol", {title: "Телеуправление",cells: tu_points});