WB7 wb-2304 wb-rules последние, таймеры и дедлайн

Здрасьте! Вот прям ночь не спали. С утра написал проверку.
Садимся за веб интерфейс и дергаем таймеры на вкл выкл… минуты 3-5.
И оно медленно умирает. Основная проблема это огромная задержка на реакцию правил. Например на переключатели в примере.
Я слишком многого требую от скорострельности нашего четырёхъядерного калькулятора?

И в догонку. А как к таймеру через строковую переменную обращаться? А то код кривой получился…

var dn = "TT_01";
var dt = new Array();
var TT_01_PERIOD = 300;

var BUZ_DURATION = 200; // мс,
dt[0] = new Date();

startTicker("debug_sec_timer", 1000);

// Основной модуль
defineRule({
  when: function () { return timers.debug_sec_timer.firing; },
  then: function () {
    log.info(Math.round((new Date() - dt[0]) / 10) / 100, " сек.");
    dt[0] = new Date();
  }
});

defineVirtualDevice(dn, {
  title: dn,
  cells: {
    T1: { // 
      title: "T1",
      type: "switch",
      value: false,
      readonly: false,
      defaultvalue: true,
      order: 1
    },
    S1: { // 
      title: "S1",
      type: "text",
      value: "blank",
      readonly: true,
      defaultvalue: true,
      order: 101
    },
    T2: { // 
      title: "T2",
      type: "switch",
      value: false,
      readonly: false,
      defaultvalue: true,
      order: 2
    },
    S2: { // 
      title: "S2",
      type: "text",
      value: "blank",
      readonly: true,
      defaultvalue: true,
      order: 102
    },
    T3: { // 
      title: "T3",
      type: "switch",
      value: false,
      readonly: false,
      defaultvalue: true,
      order: 3
    },
    S3: { // 
      title: "S3",
      type: "text",
      value: "blank",
      readonly: true,
      defaultvalue: true,
      order: 103
    },
    T4: { // 
      title: "T4",
      type: "switch",
      value: false,
      readonly: false,
      defaultvalue: true,
      order: 4
    },
    S4: { // 
      title: "S4",
      type: "text",
      value: "blank",
      readonly: true,
      defaultvalue: true,
      order: 104
    },
    T5: { // 
      title: "T5",
      type: "switch",
      value: false,
      readonly: false,
      defaultvalue: true,
      order: 5
    },
    S5: { // 
      title: "S5",
      type: "text",
      value: "blank",
      readonly: true,
      defaultvalue: true,
      order: 105
    },
    T6: { // 
      title: "T6",
      type: "switch",
      value: false,
      readonly: false,
      defaultvalue: true,
      order: 6
    },
    S6: { // 
      title: "S6",
      type: "text",
      value: "blank",
      readonly: true,
      defaultvalue: true,
      order: 106
    },
    T7: { // 
      title: "T7",
      type: "switch",
      value: false,
      readonly: false,
      defaultvalue: true,
      order: 7
    },
    S7: { // 
      title: "S7",
      type: "text",
      value: "blank",
      readonly: true,
      defaultvalue: true,
      order: 107
    },
    T8: { // 
      title: "T8",
      type: "switch",
      value: false,
      readonly: false,
      defaultvalue: true,
      order: 8
    },
    S8: { // 
      title: "S8",
      type: "text",
      value: "blank",
      readonly: true,
      defaultvalue: true,
      order: 108
    },
    T9: { // 
      title: "T9",
      type: "switch",
      value: false,
      readonly: false,
      defaultvalue: true,
      order: 9
    },
    S9: { // 
      title: "S9",
      type: "text",
      value: "blank",
      readonly: true,
      defaultvalue: true,
      order: 109
    },
    T10: { // 
      title: "T10",
      type: "switch",
      value: false,
      readonly: false,
      defaultvalue: true,
      order: 10
    },
    S10: { // 
      title: "S10",
      type: "text",
      value: "blank",
      readonly: true,
      defaultvalue: true,
      order: 110
    }
  },
});

defineRule("T1", {
  whenChanged: [dn + "/T1"],
  then: function (newValue, devName, cellName) {
    log("Правило: {}.{}={}", devName, cellName, newValue) // вывод сообщения в лог   
    if (newValue == true) {
      startTicker("TT_01_T1", TT_01_PERIOD);
      dt[1] = new Date();
    }
    else
      timers.TT_01_T1.stop();
  }
});

