Add files from GitHub.

This commit is contained in:
WickedJack99
2025-12-07 01:00:15 +01:00
commit a0b6239334
53 changed files with 2843 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
# ======== Applications ========
# 1) -----------
# name: Theory
# Project: LHC@home
# 2) -----------
# name: sixtrack
# Project: LHC@home
class BoincApplication:
def __init__(self, name, project):
self.name = name
self.project = project

View File

@@ -0,0 +1,35 @@
# ======== Application versions ========
# 1) -----------
# project: LHC@home
# application: Theory
# platform: x86_64-pc-linux-gnu
# plan class: native_theory
# version: 300.08
# estimated GFLOPS: 0.08
# filename: wrapper_2019_03_02_x86_64-linux
# 2) -----------
# project: LHC@home
# application: sixtrack
# platform: x86_64-pc-linux-gnu
# plan class: avx
# version: 502.05
# estimated GFLOPS: 1.28
# filename: sixtrack_lin64_50205_avx.linux
# 3) -----------
# project: LHC@home
# application: sixtrack
# platform: x86_64-pc-linux-gnu
# plan class: sse2
# version: 502.05
# estimated GFLOPS: 1.44
# filename: sixtrack_lin64_50205_sse2.linux
class BoincApplicationVersion:
def __init__(self, project, application, platform, plan_class, version, estimated, filename):
self.project = project # string
self.application = application # string
self.platform = platform # string
self.plan_class = plan_class # string
self.version = version # string
self.estimated = estimated # string
self.filename = filename # string

View File

@@ -0,0 +1,56 @@
# Commands:
# --acct_mgr attach URL name passwd attach to account manager
ACCOUNT_MANAGER_INFO = "--acct_mgr info" # show current account manager info
ACCOUNT_MANAGER_SYNCHRONIZE = "--acct_mgr sync" # synchronize with acct mgr
ACCOUNT_MANAGER_DETACH = "--acct_mgr detach" # detach from acct mgr
CLIENT_VERSION = "--client_version" # show client version
# --create_account URL email passwd name
# --file_transfer URL filename op file transfer operation
# op = retry | abort
# --get_app_config URL show app config for given project
GET_CC_STATUS = "--get_cc_status"
GET_NETWORK_TRAFFIC_HISTORY = "--get_daily_xfer_history" # show network traffic history
GET_DISK_USAGE = "--get_disk_usage" # show disk usage
GET_FILE_TRANSFER = "--get_file_transfers" # show file transfers
GET_HOST_INFO = "--get_host_info"
GET_MESSAGE_COUNT = "--get_message_count" # show largest message seqno
# --get_messages [ seqno ] show messages > seqno
# --get_notices [ seqno ] show notices > seqno
# --get_project_config URL
GET_PROJECTS_STATUS = "--get_project_status" # show status of all attached projects
GET_PROXY_SETTINGS = "--get_proxy_settings"
GET_SIMPLE_GUI_INFO = "--get_simple_gui_info" # show status of projects and active tasks
GET_STATE = "--get_state" # show entire state
GET_TASKS = "--get_tasks" # show tasks
# --get_old_tasks show reported tasks from last 1 hour
# --join_acct_mgr URL name passwd same as --acct_mgr attach
# --lookup_account URL email passwd
# --network_available retry deferred network communication
# --project URL op project operation
# op = reset | detach | update | suspend | resume | nomorework | allowmorework | detach_when_done | dont_detach_when_done
# --project_attach URL auth attach to project
# --quit tell client to exit
# --quit_acct_mgr same as --acct_mgr detach
# --read_cc_config
# --read_global_prefs_override
# --reset_host_info have client get mem size, #CPUs etc. again
# --run_benchmarks
# --run_graphics_app id op (Macintosh only) run, test or stop graphics app
# op = run | runfullscreen | stop | test
# id = slot # for run or runfullscreen, process ID for stop or test
# id = -1 for default screensaver (boincscr)
# --set_gpu_mode mode duration set GPU run mode for given duration
# mode = always | auto | never
# --set_host_info product_name
# --set_network_mode mode duration set network mode for given duration
# mode = always | auto | never
# --set_proxy_settings
# --set_run_mode mode duration set run mode for given duration
# mode = always | auto | never
# --task url task_name op task operation
# op = suspend | resume | abort

View File

@@ -0,0 +1,18 @@
from boinc_components.boinc_project import BoincProject
from boinc_components.boinc_project_gui_url import BoincProjectGuiUrl
from boinc_components.boinc_application import BoincApplication
from boinc_components.boinc_application_version import BoincApplicationVersion
from boinc_components.boinc_workunit import BoincWorkunit
from boinc_components.boinc_task import BoincTask
from boinc_components.boinc_time_stats import BoincTimeStats
class BoincHost:
def __init__(self, address, password, projects=None, applications=None, application_versions=None, workunits=None, tasks=None, time_stats=None):
self.address = address # string
self.password = password # string
self.projects = projects # list of BoincProject
self.applications = applications # list of BoincApplication
self.application_versions = application_versions # list of BoincApplicationVersion
self.workunits = workunits # list of BoincWorkunit
self.tasks = tasks # list of BoincTask
self.time_stats = time_stats # TimeStats

View File

@@ -0,0 +1,74 @@
# ======== Projects ========
# 1) -----------
# name: LHC@home
# master URL: https://lhcathome.cern.ch/lhcathome/
# user_name: WickedJack
# team_name:
# resource share: 100.000000
# user_total_credit: 412.990429
# user_expavg_credit: 22.766969
# host_total_credit: 412.990429
# host_expavg_credit: 25.140667
# nrpc_failures: 0
# master_fetch_failures: 0
# master fetch pending: no
# scheduler RPC pending: no
# trickle upload pending: no
# attached via Account Manager: no
# ended: no
# suspended via GUI: no
# don't request more work: no
# disk usage: 42.67MB
# last RPC: Tue Apr 23 11:23:53 2024
# project files downloaded: 0.000000
from boinc_components.boinc_project_gui_url import BoincProjectGuiUrl
class BoincProject:
def __init__(
self,
name,
master_url,
user_name,
team_name,
resource_share,
user_total_credit,
user_exponential_average_credit,
host_total_credit,
host_exponential_average_credit,
nrpc_failures,
master_fetch_failures,
master_fetch_pending,
scheduler_rpc_pending,
trickle_upload_pending,
attached_via_account_manager,
ended,
suspended_via_gui,
dont_request_more_work,
disk_usage,
last_rpc,
project_files_downloaded,
gui_urls
):
self.name = name
self.master_url = master_url
self.user_name = user_name
self.team_name = team_name
self.resource_share = resource_share
self.user_total_credit = user_total_credit
self.user_exponential_average_credit = user_exponential_average_credit
self.host_total_credit = host_total_credit
self.host_exponential_average_credit = host_exponential_average_credit
self.nrpc_failures = nrpc_failures
self.master_fetch_failures = master_fetch_failures
self.master_fetch_pending = master_fetch_pending
self.scheduler_rpc_pending = scheduler_rpc_pending
self.trickle_upload_pending = trickle_upload_pending
self.attached_via_account_manager = attached_via_account_manager
self.ended = ended
self.suspended_via_gui = suspended_via_gui
self.dont_request_more_work = dont_request_more_work
self.disk_usage = disk_usage
self.last_rpc = last_rpc
self.project_files_downloaded = project_files_downloaded
self.gui_urls = gui_urls

View File

@@ -0,0 +1,30 @@
# GUI URL:
# name: Message boards
# description: Correspond with other users on the LHC@home message boards
# URL: https://lhcathome.cern.ch/lhcathome/forum_index.php
# GUI URL:
# name: Your account
# description: View your account information
# URL: https://lhcathome.cern.ch/lhcathome/home.php
# GUI URL:
# name: Your tasks
# description: View the last week or so of computational work
# URL: https://lhcathome.cern.ch/lhcathome/results.php?userid=1062754
# GUI URL:
# name: FAQ
# description: Frequently Asked Questions on LHC@home
# URL: http://lhcathome.web.cern.ch/faq
# jobs succeeded: 39
# jobs failed: 492
# elapsed time: 430959.162318
# cross-project ID: 773913b4ca09b7fae8533c9c9c859d07
class BoincProjectGuiUrl:
def __init__(self, name, description, url, jobs_succeeded, jobs_failed, elapsed_time, cross_project_id):
self.name = name
self.description = description
self.url = url
self.jobs_succeeded = jobs_succeeded
self.jobs_failed = jobs_failed
self.elapsed_time = elapsed_time
self.cross_project_id = cross_project_id

