Read Create gauges through block readers
This commit is contained in:
@@ -21,9 +21,10 @@ werden ergaenzt, sobald die Peripherie- und Methodennamen der gebauten Anlagen f
|
|||||||
|
|
||||||
## Einfache Create-Boiler-Anzeige
|
## Einfache Create-Boiler-Anzeige
|
||||||
|
|
||||||
Fuer die erste Kraftwerksanzeige werden ein Monitor, ein Create-Speedometer und
|
Fuer die erste Kraftwerksanzeige werden ein Monitor sowie pro Boiler ein Block Reader aus
|
||||||
ein Create-Stressometer an der gemeinsamen Hauptwelle sowie pro Boiler ein Block Reader aus
|
Advanced Peripherals benoetigt. Mindestens ein weiterer Block Reader zeigt auf das Create-
|
||||||
Advanced Peripherals ueber Wired Modems mit dem Computer verbunden. Installation und Start:
|
Speedometer oder Stressometer der gemeinsamen Hauptwelle. Alle Reader und der Monitor werden
|
||||||
|
ueber Wired Modems mit dem Computer verbunden. Installation und Start:
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
wget https://git.peli-server.de/Dystroyer8/ATM10_CC_Codes/raw/branch/main/create_boiler_display.lua create_boiler_display.lua
|
wget https://git.peli-server.de/Dystroyer8/ATM10_CC_Codes/raw/branch/main/create_boiler_display.lua create_boiler_display.lua
|
||||||
@@ -31,7 +32,8 @@ create_boiler_display
|
|||||||
```
|
```
|
||||||
|
|
||||||
Das Programm liest Online-Status und Heizmodus jedes Boilers ueber dessen Block Reader.
|
Das Programm liest Online-Status und Heizmodus jedes Boilers ueber dessen Block Reader.
|
||||||
Drehzahl, Kapazitaet, Verbrauch und Auslastung werden fuer die gemeinsame Hauptwelle angezeigt.
|
Drehzahl, Kapazitaet, Verbrauch und Auslastung liest es aus den Netzwerkdaten des Gauge-Readers.
|
||||||
|
Die Monitoraufloesung wird automatisch erkannt und die groesste passende Textskalierung gewaehlt.
|
||||||
Die Boiler-Zuordnung erfolgt nach den internen Peripheral-Namen; die drei Block Reader sollten
|
Die Boiler-Zuordnung erfolgt nach den internen Peripheral-Namen; die drei Block Reader sollten
|
||||||
deshalb in der Reihenfolge Boiler 1 bis 3 an das Wired Network angeschlossen werden. Jeder Block
|
deshalb 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.
|
Reader muss auf den Controller-Tank des zugeordneten Boiler-Multiblocks zeigen.
|
||||||
|
|||||||
+82
-135
@@ -1,25 +1,34 @@
|
|||||||
-- Create boiler status board for CC:Tweaked.
|
-- Create boiler status board for CC:Tweaked.
|
||||||
-- Hardware:
|
-- All Create values are read through Advanced Peripherals Block Readers.
|
||||||
-- * one monitor
|
|
||||||
-- * one Create Speedometer on the shared main shaft
|
|
||||||
-- * one Create Stressometer on the main shaft
|
|
||||||
-- * one Advanced Peripherals Block Reader per boiler controller
|
|
||||||
-- Everything must be connected to the computer, preferably using Wired Modems.
|
|
||||||
|
|
||||||
local EXPECTED_BOILERS = 3
|
local EXPECTED_BOILERS = 3
|
||||||
local REFRESH_SECONDS = 1
|
local REFRESH_SECONDS = 1
|
||||||
local MONITOR_SCALE = 0.5
|
local MIN_WIDTH = 38
|
||||||
local SPEEDOMETER_TYPE = "Create_Speedometer"
|
local MIN_HEIGHT = 11
|
||||||
local STRESSOMETER_TYPE = "Create_Stressometer"
|
|
||||||
local BLOCK_READER_TYPE = "block_reader"
|
|
||||||
|
|
||||||
local monitor = peripheral.find("monitor")
|
local monitor = peripheral.find("monitor")
|
||||||
if not monitor then
|
if not monitor then
|
||||||
error("Kein Monitor gefunden. Monitor direkt anschliessen oder per Wired Modem verbinden.", 0)
|
error("Kein Monitor gefunden. Monitor direkt oder per Wired Modem verbinden.", 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
monitor.setTextScale(MONITOR_SCALE)
|
local function fitMonitor()
|
||||||
|
local chosen = 0.5
|
||||||
|
for step = 10, 1, -1 do
|
||||||
|
local scale = step / 2
|
||||||
|
local ok = pcall(monitor.setTextScale, scale)
|
||||||
|
if ok then
|
||||||
|
local width, height = monitor.getSize()
|
||||||
|
if width >= MIN_WIDTH and height >= MIN_HEIGHT then
|
||||||
|
chosen = scale
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
monitor.setTextScale(chosen)
|
||||||
monitor.setCursorBlink(false)
|
monitor.setCursorBlink(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
fitMonitor()
|
||||||
|
|
||||||
local function center(y, text, foreground, background)
|
local function center(y, text, foreground, background)
|
||||||
local width = monitor.getSize()
|
local width = monitor.getSize()
|
||||||
@@ -34,28 +43,6 @@ local function center(y, text, foreground, background)
|
|||||||
monitor.write(text)
|
monitor.write(text)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function hasMethod(name, wantedMethod)
|
|
||||||
local methods = peripheral.getMethods(name) or {}
|
|
||||||
for _, method in ipairs(methods) do
|
|
||||||
if method == wantedMethod then return true end
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local function getPeripherals(wantedType, requiredMethod)
|
|
||||||
local names = {}
|
|
||||||
for _, name in ipairs(peripheral.getNames()) do
|
|
||||||
local peripheralType = peripheral.getType(name)
|
|
||||||
local typeMatches = type(peripheralType) == "string"
|
|
||||||
and peripheralType:lower() == wantedType:lower()
|
|
||||||
if typeMatches or (requiredMethod and hasMethod(name, requiredMethod)) then
|
|
||||||
names[#names + 1] = name
|
|
||||||
end
|
|
||||||
end
|
|
||||||
table.sort(names)
|
|
||||||
return names
|
|
||||||
end
|
|
||||||
|
|
||||||
local function getValueIgnoringCase(data, wantedKeys)
|
local function getValueIgnoringCase(data, wantedKeys)
|
||||||
if type(data) ~= "table" then return nil end
|
if type(data) ~= "table" then return nil end
|
||||||
for key, value in pairs(data) do
|
for key, value in pairs(data) do
|
||||||
@@ -67,79 +54,71 @@ local function getValueIgnoringCase(data, wantedKeys)
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local function findBoilerData(data, visited)
|
local function samePosition(a, b)
|
||||||
if type(data) ~= "table" then return nil end
|
if type(a) ~= "table" or type(b) ~= "table" then return true end
|
||||||
visited = visited or {}
|
return tonumber(a[1]) == tonumber(b[1])
|
||||||
if visited[data] then return nil end
|
and tonumber(a[2]) == tonumber(b[2])
|
||||||
visited[data] = true
|
and tonumber(a[3]) == tonumber(b[3])
|
||||||
|
end
|
||||||
|
|
||||||
|
local function discoverReaders()
|
||||||
|
local boilers, networks = {}, {}
|
||||||
|
local names = peripheral.getNames()
|
||||||
|
table.sort(names)
|
||||||
|
|
||||||
|
for _, name in ipairs(names) do
|
||||||
|
if peripheral.hasType(name, "block_reader") then
|
||||||
|
local ok, data = pcall(peripheral.call, name, "getBlockData")
|
||||||
|
if ok and type(data) == "table" then
|
||||||
local boiler = getValueIgnoringCase(data, { "Boiler" })
|
local boiler = getValueIgnoringCase(data, { "Boiler" })
|
||||||
if type(boiler) == "table" then return boiler end
|
local network = getValueIgnoringCase(data, { "Network" })
|
||||||
|
local speed = getValueIgnoringCase(data, { "Speed" })
|
||||||
|
|
||||||
local heat = getValueIgnoringCase(data, { "Heat", "activeHeat", "actualHeat" })
|
if type(boiler) == "table" then
|
||||||
local passive = getValueIgnoringCase(data, { "PassiveHeat", "passive" })
|
boilers[#boilers + 1] = { name = name, data = data, boiler = boiler }
|
||||||
if heat ~= nil or passive ~= nil then return data end
|
elseif type(network) == "table" and tonumber(speed) then
|
||||||
|
networks[#networks + 1] = { name = name, data = data, network = network }
|
||||||
for _, value in pairs(data) do
|
|
||||||
local found = findBoilerData(value, visited)
|
|
||||||
if found then return found end
|
|
||||||
end
|
end
|
||||||
return nil
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return boilers, networks
|
||||||
end
|
end
|
||||||
|
|
||||||
local function readBoiler(readerName)
|
local function readBoiler(entry)
|
||||||
if not readerName then return "MISSING", "UNKNOWN", colors.red end
|
if not entry then return "MISSING", "NO READER", colors.red end
|
||||||
local ok, data = pcall(peripheral.call, readerName, "getBlockData")
|
|
||||||
if not ok then return "ERROR", "UNKNOWN", colors.red end
|
|
||||||
|
|
||||||
local boiler = findBoilerData(data)
|
local controller = getValueIgnoringCase(entry.data, { "Controller" })
|
||||||
if not boiler then return "NO DATA", "UNKNOWN", colors.red end
|
local readerPos = getValueIgnoringCase(entry.data, { "LastKnownPos" })
|
||||||
|
if not samePosition(controller, readerPos) then
|
||||||
local passive = getValueIgnoringCase(boiler, { "PassiveHeat", "passive" })
|
return "READER", "WRONG TANK", colors.red
|
||||||
local heat = tonumber(getValueIgnoringCase(boiler,
|
|
||||||
{ "Heat", "activeHeat", "actualHeat" }))
|
|
||||||
local supply = tonumber(getValueIgnoringCase(boiler,
|
|
||||||
{ "Supply", "waterSupply", "gatheredSupply" }))
|
|
||||||
local engines = tonumber(getValueIgnoringCase(boiler,
|
|
||||||
{ "Engines", "attachedEngines" }))
|
|
||||||
local active = getValueIgnoringCase(boiler,
|
|
||||||
{ "Active", "isActive", "prevActive" })
|
|
||||||
|
|
||||||
local heatMode
|
|
||||||
if passive == true or passive == 1 then
|
|
||||||
heatMode = "CAMPFIRE"
|
|
||||||
elseif heat and heat >= 18 then
|
|
||||||
heatMode = "SUPERHEATED"
|
|
||||||
elseif heat and heat > 0 then
|
|
||||||
heatMode = "HEATED"
|
|
||||||
else
|
|
||||||
heatMode = "COLD"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if active == nil then
|
local boiler = entry.boiler
|
||||||
local hasHeat = heatMode ~= "COLD"
|
local passive = tonumber(getValueIgnoringCase(boiler, { "PassiveHeat", "passive" })) or 0
|
||||||
local hasWater = supply == nil or supply > 0
|
local heat = tonumber(getValueIgnoringCase(boiler, { "ActiveHeat", "Heat", "actualHeat" })) or 0
|
||||||
local hasEngines = engines == nil or engines > 0
|
local supply = tonumber(getValueIgnoringCase(boiler, { "Supply", "waterSupply" })) or 0
|
||||||
active = hasHeat and hasWater and hasEngines
|
local engines = tonumber(getValueIgnoringCase(boiler, { "Engines", "attachedEngines" })) or 0
|
||||||
|
|
||||||
|
if passive > 0 then return "ONLINE", "CAMPFIRE", colors.lime end
|
||||||
|
if heat >= 18 then return "ONLINE", "SUPERHEATED", colors.lime end
|
||||||
|
if heat > 0 then return "ONLINE", "HEATED", colors.lime end
|
||||||
|
if engines > 0 and supply > 0 then return "STANDBY", "NO HEAT", colors.orange end
|
||||||
|
return "STANDBY", "COLD", colors.orange
|
||||||
end
|
end
|
||||||
|
|
||||||
if active == true or active == 1 then
|
local function readMainShaft(entry)
|
||||||
return "ONLINE", heatMode, colors.lime
|
if not entry then return nil, nil, nil, nil end
|
||||||
end
|
local speed = math.abs(tonumber(getValueIgnoringCase(entry.data, { "Speed" })) or 0)
|
||||||
return "STANDBY", heatMode, colors.orange
|
local capacity = tonumber(getValueIgnoringCase(entry.network, { "Capacity" })) or 0
|
||||||
end
|
local usage = tonumber(getValueIgnoringCase(entry.network, { "Stress" })) or 0
|
||||||
|
local load = capacity > 0 and usage / capacity * 100 or 0
|
||||||
local function readMainSpeed(speedometerName)
|
return speed, capacity, usage, load
|
||||||
if not speedometerName then return nil end
|
|
||||||
local ok, speed = pcall(peripheral.call, speedometerName, "getSpeed")
|
|
||||||
if not ok or type(speed) ~= "number" then return nil end
|
|
||||||
return math.abs(speed)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function formatInteger(value)
|
local function formatInteger(value)
|
||||||
value = math.floor(math.abs(tonumber(value) or 0) + 0.5)
|
value = math.floor(math.abs(tonumber(value) or 0) + 0.5)
|
||||||
local digits = tostring(value)
|
local digits, parts = tostring(value), {}
|
||||||
local parts = {}
|
|
||||||
while #digits > 3 do
|
while #digits > 3 do
|
||||||
table.insert(parts, 1, digits:sub(-3))
|
table.insert(parts, 1, digits:sub(-3))
|
||||||
digits = digits:sub(1, -4)
|
digits = digits:sub(1, -4)
|
||||||
@@ -148,65 +127,33 @@ local function formatInteger(value)
|
|||||||
return table.concat(parts, ".")
|
return table.concat(parts, ".")
|
||||||
end
|
end
|
||||||
|
|
||||||
local function readMainShaft(stressometerName)
|
|
||||||
if not stressometerName then return nil, nil, nil end
|
|
||||||
local capacityMethod = hasMethod(stressometerName, "getStressCapacity")
|
|
||||||
and "getStressCapacity" or "getNetworkCapacity"
|
|
||||||
local usageMethod = hasMethod(stressometerName, "getStress")
|
|
||||||
and "getStress" or "getNetworkStress"
|
|
||||||
local okCapacity, capacity = pcall(peripheral.call, stressometerName, capacityMethod)
|
|
||||||
local okUsage, usage = pcall(peripheral.call, stressometerName, usageMethod)
|
|
||||||
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 function render()
|
||||||
local _, height = monitor.getSize()
|
local _, height = monitor.getSize()
|
||||||
local speedometers = getPeripherals(SPEEDOMETER_TYPE, "getSpeed")
|
local boilers, networks = discoverReaders()
|
||||||
local stressometers = getPeripherals(STRESSOMETER_TYPE, "getStress")
|
local speed, capacity, usage, load = readMainShaft(networks[1])
|
||||||
local readers = getPeripherals(BLOCK_READER_TYPE, "getBlockData")
|
|
||||||
|
|
||||||
monitor.setBackgroundColor(colors.black)
|
monitor.setBackgroundColor(colors.black)
|
||||||
monitor.clear()
|
monitor.clear()
|
||||||
center(1, "CREATE KRAFTWERK", colors.cyan)
|
center(1, "CREATE KRAFTWERK", colors.cyan)
|
||||||
local firstRow = 3
|
|
||||||
for index = 1, EXPECTED_BOILERS do
|
for index = 1, EXPECTED_BOILERS do
|
||||||
local state, heatMode, color = readBoiler(readers[index])
|
local state, heatMode, color = readBoiler(boilers[index])
|
||||||
local row = firstRow + index - 1
|
center(2 + index, ("BOILER %d %-7s %s"):format(index, state, heatMode), color)
|
||||||
if row <= height then
|
|
||||||
center(row, ("BOILER %d %-7s %s")
|
|
||||||
:format(index, state, heatMode), color)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local speed = readMainSpeed(speedometers[1])
|
center(7, "MAIN SHAFT", colors.cyan)
|
||||||
local capacity, usage, load = readMainShaft(stressometers[1])
|
|
||||||
if height >= 7 then center(7, "MAIN SHAFT", colors.cyan) end
|
|
||||||
if height >= 8 then
|
|
||||||
if speed then
|
if speed then
|
||||||
center(8, ("Speed: %.1f RPM"):format(speed), speed > 0 and colors.lime or colors.orange)
|
center(8, ("Speed: %.1f RPM"):format(speed), speed > 0 and colors.lime or colors.orange)
|
||||||
else
|
center(9, "Capacity: " .. formatInteger(capacity) .. " SU", colors.white)
|
||||||
center(8, "Speedometer nicht gefunden", colors.red)
|
center(10, "Usage: " .. formatInteger(usage) .. " SU", colors.white)
|
||||||
end
|
|
||||||
end
|
|
||||||
if capacity then
|
|
||||||
if height >= 9 then center(9, "Capacity: " .. formatInteger(capacity) .. " SU", colors.white) end
|
|
||||||
if height >= 10 then center(10, "Usage: " .. formatInteger(usage) .. " SU", colors.white) end
|
|
||||||
if height >= 11 then
|
|
||||||
local loadText = ("Load: %.1f %%"):format(load):gsub("%.", ",")
|
local loadText = ("Load: %.1f %%"):format(load):gsub("%.", ",")
|
||||||
center(11, loadText, load >= 90 and colors.red or load >= 70 and colors.orange or colors.lime)
|
center(11, loadText, load >= 90 and colors.red or load >= 70 and colors.orange or colors.lime)
|
||||||
end
|
|
||||||
else
|
else
|
||||||
if height >= 9 then center(9, "Stressometer nicht gefunden", colors.red) end
|
center(8, "Kein Gauge-Reader gefunden", colors.red)
|
||||||
end
|
center(9, "Reader auf Speed-/Stressometer", colors.orange)
|
||||||
|
|
||||||
if height >= 13 then
|
|
||||||
center(height, "Nur Anzeige - keine Steuerung", colors.gray)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if height >= 13 then center(height, "Nur Anzeige - keine Steuerung", colors.gray) end
|
||||||
monitor.setTextColor(colors.white)
|
monitor.setTextColor(colors.white)
|
||||||
monitor.setBackgroundColor(colors.black)
|
monitor.setBackgroundColor(colors.black)
|
||||||
term.setCursorPos(1, 1)
|
term.setCursorPos(1, 1)
|
||||||
|
|||||||
Reference in New Issue
Block a user