defineRule("T2", {
  whenChanged: [dn + "/T2"],
  then: function (newValue, devName, cellName) {
    log("Правило: {}.{}={}", devName, cellName, newValue) // вывод сообщения в лог   
    if (newValue == true) {
      startTicker("TT_01_T2", TT_01_PERIOD);
      dt[2] = new Date();
    }
    else
      timers.TT_01_T2.stop();
  }
});

defineRule("T3", {
  whenChanged: [dn + "/T3"],
  then: function (newValue, devName, cellName) {
    log("Правило: {}.{}={}", devName, cellName, newValue) // вывод сообщения в лог   
    if (newValue == true) {
      startTicker("TT_01_T3", TT_01_PERIOD);
      dt[3] = new Date();
    }
    else
      timers.TT_01_T3.stop();
  }
});

defineRule("T4", {
  whenChanged: [dn + "/T4"],
  then: function (newValue, devName, cellName) {
    log("Правило: {}.{}={}", devName, cellName, newValue) // вывод сообщения в лог   
    if (newValue == true) {
      startTicker("TT_01_T4", TT_01_PERIOD);
      dt[4] = new Date();
    }
    else
      timers.TT_01_T4.stop();
  }
});

defineRule("T5", {
  whenChanged: [dn + "/T5"],
  then: function (newValue, devName, cellName) {
    log("Правило: {}.{}={}", devName, cellName, newValue) // вывод сообщения в лог   
    if (newValue == true) {
      startTicker("TT_01_T5", TT_01_PERIOD);
      dt[5] = new Date();
    }
    else
      timers.TT_01_T5.stop();
  }
});

defineRule("T6", {
  whenChanged: [dn + "/T6"],
  then: function (newValue, devName, cellName) {
    log("Правило: {}.{}={}", devName, cellName, newValue) // вывод сообщения в лог   
    if (newValue == true) {
      startTicker("TT_01_T6", TT_01_PERIOD);
      dt[6] = new Date();
    }
    else
      timers.TT_01_T6.stop();
  }
});

defineRule("T7", {
  whenChanged: [dn + "/T7"],
  then: function (newValue, devName, cellName) {
    log("Правило: {}.{}={}", devName, cellName, newValue) // вывод сообщения в лог   
    if (newValue == true) {
      startTicker("TT_01_T7", TT_01_PERIOD);
      dt[7] = new Date();
    }
    else
      timers.TT_01_T7.stop();
  }
});

defineRule("T8", {
  whenChanged: [dn + "/T8"],
  then: function (newValue, devName, cellName) {
    log("Правило: {}.{}={}", devName, cellName, newValue) // вывод сообщения в лог   
    if (newValue == true) {
      startTicker("TT_01_T8", TT_01_PERIOD);
      dt[8] = new Date();
    }
    else
      timers.TT_01_T8.stop();
  }
});

defineRule("T9", {
  whenChanged: [dn + "/T9"],
  then: function (newValue, devName, cellName) {
    log("Правило: {}.{}={}", devName, cellName, newValue) // вывод сообщения в лог   
    if (newValue == true) {
      startTicker("TT_01_T9", TT_01_PERIOD);
      dt[9] = new Date();
    }
    else
      timers.TT_01_T9.stop();
  }
});

defineRule("T10", {
  whenChanged: [dn + "/T10"],
  then: function (newValue, devName, cellName) {
    log("Правило: {}.{}={}", devName, cellName, newValue) // вывод сообщения в лог   
    if (newValue == true) {
      startTicker("TT_01_T10", TT_01_PERIOD);
      dt[10] = new Date();
    }
    else
      timers.TT_01_T10.stop();
  }
});

// TIMER #1
defineRule({
  when: function () { return timers.TT_01_T1.firing; },
  then: function () {
    dtl = Math.round((new Date() - dt[1]) / 10) / 100;
    log("TT_01_T1 ", dtl, " сек.");
    dev[dn + "/S1"] = String(dtl);
    dt[1] = new Date();
  }
});

// TIMER #2
defineRule({
  when: function () { return timers.TT_01_T2.firing; },
  then: function () {
    dtl = Math.round((new Date() - dt[2]) / 10) / 100;
    log("TT_01_T2 ", dtl, " сек.");
    dev[dn + "/S2"] = String(dtl);
    dt[2] = new Date();
  }
});

// TIMER #3
defineRule({
  when: function () { return timers.TT_01_T3.firing; },
  then: function () {
    dtl = Math.round((new Date() - dt[3]) / 10) / 100;
    log("TT_01_T3 ", dtl, " сек.");
    dev[dn + "/S3"] = String(dtl);
    dt[3] = new Date();
  }
});

// TIMER #4
defineRule({
  when: function () { return timers.TT_01_T4.firing; },
  then: function () {
    dtl = Math.round((new Date() - dt[4]) / 10) / 100;
    log("TT_01_T4 ", dtl, " сек.");
    dev[dn + "/S4"] = String(dtl);
    dt[4] = new Date();
  }
});

// TIMER #5
defineRule({
  when: function () { return timers.TT_01_T5.firing; },
  then: function () {
    dtl = Math.round((new Date() - dt[5]) / 10) / 100;
    log("TT_01_T5 ", dtl, " сек.");
    dev[dn + "/S5"] = String(dtl);
    dt[5] = new Date();
  }
});

// TIMER #6
defineRule({
  when: function () { return timers.TT_01_T6.firing; },
  then: function () {
    dtl = Math.round((new Date() - dt[6]) / 10) / 100;
    log("TT_01_T6 ", dtl, " сек.");
    dev[dn + "/S6"] = String(dtl);
    dt[6] = new Date();
  }
});
// TIMER #7
defineRule({
  when: function () { return timers.TT_01_T7.firing; },
  then: function () {
    dtl = Math.round((new Date() - dt[7]) / 10) / 100;
    log("TT_01_T7 ", dtl, " сек.");
    dev[dn + "/S7"] = String(dtl);
    dt[7] = new Date();
  }
});
// TIMER #8
defineRule({
  when: function () { return timers.TT_01_T8.firing; },
  then: function () {
    dtl = Math.round((new Date() - dt[8]) / 10) / 100;
    log("TT_01_T8 ", dtl, " сек.");
    dev[dn + "/S8"] = String(dtl);
    dt[8] = new Date();
  }
});
// TIMER #9
defineRule({
  when: function () { return timers.TT_01_T9.firing; },
  then: function () {
    dtl = Math.round((new Date() - dt[9]) / 10) / 100;
    log("TT_01_T9 ", dtl, " сек.");
    dev[dn + "/S9"] = String(dtl);
    dt[9] = new Date();
  }
});
// TIMER #10
defineRule({
  when: function () { return timers.TT_01_T10.firing; },
  then: function () {
    dtl = Math.round((new Date() - dt[10]) / 10) / 100;
    log("TT_01_T10 ", dtl, " сек.");
    dev[dn + "/S10"] = String(dtl);
    dt[10] = new Date();
  }
});

Поговорю пока сам с собой.
Вроде для данного примера я понял как избежать смерти рулесов. Тут все достаточно просто. Даже на 10 таймерах в 100мс все вроде работает. Но как подобное реализовать для setInterval(callback, milliseconds) ?
вот решение для startTicker из примера выше.

/ TIMER #1
defineRule({
  when: function () { return timers.TT_01_T1.firing; },
  then: function () {
    timers.TT_01_T1.stop();
    dtl = Math.round((new Date() - dt[1]) / 10) / 100;
    //log("TT_01_T1 ", dtl, " сек.");
    dev[dn + "/S1"] = String(dtl);
    dt[1] = new Date();
    startTicker("TT_01_T1", TT_01_PERIOD);
  }
});

Хотя если период поставить в 10ms ситуация повторяется. Но 10ms это 10ms… там точно вся обертка вокруг правила не успевает выполняться. Но самое тревожное, что перестают обрабатываться события и в других виртуальных устройствах. Они встают в очередь и могут прилететь с опозданием на десятки и сотни секунд.
Понятно, что наша проблема в большом проекте похоже вызвана тем, что у нас какие-то участки кода не успевают обработаться за время цикла таймера. Хотя на вид таких кусков там нет, хоть проект и большой.

Добрый день.
Попробую воспроизвести сейчас.

Уже больше суток работают…

с какой частотой? поставьте 100 или 50 мс.
На включение выключение таймеров из вебки реагирует?
Не стоит ждать когда именно пример с var TT_01_PERIOD = 300; сломается.
Тут зависит от фоновых процессов…
Хватает нескольких минут при правильных значениях.

Поставил 50. Таймеры - срабатываютт, но да, нагрузка на CPU больше одного ядра и остановка таймера отрабатывает силно не сразу.
А для какой цели испоьзуются такие значения? На практике не слакивался, минимальные таймеры использовал как раз 250мс.

А у нас на 500-1000мс таймерах в продакшене такая картина рисовалась. Я же не могу выгрузить сюда кучу кода, в котором никто не разберется. Поэтому пришлось сделать простенький пример, и что бы он показательно заработал, уменьшили в нем периоды таймеров. Поведение то ровно такое же. При этом на веб интерфейсе значения бегут шустро. А вот правила - да. Начинают тормозить не по детски.

Вот последний пример тестового кода. Что-бы было.

var dn = "TT_01";
var DEBUG = 1;
var dt = new Array();
var tms = new Array();
var TT_01_PERIOD = 10;

dt[0] = new Date();

startTicker("debug_sec_timer", 1000);

// Основной модуль
defineRule({
  when: function () { return timers.debug_sec_timer.firing; },
  then: function () {
    debug("MAIN ", Math.round((new Date() - dt[0]) / 10) / 100, " сек.");
    dt[0] = new Date();

  }
});


defineVirtualDevice(dn, {
  title: dn,
  cells: {
    T1: { // 
      title: "T1",
      type: "switch",
      value: false,
      readonly: false,
      defaultvalue: true,
      order: 1
    },
    S1: { // 
      title: "S1",
      type: "text",
      value: "blank",
      readonly: true,
      defaultvalue: true,
      order: 101
    },
    T2: { // 
      title: "T2",
      type: "switch",
      value: false,
      readonly: false,
      defaultvalue: true,
      order: 2
    },
    S2: { // 
      title: "S2",
      type: "text",
      value: "blank",
      readonly: true,
      defaultvalue: true,
      order: 102
    },
    T3: { // 
      title: "T3",
      type: "switch",
      value: false,
      readonly: false,
      defaultvalue: true,
      order: 3
    },
    S3: { // 
      title: "S3",
      type: "text",
      value: "blank",
      readonly: true,
      defaultvalue: true,
      order: 103
    },
    T4: { // 
      title: "T4",
      type: "switch",
      value: false,
      readonly: false,
      defaultvalue: true,
      order: 4
    },
    S4: { // 
      title: "S4",
      type: "text",
      value: "blank",
      readonly: true,
      defaultvalue: true,
      order: 104
    },
    T5: { // 
      title: "T5",
      type: "switch",
      value: false,
      readonly: false,
      defaultvalue: true,
      order: 5
    },
    S5: { // 
      title: "S5",
      type: "text",
      value: "blank",
      readonly: true,
      defaultvalue: true,
      order: 105
    },
    T6: { // 
      title: "T6",
      type: "switch",
      value: false,
      readonly: false,
      defaultvalue: true,
      order: 6
    },
    S6: { // 
      title: "S6",
      type: "text",
      value: "blank",
      readonly: true,
      defaultvalue: true,
      order: 106
    },
    T7: { // 
      title: "T7",
      type: "switch",
      value: false,
      readonly: false,
      defaultvalue: true,
      order: 7
    },
    S7: { // 
      title: "S7",
      type: "text",
      value: "blank",
      readonly: true,
      defaultvalue: true,
      order: 107
    },
    T8: { // 
      title: "T8",
      type: "switch",
      value: false,
      readonly: false,
      defaultvalue: true,
      order: 8
    },
    S8: { // 
      title: "S8",
      type: "text",
      value: "blank",
      readonly: true,
      defaultvalue: true,
      order: 108
    },
    T9: { // 
      title: "T9",
      type: "switch",
      value: false,
      readonly: false,
      defaultvalue: true,
      order: 9
    },
    S9: { // 
      title: "S9",
      type: "text",
      value: "blank",
      readonly: true,
      defaultvalue: true,
      order: 109
    },
    T10: { // 
      title: "T10",
      type: "switch",
      value: false,
      readonly: false,
      defaultvalue: true,
      order: 10
    },
    S10: { // 
      title: "S10",
      type: "text",
      value: "blank",
      readonly: true,
      defaultvalue: true,
      order: 110
    }
  },
});

