balloonanimal

ae2Defrag.lua

Feb 5th, 2023
80
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. -- Config
  2.  
  3. local maxUsedSlotsPerCell = 55
  4. local paddingPercent = 100
  5.  
  6. local systemDriveNames = {
  7.   "appliedenergistics2:drive_28",
  8.   "appliedenergistics2:drive_25",
  9. }
  10. local workspaceNames = {
  11.   ioPort = "appliedenergistics2:io_port_8",
  12.   interface = "appliedenergistics2:interface_7",
  13.   chest = "appliedenergistics2:chest_7",
  14.   drives = {
  15.     "appliedenergistics2:drive_31",
  16.     "appliedenergistics2:drive_30",
  17.   }
  18. }
  19.  
  20. local capacityByName = {
  21.   ["appliedenergistics2:storage_cell_1k"] = 1024,
  22.   ["appliedenergistics2:storage_cell_4k"] = 4096,
  23.   ["appliedenergistics2:storage_cell_16k"] = 16384,
  24.   ["appliedenergistics2:storage_cell_64k"] = 65536
  25. }
  26.  
  27. -- /Config
  28.  
  29. -- Util Functions
  30.  
  31. local clock = os.clock
  32. local function sleep(n)  -- seconds
  33.   local t0 = clock()
  34.   while clock() - t0 <= n do end
  35. end
  36.  
  37. local function map(array, func)
  38.   local new_array = {}
  39.   for i,v in pairs(array) do
  40.     new_array[i] = func(v, i)
  41.   end
  42.   return new_array
  43. end
  44.  
  45. local function values(obj)
  46.   local result = {}
  47.   for _, v in pairs(obj) do
  48.     table.insert(result, v)
  49.   end
  50.   return result
  51. end
  52.  
  53. local function groupBy(array, prop)
  54.   local result = {}
  55.   for _, element in pairs(array) do
  56.     if element[prop] ~= nil then
  57.       if result[element[prop]] == nil then
  58.         result[element[prop]] = {}
  59.       end
  60.       table.insert(result[element[prop]], element)
  61.     end
  62.   end
  63.   return result
  64. end
  65.  
  66. -- /Util Functions
  67.  
  68. -- Peripherals
  69.  
  70. local systemDrives = map(systemDriveNames, function(driveName)
  71.   return peripheral.wrap(driveName)
  72. end)
  73. local workspace = {
  74.   ioPort = peripheral.wrap(workspaceNames.ioPort),
  75.   interface = peripheral.wrap(workspaceNames.interface),
  76.   chest = peripheral.wrap(workspaceNames.chest),
  77.   drives = map(workspaceNames.drives, function(driveName)
  78.     return peripheral.wrap(driveName)
  79.   end)
  80. }
  81.  
  82. -- /Peripherals
  83.  
  84. -- Classes
  85.  
  86. -- Cell
  87.  
  88. local cells = {}
  89. local additionallyRequiredCells = {}
  90. local Cell = {}
  91. Cell.__index = Cell
  92.  
  93. Cell.capacities = (function()
  94.   local capacities = values(capacityByName)
  95.   table.sort(capacities, function(a, b) return a < b end)
  96.   return capacities
  97. end)()
  98.  
  99. function Cell.sortByUnusedBytesDesc(a, b)
  100.   return b:getNumUnusedBytes() < a:getNumUnusedBytes()
  101. end
  102.  
  103. function Cell.sortByCapacity(a, b)
  104.   return a.capacity < b.capacity
  105. end
  106.  
  107. function Cell.sortByCapacityDesc(a, b)
  108.   return b.capacity < a.capacity
  109. end
  110.  
  111. function Cell.loadAll()
  112.   for driveNum, systemDrive in ipairs(systemDrives) do
  113.     local systemCells = systemDrive.list()
  114.     local toSlotNum = 1
  115.     for _, cell in pairs(systemCells) do
  116.       table.insert(cells, Cell.new(capacityByName[cell.name], driveNum, toSlotNum))
  117.       toSlotNum = toSlotNum + 1
  118.     end
  119.   end
  120.   table.remove(cells, #cells)
  121. end
  122.  
  123. function Cell.new(capacity, driveNum, slotNum)
  124.   local self = setmetatable({}, Cell)
  125.   self.capacity = capacity
  126.   self.driveNum = driveNum
  127.   self.slotNum = slotNum
  128.   self.inventory = {}
  129.   return self
  130. end
  131.  
  132. function Cell.getSmallestCellNeededForStack(stack)
  133.   for _, capacity in ipairs(Cell.capacities) do
  134.     local cell = Cell.new(capacity)
  135.     if cell:hasSpaceFor(stack) then
  136.       return cell
  137.     end
  138.   end
  139. end
  140.  
  141. function Cell:getNumUsedBytes()
  142.   local bytesUsed = 0
  143.   for _, stackData in pairs(self.inventory) do
  144.     bytesUsed = bytesUsed + self:getBytesForStack(stackData)
  145.   end
  146.   return bytesUsed
  147. end
  148.  
  149. function Cell:add(stack)
  150.   table.insert(self.inventory, stack)
  151.   if self:getNumUsedBytes() > self.capacity then
  152.     error("Unexpected error: inventory has exceeded capacity")
  153.   end
  154. end
  155.  
  156. function Cell:getBytesForStack(stack)
  157.   return (self.capacity / 128) + math.ceil(stack.count / 8)
  158. end
  159.  
  160. function Cell:hasSpaceFor(stack)
  161.   local bytesUsed = self:getNumUsedBytes()
  162.   local hasEnoughBytes = bytesUsed + self:getBytesForStack(stack) < self.capacity
  163.   local hasEnoughSlots = self:getNumUnusedSlots() > 0
  164.   return hasEnoughBytes and hasEnoughSlots
  165. end
  166.  
  167. function Cell:getNumUnusedBytes()
  168.   return self.capacity - self:getNumUsedBytes()
  169. end
  170.  
  171. function Cell:getNumUnusedSlots()
  172.   return maxUsedSlotsPerCell - #self.inventory
  173. end
  174.  
  175. function Cell:clearAndPutInWorkspaceChest()
  176.   local drive = workspace.drives[self.driveNum]
  177.   drive.pushItems(workspaceNames.ioPort, self.slotNum)
  178.   while workspace.ioPort.list()[7] == nil do
  179.     sleep(0.1)
  180.   end
  181.   workspace.ioPort.pushItems(workspaceNames.chest, 7)
  182. end
  183.  
  184. function Cell:exportInventoryToWorkspaceChest()
  185.   for _, stack in ipairs(self.inventory) do
  186.     stack:exportToWorkspaceChest()
  187.   end
  188. end
  189.  
  190. local outputDrives = {}
  191. for _, drive in pairs(systemDrives) do
  192.   table.insert(outputDrives, drive)
  193. end
  194. local currentOutputDrive = table.remove(outputDrives, 1)
  195. function Cell:moveBackToSystem()
  196.   currentOutputDrive.pullItems(workspaceNames.chest, 2)
  197.   if #currentOutputDrive.list() == 10 then
  198.     currentOutputDrive = table.remove(outputDrives, 1)
  199.   end
  200. end
  201.  
  202. -- Stack
  203.  
  204. local stacks = {}
  205. local Stack = {}
  206. Stack.__index = Stack
  207.  
  208. function Stack.sortByCountDesc(a, b)
  209.   return b.count < a.count
  210. end
  211.  
  212. function Stack.loadAll()
  213.   local handledItemTypes = {}
  214.   local allItemTypes = workspace.interface.listAvailableItems()
  215.   for _, itemType in pairs(allItemTypes) do
  216.     if handledItemTypes[itemType.name] == nil then
  217.       handledItemTypes[itemType.name] = 1
  218.       local ccStacks = workspace.interface.findItems(itemType.name)
  219.       for _, ccStack in pairs(ccStacks) do
  220.         table.insert(stacks, Stack.new(ccStack))
  221.       end
  222.     end
  223.   end
  224. end
  225.  
  226. function Stack.new(ccStack)
  227.   local self = setmetatable({}, Stack)
  228.   self.ccStack = ccStack
  229.   self.metadata = ccStack.getMetadata()
  230.   self.displayName = self.metadata.displayName
  231.   self.count = (1+paddingPercent/100)*self.metadata.count
  232.   return self
  233. end
  234.  
  235. function Stack:addToCellWithLargestUnusuedSpace()
  236.   table.sort(cells, Cell.sortByUnusedBytesDesc)
  237.   for _, cell in ipairs(cells) do
  238.     if cell:hasSpaceFor(self) then
  239.       cell:add(self)
  240.       return
  241.     end
  242.   end
  243.   local newCell = Cell.getSmallestCellNeededForStack(self)
  244.   newCell:add(self)
  245.   table.insert(cells, newCell)
  246.   table.insert(additionallyRequiredCells, newCell)
  247.   error("No cell found to add stack to")
  248. end
  249.  
  250. function Stack:exportAllToWorkspaceChest()
  251.   local amountToExport = self.metadata.count
  252.   print("  Exporting "..amountToExport.." "..self.displayName.."...")
  253.   local amountExported = 0
  254.   while amountExported < amountToExport do
  255.     amountExported = amountExported + self.ccStack.export(workspaceNames.chest)
  256.   end
  257. end
  258.  
  259. -- /Classes
  260.  
  261. -- Main
  262.  
  263. print("Scanning for cells...")
  264. Cell.loadAll()
  265.  
  266. print("Moving cells to workspace...")
  267. local function moveDrivesFromSystemToWorkspace()
  268.   for driveNum, systemDrive in ipairs(systemDrives) do
  269.     local systemCells = systemDrive.list()
  270.     for fromSlotNum, _ in pairs(systemCells) do
  271.       systemDrive.pushItems(workspaceNames.drives[driveNum], fromSlotNum)
  272.     end
  273.   end
  274. end
  275.  
  276. moveDrivesFromSystemToWorkspace()
  277.  
  278. print("Scanning for stacks...")
  279. Stack.loadAll()
  280.  
  281. print("Planning...")
  282. table.sort(stacks, Stack.sortByCountDesc)
  283. for _, stack in ipairs(stacks) do
  284.   stack:addToCellWithLargestUnusuedSpace()
  285. end
  286.  
  287. if #additionallyRequiredCells > 0 then
  288.   print("Needed cells:")
  289.   local requiredCellsByCapacity = groupBy(additionallyRequiredCells, 'capacity')
  290.   for _, capacity in ipairs(Cell.capacities) do
  291.     if requiredCellsByCapacity[capacity] ~= nil and #requiredCellsByCapacity[capacity] > 0 then
  292.       print("  "..#requiredCellsByCapacity[capacity].." "..(capacity/1024).."k cells")
  293.     end
  294.   end
  295.   error("Add the above cells to continue")
  296. end
  297.  
  298. print("Executing plan...")
  299.  
  300. table.sort(cells, Cell.sortByCapacity)
  301.  
  302. for _, cell in ipairs(cells) do
  303.   print("clearing and putting in workspace...")
  304.   cell:clearAndPutInWorkspaceChest()
  305.   print("moving stacks to chest...")
  306.   for _, stack in ipairs(cell.inventory) do
  307.     stack:exportAllToWorkspaceChest()
  308.   end
  309.   print("moving cell back to system...")
  310.   cell:moveBackToSystem()
  311. end
Add Comment
Please, Sign In to add comment