Приоритетное подключение камер и регистратора для распределения лицензий ANYIP

Сообщения
202
Реакции
0
При работе с большим количеством IP-камер в TRASSIR может возникнуть проблема с распределением лицензий.

В моём случае на сервере используется около 40 камер разных производителей: Hikvision, HiWatch, китайские камеры и другие устройства. Лицензии также используются разных типов, в том числе ANYIP.

Проблема проявлялась после перезагрузки сервера TRASSIR:
  • часть камер должна была получить лицензии ANYIP;
  • сетевой видеорегистратор подключался раньше нужных камер;
  • регистратор занимал доступные лицензии;
  • несколько приоритетных камер оставались без лицензии;
  • приходилось вручную отключать регистратор, подключать камеры и затем снова включать регистратор.

Решил автоматизировать этот процесс с помощью встроенного Python-скрипта TRASSIR.

Что делает скрипт​


После запуска TRASSIR сценарий автоматически:
  1. Находит регистратор по IP-адресу.
  2. Временно отключает его.
  3. Отключает приоритетные камеры.
  4. Ждёт освобождения лицензий.
  5. Повторно включает приоритетные камеры.
  6. Проверяет появление реального видеопотока от каждой камеры.
  7. Несколько раз проверяет стабильность видеопотока.
  8. Только после успешного подключения всех приоритетных камер включает регистратор.
  9. Если одна из камер не появилась в течение заданного времени, срабатывает аварийный таймаут.

То есть схема получается следующая:
Код:
TRASSIR запускается
        |
        v
Регистратор -> OFF
        |
        v
Освобождаются лицензии
        |
        v
Приоритетные камеры -> OFF -> ON
        |
        v
Ожидание видеопотока от всех камер
        |
        v
Проверка стабильности
        |
        v
Регистратор -> ON

Таким образом устройство, которое раньше забирало лицензии, подключается последним.

Готовый скрипт​


Python:
# -*- coding: utf-8 -*-

# ============================================================
# TRASSIR ANYIP PRIORITY STARTUP
#
# Сначала подключаются камеры из PRIORITY_IPS.
# Регистратор NVR_IP подключается последним.
# ============================================================

import time


# ------------------------------------------------------------
# НАСТРОЙКИ
# ------------------------------------------------------------

PRIORITY_IPS = [
    "192.168.0.145",
    "192.168.0.127",
    "192.168.0.124",
    "192.168.0.121",
    "192.168.0.120",
    "192.168.0.118",
]

# Устройство, которое должно подключаться последним
NVR_IP = "192.168.0.238"


# Небольшая задержка после запуска скрипта
INITIAL_DELAY_MS = 5000

# Время на освобождение лицензий после отключения регистратора
LICENSE_RELEASE_DELAY_MS = 5000

# Интервал проверки состояния камер
CHECK_INTERVAL_MS = 2000

# Количество успешных проверок видеопотока подряд
STABLE_CHECKS_REQUIRED = 3

# Дополнительная задержка перед включением регистратора
FINAL_DELAY_MS = 5000

# Максимальное время ожидания камер
MAX_WAIT_SEC = 180

# True  - после таймаута всё равно запустить регистратор
# False - не запускать регистратор, пока не заработают все камеры
FORCE_NVR_AFTER_TIMEOUT = True


# ------------------------------------------------------------
# СЛУЖЕБНЫЕ ПЕРЕМЕННЫЕ
# ------------------------------------------------------------

wait_started = 0
stable_checks = 0
poll_counter = 0
finished = False
prepare_attempts = 0


# ------------------------------------------------------------
# ЛОГ
# ------------------------------------------------------------

def log(text):
    message("[ANYIP PRIORITY] " + text)


# ------------------------------------------------------------
# ПОИСК IP-УСТРОЙСТВА
# ------------------------------------------------------------

def find_device(ip):

    try:
        devices = settings("ip_cameras").ls()
    except Exception as e:
        log("Cannot read ip_cameras: %s" % str(e))
        return None

    for dev in devices:

        try:
            if dev.type != "Grabber":
                continue

            if dev["connection_ip"] == ip:
                return dev

        except:
            pass

    return None


# ------------------------------------------------------------
# ВКЛЮЧЕНИЕ / ВЫКЛЮЧЕНИЕ УСТРОЙСТВА
# ------------------------------------------------------------

