View difference between Paste ID: pmijevYr and NzaAJWXU
SHOW: | | - or go back to the newest paste.
1
-- RSWarehouse.lua
2
-- Author: Chuck Burgess
3
-- Updated: 2024-01-15
4
5
local logFile = "RSWarehouse.log"
6
local time_between_runs = 30
7
8
-- Initialize Monitor
9
-- see: https://www.computercraft.info/wiki/Advanced_Monitor
10
local monitor = peripheral.find("monitor")
11
if not monitor then error("Monitor not found.") end
12
monitor.setTextScale(0.5)
13
monitor.clear()
14
monitor.setCursorPos(1, 1)
15
monitor.setCursorBlink(false)
16
print("Monitor initialized.")
17
 
18
-- Initialize RS Bridge
19
-- see: https://advancedperipherals.madefor.cc/peripherals/rs_bridge/
20-
local bridge = peripheral.find("rsBridge")
20+
local bridge = peripheral.find("meBridge")
21-
if not bridge then error("RS Bridge not found.") end
21+
if not bridge then error("ME Bridge not found.") end
22-
print("RS Bridge initialized.")
22+
print("ME Bridge initialized.")
23
24
-- Initialize Colony Integrator
25
-- see: https://docs.advanced-peripherals.de/peripherals/colony_integrator/
26
local colony = peripheral.find("colonyIntegrator")
27
if not colony then error("Colony Integrator not found.") end
28
if not colony.isInColony then error("Colony Integrator is not in a colony.") end
29
print("Colony Integrator initialized.")
30
 
31
-- Establish the direction to transport the items into the Warehouse based on
32
-- where the entnglement block is sitting. Default to empty string.
33
local storage = peripheral.find("entangled:tile")
34
if not storage then error("Warehouse storage not found.") end
35
local direction = ""
36
local names = peripheral.getNames()
37
for _, pos in ipairs(names) do
38
  if peripheral.getType(pos) == "entangled:tile" then
39
    direction = pos
40
    end
41
end
42
print("Warehouse storage initialized.")
43
44
----------------------------------------------------------------------------
45
-- FUNCTIONS
46
----------------------------------------------------------------------------
47
--[[
48
  Table.Empty
49
  @desc     check to see if a table contains any data
50
  @return   boolean
51
]]
52
function table.empty (self)
53
    for _, _ in pairs(self) do
54
        return false
55
    end
56
    return true
57
end
58
59
--[[
60
    Write To Log
61
    @desc   Write the specified `table` to the file surrounded by the `blockTop` and `blockBottom`
62
    @return void
63
]]
64
function writeToLog(data, blockTop, blockBottom)
65
  file.write("\n")
66
  file.write(blockTop)
67
  file.write("\n")
68
  file.write(textutils.serialize(data, { allow_repetitions = true }))
69
  file.write("\n")
70
  file.write(blockBottom)
71
  file.write("\n")
72
end
73
74
--[[
75
    Process Work Request Item
76
    @desc Determine if this item can be delivered to the warehouse from the storage
77
    @return boolean
78
]]
79
function processWorkRequestItem(request)
80
  if string.find(request.desc, "Tool of class") then return false end
81
  if string.find(request.name, "Hoe") then return false end
82
  if string.find(request.name, "Shovel") then return false end
83
  if string.find(request.name, "Axe") then return false end
84
  if string.find(request.name, "Pickaxe") then return false end
85
  if string.find(request.name, "Bow") then return false end
86
  if string.find(request.name, "Sword") then return false end
87
  if string.find(request.name, "Shield") then return false end
88
  if string.find(request.name, "Helmet") then return false end
89
  if string.find(request.name, "Leather Cap") then return false end
90
  if string.find(request.name, "Chestplate") then return false end
91
  if string.find(request.name, "Tunic") then return false end
92
  if string.find(request.name, "Pants") then return false end
93
  if string.find(request.name, "Leggings") then return false end
