Initialize modular ATM10 CC control system
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
*.tmp
|
||||||
|
*.bak
|
||||||
|
runtime/
|
||||||
|
config/local.lua
|
||||||
|
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
# ATM10 CC Control System
|
||||||
|
|
||||||
|
Modulares CC:Tweaked-Steuerungssystem fuer die ATM10-Megabase.
|
||||||
|
|
||||||
|
## Zielbild
|
||||||
|
|
||||||
|
- zentrale Leitwarte fuer Status, Energie, Alarme und Sektor-Hauptschalter
|
||||||
|
- lokale Sektorcontroller fuer Create, Mekanism, Greenhouse und spaetere Mods
|
||||||
|
- Sicherheitslogik bleibt immer im jeweiligen Sektor
|
||||||
|
- versionierte Installation und Updates ueber Gitea
|
||||||
|
|
||||||
|
## Installation auf einem CC-Computer
|
||||||
|
|
||||||
|
```lua
|
||||||
|
wget https://git.peli-server.de/Dystroyer8/ATM10_CC_Codes/raw/branch/main/install.lua install.lua
|
||||||
|
install
|
||||||
|
```
|
||||||
|
|
||||||
|
Der Installer ist momentan ein Bootstrap. Hardwaretreiber und konkrete Anlagenlogik
|
||||||
|
werden ergaenzt, sobald die Peripherie- und Methodennamen der gebauten Anlagen feststehen.
|
||||||
|
|
||||||
|
## Struktur
|
||||||
|
|
||||||
|
```text
|
||||||
|
core/ Laufzeit, Netzwerk, Protokoll, Logging, Updates
|
||||||
|
drivers/ Adapter fuer Redstone und Mod-Peripherie
|
||||||
|
roles/ Zentrale und Sektorcontroller
|
||||||
|
ui/ Monitore, Widgets und Alarmdarstellung
|
||||||
|
config/ Konfigurationsvorlagen
|
||||||
|
docs/ Architektur- und Protokolldokumentation
|
||||||
|
```
|
||||||
|
|
||||||
|
## Sicherheitsprinzip
|
||||||
|
|
||||||
|
Die Zentrale fordert nur einen Zielzustand an. Lokale Controller fuehren sichere
|
||||||
|
Start-/Stoppsequenzen aus und bestaetigen den tatsaechlichen Zustand.
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
return {
|
||||||
|
role = "main_control",
|
||||||
|
nodeName = "atm10-main",
|
||||||
|
sector = "main",
|
||||||
|
monitor = nil,
|
||||||
|
heartbeatSeconds = 5,
|
||||||
|
commandTimeoutSeconds = 10,
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
return {
|
||||||
|
role = "main_control",
|
||||||
|
nodeName = "atm10-main",
|
||||||
|
sector = "main",
|
||||||
|
monitor = nil,
|
||||||
|
heartbeatSeconds = 5,
|
||||||
|
commandTimeoutSeconds = 10,
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
return {
|
||||||
|
PROTOCOL = "atm10-control",
|
||||||
|
PROTOCOL_VERSION = 1,
|
||||||
|
STATES = {
|
||||||
|
OFFLINE = "OFFLINE", STARTING = "STARTING", RUNNING = "RUNNING",
|
||||||
|
IDLE = "IDLE", STOPPING = "STOPPING", MAINTENANCE = "MAINTENANCE",
|
||||||
|
FAULT = "FAULT", EMERGENCY = "EMERGENCY", UNLOADED = "UNLOADED",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
local M = {}
|
||||||
|
function M.write(level, message)
|
||||||
|
local line = ("[%s] %-5s %s"):format(os.date("!%Y-%m-%dT%H:%M:%SZ"), level, tostring(message))
|
||||||
|
print(line)
|
||||||
|
local handle = fs.open("runtime/control.log", "a")
|
||||||
|
if handle then handle.writeLine(line); handle.close() end
|
||||||
|
end
|
||||||
|
function M.info(message) M.write("INFO", message) end
|
||||||
|
function M.warn(message) M.write("WARN", message) end
|
||||||
|
function M.error(message) M.write("ERROR", message) end
|
||||||
|
return M
|
||||||
|
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
local constants = require("core.constants")
|
||||||
|
local protocol = require("core.protocol")
|
||||||
|
local M = {}
|
||||||
|
function M.open()
|
||||||
|
for _, name in ipairs(peripheral.getNames()) do
|
||||||
|
if peripheral.hasType(name, "modem") then rednet.open(name) end
|
||||||
|
end
|
||||||
|
return rednet.isOpen()
|
||||||
|
end
|
||||||
|
function M.broadcast(message) rednet.broadcast(message, constants.PROTOCOL) end
|
||||||
|
function M.send(id, message) return rednet.send(id, message, constants.PROTOCOL) end
|
||||||
|
function M.receive(timeout)
|
||||||
|
local sender, message = rednet.receive(constants.PROTOCOL, timeout)
|
||||||
|
if sender and protocol.valid(message) then return sender, message end
|
||||||
|
return nil, nil
|
||||||
|
end
|
||||||
|
return M
|
||||||
|
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
local constants = require("core.constants")
|
||||||
|
local M = {}
|
||||||
|
function M.message(kind, source, target, payload)
|
||||||
|
return {
|
||||||
|
protocol = constants.PROTOCOL,
|
||||||
|
version = constants.PROTOCOL_VERSION,
|
||||||
|
type = kind,
|
||||||
|
source = source,
|
||||||
|
target = target,
|
||||||
|
requestId = ("%s-%d-%d"):format(source, os.getComputerID(), os.epoch("utc")),
|
||||||
|
timestamp = os.epoch("utc"),
|
||||||
|
payload = payload or {},
|
||||||
|
}
|
||||||
|
end
|
||||||
|
function M.valid(message)
|
||||||
|
return type(message) == "table"
|
||||||
|
and message.protocol == constants.PROTOCOL
|
||||||
|
and message.version == constants.PROTOCOL_VERSION
|
||||||
|
and type(message.type) == "string"
|
||||||
|
end
|
||||||
|
return M
|
||||||
|
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
local log = require("core.log")
|
||||||
|
local network = require("core.network")
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
local function loadConfig()
|
||||||
|
if not fs.exists("config/local.lua") then error("config/local.lua fehlt") end
|
||||||
|
return dofile("config/local.lua")
|
||||||
|
end
|
||||||
|
function M.run()
|
||||||
|
if not fs.exists("runtime") then fs.makeDir("runtime") end
|
||||||
|
local config = loadConfig()
|
||||||
|
os.setComputerLabel(config.nodeName)
|
||||||
|
network.open()
|
||||||
|
local role = require("roles." .. config.role)
|
||||||
|
log.info("Starte " .. config.nodeName .. " als " .. config.role)
|
||||||
|
role.run(config)
|
||||||
|
end
|
||||||
|
return M
|
||||||
|
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
local M = {}
|
||||||
|
function M.run()
|
||||||
|
print("Updater-Schnittstelle vorbereitet; atomisches Update folgt vor v0.1.0.")
|
||||||
|
end
|
||||||
|
return M
|
||||||
|
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Architektur
|
||||||
|
|
||||||
|
## Verantwortlichkeiten
|
||||||
|
|
||||||
|
- Hauptzentrale: Anzeige, Befehle, Alarmaggregation, Historie
|
||||||
|
- Sektorcontroller: Start-/Stoppsequenzen, lokale Interlocks, Teilanlagen
|
||||||
|
- Anlagencontroller: direkte Peripherie, Messwerte und Aktoren
|
||||||
|
|
||||||
|
## Grundsatz
|
||||||
|
|
||||||
|
Ein zentraler Befehl beschreibt einen gewuenschten Zustand. Nur der lokale
|
||||||
|
Controller entscheidet, wie dieser Zustand sicher erreicht wird.
|
||||||
|
|
||||||
|
## Sektorzustaende
|
||||||
|
|
||||||
|
`OFFLINE`, `STARTING`, `RUNNING`, `IDLE`, `STOPPING`, `MAINTENANCE`,
|
||||||
|
`FAULT`, `EMERGENCY`, `UNLOADED`.
|
||||||
|
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Netzwerkprotokoll v1
|
||||||
|
|
||||||
|
Alle Rednet-Nachrichten verwenden das Protokoll `atm10-control`.
|
||||||
|
|
||||||
|
Pflichtfelder:
|
||||||
|
|
||||||
|
- `protocol`
|
||||||
|
- `version`
|
||||||
|
- `type`
|
||||||
|
- `source`
|
||||||
|
- `target`
|
||||||
|
- `requestId`
|
||||||
|
- `timestamp`
|
||||||
|
- `payload`
|
||||||
|
|
||||||
|
Vorgesehene Nachrichtentypen: `HEARTBEAT`, `STATUS`, `COMMAND`, `ACK`,
|
||||||
|
`PROGRESS`, `ALARM`, `CLEAR_ALARM`.
|
||||||
|
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
return { probe = function() return nil, "create driver not configured" end }
|
||||||
|
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
return { probe = function() return nil, "energy driver not configured" end }
|
||||||
|
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
return { probe = function() return nil, "inventory driver not configured" end }
|
||||||
|
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
return { probe = function() return nil, "mekanism driver not configured" end }
|
||||||
|
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
local M = {}
|
||||||
|
function M.set(side, enabled) redstone.setOutput(side, enabled == true) end
|
||||||
|
function M.get(side) return redstone.getInput(side) end
|
||||||
|
return M
|
||||||
|
|
||||||
+44
@@ -0,0 +1,44 @@
|
|||||||
|
local BASE_URL = "https://git.peli-server.de/Dystroyer8/ATM10_CC_Codes/raw/branch/main/"
|
||||||
|
|
||||||
|
local function fail(message)
|
||||||
|
printError(message)
|
||||||
|
error(message, 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function fetch(path)
|
||||||
|
local response, reason = http.get(BASE_URL .. path)
|
||||||
|
if not response then fail("Download fehlgeschlagen: " .. path .. " (" .. tostring(reason) .. ")") end
|
||||||
|
local body = response.readAll()
|
||||||
|
response.close()
|
||||||
|
return body
|
||||||
|
end
|
||||||
|
|
||||||
|
local function writeFile(path, content)
|
||||||
|
local directory = fs.getDir(path)
|
||||||
|
if directory ~= "" and not fs.exists(directory) then fs.makeDir(directory) end
|
||||||
|
local temporary = path .. ".tmp"
|
||||||
|
local handle = assert(fs.open(temporary, "w"))
|
||||||
|
handle.write(content)
|
||||||
|
handle.close()
|
||||||
|
if fs.exists(path) then fs.delete(path) end
|
||||||
|
fs.move(temporary, path)
|
||||||
|
end
|
||||||
|
|
||||||
|
print("ATM10 Control bootstrap")
|
||||||
|
local manifestText = fetch("manifest.lua")
|
||||||
|
local manifestFn, loadError = load(manifestText, "manifest", "t", {})
|
||||||
|
if not manifestFn then fail("Manifest ungueltig: " .. tostring(loadError)) end
|
||||||
|
local manifest = manifestFn()
|
||||||
|
|
||||||
|
for index, path in ipairs(manifest.files) do
|
||||||
|
write(("[%d/%d] %s"):format(index, #manifest.files, path))
|
||||||
|
writeFile(path, fetch(path))
|
||||||
|
print(" OK")
|
||||||
|
end
|
||||||
|
|
||||||
|
if not fs.exists("config/local.lua") then
|
||||||
|
writeFile("config/local.lua", fetch("config/example.lua"))
|
||||||
|
end
|
||||||
|
writeFile(".atm10-version", manifest.version .. "\n")
|
||||||
|
print("Installation abgeschlossen. Konfiguriere config/local.lua und starte neu.")
|
||||||
|
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
return {
|
||||||
|
name = "atm10-control",
|
||||||
|
version = "0.1.0-dev",
|
||||||
|
channel = "main",
|
||||||
|
files = {
|
||||||
|
"startup.lua",
|
||||||
|
"core/constants.lua",
|
||||||
|
"core/log.lua",
|
||||||
|
"core/protocol.lua",
|
||||||
|
"core/network.lua",
|
||||||
|
"core/runtime.lua",
|
||||||
|
"core/updater.lua",
|
||||||
|
"drivers/redstone.lua",
|
||||||
|
"drivers/energy.lua",
|
||||||
|
"drivers/mekanism.lua",
|
||||||
|
"drivers/create.lua",
|
||||||
|
"drivers/inventory.lua",
|
||||||
|
"ui/colors.lua",
|
||||||
|
"ui/widgets.lua",
|
||||||
|
"ui/dashboard.lua",
|
||||||
|
"ui/alarms.lua",
|
||||||
|
"roles/main_control.lua",
|
||||||
|
"roles/create_control.lua",
|
||||||
|
"roles/mekanism_control.lua",
|
||||||
|
"roles/greenhouse_control.lua",
|
||||||
|
"config/default.lua",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
local dashboard = require("ui.dashboard")
|
||||||
|
local M = {}
|
||||||
|
function M.run(config)
|
||||||
|
dashboard.render(term, "CREATE CONTROL", { "Bootstrap aktiv", "Sector: " .. config.sector })
|
||||||
|
while true do os.pullEvent() end
|
||||||
|
end
|
||||||
|
return M
|
||||||
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
local dashboard = require("ui.dashboard")
|
||||||
|
local M = {}
|
||||||
|
function M.run(config)
|
||||||
|
dashboard.render(term, "GREENHOUSE CONTROL", { "Bootstrap aktiv", "Sector: " .. config.sector })
|
||||||
|
while true do os.pullEvent() end
|
||||||
|
end
|
||||||
|
return M
|
||||||
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
local dashboard = require("ui.dashboard")
|
||||||
|
local M = {}
|
||||||
|
function M.run(config)
|
||||||
|
dashboard.render(term, "ATM10 MAIN CONTROL", { "Bootstrap aktiv", "Node: " .. config.nodeName })
|
||||||
|
while true do os.pullEvent() end
|
||||||
|
end
|
||||||
|
return M
|
||||||
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
local dashboard = require("ui.dashboard")
|
||||||
|
local M = {}
|
||||||
|
function M.run(config)
|
||||||
|
dashboard.render(term, "MEKANISM CONTROL", { "Bootstrap aktiv", "Sector: " .. config.sector })
|
||||||
|
while true do os.pullEvent() end
|
||||||
|
end
|
||||||
|
return M
|
||||||
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
local ok, runtime = pcall(require, "core.runtime")
|
||||||
|
if not ok then
|
||||||
|
printError("ATM10 Control konnte nicht starten: " .. tostring(runtime))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
runtime.run()
|
||||||
|
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
return { active = function() return {} end }
|
||||||
|
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
return { background = colors.black, text = colors.white, ok = colors.lime, warning = colors.orange, fault = colors.red, accent = colors.cyan }
|
||||||
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
local widgets = require("ui.widgets")
|
||||||
|
local M = {}
|
||||||
|
function M.render(target, title, lines)
|
||||||
|
target.setBackgroundColor(colors.black)
|
||||||
|
target.setTextColor(colors.white)
|
||||||
|
target.clear()
|
||||||
|
widgets.title(target, title)
|
||||||
|
for index, line in ipairs(lines or {}) do target.setCursorPos(1, index + 2); target.write(line) end
|
||||||
|
end
|
||||||
|
return M
|
||||||
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
local M = {}
|
||||||
|
function M.title(target, text)
|
||||||
|
target.setCursorPos(1, 1)
|
||||||
|
target.clearLine()
|
||||||
|
target.write(text)
|
||||||
|
end
|
||||||
|
return M
|
||||||
|
|
||||||
Reference in New Issue
Block a user