//RULES
//  CHNAGE!!!!
dev_index = 1;
defineRule("T" + dev_index, {
  whenChanged: [dn + "/T" + dev_index],
  then: function (newValue, devName, cellName) {
    //  CHNAGE!!!!
    var dev_index = 1;
    var timer_name = dn + "_T" + dev_index;
    debug("Правило: {}.{} = {}", devName, cellName, newValue) // вывод сообщения в лог   
    if (newValue == true) {
      startTicker(timer_name, TT_01_PERIOD);
      dt[dev_index] = new Date();
    }
    else {
      _wbStopTimer(timer_name);
      debug(cellName + " stoped");
    }
    dev[dn + "/S" + dev_index] = "0";
  }
});

defineRule({
  //  CHNAGE!!!!
  when: function () { return _wbCheckCurrentTimer(dn + "_T1"); },
  then: function () {
    //  CHNAGE!!!!
    var dev_index = 1;
    var timer_name = dn + "_T" + dev_index;
    _wbStopTimer(timer_name);
    dtl = Math.round((new Date() - dt[dev_index]) / 10) / 100;
    if (DEBUG > 4) debug(dn + "1_T" + dev_index + " " + dtl + " сек.");
    dev[dn + "/S" + dev_index] = String(dtl);
    dt[dev_index] = new Date();
    startTicker(timer_name, TT_01_PERIOD);
  }
});

//  CHNAGE!!!!
dev_index = 2;
defineRule("T" + dev_index, {
  whenChanged: [dn + "/T" + dev_index],
  then: function (newValue, devName, cellName) {
    //  CHNAGE!!!!
    var dev_index = 2;
    var timer_name = dn + "_T" + dev_index;
    debug("Правило: {}.{} = {}", devName, cellName, newValue) // вывод сообщения в лог   
    if (newValue == true) {
      startTicker(timer_name, TT_01_PERIOD);
      dt[dev_index] = new Date();
    }
    else {
      _wbStopTimer(timer_name);
      debug(cellName + " stoped");
    }
    dev[dn + "/S" + dev_index] = "0";
  }
});

defineRule({
  //  CHNAGE!!!!
  when: function () { return _wbCheckCurrentTimer(dn + "_T2"); },
  then: function () {
    //  CHNAGE!!!!
    var dev_index = 2;
    var timer_name = dn + "_T" + dev_index;
    _wbStopTimer(timer_name);
    dtl = Math.round((new Date() - dt[dev_index]) / 10) / 100;
    if (DEBUG > 4) debug(dn + "1_T" + dev_index + " " + dtl + " сек.");
    dev[dn + "/S" + dev_index] = String(dtl);
    dt[dev_index] = new Date();
    startTicker(timer_name, TT_01_PERIOD);
  }
});

//  CHNAGE!!!!
dev_index = 3;
defineRule("T" + dev_index, {
  whenChanged: [dn + "/T" + dev_index],
  then: function (newValue, devName, cellName) {
    //  CHNAGE!!!!
    var dev_index = 3;
    var timer_name = dn + "_T" + dev_index;
    debug("Правило: {}.{} = {}", devName, cellName, newValue) // вывод сообщения в лог   
    if (newValue == true) {
      startTicker(timer_name, TT_01_PERIOD);
      dt[dev_index] = new Date();
    }
    else {
      _wbStopTimer(timer_name);
      debug(cellName + " stoped");
    }
    dev[dn + "/S" + dev_index] = "0";
  }
});

defineRule({
  //  CHNAGE!!!!
  when: function () { return _wbCheckCurrentTimer(dn + "_T3"); },
  then: function () {
    //  CHNAGE!!!!
    var dev_index = 3;
    var timer_name = dn + "_T" + dev_index;
    _wbStopTimer(timer_name);
    dtl = Math.round((new Date() - dt[dev_index]) / 10) / 100;
    if (DEBUG > 4) debug(dn + "1_T" + dev_index + " " + dtl + " сек.");
    dev[dn + "/S" + dev_index] = String(dtl);
    dt[dev_index] = new Date();
    startTicker(timer_name, TT_01_PERIOD);
  }
});

//  CHNAGE!!!!
dev_index = 4;
defineRule("T" + dev_index, {
  whenChanged: [dn + "/T" + dev_index],
  then: function (newValue, devName, cellName) {
    //  CHNAGE!!!!
    var dev_index = 4;
    var timer_name = dn + "_T" + dev_index;
    debug("Правило: {}.{} = {}", devName, cellName, newValue) // вывод сообщения в лог   
    if (newValue == true) {
      startTicker(timer_name, TT_01_PERIOD);
      dt[dev_index] = new Date();
    }
    else {
      _wbStopTimer(timer_name);
      debug(cellName + " stoped");
    }
    dev[dn + "/S" + dev_index] = "0";
  }
});

