Megaddd

OpenComputers AE2 Stocker.lua

Feb 23rd, 2020
642
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.28 KB | None | 0 0
  1. -- ********************************************************************************** --
  2. -- ** ** --
  3. -- ** Minecraft AE2 Auto-Stocker by RandomBlue (E.J. Wilburn) ** --
  4. -- ** Converted to OpenComputers by Felthry ** --
  5. -- ** ---------------------------------------------------- ** --
  6. -- ** ** --
  7. -- ** This program automatically crafts items necessary to maintain a minimum ** --
  8. -- ** stock level of specific items. The items are configured in a file on ** --
  9. -- ** an openComputers computer named stock_list.txt in the autostock directory. ** --
  10. -- ** Examine that file for example formatting and details. ** --
  11. -- ** ** --
  12. -- ** Minimum stock levels and crafting batch sizes are configurable per item. ** --
  13. -- ** ** --
  14. -- ** The computer must be connected to an adapter adjacent to a full block ME ** --
  15. -- ** Interface linked to an ME Network where both the items are stored and the ** --
  16. -- ** crafting CPUs are located. Each item you wish to maintain a stock level ** --
  17. -- ** for must have autocrafting enabled for it. ** --
  18. -- ** ** --
  19. -- ** Arguments ** --
  20. -- ** ---------------------------------------------------- ** --
  21. -- ** checkFrequency (optional) - How often inventory levels are checked in ** --
  22. -- ** seconds. ** --
  23. -- ** stockFileName (optional) - Full path to the file containing stocking ** --
  24. -- ** requirements. ** --
  25. -- ** ** --
  26. -- ** Change Log: ** --
  27. -- ** 8th Sep 2015: [v0.1] Initial Release ** --
  28. -- ** 11th Sep 2015: [v0.11] Minor bug fix - attempting to crafting 0 items ** --
  29. -- ** when current quantity equals minQuantity ** --
  30. -- ** 22nd Aug 2016: [vF.1] Converted to OpenComputers. Automatic generation ** --
  31. -- ** of autorun file no longer supported ** --
  32. -- ** 23rd Aug 2016: [vF.11] A few redundant functions removed, and some minor ** --
  33. -- ** bug fixes; no longer wants an attachSide parameter ** --
  34. -- ** and now displays time properly. ** --
  35. -- ** 27th Aug 2016: [vF.12] Added more display functionality. The program now ** --
  36. -- ** shows current stock levels, crafting status (sort ** --
  37. -- ** of) and if something can't be crafted because the ** --
  38. -- ** ME network lacks a free crafting CPU ** --
  39. -- ** ** --
  40. -- ** TODO: ** --
  41. -- ** 1) Convert startup script to be compatible with OpenComputers ** --
  42. -- ** 2) Save command line parameters to startup script. ** --
  43. -- ** ** --
  44. -- ********************************************************************************** --
  45.  
  46.  
  47. -- libraries
  48. local fs = require("filesystem")
  49. local component = require("component")
  50. local serialization = require("serialization")
  51. local event = require("event")
  52. local tab = require("keyboard").keys.tab
  53.  
  54. -- Parameters with default values.
  55. local checkFrequency = 15 -- How often inventory levels are checked in seconds. Overridden by passing as the first argument.
  56. local stockFileName = "/autostock/stock_list.txt" -- Change this if you want the file somewhere else. Can be
  57. -- overridden via a parameter.
  58. local recraftDelay = 300 -- Delay, in seconds, before allowing an item to be crafted again. If them item in question exceeds
  59. -- its min quantity before the delay expires, the delay is reset as it's assumed the job
  60. -- completed. 300 seconds = 5 minutes
  61. local delayedItems = {} -- List of delayed items by id:variant with delay time in seconds. Decremented each loop by
  62. -- checkFrequency ammount. When the delay hits 0 or lower then the item is removed from
  63. -- the list.
  64.  
  65. local DEBUG = false
  66. local running = true
  67. local stocks = nil
  68.  
  69. -- Process the input arguments - storing them to global variables
  70. local args = { ... }
  71.  
  72. local events = setmetatable({}, {__index = function() return function() end end})
  73. function events.key_up(keyboard, char, code, player)
  74. if (code == tab) then
  75. running = false
  76. end
  77. end
  78.  
  79. function handleEvent(event, ...)
  80. if (event) then
  81. events[event](...)
  82. end
  83. end
  84.  
  85. function checkInventory(ae2)
  86. print("[" .. getDisplayTime() .. "] Checking inventory. Press TAB to exit.")
  87. updateDelayedItems(delayedItems)
  88. local allItems = getAllItems(ae2)
  89. for i=1, #allItems do
  90. if (allItems[i].is_craftable == true) then
  91. stockItem(allItems[i], stocks, ae2)
  92. end
  93. end
  94. end
  95.  
  96. local timerEvent = event.timer(checkFrequency, function() checkInventory(ae2) end, math.huge)
  97.  
  98. function main(args)
  99. processArgs(args)
  100. stocks = loadStockFile(stockFileName)
  101. displayStockingInfo(stocks)
  102. -- enableAutoRestart()
  103.  
  104. checkInventory(ae2)
  105. while (running) do
  106. handleEvent(event.pull())
  107. end
  108. end
  109.  
  110. function processArgs(args)
  111. if (#args >= 1) then
  112. assert(type(args[1]) == "number", "The first parameter (checkFrequency) must be a number.")
  113. checkFrequency = args[1]
  114. end
  115.  
  116. if (#args > 1) then
  117. assert(type(args[2]) == "string", "The second parameter (stockFileName) must be a string.")
  118. stockFileName = args[2]
  119. end
  120. assert(fs.exists(stockFileName), "The stock file does not exist: " .. stockFileName)
  121. end
  122.  
  123. function attachToAe2()
  124. -- Make sure there is actually an ME Interface attached.
  125. assert(component.isAvailable("me_interface"), "Error: The computer must be connected to an adapter adjacent to an ME interface.")
  126. return component.getPrimary("me_interface")
  127. end
  128.  
  129. function loadStockFile(stockFileName)
  130. local stockFile = io.open(stockFileName, "r")
  131. local stockFileContents = stockFile:read("*a");
  132. stockFile:close();
  133. local outputStocks = serialization.unserialize(stockFileContents)
  134.  
  135. if (DEBUG) then
  136. print("Stock file: ")
  137. print(stockFileContents)
  138. print("Output stocks length: " .. #outputStocks)
  139. print("Output stocks: ")
  140. for i=1, #outputStocks do
  141. print("itemId: " .. outputStocks[i].itemId)
  142. print("variant: " .. outputStocks[i].variant)
  143. print("minQuantity: " .. outputStocks[i].minQuantity)
  144. print("batchSize: " .. outputStocks[i].batchSize)
  145. end
  146. end
  147.  
  148. assert(#outputStocks > 0, "There are no entries in the " .. stockFileName .. " file.")
  149. return outputStocks
  150. end
  151.  
  152. function displayStockingInfo(stocks)
  153. print("Stocking info:")
  154. for i=1, #stocks do
  155. print(" item: " .. stocks[i].displayName .. " minQuantity: " .. stocks[i].minQuantity ..
  156. " batchSize: " .. stocks[i].batchSize)
  157. end
  158. end
  159.  
  160. function getAllItems(ae2)
  161. local outputAllItems = ae2.getAvailableItems()
  162. assert(outputAllItems ~= nil, "No craftable items found in this AE2 network.")
  163. assert(#outputAllItems > 0, "No craftable items found in this AE2 network.")
  164. return outputAllItems
  165. end
  166.  
  167. function isCpuAvailable(ae2)
  168. local cpus = ae2.getCraftingCPUs()
  169. for i=1, #cpus do
  170. if (cpus[i].busy == false) then return true end
  171. end
  172. return false
  173. end
  174.  
  175. function findStockSetting(fingerprint, stocks)
  176. for i=1, #stocks do
  177. if (stocks[i].itemId == fingerprint.id and stocks[i].variant == fingerprint.dmg) then
  178. return stocks[i]
  179. end
  180. end
  181. return nil
  182. end
  183.  
  184. function stockItem(currItem, stocks, ae2)
  185. local stockSetting = findStockSetting(currItem.fingerprint, stocks)
  186.  
  187. if (stockSetting == nil) then return end
  188. if (currItem.size >= stockSetting.minQuantity) then
  189. print(stockSetting.displayName .. ": " .. currItem.size .. "≥" .. stockSetting.minQuantity)
  190. return
  191. end
  192. if (isDelayed(currItem.fingerprint, delayedItems)) then
  193. print(stockSetting.displayName .. ": Currently crafting.")
  194. return
  195. end
  196. if (isCpuAvailable(ae2) == false) then
  197. print(stockSetting.displayName .. ": No crafting CPU available to craft.")
  198. return
  199. end
  200.  
  201. local neededAmount = math.ceil((stockSetting.minQuantity - currItem.size) / stockSetting.batchSize) * stockSetting.batchSize
  202.  
  203. ae2.requestCrafting(currItem.fingerprint, neededAmount)
  204. delayItem(currItem.fingerprint, delayedItems)
  205. print("[" .. getDisplayTime() .. "] Item " .. stockSetting.displayName ..
  206. " is below its min stock level of " .. stockSetting.minQuantity .. ". Crafting " .. neededAmount .. " more.")
  207. end
  208.  
  209. function getDisplayTime()
  210. return os.date("%H:%M:%S", os.time())
  211. end
  212.  
  213. function delayItem(fingerprint, delayedItems)
  214. local fullItemName = fingerprintToFullName(fingerprint)
  215.  
  216. if(delayedItems == nil) then
  217. delayedItems = {}
  218. end
  219.  
  220. for i=1, #delayedItems do
  221. if (delayedItems[i].fullName == fullItemName) then
  222. delayedItems[i].delay = recraftDelay
  223. return
  224. end
  225. end
  226.  
  227. local delayedItem = {fullName = fullItemName, delay = recraftDelay}
  228. delayedItems[#delayedItems+1] = delayedItem
  229. end
  230.  
  231. function updateDelayedItems(delayedItems)
  232. if (delayedItems == nil or #delayedItems < 1) then return end
  233.  
  234. local removeIndexes = {}
  235. for i=1, #delayedItems do
  236. currItem = delayedItems[i]
  237. currItem.delay = currItem.delay - checkFrequency
  238. if (currItem.delay < 0) then
  239. table.insert(removeIndexes, i)
  240. end
  241. end
  242.  
  243. -- This should remove items from the end of the list towards the beginning
  244. -- so the list being reordered won't matter.
  245. for i=1, #removeIndexes do
  246. table.remove(delayedItems, removeIndexes[i])
  247. end
  248. end
  249.  
  250. function fingerprintToFullName(fingerprint)
  251. return fingerprint.id .. ":" .. fingerprint.dmg
  252. end
  253.  
  254. function isDelayed(fingerprint, delayedItems)
  255. if (delayedItems == nil or #delayedItems < 1) then return false end
  256.  
  257. local fullItemName = fingerprintToFullName(fingerprint)
  258. for i=1, #delayedItems do
  259. if (delayedItems[i].fullName == fullItemName and delayedItems[i].delay > 0) then
  260. return true
  261. end
  262. end
  263.  
  264. return false
  265. end
  266.  
  267. --function enableAutoRestart()
  268. -- -- Skip this if any startup file already exists.
  269. -- -- Let the user manaully delete or edit the startup file at that point.
  270. -- -- Notify the user.
  271. -- if (fs.exists("startup") == true) then
  272. -- print("Startup file already exists.")
  273. -- return
  274. -- end
  275. --
  276. -- outputFile = io.open("startup", "w")
  277. --
  278. -- -- Write an info message so that people know how to get out of auto-resume
  279. -- outputFile.write("\nprint(\"Running auto-restart...\")\n")
  280. -- outputFile.write("print(\"If you want to stop auto-resume and restore original state:\")\n")
  281. -- outputFile.write("print(\"1) Hold Ctrl-T until the program terminates\")\n")
  282. -- outputFile.write("print(\"2) Type \\\"rm startup\\\" (without quotes) and hit Enter\")\n")
  283. -- outputFile.write("print(\"\")\n\n")
  284. --
  285. -- -- Write the code required to restart the turtle
  286. -- outputFile.write("shell.run(\"")
  287. -- outputFile.write(shell.getRunningProgram())
  288. -- outputFile.write("\")\n")
  289. -- outputFile.close()
  290. --end
  291.  
  292. -- Start the actual program
  293.  
  294. ae2 = attachToAe2()
  295.  
  296. local ok, err = xpcall(main, debug.traceback, args)
  297. if not ok then
  298. print("Error detected; destroying timer object.")
  299. event.cancel(timerEvent)
  300. print(err)
  301. end
  302.  
  303. -- On exit
  304. event.cancel(timerEvent)
  305. print("Stopping autostock system.")
Add Comment
Please, Sign In to add comment