View File

@@ -0,0 +1,32 @@
# ======== Tasks ========
# TODO find out if this information one task has is up to date.
class BoincTask:
def __init__(
self,
name,
workunit_name,
project_url,
received,
report_deadline,
ready_to_report,
state,
scheduler_state,
active_task_state,
app_version_num,
resources,
estimated_cpu_time_remaining
):
self.name = name
self.workunit_name = workunit_name
self.project_url = project_url
self.received = received
self.report_deadline = report_deadline
self.ready_to_report = ready_to_report
self.state = state
self.scheduler_state = scheduler_state
self.active_task_state = active_task_state
self.app_version_num = app_version_num
self.resources = resources
self.estimated_cpu_time_remaining = estimated_cpu_time_remaining

View File

@@ -0,0 +1,50 @@
# ======== Time stats ========
# now: 1713866392.601657
# on_frac: 0.999491
# connected_frac: -1.000000
# cpu_and_network_available_frac: 0.999992
# active_frac: 0.999992
# gpu_active_frac: 0.999992
# client_start_time: Tue Apr 16 14:45:29 2024
# previous_uptime: 3884.003729
# session_active_duration: 594842.232585
# session_gpu_active_duration: 594842.232585
# total_start_time: Mon Apr 15 22:52:40 2024
# total_duration: 651143.128246
# total_active_duration: 651133.088290
# total_gpu_active_duration: 651133.088290
class BoincTimeStats:
def __init__(
self,
now,
on_frac,
connected_frac,
cpu_and_network_available_frac,
active_frac,
gpu_active_frac,
client_start_time,
previous_uptime,
session_active_duration,
session_gpu_active_duration,
total_start_time,
total_duration,
total_active_duration,
total_gpu_active_duration
):
self.now = now
self.on_frac = on_frac
self.connected_frac = connected_frac
self.cpu_and_network_available_frac = cpu_and_network_available_frac
self.active_frac = active_frac
self.gpu_active_frac = gpu_active_frac
self.client_start_time = client_start_time
self.previous_uptime = previous_uptime
self.session_active_duration = session_active_duration
self.session_gpu_active_duration = session_gpu_active_duration
self.total_start_time = total_start_time
self.total_duration = total_duration
self.total_active_duration = total_active_duration
self.total_gpu_active_duration = total_gpu_active_duration

View File

@@ -0,0 +1,6 @@
# ======== Workunits ========
class BoincWorkunit:
def __init__(self):
self
# TODO find out which information is contained in a workunit

View File

@@ -0,0 +1,26 @@
from boinc_components.boinc_project import BoincProject
from boinc_components.boinc_project_gui_url import BoincProjectGuiUrl
from boinc_components.boinc_application import BoincApplication
from boinc_components.boinc_application_version import BoincApplicationVersion
from boinc_components.boinc_workunit import BoincWorkunit
from boinc_components.boinc_task import BoincTask
from boinc_components.boinc_time_stats import BoincTimeStats
from boinc_components.boinc_host import BoincHost
# TODO parse data from received output to host object
# Create subfunctions for each param of BoincHost except address and password
def parseOutputToHost(output, old_host):
address = old_host.address
password = old_host.password
projects = []
applications = []
application_versions = []
workunits = []
tasks = []
time_stats = parseOutputToBoincTimeStats(output)
return BoincHost(address, password, projects, applications, application_versions, workunits, tasks, time_stats)
def parseOutputToBoincTimeStats(output):
return BoincTimeStats()

View File

@@ -0,0 +1,15 @@
import subprocess
class HeaterInstance:
def __init__(self, host):
self.host = host
def execute_command_and_return_output(self, command):
# Define the command to execute
command = "boinccmd --host " + self.host.address + " --passwd '" + self.host.password + "' " + command
# Execute the command and capture the output
output = subprocess.check_output(command, shell=True)
# Decode the output if needed (for Python 3)
output = output.decode("utf-8")
# Print or process the output
return output

View File

@@ -0,0 +1,20 @@
import boinc_components.boinc_commands as BOINC_COMMAND
from load_balancer.boinc.heater_instance import HeaterInstance
from boinc_components.boinc_host import BoincHost
def main():
print("-------------------------------")
print("Load Balancer has been started.")
print("-------------------------------")
address = "494216f3-d1df-4ec0-ae1c-51f6643adc4a.fr.bw-cloud-instance.org"
password = "&!dBgncRmwnjurJXyhK>#G%PTy$H]kt0"
host = BoincHost(address, password)
instance = HeaterInstance(host)
output = instance.execute_command_and_return_output(BOINC_COMMAND.GET_STATE)
print(output)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,62 @@
# author Aaron Moser
from enum import Enum
# Comstants
# 96 kWh maximale Arbeit, die von einer Instanz verrichtet werden kann
# 4kW volle Leistungslast, welche in einer Stunde moeglich ist
# 4kW / 100 = 0.04kW = 1 Prozent
MAX_WORKLOAD_IN_KW:int = 4
MAX_WORKLOAD_IN_KW_ONE_PERCENT:float = MAX_WORKLOAD_IN_KW / 100
# 24h/d * 60min/h * 60s/min = 86400s/d = Anzahl Sekunden am Tag
# Ein Tag um Faktor 1000 gekuerzt = 86400s/d / 1000 = 86.4s/d
# 86,4s/d / 1440min/d = 0.06s/min
# Ratio how many seconds simulate one minute of a day
DAILY_MODE_MILLISECONDS_MINUTE_RATIO:int = 60
MINIMUM_NUMBER_CLIENTS = 1
MAXIMUM_NUMBER_CLIENTS = 20
# Error code enums
class ConnectErrorCode(Enum):
CONNECTED = 1
ALREADY_CONNECTED = 2
UNVERIFIED_KEY = 3
AUTHENTICATION_FAILED = 4
SOCKET_ERROR = 5
ALL_CONNECTION_ATTEMPTS_FAILED = 6
SSH_EXCEPTION = 7
class State(Enum):
Stopped = 1
Running = 2
# Commands
START_SIMULATION_COMMAND = "startsimulation"
STOP_SIMULATION_COMMAND = "stopsimulation"
SET_SIMULATION_MODE_COMMAND = "setsimulationmode"
ADD_INSTANCE_COMMAND = "addinstance"
REMOVE_INSTANCE_COMMAND = "removeinstance"
SHOW_INSTANCES_COMMAND = "showinstances"
DISPLAY_OUTCOME_COMMAND = "displayoutcome"
HELP_COMMAND = "help"
EXIT_COMMAND = "exit"
SET_SIMULATION_MODE_COMMAND_REALTIME = "realtime"
SET_SIMULATION_MODE_COMMAND_DAILY = "daily"
COMMANDS_HELP = [
"StartSimulation [daily, realtime] [start date] [end date] // Starts simulation with default mode or optionally with given mode, start and end date.",
"StopSimulation // Stops running simulation by stopping threads of instances.",
"SetSimulationMode // 'daily' or 'realtime', sets simulation mode depending on second argument.",
"AddInstance <hostname> <username> <ssh_key_filename> <passphrase> // Adds new instance to load balancer.",
"RemoveInstance <hostname> <username> // Removes existing instance.",
"ShowInstances // Lists all existsing instances and their state.",
"DisplayOutcome // Shows global outcome which consists of: count, tries and PI",
"Help // Shows commands and descriptions.",
"Exit // Stops load balancer and exits application."]
# Messages
START_MESSAGE = "-------------------------------\nLoad Balancer has been started."

View File

