Jump to content
  • Sky
  • Blueberry
  • Slate
  • Blackcurrant
  • Watermelon
  • Strawberry
  • Orange
  • Banana
  • Apple
  • Emerald
  • Chocolate
  • Charcoal
Felthry

A port of RandomBlue's AE2 autostocker (ported from ComputerCraft)

Recommended Posts

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 by Felthry
Link to post
Share on other sites

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):

 

2016-09-17_22-51-32.png

 

Any plans on adding something like that?

Link to post
Share on other sites

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.

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use and Privacy Policy.