Показано с 1 по 10 из 39

Тема: Имитация модулей ввода-вывода для удобной отладки

Древовидный режим

Предыдущее сообщение Предыдущее сообщение   Следующее сообщение Следующее сообщение
  1. #9

    По умолчанию Решено для моего случая. Но для себя - читайте пост ниже

    Спасибо всем за ответы. Я почему-то не увидел, что тему уже опубликовали. И сам уже себе помог )
    Более суток ковырял эту тему, разобрался с регистрами и прочим.
    Модуль ввода не подключался по причине того, что он хочет запросить в сумме 29 слов, а я создавал устройство слейва, где не было столько регистров.
    Модулю вывода для счастливой жизни достаточно всего одного регистра, потому он и работал без проблем.

    Ещё раз поясню мои мучения:
    У меня семь контакторов шнеков управляются по показаниям весов. Для отладки мне надо имитировать ответ контактора о замыкании, вовремя тыкать туда-сюда эти самые контакторы, плюс имитировать процесс увеличения веса...
    Вручную это делать очень неприятно и сложно.
    Приходится загрублять все тайминги, чтоб успевать руками нащёлкать нужные состояния в MasterOPC Universal Modbus Server (естественно, я про него знаю).
    Но при загрублении таймингов сильно уходят показания измеряемой скорости насыпания. Плюс - я постоянно в голове держу не отладку, а порядок тыкания кнопочек.
    У меня было открыто три окна:
    Кодесис с отлаживаемым проектом.
    Кодесис с имитацией весового модуля.
    Модбас этот описи сервер универсал с имитацией модулей через теги.

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

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

    В общем и целом - я набрёл на ФБ OCL.MB_SerialSlave, который в коде, без лишних бубнов поднимает слэйв устройство. Опять же - на одном порту две штуки поднять нельзя, но можно сделать так, чтоб этот слэйв отзывался на любой ID. А дальше мне тупо повезло, что в двух моих модулях не пересекаются адреса хранения данных о состоянии выходов.
    Соответсвенно - по наводке Евгения Кислова я просто завёл слэйв с общим адресным пространством для обоих своих модулей.
    И вот щас всё работает. Теперь я в этот же проект могу добавить свой имитатор весового модуля, чтоб он сам включался по выходам модуля вывода. И ответку контакторов я теперь тоже могу автоматизировать, а не руками тыкать, поглядывая в таблицу на соответствие контакторов и выводов... И расположить все кнопки управления я теперь тоже могу так, как они физически на заводе стоят, а не в столбик из этих тегов в этом модбас матьего описи сервере)


    Если кому интересно - вот код с максимально подробным описанием:


    Код:
    (* 	ДИСКЛЕЙМЕР.
    	Спасибо Евгению Кислову за пинок в нужное время в нужном направлении :)
    
    	Работа примера основана на примере  5.9.2 из документа 
    	CODESYS V3.5 Настройка обмена по протоколу Modbus Руководство пользователя 04.08.2023 версия 3.2
    	Под шаблоном я буду иметь в виду готовый шаблон Модбас устройства от компании Овен.
    	
    	ОБРАЩАЮ ВНИМАНИЕ!!!!!!! Весь этот финт ушами с двумя слейвами одовременно возможен лишь потому, что
    	конкретно у этих двух приборов не пересекаются адреса хранения состояний выходов.
    	Если адреса пересекаются - то нужно будет определять, какое устройство сейчас долбится к нам за данными
    	и класть в буфер данные для СЛЕДУЮЩЕГО по адресу устройства. Потому что ФБ выдаёт адрес устройства, которому уже всё отдал.
    	По словам Евгения - мастер опрашивает устройства по порядку, так что можно нахитрить в этом направлении.
    	Примерный алгоритм: составляем массив адресов своих устройств, на первой итерации баним их всех, чтоб не отдать кому-то не то.
    	Запоминаем, кто к нам долбится, смещаемся по своему массиву вправо от полученного адреса и кладём в буфер нужные СЛЕДУЮЩЕМУ устройству данные.
    	Разбаниваем все устройства, работаем. Но это прокатит только если между адресами имитируемых устройств нет адресов реальных устройств. 
    	Иначе всё собъётся. Короче, к сути.
    	
    	В примере реализован функционал имитации работы модуля ввода MV110_16_D_DN и модуля вывода МУ110-16R_K
    	Эти два названия я взял из названий готовых шаблонов модулей из пакета от Овена.
    	По факту у меня дискретные приборы, и меня интересовали лишь две возможности для отладки:
    	1) для модуля вывода - заиметь на визуализации лампочку, которая загоралась бы, когда выход модуля активен
    	2) иметь на визуализации кнопку, по нажатию которой имитируется замыкание входа модуля ввода.
    	
    	Счётчики, ШИМы, показания датчиков мне не нужны, поэтому в этом примере они не реализованы. Но по идее - частично обработать инфу возможно.
    	
    *)
    PROGRAM PLC_PRG
    VAR 
    		fbComControl: 			OCL.COM_Control;	// ФБ управления портом COM1
    		fbModbusSerialSlave: 	OCL.MB_SerialSlave; // ФБ для реализиации модуля вЫвода
    		usiID:					USINT;
    (*  Чо ваще тут происходит
    	в ходе экспериментов выяснилось, что шаблон модуля ввода MV110_16_D_DN работает только в случае, когда под него выделено не менее 29 байт:
    	см.инструкцию от модуля, "Таблица А1 - Регистры Modbus".
    	Он опрашивает регистр с адресом 51, забирая оттуда битовую маску значений входов. Это как раз одно слово для 16 входов.
    	И ещё ему ну прям никак не обойтисть без доступа к регистрам с адресами 64..79, откуда он берёт значения счётчиков импульсов.
    	Мне они не нужны, но без выделения под них памяти ничего не работает (имитируемый модуль висит в офлайн или мигает туда-сюда в попытках подключения).
    	Так как регисты в памяти должы быть расположены последоватьельно - нужно будет выделить 79-51+1=29 слов памяти.
    	(+1 - потому что 2-1=1, но по факту количество 2) 
    	Шаблон модуля вЫвода MV110_16_D_DN прекрасно работает при одном выделенном для него слове.
    	Повторю - меня инстересует только состояние дискретных выходов, поэтому минимально необходимое количество WORD для моего случая = 1.
    	Насколько я понял - считывание состояния происходит с того же регистра (могу ошибаться). 
    	
    	Итого мы имеем: шаблон ввода хочет иметь дело с регистром 50 (1 шт). Шаблон вывода - с регистрами 51-79 (29 шт).
    	На самом деле - на данном этапе адресация регистров не важна. Важно именно их количество. В моём случае 30 шт.
    	
    	Везение заключается в том, что у двух этих модулей не пересекаются адреса нужных нам регистров.
    	Соответственно - их можно расположить в памяти в таком порядке:
    	IN - это будет регист модуля ввода
    	IN01..16 - регистры счётчиков модуля ввода, без которых он жить не может
    	OUT - регист модуля вывода. 
    	
    	01	OUT
    	02	IN
    	03
    	04
    	05
    	06
    	07
    	08
    	09
    	10
    	11
    	12
    	13
    	14
    	15	IN01
    	16	IN02
    	17	IN03
    	18	IN04
    	19	IN05
    	20	IN06
    	21	IN07
    	22	IN08
    	23	IN09
    	24	IN10
    	25	IN11
    	26	IN12
    	27	IN13
    	28	IN14
    	29	IN15
    	30	IN16
    	Чтобы не путаться с нулевой адресацией и количеством - адресацию массива данных я буду начинать от 1, а не от 0.
    
    *)
    
    awSlaveData:				ARRAY [1..30] OF WORD; // буфер данных Modbus Slave
    
    (* в моём случае это не нужно, потому что у меня нет регистров, которые читаются и пишутся обеими участниками обмена.
    	Если такое есть - нужно тут в коде писать данные в такие регистры не циклично, а по необходимости.
    	Иначе цикличная запись будет постоянно затирать принимаемую инфу. Так делать фу.
    	xWrite: 		BOOL; // команда записи данных из программы в регистры Modbus Slave
    	fbWriteEdge:	R_TRIG; // триггер для однократной записи
    *)
    axIN: ARRAY [1..16] OF BOOL;  // массив состояний входов модуля ввода. Привязаны к тумблерам
    axOUT: ARRAY [1..16] OF BOOL; // массив состояний выходов модуля вЫвода. Привязаны к лампочкам
    END_VAR





    // поднимаем COM порт с нужными настройками
    fbComControl
    (
    xEnable := TRUE,
    udiComPort := 33,
    udiBaudrate := 19200,
    udiByteSize := 8,
    eParity := OCL.COM_PARITY.EVEN,
    eStopBit := OCL.COM_STOPBIT.ONE
    );
    //запускаем слейва с адресом 255. Это позоляет ему отвечать на запрос с любым ID.
    fbModbusSerialSlave
    (
    (* насколько я понял - начальный адрес является обманом для опрашивающего устройства.
    Когда оно просит регистр с адресом 50 - мы ему подсовываем свой самый первый регистр,
    и все остаются довольны. То есть - это смещение адресации на уровне нашего кода тут.
    Если бы не было этого параметра - пришлось бы тут в коде создавать область памяти,
    в которой первые 49 слов банально не использовались бы.
    А так - у нас шаблон модуля вывода хочет видеть данные на 50 адресе,
    шаблон модуля ввода - на 51, и мы просто указываем смещение в 50:
    чтоб вместо 50 выдать то, что лежит у нас в первой ячейке, а вместо 51 - то, что во второй.
    50 51 64 ... 79 - шаблоны модулей думают, что читают по этим адресам
    OUT IN IN01 IN16 - вот эти данные
    1 2 15 30 - на самом деле - данные у нас лежат вот с такими индексами.
    *)
    c_uiStartAddr:=50,
    xEnable :=fbComControl.xActive,
    hCom := fbComControl.hCom,
    usiSlaveId := 255,
    pData := ADR(awSlaveData),
    szSize := SIZEOF(awSlaveData)
    );

    // сорян за хардкод Уже некогда разбираться с преобразованиями
    // в первой ячейке у нас лежат состояния вЫходов (которые обманным образом транслируются под видом регистра с адресом 50)
    axOUT[1]:=awSlaveData[1].0;
    axOUT[2]:=awSlaveData[1].1;
    axOUT[3]:=awSlaveData[1].2;
    axOUT[4]:=awSlaveData[1].3;
    axOUT[5]:=awSlaveData[1].4;
    axOUT[6]:=awSlaveData[1].5;
    axOUT[7]:=awSlaveData[1].6;
    axOUT[8]:=awSlaveData[1].7;
    axOUT[9]:=awSlaveData[1].8;
    axOUT[10]:=awSlaveData[1].9;
    axOUT[11]:=awSlaveData[1].10;
    axOUT[12]:=awSlaveData[1].11;
    axOUT[13]:=awSlaveData[1].12;
    axOUT[14]:=awSlaveData[1].13;
    axOUT[15]:=awSlaveData[1].14;
    axOUT[16]:=awSlaveData[1].15;

    // во второй - состояния входов

    awSlaveData[2].0:=axIn[1];
    awSlaveData[2].1:=axIn[2];
    awSlaveData[2].2:=axIn[3];
    awSlaveData[2].3:=axIn[4];
    awSlaveData[2].4:=axIn[5];
    awSlaveData[2].5:=axIn[6];
    awSlaveData[2].6:=axIn[7];
    awSlaveData[2].7:=axIn[8];
    awSlaveData[2].8:=axIn[9];
    awSlaveData[2].9:=axIn[10];
    awSlaveData[2].10:=axIn[11];
    awSlaveData[2].11:=axIn[12];
    awSlaveData[2].12:=axIn[13];
    awSlaveData[2].13:=axIn[14];
    awSlaveData[2].14:=axIn[15];
    awSlaveData[2].15:=axIn[16];
    Последний раз редактировалось Нидвораич; 04.04.2025 в 20:47.

Похожие темы

  1. Отвалы модулей ввода/вывода.
    от FlameAtomicFox в разделе ПЛК2хх
    Ответов: 3
    Последнее сообщение: 03.02.2025, 07:16
  2. Ответов: 2
    Последнее сообщение: 21.03.2024, 08:17
  3. Подключение модулей ввода/вывода по Modbus RTU
    от известь в разделе Master SCADA 4D
    Ответов: 3
    Последнее сообщение: 13.12.2023, 08:41
  4. ПЛК160-24.А-М + 5 модулей ввода/вывода
    от des_na_laes@mail.ru в разделе Мх110
    Ответов: 25
    Последнее сообщение: 22.12.2020, 18:06
  5. ПЛК100+18 модулей ввода и вывода
    от Дулат в разделе Эксплуатация
    Ответов: 23
    Последнее сообщение: 08.11.2019, 11:52

Ваши права

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