@@ -0,0 +1,252 @@
# author Aaron Moser, Theresa Herr
import sqlite3
import json
import threading
from enum import Enum
from typing import List
from heater.host import Host
from heater_instance_interface import HeaterInstanceInterface
from tasks.task_outcome import TaskOutcome
db_lock = threading.Lock()
class DatabaseOperation(Enum):
GET_INSTANCE = 1
GET_INSTANCES = 2
UPDATE_INSTANCE = 3
REMOVE_INSTANCE = 4
REMOVE_ALL_INSTANCES = 5
class DatabaseInterface:
def __init__(self, db_path):
self.__db_path = db_path
self.__init_database()
def access_db(self, operation: DatabaseOperation, args) -> List[Host]:
db_lock.acquire()
try:
if operation == DatabaseOperation.GET_INSTANCE:
return self.__get_instance(args)
elif operation == DatabaseOperation.GET_INSTANCES:
return self.__get_all_instances()
elif operation == DatabaseOperation.UPDATE_INSTANCE:
self.__update_instance(args)
elif operation == DatabaseOperation.REMOVE_INSTANCE:
self.__remove_instance(args)
elif operation == DatabaseOperation.REMOVE_ALL_INSTANCES:
self.__remove_all_instances()
else:
print("Unknown database operation: " + operation)
finally:
db_lock.release()
def __init_database(self):
conn = sqlite3.connect(self.__db_path)
self.__init_table_instances(conn)
self.__init_table_task_outcomes(conn)
conn.close()
def __init_table_instances(self, conn: sqlite3.Connection):
cursor = conn.cursor()
cursor.execute('''
SELECT name FROM sqlite_master WHERE type='table' AND name='instances';
''')
table_exists = cursor.fetchone()
if table_exists:
print("SQLiteDB table 'instances' already exists, skipped initialization of table.")
else:
cursor.execute('''
CREATE TABLE instances (
id INTEGER PRIMARY KEY,
owner TEXT,
calculated_demand BLOB,
number_of_persons_in_household INTEGER,
fulfilled_demand BLOB,
status TEXT,
heater_busy_methods BLOB
)
''')
conn.commit()
def __init_table_task_outcomes(self, conn: sqlite3.Connection):
cursor = conn.cursor()
cursor.execute('''
SELECT name FROM sqlite_master WHERE type='table' AND name='task_outcomes';
''')
table_exists = cursor.fetchone()
if table_exists:
print("SQLiteDB table 'task_outcomes' already exists, skipped initialization of table.")
else:
cursor.execute('''
CREATE TABLE task_outcomes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
required_workload_in_percent REAL,
runtime INTEGER,
tries INTEGER,
count INTEGER,
diff REAL,
diff_factor REAL,
actual_date TEXT,
heat_demand_per_minute REAL,
actual_heat_generated REAL,
hostname TEXT,
username TEXT
)
''')
conn.commit()
def __get_instance(self, args) -> List[Host]:
conn = sqlite3.connect(self.__db_path)
cursor = conn.cursor()
cursor.execute("SELECT * FROM instances WHERE id=?", (args,))
result = cursor.fetchone()
conn.commit()
conn.close()
return result
def __get_all_instances(self) -> List[Host]:
conn = sqlite3.connect(self.__db_path)
cursor = conn.cursor()
cursor.execute("SELECT * FROM instances")
result = cursor.fetchall()
conn.commit()
conn.close()
return result
def __update_instance(self, args):
conn = sqlite3.connect(self.__db_path)
cursor = conn.cursor()
cursor.execute('''
UPDATE instances
SET owner=?, calculated_demand=?, number_of_persons_in_household=?, fulfilled_demand=?, status=?, heater_busy_methods=?
WHERE id=?
''', (args['owner'], args['calculated_demand'], args['number_of_persons_in_household'], args['fulfilled_demand'], args['status'], args['heater_busy_methods'], args['id']))
conn.commit()
conn.close()
def __remove_instance(self, args):
conn = sqlite3.connect(self.__db_path)
cursor = conn.cursor()
cursor.execute("DELETE FROM instances WHERE id=?", (args,))
conn.commit()
conn.close()
def __remove_all_instances(self):
conn = sqlite3.connect(self.__db_path)
cursor = conn.cursor()
cursor.execute("DELETE FROM instances")
conn.commit()
conn.close()
def _get_instances(self) -> List[HeaterInstanceInterface]:
conn = sqlite3.connect(self.__db_path)
cursor = conn.cursor()
cursor.execute("SELECT * FROM instances")
potential_instances = cursor.fetchall()
conn.commit()
conn.close()
# Transform potential instances to objects which application can communicate with
instance_objects = []
for instance in potential_instances:
id, owner, calculated_demand, number_of_persons_in_household, fulfilled_demand, status, heater_busy_methods = instance
calculated_demand_json = json.loads(calculated_demand)
fulfilled_demand_json = json.loads(fulfilled_demand)
heater_busy_methods_json = json.loads(heater_busy_methods)
host = Host(hostname=id, username=owner, ssh_key_filename="", passphrase="")
heater_instance_interface = HeaterInstanceInterface(host)
instance_objects.append(heater_instance_interface)
return instance_objects
def insert_task_outcome_into_instances_table(self, heater_instance_interface: HeaterInstanceInterface, outcome_list: List[TaskOutcome]):
conn = sqlite3.connect(self.__db_path)
cursor = conn.cursor()
energy_demands = []
fulfilled_demands = []
for outcome in outcome_list:
energy_demands.append({
"date": outcome.get_actual_date().strftime("%d.%m.%Y"),
"energy_demand_in_watts": outcome.get_heat_demand_per_minute() * 60 # assuming demand per minute
})
fulfilled_demands.append({
"date": outcome.get_actual_date().strftime("%d.%m.%Y"),
"scored_energy_demand_in_watts": outcome.get_actual_heat_generated() * 60, # assuming generated heat per minute
"busy_method_used": ["hpc"] # example method used
})
calculated_demand_json = {"energy_demands": energy_demands}
fulfilled_demand_json = {"scored_demand": fulfilled_demands}
calculated_demand_blob = json.dumps(calculated_demand_json).encode('utf-8')
fulfilled_demand_blob = json.dumps(fulfilled_demand_json).encode('utf-8')
heater_busy_methods_blob = json.dumps({"busy_methods_active": ["hpc"]}).encode('utf-8')
cursor.execute('''
INSERT INTO instances (
owner, calculated_demand, number_of_persons_in_household,
fulfilled_demand, status, heater_busy_methods
) VALUES (?, ?, ?, ?, ?, ?)
''', (
heater_instance_interface.get_host().username,
calculated_demand_blob,
3, # assuming a default value, should be dynamic
fulfilled_demand_blob,
'online',
heater_busy_methods_blob
))
conn.commit()
conn.close()
def insert_task_outcome(self, outcome: TaskOutcome):
conn = sqlite3.connect(self.__db_path)
cursor = conn.cursor()
cursor.execute('''
INSERT INTO task_outcomes (
required_workload_in_percent, runtime, tries, count, diff, diff_factor, actual_date, heat_demand_per_minute, actual_heat_generated, hostname, username
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
''', (
outcome.get_required_workload_in_percent(),
outcome.get_runtime(),
outcome.get_tries(),
outcome.get_count(),
outcome.get_diff(),
outcome.get_diff_factor(),
outcome.get_actual_date().strftime("%Y-%m-%d %H:%M:%S"),
outcome.get_heat_demand_per_minute(),
outcome.get_actual_heat_generated(),
outcome.get_hostname(),
outcome.get_username()
))
conn.commit()
conn.close()
# def insert_data(self, owner: str, calculated_demand: List[dict], number_of_persons_in_household: int, fulfilled_demand: List[dict], status: str, heater_busy_methods: List[str]):
# conn = sqlite3.connect(self.__db_path)
# cursor = conn.cursor()
# calculated_demand_blob = json.dumps({"energy_demands": calculated_demand}).encode('utf-8')
# fulfilled_demand_blob = json.dumps({"scored_demand": fulfilled_demand}).encode('utf-8')
# heater_busy_methods_blob = json.dumps({"busy_methods_active": heater_busy_methods}).encode('utf-8')
# cursor.execute('''
# INSERT INTO instances (
# owner, calculated_demand, number_of_persons_in_household,
# fulfilled_demand, status, heater_busy_methods
# ) VALUES (?, ?, ?, ?, ?, ?)
# ''', (
# owner,
# calculated_demand_blob,
# number_of_persons_in_household,
# fulfilled_demand_blob,
# status,
# heater_busy_methods_blob
# ))
# conn.commit()
# conn.close()

View File

@@ -0,0 +1,24 @@
# author Aaron Moser
import threading
class GlobalOutcome:
def __init__(self):
self.tries_count_lock = threading.Lock()
self.tries = 0
self.count = 0
def update_tries_count(self, new_tries:int, new_count:int):
with self.tries_count_lock:
self.tries += new_tries
self.count += new_count
def get_tries_count(self):
with self.tries_count_lock:
return self.tries, self.count
def reset_tries_count(self):
with self.tries_count_lock:
tries = 0
count = 0

View File

