-- Create boiler status board for CC:Tweaked. -- All Create values are read through Advanced Peripherals Block Readers. local EXPECTED_BOILERS = 3 local REFRESH_SECONDS = 1 local MIN_WIDTH = 38 local MIN_HEIGHT = 11 local monitor = peripheral.find("monitor") if not monitor then error("Kein Monitor gefunden. Monitor direkt oder per Wired Modem verbinden.", 0) end 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) end fitMonitor() 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 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 samePosition(a, b) if type(a) ~= "table" or type(b) ~= "table" then return true end return tonumber(a[1]) == tonumber(b[1]) and tonumber(a[2]) == tonumber(b[2]) 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 network = getValueIgnoringCase(data, { "Network" }) local speed = getValueIgnoringCase(data, { "Speed" }) if type(boiler) == "table" then boilers[#boilers + 1] = { name = name, data = data, boiler = boiler } elseif type(network) == "table" and tonumber(speed) then networks[#networks + 1] = { name = name, data = data, network = network } end end end end return boilers, networks end local function readBoiler(entry) if not entry then return "MISSING", "NO READER", colors.red end local controller = getValueIgnoringCase(entry.data, { "Controller" }) local readerPos = getValueIgnoringCase(entry.data, { "LastKnownPos" }) if not samePosition(controller, readerPos) then return "READER", "WRONG TANK", colors.red end local boiler = entry.boiler local passive = tonumber(getValueIgnoringCase(boiler, { "PassiveHeat", "passive" })) or 0 local heat = tonumber(getValueIgnoringCase(boiler, { "ActiveHeat", "Heat", "actualHeat" })) or 0 local supply = tonumber(getValueIgnoringCase(boiler, { "Supply", "waterSupply" })) or 0 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 local function readMainShaft(entry) if not entry then return nil, nil, nil, nil end local speed = math.abs(tonumber(getValueIgnoringCase(entry.data, { "Speed" })) or 0) local capacity = tonumber(getValueIgnoringCase(entry.network, { "Capacity" })) or 0 local usage = tonumber(getValueIgnoringCase(entry.network, { "Stress" })) or 0 local load = capacity > 0 and usage / capacity * 100 or 0 return speed, capacity, usage, load end local function formatInteger(value) value = math.floor(math.abs(tonumber(value) or 0) + 0.5) local digits, parts = tostring(value), {} 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 render() local _, height = monitor.getSize() local boilers, networks = discoverReaders() local speed, capacity, usage, load = readMainShaft(networks[1]) monitor.setBackgroundColor(colors.black) monitor.clear() center(1, "CREATE KRAFTWERK", colors.cyan) for index = 1, EXPECTED_BOILERS do local state, heatMode, color = readBoiler(boilers[index]) center(2 + index, ("BOILER %d %-7s %s"):format(index, state, heatMode), color) end center(7, "MAIN SHAFT", colors.cyan) if speed then center(8, ("Speed: %.1f RPM"):format(speed), speed > 0 and colors.lime or colors.orange) center(9, "Capacity: " .. formatInteger(capacity) .. " SU", colors.white) center(10, "Usage: " .. formatInteger(usage) .. " SU", colors.white) local loadText = ("Load: %.1f %%"):format(load):gsub("%.", ",") center(11, loadText, load >= 90 and colors.red or load >= 70 and colors.orange or colors.lime) else center(8, "Kein Gauge-Reader gefunden", colors.red) center(9, "Reader auf Speed-/Stressometer", colors.orange) end if height >= 13 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