PDA

Просмотр полной версии : Использование ООП в CODESYS V3.5



turkish945
26.04.2020, 22:49
Добрый вечер!

Подскажите, где есть по ООП на codesys почитать? И про архитектуру среды исполнения. В документации, которая на сайте мало про это, можно сказать ничего. По крайней мере я не смог найти...

желательно на русском языке :)

Cs-Cs
26.04.2020, 22:59
А зачем тебе ООП-то тут? Не надо его сюда, там же всё отлично через FB делается.
Хочешь я тебе вот проект скину на посмотреть? ))

Евгений Кислов
26.04.2020, 22:59
Добрый вечер!

Подскажите, где есть по ООП на codesys почитать? И про архитектуру среды исполнения. В документации, которая на сайте мало про это, можно сказать ничего. По крайней мере я не смог найти...

желательно на русском языке :)

На русском по этим темам фактически ничего нет.

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" - то кое-что найти можно.

turkish945
27.04.2020, 18:41
А зачем тебе ООП-то тут? Не надо его сюда, там же всё отлично через FB делается.
Хочешь я тебе вот проект скину на посмотреть? ))
Скидывай, посмотрю.

А вообще мне надо ООП, т.к. хочу, чтоб в моем арсенале были все инструменты. Тем более используя ООП можно более красиво и гибко решить некоторые задачи, обычно это так. Но и не только это, он мне ближе в плане понимания, видимо сказывается предыдущий опыт :)

Вот, например, есть у меня въездные ворота на участок. Как сущность они имеют следующие управляющие входы:
- 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 входа, для портов ПЛК, тогда более менее что-то получается, но все равно не выходит черного ящика, да и не красиво это....

Может быть я просто еще не научился думать в парадигме ФБ и что-то делаю не так?

turkish945
27.04.2020, 18:42
На русском по этим темам фактически ничего нет.

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" - то кое-что найти можно.

Спасибо, посмотрю.

RV9WFJ
27.04.2020, 18:57
Или исключить из арсенала ПЛК1хх 😁

На самом деле ООП на больших проектах выручает. У меня прямо сейчас создается система где 3 частотника пчв, 3 instart и 3 упп. Много чего еще, но на примере этих - по сути это 3 насосные станции, они управляются все по modbus, команды и т.д. у всех разные. Но глобально логика общая работы. ООП в данном ключе отлично подходит.

turkish945
27.04.2020, 19:07
разницы особой нет как писать через ООП или обычным способом для КДС, чем больше пользоваться тот и становиться по ощущениям более удобным, а суть не меняется. Но лучше закончить с ФБ, потому как если встанет задача творить на плк1хх сериях, то там нет ООП

Расскажите, как правильно решить описанную выше задачу с помощью FB?

ASo
27.04.2020, 19:09
На самом деле ООП на больших проектах выручает. У меня прямо сейчас создается система где 3 частотника пчв, 3 instart и 3 упп.
Т.е. Вы используете полиморфизм? Без него ООП в принципе мало нужен.

RV9WFJ
27.04.2020, 19:24
Ну вам может и не нужен, а я в визуализации массивы из этих объектов использую и это очень удобно, что можно одну визуализацию для разных объектов создавать. Удобно то что массив именно один, а стуктуры в нем разные получаются. Полиморфизма тут конечно нет, вы можете не использовать ��

turkish945
27.04.2020, 19:46
Т.е. Вы используете полиморфизм? Без него ООП в принципе мало нужен.

Если требуется - использую, но, думаю, в текущих задачах он не пригодится. Все же прелести ООП не только в этом заключаются.

ASo
27.04.2020, 22:38
А в чем? Инкапсуляция и наследование - это хорошо, но малоинтересно.

turkish945
28.04.2020, 21:09
для начала, описание задачи меня не устраивает. Для чего писать что входы сущности это выходы плк, а выходы это входы, чтобы запутать всех? Выложите код, на чем Вы там уверенно пишите и по нему можно будет вернуть код и в языках КДС. И про порты тоже стоит объяснить, что Вы имеете ввиду, потому что по документации это инструменты связи с периферией, непосредственно управлением исполнительными механизмами они не осуществляют
Весь код я пока не готов написать, потому что некоторые идеи еще не продуманы до конца, я пока представляю скелет в терминах ООП, который наполняется функционалом по мере реализации идей. Да и мне не требуется, чтобы кто-то полностью за меня решил задачу. Я лишь хочу понять некоторые вещи.

