View difference between Paste ID: Mn4WbWks and JvA56yN4
SHOW: | | - or go back to the newest paste.
1
-- Original author: Scott Adkins <[email protected]> (Zucanthor)
2
--
3
-- This program monitors work requests for the Minecolonies Warehouse and
4
-- tries to fulfill requests from the Applied Energistics 2 network. If the
5
-- AE2 network doesn't have enough items and a crafting pattern exists, a
6
-- crafting job is scheduled to restock the items in order to fulfill the
7
-- work request.  The script will continuously loop, monitoring for new
8
-- requests and checking on crafting jobs to fulfill previous requests.
9
10
-- The following is required for setup:
11
--   * 1 ComputerCraft Computer
12
--   * 1 or more ComputerCraft Monitors (recommend 3x3 advanced monitors)
13
--   * 1 Advanced Peripheral Colony Integrator
14
--   * 1 Advanced Peripheral AE2 Bridge
15
--   * 1 Chest or other storage container
16
-- Attach an AE2 Cable from the AE2 network to the AE2 Bridge. Connect the
17
-- storage container to the Minecolonies Warehouse Hut block directly or
18
-- to an ender chest and then pipe ender chest contents out to the warehouse
19
-- racks. Latter makes it easier to upgrade warehouse.
20
21
-- THINGS YOU CAN CUSTOMIZE IN THIS PROGRAM:
22
-- Line 56: Specify the side storage container is at.
23
-- Line 66: Name of log file for storing JSON data of all open requests.
24
-- Lines 231+: Any items you find that should be manually provided.
25
-- Line 373: Time in seconds between work order scans.
26
27
----------------------------------------------------------------------------
28
-- INITIALIZATION
29
----------------------------------------------------------------------------
30
31
-- Initialize Monitor
32
-- A future update may allow for multiple monitors. This would allow one
33
-- monitor to be used for logging and another to be used for work requests.
34
local monitor = peripheral.find("monitor")
35
if not monitor then error("Monitor not found.") end
36
monitor.setTextScale(0.5)
37
monitor.clear()
38
monitor.setCursorPos(1, 1)
39
monitor.setCursorBlink(false)
40
print("Monitor initialized.")
41
 
42
-- Initialize ME Bridge
43
local bridge = peripheral.find("meBridge")
44
if not bridge then error("ME Bridge not found.") end
45
print("ME Bridge initialized.")
46
 
47
-- Initialize Colony Integrator
48
local colony = peripheral.find("colonyIntegrator")
49
if not colony then error("Colony Integrator not found.") end
50
if not colony.isInColony then error("Colony Integrator is not in a colony.") end
51
print("Colony Integrator initialized.")
52
 
53
-- Point to location of chest or storage container
54
-- A future update may autodetect where the storage container is and error
55
-- out if no storage container is found.
56-
local storage = "left"
56+
--local storage = "left"
57-
print("Storage initialized.")
57+
--print("Storage initialized.")
58
 
59
-- Name of log file to capture JSON data from the open requests.  The log can
60
-- be too big to edit within CC, which may require a "pastebin put" if you want
61
-- to look at it.  Logging could be improved to only capture Skipped items,
62
-- which in turn will make log files smaller and edittable in CC directly.
63
local logFile = "RSWarehouse.log"
64
 
65
----------------------------------------------------------------------------
66
-- FUNCTIONS
67
----------------------------------------------------------------------------
68
 
69
-- Prints to the screen one row after another, scrolling the screen when
70
-- reaching the bottom. Acts as a normal display where text is printed in
71
-- a standard way. Long lines are not wrapped and newlines are printed as
72
-- spaces, both to be addressed in a future update.
73
-- NOTE: No longer used in this program.
74
function mPrintScrollable(mon, ...)
75
    w, h = mon.getSize()
76
    x, y = mon.getCursorPos()
77
 
78
    -- Blink the cursor like a normal display.
79
    mon.setCursorBlink(true)
80
 
81
    -- For multiple strings, append them with a space between each.
82
    for i = 2, #arg do t = t.." "..arg[i] end
83
    mon.write(arg[1])
84
    if y >= h then
85
        mon.scroll(1)
86
        mon.setCursorPos(1, y)
87
    else
88
        mon.setCursorPos(1, y+1)
89
    end
90
end
91
 
92
-- Prints strings left, centered, or right justified at a specific row and
93
-- specific foreground/background color.
94
function mPrintRowJustified(mon, y, pos, text, ...)
95
    w, h = mon.getSize()
