View difference between Paste ID: Pyusp5QA and aGpgGKEM
SHOW: | | - or go back to the newest paste.
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