Вход

Просмотр полной версии : Управление лампой "Авария" сериями импульсов в зависимости от статусов



Smitka
24.11.2025, 00:06
Доброго дня! Не могу сообразить, как реализовать управление лампой "Авария" импульсами в зависимости от статусов на ПР200. В зависимости от того какая аварийная ситуация, индикатор выдает серию миганий. Мигание индикатора при определении аварии происходит интервально, при этом количество включенных состояний индикатора в серии импульсов показывает номер определенной аварии. Серия из одного импульса, далее пауза 1,5-2 секунды и переход к индикации следующей аварии своим количеством импульсов (два, три и т.д.). Индикация аварий осуществляется циклично.

Сергей0308
24.11.2025, 00:54
Доброго дня! Не могу сообразить, как реализовать управление лампой "Авария" импульсами в зависимости от статусов на ПР200. В зависимости от того какая аварийная ситуация, индикатор выдает серию миганий. Мигание индикатора при определении аварии происходит интервально, при этом количество включенных состояний индикатора в серии импульсов показывает номер определенной аварии. Серия из одного импульса, далее пауза 1,5-2 секунды и переход к индикации следующей аварии своим количеством импульсов (два, три и т.д.). Индикация аварий осуществляется циклично.

С Вами всё понятно, в смысле, "ни украсть, ни покараулить"!

https://owen.ru/forum/showthread.php?t=39169&p=429835&viewfull=1#post429835

86867

И, если выводить номер(код) аварии на экран ПР200, ИПП120 или СМИ2-М, то будет намного удобней, мне так кажется:

https://owen.ru/forum/showthread.php?t=31076&p=374952&viewfull=1#post374952

86868


Ваш вариант, самый неудачный из всех возможных, проще выражаясь, худший из худших, мне так кажется!
В таких случаях обычно говорят, что более неудачный вариант трудно придумать!

Вот здесь тоже нечто подобное делали, но более ясное и понятное:
https://owen.ru/forum/showthread.php?t=26216&p=412743&viewfull=1#post412743

86869

Smitka
24.11.2025, 07:20
Благодарю Вас! Просто количеством ламп ограничен, а в логике пока только осваиваюсь

Smitka
24.11.2025, 07:31
Ваш вариант, самый неудачный из всех возможных, проще выражаясь, худший из худших, мне так кажется!
В таких случаях обычно говорят, что более неудачный вариант трудно придумать!




Вот этот идеально подходит, спасибо еще раз! На самом деле на форуме порой трудно что-то найти из-за того, что поисковый вопрос не всегда содержит "правильные" для поиска слова ))) прошу прощения за тафтологию

Smitka
24.11.2025, 09:40
Добавил вход Enable и оставил только контроль 6 входов

Васильев
25.11.2025, 09:10
я вот так делаю

Васильев
25.11.2025, 09:10
function_block fb_led_1_1 //имя функционального блока.

var_input //объявление входных переменных

init:bool;
init_flash:bool;


in0:bool;
flash0:bool;

in1 : bool; //входная переменная с типом данных bool
flash1:bool;

in2 : bool; //входная переменная с типом данных bool
flash2:bool;

in3 : bool; //входная переменная для значения счетчика после сброса
flash3:bool;

in4:bool;
flash4:bool;

flash_def:bool;

end_var

var_output //объявление выходных переменных
out : bool; //выходная переменная с типом данных udint
end_var

var //объявление локальных переменных

end_var

if init=true then
out:=init_flash;
else

if in0 = true then
out:=flash0;
else

if in1=true then
out := flash1;
else
if in2=true then
out := flash2;
else
if in3=true then
out:=flash3;
else
if in4=true then
out:=flash4;
else
out:=flash_def;
end_if

end_if

end_if
end_if
end_if

end_if

end_function_block

capzap
25.11.2025, 10:17
я вот так делаю
смотрите как ИИ понял задачу у ТС


Анализ требований
1) Источник данных: ПР200 — промышленный контроллер или устройство, выдающее статусы (например, по Modbus, дискретным входам или другим интерфейсам).
2) Тип индикации: мигающая лампа (лампа «Авария»).
3) Логика мигания:
Каждая аварийная ситуация кодируется количеством вспышек (импульсов).
Авария №1 → 1 вспышка
Авария №2 → 2 вспышки
и т.д.
После каждой серии импульсов — пауза 1.5–2 сек.
Индикация циклическая: после последней аварии — возврат к первой.

