2019-05-20 15:05:53 -07:00
|
|
|
--[[
|
|
|
|
|
|
|
|
TechAge
|
|
|
|
=======
|
|
|
|
|
|
|
|
Copyright (C) 2019 Joachim Stolberg
|
|
|
|
|
2019-08-22 11:49:47 -07:00
|
|
|
GPL v3
|
2019-05-20 15:05:53 -07:00
|
|
|
See LICENSE.txt for more information
|
|
|
|
|
|
|
|
Power distribution and consumption calculation
|
|
|
|
for any kind of power distribution network
|
|
|
|
|
|
|
|
]]--
|
|
|
|
|
|
|
|
-- for lazy programmers
|
|
|
|
local S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
|
|
|
local P = minetest.string_to_pos
|
|
|
|
local M = minetest.get_meta
|
2019-10-05 10:19:18 -07:00
|
|
|
local N = function(pos) return minetest.get_node(pos).name end
|
2019-09-08 12:54:34 -07:00
|
|
|
local D = techage.Debug
|
|
|
|
|
2019-05-20 15:05:53 -07:00
|
|
|
-- Techage Related Data
|
2019-10-06 15:09:39 -07:00
|
|
|
local PWR = function(pos) return (minetest.registered_nodes[techage.get_node_lvm(pos).name] or {}).power end
|
2019-05-20 15:05:53 -07:00
|
|
|
|
|
|
|
-- Used to determine the already passed nodes while power distribution
|
|
|
|
local Route = {}
|
2019-08-18 09:02:14 -07:00
|
|
|
local NumNodes = 0
|
2019-05-20 15:05:53 -07:00
|
|
|
|
2019-08-15 08:06:58 -07:00
|
|
|
techage.power = {}
|
|
|
|
|
2019-08-21 12:52:11 -07:00
|
|
|
local MAX_NUM_NODES = 1000
|
|
|
|
techage.MAX_NUM_NODES = MAX_NUM_NODES
|
2019-08-18 09:02:14 -07:00
|
|
|
|
2019-08-15 08:06:58 -07:00
|
|
|
-- Consumer States
|
|
|
|
local STOPPED = 1
|
|
|
|
local NOPOWER = 2
|
|
|
|
local RUNNING = 3
|
|
|
|
|
2019-08-16 14:44:11 -07:00
|
|
|
-------------------------------------------------- Migrate
|
2019-10-06 15:09:39 -07:00
|
|
|
local CRD = function(pos) return (minetest.registered_nodes[techage.get_node_lvm(pos).name] or {}).consumer end
|
2019-08-16 14:44:11 -07:00
|
|
|
local Consumer = {
|
|
|
|
["techage:streetlamp_off"] = 0,
|
|
|
|
["techage:streetlamp_on"] = 0.5,
|
|
|
|
["techage:industriallamp1_off"] = 0,
|
|
|
|
["techage:industriallamp1_on"] = 0.5,
|
|
|
|
["techage:industriallamp2_off"] = 0,
|
|
|
|
["techage:industriallamp2_on"] = 0.5,
|
|
|
|
["techage:industriallamp3_off"] = 0,
|
|
|
|
["techage:industriallamp3_on"] = 0.5,
|
|
|
|
["techage:simplelamp_off"] = 0,
|
|
|
|
["techage:simplelamp_on"] = 0.5,
|
|
|
|
["techage:ceilinglamp_off"] = 0,
|
|
|
|
["techage:ceilinglamp_on"] = 0.5,
|
|
|
|
["techage:ta2_autocrafter_pas"] = 0,
|
|
|
|
["techage:ta2_autocrafter_act"] = 4,
|
|
|
|
["techage:ta3_autocrafter_pas"] = 0,
|
|
|
|
["techage:ta3_autocrafter_act"] = 6,
|
|
|
|
["techage:ta2_electronic_fab_pas"] = 0,
|
|
|
|
["techage:ta2_electronic_fab_act"] = 8,
|
|
|
|
["techage:ta3_electronic_fab_pas"] = 0,
|
|
|
|
["techage:ta3_electronic_fab_act"] = 12,
|
|
|
|
["techage:ta2_gravelsieve_pas"] = 0,
|
|
|
|
["techage:ta2_gravelsieve_act"] = 3,
|
|
|
|
["techage:ta3_gravelsieve_pas"] = 0,
|
|
|
|
["techage:ta3_gravelsieve_act"] = 4,
|
|
|
|
["techage:ta2_grinder_pas"] = 0,
|
|
|
|
["techage:ta2_grinder_act"] = 4,
|
|
|
|
["techage:ta3_grinder_pas"] = 0,
|
|
|
|
["techage:ta3_grinder_act"] = 6,
|
|
|
|
["techage:ta2_rinser_pas"] = 0,
|
|
|
|
["techage:ta2_rinser_act"] = 3,
|
|
|
|
["techage:ta3_booster"] = 0,
|
|
|
|
["techage:ta3_booster_on"] = 3,
|
|
|
|
["techage:ta3_drillbox_pas"] = 0,
|
|
|
|
["techage:ta3_drillbox_act"] = 16,
|
|
|
|
["techage:ta3_pumpjack_pas"] = 0,
|
|
|
|
["techage:ta3_pumpjack_act"] = 16,
|
|
|
|
["techage:gearbox"] = 0,
|
|
|
|
["techage:gearbox_on"] = 1,
|
|
|
|
}
|
|
|
|
local Generator = {
|
|
|
|
["techage:t2_source"] = 20,
|
|
|
|
["techage:t3_source"] = 20,
|
|
|
|
["techage:t4_source"] = 20,
|
|
|
|
["techage:flywheel"] = 0,
|
|
|
|
["techage:flywheel_on"] = 25,
|
|
|
|
["techage:generator"] = 0,
|
|
|
|
["techage:generator_on"] = 80,
|
|
|
|
["techage:tiny_generator"] = 0,
|
|
|
|
["techage:tiny_generator_on"] = 12,
|
|
|
|
}
|
|
|
|
local Akku = {
|
|
|
|
["techage:ta3_akku"] = 10
|
|
|
|
}
|
|
|
|
|
2019-08-18 09:02:14 -07:00
|
|
|
local function migrate(pos, mem, node)
|
2019-08-17 06:02:55 -07:00
|
|
|
if mem.master_pos or mem.is_master ~= nil then
|
2019-08-18 09:02:14 -07:00
|
|
|
print("migrate", S(pos), node.name)
|
2019-08-17 06:02:55 -07:00
|
|
|
if mem.master_pos then
|
|
|
|
mem.pwr_master_pos = table.copy(mem.master_pos)
|
|
|
|
mem.master_pos = nil
|
|
|
|
end
|
2019-08-16 14:44:11 -07:00
|
|
|
mem.pwr_is_master = mem.is_master; mem.is_master = nil
|
|
|
|
mem.available1 = nil
|
|
|
|
mem.available2 = nil
|
|
|
|
mem.supply1 = nil
|
|
|
|
mem.supply2 = nil
|
|
|
|
mem.needed1 = nil
|
|
|
|
mem.needed2 = nil
|
|
|
|
mem.demand1 = nil
|
|
|
|
mem.demand2 = nil
|
|
|
|
mem.reserve = nil
|
|
|
|
mem.could_be_master = nil
|
|
|
|
mem.node_loaded = nil
|
|
|
|
|
|
|
|
mem.pwr_power_provided_cnt = 2
|
|
|
|
mem.pwr_node_alive_cnt = 4
|
|
|
|
|
2019-10-06 15:09:39 -07:00
|
|
|
local name = techage.get_node_lvm(pos).name
|
2019-08-16 14:44:11 -07:00
|
|
|
mem.pwr_needed = Consumer[name]
|
|
|
|
mem.pwr_available = Generator[name]
|
|
|
|
mem.pwr_could_provide = Akku[name]
|
|
|
|
mem.pwr_could_need = Akku[name]
|
|
|
|
|
|
|
|
if Consumer[name] then
|
|
|
|
if mem.techage_state then
|
|
|
|
if mem.techage_state == techage.STOPPED then
|
|
|
|
mem.pwr_state = STOPPED
|
2019-08-17 06:02:55 -07:00
|
|
|
elseif mem.techage_state == techage.NOPOWER or mem.techage_state == techage.RUNNING then
|
2019-08-16 14:44:11 -07:00
|
|
|
local crd = CRD(pos)
|
|
|
|
techage.power.consumer_start(pos, mem, crd.cycle_time, crd.power_consumption)
|
|
|
|
end
|
|
|
|
elseif mem.turned_on then
|
|
|
|
mem.pwr_state = RUNNING
|
|
|
|
elseif mem.pwr_needed then
|
|
|
|
mem.pwr_state = RUNNING
|
|
|
|
else
|
|
|
|
mem.pwr_state = STOPPED
|
|
|
|
end
|
2019-08-17 06:02:55 -07:00
|
|
|
if techage.in_list({"techage:ta2_electronic_fab_pas", "techage:ta2_electronic_fab_act", "techage:ta3_electronic_fab_pas", "techage:ta3_electronic_fab_act"}, name) then
|
|
|
|
mem.pwr_cycle_time = 6
|
|
|
|
elseif techage.in_list({"techage:ta3_drillbox_pas", "techage:ta3_drillbox_act"}, name) then
|
|
|
|
mem.pwr_cycle_time = 16
|
|
|
|
elseif techage.in_list({"techage:ta3_pumpjack_pas", "techage:ta3_pumpjack_act"}, name) then
|
|
|
|
mem.pwr_cycle_time = 8
|
|
|
|
else
|
|
|
|
mem.pwr_cycle_time = 4
|
|
|
|
end
|
2019-08-16 14:44:11 -07:00
|
|
|
elseif Generator[name] then
|
2019-08-18 09:02:14 -07:00
|
|
|
mem.pwr_cycle_time = 2
|
2019-08-16 14:44:11 -07:00
|
|
|
if mem.generating then
|
|
|
|
techage.power.generator_start(pos, mem, Generator[name])
|
|
|
|
else
|
|
|
|
techage.power.generator_stop(pos, mem)
|
|
|
|
end
|
2019-08-18 09:02:14 -07:00
|
|
|
elseif Akku[name] then
|
|
|
|
mem.pwr_cycle_time = 2
|
|
|
|
if mem.techage_state and mem.techage_state == techage.RUNNING then
|
|
|
|
mem.running = true
|
|
|
|
minetest.get_node_timer(pos):start(2)
|
|
|
|
techage.power.secondary_start(pos, mem, mem.pwr_could_provide, mem.pwr_could_need)
|
|
|
|
end
|
2019-08-16 14:44:11 -07:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2019-08-17 06:02:55 -07:00
|
|
|
|
|
|
|
local Nodenames={}
|
|
|
|
local n=0
|
|
|
|
|
|
|
|
for k,v in pairs(Consumer) do
|
|
|
|
n=n+1
|
|
|
|
Nodenames[n]=k
|
|
|
|
end
|
|
|
|
for k,v in pairs(Generator) do
|
|
|
|
n=n+1
|
|
|
|
Nodenames[n]=k
|
|
|
|
end
|
|
|
|
for k,v in pairs(Akku) do
|
|
|
|
n=n+1
|
|
|
|
Nodenames[n]=k
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
minetest.register_lbm({
|
|
|
|
label = "[techage] Power Conversion",
|
|
|
|
name = "techage:power",
|
|
|
|
nodenames = Nodenames,
|
|
|
|
run_at_every_load = true,
|
|
|
|
action = function(pos, node)
|
|
|
|
local mem = tubelib2.get_mem(pos)
|
2019-08-18 09:02:14 -07:00
|
|
|
migrate(pos, mem, node)
|
2019-08-17 06:02:55 -07:00
|
|
|
end
|
|
|
|
})
|
|
|
|
|
2019-08-16 14:44:11 -07:00
|
|
|
-------------------------------------------------- Migrate
|
2019-06-16 12:06:16 -07:00
|
|
|
|
2019-10-05 10:19:18 -07:00
|
|
|
local function connection_color(t)
|
2019-10-03 15:54:51 -07:00
|
|
|
local count = 0
|
|
|
|
for _ in pairs(t) do count = count + 1 end
|
|
|
|
if count == 0 then return count, "#FF0000" end
|
|
|
|
if count == 1 then return count, "#FFFF00" end
|
|
|
|
return count, "#00FF00"
|
|
|
|
end
|
|
|
|
|
2019-05-20 15:05:53 -07:00
|
|
|
local function pos_already_reached(pos)
|
|
|
|
local key = minetest.hash_node_position(pos)
|
2019-08-21 12:52:11 -07:00
|
|
|
if not Route[key] and NumNodes < MAX_NUM_NODES then
|
2019-05-20 15:05:53 -07:00
|
|
|
Route[key] = true
|
2019-08-18 09:02:14 -07:00
|
|
|
NumNodes = NumNodes + 1
|
2019-05-20 15:05:53 -07:00
|
|
|
return false
|
|
|
|
end
|
|
|
|
return true
|
|
|
|
end
|
2019-06-16 12:06:16 -07:00
|
|
|
|
2019-08-01 13:31:43 -07:00
|
|
|
local function min(val, max)
|
|
|
|
if val < 0 then return 0 end
|
|
|
|
if val > max then return max end
|
|
|
|
return val
|
|
|
|
end
|
|
|
|
|
2019-08-15 08:06:58 -07:00
|
|
|
local function accounting(pos, mem)
|
2019-08-16 14:44:11 -07:00
|
|
|
if mem.pwr_is_master then
|
|
|
|
-- calculate the primary and secondary supply and demand
|
|
|
|
mem.mst_supply1 = min(mem.mst_needed1 + mem.mst_needed2, mem.mst_available1)
|
|
|
|
mem.mst_demand1 = min(mem.mst_needed1, mem.mst_available1 + mem.mst_available2)
|
|
|
|
mem.mst_supply2 = min(mem.mst_demand1 - mem.mst_supply1, mem.mst_available2)
|
|
|
|
mem.mst_demand2 = min(mem.mst_supply1 - mem.mst_demand1, mem.mst_available1)
|
|
|
|
mem.mst_reserve = (mem.mst_available1 + mem.mst_available2) - mem.mst_needed1
|
2019-09-08 12:54:34 -07:00
|
|
|
if D.sts then D.dbg("needed = "..mem.mst_needed1.."/"..mem.mst_needed2..", available = "..mem.mst_available1.."/"..mem.mst_available2) end
|
|
|
|
if D.sts then D.dbg("supply = "..mem.mst_supply1.."/"..mem.mst_supply2..", demand = "..mem.mst_demand1.."/"..mem.mst_demand2..", reserve = "..mem.mst_reserve) end
|
2019-08-16 14:44:11 -07:00
|
|
|
end
|
2019-08-01 13:31:43 -07:00
|
|
|
end
|
|
|
|
|
2019-06-16 12:06:16 -07:00
|
|
|
local function connection_walk(pos, clbk)
|
2019-06-15 07:14:46 -07:00
|
|
|
local mem = tubelib2.get_mem(pos)
|
|
|
|
mem.interrupted_dirs = mem.interrupted_dirs or {}
|
2019-05-20 15:05:53 -07:00
|
|
|
if clbk then
|
2019-06-16 12:06:16 -07:00
|
|
|
clbk(pos, mem)
|
2019-05-20 15:05:53 -07:00
|
|
|
end
|
2019-06-15 07:14:46 -07:00
|
|
|
for out_dir,item in pairs(mem.connections or {}) do
|
|
|
|
if item.pos and not pos_already_reached(item.pos) and
|
|
|
|
not mem.interrupted_dirs[out_dir] then
|
2019-06-16 12:06:16 -07:00
|
|
|
connection_walk(item.pos, clbk)
|
2019-05-20 15:05:53 -07:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-10-05 10:19:18 -07:00
|
|
|
-- Comfort walk with abort condition and additional info
|
|
|
|
-- num_hops is for internal use only
|
|
|
|
-- clbk(pos, node, mem, num_hops, num_nodes)
|
|
|
|
local function limited_connection_walk(pos, clbk, num_hops)
|
|
|
|
num_hops = num_hops or 1
|
2019-10-02 10:33:39 -07:00
|
|
|
local mem = tubelib2.get_mem(pos)
|
|
|
|
mem.interrupted_dirs = mem.interrupted_dirs or {}
|
|
|
|
if clbk then
|
2019-10-05 10:19:18 -07:00
|
|
|
local node = techage.get_node_lvm(pos)
|
|
|
|
if clbk(pos, node, mem, num_hops, NumNodes) then return true end
|
2019-10-02 10:33:39 -07:00
|
|
|
end
|
2019-10-05 10:19:18 -07:00
|
|
|
num_hops = num_hops + 1
|
2019-10-02 10:33:39 -07:00
|
|
|
for out_dir,item in pairs(mem.connections or {}) do
|
|
|
|
if item.pos and not pos_already_reached(item.pos) and
|
|
|
|
not mem.interrupted_dirs[out_dir] then
|
2019-10-05 10:19:18 -07:00
|
|
|
limited_connection_walk(item.pos, clbk, num_hops)
|
2019-10-02 10:33:39 -07:00
|
|
|
end
|
|
|
|
end
|
2019-10-05 10:19:18 -07:00
|
|
|
return false
|
2019-10-02 10:33:39 -07:00
|
|
|
end
|
|
|
|
|
2019-08-17 04:33:46 -07:00
|
|
|
-- if no power available
|
2019-08-15 08:06:58 -07:00
|
|
|
local function consumer_turn_off(pos, mem)
|
|
|
|
local pwr = PWR(pos)
|
2019-09-08 12:54:34 -07:00
|
|
|
if D.pwr then D.dbg("consumer_turn_off") end
|
2019-08-15 08:06:58 -07:00
|
|
|
if pwr and pwr.on_nopower then
|
|
|
|
pwr.on_nopower(pos, mem)
|
|
|
|
end
|
|
|
|
mem.pwr_state = NOPOWER
|
2019-08-17 04:33:46 -07:00
|
|
|
mem.pwr_power_provided_cnt = -1
|
2019-08-15 08:06:58 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
local function consumer_turn_on(pos, mem)
|
|
|
|
local pwr = PWR(pos)
|
2019-09-08 12:54:34 -07:00
|
|
|
if D.pwr then D.dbg("consumer_turn_on") end
|
2019-08-15 08:06:58 -07:00
|
|
|
if pwr and pwr.on_power then
|
|
|
|
pwr.on_power(pos, mem)
|
|
|
|
end
|
|
|
|
mem.pwr_state = RUNNING
|
2019-08-16 02:13:30 -07:00
|
|
|
-- to avoid consumer starvation
|
|
|
|
mem.pwr_node_alive_cnt = (mem.pwr_cycle_time or 2)/2 + 1
|
2019-08-15 08:06:58 -07:00
|
|
|
end
|
2019-06-16 12:06:16 -07:00
|
|
|
|
|
|
|
-- determine one "generating" node as master (largest hash number)
|
|
|
|
local function determine_master(pos)
|
2019-05-20 15:05:53 -07:00
|
|
|
Route = {}
|
2019-08-18 09:02:14 -07:00
|
|
|
NumNodes = 0
|
2019-05-20 15:05:53 -07:00
|
|
|
pos_already_reached(pos)
|
2019-06-16 12:06:16 -07:00
|
|
|
local hash = 0
|
|
|
|
local master = nil
|
|
|
|
connection_walk(pos, function(pos, mem)
|
2019-08-15 08:06:58 -07:00
|
|
|
if (mem.pwr_node_alive_cnt or 0) >= 0 and
|
2019-08-16 14:44:11 -07:00
|
|
|
((mem.pwr_available or 0) > 0 or
|
|
|
|
(mem.pwr_available2 or 0) > 0) then -- active generator?
|
2019-08-15 08:06:58 -07:00
|
|
|
|
|
|
|
local new = minetest.hash_node_position(pos)
|
|
|
|
if hash <= new then
|
|
|
|
hash = new
|
|
|
|
master = pos
|
2019-06-16 12:06:16 -07:00
|
|
|
end
|
2019-08-15 08:06:58 -07:00
|
|
|
end
|
|
|
|
end)
|
2019-06-16 12:06:16 -07:00
|
|
|
return master
|
|
|
|
end
|
|
|
|
|
|
|
|
-- store master position on all network nodes
|
|
|
|
local function store_master(pos, master_pos)
|
2019-05-20 15:05:53 -07:00
|
|
|
Route = {}
|
2019-08-18 09:02:14 -07:00
|
|
|
NumNodes = 0
|
2019-05-20 15:05:53 -07:00
|
|
|
pos_already_reached(pos)
|
2019-06-16 12:06:16 -07:00
|
|
|
connection_walk(pos, function(pos, mem)
|
2019-08-15 08:06:58 -07:00
|
|
|
mem.pwr_master_pos = master_pos
|
|
|
|
mem.pwr_is_master = false
|
2019-06-16 12:06:16 -07:00
|
|
|
end)
|
2019-05-20 15:05:53 -07:00
|
|
|
end
|
|
|
|
|
2019-08-15 08:06:58 -07:00
|
|
|
local function handle_generator(mst_mem, mem, pos, power_available)
|
|
|
|
-- for next cycle
|
2019-08-17 06:02:55 -07:00
|
|
|
mst_mem.mst_available1 = (mst_mem.mst_available1 or 0) + power_available
|
2019-08-15 08:06:58 -07:00
|
|
|
-- current cycle
|
|
|
|
mst_mem.mst_supply1 = mst_mem.mst_supply1 or 0
|
|
|
|
if mst_mem.mst_supply1 < power_available then
|
|
|
|
mem.pwr_provided = mst_mem.mst_supply1
|
|
|
|
mst_mem.mst_supply1 = 0
|
|
|
|
else
|
|
|
|
mst_mem.mst_supply1 = mst_mem.mst_supply1 - power_available
|
|
|
|
mem.pwr_provided = power_available
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
local function handle_consumer(mst_mem, mem, pos, power_needed)
|
|
|
|
if mem.pwr_state == NOPOWER then
|
|
|
|
-- for next cycle
|
2019-08-17 06:02:55 -07:00
|
|
|
mst_mem.mst_needed1 = (mst_mem.mst_needed1 or 0) + power_needed
|
2019-08-15 08:06:58 -07:00
|
|
|
-- current cycle
|
2019-08-16 14:44:11 -07:00
|
|
|
if (mst_mem.mst_demand1 or 0) >= power_needed then
|
2019-08-15 08:06:58 -07:00
|
|
|
mst_mem.mst_demand1 = (mst_mem.mst_demand1 or 0) - power_needed
|
2019-09-08 12:54:34 -07:00
|
|
|
if D.sts then D.dbg("consumer_turn_on: mst_demand = "..mst_mem.mst_demand1..", node = "..minetest.get_node(pos).name) end
|
2019-08-15 08:06:58 -07:00
|
|
|
consumer_turn_on(pos, mem)
|
|
|
|
end
|
|
|
|
elseif mem.pwr_state == RUNNING then
|
|
|
|
-- for next cycle
|
2019-08-18 09:02:14 -07:00
|
|
|
mst_mem.mst_needed1 = (mst_mem.mst_needed1 or 0) + power_needed
|
2019-08-15 08:06:58 -07:00
|
|
|
-- current cycle
|
2019-08-16 14:44:11 -07:00
|
|
|
if (mst_mem.mst_demand1 or 0) >= power_needed then
|
|
|
|
mst_mem.mst_demand1 = (mst_mem.mst_demand1 or 0) - power_needed
|
|
|
|
-- small consumer like lamps are allowed to "use" the reserve
|
|
|
|
elseif power_needed <= 2 and (mst_mem.mst_reserve or 0) >= power_needed then
|
|
|
|
mst_mem.mst_reserve = (mst_mem.mst_reserve or 0) - power_needed
|
|
|
|
else -- no power available
|
2019-08-15 08:06:58 -07:00
|
|
|
mst_mem.mst_demand1 = 0
|
2019-09-08 12:54:34 -07:00
|
|
|
if D.sts then D.dbg("consumer_turn_off: mst_demand = "..mst_mem.mst_demand1..", node = "..minetest.get_node(pos).name) end
|
2019-08-15 08:06:58 -07:00
|
|
|
consumer_turn_off(pos, mem)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
local function handle_secondary(mst_mem, mem, pos, provides, needed)
|
|
|
|
-- for next cycle
|
|
|
|
mst_mem.mst_available2 = (mst_mem.mst_available2 or 0) + provides
|
|
|
|
mst_mem.mst_needed2 = (mst_mem.mst_needed2 or 0) + needed
|
|
|
|
-- check as generator
|
|
|
|
mst_mem.mst_supply2 = mst_mem.mst_supply2 or 0
|
|
|
|
mst_mem.mst_demand2 = mst_mem.mst_demand2 or 0
|
|
|
|
if mst_mem.mst_supply2 > 0 then
|
|
|
|
local val = math.min(provides, mst_mem.mst_supply2)
|
|
|
|
mst_mem.mst_supply2 = mst_mem.mst_supply2 - val
|
|
|
|
mem.pwr_provided = val
|
|
|
|
-- check as consumer
|
|
|
|
elseif mst_mem.mst_demand2 > 0 then
|
|
|
|
local val = math.min(needed, mst_mem.mst_demand2)
|
|
|
|
mst_mem.mst_demand2 = mst_mem.mst_demand2 - val
|
|
|
|
mem.pwr_provided = -val
|
|
|
|
else
|
|
|
|
mem.pwr_provided = 0
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
2019-08-16 02:13:30 -07:00
|
|
|
local function trigger_nodes(mst_pos, mst_mem, dec)
|
2019-06-20 05:01:37 -07:00
|
|
|
Route = {}
|
2019-08-18 09:02:14 -07:00
|
|
|
NumNodes = 0
|
2019-08-15 08:06:58 -07:00
|
|
|
pos_already_reached(mst_pos)
|
|
|
|
connection_walk(mst_pos, function(pos, mem)
|
2019-08-16 02:13:30 -07:00
|
|
|
mem.pwr_node_alive_cnt = (mem.pwr_node_alive_cnt or 1) - dec
|
2019-08-15 08:06:58 -07:00
|
|
|
mem.pwr_power_provided_cnt = 2
|
2019-09-08 12:54:34 -07:00
|
|
|
if D.pwr then D.dbg("trigger_nodes", minetest.get_node(pos).name, mem.pwr_node_alive_cnt, mem.pwr_available2 or mem.pwr_available or mem.pwr_needed) end
|
2019-09-17 13:28:39 -07:00
|
|
|
if mem.pwr_node_alive_cnt >= 0 or mem.pwr_state == NOPOWER then
|
2019-08-15 08:06:58 -07:00
|
|
|
if mem.pwr_available then
|
|
|
|
handle_generator(mst_mem, mem, pos, mem.pwr_available)
|
|
|
|
elseif mem.pwr_needed then
|
|
|
|
handle_consumer(mst_mem, mem, pos, mem.pwr_needed)
|
|
|
|
elseif mem.pwr_available2 then
|
|
|
|
handle_secondary(mst_mem, mem, pos, mem.pwr_available2, mem.pwr_needed2)
|
2019-06-20 05:01:37 -07:00
|
|
|
end
|
2019-08-15 08:06:58 -07:00
|
|
|
end
|
|
|
|
end)
|
2019-06-20 05:01:37 -07:00
|
|
|
end
|
2019-06-16 12:06:16 -07:00
|
|
|
|
2019-08-16 14:44:11 -07:00
|
|
|
local function turn_off_nodes(mst_pos)
|
|
|
|
Route = {}
|
2019-08-18 09:02:14 -07:00
|
|
|
NumNodes = 0
|
2019-08-16 14:44:11 -07:00
|
|
|
pos_already_reached(mst_pos)
|
2019-09-08 12:54:34 -07:00
|
|
|
if D.pwr then D.dbg("turn_off_nodes") end
|
2019-08-16 14:44:11 -07:00
|
|
|
connection_walk(mst_pos, function(pos, mem)
|
|
|
|
if (mem.pwr_node_alive_cnt or -1) >= 0 then
|
|
|
|
if mem.pwr_needed then
|
|
|
|
consumer_turn_off(pos, mem)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
2019-08-15 08:06:58 -07:00
|
|
|
local function determine_new_master(pos, mem)
|
|
|
|
local was_master = mem.pwr_is_master
|
|
|
|
mem.pwr_is_master = false
|
2019-06-16 12:06:16 -07:00
|
|
|
local mpos = determine_master(pos)
|
|
|
|
store_master(pos, mpos)
|
|
|
|
if mpos then
|
2019-08-18 09:02:14 -07:00
|
|
|
local mmem = tubelib2.get_mem(mpos)
|
|
|
|
mmem.pwr_is_master = true
|
|
|
|
mmem.mst_num_nodes = NumNodes
|
2019-08-15 08:06:58 -07:00
|
|
|
elseif was_master then -- no master any more
|
|
|
|
-- delete data
|
2019-08-16 02:13:30 -07:00
|
|
|
mem.mst_supply1 = 0
|
|
|
|
mem.mst_supply2 = 0
|
|
|
|
mem.mst_reserve = 0
|
2019-06-16 12:06:16 -07:00
|
|
|
end
|
2019-08-16 14:44:11 -07:00
|
|
|
return was_master or mem.pwr_is_master
|
2019-08-15 08:06:58 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
-- called from master position
|
2019-08-16 02:13:30 -07:00
|
|
|
local function power_distribution(pos, mem, dec)
|
2019-09-08 12:54:34 -07:00
|
|
|
if D.pwr then D.dbg("power_distribution") end
|
2019-08-16 14:44:11 -07:00
|
|
|
if mem.pwr_is_master then
|
|
|
|
mem.mst_needed1 = 0
|
|
|
|
mem.mst_needed2 = 0
|
|
|
|
mem.mst_available1 = 0
|
|
|
|
mem.mst_available2 = 0
|
|
|
|
end
|
2019-08-16 02:13:30 -07:00
|
|
|
trigger_nodes(pos, mem, dec or 0)
|
2019-08-15 08:06:58 -07:00
|
|
|
accounting(pos, mem)
|
2019-06-16 12:06:16 -07:00
|
|
|
end
|
|
|
|
|
2019-05-20 15:05:53 -07:00
|
|
|
--
|
2019-08-15 08:06:58 -07:00
|
|
|
-- Power API functions
|
2019-05-20 15:05:53 -07:00
|
|
|
--
|
|
|
|
|
2019-08-15 08:06:58 -07:00
|
|
|
-- To be called for each network change from any node
|
|
|
|
function techage.power.network_changed(pos, mem)
|
2019-09-08 12:54:34 -07:00
|
|
|
if D.pwr then D.dbg("network_changed") end
|
2019-08-15 08:06:58 -07:00
|
|
|
mem.pwr_node_alive_cnt = (mem.pwr_cycle_time or 2)/2 + 1
|
|
|
|
if determine_new_master(pos, mem) then -- new master?
|
|
|
|
power_distribution(pos, mem)
|
2019-08-16 14:44:11 -07:00
|
|
|
elseif not mem.pwr_master_pos then -- no master?
|
|
|
|
turn_off_nodes(pos)
|
2019-08-15 08:06:58 -07:00
|
|
|
elseif not next(mem.connections) then -- isolated?
|
|
|
|
if mem.pwr_needed then -- consumer?
|
|
|
|
consumer_turn_off(pos, mem)
|
2019-05-21 13:04:05 -07:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-08-15 08:06:58 -07:00
|
|
|
--
|
|
|
|
-- Generator related functions
|
|
|
|
--
|
|
|
|
function techage.power.generator_start(pos, mem, available)
|
|
|
|
mem.pwr_node_alive_cnt = 2
|
|
|
|
mem.pwr_cycle_time = 2
|
|
|
|
mem.pwr_available = available
|
|
|
|
if determine_new_master(pos, mem) then -- new master
|
|
|
|
power_distribution(pos, mem)
|
2019-07-12 14:27:29 -07:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-10-03 15:54:51 -07:00
|
|
|
function techage.power.generator_update(pos, mem, available)
|
|
|
|
mem.pwr_available = available
|
|
|
|
end
|
|
|
|
|
2019-08-15 08:06:58 -07:00
|
|
|
function techage.power.generator_stop(pos, mem)
|
|
|
|
mem.pwr_node_alive_cnt = 0
|
|
|
|
mem.pwr_available = 0
|
|
|
|
if determine_new_master(pos, mem) then -- last available master
|
|
|
|
power_distribution(pos, mem)
|
2019-08-01 13:31:43 -07:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-08-15 08:06:58 -07:00
|
|
|
function techage.power.generator_alive(pos, mem)
|
|
|
|
mem.pwr_node_alive_cnt = 2
|
|
|
|
if mem.pwr_is_master then
|
2019-08-16 02:13:30 -07:00
|
|
|
power_distribution(pos, mem, 1)
|
2019-06-16 12:06:16 -07:00
|
|
|
end
|
2019-08-17 16:24:34 -07:00
|
|
|
return mem.pwr_provided or 0
|
2019-06-16 12:06:16 -07:00
|
|
|
end
|
|
|
|
|
2019-08-15 08:06:58 -07:00
|
|
|
--
|
|
|
|
-- Consumer related functions
|
|
|
|
--
|
2019-08-17 04:33:46 -07:00
|
|
|
-- this is more a try to start, the start will be performed by consumer_turn_on()
|
2019-08-15 08:06:58 -07:00
|
|
|
function techage.power.consumer_start(pos, mem, cycle_time, needed)
|
|
|
|
mem.pwr_cycle_time = cycle_time
|
2019-08-17 04:33:46 -07:00
|
|
|
mem.pwr_power_provided_cnt = 0 -- must be zero!
|
2019-08-15 08:06:58 -07:00
|
|
|
mem.pwr_node_alive_cnt = 2
|
|
|
|
mem.pwr_needed = needed
|
|
|
|
mem.pwr_state = NOPOWER
|
|
|
|
end
|
2019-06-16 12:06:16 -07:00
|
|
|
|
2019-08-15 08:06:58 -07:00
|
|
|
function techage.power.consumer_stop(pos, mem)
|
|
|
|
mem.pwr_node_alive_cnt = 0
|
|
|
|
mem.pwr_needed = 0
|
|
|
|
mem.pwr_state = STOPPED
|
2019-06-16 12:06:16 -07:00
|
|
|
end
|
|
|
|
|
2019-08-16 14:44:11 -07:00
|
|
|
function techage.power.consumer_alive(pos, mem)
|
2019-09-08 12:54:34 -07:00
|
|
|
if D.pwr then D.dbg("consumer_alive", mem.pwr_power_provided_cnt, mem.pwr_cycle_time) end
|
2019-08-16 14:44:11 -07:00
|
|
|
mem.pwr_node_alive_cnt = (mem.pwr_cycle_time or 2)/2 + 1
|
2019-08-18 09:02:14 -07:00
|
|
|
mem.pwr_power_provided_cnt = (mem.pwr_power_provided_cnt or 0) - 1
|
2019-08-16 14:44:11 -07:00
|
|
|
if mem.pwr_power_provided_cnt < 0 and mem.pwr_state == RUNNING then
|
|
|
|
consumer_turn_off(pos, mem)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-08-15 08:06:58 -07:00
|
|
|
-- Lamp related function to speed up the turn on
|
|
|
|
function techage.power.power_available(pos, mem, needed)
|
|
|
|
if mem.pwr_master_pos and (mem.pwr_power_provided_cnt or 0) > 0 then
|
|
|
|
mem = tubelib2.get_mem(mem.pwr_master_pos)
|
2019-08-16 14:44:11 -07:00
|
|
|
if (mem.mst_reserve or 0) >= needed then
|
|
|
|
mem.mst_reserve = (mem.mst_reserve or 0) - needed
|
2019-08-15 08:06:58 -07:00
|
|
|
return true
|
2019-06-17 10:57:08 -07:00
|
|
|
end
|
2019-06-16 12:06:16 -07:00
|
|
|
end
|
2019-06-17 10:57:08 -07:00
|
|
|
return false
|
2019-06-16 12:06:16 -07:00
|
|
|
end
|
2019-08-15 08:06:58 -07:00
|
|
|
|
2019-10-05 10:19:18 -07:00
|
|
|
-- Debug info, used by junction boxes
|
2019-08-15 08:06:58 -07:00
|
|
|
function techage.power.power_accounting(pos, mem)
|
2019-09-13 14:01:55 -07:00
|
|
|
if mem.pwr_master_pos and (mem.pwr_power_provided_cnt or 0) > 0 then
|
|
|
|
mem.pwr_power_provided_cnt = (mem.pwr_power_provided_cnt or 0) - 1
|
2019-08-15 08:06:58 -07:00
|
|
|
mem = tubelib2.get_mem(mem.pwr_master_pos)
|
|
|
|
return {
|
2019-08-18 09:02:14 -07:00
|
|
|
prim_available = mem.mst_available1 or 0,
|
|
|
|
sec_available = mem.mst_available2 or 0,
|
|
|
|
prim_needed = mem.mst_needed1 or 0,
|
|
|
|
sec_needed = mem.mst_needed2 or 0,
|
|
|
|
num_nodes = mem.mst_num_nodes or 0,
|
2019-08-15 08:06:58 -07:00
|
|
|
}
|
2019-08-07 13:26:38 -07:00
|
|
|
end
|
2019-08-15 08:06:58 -07:00
|
|
|
return {
|
|
|
|
prim_available = 0,
|
|
|
|
sec_available = 0,
|
|
|
|
prim_needed = 0,
|
|
|
|
sec_needed = 0,
|
2019-08-18 09:02:14 -07:00
|
|
|
num_nodes = 0,
|
2019-08-15 08:06:58 -07:00
|
|
|
}
|
2019-08-07 13:26:38 -07:00
|
|
|
end
|
|
|
|
|
2019-08-15 08:06:58 -07:00
|
|
|
--
|
|
|
|
-- Akku related functions
|
|
|
|
--
|
|
|
|
function techage.power.secondary_start(pos, mem, available, needed)
|
|
|
|
mem.pwr_node_alive_cnt = 2
|
|
|
|
mem.pwr_could_provide = available
|
|
|
|
mem.pwr_could_need = needed
|
|
|
|
if determine_new_master(pos, mem) then -- new master
|
|
|
|
power_distribution(pos, mem)
|
|
|
|
end
|
2019-05-20 15:05:53 -07:00
|
|
|
end
|
|
|
|
|
2019-08-15 08:06:58 -07:00
|
|
|
function techage.power.secondary_stop(pos, mem)
|
|
|
|
mem.pwr_node_alive_cnt = 0
|
|
|
|
mem.pwr_could_provide = 0
|
|
|
|
mem.pwr_could_need = 0
|
2019-10-06 15:09:39 -07:00
|
|
|
mem.pwr_needed2 = 0
|
2019-08-15 08:06:58 -07:00
|
|
|
if determine_new_master(pos, mem) then -- last available master
|
|
|
|
power_distribution(pos, mem)
|
2019-06-16 12:06:16 -07:00
|
|
|
end
|
2019-08-15 08:06:58 -07:00
|
|
|
end
|
2019-06-16 12:06:16 -07:00
|
|
|
|
2019-08-15 08:06:58 -07:00
|
|
|
function techage.power.secondary_alive(pos, mem, capa_curr, capa_max)
|
2019-09-08 12:54:34 -07:00
|
|
|
if D.pwr then D.dbg("secondary_alive") end
|
2019-08-15 08:06:58 -07:00
|
|
|
if capa_curr >= capa_max then
|
|
|
|
mem.pwr_available2, mem.pwr_needed2 = mem.pwr_could_provide, 0 -- can provide only
|
|
|
|
elseif capa_curr <= 0 then
|
|
|
|
mem.pwr_available2, mem.pwr_needed2 = 0, mem.pwr_could_need -- can deliver only
|
2019-06-16 12:06:16 -07:00
|
|
|
else
|
2019-08-15 08:06:58 -07:00
|
|
|
mem.pwr_available2, mem.pwr_needed2 = mem.pwr_could_provide, mem.pwr_could_need
|
2019-06-16 12:06:16 -07:00
|
|
|
end
|
2019-08-15 08:06:58 -07:00
|
|
|
|
|
|
|
mem.pwr_node_alive_cnt = 2
|
|
|
|
if mem.pwr_is_master then
|
2019-09-08 12:54:34 -07:00
|
|
|
if D.pwr then D.dbg("secondary_alive is master") end
|
2019-08-16 02:13:30 -07:00
|
|
|
power_distribution(pos, mem, 1)
|
2019-08-15 08:06:58 -07:00
|
|
|
end
|
2019-10-06 15:09:39 -07:00
|
|
|
if mem.pwr_master_pos then
|
|
|
|
return mem.pwr_provided or 0
|
|
|
|
end
|
|
|
|
return 0
|
2019-09-29 14:37:35 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
--
|
|
|
|
-- Read the current power value from all connected devices (used for solar cells)
|
2019-10-03 15:54:51 -07:00
|
|
|
-- Only used by the solar inverter to collect the power of all solar cells.
|
|
|
|
-- Only one inverter per network is allowed. Therefore, we have to check,
|
|
|
|
-- if additional inverters are in the network.
|
|
|
|
-- Function returns in addition the number of found inverters.
|
|
|
|
function techage.power.get_power(start_pos, inverter)
|
2019-09-29 14:37:35 -07:00
|
|
|
Route = {}
|
|
|
|
NumNodes = 0
|
|
|
|
pos_already_reached(start_pos)
|
|
|
|
local sum = 0
|
2019-10-03 15:54:51 -07:00
|
|
|
local num_inverter = 0
|
2019-09-29 14:37:35 -07:00
|
|
|
connection_walk(start_pos, function(pos, mem)
|
|
|
|
local pwr = PWR(pos)
|
|
|
|
if pwr and pwr.on_getpower then
|
|
|
|
sum = sum + pwr.on_getpower(pos, mem)
|
2019-10-03 15:54:51 -07:00
|
|
|
else
|
2019-10-06 15:09:39 -07:00
|
|
|
local node = techage.get_node_lvm(pos)
|
2019-10-03 15:54:51 -07:00
|
|
|
if node.name == inverter then
|
|
|
|
num_inverter = num_inverter + 1
|
|
|
|
end
|
2019-09-29 14:37:35 -07:00
|
|
|
end
|
|
|
|
end)
|
2019-10-03 15:54:51 -07:00
|
|
|
return sum, num_inverter
|
2019-09-29 14:37:35 -07:00
|
|
|
end
|
|
|
|
|
2019-10-02 10:33:39 -07:00
|
|
|
function techage.power.power_network_available(start_pos)
|
|
|
|
Route = {}
|
|
|
|
NumNodes = 0
|
|
|
|
pos_already_reached(start_pos)
|
2019-10-05 10:19:18 -07:00
|
|
|
limited_connection_walk(start_pos, function(pos, node, mem, _, num_nodes)
|
|
|
|
return num_nodes > 2
|
2019-10-02 10:33:39 -07:00
|
|
|
end)
|
2019-10-05 10:19:18 -07:00
|
|
|
return NumNodes > 2
|
2019-10-02 10:33:39 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
function techage.power.mark_nodes(name, start_pos)
|
|
|
|
Route = {}
|
|
|
|
NumNodes = 0
|
|
|
|
pos_already_reached(start_pos)
|
|
|
|
techage.unmark_position(name)
|
2019-10-05 10:19:18 -07:00
|
|
|
limited_connection_walk(start_pos, function(pos, node, mem, num_hops, num_nodes)
|
|
|
|
local num, color = connection_color(mem.connections or {})
|
2019-10-03 15:54:51 -07:00
|
|
|
techage.mark_position(name, pos, S(pos).." : "..num, color)
|
2019-10-05 10:19:18 -07:00
|
|
|
return num_hops >= 3 or num_nodes >= 100
|
2019-10-02 10:33:39 -07:00
|
|
|
end)
|
|
|
|
end
|
2019-10-05 10:19:18 -07:00
|
|
|
|
|
|
|
-- Network walk with callback for each node:
|
|
|
|
--
|
|
|
|
-- limited_connection_walk(pos, clbk) --> num_hops, num_nodes
|
|
|
|
--
|
|
|
|
-- called function: clbk(pos, node, mem, num_hops, num_nodes)
|
|
|
|
-- walk will be arborted if function returns true
|
|
|
|
function techage.power.limited_connection_walk(pos, clbk)
|
|
|
|
Route = {}
|
|
|
|
NumNodes = 0
|
|
|
|
pos_already_reached(pos)
|
|
|
|
limited_connection_walk(pos, clbk)
|
|
|
|
return NumNodes
|
|
|
|
end
|
|
|
|
|
|
|
|
--local function test()
|
|
|
|
-- print("test")
|
|
|
|
-- local cnt = 0
|
|
|
|
-- tubelib2.walk_over_all(function(pos, mem)
|
|
|
|
-- local node = techage.get_node_lvm(pos)
|
|
|
|
-- print(S(pos), node.name)
|
|
|
|
-- cnt = cnt + 1
|
|
|
|
-- end)
|
|
|
|
-- print("cnt = "..cnt)
|
|
|
|
--end
|
|
|
|
|
|
|
|
--minetest.after(1, test)
|