ShadowNinja
2013-07-17 ee0765804c0a21deeb2f33c22ac1a36cb0db5f43
commit | author | age
ee5c6c 1 -- SWITCHING STATION
K 2 -- The switching station is the center of all power distribution on an electric network.
3 -- The station will collect all produced power from producers (PR) and batteries (BA)
4 -- and distribute it to receivers (RE) and depleted batteries (BA).
5 --
6 -- It works like this:
7 --  All PR,BA,RE nodes are indexed and tagged with the switching station.
8 -- The tagging is to allow more stations to be built without allowing a cheat
9 -- with duplicating power.
10 --  All the RE nodes are queried for their current EU demand. Those which are off
11 -- would require no or a small standby EU demand, while those which are on would
12 -- require more.
13 -- If the total demand is less than the available power they are all updated with the
14 -- demand number.
15 -- If any surplus exists from the PR nodes the batteries will be charged evenly with this.
16 -- If the total demand requires draw on the batteries they will be discharged evenly.
17 --
18 -- If the total demand is more than the available power all RE nodes will be shut down.
19 -- We have a brown-out situation.
20 --
21 -- Hence all the power distribution logic resides in this single node.
22 --
23 --  Nodes connected to the network will have one or more of these parameters as meta data:
24 --   <LV|MV|HV>_EU_supply : Exists for PR and BA node types. This is the EU value supplied by the node. Output
25 --   <LV|MV|HV>_EU_demand : Exists for RE and BA node types. This is the EU value the node requires to run. Output
26 --   <LV|MV|HV>_EU_input  : Exists for RE and BA node types. This is the actual EU value the network can give the node. Input
27 --
28 --  The reason the LV|MV|HV type is prepended toe meta data is because some machine could require several supplies to work.
29 --  This way the supplies are separated per network.
30 technic.DBG = 1
31 local dprint = technic.dprint
32
ee0765 33 minetest.register_craft({
S 34     output = "technic:switching_station",
35     recipe = {
36         {"default:steel_ingot",  "technic:lv_transformer", "default:steel_ingot"},
37         {"default:copper_ingot", "technic:lv_cable0",      "default:copper_ingot"},
38         {"default:steel_ingot",  "technic:lv_cable0",      "default:steel_ingot"}
39     }
40 })
ee5c6c 41
ee0765 42 minetest.register_node("technic:switching_station",{
S 43     description = "Switching Station",
44     tiles  = {"technic_water_mill_top_active.png", "technic_water_mill_top_active.png",
45                   "technic_water_mill_top_active.png", "technic_water_mill_top_active.png",
46               "technic_water_mill_top_active.png", "technic_water_mill_top_active.png"},
47     groups = {snappy=2, choppy=2, oddly_breakable_by_hand=2},
48     sounds = default.node_sound_wood_defaults(),
49     drawtype = "nodebox",
50     paramtype = "light",
51     node_box = {
52         type = "fixed",
53         fixed = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
54     },
55     on_construct = function(pos)
56         local meta = minetest.get_meta(pos)
57         meta:set_string("infotext", "Switching Station")
58     end,
59 })
ee5c6c 60
K 61 --------------------------------------------------
62 -- Functions to help the machines on the electrical network
63 --------------------------------------------------
64 -- This one provides a timeout for a node in case it was disconnected from the network
65 -- A node must be touched by the station continuously in order to function
ee0765 66 function technic.switching_station_timeout_count(pos, tier)
S 67     local meta = minetest.get_meta(pos)
68     timeout = meta:get_int(tier.."_EU_timeout")
69     if timeout == 0 then
70         meta:set_int(tier.."_EU_input", 0)
71     else
72         meta:set_int(tier.."_EU_timeout", timeout - 1)
73     end
74 end
ee5c6c 75
K 76 --------------------------------------------------
77 -- Functions to traverse the electrical network
78 --------------------------------------------------
79
80 -- Add a wire node to the LV/MV/HV network
ee0765 81 local add_new_cable_node = function(nodes, pos)
S 82     -- Ignore if the node has already been added
83     for i = 1, #nodes do
84         if pos.x == nodes[i].x and
85            pos.y == nodes[i].y and
86            pos.z == nodes[i].z then
87             return false
88         end
89     end
90     table.insert(nodes, {x=pos.x, y=pos.y, z=pos.z, visited=1})
91     return true
92 end
ee5c6c 93
K 94 -- Generic function to add found connected nodes to the right classification array
ee0765 95 local check_node_subp = function(PR_nodes, RE_nodes, BA_nodes, all_nodes, pos, machines, tier)
S 96     local meta = minetest.get_meta(pos)
97     local name = minetest.get_node(pos).name
98
99     if technic.is_tier_cable(name, tier) then
100         add_new_cable_node(all_nodes, pos)
101     elseif machines[name] then
102         --dprint(name.." is a "..machines[name])
103         if     machines[name] == technic.producer then
104             add_new_cable_node(PR_nodes, pos)
105         elseif machines[name] == technic.receiver then
106             add_new_cable_node(RE_nodes, pos)
107         elseif machines[name] == technic.battery then
108             add_new_cable_node(BA_nodes, pos)
109         end
110
111         meta:set_int(tier.."_EU_timeout", 2) -- Touch node
112     end
113 end
ee5c6c 114
K 115 -- Traverse a network given a list of machines and a cable type name
ee0765 116 local traverse_network = function(PR_nodes, RE_nodes, BA_nodes, all_nodes, i, machines, tier)
S 117     local pos = all_nodes[i]
118     local positions = {
119         {x=pos.x+1, y=pos.y,   z=pos.z},
120         {x=pos.x-1, y=pos.y,   z=pos.z},
121         {x=pos.x,   y=pos.y+1, z=pos.z},
122         {x=pos.x,   y=pos.y-1, z=pos.z},
123         {x=pos.x,   y=pos.y,   z=pos.z+1},
124         {x=pos.x,   y=pos.y,   z=pos.z-1}}
125     --print("ON")
126     for i, cur_pos in pairs(positions) do
127         check_node_subp(PR_nodes, RE_nodes, BA_nodes, all_nodes, cur_pos, machines, tier)
128     end
129 end
ee5c6c 130
ee0765 131 -----------------------------------------------
S 132 -- The action code for the switching station --
133 -----------------------------------------------
134 minetest.register_abm({
135     nodenames = {"technic:switching_station"},
ee5c6c 136     interval   = 1,
K 137     chance     = 1,
138     action = function(pos, node, active_object_count, active_object_count_wider)
ee0765 139         local meta             = minetest.get_meta(pos)
S 140         local meta1            = nil
141         local pos1             = {}
142         local PR_EU            = 0 -- EUs from PR nodes
143         local BA_PR_EU         = 0 -- EUs from BA nodes (discharching)
144         local BA_RE_EU         = 0 -- EUs to BA nodes (charging)
145         local RE_EU            = 0 -- EUs to RE nodes
ee5c6c 146
ee0765 147         local tier      = ""
S 148         local all_nodes = {}
149         local PR_nodes  = {}
150         local BA_nodes  = {} 
151         local RE_nodes  = {}
ee5c6c 152
ee0765 153         -- Which kind of network are we on:
S 154         pos1 = {x=pos.x, y=pos.y-1, z=pos.z}
155         all_nodes[1] = pos1
ee5c6c 156
ee0765 157         local name = minetest.get_node(pos1).name
S 158         local tier = technic.get_cable_tier(name)
159         if tier then
160             local i = 1
161             repeat
162                 traverse_network(PR_nodes, RE_nodes, BA_nodes, all_nodes,
163                         i, technic.machines[tier], tier)
164                 i = i + 1
165             until all_nodes[i] == nil
166         else
167             --dprint("Not connected to a network")
168             meta:set_string("infotext", "Switching Station - no network")
169             return
170         end
171         --dprint("nodes="..table.getn(all_nodes)
172         --        .." PR="..table.getn(PR_nodes)
173         --        .." BA="..table.getn(BA_nodes)
174         --        .." RE="..table.getn(RE_nodes))
ee5c6c 175
ee0765 176         -- Strings for the meta data
S 177         local eu_demand_str    = tier.."_EU_demand"
178         local eu_input_str     = tier.."_EU_input"
179         local eu_supply_str    = tier.."_EU_supply"
ee5c6c 180
ee0765 181         -- Get all the power from the PR nodes
S 182         local PR_eu_supply = 0 -- Total power
183         for _, pos1 in pairs(PR_nodes) do
184             meta1 = minetest.get_meta(pos1)
185             PR_eu_supply = PR_eu_supply + meta1:get_int(eu_supply_str)
186         end
187         --dprint("Total PR supply:"..PR_eu_supply)
ee5c6c 188
ee0765 189         -- Get all the demand from the RE nodes
S 190         local RE_eu_demand = 0
191         for _, pos1 in pairs(RE_nodes) do
192             meta1 = minetest.get_meta(pos1)
193             RE_eu_demand = RE_eu_demand + meta1:get_int(eu_demand_str)
194         end
195         --dprint("Total RE demand:"..RE_eu_demand)
ee5c6c 196
ee0765 197         -- Get all the power from the BA nodes
S 198         local BA_eu_supply = 0
199         for _, pos1 in pairs(BA_nodes) do
200             meta1 = minetest.get_meta(pos1)
201             BA_eu_supply = BA_eu_supply + meta1:get_int(eu_supply_str)
202         end
203         --dprint("Total BA supply:"..BA_eu_supply)
ee5c6c 204
ee0765 205         -- Get all the demand from the BA nodes
S 206         local BA_eu_demand = 0
207         for _, pos1 in pairs(BA_nodes) do
208             meta1 = minetest.get_meta(pos1)
209             BA_eu_demand = BA_eu_demand + meta1:get_int(eu_demand_str)
210         end
211         --dprint("Total BA demand:"..BA_eu_demand)
ee5c6c 212
ee0765 213         meta:set_string("infotext",
S 214                 "Switching Station. Supply: "..PR_eu_supply
215                 .." Demand: "..RE_eu_demand)
ee5c6c 216
ee0765 217         -- If the PR supply is enough for the RE demand supply them all
S 218         if PR_eu_supply >= RE_eu_demand then
219         --dprint("PR_eu_supply"..PR_eu_supply.." >= RE_eu_demand"..RE_eu_demand)
220             for _, pos1 in pairs(RE_nodes) do
221                 meta1 = minetest.get_meta(pos1)
222                 local eu_demand = meta1:get_int(eu_demand_str)
223                 meta1:set_int(eu_input_str, eu_demand)
224             end
225             -- We have a surplus, so distribute the rest equally to the BA nodes
226             -- Let's calculate the factor of the demand
227             PR_eu_supply = PR_eu_supply - RE_eu_demand
228             local charge_factor = 0 -- Assume all batteries fully charged
229             if BA_eu_demand > 0 then
230                 charge_factor = PR_eu_supply / BA_eu_demand
231             end
232             for n, pos1 in pairs(BA_nodes) do
233                 meta1 = minetest.get_meta(pos1)
234                 local eu_demand = meta1:get_int(eu_demand_str)
235                 meta1:set_int(eu_input_str, math.floor(eu_demand * charge_factor))
236                 --dprint("Charging battery:"..math.floor(eu_demand*charge_factor))
237             end
238             return
239         end
ee5c6c 240
ee0765 241         -- If the PR supply is not enough for the RE demand we will discharge the batteries too
S 242         if PR_eu_supply + BA_eu_supply >= RE_eu_demand then
243             --dprint("PR_eu_supply "..PR_eu_supply.."+BA_eu_supply "..BA_eu_supply.." >= RE_eu_demand"..RE_eu_demand)
244             for _, pos1 in pairs(RE_nodes) do
245                 meta1  = minetest.get_meta(pos1)
246                 local eu_demand = meta1:get_int(eu_demand_str)
247                 meta1:set_int(eu_input_str, eu_demand)
248             end
249             -- We have a deficit, so distribute to the BA nodes
250             -- Let's calculate the factor of the supply
251             local charge_factor = 0 -- Assume all batteries depleted
252             if BA_eu_supply > 0 then
253                 charge_factor = (PR_eu_supply - RE_eu_demand) / BA_eu_supply
254             end
255             for n,pos1 in pairs(BA_nodes) do
256                 meta1 = minetest.get_meta(pos1)
ee5c6c 257                 local eu_supply = meta1:get_int(eu_supply_str)
ee0765 258                 meta1:set_int(eu_input_str, math.floor(eu_supply * charge_factor))
S 259                 --dprint("Discharging battery:"..math.floor(eu_supply*charge_factor))
260             end
261             return
262         end
ee5c6c 263
ee0765 264         -- If the PR+BA supply is not enough for the RE demand: Power only the batteries
S 265         local charge_factor = 0 -- Assume all batteries fully charged
266         if BA_eu_demand > 0 then
267             charge_factor = PR_eu_supply / BA_eu_demand
268         end
269         for n, pos1 in pairs(BA_nodes) do
270             meta1 = minetest.get_meta(pos1)
271             local eu_demand = meta1:get_int(eu_demand_str)
272             meta1:set_int(eu_input_str, math.floor(eu_demand * charge_factor))
273         end
274         for n, pos1 in pairs(RE_nodes) do
275             meta1 = minetest.get_meta(pos1)
276             meta1:set_int(eu_input_str, 0)
277         end
ee5c6c 278     end,
K 279 })
ee0765 280
S 281 for tier, machines in pairs(technic.machines) do
282     -- SPECIAL will not be traversed
283     technic.register_machine(tier, "technic:switching_station", "SPECIAL")
284 end
285