you
2017-06-05 987cc5a6a425b1f9bcd9000608dc389a45c675a1
technic/machines/switching_station.lua
@@ -1,40 +1,14 @@
-- SWITCHING STATION
-- The switching station is the center of all power distribution on an electric network.
--
-- The station collects power from sources (PR), distributes it to sinks (RE),
-- and uses the excess/shortfall to charge and discharge batteries (BA).
--
-- For now, all supply and demand values are expressed in kW.
--
-- It works like this:
--  All PR,BA,RE nodes are indexed and tagged with the switching station.
-- The tagging is to allow more stations to be built without allowing a cheat
-- with duplicating power.
--  All the RE nodes are queried for their current EU demand. Those which are off
-- would require no or a small standby EU demand, while those which are on would
-- require more.
-- If the total demand is less than the available power they are all updated with the
-- demand number.
-- If any surplus exists from the PR nodes the batteries will be charged evenly with this.
-- If the total demand requires draw on the batteries they will be discharged evenly.
--
-- If the total demand is more than the available power all RE nodes will be shut down.
-- We have a brown-out situation.
--
-- Hence all the power distribution logic resides in this single node.
--
--  Nodes connected to the network will have one or more of these parameters as meta data:
--   <LV|MV|HV>_EU_supply : Exists for PR and BA node types. This is the EU value supplied by the node. Output
--   <LV|MV|HV>_EU_demand : Exists for RE and BA node types. This is the EU value the node requires to run. Output
--   <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
--
--  The reason the LV|MV|HV type is prepended toe meta data is because some machine could require several supplies to work.
--  This way the supplies are separated per network.
-- See also technic/doc/api.md
technic.networks = {}
technic.cables = {}
local mesecons_path = minetest.get_modpath("mesecons")
local digilines_path = minetest.get_modpath("digilines")
local S = technic.getter
local cable_entry = "^technic_cable_connection_overlay.png"
minetest.register_craft({
   output = "technic:switching_station",
@@ -45,11 +19,22 @@
   }
})
local mesecon_def
if mesecons_path then
   mesecon_def = {effector = {
      rules = mesecon.rules.default,
   }}
end
minetest.register_node("technic:switching_station",{
   description = S("Switching Station"),
   tiles  = {"technic_water_mill_top_active.png", "technic_water_mill_top_active.png",
                  "technic_water_mill_top_active.png", "technic_water_mill_top_active.png",
             "technic_water_mill_top_active.png", "technic_water_mill_top_active.png"},
   tiles  = {
      "technic_water_mill_top_active.png",
      "technic_water_mill_top_active.png"..cable_entry,
      "technic_water_mill_top_active.png",
      "technic_water_mill_top_active.png",
      "technic_water_mill_top_active.png",
      "technic_water_mill_top_active.png"},
   groups = {snappy=2, choppy=2, oddly_breakable_by_hand=2, technic_all_tiers=1},
   connect_sides = {"bottom"},
   sounds = default.node_sound_wood_defaults(),
@@ -57,12 +42,45 @@
      local meta = minetest.get_meta(pos)
      meta:set_string("infotext", S("Switching Station"))
      meta:set_string("active", 1)
      meta:set_string("channel", "switching_station"..minetest.pos_to_string(pos))
      meta:set_string("formspec", "field[channel;Channel;${channel}]")
   end,
   after_dig_node = function(pos)
      minetest.forceload_free_block(pos)
      pos.y = pos.y - 1
      minetest.forceload_free_block(pos)
   end,
   on_receive_fields = function(pos, formname, fields, sender)
      if not fields.channel then
         return
      end
      local plname = sender:get_player_name()
      if minetest.is_protected(pos, plname) then
         minetest.record_protection_violation(pos, plname)
         return
      end
      local meta = minetest.get_meta(pos)
      meta:set_string("channel", fields.channel)
   end,
   mesecons = mesecon_def,
   digiline = {
      receptor = {action = function() end},
      effector = {
         action = function(pos, node, channel, msg)
            if msg ~= "GET" and msg ~= "get" then
               return
            end
            local meta = minetest.get_meta(pos)
            if channel ~= meta:get_string("channel") then
               return
            end
            digilines.receptor_send(pos, digilines.rules.default, channel, {
               supply = meta:get_int("supply"),
               demand = meta:get_int("demand")
            })
         end
      },
   },
})
--------------------------------------------------
@@ -190,7 +208,7 @@
      local BA_nodes
      local RE_nodes
      local machine_name = S("Switching Station")
      -- Which kind of network are we on:
      pos1 = {x=pos.x, y=pos.y-1, z=pos.z}
@@ -216,9 +234,9 @@
         minetest.forceload_free_block(pos1)
         return
      end
      -- Run all the nodes
      local function run_nodes(list)
      local function run_nodes(list, run_stage)
         for _, pos2 in ipairs(list) do
            technic.get_or_load_node(pos2)
            local node2 = minetest.get_node(pos2)
@@ -227,14 +245,14 @@
               nodedef = minetest.registered_nodes[node2.name]
            end
            if nodedef and nodedef.technic_run then
               nodedef.technic_run(pos2, node2)
               nodedef.technic_run(pos2, node2, run_stage)
            end
         end
      end
      run_nodes(PR_nodes)
      run_nodes(RE_nodes)
      run_nodes(BA_nodes)
      run_nodes(PR_nodes, technic.producer)
      run_nodes(RE_nodes, technic.receiver)
      run_nodes(BA_nodes, technic.battery)
      -- Strings for the meta data
      local eu_demand_str    = tier.."_EU_demand"
@@ -301,6 +319,19 @@
            S("@1. Supply: @2 Demand: @3",
            machine_name, technic.pretty_num(PR_eu_supply), technic.pretty_num(RE_eu_demand)))
      -- If mesecon signal and power supply or demand changed then
      -- send them via digilines.
      if mesecons_path and digilines_path and mesecon.is_powered(pos) then
         if PR_eu_supply ~= meta:get_int("supply") or
               RE_eu_demand ~= meta:get_int("demand") then
            local channel = meta:get_string("channel")
            digilines.receptor_send(pos, digilines.rules.default, channel, {
               supply = PR_eu_supply,
               demand = RE_eu_demand
            })
         end
      end
      -- Data that will be used by the power monitor
      meta:set_int("supply",PR_eu_supply)
      meta:set_int("demand",RE_eu_demand)
@@ -366,7 +397,7 @@
         meta1 = minetest.get_meta(pos1)
         meta1:set_int(eu_input_str, 0)
      end
   end,
})
@@ -385,6 +416,7 @@
   end
end
minetest.register_abm({
   label = "Machines: timeout check",
   nodenames = {"group:technic_machine"},
   interval   = 1,
   chance     = 1,
@@ -410,6 +442,7 @@
--Re-enable disabled switching station if necessary, similar to the timeout above
minetest.register_abm({
   label = "Machines: re-enable check",
   nodenames = {"technic:switching_station"},
   interval   = 1,
   chance     = 1,