init basic
This commit is contained in:
258
AirlockRTL.lua
Normal file
258
AirlockRTL.lua
Normal file
@@ -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
|
||||||
113
AtlasOS.lua
Normal file
113
AtlasOS.lua
Normal file
@@ -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
|
||||||
Reference in New Issue
Block a user