def set_device_enabled(ip, enabled):

    dev = find_device(ip)

    if dev is None:
        log("Device not found: %s" % ip)
        return False

    try:
        dev["grabber_enabled"] = 1 if enabled else 0

        if enabled:
            log("ENABLE  %s" % ip)
        else:
            log("DISABLE %s" % ip)

        return True

    except Exception as e:
        log("Error changing %s: %s" % (ip, str(e)))
        return False


# ------------------------------------------------------------
# ПОЛУЧЕНИЕ FPS ОСНОВНОГО КАНАЛА
# ------------------------------------------------------------

def get_camera_fps(ip):

    dev = find_device(ip)

    if dev is None:
        return -1

    try:
        channel_guid = dev["channel00_guid"]

        if not channel_guid:
            return 0

        stats = settings(
            "channels/%s/stats" % channel_guid
        )

        fps = float(stats["fps_main"])

        return fps

    except:
        return 0


# ------------------------------------------------------------
# ЗАПУСК РЕГИСТРАТОРА
# ------------------------------------------------------------

def start_nvr():

    global finished

    if finished:
        return

    finished = True

    log("------------------------------------------")
    log("Starting NVR LAST: %s" % NVR_IP)

    if set_device_enabled(NVR_IP, True):
        log("NVR started successfully")
    else:
        log("ERROR: NVR could not be started")

    log("Priority startup procedure finished")
    log("------------------------------------------")


def start_nvr_after_delay():

    log(
        "All priority cameras are stable. "
        "NVR will start in %d sec."
        % (FINAL_DELAY_MS / 1000)
    )

    timeout(
        FINAL_DELAY_MS,
        start_nvr
    )


# ------------------------------------------------------------
# ПРОВЕРКА ПРИОРИТЕТНЫХ КАМЕР
# ------------------------------------------------------------

def check_priority_cameras():

    global stable_checks
    global poll_counter

    if finished:
        return

    poll_counter += 1

    ready_count = 0
    not_ready = []

    for ip in PRIORITY_IPS:

        dev = find_device(ip)

        if dev is not None:
            try:
                if dev["grabber_enabled"] == 0:
                    dev["grabber_enabled"] = 1
                    log("Re-enabled priority camera: %s" % ip)
            except:
                pass

        fps = get_camera_fps(ip)

        if fps > 0:
            ready_count += 1
        else:
            not_ready.append(ip)


    if poll_counter % 5 == 0:

        log(
            "Priority cameras ready: %d/%d"
            % (ready_count, len(PRIORITY_IPS))
        )

        if len(not_ready) > 0:
            log(
                "Waiting for: %s"
                % ", ".join(not_ready)
            )


    if ready_count == len(PRIORITY_IPS):

        stable_checks += 1

        log(
            "All cameras have video. Stability check %d/%d"
            % (
                stable_checks,
                STABLE_CHECKS_REQUIRED
            )
        )

        if stable_checks >= STABLE_CHECKS_REQUIRED:

            start_nvr_after_delay()
            return

    else:
        stable_checks = 0


    elapsed = time.time() - wait_started

    if elapsed >= MAX_WAIT_SEC:

        log("------------------------------------------")
        log(
            "WARNING: timeout %d sec reached"
            % MAX_WAIT_SEC
        )

        if len(not_ready) > 0:
            log(
                "Cameras without video: %s"
                % ", ".join(not_ready)
            )

        if FORCE_NVR_AFTER_TIMEOUT:

            log("Emergency mode: starting NVR anyway")
            start_nvr()
            return

        else:

            log("Strict mode: NVR remains disabled")


    timeout(
        CHECK_INTERVAL_MS,
        check_priority_cameras
    )


# ------------------------------------------------------------
# ВКЛЮЧЕНИЕ ПРИОРИТЕТНЫХ КАМЕР
# ------------------------------------------------------------

def start_priority_cameras():

    global wait_started
    global stable_checks
    global poll_counter

    stable_checks = 0
    poll_counter = 0

    log("------------------------------------------")
    log("Starting priority cameras")

    for ip in PRIORITY_IPS:
        set_device_enabled(ip, True)

    wait_started = time.time()

    log(
        "Waiting for real video from all %d cameras..."
        % len(PRIORITY_IPS)
    )

    timeout(
        CHECK_INTERVAL_MS,
        check_priority_cameras
    )


# ------------------------------------------------------------
# ПОДГОТОВКА
# ------------------------------------------------------------

