Advertisement
Shiny_Neko

MEWarehouse

Apr 14th, 2025
549
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  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("meBridge")
  21. if not bridge then error("ME Bridge not found.") end
  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
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement