Страница 1 из 10 123 ... ПоследняяПоследняя
Показано с 1 по 10 из 95

Тема: невероятно медленный опрос модулей МВ110-220.8АС

  1. #1

    По умолчанию невероятно медленный опрос модулей МВ110-220.8АС

    Добрый день.
    К контроллеру ПЛК110 подключены модули аналогового ввода МВ110-220.8АС и модули дискретного ввода МВ110-224.16ДН.
    Протокол связи Modbus RTU.
    Опрос каждого входа аналоговых модулей происходит очень медленно от 30 до 50сек.
    Опрос же дискретных модулей происходит мгновенно.
    Подскажите с чем может быть связана такая медленная работа аналоговых модулей МВ110-220.8АС?

  2. #2

    По умолчанию

    Проект выкладывайте.
    Даже если Вы опрашиваете через конфигурацию - все равно подозрительно.

  3. #3
    Пользователь
    Регистрация
    28.08.2008
    Адрес
    23..93..123
    Сообщений
    1,656

    По умолчанию

    RTFM! Раз миллион говорилось про то, что конфигуратор опрашивает по одному регистру, выложены прмеры для опроса всех регистров прибора одним запросом, у меня информация от модулей 8АС приходит очень быстро.

  4. #4

    По умолчанию

    Цитата Сообщение от Sergey666 Посмотреть сообщение
    RTFM! Раз миллион говорилось про то, что конфигуратор опрашивает по одному регистру, выложены прмеры для опроса всех регистров прибора одним запросом, у меня информация от модулей 8АС приходит очень быстро.
    Можете дать ссылку на этот пример?

  5. #5

    По умолчанию

    Ian Я его ща пишу под один проект. Могу примерно показать как делать.
    Ещё сам разбираюсь, но Валенку ОГРОМНОЕ пасибо, что когда-то меня в него ткнул носом!! Тема - охеренская!!

    Краткая суть такая (кто-то это может вынести в FAQ?):
    1. CodeSys v2 не умеет делать групповые запросы (это когда ему можно сказать: "Прочитай ВСЕ регистры с 0 по 68й разом").
    Это плохо тем, что в протоколе Modbus (то, как там идёт общение с модулями IO и другими устройствами) много байт уходит на заголовок и коннец запроса. Образно: "я такой-то, ща начну передавать столько-то данных (два байта), как понял, приём" =)
    Каждая позиция в дереве конфигурации ПЛК - это один такой запрос (Register .. module, Float .. module и так далее).
    Поэтому решение, когда для каждого канала модуля мы пихаем в конфигурацию ПЛК отдельны ..module - херовое.

    2. Сам протокол Modbus имеет команду "прочитать столько-то регистров подряд", и ОВЕНонвские модули её поддерживают.
    А CodeSys v2 умеет посылать такие команды при помощи String Input (или Output) Module.
    Вот Валенок всем и предлагает использовать эту фичу. Мне понравилось до чертей в животе =))
    Суть такая: String ... module читает БАЙТЫ (не регистры!!!) столько, сколько укажешь.
    В его настройках выставляем так:
    Command = Read Holding Registers (0x03)
    Register address = адрес начального регистра, с которого будем читать
    Amounth byte = сколько БАЙТ надо прочитать.

    Важно:
    * Максимальная длина одного String ... module = 79 байтов (не более). Поэтому если нам надо прочитать больше - придётся разбивать это на два или три String...module, но это будет быстрее чем куча запросов по кусочкам.
    * String ... module читает БАЙТЫ. Поэтому надо помнить, что один регистр Modbus - это ДВА байта. И пересчитывать всё по байтам. Ниже я покажу свои пересчёты.

    3. После того, как мы создали такие фиговины в конфигурации ПЛК, нам нужен доступ к этому STRING.
    Тут опять есть замутки с CodeSys v2: просто так обратиться к байтам этой STRING[79] у меня не получилось.
    Народ предлагает делать так: узнать адрес, по которому находится этот STRING в конфигурации ПЛК (AT %xxxxx) и вручную в коде программы назначить ему переменную вида ARRAY [1..скокабайт] OF BYTE;
    Мне это не нравится: я не люблю ручное назначение адресов.
    Поэтому я назначил в конфигурации ПЛК нормальную переменную (ниже будет показано для модулей измерения электросети), а дальше через SysMemCpy копирую её в свой буфер из байтов.

    Так вот дальше будет немного гимора: надо вручную из байтов склеить нужные значения регистров.
    Я сделал такое для измерительного модуля, и ща на его примере покажу с пояснениями.

    ИТАК, как это выглядит.

    1. Я подсчитал реистры для модулей МВ110-224.8А и МЭ110-224.1М.
    Прям на БУМАЖКЕ (если буду часто пользоваться - создам XLS-файлик).
    Я расписал себе, тупенькому, всё таким образом, что понял где сколько байт мне надо:
    MV110-8A.jpg ME110-1M.jpg
    Для .8А получилось что надо читать 26 регистров (72 байта) + 12 регистров (24 байта)
    А для .1М получилось 21 регистр (42 байта)

    Вот я напихал это всё в конфигурацию ПЛК и назначил переменные (для .1М пока что):
    RegRead.gif

    Дальше (для модулей измерения электросети) сделал структуру (с полями всех параметров, которые модуль меряет), и написал функцию её парсинга.
    Код:
    (* Тип для описания всех параметров нагрузки одной из фаз сети *)
    TYPE CSPhaseInfo :
    STRUCT
    	Voltage			: REAL;	(* Напряжение, В *)
    	Amperage		: REAL;	(* Ток, А *)
    	PowerFull		: REAL;	(* Мощность: Полная, ВА *)
    	PowerActive		: REAL;	(* Мощность: Активная, Вт *)
    	PowerReactive	: REAL;	(* Мощность: Реактивная, Вар *)
    	PowerFactor		: REAL;	(* Коэффициент мощности нагрузки *)
    	Frequency		: REAL;	(* Измеренная частота сети *)
    END_STRUCT
    END_TYPE
    Вот тут Валенок-то мне щас наподдаст... код там трешовый: я побайтно склеиваю нужные регистры в WORD, а потом делаю над ними операции, чтобы получить верное значение, которое модуль даёт.
    ParseFunction.gif

    Код:
    (* Функция парсит все каналы одного модуля МЭ110-224.1М в структуру данных *)
    FUNCTION CSParseME1Ch : CSPhaseInfo
    VAR_INPUT
    	pData		: DWORD;					(* Указатель (ADR) на переменную канала ПЛК *)
    	pDataSize	: DWORD;					(* Максимальная длина данных от модуля *)
    END_VAR
    VAR
    	pBuffer		: ARRAY [1..64] OF BYTE;	(* Вутренний буфер для копирования данных *)
    END_VAR
    (* Копируем полученный буфер во внутреннюю память, так как CodeSys в конфигурации ПЛК
    нее может присвоить STRING[79] в массив байтов штатно *)
    SysMemCpy(ADR(pBuffer), pData, pDataSize);
    
    CSParseME1Ch.Voltage := DWORD_TO_REAL(
    						SHL(BYTE_TO_DWORD(pBuffer[4]), 8 * 3) OR
    						SHL(BYTE_TO_DWORD(pBuffer[3]), 8 * 2) OR
    						SHL(BYTE_TO_DWORD(pBuffer[6]), 8) OR
    							BYTE_TO_DWORD(pBuffer[5])
    					) / EXPT(10, WORD_TO_REAL(
    						BYTE_TO_WORD(pBuffer[1]) OR
    						SHL(BYTE_TO_WORD(pBuffer[2]), 8)
    						)
    					);
    В итоге модуль - даёт (по 2й и 3й фазам нагрузки нету):
    TestPhases.gif

    Как-то так! Опрос летает, рядом стоят модули DI/DO - они опрашиваются быстро, ничего не тормозит.
    Потом накатаю функцию парсинга каналов для .8А, которая тоже всё будет пихать в структуру и распарсивать эти байты из буферов.

  6. #6

    По умолчанию

    Спасибо огромное!

  7. #7
    Пользователь
    Регистрация
    27.11.2011
    Адрес
    Краснодар
    Сообщений
    10,582

    По умолчанию

    у 8А и 8АС значения каналов идут через регистр, потому что умные дяди решили, что надо сделать float на значения и int на диагностический сигнал.
    Как там с word-ами не помню...

    читать группой кучу регистров например в какой-то примочке к zabbix (если не ошибаюсь modpoll на Linux там используется) не получилось, так как эта гадина (modpoll) не понимает, когда в группе разные типы данных. Хотя может человек чего-то не знает про modpoll а мне было неинтересно.
    Последний раз редактировалось melky; 29.06.2021 в 10:22.

  8. #8

    По умолчанию

    melky Я Float боюсь, так как мне непонятно как байты там клеить.
    Но один хрен - там умные люди сделали не так, а так (на каждый канал):
    * Десятичная точка
    * Целое значение
    * Статус канала
    * Float

    И поэтому вычитать все Float низя - приходится читать ВСЁ, так как между нужными данными будут и ненужные (Десятичная точка + Целое).
    Один фиг, работает быстро. Я в запросах вон всё-всё читаю ща из модуля раз в 200 мсек

  9. #9
    Пользователь
    Регистрация
    27.11.2011
    Адрес
    Краснодар
    Сообщений
    10,582

    По умолчанию

    Во float нет ничего ужасного, а вот то, что не побили по пакетам все float, все Статусы, все значения + десят. точка производителю НЕУД...

  10. #10

    По умолчанию

    Вот несколько в сокращенном виде, для целых регистров и для не АС, но суть одна

    Код:
    IF ComPortState = 2 THEN
    	IF NOT EndSendStored THEN
    		CASE Stage OF
    			1:	SendModbus4(
    				Enable:= ES,
    				Mode:= MB_RTU,
    				DevAddr:= 1,
    				FirstAddr:= 1,
    				Quantity:= 43,
    				ComHandle:= Settings.Port,
    				TimeOut:= Timeout,
    				Buffer:= Buffer);
    				IF SendModbus4.Complete THEN
    					EndSendStored:=TRUE;
    					ErrorSend4[1]:= SendModbus4.Exception;
    					IF ErrorSend4[1] = 0 THEN
    						AHU_Press:= Buffer[0]*256 + Buffer[1];
    						EF_Press:= Buffer[12]*256 + Buffer[13];
    						Water_Temp:= Buffer[24]*256 + Buffer[25];
    						Prom_Air_Temp:= Buffer[36]*256 + Buffer[37];
    					ELSE
    						EndSendError:= TRUE;
    						Stage:= 2;
    					END_IF;
    				END_IF;
    			2:	SendModbus4(
    				Enable:= ES,
    				Mode:= MB_RTU,
    				DevAddr:= 2,
    				FirstAddr:= 1,
    				Quantity:= 43,
    				ComHandle:= Settings.Port,
    				TimeOut:= Timeout,
    				Buffer:= Buffer);
    				IF SendModbus4.Complete THEN
    					EndSendStored:=TRUE;
    					ErrorSend4[2]:= SendModbus4.Exception;
    					IF ErrorSend4[2] = 0 THEN
    						iAHU2_Water:= Buffer[0]*256 + Buffer[1];
    						iAHU2_Temp:= Buffer[48]*256 + Buffer[49];
    						iAHU3_Water:= Buffer[12]*256 + Buffer[13];
    						iAHU3_Temp:= Buffer[60]*256 + Buffer[61];
    						iAHU4_Water:= Buffer[24]*256 + Buffer[25];
    						iAHU4_Temp:= Buffer[72]*256 + Buffer[73];
    						iAHU5_Water:= Buffer[36]*256 + Buffer[37];
    						iAHU5_Temp:= Buffer[84]*256 + Buffer[85];
    					ELSE
    						EndSendError:= TRUE;
    						Stage:= 4;
    					END_IF;
    				END_IF;
    ...
    		END_CASE;
    		ES:= FALSE;
    	ELSE
    		IF EndSendError THEN
    			EndSendError:= FALSE;
    			ES:= TRUE;
    			EndSendStored:= FALSE;
    		ELSE
    			HavePause(IN:= EndSendStored,PT:=T#40ms);
    			IF HavePause.Q THEN
    				ES:= TRUE;
    				EndSendStored:= FALSE;
    				IF Stage >= 34 THEN
    					Stage:= 1;
    				ELSE
    					Stage:= Stage+1;
    				END_IF;
    			END_IF;
    		END_IF;
    	END_IF;
    	HavePause(IN:= EndSendStored,PT:=T#30ms);
    END_IF;
    Последний раз редактировалось ASo; 29.06.2021 в 11:10.

Страница 1 из 10 123 ... ПоследняяПоследняя

Похожие темы

  1. Опрос модулей МВ110-хх
    от Antonio-oz в разделе Мх110
    Ответов: 2
    Последнее сообщение: 14.12.2019, 14:49
  2. Ответов: 2
    Последнее сообщение: 13.03.2015, 01:00
  3. Ответов: 5
    Последнее сообщение: 20.02.2013, 02:44
  4. Опрос несколько модулей МВ110 через лектус
    от llx89 в разделе Эксплуатация
    Ответов: 5
    Последнее сообщение: 20.02.2013, 02:44
  5. Ответов: 9
    Последнее сообщение: 17.03.2011, 05:48

Ваши права

  • Вы не можете создавать новые темы
  • Вы не можете отвечать в темах
  • Вы не можете прикреплять вложения
  • Вы не можете редактировать свои сообщения
  •