Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- Config stuff
- local INTERFACE_SIDE = nil
- local INTERFACE_EXTRACT_DIRECTION = nil
- local MACHINE_COUNT = 0
- local REGISTRY = {}
- local SIDE_REGISTRY = {}
- local CURRENT_VERSION = "1.1"
- local THE_INTERFACE = nil
- local ACTIVE_ASSEMBLY = nil
- local PRINT_ASSEMBLY_TO_TERM = true
- local AppTimerId = 0
- local AppLastEventTime = 0
- local AppTimerInterval = 1
- local IsAppRunning = true
- local COLOURS = {
- white = 1,
- orange = 2,
- magenta = 4,
- lightBlue = 8,
- yellow = 16,
- lime = 32,
- pink = 64,
- gray = 128,
- lightGray = 256,
- cyan = 512,
- purple = 1024,
- blue = 2048,
- brown = 4096,
- green = 8192,
- red = 16384,
- black = 32768
- }
- function ClearTerminal()
- term.clear()
- term.setCursorPos(1, 1)
- end
- function SetupTimer(delay)
- AppTimerId = os.startTimer(delay)
- end
- function CanonicaliseSide(side)
- if (side == nil or #side < 3 or #side > 6) then
- return nil
- end
- local s = string.lower(side)
- if (s == "left" or s == "top" or s == "right" or s == "bottom" or s == "front" or s == "back") then
- return s
- end
- return nil
- end
- --region Utils
- function Colour_Int2String(num)
- if (num == colours.white) then return "white"
- elseif (num == colours.orange) then return "orange"
- elseif (num == colours.magenta) then return "magenta"
- elseif (num == colours.lightBlue) then return "lightblue"
- elseif (num == colours.yellow) then return "yellow"
- elseif (num == colours.lime) then return "lime"
- elseif (num == colours.pink) then return "pink"
- elseif (num == colours.gray) then return "gray"
- elseif (num == colours.lightGray) then return "lightgray"
- elseif (num == colours.cyan) then return "cyan"
- elseif (num == colours.purple) then return "purple"
- elseif (num == colours.blue) then return "blue"
- elseif (num == colours.brown) then return "brown"
- elseif (num == colours.green) then return "green"
- elseif (num == colours.red) then return "red"
- elseif (num == colours.black) then return "black"
- else return nil end
- end
- function Colour_String2Int(str)
- local s = string.lower(str)
- if (s == "white") then return colours.white
- elseif (s == "orange") then return colours.orange
- elseif (s == "magenta") then return colours.magenta
- elseif (s == "lightblue") then return colours.lightBlue
- elseif (s == "yellow") then return colours.yellow
- elseif (s == "lime") then return colours.lime
- elseif (s == "pink") then return colours.pink
- elseif (s == "gray") then return colours.gray
- elseif (s == "lightgray") then return colours.lightGray
- elseif (s == "cyan") then return colours.cyan
- elseif (s == "purple") then return colours.purple
- elseif (s == "blue") then return colours.blue
- elseif (s == "brown") then return colours.brown
- elseif (s == "green") then return colours.green
- elseif (s == "red") then return colours.red
- elseif (s == "black") then return colours.black
- else return nil end
- end
- function ParseColourString(input)
- if (input == nil) then
- print("Invalid empty input for colour")
- return nil
- end
- local theColour = Colour_String2Int(input)
- if (theColour == nil) then
- print("Invalid colour: " .. input)
- end
- return theColour
- end
- function CanonicaliseNSWE(dir)
- if (dir == nil or #dir < 2 or #dir > 5) then
- return nil
- end
- local s = string.lower(dir)
- if (s == "north" or s == "east" or s == "south" or s == "west" or s == "up" or s == "down") then
- return s
- end
- return nil
- end
- function SplitInputByCommaAsTable(input)
- local values = {}
- for word in input:gmatch("([^,]+)") do
- table.insert(values, word)
- end
- return values
- end
- function math.round(value)
- local floored = math.floor(value)
- local diff = value - floored
- if (diff >= 0.5) then
- return math.ceil(value)
- else
- return floored
- end
- end
- --endregion
- function DoExtraction(id, dmg, qty)
- if (THE_INTERFACE ~= nil and INTERFACE_EXTRACT_DIRECTION ~= nil) then
- local stack = {
- id = id,
- dmg = dmg,
- qty = qty
- }
- local count = THE_INTERFACE.extractItem(stack, INTERFACE_EXTRACT_DIRECTION)
- if (PRINT_ASSEMBLY_TO_TERM) then
- local w,h = term.getSize()
- local x,y = term.getCursorPos()
- term.setCursorPos(1, y)
- term.write(string.rep(" ", w))
- local msg = "[" .. math.floor(os.clock()) .. "] Extracted " .. count .. "/" .. qty .. " of " .. id .. ":" .. dmg
- term.setCursorPos(1, y)
- term.write(msg)
- end
- return count
- end
- return 0
- end
- function OnAutoProcessingStarted(doNotStartTimer)
- print("Auto processing started with " .. #REGISTRY .. " total records")
- print("Press ENTER to stop, and enable command inputs")
- local time = os.clock()
- for i, record in pairs(REGISTRY) do
- record.lastTick = nil
- end
- if (ACTIVE_ASSEMBLY ~= nil and ACTIVE_ASSEMBLY.timeUntilCompleted ~= nil) then
- local rec = ACTIVE_ASSEMBLY
- print("Still waiting machines have finished processing " .. rec.id .. ":" .. rec.dmg .. " (" .. math.floor((rec.timeUntilCompleted - time)) .. " secs remaining)")
- end
- if (doNotStartTimer ~= true) then
- SetupTimer(0)
- end
- end
- function ClearRegistry()
- REGISTRY = {}
- SIDE_REGISTRY = {}
- end
- ---Register an item to extract from the ME system into a chest for autoprocessing
- ---@param theSide string The rednet cable side, relative to the computer
- ---@param theColour integer The colour to listen to on the side
- ---@param itemId integer The item ID to extract from the ME
- ---@param itemDmg integer The damage of the item to extract
- ---@param maxStackSize integer The maximum stack size for this item (e.g. enderpears is 16, cobble is 64)
- ---@param assemblyTime number The amount of time it takes to make 1 item for 1 machine. This must be percise!
- function Register(theSide, theColour, itemId, itemDmg, maxStackSize, assemblyTime)
- local info = {
- side = theSide,
- colour = theColour,
- id = itemId,
- dmg = itemDmg,
- assemblyTime = assemblyTime,
- maxStackSize = maxStackSize,
- currentlyAssembled = 0,
- lastTick = nil,
- buffer = 0
- }
- table.insert(REGISTRY, info)
- local sideTable = SIDE_REGISTRY[theSide]
- if (not sideTable) then
- sideTable = {}
- SIDE_REGISTRY[theSide] = sideTable
- end
- table.insert(sideTable, info)
- return info
- end
- function OnActiveAssemblyCompletionDelayFinished(record)
- record.timeUntilCompleted = nil
- ACTIVE_ASSEMBLY = nil
- end
- --- Process the active record
- ---@param record table the record
- ---@param time number os time
- ---@return boolean True if the record has finished being processed, otherwise false to keep processing
- function ProcessActiveRecord(record, time)
- local input = rs.getBundledInput(record.side)
- if (colours.test(input, record.colour)) then
- if (record.timeUntilCompleted ~= nil) then
- record.timeUntilCompleted = nil
- print("Signal restored. Continuing assembly of " .. record.id .. ":" .. record.dmg)
- end
- local assemblyTime = record.assemblyTime
- local machineCount = MACHINE_COUNT
- local itemsPerSec = machineCount / assemblyTime
- local extractCount = 0
- if (record.lastTick ~= nil) then
- local count = (time - record.lastTick) * itemsPerSec
- extractCount = math.floor(count)
- local excess = count - extractCount
- if (excess > 0) then
- record.buffer = record.buffer + excess
- end
- local extra = math.floor(record.buffer)
- if (extra >= 1) then
- record.buffer = record.buffer - extra
- extractCount = extractCount + extra
- end
- else
- extractCount = itemsPerSec
- end
- local finalExtraction = DoExtraction(record.id, record.dmg, extractCount)
- if (finalExtraction < 1) then
- local assembleCount = math.min(record.currentlyAssembled, record.maxStackSize)
- local waitTime = math.floor((record.assemblyTime * assembleCount) / 1.2)
- record.timeUntilCompleted = time + waitTime
- print("Not enough items in ME system. Waiting for machines to clear (" .. waitTime .. " secs) before switching to next extractable item")
- record.WasStoppedForNotEnoughItems = true
- record.currentlyAssembled = 0
- else
- record.currentlyAssembled = record.currentlyAssembled + finalExtraction
- end
- elseif (record.timeUntilCompleted == nil) then
- local assembleCount = math.min(record.currentlyAssembled, record.maxStackSize)
- local waitTime = math.floor((record.assemblyTime * assembleCount) / 1.2)
- record.timeUntilCompleted = time + waitTime
- print("Assembly signal no longer active, Waiting for machines to clear (" .. waitTime .. " secs)")
- elseif (time > record.timeUntilCompleted) then
- OnActiveAssemblyCompletionDelayFinished(record)
- print("Assembly completed. Beginning next item on next app tick")
- record.lastTick = nil
- record.currentlyAssembled = 0
- return true
- end
- record.lastTick = time
- return false
- end
- function OnBeginProcessRecord(record, time)
- ACTIVE_ASSEMBLY = record
- OnAutoProcessingStarted()
- end
- function ProcessNextOrCurrent(time)
- if (ACTIVE_ASSEMBLY == nil or ProcessActiveRecord(ACTIVE_ASSEMBLY, time)) then
- -- find next item to start processing
- for side, values in pairs(SIDE_REGISTRY) do
- local input = rs.getBundledInput(side)
- for i, record in pairs(values) do
- if (colours.test(input, record.colour)) then
- if (record.WasStoppedForNotEnoughItems) then
- record.WasStoppedForNotEnoughItems = false
- else
- OnBeginProcessRecord(record, time)
- return
- end
- end
- end
- end
- end
- end
- function LoadConfigInfo()
- if (not fs.exists("config")) then
- return false
- end
- local hFile = fs.open("config", "r")
- if (hFile == nil) then
- print("Failed to open config file")
- return false
- end
- local isFail = false
- if (hFile.readLine() ~= CURRENT_VERSION) then
- isFail = true
- print("Config file is old, and cannot be used")
- end
- local itf_side
- local itf_pull_dir
- local machine_count
- if (not isFail) then
- local machines_raw = hFile.readLine()
- local machines = tonumber(machines_raw)
- if (machines == nil) then
- print("Invalid integer for the machine count in config: " .. machines_raw)
- isFail = true
- elseif (machines < 1) then
- print("Machine count was negative in config: " .. machines)
- isFail = true
- else
- machine_count = machines
- end
- local itf_side_raw = hFile.readLine()
- itf_side = CanonicaliseSide(itf_side_raw)
- if (itf_side == nil) then
- print("Invalid interface side in config: " .. itf_side_raw)
- isFail = true
- end
- if (not isFail) then
- local itf_pull_dir_raw = hFile.readLine()
- itf_pull_dir = CanonicaliseNSWE(itf_pull_dir_raw)
- if (itf_pull_dir == nil) then
- print("Invalid interface extract direction: " .. itf_pull_dir_raw)
- isFail = true
- end
- end
- if (not isFail) then
- ClearRegistry()
- -- left,colour_int,id,dmg,qtyRate,isAsync
- while (true) do
- local nextLine = hFile.readLine()
- if (nextLine == nil) then
- break
- end
- local values = SplitInputByCommaAsTable(nextLine)
- if (#values ~= 6) then
- print("Invalid line in config. Expected 6 elements in an item record, separated by commas")
- isFail = true
- break
- end
- local side = CanonicaliseSide(values[1])
- if (side == nil) then
- print("Invalid side in item record: " .. values[1])
- isFail = true
- break
- end
- local col = ParseColourString(values[2])
- if (col == nil) then
- isFail = true
- break
- end
- local id = tonumber(values[3])
- if (id == nil) then
- print("Invalid number for item id in item record: " .. values[3])
- isFail = true
- break
- end
- local dmg = tonumber(values[4])
- if (dmg == nil) then
- print("Invalid number for item damage in item record: " .. values[4])
- isFail = true
- break
- end
- local maxStackSize = tonumber(values[5])
- if (maxStackSize == nil) then
- print("Invalid integer for maximum stack size in item record: " .. values[5])
- isFail = true
- break
- end
- local qtyRate = tonumber(values[6])
- if (qtyRate == nil) then
- print("Invalid number for quantity rate in item record: " .. values[6])
- isFail = true
- break
- end
- Register(side, col, id, dmg, maxStackSize, qtyRate)
- end
- end
- end
- hFile.close()
- if (isFail) then
- ClearRegistry()
- INTERFACE_SIDE = nil
- INTERFACE_EXTRACT_DIRECTION = nil
- MACHINE_COUNT = 0
- return false
- else
- INTERFACE_SIDE = itf_side
- INTERFACE_EXTRACT_DIRECTION = itf_pull_dir
- MACHINE_COUNT = machine_count
- return true
- end
- end
- function SaveConfigInfo()
- local hFile = fs.open("config", "w")
- if (hFile == nil) then
- print("Failed to open config file for writing")
- return
- end
- hFile.writeLine(CURRENT_VERSION)
- hFile.writeLine(MACHINE_COUNT)
- if (INTERFACE_SIDE ~= nil) then
- hFile.writeLine(INTERFACE_SIDE)
- else
- hFile.writeLine("")
- end
- if (INTERFACE_EXTRACT_DIRECTION ~= nil) then
- hFile.writeLine(INTERFACE_EXTRACT_DIRECTION)
- else
- hFile.writeLine("")
- end
- for i, record in pairs(REGISTRY) do
- hFile.writeLine(record.side .. "," .. Colour_Int2String(record.colour) .. "," .. record.id .. "," .. record.dmg .. "," .. record.maxStackSize .. "," .. record.assemblyTime)
- end
- hFile.close()
- end
- function ReadInputWithMsg(...)
- for i = 1, arg.n, 1 do
- print(arg[i])
- end
- term.write(">")
- return read()
- end
- function ReadRecordFromTermAndRegister()
- local theSide = CanonicaliseSide(ReadInputWithMsg("Input rednet cable side (relative to computer)"))
- if (theSide == nil) then
- print("Invalid side")
- return nil
- end
- local theColour = ParseColourString(ReadInputWithMsg("Input the rednet colour"))
- if (theColour == nil) then
- return nil
- end
- local itemId = tonumber(ReadInputWithMsg("Input the Item ID to extract"))
- if (itemId == nil) then
- print("Invalid integer for item ID")
- return nil
- end
- local itemDamage = tonumber(ReadInputWithMsg("Input the item damage"))
- if (itemDamage == nil) then
- print("Invalid integer for item damage")
- return nil
- end
- local maxStackSize = tonumber(ReadInputWithMsg("Input the maximum stack size for this item", "(or for this item in the specific machine)"))
- if (maxStackSize == nil) then
- print("Invalid integer for max stack size")
- return nil
- end
- local assemblyTime = tonumber(ReadInputWithMsg("Input the assembly time for this item in 1 machine", "(cobble in a RS furnace takes 4s, so input 4)"))
- if (assemblyTime == nil) then
- print("Invalid number for assembly time")
- return nil
- end
- return Register(theSide, theColour, itemId, itemDamage, maxStackSize, assemblyTime)
- end
- function SetupConfig()
- local machines_raw = ReadInputWithMsg("Input the number of machines")
- local machines = tonumber(machines_raw)
- if (machines == nil) then
- print("Invalid integer for the machine count: " .. machines_raw)
- return false
- end
- local itf_side_raw = ReadInputWithMsg("Input the side that the interface is on", "(top, bottom, left, right, front or back)")
- local itf_side = CanonicaliseSide(itf_side_raw)
- if (itf_side == nil) then
- print("Invalid side: " .. itf_side_raw)
- return false
- end
- local pull_dir_raw = ReadInputWithMsg("Input world direction of the chest, relative to the ME Interface", "(up, down, north, east, south, or west)")
- local pull_dir = CanonicaliseNSWE(pull_dir_raw)
- if (pull_dir == nil) then
- print("Invalid direction: " .. pull_dir_raw)
- return false
- end
- MACHINE_COUNT = math.floor(machines)
- INTERFACE_SIDE = itf_side
- INTERFACE_EXTRACT_DIRECTION = pull_dir
- return true
- end
- local function OnApplicationEvent(time, eventType, p1, p2, p3, p4, p5)
- if (eventType == "timer" and p1 == AppTimerId) then
- SetupTimer(math.max(AppTimerInterval - (os.clock() - time), 0))
- ProcessNextOrCurrent(time)
- elseif (eventType == "key" and tonumber(p1) == 28) then
- print("Automatic processing stopped. Input command")
- print("(reset, add, clear records, list, start, exit):")
- local breakLoop
- repeat
- breakLoop = true
- term.write(">")
- local cmd = read()
- if (cmd == "reset") then
- ClearTerminal()
- while (not SetupConfig()) do end
- ClearTerminal()
- SaveConfigInfo()
- OnAutoProcessingStarted()
- elseif (cmd == "add") then
- repeat
- ReadRecordFromTermAndRegister()
- until ReadInputWithMsg("Do you want to add another? (yes/no)") ~= "yes"
- ClearTerminal()
- SaveConfigInfo()
- OnAutoProcessingStarted()
- elseif (cmd == "clear records") then
- ClearRegistry()
- if (ReadInputWithMsg("Do you want to add some records? (yes/no)") == "yes") then
- repeat
- ReadRecordFromTermAndRegister()
- until ReadInputWithMsg("Do you want to add another? (yes/no)") ~= "yes"
- end
- ClearTerminal()
- SaveConfigInfo()
- OnAutoProcessingStarted()
- elseif (cmd == "list") then
- print(MACHINE_COUNT .. " machines")
- for i, record in pairs(REGISTRY) do
- print(record.id .. ":" .. record.dmg .. " via '" .. record.side .. "->" .. Colour_Int2String(record.colour) .. "'. AT = " .. record.assemblyTime .. "s" .. " (" .. math.floor(MACHINE_COUNT / record.assemblyTime) .. " itm/s)")
- end
- print("Press any key to continue")
- read()
- OnAutoProcessingStarted()
- elseif (cmd == "skipwait") then
- if (ACTIVE_ASSEMBLY == nil or ACTIVE_ASSEMBLY.timeUntilCompleted == nil) then
- print("Not currently waiting for an assembly to complete")
- else
- OnActiveAssemblyCompletionDelayFinished(ACTIVE_ASSEMBLY)
- print("Cleared assembly wait time. The machines may still have items in them")
- end
- OnAutoProcessingStarted()
- elseif (cmd == "start") then
- ClearTerminal()
- OnAutoProcessingStarted()
- elseif (cmd == "exit") then
- print("Exiting application")
- IsAppRunning = false
- else
- print("Unknown command: " .. cmd)
- breakLoop = false
- end
- until breakLoop
- end
- end
- function AppMain()
- if (not LoadConfigInfo()) then
- while (not SetupConfig()) do end
- SaveConfigInfo()
- end
- while true do
- THE_INTERFACE = peripheral.wrap(INTERFACE_SIDE)
- if (THE_INTERFACE == nil) then
- print("No interface found. Retrying in 4 seconds...")
- sleep(4)
- else
- break
- end
- end
- OnAutoProcessingStarted(true)
- SetupTimer(AppTimerInterval)
- while IsAppRunning do
- local eType, p1, p2, p3, p4, p5 = os.pullEvent()
- local time = os.clock()
- OnApplicationEvent(time, eType, p1, p2, p3, p4, p5)
- AppLastEventTime = time
- end
- end
- AppMain()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement