Add read-only ME pattern duplicate audit
This commit is contained in:
@@ -89,6 +89,18 @@ Peripheral-Name gezielt uebergeben werden:
|
||||
greenhouse_inventory_debug <peripheral_name>
|
||||
```
|
||||
|
||||
## 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:
|
||||
|
||||
@@ -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 "<cycle>" end
|
||||
if depth >= 12 then return "<max-depth>" 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 "<cycle>" 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 "<not set>"))
|
||||
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.")
|
||||
Reference in New Issue
Block a user