From d12a09addbc39644b44c57c5e2a83594707893b6 Mon Sep 17 00:00:00 2001 From: mwerezak Date: Sun, 7 Sep 2014 01:10:22 -0400 Subject: [PATCH] Powernets Moved powernet datum definition to powernet.dm Improved APC load balancing. Updated SMES charging to work better with new APC/powernet code. --- baystation12.dme | 1 + code/controllers/master_controller.dm | 2 +- code/datums/mixed.dm | 15 -- code/modules/power/power.dm | 249 ------------------------ code/modules/power/power_monitor.dm | 6 +- code/modules/power/powernet.dm | 265 ++++++++++++++++++++++++++ code/modules/power/smes.dm | 32 +--- 7 files changed, 280 insertions(+), 290 deletions(-) create mode 100644 code/modules/power/powernet.dm diff --git a/baystation12.dme b/baystation12.dme index 361118cb4b..0964b88a8c 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -1133,6 +1133,7 @@ #include "code\modules\power\port_gen.dm" #include "code\modules\power\power.dm" #include "code\modules\power\power_monitor.dm" +#include "code\modules\power\powernet.dm" #include "code\modules\power\profiling.dm" #include "code\modules\power\smes.dm" #include "code\modules\power\solar.dm" diff --git a/code/controllers/master_controller.dm b/code/controllers/master_controller.dm index e980c77657..a9d101f7de 100644 --- a/code/controllers/master_controller.dm +++ b/code/controllers/master_controller.dm @@ -355,7 +355,7 @@ datum/controller/game_controller/proc/process_powernets() while(i<=powernets.len) var/datum/powernet/Powernet = powernets[i] if(Powernet) - Powernet.reset() + Powernet.process() i++ continue powernets.Cut(i,i+1) diff --git a/code/datums/mixed.dm b/code/datums/mixed.dm index 6732e2a8b9..f0f0c70abd 100644 --- a/code/datums/mixed.dm +++ b/code/datums/mixed.dm @@ -30,20 +30,5 @@ -/datum/powernet - var/list/cables = list() // all cables & junctions - var/list/nodes = list() // all APCs & sources - - var/newload = 0 - var/load = 0 - var/newavail = 0 - var/avail = 0 - var/viewload = 0 - var/number = 0 - var/perapc = 0 // per-apc avilability - var/netexcess = 0 - - - /datum/debug var/list/debuglist diff --git a/code/modules/power/power.dm b/code/modules/power/power.dm index 6c8e6e479d..c40f96c62e 100644 --- a/code/modules/power/power.dm +++ b/code/modules/power/power.dm @@ -230,255 +230,6 @@ . += C return . - -/proc/powernet_nextlink(var/obj/O, var/datum/powernet/PN) - var/list/P - - while(1) - if( istype(O,/obj/structure/cable) ) - var/obj/structure/cable/C = O - C.powernet = PN - P = C.get_connections() - - else if(O.anchored && istype(O,/obj/machinery/power)) - var/obj/machinery/power/M = O - M.powernet = PN - P = M.get_connections() - - else - return - - if(P.len == 0) return - - O = P[1] - - for(var/L = 2 to P.len) - powernet_nextlink(P[L], PN) - - -// cut a powernet at this cable object -/datum/powernet/proc/cut_cable(var/obj/structure/cable/C) - var/turf/T1 = C.loc - if(!T1) return - var/node = 0 - if(C.d1 == 0) - node = 1 - - var/turf/T2 - if(C.d2) T2 = get_step(T1, C.d2) - if(C.d1) T1 = get_step(T1, C.d1) - - - var/list/P1 = power_list(T1, C, C.d1) // what joins on to cut cable in dir1 - var/list/P2 = power_list(T2, C, C.d2) // what joins on to cut cable in dir2 - -// if(Debug) -// for(var/obj/O in P1) -// world.log << "P1: [O] at [O.x] [O.y] : [istype(O, /obj/structure/cable) ? "[O:d1]/[O:d2]" : null] " -// for(var/obj/O in P2) -// world.log << "P2: [O] at [O.x] [O.y] : [istype(O, /obj/structure/cable) ? "[O:d1]/[O:d2]" : null] " - - - if(P1.len == 0 || P2.len == 0)//if nothing in either list, then the cable was an endpoint no need to rebuild the powernet, - cables -= C //just remove cut cable from the list -// if(Debug) world.log << "Was end of cable" - return - - //null the powernet reference of all cables & nodes in this powernet - var/i=1 - while(i<=cables.len) - var/obj/structure/cable/Cable = cables[i] - if(Cable) - Cable.powernet = null - if(Cable == C) - cables.Cut(i,i+1) - continue - i++ - i=1 - while(i<=nodes.len) - var/obj/machinery/power/Node = nodes[i] - if(Node) - Node.powernet = null - i++ - - // remove the cut cable from the network -// C.netnum = -1 - - C.loc = null - - powernet_nextlink(P1[1], src) // propagate network from 1st side of cable, using current netnum //TODO? - - // now test to see if propagation reached to the other side - // if so, then there's a loop in the network - var/notlooped = 0 - for(var/O in P2) - if( istype(O, /obj/machinery/power) ) - var/obj/machinery/power/Machine = O - if(Machine.powernet != src) - notlooped = 1 - break - else if( istype(O, /obj/structure/cable) ) - var/obj/structure/cable/Cable = O - if(Cable.powernet != src) - notlooped = 1 - break - - if(notlooped) - // not looped, so make a new powernet - var/datum/powernet/PN = new() - powernets += PN - -// if(Debug) world.log << "Was not looped: spliting PN#[number] ([cables.len];[nodes.len])" - - i=1 - while(i<=cables.len) - var/obj/structure/cable/Cable = cables[i] - if(Cable && !Cable.powernet) // non-connected cables will have powernet=null, since they weren't reached by propagation - Cable.powernet = PN - cables.Cut(i,i+1) // remove from old network & add to new one - PN.cables += Cable - continue - i++ - - i=1 - while(i<=nodes.len) - var/obj/machinery/power/Node = nodes[i] - if(Node && !Node.powernet) - Node.powernet = PN - nodes.Cut(i,i+1) - PN.nodes[Node] = Node - continue - i++ - - // Disconnect machines connected to nodes - if(node) - for(var/obj/machinery/power/P in T1) - if(P.powernet && !P.powernet.nodes[src]) - P.disconnect_from_network() -// if(Debug) -// world.log << "Old PN#[number] : ([cables.len];[nodes.len])" -// world.log << "New PN#[PN.number] : ([PN.cables.len];[PN.nodes.len])" -// -// else -// if(Debug) -// world.log << "Was looped." -// //there is a loop, so nothing to be done -// return - - - -/datum/powernet/proc/reset() - load = newload - newload = 0 - avail = newavail - newavail = 0 - - - viewload = 0.8*viewload + 0.2*load - - viewload = round(viewload) - - var/numapc = 0 - - if(nodes && nodes.len) // Added to fix a bad list bug -- TLE - for(var/obj/machinery/power/terminal/term in nodes) - if( istype( term.master, /obj/machinery/power/apc ) ) - numapc++ - - netexcess = avail - load - - if(numapc) - //very simple load balancing. If there was a net excess this tick then it must have been that some APCs used less than perapc, since perapc*numapc = avail - //Therefore we can raise the amount of power rationed out to APCs on the assumption that those APCs that used less than perapc will continue to do so. - //If that assumption fails, then some APCs will miss out on power next tick, however it will be rebalanced for the tick after. - perapc = (avail + netexcess)/numapc - - if( netexcess > 100) // if there was excess power last cycle - if(nodes && nodes.len) - for(var/obj/machinery/power/smes/S in nodes) // find the SMESes in the network - if(S.powernet == src) - S.restore() // and restore some of the power that was used - else - error("[S.name] (\ref[S]) had a [S.powernet ? "different (\ref[S.powernet])" : "null"] powernet to our powernet (\ref[src]).") - nodes.Remove(S) - - -//Returns the amount of available power -/datum/powernet/proc/surplus() - return max(avail - newload, 0) - -//Returns the amount of excess power (before refunding to SMESs) from last tick. -//This is for machines that might adjust their power consumption using this data. -/datum/powernet/proc/last_surplus() - return max(avail - load, 0) - -//Attempts to draw power from a powernet. Returns the actual amount of power drawn -/datum/powernet/proc/draw_power(var/requested_amount) - var/surplus = max(avail - newload, 0) - var/actual_draw = min(requested_amount, surplus) - newload += actual_draw - - return actual_draw - -/datum/powernet/proc/get_electrocute_damage() - switch(avail)/* - if (1300000 to INFINITY) - return min(rand(70,150),rand(70,150)) - if (750000 to 1300000) - return min(rand(50,115),rand(50,115)) - if (100000 to 750000-1) - return min(rand(35,101),rand(35,101)) - if (75000 to 100000-1) - return min(rand(30,95),rand(30,95)) - if (50000 to 75000-1) - return min(rand(25,80),rand(25,80)) - if (25000 to 50000-1) - return min(rand(20,70),rand(20,70)) - if (10000 to 25000-1) - return min(rand(20,65),rand(20,65)) - if (1000 to 10000-1) - return min(rand(10,20),rand(10,20))*/ - if (1000000 to INFINITY) - return min(rand(50,160),rand(50,160)) - if (200000 to 1000000) - return min(rand(25,80),rand(25,80)) - if (100000 to 200000)//Ave powernet - return min(rand(20,60),rand(20,60)) - if (50000 to 100000) - return min(rand(15,40),rand(15,40)) - if (1000 to 50000) - return min(rand(10,20),rand(10,20)) - else - return 0 - -//The powernet that calls this proc will consume the other powernet - Rockdtben -//TODO: rewrite so the larger net absorbs the smaller net -/proc/merge_powernets(var/datum/powernet/net1, var/datum/powernet/net2) - if(!net1 || !net2) return - if(net1 == net2) return - - //We assume net1 is larger. If net2 is in fact larger we are just going to make them switch places to reduce on code. - if(net1.cables.len < net2.cables.len) //net2 is larger than net1. Let's switch them around - var/temp = net1 - net1 = net2 - net2 = temp - - for(var/i=1,i<=net2.nodes.len,i++) //merge net2 into net1 - var/obj/machinery/power/Node = net2.nodes[i] - if(Node) - Node.powernet = net1 - net1.nodes[Node] = Node - - for(var/i=1,i<=net2.cables.len,i++) - var/obj/structure/cable/Cable = net2.cables[i] - if(Cable) - Cable.powernet = net1 - net1.cables += Cable - - del(net2) - return net1 - - /obj/machinery/power/proc/connect_to_network() var/turf/T = src.loc var/obj/structure/cable/C = T.get_cable_node() diff --git a/code/modules/power/power_monitor.dm b/code/modules/power/power_monitor.dm index bb6968a96f..bd6d852942 100644 --- a/code/modules/power/power_monitor.dm +++ b/code/modules/power/power_monitor.dm @@ -67,7 +67,7 @@ t += "" if(L.len > 0) - + var/total_demand = 0 t += "Area Eqp./Lgt./Env. Load Cell
" var/list/S = list(" Off","AOff"," On", " AOn") @@ -77,8 +77,10 @@ t += copytext(add_tspace("\The [A.area]", 30), 1, 30) t += " [S[A.equipment+1]] [S[A.lighting+1]] [S[A.environ+1]] [add_lspace(A.lastused_total, 6)] [A.cell ? "[add_lspace(round(A.cell.percent()), 3)]% [chg[A.charging+1]]" : " N/C"]
" + total_demand += A.lastused_total - t += "
" + t += "
Total demand: [total_demand] W" + t += "" user << browse(t, "window=powcomp;size=420x900") onclose(user, "powcomp") diff --git a/code/modules/power/powernet.dm b/code/modules/power/powernet.dm new file mode 100644 index 0000000000..1fbcc6f1c9 --- /dev/null +++ b/code/modules/power/powernet.dm @@ -0,0 +1,265 @@ +/datum/powernet + var/list/cables = list() // all cables & junctions + var/list/nodes = list() // all APCs & sources + + var/newload = 0 + var/load = 0 + var/newavail = 0 + var/avail = 0 + var/viewload = 0 + var/number = 0 + + var/perapc = 0 // per-apc avilability + var/perapc_excess = 0 + var/netexcess = 0 + +/datum/powernet/proc/process() + load = newload + newload = 0 + avail = newavail + newavail = 0 + + + viewload = 0.8*viewload + 0.2*load + + viewload = round(viewload) + + var/numapc = 0 + + if(nodes && nodes.len) // Added to fix a bad list bug -- TLE + for(var/obj/machinery/power/terminal/term in nodes) + if( istype( term.master, /obj/machinery/power/apc ) ) + numapc++ + + netexcess = avail - load + + if(numapc) + //very simple load balancing. If there was a net excess this tick then it must have been that some APCs used less than perapc, since perapc*numapc = avail + //Therefore we can raise the amount of power rationed out to APCs on the assumption that those APCs that used less than perapc will continue to do so. + //If that assumption fails, then some APCs will miss out on power next tick, however it will be rebalanced for the tick after. + if (netexcess >= 0) + perapc_excess += min(netexcess/numapc, (avail - perapc) - perapc_excess) + else + perapc_excess = 0 + + perapc = avail/numapc + perapc_excess + + if( netexcess > 100) // if there was excess power last cycle + if(nodes && nodes.len) + for(var/obj/machinery/power/smes/S in nodes) // find the SMESes in the network + if(S.powernet == src) + S.restore() // and restore some of the power that was used + else + error("[S.name] (\ref[S]) had a [S.powernet ? "different (\ref[S.powernet])" : "null"] powernet to our powernet (\ref[src]).") + nodes.Remove(S) + + + + +//Returns the amount of available power +/datum/powernet/proc/surplus() + return max(avail - newload, 0) + +//Returns the amount of excess power (before refunding to SMESs) from last tick. +//This is for machines that might adjust their power consumption using this data. +/datum/powernet/proc/last_surplus() + return max(avail - load, 0) + +//Attempts to draw power from a powernet. Returns the actual amount of power drawn +/datum/powernet/proc/draw_power(var/requested_amount) + var/surplus = max(avail - newload, 0) + var/actual_draw = min(requested_amount, surplus) + newload += actual_draw + + return actual_draw + +// cut a powernet at this cable object +/datum/powernet/proc/cut_cable(var/obj/structure/cable/C) + var/turf/T1 = C.loc + if(!T1) return + var/node = 0 + if(C.d1 == 0) + node = 1 + + var/turf/T2 + if(C.d2) T2 = get_step(T1, C.d2) + if(C.d1) T1 = get_step(T1, C.d1) + + + var/list/P1 = power_list(T1, C, C.d1) // what joins on to cut cable in dir1 + var/list/P2 = power_list(T2, C, C.d2) // what joins on to cut cable in dir2 + +// if(Debug) +// for(var/obj/O in P1) +// world.log << "P1: [O] at [O.x] [O.y] : [istype(O, /obj/structure/cable) ? "[O:d1]/[O:d2]" : null] " +// for(var/obj/O in P2) +// world.log << "P2: [O] at [O.x] [O.y] : [istype(O, /obj/structure/cable) ? "[O:d1]/[O:d2]" : null] " + + + if(P1.len == 0 || P2.len == 0)//if nothing in either list, then the cable was an endpoint no need to rebuild the powernet, + cables -= C //just remove cut cable from the list +// if(Debug) world.log << "Was end of cable" + return + + //null the powernet reference of all cables & nodes in this powernet + var/i=1 + while(i<=cables.len) + var/obj/structure/cable/Cable = cables[i] + if(Cable) + Cable.powernet = null + if(Cable == C) + cables.Cut(i,i+1) + continue + i++ + i=1 + while(i<=nodes.len) + var/obj/machinery/power/Node = nodes[i] + if(Node) + Node.powernet = null + i++ + + // remove the cut cable from the network +// C.netnum = -1 + + C.loc = null + + powernet_nextlink(P1[1], src) // propagate network from 1st side of cable, using current netnum //TODO? + + // now test to see if propagation reached to the other side + // if so, then there's a loop in the network + var/notlooped = 0 + for(var/O in P2) + if( istype(O, /obj/machinery/power) ) + var/obj/machinery/power/Machine = O + if(Machine.powernet != src) + notlooped = 1 + break + else if( istype(O, /obj/structure/cable) ) + var/obj/structure/cable/Cable = O + if(Cable.powernet != src) + notlooped = 1 + break + + if(notlooped) + // not looped, so make a new powernet + var/datum/powernet/PN = new() + powernets += PN + +// if(Debug) world.log << "Was not looped: spliting PN#[number] ([cables.len];[nodes.len])" + + i=1 + while(i<=cables.len) + var/obj/structure/cable/Cable = cables[i] + if(Cable && !Cable.powernet) // non-connected cables will have powernet=null, since they weren't reached by propagation + Cable.powernet = PN + cables.Cut(i,i+1) // remove from old network & add to new one + PN.cables += Cable + continue + i++ + + i=1 + while(i<=nodes.len) + var/obj/machinery/power/Node = nodes[i] + if(Node && !Node.powernet) + Node.powernet = PN + nodes.Cut(i,i+1) + PN.nodes[Node] = Node + continue + i++ + + // Disconnect machines connected to nodes + if(node) + for(var/obj/machinery/power/P in T1) + if(P.powernet && !P.powernet.nodes[src]) + P.disconnect_from_network() +// if(Debug) +// world.log << "Old PN#[number] : ([cables.len];[nodes.len])" +// world.log << "New PN#[PN.number] : ([PN.cables.len];[PN.nodes.len])" +// +// else +// if(Debug) +// world.log << "Was looped." +// //there is a loop, so nothing to be done +// return + +/datum/powernet/proc/get_electrocute_damage() + switch(avail)/* + if (1300000 to INFINITY) + return min(rand(70,150),rand(70,150)) + if (750000 to 1300000) + return min(rand(50,115),rand(50,115)) + if (100000 to 750000-1) + return min(rand(35,101),rand(35,101)) + if (75000 to 100000-1) + return min(rand(30,95),rand(30,95)) + if (50000 to 75000-1) + return min(rand(25,80),rand(25,80)) + if (25000 to 50000-1) + return min(rand(20,70),rand(20,70)) + if (10000 to 25000-1) + return min(rand(20,65),rand(20,65)) + if (1000 to 10000-1) + return min(rand(10,20),rand(10,20))*/ + if (1000000 to INFINITY) + return min(rand(50,160),rand(50,160)) + if (200000 to 1000000) + return min(rand(25,80),rand(25,80)) + if (100000 to 200000)//Ave powernet + return min(rand(20,60),rand(20,60)) + if (50000 to 100000) + return min(rand(15,40),rand(15,40)) + if (1000 to 50000) + return min(rand(10,20),rand(10,20)) + else + return 0 + +/proc/powernet_nextlink(var/obj/O, var/datum/powernet/PN) + var/list/P + + while(1) + if( istype(O,/obj/structure/cable) ) + var/obj/structure/cable/C = O + C.powernet = PN + P = C.get_connections() + + else if(O.anchored && istype(O,/obj/machinery/power)) + var/obj/machinery/power/M = O + M.powernet = PN + P = M.get_connections() + + else + return + + if(P.len == 0) return + + O = P[1] + + for(var/L = 2 to P.len) + powernet_nextlink(P[L], PN) + +//The powernet that calls this proc will consume the other powernet - Rockdtben +//TODO: rewrite so the larger net absorbs the smaller net +/proc/merge_powernets(var/datum/powernet/net1, var/datum/powernet/net2) + if(!net1 || !net2) return + if(net1 == net2) return + + //We assume net1 is larger. If net2 is in fact larger we are just going to make them switch places to reduce on code. + if(net1.cables.len < net2.cables.len) //net2 is larger than net1. Let's switch them around + var/temp = net1 + net1 = net2 + net2 = temp + + for(var/i=1,i<=net2.nodes.len,i++) //merge net2 into net1 + var/obj/machinery/power/Node = net2.nodes[i] + if(Node) + Node.powernet = net1 + net1.nodes[Node] = Node + + for(var/i=1,i<=net2.cables.len,i++) + var/obj/structure/cable/Cable = net2.cables[i] + if(Cable) + Cable.powernet = net1 + net1.cables += Cable + + del(net2) + return net1 \ No newline at end of file diff --git a/code/modules/power/smes.dm b/code/modules/power/smes.dm index 1eae70f8ca..8429ef9d18 100644 --- a/code/modules/power/smes.dm +++ b/code/modules/power/smes.dm @@ -88,32 +88,18 @@ var/last_onln = online if(terminal) - var/excess = terminal.surplus() + //If chargemod is set, try to charge + //Use charging to let the player know whether we were able to obtain our target load. + //TODO: Add a meter to tell players how much charge we are actually getting, and only set charging to 0 when we are unable to get any charge at all. + if(chargemode) + var/target_load = min((capacity-charge)/SMESRATE, chargelevel) // charge at set rate, limited to spare capacity + var/actual_load = add_load(target_load) // add the load to the terminal side network + charge += actual_load * SMESRATE // increase the charge - if(charging) - if(excess >= 0) // if there's power available, try to charge - var/load = min((capacity-charge)/SMESRATE, chargelevel) // charge at set rate, limited to spare capacity - load = add_load(load) // add the load to the terminal side network - charge += load * SMESRATE // increase the charge - - else // if not enough capcity - charging = 0 // stop charging - //chargecount = 0 - - else - if (chargemode && excess > 0 && excess >= chargelevel) + if (actual_load >= target_load) // did the powernet have enough power available for us? charging = 1 - /* if(chargemode) - if(chargecount > rand(3,6)) - charging = 1 - chargecount = 0 - - if(excess > chargelevel) - chargecount++ - else - chargecount = 0 else - chargecount = 0 */ + charging = 0 if(online) // if outputting lastout = min( charge/SMESRATE, output) //limit output to that stored