defineRule({
  //  CHNAGE!!!!
  when: function () { return _wbCheckCurrentTimer(dn + "_T4"); },
  then: function () {
    //  CHNAGE!!!!
    var dev_index = 4;
    var timer_name = dn + "_T" + dev_index;
    _wbStopTimer(timer_name);
    dtl = Math.round((new Date() - dt[dev_index]) / 10) / 100;
    if (DEBUG > 4) debug(dn + "1_T" + dev_index + " " + dtl + " сек.");
    dev[dn + "/S" + dev_index] = String(dtl);
    dt[dev_index] = new Date();
    startTicker(timer_name, TT_01_PERIOD);
  }
});

//  CHNAGE!!!!
dev_index = 5;
defineRule("T" + dev_index, {
  whenChanged: [dn + "/T" + dev_index],
  then: function (newValue, devName, cellName) {
    //  CHNAGE!!!!
    var dev_index = 5;
    var timer_name = dn + "_T" + dev_index;
    debug("Правило: {}.{} = {}", devName, cellName, newValue) // вывод сообщения в лог   
    if (newValue == true) {
      startTicker(timer_name, TT_01_PERIOD);
      dt[dev_index] = new Date();
    }
    else {
      _wbStopTimer(timer_name);
      debug(cellName + " stoped");
    }
    dev[dn + "/S" + dev_index] = "0";
  }
});

defineRule({
  //  CHNAGE!!!!
  when: function () { return _wbCheckCurrentTimer(dn + "_T5"); },
  then: function () {
    //  CHNAGE!!!!
    var dev_index = 5;
    var timer_name = dn + "_T" + dev_index;
    _wbStopTimer(timer_name);
    dtl = Math.round((new Date() - dt[dev_index]) / 10) / 100;
    if (DEBUG > 4) debug(dn + "1_T" + dev_index + " " + dtl + " сек.");
    dev[dn + "/S" + dev_index] = String(dtl);
    dt[dev_index] = new Date();
    startTicker(timer_name, TT_01_PERIOD);
  }
});

//  CHNAGE!!!!
dev_index = 6;
defineRule("T" + dev_index, {
  whenChanged: [dn + "/T" + dev_index],
  then: function (newValue, devName, cellName) {
    //  CHNAGE!!!!
    var dev_index = 6;
    var timer_name = dn + "_T" + dev_index;
    debug("Правило: {}.{} = {}", devName, cellName, newValue) // вывод сообщения в лог   
    if (newValue == true) {
      startTicker(timer_name, TT_01_PERIOD);
      dt[dev_index] = new Date();
    }
    else {
      _wbStopTimer(timer_name);
      debug(cellName + " stoped");
    }
    dev[dn + "/S" + dev_index] = "0";
  }
});

defineRule({
  //  CHNAGE!!!!
  when: function () { return _wbCheckCurrentTimer(dn + "_T6"); },
  then: function () {
    //  CHNAGE!!!!
    var dev_index = 6;
    var timer_name = dn + "_T" + dev_index;
    _wbStopTimer(timer_name);
    dtl = Math.round((new Date() - dt[dev_index]) / 10) / 100;
    if (DEBUG > 4) debug(dn + "1_T" + dev_index + " " + dtl + " сек.");
    dev[dn + "/S" + dev_index] = String(dtl);
    dt[dev_index] = new Date();
    startTicker(timer_name, TT_01_PERIOD);
  }
});

//  CHNAGE!!!!
dev_index = 7;
defineRule("T" + dev_index, {
  whenChanged: [dn + "/T" + dev_index],
  then: function (newValue, devName, cellName) {
    //  CHNAGE!!!!
    var dev_index = 7;
    var timer_name = dn + "_T" + dev_index;
    debug("Правило: {}.{} = {}", devName, cellName, newValue) // вывод сообщения в лог   
    if (newValue == true) {
      startTicker(timer_name, TT_01_PERIOD);
      dt[dev_index] = new Date();
    }
    else {
      _wbStopTimer(timer_name);
      debug(cellName + " stoped");
    }
    dev[dn + "/S" + dev_index] = "0";
  }
});