@@ -0,0 +1,148 @@
"""
HEATING DEMAND EXAMPLE
"""
import datetime
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
# comment it if you dowloaded demod from GitHub
import demod
from demod.simulators.base_simulators import SimLogger
from demod.datasets.GermanTOU.loader import GTOU
from demod.datasets.OpenPowerSystems.loader import OpenPowerSystemClimate
from demod.datasets.Germany.loader import GermanDataHerus
from demod.simulators.sparse_simulators import SparseTransitStatesSimulator
from demod.simulators.weather_simulators import RealClimate
from demod.simulators.lighting_simulators import CrestLightingSimulator
from demod.simulators.heating_simulators import FiveModulesHeatingSimulator
from demod.simulators.appliance_simulators import SubgroupApplianceSimulator
from demod.simulators.weather_simulators import RealInterpolatedClimate
#%% INITIALIZATION
def heat_demand(seed, date):
np.random.seed(seed)
data = GermanDataHerus(version='v0.1')
# time_ = datetime.datetime(2018, 1, 1, 0, 0, 0) # starting time of simulation
time_ = date
days = 1 # number of days to be simulated
n_households = 1 # number of households to be simulated
subgroup = {
'n_residents': 2,
'household_type': 2,
} # n° residents should be consisten with the household type
# Here the available household types:
# 1 = One person household
# 2 = Couple without kids
# 3 = Single Parent with at least one kid under 18 and the other under 27
# 4 = Couple with at least one kid under 18 and the other under 27
# 5 = Others
# Occupancy
occ_sim = SparseTransitStatesSimulator(
n_households, subgroup, data,
logger=SimLogger('get_active_occupancy', 'get_occupancy'),
start_datetime=time_
)
# Climate
climate_sim = RealInterpolatedClimate(
data,
start_datetime=time_,
logger=SimLogger(
'get_irradiance', 'get_outside_temperature'
),
# choose the one minute step size
step_size = datetime.timedelta(minutes=1),
)
# Heating system
# It integrates (1) heating demand, (2) thermostat, (3) building thermal
# dynamics, (4) heating system control and (5) operation
heating_sim = FiveModulesHeatingSimulator(
n_households=n_households,
initial_outside_temperature=climate_sim.get_outside_temperature(),
# The algo that computes the heat demand like in CREST
heatdemand_algo='heat_max_emmiters',
logger=SimLogger(
'get_temperatures',
'get_dhw_heat_demand',
'get_sh_heat_demand',
'get_heat_outputs',
'get_power_demand',
)
)
# Appliances, water fixtures
app_sim = SubgroupApplianceSimulator(
[subgroup], [n_households], data=data,
initial_active_occupancy=occ_sim.get_active_occupancy(),
start_datetime=time_,
logger=SimLogger(
'get_power_demand', 'get_dhw_heating_demand',
'get_current_power_consumptions', aggregated=False )
)
# Lighting
lighting_sim = CrestLightingSimulator(
n_households=n_households,
data=data,
logger=SimLogger('get_power_demand'),
bulbs_sampling_algo='randn'
)
#%% SIMULATION
appliance_usage = []
for _ in range(24*days*6):
# every 10 minutes
occ_sim.step()
for __ in range(10):
# every 1 minute
climate_sim.step()
app_sim.step(
occ_sim.get_active_occupancy()
)
lighting_sim.step(
occ_sim.get_active_occupancy(),
climate_sim.get_irradiance()
)
heating_sim.step(
climate_sim.get_outside_temperature(),
climate_sim.get_irradiance(),
app_sim.get_dhw_heating_demand(),
occ_sim.get_thermal_gains(),
lighting_sim.get_thermal_gains(),
app_sim.get_thermal_gains(),
external_target_temperature={'space_heating':20},
)
# you can pass different target temperature profiles
# here it is considered a constant temperature of 20°C
# store appliance usage
appliance_usage.append(app_sim.get_current_usage())
# Wattzahlen in 1 Minuten Schritten: Summe in kWh
power_demand_1_minute_steps = heating_sim.logger.get('get_power_demand')
return power_demand_1_minute_steps
# Returns power demand in kWh as array where each element represents demand of 10 minutes
def heat_demand_per_minute_in_kW(power_demand_1_minute_steps):
# Maps array of elements to function, that divides each element by 1000
# By dividing by 1000, W is transformed to kW
return np.array(power_demand_1_minute_steps) / 1000
# date = datetime.datetime(2018,4,13)
# seed =100005
# bedarf = heat_requirement_daily(heat_requirement(seed, date))
# print(f"Der Tageswärmebedarf am {date.day}. {date.month}. ist {bedarf:.2f} kWh")

View File

@@ -0,0 +1,11 @@
# Heater
## Kompilieren
### Ohne Multithreading
`g++ ./monteCarloPi.cpp -O3 -o monteCarloPi`
### Mit Multithreading
`g++ ./monteCarloPi.cpp -O3 -o monteCarloPi -fopenmp`

View File

@@ -0,0 +1,112 @@
import paramiko
import weakref
import math
import json
from heater.host import Host
class HeaterInstance:
def __init__(self, host: Host, iter=1000):
"""Creates a heater instance
Args:
host (Host): host to connect to
iter (int, optional): Number of iterations to compute in one go. Defaults to 1000.
Raises:
IOError: if there was an error reading the file
paramiko.ssh_exception.PasswordRequiredException: if the private key file is encrypted, and password is None
paramiko.ssh_exception.SSHException: if the key file is invalid
"""
self.host = host
self.iter = iter
self.__ssh = paramiko.SSHClient()
self.__ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
if host.passphrase:
self.__key = paramiko.RSAKey.from_private_key_file(host.ssh_key_filename, host.passphrase)
else:
self.__key = paramiko.RSAKey.from_private_key_file(host.ssh_key_filename)
self.__finalizer = weakref.finalize(self, self.close)
def run(self, percent: float, milliseconds: int) -> tuple[int, int, float, float]:
"""Runs heater for a time dependend on given parameters.
Returns number of tries, number of hits, difference between wanted runtime and executed runtime, difference as a factor.
Differnce is negative when was shorter than expected or positive when longer than expected.
Difference factor is the percent of the expected time the program ran.
Args:
percent (float): cpu usage percentage
milliseconds (int): time to run
Returns:
tuple[int, int, float, float]: tries, count, difference, difference as factor
"""
runtime = percent * milliseconds
cmd = f'./monteCarloPi -d {math.floor(runtime)} -i {self.iter}'
duration = 0.
tries = 0
count = 0
try:
_, stdout, stderr = self.__ssh.exec_command(cmd)
if stderr.readlines() == []:
try:
result_json = json.loads(stdout.read().decode().strip("\n"))
tries = result_json["tries"]
count = result_json["count"]
duration = result_json["durationMilli"]
except json.JSONDecodeError:
pass
except paramiko.SSHException:
pass
diff = (duration - runtime) * (1 / percent)
diffFactor = duration / runtime
return tries, count, diff, diffFactor
def connect(self):
"""Connect to heater per ssh.
Raises:
paramiko.ssh_exception.BadHostKeyException: if the server's host key could not be verified.
paramiko.ssh_exception.AuthenticationException: if authentication failed.
paramiko.ssh_exception.UnableToAuthenticate: if authentication failed (when auth_strategy is non-None; and note that this is a subclass of AuthenticationException).
socket.error: if a socket error (other than connection-refused or host-unreachable) occurred while connecting.
paramiko.ssh_exception.NoValidConnectionsError: if all valid connection targets for the requested hostname (eg IPv4 and IPv6) yielded connection-refused or host-unreachable socket errors.
paramiko.ssh_exception.SSHException: if there was any other error connecting or establishing an SSH session.
"""
self.__ssh.connect(hostname=self.host.hostname, username=self.host.username, pkey=self.__key)
@property
def open(self) -> bool:
"""Returns if ssh connection is open.
Returns:
bool: is ssh connection open
"""
if self.__ssh.get_transport() is not None:
return self.__ssh.get_transport().is_active()
return False
def close(self):
"""Closes ssh connection.
"""
if self.open:
self.__ssh.close()
def cleanup(self):
"""cleans heater instance up.
Only call once.
Don't use instance after cleanup.
Is called automatically by garbage collection.
"""
self.__finalizer()
@property
def cleandup(self) -> bool:
"""Returns if object was alreade cleand up.
Returns:
bool: was object cleand up
"""
return not self.__finalizer.alive

View File

