Jump to content
Форум по продукции MOXA

Recommended Posts

Здравствуйте.

На работе стоит следующая задача:
имеется десяток модулей дискретного ввода ADAM-4051 и MOXA IA-240. Необходимо опрашивать каждый модуль на состояние каналов по протоколу ModBus RTU.

 

Адреса модулей настроены, протокол тоже. Через утилиту ADAM связь установлена, если присутствует сигнал на входе, то в нужном регистре стоит "1" (проверял с помощью Master-утилит). Проблема в том, что я не знаю с чего начать работу с MOXA и в чем именно она заключается, чтобы он выступал в качестве мастера. Начитавшись множества источников появилась каша в голове (использовать стандартные функции MOXA или библиотеку на подобии libmodbus или что-то другое?).

 

Хотел узнать, мог бы кто-нибудь помочь с данной задачей? Пример .c файла на простое чтение регистров, ссылку или же Ваше решение данной задачи.

 

P.S. Первая работа в данной сфере, программировал в университете на 2ом курсе и уже мало чего помню, да и задачи стояли другие.

 

Спасибо! :)

 



 

Link to comment
  • 4 weeks later...
  • 2 months later...

На диске, прилагаемом с устройством имеются примеры работы по Modbus RTU.

 

- Пример - но вам придётся организовывать пакет передачи именно для ADAM (в моём примере применяю OWEN).

- Не все команды реализованы - добавлял по мере необходимости.

- Не ругайте - проект сырой и делался (делается) для собственных нужд, но из него можно что-то вырвать (настройка порта, контрольная сумма, команды)

 

Ещё раз - не ругайте:

 

attachicon.gifmodbustRTUserverS.zip

 

Это сервер - с одного конца соединение по Serial(ModbusRTU) с другого порт ETERNET. Это не TCPModbus (применял для своих нужд) - после компиляции готов к работе (смотрите примеры на BASH) для работы указать сериал порт и его параметры, а также TCP порт для сервера. Может обслуживать несколько клиентов для одной Modbus сети. Работает непрерывно - если раз обратились к р кгистру(рам) устройства то они в буфер читаются непрерывно какоето время (время жизни пакета) . Для каждого адресаа (по Modbus) устройства свой буфер. В связи с тем, что список регистров не всегда непрерывный - буфер поделён на "кадры". В каждом кадре свой непрерывный список регистров. Отдельный кадр можно применять и для записи в регистр - разные кадры значит и разные команды с параметрами для одного устройства. Откомпилировано для максимального количества кадров = 3. /ПРИМЕР: обращение одного клиента по TCP для устройства ввода-вывода по (Serial)ModbusRTu кадр0-чтение состояния входов; кадр1-чтение состояния выходов; кадр3-изменение состояния выходов. Другой клиетнт может обатиться как к этому же устройству, так и к другому в этойже "сети" Modbus. Важно чтобы запись в одни и теже регистры не производили. Читать можно как угодно./ Для другого последовательного порта необходимо запустить второй экземпляр сервера но указать другой TCP порт, последоательный порт настр. произвольно.

 

attachicon.gifIa240_TypesStep.zip

 

Это клиент - (правда только для моих целей) может быть запущен как на IA240, так и на соседнем по локальной сети устройстве. Тут надо указать IP и Port сервера modbusRTUserverS

 

attachicon.gifFanuc.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
  • 5 weeks later...

