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);
    }
}