Могу попробовать описать задачу еще раз, другими словами.
Есть автоматические ворота, у них есть несколько входов для управления: вкл/выкл, открыть, закрыть. Так же у них есть два выхода, сигнализирующих о достижении воротами конечных положений открыто и закрыто соответственно. Все это подключено к 5 портам ПЛК.
Соответственно нужно иметь возможность этими воротами управлять: включать/отключать, открывать, закрывать, обрабатывать сигналы о достижении конечных положений.

Цель не просто написать программу, которая будет связывать одно с другим по определенному алгоритму, а сделать это в виде самостоятельной сущности, черного ящика, отдельного блока. Чтобы при программировании сценариев управления воротами абстрагироваться от "внутренностей" и железа и предоставить "пользователю", готовый и полностью функциональный инструмент.

Как я понимаю в этом плане ФБ в первом приближении мне подходит.

На текущий момент не понятно следующее:
1. Есть ли у него что-то типа конструктора? Хочу инициализировать параметры. Можно ли как-то передавать при инициализации номера портов, с которыми он будет уже сам работать. Если такого нет, как это обычно делается? Могу ли я как-то обращаться к портам (или другому оборудованию) из ФБ?
2. ФБ потом вставляется в программу, которая "крутится" в рамках задачи с определенным временем цикла исполнения. И тут вот что не понятно. Есть, например, элемент F_TRIG, он действительно будет срабатывать по заднему фронту сигнала? Или это будет приурочено к следующему циклу программы? Или, например, таймер, установлен на 50 мс, а цикл задачи, в которой крутится программа с этим блоком, равен 200 мс, как это будет работать? В момент срабатывания таймера будет осуществляться переход к программе или это будет приурочено к следующему циклу программы?
3. Вопрос алгоритмического характера. На входе есть два параметра: isOpen и isClosed (сигналы конечных положений ворот), на выходе нужно получить два сигнала: Opening и Closing, которые true только во время открывания/закрывания ворот, в остальное время false. Как это сделать в виде FB? Мне удалось сделать схему, которая для каждого выхода использует по одному F_TRIG и R_TRIG, один AND и один SR, но мне кажется это слишком громоздко для такой простой вещи. По крайней мере с использованием ООП это делается гораздо проще.

пока все :)

Заранее спасибо.

turkish945
29.04.2020, 09:33
200мс программы не крутятся, если их не заставить это сделать, но это уже зависит от адекватности программиста

Объясните, почему 200 мс не крутится? Если для задачи не требуется чаще, зачем крутиться чаще?



ЗЫ реализация Opening := упр_сигнал_на_открытие XOR обратная_связь_от_от_концевика AND упр_сигнал_на_открытие

Так работать не будет, я забыл упомянуть, что управляющий сигнал на открытие/закрытие это короткий импульс, а не импульс на все время открывания/закрывания. И еще второе, сигнал на открытие может быть подан не через ПЛК, а, например, с пульта напрямую на блок управления воротами и мы об этом сможем узнать только косвенно по тому, что изменилось состояние концевика, соответственно управляющий сигнал на открытие в этих целям мы использовать не можем.



Вы даже терминологию не правильно используете. Понятия isOpen/isClose должны вызываться из сущности, которая должна ответить на вопрос в каком состоянии находится объект, а не быть входами

Ничего я не путаю. Как я говорил, я попытался это реализовать с помощью ФБ на CFC, получилось громоздко и я решил оформить отдельным ФБ, у которого это как раз входы, а выходы Opening и Closing.




так покажите.

В ООП можно сделать, чтобы свойство класса менялось через вызов функции set, в которой можно было бы установить Opening в нужное значение, а заодно еще кое что сделать. Например так: property isOpen: boolean read isOpen write Set_isOpen;


Извините, если мои вопросы кажутся глупыми, но ведь я только в самом начале. И не только спрашиваю тут, но и читаю материалы.

melky
29.04.2020, 10:31
могу выложить FB (макрос для ПР200) управления роллетой через одну единственную кнопку (физическая кнопка открыть-стоп-закрыть) и управления из ПР200 по сети. Переделаете в CodeSys и добавите необходимое. Там как раз тоже использовались концевики роллеты для контроля. Из особенности, так как кнопка единственная и неизвестно положение роллеты (например человек руками открыл не полностью и потом остановил) а нам надо открыть, что делается только через эту единственную кнопку, то следующее действие будет на закрытие. Соответственно роллета сперва закроется, хотя по сети мы дали команду на открытие, и еще раз нажмет кнопку для открытия, так как команда была открыть. з.ы. работает на приводе Came уже долго, нареканий нет.

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

melky
29.04.2020, 10:39
Насколько помню, R-Trig и F-Trig работают за два цикла программы. Можете код посмотреть (не помню. можете или нет код штатных FB посмотреть)...

turkish945
29.04.2020, 12:57
тогда зачем задавать вопрос, что будет с таймерами с 50мс, зачем делать 50мс, если цикл не нужен <200мс, тут Вы тоже ошибаетесь, чем больше цикл тем больше временные погрешности, это для визуализации особой разницы нет, так устроен человеческий глаз, но это другая задача со своим временем, контроллер должен работать максимально быстро, чтоб как минимум быть точнее во временах

Вопрос задал потому что мне это не понятно, может быть пример с таймером некорректный, я там еще и с F_TRIG привел аналогичный. Просто мне не понятно, работают ли эти вещи по своим событиям независимо от программы или все происходит в те временные интервалы, которые установлены для задачи. Я подозреваю, что работает все как надо, но пока в голове нет ясной картинки как это происходит.
По поводу длительности цикла, я думаю Вы согласитесь, что все зависит от задачи и нет такого, что в любом случае цикл должен быть максимально коротким. Хотя если привязываться к примеру с таймером, тут соглашусь. Но я уже сказал, что пример некорректный.


так Вы не отвлекайтесь от своей задачи создания сущности, импульс можно подать за пределами ПОУ непосредственно при присвоении физическому выходу, точно так же, нет разницы сколько управленцев будет, они должны реализовать свою логику в совершенно другом месте
Или Вы что-то не понимаете или я.
Смотрите, есть ворота, со своим блоком управления, своим пультом, который ими управляет независимо ни от ПЛК ни от той сущности, о которой мы тут разговариваем. При этом мне важно знать в рамках этой моей сущности о том, что какая-то другая система/сущность отправила управляющий сигнал. Хотя и ворота могут управляться и другими способами, но она должна четко отражать их состояние независимо ни от чего. Это можно сделать только косвенно, по сигналам от концевиков. Поэтому в данном случае, во-первых, нельзя использовать управляющий сигнал, т.к. он не уникальный, а во-вторых, то выражение все равно не дает нужного результата, т.к. длительность управляющего импульса и длительность результирующего не совпадают.

turkish945
29.04.2020, 12:59
могу выложить FB (макрос для ПР200) управления роллетой через одну единственную кнопку (физическая кнопка открыть-стоп-закрыть) и управления из ПР200 по сети. Переделаете в CodeSys и добавите необходимое. Там как раз тоже использовались концевики роллеты для контроля. Из особенности, так как кнопка единственная и неизвестно положение роллеты (например человек руками открыл не полностью и потом остановил) а нам надо открыть, что делается только через эту единственную кнопку, то следующее действие будет на закрытие. Соответственно роллета сперва закроется, хотя по сети мы дали команду на открытие, и еще раз нажмет кнопку для открытия, так как команда была открыть. з.ы. работает на приводе Came уже долго, нареканий нет.

выложите, посмотрю.

turkish945
29.04.2020, 13:11
Можно не выкладывать, я понял как это сделать, спасибо :)

melky
29.04.2020, 13:12
Сильно не пинайте, давно делал, когда еще не было возможности подцепить концевики и человек по камере ориентировался, так что может в нем что-то лишнее. Ну и Putbit и Extractbit не помню как родились, кажется с подсказок на форуме, заменяют логику проверок.

Или вам самим макросом для ОЛ ?

Второй такой же макрос управляет воротами, там тоже привод Came и кнопка управления одна единственная.