Возник вопрос - может добавить "стандартный" ModbusTCP в сервер (как открытие дополнительного TCP порта? Правда на мой взгляд этот вариант "малоинформативный" с точки зрения контроля результата связи, режима повторения запроса и ещё ряда "функций". Или просто сделать как SERIAL<---->TCP? Но это будет либо как дополнение, либо как вариант компиляции с указанием соответствующих ключей. И ещё - это кому надо?

 

Сейчас добавлен "второй тип" обращения - при котором запись обращения к устройству удаляется из цикла после успешного выполнения или при  истечении "времени жизни" цикла обращения к указанному устройству по Modbus RTU. (для каждого устройства оно своё - при соединении по TCP), "первый тип" применяется при непрерывной работе с устройством - удобно для чтения параметра (особенно если связь по Modbus имеет случайные сбои - по причине монтажа). Время жизни возобновляется при обращении к modbusRTUserverS ("S"-значит "serial port" потому как существует тестовый вариант TCP(modbusRTUserverTCP) - испытан на устройстве от ADVANTECH - но по смыслу "бестолковый" - тоже самое но в массиве набор TCP адресов и портов).

 

Сейчас идёт работа над ошибками. Кроме того возможно будет переделка для компиляции в режиме клиента, когда одна программа будет иметь доступ к нескольким сетям Modbus на разных устройствах, соединенных между собой по TCP.

 

это кем-нибудь будет применяться?

 

Заранее Спасибо!

Link to comment
  • 4 weeks later...

Доброго времеени суток! Для ранее рассмотренного сервера 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

Подозреваю - тебе Захар нужно пересобрать эти исходники с помощью toolchain (набор кросс-компиляторов) и залить на зверька и дальше что-то делать с получаемыми данными.

Вообще - то это не тривиальная задача, по сути тебя просят сделать самописный ПЛК, под какую-то конкретную задачу.

Я бы на твоем месте поискал подходящее железо для этой задачи у тех же самых адамов должно что-то быть.

 

Link to comment
  • 10 months later...

Доброго времени суток!

Простите за назойливость... Но довелось самому применять указанный выше сервер в нескольких проектах. Столкнулся с такой проблемой (конечно она уже решена), что некоторые устойства (именно с МВ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
  • 1 month later...

Вариант для 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
  • 4 weeks later...

Прошу извинений за прошлый пост... по поводу ошибок в коде. Сейчас изменены недочёты в рабочем цикле сервера, связанные с молчанием после последнего обмена... перед тем как выполнить другой. На текущий момент испытано с помощью кратких программ для устройств СИ30(счётчик импульсов от OWEN) и МК110-8Д.4Р(устройство ввода-вывода от OWEN). Данные маленькие проекты прилагаются и запускаются на PC в среде Linux. А сам сервер запускается на IA240/UC-7112-Plus.

 

Файлы приложены в последнем сообщении!

Link to comment

Доброго времени суток!

Продолжаю "допиливать" упомянутый сервер... сейчас проблема в узких метах. На текущий момент подсчёт контрольной суммы по 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

Доброго времени суток!

Продолжаю "допиливать" упомянутый сервер... сейчас проблема в узких метах. На текущий момент подсчёт контрольной суммы по 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

Доброго времени суток!

Собственно данный сервер делал для себя. Ранее делал обращение из программы к устройствам ввода-вывода из серии ADAM(6000 серии)... внутри единой программы на контроллере ADAM 6501 если мне не изменяет память по протоколу стандартного IPModbusRTU... это приблизительное название (не на слуху). Но по данному протоколу не было достаточно параметров связи и по этой причине был выработан свой псевдопротокол. И ещё требовалось реализовать работу приближенную к реальному времени - на уровне единиц (до 2) секунд. Потому собственно и вышел данный (уже вполне нормальный с моей точки зрения) сервер.

И собственно вопрос:

Необходим ли проект (исходник) для работы с данным сервером из Windows, который я по ряду причин не могу отнести к работоспособным ОС (уже был опыт с тем же ADAM 6501 и UNO-1019... последний надёжнее в плане ОС - ранее боготворил Windows CE для устройств... пока не испытал на своей шкуре, а Линуксу можно(нужно) доверять!).

Работа продолжается...

Спасибо, если дадите знать!

Link to comment
  • 1 month later...

Доброго времени суток!

Обнаружены две очень грубые ошибки в формировании функций 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

Тест доступа к станции катодной защиты СКЗ НГК-ИПКЗ Евро 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

Очередной опыт применения для работы с устройством аналогового ввода МВ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
  • 2 months later...

Конечно печально, что никто не сказал, что функция 16 в "mrtusocket.c" пустая. Сам заметил только, что когда потребовалось начать отработку обращений к ТРМ100(OWEN) и ADAM 4051, ADAM 4068 и ADAM 4150(ADVANTECH). Потому как потребовалось заменить ADAM 6501(с Windows CE) на IA-240-Lx. Виндовс в атоматике без присмотра людей - МАЗАХИЗМ. Печально что раньше я этого не знал :(  теперь испытав на своей шкуре знаю :)

Link to comment
  • 2 weeks later...

Конечно печально, что никто не сказал, что функция 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

 

Конечно печально, что никто не сказал, что функция 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
  • 1 month later...

 

 

Конечно печально, что никто не сказал, что функция 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 протестированы. В следующем:

attachicon.gifadvantech.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

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...