96
    fg = mon.getTextColor()
97
    bg = mon.getBackgroundColor()
98
 
99
    if pos == "left" then x = 1 end
100
    if pos == "center" then x = math.floor((w - #text) / 2) end
101
    if pos == "right" then x = w - #text end
102
 
103
    if #arg > 0 then mon.setTextColor(arg[1]) end
104
    if #arg > 1 then mon.setBackgroundColor(arg[2]) end
105
    mon.setCursorPos(x, y)
106
    mon.write(text)
107
    mon.setTextColor(fg)
108
    mon.setBackgroundColor(bg)
109
end
110
 
111
-- Utility function that returns true if the provided character is a digit.
112
-- Yes, this is a hack and there are better ways to do this.  Clearly.
113
function isdigit(c)
114
    if c == "0" then return true end
115
    if c == "1" then return true end
116
    if c == "2" then return true end
117
    if c == "3" then return true end
118
    if c == "4" then return true end
119
    if c == "5" then return true end
120
    if c == "6" then return true end
121
    if c == "7" then return true end
122
    if c == "8" then return true end
123
    if c == "9" then return true end
124
    return false
125
end
126
 
127
-- Utility function that displays current time and remaining time on timer.
128
-- For time of day, yellow is day, orange is sunset/sunrise, and red is night.
129
-- The countdown timer is orange over 15s, yellow under 15s, and red under 5s.
130
-- At night, the countdown timer is red and shows PAUSED insted of a time.
131
function displayTimer(mon, t)
132
    now = os.time()
133
 
134
    cycle = "day"
135
    cycle_color = colors.orange
136
    if now >= 4 and now < 6 then
137
        cycle = "sunrise"
138
        cycle_color = colors.orange
139
    elseif now >= 6 and now < 18 then
140
        cycle = "day"
141
        cycle_color = colors.yellow
142
    elseif now >= 18 and now < 19.5 then
143
        cycle = "sunset"
144
        cycle_color = colors.orange
145
    elseif now >= 19.5 or now < 5 then
146
        cycle = "night"
147
        cycle_color = colors.red
148
    end
149
 
150
    timer_color = colors.orange
151
    if t < 15 then timer_color = colors.yellow end
152
    if t < 5 then timer_color = colors.red end
153
 
154
    mPrintRowJustified(mon, 1, "left", string.format("Time: %s [%s]    ", textutils.formatTime(now, false), cycle), cycle_color)
155
    if cycle ~= "night" then mPrintRowJustified(mon, 1, "right", string.format("    Remaining: %ss", t), timer_color)
156
    else mPrintRowJustified(mon, 1, "right", "    Remaining: PAUSED", colors.red) end
157
end
158
 
159
-- Scan all open work requests from the Warehouse and attempt to satisfy those
160
-- requests.  Display all activity on the monitor, including time of day and the
161
-- countdown timer before next scan.  This function is not called at night to
162
-- save on some ticks, as the colonists are in bed anyways.  Items in red mean
163
-- work order can't be satisfied by Refined Storage (lack of pattern or lack of
164
-- required crafting ingredients).  Yellow means order partially filled and a
165
-- crafting job was scheduled for the rest.  Green means order fully filled.
166
-- Blue means the Player needs to manually fill the work order.  This includes
167
-- equipment (Tools of Class), NBT items like armor, weapons and tools, as well
168
-- as generic requests ike Compostables, Fuel, Food, Flowers, etc.
169
function scanWorkRequests(mon, rs, chest)
170
    -- Before we do anything, prep the log file for this scan.
171
    -- The log file is truncated each time this function is called.
172
    file = fs.open(logFile, "w")
173
    print("\nScan starting at", textutils.formatTime(os.time(), false) .. " (" .. os.time() ..").")
174
 
175
    -- We want to keep three different lists so that they can be
176
    -- displayed on the monitor in a more intelligent way.  The first
177
    -- list is for the Builder requests.  The second list is for the
178
    -- non-Builder requests.  The third list is for any armor, tools
179
    -- and weapons requested by the colonists.
180
    builder_list = {}
181
    nonbuilder_list = {}
182
    equipment_list = {}
183
 
184
    -- Scan RS for all items in its network. Ignore items with NBT data.
185
    -- If a Builder needs any items with NBT data, this function will need
186
    -- to be updated to not ignore those items.
187
    items = bridge.listItems()
188
    item_array = {}
189
    for index, item in ipairs(items) do
190
        if not item.nbt then
191
            item_array[item.name] = item.amount
192
        end
193
    end
194
 
195
    -- Scan the Warehouse for all open work requests. For each item, try to
196
    -- provide as much as possible from RS, then craft whatever is needed
197
    -- after that. Green means item was provided entirely. Yellow means item
198
    -- is being crafted. Red means item is missing crafting recipe.
199
    workRequests = colony.getRequests()
200
    -- file.write(textutils.serialize(workRequests))
201
    for w in pairs(workRequests) do
202
        name = workRequests[w].name
203
        item = workRequests[w].items[1].name
204
        target = workRequests[w].target
205
        desc = workRequests[w].desc
206
        needed = workRequests[w].count
207
        provided = 0
208
 
209
        target_words = {}
210
        target_length = 0
211
        for word in target:gmatch("%S+") do
212
            table.insert(target_words, word)
213
            target_length = target_length + 1
214
        end
215
 
216
        if target_length >= 3 then target_name = target_words[target_length-2] .. " " .. target_words[target_length]
217
        else target_name = target end
218
 
219
        target_type = ""
220
        target_count = 1
221
        repeat
222
            if target_type ~= "" then target_type = target_type .. " " end
223
            target_type = target_type .. target_words[target_count]
224
            target_count = target_count + 1
225
        until target_count > target_length - 3
226
 
227
        useRS = 1
228
        if string.find(desc, "Tool of class") then useRS = 0 end
229
        if string.find(name, "Hoe") then useRS = 0 end
230
        if string.find(name, "Shovel") then useRS = 0 end
231
        if string.find(name, "Axe") then useRS = 0 end
232
        if string.find(name, "Pickaxe") then useRS = 0 end
233
        if string.find(name, "Bow") then useRS = 0 end
234
        if string.find(name, "Sword") then useRS = 0 end
235
        if string.find(name, "Shield") then useRS = 0 end
236
        if string.find(name, "Helmet") then useRS = 0 end
237
        if string.find(name, "Leather Cap") then useRS = 0 end
238
        if string.find(name, "Chestplate") then useRS = 0 end
239
        if string.find(name, "Tunic") then useRS = 0 end
240
        if string.find(name, "Pants") then useRS = 0 end
241
        if string.find(name, "Leggings") then useRS = 0 end
242
        if string.find(name, "Boots") then useRS = 0 end
243
        if name == "Rallying Banner" then useRS = 0 end --bugged in alpha versions
244
        if name == "Crafter" then useRS = 0 end
245
        if name == "Compostable" then useRS = 0 end
246
        if name == "Fertilizer" then useRS = 0 end
247
        if name == "Flowers" then useRS = 0 end
248
        if name == "Food" then useRS = 0 end
249
        if name == "Fuel" then useRS = 0 end
250
        if name == "Smeltable Ore" then useRS = 0 end
251
        if name == "Stack List" then useRS = 0 end
252
 
253
        color = colors.blue
254
        if useRS == 1 then
255
            if item_array[item] then
256-
                provided = bridge.exportItem({name=item, count=needed}, chest)
256+
				print(item)
257
				print(needed)
258
                provided = bridge.exportItem({name=item, count=needed}, "west")
259
            end
260
261
            color = colors.green
262
            if provided < needed then
263
                if bridge.isItemCrafting( { name = item } ) then
264
                    color = colors.yellow
265
                    print("[Crafting]", item)
266
                else
267
                    if bridge.craftItem({name=item, count=needed}) then
268
                        color = colors.yellow
269
                        print("[Scheduled]", needed, "x", item)
270
                    else
271
                        color = colors.red
272
                        print("[Failed]", item)
273
                    end
274
                end
275
            end
276
        else
277
            nameString = name .. " [" .. target .. "]"
278
            print("[Skipped]", nameString)
279
        end
280
 
281
        if string.find(desc, "of class") then
282
            level = "Any Level"
283
            if string.find(desc, "with maximal level:Leather") then level = "Leather" end
284
            if string.find(desc, "with maximal level:Gold") then level = "Gold" end
285
            if string.find(desc, "with maximal level:Chain") then level = "Chain" end
286
            if string.find(desc, "with maximal level:Wood or Gold") then level = "Wood or Gold" end
287
            if string.find(desc, "with maximal level:Stone") then level = "Stone" end
288
            if string.find(desc, "with maximal level:Iron") then level = "Iron" end
289
            if string.find(desc, "with maximal level:Diamond") then level = "Diamond" end
290
            new_name = level .. " " .. name
291
            if level == "Any Level" then new_name = name .. " of any level" end
292
            new_target = target_type .. " " .. target_name
293
            equipment = { name=new_name, target=new_target, needed=needed, provided=provided, color=color}
294
            table.insert(equipment_list, equipment)
295
        elseif string.find(target, "Builder") then
296
            builder = { name=name, item=item, target=target_name, needed=needed, provided=provided, color=color }
297
            table.insert(builder_list, builder)
298
        else
299
            new_target = target_type .. " " .. target_name
300
            if target_length < 3 then
301
                new_target = target
302
            end
303
            nonbuilder = { name=name, target=new_target, needed=needed, provided=provided, color=color }
304
            table.insert(nonbuilder_list, nonbuilder)
305
        end
306
    end
307
 
308
    -- Show the various lists on the attached monitor.
309
    row = 3
310
    mon.clear()
311
 
312
    header_shown = 0
313
    for e in pairs(equipment_list) do
314
        equipment = equipment_list[e]
315
        if header_shown == 0 then
316
            mPrintRowJustified(mon, row, "center", "Equipment")
317
            header_shown = 1
318
            row = row + 1
319
        end
320
        text = string.format("%d %s", equipment.needed, equipment.name)
321
        mPrintRowJustified(mon, row, "left", text, equipment.color)
322
        mPrintRowJustified(mon, row, "right", " " .. equipment.target, equipment.color)
323
        row = row + 1
324
    end
325
 
326
    header_shown = 0
327
    for b in pairs(builder_list) do
328
        builder = builder_list[b]
329
        if header_shown == 0 then
330
            if row > 1 then row = row + 1 end
331
            mPrintRowJustified(mon, row, "center", "Builder Requests")
332
            header_shown = 1
333
            row = row + 1
334
        end
335
        text = string.format("%d/%s", builder.provided, builder.name)
336
        mPrintRowJustified(mon, row, "left", text, builder.color)
337
        mPrintRowJustified(mon, row, "right", " " .. builder.target, builder.color)
338
        row = row + 1
339
    end
340
 
341
    header_shown = 0
342
    for n in pairs(nonbuilder_list) do
343
        nonbuilder = nonbuilder_list[n]
344
        if header_shown == 0 then
345
            if row > 1 then row = row + 1 end
346
            mPrintRowJustified(mon, row, "center", "Nonbuilder Requests")
347
            header_shown = 1
348
            row = row + 1
349
        end
350
        text = string.format("%d %s", nonbuilder.needed, nonbuilder.name)
351
        if isdigit(nonbuilder.name:sub(1,1)) then
352
            text = string.format("%d/%s", nonbuilder.provided, nonbuilder.name)
353
        end
354
        mPrintRowJustified(mon, row, "left", text, nonbuilder.color)
355
        mPrintRowJustified(mon, row, "right", " " .. nonbuilder.target, nonbuilder.color)
356
        row = row + 1
357
    end
358
 
359
    if row == 3 then mPrintRowJustified(mon, row, "center", "No Open Requests") end
360
    print("Scan completed at", textutils.formatTime(os.time(), false) .. " (" .. os.time() ..").")
361
    file.close()
362
end
363
 
364
----------------------------------------------------------------------------
365
-- MAIN
366
----------------------------------------------------------------------------
367
 
368
-- Scan for requests periodically. This will catch any updates that were
369
-- triggered from the previous scan. Right-clicking on the monitor will
370
-- trigger an immediate scan and reset the timer. Unfortunately, there is
371
-- no way to capture left-clicks on the monitor.
372
local time_between_runs = 30
373
local current_run = time_between_runs
374
scanWorkRequests(monitor, bridge, storage)
375
displayTimer(monitor, current_run)
376
local TIMER = os.startTimer(1)
377
 
378
while true do
379
    local e = {os.pullEvent()}
380
    if e[1] == "timer" and e[2] == TIMER then
381
        now = os.time()
382
        if now >= 5 and now < 19.5 then
383
            current_run = current_run - 1
384
            if current_run <= 0 then
385
                scanWorkRequests(monitor, bridge, storage)
386
                current_run = time_between_runs
387
            end
388
        end
389
        displayTimer(monitor, current_run)
390
        TIMER = os.startTimer(1)
391
    elseif e[1] == "monitor_touch" then
392
        os.cancelTimer(TIMER)
393
        scanWorkRequests(monitor, bridge, storage)
394
        current_run = time_between_runs
395
        displayTimer(monitor, current_run)
396
        TIMER = os.startTimer(1)
397
    end
398
end