Compare commits

...
9 Commits
52 changed files with 2023 additions and 242 deletions
+106 -8
View File
@@ -1,10 +1,108 @@
# git ignore file # ============================================================
#.gitignore # .gitignore для проекта CODESYS 3.5
# ============================================================
# files # ---- Бэкапы / временные файлы проекта ----
*.project~backup~*
# directories ~$*
*.BAK
# build results *.bak
*.opt *.tmp
*.temp
*.autosave
*.~u *.~u
*.left
*.right
*.dmp
# ---- Компиляция / генерируемые артефакты CODESYS ----
*.precompilecache
*.bootinfo
*.bootinfo_guids
*.compileinfo
*.projectcompileinfo
*.SYM
*.sym
*.SDB
*.ci
*.ri
*.DFR
*.dfr
*.ECI
*.eci
_CompileInfo/
*.compiled-library
*.crossreference
*.md5
.PLClogic/
.CoDeSys/
.codesys/
*_cache/
*.git_tmp/
# ---- Boot-приложение ----
*.app
*.crc
Boot Project/
# ---- Списки форсированных переменных ----
*.forcelist
# ---- Логи ----
*.log
# ---- Настройки IDE / пользователя ----
*.opt
.vs/
*.suo
*.user
# ---- Backup-папки CODESYS ----
Backup/
AutoBackup/
**/Backup/
# ---- Библиотеки из репозитория библиотек (не пишутся вручную) ----
*.library-set
/Referenced Libraries/
# ---- ОС / файловый мусор ----
Thumbs.db
desktop.ini
.DS_Store
# ---- Python ----
__pycache__/
*.pyc
.venv/
venv/
# ---- Временные файлы редакторов ----
*~
*.swp
*.swo
# ---- Прочее ----
**/old/*
tools/
libs/
/tmp/*
/.kilo/*
.idea/
.vscode/
.pytest_cache/
.coverage/
.coverage.*
.coverage.xml
.coverage.json
.coverage.yaml
# ============================================================
# НЕ ИГНОРИРОВАТЬ БЕЗ ПРОВЕРКИ:
# *.xml
# *.jpg
# *.jpeg
# *.png
# *.ini
# *.m4
# ============================================================
+164
View File
@@ -0,0 +1,164 @@
# Проект разработки для ПЛК210 (ОВЕН)
## Описание проекта
- **ПЛК:** ОВЕН ПЛК210
- **Среда разработки:** CODESYS 3.5 SP17 Patch 3
- **Основной язык программирования:** ST (Structured Text / МЭК 61131-3)
## Правила работы ИИ-ассистента с этим репозиторием (Strict Rules)
1. **Бинарник — источник истины:** Файлы `.st` в `plc_src/` — это лишь зеркало. Никакие правки в `.st` не считаются примененными, пока пользователь не прогонит скрипт импорта в CODESYS.
2. **Паспорт файла неизменен:** Секция `(* ... *)` в начале `.st` файлов содержит служебные метаданные для Python-скриптов. **Категорически запрещено** удалять или изменять структуры этих заголовков.
3. **Запрет гадания API:** Использовать ТОЛЬКО те функциональные блоки, функции и типы из внешних библиотек, которые явно описаны в `libs/<ИмяБиблиотеки>_API.md`. Если блока нет — запросить сигнатуру у пользователя.
4. **Безопасность циклов:** Никогда не использовать неограниченные циклы `WHILE` или `REPEAT`. Вся циклическая логика должна опираться на естественный цикл задачи CODESYS (Main Task), иначе сработает **Watchdog** ПЛК.
5. **Атомарность:** При модификации файла вносить изменения минимально необходимыми диффами. Не переписывать весь блок без необходимости.
6. **Новые объекты — только с подтверждением:** Создание нового POU/GVL/DUT (объекта, которого раньше не было в проекте) — необратимое изменение структуры проекта при реальном импорте. Не создавай новый файл с новым объектом молча в рамках другой задачи — явно предупреди пользователя и дождись подтверждения.
7. **Не гадать по существующим символам проекта:** Если нужно сослаться на переменную/POU/тип, которые предположительно уже есть в проекте (не создаются заново) — проверь их в `plc_src/` (grep/поиск по имени), а не полагайся на память или правдоподобное совпадение.
---
## Архитектура проекта и структуры данных
### Иерархия вызова программ (Main Task)
- `PLC_PRG`: Главный цикл управления. Содержит только вызов подпрограмм (POU).
### Сеть и протоколы
- **Modbus TCP Master и Slave:** Порты `Ethernet1 / Ethernet2`
- **Modbus RTU:** Интерфейсы `RS-485-1 / RS-485-2`
- Modbus не могут быть экспортированы в .st файл
### Глоссарий сокращений
<!-- Расшифровка предметных сокращений в именах POU/переменных, которые не покрываются Hungarian-нотацией типов ниже. -->
-
---
## Структура репозитория
```text
.
├── <Проект>.project # бинарный проект CODESYS — источник истины для компиляции
├── plc_src/ # текстовый экспорт POU/GVL/DUT — ИМЕННО ЭТИ ФАЙЛЫ читает и правит ИИ-ассистент
│ ├── <Устройство>/
│ │ ├── POU/
│ │ ├── GVL/
│ │ └── DUT/
│ └── _common/
├── tools/
│ ├── export_plc_src.py # скрипт экспорта .project -> plc_src (текст)
│ └── import_plc_src.py # скрипт импорта plc_src (текст) -> .project
├── libs/
│ └── <ИмяБиблиотеки>_API.md # справочник по FB/FUNCTION самописной библиотеки (файл может отсутствовать)
└── Algoritm.md # Основной алгоритм работы (файл может отсутствовать)
```
`plc_src/` генерируется скриптом `tools/export_plc_src.py` из `.project` через CODESYS Scripting API.
Это НЕ исходный код в привычном смысле — это зеркало текущего состояния `.project`, нужное для того, чтобы у git был человекочитаемый дифф, а у ИИ-ассистента — текстовые файлы для правки.
---
## Рабочий цикл правки логики (важно!)
Перенос правок туда-обратно автоматизирован через пару скриптов, но применяется он **не мгновенно** — между правкой `.st`-файла и тем, что окажется в проекте, есть шаг, который делает пользователь руками в CODESYS. Порядок действий:
1. ИИ-ассистент правит `.st`-файл(ы) в `plc_src/...`. Заголовок-паспорт файла (блок `(* ... *)` в начале, с полями Project/Device/Path/Name/Type) **не трогать и не удалять** — по нему `import_plc_src.py` определяет, в какой POU/METHOD/GVL/DUT проекта записать текст.
2. Пользователь открывает проект в CODESYS и запускает `tools/import_plc_src.py`.
3. Компилирует и тестирует проект в CODESYS как обычно.
4. **Перед коммитом перезапускает `tools/export_plc_src.py`**, чтобы `plc_src/` снова точно соответствовал `.project` — иначе дифф в git будет показывать код, которого уже нет в бинарнике (или наоборот — не будет показывать то, что уже есть).
5. Коммитит `.project` и `plc_src/` вместе.
ИИ-ассистент: никогда не считай правку `.st`-файла уже применённой к `.project` — это только предложение изменения. Реальный эффект появится только после того, как пользователь прогонит `import_plc_src.py` (сам, руками) и подтвердит результат.
**ИИ-ассистент никогда не запускает `import_plc_src.py` (и команду CODESYS.exe, вызывающую его) самостоятельно — это исключительно ручной шаг пользователя.**
---
## Особенности формата .st файлов (важно при правке)
- Один `.st`-файл может содержать несколько блоков подряд: основной POU и дописанные METHOD/PROPERTY/ACTION (каждый со своим заголовком-паспортом). Если добавляешь новый METHOD/ACTION в существующий POU — дописывай его в конец того же файла со своим заголовком, а не создавай отдельный файл.
- Для ACTION в заголовке не бывает декларации, только implementation — так и должно быть.
- `END_PROGRAM` / `END_FUNCTION_BLOCK` / `END_METHOD` и т.п. в конце блока — оставляй, их пишет `export_plc_src.py`, а `import_plc_src.py` сам их убирает перед отправкой в API. Не убирай их сам и не убирай случайно при правке.
- Новый POU/GVL/DUT, которого раньше не было — можно создавать новым файлом с правильным заголовком (см. правило 6 выше про обязательное подтверждение); `import_plc_src.py` создаст объект в проекте автоматически (`get_or_create_*`). Но: сигнатуры создания через API в скрипте помечены как версионно-хрупкие — если создание упадёт с ошибкой, это ожидаемо, надо смотреть Tools → Scripting → API нужной версии.
---
## Что нельзя трогать без согласования
<!-- Конкретные POU/цепи/переменные, критичные для безопасности — правки в них только после явного обсуждения, а не как побочный эффект другой задачи. -->
-
---
## Библиотека (внешний проект)
Есть самописная библиотека — отдельный CODESYS-проект, её исходники в этот репозиторий **не включаются**.
- Справочник по публичному API лежит в `libs/<ИмяБиблиотеки>_API.md`
- Там для каждого FB/FUNCTION: сигнатура, VAR_INPUT/VAR_OUTPUT/VAR_IN_OUT с типами, краткое описание
- **ИИ-ассистент: используй только то, что описано в `libs/<ИмяБиблиотеки>_API.md`.** Не придумывай сигнатуры блоков библиотеки, которых нет в справочнике — если нужного блока/параметра там нет, спроси, а не предполагай
---
## Соглашения по коду
### Именование элементов (Hungarian Notation / Prefix System)
**Физические входы/выходы**, привязанные к модулям, — в `VAR_GLOBAL` RAW_IO:
- `RDI_` — Raw Digital Input
- `RDO_` — Raw Digital Output
- `RAI_` — Raw Analog Input
- `RAO_` — Raw Analog Output
**Входы/выходы после присвоения и нормировки** — в `VAR_GLOBAL` IO:
- `DI_` — Digital Input
- `DO_` — Digital Output
- `AI_` — Analog Input
- `AO_` — Analog Output
**Типы POU:**
- `FB_` — Функциональный блок
- `FC_` — Функция
- `DUT_` — Пользовательский тип данных (Struct, Enum)
### Правила форматирования и стиля
1. **Язык:** код, имена переменных и блоков — на **английском языке**; комментарии и документация — на **русском**.
2. **Автоматы состояний (State Machines):** всегда использовать `CASE ... OF`, не заменять его цепочкой `IF ... ELSIF`, если по сути нужен автомат.
3. **Безопасность:**
- деление на ноль и выход за границы массива должны быть явно заблокированы проверками *до* операции.
4. **Таймеры:** использовать только стандартные `TON`, `TOF`, `TP` из библиотеки `Standard`.
5. **Циклы:** без неограниченных `WHILE`/`REPEAT` — см. правило 4 в разделе Strict Rules выше (иначе сработает Watchdog).
---
## Особенности оборудования ПЛК210
- **Retain-память:** Энергонезависимые переменные объявлять строго в секции `VAR RETAIN` или `VAR PERSISTENT`. Использовать экономно.
- **Встроенные входы/выходы:** Использовать таргетные переменные из дерева `ПЛК210 -> Входы/Выходы`. Сырые переменные в RAW_IO, именно их привязку делать через `IO_Mapping`.
- **Флеш-память:** Избегать частой циклической записи файлов во внутреннюю память ПЛК (использовать буферизацию в RAM).
---
## Полезные команды
Запуск экспорта из командной строки (без открытия GUI CODESYS):
```powershell
"C:\Program Files (x86)\CODESYS 3.5.17.30\CODESYS\Common\CODESYS.exe" ^
--Profile="CODESYS V3.5 SP17 Patch 3" --runscript="tools/export_plc_src.py" ^
--project="<путь к .project>"
```
Запуск импорта из командной строки (без открытия GUI CODESYS):
```powershell
"C:\Program Files (x86)\CODESYS 3.5.17.30\CODESYS\Common\CODESYS.exe" ^
--Profile="CODESYS V3.5 SP17 Patch 3" --runscript="tools/import_plc_src.py" ^
--project="<путь к .project>"
```
+668
View File
@@ -0,0 +1,668 @@
# PLC_Library — API Reference (для использования LLM/ИИ)
> Формат: справочник интерфейсов (аналог API-документации). Для каждого POU указаны: тип, путь в дереве проекта, входы/выходы с типами, значениями по умолчанию и назначением, а также краткое описание поведения и особые примечания. Тела реализации (код) намеренно не включены — только то, что нужно, чтобы **вызвать** блок правильно.
>
> Язык: CoDeSys 3.5, Structured Text (ST).
> Скармливайте этот файл ИИ вместо исходников — этого достаточно, чтобы сгенерировать корректные вызовы блоков библиотеки.
---
## Индекс
- [Filters — Фильтры](#filters--фильтры)
- [Fastwel — Модули ввода/вывода](#fastwel--модули-вводавывода)
- [Преобразование значений](#преобразование-значений)
- [Общие — Common](#общие--common)
- [Дискретные сигналы / кнопки / таймеры](#дискретные-сигналы--кнопки--таймеры)
- [Пороговые сигналы / гистерезис](#пороговые-сигналы--гистерезис)
- [Статус аналоговых сигналов](#статус-аналоговых-сигналов)
- [Диагностика связи](#диагностика-связи)
- [Регулирование](#регулирование)
- [Структуры данных (очереди/стеки)](#структуры-данных-очередистеки)
- [Иное](#иное)
- [Structs](#structs)
- [Общие правила / соглашения библиотеки](#общие-правила--соглашения-библиотеки)
---
## Filters — Фильтры
`Path: Filters`
### ExpRunAverage — FUNCTION : REAL
Экспоненциальное скользящее среднее с постоянным коэффициентом.
```
FUNCTION ExpRunAverage : REAL
VAR_INPUT
In : REAL; // входное значение
Old : REAL; // предыдущее значение EMA (нужно хранить самому вызывающему)
K : REAL; // коэффициент сглаживания [0..1], чем больше — тем быстрее реакция
END_VAR
```
Формула: `Out := (1-K)*Old + K*In`. Без внутреннего состояния — вызывающий код сам хранит `Old` между циклами.
### ExpRunAverageAdaptive — FUNCTION : REAL
Как `ExpRunAverage`, но коэффициент переключается на увеличенный `K2`, если скачок входа больше `Delta` (быстрая реакция на резкие изменения).
```
FUNCTION ExpRunAverageAdaptive : REAL
VAR_INPUT
In : REAL;
Old : REAL;
K1 : REAL; // обычный коэффициент
Delta : REAL; // порог |In-Old| для переключения на K2
K2 : REAL; // ускоренный коэффициент (обычно K2 > K1)
END_VAR
```
### RunMiddleArifm — FUNCTION_BLOCK
Скользящее среднее арифметическое по кольцевому буферу (собственное состояние, хранит буфер сам).
```
FUNCTION_BLOCK RunMiddleArifm
VAR_INPUT
In : REAL; // входное значение
N : INT := 32; // размер окна усреднения, диапазон 0..32
RST : BOOL; // TRUE => переинициализация буфера текущим In
END_VAR
VAR_OUTPUT
Y : REAL; // результат — скользящее среднее
END_VAR
```
Особенность: при `RST` или на первом скане буфер заполняется значением `In`, а `Y := In`.
### MultipleClick — FUNCTION_BLOCK
Детектор серии кликов кнопки (одиночный/двойной/тройной).
```
FUNCTION_BLOCK MultipleClick
VAR_INPUT
In : BOOL; // сигнал кнопки
TimeToWait : TIME := T#500MS; // макс. пауза между нажатиями, после — фиксация результата
TimeRelease : TIME := T#0S; // время удержания результата на выходах
END_VAR
VAR_OUTPUT
Click1 : BOOL; // TRUE — зафиксирован 1 клик
Click2 : BOOL; // TRUE — зафиксировано 2 клика
Click3 : BOOL; // TRUE — зафиксировано 3 клика
Click : WORD; // число нажатий (0, если результат уже "отпущен")
END_VAR
```
---
## Fastwel — Модули ввода/вывода
`Path: Fastwel`
Преобразование "сырых" значений АЦП/кодов модулей Fastwel в физические единицы и обратно.
### AIM721_AI — FUNCTION : REAL
```
FUNCTION AIM721_AI : REAL
VAR_INPUT
ADC : DINT; // код АЦП
END_VAR
```
АЦП → 0…20 мА (коэффициент модуля AIM721).
### AIM722_AI — FUNCTION : REAL
```
FUNCTION AIM722_AI : REAL
VAR_INPUT
ADC : DINT;
END_VAR
```
АЦП → 0…20 мА (коэффициент модуля AIM722, отличается от AIM721).
### AIM723_AI — FUNCTION : REAL
```
FUNCTION AIM723_AI : REAL
VAR_INPUT
ADC : DINT;
END_VAR
```
АЦП → 4…20 мА.
### AIM726_AI — FUNCTION : REAL
```
FUNCTION AIM726_AI : REAL
VAR_INPUT
ADC : DWORD; // код АЦП (DWORD!)
END_VAR
```
АЦП → 0…40 В.
### AIM727_AI — FUNCTION : REAL
```
FUNCTION AIM727_AI : REAL
VAR_INPUT
ADC : DINT; // код АЦП (DINT — отличие от AIM726)
END_VAR
```
АЦП → 0…40 В.
### AIM791_AI — FUNCTION : REAL
```
FUNCTION AIM791_AI : REAL
VAR_INPUT
ADC : WORD;
RANGE : BYTE := 0; // 0=0..5мА; 1=0..20мА; 2=4..20мА
END_VAR
```
Универсальный вход, диапазон выбирается `RANGE`. Значение по умолчанию `RANGE` вне 0/1/2 → возвращает `0.0`.
### AIM730_AO — FUNCTION : WORD
```
FUNCTION AIM730_AO : WORD
VAR_INPUT
DAC : REAL; // миллиамперы
RANGE : BYTE; // 0=0..20мА; 1=4..20мА
END_VAR
```
мА → код ЦАП. При `RANGE=1` и `DAC<4` принудительно возвращает код, соответствующий 4 мА.
### AIM731_AO — FUNCTION : WORD
```
FUNCTION AIM731_AO : WORD
VAR_INPUT
DAC : REAL; // вольты
RANGE : BYTE; // 0 = -10..+10В; 1 = 0..+10В
END_VAR
```
Вольты → код ЦАП модуля AIM731.
### DIM764_DI — FUNCTION : REAL
```
FUNCTION DIM764_DI : REAL
VAR_INPUT
IN : DWORD; // период/интервал от модуля, единицы АЦП
RANGE : BYTE; // 0=Period; 1=Interval
END_VAR
```
Возвращает частоту в Гц. При `IN=0` и `RANGE=0` — возвращает 0 (защита от деления на 0).
### AIM725_Status — FUNCTION_BLOCK
Формирование статуса аналогового канала с гистерезисом/задержками + диагностика по битам статуса модуля.
```
FUNCTION_BLOCK AIM725_Status
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;
time_no_signal : TIME := T#0S;
Modul_status : BYTE; // байт диагностики модуля АЦП (побитовые флаги ошибок канала)
Chanel_num : BYTE; // номер канала модуля (1 или 2) — выбирает, какие биты Modul_status проверять
END_VAR
VAR_OUTPUT
Status : AnalogValueState; // итоговый статус (см. раздел Structs)
T_mod : INT; // Input, приведённое к INT; -500 при no_signal
END_VAR
```
Приоритет статусов: `no_signal > fail_hi > fail_low > warn_hi > warn_low > normal`.
`no_signal` срабатывает если: `Input <= -200`, либо соответствующие биты `Modul_status` (зависящие от `Chanel_num`), либо биты 2/3 (общие ошибки модуля).
---
## Преобразование значений
`Path: Преобразование значений`
### DW_TO_REAL — FUNCTION : REAL
```
FUNCTION DW_TO_REAL : REAL
VAR_INPUT
X : DWORD;
END_VAR
```
**Побитовая** реинтерпретация DWORD как REAL через указатель (`ADR`) — НЕ конвертация значения. Платформозависимо.
### REAL_TO_DW — FUNCTION : DWORD
```
FUNCTION REAL_TO_DW : DWORD
VAR_INPUT
X : REAL;
END_VAR
```
Обратная операция к `DW_TO_REAL`. Побитовая реинтерпретация.
### DWORD_OF_2WORD — FUNCTION : DWORD
```
FUNCTION DWORD_OF_2WORD : DWORD
VAR_INPUT
W1 : WORD; // старшее слово, биты 16..31 результата
W0 : WORD; // младшее слово, биты 0..15 результата
END_VAR
```
### WORD_OF_DWORD — FUNCTION : WORD
```
FUNCTION WORD_OF_DWORD : WORD
VAR_INPUT
in : DWORD; // исходное значение
N : BYTE; // 0 = младшее слово (биты 0..15), 1 = старшее (биты 16..31)
END_VAR
```
### Norm_real — FUNCTION : REAL
Линейная нормировка (масштабирование по двум опорным точкам).
```
FUNCTION Norm_real : REAL
VAR_INPUT
X : REAL; // входное (ненормированное) значение
X1 : REAL; // точка 1, вход
Y1 : REAL; // точка 1, выход
X2 : REAL; // точка 2, вход
Y2 : REAL; // точка 2, выход
END_VAR
```
Если `X2=X1` — возвращает `Y1` (защита от деления на 0).
### NTC_termistor_T — FUNCTION : REAL
Перевод сопротивления NTC-термистора в температуру (уравнение Beta).
```
FUNCTION NTC_termistor_T : REAL
VAR_INPUT
Res : REAL; // измеренное сопротивление
R0 : REAL; // сопротивление при температуре T0
T0 : REAL; // опорная температура, °C
b : REAL; // коэффициент Beta термистора
END_VAR
```
Возвращает температуру в °C. При `Res<=0`, `R0<=0` или `b=0` возвращает `0.0`.
---
## Общие — Common
`Path: Общие`
### Дискретные сигналы / кнопки / таймеры
#### Button_acceleration — FUNCTION_BLOCK
Ускоренное изменение значения при удержании кнопки (для регулировки уставок и т.п.).
```
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_D_1
T_W_2 : TIME := T#0S; // время до начала "длинного удержания"
T_D_2 : TIME := T#200MS; // шаг приращения при длинном удержании
D_2 : REAL := 0; // величина приращения на шаге T_D_2
Zeroes : BOOL := FALSE; // если TRUE — обнулять Out, когда |Out| < |D_0|
END_VAR
VAR_IN_OUT
Out : REAL; // изменяемое значение (передаётся и изменяется по ссылке!)
END_VAR
```
⚠️ Нет `VAR_OUTPUT` — результат только через `VAR_IN_OUT Out`.
#### Pulse_Switch — FUNCTION_BLOCK
```
FUNCTION_BLOCK Pulse_Switch
VAR_INPUT
Switch_ctrl : BOOL;
END_VAR
VAR_OUTPUT
Switch_on : BOOL; // импульс 500мс при фронте 0->1
Switch_off : BOOL; // импульс 500мс при фронте 1->0
END_VAR
```
#### TONOF — FUNCTION_BLOCK
Комбинированная задержка включения (TON) + выключения (TOF) для одного сигнала.
```
FUNCTION_BLOCK TONOF
VAR_INPUT
IN : BOOL;
T_ON : TIME := T#0S; // задержка появления TRUE на Q
T_OFF : TIME := T#0S; // задержка снятия TRUE с Q
END_VAR
VAR_OUTPUT
Q : BOOL;
END_VAR
```
#### TOF_RST — FUNCTION_BLOCK
Таймер выключения (TOF) с принудительным сбросом.
```
FUNCTION_BLOCK TOF_RST
VAR_INPUT
IN : BOOL;
PT : TIME; // задержка выключения
RST : BOOL; // немедленный сброс Q и таймера, приоритет над IN
END_VAR
VAR_OUTPUT
Q : BOOL;
ET : TIME; // текущее время таймера
END_VAR
```
#### TP_RST — FUNCTION_BLOCK
Одиночный импульс (TP) с принудительным сбросом.
```
FUNCTION_BLOCK TP_RST
VAR_INPUT
IN : BOOL; // фронт 0->1 запускает импульс (повторный фронт во время импульса игнорируется)
PT : TIME; // длительность импульса
RST : BOOL; // немедленно прерывает импульс
END_VAR
VAR_OUTPUT
Q : BOOL;
ET : TIME;
END_VAR
```
#### BLINK_LMP — FUNCTION_BLOCK
Управление лампой по коду состояния.
```
FUNCTION_BLOCK BLINK_LMP
VAR_INPUT
State : WORD := 0; // 0=выкл; 1=вкл; 2/3/4=мигание с частотой 1/2/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
```
#### BUZZER_CTRL — FUNCTION_BLOCK
Управление звонком/зуммером по коду состояния с отключением звука (временным и перманентным).
```
FUNCTION_BLOCK BUZZER_CTRL
VAR_INPUT
State : WORD := 0; // 0=выкл;1=вкл;2/3/4=мигание частота1/2/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; // время удержания Sound_off для перманентного откл.
END_VAR
VAR_OUTPUT
BUZZER : BOOL;
END_VAR
```
#### CONFIRMATION — FUNCTION_BLOCK
Логика квитирования аварии (лампы + звонок).
```
FUNCTION_BLOCK CONFIRMATION
VAR_INPUT
Alarm : BOOL; // авария активна
ControlState_On : BOOL; // управление ведётся с данного поста
Sound_Off : BOOL; // сигнал снятия звука
Confirmation : BOOL; // сигнал квитирования
END_VAR
VAR_OUTPUT
Lamp_Alarm_C : WORD; // код для лампы аварии (0/1/2)
Lamp_Confirmation_C : WORD; // код для лампы квитирования (0/1)
Buzzer_C : WORD; // код для зуммера (0/2)
END_VAR
```
Коды ламп совместимы с входом `State`/`Inp_BL_*` блоков `BLINK_LMP`/`BUZZER_CTRL` (2 = "мигание частота 1").
### Пороговые сигналы / гистерезис
#### HYST — FUNCTION_BLOCK
Гистерезисный компаратор с раздельными порогами включения/выключения (прямая и обратная логика).
```
FUNCTION_BLOCK HYST
VAR_INPUT
In : REAL; // контролируемое значение
ON : REAL; // порог срабатывания
OFF : REAL; // порог возврата
END_VAR
VAR_OUTPUT
Q : BOOL; // состояние компаратора
win : BOOL; // TRUE — значение внутри зоны гистерезиса (неопределённая зона)
END_VAR
```
Если `ON >= OFF` — прямая логика (растущий сигнал включает); если `ON < OFF` — обратная (падающий сигнал включает).
#### ALARM_1 — FUNCTION_BLOCK
Контроль выхода `X` за диапазон `[LO_1..HI_1]` с гистерезисом.
```
FUNCTION_BLOCK ALARM_1
VAR_INPUT
X : REAL;
LO_1 : REAL;
HI_1 : REAL;
HYS : REAL; // полный гистерезис (берётся ±HYS/2 от границы)
END_VAR
VAR_OUTPUT
Q1_LO : BOOL; // X ниже LO_1
Q1_HI : BOOL; // X выше HI_1
END_VAR
```
#### ALARM_2 — FUNCTION_BLOCK
То же, для двух независимых диапазонов одновременно.
```
FUNCTION_BLOCK ALARM_2
VAR_INPUT
X : REAL;
LO_1, HI_1 : REAL;
LO_2, HI_2 : REAL;
HYS : REAL;
END_VAR
VAR_OUTPUT
Q1_LO, Q1_HI : BOOL;
Q2_LO, Q2_HI : BOOL;
END_VAR
```
#### ALARM_3 — FUNCTION_BLOCK
То же, для трёх диапазонов одновременно.
```
FUNCTION_BLOCK ALARM_3
VAR_INPUT
X : REAL;
LO_1, HI_1 : REAL;
LO_2, HI_2 : REAL;
LO_3, HI_3 : REAL;
HYS : REAL;
END_VAR
VAR_OUTPUT
Q1_LO, Q1_HI : BOOL;
Q2_LO, Q2_HI : BOOL;
Q3_LO, Q3_HI : BOOL;
END_VAR
```
### Статус аналоговых сигналов
#### STATUS_ANALOG_M — FUNCTION_BLOCK
Статус аналогового значения REAL с гистерезисом возврата и задержками срабатывания по каждому уровню; поддерживает принудительную установку статуса извне.
```
FUNCTION_BLOCK STATUS_ANALOG_M
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; // принудительно выставить статус fail_hi
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
```
Приоритет: `no_signal > fail_hi > fail_low > warn_hi > warn_low > normal`. Возврат из состояния — при пересечении порога `± delta_*`, срабатывание — с задержкой `time_*` (TON).
#### STATUS_ANALOG_M_I — FUNCTION_BLOCK
Полный аналог `STATUS_ANALOG_M`, но все пороги/входное значение типа **INT**, а не REAL. Сигнатура идентична по составу переменных (см. выше), только типы `Input`, `fail_hi`, `delta_fail_hi`, `warn_hi`, `delta_warn_hi`, `warn_low`, `delta_warn_low`, `fail_low`, `delta_fail_low`, `no_signal`, `delta_no_signal``INT` (диапазон по умолчанию ±32767/-32768).
### Диагностика связи
#### Status_SU_Connect — FUNCTION_BLOCK
Контроль связи по "сердцебиению" (сигнал должен периодически менять состояние).
```
FUNCTION_BLOCK Status_SU_Connect
VAR_INPUT
HeartBit : BOOL; // меняющийся сигнал от удалённого устройства
T_Pulse : TIME; // макс. допустимая длительность одного состояния HeartBit
END_VAR
VAR_OUTPUT
ERROR_CONNECT : BOOL; // TRUE — HeartBit не менялся дольше T_Pulse
END_VAR
```
#### Status_Connect_Counter — FUNCTION_BLOCK
Контроль связи по инкрементируемому счётчику от удалённого устройства.
```
FUNCTION_BLOCK Status_Connect_Counter
VAR_INPUT
Counter : WORD; // счётчик, инкрементируемый удалённым устройством
T_Error : TIME; // время без изменений счётчика для фиксации обрыва связи
END_VAR
VAR_OUTPUT
CounterIsChanged : BOOL; // TRUE в цикле, где счётчик изменился
ERROR_CONNECT : BOOL; // TRUE — счётчик не менялся дольше T_Error
END_VAR
```
### Регулирование
#### PIDcontrol — FUNCTION_BLOCK
ПИД-регулятор с фильтрацией D-составляющей, зоной нечувствительности, антивиндапом и ручным режимом.
```
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; // коэффициент фильтра D-части (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; // TRUE — ручной режим
Reset : BOOL := FALSE; // сброс интегратора и D-части
AntiWindup : BOOL := TRUE; // включить компенсацию накопления интегратора
END_VAR
VAR_OUTPUT
Out : REAL := 0.0; // выходной сигнал регулятора
Err : REAL := 0.0; // текущая ошибка регулирования (Setpoint-ProcessVariable)
OutLimited : BOOL := FALSE; // TRUE — выход ограничен (насыщение) или некорректны параметры
END_VAR
```
Если `OutMin>=OutMax`, `DBmin>=DBmax` или `Ts<=0` — блок сразу выставляет `Out:=0`, `OutLimited:=TRUE` и выходит.
### Структуры данных (очереди/стеки)
Общая сигнатура для всех 4 блоков (`QUEUE_I16`, `QUEUE_I32`, `STACK_I16`, `STACK_I32`) — идентична, отличаются только ёмкость и дисциплина (FIFO/LIFO):
```
FUNCTION_BLOCK <Имя>
VAR_INPUT
Din : INT; // значение для записи
E : BOOL := TRUE; // включение блока (при FALSE — операции не выполняются)
POP : BOOL; // извлечь элемент
PUSH: BOOL; // добавить элемент
RST : BOOL; // сброс (очистка)
END_VAR
VAR_OUTPUT
Dout : INT; // извлечённое значение (при POP)
EMPTY : BOOL := TRUE; // TRUE — пусто
FULL : BOOL; // TRUE — заполнено (достигнут предел)
END_VAR
```
| Имя | Дисциплина | Ёмкость |
|---|---|---|
| `QUEUE_I16` | FIFO (очередь) | 16 элементов INT |
| `QUEUE_I32` | FIFO (очередь) | 32 элемента INT |
| `STACK_I16` | LIFO (стек) | 16 элементов INT |
| `STACK_I32` | LIFO (стек) | 32 элемента INT |
Примечание: при одновременном `PUSH` и `POP` в одном скане — оба выполняются (POP обрабатывается первым в теле, затем PUSH).
---
## Иное
`Path: Иное`
### DFS — PROGRAM
Обход графа в глубину (Depth-First Search) по матрице смежности фиксированного размера.
```
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 CONSTANT
n : INT := 12; // фиксированное количество вершин графа
END_VAR
```
⚠️ Это **PROGRAM**, а не FUNCTION_BLOCK — не инстанцируется через `:` в декларации переменной, а копируется целиком в проект пользователя (как указано в исходном комментарии `Копировать в свою программу`). Использует `STACK_I16` внутри. Результат — заполненный массив `connected^[1..k-1]` с номерами вершин, достижимых из `Start`.
---
## Structs
`Path: Structs`
### AnalogValueState — DUT (перечисление)
```
{attribute 'qualified_only'}
{attribute 'strict'}
TYPE AnalogValueState :
(
undefined := INT#0,
normal := INT#1,
warn_low := INT#2,
fail_low := INT#3,
warn_hi := INT#4,
fail_hi := INT#5,
no_signal := -INT#1
) := undefined;
END_TYPE
```
`qualified_only` — обращаться только как `AnalogValueState.normal` и т.п. (без квалификатора не компилируется).
Используется как тип выхода `Status` в `AIM725_Status`, `STATUS_ANALOG_M`, `STATUS_ANALOG_M_I`.
---
## Общие правила / соглашения библиотеки
1. **Приоритет статусов** во всех блоках статуса аналоговых сигналов (`AIM725_Status`, `STATUS_ANALOG_M`, `STATUS_ANALOG_M_I`) одинаков:
`no_signal > fail_hi > fail_low > warn_hi > warn_low > normal`.
2. **Гистерезис срабатывания/возврата**: срабатывание уровня — по достижению порога с задержкой `time_*` (аналог TON), возврат — при пересечении `порог ± delta_*` (без задержки).
3. Блоки `TOF_RST`/`TP_RST` — это стандартные МЭК TOF/TP с добавленным принудительным входом `RST` (сброс приоритетнее `IN`).
4. `DW_TO_REAL`/`REAL_TO_DW` — это **не** преобразование значения (не `DWORD_TO_REAL`), а побитовая реинтерпретация памяти через указатель. Использовать только когда явно нужна побитовая переупаковка (например, чтение REAL-регистра Modbus, пришедшего как два WORD → DWORD → REAL).
5. `QUEUE_I16`/`STACK_I16` — фиксированный размер 16 (`n:=15` внутри), `QUEUE_I32`/`STACK_I32` — размер 32. Чтобы изменить ёмкость — нужно менять исходник (константу `n` и объявление массива).
6. `DFS` — не библиотечный вызываемый блок, а шаблон программы под конкретный размер графа (`n:=12`); предназначен для копирования и адаптации в проекте пользователя.
7. Все входы с `VAR_IN_OUT` (только у `Button_acceleration.Out`) передаются **по ссылке** — вызывающая сторона должна сама объявить и инициализировать переменную, изменения применяются напрямую.
8. Единицы времени — везде тип `TIME` (`T#...`), единицы физических величин указаны в комментариях к каждому входу/выходу (мА, В, Гц, °C, Ом, °C и т.д.).
Binary file not shown.
+138 -1
View File
@@ -1,3 +1,140 @@
# PLC_Library # PLC_Library
Моя библиотека для CoDeSys 3.5 Библиотека функций (FUNCTION) и функциональных блоков (FUNCTION_BLOCK) на языке **Structured Text (ST)** для CoDeSys 3.5.
Библиотека содержит фильтры сигналов, обработку кнопок и дискретных сигналов, работу со статусами и авариями аналоговых величин, преобразование типов данных, драйверы модулей Fastwel и вспомогательные структуры/алгоритмы.
## Структура
```text
PLC_Library/
├── Filters/ — фильтрация и усреднение сигналов
├── Fastwel/ — преобразование сигналов модулей Fastwel (АЦП/ЦАП)
├── Преобразование значений/ — преобразование типов данных
├── Общие/ — общие блоки (кнопки, таймеры, статусы, очереди/стеки, ПИД, сигнализация)
├── Иное/ — прочие алгоритмы
└── Structs/ — пользовательские типы данных
```
---
## Filters — Фильтры
| Имя | Тип | Описание |
| --- | --- | --- |
| `ExpRunAverage` | FUNCTION (REAL) | Экспоненциальное скользящее среднее с постоянным коэффициентом `K`. |
| `ExpRunAverageAdaptive` | FUNCTION (REAL) | Экспоненциальное скользящее среднее с адаптивным коэффициентом: если изменение входа превышает `Delta`, используется увеличенный коэффициент `K2` вместо `K1` (быстрая реакция на резкие изменения). |
| `RunMiddleArifm` | FUNCTION_BLOCK | Скользящее среднее арифметическое по кольцевому буферу размером до 32 отсчётов (`N`), с возможностью сброса/переинициализации (`RST`). |
| `MultipleClick` | FUNCTION_BLOCK | Детектор серии нажатий кнопки (одиночный/двойной/тройной клик) по паузе между нажатиями `TimeToWait`, с удержанием результата на `TimeRelease`. |
---
## Fastwel — Модули ввода/вывода Fastwel
Функции перевода "сырых" значений АЦП/кодов модулей Fastwel в физические единицы (мА, В, Гц) и обратно (ЦАП), а также формирование статуса аналогового канала.
| Имя | Тип | Описание |
| --- | --- | --- |
| `AIM721_AI` | FUNCTION (REAL) | АЦП → 0…20 мА. |
| `AIM722_AI` | FUNCTION (REAL) | АЦП → 0…20 мА (свой коэффициент модуля). |
| `AIM723_AI` | FUNCTION (REAL) | АЦП → 4…20 мА. |
| `AIM726_AI` | FUNCTION (REAL) | АЦП (DWORD) → 0…40 В. |
| `AIM727_AI` | FUNCTION (REAL) | АЦП (DINT) → 0…40 В. |
| `AIM791_AI` | FUNCTION (REAL) | АЦП → 0…5 мА / 0…20 мА / 4…20 мА (выбор диапазона через `RANGE`). |
| `AIM730_AO` | FUNCTION (WORD) | мА (0…20 или 4…20, выбор `RANGE`) → код ЦАП модуля AIM730. |
| `AIM731_AO` | FUNCTION (WORD) | В (±10 или 0…10, выбор `RANGE`) → код ЦАП модуля AIM731. |
| `DIM764_DI` | FUNCTION (REAL) | Период/интервал импульсного входа модуля → частота в Гц (выбор режима `RANGE`). |
| `AIM725_Status` | FUNCTION_BLOCK | Формирование статуса аналогового канала (`AnalogValueState`): аварии/предупреждения по верхнему и нижнему порогам с гистерезисом и выдержками времени, а также диагностика обрыва связи по битам `Modul_status` конкретного канала (`Chanel_num`). Дополнительно выдаёт `T_mod` — значение, приведённое к INT. |
---
## Преобразование значений — Conversions
| Имя | Тип | Описание |
| --- | --- | --- |
| `DW_TO_REAL` | FUNCTION (REAL) | Побитовая реинтерпретация DWORD как REAL (без изменения значения, через указатель). |
| `REAL_TO_DW` | FUNCTION (DWORD) | Побитовая реинтерпретация REAL как DWORD (обратное преобразование). |
| `DWORD_OF_2WORD` | FUNCTION (DWORD) | Сборка DWORD из двух WORD (старшее + младшее слово). |
| `WORD_OF_DWORD` | FUNCTION (WORD) | Извлечение младшего или старшего слова (WORD) из DWORD по флагу `N`. |
| `Norm_real` | FUNCTION (REAL) | Линейная нормировка значения `X` по двум точкам `(X1,Y1)``(X2,Y2)` (масштабирование диапазона). |
| `NTC_termistor_T` | FUNCTION (REAL) | Перевод сопротивления NTC-термистора в температуру по уравнению Бета (`R0`, `T0`, `b`). |
---
## Общие — Common
### Дискретные сигналы / кнопки / таймеры
| Имя | Тип | Описание |
| --- | --- | --- |
| `Button_acceleration` | FUNCTION_BLOCK | Ускоренное изменение значения (`Out`, VAR_IN_OUT) при удержании кнопки: одиночный клик даёт приращение `D_0`, короткое удержание — приращения `D_1` с шагом `T_D_1` после `T_W_1`, длительное удержание — `D_2` с шагом `T_D_2` после `T_W_2`. Есть опция обнуления `Zeroes`. |
| `Pulse_Switch` | FUNCTION_BLOCK | Формирует импульсы по 500 мс по переднему (`Switch_on`) и заднему (`Switch_off`) фронтам управляющего сигнала. |
| `TONOF` | FUNCTION_BLOCK | Комбинированная задержка включения/выключения (TON + TOF) для одного сигнала. |
| `TOF_RST` | FUNCTION_BLOCK | Таймер выключения (TOF) с принудительным сбросом `RST`. |
| `TP_RST` | FUNCTION_BLOCK | Одиночный импульс (TP) с принудительным сбросом `RST`; повторный фронт во время импульса игнорируется. |
| `BLINK_LMP` | FUNCTION_BLOCK | Управление лампой по коду состояния `State` (выкл/вкл/мигание с одной из 3 частот, задаваемых внешними пульсарами). |
| `BUZZER_CTRL` | FUNCTION_BLOCK | Управление звонком/зуммером по коду состояния, с возможностью временного и перманентного отключения звука (`Sound_off`, `PermanentOFF_Enable`). |
| `CONFIRMATION` | FUNCTION_BLOCK | Логика квитирования аварии: формирует сигналы ламп аварии/квитирования и управления звонком. |
### Пороговые сигналы / гистерезис
| Имя | Тип | Описание |
| --- | --- | --- |
| `HYST` | FUNCTION_BLOCK | Гистерезисный компаратор с раздельными порогами включения (`ON`) и выключения (`OFF`); поддерживает прямую и обратную логику. |
| `ALARM_1` | FUNCTION_BLOCK | Контроль значения `X` на выход за один диапазон `[LO_1…HI_1]` с гистерезисом `HYS`. |
| `ALARM_2` | FUNCTION_BLOCK | То же для двух диапазонов одновременно. |
| `ALARM_3` | FUNCTION_BLOCK | То же для трёх диапазонов одновременно. |
### Статус аналоговых сигналов
| Имя | Тип | Описание |
| --- | --- | --- |
| `STATUS_ANALOG_M` | FUNCTION_BLOCK | Формирование статуса аналогового значения REAL (`AnalogValueState`) с гистерезисом возврата и задержками срабатывания по каждому из уровней (fail/warn hi/low, no_signal); поддерживает принудительную установку каждого статуса извне. |
| `STATUS_ANALOG_M_I` | FUNCTION_BLOCK | Аналог `STATUS_ANALOG_M`, но для входа типа INT. |
### Диагностика связи
| Имя | Тип | Описание |
| --- | --- | --- |
| `Status_SU_Connect` | FUNCTION_BLOCK | Контроль связи по "сердцебиению" (`HeartBit`): авария, если сигнал не меняет состояние дольше `T_Pulse`. |
| `Status_Connect_Counter` | FUNCTION_BLOCK | Контроль связи по счётчику `Counter` от удалённого устройства: авария, если счётчик не меняется дольше `T_Error`. |
### Регулирование
| Имя | Тип | Описание |
| --- | --- | --- |
| `PIDcontrol` | FUNCTION_BLOCK | ПИД-регулятор с фильтрацией дифференциальной составляющей, зоной нечувствительности (`DBmin/DBmax`), ограничением выхода, антивиндапом и ручным режимом (`ManOn`/`Manual`). |
### Структуры данных
| Имя | Тип | Описание |
| --- | --- | --- |
| `QUEUE_I16` | FUNCTION_BLOCK | Кольцевая очередь FIFO на 16 элементов типа INT (`PUSH`/`POP`, флаги `EMPTY`/`FULL`, сброс `RST`). |
| `QUEUE_I32` | FUNCTION_BLOCK | Кольцевая очередь FIFO на 32 элемента типа INT (`PUSH`/`POP`, флаги `EMPTY`/`FULL`, сброс `RST`). |
| `STACK_I16` | FUNCTION_BLOCK | Стек LIFO на 16 элементов типа INT (`PUSH`/`POP`, флаги `EMPTY`/`FULL`, сброс `RST`). |
| `STACK_I32` | FUNCTION_BLOCK | Стек LIFO на 32 элемента типа INT (`PUSH`/`POP`, флаги `EMPTY`/`FULL`, сброс `RST`). |
---
## Иное — Other
| Имя | Тип | Описание |
| --- | --- | --- |
| `DFS` | PROGRAM | Обход графа в глубину (Depth-First Search) по матрице смежности `graph` фиксированного размера (12 вершин); использует `STACK_I16`. Возвращает список посещённых из `Start` вершин в массиве `connected`. Предназначен для копирования в пользовательскую программу. |
---
## Structs — Пользовательские типы
| Имя | Тип | Описание |
| --- | --- | --- |
| `AnalogValueState` | DUT (перечисление, `qualified_only`, `strict`) | Статус аналогового значения: `undefined`(0), `normal`(1), `warn_low`(2), `fail_low`(3), `warn_hi`(4), `fail_hi`(5), `no_signal`(-1). Используется в `AIM725_Status`, `STATUS_ANALOG_M`, `STATUS_ANALOG_M_I`. |
---
## Замечания по использованию
- Функции `DW_TO_REAL` / `REAL_TO_DW` выполняют **побитовую** реинтерпретацию через указатель (`ADR`), а не преобразование значения — использовать осторожно, с учётом платформозависимости работы с указателями.
- Блоки `STATUS_ANALOG_M[_I]` и `AIM725_Status` используют единый тип `AnalogValueState` и схожую логику приоритетов статусов: `no_signal` > `fail_hi` > `fail_low` > `warn_hi` > `warn_low` > `normal`.
- `TOF_RST` и `TP_RST` — версии стандартных МЭК-таймеров TOF/TP с добавленным входом принудительного сброса `RST`.
- `QUEUE_I16` и `STACK_I16` имеют фиксированный размер 16 элементов (константа `n:=15`); для изменения размера нужно менять константу `n` и размер массива.
- `DFS` рассчитан на графы фиксированного размера (константа `n:=12`) и предназначен для копирования кода в пользовательскую программу, а не для использования "как есть" в виде библиотечного вызова.
Binary file not shown.
Binary file not shown.
-1
View File
@@ -5,7 +5,6 @@
Path : Structs Path : Structs
Name : AnalogValueState Name : AnalogValueState
Type : DUT Type : DUT
Exported : 2026-07-31 11:16:16
=============================================================================== ===============================================================================
*) *)
+2 -3
View File
@@ -5,16 +5,15 @@
Path : Fastwel Path : Fastwel
Name : AIM721_AI Name : AIM721_AI
Type : FUNCTION Type : FUNCTION
Exported : 2026-07-31 11:16:16
=============================================================================== ===============================================================================
*) *)
FUNCTION AIM721_AI : REAL FUNCTION AIM721_AI : REAL
(* (*
Перевод значений входов модуля AIM721 из единиц АЦП в 0..20 мА Перевод значений входов модуля AIM721 из единиц АЦП в 0..20 мА
*) *)
VAR_INPUT VAR_INPUT
ADC: DINT; ADC : DINT; (* Единицы АЦП *)
END_VAR END_VAR
(*----- IMPLEMENTATION -----*) (*----- IMPLEMENTATION -----*)
+2 -3
View File
@@ -5,16 +5,15 @@
Path : Fastwel Path : Fastwel
Name : AIM722_AI Name : AIM722_AI
Type : FUNCTION Type : FUNCTION
Exported : 2026-07-31 11:16:16
=============================================================================== ===============================================================================
*) *)
FUNCTION AIM722_AI : REAL FUNCTION AIM722_AI : REAL
(* (*
Перевод значений входов модуля AIM722 из единиц АЦП в 0..20 мА Перевод значений входов модуля AIM722 из единиц АЦП в 0..20 мА
*) *)
VAR_INPUT VAR_INPUT
ADC: DINT; ADC : DINT; (* Единицы АЦП *)
END_VAR END_VAR
(*----- IMPLEMENTATION -----*) (*----- IMPLEMENTATION -----*)
+2 -3
View File
@@ -5,16 +5,15 @@
Path : Fastwel Path : Fastwel
Name : AIM723_AI Name : AIM723_AI
Type : FUNCTION Type : FUNCTION
Exported : 2026-07-31 11:16:16
=============================================================================== ===============================================================================
*) *)
FUNCTION AIM723_AI : REAL FUNCTION AIM723_AI : REAL
(* (*
Перевод значений входов модуля AIM723 из единиц АЦП в 4..20 мА Перевод значений входов модуля AIM723 из единиц АЦП в 4..20 мА
*) *)
VAR_INPUT VAR_INPUT
ADC: DINT; ADC : DINT; (* Единицы АЦП *)
END_VAR END_VAR
(*----- IMPLEMENTATION -----*) (*----- IMPLEMENTATION -----*)
+47 -47
View File
@@ -5,50 +5,50 @@
Path : Fastwel Path : Fastwel
Name : AIM725_Status Name : AIM725_Status
Type : FUNCTION_BLOCK Type : FUNCTION_BLOCK
Exported : 2026-07-31 11:16:16
=============================================================================== ===============================================================================
*) *)
FUNCTION_BLOCK AIM725_Status FUNCTION_BLOCK AIM725_Status
(* (*
Модуль формирования статуса Модуль формирования статуса
Гистерезис по отключению и задержка по включению Гистерезис по отключению и задержка по включению
-1 - нет связи с датчиком
0 - не определено -1 - нет связи с датчиком
1 - норма 0 - не определено
2 - предупредительный по нижнему уровню 1 - норма
3 - аварийный по нижнему уровню 2 - предупредительный по нижнему уровню
4 - предупредительный по верхнему уровню, 3 - аварийный по нижнему уровню
5 - аварийный по верхнему уровню 4 - предупредительный по верхнему уровню,
5 - аварийный по верхнему уровню
*) *)
VAR_INPUT VAR_INPUT
Input: REAL; Input: REAL; (* Контролируемое значение *)
fail_hi: REAL:=3.402823466E+38; fail_hi: REAL:=3.402823466E+38; (* Порог аварии по верхнему уровню *)
delta_fail_hi: REAL := 0; delta_fail_hi: REAL := 0; (* Гистерезис возврата из fail_hi *)
time_fail_hi: TIME :=t#0s; time_fail_hi: TIME :=t#0s; (* Выдержка времени на срабатывание fail_hi *)
warn_hi: REAL:=3.402823466E+38; warn_hi: REAL:=3.402823466E+38; (* Порог предупреждения по верхнему уровню *)
delta_warn_hi: REAL := 0; delta_warn_hi: REAL := 0; (* Гистерезис возврата из warn_hi *)
time_warn_hi: TIME :=t#0s; time_warn_hi: TIME :=t#0s; (* Выдержка времени на срабатывание warn_hi *)
warn_low: REAL:=-3.402823466E+38; warn_low: REAL:=-3.402823466E+38; (* Порог предупреждения по нижнему уровню *)
delta_warn_low: REAL := 0; delta_warn_low: REAL := 0; (* Гистерезис возврата из warn_low *)
time_warn_low: TIME :=t#0s; time_warn_low: TIME :=t#0s; (* Выдержка времени на срабатывание warn_low *)
fail_low: REAL:=-3.402823466E+38; fail_low: REAL:=-3.402823466E+38; (* Порог аварии по нижнему уровню *)
delta_fail_low: REAL := 0; delta_fail_low: REAL := 0; (* Гистерезис возврата из fail_low *)
time_fail_low: TIME :=t#0s; time_fail_low: TIME :=T#0S; (* Выдержка времени на срабатывание fail_low *)
Modul_status: BYTE; time_no_signal: TIME :=T#0S; (* Выдержка времени на срабатывание no_signal *)
Chanel_num: BYTE; Modul_status: BYTE; (* Байт статуса модуля АЦП (биты диагностики по каналам) *)
Chanel_num: BYTE; (* Номер канала модуля (1 или 2), определяет какие биты Modul_status проверяются *)
END_VAR END_VAR
VAR_OUTPUT VAR_OUTPUT
Status: INT; Status: AnalogValueState; (* Итоговый статус аналогового значения *)
T_mod: INT; T_mod: INT; (* Значение Input, приведенное к INT; -500 при отсутствии связи (no_signal) *)
END_VAR END_VAR
VAR VAR
(* !!! ВНУТРЕННИЕ НЕВЫХОДНЫЕ ПЕРЕМЕННЫЕ!!! *)
TON_fail_hi: TON; TON_fail_hi: TON;
TON_warn_hi: TON; TON_warn_hi: TON;
TON_warn_low: TON; TON_warn_low: TON;
TON_fail_low: TON; TON_fail_low: TON;
(* !!! ВНУТРЕННИЕ ВЫХОДНЫЕ ПЕРЕМЕННЫЕ!!! *) TON_no_signal: TON;
fail_hi_st: BOOL; fail_hi_st: BOOL;
warn_hi_st: BOOL; warn_hi_st: BOOL;
normal_st: BOOL; normal_st: BOOL;
@@ -57,36 +57,36 @@ VAR
no_signal_st: BOOL; no_signal_st: BOOL;
END_VAR END_VAR
(*----- IMPLEMENTATION -----*) (*----- 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); TON_no_signal(IN := (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), PT := time_no_signal);
IF no_signal_st THEN T_mod := - INT#500; ELSE T_mod := REAL_TO_INT(Input); END_IF; IF TON_no_signal.Q THEN T_mod := - INT#500; no_signal_st := TRUE; ELSE T_mod := REAL_TO_INT(Input); no_signal_st := FALSE; END_IF;
TON_fail_low(IN := ((Input <= fail_low) AND NOT(no_signal_st)), PT := time_fail_low); TON_fail_low(IN := ((Input <= fail_low) AND NOT(TON_no_signal.Q)), 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; 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); TON_warn_low(IN := ((Input <= warn_low) AND NOT(TON_no_signal.Q) 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; 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); TON_fail_hi(IN := ((Input >= fail_hi) AND NOT(TON_no_signal.Q)), 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; 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); TON_warn_hi(IN := ((Input >= warn_hi) AND NOT(TON_no_signal.Q) 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; 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)); normal_st := (NOT(TON_no_signal.Q) 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 IF no_signal_st THEN
Status := - INT#1; Status := AnalogValueState.no_signal;
ELSIF fail_hi_st THEN ELSIF fail_hi_st THEN
Status := INT#5; Status := AnalogValueState.fail_hi;
ELSIF fail_low_st THEN ELSIF fail_low_st THEN
Status := INT#3; Status := AnalogValueState.fail_low;
ELSIF warn_hi_st THEN ELSIF warn_hi_st THEN
Status := INT#4; Status := AnalogValueState.warn_hi;
ELSIF warn_low_st THEN ELSIF warn_low_st THEN
Status := INT#2; Status := AnalogValueState.warn_low;
ELSIF normal_st THEN ELSIF normal_st THEN
Status := INT#1; Status := AnalogValueState.normal;
ELSE ELSE
Status := INT#0; Status := AnalogValueState.undefined;
END_IF; END_IF;
END_FUNCTION_BLOCK END_FUNCTION_BLOCK
+2 -3
View File
@@ -5,16 +5,15 @@
Path : Fastwel Path : Fastwel
Name : AIM726_AI Name : AIM726_AI
Type : FUNCTION Type : FUNCTION
Exported : 2026-07-31 11:16:16
=============================================================================== ===============================================================================
*) *)
FUNCTION AIM726_AI : REAL FUNCTION AIM726_AI : REAL
(* (*
Перевод значений входов модуля AIM726 из единиц АЦП в 0..40 В Перевод значений входов модуля AIM726 из единиц АЦП в 0..40 В
*) *)
VAR_INPUT VAR_INPUT
ADC : DWORD; (*Единицы АЦП*) ADC : DWORD; (* Единицы АЦП *)
END_VAR END_VAR
(*----- IMPLEMENTATION -----*) (*----- IMPLEMENTATION -----*)
AIM726_AI := DWORD_TO_REAL(ADC) * REAL#4.768372E-006; AIM726_AI := DWORD_TO_REAL(ADC) * REAL#4.768372E-006;
+1 -2
View File
@@ -5,13 +5,12 @@
Path : Fastwel Path : Fastwel
Name : AIM727_AI Name : AIM727_AI
Type : FUNCTION Type : FUNCTION
Exported : 2026-07-31 11:16:16
=============================================================================== ===============================================================================
*) *)
FUNCTION AIM727_AI : REAL FUNCTION AIM727_AI : REAL
(* (*
Перевод значений входов модуля AIM727 из единиц АЦП в 0..40 V Перевод значений входов модуля AIM727 из единиц АЦП в 0..40 V
*) *)
VAR_INPUT VAR_INPUT
ADC: DINT; (*Единицы АЦП*) ADC: DINT; (*Единицы АЦП*)
+2 -6
View File
@@ -5,20 +5,16 @@
Path : Fastwel Path : Fastwel
Name : AIM730_AO Name : AIM730_AO
Type : FUNCTION Type : FUNCTION
Exported : 2026-07-31 11:16:16
=============================================================================== ===============================================================================
*) *)
FUNCTION AIM730_AO : WORD FUNCTION AIM730_AO : WORD
(* (*
Перевод значений 0..20 мА или 4..20 мА в единицы АЦП выходов модуля AIM730 Перевод значений 0..20 мА или 4..20 мА в единицы АЦП выходов модуля AIM730
Диапазон:
RANGE = 0 - 0..20 мА
RANGE = 1 - 4..20 мА
*) *)
VAR_INPUT VAR_INPUT
DAC : REAL; (*Миллиамперы*) DAC : REAL; (*Миллиамперы*)
RANGE : BYTE; (*Диапазон: 0..20 мА - 0; 4..20 мА - 1 *) RANGE : BYTE; (*Диапазон: 0 - 0..20 мА; 1 - 4..20 мА *)
END_VAR END_VAR
(*----- IMPLEMENTATION -----*) (*----- IMPLEMENTATION -----*)
IF (RANGE = 1) AND (DAC < 4) THEN AIM730_AO := WORD#4; RETURN;END_IF; IF (RANGE = 1) AND (DAC < 4) THEN AIM730_AO := WORD#4; RETURN;END_IF;
+2 -6
View File
@@ -5,20 +5,16 @@
Path : Fastwel Path : Fastwel
Name : AIM731_AO Name : AIM731_AO
Type : FUNCTION Type : FUNCTION
Exported : 2026-07-31 11:16:16
=============================================================================== ===============================================================================
*) *)
FUNCTION AIM731_AO : WORD FUNCTION AIM731_AO : WORD
(* (*
Перевод значений -10..+10 В или 0..+10 В в единицы АЦП выходов модуля AIM731 Перевод значений -10..+10 В или 0..+10 В в единицы АЦП выходов модуля AIM731
Диапазон:
RANGE = 0 - -10..+10 В
RANGE = 1 - 0..+10 В
*) *)
VAR_INPUT VAR_INPUT
DAC : REAL; (*Вольты*) DAC : REAL; (*Вольты*)
RANGE : BYTE; (*Диапазон -10..+10 В - 0; 0..+10 В - 1 *) RANGE : BYTE; (*Диапазон: 0 - -10..+10 В; 1 - 0..+10 В *)
END_VAR END_VAR
(*----- IMPLEMENTATION -----*) (*----- IMPLEMENTATION -----*)
CASE RANGE OF CASE RANGE OF
+3 -9
View File
@@ -5,23 +5,17 @@
Path : Fastwel Path : Fastwel
Name : AIM791_AI Name : AIM791_AI
Type : FUNCTION Type : FUNCTION
Exported : 2026-07-31 11:16:16
=============================================================================== ===============================================================================
*) *)
FUNCTION AIM791_AI : REAL FUNCTION AIM791_AI : REAL
(* (*
Перевод значений входов модуля AIM791 из единиц АЦП в 0..5 мА / 0..20 мА / 4..20 мА Перевод значений входов модуля AIM791 из единиц АЦП в 0..5 мА / 0..20 мА / 4..20 мА
Диапазон:
RANGE = 0 - 0..5 мА
RANGE = 1 - 0..20 мА
RANGE = 2 - 4..20 мА
*) *)
VAR_INPUT VAR_INPUT
ADC: WORD; ADC: WORD; (* Значение АЦП *)
RANGE: BYTE := 0; RANGE: BYTE := 0; (* Диапазон: 0 - 0..5 мА; 1 - 0..20 мА; 2 - 4..20 мА *)
END_VAR END_VAR
(*----- IMPLEMENTATION -----*) (*----- IMPLEMENTATION -----*)
CASE RANGE OF CASE RANGE OF
0 : AIM791_AI := WORD_TO_REAL(ADC) * REAL#7.820248722E-005; 0 : AIM791_AI := WORD_TO_REAL(ADC) * REAL#7.820248722E-005;
+4 -3
View File
@@ -5,14 +5,15 @@
Path : Fastwel Path : Fastwel
Name : DIM764_DI Name : DIM764_DI
Type : FUNCTION Type : FUNCTION
Exported : 2026-07-31 11:16:16
=============================================================================== ===============================================================================
*) *)
FUNCTION DIM764_DI : REAL FUNCTION DIM764_DI : REAL
(*На выходе - Частота в Гц*) (*
На выходе - Частота в Гц
*)
VAR_INPUT VAR_INPUT
IN: DWORD; IN: DWORD; (* Значение периода/интервала с модуля, единицы АЦП *)
RANGE: BYTE; (* 0-Period; 1-Interval; *) RANGE: BYTE; (* 0-Period; 1-Interval; *)
END_VAR END_VAR
(*----- IMPLEMENTATION -----*) (*----- IMPLEMENTATION -----*)
-1
View File
@@ -5,7 +5,6 @@
Path : Filters Path : Filters
Name : ExpRunAverage Name : ExpRunAverage
Type : FUNCTION Type : FUNCTION
Exported : 2026-07-31 11:16:16
=============================================================================== ===============================================================================
*) *)
@@ -5,7 +5,6 @@
Path : Filters Path : Filters
Name : ExpRunAverageAdaptive Name : ExpRunAverageAdaptive
Type : FUNCTION Type : FUNCTION
Exported : 2026-07-31 11:16:16
=============================================================================== ===============================================================================
*) *)
+5 -1
View File
@@ -5,11 +5,15 @@
Path : Filters Path : Filters
Name : MultipleClick Name : MultipleClick
Type : FUNCTION_BLOCK Type : FUNCTION_BLOCK
Exported : 2026-07-31 11:16:16
=============================================================================== ===============================================================================
*) *)
FUNCTION_BLOCK MultipleClick FUNCTION_BLOCK MultipleClick
(*
Детектор серии кликов кнопки (одиночный/двойной/тройной клик).
Считает нажатия, пока пауза между ними не превысит TimeToWait, затем фиксирует
результат на выходах Click1/Click2/Click3 на время TimeRelease.
*)
VAR_INPUT VAR_INPUT
In: BOOL;(* Вход кнопки *) In: BOOL;(* Вход кнопки *)
TimeToWait: TIME:=T#500MS; (* Максимальное время между нажатиями *) TimeToWait: TIME:=T#500MS; (* Максимальное время между нажатиями *)
+6 -5
View File
@@ -5,19 +5,20 @@
Path : Filters Path : Filters
Name : RunMiddleArifm Name : RunMiddleArifm
Type : FUNCTION_BLOCK Type : FUNCTION_BLOCK
Exported : 2026-07-31 11:16:16
=============================================================================== ===============================================================================
*) *)
FUNCTION_BLOCK RunMiddleArifm FUNCTION_BLOCK RunMiddleArifm
(* Скользящая средняя *) (*
Скользящая средняя
*)
VAR_INPUT VAR_INPUT
In : REAL; In : REAL; (* Входное значение *)
N : INT:=32; (* Размер выборки 0...32 *) N : INT:=32; (* Размер выборки 0...32 *)
RST : BOOL; RST : BOOL; (* Сброс/переинициализация буфера текущим значением In *)
END_VAR END_VAR
VAR_OUTPUT VAR_OUTPUT
Y : REAL; Y : REAL; (* Значение скользящей средней *)
END_VAR END_VAR
VAR VAR
init: BOOL; init: BOOL;
-1
View File
@@ -5,7 +5,6 @@
Path : Иное Path : Иное
Name : DFS Name : DFS
Type : PROGRAM Type : PROGRAM
Exported : 2026-07-31 11:16:16
=============================================================================== ===============================================================================
*) *)
-1
View File
@@ -5,7 +5,6 @@
Path : Общие Path : Общие
Name : ALARM_1 Name : ALARM_1
Type : FUNCTION_BLOCK Type : FUNCTION_BLOCK
Exported : 2026-07-31 11:16:16
=============================================================================== ===============================================================================
*) *)
-1
View File
@@ -5,7 +5,6 @@
Path : Общие Path : Общие
Name : ALARM_2 Name : ALARM_2
Type : FUNCTION_BLOCK Type : FUNCTION_BLOCK
Exported : 2026-07-31 11:16:16
=============================================================================== ===============================================================================
*) *)
-1
View File
@@ -5,7 +5,6 @@
Path : Общие Path : Общие
Name : ALARM_3 Name : ALARM_3
Type : FUNCTION_BLOCK Type : FUNCTION_BLOCK
Exported : 2026-07-31 11:16:16
=============================================================================== ===============================================================================
*) *)
+2 -3
View File
@@ -5,13 +5,12 @@
Path : Общие Path : Общие
Name : BLINK_LMP Name : BLINK_LMP
Type : FUNCTION_BLOCK Type : FUNCTION_BLOCK
Exported : 2026-07-31 11:16:16
=============================================================================== ===============================================================================
*) *)
FUNCTION_BLOCK BLINK_LMP FUNCTION_BLOCK BLINK_LMP
(* (*
Мигание лампами в завистмости от статуса Мигание лампами в завистмости от статуса
*) *)
VAR_INPUT VAR_INPUT
State: WORD:=0; (* 0 - выключено; 1 - включено; 2 - мигание с частотой 1; 3 - мигание с частотой 2; 4 - мигание с частотой 3; *) State: WORD:=0; (* 0 - выключено; 1 - включено; 2 - мигание с частотой 1; 3 - мигание с частотой 2; 4 - мигание с частотой 3; *)
@@ -20,7 +19,7 @@ VAR_INPUT
Inp_BL_3: BOOL := FALSE; (* Пульсар миганич с частотой 3 *) Inp_BL_3: BOOL := FALSE; (* Пульсар миганич с частотой 3 *)
END_VAR END_VAR
VAR_OUTPUT VAR_OUTPUT
LMP: BOOL; LMP: BOOL; (* Управляющий сигнал на лампу *)
END_VAR END_VAR
VAR VAR
END_VAR END_VAR
+2 -3
View File
@@ -5,13 +5,12 @@
Path : Общие Path : Общие
Name : BUZZER_CTRL Name : BUZZER_CTRL
Type : FUNCTION_BLOCK Type : FUNCTION_BLOCK
Exported : 2026-07-31 11:16:16
=============================================================================== ===============================================================================
*) *)
FUNCTION_BLOCK BUZZER_CTRL FUNCTION_BLOCK BUZZER_CTRL
(* (*
Звучание звонком лампами в завистмости от статуса с возможностью отключения звука Упрвелние звонком лампами в завистмости от статуса с возможностью отключения звука
*) *)
VAR_INPUT VAR_INPUT
State: WORD:=0; (* 0 - выключено; 1 - включено; 2 - мигание с частотой 1; 3 - мигание с частотой 2; 4 - мигание с частотой 3; *) State: WORD:=0; (* 0 - выключено; 1 - включено; 2 - мигание с частотой 1; 3 - мигание с частотой 2; 4 - мигание с частотой 3; *)
@@ -23,7 +22,7 @@ VAR_INPUT
T_PermanentOFF: TIME := T#10S; (* Время удержания кнопки отключения звука для перманентного отключения *) T_PermanentOFF: TIME := T#10S; (* Время удержания кнопки отключения звука для перманентного отключения *)
END_VAR END_VAR
VAR_OUTPUT VAR_OUTPUT
BUZZER: BOOL; BUZZER: BOOL; (* Управляющий сигнал на звонок/зуммер *)
END_VAR END_VAR
VAR VAR
R_TRIG_0: R_TRIG; R_TRIG_0: R_TRIG;
@@ -5,7 +5,6 @@
Path : Общие Path : Общие
Name : Button_acceleration Name : Button_acceleration
Type : FUNCTION_BLOCK Type : FUNCTION_BLOCK
Exported : 2026-07-31 11:16:16
=============================================================================== ===============================================================================
*) *)
-1
View File
@@ -5,7 +5,6 @@
Path : Общие Path : Общие
Name : CONFIRMATION Name : CONFIRMATION
Type : FUNCTION_BLOCK Type : FUNCTION_BLOCK
Exported : 2026-07-31 11:16:16
=============================================================================== ===============================================================================
*) *)
+9 -7
View File
@@ -5,21 +5,23 @@
Path : Общие Path : Общие
Name : HYST Name : HYST
Type : FUNCTION_BLOCK Type : FUNCTION_BLOCK
Exported : 2026-07-31 11:16:16
=============================================================================== ===============================================================================
*) *)
FUNCTION_BLOCK HYST FUNCTION_BLOCK HYST
(*
Гистерезисный компаратор с зоной нечувствительности между ON и OFF.
Поддерживает как прямую (ON >= OFF), так и обратную (ON < OFF) логику переключения.
*)
VAR_INPUT VAR_INPUT
In : REAL; In : REAL; (* Входное контролируемое значение *)
ON : REAL; ON : REAL; (* Порог включения (срабатывания) *)
OFF : REAL; OFF : REAL; (* Порог выключения (возврата) *)
END_VAR END_VAR
VAR_OUTPUT VAR_OUTPUT
Q : BOOL; Q : BOOL; (* Выходное состояние компаратора *)
win : BOOL; win : BOOL; (* Флаг: входное значение находится в зоне гистерезиса *)
END_VAR END_VAR
(*----- IMPLEMENTATION -----*) (*----- IMPLEMENTATION -----*)
IF ON >= OFF THEN IF ON >= OFF THEN
IF IN < OFF THEN IF IN < OFF THEN
+5 -7
View File
@@ -5,7 +5,6 @@
Path : Общие Path : Общие
Name : Norm_real Name : Norm_real
Type : FUNCTION Type : FUNCTION
Exported : 2026-07-31 11:16:16
=============================================================================== ===============================================================================
*) *)
@@ -14,13 +13,12 @@ FUNCTION Norm_real : REAL
Нормирует значение аналогового входа X по двум заданным граничным точкам нормировки (X1,Y1) (X2,Y2) Нормирует значение аналогового входа X по двум заданным граничным точкам нормировки (X1,Y1) (X2,Y2)
*) *)
VAR_INPUT VAR_INPUT
X: REAL; X: REAL; (* Входное (ненормированное) значение *)
X1: REAL; X1: REAL; (* Первая точка нормировки, вход *)
Y1: REAL; Y1: REAL; (* Первая точка нормировки, выход *)
X2: REAL; X2: REAL; (* Вторая точка нормировки, вход *)
Y2: REAL; Y2: REAL; (* Вторая точка нормировки, выход *)
END_VAR END_VAR
(*----- IMPLEMENTATION -----*) (*----- IMPLEMENTATION -----*)
IF (X2-X1 = 0) THEN Norm_real:=Y1; RETURN; END_IF; IF (X2-X1 = 0) THEN Norm_real:=Y1; RETURN; END_IF;
Norm_real:=(X-X1)*(Y2-Y1)/(X2-X1)+Y1; Norm_real:=(X-X1)*(Y2-Y1)/(X2-X1)+Y1;
-1
View File
@@ -5,7 +5,6 @@
Path : Общие Path : Общие
Name : PIDcontrol Name : PIDcontrol
Type : FUNCTION_BLOCK Type : FUNCTION_BLOCK
Exported : 2026-07-31 11:16:16
=============================================================================== ===============================================================================
*) *)
+10 -8
View File
@@ -5,24 +5,26 @@
Path : Общие Path : Общие
Name : Pulse_Switch Name : Pulse_Switch
Type : FUNCTION_BLOCK Type : FUNCTION_BLOCK
Exported : 2026-07-31 11:16:16
=============================================================================== ===============================================================================
*) *)
FUNCTION_BLOCK Pulse_Switch FUNCTION_BLOCK Pulse_Switch
(*
Формирует импульсы (500 мс) по переднему и заднему фронту управляющего сигнала.
*)
VAR_INPUT
Switch_ctrl: BOOL; (* Управляющий сигнал *)
END_VAR
VAR_OUTPUT
Switch_on: BOOL; (* Импульс 500 мс при переходе Switch_ctrl 0->1 *)
Switch_off: BOOL; (* Импульс 500 мс при переходе Switch_ctrl 1->0 *)
END_VAR
VAR VAR
R_tr_1: R_TRIG; R_tr_1: R_TRIG;
F_tr_1: F_TRIG; F_tr_1: F_TRIG;
tp_on: TP:=(PT:=t#500ms); tp_on: TP:=(PT:=t#500ms);
tp_off: TP:=(PT:=t#500ms); tp_off: TP:=(PT:=t#500ms);
END_VAR END_VAR
VAR_INPUT
Switch_ctrl: BOOL;
END_VAR
VAR_OUTPUT
Switch_on: BOOL;
Switch_off: BOOL;
END_VAR
(*----- IMPLEMENTATION -----*) (*----- IMPLEMENTATION -----*)
R_tr_1(CLK:=Switch_ctrl); R_tr_1(CLK:=Switch_ctrl);
tp_on(IN:=R_tr_1.Q, Q=>Switch_on); tp_on(IN:=R_tr_1.Q, Q=>Switch_on);
+66
View File
@@ -0,0 +1,66 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Общие
Name : QUEUE_I16
Type : FUNCTION_BLOCK
===============================================================================
*)
FUNCTION_BLOCK QUEUE_I16
(*
Очередь FIFO на 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
queue : ARRAY[0..n] OF INT;
Head : INT; (* индекс чтения *)
Tail : INT; (* индекс записи *)
Count : INT; (* количество элементов *)
END_VAR
VAR CONSTANT
n : INT := 15; (* 16 элементов *)
END_VAR
(*----- IMPLEMENTATION -----*)
IF RST THEN
Head := 0;
Tail := 0;
Count := 0;
EMPTY := TRUE;
FULL := FALSE;
Dout := 0;
ELSIF E THEN
(* Извлечение *)
IF POP AND NOT EMPTY THEN
Dout := queue[Head];
Head := Head + 1;
IF Head > n THEN
Head := 0;
END_IF;
Count := Count - 1;
END_IF;
(* Добавление *)
IF PUSH AND NOT FULL THEN
queue[Tail] := Din;
Tail := Tail + 1;
IF Tail > n THEN
Tail := 0;
END_IF;
Count := Count + 1;
END_IF;
EMPTY := (Count = 0);
FULL := (Count = n + 1);
END_IF;
END_FUNCTION_BLOCK
+66
View File
@@ -0,0 +1,66 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Общие
Name : QUEUE_I32
Type : FUNCTION_BLOCK
===============================================================================
*)
FUNCTION_BLOCK QUEUE_I32
(*
Очередь FIFO на 32 элемента типа 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
queue : ARRAY[0..n] OF INT;
Head : INT; (* индекс чтения *)
Tail : INT; (* индекс записи *)
Count : INT; (* количество элементов *)
END_VAR
VAR CONSTANT
n : INT := 31; (* 32 элемента *)
END_VAR
(*----- IMPLEMENTATION -----*)
IF RST THEN
Head := 0;
Tail := 0;
Count := 0;
EMPTY := TRUE;
FULL := FALSE;
Dout := 0;
ELSIF E THEN
(* Извлечение *)
IF POP AND NOT EMPTY THEN
Dout := queue[Head];
Head := Head + 1;
IF Head > n THEN
Head := 0;
END_IF;
Count := Count - 1;
END_IF;
(* Добавление *)
IF PUSH AND NOT FULL THEN
queue[Tail] := Din;
Tail := Tail + 1;
IF Tail > n THEN
Tail := 0;
END_IF;
Count := Count + 1;
END_IF;
EMPTY := (Count = 0);
FULL := (Count = n + 1);
END_IF;
END_FUNCTION_BLOCK
+12 -12
View File
@@ -5,35 +5,35 @@
Path : Общие Path : Общие
Name : STACK_I16 Name : STACK_I16
Type : FUNCTION_BLOCK Type : FUNCTION_BLOCK
Exported : 2026-07-31 11:16:16
=============================================================================== ===============================================================================
*) *)
FUNCTION_BLOCK STACK_I16 FUNCTION_BLOCK STACK_I16
(* стек на 16 элементов типа INT*) (*
Стек LIFO на 16 элементов типа INT
*)
VAR_INPUT VAR_INPUT
Din : INT; (* Переменная для записи в стек *) Din : INT; (* Переменная для записи в стек *)
E : BOOL := TRUE;(* Включение стека *) E : BOOL := TRUE; (* Включение стека *)
POP : BOOL; (* Извлечение из стека *) POP : BOOL; (* Извлечение из стека *)
PUSH : BOOL; (* Запись в стек *) PUSH : BOOL; (* Запись в стек *)
RST : BOOL; (* Обнуление стека *) RST : BOOL; (* Обнуление стека *)
END_VAR END_VAR
VAR_OUTPUT VAR_OUTPUT
Dout : INT; Dout : INT; (* Значение, извлеченное из стека при POP *)
EMPTY : BOOL := TRUE; EMPTY : BOOL := TRUE; (* Флаг "стек пуст" *)
FULL : BOOL; FULL : BOOL; (* Флаг "стек заполнен" (достигнут предел n) *)
END_VAR END_VAR
VAR VAR
stack: ARRAY[0..n] OF INT; stack: ARRAY[0..n] OF INT;
pt : INT; pt : INT;
END_VAR END_VAR
VAR CONSTANT VAR CONSTANT
n : INT := 15; (* changing this value will chage the number of stored elements in the fifo *) n : INT := 15; (* 16 элементов *)
END_VAR END_VAR
(*----- IMPLEMENTATION -----*) (*----- IMPLEMENTATION -----*)
IF RST THEN IF RST THEN
(* asynchronous reset for the fifo *)
pt := 0; pt := 0;
EMPTY := TRUE; EMPTY := TRUE;
FULL := FALSE; FULL := FALSE;
+55
View File
@@ -0,0 +1,55 @@
(*
===============================================================================
Project : PLC_Library
Device : -
Path : Общие
Name : STACK_I32
Type : FUNCTION_BLOCK
===============================================================================
*)
FUNCTION_BLOCK STACK_I32
(*
Стек LIFO на 32 элемента типа INT
*)
VAR_INPUT
Din : INT; (* Переменная для записи в стек *)
E : BOOL := TRUE; (* Включение стека *)
POP : BOOL; (* Извлечение из стека *)
PUSH : BOOL; (* Запись в стек *)
RST : BOOL; (* Обнуление стека *)
END_VAR
VAR_OUTPUT
Dout : INT; (* Значение, извлеченное из стека при POP *)
EMPTY : BOOL := TRUE; (* Флаг "стек пуст" *)
FULL : BOOL; (* Флаг "стек заполнен" (достигнут предел n) *)
END_VAR
VAR
stack: ARRAY[0..n] OF INT;
pt : INT;
END_VAR
VAR CONSTANT
n : INT := 31; (* 32 элемента *)
END_VAR
(*----- IMPLEMENTATION -----*)
IF RST THEN
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
+22 -23
View File
@@ -5,7 +5,6 @@
Path : Общие Path : Общие
Name : STATUS_ANALOG_M Name : STATUS_ANALOG_M
Type : FUNCTION_BLOCK Type : FUNCTION_BLOCK
Exported : 2026-07-31 11:16:16
=============================================================================== ===============================================================================
*) *)
@@ -23,31 +22,31 @@ FUNCTION_BLOCK STATUS_ANALOG_M
5 - аварийный по верхнему уровню 5 - аварийный по верхнему уровню
*) *)
VAR_INPUT VAR_INPUT
Input: REAL; Input: REAL; (* Контролируемое значение *)
fail_hi: REAL:=3.402823466E+38; fail_hi: REAL:=3.402823466E+38; (* Порог аварии по верхнему уровню *)
delta_fail_hi: REAL := 0; delta_fail_hi: REAL := 0; (* Гистерезис возврата из fail_hi (снятие при Input < fail_hi - delta_fail_hi) *)
time_fail_hi: TIME :=t#0s; time_fail_hi: TIME :=t#0s; (* Выдержка времени на срабатывание fail_hi *)
warn_hi: REAL:=3.402823466E+38; warn_hi: REAL:=3.402823466E+38; (* Порог предупреждения по верхнему уровню *)
delta_warn_hi: REAL := 0; delta_warn_hi: REAL := 0; (* Гистерезис возврата из warn_hi *)
time_warn_hi: TIME :=t#0s; time_warn_hi: TIME :=t#0s; (* Выдержка времени на срабатывание warn_hi *)
warn_low: REAL:=-3.402823466E+38; warn_low: REAL:=-3.402823466E+38; (* Порог предупреждения по нижнему уровню *)
delta_warn_low: REAL := 0; delta_warn_low: REAL := 0; (* Гистерезис возврата из warn_low *)
time_warn_low: TIME :=t#0s; time_warn_low: TIME :=t#0s; (* Выдержка времени на срабатывание warn_low *)
fail_low: REAL:=-3.402823466E+38; fail_low: REAL:=-3.402823466E+38; (* Порог аварии по нижнему уровню *)
delta_fail_low: REAL := 0; delta_fail_low: REAL := 0; (* Гистерезис возврата из fail_low *)
time_fail_low: TIME :=t#0s; time_fail_low: TIME :=t#0s; (* Выдержка времени на срабатывание fail_low *)
no_signal: REAL:=-3.402823466E+38; no_signal: REAL:=-3.402823466E+38; (* Порог отсутствия связи с датчиком *)
delta_no_signal: REAL := 0; delta_no_signal: REAL := 0; (* Гистерезис возврата из no_signal *)
time_no_signal: TIME :=T#0S; time_no_signal: TIME :=T#0S; (* Выдержка времени на срабатывание no_signal *)
In_fail_hi: BOOL := FALSE; In_fail_hi: BOOL := FALSE; (* Принудительная установка статуса fail_hi извне *)
In_warn_hi: BOOL := FALSE; In_warn_hi: BOOL := FALSE; (* Принудительная установка статуса warn_hi извне *)
In_warn_low: BOOL := FALSE; In_warn_low: BOOL := FALSE; (* Принудительная установка статуса warn_low извне *)
In_fail_low: BOOL := FALSE; In_fail_low: BOOL := FALSE; (* Принудительная установка статуса fail_low извне *)
In_no_signal: BOOL := FALSE; In_no_signal: BOOL := FALSE; (* Принудительная установка статуса no_signal извне *)
END_VAR END_VAR
VAR_OUTPUT VAR_OUTPUT
Status: AnalogValueState; Status: AnalogValueState; (* Итоговый статус аналогового значения *)
END_VAR END_VAR
VAR VAR
fail_hi_st: BOOL; fail_hi_st: BOOL;
+22 -26
View File
@@ -5,7 +5,6 @@
Path : Общие Path : Общие
Name : STATUS_ANALOG_M_I Name : STATUS_ANALOG_M_I
Type : FUNCTION_BLOCK Type : FUNCTION_BLOCK
Exported : 2026-07-31 11:16:16
=============================================================================== ===============================================================================
*) *)
@@ -23,42 +22,39 @@ FUNCTION_BLOCK STATUS_ANALOG_M_I
5 - аварийный по верхнему уровню 5 - аварийный по верхнему уровню
*) *)
VAR_INPUT VAR_INPUT
Input: INT; Input: INT; (* Контролируемое значение *)
fail_hi: INT:=32767; fail_hi: INT:=32767; (* Порог аварии по верхнему уровню *)
delta_fail_hi: INT := 0; delta_fail_hi: INT := 0; (* Гистерезис возврата из fail_hi (снятие при Input < fail_hi - delta_fail_hi) *)
time_fail_hi: TIME :=t#0s; time_fail_hi: TIME :=t#0s; (* Выдержка времени на срабатывание fail_hi *)
warn_hi: INT:=32767; warn_hi: INT:=32767; (* Порог предупреждения по верхнему уровню *)
delta_warn_hi: INT := 0; delta_warn_hi: INT := 0; (* Гистерезис возврата из warn_hi *)
time_warn_hi: TIME :=t#0s; time_warn_hi: TIME :=t#0s; (* Выдержка времени на срабатывание warn_hi *)
warn_low: INT:=-32768; warn_low: INT:=-32768; (* Порог предупреждения по нижнему уровню *)
delta_warn_low: INT := 0; delta_warn_low: INT := 0; (* Гистерезис возврата из warn_low *)
time_warn_low: TIME :=t#0s; time_warn_low: TIME :=t#0s; (* Выдержка времени на срабатывание warn_low *)
fail_low: INT:=-32768; fail_low: INT:=-32768; (* Порог аварии по нижнему уровню *)
delta_fail_low: INT := 0; delta_fail_low: INT := 0; (* Гистерезис возврата из fail_low *)
time_fail_low: TIME :=t#0s; time_fail_low: TIME :=t#0s; (* Выдержка времени на срабатывание fail_low *)
no_signal: INT:=-32768; no_signal: INT:=-32768; (* Порог отсутствия связи с датчиком *)
delta_no_signal: INT := 0; delta_no_signal: INT := 0; (* Гистерезис возврата из no_signal *)
time_no_signal: TIME :=T#0S; time_no_signal: TIME :=T#0S; (* Выдержка времени на срабатывание no_signal *)
In_fail_hi: BOOL := FALSE; In_fail_hi: BOOL := FALSE; (* Принудительная установка статуса fail_hi извне *)
In_warn_hi: BOOL := FALSE; In_warn_hi: BOOL := FALSE; (* Принудительная установка статуса warn_hi извне *)
In_warn_low: BOOL := FALSE; In_warn_low: BOOL := FALSE; (* Принудительная установка статуса warn_low извне *)
In_fail_low: BOOL := FALSE; In_fail_low: BOOL := FALSE; (* Принудительная установка статуса fail_low извне *)
In_no_signal: BOOL := FALSE; In_no_signal: BOOL := FALSE; (* Принудительная установка статуса no_signal извне *)
END_VAR END_VAR
VAR_OUTPUT VAR_OUTPUT
Status: AnalogValueState; Status: AnalogValueState; (* Итоговый статус аналогового значения *)
END_VAR END_VAR
VAR VAR
(* !!! ВНУТРЕННИЕ ВЫХОДНЫЕ ПЕРЕМЕННЫЕ!!! *)
fail_hi_st: BOOL; fail_hi_st: BOOL;
warn_hi_st: BOOL; warn_hi_st: BOOL;
normal_st: BOOL; normal_st: BOOL;
warn_low_st: BOOL; warn_low_st: BOOL;
fail_low_st: BOOL; fail_low_st: BOOL;
no_signal_st: BOOL; no_signal_st: BOOL;
(* !!! ВНУТРЕННИЕ НЕВЫХОДНЫЕ ПЕРЕМЕННЫЕ!!! *)
TON_fail_hi: TON; TON_fail_hi: TON;
TON_warn_hi: TON; TON_warn_hi: TON;
TON_warn_low: TON; TON_warn_low: TON;
@@ -5,19 +5,20 @@
Path : Общие Path : Общие
Name : Status_Connect_Counter Name : Status_Connect_Counter
Type : FUNCTION_BLOCK Type : FUNCTION_BLOCK
Exported : 2026-07-31 11:16:16
=============================================================================== ===============================================================================
*) *)
FUNCTION_BLOCK Status_Connect_Counter FUNCTION_BLOCK Status_Connect_Counter
(* Статус связи с СВУ по счетчику*) (*
Статус связи с СВУ по счетчику
*)
VAR_INPUT VAR_INPUT
Counter: WORD; Counter: WORD; (* Счетчик, инкрементируемый удаленным устройством (СВУ) *)
T_Error: TIME; (* время для диагностирования ошибки*) T_Error: TIME; (* Время отсутствия изменений счетчика для диагностики обрыва связи *)
END_VAR END_VAR
VAR_OUTPUT VAR_OUTPUT
CounterIsChanged: BOOL; CounterIsChanged: BOOL; (* Флаг: счетчик изменился в текущем цикле *)
ERROR_CONNECT: BOOL; ERROR_CONNECT: BOOL; (* Флаг ошибки связи: счетчик не менялся дольше T_Error *)
END_VAR END_VAR
VAR VAR
CounterOld: WORD; CounterOld: WORD;
+6 -5
View File
@@ -5,18 +5,19 @@
Path : Общие Path : Общие
Name : Status_SU_Connect Name : Status_SU_Connect
Type : FUNCTION_BLOCK Type : FUNCTION_BLOCK
Exported : 2026-07-31 11:16:16
=============================================================================== ===============================================================================
*) *)
FUNCTION_BLOCK Status_SU_Connect FUNCTION_BLOCK Status_SU_Connect
(* Статус связи с СВУ по серцебиению*) (*
Статус связи с СВУ по серцебиению
*)
VAR_INPUT VAR_INPUT
HeartBit: BOOL; HeartBit: BOOL; (* Сигнал "сердцебиения" от СВУ *)
T_Pulse: TIME; T_Pulse: TIME; (* Максимально допустимая длительность одного состояния HeartBit *)
END_VAR END_VAR
VAR_OUTPUT VAR_OUTPUT
ERROR_CONNECT: BOOL; ERROR_CONNECT: BOOL; (* Флаг ошибки связи: HeartBit не меняется дольше T_Pulse *)
END_VAR END_VAR
VAR VAR
TON_SU_1: TON; TON_SU_1: TON;
+5 -1
View File
@@ -5,11 +5,15 @@
Path : Общие Path : Общие
Name : TOF_RST Name : TOF_RST
Type : FUNCTION_BLOCK Type : FUNCTION_BLOCK
Exported : 2026-07-31 11:16:16
=============================================================================== ===============================================================================
*) *)
FUNCTION_BLOCK TOF_RST FUNCTION_BLOCK TOF_RST
(*
Таймер выключения (TOF) с возможностью принудительного сброса RST.
При снятии IN выход Q остается TRUE еще в течение PT.
RST немедленно сбрасывает выход и таймер вне зависимости от IN.
*)
VAR_INPUT VAR_INPUT
IN: BOOL; // Управляющий сигнал IN: BOOL; // Управляющий сигнал
PT: TIME; // Время задержки выключения PT: TIME; // Время задержки выключения
+6 -7
View File
@@ -5,22 +5,21 @@
Path : Общие Path : Общие
Name : TONOF Name : TONOF
Type : FUNCTION_BLOCK Type : FUNCTION_BLOCK
Exported : 2026-07-31 11:16:16
=============================================================================== ===============================================================================
*) *)
FUNCTION_BLOCK TONOF FUNCTION_BLOCK TONOF
(* (*
TONOF генерирует TON и TOF задержки для переменной IN TONOF генерирует TON и TOF задержки для переменной IN
Задержка на вход T_ON, задержка на выход T_OFF Задержка на вход T_ON, задержка на выход T_OFF
*) *)
VAR_INPUT VAR_INPUT
IN : BOOL; IN : BOOL; (* Входной сигнал *)
T_ON : TIME := T#0s; (* Задержка на вход *) T_ON : TIME := T#0S; (* Задержка на вход *)
T_OFF : TIME := T#0s; (* Задержка на выход *) T_OFF : TIME := T#0S; (* Задержка на выход *)
END_VAR END_VAR
VAR_OUTPUT VAR_OUTPUT
Q : BOOL; Q : BOOL; (* Выходной сигнал с учетом задержек включения/выключения *)
END_VAR END_VAR
VAR VAR
X : TON; X : TON;
+5 -1
View File
@@ -5,11 +5,15 @@
Path : Общие Path : Общие
Name : TP_RST Name : TP_RST
Type : FUNCTION_BLOCK Type : FUNCTION_BLOCK
Exported : 2026-07-31 11:16:16
=============================================================================== ===============================================================================
*) *)
FUNCTION_BLOCK TP_RST FUNCTION_BLOCK TP_RST
(*
Формирователь одиночного импульса (TP) длительностью PT с возможностью принудительного сброса RST.
Повторный передний фронт IN во время выполнения импульса игнорируется.
RST немедленно прерывает выполняющийся импульс.
*)
VAR_INPUT VAR_INPUT
IN: BOOL; // Управляющий сигнал IN: BOOL; // Управляющий сигнал
PT: TIME; // Длительность импульса PT: TIME; // Длительность импульса
@@ -5,16 +5,15 @@
Path : Преобразование значений Path : Преобразование значений
Name : DWORD_OF_2WORD Name : DWORD_OF_2WORD
Type : FUNCTION Type : FUNCTION
Exported : 2026-07-31 11:16:16
=============================================================================== ===============================================================================
*) *)
FUNCTION DWORD_OF_2WORD : DWORD FUNCTION DWORD_OF_2WORD : DWORD
(* Сборка DWORD из двух WORD *)
VAR_INPUT VAR_INPUT
W1 : WORD; W1 : WORD; (* Старшее слово (биты 16..31 результата) *)
W0 : WORD; W0 : WORD; (* Младшее слово (биты 0..15 результата) *)
END_VAR END_VAR
(*----- IMPLEMENTATION -----*) (*----- IMPLEMENTATION -----*)
DWORD_OF_2WORD := SHL(WORD_TO_DWORD(W1),16) OR WORD_TO_DWORD(W0); DWORD_OF_2WORD := SHL(WORD_TO_DWORD(W1),16) OR WORD_TO_DWORD(W0);
END_FUNCTION END_FUNCTION
@@ -5,13 +5,13 @@
Path : Преобразование значений Path : Преобразование значений
Name : DW_TO_REAL Name : DW_TO_REAL
Type : FUNCTION Type : FUNCTION
Exported : 2026-07-31 11:16:16
=============================================================================== ===============================================================================
*) *)
FUNCTION DW_TO_REAL : REAL FUNCTION DW_TO_REAL : REAL
(* Побитовое переинтерпретирование DWORD как REAL (без преобразования значения) *)
VAR_INPUT VAR_INPUT
X : DWORD; X : DWORD; (* Значение для переинтерпретации *)
END_VAR END_VAR
VAR VAR
pt : POINTER TO REAL; pt : POINTER TO REAL;
@@ -5,7 +5,6 @@
Path : Преобразование значений Path : Преобразование значений
Name : NTC_termistor_T Name : NTC_termistor_T
Type : FUNCTION Type : FUNCTION
Exported : 2026-07-31 11:16:16
=============================================================================== ===============================================================================
*) *)
@@ -5,13 +5,13 @@
Path : Преобразование значений Path : Преобразование значений
Name : REAL_TO_DW Name : REAL_TO_DW
Type : FUNCTION Type : FUNCTION
Exported : 2026-07-31 11:16:16
=============================================================================== ===============================================================================
*) *)
FUNCTION REAL_TO_DW : DWORD FUNCTION REAL_TO_DW : DWORD
(* Побитовое переинтерпретирование REAL как DWORD (без преобразования значения) *)
VAR_INPUT VAR_INPUT
X : REAL; X : REAL; (* Значение для переинтерпретации *)
END_VAR END_VAR
VAR VAR
pt : POINTER TO DWORD; pt : POINTER TO DWORD;
@@ -5,16 +5,15 @@
Path : Преобразование значений Path : Преобразование значений
Name : WORD_OF_DWORD Name : WORD_OF_DWORD
Type : FUNCTION Type : FUNCTION
Exported : 2026-07-31 11:16:16
=============================================================================== ===============================================================================
*) *)
FUNCTION WORD_OF_DWORD : WORD FUNCTION WORD_OF_DWORD : WORD
(* Извлечение одного из двух 16-битных слов DWORD *)
VAR_INPUT VAR_INPUT
in : DWORD; in : DWORD; (* Исходное значение *)
N : BYTE; N : BYTE; (* Номер извлекаемого слова: 0 - младшее (биты 0..15), 1 - старшее (биты 16..31) *)
END_VAR END_VAR
(*----- IMPLEMENTATION -----*) (*----- IMPLEMENTATION -----*)
WORD_OF_DWORD := DWORD_TO_WORD(SHR(in,SHL(n,4))); WORD_OF_DWORD := DWORD_TO_WORD(SHR(in,SHL(n,4)));
END_FUNCTION END_FUNCTION
-7
View File
@@ -32,13 +32,8 @@ from __future__ import print_function
import os import os
import codecs import codecs
import re import re
import datetime
PRAGMA_RE = re.compile(r"^\s*\{[^}]*\}\s*") PRAGMA_RE = re.compile(r"^\s*\{[^}]*\}\s*")
# Единая метка времени на весь запуск экспорта (одинаковая во всех файлах).
EXPORT_TIMESTAMP = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# Имена служебных узлов дерева, которые не нужно показывать в поле Path # Имена служебных узлов дерева, которые не нужно показывать в поле Path
# (сравнение без учёта регистра). Сами узлы всё равно обходятся -- пропускаются # (сравнение без учёта регистра). Сами узлы всё равно обходятся -- пропускаются
# только их имена при формировании пути. # только их имена при формировании пути.
@@ -197,7 +192,6 @@ def build_header(project_name, device_name, path_parts, obj_name, type_label):
Path : Application/Logic/Motion Path : Application/Logic/Motion
Name : FB_Axis Name : FB_Axis
Type : FUNCTION_BLOCK Type : FUNCTION_BLOCK
Exported : 2026-07-30 14:32:18
=============================================================================== ===============================================================================
*) *)
""" """
@@ -211,7 +205,6 @@ def build_header(project_name, device_name, path_parts, obj_name, type_label):
" Path : %s" % rel_path, " Path : %s" % rel_path,
" Name : %s" % obj_name, " Name : %s" % obj_name,
" Type : %s" % type_label, " Type : %s" % type_label,
" Exported : %s" % EXPORT_TIMESTAMP,
sep, sep,
"*)", "*)",
"", "",
+556
View File
@@ -0,0 +1,556 @@
# encoding:utf-8
# import_plc_src.py
# Обратный скрипт к export_plc_src.py
# Читает .st файлы из plc_src/ и создаёт/обновляет соответствующие
# POU / METHOD / PROPERTY / ACTION / GVL / DUT в открытом проекте CODESYS.
#
# ВАЖНО про ACTION / END_ACTION / END_PROGRAM и т.п.:
# CODESYS сам оборачивает textual_declaration / textual_implementation в
# неявный блок в зависимости от типа объекта. Заголовок "ACTION Имя :" и
# закрывающие END_* НЕ нужно (и часто нельзя) писать в текст через API --
# они генерируются автоматически самим объектом. Поэтому при импорте эти
# строки сначала вырезаются (см. strip_end_keyword / parse_block_text),
# и в API передаётся только "голое" тело.
#
# Запуск: аналогично export_plc_src.py --
# Tools -> Scripting -> Execute Script File, либо --runscript=...
#
# Скрипт по умолчанию работает в режиме импорта.
# Для работы в режиме DRY_RUN (где он только печатает, что бы сделал,
# ничего не создавая и не перезаписывая) поставь DRY_RUN = True.
#
# ПРЕДУПРЕЖДЕНИЕ: сигнатуры create_pou / create_method / create_property /
# create_action / create_global_var_list / create_dut могут отличаться
# между версиями CODESYS. Значения ниже рассчитаны на типовой scripting
# API (аналог CODESYS V3.5 SP17). Если создание объекта падает с ошибкой --
# смотри Tools -> Scripting -> справку по API твоей версии и поправь
# соответствующую get_or_create_* функцию.
from __future__ import print_function
import os
import re
import codecs
# Должно совпадать с EXCLUDED_PATH_NAMES в export_plc_src.py --
# именно эти узлы дерева были "прозрачно" пропущены при формировании Path.
EXCLUDED_PATH_NAMES = set(["application", "plc logic"])
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",
}
# Терпимо к вариациям пробелов/кол-ва дефисов, которые может внести
# автоформатирование редактора, напр. "(* ----- IMPLEMENTATION ----- *)"
# вместо канонического "(*----- IMPLEMENTATION -----*)".
IMPL_MARKER_RE = re.compile(r"\(\*\s*-{3,}\s*IMPLEMENTATION\s*-{3,}\s*\*\)")
HEADER_RE = re.compile(
r"^\(\*[ \t]*\n"
r"=+[ \t]*\n"
r"[ \t]*Project[ \t]*:[ \t]*(?P<project>[^\n]*?)[ \t]*\n"
r"[ \t]*Device[ \t]*:[ \t]*(?P<device>[^\n]*?)[ \t]*\n"
r"[ \t]*Path[ \t]*:[ \t]*(?P<path>[^\n]*?)[ \t]*\n"
r"[ \t]*Name[ \t]*:[ \t]*(?P<name>[^\n]*?)[ \t]*\n"
r"[ \t]*Type[ \t]*:[ \t]*(?P<type>[^\n]*?)[ \t]*\n"
r"=+[ \t]*\n"
r"[ \t]*\*\)[ \t]*\n",
re.MULTILINE,
)
def normalize_newlines(text):
"""CODESYS/IronPython на Windows при записи через codecs.open иногда
транслирует каждый \\n в \\r\\n даже там, где \\r уже стоит вручную --
получается \\r\\r\\n вместо \\r\\n. Лишние оказываются именно \\r, а не
\\n, поэтому надёжная нормализация -- просто выбросить все \\r: структура
строк (в т.ч. пустые строки) при этом сохраняется, независимо от того,
что реально лежит на диске (\\r\\n, \\r\\r\\n, голый \\n, смесь)."""
return text.replace("\r", "")
POU_TYPES = ("PROGRAM", "FUNCTION_BLOCK", "FUNCTION", "INTERFACE")
# ==== НАСТРОЙКА ====
DRY_RUN = False # поставь True, дле тествового запуска без записи в проект, False -- реальный импорт
MANUAL_INPUT_ROOT = r"" # пусто -- искать plc_src рядом с .project
# =====================================================
# ---------- вспомогательные функции (частично повторяют export_plc_src.py) ----------
def get_project_file_path(proj):
for attr in ("path", "get_path", "project_path", "full_path"):
try:
val = getattr(proj, attr)
val = val() if callable(val) else val
if val:
return val
except Exception:
pass
try:
s = str(proj)
m = re.search(r"stPath=(.*?)\)", s)
if m:
return m.group(1)
except Exception:
pass
return None
def safe_name(obj):
try:
return obj.get_name(False)
except Exception:
return "unnamed"
def is_device_node(obj):
try:
return bool(getattr(obj, "is_device", False))
except Exception:
return False
def find_child(node, name):
for c in node.get_children(False):
if safe_name(c) == name:
return c
return None
def find_device(proj, device_name):
for c in proj.get_children(False):
if is_device_node(c) and safe_name(c) == device_name:
return c
return None
def find_entry_container(root):
"""Спускается через служебные узлы (Application / PLC Logic и т.п.) --
те же имена, что фильтровались при построении Path на экспорте."""
node = root
while True:
matched = None
for c in node.get_children(False):
if safe_name(c).strip().lower() in EXCLUDED_PATH_NAMES:
matched = c
break
if matched is None:
return node
node = matched
def get_or_create_folder(parent, name):
child = find_child(parent, name)
if child is not None:
return child
try:
parent.create_folder(name, "")
except TypeError:
parent.create_folder(name)
# create_folder может не возвращать созданный объект (возвращает None) --
# поэтому вместо доверия return-значению ищем папку заново по имени.
child = find_child(parent, name)
if child is None:
raise RuntimeError("create_folder('%s') не создал папку и не найден через find_child" % name)
return child
def navigate_path(container, path_parts):
node = container
for part in path_parts:
node = get_or_create_folder(node, part)
return node
# ---------- разбор .st файла на блоки ----------
def strip_end_keyword(text, type_label):
"""Убирает END_* в конце текста -- CODESYS добавляет его сам, в API
он не передаётся. Работает с уже нормализованным (\\n) текстом."""
kw = END_KEYWORDS.get(type_label)
if not kw:
return text
pattern = re.compile(r"\n\s*" + re.escape(kw) + r"\s*\n?\s*$")
new_text, n = pattern.subn("", text, count=1)
return new_text if n else text
def split_blocks(raw_text):
"""raw_text уже нормализован (только \\n). Один .st файл может содержать
несколько блоков: основной POU и дописанные METHOD/PROPERTY/ACTION.
Разбиваем по заголовкам-паспортам."""
matches = list(HEADER_RE.finditer(raw_text))
blocks = []
for i, m in enumerate(matches):
start = m.end()
end = matches[i + 1].start() if i + 1 < len(matches) else len(raw_text)
body = raw_text[start:end]
blocks.append((m.groupdict(), body))
return blocks
def to_crlf(text):
"""CODESYS ожидает обычный текст -- перед передачей в API возвращаем
стандартный \\r\\n (на входе текст уже нормализован в \\n)."""
if not text:
return text
return text.replace("\n", "\r\n")
def parse_block_text(body, type_label):
"""Возвращает (declaration_text_or_None, implementation_text_or_None)
-- зеркало build_pou_text / build_action_text из export_plc_src.py.
body уже нормализован (только \\n)."""
body = body.strip("\n")
if not body:
return None, None
body = strip_end_keyword(body, type_label)
if type_label == "ACTION":
# первая строка "ACTION Имя :" синтезирована на экспорте вручную --
# убираем её, для API нужна только implementation
parts = body.split("\n", 1)
impl = parts[1].strip("\n") if len(parts) > 1 else ""
return None, (to_crlf(impl) + "\r\n" if impl else "")
m = IMPL_MARKER_RE.search(body)
if m:
decl, impl = body[:m.start()], body[m.end():]
decl = decl.strip("\n")
impl = impl.strip("\n")
return (to_crlf(decl) + "\r\n" if decl else ""), (to_crlf(impl) + "\r\n" if impl else "")
# GVL / DUT -- только декларация, END_* для них не определён (и не нужен)
decl = body.strip("\n")
return (to_crlf(decl) + "\r\n" if decl else ""), None
def extract_return_type(declaration, type_label, object_name):
"""Извлекает return type из FUNCTION/METHOD/PROPERTY declaration."""
if not declaration:
return None
text = normalize_newlines(declaration)
# Удаляем только ведущие прагмы для анализа первой строки.
while True:
new_text = re.sub(r"^\s*\{[^}]*\}\s*", "", text, count=1)
if new_text == text:
break
text = new_text
for line in text.split("\n"):
line = line.strip()
if not line:
continue
# Для анализа нам не нужны inline block-comments.
line = re.sub(r"\(\*.*?\*\)", "", line).strip()
m = re.match(
r"^" + re.escape(type_label) + r"\s+\S+\s*:\s*(.+?)\s*$",
line,
re.IGNORECASE
)
if m:
return m.group(1).strip()
# Если это нужный заголовок, но return type отсутствует.
if line.upper().startswith(type_label + " "):
return None
return None
# ---------- запись в объекты проекта ----------
def set_declaration(obj, text):
if not text:
return
try:
obj.textual_declaration.replace(text)
except Exception as e:
print(" ! ошибка записи declaration: %s" % e)
def set_implementation(obj, text):
if not text:
return
try:
obj.textual_implementation.replace(text)
except Exception as e:
print(" ! ошибка записи implementation: %s" % e)
def get_or_create_pou(parent, name, type_label, declaration_text):
"""Создаёт PROGRAM/FB/FUNCTION/INTERFACE через специализированный API."""
existing = find_child(parent, name)
if existing is not None:
print(" = %s '%s' уже существует" % (type_label, name))
return existing, False
try:
if type_label == "PROGRAM":
create_program = getattr(parent, "create_program", None)
if create_program is not None:
return create_program(name, ImplementationLanguages.st), True
return parent.create_pou(name, PouType.Program, ImplementationLanguages.st), True
if type_label == "FUNCTION_BLOCK":
create_fb = getattr(parent, "create_function_block", None)
if create_fb is not None:
return create_fb(name, ImplementationLanguages.st), True
return parent.create_pou(name, PouType.FunctionBlock, ImplementationLanguages.st), True
if type_label == "FUNCTION":
return_type = extract_return_type(declaration_text, "FUNCTION", name)
if not return_type:
raise RuntimeError("не удалось определить return type FUNCTION '%s'" % name)
print(" -> FUNCTION '%s' : %s" % (name, return_type))
create_function = getattr(parent, "create_function", None)
if create_function is not None:
return create_function(name, return_type, ImplementationLanguages.st), True
# Fallback для старых версий API.
try:
return parent.create_pou(name, PouType.Function, return_type, ImplementationLanguages.st), True
except TypeError:
return parent.create_pou(name, PouType.Function, ImplementationLanguages.st, return_type), True
if type_label == "INTERFACE":
return parent.create_interface(name), True
raise RuntimeError("неизвестный тип POU '%s'" % type_label)
except Exception as e:
print(" ! НЕ УДАЛОСЬ создать %s '%s': %s" % (type_label, name, e))
return None, False
def get_or_create_method(parent_pou, name, declaration_text):
existing = find_child(parent_pou, name)
if existing is not None:
print(" = METHOD '%s' уже существует" % name)
return existing, False
return_type = extract_return_type(declaration_text, "METHOD", name)
try:
return parent_pou.create_method(
name, return_type, ImplementationLanguages.st
), True
except TypeError:
try:
return parent_pou.create_method(name, return_type), True
except Exception as e:
print(" ! НЕ УДАЛОСЬ создать METHOD '%s': %s" % (name, e))
return None, False
except Exception as e:
print(" ! НЕ УДАЛОСЬ создать METHOD '%s': %s" % (name, e))
return None, False
def get_or_create_property(parent_pou, name, declaration_text):
existing = find_child(parent_pou, name)
if existing is not None:
print(" = PROPERTY '%s' уже существует" % name)
return existing, False
return_type = extract_return_type(declaration_text, "PROPERTY", name)
if not return_type:
print(" ! НЕ УДАЛОСЬ определить return type PROPERTY '%s'" % name)
return None, False
try:
return parent_pou.create_property(
name, return_type, ImplementationLanguages.st
), True
except TypeError:
try:
return parent_pou.create_property(name, return_type), True
except Exception as e:
print(" ! НЕ УДАЛОСЬ создать PROPERTY '%s': %s" % (name, e))
return None, False
except Exception as e:
print(" ! НЕ УДАЛОСЬ создать PROPERTY '%s': %s" % (name, e))
return None, False
def get_or_create_action(parent_pou, name):
existing = find_child(parent_pou, name)
if existing is not None:
print(" = ACTION '%s' уже существует" % name)
return existing, False
try:
return parent_pou.create_action(name, ImplementationLanguages.st), True
except TypeError:
try:
return parent_pou.create_action(name), True
except Exception as e:
print(" ! НЕ УДАЛОСЬ создать ACTION '%s': %s" % (name, e))
return None, False
except Exception as e:
print(" ! НЕ УДАЛОСЬ создать ACTION '%s': %s" % (name, e))
return None, False
def get_or_create_gvl(parent, name):
existing = find_child(parent, name)
if existing is not None:
return existing, False
for method_name in ("create_gvl", "create_global_variable_list", "create_global_var_list"):
method = getattr(parent, method_name, None)
if method is None:
continue
try:
obj = method(name)
return obj, True
except Exception as e:
print(" ! не удалось создать GVL '%s' через %s: %s" % (name, method_name, e))
print(" ! не удалось создать GVL '%s': подходящий метод API не найден" % name)
return None, False
def get_or_create_dut(parent, name):
existing = find_child(parent, name)
if existing is not None:
return existing, False
try:
obj = parent.create_dut(name, DutType.Structure) # noqa: F821 -- перезапишется текстом ниже
return obj, True
except Exception as e:
print(" ! не удалось создать DUT '%s': %s" % (name, e))
return None, False
# ---------- обработка одного файла ----------
def process_st_file(proj, filepath, dry_run):
with codecs.open(filepath, "r", encoding="utf-8") as f:
raw = f.read()
raw = normalize_newlines(raw)
blocks = split_blocks(raw)
if not blocks:
print(" (пропущено, заголовок не найден)")
return
parent_pou = None # POU, к которому относятся идущие следом METHOD/PROPERTY/ACTION
for header, body in blocks:
device_name = header["device"].strip()
path_str = header["path"].strip()
name = header["name"].strip()
type_label = header["type"].strip()
path_parts = [] if path_str == "-" else path_str.split("/")
if device_name == "-":
root = proj
else:
root = find_device(proj, device_name)
if root is None:
print(" ! устройство '%s' не найдено, пропуск '%s'" % (device_name, name))
continue
container = find_entry_container(root)
target_parent = navigate_path(container, path_parts)
decl_text, impl_text = parse_block_text(body, type_label)
print(" %s: %s/%s" % (type_label, path_str, name))
if dry_run:
continue
if type_label in POU_TYPES:
obj, _ = get_or_create_pou(target_parent, name, type_label, decl_text)
if obj is None:
parent_pou = None
continue
set_declaration(obj, decl_text)
set_implementation(obj, impl_text)
parent_pou = obj
elif type_label in ("METHOD", "PROPERTY"):
if parent_pou is None:
print(" ! нет родительского POU в этом файле, пропуск")
continue
if type_label == "METHOD":
obj, _ = get_or_create_method(parent_pou, name, decl_text)
else:
obj, _ = get_or_create_property(parent_pou, name, decl_text)
if obj is None:
continue
set_declaration(obj, decl_text)
set_implementation(obj, impl_text)
elif type_label == "ACTION":
if parent_pou is None:
print(" ! нет родительского POU в этом файле, пропуск")
continue
obj, _ = get_or_create_action(parent_pou, name)
if obj is None:
continue
set_implementation(obj, impl_text)
elif type_label == "GVL":
obj, _ = get_or_create_gvl(target_parent, name)
if obj is None:
continue
set_declaration(obj, decl_text)
elif type_label == "DUT":
obj, _ = get_or_create_dut(target_parent, name)
if obj is None:
continue
set_declaration(obj, decl_text)
else:
print(" ! неизвестный тип '%s', пропуск" % type_label)
def find_st_files(root):
result = []
for dirpath, _dirnames, filenames in os.walk(root):
for fn in filenames:
if fn.lower().endswith(".st"):
result.append(os.path.join(dirpath, fn))
return sorted(result)
# ==== ТОЧКА ВХОДА ====
proj = projects.primary # noqa: F821 -- глобал CODESYS scripting
if proj is None:
print("ОШИБКА: нет открытого проекта. Открой .project перед запуском скрипта.")
else:
project_file = get_project_file_path(proj)
if MANUAL_INPUT_ROOT:
INPUT_ROOT = MANUAL_INPUT_ROOT
print("Папка ввода задана вручную: %s" % INPUT_ROOT)
elif project_file:
INPUT_ROOT = os.path.join(os.path.dirname(project_file), "plc_src")
print("Проект найден: %s" % project_file)
print("Папка ввода определена автоматически: %s" % INPUT_ROOT)
else:
INPUT_ROOT = None
print("ОШИБКА: не удалось определить путь к .project. Задай MANUAL_INPUT_ROOT.")
if INPUT_ROOT and os.path.isdir(INPUT_ROOT):
print("--- Импорт из %s (DRY_RUN=%s) ---" % (INPUT_ROOT, DRY_RUN))
files = find_st_files(INPUT_ROOT)
print("Найдено файлов: %d" % len(files))
for fp in files:
print("Файл: %s" % fp)
process_st_file(proj, fp, DRY_RUN)
print("--- Импорт завершён ---")
if DRY_RUN:
print("Это был DRY-RUN -- ничего не записано в проект.")
print("Проверь вывод выше и поставь DRY_RUN = False для реального импорта.")
elif INPUT_ROOT:
print("ОШИБКА: папка не найдена: %s" % INPUT_ROOT)