turkish945
29.04.2020, 21:07
в плк мы не управляем временем цикла, мы задаем минимальное время цикла и максимальное. Первое нужно чтоб обеспечить постоянство начало времени исполнения каждого скана, для того чтоб можно было спрогнозировать через сколько циклов пройдет некоторое время, второе для понимания что всё зависло и надо делать вачдог. Контроллер "съест" столько миллисекунд сколько потребуется выполнить все инструкции машинного кода в каждом конкретном скане. Если минимальное время превышает необходимое, плк будет выполнять фоновую задачу пока не наступит время нового цикла.

Я сегодня пообщался с товарищем, он мне все объяснил. Я то думал, что тут аппаратный таймер (как это там, где я до этого имел опыт) и программа работает на его прерываниях, а оказывается нифига и все завязано на циклы задач, теперь многие вещи стали понятны.


и не поймем, потому что в начале
Ну первое Вам не понравилось, объяснил по-другому, при этом выкинул один управляющий сигнал (ButtonEnabled), т.к. на суть вопроса он не влияет, чтобы не объяснять что это и зачем.


назначение плк до сих пор не понятно
Есть ворота со своим блоком управления, это стандартная комплектация, обычно ограничиваются этим, открывают/закрывают с пульта или кнопки, которая напрямую к блоку управления подключается. При этом блок управления умеет принимать сигналы управления: вкл/выкл, открыть, закрыть и умеет отдавать сигналы с концевиков. Вот эти 5 каналов подключены к ПЛК и ими я хочу рулить в рамках этой сущности. А рулить ими я хочу в рамках системы умного дома.

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

PS: насчет ООП в codesys, интерес я не потерял, все равно изучу вопрос, чтобы знать что там к чему :)

melky
29.04.2020, 22:48
с циклами все не так однозначно. Многое зависит от выбранного ПЛК, некоторые например умеют в цикле программы опросить вход и записать выход кроме штатных сперва спрашиваем состояние входов, потом цикл программы, потом запись выходов.

Да и таймеры есть различные, онли софтовые как в CodeSys так и хардовые.

з.ы. еще раз, зависит от выбранного ПЛК и что в нем производители учудили...

turkish945
30.04.2020, 08:56
Ну мы же сейчас конкретно про codesys говорим...

melky
30.04.2020, 10:41
я с версией 3,5 особо не знаком, и 210-й ПЛК не смотрел, не знаю что там можно придумать и как.
Но в общих чертах в основном у всех одинаково - опрос входов, цикл программы, запись выходов.
Выполнение отдельных POU можно по таймеру отдельной задачей и т.д.

Думаю растягивать специально цикл не стоит, пусть себе крутится за то время, какая у вас там программа получится. Хуже ему не будет.

turkish945
01.05.2020, 22:19
Написал код для ворот. Проверить на реальном объекте на текущий момент не могу - надо там кое-что по "физике" допилить. Попинайте код кому не сложно:



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

Cs-Cs
01.05.2020, 22:38
1. У тебя там в сигналах ошибки )) Написано про триггер фронта и спада, а стоят везде F_TRIG. А на спаде на R_TRIG.
2. Дальше непонятно: вот если ворота выключены, то ты выставил FALSE на выходы... а дальше-то что? Идёт логика, которая эти же выходы меняет. И на выходе FB они и будут всё равно чего-то значить. Совсем ты ни хрена не так прогаешь чего-то!

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

CASE State OF
stManual : ****
stCmdOpen : ****
stCmdClose : ****
stMoving : ****
stNotClosed : ****
и так далее...

turkish945
01.05.2020, 23:30
1. У тебя там в сигналах ошибки )) Написано про триггер фронта и спада, а стоят везде F_TRIG. А на спаде на R_TRIG.

Где? Посмотрел, не нашел ошибки, вроде все верно.


Дальше непонятно: вот если ворота выключены, то ты выставил FALSE на выходы... а дальше-то что? Идёт логика, которая эти же выходы меняет.

Нет такой логики, везде, где на что-то меняется есть условия, которые не выполняются. По крайней мере я не нашел.

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

Мне тоже не очень понравился код, красоты нет, но лучше пока не придумал....

Cs-Cs
01.05.2020, 23:34
А вот ты ж пишешь так:

ftrigIsClosed: F_TRIG; //триггер спада
rtrigIsClosed: F_TRIG; //триггер фронта
Это не ошибка?

Про логику - я про этот код говорю:

//если ворота выключены, то остальные сигналы также false
xgpioButtonEnabled:=xButtonEnabled AND xEnabled;
xOpen:=xOpen AND xEnabled AND NOT xManual;
xClose:=xClose AND xEnabled AND NOT xManual;
Почему у тебя он в начале стоит? Я не весь код смотрел, но не получается ли так, что ты по xEnabled выставил xOpen/xClose в FALSE, а потом поменял их?

turkish945
02.05.2020, 00:08
Это не ошибка?

Ошибка, точно! Поправил! Спасибо!



Почему у тебя он в начале стоит? Я не весь код смотрел, но не получается ли так, что ты по xEnabled выставил xOpen/xClose в FALSE, а потом поменял их?

Не получается.
А в начале я его поставил потому что именно тут проще всего пресечь разные поползновения, чтобы ФБ не пытался там что-то делать. При этом он отслеживает состояние концевиков, отслеживает движение. То есть как бы не совсем disabled, а только команды не принимает. Ни через ПЛК, ни через другие каналы, т.к. блок управления ворот отключен по этому сигналу. Но ведь ворота можно руками перемещать, вот это отслеживается.

PS: Правда может при этом работать таймер повтора последней команды и даже подать ее, но эта команда может выполниться только в следующем цикле, в котором ей так же будет присвоен false и ничего не произойдет.

PPS: Вообще для прозрачности сделаю, чтоб и таймеры просто так не работали, нефиг.

turkish945
02.05.2020, 10:29
только зачем в коде постоянная избыточность, типа А := А AND Б; ?
Имеете в виду, что надо заменить на IF и не выполнять операцию каждый цикл? Разве это даст экономию? Просто так красивее, люблю когда компактно.


Стоит перечитать про работу таймеров, если они запускаются в теле импульсного условия, то ихним функционалом не воспользоваться
Где перечитать? В руководстве пользователя практически ничего про них, я ничего противоречащего не нашел. Имеется в виду, если его использовать в условии if .... then? Как тут:


IF tpCMDRepeat.IN AND tpCMDRepeat.ET=tpCMDRepeat.PT THEN

Что при этом происходит? ФБ таймера не исполняется?


Возможно Вы не в курсе, но если использовать в меню галку симуляция, то можно проверить свой код на работоспособность не дожидаясь наличия контроллера
Да, не знал, попробую, спасибо.

RV9WFJ
02.05.2020, 11:32
Засрали тему про ООП��

приборист
02.05.2020, 12:31
Засрали тему про ООП��

Где бы найти пример реального использования ООП?

Не ООП ради ООП - а именно пример с живого объекта, и использование всех функций (Интерфейсы, Действие, Метод, Переход, Свойства).
Где действительно видно разницу с ФБ.

Cs-Cs
02.05.2020, 13:38
IF tpCMDRepeat.IN AND tpCMDRepeat.ET=tpCMDRepeat.PT THENЧто при этом происходит? ФБ таймера не исполняется?
Ага, НЕ исполняется. Тут ты только его выходами пользуешься.
А исполняется он тогда, когда ты его вызываешь как

tpCMDRepeat(.....);

RV9WFJ
03.05.2020, 09:23
Где бы найти пример реального использования ООП?

Не ООП ради ООП - а именно пример с живого объекта, и использование всех функций (Интерфейсы, Действие, Метод, Переход, Свойства).
Где действительно видно разницу с ФБ.А разницы и нет, все тоже самое на выходе. Просто этот механизм позволяет использовать один код для разных ФБ в их одинаковой части. Кому-то проще копи паст, я для себя решил что лучше заморочиться этим способом но потом изменять можно только в одном месте. Когда код живет и развивается от проекта к проекту то это имеет смысл. Если подход лишь бы сдать и забыть то смотреть в сторону ООП от CoDeSys смысла нет совсем, лишние танцы с бубном. Ну и как бонус ООП это еще более универсальная визуализация - опять из проекта в проект проще перетаскивать.

приборист
03.05.2020, 11:16
А разницы и нет, все тоже самое на выходе. Просто этот механизм позволяет использовать один код для разных ФБ в их одинаковой части. Кому-то проще копи паст, я для себя решил что лучше заморочиться этим способом но потом изменять можно только в одном месте. Когда код живет и развивается от проекта к проекту то это имеет смысл. Если подход лишь бы сдать и забыть то смотреть в сторону ООП от CoDeSys смысла нет совсем, лишние танцы с бубном. Ну и как бонус ООП это еще более универсальная визуализация - опять из проекта в проект проще перетаскивать.

