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

4
.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
load_balancer/__pycache__
credentials.py
__pycache__
sqlitedb.db

14
README.md Normal file
View File

@@ -0,0 +1,14 @@
# Uebersicht
## Kleine Boinc Projekte / Tasks
> LHC@home Sixtrack: https://lhcathome.cern.ch
> World Community Grid: https://worldcommunitygrid.org
> climateprediction.net: https://climateprediction.net/
> Rosetta@home: https://boinc.bakerlab.org/rosetta/
## Required Dependencies

10
demo.md Normal file
View File

@@ -0,0 +1,10 @@
# Anleitung fuer Demo in presentation
AddInstance 192.52.32.182 ubuntu /Users/theresaherr/.ssh/id_rsa
AddInstance 192.52.32.236 ubuntu /Users/theresaherr/.ssh/id_rsa
AddInstance 192.52.34.222 ubuntu /Users/theresaherr/.ssh/id_rsa
StartSimulation Daily 01.03.2018 03.03.2018
DisplayOutcome
StopSimulation

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,42 @@
Table instances:
A. id - Python int - SQLite INTEGER - ID der Instanz
B. owner - Python str - SQLite TEXT - wer besitzt die Instanz?
C. calculated_demand - Python bytes (UTF-8 encoding) - SQLite BLOB - die vorberechnete Liste ueber
den Energiebedarf jeder Instanz
im json Format
Beispiel:
{
"energy_demands":[
{
"date": "01.01.2024",
"energy_demand_in_watts": 4
}
]
}
D. number_of_persons_in_household - Python int - SQLite INTEGER - Anzahl Personen in Haushalt
E. fulfilled_demand - Python bytes (UTF-8 encoding) - SQLite BLOB - der tatsaechlich gedeckte Energiebedarf
am jeweiligen Datum im json Format
Beispiel:
{
"scored_demand":[
{
"date": "01.01.2024",
"scored_energy_demand_in_watts": 3,
"busy_method_used":[
"hpc"
]
}
]
}
F. status - Python str - SQLite TEXT - Status der Instanz, "offline", "online", "error"
G. heater_busy_methods - Python bytes (UTF-8 encoding) - SQLite BLOB - Methoden, welche die Instanz ausfuehrt im json Format
Beispiel:
{
"busy_methods_active":[
"hpc"
]
}
Table application:
A. state - Python constants.State - SQLite TEXT - Zustand der Anwendung, Running oder Stopped

7
docs/demod/demod_use.md Normal file
View File

@@ -0,0 +1,7 @@
# Demod installieren
> pip install demod
> pip install openpyxl
> pip install odfpy

View File

