Initialize modular ATM10 CC control system

This commit is contained in:
2026-07-13 02:46:11 +02:00
commit 5362d31036
28 changed files with 331 additions and 0 deletions
+5
View File
@@ -0,0 +1,5 @@
*.tmp
*.bak
runtime/
config/local.lua
+37
View File
@@ -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.
+9
View File
@@ -0,0 +1,9 @@
return {
role = "main_control",
nodeName = "atm10-main",
sector = "main",
monitor = nil,
heartbeatSeconds = 5,
commandTimeoutSeconds = 10,
}
+9
View File
@@ -0,0 +1,9 @@
return {
role = "main_control",
nodeName = "atm10-main",
sector = "main",
monitor = nil,
heartbeatSeconds = 5,
commandTimeoutSeconds = 10,
}
+10
View File
@@ -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",
},
}
+12
View File
@@ -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
+18
View File
@@ -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
+22
View File
@@ -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
+19
View File
@@ -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
+6
View File
@@ -0,0 +1,6 @@
local M = {}
function M.run()
print("Updater-Schnittstelle vorbereitet; atomisches Update folgt vor v0.1.0.")
end
return M
+18
View File
@@ -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`.
+18
View File
@@ -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`.
+2
View File
@@ -0,0 +1,2 @@
return { probe = function() return nil, "create driver not configured" end }
+2
View File
@@ -0,0 +1,2 @@
return { probe = function() return nil, "energy driver not configured" end }
+2
View File
@@ -0,0 +1,2 @@
return { probe = function() return nil, "inventory driver not configured" end }
+2
View File
@@ -0,0 +1,2 @@
return { probe = function() return nil, "mekanism driver not configured" end }
+5
View File
@@ -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
View File
@@ -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.")
+29
View File
@@ -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",
},
}
+8
View File
@@ -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
+8
View File
@@ -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
+8
View File
@@ -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
+8
View File
@@ -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
+7
View File
@@ -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()
+2
View File
@@ -0,0 +1,2 @@
return { active = function() return {} end }
+2
View File
@@ -0,0 +1,2 @@
return { background = colors.black, text = colors.white, ok = colors.lime, warning = colors.orange, fault = colors.red, accent = colors.cyan }
+11
View File
@@ -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
+8
View File
@@ -0,0 +1,8 @@
local M = {}
function M.title(target, text)
target.setCursorPos(1, 1)
target.clearLine()
target.write(text)
end
return M