Files
BarotraumaLuaProjects/AirlockRTL.lua
2026-06-07 02:33:22 +03:00

259 lines
9.1 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.
-- Airlock control v2.0
-- Физическая раскладка шлюза:
-- Слева — большая внешняя дверь (наружу, под воду)
-- Справа (изнутри) — стеклянная внутренняя дверь (внутрь станции)
-- Кнопки внутри шлюза (слева направо):
-- Пин 3 = левая кнопка = внутренняя (вход 4)
-- Пин 4 = средняя кнопка = правая внутри (вход 3)
-- Пин 5 = наружная правая кнопка снаружи (вход 5)
-- === Состояния ===
-- IDLE — вода ~0%, обе двери закрыты. Ожидание команды.
-- FLOODING — насос качает воду внутрь (подготовка к выходу). "Затопление"
-- READY_OUT — вода ≥95%, внутр.дверь закрыта, внешн. закрыта. "Готов вых"
-- READY_IN — вода ≥95%, внутр.дверь закрыта, внешн. ОТКРЫТА. "Готов Вх"
-- DRAINING — насос откачивает воду (после входа снаружи). "Осушение"
-- FAULT — нештатная ситуация. "Неисправен"
-- === Входы ===
-- 1 — левая дверь (внешняя): 0=закрыта, 1=открыта
-- 2 — правая дверь (внутренняя): 0=закрыта, 1=открыта
-- 3 — правая кнопка (внутри, пин 4)
-- 4 — внутренняя кнопка (внутри, левая, пин 3)
-- 5 — наружная правая кнопка (снаружи, пин 5)
-- 6 — уровень воды в шлюзе (-100..100)
-- === Выходы ===
-- 1 — внешняя дверь: 1=открыть, 0=закрыть
-- 2 — внутренняя дверь: 1=открыть, 0=закрыть
-- 3 — насос: 1=вкл, 0=выкл
-- 4 — целевой уровень воды (-100 сухо, 100 заполнен)
-- 5 — текст статуса
-- 6 — цвет текста (rrr,ggg,bbb)
inp = {}
--out = {
--[1] = 0,
--[2] = 0,
--[3] = 0,
--[4] = 0,
--[5] = "",
--[6] = "100,180,100",
--}
-- Константы задержек (сек)
local MIN_FLOOD_TIME = 4.0
local MIN_DRAIN_TIME = 4.0
local INTERNAL_DOOR_DELAY = 4.0
local FAULT_HYSTERESIS = 2.0
local S = {
IDLE = 0,
FLOODING = 1,
READY_OUT = 2,
READY_IN = 3,
DRAINING = 4,
FAULT = 5,
}
local state = S.IDLE
local stateTimer = 0.0
local doorTimer = 0.0
local openIntDoor= false -- флаг: открыть внутреннюю дверь (пропустить человека)
-- Детекция фронта кнопок
local prevBtn = { [3] = 0, [4] = 0, [5] = 0 }
local btnEdge = { [3] = 0, [4] = 0, [5] = 0 }
local STATUS_TEXT = {
[S.IDLE] = "",
[S.FLOODING] = "Затопление",
[S.READY_OUT] = "Готов вых",
[S.READY_IN] = "Готов Вх",
[S.DRAINING] = "Осушение",
[S.FAULT] = "Неисправен",
}
local STATUS_COLOR = {
[S.IDLE] = "100,180,100",
[S.FLOODING] = "255,165,0",
[S.READY_OUT] = "0,200,50",
[S.READY_IN] = "0,200,200",
[S.DRAINING] = "0,100,255",
[S.FAULT] = "200,0,0",
}
local function setOutputs(d1, d2, pump, target)
out[1] = d1 and 1 or 0
out[2] = d2 and 1 or 0
out[3] = pump and 1 or 0
out[4] = target or 0
end
local function setStatus(s)
out[5] = STATUS_TEXT[s] or ""
out[6] = STATUS_COLOR[s] or STATUS_COLOR[S.FAULT]
end
local function gotoState(s)
if state == s then return end
state = s
stateTimer = 0.0
end
-- Аварийные условия
local function checkFault(water, leftDoor, rightDoor)
if rightDoor == 1 and water > 10 then return true end
if leftDoor == 1 and rightDoor == 1 then return true end
if leftDoor == 1 and water < 50 then return true end
return false
end
function upd(dt)
-- Чтение входов
local leftDoor = inp[1] or 0
local rightDoor = inp[2] or 0
local btnRight = inp[3] or 0
local btnInternal= inp[4] or 0
local btnExternal= inp[5] or 0
local water = inp[6] or 0
-- Детекция фронтов
for _, p in ipairs({3,4,5}) do
local v = inp[p] or 0
btnEdge[p] = (v == 1 and prevBtn[p] == 0) and 1 or 0
prevBtn[p] = v
end
stateTimer = stateTimer + dt
-- === Аварийный контроль (высший приоритет) ===
if checkFault(water, leftDoor, rightDoor) then
gotoState(S.FAULT)
end
if state == S.FAULT and not checkFault(water, leftDoor, rightDoor)
and stateTimer > FAULT_HYSTERESIS then
gotoState(S.IDLE)
end
-- === Логика состояний ===
-- IDLE
if state == S.IDLE then
-- Если дверь открывали для пропуска — закрыть по таймеру
if openIntDoor then
doorTimer = doorTimer + dt
openIntDoor = doorTimer < INTERNAL_DOOR_DELAY
end
if btnEdge[4] == 1 then
-- Внутренняя кнопка → подготовка к выходу (затопление)
setOutputs(false, false, true, 100)
gotoState(S.FLOODING)
elseif btnEdge[5] == 1 then
-- Наружная кнопка → открыть внешнюю дверь, впустить воду
setOutputs(true, false, false, 0)
gotoState(S.FLOODING)
elseif leftDoor == 1 then
-- Внешнюю открыли вручную — подтверждаем и начинаем заполнение
out[1] = 1
gotoState(S.FLOODING)
else
-- Ручное управление внутренней дверью (для пропуска)
setOutputs(false, openIntDoor, false, 0)
setStatus(S.IDLE)
end
-- FLOODING
-- Внешнюю дверь НЕ трогаем — она уже установлена при входе в состояние:
-- закрыта (выход — закачка насосом) или открыта (вход — естественное заполнение)
elseif state == S.FLOODING then
out[2] = 0
out[3] = 1
out[4] = 100
-- Если дверь открыли вручную — подтверждаем, иначе привод будет бороться
if leftDoor == 1 then out[1] = 1 end
setStatus(S.FLOODING)
if btnEdge[3] == 1 then
-- Правая кнопка → отмена, сброс
setOutputs(false, false, false, 0)
gotoState(S.IDLE)
end
if water >= 95 and stateTimer >= MIN_FLOOD_TIME then
if leftDoor == 1 then
-- Внешняя открыта, вода есть → готовность к входу
gotoState(S.READY_IN)
else
-- Внешняя закрыта, вода закачана насосом → готовность к выходу
gotoState(S.READY_OUT)
end
end
-- READY_OUT
-- Не перезаписываем out[1] — внешняя дверь открывается однократно по кнопке
elseif state == S.READY_OUT then
out[2] = 0
out[3] = 0
out[4] = 100
setStatus(S.READY_OUT)
if btnEdge[3] == 1 then
out[1] = 1 -- открыть внешнюю дверь (выход)
end
if leftDoor == 1 then
gotoState(S.READY_IN)
end
if btnEdge[4] == 1 then
out[1] = 0
out[3] = 1
out[4] = -100
gotoState(S.DRAINING)
end
-- READY_IN
elseif state == S.READY_IN then
setOutputs(true, false, false, 100)
setStatus(S.READY_IN)
if leftDoor == 0 then
-- Внешняя закрыта (человек зашёл) → осушение
setOutputs(false, false, true, -100)
gotoState(S.DRAINING)
elseif btnEdge[3] == 1 then
-- Правая кнопка → закрыть внешнюю дверь и начать осушение
setOutputs(false, false, true, -100)
gotoState(S.DRAINING)
end
-- DRAINING
elseif state == S.DRAINING then
setOutputs(false, false, true, -100)
setStatus(S.DRAINING)
if btnEdge[3] == 1 then
-- Правая кнопка → отмена осушения
setOutputs(false, false, false, 0)
gotoState(S.IDLE)
end
if water <= 5 and stateTimer >= MIN_DRAIN_TIME then
openIntDoor = true
doorTimer = 0.0
gotoState(S.IDLE)
end
-- FAULT
elseif state == S.FAULT then
setOutputs(false, false, false, 0)
setStatus(S.FAULT)
end
end