@@ -0,0 +1,11 @@
### Load Balancer Funktionalitaet
Der Load Balancer wird gestartet. Er prueft ob in der SQLite-DB bereits Daten ueber Instanzen liegen.
Check out [Database columns](../../database/database_description.md)
Pro Eintrag (Zeile) werden A, B, C geholt und ein Instanz - Objekt mit den entsprechenden Daten erzeugt. Die Load - Balancer Klasse kommuniziert ueber die Klasse DBInterface mit der Datenbank. Die Klasse DBInterface implementiert die Funktionalitaeten, die notwendig sind, um mit der Datenbank zu kommunizieren.
[Python SQLite Bibliothek](https://docs.python.org/3/library/sqlite3.html#)
[JSON Validator](https://jsonlint.com/)

View File

@@ -0,0 +1,90 @@
<mxfile host="Electron" modified="2024-05-28T06:53:56.903Z" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/24.2.5 Chrome/120.0.6099.109 Electron/28.1.0 Safari/537.36" etag="tYrShgPT8Ox6WKzFjUai" version="24.2.5" type="device">
<diagram name="Page-1" id="qy58Fjep3ziJMWIOLXcW">
<mxGraphModel dx="1114" dy="663" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1169" pageHeight="827" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="7EknwZf3zE2kvBgCffaO-8" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="7EknwZf3zE2kvBgCffaO-1" target="7EknwZf3zE2kvBgCffaO-2">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-1" value="&lt;font style=&quot;font-size: 14px;&quot;&gt;LoadBalancer&lt;/font&gt;" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="525" y="40" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-6" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.75;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" edge="1" parent="1" source="7EknwZf3zE2kvBgCffaO-2" target="7EknwZf3zE2kvBgCffaO-3">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-13" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="7EknwZf3zE2kvBgCffaO-2" target="7EknwZf3zE2kvBgCffaO-10">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-14" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="7EknwZf3zE2kvBgCffaO-2" target="7EknwZf3zE2kvBgCffaO-11">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-15" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" edge="1" parent="1" source="7EknwZf3zE2kvBgCffaO-2">
<mxGeometry relative="1" as="geometry">
<mxPoint x="780" y="290" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-2" value="&lt;div style=&quot;font-size: 14px; line-height: 19px; white-space: pre;&quot;&gt;&lt;font face=&quot;Helvetica&quot;&gt;HeaterInstanceWorkerThreadPoolOrchestrator&lt;/font&gt;&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;fontColor=default;" vertex="1" parent="1">
<mxGeometry x="525" y="160" width="320" height="60" as="geometry" />
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-3" value="&lt;div style=&quot;font-size: 14px; line-height: 19px; white-space: pre;&quot;&gt;&lt;div style=&quot;line-height: 19px;&quot;&gt;&lt;font face=&quot;Helvetica&quot;&gt;HeaterInstancesOrchestrator&lt;/font&gt;&lt;/div&gt;&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;fontColor=default;" vertex="1" parent="1">
<mxGeometry x="690" y="40" width="320" height="60" as="geometry" />
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-7" value="Fetch Data about Instances" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=14;" vertex="1" parent="1">
<mxGeometry x="860" y="110" width="110" height="50" as="geometry" />
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-9" value="&lt;font style=&quot;font-size: 14px;&quot;&gt;Starts&lt;/font&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="525" y="110" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-20" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;dashed=1;" edge="1" parent="1" source="7EknwZf3zE2kvBgCffaO-10" target="7EknwZf3zE2kvBgCffaO-16">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-21" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;dashed=1;" edge="1" parent="1" source="7EknwZf3zE2kvBgCffaO-10" target="7EknwZf3zE2kvBgCffaO-17">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-10" value="&lt;div style=&quot;font-size: 14px; line-height: 19px; white-space: pre;&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;&lt;font style=&quot;&quot; face=&quot;Helvetica&quot;&gt;HeaterInstanceWorkerThread A&lt;/font&gt;&lt;/span&gt;&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;fontSize=14;fontColor=default;" vertex="1" parent="1">
<mxGeometry x="520" y="290" width="210" height="60" as="geometry" />
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-11" value="&lt;div style=&quot;font-size: 14px; line-height: 19px; white-space: pre;&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;&lt;font style=&quot;&quot; face=&quot;Helvetica&quot;&gt;HeaterInstanceWorkerThread n&lt;/font&gt;&lt;/span&gt;&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;fontSize=14;fontColor=default;" vertex="1" parent="1">
<mxGeometry x="830" y="290" width="210" height="60" as="geometry" />
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-12" value="" style="endArrow=none;dashed=1;html=1;dashPattern=1 3;strokeWidth=2;rounded=0;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="740" y="320" as="sourcePoint" />
<mxPoint x="820" y="320" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-29" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.25;exitY=1;exitDx=0;exitDy=0;entryX=0.25;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="7EknwZf3zE2kvBgCffaO-16" target="7EknwZf3zE2kvBgCffaO-26">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-16" value="daily_task()" style="shape=process;whiteSpace=wrap;html=1;backgroundOutline=1;fontSize=14;" vertex="1" parent="1">
<mxGeometry x="520" y="414" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-17" value="realtime_task()" style="shape=process;whiteSpace=wrap;html=1;backgroundOutline=1;fontSize=14;" vertex="1" parent="1">
<mxGeometry x="670" y="414" width="130" height="60" as="geometry" />
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-22" value="&lt;div style=&quot;font-size: 14px; line-height: 19px; white-space-collapse: preserve;&quot;&gt;&lt;font face=&quot;Helvetica&quot;&gt;HeaterInstanceInterface&lt;/font&gt;&lt;/div&gt;" style="shape=message;html=1;html=1;outlineConnect=0;labelPosition=center;verticalLabelPosition=bottom;align=center;verticalAlign=top;" vertex="1" parent="1">
<mxGeometry x="480" y="360" width="35" height="20" as="geometry" />
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-24" value="&lt;div style=&quot;font-size: 14px; line-height: 19px; white-space-collapse: preserve;&quot;&gt;&lt;font face=&quot;Helvetica&quot;&gt;HeaterInstanceInterface&lt;/font&gt;&lt;/div&gt;" style="shape=message;html=1;html=1;outlineConnect=0;labelPosition=center;verticalLabelPosition=bottom;align=center;verticalAlign=top;" vertex="1" parent="1">
<mxGeometry x="975" y="140" width="35" height="20" as="geometry" />
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-25" value="&lt;div style=&quot;font-size: 14px; line-height: 19px; white-space-collapse: preserve;&quot;&gt;&lt;font face=&quot;Helvetica&quot;&gt;HeaterInstanceInterface&lt;/font&gt;&lt;/div&gt;" style="shape=message;html=1;html=1;outlineConnect=0;labelPosition=center;verticalLabelPosition=bottom;align=center;verticalAlign=top;" vertex="1" parent="1">
<mxGeometry x="525" y="230" width="35" height="20" as="geometry" />
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-28" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.75;exitY=0;exitDx=0;exitDy=0;entryX=0.75;entryY=1;entryDx=0;entryDy=0;" edge="1" parent="1" source="7EknwZf3zE2kvBgCffaO-26" target="7EknwZf3zE2kvBgCffaO-16">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-26" value="&lt;font style=&quot;font-size: 14px;&quot;&gt;HeaterInstanceInterface.run()&lt;/font&gt;" style="shape=process;whiteSpace=wrap;html=1;backgroundOutline=1;" vertex="1" parent="1">
<mxGeometry x="520" y="520" width="260" height="60" as="geometry" />
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-32" value="&lt;font style=&quot;font-size: 14px;&quot;&gt;HeaterInstance&lt;/font&gt;&lt;span style=&quot;font-size: 14px; background-color: initial;&quot;&gt;.run()&lt;/span&gt;" style="shape=process;whiteSpace=wrap;html=1;backgroundOutline=1;" vertex="1" parent="1">
<mxGeometry x="520" y="580" width="260" height="60" as="geometry" />
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-33" value="Threadcount" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="690" y="225" width="60" height="30" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

View File

@@ -0,0 +1,201 @@
<mxfile host="Electron" modified="2024-05-07T09:33:55.233Z" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/24.2.5 Chrome/120.0.6099.109 Electron/28.1.0 Safari/537.36" etag="PlsJbYsQtKTGQ-h2Sm5Q" version="24.2.5" type="device">
<diagram id="C5RBs43oDa-KdzZeNtuy" name="Page-1">
<mxGraphModel dx="1061" dy="1458" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1169" pageHeight="827" math="0" shadow="0">
<root>
<mxCell id="WIyWlLk6GJQsqaUBKTNV-0" />
<mxCell id="WIyWlLk6GJQsqaUBKTNV-1" parent="WIyWlLk6GJQsqaUBKTNV-0" />
<mxCell id="k0BLKvAXlocCeE4DJ_wN-23" value="&lt;span style=&quot;white-space: pre;&quot;&gt;&#x9;&lt;/span&gt;Package: load_balancer" style="whiteSpace=wrap;html=1;shape=mxgraph.basic.document;align=left;verticalAlign=top;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="10" y="-820" width="1120" height="790" as="geometry" />
</mxCell>
<mxCell id="k0BLKvAXlocCeE4DJ_wN-25" value="&lt;span style=&quot;white-space: pre;&quot;&gt;&#x9;&lt;/span&gt;Package: heater_interface" style="whiteSpace=wrap;html=1;shape=mxgraph.basic.document;align=left;verticalAlign=top;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="797" y="-720" width="263" height="670" as="geometry" />
</mxCell>
<mxCell id="k0BLKvAXlocCeE4DJ_wN-24" value="&lt;span style=&quot;white-space: pre;&quot;&gt;&#x9;&lt;/span&gt;Package: db_interface" style="whiteSpace=wrap;html=1;shape=mxgraph.basic.document;align=left;verticalAlign=top;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="20" y="-720" width="360" height="370" as="geometry" />
</mxCell>
<mxCell id="k0BLKvAXlocCeE4DJ_wN-22" value="&lt;span style=&quot;white-space: pre;&quot;&gt;&#x9;&lt;/span&gt;Package: heater" style="whiteSpace=wrap;html=1;shape=mxgraph.basic.document;align=left;verticalAlign=top;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="1180" y="-820" width="375" height="525" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--0" value="LoadBalancer" style="swimlane;fontStyle=0;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;rounded=0;shadow=0;strokeWidth=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="440" y="-690" width="290" height="170" as="geometry">
<mxRectangle x="230" y="140" width="160" height="26" as="alternateBounds" />
</mxGeometry>
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--1" value="heater_instances : HeaterInstance []" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--0" vertex="1">
<mxGeometry y="26" width="290" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--2" value="db_interface : DBInterface" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="zkfFHV4jXpPFQw0GAbJ--0" vertex="1">
<mxGeometry y="52" width="290" height="30" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--3" value="foo" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="zkfFHV4jXpPFQw0GAbJ--0" vertex="1">
<mxGeometry y="82" width="290" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--4" value="" style="line;html=1;strokeWidth=1;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--0" vertex="1">
<mxGeometry y="108" width="290" height="8" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--5" value="add_instance(instance : HeaterInstance) : void" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--0" vertex="1">
<mxGeometry y="116" width="290" height="26" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-29" value="remove_instance(instance : HeaterInstance) : void" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--0" vertex="1">
<mxGeometry y="142" width="290" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--17" value="HeaterInstance" style="swimlane;fontStyle=0;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;rounded=0;shadow=0;strokeWidth=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="835" y="-440" width="160" height="216" as="geometry">
<mxRectangle x="550" y="140" width="160" height="26" as="alternateBounds" />
</mxGeometry>
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--18" value="host : str" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
<mxGeometry y="26" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--19" value="user : str" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
<mxGeometry y="52" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--20" value="ssh_key : ssh_key" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
<mxGeometry y="78" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--22" value="owner : Owner" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
<mxGeometry y="104" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--23" value="" style="line;html=1;strokeWidth=1;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
<mxGeometry y="130" width="160" height="8" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-31" value="foo" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
<mxGeometry y="138" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-0" value="Instance" style="swimlane;fontStyle=2;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;rounded=0;shadow=0;strokeWidth=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="830" y="-690" width="170" height="160" as="geometry">
<mxRectangle x="340" y="380" width="170" height="26" as="alternateBounds" />
</mxGeometry>
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-5" value="ssh_session : undefined" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="5vzdqylje9so0Pf0NUx3-0" vertex="1">
<mxGeometry y="26" width="170" height="30" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-7" value="demand : undefined" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="5vzdqylje9so0Pf0NUx3-0" vertex="1">
<mxGeometry y="56" width="170" height="30" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-8" value="outcome : undefined" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="5vzdqylje9so0Pf0NUx3-0" vertex="1">
<mxGeometry y="86" width="170" height="30" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-9" value="status : bool" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="5vzdqylje9so0Pf0NUx3-0" vertex="1">
<mxGeometry y="116" width="170" height="30" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-2" value="" style="line;html=1;strokeWidth=1;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;" parent="5vzdqylje9so0Pf0NUx3-0" vertex="1">
<mxGeometry y="146" width="170" height="14" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-3" value="" style="endArrow=block;endSize=10;endFill=1;shadow=0;strokeWidth=1;rounded=0;curved=0;edgeStyle=elbowEdgeStyle;elbow=vertical;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="zkfFHV4jXpPFQw0GAbJ--17" target="5vzdqylje9so0Pf0NUx3-0" edge="1">
<mxGeometry width="160" relative="1" as="geometry">
<mxPoint x="917" y="-418" as="sourcePoint" />
<mxPoint x="807" y="-520" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-15" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;endArrow=open;endFill=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="zkfFHV4jXpPFQw0GAbJ--2" target="zkfFHV4jXpPFQw0GAbJ--18" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="760" y="-390" as="targetPoint" />
<Array as="points">
<mxPoint x="760" y="-625" />
<mxPoint x="760" y="-401" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-16" value="0..n" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="730" y="-650" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-17" value="1" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="760" y="-430" width="37" height="30" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-18" value="orchestrates" style="text;html=1;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="690" y="-510" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-19" value="Owner" style="swimlane;fontStyle=0;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;rounded=0;shadow=0;strokeWidth=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="835" y="-160" width="160" height="90" as="geometry">
<mxRectangle x="130" y="380" width="160" height="26" as="alternateBounds" />
</mxGeometry>
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-20" value="first_name : str" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="5vzdqylje9so0Pf0NUx3-19" vertex="1">
<mxGeometry y="26" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-21" value="last_name : str" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="5vzdqylje9so0Pf0NUx3-19" vertex="1">
<mxGeometry y="52" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-22" value="" style="line;html=1;strokeWidth=1;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;" parent="5vzdqylje9so0Pf0NUx3-19" vertex="1">
<mxGeometry y="78" width="160" height="8" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-25" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=open;endFill=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" target="5vzdqylje9so0Pf0NUx3-19" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="690" y="-310" as="targetPoint" />
<mxPoint x="915" y="-222" as="sourcePoint" />
</mxGeometry>
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-26" value="1" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="912" y="-190" width="36" height="30" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-27" value="1" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="910" y="-224" width="40" height="30" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-28" value="owned by" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="850" y="-210" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-32" value="DBSettings" style="swimlane;fontStyle=0;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;rounded=0;shadow=0;strokeWidth=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="30" y="-690" width="270" height="70" as="geometry">
<mxRectangle x="130" y="380" width="160" height="26" as="alternateBounds" />
</mxGeometry>
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-33" value="file_path : str" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="5vzdqylje9so0Pf0NUx3-32" vertex="1">
<mxGeometry y="26" width="270" height="26" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-35" value="" style="line;html=1;strokeWidth=1;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;" parent="5vzdqylje9so0Pf0NUx3-32" vertex="1">
<mxGeometry y="52" width="270" height="8" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-39" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;endArrow=open;endFill=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="5vzdqylje9so0Pf0NUx3-36" target="5vzdqylje9so0Pf0NUx3-32" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-36" value="DBInterface" style="swimlane;fontStyle=0;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;rounded=0;shadow=0;strokeWidth=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="30" y="-590" width="280" height="220" as="geometry">
<mxRectangle x="130" y="380" width="160" height="26" as="alternateBounds" />
</mxGeometry>
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-37" value="settings : DBSettings" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="5vzdqylje9so0Pf0NUx3-36" vertex="1">
<mxGeometry y="26" width="280" height="26" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-38" value="" style="line;html=1;strokeWidth=1;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;" parent="5vzdqylje9so0Pf0NUx3-36" vertex="1">
<mxGeometry y="52" width="280" height="8" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-46" value="add_instance(instance : HeaterInstance) : void" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="5vzdqylje9so0Pf0NUx3-36" vertex="1">
<mxGeometry y="60" width="280" height="26" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-47" value="update_instance(instance : HeaterInstance) : void" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="5vzdqylje9so0Pf0NUx3-36" vertex="1">
<mxGeometry y="86" width="280" height="26" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-51" value="remove_instance(instance : HeaterInstance) : void" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="5vzdqylje9so0Pf0NUx3-36" vertex="1">
<mxGeometry y="112" width="280" height="26" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-40" value="1&lt;div&gt;1&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="150" y="-620" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-42" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;endArrow=open;endFill=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="zkfFHV4jXpPFQw0GAbJ--2" target="5vzdqylje9so0Pf0NUx3-37" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="390" y="-623" />
<mxPoint x="390" y="-551" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-44" value="1" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="300" y="-580" width="40" height="30" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-45" value="1" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="400" y="-650" width="40" height="30" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-48" value="uses" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="380" y="-560" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-49" value="has" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="130" y="-620" width="40" height="30" as="geometry" />
</mxCell>
<mxCell id="k0BLKvAXlocCeE4DJ_wN-26" value="get_instances() : HeaterInstance []" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="30" y="-450" width="280" height="26" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

View File

@@ -0,0 +1,37 @@
<mxfile host="Electron" modified="2024-06-09T23:20:32.162Z" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/24.2.5 Chrome/120.0.6099.109 Electron/28.1.0 Safari/537.36" etag="i5KB9fsT4zV48CXnobgm" version="24.2.5" type="device">
<diagram name="Page-1" id="Ah_-kuApFwH53ASNQh7D">
<mxGraphModel dx="1114" dy="663" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1169" pageHeight="827" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="8_F79eOSLjp0VsFUqVzp-15" value="LoadBalancer" style="rounded=0;whiteSpace=wrap;html=1;verticalAlign=top;" parent="1" vertex="1">
<mxGeometry x="20" y="20" width="670" height="390" as="geometry" />
</mxCell>
<mxCell id="8_F79eOSLjp0VsFUqVzp-16" value="&lt;div style=&quot;line-height: 19px; white-space: pre;&quot;&gt;&lt;font style=&quot;font-size: 12px;&quot; face=&quot;Helvetica&quot;&gt;HeaterInstanceWorkerThread&lt;/font&gt;&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="40" y="140" width="630" height="80" as="geometry" />
</mxCell>
<mxCell id="8_F79eOSLjp0VsFUqVzp-17" value="&lt;div style=&quot;line-height: 19px; white-space: pre;&quot;&gt;&lt;font face=&quot;Helvetica&quot; style=&quot;font-size: 12px;&quot;&gt;HeaterInstanceInterfaceManager&lt;/font&gt;&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" parent="1" vertex="1">
<mxGeometry x="40" y="50" width="500" height="80" as="geometry" />
</mxCell>
<mxCell id="8_F79eOSLjp0VsFUqVzp-18" value="&lt;div style=&quot;line-height: 19px; white-space: pre;&quot;&gt;&lt;font face=&quot;Helvetica&quot; style=&quot;font-size: 12px;&quot;&gt;HeaterInstanceInterface&lt;/font&gt;&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" parent="1" vertex="1">
<mxGeometry x="225" y="60" width="300" height="60" as="geometry" />
</mxCell>
<mxCell id="8_F79eOSLjp0VsFUqVzp-19" value="&lt;div style=&quot;line-height: 19px; white-space: pre;&quot;&gt;&lt;font face=&quot;Helvetica&quot; style=&quot;font-size: 12px;&quot;&gt;Simulation&lt;/font&gt;&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="550" y="50" width="120" height="80" as="geometry" />
</mxCell>
<mxCell id="8_F79eOSLjp0VsFUqVzp-20" value="&lt;div style=&quot;line-height: 19px; white-space: pre;&quot;&gt;&lt;font face=&quot;Helvetica&quot; style=&quot;font-size: 12px;&quot;&gt;GlobalOutcome&lt;/font&gt;&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="40" y="320" width="630" height="80" as="geometry" />
</mxCell>
<mxCell id="8_F79eOSLjp0VsFUqVzp-21" value="&lt;div style=&quot;line-height: 19px; white-space: pre;&quot;&gt;&lt;font face=&quot;Helvetica&quot; style=&quot;font-size: 12px;&quot;&gt;TaskOutcome&lt;/font&gt;&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="40" y="230" width="630" height="80" as="geometry" />
</mxCell>
<mxCell id="8_F79eOSLjp0VsFUqVzp-22" value="&lt;div style=&quot;line-height: 19px; white-space: pre;&quot;&gt;&lt;font style=&quot;font-size: 12px;&quot;&gt;HeaterInstance&lt;/font&gt;&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" parent="1" vertex="1">
<mxGeometry x="365" y="70" width="150" height="40" as="geometry" />
</mxCell>
<mxCell id="8_F79eOSLjp0VsFUqVzp-23" value="&lt;div style=&quot;line-height: 19px; white-space: pre;&quot;&gt;&lt;font style=&quot;font-size: 12px;&quot; face=&quot;Helvetica&quot;&gt;Host&lt;/font&gt;&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="455" y="80" width="50" height="20" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

View File

@@ -0,0 +1,84 @@
<mxfile host="Electron" modified="2024-05-31T12:12:03.617Z" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/24.2.5 Chrome/120.0.6099.109 Electron/28.1.0 Safari/537.36" etag="Hc0sJxNv_TmvKQj58595" version="24.2.5" type="device">
<diagram name="Page-1" id="qy58Fjep3ziJMWIOLXcW">
<mxGraphModel dx="1114" dy="663" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1169" pageHeight="827" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="7EknwZf3zE2kvBgCffaO-8" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="7EknwZf3zE2kvBgCffaO-1" target="7EknwZf3zE2kvBgCffaO-2" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-1" value="&lt;font style=&quot;font-size: 14px;&quot;&gt;LoadBalancer&lt;/font&gt;" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="525" y="40" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-6" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.75;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" parent="1" source="7EknwZf3zE2kvBgCffaO-2" target="7EknwZf3zE2kvBgCffaO-3" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-13" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="7EknwZf3zE2kvBgCffaO-2" target="7EknwZf3zE2kvBgCffaO-10" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-14" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="7EknwZf3zE2kvBgCffaO-2" target="7EknwZf3zE2kvBgCffaO-11" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-15" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" parent="1" source="7EknwZf3zE2kvBgCffaO-2" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="780" y="290" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-2" value="&lt;div style=&quot;font-size: 14px; line-height: 19px; white-space: pre;&quot;&gt;&lt;font face=&quot;Helvetica&quot;&gt;HeaterInstanceWorkerThreadPoolOrchestrator&lt;/font&gt;&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;fontColor=default;" parent="1" vertex="1">
<mxGeometry x="525" y="160" width="320" height="60" as="geometry" />
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-3" value="&lt;div style=&quot;font-size: 14px; line-height: 19px; white-space: pre;&quot;&gt;&lt;div style=&quot;line-height: 19px;&quot;&gt;&lt;font face=&quot;Helvetica&quot;&gt;HeaterInstancesOrchestrator&lt;/font&gt;&lt;/div&gt;&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;fontColor=default;" parent="1" vertex="1">
<mxGeometry x="690" y="40" width="320" height="60" as="geometry" />
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-7" value="Fetch Data about Instances" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="860" y="110" width="110" height="50" as="geometry" />
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-9" value="&lt;font style=&quot;font-size: 14px;&quot;&gt;Starts&lt;/font&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="525" y="110" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-20" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;dashed=1;" parent="1" source="7EknwZf3zE2kvBgCffaO-10" target="7EknwZf3zE2kvBgCffaO-16" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-21" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;dashed=1;" parent="1" source="7EknwZf3zE2kvBgCffaO-10" target="7EknwZf3zE2kvBgCffaO-17" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-10" value="&lt;div style=&quot;font-size: 14px; line-height: 19px; white-space: pre;&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;&lt;font style=&quot;&quot; face=&quot;Helvetica&quot;&gt;HeaterInstanceWorkerThread A&lt;/font&gt;&lt;/span&gt;&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;fontSize=14;fontColor=default;" parent="1" vertex="1">
<mxGeometry x="520" y="290" width="210" height="60" as="geometry" />
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-11" value="&lt;div style=&quot;font-size: 14px; line-height: 19px; white-space: pre;&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;&lt;font style=&quot;&quot; face=&quot;Helvetica&quot;&gt;HeaterInstanceWorkerThread n&lt;/font&gt;&lt;/span&gt;&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;fontSize=14;fontColor=default;" parent="1" vertex="1">
<mxGeometry x="830" y="290" width="210" height="60" as="geometry" />
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-12" value="" style="endArrow=none;dashed=1;html=1;dashPattern=1 3;strokeWidth=2;rounded=0;" parent="1" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="740" y="320" as="sourcePoint" />
<mxPoint x="820" y="320" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-29" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.25;exitY=1;exitDx=0;exitDy=0;entryX=0.25;entryY=0;entryDx=0;entryDy=0;" parent="1" source="7EknwZf3zE2kvBgCffaO-16" target="7EknwZf3zE2kvBgCffaO-26" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-16" value="daily_task()" style="shape=process;whiteSpace=wrap;html=1;backgroundOutline=1;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="520" y="414" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-17" value="realtime_task()" style="shape=process;whiteSpace=wrap;html=1;backgroundOutline=1;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="670" y="414" width="130" height="60" as="geometry" />
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-22" value="&lt;div style=&quot;font-size: 14px; line-height: 19px; white-space-collapse: preserve;&quot;&gt;&lt;font face=&quot;Helvetica&quot;&gt;HeaterInstanceInterface&lt;/font&gt;&lt;/div&gt;" style="shape=message;html=1;html=1;outlineConnect=0;labelPosition=center;verticalLabelPosition=bottom;align=center;verticalAlign=top;" parent="1" vertex="1">
<mxGeometry x="480" y="360" width="35" height="20" as="geometry" />
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-24" value="&lt;div style=&quot;font-size: 14px; line-height: 19px; white-space-collapse: preserve;&quot;&gt;&lt;font face=&quot;Helvetica&quot;&gt;HeaterInstanceInterface&lt;/font&gt;&lt;/div&gt;" style="shape=message;html=1;html=1;outlineConnect=0;labelPosition=center;verticalLabelPosition=bottom;align=center;verticalAlign=top;" parent="1" vertex="1">
<mxGeometry x="975" y="140" width="35" height="20" as="geometry" />
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-25" value="&lt;div style=&quot;font-size: 14px; line-height: 19px; white-space-collapse: preserve;&quot;&gt;&lt;font face=&quot;Helvetica&quot;&gt;HeaterInstanceInterface&lt;/font&gt;&lt;/div&gt;" style="shape=message;html=1;html=1;outlineConnect=0;labelPosition=center;verticalLabelPosition=bottom;align=center;verticalAlign=top;" parent="1" vertex="1">
<mxGeometry x="525" y="230" width="35" height="20" as="geometry" />
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-28" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.75;exitY=0;exitDx=0;exitDy=0;entryX=0.75;entryY=1;entryDx=0;entryDy=0;" parent="1" source="7EknwZf3zE2kvBgCffaO-26" target="7EknwZf3zE2kvBgCffaO-16" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="7EknwZf3zE2kvBgCffaO-26" value="&lt;font style=&quot;font-size: 14px;&quot;&gt;HeaterInstance.run()&lt;/font&gt;" style="shape=process;whiteSpace=wrap;html=1;backgroundOutline=1;" parent="1" vertex="1">
<mxGeometry x="510" y="520" width="180" height="60" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

View File

@@ -0,0 +1,201 @@
<mxfile host="Electron" modified="2024-05-07T09:35:52.261Z" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/24.2.5 Chrome/120.0.6099.109 Electron/28.1.0 Safari/537.36" etag="DmmaRJrs2XHzGILdNXqB" version="24.2.5" type="device">
<diagram id="C5RBs43oDa-KdzZeNtuy" name="Page-1">
<mxGraphModel dx="1061" dy="1458" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1169" pageHeight="827" math="0" shadow="0">
<root>
<mxCell id="WIyWlLk6GJQsqaUBKTNV-0" />
<mxCell id="WIyWlLk6GJQsqaUBKTNV-1" parent="WIyWlLk6GJQsqaUBKTNV-0" />
<mxCell id="k0BLKvAXlocCeE4DJ_wN-23" value="&lt;span style=&quot;white-space: pre;&quot;&gt;&#x9;&lt;/span&gt;Package: load_balancer" style="whiteSpace=wrap;html=1;shape=mxgraph.basic.document;align=left;verticalAlign=top;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="10" y="-820" width="1120" height="790" as="geometry" />
</mxCell>
<mxCell id="k0BLKvAXlocCeE4DJ_wN-25" value="&lt;span style=&quot;white-space: pre;&quot;&gt;&#x9;&lt;/span&gt;Package: heater_interface" style="whiteSpace=wrap;html=1;shape=mxgraph.basic.document;align=left;verticalAlign=top;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="797" y="-720" width="263" height="670" as="geometry" />
</mxCell>
<mxCell id="k0BLKvAXlocCeE4DJ_wN-24" value="&lt;span style=&quot;white-space: pre;&quot;&gt;&#x9;&lt;/span&gt;Package: db_interface" style="whiteSpace=wrap;html=1;shape=mxgraph.basic.document;align=left;verticalAlign=top;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="20" y="-720" width="360" height="370" as="geometry" />
</mxCell>
<mxCell id="k0BLKvAXlocCeE4DJ_wN-22" value="&lt;span style=&quot;white-space: pre;&quot;&gt;&#x9;&lt;/span&gt;Package: heater" style="whiteSpace=wrap;html=1;shape=mxgraph.basic.document;align=left;verticalAlign=top;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="1180" y="-820" width="375" height="525" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--0" value="LoadBalancer" style="swimlane;fontStyle=0;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;rounded=0;shadow=0;strokeWidth=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="440" y="-690" width="290" height="240" as="geometry">
<mxRectangle x="230" y="140" width="160" height="26" as="alternateBounds" />
</mxGeometry>
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--1" value="heater_instances : HeaterInstance []" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--0" vertex="1">
<mxGeometry y="26" width="290" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--2" value="db_interface : DBInterface" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="zkfFHV4jXpPFQw0GAbJ--0" vertex="1">
<mxGeometry y="52" width="290" height="30" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--3" value="foo" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="zkfFHV4jXpPFQw0GAbJ--0" vertex="1">
<mxGeometry y="82" width="290" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--4" value="" style="line;html=1;strokeWidth=1;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--0" vertex="1">
<mxGeometry y="108" width="290" height="8" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--5" value="bar" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--0" vertex="1">
<mxGeometry y="116" width="290" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--17" value="HeaterInstance" style="swimlane;fontStyle=0;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;rounded=0;shadow=0;strokeWidth=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="835" y="-440" width="160" height="216" as="geometry">
<mxRectangle x="550" y="140" width="160" height="26" as="alternateBounds" />
</mxGeometry>
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--18" value="host : str" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
<mxGeometry y="26" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--19" value="user : str" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
<mxGeometry y="52" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--20" value="ssh_key : ssh_key" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
<mxGeometry y="78" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--22" value="owner : Owner" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
<mxGeometry y="104" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--23" value="" style="line;html=1;strokeWidth=1;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
<mxGeometry y="130" width="160" height="8" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-31" value="foo" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
<mxGeometry y="138" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-0" value="Instance" style="swimlane;fontStyle=2;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;rounded=0;shadow=0;strokeWidth=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="830" y="-690" width="170" height="160" as="geometry">
<mxRectangle x="340" y="380" width="170" height="26" as="alternateBounds" />
</mxGeometry>
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-5" value="ssh_session : undefined" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="5vzdqylje9so0Pf0NUx3-0" vertex="1">
<mxGeometry y="26" width="170" height="30" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-7" value="demand : undefined" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="5vzdqylje9so0Pf0NUx3-0" vertex="1">
<mxGeometry y="56" width="170" height="30" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-8" value="outcome : undefined" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="5vzdqylje9so0Pf0NUx3-0" vertex="1">
<mxGeometry y="86" width="170" height="30" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-9" value="status : bool" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="5vzdqylje9so0Pf0NUx3-0" vertex="1">
<mxGeometry y="116" width="170" height="30" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-2" value="" style="line;html=1;strokeWidth=1;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;" parent="5vzdqylje9so0Pf0NUx3-0" vertex="1">
<mxGeometry y="146" width="170" height="14" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-3" value="" style="endArrow=block;endSize=10;endFill=1;shadow=0;strokeWidth=1;rounded=0;curved=0;edgeStyle=elbowEdgeStyle;elbow=vertical;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="zkfFHV4jXpPFQw0GAbJ--17" target="5vzdqylje9so0Pf0NUx3-0" edge="1">
<mxGeometry width="160" relative="1" as="geometry">
<mxPoint x="917" y="-418" as="sourcePoint" />
<mxPoint x="807" y="-520" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-15" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;endArrow=open;endFill=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="zkfFHV4jXpPFQw0GAbJ--2" target="zkfFHV4jXpPFQw0GAbJ--18" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="760" y="-390" as="targetPoint" />
<Array as="points">
<mxPoint x="760" y="-625" />
<mxPoint x="760" y="-401" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-16" value="0..n" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="730" y="-650" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-17" value="1" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="760" y="-430" width="37" height="30" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-18" value="orchestrates" style="text;html=1;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="730" y="-400" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-19" value="Owner" style="swimlane;fontStyle=0;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;rounded=0;shadow=0;strokeWidth=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="835" y="-160" width="160" height="90" as="geometry">
<mxRectangle x="130" y="380" width="160" height="26" as="alternateBounds" />
</mxGeometry>
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-20" value="first_name : str" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="5vzdqylje9so0Pf0NUx3-19" vertex="1">
<mxGeometry y="26" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-21" value="last_name : str" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="5vzdqylje9so0Pf0NUx3-19" vertex="1">
<mxGeometry y="52" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-22" value="" style="line;html=1;strokeWidth=1;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;" parent="5vzdqylje9so0Pf0NUx3-19" vertex="1">
<mxGeometry y="78" width="160" height="8" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-25" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=open;endFill=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" target="5vzdqylje9so0Pf0NUx3-19" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="690" y="-310" as="targetPoint" />
<mxPoint x="915" y="-222" as="sourcePoint" />
</mxGeometry>
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-26" value="1" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="912" y="-190" width="36" height="30" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-27" value="1" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="910" y="-224" width="40" height="30" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-28" value="owned by" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="850" y="-210" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-32" value="DBSettings" style="swimlane;fontStyle=0;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;rounded=0;shadow=0;strokeWidth=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="30" y="-690" width="270" height="70" as="geometry">
<mxRectangle x="130" y="380" width="160" height="26" as="alternateBounds" />
</mxGeometry>
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-33" value="file_path : str" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="5vzdqylje9so0Pf0NUx3-32" vertex="1">
<mxGeometry y="26" width="270" height="26" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-35" value="" style="line;html=1;strokeWidth=1;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;" parent="5vzdqylje9so0Pf0NUx3-32" vertex="1">
<mxGeometry y="52" width="270" height="8" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-39" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;endArrow=open;endFill=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="5vzdqylje9so0Pf0NUx3-36" target="5vzdqylje9so0Pf0NUx3-32" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-36" value="DBInterface" style="swimlane;fontStyle=0;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;rounded=0;shadow=0;strokeWidth=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="30" y="-590" width="280" height="220" as="geometry">
<mxRectangle x="130" y="380" width="160" height="26" as="alternateBounds" />
</mxGeometry>
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-37" value="settings : DBSettings" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="5vzdqylje9so0Pf0NUx3-36" vertex="1">
<mxGeometry y="26" width="280" height="26" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-38" value="" style="line;html=1;strokeWidth=1;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;" parent="5vzdqylje9so0Pf0NUx3-36" vertex="1">
<mxGeometry y="52" width="280" height="8" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-46" value="add_instance(instance : HeaterInstance) : void" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="5vzdqylje9so0Pf0NUx3-36" vertex="1">
<mxGeometry y="60" width="280" height="26" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-47" value="update_instance(instance : HeaterInstance) : void" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="5vzdqylje9so0Pf0NUx3-36" vertex="1">
<mxGeometry y="86" width="280" height="26" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-51" value="remove_instance(instance : HeaterInstance) : void" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="5vzdqylje9so0Pf0NUx3-36" vertex="1">
<mxGeometry y="112" width="280" height="26" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-40" value="1&lt;div&gt;1&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="150" y="-620" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-42" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;endArrow=open;endFill=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="zkfFHV4jXpPFQw0GAbJ--2" target="5vzdqylje9so0Pf0NUx3-37" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="390" y="-623" />
<mxPoint x="390" y="-551" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-44" value="1" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="300" y="-580" width="40" height="30" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-45" value="1" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="400" y="-650" width="40" height="30" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-48" value="uses" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="380" y="-560" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="5vzdqylje9so0Pf0NUx3-49" value="has" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="130" y="-620" width="40" height="30" as="geometry" />
</mxCell>
<mxCell id="k0BLKvAXlocCeE4DJ_wN-26" value="get_instances() : HeaterInstance []" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="30" y="-450" width="280" height="26" as="geometry" />
</mxCell>
<mxCell id="k0BLKvAXlocCeE4DJ_wN-27" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="5vzdqylje9so0Pf0NUx3-18" target="5vzdqylje9so0Pf0NUx3-18">
<mxGeometry relative="1" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

View File

@@ -0,0 +1,37 @@
<mxfile host="Electron" modified="2024-06-09T23:20:50.047Z" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/24.2.5 Chrome/120.0.6099.109 Electron/28.1.0 Safari/537.36" etag="oc_10AI9Czrxgo6AxFQL" version="24.2.5" type="device">
<diagram name="Page-1" id="Ah_-kuApFwH53ASNQh7D">
<mxGraphModel dx="1086" dy="663" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1169" pageHeight="827" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="8_F79eOSLjp0VsFUqVzp-15" value="LoadBalancer" style="rounded=0;whiteSpace=wrap;html=1;verticalAlign=top;" parent="1" vertex="1">
<mxGeometry x="210" y="140" width="670" height="390" as="geometry" />
</mxCell>
<mxCell id="8_F79eOSLjp0VsFUqVzp-16" value="&lt;div style=&quot;line-height: 19px; white-space: pre;&quot;&gt;&lt;font style=&quot;font-size: 12px;&quot; face=&quot;Helvetica&quot;&gt;HeaterInstanceWorkerThread&lt;/font&gt;&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="230" y="260" width="630" height="80" as="geometry" />
</mxCell>
<mxCell id="8_F79eOSLjp0VsFUqVzp-17" value="&lt;div style=&quot;line-height: 19px; white-space: pre;&quot;&gt;&lt;font face=&quot;Helvetica&quot; style=&quot;font-size: 12px;&quot;&gt;HeaterInstanceInterfaceManager&lt;/font&gt;&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" parent="1" vertex="1">
<mxGeometry x="230" y="170" width="500" height="80" as="geometry" />
</mxCell>
<mxCell id="8_F79eOSLjp0VsFUqVzp-18" value="&lt;div style=&quot;line-height: 19px; white-space: pre;&quot;&gt;&lt;font face=&quot;Helvetica&quot; style=&quot;font-size: 12px;&quot;&gt;HeaterInstanceInterface&lt;/font&gt;&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" parent="1" vertex="1">
<mxGeometry x="415" y="180" width="300" height="60" as="geometry" />
</mxCell>
<mxCell id="8_F79eOSLjp0VsFUqVzp-19" value="&lt;div style=&quot;line-height: 19px; white-space: pre;&quot;&gt;&lt;font face=&quot;Helvetica&quot; style=&quot;font-size: 12px;&quot;&gt;Simulation&lt;/font&gt;&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="740" y="170" width="120" height="80" as="geometry" />
</mxCell>
<mxCell id="8_F79eOSLjp0VsFUqVzp-20" value="&lt;div style=&quot;line-height: 19px; white-space: pre;&quot;&gt;&lt;font face=&quot;Helvetica&quot; style=&quot;font-size: 12px;&quot;&gt;GlobalOutcome&lt;/font&gt;&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="230" y="440" width="630" height="80" as="geometry" />
</mxCell>
<mxCell id="8_F79eOSLjp0VsFUqVzp-21" value="&lt;div style=&quot;line-height: 19px; white-space: pre;&quot;&gt;&lt;font face=&quot;Helvetica&quot; style=&quot;font-size: 12px;&quot;&gt;TaskOutcome&lt;/font&gt;&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="230" y="350" width="630" height="80" as="geometry" />
</mxCell>
<mxCell id="8_F79eOSLjp0VsFUqVzp-22" value="&lt;div style=&quot;line-height: 19px; white-space: pre;&quot;&gt;&lt;font style=&quot;font-size: 12px;&quot;&gt;HeaterInstance&lt;/font&gt;&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;" parent="1" vertex="1">
<mxGeometry x="555" y="190" width="150" height="40" as="geometry" />
</mxCell>
<mxCell id="8_F79eOSLjp0VsFUqVzp-23" value="&lt;div style=&quot;line-height: 19px; white-space: pre;&quot;&gt;&lt;font style=&quot;font-size: 12px;&quot; face=&quot;Helvetica&quot;&gt;Host&lt;/font&gt;&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="645" y="200" width="50" height="20" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

View File

@@ -0,0 +1,17 @@
Load Balancer lowers Power Consumption in summer
modifizieren des Heaters erlaubt, die Instanz ist der Heater
0.72h
Skalierbarkeit: Aufgabe des Load Balancers
Seed in Zufallsgenerator um Gebaeude auszuwaehlen
min. 4 Personen
Starten und Stoppen von Tasks Kommandozeilenbefehle
Klasse mit hostname, passwort, die eine Instanz repraesentieren
Status abfragen
auf objekt start tasks aufrufen
Parser von Status um Informationen rauszulesen
In 14 Tagen
GUI: An welchen Tagen wurde wie viel gearbeitet, Dashboard, stats von Boinc Instanzen darstellen
Treffen 15 Uhr 23.04.24
Mockup der UI
5 Wochen
GUI

View File

@@ -0,0 +1,18 @@
Von Ziel aus anfangen zu planen
HPC Aufgabe, mit SSH aufschalten, Programm starten, C++
Idee: Falls kein Task verfuegbar, dann HPC Aufgabe, ansonsten Tasks
CPUh zu Waerme 1 CPUh == 4kW Waerme
CPU Zeit muss nicht genau gemessen werden, sondern nur Gesamtlaufzeit des Programms
Demod ein Mal aufrufen um Daten fuer Zeitraum zu generieren
Daten in sqlight DB schreiben
Project Outline (max 1 Din A4 Seite) auf Moodle hochladen, verwendete Progsprachen, Aufgaben pro Teammember
Aaron: Demod, Load Balancer, Definieren, was Load Balancer als Rueckgabewert haben will, ssh session von allen Heatern uebergeben an, Session in Objekt speichern und an Davids Programm uebergeben, Rueckgabe wann wurde es ausgefuehrt, wie lange lief es, Ergebnis soll im Load balancer landen, Verteilung der Aufgabe in Prozent auf die Instanzen an Davids Middleware schicken.
Python Funktion, Load Balancer soll selbstheilend sein, wenn Heater aussteigt
ssh -> Paramiko, pip install paramiko
Array mit Objekten, ein Objekt enthaelt ssh session und Prozentzahl, wie viel Bedarf, Feld fuer Ergebnis, in das Funktion von David Ergebnis schreibt, Statuswert ob Heater erreichbar war oder nicht
David: HPC Aufgabe, dominik.giel dgiel auf GitHub hinzufuegen.

View File

@@ -0,0 +1,7 @@
In Instanz anmelden
nano ./.ssh/authorized_keys
Schluessel einfuegen
Strg x zum Speichern
Fertig

View File

@@ -0,0 +1 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCz9n3pJLtqT4p6zYvirbGh+9jTIfPjslK59NUvomnNczwh7Xw80I9bmeykygJNIvsOvwbYkRbTn66712x9pnzlj98kVgUrr2/IgsPHvhTRYF+7m00uaYZWbp24x6AQ4HggeIASKeBx5AVmT+JYLUb/vN2uaF6NnLTYVSn3OgKW65HRkwL+6qIitG18uj1a4G3wfSxQ1neU9gWrg+pf9oLkS3BxZHWhXqgQYuhR6hn4IsqjCRuav+Bv6y4RjoRbqrhOAwaRqysDufwavPGV12Xt0t2YqnqXSc8x9lUfJIvMzYLkWgkYr5pqhSoi3ZJwRaVDLPtZT1ZlVoFEI5hIQ0Zuh4l8bUkmgd1m+O7EzO9A3s1aGfM+LDFmS0xpYrgW84E9w1KIGX/mjXaE/wK1VLg/jTjM1wJ5C9F3G5cyj+spD2GQKD5i671HnYA7lW7Vclw4k2uuyMym9lDguhFWTiOj2EodCex5SwVoSypM1Bc6cEhB3V4YWYRlGedgVRDMBIz4gSL8shKPg8Fw5+czYiIm+lb3d97o51t8RNpeGdsfDuSqKGKPNtl1isGZgUnmZNu+djqlM1ziYmrGqHpZidDwg66y9VFLiWLCxgpNuNjUgvnKoZATVGP18GNfAAMzG8pT4TL3uFPqZ2iaT7o9/jzQjfQIQMDAkfC3a4KKw9turQ== theresaherr@Theresas-MacBook-Pro-9.fritz.box

View File

@@ -0,0 +1 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC6mVWkB/4QlXtq40UjgsYZoE6hEs/vv/aUyJ7bxPtJnHu/S7SnPovWS1UjI8sMhuwXQ+XK/SOwjxosuLfozdrDHisWWmBmKvjrWwmhJPl51K7GRAZNXK/DWyrXhJeuVoYnA2fxzXFUnpBkl3hxtJNTh0kz1beCxATbh4uprJotgh0rrsRCJwivgkgrcrKBUZI3Op6L+VeYdqcxbcpothh3w7FLEmloCn40JRxowQqMozr1Ph5uujCyNk2+imgxnxnzvYBJLyVLr24XV2jzl7FEnGa5knRd9zV0icG2N0Y2gTY1wgCrKqfnsV99WsscPZ+jGG2cntKeaaCrtxLLlwY4JCvHqfJlsi0i724f80Q5BRIiSiaAqa7Sk3VdL7E9A3bd3HeuAuw7YOMKE05DzJ+K+2FZrZT00R1+bYsgpIfHkHSND08cO13gkF4mgPvD+lG0b2ZBn1/9A90gAwP0YqK7ZuQ7YX+uu03ginnZAGkdF9BvNvnvouCb/8EKAsjMrAs= student@student

View File

@@ -0,0 +1,11 @@
1. Muss die GUI schoen aussehen? -> Duerfen wir Graphana verwenden?
> Graphana Beispiel: https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fwww.lionbloggertech.com%2Fwp-content%2Fuploads%2F2020%2F08%2Fgrafana_visualize.jpg&f=1&nofb=1&ipt=600c75674993907aa247a68c66bcff6b88265e5d0e1b1d36351a67d43a1cc4b8&ipo=images
> Graphana Website: https://grafana.com/
Antwort: Ist erlaubt
2. Was tun wenn dauerhafte Ausnutzung der Instanz nicht moeglich ist, da nicht durchgehend Tasks verfuegbar sind?
Antwort: HPC Problem loesen

View File

@@ -0,0 +1,23 @@
Datenbank? Dateien?
sqlight, sehr einfache DB, Daten werden dort rein geschrieben
David: Graphana baut Dashboards aus Datenbank, sql abfragen bauen (naechstes Mal fragen ob graphana oder React? Schoenheit?)
UI holt sich Daten
Load Balancer kommuniziert mit den Heater Instanzen und Heat requirement system und schreibt die Daten in sqlight db.
An welchen Tagen wurde wie viel gearbeitet: Histogram
Hitzebedarf ueber Zeit: Zeitgraph x Zeit y Hitzebedarf
Auf naechsten Dienstag:
Theresa:
kleines GUI Mockup vorbereiten
David:
Load Balancer, demod einlesen, kann erst ab 03.05 wg. Bachelorarbeit
Instanz zum laufen bringen
Aaron:
Anderes Projekt mit einfachen Tasks researchen, Iterationsschritt immer gleich lang, Zuverlaessigkeit der Tasks gegeben? CPU soll was zu tun haben
Parsen des Outputs

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