46045-syslab/supervisory_controller.py

94 lines
3.6 KiB
Python

from util import pos, clamp
import parameters
from time import time, sleep
import zmq
import syslab
from syslab.comm.LogUtils import setup_event_logger
logging = setup_event_logger()
### Parameters
# If soc is below this limit, divert all power to the battery.
base_soc_lower_limit = 0.2
# If soc is above this limit, divert all power to the mobile load.
base_soc_upper_limit = 0.8
# This much renewable production shifts our soc_lower_limit down by 0.1 and soc_upper_limit up by 0.1.
# I.e. if there is a lot of renewable production, the range over which we split between battery and
# load widens.
base_renewable_shift = 10.0
# # Variables
# Controller variables
x_battery = 0.5 # Default splitting factor
x_load = 0.5 # Default splitting factor
# Info on battery state
battery_soc = 0.5 # Default SOC (= state of charge)
soc_lower_limit = 0.2
soc_upper_limit = 0.8
# # Communication
# Handle the sockets we need
# Make a context that we use to set up sockets
context = zmq.Context()
# Set up a socket we can use to broadcast splitting factors on
splitting_out_socket = context.socket(zmq.PUB)
splitting_out_socket.bind(f"tcp://*:{parameters.SUPERVISOR_PORT}")
# Set up a socket to subscribe to the battery's soc
soc_in_socket = context.socket(zmq.SUB)
soc_in_socket.connect(f"tcp://{parameters.BATTERY_SOC_IP}:{parameters.BATTERY_SOC_PORT}")
# Ensure we only see message on the battery's soc
soc_in_socket.subscribe(parameters.TOPIC_BATTERY_SOC)
### Unit connections
# TODO step 1.3: Set up connection to the units
try:
while True:
try:
# Try to connect the the battery.
# If we have a connection to the battery, get its current soc.
# If none have come in, continue with previous soc.
# We put this in a while-loop to ensure we empty the queue each time,
# so we always have the latest value.
while True:
# Receive the latest splitting factor
incoming_str = soc_in_socket.recv_string(flags=zmq.NOBLOCK)
# The incoming string will look like "batt_split;0.781",
# so we split it up and take the last bit forward.
battery_soc = float(incoming_str.split(" ")[-1])
logging.info(f"New battery soc: {battery_soc}")
except zmq.Again as e:
# No (more) new messages, move along
pass
# Poll the grid connection to get the current production of renewables.
wind_p = 4.0 # TODO Task 1.3: Change into syslab connection. Remember that the measurements need to be collected directly from the units since vswitchboard is not working
solar_p = 7.0
logging.info(f"Current renewable production: {wind_p + solar_p} kW.")
soc_lower_limit = base_soc_lower_limit - (wind_p + solar_p) / base_renewable_shift / 10.0
soc_upper_limit = base_soc_upper_limit + (wind_p + solar_p) / base_renewable_shift / 10.0
# Calculate a new splitting factor for the battery.
x_battery = clamp(0, (soc_upper_limit - battery_soc)/(soc_upper_limit - soc_lower_limit), 1)
# If anything is not taken by the battery, put it in the load.
x_load = 1 - x_battery
# Publish the new splitting factors.
splitting_out_socket.send_string(f"{parameters.TOPIC_BATTERY_SPLITTING} {x_battery:.06f}")
splitting_out_socket.send_string(f"{parameters.TOPIC_LOAD_SPLITTING} {x_load:.06f}")
logging.info(f"Battery split: {x_battery:.03f}; Load split: {x_load:.03f}")
# Loop once more in a second
sleep(1)
finally:
# Clean up by closing our sockets.
soc_in_socket.close()
splitting_out_socket.close()