279 lines
11 KiB
Lua
279 lines
11 KiB
Lua
-- AirlockRTL.lua — Контроллер шлюза (State Machine)
|
||
-- =================================================
|
||
--
|
||
-- Физическая схема:
|
||
-- [Сухой отсек] — [Внутр.дверь] — [ШЛЮЗ] — [Наруж.дверь] — [Снаружи/вода]
|
||
--
|
||
-- Входы:
|
||
-- 1 — состояние наружней (левой) двери (1=открыта)
|
||
-- 2 — состояние внутренней (правой) двери (1=открыта)
|
||
-- 3 — кнопка внутри шлюза (слева) — вход в шлюз из сухого отсека
|
||
-- 4 — кнопка внутри шлюза (центр) — выход из шлюза (контекстно)
|
||
-- 5 — кнопка снаружи — вход в шлюз снаружи
|
||
-- 6 — уровень воды в шлюзе (0 = пусто, 100 = полно)
|
||
--
|
||
-- Выходы:
|
||
-- 1 — наружняя дверь (1=открыть, 0=закрыть)
|
||
-- 2 — внутренняя дверь (1=открыть, 0=закрыть)
|
||
-- 3 — насос (1=вкл, 0=выкл)
|
||
-- 4 — целевой уровень воды (-100=осушить, +100=заполнить)
|
||
-- 5 — текст статуса
|
||
-- 6 — цвет текста (rrr,ggg,bbb)
|
||
--
|
||
-- Принцип: любое нажатие сначала управляет водой, затем дверью.
|
||
-- Слева всегда море (вода), справа отсек (сухо).
|
||
-- Направление (flow) запоминается при входе.
|
||
--
|
||
-- Сценарий FLOW_OUT (в море):
|
||
-- btn3 → если вода >10 осушить → открыть внутрь → войти
|
||
-- → btn4/timer → закрыть внутрь → осушить (btn4 = пропуск) → открыть внешнюю
|
||
-- → выйти → btn4/timer → закрыть внешнюю → IDLE
|
||
--
|
||
-- Сценарий FLOW_IN (в отсек):
|
||
-- btn5 → затопить (btn4 = пропуск) → открыть внешнюю → войти
|
||
-- → btn4/timer → закрыть внешнюю → осушить (btn4 = пропуск)
|
||
-- → открыть внутрь → войти → btn4/timer → закрыть внутрь → IDLE
|
||
--
|
||
-- btn4 из IDLE: направление по flow (запомнен при входе), иначе по воде.
|
||
|
||
-- Состояния автомата
|
||
local ST_IDLE = 0 -- Ожидание команд
|
||
local ST_OPEN_IN = 1 -- Открытие внутренней двери
|
||
local ST_WAIT_IN = 2 -- Ожидание прохода через внутреннюю дверь
|
||
local ST_CLOSE_IN = 3 -- Закрытие внутренней двери
|
||
local ST_DRAIN_EXIT = 4 -- Осушение для выхода наружу
|
||
local ST_FLOOD = 5 -- Заполнение для входа снаружи
|
||
local ST_DRAIN_RET = 6 -- Осушение для возврата в сухой отсек
|
||
local ST_OPEN_OUT = 7 -- Открытие внешней двери
|
||
local ST_WAIT_OUT = 8 -- Ожидание прохода через внешнюю дверь
|
||
local ST_CLOSE_OUT = 9 -- Закрытие внешней двери
|
||
|
||
-- Сценарии
|
||
local FLOW_NONE = 0
|
||
local FLOW_OUT = 1 -- Из сухого наружу
|
||
local FLOW_IN = 2 -- Снаружи в сухой
|
||
|
||
-- Параметры (шкала воды: 0 = пусто, 100 = полно)
|
||
local WAIT_TIMEOUT = 5 -- секунд ожидания у открытой двери
|
||
local DRAIN_THRESHOLD = 5 -- почти пусто (выход в море)
|
||
local INNER_THRESHOLD = 10 -- можно открыть внутрь (по запросу: вода ниже 10%)
|
||
local FLOOD_THRESHOLD = 95 -- почти полно (вход снаружи)
|
||
local WATER_OPEN_SAFE = 10 -- при btn3: если воды > 10%, сначала осушить
|
||
|
||
-- Переменные состояния
|
||
local state = ST_IDLE
|
||
local flow = FLOW_NONE
|
||
local timer = 0
|
||
|
||
-- Предыдущие значения кнопок для детекции фронта
|
||
local prev3 = false
|
||
local prev4 = false
|
||
local prev5 = false
|
||
|
||
-- Установка текста и цвета статуса
|
||
local function setStatus(text, color)
|
||
out[5] = text
|
||
out[6] = color
|
||
end
|
||
|
||
-- Сброс в IDLE: закрыть двери, выключить насос, контекст (flow) сохраняется
|
||
local function goIdle()
|
||
out[1] = 0
|
||
out[2] = 0
|
||
out[3] = 0
|
||
setStatus("Готов", "0,255,0")
|
||
state = ST_IDLE
|
||
timer = 0
|
||
end
|
||
|
||
-- Инициализация при старте
|
||
out[1] = 0
|
||
out[2] = 0
|
||
out[3] = 0
|
||
setStatus("Готов", "0,255,0")
|
||
|
||
inp = {}
|
||
|
||
-- Главная функция, вызывается каждый кадр
|
||
function upd(dt)
|
||
dt = dt or 0
|
||
|
||
-- Чтение входов
|
||
local doorOut = inp[1] == 1
|
||
local doorIn = inp[2] == 1
|
||
local btn3 = inp[3] == 1
|
||
local btn4 = inp[4] == 1
|
||
local btn5 = inp[5] == 1
|
||
local water = inp[6] or 0
|
||
|
||
-- Детекция фронта кнопок (только нажатие, не удержание)
|
||
local press3 = btn3 and not prev3
|
||
local press4 = btn4 and not prev4
|
||
local press5 = btn5 and not prev5
|
||
prev3 = btn3
|
||
prev4 = btn4
|
||
prev5 = btn5
|
||
|
||
-- Флаг: была ли кнопка 4 обработана в машине состояний
|
||
local press4consumed = false
|
||
|
||
-- === Машина состояний ===
|
||
|
||
if state == ST_IDLE then
|
||
-- IDLE: ожидание команд. Обе двери закрыты, оборудование выключено.
|
||
if press3 then
|
||
-- Вход из сухого отсека: если есть вода — сначала осушаем, затем внутрь
|
||
if water > WATER_OPEN_SAFE then
|
||
setStatus("Осушение перед входом...", "0,200,255")
|
||
out[3] = 1
|
||
out[4] = -100
|
||
flow = FLOW_OUT
|
||
state = ST_DRAIN_RET
|
||
else
|
||
setStatus("Открытие внутр. двери...", "0,255,0")
|
||
out[2] = 1
|
||
flow = FLOW_OUT
|
||
state = ST_OPEN_IN
|
||
end
|
||
elseif press5 then
|
||
-- Вход снаружи → заполнить шлюз водой, затем открыть внешнюю дверь
|
||
setStatus("Заполнение шлюза...", "255,200,0")
|
||
out[3] = 1
|
||
out[4] = 100
|
||
flow = FLOW_IN
|
||
state = ST_FLOOD
|
||
elseif press4 then
|
||
-- Выход из шлюза. Направление: flow (запомнен при входе), иначе уровень воды.
|
||
press4consumed = true
|
||
local dir = flow
|
||
if dir == FLOW_NONE then
|
||
dir = (water <= DRAIN_THRESHOLD) and FLOW_OUT or FLOW_IN
|
||
flow = dir
|
||
end
|
||
if dir == FLOW_OUT then
|
||
setStatus("Осушение для выхода...", "0,200,255")
|
||
out[3] = 1
|
||
out[4] = -100
|
||
state = ST_DRAIN_EXIT
|
||
else
|
||
setStatus("Осушение для отсека...", "0,200,255")
|
||
out[3] = 1
|
||
out[4] = -100
|
||
state = ST_DRAIN_RET
|
||
end
|
||
end
|
||
|
||
elseif state == ST_OPEN_IN then
|
||
-- Открытие внутренней двери. Ждём сигнала от двери.
|
||
out[2] = 1
|
||
if doorIn then
|
||
timer = 0
|
||
setStatus("Внутр. дверь открыта. Войдите.", "0,255,0")
|
||
state = ST_WAIT_IN
|
||
end
|
||
|
||
elseif state == ST_WAIT_IN then
|
||
-- Внутренняя дверь открыта. Ждём прохода человека (btn4 или таймер).
|
||
out[2] = 1
|
||
timer = timer + dt
|
||
if press4 or timer >= WAIT_TIMEOUT then
|
||
press4consumed = press4
|
||
setStatus("Закрытие внутр. двери...", "255,200,0")
|
||
out[2] = 0
|
||
state = ST_CLOSE_IN
|
||
end
|
||
|
||
elseif state == ST_CLOSE_IN then
|
||
-- Закрытие внутренней двери. Ждём полного закрытия.
|
||
if not doorIn then
|
||
if flow == FLOW_OUT then
|
||
-- Сценарий "в море": после входа → осушение и выход
|
||
setStatus("Осушение для выхода...", "0,200,255")
|
||
out[3] = 1
|
||
out[4] = -100
|
||
state = ST_DRAIN_EXIT
|
||
else
|
||
-- Вернулись в отсек: завершено
|
||
setStatus("Готов", "0,255,0")
|
||
flow = FLOW_NONE
|
||
state = ST_IDLE
|
||
end
|
||
end
|
||
|
||
elseif state == ST_DRAIN_EXIT then
|
||
-- Осушение шлюза перед выходом в море.
|
||
out[3] = 1
|
||
out[4] = -100
|
||
if water <= DRAIN_THRESHOLD or press4 then
|
||
press4consumed = press4
|
||
setStatus("Открытие внеш. двери...", "0,200,255")
|
||
out[3] = 0
|
||
out[1] = 1
|
||
state = ST_OPEN_OUT
|
||
end
|
||
|
||
elseif state == ST_FLOOD then
|
||
-- Заполнение шлюза для входа снаружи (уравнивание давления).
|
||
out[3] = 1
|
||
out[4] = 100
|
||
if water >= FLOOD_THRESHOLD or press4 then
|
||
press4consumed = press4
|
||
setStatus("Открытие внеш. двери...", "255,200,0")
|
||
out[3] = 0
|
||
out[1] = 1
|
||
state = ST_OPEN_OUT
|
||
end
|
||
|
||
elseif state == ST_DRAIN_RET then
|
||
-- Осушение шлюза для входа в сухой отсек (можно раньше, порог INNER_THRESHOLD).
|
||
out[3] = 1
|
||
out[4] = -100
|
||
if water <= INNER_THRESHOLD or press4 then
|
||
press4consumed = press4
|
||
setStatus("Открытие внутр. двери...", "0,255,0")
|
||
out[3] = 0
|
||
out[2] = 1
|
||
state = ST_OPEN_IN
|
||
end
|
||
|
||
elseif state == ST_OPEN_OUT then
|
||
-- Открытие внешней двери. Ждём сигнала от двери.
|
||
out[1] = 1
|
||
if doorOut then
|
||
timer = 0
|
||
setStatus("Внеш. дверь открыта. Выходите.", "0,200,255")
|
||
state = ST_WAIT_OUT
|
||
end
|
||
|
||
elseif state == ST_WAIT_OUT then
|
||
-- Внешняя дверь открыта. Ждём прохода человека (btn4 или таймер).
|
||
out[1] = 1
|
||
timer = timer + dt
|
||
if press4 or timer >= WAIT_TIMEOUT then
|
||
press4consumed = press4
|
||
setStatus("Закрытие внеш. двери...", "255,200,0")
|
||
out[1] = 0
|
||
state = ST_CLOSE_OUT
|
||
end
|
||
|
||
elseif state == ST_CLOSE_OUT then
|
||
-- Закрытие внешней двери. Ждём полного закрытия.
|
||
if not doorOut then
|
||
if flow == FLOW_IN then
|
||
-- Сценарий "снаружи": после закрытия — осушение и вход в отсек
|
||
setStatus("Осушение после входа...", "0,200,255")
|
||
out[3] = 1
|
||
out[4] = -100
|
||
state = ST_DRAIN_RET
|
||
else
|
||
-- Сценарий "наружу": завершён
|
||
setStatus("Готов", "0,255,0")
|
||
state = ST_IDLE
|
||
flow = FLOW_NONE
|
||
end
|
||
end
|
||
end
|
||
|
||
table.clear(inp)
|
||
end
|