@@ -0,0 +1,17 @@
class Host:
def __init__(self, hostname: str, username: str, ssh_key_filename: str, passphrase: str):
"""Create host to connect to.
Args:
hostname (str): hostname to connect to
username (str): user to use for connection
ssh_key_filename (str): path to ssh key file
passphrase (str): passphrase for ssh key
"""
self.hostname = hostname
self.username = username
self.ssh_key_filename = ssh_key_filename
self.passphrase = passphrase
def equals(self, other):
return (self.hostname == other.hostname) and (self.username == other.username)

View File

@@ -0,0 +1,146 @@
#include <iostream>
#include <chrono>
#include <random>
#include <algorithm>
#include <string>
#include <filesystem>
#if defined(_OPENMP)
#include <omp.h>
#endif
class InputParser
{
public:
InputParser(int &argc, char **argv)
{
for (int i = 1; i < argc; ++i)
{
this->tokens.push_back(std::string(argv[i]));
}
}
const std::string &getCmdOption(const std::string &option) const
{
std::vector<std::string>::const_iterator itr;
itr = std::find(this->tokens.begin(), this->tokens.end(), option);
if (itr != this->tokens.end() && ++itr != this->tokens.end())
{
return *itr;
}
static const std::string empty_string("");
return empty_string;
}
bool cmdOptionExists(const std::string &option) const
{
return std::find(this->tokens.begin(), this->tokens.end(), option) != this->tokens.end();
}
private:
std::vector<std::string> tokens;
};
struct MonteCarloResult
{
int64_t tries = 0;
int64_t count = 0;
double durationMilli = 0;
};
const int defaultIterationsPerChunk = 1000;
MonteCarloResult computePI(int milliseconds, int iterationsPerChunk)
{
std::chrono::duration<double, std::milli> diff;
auto start = std::chrono::system_clock::now();
int64_t tries = 0;
int64_t count = 0;
#if defined(_OPENMP)
#pragma omp parallel reduction(+ : count, tries)
#endif
{
#if defined(_OPENMP)
std::mt19937 rng(static_cast<unsigned>(omp_get_thread_num()));
#else
std::mt19937 rng(0);
#endif
std::uniform_real_distribution<double> dist(0., 1.);
do
{
#if defined(_OPENMP)
#pragma omp for nowait
#endif
for (int64_t i = 0; i < iterationsPerChunk; ++i)
{
double x = dist(rng);
double y = dist(rng);
if ((x * x + y * y) < 1)
{
count++;
}
tries++;
}
#if defined(_OPENMP)
#pragma omp master
#endif
{
auto end = std::chrono::system_clock::now();
diff = end - start;
}
#if defined(_OPENMP)
#pragma omp barrier
#endif
} while (diff.count() < milliseconds);
}
auto end = std::chrono::system_clock::now();
diff = end - start;
MonteCarloResult result{tries, count, diff.count()};
return result;
}
int main(int argc, char *argv[])
{
// parse parameters
InputParser input(argc, argv);
std::filesystem::path path(argv[0]);
// help
if (input.cmdOptionExists("-h") || argc <= 1)
{
std::cout << "Usage: " << path.filename() << " -d <milliseconds>" << std::endl;
std::cout << " Options:" << std::endl;
std::cout << " -d <milliseconds>: Number of milliseconds to compute pi for" << std::endl;
std::cout << " -i <iterations>: Number of iterations to compute in one go" << std::endl;
return 0;
}
// error if -d parameter not set
if (!input.cmdOptionExists("-d"))
{
std::cout << "Error: Option -d must be set" << std::endl;
return -1;
}
// check if -i parameter is set and set iterations
int iterationsPerChunk = defaultIterationsPerChunk;
if (input.cmdOptionExists("-i"))
{
iterationsPerChunk = std::stoi(input.getCmdOption("-i"));
}
// get duration
const int duration = std::stoi(input.getCmdOption("-d"));
// run Monte Carlo
MonteCarloResult result = computePI(duration, iterationsPerChunk);
// format result
std::cout << "{\"tries\":" << result.tries << ", \"count\":" << result.count << ", \"durationMilli\":" << result.durationMilli << "}" << std::endl;
return 0;
}

View File

@@ -0,0 +1,32 @@
import paramiko
from credentials import hostname, username, ssh_key_filename, passphrase
from host import Host
from heater_instance import HeaterInstance
def test_ssh():
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
key = paramiko.RSAKey.from_private_key_file(ssh_key_filename, passphrase)
ssh.connect(hostname, username, pkey=key)
stdin, stdout, stderr = ssh.exec_command("ls")
print(stdout.readlines())
print(stderr.readlines())
ssh.close()
def test_heater():
host = Host(hostname, username, ssh_key_filename, passphrase)
heater = HeaterInstance(host)
heater.connect()
tries, count, diff = heater.run(0.5, 10)
print("diff = %s" % diff)
print("pi = %f" % ((count / tries) * 4))
heater.close()
test_heater()
# test_ssh()

View File

@@ -0,0 +1,96 @@
# author Aaron Moser
import paramiko
import socket
import paramiko.ssh_exception
from enum import Enum
from heater.heater_instance import HeaterInstance
from heater.host import Host
from constants import ConnectErrorCode
class HeaterInstanceState(Enum):
INITIALIZED = 1
RUNNING = 2
ERROR = 3
FINISHED = 4
# Interface to class HeaterInstance
class HeaterInstanceInterface:
def __init__(self, host: Host):
self.host = host
self._heater_instance = HeaterInstance(self.host)
self._seed = (abs(hash(self.host.hostname + self.host.username)) % 4294967295)
self._state: HeaterInstanceState = HeaterInstanceState.INITIALIZED
# Tries to connect to instance via ssh, returns an error code if failing
def connect(self) -> ConnectErrorCode:
try:
if (not self._heater_instance.open):
self._heater_instance.connect()
#print("Connected to the SSH server successfully.")
return ConnectErrorCode.CONNECTED
else:
#print("Already connected to SSH server.")
return ConnectErrorCode.ALREADY_CONNECTED
except paramiko.ssh_exception.BadHostKeyException as e:
print(f"BadHostKeyException: The server's host key could not be verified. {e}")
self._state = HeaterInstanceState.ERROR
return ConnectErrorCode.UNVERIFIED_KEY
except paramiko.ssh_exception.AuthenticationException as e:
print(f"AuthenticationException: Authentication failed. {e}")
self._state = HeaterInstanceState.ERROR
return ConnectErrorCode.AUTHENTICATION_FAILED
except paramiko.ssh_exception.UnableToAuthenticate as e:
print(f"UnableToAuthenticate: Authentication failed with the provided strategy. {e}")
self._state = HeaterInstanceState.ERROR
return ConnectErrorCode.AUTHENTICATION_FAILED
except socket.error as e:
print(f"SocketError: A socket error occurred while connecting. {e}")
self._state = HeaterInstanceState.ERROR
return ConnectErrorCode.SOCKET_ERROR
except paramiko.ssh_exception.NoValidConnectionsError as e:
print(f"NoValidConnectionsError: All connection attempts failed. {e}")
self._state = HeaterInstanceState.ERROR
return ConnectErrorCode.ALL_CONNECTION_ATTEMPTS_FAILED
except paramiko.ssh_exception.SSHException as e:
print(f"SSHException: An error occurred while connecting or establishing an SSH session. {e}")
self._state = HeaterInstanceState.ERROR
return ConnectErrorCode.SSH_EXCEPTION
# Closes ssh connection to instance
def close(self) -> None:
self._heater_instance.close()
# Sends a task to an instance to produce the required heat
def run(self, percent: float, seconds: float) -> tuple[int, int, float, float]:
if (not self._heater_instance.open):
self.connect()
result = 0,0,0
if self._state != HeaterInstanceState.ERROR:
self._state = HeaterInstanceState.RUNNING
try:
result = self._heater_instance.run(percent, seconds)
except Exception as e:
print("Connection closed.")
if self._state == HeaterInstanceState.RUNNING:
self._state = HeaterInstanceState.FINISHED
return result
# Returns host of instance
def get_host(self) -> Host:
return self.host
def get_seed(self) -> int:
return self._seed
def get_state(self) -> HeaterInstanceState:
return self._state

View File

