Wb-rules gsm modem watchdog

всем привет.
Удалённый объект. WB8 (Welcome to Wiren Board 8.5.3) с встроенным GSM модемом. По неустановленным причинам примерно раз в сутки модем отваливается от сети, и восстановить соединение можно только отправив миньона, который передёрнет автомат на питании, а с установленными supercup приходится ещё и ждать пару минут пока погаснет “лампочка“. Сначала написал скрипт, который тупо раз в сутки ребутит весь контроллер. Потом появилось немного времени, накатал rules которое проверяет пинг на 8.8.8.8 и если 10 минут нет ответа, то ребутит модем. Может кому то поможет.


После добавления правила, появляется новый девайс, где видно основные параметры

// Настройки Watchdog для Wiren Board 8 + ModemManager
var pingHost = "8.8.8.8";
var checkIntervalMs = 60 * 1000; // Проверка раз в минуту
var maxFailures = 10; // Перезагрузка после 10 минут (10 ошибок)

// Переменные
var failureCounter = 0;
var currentConnection = "wb-gsm-sim1"; // Соединение по умолчанию (на случай, если не удастся определить)

defineVirtualDevice("network_watchdog", {
    title: "Network Watchdog",
    cells: {
        "status": { type: "text", value: "OK", readonly: true },
        "errors_count": { type: "value", value: 0, readonly: true },
        "last_restart": { type: "text", value: "Never", readonly: true },
        "active_connection": { type: "text", value: currentConnection, readonly: true }
    }
});

// Функция определения активного GSM-соединения
function updateActiveGsmConnection() {
    // nmcli -t -f TYPE,NAME connection show --active
    // Вывод будет в формате: тип:имя
    // Ищем строку, начинающуюся с "gsm:"
    runShellCommand("nmcli -t -f TYPE,NAME connection show --active", {
        captureOutput: true,
        exitCallback: function (exitCode, output) {
            if (exitCode === 0 && output) {
                var lines = output.split("\n");
                for (var i = 0; i < lines.length; i++) {
                    var line = lines[i].trim();
                    if (line.indexOf("gsm:") === 0) {
                        // Нашли GSM соединение (формат gsm:имя)
                        var connName = line.substring(4); // Берем подстроку после "gsm:"
                        if (connName && connName.length > 0) {
                            if (currentConnection !== connName) {
                                log("Network Watchdog: Active connection detected: " + connName);
                                currentConnection = connName;
                                dev["network_watchdog"]["active_connection"] = currentConnection;
                            }
                        }
                        // Если нашли одно, выходим (предполагаем один активный модем в данный момент)
                        return;
                    }
                }
            }
        }
    });
}

function checkConnection() {
    // Сначала обновляем информацию об активном соединении (если оно есть)
    updateActiveGsmConnection();

    // Пинг
    runShellCommand("ping -c 1 -W 5 " + pingHost + " > /dev/null 2>&1", {
        captureOutput: false,
        exitCallback: function (exitCode, capturedOutput) {
            if (exitCode === 0) {
                // СВЯЗЬ ЕСТЬ
                if (failureCounter > 0) {
                    log("Network Watchdog: Connection restored automatically.");
                }
                failureCounter = 0;
                dev["network_watchdog"]["status"] = "OK";
            } else {
                // СВЯЗИ НЕТ
                failureCounter++;
                dev["network_watchdog"]["status"] = "ERROR";
                log("Network Watchdog: Ping failed (" + failureCounter + "/" + maxFailures + ")");
            }

            // Обновляем счетчик
            dev["network_watchdog"]["errors_count"] = failureCounter;

            // ПОРА ПЕРЕЗАГРУЖАТЬ
            if (failureCounter >= maxFailures) {
                log("Network Watchdog: MAX failures reached. Restarting ModemManager and connection: " + currentConnection);
                
                dev["network_watchdog"]["last_restart"] = new Date().toLocaleString();
                dev["network_watchdog"]["status"] = "RESTARTING";

                // Формируем команду восстановления динамически, используя текущее (или последнее известное) соединение
                var restartCmd = "systemctl restart ModemManager; sleep 65; nmcli connection up \"" + currentConnection + "\" || true";

                // Запуск команды восстановления
                runShellCommand(restartCmd, {
                    exitCallback: function (code) {
                        // Код 0 значит, что скрипт отработал (даже если связь уже была)
                        log("Network Watchdog: Recovery sequence finished.");
                    }
                });

                // Сбрасываем счетчик на 0.
                // Это дает системе "кредит доверия" еще на 10 минут.
                failureCounter = 0; 
            }
        }
    });
}

// Запуск
setInterval(checkConnection, checkIntervalMs);

Добрый день.

Я бы предпочел разобраться что происходит, лучше.

Не очень хорошо - зачем перезапускать сервис если достаточно соединение?
Например у меня оператор выдает при соединении с ограничением адрес из другой подсети. Я его проверяю - и переподнимаю соединение.

log.info("strt")
//Определим переменные.
var interfaceControlName = "network/GPRS IP" //топик с адресом соединения
var checkPeriod = 5000 //период проверки, мс
var connectionName = "wb-gsm-sim1" //имя соединения

var partIP = "100.211" //старшая часть адреса, текст

//Опишем, пожалуй, функцию проверки отдельно
function interfaceControl(cName, ipPart){ //если не подключено или подключено с верным адресов - возвращается true; иначе - false
  var currentAddressString = dev[cName];
  log.debug("При проверке текущего адреса получен", currentAddressString)
  if (currentAddressString == null) {
    log.debug("адреса нет");
    return true;
  } else { //Тут проверим адрес (Который не null) на соответствие заданному
    if ((currentAddressString.indexOf("ipPart",0)) == 0){
      log.debug("адреса верный");
      return true;
    }
  }
  log.debug("адрес неверный");
  return false; 
}

//функция разрывающая соединения
function nmcliReconnect(conName){ //аргумент - имя соединения NM
  runShellCommand("nmcli connection down "+ conName+"; sleep 6; nmcli connection up "+conName, {
    captureOutput: true,
    exitCallback: function(exitCode, capturedOutput) {
        if (exitCode === 0) {
            return true
        }
    }
});

}


//log.info("проверяем", interfaceControl(interfaceControlName, partIP))

//тут опишем правило, которое будет срабатывать при изменении самого адреса. И вызывать функцию.
var checkRule = defineRule("change_IP", {
  whenChanged: interfaceControlName, // топик, при изменении которого сработает правило
  then: function (newValue, devName, cellName) {
    log.info("devName:{}, cellName:{}, newValue:{}", devName, cellName, newValue); // вывод сообщения в лог
    log.debug("devName:{}, cellName:{}, newValue:{}", devName, cellName, newValue); // вывод сообщения в лог
    if (!interfaceControl(interfaceControlName, partIP)){
      log.debug("при смене адреса новый:{} не прошел проверку", newValue); // вывод сообщения в лог
      nmcliReconnect(connectionName)
      log.info("соединение:{} перезапущено. Адрес {} некорректен", connectionName, newValue);
    } else {
      log.info("при смене адреса новый:{} пуст или прошел проверку", newValue);
    }
 }
});

runRule(checkRule)

То есть в зависимости от цели механизм проверки и реакции может быть разным. Ну и реализуется по-разному.