Files
BarotraumaLuaProjects/AirlockRTL.lua
2026-06-13 23:53:48 +03:00

315 lines
12 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- 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 → открыть внутрь → войти → btn4 (1-е) → закрыть внутрь
-- → затопить → открыть внешнюю → выйти → btn4 (2-е) → закрыть внешнюю → IDLE
--
-- Сценарий FLOW_IN (в отсек):
-- btn5 → затопить → открыть внешнюю → войти → btn4 (1-е) → закрыть внешнюю
-- → осушить → открыть внутрь → выйти → btn4 (2-е) → закрыть внутрь → IDLE
--
-- btn4: единственный способ перехода через WAIT_* состояния (без таймера).
-- Таймер WAIT_TIMEOUT (10с) — только аварийная страховка.
-- В DRAIN/FLOOD: btn4 или PUMP_TIMEOUT — пропустить и открыть дверь.
-- 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 -- Снаружи в сухой
-- Статусы (с ведущим пробелом для отступа на дисплее)
local TXT_READY = " Готов"
local TXT_DRAIN_PRE = " Осушение перед входом"
local TXT_OPEN_IN = " Открытие внутренней двери"
local TXT_WAIT_IN = " Внутренняя дверь открыта"
local TXT_CLOSE_IN = " Закрытие внутренней двери"
local TXT_FLOOD = " Заполнение шлюза"
local TXT_DRAIN_OUT = " Осушение для выхода"
local TXT_DRAIN_RET = " Осушение для отсека"
local TXT_OPEN_OUT = " Открытие внешней двери"
local TXT_WAIT_OUT = " Внешняя дверь открыта"
local TXT_CLOSE_OUT = " Закрытие внешней двери"
local TXT_DRAIN_AFTER = " Осушение после входа"
-- Цвета
local C_GREEN = "0,255,0"
local C_CYAN = "0,200,255"
local C_YELLOW = "255,200,0"
-- Параметры (шкала воды: 0 = пусто, 100 = полно)
local WAIT_TIMEOUT = 5 -- аварийный таймаут открытой двери (основное — btn4)
local PUMP_TIMEOUT = 10 -- макс. секунд работы насоса до принудительного продолжения
local DRAIN_THRESHOLD = 5 -- осушить до 5% перед выходом в море
local INNER_THRESHOLD = 0 -- открыть внутрь при воде ≤ 10%
local FLOOD_THRESHOLD = 90 -- заполнить до 90% перед открытием внешней
local WATER_OPEN_SAFE = 10 -- при btn3: если воды > 10%, сначала осушить
-- Переменные состояния
local state = ST_IDLE
local flow = FLOW_NONE
local timer = 0
local pumpTimer = 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(TXT_READY, C_GREEN)
state = ST_IDLE
timer = 0
end
-- Инициализация при старте
-- out[1] = 0
-- out[2] = 0
-- out[3] = 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
-- Сброс таймера насоса при входе в состояния с насосом
if state == ST_DRAIN_EXIT or state == ST_FLOOD or state == ST_DRAIN_RET then
pumpTimer = pumpTimer + dt
else
pumpTimer = 0
end
-- Флаг: была ли кнопка 4 обработана в машине состояний
local press4consumed = false
-- === Машина состояний ===
if state == ST_IDLE then
-- IDLE: ожидание команд. Обе двери закрыты, оборудование выключено.
if press3 then
-- Вход из сухого отсека: если есть вода — сначала осушаем, затем внутрь
if water > WATER_OPEN_SAFE then
setStatus(TXT_DRAIN_PRE, C_CYAN)
out[3] = 1
out[4] = -100
flow = FLOW_OUT
state = ST_DRAIN_RET
else
setStatus(TXT_OPEN_IN, C_GREEN)
out[2] = 1
flow = FLOW_OUT
state = ST_OPEN_IN
end
elseif press5 then
-- Вход снаружи → заполнить шлюз водой, затем открыть внешнюю дверь
setStatus(TXT_FLOOD, C_YELLOW)
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(TXT_DRAIN_OUT, C_CYAN)
out[3] = 1
out[4] = -100
state = ST_DRAIN_EXIT
else
setStatus(TXT_DRAIN_RET, C_CYAN)
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(TXT_WAIT_IN, C_GREEN)
state = ST_WAIT_IN
end
elseif state == ST_WAIT_IN then
-- Внутренняя дверь открыта. Ждём btn4 (подтверждение прохода).
out[2] = 1
timer = timer + dt
if press4 then
press4consumed = true
setStatus(TXT_CLOSE_IN, C_YELLOW)
out[2] = 0
state = ST_CLOSE_IN
elseif timer >= WAIT_TIMEOUT then
setStatus(TXT_CLOSE_IN, C_YELLOW)
out[2] = 0
state = ST_CLOSE_IN
end
elseif state == ST_CLOSE_IN then
-- Закрытие внутренней двери. Ждём полного закрытия.
if not doorIn then
if flow == FLOW_OUT then
-- Сценарий "в море": после входа → заполнить (давление) и открыть внешнюю
setStatus(TXT_FLOOD, C_YELLOW)
out[3] = 1
out[4] = 100
state = ST_FLOOD
else
-- Вернулись в отсек: завершено
setStatus(TXT_READY, C_GREEN)
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 or pumpTimer >= PUMP_TIMEOUT then
press4consumed = press4
setStatus(TXT_OPEN_OUT, C_CYAN)
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 or pumpTimer >= PUMP_TIMEOUT then
press4consumed = press4
setStatus(TXT_OPEN_OUT, C_YELLOW)
out[3] = 0
out[1] = 1
state = ST_OPEN_OUT
end
elseif state == ST_DRAIN_RET then
-- Осушение шлюза для входа в сухой отсек.
out[3] = 1
out[4] = -100
if water <= INNER_THRESHOLD or press4 or pumpTimer >= PUMP_TIMEOUT then
press4consumed = press4
setStatus(TXT_OPEN_IN, C_GREEN)
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(TXT_WAIT_OUT, C_CYAN)
state = ST_WAIT_OUT
end
elseif state == ST_WAIT_OUT then
-- Внешняя дверь открыта. Ждём btn4 (подтверждение прохода).
out[1] = 1
timer = timer + dt
if press4 then
press4consumed = true
setStatus(TXT_CLOSE_OUT, C_YELLOW)
out[1] = 0
state = ST_CLOSE_OUT
elseif timer >= WAIT_TIMEOUT then
setStatus(TXT_CLOSE_OUT, C_YELLOW)
out[1] = 0
state = ST_CLOSE_OUT
end
elseif state == ST_CLOSE_OUT then
-- Закрытие внешней двери. Ждём полного закрытия.
if not doorOut then
if flow == FLOW_IN then
-- Сценарий "снаружи": после закрытия — осушение и вход в отсек
setStatus(TXT_DRAIN_AFTER, C_CYAN)
out[3] = 1
out[4] = -100
state = ST_DRAIN_RET
else
-- Сценарий "наружу": завершён
setStatus(TXT_READY, C_GREEN)
state = ST_IDLE
flow = FLOW_NONE
end
end
end
table.clear(inp)
end