@@ -0,0 +1,108 @@
# author Aaron Moser
from typing import List
from enum import Enum
import constants
from heater_instance_interface import HeaterInstanceInterface
from heater_instance_worker_thread import HeaterInstanceWorkerThread
from database_interface import DatabaseInterface
from global_outcome import GlobalOutcome
from heater.host import Host
from simulation import Simulation
from datetime import datetime
class HeaterInstanceInterfaceManager:
class HeaterInstanceInterfaceManagerErrorCode(Enum):
HEATER_INSTANCE_ALREADY_EXISTS = 1
HEATER_INSTANCE_ADDED = 2
HEATER_INSTANCE_ADDED_AND_STARTED = 3
HEATER_INSTANCE_REMOVED = 4
def __init__(self, global_outcome: GlobalOutcome, mode: Simulation.Mode, start_date: datetime, end_date: datetime, database_interface: DatabaseInterface):
self.global_outcome = global_outcome
self.heater_instances: List[HeaterInstanceInterface] = []
self.__worker_thread_pool: List[HeaterInstanceWorkerThread] = []
self._mode: Simulation.Mode = mode
self.database_interface = database_interface
self.start_date = start_date
self.end_date = end_date
# Instance management --------------------------------------------------------------------------------------
# Adds new heater instance to list of instances
def add_heater_instance_and_start_thread_if_running(self, host: Host, simulation_state: constants.State) -> HeaterInstanceInterfaceManagerErrorCode:
heater_instance_exists = False
for heater_instance in self.heater_instances:
if heater_instance.get_host().equals(host):
heater_instance_exists = True
return HeaterInstanceInterfaceManager.HeaterInstanceInterfaceManagerErrorCode.HEATER_INSTANCE_ALREADY_EXISTS
if not heater_instance_exists:
new_heater_instance_interface = HeaterInstanceInterface(host)
self.heater_instances.append(new_heater_instance_interface)
if simulation_state == constants.State.Running:
new_thread = HeaterInstanceWorkerThread(self.global_outcome, self._mode, new_heater_instance_interface, self.start_date, self.end_date, self.database_interface)
self.__worker_thread_pool.append(new_thread)
new_thread.run()
return HeaterInstanceInterfaceManager.HeaterInstanceInterfaceManagerErrorCode.HEATER_INSTANCE_ADDED_AND_STARTED
else:
return HeaterInstanceInterfaceManager.HeaterInstanceInterfaceManagerErrorCode.HEATER_INSTANCE_ADDED
# Removes heater instance from list of instances
def remove_heater_instance(self, host) -> HeaterInstanceInterfaceManagerErrorCode:
heater_instance_exists = False
for heater_instance in self.heater_instances:
if heater_instance.get_host().equals(host):
heater_instance_exists = True
if heater_instance_exists:
self.heater_instances.remove(HeaterInstanceInterface(host))
for thread in self.__worker_thread_pool:
if thread.get_instance_interface().get_host().equals(host):
thread.stop()
return HeaterInstanceInterfaceManager.HeaterInstanceInterfaceManagerErrorCode.HEATER_INSTANCE_REMOVED
# Returns list of all heater instances
def get_heater_instances(self):
return self.heater_instances
def show_heater_instances(self):
if len(self.heater_instances) == 0:
print("No instances registered.")
else:
for heater_instance in self.heater_instances:
print("Hostname: " + heater_instance.get_host().hostname + ", Username: " + heater_instance.get_host().username + ", Seed: " + str(heater_instance.get_seed()))
def get_heater_instances_count(self) -> int:
return len(self.heater_instances)
def set_start_and_end_date(self, start_date: datetime, end_date: datetime):
self.start_date = start_date
self.end_date = end_date
# Threading-------------------------------------------------------------------------------------------------
# Changes simulation mode, by restarting all threads with new mode
def change_simulation_mode(self, mode: Simulation.Mode):
self._mode = mode
self.stop_threads()
self.instantiate_threads()
self.start_threads()
# Stops all running threads
def stop_threads(self):
for worker_thread in self.__worker_thread_pool:
if worker_thread.get_stop_event_state() == False:
worker_thread.stop()
# Clears list of threads and creates new threads which are then appended to cleared list
def instantiate_threads(self):
self.__worker_thread_pool: List[HeaterInstanceWorkerThread] = []
for heater_instance_interface in self.heater_instances:
self.__worker_thread_pool.append(HeaterInstanceWorkerThread(self.global_outcome, self._mode, heater_instance_interface, self.start_date, self.end_date, self.database_interface))
# Starts threads in list of threads if state of instance is 'INITIALIZED'
def start_threads(self):
for worker_thread in self.__worker_thread_pool:
if worker_thread.get_stop_event_state() == False:
worker_thread.start()

View File

@@ -0,0 +1,46 @@
# author Aaron Moser, Theresa Herr
import datetime
import threading
from heater_instance_interface import HeaterInstanceInterface
from database_interface import DatabaseInterface
from global_outcome import GlobalOutcome
from simulation import Simulation
import tasks.daily_task
import tasks.realtime_task
class HeaterInstanceWorkerThread(threading.Thread):
def __init__(self, global_outcome: GlobalOutcome, mode: Simulation.Mode, heater_instance_interface: HeaterInstanceInterface, start_date: datetime.datetime, end_date: datetime.datetime, database_interface: DatabaseInterface):
super().__init__()
self.global_outcome: GlobalOutcome = global_outcome
self.__mode: Simulation.Mode = mode
self._stop_event: threading.Event = threading.Event()
self.heater_instance_interface: HeaterInstanceInterface = heater_instance_interface
self.start_date: datetime.datetime = start_date
self.end_date: datetime.datetime = end_date
self.db_interface: DatabaseInterface = database_interface
def run(self):
while not self._stop_event.is_set():
match self.__mode:
case Simulation.Mode.REALTIME:
tasks.realtime_task.realtime_task(self.global_outcome, self.heater_instance_interface, self.start_date, self.end_date, self.save_outcome)
case Simulation.Mode.DAILY:
tasks.daily_task.daily_task(self.global_outcome, self.heater_instance_interface, self.start_date, self.end_date, self.save_outcome)
case _:
print("Error unknown mode.")
def get_instance_interface(self) -> HeaterInstanceInterface:
return self.heater_instance_interface
def stop(self):
self.heater_instance_interface.close()
self._stop_event.set()
def get_stop_event_state(self) -> bool:
return self._stop_event.is_set()
def save_outcome(self, outcome_list: list):
for outcome in outcome_list:
self.db_interface.insert_task_outcome(outcome)

View File

@@ -0,0 +1,383 @@
# author Aaron Moser
#---------------------------------------------------------------------------------------------------------------
import constants
from typing import List
from datetime import datetime, timedelta
from heater_instance_interface_manager import HeaterInstanceInterfaceManager
from global_outcome import GlobalOutcome
from simulation import Simulation
from heater.host import Host
#---------------------------------------------------------------------------------------------------------------
class LoadBalancer:
"""Implementation of load balancer, allows to add instances and start the simulation.
"""
#---------------------------------------------------------------------------------------------------------------
def __init__(
self,
global_outcome,
db_interface,
simulation_mode: Simulation.Mode,
start_date: datetime,
end_date: datetime
):
"""Instantiates a LoadBalancer object.
Args:
self (LoadBalancer): this current object, which is instantiated.
db_interface (DatabaseInterface): interface class to database.
simulation_mode (Simulation.Mode): current mode of simulation.
"""
self.state: constants.State = constants.State.Stopped
self.global_outcome: GlobalOutcome = global_outcome
self.db_interface = db_interface
self.simulation = Simulation()
self.simulation.set_mode(simulation_mode)
self.running = False
self.start_date = start_date
self.end_date = end_date
self.heater_instance_interface_manager: HeaterInstanceInterfaceManager = HeaterInstanceInterfaceManager(global_outcome, simulation_mode, start_date, end_date, db_interface)
#---------------------------------------------------------------------------------------------------------------
def run(self) -> None:
"""Loop of asking user for input and processing entered command.
Args:
self (LoadBalancer): this current object, on which method is called.
Returns:
None
"""
self.print_start_message()
self.running = True
while self.running:
self._process_first_level_command(self._get_command_args())
#---------------------------------------------------------------------------------------------------------------
def print_start_message(self) -> None:
"""Prints message, load balancer started
Args:
self (LoadBalancer): this current object, on which method is called.
Returns:
None
"""
print(constants.START_MESSAGE)
#---------------------------------------------------------------------------------------------------------------
def _get_command_args(self) -> List[str]:
"""Prompts user to enter command and returns command string array splitted by spaces
Args:
self (LoadBalancer): this current object, on which method is called.
Returns:
List[str]: Partstrings which are created by splitting the command entered by user at spaces.
"""
return input("-------------------------------\nPlease enter command (Enter 'help' to see all commands)\nCommand: ").lower().split(" ")
#---------------------------------------------------------------------------------------------------------------
def _process_first_level_command(self, command_args) -> None:
"""Processes the first level (command_args[0]) command.
Args:
self (LoadBalancer): this current object, on which method is called.
command_args (List[str]): partstrings of command string split at spaces.
Returns:
None
"""
match command_args[0]:
#
case constants.START_SIMULATION_COMMAND:
if self.heater_instance_interface_manager.get_heater_instances_count() >= constants.MINIMUM_NUMBER_CLIENTS:
self.start_simulation(command_args)
else:
print("Please add more instances, before starting the simulation.")
case constants.STOP_SIMULATION_COMMAND:
self.stop_simulation()
#
case constants.SET_SIMULATION_MODE_COMMAND:
if self.heater_instance_interface_manager.get_heater_instances_count() >= constants.MINIMUM_NUMBER_CLIENTS:
self.set_simulation_mode(command_args)
else:
print("Please add more instances, before re-setting the simulation mode.")
#
case constants.ADD_INSTANCE_COMMAND:
if self.heater_instance_interface_manager.get_heater_instances_count() < constants.MAXIMUM_NUMBER_CLIENTS:
self.add_instance(command_args)
else:
print("Maximum number of clients reached, you have to remove an instance before you can add another.")
#
case constants.REMOVE_INSTANCE_COMMAND:
self.remove_instance(command_args)
#
case constants.SHOW_INSTANCES_COMMAND:
self.show_instances()
case constants.DISPLAY_OUTCOME_COMMAND:
self.display_outcome()
#
case constants.HELP_COMMAND:
self.print_help()
#
case constants.EXIT_COMMAND:
self.exit_application()
# If user enters command, which doesn't match any case above.
case _:
print("Unknown command: " + command_args[0])
#---------------------------------------------------------------------------------------------------------------
def start_simulation(self, command_args) -> None:
"""Starts simulation.
Args:
self (LoadBalancer): this current object, on which method is called.
command_args (List[str]): partstrings of command string split at spaces.
Returns:
None
"""
if len(command_args) > 1:
match command_args[1]:
case constants.SET_SIMULATION_MODE_COMMAND_REALTIME:
if len(command_args) == 4:
start_date = command_args[2]
end_date = command_args[3]
date_format = "%d.%m.%Y"
self.start_date = datetime.strptime(start_date, date_format)
self.end_date = datetime.strptime(end_date, date_format)
self.start_simulation_in_mode(Simulation.Mode.REALTIME)
elif len(command_args) == 3:
start_date = command_args[2]
date_format = "%d.%m.%Y"
self.start_date = datetime.strptime(start_date, date_format)
one_day_difference = timedelta(days=1)
self.end_date = start_date + one_day_difference
self.start_simulation_in_mode(Simulation.Mode.REALTIME)
else:
self.start_simulation_in_mode(Simulation.Mode.REALTIME)
case constants.SET_SIMULATION_MODE_COMMAND_DAILY:
if len(command_args) == 4:
start_date = command_args[2]
end_date = command_args[3]
date_format = "%d.%m.%Y"
self.start_date = datetime.strptime(start_date, date_format)
self.end_date = datetime.strptime(end_date, date_format)
self.start_simulation_in_mode(Simulation.Mode.DAILY)
elif len(command_args) == 3:
start_date = command_args[2]
date_format = "%d.%m.%Y"
self.start_date = datetime.strptime(start_date, date_format)
one_day_difference = timedelta(days=1)
self.end_date = start_date + one_day_difference
self.start_simulation_in_mode(Simulation.Mode.DAILY)
else:
self.start_simulation_in_mode(Simulation.Mode.DAILY)
case _:
print("Unknown simulation command.")
else:
self.heater_instance_interface_manager.change_simulation_mode(self.simulation.get_mode())
def start_simulation_in_mode(self, simulation_mode: Simulation.Mode):
self.heater_instance_interface_manager.set_start_and_end_date(self.start_date, self.end_date)
self.heater_instance_interface_manager.change_simulation_mode(simulation_mode)
self.simulation.set_mode(simulation_mode)
self.state: constants.State = constants.State.Running
match simulation_mode:
case Simulation.Mode.DAILY:
print("Simulation started in daily mode. With start date: " + str(self.start_date) + " and end date: " + str(self.end_date))
case Simulation.Mode.REALTIME:
print("Simulation started in realtime mode. With start date: " + str(self.start_date) + " and end date: " + str(self.end_date))
#---------------------------------------------------------------------------------------------------------------
def stop_simulation(self) -> None:
if self.state == constants.State.Running:
self.heater_instance_interface_manager.stop_threads()
self.state = constants.State.Stopped
print("Stopped simulation. To restart enter StartSimulation [mode] [start date] [end date].")
else:
print("Error: Simulation is not running. Can't stop if not running.")
#---------------------------------------------------------------------------------------------------------------
def set_simulation_mode(self, command_args) -> None:
"""If simulation is running, a user has to use stop simulation and start again or just SetSimulationMode.
Will stop currently running threads and restart them with new mode.
Args:
self (LoadBalancer): this current object, on which method is called.
Returns:
None
"""
if len(command_args) > 1:
match command_args[1]:
case constants.SET_SIMULATION_MODE_COMMAND_REALTIME:
if self.get_approval():
self.heater_instance_interface_manager.change_simulation_mode(Simulation.Mode.REALTIME)
self.simulation.set_mode(Simulation.Mode.REALTIME)
else:
print("Continuing at current mode.")
case constants.SET_SIMULATION_MODE_COMMAND_DAILY:
if self.get_approval():
self.heater_instance_interface_manager.change_simulation_mode(Simulation.Mode.DAILY)
self.simulation.set_mode(Simulation.Mode.DAILY)
else:
print("Continuing at current mode.")
else:
print("Error, simulation mode missing, please re-enter command.")
#---------------------------------------------------------------------------------------------------------------
def get_approval(self) -> bool:
"""Prompts user if s/he really wants to stop the simulation.
Args:
self (LoadBalancer): this current object, on which method is called.
Returns:
bool: True if user enters 'Y|y', False otherwise.
"""
return input("Warning, current simulation will stop. Do you really want to stop? (Y)es (N)o\nCommand: ").lower() == "y"
#---------------------------------------------------------------------------------------------------------------
def add_instance(self, command_args) -> None:
"""Add instance to instance interface manager and start worker thread.
Args:
self (LoadBalancer): this current object, on which method is called.
command_args (List[str]): partstrings of command string split at spaces.
Returns:
None
"""
hostname = command_args[1]
username = command_args[2]
ssh_key_filename = command_args[3]
passphrase = ""
if len(command_args) >= 5:
passphrase = command_args[4]
match self.heater_instance_interface_manager.add_heater_instance_and_start_thread_if_running(Host(hostname, username, ssh_key_filename, passphrase), self.state):
case HeaterInstanceInterfaceManager.HeaterInstanceInterfaceManagerErrorCode.HEATER_INSTANCE_ALREADY_EXISTS:
print("Instance already exists.")
case HeaterInstanceInterfaceManager.HeaterInstanceInterfaceManagerErrorCode.HEATER_INSTANCE_ADDED:
print("Instance added.")
case HeaterInstanceInterfaceManager.HeaterInstanceInterfaceManagerErrorCode.HEATER_INSTANCE_ADDED_AND_STARTED:
print("Instance added and thread started.")
case _:
print("Unkown error code.")
#---------------------------------------------------------------------------------------------------------------
def remove_instance(self, command_args) -> None:
"""Removes instance indicated by host- and username given in command_args and stops worker thread.
Args:
self (LoadBalancer): this current object, on which method is called.
command_args (List[str]): partstrings of command string split at spaces.
Returns:
None.
"""
hostname = command_args[1]
username = command_args[2]
match self.heater_instance_interface_manager.remove_heater_instance(Host(hostname, username, None, None)):
case HeaterInstanceInterfaceManager.HeaterInstanceInterfaceManagerErrorCode.HEATER_INSTANCE_REMOVED:
print("Instance removed successfully.")
case _:
print("Unknown error code at remove_instance received.")
#---------------------------------------------------------------------------------------------------------------
def show_instances(self) -> None:
"""Shows instances by printing them to console.
Args:
self (LoadBalancer): this current object, on which method is called.
Returns:
None.
"""
self.heater_instance_interface_manager.show_heater_instances()
#---------------------------------------------------------------------------------------------------------------
def display_outcome(self) -> None:
"""Displays global outcome stats by printing them to console.
Args:
self (LoadBalancer): this current object, on which method is called.
Returns:
None.
"""
tries, count = self.global_outcome.get_tries_count()
pi:float = 0.0
if tries != 0:
pi = float(count) / float(tries) * float(4)
print("Count: " + str(count) + ", Tries: " + str(tries) + ", PI: " + str(pi))
#---------------------------------------------------------------------------------------------------------------
def print_help(self) -> None:
"""Prints list of commands with description from constants.
Args:
self (LoadBalancer): this current object, on which method is called.
Returns:
None.
"""
for command_description in constants.COMMANDS_HELP:
print(command_description)
#---------------------------------------------------------------------------------------------------------------
def exit_application(self) -> None:
"""Exits application by stopping all threads and sets own state to stopped,
then stops while loop and terminates.
Args:
self (LoadBalancer): this current object, on which method is called.
Returns:
None.
"""
self.heater_instance_interface_manager.stop_threads()
self.simulation.set_state(Simulation.State.STOPPED)
self.running = False
#---------------------------------------------------------------------------------------------------------------

