Uploaded working catalyst farm code
authorOlav Bakke Svendsen <mail@olavbs.no>
Tue, 26 Nov 2024 23:39:56 +0000 (00:39 +0100)
committerOlav Bakke Svendsen <mail@olavbs.no>
Tue, 26 Nov 2024 23:39:56 +0000 (00:39 +0100)
catalyst/auto.lua [new file with mode: 0644]
catalyst/carriage-controller.lua [new file with mode: 0644]
catalyst/config.lua [new file with mode: 0644]
catalyst/drill.lua [new file with mode: 0644]
catalyst/package [new file with mode: 0644]
catalyst/park.lua [new file with mode: 0644]
catalyst/startup.lua [new file with mode: 0644]
lib/inventory.lua

diff --git a/catalyst/auto.lua b/catalyst/auto.lua
new file mode 100644 (file)
index 0000000..11ba2d8
--- /dev/null
@@ -0,0 +1,9 @@
+local args = {...}
+if args[1] == "on" or args[1] == "true" then
+  os.queueEvent("auto", true)
+elseif args[1] == "off" or args[1] == "false" then
+  os.queueEvent("auto", false)
+else
+  print "usage: auto on | off"
+end
+  
diff --git a/catalyst/carriage-controller.lua b/catalyst/carriage-controller.lua
new file mode 100644 (file)
index 0000000..5a85fb2
--- /dev/null
@@ -0,0 +1,290 @@
+-- /bin/carriage.lua
+-- Configuration file at /etc/carriage.lua
+-- Operates drilling carriage. Starts drilling on "drill" event.
+-- Enable/disable automatic redrilling with "auto" bool event.
+-- Transfers items and restocks deployers after drilling.
+
+
+-- options
+
+local verbose
+do
+  local args = {...}
+  for i = 1, #args do
+    if args[i] == "-v" or args[i] == "--verbose" then
+      verbose = true
+    end
+  end
+end
+
+
+-- imports
+
+dofile "/lib/import.lua"
+
+global_import "/lib/control.lua" { "forever", "simultaneously" }
+
+local inv = import "/lib/inventory.lua"
+  { "free"
+  , "find"
+  , "move_all"
+  }
+
+local log = dofile "/lib/log.lua"
+-- log.on_insert = log.queue_event("log")
+log.debug = function(self, str) self:insert("debug", str) end
+log.on_insert = function(t)
+  if not verbose and t.status == "debug" then return end
+  local color = (t.status == "debug" and colors.gray)
+    or (t.status == "warn" and colors.yellow)
+    or (t.status == "err" and colors.red)
+    or colors.white
+  term.setTextColor(colors.gray)
+  write(os.date("%a %T ", t.epoch))
+  term.setTextColor(color)
+  print(t.entry)
+end
+
+
+-- configuration
+
+local config_path = "/etc/carriage-controller.lua"
+local config = {}
+do
+  local ok, config_file = pcall(dofile, config_path)
+  if not ok then return "couldn't load config" end
+--   local config_file = log:pwarn("Couldn't load \""..config_path.."\"", dofile, config_path)
+--   if err then log.warn("Couldn't load \""..config_path.."\": "..err) end
+  config_file = config_file or {}
+  for k,v in pairs(config_file) do config[k] = v end
+
+  if not config.deployers then
+    log:ok("auto-detecting deployers")
+    config.deployers = {}
+    for _,p in ipairs(peripheral.getNames()) do
+      if string.match(p, "^create:deployer") then
+        table.insert(config.deployers, p)
+      end
+    end
+    local f = #config.deployers > 0 and log.ok or log.err
+    f(log, "found "..tostring(#config.deployers).." deployers")
+  end
+  if not config.deployers or #config.deployers <= 0 then return end
+  
+  for k, v in pairs(config.contacts) do
+    local addr, side = table.unpack(config.contacts[k])
+    config.contacts[k].getInput = addr == "internal"
+      and function() return rs.getInput(side) end
+      or  function() return peripheral.call(addr, "getInput", side) end
+  end
+end
+
+
+-- signals
+
+local signal = {}
+signal.carriage_ready  = function() os.queueEvent("carriage", "ready")  end
+signal.carriage_busy   = function() os.queueEvent("carriage", "busy")   end
+signal.carriage_parked = function() os.queueEvent("carriage", "parked") end
+signal.drill = function() os.queueEvent("drill") end
+signal.auto = function(bool) os.queueEvent("auto", bool) end
+
+
+-- wait_until functions: yields until some condition is met
+
+local wait_until = {}
+
+wait_until.carriage_event = function(status)
+  return function()
+    local ev
+    repeat
+      _, ev = os.pullEvent("carriage")
+    until ev == status
+  end
+end
+
+wait_until.carriage_ready = wait_until.carriage_event("ready")
+
+wait_until.carriage_busy = wait_until.carriage_event("busy")
+
+wait_until.carriage_parked = wait_until.carriage_event("parked")
+
+wait_until.drilling_started = function() os.pullEvent("drill") end
+
+wait_until.auto = function(bool)
+  local auto
+  repeat
+    _, auto = os.pullEvent("auto")
+  until auto == bool
+end
+
+wait_until.carriage_should_return = function()
+  while not config.contacts.away.getInput() do os.sleep(0) end
+end
+
+wait_until.carriage_has_returned = function()
+  while not config.contacts.home.getInput() do os.sleep(0) end
+end
+
+wait_until.deployers_available = function()
+  local available = function()
+    for _,d in ipairs(config.deployers) do
+      if not peripheral.wrap(d) then return false end
+    end
+    return true
+  end
+  while not available() do os.sleep(); print("waiting for deployers") end
+end
+
+wait_until.carriage_inventory_available = function()
+  while not peripheral.wrap(config.carriage_inventory) do os.sleep() end
+end
+
+wait_until.output_below_threshold = function()
+  while inv.free(config.output_inventory) >= config.output_threshold do
+    os.sleep()
+  end
+end
+
+
+-- gearshift
+local gearshift = {}
+gearshift.forward = nil
+gearshift.back = nil
+
+do
+  local addr, side = table.unpack(config.gearshift)
+  local set
+  if addr == "internal" then
+    set = function(bool) return function() rs.setOutput(side, bool) end end
+  else
+    set = function(bool) return function() peripheral.call(addr, "setOutput", side, bool) end end
+  end
+  gearshift.forward = set(true)
+  gearshift.back = set(false)
+end
+
+
+-- inventory management
+
+local restock_deployers
+local transfer_items
+
+restock_deployers = function()
+--   wait_until.deployers_available()
+  for _, d in pairs(config.deployers) do
+--     local deployer = peripheral.wrap(d)
+    local empty = { name = "minecraft:redstone", count = 0, maxCount = 64 }
+    local item = peripheral.call(d, "getItemDetail", 1) or empty
+    if item.name ~= "minecraft:redstone" then
+      log:err(d.." holding "..item.name)
+    end
+    local found_all, index = inv.find(config.redstone_inventory, { ["minecraft:redstone"] = item.maxCount - item.count })
+    if not found_all then
+      log:warn("low on redstone")
+    end
+    if not index["minecraft:redstone"] then
+      log:warn("out of redstone")
+    else
+      for slot, amount in pairs(index["minecraft:redstone"].slots) do
+        peripheral.call(config.redstone_inventory, "pushItems", d, slot, amount)
+      end
+    end
+  end
+end
+
+transfer_items = function()
+--   wait_until.carriage_inventory_available()
+--   wait_until.output_below_threshold()
+  local moved = inv.move_all(config.carriage_inventory, config.output_inventory)
+  log:ok("transferred "..tostring(moved).." items")
+end
+
+
+-- routines 
+
+local drill
+local ready_carriage
+local toggle_auto
+local print_log
+local initialize
+
+drill = forever(function()
+  wait_until.carriage_ready()
+  log:debug("carriage ready")
+  wait_until.drilling_started()
+  log:debug("drilling started")
+  signal.carriage_busy()
+  gearshift.forward()
+  wait_until.carriage_should_return()
+  log:debug("carriage returning")
+  gearshift.back()
+  wait_until.carriage_has_returned()
+  log:debug("carriage parked")
+  signal.carriage_parked()
+end)
+
+ready_carriage = forever(function()
+  wait_until.carriage_parked()
+--   wait_until.deployers_available()
+  log:debug("restocking deployers")
+  restock_deployers()
+--   wait_until.output_below_threshold()
+--   wait_until.carriage_inventory_available()
+  log:debug("transferring items")
+  transfer_items()
+  signal.carriage_ready()
+end)
+
+toggle_auto = forever(function()
+  wait_until.auto(true)
+  log:ok("auto enabled")
+  simultaneously
+    { function() wait_until.auto(false) end
+    , forever(function()
+        wait_until.carriage_ready()
+        log:debug("auto drilling on ready")
+        signal.drill()
+      end)
+    , function()
+        if config.start_immediately then
+          log:debug("auto drilling immediately")
+          signal.drill()
+        end
+        forever(coroutine.yield)()
+      end
+    }
+  log:ok("auto disabled")
+end)
+
+-- print_log = forever(function()
+--   local _, epoch, status, entry = os.pullEvent("log")
+--   print(status, entry)
+-- end)
+
+initialize = function()
+  if not config.contacts.home.getInput() then
+    log:warn("carriage not found")
+    log:ok("(tip: \"park home\" forces it home)")
+  end
+  wait_until.carriage_has_returned()
+--   wait_until.deployers_available()
+--   wait_until.carriage_inventory_available()
+  log:ok("carriage parked")
+  signal.carriage_parked()
+  wait_until.carriage_ready()
+  if config.auto then signal.auto(true) end
+  forever(coroutine.yield)()
+end
+
+
+-- main
+
+simultaneously
+  { drill
+  , ready_carriage
+  , toggle_auto
+--   , print_log
+  , initialize
+  , function() os.pullEventRaw("terminate") end
+  }
diff --git a/catalyst/config.lua b/catalyst/config.lua
new file mode 100644 (file)
index 0000000..ee523e8
--- /dev/null
@@ -0,0 +1,44 @@
+-- catalyst controller config
+
+return
+
+  -- enable automatic redrilling by default
+  { auto = true
+
+  -- whether to start drilling when auto is enabled
+  , start_immediately = true
+
+  -- redstone contacts
+  , gearshift = { "redstoneIntegrator_3", "front" }
+
+  -- redstone contacts
+  , contacts =
+    { home = { "redstoneIntegrator_3", "top" }
+    , away = { "redstoneIntegrator_5", "top" }
+    }
+
+  -- list of deployer addresses
+  -- (automatically detects deployers when nil)
+  , deployers =
+    { "create:deployer_4"
+    , "create:deployer_5"
+    , "create:deployer_6"
+    , "create:deployer_7"
+    }
+
+  -- carriage's inventory address
+  -- (must be defined)
+  , carriage_inventory = "minecraft:barrel_6"
+
+  -- stationary inventory addresses
+  -- (must be defined)
+  , redstone_inventory = "functionalstorage:birch_1_2"
+  , output_inventory = "functionalstorage:birch_1_3"
+
+  -- pause automatic drilling unless output
+  -- inventory has space for this many items
+  , output_threshold = 1000 -- math.huge
+
+  }
+
+
diff --git a/catalyst/drill.lua b/catalyst/drill.lua
new file mode 100644 (file)
index 0000000..3954131
--- /dev/null
@@ -0,0 +1 @@
+os.queueEvent("drill")
diff --git a/catalyst/package b/catalyst/package
new file mode 100644 (file)
index 0000000..520fdf2
--- /dev/null
@@ -0,0 +1,10 @@
+cc catalyst/carriage-controller.lua:/bin/carriage-controller.lua:o
+cc catalyst/startup.lua:/bin/carriage-controller-startup.lua:o
+cc catalyst/drill.lua:/bin/drill.lua:o
+cc catalyst/auto.lua:/bin/auto.lua:o
+cc catalyst/park.lua:/bin/park.lua:o
+cc catalyst/config.lua:/etc/carriage-controller.lua
+cc lib/import.lua:/lib/import.lua:o
+cc lib/control.lua:/lib/control.lua:o
+cc lib/inventory.lua:/lib/inventory.lua:o
+cc lib/log.lua:/lib/log.lua:o
diff --git a/catalyst/park.lua b/catalyst/park.lua
new file mode 100644 (file)
index 0000000..448c1b9
--- /dev/null
@@ -0,0 +1,19 @@
+local args = {...}
+
+local config = dofile "/etc/carriage-controller.lua"
+
+local addr, side = table.unpack(config.gearshift)
+local set
+if addr == "internal" then
+  set = function(bool) rs.setOutput(side, bool) end
+else
+  set = function(bool) peripheral.call(addr, "setOutput", side, bool) end
+end
+
+if args[1] == "home" or args[1] == "false" then
+  set(false)
+elseif args[1] == "away" or args[1] == "true" then
+  set(true)
+else
+  print "usage: park home | away"
+end
diff --git a/catalyst/startup.lua b/catalyst/startup.lua
new file mode 100644 (file)
index 0000000..48e158d
--- /dev/null
@@ -0,0 +1,11 @@
+shell.run("bg carriage-controller -v")
+term.clear()
+term.setCursorPos(1,1)
+term.setTextColor(colors.yellow)
+print("Catalyst farm")
+term.setTextColor(colors.white)
+print("Config at /etc/carriage-controller.lua")
+print("Commands:")
+print("  drill")
+print("  auto on | off")
+print("  park home | away")
index bf8996caeff2d8f3e3bc321fea234834018a3fde..41d84426cab74de4b810910fc6b03126fc2df3d9 100644 (file)
@@ -103,6 +103,14 @@ t.move = function(source_address, destination_address, item_amounts)
   return not err or table.unpack({ false, "Could not move: "..err })
 end
 
+t.move_all = function(src, dst)
+  local moved = 0
+  for slot = 1, peripheral.call(src, "size") do
+    moved = moved + peripheral.call(src, "pushItems", dst, slot)
+  end
+  return moved
+end
+
 t.count = function(address, item_names)
   local slots = peripheral.call(address, "list")
   local count = 0
@@ -114,4 +122,74 @@ t.count = function(address, item_names)
   return count
 end
 
+t.free = function(address)
+  local inventory = peripheral.wrap(address)
+--   local items = inventory.list()
+  local free = 0
+  for slot = 1, inventory.size() do
+    local limit = inventory.getItemLimit(slot)
+    local item = inventory.getItemDetail(slot)
+    local amount = item and item.count or 0
+    free = free + limit - amount
+  end
+  return free
+end
+
+
+-- index :: inventory name -> index
+-- index :: name -> m (name, index)
+-- move :: (inv, { slot = amt })
+-- index :: inv -> (inv, { item = { total = amount, slots = { slot = amount } } })
+-- find :: {inv} -> item -> amount -> { inv = { total = amount, slots = { slot = amount } } }
+
+-- by_item :: ... -> { item = { total = amount, inventories = { total = amount, slots = { slot = amount } } } }
+-- by_inventory :: ... -> { inventory = { item = { total = amount, slots = { slot = amount } } } }
+
+-- internal inventories are indexed once. the index is stored in the inventory object and modified as the contents are operated on.
+-- normal inventories do not keep track of their index, and is re-indexed every time index() is called
+
+local inventory_index = {}
+-- { ["item_name"] = { total = amt, slots = { slot = amt } } }
+do
+  local mt = {}
+  mt.__index = function() return { total = 0, slots = {} } end
+  mt.__call = function()
+  end
+  setmetatable(inventory_index, mt)
+end
+
+local do_index = function(inventory)
+  if inventory.internal then
+    return inventory.index
+  end
+  if not inventory.current_index then
+    -- do the indexing
+  end
+  return inventory.index
+end
+
+local do_move = function(from, to, what)
+  local from_index
+  if not from.indexed then
+  end
+end
+
+local inventory = {}
+inventory.wrap = function(inventory_addr, block_reader_addr)
+  local inv =
+    { addr = inventory_addr
+    , block_reader_addr = block_reader_addr or false
+    , internal = false
+    , current_index = false
+    , index = function(self) end
+    , move = function(self, ...) end
+    }
+  return inv
+end
+inventory.wrap_internal = function(...)
+  local inv = inventory.wrap(...)
+  inv.internal = true
+  return inv
+end
+
 return t