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

Felthry

Members
  • Content Count

    6
  • Joined

  • Last visited

Posts posted by Felthry

  1. 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

  2. I've tried everything I can find to figure out how to stop this from happening, but since I can't seem to find anything at all, I'll put it here.

     

    I have a program that relies on a timer to execute some control logic periodically. The code currently has a fatal error in it and crashes, but I haven't been able fix it because I'm not able to use the computer afterward without rebooting it, because the timer event that the program starts apparently never stops, so no matter what I'm doing with the computer, every fifteen seconds it spits out a timestamp followed by "Checking inventory.... Press TAB to exit.". Even though the program that started the timer has crashed.

     

    Now, I understand that the timer ends up as a global object or whatever it is that doesn't go away until explicitly destroyed by a call to event.cancel(). What I can't figure out is how to catch a crash--any crash--and destroy this timer so that it doesn't keep running this subroutine while I'm trying to debug.

     

    My first thought was to wrap the whole program in a try ... catch, but apparently those don't exist in Lua and I can't even begin to understand what's going on with pcall and xpcall; trying to wrap the main function in them doesn't seem to do anything.

     

    Any help would be greatly appreciated.

×
×
  • Create New...

Important Information

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