View File

@@ -0,0 +1,31 @@
# author Aaron Moser
from enum import Enum
class Simulation:
class State(Enum):
INITIALIZED = 1
RUNNING = 2
PAUSED = 3
STOPPED = 4
class Mode(Enum):
REALTIME = 1
DAILY = 2
def __init__(self):
self.state = Simulation.State.INITIALIZED
self.mode = Simulation.Mode.REALTIME
def set_state(self, state):
self.state = state
def get_state(self):
return self.state
def set_mode(self, mode):
self.mode = mode
def get_mode(self):
return self.mode

View File

@@ -0,0 +1,22 @@
# author Aaron Moser
import datetime
from database_interface import DatabaseInterface
from global_outcome import GlobalOutcome
from load_balancer_impl import LoadBalancer
from simulation import Simulation
def main():
# Establish connection to db, if not possible, print error
# If error suggests, that db couldn't be found, prompt user to enter location of db
# If error suggests, that user / pw were wrong, prompt user to reenter creds
# Retry to connect to db
# Create instance of DBInterface class
db_path = "sqlitedb.db"
db_interface = DatabaseInterface(db_path)
global_outcome = GlobalOutcome()
LoadBalancer(global_outcome, db_interface, Simulation.Mode.DAILY, datetime.datetime(2018,4,1), datetime.datetime(2018,4,2)).run()
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,59 @@
import time
import constants
import heat_demand_simulator
from datetime import datetime, timedelta
from typing import List
from heater_instance_interface import HeaterInstanceInterface
from global_outcome import GlobalOutcome
from tasks.task_outcome import TaskOutcome
def daily_task(global_outcome: GlobalOutcome, heater_instance_interface: HeaterInstanceInterface, start_date: datetime, end_date: datetime, store_db_function):
one_minute_difference = timedelta(minutes=1)
actual_date: datetime = start_date
# Get difference in days between start and end date.
difference: timedelta = end_date - start_date
stopped = False
for i in range(difference.days):
outcome_list:List[TaskOutcome] = []
seed:int = heater_instance_interface.get_seed()
heat_demand_per_minute_in_kW_day = heat_demand_simulator.heat_demand_per_minute_in_kW(heat_demand_simulator.heat_demand(seed, actual_date))
for heat_demand_per_minute in heat_demand_per_minute_in_kW_day:
required_workload_in_percent = diff = diff_factor = 0.0
tries = count = 0
if heat_demand_per_minute > 0.0:
# Calculate percentage of workload for cpu while runtime.
# example: 1.5 / 0.04 / 100 = 0.375 = 37.5%
required_workload_in_percent = heat_demand_per_minute / constants.MAX_WORKLOAD_IN_KW_ONE_PERCENT / 100
try:
start_time = time.time()
tries, count, diff, diff_factor = heater_instance_interface.run(required_workload_in_percent, constants.DAILY_MODE_MILLISECONDS_MINUTE_RATIO)
end_time = time.time()
runtime = end_time - start_time
sleep_time = 0.06 - runtime
if sleep_time > 0:
time.sleep(sleep_time)
except:
print()
stopped = True
break
global_outcome.update_tries_count(tries, count)
else:
time.sleep(0.06)
actual_heat_generated = diff_factor * heat_demand_per_minute
outcome_list.append(TaskOutcome(required_workload_in_percent, constants.DAILY_MODE_MILLISECONDS_MINUTE_RATIO, tries, count, diff / 1000, diff_factor, actual_date, heat_demand_per_minute, actual_heat_generated, heater_instance_interface.get_host().hostname, heater_instance_interface.get_host().username))
actual_date += one_minute_difference
if stopped:
break
store_db_function(outcome_list)

View File

@@ -0,0 +1,55 @@
import time
import constants
import heat_demand_simulator
from datetime import datetime, timedelta
from typing import List
from heater_instance_interface import HeaterInstanceInterface
from global_outcome import GlobalOutcome
from tasks.task_outcome import TaskOutcome
def realtime_task(global_outcome: GlobalOutcome, heater_instance_interface: HeaterInstanceInterface, start_date: datetime, end_date: datetime, store_db_function):
one_minute_difference = timedelta(minutes=1)
actual_date: datetime = start_date
# Get difference in days between start and end date.
difference: timedelta = end_date - start_date
stopped = False
for i in range(difference.days):
seed = heater_instance_interface.get_seed()
heat_demand_per_minute_in_kW_day = heat_demand_simulator.heat_demand_per_minute_in_kW(heat_demand_simulator.heat_demand(seed, actual_date))
for heat_demand_per_minute in heat_demand_per_minute_in_kW_day:
required_workload_in_percent = diff = diff_factor = 0.0
tries = count = 0
if heat_demand_per_minute > 0.0:
# Calculate percentage of workload for cpu while runtime.
# example: 1.5 / 0.04 / 100 = 0.375 = 37.5%
required_workload_in_percent = heat_demand_per_minute / constants.MAX_WORKLOAD_IN_KW_ONE_PERCENT / 100
try:
start_time = time.time()
tries, count, diff, diff_factor = heater_instance_interface.run(required_workload_in_percent, 60000)
end_time = time.time()
runtime = end_time - start_time
sleep_time = 60 - runtime
if sleep_time > 0:
time.sleep(sleep_time)
except:
print()
stopped = True
break
global_outcome.update_tries_count(tries, count)
else:
# If heat demand is 0, just sleep 1 minute and put values of 0 into list, since no heating required
time.sleep(60)
actual_heat_generated = diff_factor * heat_demand_per_minute
actual_date += one_minute_difference
store_db_function([TaskOutcome(required_workload_in_percent * 100, 60000, tries, count, diff / 1000, diff_factor, actual_date, heat_demand_per_minute, actual_heat_generated, heater_instance_interface.get_host().hostname, heater_instance_interface.get_host().username)])
if stopped:
break

View File

@@ -0,0 +1,49 @@
from datetime import datetime
class TaskOutcome:
def __init__(self, required_workload_in_percent, runtime, tries, count, diff, diff_factor, actual_date, heat_demand_per_minute, actual_heat_generated, hostname: str, username: str):
self.required_workload_in_percent:float = required_workload_in_percent
self.runtime: float = runtime
self.tries: int = tries
self.count: int = count
self.diff: float = diff
self.diff_factor: float = diff_factor
self.actual_date: datetime = actual_date
self.heat_demand_per_minute: float = heat_demand_per_minute
self.actual_heat_generated: float = actual_heat_generated
self.hostname: str = hostname
self.username: str = username
def get_required_workload_in_percent(self) -> float:
return self.required_workload_in_percent
def get_runtime(self) -> float:
return self.runtime
def get_tries(self) -> int:
return self.tries
def get_count(self) -> int:
return self.count
def get_diff(self) -> float:
return self.diff
def get_diff_factor(self) -> float:
return self. diff_factor
def get_actual_date(self) -> datetime:
return self.actual_date
def get_heat_demand_per_minute(self) -> float:
return self.heat_demand_per_minute
def get_actual_heat_generated(self) -> float:
return self.actual_heat_generated
def get_hostname(self) -> str:
return self.hostname
def get_username(self) -> str:
return self.username