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