4) Одновременные аварии: если активно несколько аварий, каждая из них должна быть отображена поочерёдно своей серией миганий.
5) Мигание:
Длительность включения/выключения в рамках серии — стандартная, например: вкл — 0.5 с, выкл — 0.5 с (уточняется по ТЗ).
Пауза между сериями — 1.5–2 с.


Дополнительные рекомендации

Приоритетность: если важно, чтобы критические аварии отображались чаще — можно реализовать приоритетную очередь.
Сброс: при исчезновении аварии — обновлять список и перезапускать индикацию.
Диагностика: при отсутствии связи с ПР200 — можно мигать, например, 10 раз (как ошибка связи).
Тестирование: реализовать кнопку «тест индикации» для проверки работоспособности лампы.


Ваш код так может?

Валенок
25.11.2025, 11:49
...
end_if

end_if

end_if
end_if
end_if

end_if

Извините, но это простейший признак как делать не надо. Это помимо "in0 = true"

elsif есть для однорангового приоритетного сравнения

Валенок
25.11.2025, 14:04
смотрите как ИИ понял задачу у ТС

Авария №1 → 1 вспышка
Авария №2 → 2 вспышки
и т.д.....
...
Авария №71 → 71 вспышка
норм, чо

Васильев
25.11.2025, 14:09
Извините, но это простейший признак как делать не надо. Это помимо "in0 = true"

elsif есть для однорангового приоритетного сравнения

Да, я понимаю. Но мне так больше нравится. Более наглядно, по крайней мере для меня.

Васильев
25.11.2025, 14:10
смотрите как ИИ понял задачу у ТС

Ваш код так может?
Этого не нужно! Достаточно отобразить самую критическую.

capzap
25.11.2025, 14:20
Этого не нужно! Достаточно отобразить самую критическую.

Кому не нужно, Вам вполне возможно. А ТС-у Вы чем поможете своим кодом, там ни чего нет что ему требуется, даже генераторы импульсов с разной частотой ему не подходят

kondor3000
25.11.2025, 14:29
Этого не нужно! Достаточно отобразить самую критическую.

Как наставишь много IF-ов,
зародится интеллект,
сам решит, где что поставить,*
что сготовить на обед.

*Решит какая задача более критическая.

capzap
25.11.2025, 14:45
Да, я понимаю. Но мне так больше нравится. Более наглядно, по крайней мере для меня.

ответ ИИ на такое заявление:
Проблемы с точки зрения промышленного программирования:
1. Избыточная вложенность и дублирование структуры
Код использует каскад вложенных IF ... THEN ... ELSE, что:
Трудно читать и поддерживать, особенно при расширении (что будет при in5, in6?).
Сильно нарушает принцип DRY.
Увеличивает когнитивную нагрузку — читателю приходится отслеживать глубину вложенности.
Уязвим к ошибкам при изменении (например, пропущенный END_IF).
⚠️ Замечание: в ST разрешено использовать ELSIF, что существенно улучшает читаемость.

2. Сравнение с = true избыточно
В языках, где переменные имеют тип BOOL, выражение if in0 = true then можно и нужно писать как:

IF in0 THEN

Это стандартная практика и уменьшает шум.
3. Отсутствие масштабируемости
Если завтра понадобится 10 входов — код придётся копипастить. В промышленной среде часто применяют:
массивы (ARRAY[0..4] OF BOOL),
циклы (FOR ... DO),
или даже пользовательские функции/функциональные блоки с параметризацией.
4. «Наглядно для меня» ≠ «наглядно для команды»
В промышленной автоматизации:
Код может поддерживать другой инженер через 2 года.
Часто аудит проводится сторонними специалистами.
Требуется соответствие стандартам (IEC 61131-3) и внутренним гайдлайнам по читаемости.
Настоящая «наглядность» — это когда логика видна с первого взгляда, без отслеживания вложенности на 6 уровней.


Ответ на утверждение автора: «Мне так больше нравится. Более наглядно, по крайней мере для меня»
Это вполне естественное чувство, особенно если человек привык к императивному стилю с вложенными условиями (как в школьных примерах или в C-подобных языках). Однако в промышленном программировании:
"Наглядность" — это не то, что понятно автору сегодня, а то, что будет однозначно понятно любому члену команды завтра.
Ваш вариант — субъективно удобен, но объективно уступает по читаемости, поддерживаемости и соответствию стандартам. Использование ELSIF здесь не просто «стилистика» — это стандартный и рекомендуемый способ реализации приоритетного выбора в ST.

