Compare commits
9
Commits
3d52fdec57
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
edba921ffa | ||
|
|
297dc62f48 | ||
|
|
34f5e2405a | ||
|
|
8017d65662 | ||
|
|
787f875581 | ||
|
|
1b7aa09b0b | ||
|
|
5519d81c5a | ||
|
|
a62be1c4bd | ||
|
|
9627b0abb9 |
+106
-8
@@ -1,10 +1,108 @@
|
||||
# git ignore file
|
||||
#.gitignore
|
||||
# ============================================================
|
||||
# .gitignore для проекта CODESYS 3.5
|
||||
# ============================================================
|
||||
|
||||
# files
|
||||
|
||||
# directories
|
||||
|
||||
# build results
|
||||
*.opt
|
||||
# ---- Бэкапы / временные файлы проекта ----
|
||||
*.project~backup~*
|
||||
~$*
|
||||
*.BAK
|
||||
*.bak
|
||||
*.tmp
|
||||
*.temp
|
||||
*.autosave
|
||||
*.~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
@@ -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>"
|
||||
```
|
||||
@@ -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.
@@ -1,3 +1,140 @@
|
||||
# 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.
@@ -5,7 +5,6 @@
|
||||
Path : Structs
|
||||
Name : AnalogValueState
|
||||
Type : DUT
|
||||
Exported : 2026-07-31 11:16:16
|
||||
===============================================================================
|
||||
*)
|
||||
|
||||
|
||||
@@ -5,16 +5,15 @@
|
||||
Path : Fastwel
|
||||
Name : AIM721_AI
|
||||
Type : FUNCTION
|
||||
Exported : 2026-07-31 11:16:16
|
||||
===============================================================================
|
||||
*)
|
||||
|
||||
FUNCTION AIM721_AI : REAL
|
||||
(*
|
||||
Перевод значений входов модуля AIM721 из единиц АЦП в 0..20 мА
|
||||
Перевод значений входов модуля AIM721 из единиц АЦП в 0..20 мА
|
||||
*)
|
||||
VAR_INPUT
|
||||
ADC: DINT;
|
||||
ADC : DINT; (* Единицы АЦП *)
|
||||
END_VAR
|
||||
|
||||
(*----- IMPLEMENTATION -----*)
|
||||
|
||||
@@ -5,16 +5,15 @@
|
||||
Path : Fastwel
|
||||
Name : AIM722_AI
|
||||
Type : FUNCTION
|
||||
Exported : 2026-07-31 11:16:16
|
||||
===============================================================================
|
||||
*)
|
||||
|
||||
FUNCTION AIM722_AI : REAL
|
||||
(*
|
||||
Перевод значений входов модуля AIM722 из единиц АЦП в 0..20 мА
|
||||
Перевод значений входов модуля AIM722 из единиц АЦП в 0..20 мА
|
||||
*)
|
||||
VAR_INPUT
|
||||
ADC: DINT;
|
||||
ADC : DINT; (* Единицы АЦП *)
|
||||
END_VAR
|
||||
|
||||
(*----- IMPLEMENTATION -----*)
|
||||
|
||||
@@ -5,16 +5,15 @@
|
||||
Path : Fastwel
|
||||
Name : AIM723_AI
|
||||
Type : FUNCTION
|
||||
Exported : 2026-07-31 11:16:16
|
||||
===============================================================================
|
||||
*)
|
||||
|
||||
FUNCTION AIM723_AI : REAL
|
||||
(*
|
||||
Перевод значений входов модуля AIM723 из единиц АЦП в 4..20 мА
|
||||
Перевод значений входов модуля AIM723 из единиц АЦП в 4..20 мА
|
||||
*)
|
||||
VAR_INPUT
|
||||
ADC: DINT;
|
||||
ADC : DINT; (* Единицы АЦП *)
|
||||
END_VAR
|
||||
|
||||
(*----- IMPLEMENTATION -----*)
|
||||
|
||||
@@ -5,50 +5,50 @@
|
||||
Path : Fastwel
|
||||
Name : AIM725_Status
|
||||
Type : FUNCTION_BLOCK
|
||||
Exported : 2026-07-31 11:16:16
|
||||
===============================================================================
|
||||
*)
|
||||
|
||||
FUNCTION_BLOCK AIM725_Status
|
||||
(*
|
||||
Модуль формирования статуса
|
||||
Гистерезис по отключению и задержка по включению
|
||||
-1 - нет связи с датчиком
|
||||
0 - не определено
|
||||
1 - норма
|
||||
2 - предупредительный по нижнему уровню
|
||||
3 - аварийный по нижнему уровню
|
||||
4 - предупредительный по верхнему уровню,
|
||||
5 - аварийный по верхнему уровню
|
||||
Модуль формирования статуса
|
||||
Гистерезис по отключению и задержка по включению
|
||||
|
||||
-1 - нет связи с датчиком
|
||||
0 - не определено
|
||||
1 - норма
|
||||
2 - предупредительный по нижнему уровню
|
||||
3 - аварийный по нижнему уровню
|
||||
4 - предупредительный по верхнему уровню,
|
||||
5 - аварийный по верхнему уровню
|
||||
*)
|
||||
VAR_INPUT
|
||||
Input: REAL;
|
||||
fail_hi: REAL:=3.402823466E+38;
|
||||
delta_fail_hi: REAL := 0;
|
||||
time_fail_hi: TIME :=t#0s;
|
||||
warn_hi: REAL:=3.402823466E+38;
|
||||
delta_warn_hi: REAL := 0;
|
||||
time_warn_hi: TIME :=t#0s;
|
||||
warn_low: REAL:=-3.402823466E+38;
|
||||
delta_warn_low: REAL := 0;
|
||||
time_warn_low: TIME :=t#0s;
|
||||
fail_low: REAL:=-3.402823466E+38;
|
||||
delta_fail_low: REAL := 0;
|
||||
time_fail_low: TIME :=t#0s;
|
||||
Modul_status: BYTE;
|
||||
Chanel_num: BYTE;
|
||||
Input: REAL; (* Контролируемое значение *)
|
||||
fail_hi: REAL:=3.402823466E+38; (* Порог аварии по верхнему уровню *)
|
||||
delta_fail_hi: REAL := 0; (* Гистерезис возврата из fail_hi *)
|
||||
time_fail_hi: TIME :=t#0s; (* Выдержка времени на срабатывание fail_hi *)
|
||||
warn_hi: REAL:=3.402823466E+38; (* Порог предупреждения по верхнему уровню *)
|
||||
delta_warn_hi: REAL := 0; (* Гистерезис возврата из warn_hi *)
|
||||
time_warn_hi: TIME :=t#0s; (* Выдержка времени на срабатывание warn_hi *)
|
||||
warn_low: REAL:=-3.402823466E+38; (* Порог предупреждения по нижнему уровню *)
|
||||
delta_warn_low: REAL := 0; (* Гистерезис возврата из warn_low *)
|
||||
time_warn_low: TIME :=t#0s; (* Выдержка времени на срабатывание warn_low *)
|
||||
fail_low: REAL:=-3.402823466E+38; (* Порог аварии по нижнему уровню *)
|
||||
delta_fail_low: REAL := 0; (* Гистерезис возврата из fail_low *)
|
||||
time_fail_low: TIME :=T#0S; (* Выдержка времени на срабатывание fail_low *)
|
||||
time_no_signal: TIME :=T#0S; (* Выдержка времени на срабатывание no_signal *)
|
||||
Modul_status: BYTE; (* Байт статуса модуля АЦП (биты диагностики по каналам) *)
|
||||
Chanel_num: BYTE; (* Номер канала модуля (1 или 2), определяет какие биты Modul_status проверяются *)
|
||||
END_VAR
|
||||
VAR_OUTPUT
|
||||
Status: INT;
|
||||
T_mod: INT;
|
||||
Status: AnalogValueState; (* Итоговый статус аналогового значения *)
|
||||
T_mod: INT; (* Значение Input, приведенное к INT; -500 при отсутствии связи (no_signal) *)
|
||||
END_VAR
|
||||
VAR
|
||||
(* !!! ВНУТРЕННИЕ НЕВЫХОДНЫЕ ПЕРЕМЕННЫЕ!!! *)
|
||||
TON_fail_hi: TON;
|
||||
TON_warn_hi: TON;
|
||||
TON_warn_low: TON;
|
||||
TON_fail_low: TON;
|
||||
(* !!! ВНУТРЕННИЕ ВЫХОДНЫЕ ПЕРЕМЕННЫЕ!!! *)
|
||||
TON_no_signal: TON;
|
||||
fail_hi_st: BOOL;
|
||||
warn_hi_st: BOOL;
|
||||
normal_st: BOOL;
|
||||
@@ -57,36 +57,36 @@ VAR
|
||||
no_signal_st: BOOL;
|
||||
END_VAR
|
||||
(*----- IMPLEMENTATION -----*)
|
||||
no_signal_st := (Input <= - REAL#200 OR ((Modul_status.0 OR Modul_status.4 OR Modul_status.5) AND Chanel_num = 1) OR ((Modul_status.1 OR Modul_status.6 OR Modul_status.7) AND Chanel_num = 2) OR Modul_status.2 OR Modul_status.3);
|
||||
IF no_signal_st THEN T_mod := - INT#500; ELSE T_mod := REAL_TO_INT(Input); END_IF;
|
||||
TON_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 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);
|
||||
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_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;
|
||||
|
||||
TON_warn_low(IN := ((Input <= warn_low) AND NOT(no_signal_st) AND NOT(TON_fail_low.Q)), PT := time_warn_low);
|
||||
IF TON_warn_low.Q THEN warn_low_st := TRUE; ELSIF (Input > warn_low + delta_warn_low) THEN warn_low_st := FALSE; END_IF;
|
||||
TON_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;
|
||||
|
||||
TON_fail_hi(IN := ((Input >= fail_hi) AND NOT(no_signal_st)), PT := time_fail_hi);
|
||||
IF TON_fail_hi.Q THEN fail_hi_st := TRUE; ELSIF (Input < fail_hi - delta_fail_hi) THEN fail_hi_st := FALSE; END_IF;
|
||||
TON_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;
|
||||
|
||||
TON_warn_hi(IN := ((Input >= warn_hi) AND NOT(no_signal_st) AND NOT(TON_fail_hi.Q)), PT := time_warn_hi);
|
||||
IF TON_warn_hi.Q THEN warn_hi_st := TRUE; ELSIF (Input < warn_hi - delta_warn_hi) THEN warn_hi_st := FALSE; END_IF;
|
||||
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;
|
||||
|
||||
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
|
||||
Status := - INT#1;
|
||||
Status := AnalogValueState.no_signal;
|
||||
ELSIF fail_hi_st THEN
|
||||
Status := INT#5;
|
||||
Status := AnalogValueState.fail_hi;
|
||||
ELSIF fail_low_st THEN
|
||||
Status := INT#3;
|
||||
Status := AnalogValueState.fail_low;
|
||||
ELSIF warn_hi_st THEN
|
||||
Status := INT#4;
|
||||
Status := AnalogValueState.warn_hi;
|
||||
ELSIF warn_low_st THEN
|
||||
Status := INT#2;
|
||||
Status := AnalogValueState.warn_low;
|
||||
ELSIF normal_st THEN
|
||||
Status := INT#1;
|
||||
Status := AnalogValueState.normal;
|
||||
ELSE
|
||||
Status := INT#0;
|
||||
Status := AnalogValueState.undefined;
|
||||
END_IF;
|
||||
END_FUNCTION_BLOCK
|
||||
|
||||
@@ -5,16 +5,15 @@
|
||||
Path : Fastwel
|
||||
Name : AIM726_AI
|
||||
Type : FUNCTION
|
||||
Exported : 2026-07-31 11:16:16
|
||||
===============================================================================
|
||||
*)
|
||||
|
||||
FUNCTION AIM726_AI : REAL
|
||||
(*
|
||||
Перевод значений входов модуля AIM726 из единиц АЦП в 0..40 В
|
||||
Перевод значений входов модуля AIM726 из единиц АЦП в 0..40 В
|
||||
*)
|
||||
VAR_INPUT
|
||||
ADC : DWORD; (*Единицы АЦП*)
|
||||
ADC : DWORD; (* Единицы АЦП *)
|
||||
END_VAR
|
||||
(*----- IMPLEMENTATION -----*)
|
||||
AIM726_AI := DWORD_TO_REAL(ADC) * REAL#4.768372E-006;
|
||||
|
||||
@@ -5,13 +5,12 @@
|
||||
Path : Fastwel
|
||||
Name : AIM727_AI
|
||||
Type : FUNCTION
|
||||
Exported : 2026-07-31 11:16:16
|
||||
===============================================================================
|
||||
*)
|
||||
|
||||
FUNCTION AIM727_AI : REAL
|
||||
(*
|
||||
Перевод значений входов модуля AIM727 из единиц АЦП в 0..40 V
|
||||
Перевод значений входов модуля AIM727 из единиц АЦП в 0..40 V
|
||||
*)
|
||||
VAR_INPUT
|
||||
ADC: DINT; (*Единицы АЦП*)
|
||||
|
||||
@@ -5,20 +5,16 @@
|
||||
Path : Fastwel
|
||||
Name : AIM730_AO
|
||||
Type : FUNCTION
|
||||
Exported : 2026-07-31 11:16:16
|
||||
===============================================================================
|
||||
*)
|
||||
|
||||
FUNCTION AIM730_AO : WORD
|
||||
(*
|
||||
Перевод значений 0..20 мА или 4..20 мА в единицы АЦП выходов модуля AIM730
|
||||
Диапазон:
|
||||
RANGE = 0 - 0..20 мА
|
||||
RANGE = 1 - 4..20 мА
|
||||
Перевод значений 0..20 мА или 4..20 мА в единицы АЦП выходов модуля AIM730
|
||||
*)
|
||||
VAR_INPUT
|
||||
DAC : REAL; (*Миллиамперы*)
|
||||
RANGE : BYTE; (*Диапазон: 0..20 мА - 0; 4..20 мА - 1 *)
|
||||
RANGE : BYTE; (*Диапазон: 0 - 0..20 мА; 1 - 4..20 мА *)
|
||||
END_VAR
|
||||
(*----- IMPLEMENTATION -----*)
|
||||
IF (RANGE = 1) AND (DAC < 4) THEN AIM730_AO := WORD#4; RETURN;END_IF;
|
||||
|
||||
@@ -5,20 +5,16 @@
|
||||
Path : Fastwel
|
||||
Name : AIM731_AO
|
||||
Type : FUNCTION
|
||||
Exported : 2026-07-31 11:16:16
|
||||
===============================================================================
|
||||
*)
|
||||
|
||||
FUNCTION AIM731_AO : WORD
|
||||
(*
|
||||
Перевод значений -10..+10 В или 0..+10 В в единицы АЦП выходов модуля AIM731
|
||||
Диапазон:
|
||||
RANGE = 0 - -10..+10 В
|
||||
RANGE = 1 - 0..+10 В
|
||||
Перевод значений -10..+10 В или 0..+10 В в единицы АЦП выходов модуля AIM731
|
||||
*)
|
||||
VAR_INPUT
|
||||
DAC : REAL; (*Вольты*)
|
||||
RANGE : BYTE; (*Диапазон -10..+10 В - 0; 0..+10 В - 1 *)
|
||||
RANGE : BYTE; (*Диапазон: 0 - -10..+10 В; 1 - 0..+10 В *)
|
||||
END_VAR
|
||||
(*----- IMPLEMENTATION -----*)
|
||||
CASE RANGE OF
|
||||
|
||||
@@ -5,23 +5,17 @@
|
||||
Path : Fastwel
|
||||
Name : AIM791_AI
|
||||
Type : FUNCTION
|
||||
Exported : 2026-07-31 11:16:16
|
||||
===============================================================================
|
||||
*)
|
||||
|
||||
FUNCTION AIM791_AI : REAL
|
||||
(*
|
||||
Перевод значений входов модуля AIM791 из единиц АЦП в 0..5 мА / 0..20 мА / 4..20 мА
|
||||
Диапазон:
|
||||
RANGE = 0 - 0..5 мА
|
||||
RANGE = 1 - 0..20 мА
|
||||
RANGE = 2 - 4..20 мА
|
||||
Перевод значений входов модуля AIM791 из единиц АЦП в 0..5 мА / 0..20 мА / 4..20 мА
|
||||
*)
|
||||
VAR_INPUT
|
||||
ADC: WORD;
|
||||
RANGE: BYTE := 0;
|
||||
ADC: WORD; (* Значение АЦП *)
|
||||
RANGE: BYTE := 0; (* Диапазон: 0 - 0..5 мА; 1 - 0..20 мА; 2 - 4..20 мА *)
|
||||
END_VAR
|
||||
|
||||
(*----- IMPLEMENTATION -----*)
|
||||
CASE RANGE OF
|
||||
0 : AIM791_AI := WORD_TO_REAL(ADC) * REAL#7.820248722E-005;
|
||||
|
||||
@@ -5,14 +5,15 @@
|
||||
Path : Fastwel
|
||||
Name : DIM764_DI
|
||||
Type : FUNCTION
|
||||
Exported : 2026-07-31 11:16:16
|
||||
===============================================================================
|
||||
*)
|
||||
|
||||
FUNCTION DIM764_DI : REAL
|
||||
(*На выходе - Частота в Гц*)
|
||||
(*
|
||||
На выходе - Частота в Гц
|
||||
*)
|
||||
VAR_INPUT
|
||||
IN: DWORD;
|
||||
IN: DWORD; (* Значение периода/интервала с модуля, единицы АЦП *)
|
||||
RANGE: BYTE; (* 0-Period; 1-Interval; *)
|
||||
END_VAR
|
||||
(*----- IMPLEMENTATION -----*)
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
Path : Filters
|
||||
Name : ExpRunAverage
|
||||
Type : FUNCTION
|
||||
Exported : 2026-07-31 11:16:16
|
||||
===============================================================================
|
||||
*)
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
Path : Filters
|
||||
Name : ExpRunAverageAdaptive
|
||||
Type : FUNCTION
|
||||
Exported : 2026-07-31 11:16:16
|
||||
===============================================================================
|
||||
*)
|
||||
|
||||
|
||||
@@ -5,11 +5,15 @@
|
||||
Path : Filters
|
||||
Name : MultipleClick
|
||||
Type : FUNCTION_BLOCK
|
||||
Exported : 2026-07-31 11:16:16
|
||||
===============================================================================
|
||||
*)
|
||||
|
||||
FUNCTION_BLOCK MultipleClick
|
||||
(*
|
||||
Детектор серии кликов кнопки (одиночный/двойной/тройной клик).
|
||||
Считает нажатия, пока пауза между ними не превысит TimeToWait, затем фиксирует
|
||||
результат на выходах Click1/Click2/Click3 на время TimeRelease.
|
||||
*)
|
||||
VAR_INPUT
|
||||
In: BOOL;(* Вход кнопки *)
|
||||
TimeToWait: TIME:=T#500MS; (* Максимальное время между нажатиями *)
|
||||
|
||||
@@ -5,19 +5,20 @@
|
||||
Path : Filters
|
||||
Name : RunMiddleArifm
|
||||
Type : FUNCTION_BLOCK
|
||||
Exported : 2026-07-31 11:16:16
|
||||
===============================================================================
|
||||
*)
|
||||
|
||||
FUNCTION_BLOCK RunMiddleArifm
|
||||
(* Скользящая средняя *)
|
||||
(*
|
||||
Скользящая средняя
|
||||
*)
|
||||
VAR_INPUT
|
||||
In : REAL;
|
||||
In : REAL; (* Входное значение *)
|
||||
N : INT:=32; (* Размер выборки 0...32 *)
|
||||
RST : BOOL;
|
||||
RST : BOOL; (* Сброс/переинициализация буфера текущим значением In *)
|
||||
END_VAR
|
||||
VAR_OUTPUT
|
||||
Y : REAL;
|
||||
Y : REAL; (* Значение скользящей средней *)
|
||||
END_VAR
|
||||
VAR
|
||||
init: BOOL;
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
Path : Иное
|
||||
Name : DFS
|
||||
Type : PROGRAM
|
||||
Exported : 2026-07-31 11:16:16
|
||||
===============================================================================
|
||||
*)
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
Path : Общие
|
||||
Name : ALARM_1
|
||||
Type : FUNCTION_BLOCK
|
||||
Exported : 2026-07-31 11:16:16
|
||||
===============================================================================
|
||||
*)
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
Path : Общие
|
||||
Name : ALARM_2
|
||||
Type : FUNCTION_BLOCK
|
||||
Exported : 2026-07-31 11:16:16
|
||||
===============================================================================
|
||||
*)
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
Path : Общие
|
||||
Name : ALARM_3
|
||||
Type : FUNCTION_BLOCK
|
||||
Exported : 2026-07-31 11:16:16
|
||||
===============================================================================
|
||||
*)
|
||||
|
||||
|
||||
@@ -5,13 +5,12 @@
|
||||
Path : Общие
|
||||
Name : BLINK_LMP
|
||||
Type : FUNCTION_BLOCK
|
||||
Exported : 2026-07-31 11:16:16
|
||||
===============================================================================
|
||||
*)
|
||||
|
||||
FUNCTION_BLOCK BLINK_LMP
|
||||
(*
|
||||
Мигание лампами в завистмости от статуса
|
||||
Мигание лампами в завистмости от статуса
|
||||
*)
|
||||
VAR_INPUT
|
||||
State: WORD:=0; (* 0 - выключено; 1 - включено; 2 - мигание с частотой 1; 3 - мигание с частотой 2; 4 - мигание с частотой 3; *)
|
||||
@@ -20,7 +19,7 @@ VAR_INPUT
|
||||
Inp_BL_3: BOOL := FALSE; (* Пульсар миганич с частотой 3 *)
|
||||
END_VAR
|
||||
VAR_OUTPUT
|
||||
LMP: BOOL;
|
||||
LMP: BOOL; (* Управляющий сигнал на лампу *)
|
||||
END_VAR
|
||||
VAR
|
||||
END_VAR
|
||||
|
||||
@@ -5,13 +5,12 @@
|
||||
Path : Общие
|
||||
Name : BUZZER_CTRL
|
||||
Type : FUNCTION_BLOCK
|
||||
Exported : 2026-07-31 11:16:16
|
||||
===============================================================================
|
||||
*)
|
||||
|
||||
FUNCTION_BLOCK BUZZER_CTRL
|
||||
(*
|
||||
Звучание звонком лампами в завистмости от статуса с возможностью отключения звука
|
||||
Упрвелние звонком лампами в завистмости от статуса с возможностью отключения звука
|
||||
*)
|
||||
VAR_INPUT
|
||||
State: WORD:=0; (* 0 - выключено; 1 - включено; 2 - мигание с частотой 1; 3 - мигание с частотой 2; 4 - мигание с частотой 3; *)
|
||||
@@ -23,7 +22,7 @@ VAR_INPUT
|
||||
T_PermanentOFF: TIME := T#10S; (* Время удержания кнопки отключения звука для перманентного отключения *)
|
||||
END_VAR
|
||||
VAR_OUTPUT
|
||||
BUZZER: BOOL;
|
||||
BUZZER: BOOL; (* Управляющий сигнал на звонок/зуммер *)
|
||||
END_VAR
|
||||
VAR
|
||||
R_TRIG_0: R_TRIG;
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
Path : Общие
|
||||
Name : Button_acceleration
|
||||
Type : FUNCTION_BLOCK
|
||||
Exported : 2026-07-31 11:16:16
|
||||
===============================================================================
|
||||
*)
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
Path : Общие
|
||||
Name : CONFIRMATION
|
||||
Type : FUNCTION_BLOCK
|
||||
Exported : 2026-07-31 11:16:16
|
||||
===============================================================================
|
||||
*)
|
||||
|
||||
|
||||
@@ -5,21 +5,23 @@
|
||||
Path : Общие
|
||||
Name : HYST
|
||||
Type : FUNCTION_BLOCK
|
||||
Exported : 2026-07-31 11:16:16
|
||||
===============================================================================
|
||||
*)
|
||||
|
||||
FUNCTION_BLOCK HYST
|
||||
(*
|
||||
Гистерезисный компаратор с зоной нечувствительности между ON и OFF.
|
||||
Поддерживает как прямую (ON >= OFF), так и обратную (ON < OFF) логику переключения.
|
||||
*)
|
||||
VAR_INPUT
|
||||
In : REAL;
|
||||
ON : REAL;
|
||||
OFF : REAL;
|
||||
In : REAL; (* Входное контролируемое значение *)
|
||||
ON : REAL; (* Порог включения (срабатывания) *)
|
||||
OFF : REAL; (* Порог выключения (возврата) *)
|
||||
END_VAR
|
||||
VAR_OUTPUT
|
||||
Q : BOOL;
|
||||
win : BOOL;
|
||||
Q : BOOL; (* Выходное состояние компаратора *)
|
||||
win : BOOL; (* Флаг: входное значение находится в зоне гистерезиса *)
|
||||
END_VAR
|
||||
|
||||
(*----- IMPLEMENTATION -----*)
|
||||
IF ON >= OFF THEN
|
||||
IF IN < OFF THEN
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
Path : Общие
|
||||
Name : Norm_real
|
||||
Type : FUNCTION
|
||||
Exported : 2026-07-31 11:16:16
|
||||
===============================================================================
|
||||
*)
|
||||
|
||||
@@ -14,13 +13,12 @@ FUNCTION Norm_real : REAL
|
||||
Нормирует значение аналогового входа X по двум заданным граничным точкам нормировки (X1,Y1) (X2,Y2)
|
||||
*)
|
||||
VAR_INPUT
|
||||
X: REAL;
|
||||
X1: REAL;
|
||||
Y1: REAL;
|
||||
X2: REAL;
|
||||
Y2: REAL;
|
||||
X: REAL; (* Входное (ненормированное) значение *)
|
||||
X1: REAL; (* Первая точка нормировки, вход *)
|
||||
Y1: REAL; (* Первая точка нормировки, выход *)
|
||||
X2: REAL; (* Вторая точка нормировки, вход *)
|
||||
Y2: REAL; (* Вторая точка нормировки, выход *)
|
||||
END_VAR
|
||||
|
||||
(*----- IMPLEMENTATION -----*)
|
||||
IF (X2-X1 = 0) THEN Norm_real:=Y1; RETURN; END_IF;
|
||||
Norm_real:=(X-X1)*(Y2-Y1)/(X2-X1)+Y1;
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
Path : Общие
|
||||
Name : PIDcontrol
|
||||
Type : FUNCTION_BLOCK
|
||||
Exported : 2026-07-31 11:16:16
|
||||
===============================================================================
|
||||
*)
|
||||
|
||||
|
||||
@@ -5,24 +5,26 @@
|
||||
Path : Общие
|
||||
Name : Pulse_Switch
|
||||
Type : FUNCTION_BLOCK
|
||||
Exported : 2026-07-31 11:16:16
|
||||
===============================================================================
|
||||
*)
|
||||
|
||||
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
|
||||
R_tr_1: R_TRIG;
|
||||
F_tr_1: F_TRIG;
|
||||
tp_on: TP:=(PT:=t#500ms);
|
||||
tp_off: TP:=(PT:=t#500ms);
|
||||
END_VAR
|
||||
VAR_INPUT
|
||||
Switch_ctrl: BOOL;
|
||||
END_VAR
|
||||
VAR_OUTPUT
|
||||
Switch_on: BOOL;
|
||||
Switch_off: BOOL;
|
||||
END_VAR
|
||||
(*----- IMPLEMENTATION -----*)
|
||||
R_tr_1(CLK:=Switch_ctrl);
|
||||
tp_on(IN:=R_tr_1.Q, Q=>Switch_on);
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -5,35 +5,35 @@
|
||||
Path : Общие
|
||||
Name : STACK_I16
|
||||
Type : FUNCTION_BLOCK
|
||||
Exported : 2026-07-31 11:16:16
|
||||
===============================================================================
|
||||
*)
|
||||
|
||||
FUNCTION_BLOCK STACK_I16
|
||||
(* стек на 16 элементов типа INT*)
|
||||
(*
|
||||
Стек LIFO на 16 элементов типа INT
|
||||
*)
|
||||
VAR_INPUT
|
||||
Din : INT; (* Переменная для записи в стек *)
|
||||
E : BOOL := TRUE;(* Включение стека *)
|
||||
POP : BOOL; (* Извлечение из стека *)
|
||||
PUSH : BOOL; (* Запись в стек *)
|
||||
RST : BOOL; (* Обнуление стека *)
|
||||
Din : INT; (* Переменная для записи в стек *)
|
||||
E : BOOL := TRUE; (* Включение стека *)
|
||||
POP : BOOL; (* Извлечение из стека *)
|
||||
PUSH : BOOL; (* Запись в стек *)
|
||||
RST : BOOL; (* Обнуление стека *)
|
||||
END_VAR
|
||||
VAR_OUTPUT
|
||||
Dout : INT;
|
||||
EMPTY : BOOL := TRUE;
|
||||
FULL : BOOL;
|
||||
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 := 15; (* changing this value will chage the number of stored elements in the fifo *)
|
||||
n : INT := 15; (* 16 элементов *)
|
||||
END_VAR
|
||||
|
||||
(*----- IMPLEMENTATION -----*)
|
||||
IF RST THEN
|
||||
(* asynchronous reset for the fifo *)
|
||||
pt := 0;
|
||||
EMPTY := TRUE;
|
||||
FULL := FALSE;
|
||||
|
||||
@@ -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
|
||||
@@ -5,7 +5,6 @@
|
||||
Path : Общие
|
||||
Name : STATUS_ANALOG_M
|
||||
Type : FUNCTION_BLOCK
|
||||
Exported : 2026-07-31 11:16:16
|
||||
===============================================================================
|
||||
*)
|
||||
|
||||
@@ -23,31 +22,31 @@ FUNCTION_BLOCK STATUS_ANALOG_M
|
||||
5 - аварийный по верхнему уровню
|
||||
*)
|
||||
VAR_INPUT
|
||||
Input: REAL;
|
||||
fail_hi: REAL:=3.402823466E+38;
|
||||
delta_fail_hi: REAL := 0;
|
||||
time_fail_hi: TIME :=t#0s;
|
||||
warn_hi: REAL:=3.402823466E+38;
|
||||
delta_warn_hi: REAL := 0;
|
||||
time_warn_hi: TIME :=t#0s;
|
||||
warn_low: REAL:=-3.402823466E+38;
|
||||
delta_warn_low: REAL := 0;
|
||||
time_warn_low: TIME :=t#0s;
|
||||
fail_low: REAL:=-3.402823466E+38;
|
||||
delta_fail_low: REAL := 0;
|
||||
time_fail_low: TIME :=t#0s;
|
||||
no_signal: REAL:=-3.402823466E+38;
|
||||
delta_no_signal: REAL := 0;
|
||||
time_no_signal: TIME :=T#0S;
|
||||
Input: REAL; (* Контролируемое значение *)
|
||||
fail_hi: REAL:=3.402823466E+38; (* Порог аварии по верхнему уровню *)
|
||||
delta_fail_hi: REAL := 0; (* Гистерезис возврата из fail_hi (снятие при Input < fail_hi - delta_fail_hi) *)
|
||||
time_fail_hi: TIME :=t#0s; (* Выдержка времени на срабатывание fail_hi *)
|
||||
warn_hi: REAL:=3.402823466E+38; (* Порог предупреждения по верхнему уровню *)
|
||||
delta_warn_hi: REAL := 0; (* Гистерезис возврата из warn_hi *)
|
||||
time_warn_hi: TIME :=t#0s; (* Выдержка времени на срабатывание warn_hi *)
|
||||
warn_low: REAL:=-3.402823466E+38; (* Порог предупреждения по нижнему уровню *)
|
||||
delta_warn_low: REAL := 0; (* Гистерезис возврата из warn_low *)
|
||||
time_warn_low: TIME :=t#0s; (* Выдержка времени на срабатывание warn_low *)
|
||||
fail_low: REAL:=-3.402823466E+38; (* Порог аварии по нижнему уровню *)
|
||||
delta_fail_low: REAL := 0; (* Гистерезис возврата из fail_low *)
|
||||
time_fail_low: TIME :=t#0s; (* Выдержка времени на срабатывание fail_low *)
|
||||
no_signal: REAL:=-3.402823466E+38; (* Порог отсутствия связи с датчиком *)
|
||||
delta_no_signal: REAL := 0; (* Гистерезис возврата из no_signal *)
|
||||
time_no_signal: TIME :=T#0S; (* Выдержка времени на срабатывание no_signal *)
|
||||
|
||||
In_fail_hi: BOOL := FALSE;
|
||||
In_warn_hi: BOOL := FALSE;
|
||||
In_warn_low: BOOL := FALSE;
|
||||
In_fail_low: BOOL := FALSE;
|
||||
In_no_signal: BOOL := FALSE;
|
||||
In_fail_hi: BOOL := FALSE; (* Принудительная установка статуса fail_hi извне *)
|
||||
In_warn_hi: BOOL := FALSE; (* Принудительная установка статуса warn_hi извне *)
|
||||
In_warn_low: BOOL := FALSE; (* Принудительная установка статуса warn_low извне *)
|
||||
In_fail_low: BOOL := FALSE; (* Принудительная установка статуса fail_low извне *)
|
||||
In_no_signal: BOOL := FALSE; (* Принудительная установка статуса no_signal извне *)
|
||||
END_VAR
|
||||
VAR_OUTPUT
|
||||
Status: AnalogValueState;
|
||||
Status: AnalogValueState; (* Итоговый статус аналогового значения *)
|
||||
END_VAR
|
||||
VAR
|
||||
fail_hi_st: BOOL;
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
Path : Общие
|
||||
Name : STATUS_ANALOG_M_I
|
||||
Type : FUNCTION_BLOCK
|
||||
Exported : 2026-07-31 11:16:16
|
||||
===============================================================================
|
||||
*)
|
||||
|
||||
@@ -23,42 +22,39 @@ FUNCTION_BLOCK STATUS_ANALOG_M_I
|
||||
5 - аварийный по верхнему уровню
|
||||
*)
|
||||
VAR_INPUT
|
||||
Input: INT;
|
||||
fail_hi: INT:=32767;
|
||||
delta_fail_hi: INT := 0;
|
||||
time_fail_hi: TIME :=t#0s;
|
||||
warn_hi: INT:=32767;
|
||||
delta_warn_hi: INT := 0;
|
||||
time_warn_hi: TIME :=t#0s;
|
||||
warn_low: INT:=-32768;
|
||||
delta_warn_low: INT := 0;
|
||||
time_warn_low: TIME :=t#0s;
|
||||
fail_low: INT:=-32768;
|
||||
delta_fail_low: INT := 0;
|
||||
time_fail_low: TIME :=t#0s;
|
||||
no_signal: INT:=-32768;
|
||||
delta_no_signal: INT := 0;
|
||||
time_no_signal: TIME :=T#0S;
|
||||
Input: INT; (* Контролируемое значение *)
|
||||
fail_hi: INT:=32767; (* Порог аварии по верхнему уровню *)
|
||||
delta_fail_hi: INT := 0; (* Гистерезис возврата из fail_hi (снятие при Input < fail_hi - delta_fail_hi) *)
|
||||
time_fail_hi: TIME :=t#0s; (* Выдержка времени на срабатывание fail_hi *)
|
||||
warn_hi: INT:=32767; (* Порог предупреждения по верхнему уровню *)
|
||||
delta_warn_hi: INT := 0; (* Гистерезис возврата из warn_hi *)
|
||||
time_warn_hi: TIME :=t#0s; (* Выдержка времени на срабатывание warn_hi *)
|
||||
warn_low: INT:=-32768; (* Порог предупреждения по нижнему уровню *)
|
||||
delta_warn_low: INT := 0; (* Гистерезис возврата из warn_low *)
|
||||
time_warn_low: TIME :=t#0s; (* Выдержка времени на срабатывание warn_low *)
|
||||
fail_low: INT:=-32768; (* Порог аварии по нижнему уровню *)
|
||||
delta_fail_low: INT := 0; (* Гистерезис возврата из fail_low *)
|
||||
time_fail_low: TIME :=t#0s; (* Выдержка времени на срабатывание fail_low *)
|
||||
no_signal: INT:=-32768; (* Порог отсутствия связи с датчиком *)
|
||||
delta_no_signal: INT := 0; (* Гистерезис возврата из no_signal *)
|
||||
time_no_signal: TIME :=T#0S; (* Выдержка времени на срабатывание no_signal *)
|
||||
|
||||
In_fail_hi: BOOL := FALSE;
|
||||
In_warn_hi: BOOL := FALSE;
|
||||
In_warn_low: BOOL := FALSE;
|
||||
In_fail_low: BOOL := FALSE;
|
||||
In_no_signal: BOOL := FALSE;
|
||||
In_fail_hi: BOOL := FALSE; (* Принудительная установка статуса fail_hi извне *)
|
||||
In_warn_hi: BOOL := FALSE; (* Принудительная установка статуса warn_hi извне *)
|
||||
In_warn_low: BOOL := FALSE; (* Принудительная установка статуса warn_low извне *)
|
||||
In_fail_low: BOOL := FALSE; (* Принудительная установка статуса fail_low извне *)
|
||||
In_no_signal: BOOL := FALSE; (* Принудительная установка статуса no_signal извне *)
|
||||
END_VAR
|
||||
VAR_OUTPUT
|
||||
Status: AnalogValueState;
|
||||
Status: AnalogValueState; (* Итоговый статус аналогового значения *)
|
||||
END_VAR
|
||||
VAR
|
||||
(* !!! ВНУТРЕННИЕ ВЫХОДНЫЕ ПЕРЕМЕННЫЕ!!! *)
|
||||
fail_hi_st: BOOL;
|
||||
warn_hi_st: BOOL;
|
||||
normal_st: BOOL;
|
||||
warn_low_st: BOOL;
|
||||
fail_low_st: BOOL;
|
||||
no_signal_st: BOOL;
|
||||
|
||||
(* !!! ВНУТРЕННИЕ НЕВЫХОДНЫЕ ПЕРЕМЕННЫЕ!!! *)
|
||||
TON_fail_hi: TON;
|
||||
TON_warn_hi: TON;
|
||||
TON_warn_low: TON;
|
||||
|
||||
@@ -5,19 +5,20 @@
|
||||
Path : Общие
|
||||
Name : Status_Connect_Counter
|
||||
Type : FUNCTION_BLOCK
|
||||
Exported : 2026-07-31 11:16:16
|
||||
===============================================================================
|
||||
*)
|
||||
|
||||
FUNCTION_BLOCK Status_Connect_Counter
|
||||
(* Статус связи с СВУ по счетчику*)
|
||||
(*
|
||||
Статус связи с СВУ по счетчику
|
||||
*)
|
||||
VAR_INPUT
|
||||
Counter: WORD;
|
||||
T_Error: TIME; (* время для диагностирования ошибки*)
|
||||
Counter: WORD; (* Счетчик, инкрементируемый удаленным устройством (СВУ) *)
|
||||
T_Error: TIME; (* Время отсутствия изменений счетчика для диагностики обрыва связи *)
|
||||
END_VAR
|
||||
VAR_OUTPUT
|
||||
CounterIsChanged: BOOL;
|
||||
ERROR_CONNECT: BOOL;
|
||||
CounterIsChanged: BOOL; (* Флаг: счетчик изменился в текущем цикле *)
|
||||
ERROR_CONNECT: BOOL; (* Флаг ошибки связи: счетчик не менялся дольше T_Error *)
|
||||
END_VAR
|
||||
VAR
|
||||
CounterOld: WORD;
|
||||
|
||||
@@ -5,18 +5,19 @@
|
||||
Path : Общие
|
||||
Name : Status_SU_Connect
|
||||
Type : FUNCTION_BLOCK
|
||||
Exported : 2026-07-31 11:16:16
|
||||
===============================================================================
|
||||
*)
|
||||
|
||||
FUNCTION_BLOCK Status_SU_Connect
|
||||
(* Статус связи с СВУ по серцебиению*)
|
||||
(*
|
||||
Статус связи с СВУ по серцебиению
|
||||
*)
|
||||
VAR_INPUT
|
||||
HeartBit: BOOL;
|
||||
T_Pulse: TIME;
|
||||
HeartBit: BOOL; (* Сигнал "сердцебиения" от СВУ *)
|
||||
T_Pulse: TIME; (* Максимально допустимая длительность одного состояния HeartBit *)
|
||||
END_VAR
|
||||
VAR_OUTPUT
|
||||
ERROR_CONNECT: BOOL;
|
||||
ERROR_CONNECT: BOOL; (* Флаг ошибки связи: HeartBit не меняется дольше T_Pulse *)
|
||||
END_VAR
|
||||
VAR
|
||||
TON_SU_1: TON;
|
||||
|
||||
@@ -5,11 +5,15 @@
|
||||
Path : Общие
|
||||
Name : TOF_RST
|
||||
Type : FUNCTION_BLOCK
|
||||
Exported : 2026-07-31 11:16:16
|
||||
===============================================================================
|
||||
*)
|
||||
|
||||
FUNCTION_BLOCK TOF_RST
|
||||
(*
|
||||
Таймер выключения (TOF) с возможностью принудительного сброса RST.
|
||||
При снятии IN выход Q остается TRUE еще в течение PT.
|
||||
RST немедленно сбрасывает выход и таймер вне зависимости от IN.
|
||||
*)
|
||||
VAR_INPUT
|
||||
IN: BOOL; // Управляющий сигнал
|
||||
PT: TIME; // Время задержки выключения
|
||||
|
||||
@@ -5,22 +5,21 @@
|
||||
Path : Общие
|
||||
Name : TONOF
|
||||
Type : FUNCTION_BLOCK
|
||||
Exported : 2026-07-31 11:16:16
|
||||
===============================================================================
|
||||
*)
|
||||
|
||||
FUNCTION_BLOCK TONOF
|
||||
(*
|
||||
TONOF генерирует TON и TOF задержки для переменной IN
|
||||
Задержка на вход T_ON, задержка на выход T_OFF
|
||||
TONOF генерирует TON и TOF задержки для переменной IN
|
||||
Задержка на вход T_ON, задержка на выход T_OFF
|
||||
*)
|
||||
VAR_INPUT
|
||||
IN : BOOL;
|
||||
T_ON : TIME := T#0s; (* Задержка на вход *)
|
||||
T_OFF : TIME := T#0s; (* Задержка на выход *)
|
||||
IN : BOOL; (* Входной сигнал *)
|
||||
T_ON : TIME := T#0S; (* Задержка на вход *)
|
||||
T_OFF : TIME := T#0S; (* Задержка на выход *)
|
||||
END_VAR
|
||||
VAR_OUTPUT
|
||||
Q : BOOL;
|
||||
Q : BOOL; (* Выходной сигнал с учетом задержек включения/выключения *)
|
||||
END_VAR
|
||||
VAR
|
||||
X : TON;
|
||||
|
||||
@@ -5,11 +5,15 @@
|
||||
Path : Общие
|
||||
Name : TP_RST
|
||||
Type : FUNCTION_BLOCK
|
||||
Exported : 2026-07-31 11:16:16
|
||||
===============================================================================
|
||||
*)
|
||||
|
||||
FUNCTION_BLOCK TP_RST
|
||||
(*
|
||||
Формирователь одиночного импульса (TP) длительностью PT с возможностью принудительного сброса RST.
|
||||
Повторный передний фронт IN во время выполнения импульса игнорируется.
|
||||
RST немедленно прерывает выполняющийся импульс.
|
||||
*)
|
||||
VAR_INPUT
|
||||
IN: BOOL; // Управляющий сигнал
|
||||
PT: TIME; // Длительность импульса
|
||||
|
||||
@@ -5,16 +5,15 @@
|
||||
Path : Преобразование значений
|
||||
Name : DWORD_OF_2WORD
|
||||
Type : FUNCTION
|
||||
Exported : 2026-07-31 11:16:16
|
||||
===============================================================================
|
||||
*)
|
||||
|
||||
FUNCTION DWORD_OF_2WORD : DWORD
|
||||
(* Сборка DWORD из двух WORD *)
|
||||
VAR_INPUT
|
||||
W1 : WORD;
|
||||
W0 : WORD;
|
||||
W1 : WORD; (* Старшее слово (биты 16..31 результата) *)
|
||||
W0 : WORD; (* Младшее слово (биты 0..15 результата) *)
|
||||
END_VAR
|
||||
|
||||
(*----- IMPLEMENTATION -----*)
|
||||
DWORD_OF_2WORD := SHL(WORD_TO_DWORD(W1),16) OR WORD_TO_DWORD(W0);
|
||||
END_FUNCTION
|
||||
|
||||
@@ -5,13 +5,13 @@
|
||||
Path : Преобразование значений
|
||||
Name : DW_TO_REAL
|
||||
Type : FUNCTION
|
||||
Exported : 2026-07-31 11:16:16
|
||||
===============================================================================
|
||||
*)
|
||||
|
||||
FUNCTION DW_TO_REAL : REAL
|
||||
(* Побитовое переинтерпретирование DWORD как REAL (без преобразования значения) *)
|
||||
VAR_INPUT
|
||||
X : DWORD;
|
||||
X : DWORD; (* Значение для переинтерпретации *)
|
||||
END_VAR
|
||||
VAR
|
||||
pt : POINTER TO REAL;
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
Path : Преобразование значений
|
||||
Name : NTC_termistor_T
|
||||
Type : FUNCTION
|
||||
Exported : 2026-07-31 11:16:16
|
||||
===============================================================================
|
||||
*)
|
||||
|
||||
|
||||
@@ -5,13 +5,13 @@
|
||||
Path : Преобразование значений
|
||||
Name : REAL_TO_DW
|
||||
Type : FUNCTION
|
||||
Exported : 2026-07-31 11:16:16
|
||||
===============================================================================
|
||||
*)
|
||||
|
||||
FUNCTION REAL_TO_DW : DWORD
|
||||
(* Побитовое переинтерпретирование REAL как DWORD (без преобразования значения) *)
|
||||
VAR_INPUT
|
||||
X : REAL;
|
||||
X : REAL; (* Значение для переинтерпретации *)
|
||||
END_VAR
|
||||
VAR
|
||||
pt : POINTER TO DWORD;
|
||||
|
||||
@@ -5,16 +5,15 @@
|
||||
Path : Преобразование значений
|
||||
Name : WORD_OF_DWORD
|
||||
Type : FUNCTION
|
||||
Exported : 2026-07-31 11:16:16
|
||||
===============================================================================
|
||||
*)
|
||||
|
||||
FUNCTION WORD_OF_DWORD : WORD
|
||||
(* Извлечение одного из двух 16-битных слов DWORD *)
|
||||
VAR_INPUT
|
||||
in : DWORD;
|
||||
N : BYTE;
|
||||
in : DWORD; (* Исходное значение *)
|
||||
N : BYTE; (* Номер извлекаемого слова: 0 - младшее (биты 0..15), 1 - старшее (биты 16..31) *)
|
||||
END_VAR
|
||||
|
||||
(*----- IMPLEMENTATION -----*)
|
||||
WORD_OF_DWORD := DWORD_TO_WORD(SHR(in,SHL(n,4)));
|
||||
END_FUNCTION
|
||||
|
||||
@@ -32,13 +32,8 @@ from __future__ import print_function
|
||||
import os
|
||||
import codecs
|
||||
import re
|
||||
import datetime
|
||||
|
||||
PRAGMA_RE = re.compile(r"^\s*\{[^}]*\}\s*")
|
||||
|
||||
# Единая метка времени на весь запуск экспорта (одинаковая во всех файлах).
|
||||
EXPORT_TIMESTAMP = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
# Имена служебных узлов дерева, которые не нужно показывать в поле Path
|
||||
# (сравнение без учёта регистра). Сами узлы всё равно обходятся -- пропускаются
|
||||
# только их имена при формировании пути.
|
||||
@@ -197,7 +192,6 @@ def build_header(project_name, device_name, path_parts, obj_name, type_label):
|
||||
Path : Application/Logic/Motion
|
||||
Name : FB_Axis
|
||||
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,
|
||||
" Name : %s" % obj_name,
|
||||
" Type : %s" % type_label,
|
||||
" Exported : %s" % EXPORT_TIMESTAMP,
|
||||
sep,
|
||||
"*)",
|
||||
"",
|
||||
|
||||
@@ -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)
|
||||
Reference in New Issue
Block a user