На выходе понятно.
Можно вообще все в PCL_PRG написать и делайте что хотите дальше, работает же :)

А можно увидеть пример кода? В связке с визуализацией.

RV9WFJ
03.05.2020, 11:47
Попробую накидать что-то упрощеное, чтобы не влезать в лишние дебри не имеющие отношение к вопросу.

alex2021
26.03.2021, 14:28
Подскажите, как реализовать импульсное изменение переменной для ПЛК на языке ST, чем оно отличается от изменения моностабильной кнопкой?

Cs-Cs
26.03.2021, 15:41
alex2021 Раз ты спрашиваешь, "КАК" реализовать - то значит ты на ST умеешь.
Так вот делаешь FB, в котором на вход кнопки вешаешь R_TRIG, чтобы при длинном считывании кнопки всё срабатывало один раз, пока кнопку снова не отпустят и не нажмут. А потом по выходу этого R_TRIG фигачишь изменение переменной (да хотя бы bOut := NOT bOut;).
Отличается от моностабильного тем, что импульсное реле - бистабильное.

keysansa
12.05.2021, 17:14
Где бы найти пример реального использования ООП?

Не ООП ради ООП - а именно пример с живого объекта, и использование всех функций (Интерфейсы, Действие, Метод, Переход, Свойства).
Где действительно видно разницу с ФБ.

Представьте СУ, с 8 (например) аналоговыми входами, у которой 2 типа аналоговых датчиков. Локальные и удаленные (например, modbus), При этом, эти 2 типа еще и подразделяются на, например, температурные и уровня.
И у вас по ТЗ есть необходимость, что бы к каждому из входов СУ можно было подключить любой из датчиков, любого типа, сконфигурировав их в интерфейсе пользователя.
Используя интерфейсы, вы создаете 8 входов СУ, используя наследование (раздельно для modbus и локальных) и интерфейсы - создаете датчики.
Профит, все красиво, читаемо, рабочее.

RomeoVar
26.06.2021, 08:48
Представьте СУ, с 8 (например) аналоговыми входами, у которой 2 типа аналоговых датчиков. Локальные и удаленные (например, modbus), При этом, эти 2 типа еще и подразделяются на, например, температурные и уровня.
И у вас по ТЗ есть необходимость, что бы к каждому из входов СУ можно было подключить любой из датчиков, любого типа, сконфигурировав их в интерфейсе пользователя.
Используя интерфейсы, вы создаете 8 входов СУ, используя наследование (раздельно для modbus и локальных) и интерфейсы - создаете датчики.
Профит, все красиво, читаемо, рабочее.

У ООП много плюсов. Но насчет все красиво, читаемо - это не главное. Инкапсуляция, полиморфизм - да. Защитить данные объекта от некорректной записи, быстро создать множество однотипных сущностей с различными реализациями интерфейсов - тысячу раз да. Но есть один нюанс в работе на ПЛК. Если Ваше приложение не требует быстрой реакции на воздействия - можно творить что угодно, вызывать функции в функции. А вот если время реакции важно сократить, нужно учитывать что есть временные затраты на загрузку/выгрузку в/из стека при вызовах функционального блока, функции, задачи. Я пока новичок в ПЛК. И мне интересно - кто-нибудь оценивал как влияют временные задержки при вызовах функций, блоков и т.д. например на джиттер?

RV9WFJ
26.06.2021, 10:18
Время реакции зависит от времени цикла и скорости связи с модулями ввода/вывода.

RomeoVar
26.06.2021, 19:22
Время реакции зависит от времени цикла и скорости связи с модулями ввода/вывода.

