Добрый вечер!
Подскажите, где есть по ООП на codesys почитать? И про архитектуру среды исполнения. В документации, которая на сайте мало про это, можно сказать ничего. По крайней мере я не смог найти...
желательно на русском языке :)
Вид для печати
Добрый вечер!
Подскажите, где есть по ООП на codesys почитать? И про архитектуру среды исполнения. В документации, которая на сайте мало про это, можно сказать ничего. По крайней мере я не смог найти...
желательно на русском языке :)
А зачем тебе ООП-то тут? Не надо его сюда, там же всё отлично через FB делается.
Хочешь я тебе вот проект скину на посмотреть? ))
На русском по этим темам фактически ничего нет.
1. Лучшая (и, собственно, единственная мне известная) книга про реализацию ООП в контексте МЭК 61131-3 - это Michael Braun, Wolfgang Horn.
Object-Oriented Programming with SIMOTION. Fundamentals, Program Examples and Software Concepts According to IEC 61131-3
Она, конечно, не про CODESYS - но так как и SIMOTION, и CODESYS базируются на МЭК 61131-3 - то отличий не очень много.
2. Эта документация предоставляется только производителям оборудования и обычно не выкладывается в свободный доступ. Но если погуглить "codesys control win v3 manual" - то кое-что найти можно.
Скидывай, посмотрю.
А вообще мне надо ООП, т.к. хочу, чтоб в моем арсенале были все инструменты. Тем более используя ООП можно более красиво и гибко решить некоторые задачи, обычно это так. Но и не только это, он мне ближе в плане понимания, видимо сказывается предыдущий опыт :)
Вот, например, есть у меня въездные ворота на участок. Как сущность они имеют следующие управляющие входы:
- Enabled (вкл/выкл - выход ПЛК),
- ButtonEnabled (уличная кнопка для управления - вкл/выкл - выход ПЛК),
- Open (команда открыть - выход ПЛК),
- Close (команда закрыть - выход ПЛК);
и следующие выходы:
- isOpen (ворота открыты - вход ПЛК),
- isClosed (ворота закрыты - вход ПЛК),
- Opening (оба входа ПЛК false, был true isClosed),
- Closing (оба входа ПЛК false, был true isOpen).
На ООП я бы создал класс со следующими полями: Enabled, ButtonEnabled, isOpen (readonly), isClosed (readonly), методами: Open и Close и событиями: OnOpening, OnClosing, OnOpen, OnClose, а в вызове конструктора передал бы адреса портов ПЛК. И все, "черный ящик" готов.
Ну, думаю, я и ФБ так создам, накидал выходы: Enabled, ButtonEnabled, Open, Close и выходы: isOpen, isClosed, Opening, Closing и тут понял, что порты ПЛК я никак не завязываю внутри блока и черный ящик не получается. Посмотрел, вроде действительно внутри блока обратиться к портам нельзя (я не нашел как). Можно добавить в ФБ еще 4 входа, для портов ПЛК, тогда более менее что-то получается, но все равно не выходит черного ящика, да и не красиво это....
Может быть я просто еще не научился думать в парадигме ФБ и что-то делаю не так?
Или исключить из арсенала ПЛК1хх 😁
На самом деле ООП на больших проектах выручает. У меня прямо сейчас создается система где 3 частотника пчв, 3 instart и 3 упп. Много чего еще, но на примере этих - по сути это 3 насосные станции, они управляются все по modbus, команды и т.д. у всех разные. Но глобально логика общая работы. ООП в данном ключе отлично подходит.
Ну вам может и не нужен, а я в визуализации массивы из этих объектов использую и это очень удобно, что можно одну визуализацию для разных объектов создавать. Удобно то что массив именно один, а стуктуры в нем разные получаются. Полиморфизма тут конечно нет, вы можете не использовать
А в чем? Инкапсуляция и наследование - это хорошо, но малоинтересно.
Весь код я пока не готов написать, потому что некоторые идеи еще не продуманы до конца, я пока представляю скелет в терминах ООП, который наполняется функционалом по мере реализации идей. Да и мне не требуется, чтобы кто-то полностью за меня решил задачу. Я лишь хочу понять некоторые вещи.
Могу попробовать описать задачу еще раз, другими словами.
Есть автоматические ворота, у них есть несколько входов для управления: вкл/выкл, открыть, закрыть. Так же у них есть два выхода, сигнализирующих о достижении воротами конечных положений открыто и закрыто соответственно. Все это подключено к 5 портам ПЛК.
Соответственно нужно иметь возможность этими воротами управлять: включать/отключать, открывать, закрывать, обрабатывать сигналы о достижении конечных положений.
Цель не просто написать программу, которая будет связывать одно с другим по определенному алгоритму, а сделать это в виде самостоятельной сущности, черного ящика, отдельного блока. Чтобы при программировании сценариев управления воротами абстрагироваться от "внутренностей" и железа и предоставить "пользователю", готовый и полностью функциональный инструмент.
Как я понимаю в этом плане ФБ в первом приближении мне подходит.
На текущий момент не понятно следующее:
1. Есть ли у него что-то типа конструктора? Хочу инициализировать параметры. Можно ли как-то передавать при инициализации номера портов, с которыми он будет уже сам работать. Если такого нет, как это обычно делается? Могу ли я как-то обращаться к портам (или другому оборудованию) из ФБ?
2. ФБ потом вставляется в программу, которая "крутится" в рамках задачи с определенным временем цикла исполнения. И тут вот что не понятно. Есть, например, элемент F_TRIG, он действительно будет срабатывать по заднему фронту сигнала? Или это будет приурочено к следующему циклу программы? Или, например, таймер, установлен на 50 мс, а цикл задачи, в которой крутится программа с этим блоком, равен 200 мс, как это будет работать? В момент срабатывания таймера будет осуществляться переход к программе или это будет приурочено к следующему циклу программы?
3. Вопрос алгоритмического характера. На входе есть два параметра: isOpen и isClosed (сигналы конечных положений ворот), на выходе нужно получить два сигнала: Opening и Closing, которые true только во время открывания/закрывания ворот, в остальное время false. Как это сделать в виде FB? Мне удалось сделать схему, которая для каждого выхода использует по одному F_TRIG и R_TRIG, один AND и один SR, но мне кажется это слишком громоздко для такой простой вещи. По крайней мере с использованием ООП это делается гораздо проще.
пока все :)
Заранее спасибо.
Объясните, почему 200 мс не крутится? Если для задачи не требуется чаще, зачем крутиться чаще?
Так работать не будет, я забыл упомянуть, что управляющий сигнал на открытие/закрытие это короткий импульс, а не импульс на все время открывания/закрывания. И еще второе, сигнал на открытие может быть подан не через ПЛК, а, например, с пульта напрямую на блок управления воротами и мы об этом сможем узнать только косвенно по тому, что изменилось состояние концевика, соответственно управляющий сигнал на открытие в этих целям мы использовать не можем.
Ничего я не путаю. Как я говорил, я попытался это реализовать с помощью ФБ на CFC, получилось громоздко и я решил оформить отдельным ФБ, у которого это как раз входы, а выходы Opening и Closing.
В ООП можно сделать, чтобы свойство класса менялось через вызов функции set, в которой можно было бы установить Opening в нужное значение, а заодно еще кое что сделать. Например так: property isOpen: boolean read isOpen write Set_isOpen;
Извините, если мои вопросы кажутся глупыми, но ведь я только в самом начале. И не только спрашиваю тут, но и читаю материалы.
могу выложить FB (макрос для ПР200) управления роллетой через одну единственную кнопку (физическая кнопка открыть-стоп-закрыть) и управления из ПР200 по сети. Переделаете в CodeSys и добавите необходимое. Там как раз тоже использовались концевики роллеты для контроля. Из особенности, так как кнопка единственная и неизвестно положение роллеты (например человек руками открыл не полностью и потом остановил) а нам надо открыть, что делается только через эту единственную кнопку, то следующее действие будет на закрытие. Соответственно роллета сперва закроется, хотя по сети мы дали команду на открытие, и еще раз нажмет кнопку для открытия, так как команда была открыть. з.ы. работает на приводе Came уже долго, нареканий нет.
А так capzap правильно написал, FB и есть сущность, и FB можно размножать, под них выделяется память под каждый и это надо учитывать, чтобы переменные одного не пересекались с переменными другого (не лезть из кода FB в глобальные переменные например)
Насколько помню, R-Trig и F-Trig работают за два цикла программы. Можете код посмотреть (не помню. можете или нет код штатных FB посмотреть)...
Вопрос задал потому что мне это не понятно, может быть пример с таймером некорректный, я там еще и с F_TRIG привел аналогичный. Просто мне не понятно, работают ли эти вещи по своим событиям независимо от программы или все происходит в те временные интервалы, которые установлены для задачи. Я подозреваю, что работает все как надо, но пока в голове нет ясной картинки как это происходит.
По поводу длительности цикла, я думаю Вы согласитесь, что все зависит от задачи и нет такого, что в любом случае цикл должен быть максимально коротким. Хотя если привязываться к примеру с таймером, тут соглашусь. Но я уже сказал, что пример некорректный.
Или Вы что-то не понимаете или я.
Смотрите, есть ворота, со своим блоком управления, своим пультом, который ими управляет независимо ни от ПЛК ни от той сущности, о которой мы тут разговариваем. При этом мне важно знать в рамках этой моей сущности о том, что какая-то другая система/сущность отправила управляющий сигнал. Хотя и ворота могут управляться и другими способами, но она должна четко отражать их состояние независимо ни от чего. Это можно сделать только косвенно, по сигналам от концевиков. Поэтому в данном случае, во-первых, нельзя использовать управляющий сигнал, т.к. он не уникальный, а во-вторых, то выражение все равно не дает нужного результата, т.к. длительность управляющего импульса и длительность результирующего не совпадают.
Можно не выкладывать, я понял как это сделать, спасибо :)
Сильно не пинайте, давно делал, когда еще не было возможности подцепить концевики и человек по камере ориентировался, так что может в нем что-то лишнее. Ну и Putbit и Extractbit не помню как родились, кажется с подсказок на форуме, заменяют логику проверок.
Или вам самим макросом для ОЛ ?
Второй такой же макрос управляет воротами, там тоже привод Came и кнопка управления одна единственная.
Я сегодня пообщался с товарищем, он мне все объяснил. Я то думал, что тут аппаратный таймер (как это там, где я до этого имел опыт) и программа работает на его прерываниях, а оказывается нифига и все завязано на циклы задач, теперь многие вещи стали понятны.
Ну первое Вам не понравилось, объяснил по-другому, при этом выкинул один управляющий сигнал (ButtonEnabled), т.к. на суть вопроса он не влияет, чтобы не объяснять что это и зачем.
Есть ворота со своим блоком управления, это стандартная комплектация, обычно ограничиваются этим, открывают/закрывают с пульта или кнопки, которая напрямую к блоку управления подключается. При этом блок управления умеет принимать сигналы управления: вкл/выкл, открыть, закрыть и умеет отдавать сигналы с концевиков. Вот эти 5 каналов подключены к ПЛК и ими я хочу рулить в рамках этой сущности. А рулить ими я хочу в рамках системы умного дома.
В целом у меня вроде как сформировалось общее понимание как это все должно быть реализовано, пока вопросов больше не имею, буду пробовать, возможно что-то возникнет в процессе. Всем спасибо.
PS: насчет ООП в codesys, интерес я не потерял, все равно изучу вопрос, чтобы знать что там к чему :)
с циклами все не так однозначно. Многое зависит от выбранного ПЛК, некоторые например умеют в цикле программы опросить вход и записать выход кроме штатных сперва спрашиваем состояние входов, потом цикл программы, потом запись выходов.
Да и таймеры есть различные, онли софтовые как в CodeSys так и хардовые.
з.ы. еще раз, зависит от выбранного ПЛК и что в нем производители учудили...
Ну мы же сейчас конкретно про codesys говорим...
я с версией 3,5 особо не знаком, и 210-й ПЛК не смотрел, не знаю что там можно придумать и как.
Но в общих чертах в основном у всех одинаково - опрос входов, цикл программы, запись выходов.
Выполнение отдельных POU можно по таймеру отдельной задачей и т.д.
Думаю растягивать специально цикл не стоит, пусть себе крутится за то время, какая у вас там программа получится. Хуже ему не будет.
Написал код для ворот. Проверить на реальном объекте на текущий момент не могу - надо там кое-что по "физике" допилить. Попинайте код кому не сложно:
Код:FUNCTION_BLOCK TSH_Portal //префикс: tshp
VAR_INPUT
xEnabled: BOOL:=TRUE; //Включение/выключение управляющего блока ворот
xButtonEnabled: BOOL:=FALSE; //Включение/выключение кнопки управления воротами на столбе
xManual: BOOL:=FALSE; //флаг режима ручного управления (чтобы можно было самому дергать релюшки в щите)
xRepeatCommand: BOOL:=FALSE; //повторять ли команду, если ворота не закрылись через установленное время
xgpioIsOpen: BOOL:=FALSE; //вход от концевика "открыто"
xgpioIsClosed: BOOL:=FALSE; //вход от концевика "закрыто"
timMovingDutarion: TIME:=T#35S; //время движения ворот между конечными положениями
timCommandImpulseDuration: TIME:=T#500MS; //длительность импульса команды открывания/закрывания ворот
timCommandRepeatTime: TIME:=T#150S; //время ожидания между повторной попыткой открытия/закрытия, если в предыдущий раз не получилось (препятствие и тд)
timNotClosedLimit: TIME:=T#5M; //максимальная допустимая продолжительность нахождения ворот в не закрытом состоянии
END_VAR
VAR_IN_OUT
xOpen: BOOL:=FALSE; //команда "открыть"
xClose: BOOL:=FALSE; //команда "закрыть"
END_VAR
VAR_OUTPUT
xgpioEnabled: BOOL; //выход на реле "вкл/выкл"
xgpioButtonEnabled: BOOL; //выход на реле "вкл/выкл кнопки управления воротами"
xgpioOpen: BOOL; //выход на реле сигнала команды "открыть"
xgpioClose: BOOL; //выход на реле сигнала команды "закрыть"
eState: TSH_PortalStates; //состояние ворот
xerrLimitSwitches: BOOL; //ошибка концевиков - оба показывают, что замкнуты
xerrForbidden: BOOL; //запрещенная операция (одновременная подача команд октрыто и закрыто)
xerrNoAction: BOOL; //нет реакции на команду
xerrObstacle: BOOL; //препятствие в створе ворот
xerrNotClosedLimitExceed: BOOL; //слишком долго открыты
timNotClosed: TIME; //количество времени, которое ворота не закрыты
END_VAR
VAR
//сигнал концевика "закрыто"
ftrigIsClosed: F_TRIG; //триггер спада
rtrigIsClosed: R_TRIG; //триггер фронта
xRaiseIsClosed: BOOL; //флаг фронта
xFadeIsClosed: BOOL; //флаг спада
//сигнал концевика "открыто"
rtrigIsOpen: R_TRIG; //триггер фронта
ftrigIsOpen: F_TRIG; //триггер спада
xRaiseIsOpen: BOOL; //флаг фронта
xFadeIsOpen: BOOL; //флаг спада
//команды
rtrigOpen: R_TRIG; //триггер фронта команды Open
rtrigClose: R_TRIG; //триггер фронта команды Close
xRaiseOpen: BOOL; //флаг фронта сигнала "открыть"
xRaiseClose: BOOL; //флаг фронта сигнала "закрыть"
xLastCommandIsOpen: BOOL; //какая была последняя команда: true - открытие
//таймеры
tpCMDImpulse: TP; //импульс команды
tpMoving: TP; //перемещение ворот между конечными положениями
tpCMDRepeat: TP; //пауза между повторной отправкой команды, если ворота в ожидаемое время не пришли в конечное положение
timStartMoving: TIME; //время начала движения ворот
END_VAR
xgpioEnabled:=xEnabled;
//если ворота выключены, то остальные сигналы также false
xgpioButtonEnabled:=xButtonEnabled AND xEnabled;
xOpen:=xOpen AND xEnabled AND NOT xManual;
xClose:=xClose AND xEnabled AND NOT xManual;
//******ОБРАБОТКА КОМАНД******
//определяем наличие фронтов сигналов команд
rtrigOpen(CLK:=xOpen, Q=>xRaiseOpen);
rtrigClose(CLK:=xClose, Q=>xRaiseClose);
IF xRaiseOpen AND xRaiseClose THEN //два фронта одновременно - ошибка
xerrForbidden:=TRUE;
xRaiseOpen:=FALSE;
xRaiseClose:=FALSE;
xOpen:=FALSE;
xClose:=FALSE;
END_IF
//Если вторая команда пришла во время импульса первой, отменить первую запустить вторую
IF xOpen AND xClose AND (xRaiseClose XOR xRaiseOpen) THEN
//актуализируем активную команду
xOpen:=xRaiseOpen;
xClose:=xRaiseClose;
//сбрасываем таймеры
tpCMDImpulse(IN:=FALSE, PT:=T#0S);
tpMoving(IN:=FALSE, PT:=T#0S);
tpCMDRepeat(IN:=FALSE, PT:=T#0S);
END_IF
tpCMDImpulse(IN:=xRaiseOpen XOR xRaiseClose, PT:=timCommandImpulseDuration); //если прошла команда, запускаем таймер длительности импульса команды
xOpen:=xOpen AND tpCMDImpulse.Q; //приводим импульс команды на входе ФБ в соответствие реальному импульсу
xClose:=xClose AND tpCMDImpulse.Q; //приводим импульс команды на входе ФБ в соответствие реальному импульсу
xgpioOpen:=xOpen;
xgpioClose:=xClose;
//запоминаем последнюю команду (для того, чтобы ее повторить, если потребуется)
IF xOpen THEN
xLastCommandIsOpen:=TRUE;
ELSIF xClose THEN
xLastCommandIsOpen:=FALSE;
END_IF
//******ОБРАБОТКА КОМАНД******
//******ОБРАБОТКА СИГНАЛОВ КОНЦЕВИКОВ******
xerrLimitSwitches:=xgpioIsOpen AND xgpioIsClosed; //оба концевика включены - ошибка
//Определение изменений состояний концевиков
rtrigIsOpen(CLK:=xgpioIsOpen, Q=>xRaiseIsOpen);
ftrigIsOpen(CLK:=xgpioIsOpen, Q=>xFadeIsOpen);
rtrigIsOpen(CLK:=xgpioIsClosed, Q=>xRaiseIsClosed);
ftrigIsOpen(CLK:=xgpioIsClosed, Q=>xFadeIsClosed);
//обработка начала движения (по спаду сигнала концевика)
IF xFadeIsOpen OR xFadeIsClosed THEN
timStartMoving:=TIME();
tpMoving(IN:=xEnabled, PT:=2*timMovingDutarion); //запускаем таймер длительности перемещения ворот между конечными положениями, на двойное время, т.к. часто в процессе движения напрвление движения меняется через другие каналы (кнопка, пульт)
END_IF
timNotClosed:=SEL(NOT xgpioIsOpen AND NOT xgpioIsClosed, 0, TIME()-timStartMoving);
xerrNotClosedLimitExceed:=(timNotClosed>timNotClosedLimit);
xerrNotClosedLimitExceed:=(timNotClosed>timNotClosedLimit);
//обработка прихода ворот в конечное положение
IF xRaiseIsOpen OR xRaiseIsClosed THEN
//сбрасываем таймеры
tpCMDImpulse(IN:=FALSE, PT:=T#0S);
tpMoving(IN:=FALSE, PT:=T#0S);
tpCMDRepeat(IN:=FALSE, PT:=T#0S);
//сбрасываем сигналы команд
xOpen:=FALSE;
xClose:=FALSE;
//и ошибки "препятствие в створе" и предела времени в незакрытом состоянии
xerrObstacle:=FALSE;
xerrNotClosedLimitExceed:=FALSE;
END_IF
//импульс команды закончился, а ворота так и не сдвинулись с места - ошибка
//выражение идет после обработки прихода ворот в конечное положение, т.к. за время
//импульса они могли начать движение и успеть вернуться обратно, что не является ошибкой
xerrNoAction:=tpCMDImpulse.IN AND (tpCMDImpulse.ET=tpCMDImpulse.PT) AND (xgpioIsOpen OR xgpioIsClosed);
//******ОБРАБОТКА СИГНАЛОВ КОНЦЕВИКОВ******
//******ОБРАБОТКА ТАЙМЕРОВ ******
//ожидаемое время движения ворот между конечными положениями истекло дважды
IF tpMoving.IN AND tpMoving.ET=tpMoving.PT THEN
xerrObstacle:=TRUE; //считается, что обнаружено препятствие
tpCMDRepeat(IN:=xRepeatCommand AND xEnabled, PT:=timCommandRepeatTime); //запускаем таймер ожидания перед повторной отправкой команды
tpMoving(IN:=FALSE, PT:=T#0S); //сбрасываем таймер движения
END_IF
//время ожидания перед повторной отправкой команды истекло
IF tpCMDRepeat.IN AND tpCMDRepeat.ET=tpCMDRepeat.PT THEN
xOpen:=xLastCommandIsOpen;
xClose:=NOT xLastCommandIsOpen;
//сбрасываем таймер
tpCMDRepeat(IN:=FALSE, PT:=T#0S);
END_IF
//******ОБРАБОТКА ТАЙМЕРОВ******
//Устанавливаем состояние ворот
IF xgpioIsOpen THEN
eState:=TSH_PortalStates.tshpsIsOpen;
ELSIF xgpioIsClosed THEN
eState:=TSH_PortalStates.tshpsIsClosed;
ELSE
eState:=TSH_PortalStates.tshpsInBetween;
END_IF
1. У тебя там в сигналах ошибки )) Написано про триггер фронта и спада, а стоят везде F_TRIG. А на спаде на R_TRIG.
2. Дальше непонятно: вот если ворота выключены, то ты выставил FALSE на выходы... а дальше-то что? Идёт логика, которая эти же выходы меняет. И на выходе FB они и будут всё равно чего-то значить. Совсем ты ни хрена не так прогаешь чего-то!
Вообще это бы написать бы на конечном автомате... взять переменную типа State, к ней ENUM, и дальше что-то типа
и так далее...Код:CASE State OF
stManual : ****
stCmdOpen : ****
stCmdClose : ****
stMoving : ****
stNotClosed : ****
Где? Посмотрел, не нашел ошибки, вроде все верно.
Нет такой логики, везде, где на что-то меняется есть условия, которые не выполняются. По крайней мере я не нашел.
На конечном автомате не получится, потому что state может принимать несколько значений одновременно (даже из тех, что ты перечислил), а если оставить только те, которые исключают друг друга, то тогда почти весь код останется.
Мне тоже не очень понравился код, красоты нет, но лучше пока не придумал....
А вот ты ж пишешь так:
Это не ошибка?Код:ftrigIsClosed: F_TRIG; //триггер спада
rtrigIsClosed: F_TRIG; //триггер фронта
Про логику - я про этот код говорю:
Почему у тебя он в начале стоит? Я не весь код смотрел, но не получается ли так, что ты по xEnabled выставил xOpen/xClose в FALSE, а потом поменял их?Код://если ворота выключены, то остальные сигналы также false
xgpioButtonEnabled:=xButtonEnabled AND xEnabled;
xOpen:=xOpen AND xEnabled AND NOT xManual;
xClose:=xClose AND xEnabled AND NOT xManual;
Ошибка, точно! Поправил! Спасибо!
Не получается.
А в начале я его поставил потому что именно тут проще всего пресечь разные поползновения, чтобы ФБ не пытался там что-то делать. При этом он отслеживает состояние концевиков, отслеживает движение. То есть как бы не совсем disabled, а только команды не принимает. Ни через ПЛК, ни через другие каналы, т.к. блок управления ворот отключен по этому сигналу. Но ведь ворота можно руками перемещать, вот это отслеживается.
PS: Правда может при этом работать таймер повтора последней команды и даже подать ее, но эта команда может выполниться только в следующем цикле, в котором ей так же будет присвоен false и ничего не произойдет.
PPS: Вообще для прозрачности сделаю, чтоб и таймеры просто так не работали, нефиг.
Имеете в виду, что надо заменить на IF и не выполнять операцию каждый цикл? Разве это даст экономию? Просто так красивее, люблю когда компактно.
Где перечитать? В руководстве пользователя практически ничего про них, я ничего противоречащего не нашел. Имеется в виду, если его использовать в условии if .... then? Как тут:
Что при этом происходит? ФБ таймера не исполняется?Код:IF tpCMDRepeat.IN AND tpCMDRepeat.ET=tpCMDRepeat.PT THEN
Да, не знал, попробую, спасибо.
Засрали тему про ООП
А разницы и нет, все тоже самое на выходе. Просто этот механизм позволяет использовать один код для разных ФБ в их одинаковой части. Кому-то проще копи паст, я для себя решил что лучше заморочиться этим способом но потом изменять можно только в одном месте. Когда код живет и развивается от проекта к проекту то это имеет смысл. Если подход лишь бы сдать и забыть то смотреть в сторону ООП от CoDeSys смысла нет совсем, лишние танцы с бубном. Ну и как бонус ООП это еще более универсальная визуализация - опять из проекта в проект проще перетаскивать.
Попробую накидать что-то упрощеное, чтобы не влезать в лишние дебри не имеющие отношение к вопросу.
Подскажите, как реализовать импульсное изменение переменной для ПЛК на языке ST, чем оно отличается от изменения моностабильной кнопкой?
alex2021 Раз ты спрашиваешь, "КАК" реализовать - то значит ты на ST умеешь.
Так вот делаешь FB, в котором на вход кнопки вешаешь R_TRIG, чтобы при длинном считывании кнопки всё срабатывало один раз, пока кнопку снова не отпустят и не нажмут. А потом по выходу этого R_TRIG фигачишь изменение переменной (да хотя бы bOut := NOT bOut;).
Отличается от моностабильного тем, что импульсное реле - бистабильное.
Представьте СУ, с 8 (например) аналоговыми входами, у которой 2 типа аналоговых датчиков. Локальные и удаленные (например, modbus), При этом, эти 2 типа еще и подразделяются на, например, температурные и уровня.
И у вас по ТЗ есть необходимость, что бы к каждому из входов СУ можно было подключить любой из датчиков, любого типа, сконфигурировав их в интерфейсе пользователя.
Используя интерфейсы, вы создаете 8 входов СУ, используя наследование (раздельно для modbus и локальных) и интерфейсы - создаете датчики.
Профит, все красиво, читаемо, рабочее.
У ООП много плюсов. Но насчет все красиво, читаемо - это не главное. Инкапсуляция, полиморфизм - да. Защитить данные объекта от некорректной записи, быстро создать множество однотипных сущностей с различными реализациями интерфейсов - тысячу раз да. Но есть один нюанс в работе на ПЛК. Если Ваше приложение не требует быстрой реакции на воздействия - можно творить что угодно, вызывать функции в функции. А вот если время реакции важно сократить, нужно учитывать что есть временные затраты на загрузку/выгрузку в/из стека при вызовах функционального блока, функции, задачи. Я пока новичок в ПЛК. И мне интересно - кто-нибудь оценивал как влияют временные задержки при вызовах функций, блоков и т.д. например на джиттер?
Время реакции зависит от времени цикла и скорости связи с модулями ввода/вывода.