defineRule({
  //  CHNAGE!!!!
  when: function () { return _wbCheckCurrentTimer(dn + "_T7"); },
  then: function () {
    //  CHNAGE!!!!
    var dev_index = 7;
    var timer_name = dn + "_T" + dev_index;
    _wbStopTimer(timer_name);
    dtl = Math.round((new Date() - dt[dev_index]) / 10) / 100;
    if (DEBUG > 4) debug(dn + "1_T" + dev_index + " " + dtl + " сек.");
    dev[dn + "/S" + dev_index] = String(dtl);
    dt[dev_index] = new Date();
    startTicker(timer_name, TT_01_PERIOD);
  }
});
//  CHNAGE!!!!
dev_index = 8;
defineRule("T" + dev_index, {
  whenChanged: [dn + "/T" + dev_index],
  then: function (newValue, devName, cellName) {
    //  CHNAGE!!!!
    var dev_index = 8;
    var timer_name = dn + "_T" + dev_index;
    debug("Правило: {}.{} = {}", devName, cellName, newValue) // вывод сообщения в лог   
    if (newValue == true) {
      startTicker(timer_name, TT_01_PERIOD);
      dt[dev_index] = new Date();
    }
    else {
      _wbStopTimer(timer_name);
      debug(cellName + " stoped");
    }
    dev[dn + "/S" + dev_index] = "0";
  }
});

defineRule({
  //  CHNAGE!!!!
  when: function () { return _wbCheckCurrentTimer(dn + "_T8"); },
  then: function () {
    //  CHNAGE!!!!
    var dev_index = 8;
    var timer_name = dn + "_T" + dev_index;
    _wbStopTimer(timer_name);
    dtl = Math.round((new Date() - dt[dev_index]) / 10) / 100;
    if (DEBUG > 4) debug(dn + "1_T" + dev_index + " " + dtl + " сек.");
    dev[dn + "/S" + dev_index] = String(dtl);
    dt[dev_index] = new Date();
    startTicker(timer_name, TT_01_PERIOD);
  }
});
//  CHNAGE!!!!
dev_index = 9;
defineRule("T" + dev_index, {
  whenChanged: [dn + "/T" + dev_index],
  then: function (newValue, devName, cellName) {
    //  CHNAGE!!!!
    var dev_index = 9;
    var timer_name = dn + "_T" + dev_index;
    debug("Правило: {}.{} = {}", devName, cellName, newValue) // вывод сообщения в лог   
    if (newValue == true) {
      startTicker(timer_name, TT_01_PERIOD);
      dt[dev_index] = new Date();
    }
    else {
      _wbStopTimer(timer_name);
      debug(cellName + " stoped");
    }
    dev[dn + "/S" + dev_index] = "0";
  }
});

defineRule({
  //  CHNAGE!!!!
  when: function () { return _wbCheckCurrentTimer(dn + "_T9"); },
  then: function () {
    //  CHNAGE!!!!
    var dev_index = 9;
    var timer_name = dn + "_T" + dev_index;
    _wbStopTimer(timer_name);
    dtl = Math.round((new Date() - dt[dev_index]) / 10) / 100;
    if (DEBUG > 4) debug(dn + "1_T" + dev_index + " " + dtl + " сек.");
    dev[dn + "/S" + dev_index] = String(dtl);
    dt[dev_index] = new Date();
    startTicker(timer_name, TT_01_PERIOD);
  }
});
//  CHNAGE!!!!
dev_index = 10;
defineRule("T" + dev_index, {
  whenChanged: [dn + "/T" + dev_index],
  then: function (newValue, devName, cellName) {
    //  CHNAGE!!!!
    var dev_index = 10;
    var timer_name = dn + "_T" + dev_index;
    debug("Правило: {}.{} = {}", devName, cellName, newValue) // вывод сообщения в лог   
    if (newValue == true) {
      startTicker(timer_name, TT_01_PERIOD);
      dt[dev_index] = new Date();
    }
    else {
      _wbStopTimer(timer_name);
      debug(cellName + " stoped");
    }
    dev[dn + "/S" + dev_index] = "0";
  }
});

defineRule({
  //  CHNAGE!!!!
  when: function () { return _wbCheckCurrentTimer(dn + "_T10"); },
  then: function () {
    //  CHNAGE!!!!
    var dev_index = 10;
    var timer_name = dn + "_T" + dev_index;
    _wbStopTimer(timer_name);
    dtl = Math.round((new Date() - dt[dev_index]) / 10) / 100;
    if (DEBUG > 4) debug(dn + "1_T" + dev_index + " " + dtl + " сек.");
    dev[dn + "/S" + dev_index] = String(dtl);
    dt[dev_index] = new Date();
    startTicker(timer_name, TT_01_PERIOD);
  }
});


И немного резюмируя. Похоже, что мы намудрили в коде и плохо посчитали все периодические циклы, их вложенность и скорость их выполнения. Понемногу эту ситуацию мы выправляем. Основная проблема в том, что при такой загрузке системы появляется очень большая задержка в обработке правил. Я так понимаю, что это ограничение JS. Приоритезацию тут сделать нельзя, а хотелось бы.

1 лайк

И вопрос общего плана.
Функции
_wbCheckCurrentTimer(string)
и
_wbStopTimer(string)
Я ведь могу использовать в коде? Документированных альтернатив я не нашел.

Скорее текущего движка. В планах есть его очередной рефакторинг с заменой части кода.

Да, конечно.

Рефакторинг это конечно не плохо. Но в продолжении философии организации правил для текущей версии движка есть такой вопрос:
Вводная. (код мы написали и он у нас есть рабочий, но я крайне не уверен в его оптимальности)
Итак, у нас есть исполнительный механизм. Будем честны - это щетка пола. Она в не рабочем состоянии находится в стене, закрытая крышкой. И крышка и щетка имеет привод и по 2 концевика на min и max. Что бы выполнить работу по помывке пола, необходимо выполнить следующую последовательность задач:

  1. открыть крышку щетки. (до концевика на крышке)
  2. дождаться открытия крышки щетки по концевику. Если крышка не открылась за 1с уходим в аварию.
  3. Начать движение щетки по полу.
  4. ждать пока щетка доедет до концевика max. Если не доехало за 10с уходим в аварию.
  5. когда доехали щеткой до MAX, готовимся к пути назад:
    5.1 выключаем питание щетки.
    5.2 пауза не менее 100мс
    5.3 переключаем полярность
    5.4 пауза не менее 100мс
    5.5 включаем питание щетки и едем назад.
  6. Доехали назад до концевика МИН. если за 10 сек назад не доехали - в аварию.
  7. Закрыли крышку щетки. если крышка за 1с не закрылась - авария.

Вопрос. Какая с вашей точки зрения должна быть структура и иерархия правил для решения подобной робототехнической задачи?

А еще задачу можно усугубить, и перед аварией попытаться выполнить не закончившуюся операцию еще 2 раза.

Получается классическая машина состояний (конечный автомат)
Реализовать можно, например через общий “флаг” (или счетчик).
Значение счетчика 0 - исходное состояние.

  • Значение 1 - выполняется операция открытия крышки, в случае успеха флаг меняется на 2.
  • Значение 2 - начинается движение щетки “туда”, успех (с несколькими попытками, возможно) - значение 3
  • Движение щетки обратно, если успех - значение 4
  • Закрытие крышки, если успех - то значение опять 0
    Ну и, соответственно уже для каждой из операций - функции, ее реализующие.
    Естественно, в случае “неудачи” - обработка и (или) оповещение. Например в случае неудачи шага 2 - пробуем шаг 3 (возврат щетки в исходное положение).
    Так, “шагами” у меня например приведение манипулятора в исходное состояние было сделано.

Да, конечный автомат это наше все. А как таймеры и задержки то правильно добавлять и обрабатывать? я же в коде delay задать не могу. с таймерами еще куда ни шло, а вот как проще делать задержки, что бы не блокировать движок?
У нас это каскадные setTimeout. Страшненько выглядит.

Да тут несложно на самом деле.
Запускаем таймер а в обработчик ему действие сразу.
С тем же приводом - включаем реле, запускаем таймер. Либо сработает он, выдавая ошибку таймаута и выключая реле либо сработает правило концевика, в правиле будет остановлен таймер и выключено реле опять же.
Кстати, выключать привод можно и аппаратно, заведя концевики на входы Modbus модуля реле.

В целом так мы на это и смотрим. Пока получается некая слабочитаемая структура кода, особенно если еще добавить в алгоритмы “если не доехали до концевика, попробуй еще 2 раза. и лишь потом выдавай ошибку.”
Привод мы выключаем по комбинированному концевику, он механически отключает питание у привода, ну и как концевик работает. Скорость обмена то не предсказуемая. Механика рулит.

1 запись была перенесена в новую тему: Установка Wireguard

Эта тема была автоматически закрыта через 7 дней после последнего ответа. В ней больше нельзя отвечать.