1. Общий шаблон обработки событий
# Программа демонстрирует общий шаблон обработки событий и часть возможностей языка программирования EXT.
# При отпускании кнопки вскрытия корпуса контроллера (тампера) будет происходить переключение реле 1.
proc main() # основная подпрограмма (точка входа)
{
var e = $get_event_id(); # получение обрабатываемого события
if e == $EVT_INIT { # обработка инициализации
$set_event_mask($EM_SYSTEM); # разрешение системных событий
} else if e == $EVT_CASE_OPEN { # обработка вскрытия корпуса контроллера
toggle(1); # переключение реле 1
}
}
proc toggle(n) # подпрограмма управления выходом
{ # n - номер выхода
var s = $get_output_state(n); # получение текущего состояния
$set_output_state(n, !s); # установка инверсного состояния
}
2. Одновременный контроль и управление по одному входу
# Пример программы для одновременного контроля и управления по одному входу.
#
# Управление осуществляется встроенной логикой входа.
#
# В примере предполагается, что ко входу 1 подключен термодатчик RTD.
# Необходимые настройки входа 1:
# - тип входа: RTD-03 или RTD-04
# - тревожный диапазон: низкий гистерезисный (заданы необходимые границы температуры для управления)
# - круглосуточный контроль: включен
# - все реле/выходы управляются: напрямую входом
# - задана реакция реле/выходов на изменение состояния входа
#
# Контроль осуществляется программируемой логикой.
#
# Диапазон тревожного оповещения задается константой DELTA_T в градусах относительно нижней и верхней границы управления (см. настройки входа 1).
# Для оповещения о выходе температуры за тревожные границы должно быть настроено соединение:
# - задан номер телефона
# - задан пароль (зависит от типа оповещения)
# - разрешен необходимый тип оповещения для In1
const INPUT_N = 1; # номер входа с термодатчиком
const EVT_ACTIVE = $EVT_INPUT1_ACTIVE; # идентификатор активного состояния входа
const EVT_PASSIVE = $EVT_INPUT1_PASSIVE; # идентификатор пассивного состояния входа
const POLL_PERIOD = 10; # период опроса входа (1 единица = 100 мс)
const FRACT = 8; # размер дробной части fixed-point в битах
const DELTA_T = 5 << FRACT; # диапазон тревожного оповещения в градусах (fixed-point)
const ST_NONE = -1;
const ST_PASSIVE = 0;
const ST_ACTIVE = 1;
var t, state = ST_NONE, self_event;
proc main()
{
var e = $get_event_id();
if e == $EVT_INIT {
init();
} else if e == $EVT_TIMER1 {
poll();
} else if e == EVT_ACTIVE {
cancel();
} else if e == EVT_PASSIVE {
cancel();
}
}
proc init()
{
$set_event_mask($EM_INPUT);
$set_timer(1, POLL_PERIOD);
t = get_temp();
}
proc poll()
{
var t_low = $get_sensor_low_limit(INPUT_N, FRACT) - DELTA_T;
var t_high = $get_sensor_high_limit(INPUT_N, FRACT) + DELTA_T;
t = get_temp();
if t <= t_low && state != ST_ACTIVE {
raise(EVT_ACTIVE);
state = ST_ACTIVE;
} else if t >= t_high && state != ST_ACTIVE {
raise(EVT_ACTIVE);
state = ST_ACTIVE;
} else if t > t_low && t < t_high && state != ST_PASSIVE {
raise(EVT_PASSIVE);
state = ST_PASSIVE;
}
}
fun get_temp()
{
return $get_sensor_value(INPUT_N, FRACT);
}
proc raise(e)
{
$raise_event(e);
self_event = 1;
}
proc cancel()
{
if self_event {
self_event = 0;
} else {
$cancel_event();
}
}
3. Контроль входа с двумя активными уровнями
# Пример программы для контроля входа с двумя активными уровнями.
# В примере предполагается, что ко входу 1 подключен термодатчик RTD.
# Необходимые настройки входа 1:
# - тип входа: RTD-03 или RTD-04
# - круглосуточный контроль: включен
# Уровни задаются константами T1 и T2 в градусах:
# t < T1 - пассивный
# T1 <= t < T2 - активный 1
# t >= T2 - активный 2
#
# Для оповещения о выходе температуры за тревожные границы должно быть настроено соединение:
# - задан номер телефона
# - задан пароль (зависит от типа оповещения)
# - разрешен необходимый тип оповещения для In1
const INPUT_N = 1; # номер входа с термодатчиком
const EVT_ACTIVE = $EVT_INPUT1_ACTIVE; # идентификатор активного состояния входа
const EVT_PASSIVE = $EVT_INPUT1_PASSIVE; # идентификатор пассивного состояния входа
const POLL_PERIOD = 10; # период опроса входа (1 единица = 100 мс)
const FRACT = 8; # размер дробной части fixed-point в битах
const T1 = 20 << FRACT; # первая граница в градусах (fixed-point)
const T2 = 30 << FRACT; # вторая граница в градусах (fixed-point)
const ST_NONE = -1;
const ST_PASSIVE = 0;
const ST_ACTIVE1 = 1;
const ST_ACTIVE2 = 2;
var t, state = ST_NONE, self_event;
proc main()
{
var e = $get_event_id();
if e == $EVT_INIT {
init();
} else if e == $EVT_TIMER1 {
poll();
} else if e == EVT_ACTIVE {
cancel();
} else if e == EVT_PASSIVE {
cancel();
}
}
proc init()
{
$set_event_mask($EM_INPUT);
$set_timer(1, POLL_PERIOD);
t = get_temp();
}
proc poll()
{
t = get_temp();
if t >= T1 && t < T2 && state != ST_ACTIVE1 {
raise(EVT_ACTIVE);
state = ST_ACTIVE1;
} else if t >= T2 && state != ST_ACTIVE2 {
raise(EVT_ACTIVE);
state = ST_ACTIVE2;
} else if t < T1 && state != ST_PASSIVE {
raise(EVT_PASSIVE);
state = ST_PASSIVE;
}
}
fun get_temp()
{
return $get_sensor_value(INPUT_N, FRACT);
}
proc raise(e)
{
$raise_event(e);
self_event = 1;
}
proc cancel()
{
if self_event {
self_event = 0;
} else {
$cancel_event();
}
}
4. Отмена оповещения при изменении состояния входов на время меньше заданного
# Пример программы для отмены оповещения при изменении состояния входов на время меньше заданного.
# Время задается константой INPUT_DELAY и может быть порядка минут или часов.
const POLL_PERIOD = 600; # период опроса входов (600 * 100 мс = 60 с = 1 мин)
const INPUT_DELAY = 120; # длительность задержки оповещения (120 мин = 2 ч)
const FALSE = 0;
const TRUE = 1;
var inputs_state;
var self_events;
var input1_time;
var input2_time;
var input3_time;
var input4_time;
var input5_time;
var input6_time;
var input7_time;
var input8_time;
proc main()
{
var e = $get_event_id();
if e == $EVT_INIT {
init();
} else if e == $EVT_TIMER1 {
poll();
} else if e >= $EVT_INPUT1_ACTIVE && e <= $EVT_INPUT8_ACTIVE {
var n = e - $EVT_INPUT1_ACTIVE + 1;
handle_input_event(n, 1);
} else if e >= $EVT_INPUT1_PASSIVE && e <= $EVT_INPUT8_PASSIVE {
var n = e - $EVT_INPUT1_PASSIVE + 1;
handle_input_event(n, -1);
}
}
proc init()
{
$set_event_mask($EM_INPUT);
$set_timer(1, POLL_PERIOD);
init_inputs();
poll();
}
proc init_inputs()
{
var n = 1;
while n <= 8 {
var s = $get_input_state(n);
var t;
if s {
t = INPUT_DELAY;
} else {
t = -INPUT_DELAY;
}
set_input_state(n, !s);
set_input_time(n, t);
n = n + 1;
}
}
proc handle_input_event(n, t)
{
if is_self_event(n) {
set_self_event(n, FALSE);
} else {
set_input_time(n, t);
$cancel_event();
}
}
proc set_input_time(n, t)
{
if n == 1 {
input1_time = t;
} else if n == 2 {
input2_time = t;
} else if n == 3 {
input3_time = t;
} else if n == 4 {
input4_time = t;
} else if n == 5 {
input5_time = t;
} else if n == 6 {
input6_time = t;
} else if n == 7 {
input7_time = t;
} else if n == 8 {
input8_time = t;
}
}
proc poll()
{
input1_time = handle_input_time(1, input1_time);
input2_time = handle_input_time(2, input2_time);
input3_time = handle_input_time(3, input3_time);
input4_time = handle_input_time(4, input4_time);
input5_time = handle_input_time(5, input5_time);
input6_time = handle_input_time(6, input6_time);
input7_time = handle_input_time(7, input7_time);
input8_time = handle_input_time(8, input8_time);
}
fun handle_input_time(n, t)
{
if abs(t) <= INPUT_DELAY {
t = tick(t);
var s = t > 0;
if abs(t) == INPUT_DELAY + 1 && is_state_switch(n, s) {
raise(n, s);
}
}
return t;
}
fun abs(x)
{
if x < 0 {
return -x;
} else {
return x;
}
}
fun tick(t)
{
if t < 0 {
return t - 1;
} else {
return t + 1;
}
}
fun is_state_switch(n, s)
{
return get_input_state(n) != s;
}
proc raise(n, s)
{
var e;
if s {
e = $EVT_INPUT1_ACTIVE;
set_input_state(n, TRUE);
} else {
e = $EVT_INPUT1_PASSIVE;
set_input_state(n, FALSE);
}
$raise_event(e + n - 1);
set_self_event(n, TRUE);
}
fun mask(n)
{
return 1 << (n - 1);
}
proc set_input_state(n, s)
{
if s {
inputs_state = inputs_state | mask(n);
} else {
inputs_state = inputs_state & ~mask(n);
}
}
fun get_input_state(n)
{
return (inputs_state & mask(n)) != 0;
}
proc set_self_event(n, s)
{
if s {
self_events = self_events | mask(n);
} else {
self_events = self_events & ~mask(n);
}
}
fun is_self_event(n)
{
return (self_events & mask(n)) != 0;
}
5. Астрономическое реле
const DELTA_ON = -10;
const DELTA_OFF = 20;
const DELTA_MIDNIGHT = 0;
const DELTA_MORNING = 0;
const DAY_MINUTES = 24 * 60;
const POLL_PERIOD = 100;
const BAD_TIME = $EVT_INPUT14_ACTIVE;
const EVENING_LIGHT = 1;
const NIGHT_LIGHT = 2;
const JAN = 1;
const FEB = 2;
const MAR = 3;
const APR = 4;
const MAY = 5;
const JUN = 6;
const JUL = 7;
const AUG = 8;
const SEP = 9;
const OCT = 10;
const NOV = 11;
const DEC = 12;
const I1_BEGIN = 1;
const I1_END = 5;
const I2_BEGIN = 6;
const I2_END = 10;
const I3_BEGIN = 11;
const I3_END = 15;
const I4_BEGIN = 16;
const I4_END = 20;
const I5_BEGIN = 21;
const I5_END = 25;
const NONE = -1;
const ALL_LIGHT_OFF = 0;
const ALL_LIGHT_ON = 1;
const NIGHT_LIGHT_ON = 2;
var light_state = NONE;
var bad_time;
proc main()
{
var e = $get_event_id();
if e == $EVT_INIT {
$set_timer(1, POLL_PERIOD);
poll();
} else if e == $EVT_TIMER1 {
poll();
}
}
proc poll()
{
var t_on, t_off;
var t = $get_time();
if t < 0 {
if !bad_time {
$raise_event(BAD_TIME);
bad_time = 1;
}
return;
}
bad_time = 0;
var d = $get_day(t);
var m = $get_month(t);
if m == JAN {
if interval(d, I1_BEGIN, I1_END) {
t_on = time(16, 51);
t_off = time(8, 14);
} else if interval(d, I2_BEGIN, I2_END) {
t_on = time(16, 59);
t_off = time(8, 12);
} else if interval(d, I3_BEGIN, I3_END) {
t_on = time(17, 6);
t_off = time(8, 8);
} else if interval(d, I4_BEGIN, I4_END) {
t_on = time(17, 14);
t_off = time(8, 3);
} else if interval(d, I5_BEGIN, I5_END) {
t_on = time(17, 23);
t_off = time(7, 56);
} else {
t_on = time(17, 34);
t_off = time(7, 48);
}
} else if m == FEB {
if interval(d, I1_BEGIN, I1_END) {
t_on = time(17, 43);
t_off = time(7, 40);
} else if interval(d, I2_BEGIN, I2_END) {
t_on = time(17, 53);
t_off = time(7, 31);
} else if interval(d, I3_BEGIN, I3_END) {
t_on = time(18, 3);
t_off = time(7, 22);
} else if interval(d, I4_BEGIN, I4_END) {
t_on = time(18, 13);
t_off = time(7, 11);
} else if interval(d, I5_BEGIN, I5_END) {
t_on = time(18, 22);
t_off = time(7, 0);
} else {
t_on = time(18, 29);
t_off = time(6, 53);
}
} else if m == MAR {
if interval(d, I1_BEGIN, I1_END) {
t_on = time(18, 40);
t_off = time(6, 41);
} else if interval(d, I2_BEGIN, I2_END) {
t_on = time(18, 49);
t_off = time(6, 29);
} else if interval(d, I3_BEGIN, I3_END) {
t_on = time(18, 58);
t_off = time(6, 17);
} else if interval(d, I4_BEGIN, I4_END) {
t_on = time(19, 7);
t_off = time(6, 4);
} else if interval(d, I5_BEGIN, I5_END) {
t_on = time(19, 17);
t_off = time(5, 52);
} else {
t_on = time(19, 29);
t_off = time(5, 35);
}
} else if m == APR {
if interval(d, I1_BEGIN, I1_END) {
t_on = time(19, 38);
t_off = time(5, 19);
} else if interval(d, I2_BEGIN, I2_END) {
t_on = time(19, 49);
t_off = time(5, 9);
} else if interval(d, I3_BEGIN, I3_END) {
t_on = time(19, 59);
t_off = time(4, 57);
} else if interval(d, I4_BEGIN, I4_END) {
t_on = time(20, 8);
t_off = time(4, 45);
} else if interval(d, I5_BEGIN, I5_END) {
t_on = time(20, 19);
t_off = time(4, 33);
} else {
t_on = time(20, 29);
t_off = time(4, 21);
}
} else if m == MAY {
if interval(d, I1_BEGIN, I1_END) {
t_on = time(20, 39);
t_off = time(4, 10);
} else if interval(d, I2_BEGIN, I2_END) {
t_on = time(20, 49);
t_off = time(3, 59);
} else if interval(d, I3_BEGIN, I3_END) {
t_on = time(20, 58);
t_off = time(3, 49);
} else if interval(d, I4_BEGIN, I4_END) {
t_on = time(21, 6);
t_off = time(3, 40);
} else if interval(d, I5_BEGIN, I5_END) {
t_on = time(21, 16);
t_off = time(3, 32);
} else {
t_on = time(21, 26);
t_off = time(3, 24);
}
} else if m == JUN {
if interval(d, I1_BEGIN, I1_END) {
t_on = time(21, 30);
t_off = time(3, 18);
} else if interval(d, I2_BEGIN, I2_END) {
t_on = time(21, 37);
t_off = time(3, 14);
} else if interval(d, I3_BEGIN, I3_END) {
t_on = time(21, 42);
t_off = time(3, 11);
} else if interval(d, I4_BEGIN, I4_END) {
t_on = time(21, 43);
t_off = time(3, 11);
} else if interval(d, I5_BEGIN, I5_END) {
t_on = time(21, 43);
t_off = time(3, 13);
} else {
t_on = time(21, 43);
t_off = time(3, 16);
}
} else if m == JUL {
if interval(d, I1_BEGIN, I1_END) {
t_on = time(21, 39);
t_off = time(3, 21);
} else if interval(d, I2_BEGIN, I2_END) {
t_on = time(21, 35);
t_off = time(3, 27);
} else if interval(d, I3_BEGIN, I3_END) {
t_on = time(21, 31);
t_off = time(3, 33);
} else if interval(d, I4_BEGIN, I4_END) {
t_on = time(21, 23);
t_off = time(3, 42);
} else if interval(d, I5_BEGIN, I5_END) {
t_on = time(21, 15);
t_off = time(3, 50);
} else {
t_on = time(21, 04);
t_off = time(4, 1);
}
} else if m == AUG {
if interval(d, I1_BEGIN, I1_END) {
t_on = time(20, 54);
t_off = time(4, 11);
} else if interval(d, I2_BEGIN, I2_END) {
t_on = time(20, 44);
t_off = time(4, 21);
} else if interval(d, I3_BEGIN, I3_END) {
t_on = time(20, 32);
t_off = time(4, 31);
} else if interval(d, I4_BEGIN, I4_END) {
t_on = time(20, 21);
t_off = time(4, 41);
} else if interval(d, I5_BEGIN, I5_END) {
t_on = time(20, 8);
t_off = time(4, 51);
} else {
t_on = time(19, 53);
t_off = time(5, 1);
}
} else if m == SEP {
if interval(d, I1_BEGIN, I1_END) {
t_on = time(19, 39);
t_off = time(5, 12);
} else if interval(d, I2_BEGIN, I2_END) {
t_on = time(19, 27);
t_off = time(5, 22);
} else if interval(d, I3_BEGIN, I3_END) {
t_on = time(19, 13);
t_off = time(5, 32);
} else if interval(d, I4_BEGIN, I4_END) {
t_on = time(19, 0);
t_off = time(5, 40);
} else if interval(d, I5_BEGIN, I5_END) {
t_on = time(18, 50);
t_off = time(5, 49);
} else {
t_on = time(18, 37);
t_off = time(5, 59);
}
} else if m == OCT {
if interval(d, I1_BEGIN, I1_END) {
t_on = time(18, 26);
t_off = time(6, 7);
} else if interval(d, I2_BEGIN, I2_END) {
t_on = time(18, 13);
t_off = time(6, 16);
} else if interval(d, I3_BEGIN, I3_END) {
t_on = time(18, 1);
t_off = time(6, 25);
} else if interval(d, I4_BEGIN, I4_END) {
t_on = time(17, 50);
t_off = time(6, 34);
} else if interval(d, I5_BEGIN, I5_END) {
t_on = time(17, 39);
t_off = time(6, 43);
} else {
t_on = time(17, 27);
t_off = time(6, 55);
}
} else if m == NOV {
if interval(d, I1_BEGIN, I1_END) {
t_on = time(17, 17);
t_off = time(7, 5);
} else if interval(d, I2_BEGIN, I2_END) {
t_on = time(17, 7);
t_off = time(7, 15);
} else if interval(d, I3_BEGIN, I3_END) {
t_on = time(17, 0);
t_off = time(7, 25);
} else if interval(d, I4_BEGIN, I4_END) {
t_on = time(16, 52);
t_off = time(7, 35);
} else if interval(d, I5_BEGIN, I5_END) {
t_on = time(16, 45);
t_off = time(7, 43);
} else {
t_on = time(16, 42);
t_off = time(7, 51);
}
} else if m == DEC {
if interval(d, I1_BEGIN, I1_END) {
t_on = time(16, 39);
t_off = time(7, 57);
} else if interval(d, I2_BEGIN, I2_END) {
t_on = time(16, 36);
t_off = time(8, 3);
} else if interval(d, I3_BEGIN, I3_END) {
t_on = time(16, 36);
t_off = time(8, 9);
} else if interval(d, I4_BEGIN, I4_END) {
t_on = time(16, 37);
t_off = time(8, 12);
} else if interval(d, I5_BEGIN, I5_END) {
t_on = time(16, 39);
t_off = time(8, 14);
} else {
t_on = time(16, 45);
t_off = time(8, 15);
}
}
t_on = add_time(t_on, DELTA_ON);
t_off = add_time(t_off, DELTA_OFF);
var midnight = add_time(time(0, 0), DELTA_MIDNIGHT);
var morning = add_time(time(6, 0), DELTA_MORNING);
var t_now = time($get_hour(t), $get_minute(t));
if t_on < t_off {
if t_now >= t_on && t_now < t_off {
all_light_on();
} else {
all_light_off();
}
} else {
if t_now >= morning && t_now < t_off {
all_light_on();
} else if t_now >= midnight && t_now < t_off {
night_light_on();
} else if t_now >= t_off && t_now < t_on {
all_light_off();
} else {
all_light_on();
}
}
}
fun interval(i, i0, i1)
{
return i >= i0 && i <= i1;
}
fun time(hh, mm)
{
return hh * 60 + mm;
}
fun add_time(t0, dt)
{
var t1 = t0 + dt;
if t1 < 0 {
t1 = t1 + DAY_MINUTES;
} else if t1 >= DAY_MINUTES {
t1 = t1 - DAY_MINUTES;
}
return t1;
}
proc all_light_on()
{
if light_state != ALL_LIGHT_ON {
set_light_state($ON, $ON);
light_state = ALL_LIGHT_ON;
}
}
proc night_light_on()
{
if light_state != NIGHT_LIGHT_ON {
set_light_state($OFF, $ON);
light_state = NIGHT_LIGHT_ON;
}
}
proc all_light_off()
{
if light_state != ALL_LIGHT_OFF {
set_light_state($OFF, $OFF);
light_state = ALL_LIGHT_OFF;
}
}
proc set_light_state(evening_state, night_state)
{
if $get_arm_mode() != $DISARM {
$set_output_state(EVENING_LIGHT, evening_state);
$set_output_state(NIGHT_LIGHT, night_state);
}
}