Примеры драйверов

Пример, который можно проверить без оборудования, — драйвер для Local-транспорта (loopback). Local не выходит в сеть: всё, что драйвер «отправил», сразу возвращается к нему как полученное. Поэтому на нём можно учиться: написать драйвер, нажать «Развернуть» (кнопка U-Logic, которая запускает правки программы в работу) и сразу увидеть результат без устройства, адреса и порта. Так же проверяют логику драйвера до выезда на объект.

Как писать драйвер для настоящего устройства — на странице Как написать драйвер. Если нужен драйвер по описанию протокола, начните с наброска от ИИ — AI-Driver.

Драйвер для Local-транспорта

Драйвер умеет пять команд: вернуть сообщение, посчитать, увеличить счётчик, прочитать счётчик и показать, сколько он работает. Счётчик хранится в общей памяти U-Logic, в контексте global. Откуда берётся контекст в драйвере, описано на странице Расширенные возможности.

const BaseDriver = require('base-driver');

class LoopbackDemoDriver extends BaseDriver {
  static metadata = {
    name: 'Loopback Demo',
    manufacturer: 'Dynamic Driver',
    version: '1.0.0',
    description: 'Драйвер для проверки без устройства (Local-транспорт)',
    parameters: [
      { name: 'counterKey', default: 'demoCounter' }
    ]
  };

  static commands = {
    Echo: {
      description: 'Вернуть переданное сообщение',
      parameters: [
        { name: 'message', type: 'string', description: 'Сообщение', default: 'Привет!' }
      ]
    },
    Calculate: {
      description: 'Выполнить арифметическое действие',
      parameters: [
        { name: 'a', type: 'number', required: true },
        { name: 'b', type: 'number', required: true },
        { name: 'operation', type: 'string', enum: ['add', 'sub', 'mul', 'div'], default: 'add' }
      ]
    },
    IncrementCounter: {
      description: 'Увеличить счётчик',
      parameters: [
        { name: 'step', type: 'number', default: 1 }
      ]
    },
    GetCounter: {
      description: 'Прочитать счётчик'
    },
    GetStatus: {
      description: 'Сколько секунд работает драйвер'
    }
  };

  constructor(options) {
    super(options);
    this.startTime = Date.now();
  }

  // Команда возвращает строку JSON. Local сразу отдаёт её обратно как «полученные данные».
  Echo(params) {
    return JSON.stringify({
      command: 'Echo',
      message: params.message || 'Привет!',
      time: new Date().toISOString()
    });
  }

  Calculate(params) {
    const a = Number(params.a) || 0;
    const b = Number(params.b) || 0;
    const operation = params.operation || 'add';
    let result = null;
    let error = null;

    switch (operation) {
      case 'add': result = a + b; break;
      case 'sub': result = a - b; break;
      case 'mul': result = a * b; break;
      case 'div':
        if (b === 0) error = 'Деление на ноль';
        else result = a / b;
        break;
      default:
        error = `Неизвестная операция: ${operation}`;
    }
    return JSON.stringify({ command: 'Calculate', a, b, operation, result, error });
  }

  IncrementCounter(params) {
    const key = this.config.counterKey || 'demoCounter';
    const step = Number(params.step) || 1;
    const previous = global.get(key) || 0;
    global.set(key, previous + step);
    return JSON.stringify({
      command: 'IncrementCounter',
      previousValue: previous,
      step,
      newValue: previous + step
    });
  }

  GetCounter() {
    const key = this.config.counterKey || 'demoCounter';
    return JSON.stringify({ command: 'GetCounter', value: global.get(key) || 0 });
  }

  GetStatus() {
    return JSON.stringify({
      command: 'GetStatus',
      uptimeSeconds: Math.floor((Date.now() - this.startTime) / 1000)
    });
  }

  // Ответ приходит как обёртка: текст лежит в data.data
  parseResponse(data) {
    const text = String(data.data);
    try {
      return JSON.parse(text);
    } catch (e) {
      return { error: 'Ответ не удалось разобрать', raw: text };
    }
  }
}

module.exports = LoopbackDemoDriver;

Что показывает этот пример:

Что

Где в коде

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

parameters в metadata, чтение через this.config

параметры команд: число, строка, список значений, значение по умолчанию

static commands, команды Calculate и Echo

команда без параметров

GetCounter, GetStatus

команда возвращает строку, а не { payload }

любой метод команды — см. Методы команд

разбор ответа вручную

parseResponse — см. Ответы устройства

Разбор устроен так: ответа в static responses у драйвера нет, поэтому каждый ответ достаётся parseResponse. Он возвращает объект без поля type, а ответ без типа device - response - listener отдаёт на первый выход. О типах ответов и о выборе выхода — на странице Ответы устройства.

Как проверить

  1. Откройте редактор драйверов (Редактор драйверов). Создайте новый драйвер с именем LoopbackDemo, вставьте код выше и сохраните. Должно появиться зелёное сообщение «Сохранено».

  2. Добавьте на рабочую область узлы Inject, device - command, device - response - listener и Debug и соедините их: Inject — device - command, выход device - response - listener — Debug.

  3. Откройте device - command и рядом с полем «Соединение» создайте новое соединение: узел Device Connection (драйвер плюс транспорт к одному конкретному устройству, см. Device Connection: подключение драйвера к транспорту):

    • «Драйвер» — LoopbackDemo;

    • «Транспорт» — Local (loopback);

    • в поле «Local Config» создайте новый узел и сохраните его, не меняя поля (в нём только название и отладка; см. Local (loopback)).

  4. В device - command выберите команду Calculate и заполните параметры: a — 10, b — 5, operation — mul.

  5. Откройте device - response - listener, выберите то же соединение, в «Выходы» оставьте 1.

  6. Нажмите «Развернуть», затем кнопку на узле Inject.

Что вы увидите: в панели отладки появится сообщение, в msg.payload которого — объект:

{
  command: 'Calculate',
  a: 10,
  b: 5,
  operation: 'mul',
  result: 50,
  error: null
}

Проверьте остальное:

  1. Измените operation на div и b на 0, снова разверните поток и запустите: в result будет null, в error — «Деление на ноль».

  2. Выберите IncrementCounter и запустите дважды: newValue вырастет на шаг при каждом запуске, потому что счётчик хранится в общей памяти.

  3. Включите «Driver Debug» в Device Connection: в панель отладки попадут отправленная команда и «сырой» ответ до разбора.

Частые ошибки:

  • Ничего не пришло. Проверьте, что в device - response - listener выбрано то же соединение, а поток развёрнут после последней правки.

  • Сохранение отказало с «Синтаксические ошибки, сохранение отменено». В коде драйвера пропущена скобка или запятая — исправьте и сохраните снова.

  • Драйвер не виден в списке. Закройте окно узла и откройте его заново: список читается при открытии.

  • Пришёл объект с полем error: 'Ответ не удалось разобрать'. Ответ оказался не JSON — например, команда вернула не JSON.stringify(...).