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