Время реакции - да. Я говорю о стеке. Как все происходит - По таймеру вызывается программа (предположим Main_Task). Допустим у нас есть функциональный блок, точка вызова блока выгружается в стек, на это время выполнение программы приостанавливается (пока данные не выгрузятся в стек) - это временная задержка, Далее в функциональном блоке есть другие функции, в них могут быть вложены другие функции каждый вызов - это остановка программы на время выгрузки в стек точки вызова. И потом все в обратном направлении - из стека т.е. задержки умножаем на 2. Таким образом если мне нужно вызывать какую-нибудь задачу с периодичностью 5 мс. А я, при использовании ООП на все вызовы функций буду тратить процессорное время на выгрузку/загрузку в/из стека - то тут нужно задуматься что лучше Красиво оформленный код с Объектами и кучей методов - но возросшим временем выполнения цикла или более простая реализация без лишних переходов и с минимальным количеством вызываемых функций. Вот о чем собственно я.
Но если приложение не критично к джиттеру и цикл вызова больше 50 мс. То тут конечно можно ООП полным ходом - Объекты, методы, функции и красота и ООП.

RV9WFJ
26.06.2021, 20:06
Ну раз мы про ООП говорим то это CS3. Хоть какой-то логический смысл делать цикл 5мс есть только на ПЛК2хх. Вы можете задачу где это критично вызывать отдельным Task -ом. На ПЛК2хх это уже имеет смысл. Но по практике сдельть реалтное время выполнения цикла даже 3мс это очень постараться надо. Думаю то о чем вы пишите не имеет значения, поскольку затраты сравнимы с погрешностью измерения.

RV9WFJ
26.06.2021, 20:07
А джиттер обычно графика портит. Хоть она и редко относительно вызывается, но "метко". Так что лучше графику оптимизируйте.

RomeoVar
27.06.2021, 21:12
Ну раз мы про ООП говорим то это CS3. Хоть какой-то логический смысл делать цикл 5мс есть только на ПЛК2хх. Вы можете задачу где это критично вызывать отдельным Task -ом. На ПЛК2хх это уже имеет смысл. Но по практике сдельть реалтное время выполнения цикла даже 3мс это очень постараться надо. Думаю то о чем вы пишите не имеет значения, поскольку затраты сравнимы с погрешностью измерения.
Насчет погрешности измерения - тут вопросов несколько. Программа в контроллере (на 210 в частности) запускается под Linux. Я, конечно, глубоко не копал но о Linux представление имею. Все Task-и запускаются скорее всего из crontab-a, а вот CS3 во что собирает? В исполняемый файл для системы (как обычное приложение)? Вероятно нет, думаю что под какой-нибудь интерпретатор (типа java) а тут уже разница в скорости исполнения существенная. Насчет графики тоже вопрос? В ОСи контроллера http сервер, обрабатывает запросы визуализации. И там-то логика точно работает через интерпретатор (php, java, или что нибудь свое, специфическое). Отсюда и такая "низкая" реакция.
Поэтому джиттер не только из-за графики. Если графика (VISU_TASK) имеет приоритет 31, а MAIN_TASK имеет приоритет 1, то планировщик при наступлении точки вызова MAIN_TASK остановит выполнение графики, выгрузит точку останова в стек, перейдет к выполнению высокоприоритетной задачи, а уже по ее окончанию загрузит из стека точку вызова VISU_TASK и продолжит ее выполнение. И тут тоже вопрос - сколько времени требуется на выполение MAIN_TASK и VISU_TASK? Если первая 1мс, а вторая - 25, при цикле первой 5 мс. То VISU_TASK будет 4 раза остановлена соответственно и джиттер будет ого-го какой. А т.к. я думаю что crontab (ну или что там) вряд-ли вызовет MAIN_TASK заранее, чтобы успеть выгрузить в стек точку низкоприоритетной задачи. Соостветственно по джиттеру можно судить сколько времени нужно на операцию выгрузки в стек.
Так что насчет джиттера не все так однозначно.
Вот скорость исполнения задач графики - это да, нельзя ее делать слишком длинной. Да и вообще, я понял одно - циклы в ПЛК это зло.
Вот, примерно то-же самое происходит и при вызовах ФБ И ФУНКЦИЙ. Так что здесь нужно балансировать между красотой и производительностью (ООП только по необходимости).

keysansa
06.07.2021, 11:52
Я все не читал, но за 2 последних страницы никто не вспомнил про цикл АЦП. Он может быть гораздо больше 5 мс.

RV9WFJ
06.07.2021, 14:53
А цикл АЦП то тут при чем???

RomeoVar
31.07.2021, 16:08
Чет заглохла тема с ООП