commit f4e525b0355a317bf1bcf2177e63043397aa1dfd Author: SlavaVlad Date: Sun Jun 7 02:33:22 2026 +0300 init basic diff --git a/AirlockRTL.lua b/AirlockRTL.lua new file mode 100644 index 0000000..ceb8fb8 --- /dev/null +++ b/AirlockRTL.lua @@ -0,0 +1,258 @@ +-- 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 diff --git a/AtlasOS.lua b/AtlasOS.lua new file mode 100644 index 0000000..fd98f5d --- /dev/null +++ b/AtlasOS.lua @@ -0,0 +1,113 @@ +-- CLI бортового компьютера AtlasOS v1.1 +-- Вход: пин 1 - команда; остальные пины не используются +-- Выходы: 1 - текст, 2 - очистка (1), 3 - цвет (R,G,B) + +-- Глобальный цвет по умолчанию (зелёный) +local defaultColor = "0,255,0" +local currentColor = defaultColor +-- Устанавливаем начальный цвет +out[3] = currentColor + +-- Таблица команд +local commands = {} + +-- Регистрация команды +local function register_command(name, func, description) + commands[name] = { func = func, desc = description } +end + +-- Вывод обычного текста с текущим цветом +local function print(text) + out[3] = currentColor + out[1] = text +end + +-- Вывод текста ошибки (красный) с последующим возвратом цвета +local function printerror(text) + out[3] = "255,40,40" + out[1] = text + out[3] = currentColor +end + +-- Обработчик команд +local function process_command(cmd_str) + local parts = {} + for token in tostring(cmd_str):gmatch("%S+") do + table.insert(parts, token) + end + local cmd = parts[1] + local args = {} + for i = 2, #parts do + table.insert(args, parts[i]) + end + + local cmd_entry = commands[cmd] + if cmd_entry then + local success, result = pcall(cmd_entry.func, args) + if success then + if result ~= nil then + -- Команда вернула строку для вывода + print(result) + end + -- если result == nil, команда сама обработала вывод + else + printerror(tostring(result)) + end + else + printerror("Error: Unknown command. Type 'help' for list.") + end +end + +-- ---- Команды ---- + +-- help +register_command("help", function(args) + local help_text = "Available commands:\n" + for name, entry in pairs(commands) do + help_text = help_text .. " " .. name .. " - " .. entry.desc .. "\n" + end + return help_text +end, "Show this help") + +-- echo +register_command("echo", function(args) + if #args == 0 then + error("echo requires text.") -- будет перехвачено pcall + end + return table.concat(args, " ") +end, "Echo the input text") + +-- clear +register_command("clear", function(args) + out[2] = 1 + return nil +end, "Clear the screen") + +-- color (устанавливает цвет для последующих обычных выводов) +register_command("color", function(args) + if #args < 3 then + error("color requires 3 numbers (R G B)") + end + local r, g, b = tonumber(args[1]), tonumber(args[2]), tonumber(args[3]) + if not (r and g and b) then + error("invalid color values") + end + r = math.max(0, math.min(255, math.floor(r))) + g = math.max(0, math.min(255, math.floor(g))) + b = math.max(0, math.min(255, math.floor(b))) + currentColor = r .. "," .. g .. "," .. b + print("Color set to " .. currentColor) + return nil -- print уже вывел сообщение +end, "Set text color (R G B)") + +-- status +register_command("status", function(args) + return "AtlasOS v1.1 - All systems nominal." +end, "Show system status") + +-- Входной сигнал +function inp(pin, val) + if pin == 1 then + process_command(val) + end +end \ No newline at end of file