zahar Posted April 15, 2016 Share Posted April 15, 2016 Здравствуйте. На работе стоит следующая задача:имеется десяток модулей дискретного ввода ADAM-4051 и MOXA IA-240. Необходимо опрашивать каждый модуль на состояние каналов по протоколу ModBus RTU. Адреса модулей настроены, протокол тоже. Через утилиту ADAM связь установлена, если присутствует сигнал на входе, то в нужном регистре стоит "1" (проверял с помощью Master-утилит). Проблема в том, что я не знаю с чего начать работу с MOXA и в чем именно она заключается, чтобы он выступал в качестве мастера. Начитавшись множества источников появилась каша в голове (использовать стандартные функции MOXA или библиотеку на подобии libmodbus или что-то другое?). Хотел узнать, мог бы кто-нибудь помочь с данной задачей? Пример .c файла на простое чтение регистров, ссылку или же Ваше решение данной задачи. P.S. Первая работа в данной сфере, программировал в университете на 2ом курсе и уже мало чего помню, да и задачи стояли другие. Спасибо! Link to comment
oiv_1968 Posted May 11, 2016 Share Posted May 11, 2016 Обратите внимание - в последнем сообщении актуальная версия... а это только начальная! Link to comment
oiv_1968 Posted July 13, 2016 Share Posted July 13, 2016 На диске, прилагаемом с устройством имеются примеры работы по Modbus RTU. - Пример - но вам придётся организовывать пакет передачи именно для ADAM (в моём примере применяю OWEN). - Не все команды реализованы - добавлял по мере необходимости. - Не ругайте - проект сырой и делался (делается) для собственных нужд, но из него можно что-то вырвать (настройка порта, контрольная сумма, команды) Ещё раз - не ругайте: modbustRTUserverS.zip Это сервер - с одного конца соединение по Serial(ModbusRTU) с другого порт ETERNET. Это не TCPModbus (применял для своих нужд) - после компиляции готов к работе (смотрите примеры на BASH) для работы указать сериал порт и его параметры, а также TCP порт для сервера. Может обслуживать несколько клиентов для одной Modbus сети. Работает непрерывно - если раз обратились к р кгистру(рам) устройства то они в буфер читаются непрерывно какоето время (время жизни пакета) . Для каждого адресаа (по Modbus) устройства свой буфер. В связи с тем, что список регистров не всегда непрерывный - буфер поделён на "кадры". В каждом кадре свой непрерывный список регистров. Отдельный кадр можно применять и для записи в регистр - разные кадры значит и разные команды с параметрами для одного устройства. Откомпилировано для максимального количества кадров = 3. /ПРИМЕР: обращение одного клиента по TCP для устройства ввода-вывода по (Serial)ModbusRTu кадр0-чтение состояния входов; кадр1-чтение состояния выходов; кадр3-изменение состояния выходов. Другой клиетнт может обатиться как к этому же устройству, так и к другому в этойже "сети" Modbus. Важно чтобы запись в одни и теже регистры не производили. Читать можно как угодно./ Для другого последовательного порта необходимо запустить второй экземпляр сервера но указать другой TCP порт, последоательный порт настр. произвольно. Ia240_TypesStep.zip Это клиент - (правда только для моих целей) может быть запущен как на IA240, так и на соседнем по локальной сети устройстве. Тут надо указать IP и Port сервера modbusRTUserverS Fanuc.zip Это BASH скрипт запуска всех модулей - примеры настроек там же. Ещё раз не ругайте - проект сырой. Добавлено изменение! Используются отделяемые потоки (в файле modbusRTU.c): int OpenSerial(void) { int res; float work; pthread_attr_t tattr; // <--- параметр атрибутов создавакмого потока res = IA241sopen(ModbusRTU.serialport, RS485_2WIRE_MODE, ModbusRTU.baudrate, 0, ModbusRTU.databits, ModbusRTU.stopbits); if(res < 0) return res; // pthread_attr_init(&tattr); // <--- инициализируем атрибуты pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED); // <--- устанавливаем для потока режим отделения /* ************************* * вычисляемые параметры * ************************* */ //полный размер символа BIT ModbusRTU.full_size_sym = 2 + ModbusRTU.databits + ModbusRTU.stopbits; //время полного символа в секундах work = (float)((float)(ModbusRTU.full_size_sym)) * (float)(1.0 / (float)(ModbusRTU.baudrate)); ModbusRTU.time_one_sym = (int)((float)(work * 1000000.0)); //время сна - ожидания (четырёх символов) ModbusRTU.time_silence = ModbusRTU.time_one_sym * 4; //запуск потока последовательного сервера SerialWork = 1; CountDev = 0; pthread_mutex_init(&SerialMutex,NULL); //pthread_create(&hSerialServer,NULL,(void*)SerialServer, NULL); pthread_create(&hSerialServer,&tattr,(void*)SerialServer, NULL); // <--- создаём поток обслуживания порта RS485 (ModbusRTU) return res; } Теперь простоя почти нет, общий доступ к глобальным данным, мютекс приобрёл полный смысл! Аналогично поступил с потоками обработки клиента по Ethernnet (файл mrtusocket.c): void ListenProcess(void) { int err; fd_set fdss; struct timeval tvs; int count; int csock; pthread_attr_t tattr; // pthread_attr_init(&tattr); pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED); //инициализация данных memset(&MRTUServer.midcl[0], 0, _max_sock); MRTUServer.cntclient = 0; MRTUServer.set_pr_ssleep = _cl_pr_ssleep; MRTUServer.setcntlive = _time_life; //основной цикл прослушивания while(MRTUServer.work) { mt1: tvs.tv_sec = 0; tvs.tv_usec = _LSOCKET_TIMEOUT; FD_ZERO(&fdss); FD_SET(listener,&fdss); // err = select(listener + 1,&fdss,&fdss,NULL,&tvs); //err = select(listener + 1,&fdss,NULL,&fdss,&tvs); if(err <= 0) { usleep(_SSRLEEP); goto mt1; } csock = accept(listener,NULL,NULL); if((MRTUServer.csock > 0) && (csock == MRTUServer.csock)) { usleep(_SSRLEEP); goto mt1; } //создание потока клиента printf("-----CLIENT-----\r\n"); printf("-- sock. %i --\r\n", csock); count = 0; while(count < _max_sock) { if(MRTUServer.midcl[count] == 0) { MRTUServer.csock = csock; MRTUServer.idclient = count; MRTUServer.midcl[count] = 0xFF; //pthread_create(&MRTUServer.pth[count],NULL,(void*) ClientProcess, &MRTUServer); pthread_create(&MRTUServer.pth[count],&tattr,(void*) ClientProcess, &MRTUServer); MRTUServer.cntclient++; break; } count++; } // //usleep(TServerMutex); usleep(_pr_ssleep); } } Соответственно нормально происходит обслуживание клиентов по ETHERNET соединению. в файле mrtusocket.h установил другие параметры - время ожидания: #define _pr_ssleep 300000 #define _cl_pr_ssleep 20000 В моём случае клиенты и серверы находятся на одном устройстве: 1) чтение состояния по Modbus RTU(скорость 115200) - modbusRTUserverS - порт 0 (задание на включение - устройство ввода вывода (МК110-8Д4Р) - кнопка) и передача по Ethernet (локальный адрес) - клиенту программа 1. 2) прграмма 1 - задание по кнопке значение частоты - передача по Ethernet серверу связи (общие данные для просессов ServConn) 3) сервер связи - передача по Ethernet программе 2 4) программа 2 (скорость + командное слово)- передача modbusMRTUserverS (скорость 19200) - порт 2 (задание частотному преобразователю ПЧВ3) Следует отметить, что на порт 0 подключены устройства: 1) МК110-8Д4Р (устройства ввода-вывода) - чтение (кадр 0 - регистр состояния входов; кадр 1 - регистр состояния выходов) -запись(кадр 2 - регистр задания выходов) 2) МВ110-2АС (устройство аналогового ввода) - чтение (кадр 0 - список регистров FLOAT) 3) счётчик СИ30 - (реверсивный счётчик импульсов) - чтение(кадр 0 - список регистров более 16) -запись(кадр 1 регистр команды сброса - (0 - нет сброса; 0xFF - сброс)) на порт 3 подключен только частотный преобразователь ПЧВ3 -чтение(кадр 0 - регистр татуса) -запись(кадр 1 - регистр задания частоты; кадр 2 - регистр командного слова) Результат: управление (ремя реакции) около двух секунд. Link to comment
oiv_1968 Posted August 13, 2016 Share Posted August 13, 2016 Возник вопрос - может добавить "стандартный" ModbusTCP в сервер (как открытие дополнительного TCP порта? Правда на мой взгляд этот вариант "малоинформативный" с точки зрения контроля результата связи, режима повторения запроса и ещё ряда "функций". Или просто сделать как SERIAL<---->TCP? Но это будет либо как дополнение, либо как вариант компиляции с указанием соответствующих ключей. И ещё - это кому надо? Сейчас добавлен "второй тип" обращения - при котором запись обращения к устройству удаляется из цикла после успешного выполнения или при истечении "времени жизни" цикла обращения к указанному устройству по Modbus RTU. (для каждого устройства оно своё - при соединении по TCP), "первый тип" применяется при непрерывной работе с устройством - удобно для чтения параметра (особенно если связь по Modbus имеет случайные сбои - по причине монтажа). Время жизни возобновляется при обращении к modbusRTUserverS ("S"-значит "serial port" потому как существует тестовый вариант TCP(modbusRTUserverTCP) - испытан на устройстве от ADVANTECH - но по смыслу "бестолковый" - тоже самое но в массиве набор TCP адресов и портов). Сейчас идёт работа над ошибками. Кроме того возможно будет переделка для компиляции в режиме клиента, когда одна программа будет иметь доступ к нескольким сетям Modbus на разных устройствах, соединенных между собой по TCP. это кем-нибудь будет применяться? Заранее Спасибо! Link to comment
oiv_1968 Posted September 9, 2016 Share Posted September 9, 2016 Доброго времеени суток! Для ранее рассмотренного сервера modbusRTUserverS добавлена dll-ка, которая обеспечивает связь с устройствами по Modbus RTU, программ написанных в Visual Studio(C++) - испытано. И Delphi - производится испытание. Собстенно эта dll-ка обеспечивает связь с UC-7101, UC7112plus, IA240(241), на котоых запущен сервер и к котрым подключены устройства по Modbus RTU. Одним словом написанная вами прогамма под Windows XP через эту dll-ку будет работать с сервером modbusRTUserverS. Собственно со стороны dll пока поддерживается только несколько команд: чтение регистров хранения : 3 чтение регистров ввода : 4 запись в единичный регистр : 6 запись данных в регистры : 16 Список функций dll: Init - без параметров, возвращаемых значений нет. Производит инициализацию структуры. cMRTUsConnect (соединение) - входные параметры: 1) указатель на строку типа char, котрая содержит IP адрес сервера modbusRTUserverS например "192.168.0.246"; 2) номер порта сервера modbusRTUserverS. Тип int При удачном выполнении (соединении) возвращает 0. При этом в dll запускается рабочий поток поддержания связи и обмена с сервером modbusRTUserverS и соответсвенно устройствами, подключенными к MOXA по Modbus RTU. Другие возвращаемые значения: 1 - ошибка сокета 2 - ошибка соединения 3 - не запустился рабочий поток dll-ки Возвращаемые значения типа int. cMRTUsCMD - собственно работа с устройствами. Входные параметры: 1) адрес устройства на шине Modbus RTU. Параметр типа unsigned char (uint8, byte ...) 2) номер функции (3, 4, 6, 16) - параметр типа unsigned char (uint8, byte ...) 3) началтный регистр устройства - параметр типа unsigned short (uint16 ...) как принято в протоколе. 4) количество регистров для записи (команда 16) или чтения (команды 3 и 4) - параметр типа unsigned short (uint16 ...) как принято в протоколе. Для команды 6 - запись в один регистр данный параметр игнорируется. 5) указатель на начальный регистр типа unsigned short (uint16 ...) как принято в протоколе. В случае чтения (функции 3 и 4) по указателю в этот массив будут записаны регистры из устройства. В случае записи из этого массива будут записаны значения в регистры устройства. 6) фрейм (кадр) - параметр типа int специально для сервера (0..2) позволяет разделить команды для устройства (смотрите далее) 7) флаг - параметр типа int - определяет цикличность. Пример (для C++); cMRTUsCMD ( 4 - адрес устройства 3 - чтение регистров 25 - начальый регистр 1 - в количестве одного &rd - прочитать в указанное место 0 - используем кадр 0 1 - и при этом сервер будет непрерывно читать данные из этого регистра потом и без нас ) Все действия выполняются в потоках как самого сервера, так и в нашей dll-ке. Так же кроме dll-ки cMRTUsS, измененный сервер modbusRTUserverS - в CodeLite, в директории Release откомпилирован специально для UC-7101-LX. Для UC-7112-LX-Plus и IA240(241)-LX применить другой компилятор! Не ругайтесь Спасибо! Если угодно, то проект (исходники в Visual Studio 2005) cMRTUsS.dll могу предоставить. Тут в архиве только готовая к работе, потому как хочу проверить совместимость(работоспособность) для Дельфи. В Дельфи я ноль, а моим сотрудникам нет времени уделить достаточно времени, а я ухожу в отпуск. В C# у меня не получается обращение по указателям. В нормальном C/C++ всё работает - сам делаю и пробую. cMRTUsS.zip modbusRTUserverS.zip Link to comment
boris_r_v Posted September 16, 2016 Share Posted September 16, 2016 Подозреваю - тебе Захар нужно пересобрать эти исходники с помощью toolchain (набор кросс-компиляторов) и залить на зверька и дальше что-то делать с получаемыми данными. Вообще - то это не тривиальная задача, по сути тебя просят сделать самописный ПЛК, под какую-то конкретную задачу. Я бы на твоем месте поискал подходящее железо для этой задачи у тех же самых адамов должно что-то быть. Link to comment
oiv_1968 Posted August 1, 2017 Share Posted August 1, 2017 Доброго времени суток! Простите за назойливость... Но довелось самому применять указанный выше сервер в нескольких проектах. Столкнулся с такой проблемой (конечно она уже решена), что некоторые устойства (именно с МВ110-224.2А) по Modbus RTU являясь тормозами никак не хотели работать в одной сети с другими устройствами. Именно было так: - станция катодной защиты "СКЗ НГК-ИПКЗ Евро 12Н/-02/24/-У1" в наличии у которой 12 блоков управления (каждй со своим адресом в сети и все на скорости 9600); - станция катодной защиты "Дон" без связи... но в роли сбора двух параметров МВ110-224.2А, который настроен на те же параметры сети. В роли ведущего устройства UC7101-Lx в задачу которого входит непрерывный опрос параметров со всех устройств... невзирая на то, опрашивают его клиенты по ETHERNET или нет. Одним словом опрос с максимально возможной частотой опроса. Выяснилось, что МВ110-224.2А желает быть опрошенным не ранее, чем 500 милисекунд после окончания последненго обмена (а это уж точно не 3,5 символа на скорости 9600). И было добавлено время сна после обмена. А выглядит это так: #if(_client == 1) //команда 3 int C_MRTU_CMD_0x03(U8 AddrMRTU, //адрес устройства U16 AddrReg, //адрес регистра U16 CntReg, //количество регистров U16 * Rdat, //массив данных U8 Frame, //номер кадра U32 TimeWait, //время ожидания ответа от устройства U32 TimeLive, //время жизни пакета U32 TwSleep, //время сна после обмена U8 Flag //флаг запроса MRTU ) { int res; U16 cntreg = 0; int paddr = 0; U16 reg; U8 dsend[256] = {0}; U8 drecv[256] = {0}; // dsend[0] = AddrMRTU; //адрес устройства dsend[1] = 0x03; //команда dsend[2] = (U8)((AddrReg >> 8) & 0xFF); //адрес первого регистра dsend[3] = (U8)(AddrReg & 0xFF); // dsend[4] = (U8)((CntReg >> 8) & 0xFF); //количество регистров dsend[5] = (U8)(CntReg & 0xFF); // res =C_MRTU_CMD(&dsend[0], &drecv[0], 6, //размер запроса Frame, TimeWait, TimeLive, TwSleep, Flag ); while(cntreg < CntReg) { reg = drecv[0x03 + paddr + 1]; reg |= drecv[0x03 + paddr]; paddr += 2; *(Rdat + cntreg) = reg; cntreg++; } // return res; } #endif Ну понятно, что это задаётся со стороны клиента... но выполняться будет на сервере. Эта функция в файле "mrtusocket.c" реализуется в прграмме клиента (ряд файлов что на сервере или клиенте одни и те же, только при компиляции указать сервер это или клиент в "config.h): #define _client 0 //1 - клиент, 0 - сервер #if(_client == 1) #define _server 0 #else #define _server 1 #endif Кроме того добавлены функции сразу реализующие команды, а не как ранее надо "собирать" массив, где только контрольная сумма автоматом. Сервер компилировался в среде CodeLite... и весь проект прилагается. Следует отметить, что команда 16 не реализовывалась со стороны клиента, но на сервере всё работает. Это Не Modbus TCP... потому и пакет запроса и ответа отличается: /* Формат пакета по TCP */ // #define _sizeL_tcp_pkt 0x0000 //размер пакета TCP (L) #define _sizeH_tcp_pkt 0x0001 //размер пакета TCP (H) #define _type_tcp_pkt 0x0002 //тип пакета (запроса-ответа) #define _cmd_tcp_pkt 0x0003 //режим обращения (пассивный, активный ...) #define _tlive0_tcp_pkt 0x0004 //время жизни сокета (0) #define _tlive1_tcp_pkt 0x0005 //время жизни сокета (1) #define _tlive2_tcp_pkt 0x0006 //время жизни сокета (2) #define _tlive3_tcp_pkt 0x0007 //время жизни сокета (3) #define _tsleepL_tcp_pkt 0x0008 //время сна процесса сокета (L) #define _tsleepH_tcp_pkt 0x0009 //время сна процесса сокета (H) //запрос-ответ #define _answr_flag_mrtu 0x000A //результат ответа устройства (флаг) #define _cmd_flag 0x000B //флаг запроса #define _time0_dev_mrtu 0x000C //время ожидания ответа от устройства (0) #define _time1_dev_mrtu 0x000D //время ожидания ответа от устройства (1) #define _time2_dev_mrtu 0x000E //время ожидания ответа от устройства (2) #define _time3_dev_mrtu 0x000F //время ожидания ответа от устройства (3) #define _frame_num 0x0010 //номер кадра для выбранного устройства Modbus RTU #define _tlive0_mrtu_pkt 0x0011 //время жизни кадра mrtu (0) #define _tlive1_mrtu_pkt 0x0012 //время жизни кадра mrtu (1) #define _tlive2_mrtu_pkt 0x0013 //время жизни кадра mrtu (2) #define _tlive3_mrtu_pkt 0x0014 //время жизни кадра mrtu (3) #define _tlive0_mrtu_sleep 0x0015 //время паузы после обмена (0) #define _tlive1_mrtu_sleep 0x0016 //время паузы после обмена (1) #define _tlive2_mrtu_sleep 0x0017 //время паузы после обмена (2) #define _tlive3_mrtu_sleep 0x0018 //время паузы после обмена (3) // #define _addr_dev_MRTU 0x0020 //адрес устройства MRTU #define _func 0x0021 //функция #define _addrH_reg 0x0022 //адрес регистра в устройстве #define _addrL_reg 0x0023 //адрес регистра в устройстве /* *************** * флаг ответа * *************** */ #define _answr_flag_no_data 0x81 //нет данных от Modebus RTU (при асинхронном обмене) #define _answr_flag_addr_err 0x82 //ошибка адреса по Modbus RTU #define _answr_flag_nfrane_err 0x84 //ошибка номера кадра #define _answr_flag_srv_cmd_err 0x88 //не поддерживаемая сервером команда #define _answr_flag_cmd_val_err 0x90 //ошибка параметра команды На стороне клиента (в программе клиента выглядит так): //команда 6 - запись в регистр res = C_MRTU_CMD_0x06(cnt + 1, _rg_Mode, work16, _frame_WDATA_0306, _MRTUtimewait, _MRTU_TCP_live, _MRTU_TwSleepWr, _flag_WDATA_0306); /* причем: #define _sz_str_mrtu 100 #define _scsADDR 1 //адрес устройства #define _MRTUtimewait 500000 //таймаут обмена (микросекунд) #define _MRTU_TCP_live 500 //таймаут TCP соединения (циклов потока) #define _MRTU_TwSleep 300000 //дополнительное ожидание после обмена (микросекунд) #define _MRTU_TwSleepWr 300000 //дополнительное ожидание после обмена (микросекунд) после записи //распределение кадров для обмена #define _frame_RDATA_04 0 //кадр чтения измерений 1 #define _frame_RCOIL 1 //чтение датчиков #define _frame_RDATA_0306 2 //кадр чтения задания параметров #define _frame_WCOIL 3 //запись для управления #define _frame_WDATA_0306 4 //кадр обращения для записи парметров #define _flag_RDATA_04 1 //чтение измерений непрерывное чтение #define _flag_RDATA_0306 1 //чтение задания параметров непрерывное чтение #define _flag_WDATA_0306 2 //однократная удачная запись задания #define _flag_RCOIL 1 //непрерывное чтение датчиков #define _flag_WCOIL 2 //однократная удачная запись управления */ //ну и команда 4 res = C_MRTU_CMD_0x04(AddrDev + 1, _rg_Uc1, _SZ_RG_RDATA_04, &scsRD[_rg_Uc1], _frame_RDATA_04, _MRTUtimewait, _MRTU_TCP_live, _MRTU_TwSleep, _flag_RDATA_04); //2 res = C_MRTU_CMD_0x02(AddrDev + 1, _coil_door, 8, &scsRDu8[0], _frame_RCOIL, _MRTUtimewait, _MRTU_TCP_live, _MRTU_TwSleep, _flag_RCOIL); Ранее рассматриваемая DLL с изменениями не актуальна. modbusRTUserverS.zip Link to comment
oiv_1968 Posted September 7, 2017 Share Posted September 7, 2017 Вариант для IA240-LX; IA241-LX; UC7112-LX-plus. Поддерживаемые команды: со стороны сервера: 1; 2; 3; 4; 5; 6; 16 со стороны клиента: 2; 3; 4; 6; 16 и заготовка при которой массив команды реализуется "вручную" Для применения со стороны клиента скопировать файлы mrtusocket.c и mrtusocket.h в директорию проекта клиента и указать клиента в заголовочном файле cjnfig.h с содерржимым: #define _client 0 //1 - клиент, 0 - сервер #if(_client == 1) #define _server 0 #else #define _server 1 #endif Для сервера всё готово и компилируется командой make. Для иных линукс машин отредактировать makefile, который прилагается: ########################### # Simple Generic Makefile # ########################### #CC=terminal-gcc #CC=gcc PREFIXPATH=/usr/local/arm-linux/bin CC=$(PREFIXPATH)/arm-linux-gcc CFLAGS=-c -Wall LDFLAGS=-lpthread #SOURCES=*.c SOURCES=$(shell ls *.c) OBJECTS=$(SOURCES:.c=.o) EXECUTABLE=modbusRTUserverS all: $(SOURCES) $(EXECUTABLE) $(EXECUTABLE): $(OBJECTS) $(CC) $(LDFLAGS) $(OBJECTS) -o $@ .c.o: $(CC) $(CFLAGS) $< -o $@ install: install -m 0755 $(EXECUTABLE) $(HOME)/local/bin clean: rm -rf *o $(EXECUTABLE) Следует только указать свой компилятор. Для Raspberry и простых PC с Debian раскомментировать GCC остальные убрать. Возможно потребуется изменить имя устройства TTY в файле ia241serial.c исходя из имени в системе. (Со стороны клиента это не требуется). При запуске сервера на устройстве: www-data@Moxa:~/cabel$ ./modbusRTUserverS Parametrs not defined! "--help" for information будет предложено ввести "--help" и при использовании его: www-data@Moxa:~/cabel$ ./modbusRTUserverS --help modbudRTUserver <parameters> : --help - current info --version - vetsion and date of modifications -sp - number of serial port 0...N (for moxa use devices /dev/ttyM<num>) -br - baudrate: 110 300 1200 2400 4800 9600 19200 38400 57600 115200 230400 460800 500000 576000 921600 -db - databits: 5 6 7 8 -sb - stop bits: 1 2 -p - tcp port for listen Получите список всех параметров для запуска Для применения mrtusocket.c смотрите предыдущий пост. Для запуска при включении (В IA240-LX) в файл /etc/rc.d/rc.local добавить строку запуска с ключами с завершающим "&". В моём случае производил запуск от пользователя. Эту проблему пытался решить в этой теме (успешно): https://www.moxa.ru/forum/index.php?/topic/6748-avtozapusk-ot-polzovatelia-v-ia240/ Прошу не ругать за кривой английский... моя MOXA кирилицы не знает. Файлы приложены в последнем сообщении. Link to comment
oiv_1968 Posted October 5, 2017 Share Posted October 5, 2017 Прошу извинений за прошлый пост... по поводу ошибок в коде. Сейчас изменены недочёты в рабочем цикле сервера, связанные с молчанием после последнего обмена... перед тем как выполнить другой. На текущий момент испытано с помощью кратких программ для устройств СИ30(счётчик импульсов от OWEN) и МК110-8Д.4Р(устройство ввода-вывода от OWEN). Данные маленькие проекты прилагаются и запускаются на PC в среде Linux. А сам сервер запускается на IA240/UC-7112-Plus. Файлы приложены в последнем сообщении! Link to comment
oiv_1968 Posted October 10, 2017 Share Posted October 10, 2017 Доброго времени суток! Продолжаю "допиливать" упомянутый сервер... сейчас проблема в узких метах. На текущий момент подсчёт контрольной суммы по Modbus RTU изменён. В целях уменьшения времени выполнения применён табличный метод подсчёта. Фактически он полностью совместим и с указанным в предыдущем сообщении, только изменена функция подсчёта. И правила "молчания" на линии RS485. Прилагается тестовый пример управления частотным преобразователем ПЧВ3 (от OWEN). Понятное дело он должен быть настроен на управление по Modbus RTU. Для работы сервер запускается на устройстве, а тестовый пример на PC(Linux) В процессе запуска из командной строки задаются параметры, аналогичные примерам в предыдущем сообщении... но задаётся скорость вращения. Направление вращения определяется по знаку задания. Изменения на 12 октября 2017: - обнаружена ошибка - выравнивание данных в массиве данных для TCP обмена. На текущий момент изменено: Формат пакета по TCP */ // #define _sizeL_tcp_pkt 0x0000 //размер пакета TCP (L) #define _sizeH_tcp_pkt 0x0001 //размер пакета TCP (H) #define _type_tcp_pkt 0x0002 //тип пакета (запроса-ответа) #define _cmd_tcp_pkt 0x0003 //режим обращения (пассивный, активный ...) #define _tlive0_tcp_pkt 0x0004 //время жизни сокета (0) #define _tlive1_tcp_pkt 0x0005 //время жизни сокета (1) #define _tlive2_tcp_pkt 0x0006 //время жизни сокета (2) #define _tlive3_tcp_pkt 0x0007 //время жизни сокета (3) #define _tsleepL_tcp_pkt 0x0008 //время сна процесса сокета (L) #define _tsleepH_tcp_pkt 0x0009 //время сна процесса сокета (H) //запрос-ответ #define _answr_flag_mrtu 0x000A //результат ответа устройства (флаг) #define _cmd_flag 0x000B //флаг запроса #define _frame_num 0x000C //номер кадра для выбранного устройства Modbus RTU #define _res1 0x000D //резерв #define _res2 0x000E //резерв #define _res3 0x000F //резерв #define _time0_dev_mrtu 0x0010 //время ожидания ответа от устройства (0) #define _time1_dev_mrtu 0x0011 //время ожидания ответа от устройства (1) #define _time2_dev_mrtu 0x0012 //время ожидания ответа от устройства (2) #define _time3_dev_mrtu 0x0013 //время ожидания ответа от устройства (3) #define _tlive0_mrtu_sleep 0x0014 //время паузы после обмена (0) #define _tlive1_mrtu_sleep 0x0015 //время паузы после обмена (1) #define _tlive2_mrtu_sleep 0x0016 //время паузы после обмена (2) #define _tlive3_mrtu_sleep 0x0017 //время паузы после обмена (2) #define _tlive0_mrtu_pkt 0x0018 //время жизни кадра mrtu (0) #define _tlive1_mrtu_pkt 0x0019 //время жизни кадра mrtu (1) #define _tlive2_mrtu_pkt 0x001A //время жизни кадра mrtu (2) #define _tlive3_mrtu_pkt 0x001B //время жизни кадра mrtu (3) #define _res6 0x001C //резерв #define _res7 0x001D //резерв #define _res8 0x001E //резерв #define _res9 0x001F //резерв // #define _addr_dev_MRTU 0x0020 //адрес устройства MRTU #define _func 0x0021 //функция #define _addrH_reg 0x0022 //адрес регистра в устройстве #define _addrL_reg 0x0023 //адрес регистра в устройстве Таким образом параметры: -Время "жизни соединения" по Modbus для циклического режима; -Время ожидания ответа устройства ( в ряде устройств это время больше, чем 3,5 символа по стандарту и надо добавить); -Время "молчания" ведущего на шине... это тоже для специфических устройств. Link to comment
oiv_1968 Posted October 13, 2017 Share Posted October 13, 2017 Доброго времени суток! Продолжаю "допиливать" упомянутый сервер... сейчас проблема в узких метах. На текущий момент подсчёт контрольной суммы по Modbus RTU изменён. В целях уменьшения времени выполнения применён табличный метод подсчёта. Фактически он полностью совместим и с указанным в предыдущем сообщении, только изменена функция подсчёта. И правила "молчания" на линии RS485. Прилагается тестовый пример управления частотным преобразователем ПЧВ3 (от OWEN). Понятное дело он должен быть настроен на управление по Modbus RTU. Для работы сервер запускается на устройстве, а тестовый пример на PC(Linux) В процессе запуска из командной строки задаются параметры, аналогичные примерам в предыдущем сообщении... но задаётся скорость вращения. Направление вращения определяется по знаку задания. Изменения на 12 октября 2017: - обнаружена ошибка - выравнивание данных в массиве данных для TCP обмена. На текущий момент изменено: Формат пакета по TCP */ // #define _sizeL_tcp_pkt 0x0000 //размер пакета TCP (L) #define _sizeH_tcp_pkt 0x0001 //размер пакета TCP (H) #define _type_tcp_pkt 0x0002 //тип пакета (запроса-ответа) #define _cmd_tcp_pkt 0x0003 //режим обращения (пассивный, активный ...) #define _tlive0_tcp_pkt 0x0004 //время жизни сокета (0) #define _tlive1_tcp_pkt 0x0005 //время жизни сокета (1) #define _tlive2_tcp_pkt 0x0006 //время жизни сокета (2) #define _tlive3_tcp_pkt 0x0007 //время жизни сокета (3) #define _tsleepL_tcp_pkt 0x0008 //время сна процесса сокета (L) #define _tsleepH_tcp_pkt 0x0009 //время сна процесса сокета (H) //запрос-ответ #define _answr_flag_mrtu 0x000A //результат ответа устройства (флаг) #define _cmd_flag 0x000B //флаг запроса #define _frame_num 0x000C //номер кадра для выбранного устройства Modbus RTU #define _res1 0x000D //резерв #define _res2 0x000E //резерв #define _res3 0x000F //резерв #define _time0_dev_mrtu 0x0010 //время ожидания ответа от устройства (0) #define _time1_dev_mrtu 0x0011 //время ожидания ответа от устройства (1) #define _time2_dev_mrtu 0x0012 //время ожидания ответа от устройства (2) #define _time3_dev_mrtu 0x0013 //время ожидания ответа от устройства (3) #define _tlive0_mrtu_sleep 0x0014 //время паузы после обмена (0) #define _tlive1_mrtu_sleep 0x0015 //время паузы после обмена (1) #define _tlive2_mrtu_sleep 0x0016 //время паузы после обмена (2) #define _tlive3_mrtu_sleep 0x0017 //время паузы после обмена (2) #define _tlive0_mrtu_pkt 0x0018 //время жизни кадра mrtu (0) #define _tlive1_mrtu_pkt 0x0019 //время жизни кадра mrtu (1) #define _tlive2_mrtu_pkt 0x001A //время жизни кадра mrtu (2) #define _tlive3_mrtu_pkt 0x001B //время жизни кадра mrtu (3) #define _res6 0x001C //резерв #define _res7 0x001D //резерв #define _res8 0x001E //резерв #define _res9 0x001F //резерв // #define _addr_dev_MRTU 0x0020 //адрес устройства MRTU #define _func 0x0021 //функция #define _addrH_reg 0x0022 //адрес регистра в устройстве #define _addrL_reg 0x0023 //адрес регистра в устройстве Таким образом параметры: -Время "жизни соединения" по Modbus для циклического режима; -Время ожидания ответа устройства ( в ряде устройств это время больше, чем 3,5 символа по стандарту и надо добавить); -Время "молчания" ведущего на шине... это тоже для специфических устройств. На 13 октября приложено с указанными ранее изменениями и испытано: -modbusRTUserverS (Как и ранее Сервер Modbus RTU на MOXA IA-240-LX/UC-7112-LX-PLUS) -test_SI30 обращение к счётчику СИ30 (OWEN) запускается на ПК с Linux -test_mk110_8d4r обращение к устройству дискретного ввода-вывода МК110-8Д.4Р (OWEN) запускается на ПК с Linux -test_PCHV3 обращение к частотному преобразователю ПЧВ3 (OWEN) с возможностью управления приводом запускается на ПК с Linux для запуска на ПК или MOXA потребуется перекомпиляция. Для этого отредактируйте файл Makefile в соответственном проекте так: для ПК выбрать компилятор так: ########################### # Simple Generic Makefile # ########################### #CC=terminal-gcc CC=gcc #PREFIXPATH=/usr/local/arm-linux/bin #CC=$(PREFIXPATH)/arm-linux-gcc Для MOXA выбрать кимпилятор так: ########################### # Simple Generic Makefile # ########################### #CC=terminal-gcc #CC=gcc PREFIXPATH=/usr/local/arm-linux/bin CC=$(PREFIXPATH)/arm-linux-gcc Настройки параметров интервалов и режимов осуществляются со стороны клиентов и передаются серверу при обращении в прцессе обмена. Такие как время ожидания и время продолжения "теневого" опроса устройств при отсутствии обрашения клиента к серверу. Так же и режимы обращения. Сейчас производится тестирование, добавление режимов обмена и функционала. Сервер в последнем сообщении test_si30.zip test_mk110_8d4r.zip test_PCHV3.zip Link to comment
oiv_1968 Posted October 13, 2017 Share Posted October 13, 2017 Доброго времени суток! Собственно данный сервер делал для себя. Ранее делал обращение из программы к устройствам ввода-вывода из серии ADAM(6000 серии)... внутри единой программы на контроллере ADAM 6501 если мне не изменяет память по протоколу стандартного IPModbusRTU... это приблизительное название (не на слуху). Но по данному протоколу не было достаточно параметров связи и по этой причине был выработан свой псевдопротокол. И ещё требовалось реализовать работу приближенную к реальному времени - на уровне единиц (до 2) секунд. Потому собственно и вышел данный (уже вполне нормальный с моей точки зрения) сервер. И собственно вопрос: Необходим ли проект (исходник) для работы с данным сервером из Windows, который я по ряду причин не могу отнести к работоспособным ОС (уже был опыт с тем же ADAM 6501 и UNO-1019... последний надёжнее в плане ОС - ранее боготворил Windows CE для устройств... пока не испытал на своей шкуре, а Линуксу можно(нужно) доверять!). Работа продолжается... Спасибо, если дадите знать! Link to comment
oiv_1968 Posted November 14, 2017 Share Posted November 14, 2017 Доброго времени суток! Обнаружены две очень грубые ошибки в формировании функций 3 и 4 а именно: #if(_client == 1) //команда 3 int C_MRTU_CMD_0x03(U8 AddrMRTU, //адрес устройства U16 AddrReg, //адрес регистра U16 CntReg, //количество регистров U16 * Rdat, //массив данных U8 Frame, //номер кадра U32 TimeWait, //время ожидания ответа от устройства U32 TimeLive, //время жизни пакета U32 TwSleep, //время сна после обмена U8 Flag //флаг запроса MRTU ) { int res; U16 cntreg = 0; int paddr = 0; U16 reg; U8 dsend[256] = {0}; U8 drecv[256] = {0}; // dsend[0] = AddrMRTU; //адрес устройства dsend[1] = 0x03; //команда dsend[2] = (U8)((AddrReg >> 8) & 0xFF); //адрес первого регистра dsend[3] = (U8)(AddrReg & 0xFF); // dsend[4] = (U8)((CntReg >> 8) & 0xFF); //количество регистров dsend[5] = (U8)(CntReg & 0xFF); // res =C_MRTU_CMD(&dsend[0], &drecv[0], 6, //размер запроса Frame, TimeWait, TimeLive, TwSleep, Flag ); while(cntreg < CntReg) { reg = drecv[0x03 + paddr + 1]; reg |= (drecv[0x03 + paddr] << 8); //ранее было без сдвига влево на 8 paddr += 2; *(Rdat + cntreg) = reg; cntreg++; } // return res; } #endif аналогично и для функции 4, но они только со стороны клиента в применении файла mrtusocket.c. Со стороны клиента при применении указанного файла эта команда выполнялась не правильно... была накладка - старшая часть слова и его младшая часть "слипалась" и помещалась в младшую часть результата приема по этим командам. Ниже приложено с исправлениями. Но в любом случае на сервер это не отражалось, а только со стороны клиента. Все ранее приведённые тестовые программы-клиенты для счётчиков, частотников и устройств ввода-вывода не использовали указанные команды. Там функции формировались "в ручную" с применением файла owen_modbus_rtu.c Прошу извинить! Работа продолжается. Link to comment
oiv_1968 Posted November 21, 2017 Share Posted November 21, 2017 Тест доступа к станции катодной защиты СКЗ НГК-ИПКЗ Евро 12Н/-02/24/-У1 где было обнаружено, что доступ поочерёдно к блокам не получается. В том смысле, что "перечитывать" как угодно не выходит... а именно при обращении к блоку с меньшим адресом теряется связь с более старшим адресом. Всё с циклическим опросом... но и с одиночным так же Всё делалось через UC-7101-Lx с рассматриваемым сервером: kto@debian:~/coding/moxa/ia240_uc7112lpus/catodeshield/test_module$ ./testSCS Ошибка!: Не определён параметр! "--help" для информации kto@debian:~/coding/moxa/ia240_uc7112lpus/catodeshield/test_module$ ./testSCS --help --help - Подсказка -ip <ip addr> - IP адрес сервера Modbus RTU (modbusRTUerverS) -p <port> - порт сервера Modbus RTU (modbusRTUserverS) -a <mAddr> - адрес блока по Modbus RTU -tw <time> - время ожидания ответа (микросекнд) -ts <time> - время сна после обмена (микросекунд) -tl <tloop> - время жизни в циклах процесса Modbus RTU kto@debian:~/coding/moxa/ia240_uc7112lpus/catodeshield/test_module$ При этом: kto@debian:~/coding/moxa/ia240_uc7112lpus/catodeshield/test_module$ ./testSCS -ip 192.168.0.246 -p 8971 -a 1 -tw 1000000 -ts 100000 -tl 2 ip=192.168.0.246 IP:192.168.0.246 PORT:8971 ---- opening tcp/ip 8971 ---- MODULE: 01 CMD: 02 RES(HEX): 81 MODULE: 01 CMD: 03 RES(HEX): 81 MODULE: 01 CMD: 04 RES(HEX): 81 MODULE: 01 CMD: 02 RES(HEX): 00 DATA(HEX): 00 MODULE: 01 CMD: 03 RES(HEX): 81 MODULE: 01 CMD: 04 RES(HEX): 81 MODULE: 01 CMD: 02 RES(HEX): 00 DATA(HEX): 00 MODULE: 01 CMD: 03 RES(HEX): 00 DATA(HEX): 0031 FF38 FFA6 0001 0000 MODULE: 01 CMD: 04 RES(HEX): 00 MODULE: 01 CMD: 02 RES(HEX): 00 DATA(HEX): 00 MODULE: 01 CMD: 03 RES(HEX): 00 DATA(HEX): 0031 FF38 FFA6 0001 0000 MODULE: 01 CMD: 04 RES(HEX): 00 MODULE: 01 CMD: 02 RES(HEX): 00 DATA(HEX): 00 MODULE: 01 CMD: 03 RES(HEX): 00 DATA(HEX): 0031 FF38 FFA6 0001 0000 MODULE: 01 CMD: 04 RES(HEX): 00 MODULE: 01 CMD: 02 RES(HEX): 00 DATA(HEX): 00 MODULE: 01 CMD: 03 RES(HEX): 00 DATA(HEX): 0031 FF38 FFA6 0001 0000 MODULE: 01 CMD: 04 RES(HEX): 00 MODULE: 01 CMD: 02 RES(HEX): 00 DATA(HEX): 00 MODULE: 01 CMD: 03 RES(HEX): 00 DATA(HEX): 0031 FF38 FFA6 0001 0000 То есть связь налажена и данные идут в то время, как устройство по адресу 2 теряет связь, а по адресу 3 связь восстанавливается. Проба проведена с помощью запускаемых копий программы с разных компьютеров в одной сети. Тем не менее при обращении аналогично к распространённым портам ввода-вывода и другим устройствам всё в порядке. Работа потребовалась при написании клиент-сервера для данной станции катодной защиты. Выяснено, что требуется очень большое время между обращениями к разным блокам(адресам)... непрерывное обращение к одному блоку проходит нормально при любом раскладе. test_module.zip Link to comment
oiv_1968 Posted November 23, 2017 Share Posted November 23, 2017 Очередной опыт применения для работы с устройством аналогового ввода МВ110-224.2А... Читаются все 12 регистров устройства и выводятся на консоль тестовой программой (прилагается). Реализовано через команду 3 Modbus RTU. kto@debian:~/coding/moxa/ia240_uc7112lpus/test_mv110_224_2a$ ./test_mv110_224_2a Ошибка!: Не определён параметр! "--help" для информации kto@debian:~/coding/moxa/ia240_uc7112lpus/test_mv110_224_2a$ ./test_mv110_224_2a --help --help - Подсказка -ip <ip addr> - IP адрес сервера Modbus RTU (modbusRTUerverS) -p <port> - порт сервера Modbus RTU (modbusRTUserverS) -a <mAddr> - адрес МВ110-224.2А по Modbus RTU -tw <time> - время сна после обмена (микросекунд) kto@debian:~/coding/moxa/ia240_uc7112lpus/test_mv110_224_2a$ И собственно отчёт: kto@debian:~/coding/moxa/ia240_uc7112lpus/test_mv110_224_2a$ ./test_mv110_224_2a -ip 192.168.0.246 -p 8971 -a 16 -tw 1000 ip=192.168.0.246 IP:192.168.0.246 PORT:8971 ---- opening tcp/ip 8971 ---- Err connect МВ110-224.2А (Rd InMask)------------------------------ addr:16 res:129 Регистры 0...11: 0000 0043 0000 0873 4284 69A8 0000 0055 0000 0834 42A8 11FE (Рег.0) Положение десятичной точки в целом для входа 1..........: 0 (Рег.1) Целое значение измерения входа 1 со смещением точки.....: 67 (Рег.2) Статус измерения входа 1 (код исключительной ситуации)..: 0000(HEX) (Рег.3) Циклическое время измерения входа 1.....................: 2163 (Рег.4,5) Измерение входа 1 в представлении с плавающей точкой..: 66.206360(FLOAT) 4284(HEX Reg4) 69A8(HEX Reg5) (Рег.6) Положение десятичной точки в целом для входа 1..........: 0 (Рег.7) Целое значение измерения входа 1 со смещением точки.....: 85 (Рег.8) Статус измерения входа 1 (код исключительной ситуации)..: 0000(HEX) (Рег.9) Циклическое время измерения входа 1.....................: 2100 (Рег.10,11) Измерение входа 1 в представлении с плавающей точкой: 84.035141(FLOAT) 42A8(HEX Reg10) 11FE(HEX Reg11) Регистры 0...11: 0000 0043 0000 0873 4284 69A8 0000 0055 0000 0834 42A8 11FE (Рег.0) Положение десятичной точки в целом для входа 1..........: 0 (Рег.1) Целое значение измерения входа 1 со смещением точки.....: 67 (Рег.2) Статус измерения входа 1 (код исключительной ситуации)..: 0000(HEX) (Рег.3) Циклическое время измерения входа 1.....................: 2163 (Рег.4,5) Измерение входа 1 в представлении с плавающей точкой..: 66.206360(FLOAT) 4284(HEX Reg4) 69A8(HEX Reg5) (Рег.6) Положение десятичной точки в целом для входа 1..........: 0 (Рег.7) Целое значение измерения входа 1 со смещением точки.....: 85 (Рег.8) Статус измерения входа 1 (код исключительной ситуации)..: 0000(HEX) (Рег.9) Циклическое время измерения входа 1.....................: 2100 (Рег.10,11) Измерение входа 1 в представлении с плавающей точкой: 84.035141(FLOAT) 42A8(HEX Reg10) 11FE(HEX Reg11) Регистры 0...11: 0000 0043 0000 0973 4284 6883 0000 0055 0000 0933 42A8 1C46 (Рег.0) Положение десятичной точки в целом для входа 1..........: 0 (Рег.1) Целое значение измерения входа 1 со смещением точки.....: 67 (Рег.2) Статус измерения входа 1 (код исключительной ситуации)..: 0000(HEX) (Рег.3) Циклическое время измерения входа 1.....................: 2419 (Рег.4,5) Измерение входа 1 в представлении с плавающей точкой..: 66.204124(FLOAT) 4284(HEX Reg4) 6883(HEX Reg5) (Рег.6) Положение десятичной точки в целом для входа 1..........: 0 (Рег.7) Целое значение измерения входа 1 со смещением точки.....: 85 (Рег.8) Статус измерения входа 1 (код исключительной ситуации)..: 0000(HEX) (Рег.9) Циклическое время измерения входа 1.....................: 2355 (Рег.10,11) Измерение входа 1 в представлении с плавающей точкой: 84.055222(FLOAT) 42A8(HEX Reg10) 1C46(HEX Reg11) Работа производиться с ПК с Linux в консоли. Подключение к устройству МК110-224.2А через ранее рассматриваемый сервер на устройстве MOXA. test_mv110_224_2a.zip Link to comment
oiv_1968 Posted February 19, 2018 Share Posted February 19, 2018 Конечно печально, что никто не сказал, что функция 16 в "mrtusocket.c" пустая. Сам заметил только, что когда потребовалось начать отработку обращений к ТРМ100(OWEN) и ADAM 4051, ADAM 4068 и ADAM 4150(ADVANTECH). Потому как потребовалось заменить ADAM 6501(с Windows CE) на IA-240-Lx. Виндовс в атоматике без присмотра людей - МАЗАХИЗМ. Печально что раньше я этого не знал теперь испытав на своей шкуре знаю Link to comment
oiv_1968 Posted February 27, 2018 Share Posted February 27, 2018 Конечно печально, что никто не сказал, что функция 16 в "mrtusocket.c" пустая. Сам заметил только, что когда потребовалось начать отработку обращений к ТРМ200(OWEN) и ADAM 4051, ADAM 4068 и ADAM 4150(ADVANTECH). Потому как потребовалось заменить ADAM 6501(с Windows CE) на IA-240-Lx. Виндовс в атоматике без присмотра людей - МАЗАХИЗМ. Печально что раньше я этого не знал теперь испытав на своей шкуре знаю Доброго времени суток! Изменения. Пока не проверены функции 15 и 16(16 ранее "удалена" случайно). Всё касается только mrtusocket.c в применении со стороны клиента. Поддержка 5-ти "кадров" обмена для каждого устройства. ...следующий пост содержит уже испытанный сервер с доступом по функции 15 и работа к устройствам ADAM Так же вариант применения для обмена с ТРМ200 (ОВЕН). Прилагаемый вариант для применения с mrtusocket.c в конфигурации клиента. Применена функция 3. Кадр (его номер) указывать не надо, а только структуру из owenmrtu.h Вот эту: #define _owenversion "01.02.01" #define _owendate "07.02.2018" #include "mrtusocket.h" /***************************** ТРМ200 *****************************/ #define _TRM200_STAT 0x1008 //регистр статуса #define _TRM200_PV1 0x1009 //измеренная величина на входе 1 Float32 #define _TRM200_PV2 0x100D //измеренная величина на входе 2 Float32 #define _TRM200_rdframe1 0 #define _TRM200_rdframe2 1 #define _TRM200_TimeWait 2000000 #define _TRM200_TimeLive 500 #define _TRM200_TwSleep 1000000 // #define _TRM200_stat_err_pv1 0x0001 //ошибка входа 1 #define _TRM200_stat_err_pv2 0x0002 //ошибка входа 2 typedef struct _mTRM200 { u_int16_t stat; float pv1; float pv2; } mTRM200; #define _mTRM200_regs1 4 //количество регистров для структуры mTRM200 //чтение данных измерений extern int rdTRM200( u_int8_t mAddr, mTRM200 *trm200, u_int8_t Flag ); #endif Вот так объявить и применить: //объявить.. в моём случае в файле main.c ... mTRM200 dtrm200; TRM200 trm200; ... //так делаем соединение с сервером modbusRTUserverS //запуск клиента связи с Modbus RTU чтения термодатчиков res = mrtusocketConnect(); switch(res) { case 0: workloop = 1; //запуск потока чтения температуры по Modbus RTRU pthread_create(&hlopptMRTU,NULL,(void*) lopptMRTU,NULL); break; default: printf("Не удалось подключение к серверу чтения термодатчиков по Modbus RTU:%i\n", res); return -3; } ... //и применить в функции так... она у меня отдельным потоком (нитью) void lopptMRTU(void) { int res; mTRM200 dtrm200; //наша структура из файла owenmrtu.h while(workloop) { res = rdTRM200(6, (mTRM200*)(&dtrm200), 1); switch(res) { case 0: pthread_mutex_lock(&MainMutex); trm200.trm200.t_out = dtrm200.pv2; trm200.trm200.t_in = dtrm200.pv1; trm200.trm200.stat = dtrm200.stat; trm200.conn = 0xFF; //наличие связи с прибором pthread_mutex_unlock(&MainMutex); break; default: pthread_mutex_lock(&MainMutex); trm200.conn = 0x00; //отсутствие связи с прибором pthread_mutex_unlock(&MainMutex); break; } usleep(_loopMRTUsleep); } pthread_exit(0); } ... Понятное дело, что кусок данной программы передаёт данные основной программе... которая собственно и является тоже сервером для клиентов. Почему так? Да потому, что данный ТРМ200 имеет привычку "зависать" и вешать целый Modbus. Потому на такую программу вместе с экземпляром сервера modbusRTUserverS внимание не критично. Если и повиснет, то всё равно другой Modbus (modbusRTUservrerS) для управления продолжит работать ка ни в чём не бывало на этом же устройство. Потихоньку переношу всё из ADAM6501 на IA240-LX. Работа такова, что после удаления ADAM6501 у меня будет только одна 8-ми часовая смена для запуска... и возможно потребуется остаться на сутки. Потому и готовлюсь основательно - все элементы системы надо приготовить сразу. После этого будут предоставлен код ля работы с устройствами ввода и вывода ADAM4051, ADAM4068 и ADAM4150... для которых и будет другой Modbus RTU. Кстати... а что с применением новых устройств подобного класса? Именно контроллеры на Linux. modbusRTUserverS.zip owenmrtu.zip Link to comment
oiv_1968 Posted March 7, 2018 Share Posted March 7, 2018 Конечно печально, что никто не сказал, что функция 16 в "mrtusocket.c" пустая. Сам заметил только, что когда потребовалось начать отработку обращений к ТРМ200(OWEN) и ADAM 4051, ADAM 4068 и ADAM 4150(ADVANTECH). Потому как потребовалось заменить ADAM 6501(с Windows CE) на IA-240-Lx. Виндовс в атоматике без присмотра людей - МАЗАХИЗМ. Печально что раньше я этого не знал теперь испытав на своей шкуре знаю Доброго времени суток! Простите за назойливость... но функция 15 протестирована Простите modbusRTUserverS с ошибкой для функции со стороны клиента в mrtusocket.c в следующем посте исправлено... остальное нормально. и... основные варианты доступа к ADAM 4051, ADAM 4068 и ADAM4150 протестированы. В следующем: advantech.h.zip Ну и соответственно в проекте надо применить файлы mrtusocket.h и mtrusocket.c. пример вызова функций следующий: Для чтения входов ADAM 4051: u_int16_t coils; //16-ти битная переменная масеи входов устройства res = ADAM_4051_RCoils(1, &coils); //1 - это адрес устройства Modbus RTU if(res == 0) { //доступ нормален - приступаем к анализу маски входов ... } Для чтения входов устройства ADAM 4150: u_int16_t coils; //переменная маски входов ... res = ADAM_4150_RInCoils(4, &coils); //4 - это адрес устройства Modbus RTU if(res == 0) { //всё нормально - проводим анализ входов ... } Для установки выходов ADAM 4150: u_int8_t coils; //переменная маски выходов ... //устанавливаем нужные биты выходов res = ADAM_4150_WOutCoils(4, &coils); //4 - это адрес устройства Modbus RTU //res - это результат обмена Для установки выходов (реле) устройства ADAM 4068: u_int8_t coils; //переменная маски выходов ... //устанавливаем нужные биты выходов res = ADAM_4068_WrOutCoils(5, &coils); //5 - это адрес устройства Modbus RTU //res - это результат обмена У меня скорость 9600 в условиях больших помех в шкафу. Ну соответственно скрипт запуска собственно сервера Modbus RTU и программ проекта: #!/bin/bash # _mainversion="01.00.01" _maindate="10.02.2018" _mbrtu="./modbusRTUserverS" #сервер Modbus RTU _swc="./swc_moxa" #модуль управления и сервер swc_moxa _swct="./swct_moxa" #модуль работы с термодатчиками swct_moxa # _urlmrtu1="127.0.0.1" #IP адрес Modbus 1 _portmrtu1="8971" #IP порт Modbus 1 _urlmrtu2="127.0.0.1" #IP адрес Modbus 2 _portmrtu2="8972" #IP порт Modbus 2 _lurl="127.0.0.1" #IP адрес для обращения к swc_moxa _lport="8197" #порт прослешивания модуля swc_moxa # _par1="" _par2="" function _help() { echo -e "----------- swc_moxa.sh version:$_mainversion date:$_maindate ------------" echo -e "$0 help - текущая подсказка" echo -e "$0 versions - версии компонентов системы" echo -e "$0 start - обычный запуск" echo -e "$0 start --inf - работа с выводом информации в консоль" echo -e "$0 start --noout - работа без реализации устройств вывода" echo -e "$0 start --inf --noout - для диагностики без реализации только вывода в консоль" echo -e "$0 stop - остановка всех компонентов системы включая modbusRTUserverS" } function _loop() { ((loop = 1)) while( ((loop > 0)) ); do sleep 1 done } case $1 in "start") killall -9 swct_moxa killall -9 swc_moxa killall -9 modbusRTUserverS echo ">>> start ModbusRTU <<<" #сервер связи с блоками управления $_mbrtu -sp 0 -br 9600 -db 8 -sb 1 -p 8971 & #сервер связи с термоизмерителями $_mbrtu -sp 1 -br 9600 -db 8 -sb 1 -p 8972 & sleep 3 echo ">>> start <<<" $_swc --version $_swct --version echo ">>> start SWC <<<" _par1=$2 _par2=$3 $_swc $_par1 $_par2 -mbip $_urlmrtu1 -mbp $_portmrtu1 -sp $_lport & sleep 3 echo ">>> start SWCT <<<" #$_swct $_par1 $_par2 -mbip $_urlmrtu2 -mbp $_portmrtu2 -sip $_lurl -sp $_lport & $_swct -mbip $_urlmrtu2 -mbp $_portmrtu2 -sip $_lurl -sp $_lport & sleep 1 #_loop & ;; "help") _help ;; "versions") echo "Сервер Modbus RTU:" $_mbrtu --version echo "Основной элемент системы управления (уровни, клапаны, насосы...):" $_swc --version echo "Элемент связи с температурными датчиками:" $_swct --version ;; "stop") echo ">>> stop SWCT <<<" killall -9 swct_moxa echo ">>> stop SWC <<<" killall -9 swc_moxa echo ">>> stop Modbus RTU <<<" killall -9 modbusRTUserverS sleep 1 killall -9 $0 ;; *) echo "usage: $0 {start|stop|start --noout|start --inf|help}" ;; esac И... простите за назойливость! Link to comment
oiv_1968 Posted April 12, 2018 Share Posted April 12, 2018 Конечно печально, что никто не сказал, что функция 16 в "mrtusocket.c" пустая. Сам заметил только, что когда потребовалось начать отработку обращений к ТРМ200(OWEN) и ADAM 4051, ADAM 4068 и ADAM 4150(ADVANTECH). Потому как потребовалось заменить ADAM 6501(с Windows CE) на IA-240-Lx. Виндовс в атоматике без присмотра людей - МАЗАХИЗМ. Печально что раньше я этого не знал теперь испытав на своей шкуре знаю Доброго времени суток! Простите за назойливость... но функция 15 протестирована Простите с ошибкой для функции со стороны клиента в mrtusocket.c в следующем посте исправлено. и... основные варианты доступа к ADAM 4051, ADAM 4068 и ADAM4150 протестированы. В следующем: advantech.h.zip Ну и соответственно в проекте надо применить файлы mrtusocket.h и mtrusocket.c. пример вызова функций следующий: Для чтения входов ADAM 4051: u_int16_t coils; //16-ти битная переменная масеи входов устройства res = ADAM_4051_RCoils(1, &coils); //1 - это адрес устройства Modbus RTU if(res == 0) { //доступ нормален - приступаем к анализу маски входов ... } Для чтения входов устройства ADAM 4150: u_int16_t coils; //переменная маски входов ... res = ADAM_4150_RInCoils(4, &coils); //4 - это адрес устройства Modbus RTU if(res == 0) { //всё нормально - проводим анализ входов ... } Для установки выходов ADAM 4150: u_int8_t coils; //переменная маски выходов ... //устанавливаем нужные биты выходов res = ADAM_4150_WOutCoils(4, &coils); //4 - это адрес устройства Modbus RTU //res - это результат обмена Для установки выходов (реле) устройства ADAM 4068: u_int8_t coils; //переменная маски выходов ... //устанавливаем нужные биты выходов res = ADAM_4068_WrOutCoils(5, &coils); //5 - это адрес устройства Modbus RTU //res - это результат обмена У меня скорость 9600 в условиях больших помех в шкафу. Ну соответственно скрипт запуска собственно сервера Modbus RTU и программ проекта: #!/bin/bash # _mainversion="01.00.01" _maindate="10.02.2018" _mbrtu="./modbusRTUserverS" #сервер Modbus RTU _swc="./swc_moxa" #модуль управления и сервер swc_moxa _swct="./swct_moxa" #модуль работы с термодатчиками swct_moxa # _urlmrtu1="127.0.0.1" #IP адрес Modbus 1 _portmrtu1="8971" #IP порт Modbus 1 _urlmrtu2="127.0.0.1" #IP адрес Modbus 2 _portmrtu2="8972" #IP порт Modbus 2 _lurl="127.0.0.1" #IP адрес для обращения к swc_moxa _lport="8197" #порт прослешивания модуля swc_moxa # _par1="" _par2="" function _help() { echo -e "----------- swc_moxa.sh version:$_mainversion date:$_maindate ------------" echo -e "$0 help - текущая подсказка" echo -e "$0 versions - версии компонентов системы" echo -e "$0 start - обычный запуск" echo -e "$0 start --inf - работа с выводом информации в консоль" echo -e "$0 start --noout - работа без реализации устройств вывода" echo -e "$0 start --inf --noout - для диагностики без реализации только вывода в консоль" echo -e "$0 stop - остановка всех компонентов системы включая modbusRTUserverS" } function _loop() { ((loop = 1)) while( ((loop > 0)) ); do sleep 1 done } case $1 in "start") killall -9 swct_moxa killall -9 swc_moxa killall -9 modbusRTUserverS echo ">>> start ModbusRTU <<<" #сервер связи с блоками управления $_mbrtu -sp 0 -br 9600 -db 8 -sb 1 -p 8971 & #сервер связи с термоизмерителями $_mbrtu -sp 1 -br 9600 -db 8 -sb 1 -p 8972 & sleep 3 echo ">>> start <<<" $_swc --version $_swct --version echo ">>> start SWC <<<" _par1=$2 _par2=$3 $_swc $_par1 $_par2 -mbip $_urlmrtu1 -mbp $_portmrtu1 -sp $_lport & sleep 3 echo ">>> start SWCT <<<" #$_swct $_par1 $_par2 -mbip $_urlmrtu2 -mbp $_portmrtu2 -sip $_lurl -sp $_lport & $_swct -mbip $_urlmrtu2 -mbp $_portmrtu2 -sip $_lurl -sp $_lport & sleep 1 #_loop & ;; "help") _help ;; "versions") echo "Сервер Modbus RTU:" $_mbrtu --version echo "Основной элемент системы управления (уровни, клапаны, насосы...):" $_swc --version echo "Элемент связи с температурными датчиками:" $_swct --version ;; "stop") echo ">>> stop SWCT <<<" killall -9 swct_moxa echo ">>> stop SWC <<<" killall -9 swc_moxa echo ">>> stop Modbus RTU <<<" killall -9 modbusRTUserverS sleep 1 killall -9 $0 ;; *) echo "usage: $0 {start|stop|start --noout|start --inf|help}" ;; esac И... простите за назойливость! modbusRTUserverS.zip Link to comment
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now