Васильев
26.11.2025, 02:45
Автору ТС не интерес наш диалог..

Валенок
26.11.2025, 02:52
Немного очеловечу ИИ, а то как-то мутно про ELSIF

... в ST разрешено использовать ELSIF, что существенно улучшает читаемость...
Использование ELSIF здесь не просто «стилистика» — это стандартный и рекомендуемый способ реализации приоритетного выбора в ST...

Вот исходное (только убрал =TRUE)

if init then
out := init_flash;
else
if in0 then
out := flash0;
else
if in1 then
out := flash1;
else
if in2 then
out := flash2;
else
if in3 then
out := flash3;
else
if in4 then
out := flash4;
else
out := flash_def;
end_if
end_if
end_if
end_if
end_if
end_if
(пока слезал со стремянки - упал и расплескал пиво)

поскольку это приоритетный выбор - запишу в один уровень

if init then out := init_flash;
else if in0 then out := flash0;
else if in1 then out := flash1;
else if in2 then out := flash2;
else if in3 then out := flash3;
else if in4 then out := flash4;
else out := flash_def;
end_if
end_if
end_if
end_if
end_if
end_if

тоже в делфях

if init then out := init_flash
else if in0 then out := flash0
else if in1 then out := flash1
else if in2 then out := flash2
else if in3 then out := flash3
else if in4 then out := flash4
else then out := flash_def;

и сях

if (init) out = init_flash;
else if (in0) out = flash0;
else if (in1) out = flash1;
else if (in2) out = flash2;
else if (in3) out = flash3;
else if (in4) out = flash4;
else out = flash_def;


Но мне так больше нравится. Более наглядно, по крайней мере для меня.
Нравится - все что угодно. Но про наглядность логики (любой вариант) - не говорите.
Даже упомянутая выше ИИем масштабируемость (пока для си/делфи) для еще десятка входов (без мутилова массива/цикла, туда еще пихать надо) - копи-пасте 10 строк и заполнить с любого будуна сверху вниз 2 столбца цифр.

Но что-то как-то в ST неочень из-за хвоста из END_IF'ов?
Вот для этого в ST и сделан кусочек синтаксического сахара "ELSIF" чтоб превратить убогую белку в изящного хомяка

if init then out := init_flash;
elsif in0 then out := flash0;
elsif in1 then out := flash1;
elsif in2 then out := flash2;
elsif in3 then out := flash3;
elsif in4 then out := flash4;
else out := flash_def;
end_if
А сям/делфям такое и не надо

melky
26.11.2025, 06:50
А если смотреть по другому, то нужен массив, сортировка и циклическое мигание с паузой между и так по кругу.

Elsif же прерывает дальнейшую проверку, не? В любых языках.

МихаилГл
26.11.2025, 07:07
if init then
out := init_flash;
else
if in0 then
out := flash0;
else
if in1 then
out := flash1;
else
if in2 then
out := flash2;
else
if in3 then
out := flash3;
else
if in4 then
out := flash4;
else
out := flash_def;
end_if
end_if
end_if
end_if
end_if
end_if

А не лучше в одну строчку:



out := (init_flash and init) or (flash0 and in0) or (flash1 and in1) or (flash2 and in2) or (flash3 and in3) or (flash4 and in4) or (flash5 and in5) or (flash6 and in6) or (flash7 and in7) or ...;


Но это только если в коде init, in0, in1 и т.д. - только одно из условий true, остальные должны быть false...

МихаилГл
26.11.2025, 08:08
Решение по ТЗ ТС, но для кодесиса 3.5:

Таск 100 мс:


PROGRAM POU_SIG
VAR
HLA_TYP: INT;
HLA_COD: ARRAY [1..4, 1..4] OF INT:= [1, 2, 2, 15, 2, 2, 2, 15, 3, 2, 2, 15, 4, 2, 2, 15];
HLA_IMP: ARRAY [1..4] OF INT:= [0, 0, 0, 0];
HLA_CUR: ARRAY [1..4] OF INT:= [1, 1, 1, 1];
OutX: ARRAY [1..4] OF BOOL:= [1, 1, 1, 1];
OutY: ARRAY [1..4] OF BOOL;
END_VAR
//Программа управления индикаторами с кодировкой
FOR HLA_TYP:= 1 TO 4 DO
HLA_IMP[HLA_TYP]:= HLA_IMP[HLA_TYP] + 1;
IF HLA_IMP[HLA_TYP]= (HLA_CUR[HLA_TYP] * HLA_COD[HLA_TYP, 2] + (HLA_CUR[HLA_TYP] - 1) * HLA_COD[HLA_TYP, 3]) THEN OutX[HLA_TYP]:= FALSE; HLA_CUR[HLA_TYP]:= HLA_CUR[HLA_TYP] + 1; END_IF
IF HLA_IMP[HLA_TYP]= (HLA_CUR[HLA_TYP] * HLA_COD[HLA_TYP, 2] + (HLA_CUR[HLA_TYP] - 1) * HLA_COD[HLA_TYP, 3] - HLA_COD[HLA_TYP, 2]) THEN OutX[HLA_TYP]:= TRUE; END_IF
IF HLA_CUR[HLA_TYP]> HLA_COD[HLA_TYP, 1] THEN HLA_CUR[HLA_TYP]:= 1; END_IF
IF HLA_IMP[HLA_TYP]= (HLA_COD[HLA_TYP, 1] * HLA_COD[HLA_TYP, 2] + (HLA_COD[HLA_TYP, 1] - 1) * HLA_COD[HLA_TYP, 3] + HLA_COD[HLA_TYP, 4]) THEN HLA_IMP[HLA_TYP]:= 0; OutX[HLA_TYP]:= TRUE; END_IF
OutY[HLA_TYP]:= NOT OutX[HLA_TYP];
END_FOR
//Программа управления индикаторами с кодировкой

Первые ARRAY [1..4 всех массивов - это количество кодировок
HLA_COD (ARRAY [1..4, 1..4] OF INT:= [1, 2, 2, 15, 2, 2, 2, 15, 3, 2, 2, 15, 4, 2, 2, 15]) формируемый код

Таск основной программы:



PROGRAM PLC_PRG
VAR
in: ARRAY [0..100] OF BOOL; //наличие аварии для данного кода
blablabla: ton; //таймер на каждую активную аварию
dadada: INT:= 1; //номер шага
nextOK: BOOL;
outizm: BOOL;
i: INT; //Для оптимизации последней строчки
END_VAR
blablabla(in:= NOT blablabla.q, pt:= T#5S); //каждые 5 секунд новую аварию показывать
IF dadada> 100 THEN
dadada:= 1;
END_IF
IF NOT in[dadada] AND NOT nextOK THEN
dadada:= dadada + 1;
IF in[dadada] THEN
nextOK:= TRUE;
END_IF
END_IF

IF blablabla.q THEN
nextOK:= FALSE;
dadada:= dadada + 1;
END_IF

outizm:= (POU_SIG.OutX[1] AND (dadada= 1)) OR (POU_SIG.OutX[2] AND (dadada= 2)) OR (POU_SIG.OutX[3] AND (dadada= 3)) OR (POU_SIG.OutX[4] AND (dadada= 4));


Либо оптимизация для последней строчки:

outizm:= FALSE;
FOR i:= 1 TO 4 DO
outizm:= outizm OR (POU_SIG.OutX[i] AND (dadada= i));
END_FOR

МихаилГл
26.11.2025, 10:10
Доброго дня! Не могу сообразить, как реализовать управление лампой "Авария" импульсами в зависимости от статусов на ПР200. В зависимости от того какая аварийная ситуация, индикатор выдает серию миганий. Мигание индикатора при определении аварии происходит интервально, при этом количество включенных состояний индикатора в серии импульсов показывает номер определенной аварии. Серия из одного импульса, далее пауза 1,5-2 секунды и переход к индикации следующей аварии своим количеством импульсов (два, три и т.д.). Индикация аварий осуществляется циклично.

Добавляю более читаемый код (разработан в codesys 3.5), который, думаю, можно переделать и для ПР. Остается только коды аварий самому разработать...



PROGRAM PLC_PRG
VAR
ALARMok: ARRAY [1..VOLUMEofALARM] OF BOOL; //наличие аварии для данного кода
TIMEforEACHcode: ton; //таймер на каждую активную аварию
NEXTalarm: INT:= 1; //номер шага для поиска следующей активной аварии
NEXTok: BOOL; //следующая активная авария найдена
OUTforLAMP: BOOL; //выход на сигнализацию
i: INT; //для цикла
END_VAR
VAR CONSTANT
VOLUMEofALARM: INT:= 4; //количество аварий
END_VAR
-----------------------------------------------------------
//каждые 10 секунд показывать новую аварию
TIMEforEACHcode(in:= NOT TIMEforEACHcode.q, pt:= T#10S);
//ограничение по максимуму аварий
IF NEXTalarm> VOLUMEofALARM THEN
NEXTalarm:= 1;
END_IF
//поиск следующей активной аварии
IF NOT ALARMok[NEXTalarm] AND NOT NEXTok THEN
NEXTalarm:= NEXTalarm + 1;
IF ALARMok[NEXTalarm] THEN
NEXTok:= TRUE;
END_IF
END_IF
//старт поиска следующей активной аварии
IF TIMEforEACHcode.q THEN
NEXTok:= FALSE;
NEXTalarm:= NEXTalarm + 1;
END_IF

//выход на сигнализацию
//так:
//OUTforLAMP:= (POU_SIG.OutX[1] AND (NEXTalarm= 1)) OR (POU_SIG.OutX[2] AND (NEXTalarm= 2)) OR (POU_SIG.OutX[3] AND (NEXTalarm= 3)) OR (POU_SIG.OutX[4] AND (NEXTalarm= 4));
//или так:
OUTforLAMP:= FALSE;
FOR i:= 1 TO VOLUMEofALARM DO
OUTforLAMP:= OUTforLAMP OR (POU_SIG.OutX[i] AND (NEXTalarm= i));
END_FOR


Эх, без аварий - косячит... Надо разбираться. Но все равно, мне на ST больше код удобен, чем на FBD

Dimensy
26.11.2025, 15:06
Ну, у меня как-то так получилось. На нулевой бит повесил постоянное свечение



function_block AlarmFlash

var_input
bmAlarm : udint; //битовая маска аварий
end_var

var_output
Q : bool;
end_var

var
bitAl :bool;
numAl : udint := 1;
blink : SYS.BLINK;
ctu : SYS.CTU;
pause : SYS.TON;
fl : SYS.FTRIG;
end_var

bitAl := shr(bmAlarm, numAl) mod 2 > 0;

if bmAlarm > 1 then
if not bitAl or pause.Q then
ctu(R := true);
numAl := numAl + 1;
if numAl > 7 then numAl := 1; end_if
end_if

blink(I := bitAl and not ctu.Q, Th := t#500ms, Tl := t#500ms);
fl(I := blink.Q);
ctu(C := fl.Q, N := numAl, R := false);
pause(I := ctu.Q, T := t#2s);
else
numAl := 1;
ctu(R := true);
end_if

Q := blink.Q or bmAlarm.0;

end_function_block

Валенок
26.11.2025, 17:42
to Dimensy
массив не нужен. i-тый бит маски в этом лайт-ST
shr(маска,i) mod 2 > 0;

Dimensy
27.11.2025, 10:20
В продолжении мармезонского балета, еще один макрос на 32 аварии. Аварии разбиты на 4 группы по 8 бит



function_block AlarmFlash_2

var_input
bmAlarm : udint; //битовая маска аварий
end_var

var_output
Q : bool;
Code : udint;
end_var

var
bitAlarm : bool;
numGr : udint;
numAl : udint := 0;
codeAl : SYS.BLINK;
cGroup, cAlarm : SYS.CTU;
pGroup : SYS.TON;
pAlarm : SYS.TP;
faseGr, faseAl : bool;
flashAl :udint;
next : SYS.RTRIG;
res : bool;
end_var

codeAl(Th := t#300ms, Tl := t#300ms); //период моргания
pGroup(T := t#500ms); //пауза между группа - код
pAlarm(T := t#2700ms); // пауза между авариями

//Поиск аварий
bitAlarm := shr(bmAlarm, numAl) mod 2 > 0;
if not bitAlarm or next.Q then
res := true;
numAl := numAl + 1;
if numAl > 31 then
numAl := 0;
end_if
else
res := false;
end_if

if not pAlarm.Q then
flashAl := 1 + (numAl mod 8);
if numAl > 23 then numGr := 5;
elsif numAl > 15 then numGr := 4;
elsif numAl > 7 then numGr := 3;
else numGr := 2;
end_if;
end_if

cGroup(C := not Q and faseGr, N := numGr, R := res);
cAlarm(C := not Q and faseAl, N := flashAl, R := res);
next(I := cAlarm.Q);
pGroup(I := cGroup.Q);
pAlarm(I := cAlarm.Q);

faseGr := bitAlarm and not (cGroup.Q or pAlarm.Q);
faseAl := pGroup.Q and not cAlarm.Q;

codeAl(I := (faseGr or faseAl), Q => Q);

if faseGr or faseAl then
Code := numAl + 1;
elsif bmAlarm = 0 then
Code := 0;
end_if

end_function_block