Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local sides = require("sides")
- local component = require("component")
- local computer = require("computer")
- local uptime = computer.uptime
- local beep = computer.beep
- local lib = {}
- ---@class autocompressor.inventory
- ---@field unit autocompressor.crafting_unit
- ---@field side number
- local inventory = {}
- ---@param unit autocompressor.crafting_unit
- ---@param side number
- ---@return component.inventory
- function inventory:new(unit, side)
- local instance = {unit = unit, side = side}
- setmetatable(instance, {__index = inventory})
- return instance
- end
- ---@return component.transposer
- function inventory:getTransposer()
- return self.unit:getTransposer()
- end
- ---@return number
- function inventory:getInventorySize()
- return self.unit:getInventorySize(self.side)
- end
- ---@param slot number
- ---@return _item_stack
- function inventory:getStackInSlot(slot)
- if slot == -1 then return nil end
- return self:getTransposer().getStackInSlot(self.side, slot)
- end
- ---@param slots number[]
- ---@return table<number, _item_stack>
- function inventory:getStacksFromSlots(slots)
- local stacks = {}
- local i = 0
- for _, slot in ipairs(slots) do
- i = i + 1
- if slot ~= -1 then
- local stack = self:getStackInSlot(slot)
- if stack ~= nil then
- stacks[i] = stack
- end
- end
- end
- return stacks
- end
- ---@class autocompressor.compressor : autocompressor.inventory
- ---@field status string
- ---@field producing autocompressor.grid
- local compressor = {status = "init"}
- setmetatable(compressor, {__index = inventory})
- ---@param unit autocompressor.crafting_unit
- ---@param side number
- ---@return autocompressor.compressor
- function compressor:new(unit, side)
- local instance = inventory:new(unit, side)
- setmetatable(instance, {__index = compressor})
- return instance
- end
- function compressor:isCircuit()
- return self:getInventorySize() == 7
- end
- ---@return boolean
- function compressor:isElectric()
- return self:getInventorySize() == 12
- end
- ---@return number[]
- function compressor:getCoalSlots()
- if self:isElectric() or self:isCircuit() then
- return {}
- else
- return {1}
- end
- end
- ---@return number[]
- function compressor:getBatterySlots()
- if self:isElectric() or self:isCircuit() then
- return {1}
- else
- return {}
- end
- end
- ---@return number[]
- function compressor:getOutputSlots()
- if self:isElectric() then
- return {2, 3}
- elseif self:isCircuit() then
- return {7}
- else
- return {2}
- end
- end
- ---@return number[]
- function compressor:getMatrixSlots()
- if self:isElectric() then
- return {
- 4, 5, 6,
- 7, 8, 9,
- 10, 11, 12
- }
- elseif self:isCircuit() then
- return {
- 2, -1, 6,
- 3, 5, -1,
- 4, -1, -1
- }
- else
- return {
- 3, 4, 5,
- 6, 7, 8,
- 9, 10, 11
- }
- end
- end
- ---@return table<number, _item_stack>
- function compressor:getCoalStacks()
- return self:getStacksFromSlots(self:getCoalSlots())
- end
- ---@return table<number, _item_stack>
- function compressor:getOutputStacks()
- return self:getStacksFromSlots(self:getOutputSlots())
- end
- ---@return table<number, _item_stack>
- function compressor:getMatrixStacks()
- return self:getStacksFromSlots(self:getMatrixSlots())
- end
- ---@class autocompressor.chest : autocompressor.inventory
- ---@field grids autocompressor.grid[]
- local chest = {}
- setmetatable(chest, {__index = inventory})
- ---@param unit autocompressor.crafting_unit
- ---@param side number
- ---@return autocompressor.chest
- function chest:new(unit, side)
- ---@type autocompressor.chest
- local instance = inventory:new(unit, side)
- setmetatable(instance, {__index = chest})
- instance.grids = {}
- --instance.delayed = {}
- return instance
- end
- ---@return number[]
- function chest:getGridSlots()
- if self:getInventorySize() >= 27 then
- return {
- 7, 8, 9,
- 16, 17, 18,
- 25, 26, 27
- }
- --[[
- return {
- 25, 16, 7,
- 26, 17, 8,
- 27, 18, 9
- }
- ]]--
- else
- return {}
- end
- end
- ---@return table<number, _item_stack>
- function chest:getGridStacks()
- return self:getStacksFromSlots(self:getGridSlots())
- end
- ---@return number[]
- function chest:getInputSlots()
- local slots = {}
- for i = 1, self:getInventorySize() do
- table.insert(slots, i)
- end
- return slots
- end
- ---@return table<number, _item_stack>
- function chest:getInputStacks()
- return self:getStacksFromSlots(self:getInputSlots())
- end
- ---@class autocompressor.grid.item
- ---@field name string
- ---@field amount number
- local grid_item = {}
- ---@param name string
- ---@param amount number
- ---@return autocompressor.grid.item
- function grid_item:new(name, amount)
- assert(name ~= nil, "Name can't be null")
- assert(amount ~= nil, "Amount can't be null")
- local instance = {name = name, amount = amount}
- setmetatable(instance, {__index = grid_item})
- return instance
- end
- ---@class autocompressor.grid.stack : autocompressor.grid.item
- ---@field id string
- ---@field metadata number
- local grid_stack = {}
- setmetatable(grid_stack, {__index = grid_item})
- function grid_item:isStackValid(stack) return false end
- ---@param name string
- ---@param amount number
- ---@param id string
- ---@param metadata number
- ---@return autocompressor.grid.stack
- function grid_stack:new(name, amount, id, metadata)
- ---@type autocompressor.grid.stack
- local instance = grid_item:new(name, amount)
- setmetatable(instance, {__index = grid_stack})
- instance.id = id
- instance.metadata = metadata
- return instance
- end
- ---@param stack _item_stack
- ---@return boolean
- function grid_stack:isStackValid(stack)
- return stack.name == self.id and stack.damage == self.metadata
- end
- ---@class autocompressor.grid
- ---@field matrix table<number, autocompressor.grid.stack>
- ---@field product string
- ---@field amount number
- local grid = {}
- ---@param matrix table<number, autocompressor.grid.item>
- ---@param product string
- ---@param amount number
- ---@return autocompressor.grid
- function grid:new(matrix, product, amount)
- checkArg(3, amount, "number")
- local instance = {matrix = matrix, product=product, amount=amount}
- setmetatable(instance, {__index = grid})
- return instance
- end
- ---@param slot number
- ---@param stack _item_stack
- ---@return boolean
- function grid:isStackValid(slot, stack)
- local item = self.matrix[slot]
- if item == nil and stack == nil then
- return true
- elseif item == nil or stack == nil then
- --print("nil!", lib.serialize(item), lib.serialize(stack))
- return false
- else
- local result = item:isStackValid(stack)
- if not result then
- --print("not!", lib.serialize(item), lib.serialize(stack))
- end
- return result
- end
- end
- ---@return table<string, table<number, number>>
- function grid:requiredMaterials()
- local requirements = {}
- for _, item in pairs(self.matrix) do
- if item ~= nil then
- local itemTable = requirements[item.id] or {}
- requirements[item.id] = itemTable
- local amount = itemTable[item.metadata] or 0
- amount = amount + item.amount
- itemTable[item.metadata] = amount
- end
- end
- return requirements
- end
- ---@param inventoryContents table<number, _item_stack>
- ---@param chest autocompressor.chest
- ---@return table<string, table<number, table<number, number> > >
- function grid:findResources(inventoryContents, chest)
- local requirements = self:requiredMaterials()
- local resources = {}
- --[[
- local save = io.open("requirements.lua", "w")
- save:write(require("serialization").serialize(requirements))
- save:close()
- ]]--
- for slot, stack in pairs(inventoryContents) do
- if stack ~= nil and not stack.hasTag and stack.size > 0 then
- --[[
- local delays = chest.delayed[slot]
- local reserved = 0
- if delays ~= nil then
- for i, delay in ipairs(delays) do
- reserved = reserved + delay.amount
- end
- end
- ]]--
- local itemTable = requirements[stack.name]
- if itemTable ~= nil --[[ and stack.size - reserved > 0 ]] then
- local need = itemTable[stack.damage] or 0
- if need > 0 then
- local take = stack.size --[[ - reserved ]]--
- if take > need then
- take = need
- end
- need = need - take
- if not stack.originalSize then
- stack.originalSize = stack.size
- end
- stack.size = stack.size - take
- local foundItemTable = resources[stack.name] or {}
- resources[stack.name] = foundItemTable
- local damageTable = foundItemTable[stack.damage] or {}
- foundItemTable[stack.damage] = damageTable
- damageTable[slot] = take
- if need > 0 then
- itemTable[stack.damage] = need
- else
- itemTable[stack.damage] = nil
- if next(itemTable) == nil then
- requirements[stack.name] = nil
- if next(requirements) == nil then
- return resources
- end
- end
- end
- end
- end
- end
- end
- for k, stack in pairs(inventoryContents) do
- if stack.originalSize then
- stack.size = stack.originalSize
- stack.originalSize = nil
- end
- end
- return nil
- end
- ---@param chest autocompressor.chest
- ---@param compressor autocompressor.compressor
- ---@param optional stacks table<number, _item_stack>
- function grid:moveResources(chest, compressor, stacks)
- if chest.unit ~= compressor.unit then
- error("Chest and compressor are from different unities")
- end
- stacks = stacks or chest:getInputStacks()
- --[[
- local save = io.open("debug.lua", "w")
- save:write(require("serialization").serialize(stacks))
- save:close()
- ]]--
- local resources = self:findResources(stacks, chest)
- if resources == nil then
- return false
- end
- --[[
- save = io.open("resources.lua", "w")
- save:write(require("serialization").serialize(resources))
- save:close()
- ]]
- local compressorStacks = compressor:getMatrixStacks()
- if next(compressorStacks) ~= nil then
- local expectedAmount = select(2, next(compressorStacks)).size
- for slot, compressorStack in pairs(compressorStacks) do
- if compressorStack.size ~= expectedAmount then
- --beep(2000)
- return false
- elseif not self:isStackValid(slot, compressorStack) then
- --beep()
- return false
- elseif compressorStack ~= nil and compressorStack.size + self.matrix[slot].amount > compressorStack.maxSize then
- --beep(20)
- return false
- end
- end
- end
- local transposer = chest:getTransposer()
- local compressorSlots = compressor:getMatrixSlots()
- local chestSlots = chest:getInputSlots()
- local failed = false
- for matrixIndex, item in pairs(self.matrix) do
- if item ~= nil and compressorSlots[matrixIndex] ~= -1 then
- if item.id == nil then error("item.id = nil!")
- elseif item.metadata == nil then error("item.metadata = nil!")
- elseif resources == nil then error("resources = nil!")
- elseif resources[item.id] == nil then error("resources[item.id] = nil! item.id="..item.id)
- elseif resources[item.id][item.metadata] == nil then error("resources[item.id][item.metadata] = nil! item.id="..item.id.." item.metadata="..item.metadata)
- end
- local resouceMap = resources[item.id][item.metadata]
- local need = item.amount
- for chestIndex, available in pairs(resouceMap) do
- if available > 0 then
- local take = available
- if take > need then
- take = need
- end
- if not transposer.transferItem(chest.side, compressor.side, take, chestSlots[chestIndex], compressorSlots[matrixIndex]) then
- failed = true
- --[[
- local delayed = chest.delayed[chestSlots[chestIndex] ]
- if not delayed then
- delayed = {}
- chest.delayed[chestSlots[chestIndex] ] = delayed
- end
- table.insert(delayed, delayMove(chestSlots[chestIndex], take, compressor.side, compressorSlots[matrixIndex]))
- ]]--
- end
- --print("Moved "..take.." "..item.name.." from "..chestSlots[chestIndex].." to "..compressorSlots[matrixIndex])
- need = need - take
- resouceMap[chestIndex] = available - take
- if need <= 0 then
- break
- end
- end
- end
- end
- end
- if failed then
- for i, slot in ipairs(compressor:getMatrixSlots()) do
- if slot ~= -1 then
- transposer.transferItem(compressor.side, chest.side, 64, slot)
- end
- end
- return false
- end
- return true
- end
- ---@param stacks table<number, _item_stack>
- ---@param product string
- ---@param amount number
- ---@return boolean
- function chest:createGrid(stacks, product, amount)
- if next(stacks) == nil then
- return false
- end
- local pattern = {}
- for slot, stack in pairs(stacks) do
- if stack ~= nil then
- pattern[slot] = grid_stack:new(stack.label, stack.size, stack.name, stack.damage)
- end
- end
- table.insert(self.grids, grid:new(pattern, product, amount))
- return true
- end
- --[[
- function chest:processDelayedMoves()
- for slot, k in pairs(self.delayed) do
- for i, delayed in ipairs(k) do
- if self:getTransposer().transferItem(self.side, delayed.toSide, delayed.amount, delayed.slot, delayed.toSlot) then
- table.remove(k, i)
- end
- end
- if not next(k) then
- self.delayed[slot] = nil
- end
- end
- end
- ]]--
- function chest:scanPatterns()
- local compressors = self.unit:getCompressors()
- local stacks = lib.checkTime("getInputStacks", self.getInputStacks, self)
- for k, g in pairs(self.grids) do
- for k, c in pairs(compressors) do
- if lib.checkTime("moveResources", g.moveResources, g, self, c, stacks) then
- c.producing = g
- --stacks = lib.checkTime("getInputStacks", self.getInputStacks, self)
- end
- end
- end
- end
- function compressor:updateStatus()
- if self.producing == nil then
- self.status = "waiting"
- else
- local slot, item = next(self.producing.matrix)
- if slot == nil or item == nil then
- self.status = "error1"
- return
- end
- local stack = self:getStackInSlot(self:getMatrixSlots()[slot])
- if stack == nil then
- if next(self:getMatrixStacks()) then
- self.status = "error2"
- else
- self.producing = nil
- self.status = "waiting"
- end
- else
- local unit = self.producing.amount / item.amount
- self.status = (stack.size * unit).."x"..self.producing.product
- end
- end
- end
- ---@class autocompressor.crafting_unit
- ---@field status string
- ---@field name string
- ---@field address string
- ---@field private compressors table<number, autocompressor.compressor>
- ---@field private inputs table<number, autocompressor.chest>
- local unit = {status="init"}
- ---@return component.transposer
- function unit:getTransposer()
- local comp, err = component.get(self.address, "transposer")
- if comp == nil then
- error(err)
- else
- return component.proxy(comp)
- end
- end
- ---@return number[]
- function unit:getInputSides()
- local inputs = {}
- for _, sideName in ipairs(sides) do
- local side = sides[sideName]
- local size = self:getInventorySize(side)
- if(size ~= nil and size > 0 and size ~= 12 and size ~= 11 and size ~= 7) then
- table.insert(inputs, side)
- end
- end
- return inputs
- end
- ---@return number[]
- function unit:getOutputSides()
- local outputs = {}
- for _, sideName in ipairs(sides) do
- local side = sides[sideName]
- local size = self:getInventorySize(side)
- if(size == 12 or size == 11 or size == 7) then
- table.insert(outputs, side)
- end
- end
- return outputs
- end
- ---@return table<number, autocompressor.compressor>
- function unit:getCompressors()
- local compressors = {}
- for _, side in ipairs(self:getOutputSides()) do
- local instance = self.compressors[side]
- if instance == nil then
- instance = compressor:new(self, side)
- self.compressors[side] = instance
- end
- compressors[side] = instance
- end
- return compressors
- end
- ---@return table<number, autocompressor.chest>
- function unit:getInputs()
- local inputs = {}
- for _, side in ipairs(self:getInputSides()) do
- local instance = self.inputs[side]
- if instance == nil then
- instance = chest:new(self, side)
- self.inputs[side] = instance
- end
- inputs[side] = instance
- end
- return inputs
- end
- function unit:getInventorySize(side)
- local cache = self.cachedSize[side]
- if cache ~= nil and uptime() - cache.time < 10 then
- return cache.size
- end
- local result = {size=self:getTransposer().getInventorySize(side) or 0, time=uptime()}
- self.cachedSize[side] = result
- return result.size
- end
- -- local unities = {}
- ---@param transposer component.transposer|string
- ---@param optional name string
- ---@return autocompressor.crafting_unit
- function lib.wrap(transposer, name)
- local address
- if type(transposer) == "table" then
- address = transposer.address
- elseif type(transposer) == "string" then
- address = transposer
- else
- error("Expected a transpoer address or the component's table")
- end
- --[[
- local instance = unities[address]
- if instance ~= nil then
- if name ~= nil then
- instance.name = name
- end
- return instance
- end
- ]]--
- name = name or address:sub(1,8)
- local instance = {address = address, name = name, compressors={}, inputs={}, cachedSize={}}
- setmetatable(instance, {__index = unit})
- --unities[address] = instance
- return instance
- end
- function lib.checkTime(name, func, ...)
- local start = uptime()
- local stat, result = xpcall(function(...) return table.pack(func(...)) end, function(msg)
- -- msg can be a custom error object
- if type(msg) == 'table' then
- if msg.reason ~= "terminated" then
- io.stderr:write(msg.reason.."\n")
- end
- return msg.code or 0
- end
- local stack = debug.traceback():gsub('^([^\n]*\n)[^\n]*\n[^\n]*\n','%1')
- local file = io.open("err-stack.txt", "a")
- file:write(string.format('%s:\n%s', msg or '', stack))
- file:close()
- return 128 -- syserr
- end, ...)
- local finish = uptime()
- --print(name.." took "..(finish - start).." seconds SUCCESS:"..tostring(stat or false))
- --[[
- local save = io.open("result.lua", "w")
- save:write(require("serialization").serialize({stat=stat, err=err, result=result}))
- save:close()
- ]]
- if not result then
- return nil
- else
- return table.unpack(result)
- end
- end
- function lib.tablelength(T)
- local count = 0
- for _ in pairs(T) do count = count + 1 end
- return count
- end
- function lib.serialize(t)
- return require("serialization").serialize(t, true)
- end
- return lib
Add Comment
Please, Sign In to add comment