Compare commits

...

2 Commits

Author SHA1 Message Date
Dystroyer8 a91b354295 Add Gitea reauthentication helper 2026-07-16 20:34:02 +02:00
Dystroyer8 215df3f75d Add Create boiler status display 2026-07-16 20:28:11 +02:00
4 changed files with 261 additions and 2 deletions
+17 -1
View File
@@ -19,6 +19,23 @@ install
Der Installer ist momentan ein Bootstrap. Hardwaretreiber und konkrete Anlagenlogik
werden ergaenzt, sobald die Peripherie- und Methodennamen der gebauten Anlagen feststehen.
## Einfache Create-Boiler-Anzeige
Fuer die erste Kraftwerksanzeige werden ein Monitor, pro Boiler ein Create-Speedometer,
ein Create-Stressometer an der Hauptwelle und optional pro Boiler ein Block Reader aus
Advanced Peripherals ueber Wired Modems mit dem Computer verbunden. Installation und Start:
```lua
wget https://git.peli-server.de/Dystroyer8/ATM10_CC_Codes/raw/branch/main/create_boiler_display.lua create_boiler_display.lua
create_boiler_display
```
Das Programm zeigt pro Boiler Online-Status, RPM und - mit Block Reader - den Heizmodus.
Zusaetzlich zeigt es Kapazitaet, Verbrauch und Auslastung der Hauptwelle. Die Zuordnung erfolgt
nach den internen Peripheral-Namen; Speedometer und Block Reader sollten deshalb jeweils in der
Reihenfolge Boiler 1 bis 3 an das Wired Network angeschlossen werden. Jeder Block Reader muss
auf den Controller-Tank des zugeordneten Boiler-Multiblocks zeigen.
## Struktur
```text
@@ -34,4 +51,3 @@ docs/ Architektur- und Protokolldokumentation
Die Zentrale fordert nur einen Zielzustand an. Lokale Controller fuehren sichere
Start-/Stoppsequenzen aus und bestaetigen den tatsaechlichen Zustand.
+189
View File
@@ -0,0 +1,189 @@
-- Create boiler status board for CC:Tweaked.
-- Hardware:
-- * one monitor
-- * one Create Speedometer per boiler
-- * one Create Stressometer on the main shaft
-- * optionally one Advanced Peripherals Block Reader per boiler controller
-- Everything must be connected to the computer, preferably using Wired Modems.
local EXPECTED_BOILERS = 3
local REFRESH_SECONDS = 1
local MONITOR_SCALE = 0.5
local SPEEDOMETER_TYPE = "Create_Speedometer"
local STRESSOMETER_TYPE = "Create_Stressometer"
local BLOCK_READER_TYPE = "block_reader"
local monitor = peripheral.find("monitor")
if not monitor then
error("Kein Monitor gefunden. Monitor direkt anschliessen oder per Wired Modem verbinden.", 0)
end
monitor.setTextScale(MONITOR_SCALE)
monitor.setCursorBlink(false)
local function center(y, text, foreground, background)
local width = monitor.getSize()
text = tostring(text)
if #text > width then text = text:sub(1, width) end
local x = math.max(1, math.floor((width - #text) / 2) + 1)
monitor.setBackgroundColor(background or colors.black)
monitor.setTextColor(foreground or colors.white)
monitor.setCursorPos(1, y)
monitor.write((" "):rep(width))
monitor.setCursorPos(x, y)
monitor.write(text)
end
local function getPeripheralsByType(wantedType)
local names = {}
for _, name in ipairs(peripheral.getNames()) do
if peripheral.getType(name) == wantedType then
names[#names + 1] = name
end
end
table.sort(names)
return names
end
local function getValueIgnoringCase(data, wantedKeys)
if type(data) ~= "table" then return nil end
for key, value in pairs(data) do
local lowered = tostring(key):lower()
for _, wanted in ipairs(wantedKeys) do
if lowered == wanted:lower() then return value end
end
end
return nil
end
local function findBoilerData(data, visited)
if type(data) ~= "table" then return nil end
visited = visited or {}
if visited[data] then return nil end
visited[data] = true
local boiler = getValueIgnoringCase(data, { "Boiler" })
if type(boiler) == "table" then return boiler end
local heat = getValueIgnoringCase(data, { "Heat", "activeHeat", "actualHeat" })
local passive = getValueIgnoringCase(data, { "PassiveHeat", "passive" })
if heat ~= nil or passive ~= nil then return data end
for _, value in pairs(data) do
local found = findBoilerData(value, visited)
if found then return found end
end
return nil
end
local function readHeatMode(readerName)
if not readerName then return "UNKNOWN" end
local ok, data = pcall(peripheral.call, readerName, "getBlockData")
if not ok then return "ERROR" end
local boiler = findBoilerData(data)
if not boiler then return "UNKNOWN" end
local passive = getValueIgnoringCase(boiler, { "PassiveHeat", "passive" })
local heat = tonumber(getValueIgnoringCase(boiler,
{ "Heat", "activeHeat", "actualHeat", "boilerLevel", "level" }))
if passive == true or passive == 1 then return "CAMPFIRE" end
if heat and heat >= 18 then return "SUPERHEATED" end
if heat and heat > 0 then return "HEATED" end
return "COLD"
end
local function readBoiler(name, readerName)
if not name then
return "MISSING", 0, "UNKNOWN", colors.red
end
local ok, speed = pcall(peripheral.call, name, "getSpeed")
if not ok or type(speed) ~= "number" then
return "ERROR", 0, "UNKNOWN", colors.red
end
speed = math.abs(speed)
if speed < 0.5 then
return "STANDBY", 0, readHeatMode(readerName), colors.orange
end
return "ONLINE", speed, readHeatMode(readerName), colors.lime
end
local function formatInteger(value)
value = math.floor(math.abs(tonumber(value) or 0) + 0.5)
local digits = tostring(value)
local parts = {}
while #digits > 3 do
table.insert(parts, 1, digits:sub(-3))
digits = digits:sub(1, -4)
end
table.insert(parts, 1, digits)
return table.concat(parts, ".")
end
local function readMainShaft(stressometerName)
if not stressometerName then return nil, nil, nil end
local okCapacity, capacity = pcall(peripheral.call,
stressometerName, "getNetworkCapacity")
local okUsage, usage = pcall(peripheral.call,
stressometerName, "getNetworkStress")
if not okCapacity or not okUsage then return nil, nil, nil end
capacity = tonumber(capacity) or 0
usage = tonumber(usage) or 0
local load = capacity > 0 and usage / capacity * 100 or 0
return capacity, usage, load
end
local function render()
local _, height = monitor.getSize()
local speedometers = getPeripheralsByType(SPEEDOMETER_TYPE)
local stressometers = getPeripheralsByType(STRESSOMETER_TYPE)
local readers = getPeripheralsByType(BLOCK_READER_TYPE)
monitor.setBackgroundColor(colors.black)
monitor.clear()
center(1, "CREATE KRAFTWERK", colors.cyan)
local firstRow = 3
for index = 1, EXPECTED_BOILERS do
local state, speed, heatMode, color = readBoiler(speedometers[index], readers[index])
local row = firstRow + index - 1
if row <= height then
center(row, ("BOILER %d %-7s %5.1f RPM %s")
:format(index, state, speed, heatMode), color)
end
end
local capacity, usage, load = readMainShaft(stressometers[1])
if height >= 7 then center(7, "MAIN SHAFT", colors.cyan) end
if capacity then
if height >= 8 then center(8, "Capacity: " .. formatInteger(capacity) .. " SU", colors.white) end
if height >= 9 then center(9, "Usage: " .. formatInteger(usage) .. " SU", colors.white) end
if height >= 10 then
local loadText = ("Load: %.1f %%"):format(load):gsub("%.", ",")
center(10, loadText, load >= 90 and colors.red or load >= 70 and colors.orange or colors.lime)
end
else
if height >= 8 then center(8, "Stressometer nicht gefunden", colors.red) end
end
if height >= 12 then
center(height, "Nur Anzeige - keine Steuerung", colors.gray)
end
monitor.setTextColor(colors.white)
monitor.setBackgroundColor(colors.black)
term.setCursorPos(1, 1)
end
while true do
local ok, reason = pcall(render)
if not ok then
monitor.setBackgroundColor(colors.black)
monitor.clear()
center(1, "ANZEIGEFEHLER", colors.red)
center(3, tostring(reason), colors.orange)
end
sleep(REFRESH_SECONDS)
end
+1 -1
View File
@@ -4,6 +4,7 @@ return {
channel = "main",
files = {
"startup.lua",
"create_boiler_display.lua",
"core/constants.lua",
"core/log.lua",
"core/protocol.lua",
@@ -26,4 +27,3 @@ return {
"config/default.lua",
},
}
+54
View File
@@ -0,0 +1,54 @@
[CmdletBinding()]
param(
[string]$Remote = "origin",
[string]$Branch = "main"
)
$ErrorActionPreference = "Stop"
$HostName = "git.peli-server.de"
$Repository = $PSScriptRoot
function Invoke-Git {
param([Parameter(ValueFromRemainingArguments = $true)][string[]]$Arguments)
& git -C $Repository @Arguments
if ($LASTEXITCODE -ne 0) {
throw "Git-Befehl fehlgeschlagen: git $($Arguments -join ' ')"
}
}
if (-not (Get-Command git -ErrorAction SilentlyContinue)) {
throw "Git wurde nicht gefunden. Installiere Git for Windows oder fuege es zum PATH hinzu."
}
$remoteUrl = (& git -C $Repository remote get-url $Remote 2>$null)
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($remoteUrl)) {
throw "Remote '$Remote' wurde im Repository nicht gefunden."
}
if ($remoteUrl -notmatch [regex]::Escape($HostName)) {
throw "Sicherheitsabbruch: Remote '$Remote' zeigt nicht auf $HostName ($remoteUrl)."
}
Write-Host "Gitea-Anmeldung fuer $HostName wird erneuert." -ForegroundColor Cyan
Write-Host "Der alte Windows-Credential-Manager-Eintrag wird jetzt entfernt."
$credentialQuery = "protocol=https`nhost=$HostName`n`n"
$credentialQuery | & git credential reject
if ($LASTEXITCODE -ne 0) {
throw "Der alte Git-Credential-Eintrag konnte nicht entfernt werden."
}
Write-Host ""
Write-Host "Beim folgenden Push sollte Git Credential Manager nach der Anmeldung fragen." -ForegroundColor Yellow
Write-Host "Benutzername: dein Gitea-Benutzername"
Write-Host "Passwort: dein Gitea-Passwort oder besser ein Personal Access Token"
Write-Host "Das Passwort/Token niemals hier im Chat einfuegen."
Write-Host ""
$env:GCM_INTERACTIVE = "always"
Invoke-Git push --set-upstream $Remote $Branch
Write-Host ""
Write-Host "Anmeldung gespeichert und Push erfolgreich." -ForegroundColor Green
Write-Host "Kuenftige Pushes aus Codex sollten wieder ohne erneute Anmeldung funktionieren."