UniSet 2.45.1
IEC 61131-3 — Стандартные функциональные блоки

Модуль uniset2-iec61131.js реализует стандартные функциональные блоки по IEC 61131-3:2013, Edition 3 (Tables 36–45). Все блоки вызываются циклически из uniset_on_step() через метод update().

load("uniset2-iec61131.js");

Бистабильные элементы

RS — Reset-dominant bistable (Table 36)

Триггер с доминантным сбросом. При одновременной подаче SET и RESET1 — выход сбрасывается.

Body: Q1 := NOT RESET1 AND (SET OR Q1)
SET RESET1 Q1
0 0 Q1 (без изменений)
1 0 1
0 1 0
1 1 0 (reset dominant)
const rs = new RS();
rs.update(SET, RESET1); // -> rs.Q1

SR — Set-dominant bistable (Table 37)

Триггер с доминантной установкой. При одновременной подаче SET1 и RESET — выход устанавливается.

Body: Q1 := SET1 OR (NOT RESET AND Q1)
SET1 RESET Q1
0 0 Q1 (без изменений)
1 0 1
0 1 0
1 1 1 (set dominant)
const sr = new SR();
sr.update(SET1, RESET); // -> sr.Q1

Детекторы фронтов

R_TRIG — Rising edge detector (Table 38)

Детектор нарастающего фронта. Q = true в течение одного цикла при переходе CLK из false в true.

Body: Q := CLK AND NOT M; M := CLK;
const rt = new R_TRIG();
function uniset_on_step() {
rt.update(in_Button);
if (rt.Q) {
// Нажатие кнопки (один цикл)
}
}

F_TRIG — Falling edge detector (Table 39)

Детектор спадающего фронта. Q = true в течение одного цикла при переходе CLK из true в false.

Body: Q := NOT CLK AND M; M := CLK;
const ft = new F_TRIG();
function uniset_on_step() {
ft.update(in_Button);
if (ft.Q) {
// Отпускание кнопки (один цикл)
}
}

Счётчики

CTU — Up counter (Table 40)

Счётчик вверх. Считает нарастающие фронты на CU. Q = true когда CV >= PV.

Body:
IF RESET THEN CV := 0;
ELSIF CU THEN CV := CV + 1;
END_IF;
Q := (CV >= PV);
Вход/Выход Тип Описание
CU BOOL (R_EDGE) Счётный вход (по фронту)
RESET BOOL Сброс CV в 0
PV INT Уставка (задаётся в конструкторе)
Q BOOL Выход: CV >= PV
CV INT Текущее значение счётчика
const ctu = new CTU(10); // PV = 10
function uniset_on_step() {
ctu.update(in_Pulse, in_Reset);
out_CountReached = ctu.Q ? 1 : 0;
}

CTD — Down counter (Table 41)

Счётчик вниз. Считает вниз по фронтам CD. Q = true когда CV <= 0.

Body:
IF LOAD THEN CV := PV;
ELSIF CD AND (CV > 0) THEN CV := CV - 1;
END_IF;
Q := (CV <= 0);
Вход/Выход Тип Описание
CD BOOL (R_EDGE) Счётный вход (по фронту)
LOAD BOOL Загрузка PV в CV
PV INT Уставка (задаётся в конструкторе)
Q BOOL Выход: CV <= 0
CV INT Текущее значение счётчика
const ctd = new CTD(5); // PV = 5
ctd.update(false, true); // LOAD: CV = 5
ctd.update(pulse, false); // счёт вниз по фронтам

CTUD — Up/Down counter (Table 42)

Реверсивный счётчик. Считает вверх по CU, вниз по CD. RESET имеет приоритет над LOAD.

Body:
IF RESET THEN CV := 0;
ELSIF LOAD THEN CV := PV;
ELSE
IF CU THEN CV := CV + 1; END_IF;
IF CD AND (CV > 0) THEN CV := CV - 1; END_IF;
END_IF;
QU := (CV >= PV);
QD := (CV <= 0);
Вход/Выход Тип Описание
CU BOOL (R_EDGE) Счёт вверх (по фронту)
CD BOOL (R_EDGE) Счёт вниз (по фронту)
RESET BOOL Сброс CV в 0
LOAD BOOL Загрузка PV в CV
PV INT Уставка (задаётся в конструкторе)
QU BOOL Выход: CV >= PV
QD BOOL Выход: CV <= 0
CV INT Текущее значение счётчика
const ctud = new CTUD(100); // PV = 100
ctud.update(up_pulse, down_pulse, reset, load);
// ctud.QU, ctud.QD, ctud.CV

Таймеры

Все таймеры используют Date.now() для отслеживания времени и должны вызываться циклически из uniset_on_step(). Точность ограничена интервалом цикла (~150 мс по умолчанию).

TON — On-delay timer (Table 43)

Таймер задержки включения. При IN=true выход Q становится true через PT миллисекунд. При IN=false — Q и ET сбрасываются немедленно.

IN: ___/‾‾‾‾‾‾‾‾‾‾‾‾\___
Q: _________/‾‾‾‾‾‾\___
ET: ___/‾‾‾‾‾|PT|___\___
<-PT->
Вход/Выход Тип Описание
IN BOOL Вход разрешения
PT TIME (ms) Уставка времени (задаётся в конструкторе)
Q BOOL Выход (true после задержки)
ET TIME (ms) Прошедшее время (0..PT)
const ton = new TON(3000); // PT = 3 секунды
function uniset_on_step() {
ton.update(in_Enable);
out_Delayed = ton.Q ? 1 : 0;
}

