Добрый день! Попытался реализовать протокол для устройства в MasterOPC Server согласно инструкции для функции Modbus Function Code 23 (0x17) Read/Write Multiple Registers https://insat.ru/products/Universal_...ver_API_UG.pdf, но что то пошло не так.
Запрос по маске сделал на основе скрипта первого примера для датчика температуры и влажности МАВ-ТС100 фирмы «Микрофор». Решил начать с самого простого старт/остановка устройства, старт запись в 0x000C значения 0x0001, стоп запись 0x0000 соответственно. Скрипт из примера для теста обрезал по максимуму для своих тестов.
function Start_Chill(num_reg)
local send={}; --массив отправляемых чисел
local Addr=server.GetCurrentDeviceAddress( );--получения адреса устройства
table.insert(send,Addr); --добавляем в таблицу первый элемент - адрес
table.insert(send,0x17); --добавляем в таблицу второй элемент - идентификатор команды
table.insert(send,0x0000); --добавляем в таблицу третий Read Address Hi Lo
table.insert(send,0x0000); --добавляем в таблицу четвертый Quantity to Read Hi Lo
table.insert(send,0x000C); --добавляем в таблицу пятый Write Address Hi Lo
table.insert(send,0x01); --добавляем в таблицу шестой Quantity to Write
table.insert(send,0x02); --добавляем в таблицу седьмой Byte Count
table.insert(send,num_reg); --добавляем в таблицу восьмой Значение на запись
local sendmask={"byte","byte","int16:10","int16:10","int 16:10","int16:10","byte","int16:10"}; --маска отправляемого запроса
local dest={}; --массив полученных чисел
local destmask={"byte","byte","byte","int16:10"}; --маска принимаемого запроса
local err,len;
local n=0;
repeat
--посылка и получение запроса в устройство
err,dest,len=server.SendAndReceiveDataByMask(3,8,s endmask,send,destmask,20);
n=n+1;
--условие выхода - корректный ответ или превышение запросов
until err>=0 or n>=server.GetCurrentDeviceRetry()
--обрабатываем полученные данные
if err>=0 then
--запрос выполнен корректно
if dest[2]==0x17 then
return true,dest[3]; --возвращаем флаг что запрос корректен и значение
else
server.Message("Неизвестная функция"); --пишем сообщение в лог
return false,0; --запрос некорректен
end;
else
return false,0; --запрос некорректен, возвращаем соответсвующий флаг
end;
end;
-- Initialization
function OnInit()
end
-- Uninitialization
function OnClose()
end
-- Processing
function OnWrite()
if server.ReadCurrentTag()==true
then noerr,RegH=Start_Chill(0x0001)
else noerr,RegH=Start_Chill(0x0000)
end;
end
Формируемая маска запроса в MasterOPC Server 21-07-2021 17:36:51.224 SMS HRZ::Chiller:(COM5) Tx: [0014] 01 17 00 00 00 00 00 0C 00 01 02 D9 0D 0A , причем я сделал логическое переключение для старта и остановки устройства, но при задании true либо false маска запроса никак не изменяется. Я ожидал получить такую маску : 01 17 00 00 00 00 00 0C 00 01 02 00 01 LRC 0D 0A
22.07.2021, 17:49
fizhimik
Я еще тут поэкспериментировал немного с устройством, даже такая сформированная маска не подходит, нужно маску :01 17 00 00 00 00 00 0C 00 01 02 00 01 LRC сформировать без пробелов и передать в ASCII кодировке. Как это можно сделать в MasterOPC Server? Вернее эта маска должна быть без пробелов :011700000000000C0001020001LRC[CR][LF]
23.07.2021, 14:44
fizhimik
Вложений: 1
Я немного разобрался, мне подошел второй пример из https://insat.ru/products/Universal_...ver_API_UG.pdf, но затем при попытке повторить запрос в устройство как в примере из мануала к устройству, я понял что расчет LRC в функции server.SendAndReceiveDataByMask мне не подходит. Можете подсказать как это можно реализовать?
Добрый день! Забросил пока этот прибор, решил попробовать подключить другой, без контрольной суммы, но не могу конвертировать получаемые ASCII символы.
Запрос на измерение выглядит так Tx: [0017] 3A 30 36 30 33 30 34 32 31 34 30 32 31 34 30 0D 0A, ответ от прибора Rx: [0021] 3A 30 38 30 33 30 32 32 31 34 30 30 30 30 30 30 30 30 30 0D 0A. В выделенных символах находится измеряемое число (оно на данный момент 0). В маске получаемых данных я прописал destmask={"string:32","string:23","string:6"}; --маска принимаемого запроса. Так правильно, стоит ли считать пробелы? В Справке к Master OPC описан пример пересчета значения в FLOAT, но я не понимаю как перейти от получаемых ASCII к значению, которое можно подставить в пример для пересчета. Можете подсказать?
04.08.2021, 13:29
SCADAMaster
Вы можете получить как строку, а потом с помощью стандартной функции tonumber (описание в справке) привезти к числу
Либо вы можете сразу получать как sdouble - посмотрите пример DCON в инструкции
04.08.2021, 14:50
fizhimik
Цитата:
Сообщение от SCADAMaster
Вы можете получить как строку, а потом с помощью стандартной функции tonumber (описание в справке) привезти к числу
Либо вы можете сразу получать как sdouble - посмотрите пример DCON в инструкции
Спасибо.
Но к сожалению, я что то делаю не так. Ни одним из способов преобразовать данные не получилось. Вот конец кода, функция ToFloat из справки.
Код:
local dest={}; --массив полученных чисел
local destmask={"string:32","sdouble:1:8","string:6"}; --маска принимаемого запроса
local err,len;
local n=0;
local list={};
local val;
err,dest,len=server.SendAndReceiveDataByMask(0,tab le.maxn(send),sendmask,send,destmask,200);
server.WriteCurrentTag(ToFloat(dest[2]));
Ошибка тега:
04-08-2021 14:48:30.885 Measure:Node1.Device1.Measure >> [string "--[[(R)Node1.Device1.Measure]]-- Initializati..."]:3: bad argument #1 to 'BitAnd' (number expected, got string)
stack traceback:
[C]: at 0x00873ad0
[C]: in function 'BitAnd'
[string "--[[(R)Node1.Device1.Measure]]-- Initializati..."]:3: in function 'ToFloat'
[string "--[[(R)Node1.Device1.Measure]]-- Initializati..."]:44: in function <[string "--[[(R)Node1.Device1.Measure]]-- Initializati..."]:23>
04.08.2021, 15:03
SCADAMaster
Что за tofloat? Нет такой функции - есть функция tonumber. Посмотрите ее описание в справке
04.08.2021, 15:11
fizhimik
Цитата:
Сообщение от SCADAMaster
Что за tofloat? Нет такой функции - есть функция tonumber. Посмотрите ее описание в справке
пример из справки:
Пример функции
function ToFloat(register)
local fraction=bit.BitAnd(register,0x7FFFFF); --маскируем 23 бита - получем мантиссу
local exp=bit.BitRshift(register,23); --смещаемся вправо на 23 бита - получаем экспоненту
local exp=bit.BitAnd(exp,0xFF); --маскируем биты экспоненты
local sign=bit.BitFromData(register,31); --выделяем бит знака
local float=2^(exp-127)*(1+fraction/2^23); --вычисляем число
if sign==true then float=float*(-1); end; --проверяем знак
return float;
end
Пример вызова функции.
val=1042284544;
FloatNumber=ToFloat(val);
Может я совсем не правильно делаю, ведь пробел это то же символ и это надо учитывать?
Либо извлекаемую строку с данными обработать string.split, получив таблицу с отдельными ASCII символами, преобразовать таблицу получив значения HEX(тут я могу ошибаться в терминологии) и затем как то совместить значения из таблицы получив одно число, которое потом сконвертировать во Float?
04.08.2021, 15:49
SCADAMaster
Ну так там ToFloat это пользовательская функция и делает она не это.
Посмотрите описание tonumber
И где вы пробел увидели? У пробела ASCII код - 0x20
04.08.2021, 16:02
fizhimik
Вложений: 1
Цитата:
Сообщение от SCADAMaster
Ну так там ToFloat это пользовательская функция и делает она не это.
Посмотрите описание tonumber
И где вы пробел увидели? У пробела ASCII код - 0x20
Пробел между символами 30 30...
Я решил перепроверить работу tonumber в Lua, вот результат