Добрый день! Попытался реализовать протокол для устройства в 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, вот результат
Если пробелы убрать то функция работает. Что я делаю не так?
04.08.2021, 19:04
SCADAMaster
Уже даже не знаю как это комментировать...
Никакого пробела нет - его добавляет сервер в лог, чтобы визуально различать байты.
Конвертировать 30 30 30 ... в число смысла нет - потому что это не число. Вам с помощью маски нужно преобразование в строку, а уже полученную строку преобразовывать в число функцией tonumber.
05.08.2021, 11:13
fizhimik
Цитата:
Сообщение от SCADAMaster
Уже даже не знаю как это комментировать...
Никакого пробела нет - его добавляет сервер в лог, чтобы визуально различать байты.
Конвертировать 30 30 30 ... в число смысла нет - потому что это не число. Вам с помощью маски нужно преобразование в строку, а уже полученную строку преобразовывать в число функцией tonumber.
Спасибо! Я теперь знаю, что пробела нет, для меня это было вообще не очевидно, так как при опросе по стандартным функциям в логе Запроса команда идет без пробела, это меня и смущало. Подобной задачей я занимаюсь первый раз и открываю для себя много нового, поэтому не удивляйтесь подобным вопросам.
Тогда получается для ответа от сервера в виде 3A 30 38 30 33 30 32 32 31 34 30 30 30 30 30 30 30 30 30 0D 0A, маска должна быть destmask={"string:22","string:16","string:4"}; --маска принимаемого запроса
UPD.
Я тут наугад попробовал поменять маску destmask={"string:11","string:8","string:2"}; обработал tonumber, затем в функцию приведенную для примера в справке ToFloat и заработало.
Оказывается, для меня было не очевидно, что string:11 это количество элементов, а не количество символов. В самом первом примере Руководства было string:1 и там был только один символ для инициализации команды, это меня и спутало. В руководстве это описано на странице 57 для int16, но эту информацию я как то упустил.
Спасибо большое за поддержку!
10.08.2021, 12:35
fizhimik
Добрый день! Появилась задача записи float числа в прибор. В справке приведен пример перевода через server.SendAndReceiveDataByMask и результатом в виде таблицы. Как следует поступить дальше с таблицей? Для теста обработал результат таблицы tostring и в сервере установил тип данных string и записал результат в тег. Результатом после запуска сервера для числа 12.3 стало table: 0x0ab31c60, причем последние три символа постоянно меняются. Кроме того если воспользоваться онлайн калькулятором то число 12.3 будет 0x4144cccd. Подскажите как дальше быть.
10.08.2021, 13:01
SCADAMaster
А зачем так делать? Вы можете просто маску указать и все ОРС - сам преобразует как надо.
Если вам это нужно сделать для подсчета контрольной суммы, то лучше просто сделать для этого внешнюю функцию которая вызывается с помощью 7 и 8 аргумента функции SendAndReciveDataByMask (посмотрите справку - там это описано)
10.08.2021, 13:10
fizhimik
Цитата:
Сообщение от SCADAMaster
А зачем так делать? Вы можете просто маску указать и все ОРС - сам преобразует как надо.
Если вам это нужно сделать для подсчета контрольной суммы, то лучше просто сделать для этого внешнюю функцию которая вызывается с помощью 7 и 8 аргумента функции SendAndReciveDataByMask (посмотрите справку - там это описано)
То есть достаточно указать маску "float" в маске отправляемого запроса? Просто в таблицу добавляются такого формата данные для преобразования в ASCII символы
send[2] = string.format ("%02X",0x08) и я думал что мое float число должно быть так добавлено
send[7]= string.format ("%08X",число на запись);
10.08.2021, 13:28
SCADAMaster
Насчет ASCII - пока не будут преобразовываться. Как раз это мы сейчас исправляем. Как исправим - да, будет нормально.
10.08.2021, 13:58
fizhimik
Цитата:
Сообщение от SCADAMaster
Насчет ASCII - пока не будут преобразовываться. Как раз это мы сейчас исправляем. Как исправим - да, будет нормально.
Понятно, спасибо. Может есть какой иной пример перевода float to hex на языке Lua? То что я нашел используют функции math.floor, math.mod но ОРС сервер их видимо не использует и выдает ошибку скрипта. https://coderoad.ru/18886447/%D0%9F%...BD%D0%B8%D0%B5
10.08.2021, 14:15
fizhimik
У меня в принципе получилось по первому коду в ссылке (функция function float2hex (n)), далее string.format("%x",float2hex(12.3)), результат 4144cccс что соответствует 12.2999992
12.08.2021, 12:23
fizhimik
Добрый день! Существует ли возможность опроса устройства при подачи питания на него, если ОРС сервер уже запущен? То есть ОРС сервер работает, опрашивает датчики, и тут мы решаем без перезапуска сервера задействовать еще устройства, теги которых опрашиваются в сервере. У меня в итоге ОРС сервер останавливается из за ошибок с криптах
Ошибки скрипта:
12-08-2021 12:04:42.760 Max capacity:BronkHorst.Device1.Max capacity >> [string "--[[(R)BronkHorst.Device1.Max capacity]]-- In..."]:3: bad argument #1 to 'BitAnd' (number expected, got nil)
stack traceback:
[C]: at 0x00873ad0
[C]: in function 'BitAnd'
[string "--[[(R)BronkHorst.Device1.Max capacity]]-- In..."]:3: in function 'ToFloat'
[string "--[[(R)BronkHorst.Device1.Max capacity]]-- In..."]:42: in function <[string "--[[(R)BronkHorst.Device1.Max capacity]]-- In..."]:18>
12-08-2021 12:04:42.155 Measure:BronkHorst.Device1.Measure >> [string "--[[(R)BronkHorst.Device1.Measure]]-- Initial..."]:3: bad argument #1 to 'BitAnd' (number expected, got nil)
stack traceback:
[C]: at 0x00873ad0
[C]: in function 'BitAnd'
[string "--[[(R)BronkHorst.Device1.Measure]]-- Initial..."]:3: in function 'ToFloat'
[string "--[[(R)BronkHorst.Device1.Measure]]-- Initial..."]:42: in function <[string "--[[(R)BronkHorst.Device1.Measure]]-- Initial..."]:18>
Ответ устройства ***** BHT MBC3C EL PRESTIGE V2.02b Nov 15 2018 12:01:07 - очевидно его модель, прошивка и текущее время.
Если сымитировать обрыв провода данных - вытащить преобразователь USB-RS232 из компьютера, то проблем нет, данные затем снова начинают приходить (повторен цикл опроса устройства как в руководстве).
Спасибо.
12.08.2021, 13:37
SCADAMaster
Если у вас скрипт не упадет, то все будет работать.
Нужно тщательно обрабатывать принимаемые данные - убеждаться что верная контрольная сумма, что в таблице столько элементов сколько ожидается, что при преобразовании из строки в число не получился nil и т.д.
12.08.2021, 15:41
fizhimik
Цитата:
Сообщение от SCADAMaster
Если у вас скрипт не упадет, то все будет работать.
Нужно тщательно обрабатывать принимаемые данные - убеждаться что верная контрольная сумма, что в таблице столько элементов сколько ожидается, что при преобразовании из строки в число не получился nil и т.д.
Спасибо! Взял пример из руководства по RNet, добавил условий если при преобразовании из строки в число получился nil и скрипты перестали падать.
13.08.2021, 14:20
fizhimik
Вложений: 1
В итоге такой получился узел для BRONKHORST RS232 interface with ProPar protocol for digital multibus Mass Flow / Pressure instruments с минимально необходимой поддержкой вывода данных