RealBadAngel
2013-07-25 d8ec3f8bbee5c3c489e4c5740fc8c84449eb86c3
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
33 minetest.register_craft(
34    {
35       output = 'technic:switching_station 1',
36       recipe = {
37      {'technic:lv_transformer', 'technic:mv_transformer', 'technic:hv_transformer'},
38      {'technic:lv_transformer', 'technic:mv_transformer', 'technic:hv_transformer'},
39      {'technic:lv_cable',       'technic:mv_cable',       'technic:hv_cable'},
40       }
41    })
42
43 minetest.register_node(
44    "technic:switching_station",
45    {description = "Switching Station",
9770be 46     tiles  = {"technic_water_mill_top_active.png", "technic_water_mill_top_active.png", "technic_water_mill_top_active.png",
K 47           "technic_water_mill_top_active.png", "technic_water_mill_top_active.png", "technic_water_mill_top_active.png"},
ee5c6c 48     groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2},
K 49     sounds = default.node_sound_wood_defaults(),
50     drawtype = "nodebox",
51     paramtype = "light",
52     is_ground_content = true,
53     node_box = {
54      type = "fixed",
55        fixed = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
56     },
57     selection_box = {
58        type = "fixed",
59        fixed = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
60     },
61     on_construct = function(pos)
62               local meta = minetest.env:get_meta(pos)
63               meta:set_string("infotext", "Switching Station")
64 --            minetest.chat_send_player(puncher:get_player_name(), "Switching station constructed. Punch the station to shut down the network.");
65 --            meta:set_int("active", 1)
66            end,
67 --      on_punch = function(pos, node, puncher)
68 --            local meta   = minetest.env:get_meta(pos)
69 --            local active = meta:get_int("active")
70 --            if active == 1 then
71 --               meta:set_int("active", 0)
72 --               minetest.chat_send_player(puncher:get_player_name(), "Electrical network shut down. Punch again to turn it on.");
73 --            else
74 --               meta:set_int("active", 1)
75 --               minetest.chat_send_player(puncher:get_player_name(), "Electrical network turned on. Punch again to shut it down.");
76 --            end
77 --         end
78  })
79
80 --------------------------------------------------
81 -- Functions to help the machines on the electrical network
82 --------------------------------------------------
83 -- This one provides a timeout for a node in case it was disconnected from the network
84 -- A node must be touched by the station continuously in order to function
85 technic.switching_station_timeout_count = function(pos, machine_tier)
86                          local meta = minetest.env:get_meta(pos)
87                          timeout =  meta:get_int(machine_tier.."_EU_timeout")
88                          --print("Counting timeout "..timeout)
89                          if timeout == 0 then
90                         --print("OFF")
91                         meta:set_int(machine_tier.."_EU_input", 0)
92                          else
93                         --print("ON")
94                         meta:set_int(machine_tier.."_EU_timeout", timeout-1)
95                          end
96                       end
97
98 --------------------------------------------------
99 -- Functions to traverse the electrical network
100 --------------------------------------------------
101
102 -- Add a wire node to the LV/MV/HV network
103 local add_new_cable_node = function(nodes,pos)
104                   local i = 1
105                   repeat
106                  if nodes[i]==nil then break end
107                  if pos.x==nodes[i].x and pos.y==nodes[i].y and pos.z==nodes[i].z then return false end
108                  i=i+1
109                   until false
110                   nodes[i] = {x=pos.x, y=pos.y, z=pos.z, visited=1} -- copy position
111                   return true
112                end
113
114 -- Generic function to add found connected nodes to the right classification array
115 local check_node_subp = function(PR_nodes,RE_nodes,BA_nodes,all_nodes,pos,machines,cablename)
116                local meta = minetest.env:get_meta(pos)
117                local name = minetest.env:get_node(pos).name
118                if meta:get_float(cablename)==1 then
119                   add_new_cable_node(all_nodes,pos)
120                elseif machines[name] then
121                   --dprint(name.." is a "..machines[name])
122                   if     machines[name] == "PR" then
123                  add_new_cable_node(PR_nodes,pos)
124                   elseif machines[name] == "RE" then
125                  add_new_cable_node(RE_nodes,pos)
126                   elseif machines[name] == "BA" then
127                  add_new_cable_node(BA_nodes,pos)
128                   end
129                   if cablename == "cablelike" then
130                  meta:set_int("LV_EU_timeout", 2) -- Touch node
131                   elseif cablename == "mv_cablelike" then
132                  meta:set_int("MV_EU_timeout", 2) -- Touch node
133                   elseif cablename == "hv_cablelike" then
134                  meta:set_int("HV_EU_timeout", 2) -- Touch node
135                   end
136                end
137             end
138
139 -- Traverse a network given a list of machines and a cable type name
140 local traverse_network = function(PR_nodes,RE_nodes,BA_nodes,all_nodes, i, machines, cablename)
141                 local pos = {x=all_nodes[i].x, y=all_nodes[i].y, z=all_nodes[i].z} -- copy position
142                 pos.x=pos.x+1
143                 check_node_subp(PR_nodes,RE_nodes,BA_nodes,all_nodes,pos, machines, cablename)
144                 pos.x=pos.x-2
145                 check_node_subp(PR_nodes,RE_nodes,BA_nodes,all_nodes,pos, machines, cablename)
146                 pos.x=pos.x+1
147                 
148                 pos.y=pos.y+1
149                 check_node_subp(PR_nodes,RE_nodes,BA_nodes,all_nodes,pos, machines, cablename)
150                 pos.y=pos.y-2
151                 check_node_subp(PR_nodes,RE_nodes,BA_nodes,all_nodes,pos, machines, cablename)
152                 pos.y=pos.y+1
153                 
154                 pos.z=pos.z+1
155                 check_node_subp(PR_nodes,RE_nodes,BA_nodes,all_nodes,pos, machines, cablename)
156                 pos.z=pos.z-2
157                 check_node_subp(PR_nodes,RE_nodes,BA_nodes,all_nodes,pos, machines, cablename)
158                 pos.z=pos.z+1
159               end
160
161 ----------------------------------------------
162 -- The action code for the switching station
163 ----------------------------------------------
164 minetest.register_abm(
165     {nodenames = {"technic:switching_station"},
166     interval   = 1,
167     chance     = 1,
168     action = function(pos, node, active_object_count, active_object_count_wider)
169             local meta             = minetest.env:get_meta(pos)
170             local meta1            = nil
171             local pos1             = {}
172             local PR_EU            = 0 -- EUs from PR nodes
173             local BA_PR_EU         = 0 -- EUs from BA nodes (discharching)
174             local BA_RE_EU         = 0 -- EUs to BA nodes (charging)
175             local RE_EU            = 0 -- EUs to RE nodes
176
177             local network   = ""
178             local all_nodes = {}
179             local PR_nodes  = {}
180             local BA_nodes  = {} 
181             local RE_nodes  = {}
182
183 --            -- Possible to turn off the entire network
184 --            if meta:get_int("active") == 0 then
185 --               for _,pos1 in pairs(RE_nodes) do
186 --              meta1  = minetest.env:get_meta(pos1)
187 --              meta1:set_int("EU_input", 0)
188 --               end
189 --               for _,pos1 in pairs(BA_nodes) do
190 --              meta1  = minetest.env:get_meta(pos1)
191 --              meta1:set_int("EU_input", 0)
192 --               end
193 --               return
194 --            end
195
196             -- Which kind of network are we on:
197             pos1 = {x=pos.x, y=pos.y-1, z=pos.z}
198             all_nodes[1] = pos1
199
200             meta1  = minetest.env:get_meta(pos1)
201             if meta1:get_float("cablelike") ==1 then
202                -- LV type
203                --dprint("LV type")
204                network = "LV"
205                local table_index = 1
206                repeat
207               traverse_network(PR_nodes,RE_nodes,BA_nodes,all_nodes,table_index, technic.LV_machines, "cablelike")
208               table_index = table_index + 1
209               if all_nodes[table_index] == nil then break end
210                until false
211             elseif meta1:get_float("mv_cablelike") ==1 then
212                -- MV type
213                --dprint("MV type")
214                network = "MV"
215                local table_index = 1
216                repeat
217               traverse_network(PR_nodes,RE_nodes,BA_nodes,all_nodes,table_index, technic.MV_machines, "mv_cablelike")
218               table_index = table_index + 1
219               if all_nodes[table_index] == nil then break end
220                until false
221             elseif meta1:get_float("hv_cablelike") ==1 then
222                -- HV type
223                --dprint("HV type")
224                network = "HV"
225                local table_index = 1
226                repeat
227               traverse_network(PR_nodes,RE_nodes,BA_nodes,all_nodes,table_index, technic.HV_machines, "hv_cablelike")
228               table_index = table_index + 1
229               if all_nodes[table_index] == nil then break end
230                until false
231             else
232                -- No type :-)
233                --dprint("Not connected to a network")
234                meta:set_string("infotext", "Switching Station - no network")
235                return
236             end
237             --dprint("nodes="..table.getn(all_nodes).." PR="..table.getn(PR_nodes).." BA="..table.getn(BA_nodes).." RE="..table.getn(RE_nodes))
238
239             -- Strings for the meta data
240             local eu_demand_str    = network.."_EU_demand"
241             local eu_input_str     = network.."_EU_input"
242             local eu_supply_str    = network.."_EU_supply"
243             local eu_from_fuel_str = network.."_EU_from_fuel"
244
245             -- Get all the power from the PR nodes
246             local PR_eu_supply = 0 -- Total power
247             for _,pos1 in pairs(PR_nodes) do
248                meta1  = minetest.env:get_meta(pos1)
249                PR_eu_supply = PR_eu_supply + meta1:get_int(eu_supply_str)
250             end
251             --dprint("Total PR supply:"..PR_eu_supply)
252
253             -- Get all the demand from the RE nodes
254             local RE_eu_demand = 0
255             for _,pos1 in pairs(RE_nodes) do
256                meta1  = minetest.env:get_meta(pos1)
257                RE_eu_demand = RE_eu_demand + meta1:get_int(eu_demand_str)
258             end
259             --dprint("Total RE demand:"..RE_eu_demand)
260
261             -- Get all the power from the BA nodes
262             local BA_eu_supply = 0
263             for _,pos1 in pairs(BA_nodes) do
264                meta1  = minetest.env:get_meta(pos1)
265                BA_eu_supply = BA_eu_supply + meta1:get_int(eu_supply_str)
266             end
267             --dprint("Total BA supply:"..BA_eu_supply)
268
269             -- Get all the demand from the BA nodes
270             local BA_eu_demand = 0
271             for _,pos1 in pairs(BA_nodes) do
272                meta1  = minetest.env:get_meta(pos1)
273                BA_eu_demand = BA_eu_demand + meta1:get_int(eu_demand_str)
274             end
275             --dprint("Total BA demand:"..BA_eu_demand)
276
277             meta:set_string("infotext", "Switching Station. PR("..(PR_eu_supply+BA_eu_supply)..") RE("..(RE_eu_demand+BA_eu_demand)..")")
278
279             -- If the PR supply is enough for the RE demand supply them all
280             if PR_eu_supply >= RE_eu_demand then
281                --dprint("PR_eu_supply"..PR_eu_supply.." >= RE_eu_demand"..RE_eu_demand)
282                for _,pos1 in pairs(RE_nodes) do
283               meta1  = minetest.env:get_meta(pos1)
284               local eu_demand = meta1:get_int(eu_demand_str)
285               meta1:set_int(eu_input_str, eu_demand)
286                end
287                -- We have a surplus, so distribute the rest equally to the BA nodes
288                -- Let's calculate the factor of the demand
289                PR_eu_supply = PR_eu_supply - RE_eu_demand
290                local charge_factor = 0 -- Assume all batteries fully charged
291                if BA_eu_demand > 0 then
292               charge_factor = PR_eu_supply / BA_eu_demand
293                end
294                for n,pos1 in pairs(BA_nodes) do
295               meta1  = minetest.env:get_meta(pos1)
296               local eu_demand = meta1:get_int(eu_demand_str)
297               meta1:set_int(eu_input_str, math.floor(eu_demand*charge_factor))
298               --dprint("Charging battery:"..math.floor(eu_demand*charge_factor))
299                end
300                -- If still a surplus we can start giving back to the fuel burning generators
301                -- Only full EU packages are given back. The rest is wasted.
302                if BA_eu_demand == 0 then
303               for _,pos1 in pairs(PR_nodes) do
304                  meta1  = minetest.env:get_meta(pos1)
305                  if meta1:get_int(eu_from_fuel_str) == 1 then
306                 local eu_supply = meta1:get_int(eu_supply_str)
307                 if PR_eu_supply < eu_supply then
308                    break
309                 else
310                    -- Set the supply to 0 if we did not require it.
311                    meta1:set_int(eu_supply_str, 0)
312                    PR_eu_supply = PR_eu_supply - eu_supply
313                 end
314                  end
315               end
316                end
317                return
318             end
319
320             -- If the PR supply is not enough for the RE demand we will discharge the batteries too
321             if PR_eu_supply+BA_eu_supply >= RE_eu_demand then
322                --dprint("PR_eu_supply "..PR_eu_supply.."+BA_eu_supply "..BA_eu_supply.." >= RE_eu_demand"..RE_eu_demand)
323                for _,pos1 in pairs(RE_nodes) do
324               meta1  = minetest.env:get_meta(pos1)
325               local eu_demand = meta1:get_int(eu_demand_str)
326               meta1:set_int(eu_input_str, eu_demand)
327                end
328                -- We have a deficit, so distribute to the BA nodes
329                -- Let's calculate the factor of the supply
330                local charge_factor = 0 -- Assume all batteries depleted
331                if BA_eu_supply > 0 then
332               charge_factor = (PR_eu_supply - RE_eu_demand) / BA_eu_supply
333                end
334                for n,pos1 in pairs(BA_nodes) do
335               meta1  = minetest.env:get_meta(pos1)
336               local eu_supply = meta1:get_int(eu_supply_str)
337               meta1:set_int(eu_input_str, math.floor(eu_supply*charge_factor))
338               --dprint("Discharging battery:"..math.floor(eu_supply*charge_factor))
339                end
340                return
341             end
342
343             -- If the PR+BA supply is not enough for the RE demand: Shut everything down!
344             -- Note: another behaviour could also be imagined: provide the average power for all and let the node decide what happens.
345             -- This is much simpler though: Not enough power for all==no power for all
346             --print("NO POWER")
347             for _,pos1 in pairs(RE_nodes) do
348                meta1  = minetest.env:get_meta(pos1)
349                meta1:set_int(eu_input_str, 0)
350             end
351     end,
352 })