PDA

Просмотр полной версии : Первые шаги в ST. Прошу совета по архитектуре программы



sakanchik
10.03.2026, 18:26
Всем добрый день.

Я только начинаю изучать язык Structured Text и программирование ПЛК в целом. Работаю с Owen Logic и контроллером ПР200. До этого в основном использовал FBD, но сейчас пытаюсь освоить ST, потому что для более сложной логики он кажется удобнее.В качестве практики пытаюсь написать программу управления коптильной камерой. Логика примерно такая:есть рецепты,в каждом рецепте есть несколько шагов,каждый шаг соответствует определённому процессу (нагрев, розжиг, копчение, проветривание и т.д.),оператор может редактировать рецепт, выбирая процесс для каждого шага,затем программа выполняет шаги по порядку.

Пока я сделал простую архитектуру:
Рецепты хранятся в массиве RecipeSteps,каждый элемент массива хранит номер процесса,по номеру процесса через CASE включаются нужные выходы.
Это мой первый более-менее большой кусок ST, поэтому понимаю, что в нём могут быть ошибки и не самые правильные решения.
Буду очень благодарен, если подскажете.Правильно ли я вообще выбрал архитектуру,есть ли более правильный способ хранить рецепты,как лучше организовать редактирование шагов,нет ли типичных ошибок начинающих, которые я уже допустилю

Код прикладываю ниже:



FUNCTION_BLOCK koptilna

VAR_INPUT
ProgramRun : BOOL; //программа в работе
BtnStart : BOOL; //запуск программы
BtnStop : BOOL; //стоп рограммы
SelectRecipe : UDINT; // выбранный рецепт
EditStep : UDINT; // редактирование шага
SelectProcess : UDINT; // выбранный процесс
END_VAR

VAR_OUTPUT
HeatCamera : BOOL; //нагрев камеры
HeatGen : BOOL; //розжиг дымогенератора
Speed1 : BOOL; //двигатели скорость 1
Speed2 : BOOL; //двигатели скорость 2
Valve1 : BOOL; //выброс 1/3
Valve2 : BOOL; //выброс 2/3
Boost : BOOL; //наддув
Blowdown : BOOL; //поддув
Firefighting : BOOL; //пожаротушение
Stirrer : BOOL; //ворошитель
Sink : BOOL; //мойка
Hydration : BOOL; //увлажнение
CurenStep : UDINT; //позицая шага
END_VAR

VAR
RecipeSteps : ARRAY [0..200] OF UDINT;
RecipeIndex : UDINT; // индекс рецепта
EditIndex : UDINT; // индекс редактирования
SelectProcessOld : UDINT; //старое значение выбраного процесса
StepNomber : UDINT; //номер шага
END_VAR

//Значение по умолчанию для выходов
HeatCamera := FALSE;
HeatGen := FALSE;
Speed1 := FALSE;
Speed2 := FALSE;
Valve1 := FALSE;
Valve2 := FALSE;
Boost := FALSE;
Blowdown := FALSE;
Firefighting := FALSE;
Stirrer := FALSE;
Sink := FALSE;
Hydration := FALSE;

//Запуск программы
IF BtnStart THEN
ProgramRun := TRUE;
CurenStep := 1;
END_IF;

//Остановка программы
IF BtnStop THEN
ProgramRun := FALSE;
CurenStep := 1;
END_IF

//Автоматическое сохранние при редактировании рецепта
IF SelectProcess <> SelectProcessOld THEN
IF (SelectProcess >= 0) AND (SelectProcessOld <=10) THEN
IF (EditStep >= 0) AND (EditStep <=20) THEN
EditIndex := (SelectRecipe - 1) * 20 + EditStep;
RecipeSteps [EditIndex] := SelectProcess;
END_IF
END_IF
END_IF

StepNomber := RecipeSteps [RecipeIndex];

