Skip to content

Instantly share code, notes, and snippets.

@AntaeusNar
Last active January 20, 2020 22:12
Show Gist options
  • Save AntaeusNar/f4b08d8377fa1907caa1f7273d359fde to your computer and use it in GitHub Desktop.
Save AntaeusNar/f4b08d8377fa1907caa1f7273d359fde to your computer and use it in GitHub Desktop.
The Start of a lua script for Opencomputers to control power flow through the base.
--AntaeusNar, 2020
--Based on power-monitor.lua by SuPeRMiNoR2, 2015 https://github.com/OpenPrograms/SuPeRMiNoR2-Programs/tree/master/power-monitor
local version = "0.1.0"
-- Requires
local component = require("component")
local sides = require("sides")
local term = require("term")
local gpu = component.gpu
local superlib = require("superlib")
local event = require("event")
local keyboard = require("keyboard")
-- Aliases
local pad = superlib.pad
local round = superlib.round
local comma = superlib.format_comma
local pretty = superlib.pretty
-- Config/Init
local char_space = string.byte(" ")
local running = true
local total_last_amount = false
local current_rate = 0
local loop_speed = 1.5
local lower_pwr_limit = .001
local upper_pwr_limit = .9999
local usage_table = {}
local supported_types = {
{id="tile_thermalexpansion_cell_basic_name", type=2, name="Leadstone Cell"},
{id="tile_thermalexpansion_cell_hardened_name", type=2, name="Hardened Cell"},
{id="tile_thermalexpansion_cell_reinforced_name", type=2, name="Redstone Cell"},
{id="tile_thermalexpansion_cell_resonant_name", type=2, name="Resonant Cell"},
{id="mfsu", type=1, name="MFSU"},
{id="mfe", type=1, name="MFE"},
{id="cesu", type=1, name="CESU"},
{id="batbox", type=1, name="BatBox"},
{id="capacitor_bank", type=2, name="Capacitor Bank"},
{id="basic_energy_cube", type=3, name="Basic Energy Cube"},
{id="advanced_energy_cube", type=3, name="Advanced Energy Cube"},
{id="elite_energy_cube", type=3, name="Elite Energy Cube"},
{id="ultimate_energy_cube", type=3, name="Ultimate Energy Cube"},
{id="creative_energy_cube", type=3, name="Creative Energy Cube"},
{id="induction_matrix", type=3, name="Induction Matrix"},
{id="energy_device", type=2, name="Energy Cell"}
}
-- Meta Tables for functions
local myEventHandlers = setmetatable({}, {__index = function() return unknownEvent end })
-- Functions
local function exitProgram() -- Exits the program Cleanly
running = false
component.redstone.setOutput(sides.bottom, 0)
component.redstone.setOutput(sides.top, 0)
component.redstone.setOutput(sides.north, 0)
component.redstone.setOutput(sides.south, 0)
term.clear()
os.execute("clear")
event.ignore("key_up", handleEvent)
os.exit()
end
local function unknownEvent() -- Do Nothing if an event is heard but isn't relevant
end
function myEventHandlers.key_up(adress, char, code, playerName)
if (char == string.byte("x")) then
exitProgram()
end
if (char == string.byte("p")) then
primary_unit = primary_unit + 1
if primary_unit > #powerdb then
primary_unit = 1
end
end
if (char == string.byte("s")) then
secondary_unit = secondary_unit + 1
if secondary_unit > #powerdb then
secondary_unit = 1
end
end
if (char == string.byte("t")) then
tertiary_unit = tertiary_unit + 1
if tertiary_unit > #powerdb then
tertiary_unit = 1
end
end
if (char == string.byte("q")) then
quaternary_unit = quaternary_unit + 1
if quaternary_unit > #powerdb then
quaternary_unit = 1
end
end
end
function handleEvent(eventID, ...) -- The main event handler as function to separate eventID from the remaining arguments
if (eventID) then -- can be nil if no event was pulled for some time
myEventHandlers[eventID](...) -- call the appropriate event handler with all remaining arguments
end
end
local function time_calc(rate, stored, unit) -- Depletion Timeline Calc
unit = unit or "Minecraft Days"
if unit == "Minecraft Days" then
rtime = superlib.round(stored/(rate * 20 * 60 * 20), 1)
end
return rtime
end
local function trend(current_rate) -- More Complex historical data tracking
local calc_rate = 0
local tab_size = 0
if #usage_table < 1024 then
table.insert(usage_table, current_rate)
elseif #usage_table > 1024 then
usage_table = table.remove(usage_table, 1)
table.insert(usage_table, current_rate)
end
if #usage_table > 1 then
for k, v in ipairs(usage_table) do
tab_size = tab_size + 1
calc_rate = calc_rate + v
end
calc_rate = calc_rate/tab_size
else
calc_rate = current_rate
end
return calc_rate
end
local function percent_gen_db(powerdb, uid) -- Simple find the % available
storedPower = powerdb[uid]["stored"]
powerCapacity = powerdb[uid]["capacity"]
return superlib.pgen(storedPower, powerCapacity, 2) .. "%"
end
local function readCapacity(proxy, ltype) -- Read the Max capacity of a block
capacity = 0
if ltype == 1 then --For IC2
capacity = proxy.getCapacity()
end
if ltype == 2 then --For TE and older mek blocks
capacity = proxy.getMaxEnergyStored()
end
if ltype == 3 then --For newer mekanism blocks
capacity = proxy.getMaxEnergy()
end
return capacity
end
local function readStored(proxy, ltype) -- Read Current Stored Power
stored = 0
if ltype == 1 then
stored = proxy.getStored()
end
if ltype == 2 then
stored = proxy.getEnergyStored()
end
if ltype == 3 then
stored = proxy.getEnergy()
end
return stored
end
local function getPower() -- Power Getting
local total_stored = 0
local powerdb = {}
for uid in pairs(mlist) do
proxy = mlist[uid]["proxy"]
ltype = mlist[uid]["type"]
lname = mlist[uid]["name"]
c = mlist[uid]["capacity"]
s = readStored(proxy, ltype)
if s > c then --Stupid IC2 Bug, full ic2 blocks read over their capacity sometimes
s = c
end
total_stored = total_stored + s
powerdb[uid] = {capacity=c, stored=s, name=lname}
end
return powerdb, total_stored
end
local function calculate_rate(last, current, loop_speed) -- Calculates the flow of RF in or out
rate = current - last
ticks_per_loop = loop_speed * 20
rate = rate/ticks_per_loop
rate = superlib.round(rate, 1)
return rate
end
local function checkPower(powerdb, loop_speed) -- Power Checking
local total_stored = 0
local powerdb = powerdb
for lid in ipairs(powerdb) do
proxy = mlist[lid]["proxy"]
ltype = mlist[lid]["type"]
lname = mlist[lid]["name"]
c = mlist[lid]["capacity"]
old_s = powerdb[lid]["stored"]
s = readStored(proxy, ltype)
if s > c then
s = c
end
total_stored = total_stored + s
rate = calculate_rate(old_s, s, loop_speed)
powerdb[lid] = {capacity=c, stored=s, name=lname, rate=rate}
end
return powerdb, total_stored
end
local function rsctl(unit, side) -- redstone control
if powerdb[unit]["stored"]/powerdb[unit]["capacity"] < lower_pwr_limit then
component.redstone.setOutput(side, 15)
end
if powerdb[unit]["stored"]/powerdb[unit]["capacity"] > upper_pwr_limit then
component.redstone.setOutput(side, 0)
end
end
local function systemctl() -- system control
rsctl(primary_unit, sides.bottom)
if secondary_unit ~= 0 then
rsctl(secondary_unit, sides.top)
end
if tertiary_unit ~= 0 then
rsctl(tertiary_unit, sides.north)
end
if quaternary_unit ~= 0 then
rsctl(quaternary_unit, sides.south)
end
end
local function text_gen(unit)
days_plusminus = time_calc(powerdb[unit]["rate"], powerdb[unit]["stored"])
if days_plusminus == math.huge then
text = string.format("Power Stable, unit standing by")
elseif powerdb[unit]["rate"] > 0 then
text = string.format("Fully charged in %s days.", days_plusminus)
else
text = string.format("Depleted in %s days.", math.abs(days_plusminus))
end
return text
end
local function userctl() -- user input/display
print(string.format("(P)rimary unit is : %s. Output is Down. %s", primary_unit, text_gen(primary_unit)))
if secondary_unit ~= 0 then
print(string.format("(S)econdary Unit is: %s. Output is Up. %s", secondary_unit, text_gen(secondary_unit)))
end
if tertiary_unit ~= 0 then
print(string.format("(T)ertiary Unit is: %s Output is North. %s", tertiary_unit, text_gen(tertiary_unit)))
end
if quaternary_unit ~= 0 then
print(string.format("(Q)uaternary Unit is: %s Output is South. %s", quaternary_unit, text_gen(quaternary_unit)))
end
end
local function scan() -- Block hunting
local unit_id = 1
mlist = {}
total_capacity = 0
max_cap = 0
primary_unit = 1
secondary_unit = 0
tertiary_unit = 0
quaternary_unit =0
for address, ctype in component.list() do -- Looks at all connected and addressed components
for sindex, stype in pairs(supported_types) do
if stype["id"] == ctype then -- Test to see if the component is on our list of supported types
t = component.proxy(address)
ltype = stype["type"]
name = stype["name"]
c = readCapacity(t, ltype)
mlist[unit_id] = {address=address, proxy=t, type=ltype, name=name, capacity=c}
unit_id = unit_id + 1
total_capacity = total_capacity + c
end
end
end
total_units = unit_id - 1
if total_units == 2 then
secondary_unit = 2
elseif total_units == 3 then
secondary_unit = 2
tertiary_unit = 3
elseif total_units >= 3 then
secondary_unit = 2
tertiary_unit = 3
quaternary_unit = 4
end
return mlist, total_capacity, total_units
end
-- Main Startup
term.clear()
print("Loading AntaeusNar's Power Control.")
print("Version: "..version)
print("Scanning for energy storage units")
mlist, total_capacity, total_units = scan() -- Inital component scan and inventory
print("Found ".. total_units .. " storage unit[s]")
print("Total capacity detected: "..pretty(total_capacity))
print("Press x to close the program")
event.listen("key_up", handleEvent) -- register handleEvent to be called on key_up
component.redstone.setOutput(sides.bottom, 0) -- turn off leftover outputs
powerdb, total_stored = getPower() -- Inital Power Scan
os.sleep(1)
--Begin main loop
while running do
-- Re-Calculation Section
success, temp_powerdb, temp_total_stored = pcall(checkPower, powerdb, loop_speed) --If this fails, that means a power storage device was removed, so I rescan the list of devices.
if success == true then
powerdb = temp_powerdb
total_stored = temp_total_stored
end
if success == false then
print("Something changed, rescanning all units.")
mlist, total_capacity, total_units = scan()
powerdb, total_stored = getPower()
end
if total_units == 0 then
total = 0
else
total = superlib.pgen(total_stored, total_capacity, 2)
end
if total_last_amount == false then
total_last_amount = total_stored
end
current_rate = calculate_rate(total_last_amount, total_stored, loop_speed)
calced_rate = trend(current_rate)
total_last_amount = total_stored
powerdata = {{"ID", "Capacity", "Stored", "Level", "Type", "RF/t", "Address"}}
for lid in pairs(powerdb) do
table.insert(powerdata, {lid, pretty(powerdb[lid]["capacity"]), pretty(powerdb[lid]["stored"]), percent_gen_db(powerdb, lid), powerdb[lid]["name"], powerdb[lid]["rate"], mlist[lid]["address"]})
end
-- Display Section
term.clear()
if #powerdata > 1 then
print(string.format("Storage Total: %s%%, Total Flow: %s(%s) RF/t, Full Depletion projected in %s(%s) Minecraft Days", total, pretty(current_rate), pretty(calced_rate), math.abs(time_calc(current_rate, total_stored)), math.abs(time_calc(calced_rate, total_stored))))
superlib.rendertable(powerdata)
print("")
end
-- Control Section
systemctl()
userctl()
print("Press x to shutdown")
os.sleep(loop_speed)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment