Поправил скрипт, поправил неточности в библиотеке

This commit is contained in:
2026-07-31 11:20:45 +03:00
parent d3da512178
commit 3d52fdec57
44 changed files with 1956 additions and 51 deletions
+2 -1
View File
@@ -6,4 +6,5 @@
# directories
# build results
*.opt
*.opt
*.~u
Binary file not shown.
Binary file not shown.
+25
View File
@@ -0,0 +1,25 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Structs
Name : AnalogValueState
Type : DUT
Exported : 2026-07-31 11:16:16
===============================================================================
*)
{attribute 'qualified_only'}
{attribute 'strict'}
TYPE AnalogValueState:
(
undefined := INT#0, (* 0 - не определено *)
fail_hi := INT#5, (* 5 - аварийный по верхнему уровню *)
warn_hi := INT#4, (* 4 - предупредительный по верхнему уровню, *)
normal := INT#1, (* 1 - норма *)
warn_low := INT#2, (* 2 - предупредительный по нижнему уровню *)
fail_low := INT#3, (* 3 - аварийный по нижнему уровню *)
no_signal := - INT#1 (* -1 - нет связи с датчиком *)
) := undefined;
END_TYPE
+22
View File
@@ -0,0 +1,22 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Fastwel
Name : AIM721_AI
Type : FUNCTION
Exported : 2026-07-31 11:16:16
===============================================================================
*)
FUNCTION AIM721_AI : REAL
(*
Перевод значений входов модуля AIM721 из единиц АЦП в 0..20 мА
*)
VAR_INPUT
ADC: DINT;
END_VAR
(*----- IMPLEMENTATION -----*)
AIM721_AI := DINT_TO_REAL(ADC)*REAL#2.40802793E-006;
END_FUNCTION
+22
View File
@@ -0,0 +1,22 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Fastwel
Name : AIM722_AI
Type : FUNCTION
Exported : 2026-07-31 11:16:16
===============================================================================
*)
FUNCTION AIM722_AI : REAL
(*
Перевод значений входов модуля AIM722 из единиц АЦП в 0..20 мА
*)
VAR_INPUT
ADC: DINT;
END_VAR
(*----- IMPLEMENTATION -----*)
AIM722_AI := DINT_TO_REAL(ADC)*REAL#2.384185791015625E-006;
END_FUNCTION
+22
View File
@@ -0,0 +1,22 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Fastwel
Name : AIM723_AI
Type : FUNCTION
Exported : 2026-07-31 11:16:16
===============================================================================
*)
FUNCTION AIM723_AI : REAL
(*
Перевод значений входов модуля AIM723 из единиц АЦП в 4..20 мА
*)
VAR_INPUT
ADC: DINT;
END_VAR
(*----- IMPLEMENTATION -----*)
AIM723_AI := REAL#4.0 + DINT_TO_REAL(ADC)*REAL#1.93119072E-006;
END_FUNCTION
+92
View File
@@ -0,0 +1,92 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Fastwel
Name : AIM725_Status
Type : FUNCTION_BLOCK
Exported : 2026-07-31 11:16:16
===============================================================================
*)
FUNCTION_BLOCK AIM725_Status
(*
Модуль формирования статуса
Гистерезис по отключению и задержка по включению
-1 - нет связи с датчиком
0 - не определено
1 - норма
2 - предупредительный по нижнему уровню
3 - аварийный по нижнему уровню
4 - предупредительный по верхнему уровню,
5 - аварийный по верхнему уровню
*)
VAR_INPUT
Input: REAL;
fail_hi: REAL:=3.402823466E+38;
delta_fail_hi: REAL := 0;
time_fail_hi: TIME :=t#0s;
warn_hi: REAL:=3.402823466E+38;
delta_warn_hi: REAL := 0;
time_warn_hi: TIME :=t#0s;
warn_low: REAL:=-3.402823466E+38;
delta_warn_low: REAL := 0;
time_warn_low: TIME :=t#0s;
fail_low: REAL:=-3.402823466E+38;
delta_fail_low: REAL := 0;
time_fail_low: TIME :=t#0s;
Modul_status: BYTE;
Chanel_num: BYTE;
END_VAR
VAR_OUTPUT
Status: INT;
T_mod: INT;
END_VAR
VAR
(* !!! ВНУТРЕННИЕ НЕВЫХОДНЫЕ ПЕРЕМЕННЫЕ!!! *)
TON_fail_hi: TON;
TON_warn_hi: TON;
TON_warn_low: TON;
TON_fail_low: TON;
(* !!! ВНУТРЕННИЕ ВЫХОДНЫЕ ПЕРЕМЕННЫЕ!!! *)
fail_hi_st: BOOL;
warn_hi_st: BOOL;
normal_st: BOOL;
warn_low_st: BOOL;
fail_low_st: BOOL;
no_signal_st: BOOL;
END_VAR
(*----- IMPLEMENTATION -----*)
no_signal_st := (Input <= - REAL#200 OR ((Modul_status.0 OR Modul_status.4 OR Modul_status.5) AND Chanel_num = 1) OR ((Modul_status.1 OR Modul_status.6 OR Modul_status.7) AND Chanel_num = 2) OR Modul_status.2 OR Modul_status.3);
IF no_signal_st THEN T_mod := - INT#500; ELSE T_mod := REAL_TO_INT(Input); END_IF;
TON_fail_low(IN := ((Input <= fail_low) AND NOT(no_signal_st)), PT := time_fail_low);
IF TON_fail_low.Q THEN fail_low_st := TRUE; ELSIF (Input > fail_low + delta_fail_low) THEN fail_low_st := FALSE; END_IF;
TON_warn_low(IN := ((Input <= warn_low) AND NOT(no_signal_st) AND NOT(TON_fail_low.Q)), PT := time_warn_low);
IF TON_warn_low.Q THEN warn_low_st := TRUE; ELSIF (Input > warn_low + delta_warn_low) THEN warn_low_st := FALSE; END_IF;
TON_fail_hi(IN := ((Input >= fail_hi) AND NOT(no_signal_st)), PT := time_fail_hi);
IF TON_fail_hi.Q THEN fail_hi_st := TRUE; ELSIF (Input < fail_hi - delta_fail_hi) THEN fail_hi_st := FALSE; END_IF;
TON_warn_hi(IN := ((Input >= warn_hi) AND NOT(no_signal_st) AND NOT(TON_fail_hi.Q)), PT := time_warn_hi);
IF TON_warn_hi.Q THEN warn_hi_st := TRUE; ELSIF (Input < warn_hi - delta_warn_hi) THEN warn_hi_st := FALSE; END_IF;
normal_st := (NOT(no_signal_st) AND NOT(fail_low_st) AND NOT(warn_low_st) AND NOT(warn_hi_st) AND NOT(fail_hi_st));
IF no_signal_st THEN
Status := - INT#1;
ELSIF fail_hi_st THEN
Status := INT#5;
ELSIF fail_low_st THEN
Status := INT#3;
ELSIF warn_hi_st THEN
Status := INT#4;
ELSIF warn_low_st THEN
Status := INT#2;
ELSIF normal_st THEN
Status := INT#1;
ELSE
Status := INT#0;
END_IF;
END_FUNCTION_BLOCK
+21
View File
@@ -0,0 +1,21 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Fastwel
Name : AIM726_AI
Type : FUNCTION
Exported : 2026-07-31 11:16:16
===============================================================================
*)
FUNCTION AIM726_AI : REAL
(*
Перевод значений входов модуля AIM726 из единиц АЦП в 0..40 В
*)
VAR_INPUT
ADC : DWORD; (*Единицы АЦП*)
END_VAR
(*----- IMPLEMENTATION -----*)
AIM726_AI := DWORD_TO_REAL(ADC) * REAL#4.768372E-006;
END_FUNCTION
+22
View File
@@ -0,0 +1,22 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Fastwel
Name : AIM727_AI
Type : FUNCTION
Exported : 2026-07-31 11:16:16
===============================================================================
*)
FUNCTION AIM727_AI : REAL
(*
Перевод значений входов модуля AIM727 из единиц АЦП в 0..40 V
*)
VAR_INPUT
ADC: DINT; (*Единицы АЦП*)
END_VAR
(*----- IMPLEMENTATION -----*)
AIM727_AI := DINT_TO_REAL(ADC)*REAL#4.768372E-006;
END_FUNCTION
+31
View File
@@ -0,0 +1,31 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Fastwel
Name : AIM730_AO
Type : FUNCTION
Exported : 2026-07-31 11:16:16
===============================================================================
*)
FUNCTION AIM730_AO : WORD
(*
Перевод значений 0..20 мА или 4..20 мА в единицы АЦП выходов модуля AIM730
Диапазон:
RANGE = 0 - 0..20 мА
RANGE = 1 - 4..20 мА
*)
VAR_INPUT
DAC : REAL; (*Миллиамперы*)
RANGE : BYTE; (*Диапазон: 0..20 мА - 0; 4..20 мА - 1 *)
END_VAR
(*----- IMPLEMENTATION -----*)
IF (RANGE = 1) AND (DAC < 4) THEN AIM730_AO := WORD#4; RETURN;END_IF;
CASE RANGE OF
0 : AIM730_AO := REAL_TO_WORD(DAC/REAL#0.00030518);
1 : AIM730_AO := REAL_TO_WORD((DAC - REAL#4)/REAL#0.00024414435);
ELSE
AIM730_AO := WORD#0;
END_CASE;
END_FUNCTION
+30
View File
@@ -0,0 +1,30 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Fastwel
Name : AIM731_AO
Type : FUNCTION
Exported : 2026-07-31 11:16:16
===============================================================================
*)
FUNCTION AIM731_AO : WORD
(*
Перевод значений -10..+10 В или 0..+10 В в единицы АЦП выходов модуля AIM731
Диапазон:
RANGE = 0 - -10..+10 В
RANGE = 1 - 0..+10 В
*)
VAR_INPUT
DAC : REAL; (*Вольты*)
RANGE : BYTE; (*Диапазон -10..+10 В - 0; 0..+10 В - 1 *)
END_VAR
(*----- IMPLEMENTATION -----*)
CASE RANGE OF
0 : AIM731_AO := REAL_TO_WORD((DAC + REAL#10.0)/REAL#0.00030518);
1 : AIM731_AO := REAL_TO_WORD(DAC/REAL#0.00015259);
ELSE
AIM731_AO := 0;
END_CASE;
END_FUNCTION
+33
View File
@@ -0,0 +1,33 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Fastwel
Name : AIM791_AI
Type : FUNCTION
Exported : 2026-07-31 11:16:16
===============================================================================
*)
FUNCTION AIM791_AI : REAL
(*
Перевод значений входов модуля AIM791 из единиц АЦП в 0..5 мА / 0..20 мА / 4..20 мА
Диапазон:
RANGE = 0 - 0..5 мА
RANGE = 1 - 0..20 мА
RANGE = 2 - 4..20 мА
*)
VAR_INPUT
ADC: WORD;
RANGE: BYTE := 0;
END_VAR
(*----- IMPLEMENTATION -----*)
CASE RANGE OF
0 : AIM791_AI := WORD_TO_REAL(ADC) * REAL#7.820248722E-005;
1 : AIM791_AI := WORD_TO_REAL(ADC) * REAL#3.128099488E-004;
2 : AIM791_AI := (WORD_TO_REAL(ADC) * REAL#3.128099488E-004) - REAL#4.0;
ELSE
AIM791_AI := REAL#0.0;
END_CASE;
END_FUNCTION
+25
View File
@@ -0,0 +1,25 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Fastwel
Name : DIM764_DI
Type : FUNCTION
Exported : 2026-07-31 11:16:16
===============================================================================
*)
FUNCTION DIM764_DI : REAL
(*На выходе - Частота в Гц*)
VAR_INPUT
IN: DWORD;
RANGE: BYTE; (* 0-Period; 1-Interval; *)
END_VAR
(*----- IMPLEMENTATION -----*)
CASE RANGE OF
0 : IF(IN=0) THEN DIM764_DI:=0; ELSE DIM764_DI := 50000000.0/DWORD_TO_REAL(IN); END_IF;
1 : DIM764_DI:=DWORD_TO_REAL(IN)/50.0;
ELSE
DIM764_DI:=0;
END_CASE;
END_FUNCTION
+21
View File
@@ -0,0 +1,21 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Filters
Name : ExpRunAverage
Type : FUNCTION
Exported : 2026-07-31 11:16:16
===============================================================================
*)
FUNCTION ExpRunAverage : REAL
(* Экспоненциальное скользящее среднее (экспоненциальное сглаживание)*)
VAR_INPUT
In : REAL; (* Входное значение *)
Old : REAL; (* Значение экспоненциального скользящего среднего на прошлом шаге *)
K : REAL; (* Весовой коэффициент в интервале от 0 до 1, отражающий скорость старения прошлых данных: чем выше его значение, тем больший удельный вес имеют новые наблюдения величины *)
END_VAR
(*----- IMPLEMENTATION -----*)
ExpRunAverage := (1.0 - K) * Old + K * In;
END_FUNCTION
@@ -0,0 +1,27 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Filters
Name : ExpRunAverageAdaptive
Type : FUNCTION
Exported : 2026-07-31 11:16:16
===============================================================================
*)
FUNCTION ExpRunAverageAdaptive : REAL
(* Экспоненциальное скользящее среднее c адаптивным коэффициентом*)
VAR_INPUT
In : REAL; (* Входное значение *)
Old : REAL; (* Значение экспоненциального скользящего среднего на прошлом шаге *)
K1 : REAL; (* Весовой коэффициент в интервале от 0 до 1, отражающий скорость старения прошлых данных: чем выше его значение, тем больший удельный вес имеют новые наблюдения величины *)
Delta : REAL; (* Разница для изменения коэффициента *)
K2 : REAL; (* Измененный весовой коэффициент в интервале от 0 до 1 (больший чем K1) *)
END_VAR
VAR
K: REAL;
END_VAR
(*----- IMPLEMENTATION -----*)
IF ABS(In - Old) > Delta THEN K := K2; ELSE K := K1; END_IF;
ExpRunAverageAdaptive := (1.0 - K) * Old + K * In;
END_FUNCTION
+58
View File
@@ -0,0 +1,58 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Filters
Name : MultipleClick
Type : FUNCTION_BLOCK
Exported : 2026-07-31 11:16:16
===============================================================================
*)
FUNCTION_BLOCK MultipleClick
VAR_INPUT
In: BOOL;(* Вход кнопки *)
TimeToWait: TIME:=T#500MS; (* Максимальное время между нажатиями *)
TimeRelease: TIME:=T#0S; (* Время удержания выхода*)
END_VAR
VAR_OUTPUT
Click1: BOOL;(* Было сделано 1 нажатие *)
Click2: BOOL;(* Было сделано 2 нажатия *)
Click3: BOOL;(* Было сделано 3 нажатия *)
Click: WORD;(* Количество нажатий *)
END_VAR
VAR
R_TRIG_BTN: R_TRIG;
TOF_Click: TOF;
CurrentClick: WORD := 0;
TP_Release: TP;
F_TRIG_Click: F_TRIG;
END_VAR
(*----- IMPLEMENTATION -----*)
R_TRIG_BTN(CLK:=In);
TOF_Click(IN:=R_TRIG_BTN.Q, PT:=TimeToWait);
IF TOF_Click.Q AND R_TRIG_BTN.Q THEN
CurrentClick:=CurrentClick+1;
END_IF
F_TRIG_Click(CLK:=TOF_Click.Q);
TP_Release(IN:=F_TRIG_Click.Q, PT:=TimeRelease);
IF F_TRIG_Click.Q THEN
Click:=CurrentClick;
CurrentClick:=0;
END_IF
IF TP_Release.Q THEN
Click1 := (Click = 1);
Click2 := (Click = 2);
Click3 := (Click = 3);
ELSE
Click:=0;
Click1 := 0;
Click2 := 0;
Click3 := 0;
END_IF
END_FUNCTION_BLOCK
+42
View File
@@ -0,0 +1,42 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Filters
Name : RunMiddleArifm
Type : FUNCTION_BLOCK
Exported : 2026-07-31 11:16:16
===============================================================================
*)
FUNCTION_BLOCK RunMiddleArifm
(* Скользящая средняя *)
VAR_INPUT
In : REAL;
N : INT:=32; (* Размер выборки 0...32 *)
RST : BOOL;
END_VAR
VAR_OUTPUT
Y : REAL;
END_VAR
VAR
init: BOOL;
buffer : ARRAY[0..31] OF REAL;
i: INT;
nEff: INT;
END_VAR
(*----- IMPLEMENTATION -----*)
nEff := LIMIT(0, N, 32);
IF NOT init OR RST OR nEff = 0 THEN
init := TRUE;
FOR i := 0 TO nEff - 1 DO
buffer[i] := In;
END_FOR;
Y := In;
ELSE
IF i >= nEff - 1 THEN i := 0; ELSE i := i + 1; END_IF;
Y := Y + (In - buffer[i]) / INT_TO_REAL(nEff);
buffer[i] := In;
END_IF;
END_FUNCTION_BLOCK
+49
View File
@@ -0,0 +1,49 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Иное
Name : DFS
Type : PROGRAM
Exported : 2026-07-31 11:16:16
===============================================================================
*)
PROGRAM DFS
(* Алгоритм поиска в глубину *)
(* Копировать в свою программу *)
VAR_INPUT
graph: POINTER TO ARRAY[1..n, 1..n] OF BOOL; // Граф связности (редактируется извне программы через DFS.graph[i,j])
connected: POINTER TO ARRAY[1..n] OF INT; //подключенные вершины
Start: INT := 1; // Вершина из которой начинается поиск
END_VAR
VAR
STACK: STACK_I16;
v: INT;
i: INT;
visited: ARRAY[1..n] OF BOOL; // посещенные вершины
k: INT := 1; // номер посещенной вершины
END_VAR
VAR CONSTANT
n : INT := 12; // Количество вершин графа
END_VAR
(*----- IMPLEMENTATION -----*)
FOR i:=1 TO n DO
visited[i]:=0;
connected^[i]:=0;
END_FOR
k:=1;
STACK(Din:= Start, PUSH:= 1);
visited[Start]:= 1;
WHILE NOT STACK.EMPTY DO
STACK(pop:= 1, Dout=>v);
FOR i:=1 TO n DO
IF (graph^[v,i] AND NOT(visited[i])) THEN
visited[i]:= 1;
STACK(Din:= i, PUSH:= 1);
connected^[k]:=i;k:=k+1;
END_IF
END_FOR
END_WHILE
END_PROGRAM
+34
View File
@@ -0,0 +1,34 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Общие
Name : ALARM_1
Type : FUNCTION_BLOCK
Exported : 2026-07-31 11:16:16
===============================================================================
*)
FUNCTION_BLOCK ALARM_1
(* Функциональный блок проверяет входное значение X на принадлежность [LO_1HI_1]*)
VAR_INPUT
X : REAL; (* Контролируемое значение. *)
LO_1 : REAL; (* Нижний предел 1. *)
HI_1 : REAL; (* Верхний предел 1. *)
HYS : REAL; (* Значение гистерезиса (берется как HYS/2 отностительно середины) *)
END_VAR
VAR_OUTPUT
Q1_LO : BOOL := FALSE; (* Флаг «значение меньше нижнего предела 1». *)
Q1_HI : BOOL := FALSE; (* Флаг «значение больше верхнего предела 1». *)
END_VAR
VAR
tmp: REAL;
END_VAR
(*----- IMPLEMENTATION -----*)
tmp := X - HYS * 0.5;
IF tmp > LO_1 THEN Q1_LO := FALSE; END_IF;
IF tmp > HI_1 THEN Q1_HI := TRUE; END_IF;
tmp := tmp + HYS;
IF tmp < LO_1 THEN Q1_LO := TRUE; END_IF;
IF tmp < HI_1 THEN Q1_HI := FALSE; END_IF;
END_FUNCTION_BLOCK
+42
View File
@@ -0,0 +1,42 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Общие
Name : ALARM_2
Type : FUNCTION_BLOCK
Exported : 2026-07-31 11:16:16
===============================================================================
*)
FUNCTION_BLOCK ALARM_2
(* Функциональный блок проверяет входное значение X на принадлежность [LO_1HI_1] [LO_2HI_2] *)
VAR_INPUT
X : REAL; (* Контролируемое значение. *)
LO_1 : REAL; (* Нижний предел 1. *)
HI_1 : REAL; (* Верхний предел 1. *)
LO_2 : REAL; (* Нижний предел 2. *)
HI_2 : REAL; (* Верхний предел 2. *)
HYS : REAL; (* Значение гистерезиса (берется как HYS/2 отностительно середины) *)
END_VAR
VAR_OUTPUT
Q1_LO : BOOL := FALSE; (* Флаг «значение меньше нижнего предела 1». *)
Q1_HI : BOOL := FALSE; (* Флаг «значение больше верхнего предела 1». *)
Q2_LO : BOOL := FALSE; (* Флаг «значение меньше нижнего предела 2». *)
Q2_HI : BOOL := FALSE; (* Флаг «значение больше верхнего предела 2». *)
END_VAR
VAR
tmp: REAL;
END_VAR
(*----- IMPLEMENTATION -----*)
tmp := X - HYS * 0.5;
IF tmp > LO_1 THEN Q1_LO := FALSE; END_IF;
IF tmp > LO_2 THEN Q2_LO := FALSE; END_IF;
IF tmp > HI_1 THEN Q1_HI := TRUE; END_IF;
IF tmp > HI_2 THEN Q2_HI := TRUE; END_IF;
tmp := tmp + HYS;
IF tmp < LO_1 THEN Q1_LO := TRUE; END_IF;
IF tmp < LO_2 THEN Q2_LO := TRUE; END_IF;
IF tmp < HI_1 THEN Q1_HI := FALSE; END_IF;
IF tmp < HI_2 THEN Q2_HI := FALSE; END_IF;
END_FUNCTION_BLOCK
+50
View File
@@ -0,0 +1,50 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Общие
Name : ALARM_3
Type : FUNCTION_BLOCK
Exported : 2026-07-31 11:16:16
===============================================================================
*)
FUNCTION_BLOCK ALARM_3
(* Функциональный блок проверяет входное значение X на принадлежность [LO_1HI_1] [LO_2HI_2] [LO_3HI_3] *)
VAR_INPUT
X : REAL; (* Контролируемое значение. *)
LO_1 : REAL; (* Нижний предел 1. *)
HI_1 : REAL; (* Верхний предел 1. *)
LO_2 : REAL; (* Нижний предел 2. *)
HI_2 : REAL; (* Верхний предел 2. *)
LO_3 : REAL; (* Нижний предел 3. *)
HI_3 : REAL; (* Верхний предел 3. *)
HYS : REAL; (* Значение гистерезиса (берется как HYS/2 отностительно середины) *)
END_VAR
VAR_OUTPUT
Q1_LO : BOOL := FALSE; (* Флаг «значение меньше нижнего предела 1». *)
Q1_HI : BOOL := FALSE; (* Флаг «значение больше верхнего предела 1». *)
Q2_LO : BOOL := FALSE; (* Флаг «значение меньше нижнего предела 2». *)
Q2_HI : BOOL := FALSE; (* Флаг «значение больше верхнего предела 2». *)
Q3_LO : BOOL := FALSE; (* Флаг «значение меньше нижнего предела 3». *)
Q3_HI : BOOL := FALSE; (* Флаг «значение больше верхнего предела 3». *)
END_VAR
VAR
tmp: REAL;
END_VAR
(*----- IMPLEMENTATION -----*)
tmp := X - HYS * 0.5;
IF tmp > LO_1 THEN Q1_LO := FALSE; END_IF;
IF tmp > LO_2 THEN Q2_LO := FALSE; END_IF;
IF tmp > LO_3 THEN Q3_LO := FALSE; END_IF;
IF tmp > HI_1 THEN Q1_HI := TRUE; END_IF;
IF tmp > HI_2 THEN Q2_HI := TRUE; END_IF;
IF tmp > HI_3 THEN Q3_HI := TRUE; END_IF;
tmp := tmp + HYS;
IF tmp < LO_1 THEN Q1_LO := TRUE; END_IF;
IF tmp < LO_2 THEN Q2_LO := TRUE; END_IF;
IF tmp < LO_3 THEN Q3_LO := TRUE; END_IF;
IF tmp < HI_1 THEN Q1_HI := FALSE; END_IF;
IF tmp < HI_2 THEN Q2_HI := FALSE; END_IF;
IF tmp < HI_3 THEN Q3_HI := FALSE; END_IF;
END_FUNCTION_BLOCK
+36
View File
@@ -0,0 +1,36 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Общие
Name : BLINK_LMP
Type : FUNCTION_BLOCK
Exported : 2026-07-31 11:16:16
===============================================================================
*)
FUNCTION_BLOCK BLINK_LMP
(*
Мигание лампами в завистмости от статуса
*)
VAR_INPUT
State: WORD:=0; (* 0 - выключено; 1 - включено; 2 - мигание с частотой 1; 3 - мигание с частотой 2; 4 - мигание с частотой 3; *)
Inp_BL_1: BOOL := FALSE; (* Пульсар миганич с частотой 1 *)
Inp_BL_2: BOOL := FALSE; (* Пульсар миганич с частотой 2 *)
Inp_BL_3: BOOL := FALSE; (* Пульсар миганич с частотой 3 *)
END_VAR
VAR_OUTPUT
LMP: BOOL;
END_VAR
VAR
END_VAR
(*----- IMPLEMENTATION -----*)
CASE State OF
0: LMP:=FALSE;
1: LMP:=TRUE;
2: LMP:=Inp_BL_1;
3: LMP:=Inp_BL_2;
4: LMP:=Inp_BL_3;
END_CASE;
END_FUNCTION_BLOCK
+72
View File
@@ -0,0 +1,72 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Общие
Name : BUZZER_CTRL
Type : FUNCTION_BLOCK
Exported : 2026-07-31 11:16:16
===============================================================================
*)
FUNCTION_BLOCK BUZZER_CTRL
(*
Звучание звонком лампами в завистмости от статуса с возможностью отключения звука
*)
VAR_INPUT
State: WORD:=0; (* 0 - выключено; 1 - включено; 2 - мигание с частотой 1; 3 - мигание с частотой 2; 4 - мигание с частотой 3; *)
Sound_off: BOOL; (* Отключение звука *)
Inp_BL_1: BOOL := FALSE; (* Пульсар миганич с частотой 1 *)
Inp_BL_2: BOOL := FALSE; (* Пульсар миганич с частотой 2 *)
Inp_BL_3: BOOL := FALSE; (* Пульсар миганич с частотой 3 *)
PermanentOFF_Enable: BOOL := FALSE; (*Включение возможности отключить звук перманентно *)
T_PermanentOFF: TIME := T#10S; (* Время удержания кнопки отключения звука для перманентного отключения *)
END_VAR
VAR_OUTPUT
BUZZER: BOOL;
END_VAR
VAR
R_TRIG_0: R_TRIG;
R_TRIG_1: R_TRIG;
R_TRIG_2: R_TRIG;
R_TRIG_3: R_TRIG;
R_TRIG_4: R_TRIG;
B_ctrl: WORD;
T_S_OFF: TON;
R_TRIG_S_OFF: R_TRIG;
AlltimeOff: BOOL;
TP1: TP:=(PT:=T#100MS);
R_TRIG1: R_TRIG;
F_TRIG1: F_TRIG;
F_TRIG2: F_TRIG;
END_VAR
(*----- IMPLEMENTATION -----*)
R_TRIG_0(CLK:=(State=0)); IF R_TRIG_0.Q THEN B_ctrl:=0; END_IF;
R_TRIG_1(CLK:=(State=1)); IF R_TRIG_1.Q THEN B_ctrl:=1; END_IF;
R_TRIG_2(CLK:=(State=2)); IF R_TRIG_2.Q THEN B_ctrl:=2; END_IF;
R_TRIG_3(CLK:=(State=3)); IF R_TRIG_3.Q THEN B_ctrl:=3; END_IF;
R_TRIG_4(CLK:=(State=4)); IF R_TRIG_4.Q THEN B_ctrl:=4; END_IF;
T_S_OFF(IN:=Sound_off, PT:=T_PermanentOFF);
R_TRIG_S_OFF(CLK:=Sound_off AND PermanentOFF_Enable);
R_TRIG1(CLK:=AlltimeOff);
F_TRIG1(CLK:=AlltimeOff);
TP1(IN:=R_TRIG1.Q OR F_TRIG1.Q);
F_TRIG2(CLK:=TP1.Q);
IF R_TRIG_S_OFF.Q THEN B_ctrl:=0; AlltimeOff:=0; END_IF
IF T_S_OFF.Q THEN AlltimeOff:=1; END_IF
IF AlltimeOff THEN B_ctrl:=0; END_IF;
IF TP1.Q THEN B_ctrl:=1; END_IF;
IF (F_TRIG2.Q OR F_TRIG1.Q) THEN B_ctrl:=0; END_IF;
CASE B_ctrl OF
0: BUZZER:=FALSE;
1: BUZZER:=TRUE;
2: BUZZER:=Inp_BL_1;
3: BUZZER:=Inp_BL_2;
4: BUZZER:=Inp_BL_3;
END_CASE;
END_FUNCTION_BLOCK
@@ -0,0 +1,59 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Общие
Name : Button_acceleration
Type : FUNCTION_BLOCK
Exported : 2026-07-31 11:16:16
===============================================================================
*)
FUNCTION_BLOCK Button_acceleration
(* Ускорение изменения значения при нажатой кнопке *)
VAR_INPUT
Button: BOOL;
D_0: REAL := 0; (* Прирост при одиночном нажатии *)
T_W_1: TIME := T#0s; (* Время кратковременного удержания *)
T_D_1: TIME := T#200ms; (* Шаг времени прироста при кратковременном удержании *)
D_1: REAL := 0; (* Прирост при кратковременном удержании *)
T_W_2: TIME := T#0S; (* Время длительного удержания *)
T_D_2: TIME := T#200MS; (* Шаг времени прироста при длительном удержании *)
D_2: REAL := 0; (* Прирост при длительном удержании *)
Zeroes : BOOL := false; (* При этом флаге происходит сброс в 0 при дадании -D_0 << D_0 *)
END_VAR
VAR_IN_OUT
Out: REAL; (* Вход-выход который изменяется *)
END_VAR
VAR
Ton_1: TON;
Ton_2: TON;
TOF_1: TOF;
TOF_2: TOF;
R_TRIG_0: R_TRIG;
R_TRIG_1: R_TRIG;
R_TRIG_2: R_TRIG;
END_VAR
VAR_OUTPUT
END_VAR
(*----- IMPLEMENTATION -----*)
Ton_1(PT := T_W_1,IN:=Button);
Ton_2(PT := T_W_2,IN:=Button);
TOF_1(PT := T_D_1,IN:=Ton_1.Q AND NOT(TOF_1.Q) AND NOT(Ton_2.Q));
TOF_2(PT := T_D_2,IN:=Ton_2.Q AND NOT(TOF_2.Q));
R_TRIG_0(CLK:=Button AND NOT(Ton_1.Q) AND NOT(Ton_2.Q));
R_TRIG_1(CLK:=TOF_1.Q);
R_TRIG_2(CLK:=TOF_2.Q);
IF R_TRIG_0.Q THEN
Out:=Out+D_0;
END_IF
IF R_TRIG_1.Q THEN
Out:=Out+D_1;
END_IF
IF R_TRIG_2.Q THEN
Out:=Out+D_2;
END_IF
IF Zeroes AND ABS(Out) < ABS(D_0) THEN
Out:=0;
END_IF
END_FUNCTION_BLOCK
+46
View File
@@ -0,0 +1,46 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Общие
Name : CONFIRMATION
Type : FUNCTION_BLOCK
Exported : 2026-07-31 11:16:16
===============================================================================
*)
FUNCTION_BLOCK CONFIRMATION
(*
Квитирование
*)
VAR_INPUT
Alarm: BOOL; // Авария
ControlState_On: BOOL; // Управление с текущего поста
Sound_Off: BOOL; // Сигнал снятия звука
Confirmation: BOOL; // Сигнал квитирования
END_VAR
VAR_OUTPUT
Lamp_Alarm_C: WORD; // Лампа аварии
Lamp_Confirmation_C: WORD; // Лампа квитирования
Buzzer_C: WORD; // Управление звонком
END_VAR
VAR
Alarm_Not_Sound_Off: BOOL;
Alarm_Not_Confirm: BOOL;
R_TR1: R_TRIG;
R_TR2: R_TRIG;
R_TR3: R_TRIG;
END_VAR
(*----- IMPLEMENTATION -----*)
R_TR1(CLK:=Confirmation); IF R_TR1.Q THEN Alarm_Not_Confirm:=0; END_IF;
R_TR2(CLK:=Sound_Off); IF R_TR2.Q THEN Alarm_Not_Sound_Off:=0; END_IF;
R_TR3(CLK:=Alarm); IF R_TR3.Q AND ControlState_On THEN Alarm_Not_Confirm:=1; Alarm_Not_Sound_Off:=1; END_IF;
IF NOT ControlState_On THEN Alarm_Not_Confirm:=0; Alarm_Not_Sound_Off:=0; END_IF;
IF NOT Alarm AND NOT Alarm_Not_Confirm THEN Lamp_Alarm_C:=0; Lamp_Confirmation_C:=0; END_IF;
IF Alarm AND NOT Alarm_Not_Confirm THEN Lamp_Alarm_C:=1; Lamp_Confirmation_C:=0; END_IF;
IF Alarm_Not_Confirm THEN Lamp_Alarm_C:=2; Lamp_Confirmation_C:=1; END_IF;
IF Alarm_Not_Sound_Off THEN Buzzer_C:=2; ELSE Buzzer_C:=0; END_IF;
END_FUNCTION_BLOCK
+45
View File
@@ -0,0 +1,45 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Общие
Name : HYST
Type : FUNCTION_BLOCK
Exported : 2026-07-31 11:16:16
===============================================================================
*)
FUNCTION_BLOCK HYST
VAR_INPUT
In : REAL;
ON : REAL;
OFF : REAL;
END_VAR
VAR_OUTPUT
Q : BOOL;
win : BOOL;
END_VAR
(*----- IMPLEMENTATION -----*)
IF ON >= OFF THEN
IF IN < OFF THEN
Q := FALSE;
WIN := FALSE;
ELSIF IN > ON THEN
Q := TRUE;
WIN := FALSE;
ELSE
WIN := TRUE;
END_IF;
ELSE
IF IN > OFF THEN
Q := FALSE;
WIN := FALSE;
ELSIF IN < ON THEN
Q := TRUE;
WIN := FALSE;
ELSE
WIN := TRUE;
END_IF;
END_IF;
END_FUNCTION_BLOCK
+27
View File
@@ -0,0 +1,27 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Общие
Name : Norm_real
Type : FUNCTION
Exported : 2026-07-31 11:16:16
===============================================================================
*)
FUNCTION Norm_real : REAL
(*
Нормирует значение аналогового входа X по двум заданным граничным точкам нормировки (X1,Y1) (X2,Y2)
*)
VAR_INPUT
X: REAL;
X1: REAL;
Y1: REAL;
X2: REAL;
Y2: REAL;
END_VAR
(*----- IMPLEMENTATION -----*)
IF (X2-X1 = 0) THEN Norm_real:=Y1; RETURN; END_IF;
Norm_real:=(X-X1)*(Y2-Y1)/(X2-X1)+Y1;
END_FUNCTION
+125
View File
@@ -0,0 +1,125 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Общие
Name : PIDcontrol
Type : FUNCTION_BLOCK
Exported : 2026-07-31 11:16:16
===============================================================================
*)
FUNCTION_BLOCK PIDcontrol (* ПИД-регулятор. *)
VAR_INPUT
ProcessVariable: REAL := 0.0; (* Измеренное значение параметра *)
Setpoint: REAL := 0.0; (* Заданное значение *)
Kp: REAL := 0.001; (* Пропорциональный коэффициент *)
Ki: REAL := 0.001; (* Интегральный коэффициент *)
Kd: REAL := 0.0; (* Дифференциальный коэффициент *)
Kdf: REAL := 1.0; (* Коэффициент фильтрации (1/Tdf) *)
DBmax: REAL := 0.01; (* Верхняя граница зоны нечувствительности *)
DBmin: REAL := - 0.01; (* Нижняя граница зоны нечувствительности *)
OutMax: REAL := 100.0; (* Максимальный выход *)
OutMin: REAL := 0.0; (* Минимальный выход *)
Ts: REAL := 0.1; (* Время дискретизации [с] *)
Manual: REAL := 25.0; (* Значение в ручном режиме *)
ManOn: BOOL := FALSE; (* Флаг ручного режима *)
Reset: BOOL := FALSE; (* Сброс интегратора *)
AntiWindup: BOOL := TRUE; (* Включить антивиндап *)
END_VAR
VAR_OUTPUT
Out: REAL := 0.0; (* Выходной сигнал *)
Err: REAL := 0.0; (* Ошибка регулирования *)
OutLimited: BOOL := FALSE; (* Флаг ограничения выхода *)
END_VAR
VAR (* Внутренние переменные, сохраняемые. *)
Er: REAL := 0.0; (* Текущая ошибка *)
Ppart: REAL := 0.0; (* Пропорциональная часть *)
Ipart: REAL := 0.0; (* Интегральная часть *)
Dpart: REAL := 0.0; (* Дифференциальная часть *)
PrevEr: REAL := 0.0; (* Ошибка на предыдущем шаге (для дифференциатора). *)
FirstScan: BOOL := TRUE; (* Флаг первого сканирования. *)
END_VAR
VAR_TEMP
Auto: REAL; (* Выход ПИД *)
Sw: REAL; (* Промежуточное значение *)
END_VAR
(*----- IMPLEMENTATION -----*)
(* === ИНИЦИАЛИЗАЦИЯ И СБРОС === *)
IF Reset OR FirstScan THEN
Ipart := 0.0;
Dpart := 0.0;
PrevEr := 0.0;
FirstScan := FALSE;
END_IF;
(* === ПРОВЕРКА ВХОДНЫХ ПАРАМЕТРОВ === *)
IF (OutMin >= OutMax) OR (DBmin >= DBmax) OR (Ts <= 0) THEN
Out := 0.0;
OutLimited := TRUE;
RETURN;
END_IF;
(* === РАСЧЕТ ОШИБКИ === *)
Er := Setpoint - ProcessVariable;
Err := Er;
(* ЗОНА НЕЧУВСТВИТЕЛЬНОСТИ *)
IF (DBmin < Er) AND (Er < DBmax) THEN
Er := 0.0;
END_IF;
(* === ПРОПОРЦИОНАЛЬНАЯ ЧАСТЬ === *)
Ppart := Kp * Er;
(* === ИНТЕГРАЛЬНАЯ ЧАСТЬ === *)
IF Ki <> 0.0 THEN
Ipart := Ipart + (Ki * Er * Ts);
Ipart := LIMIT(OutMin, Ipart, OutMax);
ELSE
Ipart := 0.0;
END_IF;
(* === ДИФФЕРЕНЦИАЛЬНАЯ ЧАСТЬ (фильтрованная) === *)
IF Kd <> 0.0 THEN
Dpart := Kd * Kdf * (Er - PrevEr) - Kdf * Dpart * Ts;
PrevEr := Er; (* Сохраняем ошибку для следующего шага *)
ELSE
Dpart := 0.0;
END_IF;
(* === ВЫЧИСЛЕНИЕ ВЫХОДА === *)
Auto := Ppart + Ipart + Dpart;
(* === РЕЖИМ РУЧНОЙ/АВТОМАТИЧЕСКИЙ === *)
IF ManOn THEN
Sw := Manual;
(* Сброс интегратора при переходе в ручной режим *)
IF AntiWindup THEN
Ipart := Sw - (Ppart + Dpart);
END_IF;
ELSE
Sw := Auto;
END_IF;
(* === ОГРАНИЧЕНИЕ ВЫХОДА И АНТИВИНДАП === *)
IF Sw >= OutMax THEN
Out := OutMax;
OutLimited := TRUE;
IF AntiWindup AND NOT ManOn THEN
Ipart := OutMax - (Ppart + Dpart);
END_IF;
ELSIF Sw <= OutMin THEN
Out := OutMin;
OutLimited := TRUE;
IF AntiWindup AND NOT ManOn THEN
Ipart := OutMin - (Ppart + Dpart);
END_IF;
ELSE
Out := Sw;
OutLimited := FALSE;
END_IF;
END_FUNCTION_BLOCK
+32
View File
@@ -0,0 +1,32 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Общие
Name : Pulse_Switch
Type : FUNCTION_BLOCK
Exported : 2026-07-31 11:16:16
===============================================================================
*)
FUNCTION_BLOCK Pulse_Switch
VAR
R_tr_1: R_TRIG;
F_tr_1: F_TRIG;
tp_on: TP:=(PT:=t#500ms);
tp_off: TP:=(PT:=t#500ms);
END_VAR
VAR_INPUT
Switch_ctrl: BOOL;
END_VAR
VAR_OUTPUT
Switch_on: BOOL;
Switch_off: BOOL;
END_VAR
(*----- IMPLEMENTATION -----*)
R_tr_1(CLK:=Switch_ctrl);
tp_on(IN:=R_tr_1.Q, Q=>Switch_on);
F_tr_1(CLK:=Switch_ctrl);
tp_off(IN:=F_tr_1.Q, Q=>Switch_off);
END_FUNCTION_BLOCK
+55
View File
@@ -0,0 +1,55 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Общие
Name : STACK_I16
Type : FUNCTION_BLOCK
Exported : 2026-07-31 11:16:16
===============================================================================
*)
FUNCTION_BLOCK STACK_I16
(* стек на 16 элементов типа INT*)
VAR_INPUT
Din : INT; (* Переменная для записи в стек *)
E : BOOL := TRUE;(* Включение стека *)
POP : BOOL; (* Извлечение из стека *)
PUSH : BOOL; (* Запись в стек *)
RST : BOOL; (* Обнуление стека *)
END_VAR
VAR_OUTPUT
Dout : INT;
EMPTY : BOOL := TRUE;
FULL : BOOL;
END_VAR
VAR
stack: ARRAY[0..n] OF INT;
pt : INT;
END_VAR
VAR CONSTANT
n : INT := 15; (* changing this value will chage the number of stored elements in the fifo *)
END_VAR
(*----- IMPLEMENTATION -----*)
IF RST THEN
(* asynchronous reset for the fifo *)
pt := 0;
EMPTY := TRUE;
FULL := FALSE;
Dout := 0;
ELSIF E THEN
IF NOT EMPTY AND POP THEN
pt := pt - 1;
Dout := stack[pt];
EMPTY := pt = 0;
FULL := FALSE;
END_IF;
IF NOT FULL AND PUSH THEN
stack[pt] := Din;
pt := pt + 1;
FULL := pt > n;
EMPTY := FALSE;
END_IF;
END_IF;
END_FUNCTION_BLOCK
+99
View File
@@ -0,0 +1,99 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Общие
Name : STATUS_ANALOG_M
Type : FUNCTION_BLOCK
Exported : 2026-07-31 11:16:16
===============================================================================
*)
FUNCTION_BLOCK STATUS_ANALOG_M
(*
Модуль формирования статуса
Гистерезис по отключению и задержка по включению, вход типа REAL
-1 - нет связи с датчиком
0 - не определено
1 - норма
2 - предупредительный по нижнему уровню
3 - аварийный по нижнему уровню
4 - предупредительный по верхнему уровню,
5 - аварийный по верхнему уровню
*)
VAR_INPUT
Input: REAL;
fail_hi: REAL:=3.402823466E+38;
delta_fail_hi: REAL := 0;
time_fail_hi: TIME :=t#0s;
warn_hi: REAL:=3.402823466E+38;
delta_warn_hi: REAL := 0;
time_warn_hi: TIME :=t#0s;
warn_low: REAL:=-3.402823466E+38;
delta_warn_low: REAL := 0;
time_warn_low: TIME :=t#0s;
fail_low: REAL:=-3.402823466E+38;
delta_fail_low: REAL := 0;
time_fail_low: TIME :=t#0s;
no_signal: REAL:=-3.402823466E+38;
delta_no_signal: REAL := 0;
time_no_signal: TIME :=T#0S;
In_fail_hi: BOOL := FALSE;
In_warn_hi: BOOL := FALSE;
In_warn_low: BOOL := FALSE;
In_fail_low: BOOL := FALSE;
In_no_signal: BOOL := FALSE;
END_VAR
VAR_OUTPUT
Status: AnalogValueState;
END_VAR
VAR
fail_hi_st: BOOL;
warn_hi_st: BOOL;
normal_st: BOOL;
warn_low_st: BOOL;
fail_low_st: BOOL;
no_signal_st: BOOL;
TON_fail_hi: TON;
TON_warn_hi: TON;
TON_warn_low: TON;
TON_fail_low: TON;
TON_no_signal: TON;
END_VAR
(*----- IMPLEMENTATION -----*)
TON_no_signal(IN := (Input <= no_signal) OR In_no_signal, PT := time_no_signal);
IF TON_no_signal.Q THEN no_signal_st := TRUE; ELSIF (Input > no_signal + delta_no_signal) THEN no_signal_st := FALSE; END_IF;
TON_fail_low(IN := ((Input <= fail_low) AND NOT(no_signal_st)) OR In_fail_low, PT := time_fail_low);
IF TON_fail_low.Q THEN fail_low_st := TRUE; ELSIF (Input > fail_low + delta_fail_low) THEN fail_low_st := FALSE; END_IF;
TON_warn_low(IN := ((Input <= warn_low) AND NOT(no_signal_st) AND NOT(TON_fail_low.Q)) OR In_warn_low, PT := time_warn_low);
IF TON_warn_low.Q THEN warn_low_st := TRUE; ELSIF (Input > warn_low + delta_warn_low) THEN warn_low_st := FALSE; END_IF;
TON_fail_hi(IN := ((Input >= fail_hi) AND NOT(no_signal_st)) OR In_fail_hi, PT := time_fail_hi);
IF TON_fail_hi.Q THEN fail_hi_st := TRUE; ELSIF (Input < fail_hi - delta_fail_hi) THEN fail_hi_st := FALSE; END_IF;
TON_warn_hi(IN := ((Input >= warn_hi) AND NOT(no_signal_st) AND NOT(TON_fail_hi.Q)) OR In_warn_hi, PT := time_warn_hi);
IF TON_warn_hi.Q THEN warn_hi_st := TRUE; ELSIF (Input < warn_hi - delta_warn_hi) THEN warn_hi_st := FALSE; END_IF;
normal_st := (NOT(no_signal_st) AND NOT(fail_low_st) AND NOT(warn_low_st) AND NOT(warn_hi_st) AND NOT(fail_hi_st));
IF no_signal_st THEN
Status := AnalogValueState.no_signal;
ELSIF fail_hi_st THEN
Status := AnalogValueState.fail_hi;
ELSIF fail_low_st THEN
Status := AnalogValueState.fail_low;
ELSIF warn_hi_st THEN
Status := AnalogValueState.warn_hi;
ELSIF warn_low_st THEN
Status := AnalogValueState.warn_low;
ELSIF normal_st THEN
Status := AnalogValueState.normal;
ELSE
Status := AnalogValueState.undefined;
END_IF;
END_FUNCTION_BLOCK
+102
View File
@@ -0,0 +1,102 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Общие
Name : STATUS_ANALOG_M_I
Type : FUNCTION_BLOCK
Exported : 2026-07-31 11:16:16
===============================================================================
*)
FUNCTION_BLOCK STATUS_ANALOG_M_I
(*
Модуль формирования статуса
Гистерезис по отключению и задержка по включению, вход типа INT
-1 - нет связи с датчиком
0 - не определено
1 - норма
2 - предупредительный по нижнему уровню
3 - аварийный по нижнему уровню
4 - предупредительный по верхнему уровню,
5 - аварийный по верхнему уровню
*)
VAR_INPUT
Input: INT;
fail_hi: INT:=32767;
delta_fail_hi: INT := 0;
time_fail_hi: TIME :=t#0s;
warn_hi: INT:=32767;
delta_warn_hi: INT := 0;
time_warn_hi: TIME :=t#0s;
warn_low: INT:=-32768;
delta_warn_low: INT := 0;
time_warn_low: TIME :=t#0s;
fail_low: INT:=-32768;
delta_fail_low: INT := 0;
time_fail_low: TIME :=t#0s;
no_signal: INT:=-32768;
delta_no_signal: INT := 0;
time_no_signal: TIME :=T#0S;
In_fail_hi: BOOL := FALSE;
In_warn_hi: BOOL := FALSE;
In_warn_low: BOOL := FALSE;
In_fail_low: BOOL := FALSE;
In_no_signal: BOOL := FALSE;
END_VAR
VAR_OUTPUT
Status: AnalogValueState;
END_VAR
VAR
(* !!! ВНУТРЕННИЕ ВЫХОДНЫЕ ПЕРЕМЕННЫЕ!!! *)
fail_hi_st: BOOL;
warn_hi_st: BOOL;
normal_st: BOOL;
warn_low_st: BOOL;
fail_low_st: BOOL;
no_signal_st: BOOL;
(* !!! ВНУТРЕННИЕ НЕВЫХОДНЫЕ ПЕРЕМЕННЫЕ!!! *)
TON_fail_hi: TON;
TON_warn_hi: TON;
TON_warn_low: TON;
TON_fail_low: TON;
TON_no_signal: TON;
END_VAR
(*----- IMPLEMENTATION -----*)
TON_no_signal(IN := (Input <= no_signal) OR In_no_signal, PT := time_no_signal);
IF TON_no_signal.Q THEN no_signal_st := TRUE; ELSIF (Input > no_signal + delta_no_signal) THEN no_signal_st := FALSE; END_IF;
TON_fail_low(IN := ((Input <= fail_low) AND NOT(no_signal_st)) OR In_fail_low, PT := time_fail_low);
IF TON_fail_low.Q THEN fail_low_st := TRUE; ELSIF (Input > fail_low + delta_fail_low) THEN fail_low_st := FALSE; END_IF;
TON_warn_low(IN := ((Input <= warn_low) AND NOT(no_signal_st) AND NOT(TON_fail_low.Q)) OR In_warn_low, PT := time_warn_low);
IF TON_warn_low.Q THEN warn_low_st := TRUE; ELSIF (Input > warn_low + delta_warn_low) THEN warn_low_st := FALSE; END_IF;
TON_fail_hi(IN := ((Input >= fail_hi) AND NOT(no_signal_st)) OR In_fail_hi, PT := time_fail_hi);
IF TON_fail_hi.Q THEN fail_hi_st := TRUE; ELSIF (Input < fail_hi - delta_fail_hi) THEN fail_hi_st := FALSE; END_IF;
TON_warn_hi(IN := ((Input >= warn_hi) AND NOT(no_signal_st) AND NOT(TON_fail_hi.Q)) OR In_warn_hi, PT := time_warn_hi);
IF TON_warn_hi.Q THEN warn_hi_st := TRUE; ELSIF (Input < warn_hi - delta_warn_hi) THEN warn_hi_st := FALSE; END_IF;
normal_st := (NOT(no_signal_st) AND NOT(fail_low_st) AND NOT(warn_low_st) AND NOT(warn_hi_st) AND NOT(fail_hi_st));
IF no_signal_st THEN
Status := AnalogValueState.no_signal;
ELSIF fail_hi_st THEN
Status := AnalogValueState.fail_hi;
ELSIF fail_low_st THEN
Status := AnalogValueState.fail_low;
ELSIF warn_hi_st THEN
Status := AnalogValueState.warn_hi;
ELSIF warn_low_st THEN
Status := AnalogValueState.warn_low;
ELSIF normal_st THEN
Status := AnalogValueState.normal;
ELSE
Status := AnalogValueState.undefined;
END_IF;
END_FUNCTION_BLOCK
@@ -0,0 +1,39 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Общие
Name : Status_Connect_Counter
Type : FUNCTION_BLOCK
Exported : 2026-07-31 11:16:16
===============================================================================
*)
FUNCTION_BLOCK Status_Connect_Counter
(* Статус связи с СВУ по счетчику*)
VAR_INPUT
Counter: WORD;
T_Error: TIME; (* время для диагностирования ошибки*)
END_VAR
VAR_OUTPUT
CounterIsChanged: BOOL;
ERROR_CONNECT: BOOL;
END_VAR
VAR
CounterOld: WORD;
TON_SU: TON;
END_VAR
(*----- IMPLEMENTATION -----*)
TON_SU(IN:=(Counter = CounterOld), PT:=T_Error);
ERROR_CONNECT:=TON_SU.Q;
IF(Counter <> CounterOld) THEN
CounterIsChanged:=TRUE;
ELSE
CounterIsChanged:=FALSE;
END_IF
CounterOld:=Counter;
END_FUNCTION_BLOCK
@@ -0,0 +1,30 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Общие
Name : Status_SU_Connect
Type : FUNCTION_BLOCK
Exported : 2026-07-31 11:16:16
===============================================================================
*)
FUNCTION_BLOCK Status_SU_Connect
(* Статус связи с СВУ по серцебиению*)
VAR_INPUT
HeartBit: BOOL;
T_Pulse: TIME;
END_VAR
VAR_OUTPUT
ERROR_CONNECT: BOOL;
END_VAR
VAR
TON_SU_1: TON;
TON_SU_2: TON;
END_VAR
(*----- IMPLEMENTATION -----*)
TON_SU_1(IN:=HeartBit, PT:=T_Pulse);
TON_SU_2(IN:=NOT HeartBit, PT:=T_Pulse);
ERROR_CONNECT:=TON_SU_1.Q OR TON_SU_2.Q;
END_FUNCTION_BLOCK
+68
View File
@@ -0,0 +1,68 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Общие
Name : TOF_RST
Type : FUNCTION_BLOCK
Exported : 2026-07-31 11:16:16
===============================================================================
*)
FUNCTION_BLOCK TOF_RST
VAR_INPUT
IN: BOOL; // Управляющий сигнал
PT: TIME; // Время задержки выключения
RST: BOOL; // Принудительный сброс
END_VAR
VAR_OUTPUT
Q: BOOL; // Выход
ET: TIME; // Время работы таймера
END_VAR
VAR
_ton: TON;
prevIN: BOOL; // предыдущее состояние IN для детектирования фронта
timerRunning: BOOL; // TRUE идёт отсчёт задержки
END_VAR
(*----- IMPLEMENTATION -----*)
// Принудительный сброс наивысший приоритет
IF RST THEN
Q := FALSE;
timerRunning := FALSE;
prevIN := FALSE; // после RST не ловим задний фронт
_ton(IN := FALSE, PT := PT);
ET := _ton.ET;
RETURN;
END_IF
// Передний фронт IN отменяем задержку
IF IN AND NOT prevIN THEN
timerRunning := FALSE;
END_IF
// Задний фронт IN запускаем задержку
IF NOT IN AND prevIN THEN
timerRunning := TRUE;
END_IF
prevIN := IN;
// Запуск/работа таймера
_ton(IN := timerRunning, PT := PT);
ET := _ton.ET;
// Логика выхода
IF IN THEN
Q := TRUE;
timerRunning := FALSE;
ELSIF timerRunning THEN
IF _ton.Q THEN
Q := FALSE;
timerRunning := FALSE;
ELSE
Q := TRUE;
END_IF
ELSE
Q := FALSE;
END_IF
END_FUNCTION_BLOCK
+32
View File
@@ -0,0 +1,32 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Общие
Name : TONOF
Type : FUNCTION_BLOCK
Exported : 2026-07-31 11:16:16
===============================================================================
*)
FUNCTION_BLOCK TONOF
(*
TONOF генерирует TON и TOF задержки для переменной IN
Задержка на вход T_ON, задержка на выход T_OFF
*)
VAR_INPUT
IN : BOOL;
T_ON : TIME := T#0s; (* Задержка на вход *)
T_OFF : TIME := T#0s; (* Задержка на выход *)
END_VAR
VAR_OUTPUT
Q : BOOL;
END_VAR
VAR
X : TON;
END_VAR
(*----- IMPLEMENTATION -----*)
X(IN := IN XOR Q, PT := SEL(IN,T_OFF,T_ON));
IF X.Q THEN Q := IN; END_IF;
END_FUNCTION_BLOCK
+60
View File
@@ -0,0 +1,60 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Общие
Name : TP_RST
Type : FUNCTION_BLOCK
Exported : 2026-07-31 11:16:16
===============================================================================
*)
FUNCTION_BLOCK TP_RST
VAR_INPUT
IN: BOOL; // Управляющий сигнал
PT: TIME; // Длительность импульса
RST: BOOL; // Принудительный сброс
END_VAR
VAR_OUTPUT
Q: BOOL; // Выход
ET: TIME; // Прошедшее время импульса
END_VAR
VAR
_ton: TON;
prevIN: BOOL; // Предыдущее состояние IN
timerRunning: BOOL; // TRUE идёт отсчёт импульса
END_VAR
(*----- IMPLEMENTATION -----*)
// Принудительный сброс наивысший приоритет
IF RST THEN
Q := FALSE;
timerRunning := FALSE;
prevIN := FALSE;
_ton(IN := FALSE, PT := PT);
ET := _ton.ET;
RETURN;
END_IF
// Передний фронт IN запускаем импульс (только если не идёт уже)
IF IN AND NOT prevIN AND NOT timerRunning THEN
timerRunning := TRUE;
END_IF
prevIN := IN;
// Таймер вызывается каждый цикл
_ton(IN := timerRunning, PT := PT);
ET := _ton.ET;
// Логика выхода
IF timerRunning THEN
IF _ton.Q THEN
Q := FALSE;
timerRunning := FALSE;
ELSE
Q := TRUE;
END_IF
ELSE
Q := FALSE;
END_IF
END_FUNCTION_BLOCK
@@ -0,0 +1,20 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Преобразование значений
Name : DWORD_OF_2WORD
Type : FUNCTION
Exported : 2026-07-31 11:16:16
===============================================================================
*)
FUNCTION DWORD_OF_2WORD : DWORD
VAR_INPUT
W1 : WORD;
W0 : WORD;
END_VAR
(*----- IMPLEMENTATION -----*)
DWORD_OF_2WORD := SHL(WORD_TO_DWORD(W1),16) OR WORD_TO_DWORD(W0);
END_FUNCTION
@@ -0,0 +1,23 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Преобразование значений
Name : DW_TO_REAL
Type : FUNCTION
Exported : 2026-07-31 11:16:16
===============================================================================
*)
FUNCTION DW_TO_REAL : REAL
VAR_INPUT
X : DWORD;
END_VAR
VAR
pt : POINTER TO REAL;
END_VAR
(*----- IMPLEMENTATION -----*)
pt := ADR(X);
DW_TO_REAL := pt^;
END_FUNCTION
@@ -0,0 +1,29 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Преобразование значений
Name : NTC_termistor_T
Type : FUNCTION
Exported : 2026-07-31 11:16:16
===============================================================================
*)
FUNCTION NTC_termistor_T : REAL
(* Перевод сопротивдления в температуру для NTC-type термистолров *)
VAR_INPUT
Res: REAL; (* Значение сопротивления *)
R0: REAL; (* Сопротивление при T0 *)
T0: REAL; (* T0 *)
b: REAL; (* Beta *)
END_VAR
VAR
END_VAR
(*----- IMPLEMENTATION -----*)
IF (Res <= REAL#0.0) OR (R0 <= REAL#0.0) OR (b = REAL#0.0) THEN
NTC_termistor_T := REAL#0.0;
RETURN;
END_IF;
NTC_termistor_T := (1.0 / ((1.0 / (T0 + 273.15)) + (1.0 / b) * LN(Res / R0))) - 273.15;
END_FUNCTION
@@ -0,0 +1,23 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Преобразование значений
Name : REAL_TO_DW
Type : FUNCTION
Exported : 2026-07-31 11:16:16
===============================================================================
*)
FUNCTION REAL_TO_DW : DWORD
VAR_INPUT
X : REAL;
END_VAR
VAR
pt : POINTER TO DWORD;
END_VAR
(*----- IMPLEMENTATION -----*)
pt := ADR(X);
REAL_TO_DW := pt^;
END_FUNCTION
@@ -0,0 +1,20 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Преобразование значений
Name : WORD_OF_DWORD
Type : FUNCTION
Exported : 2026-07-31 11:16:16
===============================================================================
*)
FUNCTION WORD_OF_DWORD : WORD
VAR_INPUT
in : DWORD;
N : BYTE;
END_VAR
(*----- IMPLEMENTATION -----*)
WORD_OF_DWORD := DWORD_TO_WORD(SHR(in,SHL(n,4)));
END_FUNCTION
+244 -50
View File
@@ -1,7 +1,22 @@
# encoding:utf-8
# export_plc_src.py
# Экспорт POU / GVL / DUT из открытого проекта CODESYS в текстовые файлы
# для хранения в git и работы с Claude Code.
# plc_src/
# ├── PLC1/
# │ ├── POU/...
# │ ├── GVL/...
# │ └── DUT/...
# ├── PLC2/
# │ ├── POU/...
# │ ├── GVL/...
# │ └── DUT/...
# └── _common/
# │ ├── POU/...
# │ ├── GVL/...
# │ └── DUT/..
#
# Методы (METHOD) и действия (ACTION) не создают отдельных файлов --
# они дописываются в конец файла того POU/FB, в котором были созданы
#
# Запуск:
# 1) Внутри CODESYS: Tools -> Scripting -> Execute Script File -> выбрать этот файл
@@ -10,8 +25,6 @@
# --Profile="CODESYS V3.5 SP17 Patch 3" --runscript="export_plc_src.py" ^
# --project="D:\path\to\ТвойПроект.project"
#
# Проверено для установки: CODESYS V3.5 SP17 Patch 3 (32-bit)
#
# По умолчанию папка plc_src создаётся автоматически рядом с файлом .project.
# Если авто-определение не сработает, впиши путь вручную в MANUAL_OUTPUT_ROOT ниже.
@@ -19,9 +32,31 @@ from __future__ import print_function
import os
import codecs
import re
import datetime
PRAGMA_RE = re.compile(r"^\s*\{[^}]*\}\s*")
# Единая метка времени на весь запуск экспорта (одинаковая во всех файлах).
EXPORT_TIMESTAMP = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# Имена служебных узлов дерева, которые не нужно показывать в поле Path
# (сравнение без учёта регистра). Сами узлы всё равно обходятся -- пропускаются
# только их имена при формировании пути.
EXCLUDED_PATH_NAMES = set(["application", "plc logic"])
# Закрывающие ключевые слова, которые CODESYS не включает ни в textual_declaration,
# ни в textual_implementation -- их нужно дописывать вручную, чтобы файл был
# синтаксически полным ST-блоком.
END_KEYWORDS = {
"PROGRAM": "END_PROGRAM",
"FUNCTION_BLOCK": "END_FUNCTION_BLOCK",
"FUNCTION": "END_FUNCTION",
"INTERFACE": "END_INTERFACE",
"METHOD": "END_METHOD",
"PROPERTY": "END_PROPERTY",
"ACTION": "END_ACTION",
}
def strip_leading_pragmas(text):
"""Убирает ведущие блоки вида {attribute 'qualified_only'} перед проверкой ключевого слова."""
@@ -43,8 +78,6 @@ def get_project_file_path(proj):
return val
except Exception:
pass
# Фолбэк: вытащить путь из текстового представления объекта,
# например "Project(Project=0, stPath=C:\...\*.project)"
try:
s = str(proj)
m = re.search(r"stPath=(.*?)\)", s)
@@ -71,37 +104,148 @@ def get_declaration_text(obj):
return None
def get_text(obj, declaration_text):
"""Собирает декларацию + реализацию (если есть) в единый текст."""
text = declaration_text or ""
def has_implementation(obj):
try:
if hasattr(obj, "has_textual_implementation") and obj.has_textual_implementation:
text += "\r\n(*----- IMPLEMENTATION -----*)\r\n"
text += obj.textual_implementation.text
return bool(getattr(obj, "has_textual_implementation", False))
except Exception:
return False
def get_implementation_text(obj):
try:
return obj.textual_implementation.text
except Exception as e:
text += "(* ошибка чтения implementation: %s *)\r\n" % e
return text
return "(* ошибка чтения implementation: %s *)\r\n" % e
def classify(declaration_text):
"""Определяет тип объекта по первому ключевому слову декларации."""
if not declaration_text:
def classify(obj, declaration_text):
"""Определяет тип объекта.
Если есть декларация -- смотрим по ключевому слову (POU/GVL/DUT/METHOD/ACTION).
Если декларации нет, но есть implementation -- это ACTION (у него нет декларации в API)."""
if declaration_text:
stripped = strip_leading_pragmas(declaration_text.strip()).strip().upper()
if stripped.startswith(("PROGRAM", "FUNCTION_BLOCK", "FUNCTION", "INTERFACE")):
return "pou"
if stripped.startswith(("METHOD", "PROPERTY")):
return "method"
if stripped.startswith("VAR_GLOBAL"):
return "gvl"
if stripped.startswith("TYPE"):
return "dut"
return None
stripped = strip_leading_pragmas(declaration_text.strip()).strip().upper()
if stripped.startswith(("PROGRAM", "FUNCTION_BLOCK", "FUNCTION", "INTERFACE", "METHOD", "ACTION", "PROPERTY")):
return "pou"
if stripped.startswith("VAR_GLOBAL"):
return "gvl"
if stripped.startswith("TYPE"):
return "dut"
if has_implementation(obj):
return "action"
return None
def write_file(folder, name, text):
path = os.path.join(folder, name + ".st")
def get_type_label(kind, declaration_text):
"""Возвращает читаемое название типа объекта, например 'FUNCTION_BLOCK', 'METHOD', 'ACTION'."""
if kind == "action":
return "ACTION"
if declaration_text:
stripped = strip_leading_pragmas(declaration_text.strip()).strip().upper()
for kw in ("FUNCTION_BLOCK", "PROGRAM", "FUNCTION", "INTERFACE", "METHOD", "PROPERTY"):
if stripped.startswith(kw):
return kw
if stripped.startswith("VAR_GLOBAL"):
return "GVL"
if stripped.startswith("TYPE"):
return "DUT"
return kind.upper() if kind else "UNKNOWN"
def close_block(text, type_label):
"""Дописывает END_PROGRAM / END_FUNCTION_BLOCK / END_METHOD / END_ACTION и т.п.,
если это применимо к данному типу объекта."""
kw = END_KEYWORDS.get(type_label)
if not kw:
return text
if not text.endswith("\n"):
text += "\r\n"
text += kw + "\r\n"
return text
def build_pou_text(obj, declaration_text, type_label):
"""Декларация + implementation (если есть) + закрывающее END_*."""
text = declaration_text or ""
if has_implementation(obj):
text += "\r\n(*----- IMPLEMENTATION -----*)\r\n"
text += get_implementation_text(obj)
text = close_block(text, type_label)
return text
def build_action_text(obj, name):
"""У Action нет declaration в API -- собираем заголовок 'ACTION Имя :' вручную,
затем тело и закрывающее END_ACTION."""
text = "ACTION %s :\r\n" % name
text += get_implementation_text(obj)
text = close_block(text, "ACTION")
return text
def build_header(project_name, device_name, path_parts, obj_name, type_label):
"""Заголовок-паспорт файла:
(*
===============================================================================
Project : MyProject
Device : PLC
Path : Application/Logic/Motion
Name : FB_Axis
Type : FUNCTION_BLOCK
Exported : 2026-07-30 14:32:18
===============================================================================
*)
"""
rel_path = "/".join(path_parts) if path_parts else "-"
sep = "=" * 79
lines = [
"(*",
sep,
" Project : %s" % project_name,
" Device : %s" % device_name,
" Path : %s" % rel_path,
" Name : %s" % obj_name,
" Type : %s" % type_label,
" Exported : %s" % EXPORT_TIMESTAMP,
sep,
"*)",
"",
"",
]
return "\r\n".join(lines)
def write_new_file(folder, path_parts, obj_name, text, project_name, device_name, type_label):
"""path_parts -- путь БЕЗ имени самого объекта (папки родителей).
Создаёт вложенные папки и записывает файл с заголовком-паспортом."""
sub_parts = [sanitize_folder_name(p) for p in path_parts]
sub_folder = os.path.join(folder, *sub_parts) if sub_parts else folder
if not os.path.exists(sub_folder):
os.makedirs(sub_folder)
file_name = sanitize_folder_name(obj_name)
path = os.path.join(sub_folder, file_name + ".st")
with codecs.open(path, "w", encoding="utf-8") as f:
f.write(build_header(project_name, device_name, path_parts, obj_name, type_label))
f.write(text)
print(" -> %s" % path)
return path
def append_to_file(path, path_parts, obj_name, text, project_name, device_name, type_label):
"""path_parts здесь включает имя родительского POU (т.к. метод/action лежит "внутри" него)."""
if not path or not os.path.exists(path):
return False
with codecs.open(path, "a", encoding="utf-8") as f:
f.write("\r\n")
f.write(build_header(project_name, device_name, path_parts, obj_name, type_label))
f.write(text)
print(" -> дописано в %s (%s: %s)" % (path, type_label, obj_name))
return True
def sanitize_folder_name(name):
@@ -132,34 +276,78 @@ def is_device_node(obj):
return False
def walk(node, dirs, exported):
def walk(node, dirs, exported, project_name, device_name, current_path=None, parent_pou_path=None):
"""current_path -- список имён родителей от корня устройства (без учёта device_name самого),
накапливается по мере рекурсивного спуска по реальному дереву проекта -- поэтому Path
строится из фактической структуры, а не из строки, которую отдаёт CODESYS API."""
if current_path is None:
current_path = []
for child in node.get_children(False):
name = safe_name(child)
decl = get_declaration_text(child)
kind = classify(decl)
kind = classify(child, decl)
type_label = get_type_label(kind, decl)
next_parent_path = parent_pou_path
if name.strip().lower() in EXCLUDED_PATH_NAMES:
child_path = current_path # не добавляем служебное имя в путь
else:
child_path = current_path + [name] # путь для рекурсии в детей ЭТОГО child'а
if kind == "pou":
print("POU: %s" % name)
write_file(dirs["pou"], name, get_text(child, decl))
print("POU: %s/%s" % ("/".join(current_path), name))
text = build_pou_text(child, decl, type_label)
path = write_new_file(dirs["pou"], current_path, name, text,
project_name, device_name, type_label)
exported["pou"] += 1
next_parent_path = path
elif kind == "method":
print("METHOD: %s/%s" % ("/".join(current_path), name))
text = build_pou_text(child, decl, type_label)
if parent_pou_path and append_to_file(parent_pou_path, current_path, name, text,
project_name, device_name, type_label):
exported["method"] += 1
else:
write_new_file(dirs["pou"], current_path, name, text,
project_name, device_name, type_label)
exported["method"] += 1
elif kind == "action":
print("ACTION: %s/%s" % ("/".join(current_path), name))
text = build_action_text(child, name)
if parent_pou_path and append_to_file(parent_pou_path, current_path, name, text,
project_name, device_name, type_label):
exported["action"] += 1
else:
write_new_file(dirs["pou"], current_path, name, text,
project_name, device_name, type_label)
exported["action"] += 1
elif kind == "gvl":
print("GVL: %s" % name)
write_file(dirs["gvl"], name, get_text(child, decl))
print("GVL: %s/%s" % ("/".join(current_path), name))
write_new_file(dirs["gvl"], current_path, name, decl or "",
project_name, device_name, type_label)
exported["gvl"] += 1
elif kind == "dut":
print("DUT: %s" % name)
write_file(dirs["dut"], name, get_text(child, decl))
print("DUT: %s/%s" % ("/".join(current_path), name))
write_new_file(dirs["dut"], current_path, name, decl or "",
project_name, device_name, type_label)
exported["dut"] += 1
else:
exported["skipped"] += 1
# рекурсивно обходим детей (для FB с методами, папок и т.д.)
walk(child, dirs, exported)
# рекурсивно обходим детей (методы/actions FB, вложенные папки и т.д.),
# передавая накопленный путь и (если только что создан POU) новый parent_pou_path
walk(child, dirs, exported, project_name, device_name, child_path, next_parent_path)
# ==== НАСТРОЙКА ====
# Оставь пустой строкой "" чтобы папка plc_src создавалась автоматически
# рядом с файлом .project. Укажи путь вручную только если авто-определение
# Оставить пустой строкой "" чтобы папка plc_src создавалась автоматически
# рядом с файлом .project. Указать путь вручную только если авто-определение
# не сработает (см. вывод скрипта при запуске).
MANUAL_OUTPUT_ROOT = r""
# =====================================================
@@ -169,11 +357,17 @@ proj = projects.primary
if proj is None:
print("ОШИБКА: нет открытого проекта. Открой .project перед запуском скрипта.")
else:
project_file = get_project_file_path(proj)
if project_file:
project_name = os.path.splitext(os.path.basename(project_file))[0]
else:
project_name = "UnknownProject"
if MANUAL_OUTPUT_ROOT:
OUTPUT_ROOT = MANUAL_OUTPUT_ROOT
print("Папка вывода задана вручную: %s" % OUTPUT_ROOT)
else:
project_file = get_project_file_path(proj)
if project_file:
project_dir = os.path.dirname(project_file)
OUTPUT_ROOT = os.path.join(project_dir, "plc_src")
@@ -185,31 +379,31 @@ else:
print("Задай путь вручную в переменной MANUAL_OUTPUT_ROOT и запусти снова.")
if OUTPUT_ROOT:
exported = {"pou": 0, "gvl": 0, "dut": 0, "skipped": 0}
exported = {"pou": 0, "gvl": 0, "dut": 0, "method": 0, "action": 0, "skipped": 0}
print("--- Экспорт в %s ---" % OUTPUT_ROOT)
print("--- Экспорт проекта '%s' в %s ---" % (project_name, OUTPUT_ROOT))
top_children = proj.get_children(False)
device_nodes = [c for c in top_children if is_device_node(c)]
if device_nodes:
for device in device_nodes:
dev_name = sanitize_folder_name(safe_name(device))
print("=== Устройство: %s ===" % dev_name)
dev_dirs = make_dirs(os.path.join(OUTPUT_ROOT, dev_name))
walk(device, dev_dirs, exported)
device_name = safe_name(device)
dev_folder = sanitize_folder_name(device_name)
print("=== Устройство: %s ===" % device_name)
dev_dirs = make_dirs(os.path.join(OUTPUT_ROOT, dev_folder))
walk(device, dev_dirs, exported, project_name, device_name)
# на случай текстовых объектов вне дерева устройств (редко, но на всякий случай)
other_dirs = make_dirs(os.path.join(OUTPUT_ROOT, "_common"))
for child in top_children:
if not is_device_node(child):
walk(child, other_dirs, exported)
walk(child, other_dirs, exported, project_name, "-")
else:
# устройств не нашлось (или is_device не сработал) -- работаем как раньше, без разбивки
print("Устройства верхнего уровня не найдены, экспорт без разбивки по папкам.")
dirs = make_dirs(OUTPUT_ROOT)
walk(proj, dirs, exported)
walk(proj, dirs, exported, project_name, "-")
print("--- Готово: POU=%d, GVL=%d, DUT=%d, пропущено=%d ---" % (
exported["pou"], exported["gvl"], exported["dut"], exported["skipped"]
print("--- Готово: POU=%d, METHOD=%d, ACTION=%d, GVL=%d, DUT=%d, пропущено=%d ---" % (
exported["pou"], exported["method"], exported["action"],
exported["gvl"], exported["dut"], exported["skipped"]
))