def prepare():

    global prepare_attempts

    prepare_attempts += 1

    log("Preparing priority startup...")

    nvr = find_device(NVR_IP)

    if nvr is None:

        if prepare_attempts < 30:

            log(
                "NVR %s not found yet. Retry..."
                % NVR_IP
            )

            timeout(
                2000,
                prepare
            )

            return

        log(
            "ERROR: NVR %s was not found"
            % NVR_IP
        )

        return


    log("------------------------------------------")
    log("Disabling NVR first: %s" % NVR_IP)

    set_device_enabled(
        NVR_IP,
        False
    )


    log("Resetting priority cameras...")

    for ip in PRIORITY_IPS:

        dev = find_device(ip)

        if dev is None:

            log(
                "WARNING: priority device not found: %s"
                % ip
            )

        else:

            try:
                dev["grabber_enabled"] = 0
                log("RESET OFF %s" % ip)

            except Exception as e:
                log(
                    "Cannot disable %s: %s"
                    % (ip, str(e))
                )


    log(
        "Waiting %d sec for licenses to be released..."
        % (LICENSE_RELEASE_DELAY_MS / 1000)
    )

    timeout(
        LICENSE_RELEASE_DELAY_MS,
        start_priority_cameras
    )


# ------------------------------------------------------------
# СТАРТ СКРИПТА
# ------------------------------------------------------------

log("==========================================")
log("ANYIP Priority Startup script loaded")
log("NVR must start LAST: %s" % NVR_IP)
log("==========================================")

timeout(
    INITIAL_DELAY_MS,
    prepare
)


Что необходимо изменить под свой сервер​


В большинстве случаев достаточно изменить только два параметра.

Список камер, которые должны получить лицензии первыми:
Python:
PRIORITY_IPS = [
    "192.168.0.145",
    "192.168.0.127",
    "192.168.0.124",
]

И IP устройства, которое должно подключаться последним:
Python:
NVR_IP = "192.168.0.238"

Количество приоритетных камер может быть другим.

Например:
Python:
PRIORITY_IPS = [
    "10.0.0.11",
    "10.0.0.12",
    "10.0.0.15",
    "10.0.0.20",
]

Как работает проверка камер​


Скрипт не просто ждёт фиксированное количество секунд.

Для каждой камеры проверяется наличие реального видеопотока.

Если поток появился, камера считается подключившейся.

После того как видео появилось от всех приоритетных камер, выполняется несколько дополнительных проверок:

Python:
STABLE_CHECKS_REQUIRED = 3

По умолчанию проверка выполняется каждые 2 секунды:
Python:
CHECK_INTERVAL_MS = 2000

То есть видеопоток должен сохраняться примерно 6 секунд подряд.

После этого скрипт ждёт ещё 5 секунд и включает последнее устройство.


Аварийный таймаут​


Если одна из приоритетных камер неисправна или недоступна по сети, ждать её бесконечно не нужно.

Для этого используется:
Python:
MAX_WAIT_SEC = 180

Через 180 секунд регистратор будет запущен в любом случае:
Python:
FORCE_NVR_AFTER_TIMEOUT = True

Если требуется строгий режим, можно изменить:
Python:
FORCE_NVR_AFTER_TIMEOUT = False

В этом случае последнее устройство не будет включено, пока не появятся все приоритетные камеры.


Установка скрипта в TRASSIR​


  1. Открыть настройки TRASSIR.
  2. Перейти в раздел автоматизации/скриптов.
  3. Создать новый скрипт.
  4. Вставить код.
  5. Изменить PRIORITY_IPS под свои камеры.
  6. Указать NVR_IP.
  7. Сохранить и запустить скрипт.
  8. Первую проверку желательно выполнить без перезагрузки сервера.
  9. После успешного теста перезагрузить сервер и проверить автоматическую последовательность подключения устройств.


Логирование​


Все сообщения скрипта имеют префикс:
Код:
[ANYIP PRIORITY]

Например:
Код:
[ANYIP PRIORITY] Disabling NVR first: 192.168.0.238
[ANYIP PRIORITY] Starting priority cameras
[ANYIP PRIORITY] Priority cameras ready: 6/6
[ANYIP PRIORITY] All cameras have video. Stability check 3/3
[ANYIP PRIORITY] Starting NVR LAST: 192.168.0.238
[ANYIP PRIORITY] NVR started successfully


Важно​


Перед использованием обязательно замените IP-адреса из примера на адреса своих устройств.

Желательно сначала проверить сценарий на работающем сервере вручную, без полной перезагрузки.

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