//Режимы работы
CASE StepNomber OF
0://Завершение процессов
HeatCamera := FALSE;
HeatGen := FALSE;
Speed1 := FALSE;
Speed2 := FALSE;
Valve1 := FALSE;
Valve2 := FALSE;
Boost := FALSE;
Blowdown := FALSE;
Firefighting := FALSE;
Stirrer := FALSE;
Sink := FALSE;
Hydration := FALSE;
1://Нагрев
HeatCamera := TRUE;
HeatGen := FALSE;
Speed1 := TRUE;
Speed2 := FALSE;
Valve1 := FALSE;
Valve2 := FALSE;
Boost := FALSE;
Blowdown := FALSE;
Firefighting := FALSE;
Stirrer := FALSE;
Sink := FALSE;
Hydration := FALSE;
2://Розжиг
HeatCamera := TRUE;
HeatGen := TRUE;
Speed1 := TRUE;
Speed2 := FALSE;
Valve1 := TRUE;
Valve2 := TRUE;
Boost := TRUE;
Blowdown := TRUE;
Firefighting := FALSE;
Stirrer := TRUE;
Sink := FALSE;
Hydration := TRUE;
3://Копчения
HeatCamera := TRUE;
HeatGen := TRUE;
Speed1 := FALSE;
Speed2 := TRUE;
Valve1 := FALSE;
Valve2 := TRUE;
Boost := TRUE;
Blowdown := TRUE;
Firefighting := FALSE;
Stirrer := TRUE;
Sink := FALSE;
Hydration := TRUE;
4://Проветривание
HeatCamera := FALSE;
HeatGen := FALSE;
Speed1 := FALSE;
Speed2 := TRUE;
Valve1 := TRUE;
Valve2 := TRUE;
Boost := FALSE;
Blowdown := FALSE;
Firefighting := FALSE;
Stirrer := FALSE;
Sink := FALSE;
Hydration := FALSE;
5://Мойка
HeatCamera := TRUE;
HeatGen := FALSE;
Speed1 := FALSE;
Speed2 := FALSE;
Valve1 := FALSE;
Valve2 := FALSE;
Boost := FALSE;
Blowdown := FALSE;
Firefighting := FALSE;
Stirrer := FALSE;
Sink := TRUE;
Hydration := FALSE;
6://Сушка
HeatCamera := TRUE;
HeatGen := FALSE;
Speed1 := FALSE;
Speed2 := TRUE;
Valve1 := FALSE;
Valve2 := FALSE;
Boost := FALSE;
Blowdown := FALSE;
Firefighting := FALSE;
Stirrer := FALSE;
Sink := FALSE;
Hydration := FALSE;
END_CASE;

END_FUNCTION_BLOCK


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

kondor3000
10.03.2026, 18:38
Всё тоже самое делается циклами FOR или WHILE (использовать аккуратно) в 4-5 строк кода,
пример использования тут
Работа с циклами___ 4 насоса_Нараб_Ротация по времени_4 Уровня_10________ https://owen.ru/forum/showthread.php?t=38920&page=2#13
Простой шаговый автомат, новый TON с паузой_____ https://owen.ru/forum/showthread.php?t=38239&page=43#424
Прогр. управление 6 нагрузками 6 реж. 12 шагов _ФБ_фунции_____https://owen.ru/forum/showthread.php?t=38239&page=11#105

1exan
10.03.2026, 18:55
C рецептами в OL будет некоторое неудобство, т.к:
1. Энергонезависимых переменных нельзя сделать внутри блоков на ST
2. Массивов в качестве входных/выходных переменных пока не завезли

По поводу организации CASE предлагалось нечто подобное здесь (https://owen.ru/forum/showthread.php?t=38959&p=428971&viewfull=1#post428971)

FPavel
10.03.2026, 19:49
Если рецепты хранить в панели оператора, тогда можно загружать их в ПР/ПЛК.
Единственно, у ПР200 регистров для обмена по Modbus (сетевых Slave переменных) - кот наплакал, хотя, при некотором старании, на один рецепт может хватить.

Dimensy
11.03.2026, 05:52
Помимо того, что показанный код не работает
1. как уже говорили, в ST нет энергонезависимых переменных, поэтому использовать его для хранения рецептов не имеет смысла
2. 200 энергонезависимых переменных сожрут почти всю память
3. хранить 10 рецептов по 20 двадцать шагов в одномерном массиве, так себе идея
4. помимо шагов не вижу в рецепте длительности нахождения на шаге
5. процесс удобнее представить в виде битовой маски
6. как то странно пожаротушение хранить в рецепте

EFrol
11.03.2026, 08:02
Боюсь, что как раз для логики этого приложения лучше всего подойдёт именно FBD:
1. Необходимость редактировать рецепты (энергонезависимые переменные) через экран ПР200.
2. Алгоритм не настолько сложен, чтобы его реализовывать в ST.
3. Еще не известны условия перехода на следующий шаг (время, достижение уставки и т.д.)

sakanchik
11.03.2026, 09:00
Здравствуйте, про отсутствие энергонезависимых переменных уже принял во внимание. То что код не рабочий понимаю. Длительность нахождения в шаге пока не делал, ну скорее всего и не буду. Лучше напишу всю логику работы установки на FBD. Про битовую маску вас услышал, буду изучать. А про пожаротушение, да, глупость с моей стороны. В любом случае спасибо за советы.

FPavel
11.03.2026, 19:32
Возможно, будет интересно ознакомиться с короткой статьёй
Автоматное программирование в среде Owen Logic
https://owen.ru/forum/showthread.php?t=33715&page=2&p=340279&viewfull=1#post340279

Она как раз о конечных автоматах в эпоху отсутствия в Owen Logic языка ST.