TOF — Off-delay timer (Table 44)

Таймер задержки выключения. При IN=true — Q немедленно true. При IN=false — Q остаётся true ещё PT миллисекунд.

IN: ___/‾‾‾‾‾‾\____________
Q: ___/‾‾‾‾‾‾‾‾‾‾‾‾\______
ET: ___________/‾‾‾‾‾|PT|___
<-PT->
Вход/Выход Тип Описание
IN BOOL Вход разрешения
PT TIME (ms) Уставка времени (задаётся в конструкторе)
Q BOOL Выход (задержанное выключение)
ET TIME (ms) Прошедшее время (0..PT)
const tof = new TOF(5000); // PT = 5 секунд
function uniset_on_step() {
tof.update(in_MotorRunning);
out_CoolingFan = tof.Q ? 1 : 0; // вентилятор работает ещё 5с после останова
}

TP — Pulse timer (Table 45)

Таймер-формирователь импульса. По нарастающему фронту IN выдаёт импульс длительностью PT. Импульс выполняется до конца независимо от IN. Повторный запуск возможен только после завершения импульса И возврата IN в false.

IN: ___/‾‾‾‾‾‾‾‾‾‾‾‾\___/‾‾‾\___
Q: ___/‾‾‾‾‾‾\__________/‾‾‾‾‾‾\
ET: ___/‾‾‾‾‾‾|PT|___\___/‾‾‾‾‾‾|
<-PT-> <-PT->
Вход/Выход Тип Описание
IN BOOL Вход запуска (по фронту)
PT TIME (ms) Длительность импульса (задаётся в конструкторе)
Q BOOL Выход импульса
ET TIME (ms) Прошедшее время (0..PT)
const tp = new TP(500); // импульс 500 мс
function uniset_on_step() {
tp.update(in_Trigger);
out_Pulse = tp.Q ? 1 : 0;
}

Пример: управление термостатом

Полный пример программы на JScript с использованием стандартных функциональных блоков:

load("uniset2-iec61131.js");
uniset_inputs = [
{ name: "AI_Temperature_S" },
{ name: "DI_Enable_S" },
{ name: "DI_Reset_S" }
];
uniset_outputs = [
{ name: "DO_Heater_C" },
{ name: "DO_Alarm_C" },
{ name: "AO_CycleCount_C" }
];
// Конфигурация (аналог STRUCT в ST)
let config = { setpoint: 20.0, hysteresis: 2.0, maxTemp: 95.0 };
let state = 0;
// Функциональные блоки
const startEdge = new R_TRIG(); // детектор нарастающего фронта
const onDelay = new TON(5000); // задержка включения 5с
const cycleCounter = new CTU(100); // счётчик до 100
const heaterLatch = new RS(); // RS-триггер для нагревателя
function uniset_on_step() {
// Масштабирование: целочисленный датчик → REAL (scale 100)
let temperature = in_AI_Temperature_S / 100;
// Детекция фронта кнопки Enable
startEdge.update(!!in_DI_Enable_S);
if (startEdge.Q) {
state = 1;
}
// Автомат состояний
switch (state) {
case 0: // Ожидание
out_DO_Heater_C = 0;
break;
case 1: // Работа
// Гистерезисное управление нагревателем через RS-триггер
heaterLatch.update(
temperature < config.setpoint - config.hysteresis, // SET: включить если холодно
temperature > config.setpoint + config.hysteresis // RESET1: выключить если горячо
);
out_DO_Heater_C = heaterLatch.Q1 ? 1 : 0;
// Подсчёт циклов включения нагревателя
onDelay.update(heaterLatch.Q1);
cycleCounter.update(onDelay.Q, !!in_DI_Reset_S);
out_AO_CycleCount_C = cycleCounter.CV;
break;
case 2: // Авария
out_DO_Heater_C = 0;
break;
}
// Контроль перегрева
if (temperature > config.maxTemp) {
out_DO_Alarm_C = 1;
state = 2;
}
// Сброс
if (in_DI_Reset_S) {
state = 0;
out_DO_Alarm_C = 0;
}
}

Запуск:

uniset2-jscript --js-file thermostat.js --confile configure.xml --js-name JSProxy1

Сводная таблица

Блок Тип Таблица IEC Описание
RS Бистабильный Table 36 Триггер с доминантным сбросом
SR Бистабильный Table 37 Триггер с доминантной установкой
R_TRIG Детектор фронта Table 38 Нарастающий фронт
F_TRIG Детектор фронта Table 39 Спадающий фронт
CTU Счётчик Table 40 Счётчик вверх
CTD Счётчик Table 41 Счётчик вниз
CTUD Счётчик Table 42 Реверсивный счётчик
TON Таймер Table 43 Задержка включения
TOF Таймер Table 44 Задержка выключения
TP Таймер Table 45 Формирователь импульса

Ссылки

  • IEC 61131-3:2013 — Programmable controllers, Part 3: Programming languages
  • PLCopen Function Blocks — спецификация стандартных функциональных блоков