В этом разделе перечислены свойства и методы объекта 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();
Пример использования:
// Создать объект 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 Чтение/запись
наличие и тип бита четности
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'ВНИМАНИЕ!!!
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);