94
  if string.find(request.name, "Boots") then return false end
95
  if request.name == "Rallying Banner" then return false end --bugged in alpha versions
96
  if request.name == "Crafter" then return false end
97
  if request.name == "Compostable" then return false end
98
  if request.name == "Fertilizer" then return false end
99
  if request.name == "Flowers" then return false end
100
  if request.name == "Food" then return false end
101
  if request.name == "Fuel" then return false end
102
  if request.name == "Smeltable Ore" then return false end
103
  if request.name == "Stack List" then return false end
104
  -- you can add any new items here if they are found
105
  return true
106
end
107
108
--[[
109
    Monitor Print Row Justified
110
    @desc   Print a line of data to the in-game monitor
111
    @return void
112
]]
113
function mPrintRowJustified(mon, y, pos, text, textcolor)
114
    w, h = mon.getSize()
115
    fg = colors.white
116
    bg = colors.black
117
 
118
    if pos == "left" then x = 1 end
119
    if pos == "center" then x = math.floor((w - #text) / 2) end
120
    if pos == "right" then x = w - #text end
121
  
122
    mon.setTextColor(textcolor)
123
    mon.setCursorPos(x, y)
124
    mon.write(text)
125
    mon.setTextColor(fg)
126
    mon.setBackgroundColor(bg)
127
end
128
 
129
--[[
130
    Display Timer
131
    @desc   Update the time on the monitor
132
    @return void
133
]]
134
function displayTimer(mon, t)
135
    now = os.time()
136
    cycle = "day"
137
    cycle_color = colors.orange
138
    if now >= 4 and now < 6 then
139
        cycle = "sunrise"
140
        cycle_color = colors.yellow
141
    elseif now >= 6 and now < 18 then
142
        cycle = "day"
143
        cycle_color = colors.lightBlue
144
    elseif now >= 18 and now < 19.5 then
145
        cycle = "sunset"
146
        cycle_color = colors.magenta
147
    elseif now >= 19.5 or now < 5 then
148
        cycle = "night"
149
        cycle_color = colors.red
150
    end
151
 
152
    timer_color = colors.green
153
    if t < 15 then timer_color = colors.yellow end
154
    if t < 5 then timer_color = colors.orange end
155
 
156
    mPrintRowJustified(mon, 1, "left", string.format("Time: %s [%s]    ", textutils.formatTime(now, false), cycle), cycle_color)
157
    if cycle ~= "night" then 
158
      mPrintRowJustified(mon, 1, "right", string.format("    Remaining: %ss", t), timer_color)
159
    else 
160
      mPrintRowJustified(mon, 1, "right", "    Remaining: PAUSED", colors.red)
161
    end
162
end
163
164
--[[
165
    Create Colonist Data
166
    @desc   Build a table of Colonist making the request
167
    @return table
168
]]
169
function createColonistData(colonist)
170
  title_words = {}
171
  words_in_name = 0
172
  colonist_job = ""
173
  word_count = 1
174
  
175
  for word in colonist:gmatch("%S+") do
176
    table.insert(title_words, word)
177
    words_in_name = words_in_name + 1
178
  end
179
180
  if words_in_name >= 3 then colonist_name = title_words[words_in_name-2] .. " " .. title_words[words_in_name]
181
  else colonist_name = colonist end
182
183
  repeat
184
    if colonist_job ~= "" then colonist_job = colonist_job .. " " end
185
    colonist_job = colonist_job .. title_words[word_count]
186
    word_count = word_count + 1
187
  until word_count > words_in_name - 3
188
  
189
  return  { fullName = colonist, titleWords = title_words, job = colonist_job, name = colonist_name, wordsInName = words_in_name }
190
end
191
192
--[[
193
    Get Work Request List (from colony)
194
    @desc   Build a table of the work request data from the colony
195
    @return table
196
]]
197
function getWorkRequestList(colony)
198
    requestList = {}
199
    workRequests = colony.getRequests()
200
    file = fs.open(logFile, "w")
201
    
202
    for w in pairs(workRequests) do
203
        writeToLog(workRequests[w], "--- Request start ---", "--- Request end ---");
204
        name = workRequests[w].name -- the name of the count/item being requested
205
        colonist = createColonistData(workRequests[w].target)
206
        desc = workRequests[w].desc -- the request description
207
        item = {}
208
        -- create the filter item for the transfer request through the bridge
209
        if workRequests[w].items and workRequests[w].items[1] then
210
          if not workRequests[w].items[1].nbt or table.empty(workRequests[w].items[1].nbt) then
211
            item = { name = workRequests[w].items[1].name, count =  workRequests[w].count, displayName = workRequests[w].items[1].displayName}
212
          else
213
            item = { name = workRequests[w].items[1].name, count = workRequests[w].count, displayName = workRequests[w].items[1].displayName, nbt =  workRequests[w].items[1].nbt}
214
          end
215
        end
216
        -- how many items are needed to fulfill this request?
217
        needed = workRequests[w].count
218
219
        local newRecord = {}
220
        newRecord.name = name
221
        newRecord.desc = desc
222
        newRecord.needed = needed
223
        newRecord.item = item
224
        newRecord.colonist = colonist
225
        table.insert(requestList, newRecord)
226
        writeToLog(newRecord, "--- Record start ---", "--- Record end ---");
227
      end
228
      file.close()
229
  return requestList
230
end
231
232
--[[
233
    Display List
234
    @desc   Update the monitor with the work request items currently in the system
235
    @return void
236
]]
237
function displayList(mon, listName, itemList)
238
  -- show the list header first
239
  mPrintRowJustified(mon, row, "center", listName, colors.white)
240
  row = row + 1
241
  for e in pairs(itemList) do
242
      record = itemList[e]
243
      text = string.format("%d %s", record.provided , record.name)
244
      mPrintRowJustified(mon, row, "left", text, record.color)
245
      mPrintRowJustified(mon, row, "right", " " .. record.colonist, record.color)
246
      row = row + 1
247
  end
248
  -- add a space at the end of the list
249
  row = row + 1
250
end
251
252
-- Color References:
253
-- RED:     work order can't be satisfied by Refined Storage (lack of pattern or lack of
254
--            required crafting ingredients).
255
-- YELLOW:  order partially filled and a crafting job was scheduled for the rest.
256
-- GREEN:   order fully filled.
257
-- BLUE:    the Player needs to manually fill the work order. This includes some equipment as well as generic requests ike Compostables, Fuel, Food, Flowers, etc.
258
--[[
259
    Scan Work Requests
260
    @desc   Manages all of the open work requests in the system and attempts to fulfill them from the inventory
261
    @desc   Not called at night (as determined by the server) since requests cannot be fulfilled anyway
262
    @return void
263
]]
264
function scanWorkRequests(mon, bridge, direction)
265
    
266
    print("\nScan starting at", textutils.formatTime(os.time(), false) .. " (" .. os.time() ..").")
267
    builder_list = {}
268
    nonbuilder_list = {}
269
    equipment_list = {}
270
    requestList = getWorkRequestList(colony)
271
    
272
    for j, data in ipairs(requestList) do
273
        color = colors.blue
274
        provided = 0
275
276
        if processWorkRequestItem(data) then
277
            provided = bridge.exportItemToPeripheral(data.item, direction)
278
            color = colors.lightGray
279
            if provided >= data.needed then
280
              color = colors.green
281
            end
282
            -- only handle the Non-NBT data items or empty nbt table item records
283
            if provided < data.needed then
284
                if bridge.isItemCrafting(data.item) then
285
                    color = colors.yellow
286
                    print("[Crafting]", data.name)
287
                else
288
                    if bridge.craftItem(data.item) then
289
                      color = colors.yellow
290
                      print("[Scheduled]", data.item.count, "x", data.name)
291
                    else
292
                      color = colors.red
293
                      print("[Failed]", data.name)
294
                    end
295
                end
296
            end
297
        else 
298
           nameString = data.name .. " [" .. data.colonist.fullName .. "]"
299
           print("[Skipped]", nameString)
300
        end
301
        -- ---------------------------------------------------------------------
302
        -- Build the newList data
303
        -- ---------------------------------------------------------------------
304
        -- create the target text
305
        expectedList = "Builder"
306
        colonist = data.colonist.name
307
        if not string.find(data.colonist.fullName, "Builder") then
308
            expectedList = ""
309
            colonist = data.colonist.job .. " " .. data.colonist.name
310
            if data.colonist.wordsInName < 3 then
311
                colonist = data.colonist.name
312
            end
313
        end
314
          
315
        -- create the name
316
        listName = data.name
317
        if string.find(data.desc, "level") then
318
            expectedList = "Equipment"
319
            level = "Any Level"
320
            if string.find(data.desc, "with maximal level: Leather") then level = "Leather" end
321
            if string.find(data.desc, "with maximal level: Gold") then level = "Gold" end
322
            if string.find(data.desc, "with maximal level: Chain") then level = "Chain" end
323
            if string.find(data.desc, "with maximal level: Wood or Gold") then level = "Wood or Gold" end
324
            if string.find(data.desc, "with maximal level: Stone") then level = "Stone" end
325
            if string.find(data.desc, "with maximal level: Iron") then level = "Iron" end
326
            if string.find(data.desc, "with maximal level: Diamond") then level = "Diamond" end
327
            listName = level .. " " .. data.name
328
            if level == "Any Level" then listName = data.name .. " of any level" end
329
        end
330
          
331
        -- create the new list table defining what is inserted into a specific list
332
        newList = { name=listName, colonist=colonist, needed=data.needed, provided=provided, color=color}
333
        
334
        if expectedList == "Equipment" then
335
            table.insert(equipment_list, newList)
336
        elseif expectedList == "Builder" then
337
            table.insert(builder_list, newList)
338
        else
339
            table.insert(nonbuilder_list, newList)
340
        end
341
        -- ---------------------------------------------------------------------
342
    end
343
344
  -- Show the various lists on the attached monitor.
345
  mon.clear()
346
  row = 3
347
  if not table.empty(builder_list) then displayList(mon, "Builder Requests", builder_list) end
348
  if not table.empty(nonbuilder_list) then displayList(mon, "Nonbuilder Requests", nonbuilder_list) end
349
  if not table.empty(equipment_list) then displayList(mon, "Equipment", equipment_list) end
350
351
  -- no requests
352
  if row == 3 then 
353
    mPrintRowJustified(mon, row, "center", "No Open Requests", colors.white)
354
  end
355
  print("Scan completed at", textutils.formatTime(os.time(), false) .. " (" .. os.time() ..").") 
356
end
357
358
359
--[[
360
    MAIN
361
    @desc   establish the run times and execute the work request management
362
    @return void
363
]]
364
local current_run = time_between_runs
365
scanWorkRequests(monitor, bridge, direction)
366
displayTimer(monitor, current_run)
367
local TIMER = os.startTimer(1)
368
369
while true do
370
  local e = {os.pullEvent()}
371
  if e[1] == "timer" and e[2] == TIMER then
372
    now = os.time()
373
    if now >= 5 and now < 19.5 then
374
      current_run = current_run - 1
375
      if current_run <= 0 then
376
        scanWorkRequests(monitor, bridge, direction)
377
        current_run = time_between_runs
378
      end
379
    end
380
    displayTimer(monitor, current_run)
381
    TIMER = os.startTimer(1)
382
  elseif e[1] == "monitor_touch" then
383
    os.cancelTimer(TIMER)
384
    scanWorkRequests(monitor, bridge, direction)
385
    current_run = time_between_runs
386
    displayTimer(monitor, current_run)
387
    TIMER = os.startTimer(1)
388
  end
389
end