В этом разделе перечислены свойства и методы объекта SerialTaskWrap

Назначение

Объект SerialTaskWrap предназначен для работы с заданиями на последовательном порту в контексте JavaScript

Использутся в строенный в ядро jCjS элеватор последовательных портов,
выполняющий (последовательно) задачи, переданные ему из постов jCjS

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

Такой подход позволяет размещать на одном последовательном порте несколько устройств,
работающих на различных протоколах или настройках последовательного порта


Конструктор

new SerialTaskWrap() Создать объект

// Создать объект
var task = new SerialTaskWrap();

// Сконфигурить последовательный порт
with(task)
{
    //---------------------------------------------------------
    //----- Настройки последовательного порта
    //---------------------------------------------------------

    portName        = 'COM1'; // Имя последовательного порта

    config(19200, 8, 1, 'even', 'none');

    // Управление приемо-передатчиком (актуально в rs485 и при необходимости)
    // (сколько держать передатчик включеным после записи в порт в милисекундах)
    rtsDelay        = 1;
    dtrDelay        = 1;

    priority        = 0;    // приоритет задания (чем выше число тем выше приоритет,
                            //   приоритетные задания будут размещены в начало очереди)

    timeoutRead     = 250;

    silence         =  32;    // гарантированная пауза между фреймами в символах
    debug           = false;  // отключить вывод отладочных сообщений в stdout


    // Команда, которая будет отсылаться в порт (modbus RTU)
    writeArray = [0x01, 0x01, 0x00, 0x00, 0x00, 0x02];

    // или (modbus ASCII)
    // writeString = ':010300050001EFD9\n\r';

    // Критерий останова чтения
    cntBytesToRead  = 6;    // количество байт
    stopChar        = 0;    // стоповый байт (не используется) (обычно в протоколах ASCII stopChar='\r' или '\n')
    timePacket      = 35;   // макс время прихода между пакетами (если посылка фрагментированна,
                            // т.е. сначала прочитали несколько байт, затем оставшиеся)

    modbus          = 'n'   // отключить поддержку модбас. (при включении будут анализироваться
                            //  исключения МОДБАС {Функция 0x81 - 0x8F}, а также автоматический
                            //  расчет количества принимаемых байт)
}

// Присоединить функцию обработчик к сигналу performed
task.performed.connect(function performed(){
    post.log( (task.error ? 'e' : 'i'),   'Выполнено ' + (task.error ? 'с ошибкой' : 'успешно') );
});

// Отправить задачу в очередь (после выполнения SerialTaskWrap эмитирует сигнал performed)
task.perform();


Свойства (property)

Пример использования:

// Создать объект
var task = new SerialTaskWrap();

//свойство параметра чтение/запись:

var wrArr = task.writeArray;        // Чтение

task.stopChar = '\r'.charCodeAt(0); // Запись

String task.portName Чтение/запись
Имя последовательного порта

task.portName = '/dev/ttyS1';

int task.baudRate Чтение/запись
скорость порта

task.baudRate = 9600; // 19200 по умолчанию

String task.parity Чтение/запись
наличие и тип бита четности

  • 0, '0', 'n', 'none' - нет контроля над четностью (по умолчанию)
  • 2, '2', 'e', 'even' - контроль над четностью EVEN
  • 3, '3', 'o', 'odd' - контроль над нечетностью ODD
  • 4, '4', 's', 'space'- контроль над четностью SPACE
  • 5, '5', 'm', 'mark' - контроль над четностью MARK
  • task.parity = 'n'; // эквивалентно 0, '0', 'none',  'N',  'NoNe'
    task.parity = 'e'; // эквивалентно 2, '2', 'even',  'E',  'Even'
    task.parity = 'o'; // эквивалентно 3, '3', 'odd',   'O',  'ODD'
    task.parity = 's'; // эквивалентно 4, '4', 'space', 'S',  'sPaCe'
    task.parity = 'm'; // эквивалентно 5, '5', 'mark',  'M',  'MARK'
    

    String task.flowControl Чтение/запись
    Управление потоком

    Внимание! Если стоит аппаратное/программное управление потоком,
    то управление приемопередатчиком rs485 запрещено (свойства task.rtsDelay и task.dtrDelay)

    task.flowControl = 'n'; // эквивалентно 0, '0', 'none',    'N', 'NoNe'
    task.flowControl = 'r'; // эквивалентно 1, '1', 'rts-cts', 'R', 'RtS', 'Hard'
    task.flowControl = 'x'; // эквивалентно 2, '2', 'xon-xoff','X', 'Xon', 'Soft'
    

    int task.stopBits Чтение/запись
    Количество стоповых бит.

    task.stopBits = 2;
    

    int task.dataBits Чтение/запись
    Количество бит данных (5,6,7,8). По умолчанию 8

    task.dataBits = 7;
    

    int task.rtsDelay Чтение/запись
    Управление приемо-передатчиком (актуально в rs485 и при необходимости)
    (сколько держать передатчик включеным после записи в порт в милисекундах)

    Внимание! Если стоит аппаратное/программное управление потоком (свойство task.flowControl),
    то управление приемопередатчиком rs485 запрещено

    Приемопередатчиком управляется выводом RTS. По умолчанию 0 (выключен)

    task.rtsDelay = 1;
    

    int task.dtrDelay Чтение/запись
    Управление приемо-передатчиком (актуально в rs485 и при необходимости)
    (сколько держать передатчик включеным после записи в порт в милисекундах)

    Внимание! Если стоит аппаратное/программное управление потоком (свойство task.flowControl),
    то управление приемопередатчиком rs485 запрещено

    Приемопередатчиком управляется выводом DTR. По умолчанию 0 (выключен)

    task.dtrDelay = 1;
    

    int task.priority Чтение/запись
    Приоритет задания (любое целое число) (по умолчанию 0).
    Чем выше значение, тем выше приоритет (будет установлено в начало очереди)

    task.priority = 1;
    

    int task.cntBytesToRead Чтение/запись
    Критерий останова чтения - по достижению количества байт
    ВНИМАНИЕ!!! Если выбран режим программной поддержки task.modbus = 'rtu' или 'polimaster', то этот параметр рассчитывается автоматически

    task.cntBytesToRead = 10; // 0 - не используется
    

    char task.stopChar Чтение/запись

    Критерий останова чтения - по стоп символу (по умолчанию 0 - критерий не используется)

    // Не забудьте использовать JS функцию charCodeAt()
    
    task.stopChar = '\r'.charCodeAt(0);  // Остановить чтение при первом символе перевода каретки
    
    task.stopChar = '\n'.charCodeAt(0);  // Остановить чтение при первом символе перевода строки
    
    task.stopChar = 0; // отключить остановку чтения по символу
    

    int task.timePacket Чтение/запись
    Критерий останова чтения - по времени пакета (по умолчанию 150 ms)

    Если установить 0, то будет сделана только одна попытка чтения

    task.timePacket = 50;
    

    int task.timeoutRead Чтение/запись
    Таймаут чтения в милисекундах. 50-15000 ms (по умолчанию 200 ms)
    ВНИМАНИЕ!!! Если выбран режим программной поддержки modbus = 'rtu' или 'polimaster', то этот параметр рассчитывается автоматически

    task.timeoutRead = 150;
    

    int task.silence Чтение/запись
    Гарантированная тишина в линии между фреймами в символах (допустимо 4 - 200 символов). По умолчанию 32
    ВНИМАНИЕ!!! этот параметр влияет на интервал чтения из порта.
    А также на задержки перед записью/чтением из порта.
    Для уменьшения коллизий, рекомендуется это число увеличить. Возможно придется этот параметр подобрать эксперементально
    Время передачи одного символа расчитывается по формуле:
    (startbit + databits + stopbits) * 1000 / baudRate
    Полученное время одного символа умножается на task.silence и округляется в большую сторону до ближайшего целого. В результате получаем время тишины в линии перед записью в порт.
    Интервал циклического чтения из порта равняется половине времени между фреймами.

    task.silence = 5;
    

    int task.silence_ms Только чтение
    расчетное значение паузы перед попыткой прочесть что либо из порта (расчитывается на основании silence)


    int task.intrvl_ms Только чтение
    расчетное значение периода цикла в главном потоке чтения (расчитывается на основании silence)


    String task.modbus Чтение/запись
    Режим программной поддержки протокола Modbus (По умолчанию отключена - 'none')
    Возможны варианты: 'none', 'rtu', 'ascii', 'polimaster' (или 'n', 'r', 'a', 'p')

    task.modbus = 'r';
    post.log(task.modbus); // выведет в лог 'RTU'
    task.modbus = 'a';
    post.log(task.modbus); // выведет в лог 'ASCII'
    task.modbus = 'p';
    post.log(task.modbus); // выведет в лог 'POLIMASTER'
    task.modbus = 'n';
    post.log(task.modbus); // выведет в лог 'NONE'
    
    ВНИМАНИЕ!!!
    При включеном режиме RTU или POLIMASTER, автоматически вычисляется:

    arr[] task.writeArray Чтение/запись
    данные записи в виде массива

    После выполнения task.perform(), эти данные вместе с настройками порта поместятся в очередь (согласно приоритету) и после выполения задания будет сгенерирован сигнал task.performed()

    task.writeArray = [0x01, 0x01, 0x00, 0x00, 0x00, 0x02];
    

    arr[] task.writeByteArray Чтение/запись
    данные записи в виде массива ByteArray

    После выполнения task.perform(), эти данные вместе с настройками порта поместятся в очередь (согласно приоритету) и после выполения задания будет сгенерирован сигнал task.performed()

    var wArr = new ByteArray([0x01, 0x01, 0x00, 0x00, 0x00, 0x02]);
    
    task.writeByteArray = wArr;
    

    String task.writeString Чтение/запись
    данные записи в виде строки

    После выполнения task.perform(), эти данные вместе с настройками порта поместятся в очередь (согласно приоритету) и после выполения задания будет сгенерирован сигнал task.performed()

    task.writeString = 'START|D1|F5\r\n';
    

    arr[] task.readArray только чтение
    данные ответа в виде массива

    Эти данные были прочитаны из порта (используется в функции обработчике сигнала task.performed() )

    function performed(){
    
        var rArr = task.readArray;
        // Далее можно работать с данными как с массивами (стандартными для JS средствами)
    
        // Проверка контрольной суммы
        //                            обрезать два последних символа
        var getCrc16 = jsext.crc16(   rArr.slice(0, rArr.length - 2)   );
    
        var testCrc16 = rArr.slice(-2); // оставить только два последних символа
    
        // Сравнить
        if(getCrc16[0] !== testCrc16[0] || getCrc16[1] !== testCrc16[1])
        {
            pst.log('w', 'в ответе ошибка контрольной суммы')
            return;
        }
    }
    

    arr[] task.readByteArray только чтение
    данные ответа в виде массива ByteArray

    Эти данные были прочитаны из порта (используется в функции обработчике сигнала task.performed() )

    function performed(){
    
        var rArr = task.readByteArray;
        // Далее можно работать с данными как с ByteArray
    
        // Проверка контрольной суммы
        //                            весь массив кроме последних 2ух элементов
        var getCrc16 = jsext.crc16(   rArr.left(rArr.length - 2)   );
    
        var testCrc16 = rArr.right(2); // оставить только два последних элемента
    
        // Сравнить
        if(getCrc16[0] !== testCrc16[0] || getCrc16[1] !== testCrc16[1])
        {
            pst.log('w', 'в ответе ошибка контрольной суммы')
            return;
        }
    }
    

    String task.readString только чтение
    данные ответа в виде строки

    Эти данные были прочитаны из порта (используется в функции обработчике сигнала task.performed() )

    function performed(){
    
        // Взять строку ответа
        var rStr = task.readString.trim();
    
        //проверка контрольной суммы
        var pos = rStr.search("!");
        rStr = rStr.slice(pos+1);  // Всю строку после ! знака
        //  Разбить стоку в массив
        var arr = explode(";", rStr);
        delete rStr;
        if(arr.length != 3)
        {
            // Ожидаем три числа, последее из них КС
            post.log("w", "arr.length != 3");
            return;
        }
    
        var crc = jsext.csElemer(arr[0]+";"+arr[1]+";");
        if(crc != arr[2])
        {
            post.log("w", "ошибка контрольной суммы");
            return;
        }
    
        ...
    }
    
    

    int task.status только чтение
    текущий статус выпонения задания

    switch(task.status)
    {
    case  -3: post('i', 'Инициализация');
    case  -2: post('i', 'Ошибка связанная с последовательным портом');
    case  -1: post('i', 'Размещено в очереди');
    case   0: post('i', 'Выполнено');
    case   1: post('i', 'Приступил к записи в порт');
    case   2: post('i', 'Запись в порт завершил');
    case   3: post('i', 'Приступил к чтению из порта');
    }
    

    String task.statusString только чтение
    текущий статус выпонения задания (строка)

    // данный код полностью повторяет действия листинга выше
    post('i', task.statusString);
    

    int task.error только чтение
    ошибка выполнения задания
    Все ошибки, у которых код больше 0 относятся к протоколам (см. task.modbus)

    function performed(){
    
        switch(task.error)
        {
        case 0:   post('i', 'Ошибок нет');             break;
    
        case -1:  post('i', 'ошибка открытия порта');  break;
        case -2:  post('i', 'фатальная ошибка');       break;
        case -3:  post('i', 'ошибка записи');          break;
        case -4:  post('i', 'ошибка чтения');          break;
        case -5:  post('i', 'переполнение очереди');   break;
        case -6:  post('i', 'таймаут записи');         break;
        case -7:  post('i', 'таймаут чтения');         break;
        // далее ошибки присылаемые опрашиваемыми по протоклам устройствами
        case 1:   post('i', 'Modbus - не поддерживаемая функция');                        break;
        case 2:   post('i', 'Modbus - не верный адрес');                                  break;
        case 3:   post('i', 'Modbus - не верное количество запрашиваемых регистров');     break;
        case 4:   post('i', 'Modbus - ошибка опрашиваемого устройства');                  break;
        case 5:   post('i', 'Modbus - ожидайте, данные еще не готовы');                   break;
        case 6:   post('i', 'Modbus - устройство занято, повторите позже');               break;
        case 8:   post('i', 'Modbus - ошибка четности в памяти устройства');              break;
        case 0x0A:post('i', 'Modbus - шлюз не смог найти интерфейс для передачи команды');break;
        case 0x0B:post('i', 'Modbus - устройство за шлюзом не ответило');                 break;
        case 0xF0:post('i', 'Modbus - в ответе превышено количество данных');             break;
        case 0xFF:post('i', 'Modbus - ошибка CRC');                                       break;
        case 0xF1:post('i', 'Polimaster - не найден признак ответа');                     break;
        case 0xF2:post('i', 'Polimaster - в ответе превышено количество данных');         break;
    
        }
    }
    

    String task.errorString только чтение
    ошибка выполнения задания (строка)

    // данный код полностью повторяет действия листинга выше
    post( (task.error ? 'e' : 'i'), task.errorString);
    

    bool task.isTimeout только чтение
    ошибка выполнения задания "таймаут чтения"

    if(task.isTimeout)
        post('e', 'Таймаут чтения');
    
    // равноценно следующей записи
    if(task.error == -7)
        post('e', 'Таймаут чтения');
    

    bool task.isFatal только чтение
    фатальная ошибка выполнения задания

    if(task.isFatal)
        post('e', 'проблема с последовательным интерфейсом');
    
    // равноценно следующей записи
    if(task.error > -5 && task.error < 0)
        post('e', 'проблема с последовательным интерфейсом');
    

    int task.isFroze Только чтение
    Оставшееся время в ms до возобновления обмена приостановленого методом task.froze()
    Возвращает 0 если обмен в работе


    bool task.debug Чтение/запись (по умолчанию false)
    разрешить вывод отладочных сообщений в stderr

    Внимание! вывод отладочных сообщений не записывается в лог
    и доступен только в jCjSService при запуске с параметром -e

    Этот функционал полезен при подборе временнЫх характеристик task


    bool task.echo Чтение/запись (по умолчанию false)
    подавление эха в линии

    Некоторые интерфейсные платы имеют программный функционал генерации эха. С помощью этого параметра, эхо можно подавить, тем самым отрезать от приемной посылки то, что было отправлено в линию.


    bool task.prewriteLock Чтение/запись (по умолчанию false)
    Блокировка потока на время выполнения сигнала prewrited()

    Этот параметр полезено установить в true при использовании сигнала prewrited()
    Перед записью в порт, поток будет будет ожидать завершения обработчика сигнала
    Внимание! блокировка потока заметно уменьшает скорость выполнение заданий


    float task.pollingPerMinute Только чтение
    расчетное значение количества запросов в минуту

    При записи любого числа произойдет сброс (будет расчитываться сначала)




    Сигналы

    void task.preQueue();
    генерируется объектом SerialTaskWrap перед отправкой задания в очередь

    task.preQueue.connect(task, function(){           // сигнал preQueue: задание будет отправлено в очередь
        var baw = new ByteArray([0x01, 0x04, 0x75, 0x31, 0x00, 0x01]);
        baw.append(baw.crc16());
        this.writeArray = baw;
    });
    

    void task.inQueue();
    генерируется объектом SerialTaskWrap после отправки задания в очередь

    task.inQueue.connect(timer, function(){           // сигнал inQueue: задание в очереди
        this.single(90000); // сторожевой таймер
    });
    

    void task.prewrited();
    генерируется объектом SerialTaskWrap перед тем, как данные будут записаны в порт
    Если необходимо измененять данные перед самой отправкой, то параметр prewriteLock должен быть установлен true, иначе функция обработчик будет выполнена позже отправки данных в порт
    Внимание! При prewriteLock = true будет блокироваться поток на время выполнения обработчика, что заметно уменьшает скорость выполнение заданий

    // подсоединить к сигналу prewrited функцию обработчик
    task.prewrited.connect(function prewrited(){
        post.log('i',  'отправлено в порт ' + task.writeArray.toHex() );
    });
    

    void task.performed();
    генерируется объектом SerialTaskWrap при завершении задания

    // подсоединить к сигналу performed функцию обработчик
    task.performed.connect(function performed(){
        post.log( (task.error ? 'e' : 'i'),   'Выполнено ' + (task.error ? 'с ошибкой' : 'успешно') );
    });
    // эквивалентно следующей конструкции
    function performed(){
        post.log( (task.error ? 'e' : 'i'),   'Выполнено ' + (task.error ? 'с ошибкой' : 'успешно') );
    }
    task.performed.connect(performed);
    
    // удобно передавать объект в функцию в качестве this
    // следующий код поясняет механизм передачи
    function performed(){
        // this - это переданный объект task - эквивалент sender() в Qt
        post.log( (this.error ? 'e' : 'i'),   'Выполнено ' + (this.error ? 'с ошибкой' : 'успешно') );
    }
    task.performed.connect(task, performed);
    


    void task.frozen(int);
    генерируется объектом SerialTaskWrap при действующей паузе в обмене (см. task.froze(), task.isFroze)
    Сигнал привязан к методу task.perform(ms) и будет автоматически обработан.
    Обработка сигнала в JS контексте не имеет смысла


    void task.destroy();
    генерируется объектом SerialTaskWrap при уничтожении объекта. Например после выполнения task.deleteLater()



    Методы

    bool task.perform() Функция
    Синхронная функция, возвращает true при успешном добавлении задания в очередь void task.perform(ms) Процедура
    Асинхронная функция добавлении задания в очередь
    Если ms > 0, то задание будет отправлено в очередь через заданное количество ms


    bool task.waitForReadyRead(ms = 500ms) Функция
    Ожидать ответа (полезно для организации прсевдо-синхронного)
    Внимание!!! Если вызвать этот метод сразу после perform(), то сигнал performed будет подавлен

    function func_performed(){
        post.log( (task.error ? 'e' : 'i'),   'Выполнено ' + (task.error ? 'с ошибкой' : 'успешно') );
    }
    task.performed.connect(func_performed);
    
    var tout = task.timeoutRead;
    
    task.perform();
    
    if(task.waitForReadyRead(tout))   // если waitForReadyRead возвратила true, значит сигнала performed небудет
        func_performed();             // и функцию ответ нужно вызвать самостоятельно
    


    void task.froze(int ms) Процедура
    Приостановить обмен и гарантировать тишину в линии.
    Все задачи, время которых подошло к выполнению, будут автоматически перенесены
    Время, оставшееся до окончания паузы, доступно в свойстве task.isFroze


    int task.tasksSize() функция
    Количество заданий ожидающих выполнения на текущем COM порту


    void task.config(baudRate, dataBits, stopBits, parity, flowControl) Процедура
    Сконфигурировать последовательный порт

    // Создать объект
    var task = new SerialTaskWrap();
    
    task.config(19200, 8, 1, 'even', 'none');
    
    // эквивалентно этому
    task.baudRate       = 19200;
    task.dataBits       = 8;
    task.stopBits       = 1;
    task.parity         = 'even';
    task.flowControl    = 'none';
    
    // эквивалентно этому
    task.config(19200, 8, 1, 2, 0);
    
    // эквивалентно этому
    task.baudRate       = 19200;
    task.dataBits       = 8;
    task.stopBits       = 1;
    task.parity         = 2;
    task.flowControl    = 0;
    

    bool task.crc16() Функция
    Проверить контрольную сумму CRC-16 IBM для Modbus-RTU

    function performed(){
        if(this.error)
        {
            post.log('e',   'Выполнено с ошибкой ' + this.error);
            return;
        }
        if(!this.crc16())
        {
            post.log('e',   'ошибка CRC');
            return;
        }
    }
    task.performed.connect(task, performed);
    

    bool task.crc16_ccitt() Функция
    Проверить контрольную сумму CRC-16 CCITT

    function performed(){
        if(this.error)
        {
            post.log('e',   'Выполнено с ошибкой ' + this.error);
            return;
        }
        if(!this.crc16_ccitt())
        {
            post.log('e',   'ошибка CRC');
            return;
        }
    }
    task.performed.connect(task, performed);
    

    bool task.csAscii(bool ifNumber = false) Функция
    Проверить контрольную сумму Modbus-ASCII
    ifNumber - аргумент, указывающий какой алгоритм использовать числовой или строковый (по умолчанию строковый)

    function performed(){
        if(this.error)
        {
            post.log('e',   'Выполнено с ошибкой' + task.error);
            return;
        }
        if(!this.csAscii())
        {
            post.log('e',   'ошибка CRC');
            return;
        }
    }
    task.performed.connect(task, performed);
    

    bool task.csElemer() Функция
    Проверить контрольную сумму приборов Elemer

    протокол обмена Elemer - числа разделенные символом ";"
    Пример посылки
    5;45;58;256;214;64822
    число 64822 является контрольной суммой

    function performed(){
        if(task.error)
        {
            post.log('e',   'Выполнено с ошибкой' + task.error);
            return;
        }
        if(!task.csElemer())
        {
            post.log('e',   'ошибка CRC');
            return;
        }
    }
    task.performed.connect(performed);
    

    bool task.csPolimaster() Функция
    Проверить контрольную сумму в бинарном протоколе обмена (ООО Полимастер)

    function performed(){
        if(this.error)
        {
            post.log('e',   'Выполнено с ошибкой ' + this.error);
            return;
        }
        if(!this.csPolimaster())
        {
            post.log('e',   'ошибка CRC');
            return;
        }
    }
    task.performed.connect(task, performed);