Felthry 0 Posted August 23, 2016 Share Posted August 23, 2016 (edited) This took more effort than I'd anticipated, but here you go! Has all the functionality of the original, save for the automatic generation of an autorun file (commented out if anyone wants to fix that up). This is a port of RandomBlue's Auto-Stocker program as found in http://www.computercraft.info/forums2/index.php?/topic/24624-ae2-stocker-autocraft-minimum-inventory-levels/ . stock.lua: -- ********************************************************************************** -- -- ** ** -- -- ** Minecraft AE2 Auto-Stocker by RandomBlue (E.J. Wilburn) ** -- -- ** Converted to OpenComputers by Felthry ** -- -- ** ---------------------------------------------------- ** -- -- ** ** -- -- ** This program automatically crafts items necessary to maintain a minimum ** -- -- ** stock level of specific items. The items are configured in a file on ** -- -- ** an openComputers computer named stock_list.txt in the autostock directory. ** -- -- ** Examine that file for example formatting and details. ** -- -- ** ** -- -- ** Minimum stock levels and crafting batch sizes are configurable per item. ** -- -- ** ** -- -- ** The computer must be connected to an adapter adjacent to a full block ME ** -- -- ** Interface linked to an ME Network where both the items are stored and the ** -- -- ** crafting CPUs are located. Each item you wish to maintain a stock level ** -- -- ** for must have autocrafting enabled for it. ** -- -- ** ** -- -- ** Arguments ** -- -- ** ---------------------------------------------------- ** -- -- ** checkFrequency (optional) - How often inventory levels are checked in ** -- -- ** seconds. ** -- -- ** stockFileName (optional) - Full path to the file containing stocking ** -- -- ** requirements. ** -- -- ** ** -- -- ** Change Log: ** -- -- ** 8th Sep 2015: [v0.1] Initial Release ** -- -- ** 11th Sep 2015: [v0.11] Minor bug fix - attempting to crafting 0 items ** -- -- ** when current quantity equals minQuantity ** -- -- ** 22nd Aug 2016: [vF.1] Converted to OpenComputers. Automatic generation ** -- -- ** of autorun file no longer supported ** -- -- ** 23rd Aug 2016: [vF.11] A few redundant functions removed, and some minor ** -- -- ** bug fixes; no longer wants an attachSide parameter ** -- -- ** and now displays time properly. ** -- -- ** 27th Aug 2016: [vF.12] Added more display functionality. The program now ** -- -- ** shows current stock levels, crafting status (sort ** -- -- ** of) and if something can't be crafted because the ** -- -- ** ME network lacks a free crafting CPU ** -- -- ** ** -- -- ** TODO: ** -- -- ** 1) Convert startup script to be compatible with OpenComputers ** -- -- ** 2) Save command line parameters to startup script. ** -- -- ** ** -- -- ********************************************************************************** -- -- libraries local fs = require("filesystem") local component = require("component") local serialization = require("serialization") local event = require("event") local tab = require("keyboard").keys.tab -- Parameters with default values. local checkFrequency = 15 -- How often inventory levels are checked in seconds. Overridden by passing as the first argument. local stockFileName = "/autostock/stock_list.txt" -- Change this if you want the file somewhere else. Can be -- overridden via a parameter. local recraftDelay = 300 -- Delay, in seconds, before allowing an item to be crafted again. If them item in question exceeds -- its min quantity before the delay expires, the delay is reset as it's assumed the job -- completed. 300 seconds = 5 minutes local delayedItems = {} -- List of delayed items by id:variant with delay time in seconds. Decremented each loop by -- checkFrequency ammount. When the delay hits 0 or lower then the item is removed from -- the list. local DEBUG = false local running = true local stocks = nil -- Process the input arguments - storing them to global variables local args = { ... } local events = setmetatable({}, {__index = function() return function() end end}) function events.key_up(keyboard, char, code, player) if (code == tab) then running = false end end function handleEvent(event, ...) if (event) then events[event](...) end end function checkInventory(ae2) print("[" .. getDisplayTime() .. "] Checking inventory. Press TAB to exit.") updateDelayedItems(delayedItems) local allItems = getAllItems(ae2) for i=1, #allItems do if (allItems[i].is_craftable == true) then stockItem(allItems[i], stocks, ae2) end end end local timerEvent = event.timer(checkFrequency, function() checkInventory(ae2) end, math.huge) function main(args) processArgs(args) stocks = loadStockFile(stockFileName) displayStockingInfo(stocks) -- enableAutoRestart() checkInventory(ae2) while (running) do handleEvent(event.pull()) end end function processArgs(args) if (#args >= 1) then assert(type(args[1]) == "number", "The first parameter (checkFrequency) must be a number.") checkFrequency = args[1] end if (#args > 1) then assert(type(args[2]) == "string", "The second parameter (stockFileName) must be a string.") stockFileName = args[2] end assert(fs.exists(stockFileName), "The stock file does not exist: " .. stockFileName) end function attachToAe2() -- Make sure there is actually an ME Interface attached. assert(component.isAvailable("me_interface"), "Error: The computer must be connected to an adapter adjacent to an ME interface.") return component.getPrimary("me_interface") end function loadStockFile(stockFileName) local stockFile = io.open(stockFileName, "r") local stockFileContents = stockFile:read("*a"); stockFile:close(); local outputStocks = serialization.unserialize(stockFileContents) if (DEBUG) then print("Stock file: ") print(stockFileContents) print("Output stocks length: " .. #outputStocks) print("Output stocks: ") for i=1, #outputStocks do print("itemId: " .. outputStocks[i].itemId) print("variant: " .. outputStocks[i].variant) print("minQuantity: " .. outputStocks[i].minQuantity) print("batchSize: " .. outputStocks[i].batchSize) end end assert(#outputStocks > 0, "There are no entries in the " .. stockFileName .. " file.") return outputStocks end function displayStockingInfo(stocks) print("Stocking info:") for i=1, #stocks do print(" item: " .. stocks[i].displayName .. " minQuantity: " .. stocks[i].minQuantity .. " batchSize: " .. stocks[i].batchSize) end end function getAllItems(ae2) local outputAllItems = ae2.getAvailableItems() assert(outputAllItems ~= nil, "No craftable items found in this AE2 network.") assert(#outputAllItems > 0, "No craftable items found in this AE2 network.") return outputAllItems end function isCpuAvailable(ae2) local cpus = ae2.getCraftingCPUs() for i=1, #cpus do if (cpus[i].busy == false) then return true end end return false end function findStockSetting(fingerprint, stocks) for i=1, #stocks do if (stocks[i].itemId == fingerprint.id and stocks[i].variant == fingerprint.dmg) then return stocks[i] end end return nil end function stockItem(currItem, stocks, ae2) local stockSetting = findStockSetting(currItem.fingerprint, stocks) if (stockSetting == nil) then return end if (currItem.size >= stockSetting.minQuantity) then print(stockSetting.displayName .. ": " .. currItem.size .. "≥" .. stockSetting.minQuantity) return end if (isDelayed(currItem.fingerprint, delayedItems)) then print(stockSetting.displayName .. ": Currently crafting.") return end if (isCpuAvailable(ae2) == false) then print(stockSetting.displayName .. ": No crafting CPU available to craft.") return end local neededAmount = math.ceil((stockSetting.minQuantity - currItem.size) / stockSetting.batchSize) * stockSetting.batchSize ae2.requestCrafting(currItem.fingerprint, neededAmount) delayItem(currItem.fingerprint, delayedItems) print("[" .. getDisplayTime() .. "] Item " .. stockSetting.displayName .. " is below its min stock level of " .. stockSetting.minQuantity .. ". Crafting " .. neededAmount .. " more.") end function getDisplayTime() return os.date("%H:%M:%S", os.time()) end function delayItem(fingerprint, delayedItems) local fullItemName = fingerprintToFullName(fingerprint) if(delayedItems == nil) then delayedItems = {} end for i=1, #delayedItems do if (delayedItems[i].fullName == fullItemName) then delayedItems[i].delay = recraftDelay return end end local delayedItem = {fullName = fullItemName, delay = recraftDelay} delayedItems[#delayedItems+1] = delayedItem end function updateDelayedItems(delayedItems) if (delayedItems == nil or #delayedItems < 1) then return end local removeIndexes = {} for i=1, #delayedItems do currItem = delayedItems[i] currItem.delay = currItem.delay - checkFrequency if (currItem.delay < 0) then table.insert(removeIndexes, i) end end -- This should remove items from the end of the list towards the beginning -- so the list being reordered won't matter. for i=1, #removeIndexes do table.remove(delayedItems, removeIndexes[i]) end end function fingerprintToFullName(fingerprint) return fingerprint.id .. ":" .. fingerprint.dmg end function isDelayed(fingerprint, delayedItems) if (delayedItems == nil or #delayedItems < 1) then return false end local fullItemName = fingerprintToFullName(fingerprint) for i=1, #delayedItems do if (delayedItems[i].fullName == fullItemName and delayedItems[i].delay > 0) then return true end end return false end --function enableAutoRestart() -- -- Skip this if any startup file already exists. -- -- Let the user manaully delete or edit the startup file at that point. -- -- Notify the user. -- if (fs.exists("startup") == true) then -- print("Startup file already exists.") -- return -- end -- -- outputFile = io.open("startup", "w") -- -- -- Write an info message so that people know how to get out of auto-resume -- outputFile.write("\nprint(\"Running auto-restart...\")\n") -- outputFile.write("print(\"If you want to stop auto-resume and restore original state:\")\n") -- outputFile.write("print(\"1) Hold Ctrl-T until the program terminates\")\n") -- outputFile.write("print(\"2) Type \\\"rm startup\\\" (without quotes) and hit Enter\")\n") -- outputFile.write("print(\"\")\n\n") -- -- -- Write the code required to restart the turtle -- outputFile.write("shell.run(\"") -- outputFile.write(shell.getRunningProgram()) -- outputFile.write("\")\n") -- outputFile.close() --end -- Start the actual program ae2 = attachToAe2() local ok, err = xpcall(main, debug.traceback, args) if not ok then print("Error detected; destroying timer object.") event.cancel(timerEvent) print(err) end -- On exit event.cancel(timerEvent) print("Stopping autostock system.") stock_list.txt: { { displayName = "Logic Processor", itemId = "appliedenergistics2:item.ItemMultiMaterial", variant = 22, minQuantity = 64, batchSize = 32 }, { displayName = "Calculation Processor", itemId = "appliedenergistics2:item.ItemMultiMaterial", variant = 23, minQuantity = 32, batchSize = 32 }, { displayName = "Engineering Processor", itemId = "appliedenergistics2:item.ItemMultiMaterial", variant = 24, minQuantity = 32, batchSize = 32 }, { displayName = "Stick", itemId = "minecraft:stick", variant = 0, minQuantity = 128, batchSize = 64 }, { displayName = "Sand", itemId = "minecraft:sand", variant = 0, minQuantity = 256, batchSize = 64 }, { displayName = "Glass", itemId = "minecraft:glass", variant = 0, minQuantity = 128, batchSize = 32 }, { displayName = "Oak Wood Planks", itemId = "minecraft:planks", variant = 0, minQuantity = 256, batchSize = 64 }, { displayName = "Pure Fluix Crystal", itemId = "appliedenergistics2:item.ItemMultiMaterial", variant = 12, minQuantity = 128, batchSize = 64 }, { displayName = "Pure Certus Quartz Crystal", itemId = "appliedenergistics2:item.ItemMultiMaterial", variant = 10, minQuantity = 128, batchSize = 64 }, { displayName = "Pure Nether Quartz Crystal", itemId = "appliedenergistics2:item.ItemMultiMaterial", variant = 11, minQuantity = 128, batchSize = 64 }, { displayName = "Dark Steel Ball", itemId = "EnderIO:itemMaterial", variant = 7, minQuantity = 10, batchSize = 5 }, { displayName = "Formation Core", itemId = "appliedenergistics2:item.ItemMultiMaterial", variant = 43, minQuantity = 16, batchSize = 8 }, { displayName = "Annihilation Core", itemId = "appliedenergistics2:item.ItemMultiMaterial", variant = 44, minQuantity = 16, batchSize = 8 }, { displayName = "Fluix Crystal", itemId = "appliedenergistics2:item.ItemMultiMaterial", variant = 7, minQuantity = 64, batchSize = 16 }, } Edit Aug 28th 2016: Corrected a capitalization mistake in stock_list.txt that made it not work for some items, updated stock.lua to vF.12 Edited September 18, 2016 by Felthry Quote Link to post Share on other sites
Jelmar 0 Posted September 17, 2016 Share Posted September 17, 2016 Looks nice and seems to work well. However, editing of the stock_list.txt isn't available in the computer like in the ComputerCraft version. This makes it very cumbersome to add or remove items to the stock keeping list. You have to open the .txt and manually add another entry. The CC version allows you to do that inside the program. Unless I am missing something? I.e., I mean this (screenshot from ComputerCraft version): Any plans on adding something like that? Quote Link to post Share on other sites
Felthry 0 Posted September 18, 2016 Author Share Posted September 18, 2016 I have honestly never seen that functionality in the CC version, and there's no code that would provide it. Is there perhaps a newer version than the one I ported? Quote Link to post Share on other sites
Jelmar 0 Posted September 18, 2016 Share Posted September 18, 2016 I have no idea if there is a newer version. I believe this is the github for the CC script: https://github.com/sidoh/stockpile The part we're talking about might be somewhere in .../stockpile/itemSearcher? Don't know if it is something that is able to be ported, or if you have time for it or even feel like it, but it would definitely improve the program. Quote Link to post Share on other sites
Felthry 0 Posted September 18, 2016 Author Share Posted September 18, 2016 Ah, I see the problem. The script you linked is a completely different script entirely, and, from the looks of it, a better one. http://www.computercraft.info/forums2/index.php?/topic/24624-ae2-stocker-autocraft-minimum-inventory-levels/ This is the one I ported. Simpler, and more rudimentary. Perhaps I should link it in the first post. Quote Link to post Share on other sites
Jelmar 0 Posted September 18, 2016 Share Posted September 18, 2016 Ah, right. I had no idea there was a different CC program than the one I linked. Perhaps an idea to take a look and borrow some features from that one? Quote Link to post Share on other sites
Felthry 0 Posted September 19, 2016 Author Share Posted September 19, 2016 I may well do that! Of course, you're welcome to as well; I have no objections to people making their own changes to the code. Do post it here so others can use it too, though! Quote Link to post Share on other sites
godsyn 0 Posted November 7, 2017 Share Posted November 7, 2017 Quote Link to post Share on other sites
BrisingrAerowing 12 Posted November 12, 2017 Share Posted November 12, 2017 @godsyn What MC version? Currently the only OC version with AE2 support is for 1.10.2. It hasn't been updated to newer versions yet. Quote Link to post Share on other sites