diff --git a/README.md b/README.md index f104508..67bdfcd 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,20 @@ stehen alle verbundenen Peripherien, Methoden, Frogport-Adressen sowie abrufbare Paket-, Inventar- und Stock-Ticker-Daten. Das Programm versendet keine Pakete und veraendert keine Redstone-Ausgaenge. +### Erster Logistik-Funktionstest + +Nach erfolgreicher Erkennung kann der Monitor-Testcontroller installiert werden: + +```lua +delete create_factory_test.lua +wget https://git.peli-server.de/Dystroyer8/ATM10_CC_Codes/raw/branch/main/create_factory_test.lua create_factory_test.lua +create_factory_test +``` + +Der Controller zeigt den Live-Bestand des Stock-Ticker-Netzes und bietet doppelt +zu bestaetigende Testbestellungen fuer `crushing`, `saw:1` und `saw:2`. Er steuert +noch keine Clutches oder sonstigen Redstone-Ausgaenge. + ## Zielbild - zentrale Leitwarte fuer Status, Energie, Alarme und Sektor-Hauptschalter diff --git a/create_factory_test.lua b/create_factory_test.lua new file mode 100644 index 0000000..7d04bad --- /dev/null +++ b/create_factory_test.lua @@ -0,0 +1,309 @@ +-- ATM10 Create factory logistics test controller +-- Phase 1: Stock Ticker requests and live stock display, no redstone control. + +local REFRESH_SECONDS = 1.0 +local CONFIRM_SECONDS = 6.0 +local LOG_FILE = "factory_test_log.txt" + +local trackedItems = { + { id = "create:asurine", label = "Asurine" }, + { id = "minecraft:andesite", label = "Andesite" }, + { id = "create:andesite_alloy", label = "Andesite Alloy" }, + { id = "minecraft:oak_log", label = "Oak Logs" }, + { id = "minecraft:stripped_oak_log", label = "Stripped Oak" }, + { id = "minecraft:oak_planks", label = "Oak Planks" }, + { id = "alltheores:zinc_clump", label = "Zinc Clumps" }, + { id = "alltheores:zinc_nugget", label = "Zinc Nuggets" }, +} + +local actions = { + { + id = "crush_asurine", + label = "64 ASURINE -> crushing", + address = "crushing", + item = "create:asurine", + count = 64, + }, + { + id = "strip_oak", + label = "64 OAK LOGS -> saw:2", + address = "saw:2", + item = "minecraft:oak_log", + count = 64, + }, + { + id = "planks_oak", + label = "64 STRIPPED -> saw:2", + address = "saw:2", + item = "minecraft:stripped_oak_log", + count = 64, + }, + { + id = "shafts", + label = "64 ALLOY -> saw:1", + address = "saw:1", + item = "create:andesite_alloy", + count = 64, + }, +} + +local function findPeripheral(wantedType) + for _, name in ipairs(peripheral.getNames()) do + local peripheralType = peripheral.getType(name) + if peripheralType == wantedType then + return name + end + end + return nil +end + +local tickerName = findPeripheral("Create_StockTicker") +local monitorName = findPeripheral("monitor") + +if not tickerName then + error("Kein Create Stock Ticker am Wired-Modem-Netz gefunden", 0) +end +if not monitorName then + error("Kein Monitor am Wired-Modem-Netz gefunden", 0) +end + +local monitor = peripheral.wrap(monitorName) +monitor.setTextScale(0.5) + +local stock = {} +local buttons = {} +local messages = {} +local armedAction = nil +local armedUntil = 0 +local statusText = "Bereit - nur Testbetrieb, keine Redstone-Steuerung" +local statusColor = colors.lime + +local function formatNumber(value) + local text = tostring(math.floor(tonumber(value) or 0)) + local sign, digits = text:match("^([%-]?)(%d+)$") + if not digits then + return text + end + local formatted = digits:reverse():gsub("(%d%d%d)", "%1."):reverse():gsub("^%.", "") + return sign .. formatted +end + +local function timeText() + return textutils.formatTime(os.time(), true) +end + +local function appendLog(message) + local line = "[" .. timeText() .. "] " .. message + messages[#messages + 1] = line + while #messages > 4 do + table.remove(messages, 1) + end + + local file = fs.open(LOG_FILE, "a") + if file then + file.writeLine(line) + file.close() + end +end + +local function refreshStock() + local ok, result = pcall(peripheral.call, tickerName, "stock") + if not ok then + statusText = "Stock-Ticker-Fehler: " .. tostring(result) + statusColor = colors.red + return false + end + + local nextStock = {} + for _, entry in pairs(result or {}) do + if type(entry) == "table" and entry.name then + nextStock[entry.name] = (nextStock[entry.name] or 0) + (tonumber(entry.count) or 0) + end + end + stock = nextStock + return true +end + +local function writeAt(x, y, text, foreground, background) + local width, height = monitor.getSize() + if y < 1 or y > height or x > width then + return + end + monitor.setCursorPos(math.max(1, x), y) + monitor.setTextColor(foreground or colors.white) + monitor.setBackgroundColor(background or colors.black) + monitor.write(tostring(text):sub(1, math.max(0, width - x + 1))) +end + +local function center(y, text, foreground, background) + local width = monitor.getSize() + local x = math.max(1, math.floor((width - #text) / 2) + 1) + writeAt(x, y, text, foreground, background) +end + +local function drawButton(action, x1, y1, x2, y2) + local isArmed = armedAction == action.id and os.clock() <= armedUntil + local available = stock[action.item] or 0 + local enabled = available > 0 + local background = colors.gray + local foreground = colors.lightGray + + if enabled then + background = isArmed and colors.orange or colors.green + foreground = isArmed and colors.black or colors.white + end + + for y = y1, y2 do + writeAt(x1, y, string.rep(" ", x2 - x1 + 1), foreground, background) + end + + local label = action.label + if isArmed then + label = "NOCHMALS: " .. label + elseif not enabled then + label = "KEIN BESTAND: " .. label + end + + local labelX = math.max(x1, math.floor((x1 + x2 - #label) / 2)) + writeAt(labelX, math.floor((y1 + y2) / 2), label, foreground, background) + + buttons[#buttons + 1] = { + action = action, + x1 = x1, + y1 = y1, + x2 = x2, + y2 = y2, + enabled = enabled, + } +end + +local function render() + local width, height = monitor.getSize() + monitor.setBackgroundColor(colors.black) + monitor.setTextColor(colors.white) + monitor.clear() + buttons = {} + + center(1, "CREATE FACTORY - LOGISTIKTEST", colors.cyan) + center(2, "Ticker: " .. tickerName .. " | Monitor: " .. monitorName, colors.gray) + + local columnWidth = math.floor((width - 6) / 2) + local leftX = 3 + local rightX = leftX + columnWidth + 2 + local firstStockRow = 4 + + for index, item in ipairs(trackedItems) do + local column = (index - 1) % 2 + local row = firstStockRow + math.floor((index - 1) / 2) + local x = column == 0 and leftX or rightX + local amount = formatNumber(stock[item.id] or 0) + local text = item.label .. ": " .. amount + writeAt(x, row, text, (stock[item.id] or 0) > 0 and colors.lime or colors.gray) + end + + local statusRow = firstStockRow + math.ceil(#trackedItems / 2) + 1 + center(statusRow, statusText, statusColor) + + local buttonTop = statusRow + 2 + local gap = 2 + local buttonWidth = math.floor((width - 6 - gap) / 2) + local xLeft = 3 + local xRight = xLeft + buttonWidth + gap + local buttonHeight = 2 + + drawButton(actions[1], xLeft, buttonTop, xLeft + buttonWidth - 1, buttonTop + buttonHeight - 1) + drawButton(actions[2], xRight, buttonTop, xRight + buttonWidth - 1, buttonTop + buttonHeight - 1) + drawButton(actions[3], xLeft, buttonTop + 3, xLeft + buttonWidth - 1, buttonTop + 4) + drawButton(actions[4], xRight, buttonTop + 3, xRight + buttonWidth - 1, buttonTop + 4) + + local logTitleRow = math.min(height - 4, buttonTop + 6) + writeAt(2, logTitleRow, "Letzte Ereignisse:", colors.yellow) + for index, message in ipairs(messages) do + writeAt(2, logTitleRow + index, message, colors.lightGray) + end + + writeAt(2, height, "Jede Bestellung muss innerhalb 6 Sekunden zweimal beruehrt werden.", colors.gray) +end + +local function submit(action) + local filter = { + name = action.item, + _requestCount = action.count, + } + + local ok, sent = pcall( + peripheral.call, + tickerName, + "requestFiltered", + action.address, + filter + ) + + if not ok then + statusText = "BESTELLFEHLER: " .. tostring(sent) + statusColor = colors.red + appendLog(statusText) + return + end + + local amount = tonumber(sent) or 0 + statusText = formatNumber(amount) .. "x " .. action.item .. " -> " .. action.address + statusColor = amount > 0 and colors.lime or colors.orange + appendLog(statusText) + refreshStock() +end + +local function handleTouch(x, y) + for _, button in ipairs(buttons) do + if x >= button.x1 and x <= button.x2 and y >= button.y1 and y <= button.y2 then + if not button.enabled then + statusText = "Kein passender Rohstoff im Stock-Ticker-Netz" + statusColor = colors.orange + appendLog(statusText) + return + end + + local now = os.clock() + if armedAction == button.action.id and now <= armedUntil then + local selected = button.action + armedAction = nil + armedUntil = 0 + submit(selected) + else + armedAction = button.action.id + armedUntil = now + CONFIRM_SECONDS + statusText = "Zur Bestaetigung denselben Knopf erneut beruehren" + statusColor = colors.orange + end + return + end + end +end + +appendLog("Testcontroller gestartet auf Computer " .. tostring(os.getComputerID())) +refreshStock() +render() + +local refreshTimer = os.startTimer(REFRESH_SECONDS) + +while true do + local event, parameter1, parameter2, parameter3 = os.pullEvent() + + if event == "monitor_touch" and parameter1 == monitorName then + handleTouch(parameter2, parameter3) + render() + elseif event == "timer" and parameter1 == refreshTimer then + if armedAction and os.clock() > armedUntil then + armedAction = nil + armedUntil = 0 + statusText = "Bestaetigung abgelaufen" + statusColor = colors.gray + end + refreshStock() + render() + refreshTimer = os.startTimer(REFRESH_SECONDS) + elseif event == "peripheral_detach" or event == "peripheral" then + refreshStock() + render() + end +end