Last active
January 20, 2020 22:12
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--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