-- 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