Просмотр полной версии : Общение с прибором с помощью скрипта
Доброго времени суток, столкнулись с очередной проблемой в связи с переходом с MasterSCADA 1.3(+OPC Modbus Server) на 4D. В OPC сервере использовался прибор ВТЭ-2 К2 который не имеет встроенного протокола modbus, для считывания данных был написан скрипт на языке Lua, который формировал запрос, обрабатывал полученный ответ и распределял данные по заранее созданным тегам соответственно. Проблема в том, что в MasterSCADA 4D нет языка Lua и перенести готовый скрипт невозможно.
Вот кусок скрипта на языке Lua в MasterOPC Modbus Server:
-- запрос текущего состояния командой 01h --
table.insert(ArrCMD, 6); --длина блока 6 байт
table.insert(ArrCMD, 225); --тип устройства
table.insert(ArrCMD, 59);-- мл.байт серийного номера (для экземпляра #01595)
table.insert(ArrCMD, 6);-- ст.байт серийного номера (для экземпляра #01595)
table.insert(ArrCMD, 1); --код команды
table.insert(ArrCMD, CheckSum(ArrCMD)); --Контрольная сумма блока запроса
ArrCMD_Mask={"byte", "byte", "byte", "byte", "byte", "byte"}; --маска отсылаемого запроса
ArrCMD_DestMask={"byte", "byte", "int16:1:01", "byte", "float:1:0123", "int16:3:01", "float:5:0123", "byte:6"}; --маска для текущего состояния
len,ArrDest=server.SendAndReceiveDataByMask(0,6,Ar rCMD_Mask,ArrCMD,ArrCMD_DestMask,41);
--ожидаем 41 байт в ответ
Есть ли аналогичные функции ( table.insert и server.SendAndReceiveDataByMask) в MasterSCADA 4D ?
Может есть у кого примеры на С# или любом другом языке доступном в MasterSCADA 4D по общению с приборами через скрипты?
МихаилГл
27.03.2025, 10:55
А что мешает оставить MasterOPC Modbus Server?
например ОС Linux :) сомневаюсь, что в данной версии тоже сохранен LUA
А что мешает оставить MasterOPC Modbus Server?
Это дополнительные расходы. Если MasterSCADA 4D позволяет обходится без отдельного OPC сервера, лучше переделать.
Какая ОС и Лицензия MS4D ? там вроде не везде в базовом варианте есть поддержка C# (таблицу надо смотреть).
Что собственно нужно (не знаток MS4D и вообще ее)
1. Скорее всего вам надо писать Программу или FBD на C# судя по документации
2. Как выполняется привязка COM портов в системе или вы в коде должны самостоятельно обрабатывать порты?
3. Исходя из п.2 думать над кодом.
В принципе, если там в системе все подготовлено для работы с портами, то код вполне будет примитивным. Отправить набор байт, получить ответ, распихать по каналам.
Какая ОС и Лицензия MS4D ? там вроде не везде в базовом варианте есть поддержка C# (таблицу надо смотреть).
Что собственно нужно (не знаток MS4D и вообще ее)
1. Скорее всего вам надо писать Программу или FBD на C# судя по документации
2. Как выполняется привязка COM портов в системе или вы в коде должны самостоятельно обрабатывать порты?
3. Исходя из п.2 думать над кодом.
В принципе, если там в системе все подготовлено для работы с портами, то код вполне будет примитивным. Отправить набор байт, получить ответ, распихать по каналам.
ОС Windows/ MasterSCADA 4D. Расширенная локальная исполнительная система на 100 внешних точек ввода-вывода.
В наличии имеется документация к прибору с описанием протокола.
Для общения используется преобразователь АС4 (RS485)/ в SCADA указываем только параметры COM порта.
Вот весь код:
-- функция,выполняющаяся перед чтением тегов
function OnBeforeReading()
local len; -- количество полученных байт
local startTime;
local ArrCMD={}; --массив под запрос текущих параметров, Было-send2
local ChErr; --error'ы
local ArrCMD_Mask={}; --маска отсылаемого запроса
local ArrCMD_DestMask={}; --маска для текущего состояния
local ArrDest={};
-- задержка чтобы прибор проплевался и стал принимать команды
-- Т.е. код нивелирования последствий предшествующего обмена данными в сети по modbus-протоколу
startTime=os.time()
while true do if os.time()>startTime+2 then break; end; end;
--
-- запрос текущего состояния командой 01h --
table.insert(ArrCMD, 6); --длина блока 6 байт
table.insert(ArrCMD, 225); --тип устройства
table.insert(ArrCMD, 59);-- мл.байт серийного номера (для экземпляра #01595)
table.insert(ArrCMD, 6);-- ст.байт серийного номера (для экземпляра #01595)
table.insert(ArrCMD, 1); --код команды
table.insert(ArrCMD, CheckSum(ArrCMD)); --Контрольная сумма блока запроса
ArrCMD_Mask={"byte", "byte", "byte", "byte", "byte", "byte"}; --маска отсылаемого запроса
ArrCMD_DestMask={"byte", "byte", "int16:1:01", "byte", "float:1:0123", "int16:3:01", "float:5:0123", "byte:6"}; --маска для текущего состояния
len,ArrDest=server.SendAndReceiveDataByMask(0,6,Ar rCMD_Mask,ArrCMD,ArrCMD_DestMask,41);
--ожидаем 41 байт в ответ
if len==41 then
server.Message("Запрос выполнен успешно. Получено 41 байт");
server.WriteTag("COM-порт.ВТЭ-2 K2.error", ArrDest[18], OPC_QUALITY_GOOD);
ChErr=CheckErrors(ArrDest[18]); --проверка на наличие ошибок от прибора
--перерасчеты
local temp = ArrDest[5] * 1162.790700;
ArrDest[5] = temp;
ArrDest[6]=ArrDest[6]/100;
ArrDest[7]=ArrDest[7]/100;
--присваиваем тегам считанные с прибора значения
server.WriteTag("COM-порт.ВТЭ-2 K2.ТеплСеть.ТеплЭнВсего", ArrDest[5], ChErr); -- Тепловая энергия
server.WriteTag("COM-порт.ВТЭ-2 K2.ТеплСеть.Тпод", ArrDest[6], ChErr); -- Температура в подающем трубопроводе
server.WriteTag("COM-порт.ВТЭ-2 K2.ТеплСеть.Тобр", ArrDest[7], ChErr); -- Температура в обратном трубопроводе
server.WriteTag("COM-порт.ВТЭ-2 K2.ТеплСеть.РасхВодВсего", ArrDest[9], ChErr); -- Объем по первому расходомеру
server.WriteTag("COM-порт.ВТЭ-2 K2.ТеплСеть.ЭлЭнВсего", ArrDest[13], ChErr); -- Электроэнергия по первому тарифу
else server.Message("ОШИБКА ЧТЕНИЯ: Байт получено: ", len ," -- nil в первом либо пришло меньше 41 байт");
end;
--эта пауза чтобю проплевались остальные приборы, идущие в очереди после ВТЭ-2
StartTime=os.time()
while true do if os.time()>startTime+2 then break; end; end;
end
function CheckErrors(error)
local q;
if error==0 then q=OPC_QUALITY_GOOD
else
--надо побитово анализировать код ошибки
if bit.BitFromData(error, 0) then q=OPC_QUALITY_UNCERTAIN end; --отсутствие расхода по счетчику воды...
if bit.BitFromData(error, 1) then q=OPC_QUALITY_SENSOR_FAILURE end; --температу 1или2 термопреобразователя больше 150 или меньше 0
if bit.BitFromData(error, 2) then q=OPC_QUALITY_SENSOR_FAILURE end; --обратное подключение термопреобразователей 1и2
if bit.BitFromData(error, 3) then q=OPC_QUALITY_DEVICE_FAILURE end; --ошибка системы измерения температур
if bit.BitFromData(error, 4) then q=OPC_QUALITY_DEVICE_FAILURE end; --счетчик часов наработки превысил 4,5года
if bit.BitFromData(error, 5) then q=OPC_QUALITY_DEVICE_FAILURE end; --необходимость замены батарейки
if bit.BitFromData(error, 6) then q=OPC_QUALITY_DEVICE_FAILURE end; --ошибка памяти EEPROM
if bit.BitFromData(error, 7) then q=OPC_QUALITY_COMM_FAILURE end; --энергия в открытой системе отрицательна
end;
return q;
end
function CheckSum(sendtbl)
local cs, j;
cs=0;
for j=1,table.maxn(sendtbl) do --однобайтовое суммирование
if (cs+sendtbl[j])>255 then cs=cs+sendtbl[j]-256
else cs=cs+sendtbl[j]; end;
end;
cs=256-cs; -- инверсией байта дополнение
return cs;
end;
ну? приведите это к коду C# построчно для начала.
local len - int len = 0;
local StartTime - это че? таймер? какой? у меня есть код для C# таймера Ton, ну можно доработать и Tof и Tp если надо. Это отдельные классы будут.
local ArrCMD={}; - байтовый? byte[] ArrCmd = new byte[x]; если знаете сколько байт.
local ChErr; - это что? байт, целочисленное?
Ну и так далее. Просто распишите словами что есть что.
Заполнение байт может быть по разному, например ArrCMD[0] = 6; ArrCMD[1] = 255;
Если никогда не меняется то можно в объявлении byte[] ArrCmd = new byte[] {6, 255, 59, ы ты.ды };
По сути вам больше привести к синтаксису C# нужно ваш же код. Ну и правильно задать переменные.
Ну судя по этому local temp = ArrDest[5] * 1162.790700; у вас там непонятно массив float что ли или все таки там байты? тогда такие комбинации типа ArrDest[7]=ArrDest[7]/100 не прокатят
МихаилГл
27.03.2025, 11:30
Это дополнительные расходы. Если MasterSCADA 4D позволяет обходится без отдельного OPC сервера, лучше переделать.
Тут не соглашусь... Какие расходы, вы меняете только скаду, а орс сервер оставляете старый. А если понадобится интегрировать другое оборудование, которое только орс поймет... Ладно, это лирическое отступление, выше я смотрю уже добрались до сути программной реализации...
bit.BitFromData - GetBit у меня тоже есть на C#, если надо. В общем приведите все, что вы понимаете, напишите то, что вам не хватает.
ну? приведите это к коду C# построчно для начала.
local len - int len = 0;
local StartTime - это че? таймер? какой? у меня есть код для C# таймера Ton, ну можно доработать и Tof и Tp если надо. Это отдельные классы будут.
local ArrCMD={}; - байтовый? byte[] ArrCmd = new byte[x]; если знаете сколько байт.
local ChErr; - это что? байт, целочисленное?
Ну и так далее. Просто распишите словами что есть что.
Заполнение байт может быть по разному, например ArrCMD[0] = 6; ArrCMD[1] = 255;
Если никогда не меняется то можно в объявлении byte[] ArrCmd = new byte[] {6, 255, 59, ы ты.ды };
По сути вам больше привести к синтаксису C# нужно ваш же код. Ну и правильно задать переменные.
Ну судя по этому local temp = ArrDest[5] * 1162.790700; у вас там непонятно массив float что ли или все таки там байты? тогда такие комбинации типа ArrDest[7]=ArrDest[7]/100 не прокатят
Основная проблема не в коде. Перевести на C# весь код не сложно, вопрос был изначально к определенному участку, где используются функции table.insert и server.SendAndReceiveDataByMask. Если table.insert это обычный массив, то к нему тоже нет вопросов. Остается главное, как отправить запрос в прибор и получить ответ.
Т.е. нужен аналог функции server.SendAndReceiveDataByMask в рамках MasterSCADA 4D на языке C#.
ну а техподдержка на конкретный SendAndReceiveDataByMask говорит? там же либы подключаются к MS4D и можно пользоваться всем их функционалом?
Посмотрите тут темы по C# для MS4D были. Просто подключаете эти либы в VisualStudio и смотрите что там есть, какие методы, классы и т.д.
Насколько понял, документации на либы нет или она в непонятно зачаточном состоянии (или что-то по запросу вам дадут).
Тут на форуме будете ждать долго.
table.insert это назначение элементам массива значений, если правильно понимаю.
Мне не было нужды, но если бы стояла подобная задача, то использовал:
"Универсальный протокол COM порта - служит для получения данных через COM-порт устройства, на котором установлена среда исполнения. Используется для работы с ФБ COMPort, COMPortSync, COMPortByte, COMPortByteSync. ФБ COMPortSync и COMPortByteSync используются только в программах данного протокола. COMPort и COMBytePort можно использовать в программах объектов..."
Мне не было нужды, но если бы стояла подобная задача, то использовал:
"Универсальный протокол COM порта - служит для получения данных через COM-порт устройства, на котором установлена среда исполнения. Используется для работы с ФБ COMPort, COMPortSync, COMPortByte, COMPortByteSync. ФБ COMPortSync и COMPortByteSync используются только в программах данного протокола. COMPort и COMBytePort можно использовать в программах объектов..."
Как раз пробуем разобраться с данными ФБ
ну а техподдержка на конкретный SendAndReceiveDataByMask говорит? там же либы подключаются к MS4D и можно пользоваться всем их функционалом?
Посмотрите тут темы по C# для MS4D были. Просто подключаете эти либы в VisualStudio и смотрите что там есть, какие методы, классы и т.д.
Насколько понял, документации на либы нет или она в непонятно зачаточном состоянии (или что-то по запросу вам дадут).
Тут на форуме будете ждать долго.
table.insert это назначение элементам массива значений, если правильно понимаю.
Техподдержка от Insat пока молчит. В крайнем случае придется докупить OPC сервер.
по поводу данных FB вроде тоже тема была недавно...
https://owen.ru/forum/showthread.php?t=37147 - документацию хотя бы сжатую можно запросить.
Еще вариант, загрузить в VS нужные библиотеки и написать небольшой код, который вам вывалит все доступные методы, думаю как либы в объект запихнуть найдете. Тут и свойства посмотреть можно и поля. Плюс то, что получите от Инсат.
#region TraceObject
void TraceObjectFields(object obj) // выводит список полей, их имена и значения:
{
foreach (FieldInfo mi in obj.GetType().GetFields())
// Вывод в лог_WriteLine(mi.FieldType.Name + " " + mi.Name + " = " + mi.GetValue(obj) + mi.FieldType.ToString());
}
void TraceObjectProperties(object obj) // выводит список свойств объекта:
{
foreach (PropertyInfo pi in obj.GetType().GetProperties())
// Вывод в лог_WriteLine(pi.PropertyType.Name + " " + pi.Name + " = " + pi.GetValue(obj, null));
}
void TraceObjectMethods(object obj)
{
foreach (MethodInfo mi in obj.GetType().GetMethods())
// Вывод в лог_WriteLine(mi.Name + " " + mi.IsPublic);
}
#endregion TraceObject
по поводу данных FB вроде тоже тема была недавно...
https://owen.ru/forum/showthread.php?t=37147 - документацию хотя бы сжатую можно запросить.
Еще вариант, загрузить в VS нужные библиотеки и написать небольшой код, который вам вывалит все доступные методы, думаю как либы в объект запихнуть найдете. Тут и свойства посмотреть можно и поля. Плюс то, что получите от Инсат.
#region TraceObject
void TraceObjectFields(object obj) // выводит список полей, их имена и значения:
{
foreach (FieldInfo mi in obj.GetType().GetFields())
// Вывод в лог_WriteLine(mi.FieldType.Name + " " + mi.Name + " = " + mi.GetValue(obj) + mi.FieldType.ToString());
}
void TraceObjectProperties(object obj) // выводит список свойств объекта:
{
foreach (PropertyInfo pi in obj.GetType().GetProperties())
// Вывод в лог_WriteLine(pi.PropertyType.Name + " " + pi.Name + " = " + pi.GetValue(obj, null));
}
void TraceObjectMethods(object obj)
{
foreach (MethodInfo mi in obj.GetType().GetMethods())
// Вывод в лог_WriteLine(mi.Name + " " + mi.IsPublic);
}
#endregion TraceObject
Спасибо! Будем пробовать
Снова поднимаю данную тему. Пока остановились на COMPortByteSync, ни как не можем разобраться, как полученное значение на выходе Response передать в Программу для дальнейших действий. Response имеет тип Array byte, в Программе переменную можно создать массив с заданным размером. Компилятор ругается в таком случае. Если использовать Полное имя, компилятор тоже ругается.
denprox, если C# то там private(public) byte[] name =
Вы какое имя пытаетесь использовать и где?
С# оказалось мы не можем использовать, лицензия Standart. А с вопросом разобрались. Если создать программу (в нашем случае ST), при создании входящего параметра, необходимо установить флажок "Ось 0" на вкладке Массивы, поставить тип массива Byte. Тогда будет создан параметр типа ARRAY OF BYTE. Что позволяет установить связь с выходным параметром Response в COMPortByteSync. Ну а дальше уже в скрипте можно разобрать посылку. И вот тут появился новый вопрос, как преобразовать например массив из 4 байт в число типа Real ?
Прибор присылает массив 41 байт.
[41, 225, 245, 6, 1, 243, 47, 206, 55, 204, 12, 26, 9, 0, 0, 154, 153, 153, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 215, 163, 59, 0, 0, 0, 0, 0, 15]
5-8 байты - Тепловая энергия. [243, 47, 206, 55]. Формула для преобразования будет такой: B3*(2^24) + B2*(2^16) + B1*(2^8) + B0. Чтобы не изобретать велосипед, какие встроенные функции позволяют сделать это в MS4D ?
Пытались DWORD_OF_BYTE (OSCAT) затем DWORD_TO_REAL использовать, но результат совсем не тот, что ожидается.
denprox на C# BitConverter.ToSingle если не ошибаюсь, найдете. Порядок байт надо будет правильный [ 0, 1, 2, 3 ] по умолчанию. Если прилетает по другому, надо будет переставлять/переворачивать.
По поводу BitConverter
ToSingle(Byte[], Int32)
Возвращает число одинарной точности с плавающей запятой, преобразованное из четырех байтов с указанной позицией в массив байтов.
Если переставлять байты не требуется, можете забрать число сразу из входного массива.
У вас -1,39 или 0.0000245794417 или 4.42410941e-10 ?
В общем тут можно посмотреть ваши f3 2f ce 37 в зависимости от расположения байт https://www.scadacore.com/tools/programming-calculators/online-hex-converter/
И вроде на FBD в MS4D были функции для этого, должны быть.
К сожалению мы не можем использовать C#.
kondor3000
14.04.2025, 13:58
С# оказалось мы не можем использовать, лицензия Standart. А с вопросом разобрались. Если создать программу (в нашем случае ST), при создании входящего параметра, необходимо установить флажок "Ось 0" на вкладке Массивы, поставить тип массива Byte. Тогда будет создан параметр типа ARRAY OF BYTE. Что позволяет установить связь с выходным параметром Response в COMPortByteSync. Ну а дальше уже в скрипте можно разобрать посылку. И вот тут появился новый вопрос, как преобразовать например массив из 4 байт в число типа Real ?
Прибор присылает массив 41 байт.
[41, 225, 245, 6, 1, 243, 47, 206, 55, 204, 12, 26, 9, 0, 0, 154, 153, 153, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 215, 163, 59, 0, 0, 0, 0, 0, 15]
5-8 байты - Тепловая энергия. [243, 47, 206, 55]. Формула для преобразования будет такой: B3*(2^24) + B2*(2^16) + B1*(2^8) + B0. Чтобы не изобретать велосипед, какие встроенные функции позволяют сделать это в MS4D ?
Пытались DWORD_OF_BYTE (OSCAT) затем DWORD_TO_REAL использовать, но результат совсем не тот, что ожидается.
Надо использовать вот такие преобразования, выложено на ST для Овен Лоджика.
Исправленные варианты сборки WORD2_ТО_REAL __выложил capzap ___ https://owen.ru/forum/showthread.php?t=37203&page=10#95
REAL_TO_DWORD выложил EFrol (формат IEEE754) __________ https://owen.ru/forum/showthread.php?t=40405&p=450141&viewfull=1#post450141
И всё собрано вместе REAL_TO_DWORD и DWORD_TO_REAL https://owen.ru/forum/showthread.php?t=40405&p=450146&viewfull=1#post450146
На ST это делается указателями на раз, не знаю, есть ли указатели в этой скаде.
К сожалению мы не можем использовать C#. меня это тоже бесит в MS4D :) очередной раз убеждаюсь, что берут деньги за каждый чих. Вот вам C# но не ТАМ :)
У вас -1,39 или 0.0000245794417 или 4.42410941e-10 ?
В общем тут можно посмотреть ваши f3 2f ce 37 в зависимости от расположения байт https://www.scadacore.com/tools/programming-calculators/online-hex-converter/
И вроде на FBD в MS4D были функции для этого, должны быть.
На FBD пока и тестируем. ФБ DWORD_OF_BYTE возвращает число: 4079996471
83101
Надо использовать вот такие преобразования, выложено на ST для Овен Лоджика.
Исправленные варианты сборки WORD2_ТО_REAL __выложил capzap ___ https://owen.ru/forum/showthread.php?t=37203&page=10#95
REAL_TO_DWORD выложил EFrol (формат IEEE754) __________ https://owen.ru/forum/showthread.php?t=40405&p=450141&viewfull=1#post450141
И всё собрано вместе REAL_TO_DWORD и DWORD_TO_REAL https://owen.ru/forum/showthread.php?t=40405&p=450146&viewfull=1#post450146
На ST это делается указателями на раз, не знаю, есть ли указатели в этой скаде.
Не совсем понятно, как это использовать в MS4D. Компилятор ругается если создать функцию с кодом function IEEE754_
kondor3000
14.04.2025, 14:28
Не совсем понятно, как это использовать в MS4D. Компилятор ругается если создать функцию с кодом function IEEE754_
А вы хотите прямо из Лоджика взять и в Скаду вставить? )))) Да ещё и не то что вам надо. Вам нужен arrWToReal в виде программы на ST.
Примерно так для Мастер скада 4D 83103
Входы BYTE 4 штуки и выход REAL назначить в Скаде.
denprox на счет MS4D не подскажу. Да и вам виднее, какое там в реальности число на приборе. Предположу, что ФБ DWORD_OF_BYTE дает или uint или int. Наверное 1-ое. Посмотрите описание блока в чем там Q и станет понятно.
А вы хотите прямо из Лоджика взять и в Скаду вставить? )))) Да ещё и не то что вам надо. Вам нужен arrWToReal в виде программы на ST.
Примерно так для Мастер скада 4D 83103
Входы BYTE 4 штуки и выход REAL назначить в Скаде.
Запустить получилось, но на выходе имеем значение 4.847436937090457E-36, ожидаемое значение 0.000[n]. Текущее значение прибор выдает примерно 0.000411
kondor3000
14.04.2025, 15:57
Запустить получилось, но на выходе имеем значение 4.847436937090457E-36, ожидаемое значение 0.000[n]. Текущее значение прибор выдает примерно 0.000411
Для чего я вам дал пример с 4 Байтами на входе, переставляя регистры и байты, добейтесь правильных показаний. Скрин 83105
Всего 4 варианта, 1 не правильный уже у вас есть.
И ещё в примере выход REAL (4 байта), а у вас LREAL (8 байт)
У меня всё работает 83109
Для чего я вам дал пример с 4 Байтами на входе, переставляя регистры и байты, добейтесь правильных показаний. Скрин 83105
Всего 4 варианта, 1 не правильный уже у вас есть.
И ещё в примере выход REAL (4 байта), а у вас LREAL (8 байт)
У меня всё работает 83109
Да тоже всё заработало, перепечатывал со скриншота, в этом месте "exp_raw := SHR((stuffAdd MOD 2147483648), 23);" пропустил одну цифру))
Всем спасибо за помощь!)
kondor3000
15.04.2025, 13:17
Да тоже всё заработало, перепечатывал со скриншота, в этом месте "exp_raw := SHR((stuffAdd MOD 2147483648), 23);" пропустил одну цифру))
Всем спасибо за помощь!)
А байты или регистры переставляли?
Ну вы даёте))) У вас же ссылки на программы на ST все есть, Лоджик поставить 2 минуты и скопипастить программу 10 секунд)))
Отличие в Лоджике, это возведение в степень pow поменять на EXPT и убрать функцию.
А байты или регистры переставляли?
Ну вы даёте))) У вас же ссылки на программы на ST все есть, Лоджик поставить 2 минуты и скопипастить программу 10 секунд)))
Отличие в Лоджике, это возведение в степень pow поменять EXPT и убрать функцию.
Мне показалось напечатать быстрее ))
Байты в обратном порядке идут
Powered by vBulletin® Version 4.2.3 Copyright © 2026 vBulletin Solutions, Inc. All rights reserved. Перевод: zCarot