diff --git a/README.md b/README.md index 82709f9..d8bc02f 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,18 @@ Peripheral-Name gezielt uebergeben werden: greenhouse_inventory_debug ``` +## AE2-Pattern und Duplikate erfassen + +Der rein lesende ME-Pattern-Auditor nutzt die Advanced-Peripherals-ME-Bridge und +schreibt alle vom Netzwerk angebotenen Pattern, Craftables und moegliche Duplikate +in `me_pattern_debug.txt`. Er startet keine Crafts und veraendert keine Pattern. + +```lua +delete me_pattern_debug.lua +wget https://git.peli-server.de/Dystroyer8/ATM10_CC_Codes/raw/branch/main/me_pattern_debug.lua me_pattern_debug.lua +me_pattern_debug +``` + ### Erster Logistik-Funktionstest Nach erfolgreicher Erkennung kann der Monitor-Testcontroller installiert werden: diff --git a/me_pattern_debug.lua b/me_pattern_debug.lua new file mode 100644 index 0000000..db61690 --- /dev/null +++ b/me_pattern_debug.lua @@ -0,0 +1,170 @@ +-- ATM10 AE2 pattern audit via Advanced Peripherals ME Bridge +-- Read-only: does not craft, export, import, cancel or modify patterns. + +local OUTPUT_FILE = "me_pattern_debug.txt" +local bridgeName + +local function hasType(name, wanted) + if peripheral.hasType then return peripheral.hasType(name, wanted) end + return peripheral.getType(name) == wanted +end + +for _, name in ipairs(peripheral.getNames()) do + if hasType(name, "me_bridge") then bridgeName = name break end +end +if not bridgeName then error("Keine ME Bridge am Computer gefunden", 0) end + +local function sortedKeys(value) + local keys = {} + for key in pairs(value or {}) do keys[#keys + 1] = key end + table.sort(keys, function(a, b) return tostring(a) < tostring(b) end) + return keys +end + +local function serialize(value, depth, seen) + depth, seen = depth or 0, seen or {} + local kind = type(value) + if kind == "string" then return string.format("%q", value) end + if kind ~= "table" then return tostring(value) end + if seen[value] then return "" end + if depth >= 12 then return "" end + seen[value] = true + local parts = { "{" } + for _, key in ipairs(sortedKeys(value)) do + parts[#parts + 1] = string.rep(" ", depth + 1) + .. "[" .. serialize(key, depth + 1, seen) .. "] = " + .. serialize(value[key], depth + 1, seen) .. "," + end + parts[#parts + 1] = string.rep(" ", depth) .. "}" + seen[value] = nil + return table.concat(parts, "\n") +end + +local function compact(value, seen) + local kind = type(value) + if kind == "string" then return string.format("%q", value) end + if kind ~= "table" then return tostring(value) end + seen = seen or {} + if seen[value] then return "" end + seen[value] = true + local parts = {} + for _, key in ipairs(sortedKeys(value)) do + parts[#parts + 1] = "[" .. compact(key, seen) .. "]=" .. compact(value[key], seen) + end + seen[value] = nil + return "{" .. table.concat(parts, ",") .. "}" +end + +local function callRead(method, ...) + local result = table.pack(pcall(peripheral.call, bridgeName, method, ...)) + if not result[1] then return false, tostring(result[2]) end + if result.n == 2 then return true, result[2] end + local values = { n = result.n - 1 } + for index = 2, result.n do values[index - 1] = result[index] end + return true, values +end + +local function listValues(value) + local result = {} + if type(value) ~= "table" then return result end + for key, entry in pairs(value) do + if key ~= "n" and type(entry) == "table" then result[#result + 1] = entry end + end + return result +end + +local function recipeSide(pattern, names) + for _, name in ipairs(names) do + if pattern[name] ~= nil then return pattern[name] end + end +end + +local function fingerprint(pattern) + local inputs = recipeSide(pattern, { "inputs", "input", "ingredients" }) + local outputs = recipeSide(pattern, { "outputs", "output", "results", "result" }) + if inputs ~= nil or outputs ~= nil then + return "IN=" .. compact(inputs) .. "|OUT=" .. compact(outputs) + end + return compact(pattern) +end + +local lines = {} +local function emit(value) lines[#lines + 1] = tostring(value) end + +emit("ATM10 AE2 PATTERN DEBUG") +emit("Computer ID: " .. tostring(os.getComputerID())) +emit("Computer label: " .. tostring(os.getComputerLabel() or "")) +emit("ME Bridge: " .. bridgeName) +local okOnline, online = callRead("isConnected") +emit("Connected: " .. (okOnline and tostring(online) or ("ERROR: " .. tostring(online)))) + +local okPatterns, rawPatterns = callRead("getPatterns") +if not okPatterns then okPatterns, rawPatterns = callRead("getPatterns", {}) end +if not okPatterns then error("getPatterns fehlgeschlagen: " .. tostring(rawPatterns), 0) end + +local patterns = listValues(rawPatterns) +emit("Pattern records: " .. tostring(#patterns)) +local groups = {} +for index, pattern in ipairs(patterns) do + local key = fingerprint(pattern) + groups[key] = groups[key] or {} + groups[key][#groups[key] + 1] = index +end +local duplicates = {} +for key, indexes in pairs(groups) do + if #indexes > 1 then duplicates[#duplicates + 1] = { key = key, indexes = indexes } end +end +table.sort(duplicates, function(a, b) + if #a.indexes == #b.indexes then return a.key < b.key end + return #a.indexes > #b.indexes +end) + +emit("Duplicate candidate groups: " .. tostring(#duplicates)) +emit("") +emit("DUPLICATE CANDIDATES") +emit(string.rep("=", 72)) +if #duplicates == 0 then + emit("No duplicates detected from the exposed pattern schema.") +else + for groupIndex, group in ipairs(duplicates) do + emit("") + emit("GROUP " .. groupIndex .. " | copies=" .. #group.indexes + .. " | pattern indexes=" .. table.concat(group.indexes, ", ")) + emit(group.key) + end +end + +emit("") +emit("RAW GETPATTERNS RESULT") +emit(string.rep("=", 72)) +emit(serialize(rawPatterns)) +local okCraftable, craftable = callRead("getCraftableItems") +emit("") +emit("RAW GETCRAFTABLEITEMS RESULT") +emit(string.rep("=", 72)) +emit(okCraftable and serialize(craftable) or ("ERROR: " .. tostring(craftable))) + +emit("") +emit("CONNECTED PERIPHERALS") +emit(string.rep("=", 72)) +local peripheralNames = peripheral.getNames() +table.sort(peripheralNames) +for _, name in ipairs(peripheralNames) do + local types = table.pack(peripheral.getType(name)) + local typeNames = {} + for index = 1, types.n do + if types[index] ~= nil then typeNames[#typeNames + 1] = tostring(types[index]) end + end + emit(name .. " | " .. table.concat(typeNames, ", ")) +end + +local file = assert(fs.open(OUTPUT_FILE, "w")) +file.write(table.concat(lines, "\n")) +file.write("\n") +file.close() + +print("ME Pattern Audit abgeschlossen.") +print("Pattern-Datensaetze: " .. tostring(#patterns)) +print("Duplikat-Gruppen: " .. tostring(#duplicates)) +print("Erstellt: " .. OUTPUT_FILE) +print("Bitte danach Codex 'fertig' schreiben.")