Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --[[
- IC2 fluid reactor control
- Automatically run an IC2 fluid reactor, shutting down when operation is unsafe or components depleted. Can even
- replace depleted components automatically using an attached inventory!
- Basic settings
- reactor.signal_out - side to emit redstone to activate reactor, defaults to "back"
- reactor.signal_in - side to detect redstone to manually en/disable, defaults to "front"
- reactor.interval - interval (in seconds) to run safety checks, defaults to 0.5
- reactor.heat_threshold - heat percent to automatically shut down reactor, defaults to 60
- Advanced settings
- reactor.layout - optional layout code to ensure slots are appropriately filled
- reactor.items_in - optional peripheral name for inventory to automatically resupply reactor from (also needs reactor.layout)
- reactor.items_out - optional peripheral name for inventory to automatically remove depleted
- components, defaults to same as reactor.items_in (also needs reactor.layout)
- ]]
- --[[ Config ]]
- local signalOut = settings.get("reactor.signal_out", "back")
- local signalIn = settings.get("reactor.signal_in", "front")
- local interval = settings.get("reactor.interval", 0.5)
- local heatThreshold = settings.get("reactor.heat_threshold", 60) / 100
- local layoutCode = settings.get("reactor.layout")
- local itemsInName = settings.get("reactor.items_in")
- local itemsOutName = settings.get("reactor.items_out", itemsInName)
- --[[ Utility functions/constants ]]
- local function contains(tbl, value)
- for k, v in pairs(tbl) do
- if v == value then return true end
- end
- return false
- end
- -- lookup table for simulator IDs to part names, GT parts not included
- local simulatorLookup = {
- "ic2:uranium_fuel_rod",
- "ic2:dual_uranium_fuel_rod",
- "ic2:quad_uranium_fuel_rod",
- "ic2:mox_fuel_rod",
- "ic2:dual_mox_fuel_rod",
- "ic2:quad_mox_fuel_rod",
- "ic2:neutron_reflector",
- "ic2:thick_neutron_reflector",
- "ic2:heat_vent",
- "ic2:advanced_heat_vent",
- "ic2:reactor_heat_vent",
- "ic2:component_heat_vent",
- "ic2:overclocked_heat_vent",
- "ic2:heat_storage",
- "ic2:tri_heat_storage",
- "ic2:hex_heat_storage",
- "ic2:heat_exchanger",
- "ic2:advanced_heat_exchanger",
- "ic2:reactor_heat_exchanger",
- "ic2:component_heat_exchanger",
- "ic2:plating",
- "ic2:heat_plating",
- "ic2:containment:plating",
- "ic2:rsh_condensator",
- "ic2:lzh_condensator",
- [35] = "ic2:iridium_reflector"
- }
- -- fuel times - item name pattern -> fuel time in seconds
- local fuelTimes = {
- ["uranium_fuel_rod"] = 20000,
- ["mox_fuel_rod"] = 10000
- }
- local function formatTime(s)
- local h = math.floor(s / 3600)
- s = s - h * 3600
- local m = math.floor(s / 60)
- s = s - m * 60
- return ("%02d:%02d:%02d"):format(h, m, s)
- end
- --[[ Initialization ]]
- redstone.setOutput(signalOut, false) -- ensure reactor is shut down
- -- allow tile entities to initialize properly on world load
- if os.clock() < 10 then
- sleep(5)
- end
- -- open all wireless modems
- peripheral.find("modem", function(name, wrapped)
- if wrapped.isWireless() then
- rednet.open(name)
- end
- end)
- local hatch = assert(peripheral.find("ic2:reactor_access_hatch"), "Access hatch not found")
- local core = assert(hatch.getReactorCore(), "Reactor core inaccessible")
- local fluid = assert(peripheral.find("ic2:reactor_fluid_port"), "Fluid port not found")
- local monitor = peripheral.find("monitor")
- if monitor then
- monitor.setCursorBlink(false)
- monitor.setTextScale(2)
- else
- term.setCursorBlink(false)
- end
- local itemsIn = peripheral.wrap(itemsInName or "")
- local itemsOut = peripheral.wrap(itemsOutName or "")
- assert(itemsIn or not itemsInName, "Item input not found")
- assert(itemsOut or not itemsOutName, "Item output not found")
- if not layoutCode then
- printError("Warning - layout code not specified, inventory check and automation disabled")
- elseif not (itemsIn and itemsOut) then
- printError("Warning - item input/output not specified, automation disabled")
- else
- -- ensure reactor can transfer to/from item input/output
- local locations = hatch.getTransferLocations()
- assert(contains(locations, itemsInName), "No connection between reactor and " .. itemsInName)
- assert(contains(locations, itemsOutName), "No connection between reactor and " .. itemsOutName)
- end
- local function assertEqual(a, b, message)
- assert(a == b, (message or "Expected %d, got %d"):format(a, b))
- end
- -- parse a layout code into a table of slot -> expected item name
- local function parseLayout(code)
- assertEqual(code:len(), 54 * 2, "Invalid layout code length - got %d, expected %d")
- local out = {}
- for slot = 1, 54 do
- local chunk = code:sub((2 * slot) - 1, 2 * slot)
- local id = assert(tonumber(chunk, 16), ("Invalid chunk in layout code: %s in slot %d"):format(chunk, slot))
- if id ~= 0 then
- out[slot] = assert(simulatorLookup[id], "Unrecognized component ID: " .. id)
- else
- out[slot] = false
- end
- end
- return out
- end
- local layout = parseLayout(layoutCode)
- --[[ Main functions ]]
- -- verify the temperature is safe
- local function checkTemperature(coreState)
- return coreState.heat / coreState.maxHeat <= heatThreshold
- end
- -- verify that cold coolant > 40% and hot coolant < 40%
- local function checkCoolant(tanks)
- local hot, cold = 0, 0
- for i, tank in ipairs(tanks) do
- if tank.id == "ic2:ic2hot_coolant" then
- hot = tank.amount / tank.capacity
- elseif tank.id == "ic2:ic2coolant" then
- cold = tank.amount / tank.capacity
- end
- end
- return hot < 0.4 and cold > 0.4
- end
- -- get fuel time in seconds
- local function getFuelTime(inv)
- local time, rods = math.huge, 0
- for slot, data in pairs(inv) do
- for pattern, duration in pairs(fuelTimes) do
- if data.name:match(pattern) then
- time = math.min(time, 1 + duration * (1 - hatch.getItemMeta(slot).durability))
- rods = rods + 1
- end
- end
- end
- return rods == 0 and 0 or time
- end
- -- verify reactor layout is correct
- local function checkLayout(inv, fix)
- if not layout then return true end
- if fix and not (itemsIn and itemsOut) then return true end
- for slot, expected in pairs(layout) do
- if expected then
- if inv[slot] and inv[slot].name ~= expected then
- -- wrong item present
- if fix then
- local moved = hatch.pushItems(itemsOutName, slot)
- if moved ~= inv[slot].count then
- -- push failed
- return false, slot
- else
- inv[slot] = nil
- end
- else
- return false, slot
- end
- end
- if not inv[slot] then
- -- item missing
- if fix then
- -- find appropriate item in input
- local fromSlot
- for s, v in pairs(itemsIn.list()) do
- if v.name == expected then
- fromSlot = s
- break
- end
- end
- if fromSlot then
- -- found appropriate item
- local moved = hatch.pullItems(itemsInName, fromSlot, 1, slot)
- if moved < 1 then
- -- pull failed
- return false, slot
- else
- -- full rescan
- inv = hatch.list()
- end
- else
- return false, slot
- end
- else
- return false, slot
- end
- end
- else
- if inv[slot] then
- -- item present, but shouldn't be
- if fix then
- local moved = hatch.pushItems(itemsOutName, slot)
- if moved ~= inv[slot].count then
- -- push failed
- return false, slot
- else
- inv[slot] = nil
- end
- else
- return false, slot
- end
- end
- end
- end
- return true
- end
- local oldTerm = term.redirect(monitor or term.current())
- local ok, err = pcall(function()
- while true do
- sleep(interval)
- local coreState = core.getMetadata().reactor
- local tanks = fluid.getTanks("north")
- local inv = hatch.list()
- local shouldRun = redstone.getInput(signalIn)
- local fuelTime = getFuelTime(inv)
- local tempOK = checkTemperature(coreState)
- local coolantOK = checkCoolant(tanks)
- local layoutOK, badSlot = checkLayout(inv)
- local active = false
- local message
- if not shouldRun then
- message = "Offline"
- elseif not tempOK then
- message = "Overheat"
- elseif not coolantOK then
- message = "Coolant"
- elseif not layoutOK then
- message = "Slot #" .. badSlot
- elseif fuelTime <= 0 then
- message = "No fuel"
- else
- active = true
- message = "Online"
- end
- redstone.setOutput(signalOut, active)
- term.setCursorPos(1, 1)
- term.clear()
- term.write(message)
- if active then
- term.setCursorPos(1, 2)
- term.write(formatTime(fuelTime))
- end
- rednet.broadcast({ active = active, seconds = fuelTime, message = message }, "reactor")
- if shouldRun and itemsIn and itemsOut and not layoutOK then
- -- try to fix inv
- checkLayout(inv, true)
- end
- end
- end)
- redstone.setOutput(signalOut, false)
- term.setCursorPos(1, 1)
- term.clear()
- term.redirect(oldTerm)
- if not ok then printError(err) end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement