Merge remote-tracking branch 'origin/main'

# Conflicts:
#	typescript/Frontend/src/config/axiosConfig.tsx
This commit is contained in:
Kim 2023-08-23 11:31:42 +02:00
commit 107a439e44
42 changed files with 3077 additions and 597 deletions

View File

@ -267,7 +267,7 @@ public class Controller : ControllerBase
if (! await session.Create(installation))
return Unauthorized();
return installation.FillOrderNumbers().HideParentIfUserHasNoAccessToParent(session!.User);
return installation;
}
[HttpPost(nameof(CreateFolder))]

View File

@ -7,7 +7,7 @@
"dotnetRunMessages": true,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:7087;http://localhost:5031",
"applicationUrl": "https://localhost:8000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}

View File

@ -137,7 +137,7 @@ K3 ✓<y:LabelModel><y:ErdAttributesNodeLabelModel/></y:LabelModel><y:ModelParam
<data key="d5"/>
<data key="d6">
<y:GenericNode configuration="com.yworks.entityRelationship.big_entity">
<y:Geometry height="90.0" width="70.0" x="582.0523615909593" y="160.75568014793453"/>
<y:Geometry height="90.0" width="70.0" x="393.7937432135768" y="243.91085524521077"/>
<y:Fill color="#B4B4FF" transparent="false"/>
<y:BorderStyle color="#000068" type="dashed" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" backgroundColor="#B4B4FF" configuration="com.yworks.entityRelationship.label.name" fontFamily="Dialog" fontSize="12" fontStyle="bold" hasLineColor="false" height="20.344114303588867" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000068" verticalTextPosition="bottom" visible="true" width="10.864044189453125" x="29.567977905273438" xml:space="preserve" y="4.0">5</y:NodeLabel>
@ -154,7 +154,7 @@ K3 ✓<y:LabelModel><y:ErdAttributesNodeLabelModel/></y:LabelModel><y:ModelParam
<data key="d5"/>
<data key="d6">
<y:GenericNode configuration="com.yworks.entityRelationship.big_entity">
<y:Geometry height="90.0" width="70.0" x="175.8255534152188" y="188.59145941969348"/>
<y:Geometry height="90.0" width="70.0" x="241.37121964689652" y="243.91085524521077"/>
<y:Fill color="#B4B4FF" transparent="false"/>
<y:BorderStyle color="#000068" type="dashed" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" backgroundColor="#B4B4FF" configuration="com.yworks.entityRelationship.label.name" fontFamily="Dialog" fontSize="12" fontStyle="bold" hasLineColor="false" height="20.344114303588867" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000068" verticalTextPosition="bottom" visible="true" width="10.864044189453125" x="29.567977905273438" xml:space="preserve" y="4.0">7</y:NodeLabel>
@ -172,7 +172,7 @@ K3 ✓<y:LabelModel><y:ErdAttributesNodeLabelModel/></y:LabelModel><y:ModelParam
<data key="d5"/>
<data key="d6">
<y:GenericNode configuration="com.yworks.entityRelationship.big_entity">
<y:Geometry height="90.0" width="70.0" x="779.8955241254705" y="229.98399686669086"/>
<y:Geometry height="90.0" width="70.0" x="913.7233840476495" y="390.20661887776083"/>
<y:Fill color="#FFEB9C" transparent="false"/>
<y:BorderStyle color="#683A00" type="dashed" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" backgroundColor="#FFEB9C" configuration="com.yworks.entityRelationship.label.name" fontFamily="Dialog" fontSize="12" fontStyle="bold" hasLineColor="false" height="20.344114303588867" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#683A00" verticalTextPosition="bottom" visible="true" width="17.72808837890625" x="26.135955810546875" xml:space="preserve" y="4.0">11</y:NodeLabel>
@ -190,11 +190,11 @@ K3 ✘<y:LabelModel><y:ErdAttributesNodeLabelModel/></y:LabelModel><y:ModelParam
<data key="d5"/>
<data key="d6">
<y:GenericNode configuration="com.yworks.entityRelationship.big_entity">
<y:Geometry height="90.0" width="70.0" x="1080.1411072770327" y="505.0251575795813"/>
<y:Geometry height="90.0" width="70.0" x="1032.06702167392" y="683.7966785548988"/>
<y:Fill color="#FFEB9C" transparent="false"/>
<y:BorderStyle color="#683A00" type="dashed" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" backgroundColor="#FFEB9C" configuration="com.yworks.entityRelationship.label.name" fontFamily="Dialog" fontSize="12" fontStyle="bold" hasLineColor="false" height="20.344114303588867" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#683A00" verticalTextPosition="bottom" visible="true" width="17.72808837890625" x="26.135955810546875" xml:space="preserve" y="3.999999999999943">15</y:NodeLabel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="53.0323429107666" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#683A00" verticalTextPosition="top" visible="true" width="30.628189086914062" x="2.0" xml:space="preserve" y="32.34411430358881">K1 ✓
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="53.0323429107666" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#683A00" verticalTextPosition="top" visible="true" width="30.628189086914062" x="2.0" xml:space="preserve" y="32.34411430358887">K1 ✓
K2 ✓
K3 ✓<y:LabelModel><y:ErdAttributesNodeLabelModel/></y:LabelModel><y:ModelParameter><y:ErdAttributesNodeLabelModelParameter/></y:ModelParameter></y:NodeLabel>
<y:StyleProperties>
@ -207,7 +207,7 @@ K3 ✓<y:LabelModel><y:ErdAttributesNodeLabelModel/></y:LabelModel><y:ModelParam
<data key="d5"/>
<data key="d6">
<y:GenericNode configuration="com.yworks.entityRelationship.big_entity">
<y:Geometry height="90.0" width="70.0" x="614.6002578542565" y="0.0"/>
<y:Geometry height="90.0" width="70.0" x="393.7937432135768" y="32.482490272373525"/>
<y:Fill color="#B4B4FF" transparent="false"/>
<y:BorderStyle color="#000068" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" backgroundColor="#B4B4FF" configuration="com.yworks.entityRelationship.label.name" fontFamily="Dialog" fontSize="12" fontStyle="bold" hasLineColor="false" height="20.344114303588867" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000068" verticalTextPosition="bottom" visible="true" width="17.72808837890625" x="26.135955810546875" xml:space="preserve" y="4.0">21</y:NodeLabel>
@ -224,7 +224,7 @@ K3 ✓<y:LabelModel><y:ErdAttributesNodeLabelModel/></y:LabelModel><y:ModelParam
<data key="d5"/>
<data key="d6">
<y:GenericNode configuration="com.yworks.entityRelationship.big_entity">
<y:Geometry height="90.0" width="70.0" x="448.50893604095756" y="145.7089497868842"/>
<y:Geometry height="90.0" width="70.0" x="546.2162667802571" y="243.91085524521077"/>
<y:Fill color="#B4B4FF" transparent="false"/>
<y:BorderStyle color="#000068" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" backgroundColor="#B4B4FF" configuration="com.yworks.entityRelationship.label.name" fontFamily="Dialog" fontSize="12" fontStyle="bold" hasLineColor="false" height="20.344114303588867" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000068" verticalTextPosition="bottom" visible="true" width="17.72808837890625" x="26.135955810546875" xml:space="preserve" y="4.0">17</y:NodeLabel>
@ -242,7 +242,7 @@ K3 ✘<y:LabelModel><y:ErdAttributesNodeLabelModel/></y:LabelModel><y:ModelParam
<data key="d5"/>
<data key="d6">
<y:GenericNode configuration="com.yworks.entityRelationship.big_entity">
<y:Geometry height="90.0" width="70.0" x="893.6854614285201" y="301.4829532979212"/>
<y:Geometry height="90.0" width="70.0" x="913.7233840476495" y="243.91085524521077"/>
<y:Fill color="#FFEB9C" transparent="false"/>
<y:BorderStyle color="#683A00" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" backgroundColor="#FFEB9C" configuration="com.yworks.entityRelationship.label.name" fontFamily="Dialog" fontSize="12" fontStyle="bold" hasLineColor="false" height="20.344114303588867" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#683A00" verticalTextPosition="bottom" visible="true" width="17.72808837890625" x="26.135955810546875" xml:space="preserve" y="4.0">25</y:NodeLabel>
@ -260,7 +260,7 @@ K3 ✘<y:LabelModel><y:ErdAttributesNodeLabelModel/></y:LabelModel><y:ModelParam
<data key="d5"/>
<data key="d6">
<y:GenericNode configuration="com.yworks.entityRelationship.big_entity">
<y:Geometry height="90.0" width="70.0" x="854.6780813018231" y="84.0068637493531"/>
<y:Geometry height="90.0" width="70.0" x="1110.6401046481265" y="390.20661887776083"/>
<y:Fill color="#FFEB9C" transparent="false"/>
<y:BorderStyle color="#683A00" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" backgroundColor="#FFEB9C" configuration="com.yworks.entityRelationship.label.name" fontFamily="Dialog" fontSize="12" fontStyle="bold" hasLineColor="false" height="20.344114303588867" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#683A00" verticalTextPosition="bottom" visible="true" width="17.72808837890625" x="26.135955810546875" xml:space="preserve" y="4.0">27</y:NodeLabel>
@ -278,7 +278,7 @@ K3 ✘<y:LabelModel><y:ErdAttributesNodeLabelModel/></y:LabelModel><y:ModelParam
<data key="d5"/>
<data key="d6">
<y:GenericNode configuration="com.yworks.entityRelationship.big_entity">
<y:Geometry height="90.0" width="70.0" x="1219.0187296181307" y="417.76256385308943"/>
<y:Geometry height="90.0" width="70.0" x="1268.5061818399" y="683.7966785548988"/>
<y:Fill color="#FFEB9C" color2="#FF0000" transparent="false"/>
<y:BorderStyle color="#683A00" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" backgroundColor="#FFEB9C" configuration="com.yworks.entityRelationship.label.name" fontFamily="Dialog" fontSize="12" fontStyle="bold" hasLineColor="false" height="20.344114303588867" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#683A00" verticalTextPosition="bottom" visible="true" width="17.72808837890625" x="26.135955810546875" xml:space="preserve" y="4.0">31</y:NodeLabel>
@ -414,7 +414,7 @@ K3 ✓<y:LabelModel><y:ErdAttributesNodeLabelModel/></y:LabelModel><y:ModelParam
<data key="d5"/>
<data key="d6">
<y:GenericNode configuration="com.yworks.entityRelationship.big_entity">
<y:Geometry height="90.0" width="70.0" x="410.66241765886343" y="1681.8835044865843"/>
<y:Geometry height="90.0" width="70.0" x="381.1829957002519" y="1666.836774125534"/>
<y:Fill color="#B4B4FF" transparent="false"/>
<y:BorderStyle color="#000068" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" backgroundColor="#B4B4FF" configuration="com.yworks.entityRelationship.label.name" fontFamily="Dialog" fontSize="12" fontStyle="bold" hasLineColor="false" height="20.344114303588867" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000068" verticalTextPosition="bottom" visible="true" width="17.72808837890625" x="26.135955810546875" xml:space="preserve" y="4.0">16</y:NodeLabel>
@ -448,7 +448,7 @@ K3 ✓<y:LabelModel><y:ErdAttributesNodeLabelModel/></y:LabelModel><y:ModelParam
<data key="d5"/>
<data key="d6">
<y:GenericNode configuration="com.yworks.entityRelationship.big_entity">
<y:Geometry height="90.0" width="70.0" x="244.57109584556457" y="1827.5924542734685"/>
<y:Geometry height="90.0" width="70.0" x="247.63957015025017" y="1827.5924542734685"/>
<y:Fill color="#B4B4FF" transparent="false"/>
<y:BorderStyle color="#000068" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" backgroundColor="#B4B4FF" configuration="com.yworks.entityRelationship.label.name" fontFamily="Dialog" fontSize="12" fontStyle="bold" hasLineColor="false" height="20.344114303588867" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000068" verticalTextPosition="bottom" visible="true" width="17.72808837890625" x="26.135955810546875" xml:space="preserve" y="4.0">18</y:NodeLabel>
@ -465,7 +465,7 @@ K3 ✘<y:LabelModel><y:ErdAttributesNodeLabelModel/></y:LabelModel><y:ModelParam
<data key="d5"/>
<data key="d6">
<y:GenericNode configuration="com.yworks.entityRelationship.big_entity">
<y:Geometry height="90.0" width="70.0" x="277.1189921088617" y="1666.836774125534"/>
<y:Geometry height="90.0" width="70.0" x="247.63957015025017" y="1666.836774125534"/>
<y:Fill color="#B4B4FF" transparent="false"/>
<y:BorderStyle color="#000068" type="dashed" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" backgroundColor="#B4B4FF" configuration="com.yworks.entityRelationship.label.name" fontFamily="Dialog" fontSize="12" fontStyle="bold" hasLineColor="false" height="20.344114303588867" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000068" verticalTextPosition="bottom" visible="true" width="10.864044189453125" x="29.567977905273438" xml:space="preserve" y="4.0">2</y:NodeLabel>
@ -482,7 +482,7 @@ K3 ✘<y:LabelModel><y:ErdAttributesNodeLabelModel/></y:LabelModel><y:ModelParam
<data key="d5"/>
<data key="d6">
<y:GenericNode configuration="com.yworks.entityRelationship.big_entity">
<y:Geometry height="90.0" width="70.0" x="745.7969893047066" y="1614.0294463149824"/>
<y:Geometry height="90.0" width="70.0" x="735.2707053490758" y="1666.836774125534"/>
<y:Fill color="#FFEB9C" transparent="false"/>
<y:BorderStyle color="#683A00" type="dashed" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" backgroundColor="#FFEB9C" configuration="com.yworks.entityRelationship.label.name" fontFamily="Dialog" fontSize="12" fontStyle="bold" hasLineColor="false" height="20.344114303588867" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#683A00" verticalTextPosition="bottom" visible="true" width="17.72808837890625" x="26.135955810546875" xml:space="preserve" y="4.0">10</y:NodeLabel>
@ -499,7 +499,7 @@ K3 ✘<y:LabelModel><y:ErdAttributesNodeLabelModel/></y:LabelModel><y:ModelParam
<data key="d5"/>
<data key="d6">
<y:GenericNode configuration="com.yworks.entityRelationship.big_entity">
<y:Geometry height="90.0" width="70.0" x="618.9499887405645" y="1658.4151315081558"/>
<y:Geometry height="90.0" width="70.0" x="572.3666077540471" y="1666.836774125534"/>
<y:Fill color="#FFEB9C" transparent="false"/>
<y:BorderStyle color="#683A00" type="dashed" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" backgroundColor="#FFEB9C" configuration="com.yworks.entityRelationship.label.name" fontFamily="Dialog" fontSize="12" fontStyle="bold" hasLineColor="false" height="20.344114303588867" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#683A00" verticalTextPosition="bottom" visible="true" width="17.72808837890625" x="26.135955810546875" xml:space="preserve" y="4.0">12</y:NodeLabel>
@ -516,7 +516,7 @@ K3 ✓<y:LabelModel><y:ErdAttributesNodeLabelModel/></y:LabelModel><y:ModelParam
<data key="d5"/>
<data key="d6">
<y:GenericNode configuration="com.yworks.entityRelationship.big_entity">
<y:Geometry height="90.0" width="70.0" x="659.3746230785885" y="1817.3729975910535"/>
<y:Geometry height="90.0" width="70.0" x="572.3666077540471" y="1827.5924542734685"/>
<y:Fill color="#FFEB9C" transparent="false"/>
<y:BorderStyle color="#683A00" type="dashed" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" backgroundColor="#FFEB9C" configuration="com.yworks.entityRelationship.label.name" fontFamily="Dialog" fontSize="12" fontStyle="bold" hasLineColor="false" height="20.344114303588867" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#683A00" verticalTextPosition="bottom" visible="true" width="17.72808837890625" x="26.135955810546875" xml:space="preserve" y="4.0">14</y:NodeLabel>
@ -533,7 +533,7 @@ K3 ✓<y:LabelModel><y:ErdAttributesNodeLabelModel/></y:LabelModel><y:ModelParam
<data key="d5"/>
<data key="d6">
<y:GenericNode configuration="com.yworks.entityRelationship.big_entity">
<y:Geometry height="90.0" width="70.0" x="813.3003459398944" y="1763.512057658155"/>
<y:Geometry height="90.0" width="70.0" x="735.2707053490758" y="1827.5924542734685"/>
<y:Fill color="#FFEB9C" transparent="false"/>
<y:BorderStyle color="#683A00" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" backgroundColor="#FFEB9C" configuration="com.yworks.entityRelationship.label.name" fontFamily="Dialog" fontSize="12" fontStyle="bold" hasLineColor="false" height="20.344114303588867" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#683A00" verticalTextPosition="bottom" visible="true" width="17.72808837890625" x="26.135955810546875" xml:space="preserve" y="4.0">26</y:NodeLabel>
@ -550,7 +550,7 @@ K3 ✘<y:LabelModel><y:ErdAttributesNodeLabelModel/></y:LabelModel><y:ModelParam
<data key="d5"/>
<data key="d6">
<y:GenericNode configuration="com.yworks.entityRelationship.big_entity">
<y:Geometry height="90.0" width="70.0" x="699.7992574166124" y="1976.3308636739512"/>
<y:Geometry height="90.0" width="70.0" x="572.3666077540471" y="1988.348134421403"/>
<y:Fill color="#FFEB9C" transparent="false"/>
<y:BorderStyle color="#683A00" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" backgroundColor="#FFEB9C" configuration="com.yworks.entityRelationship.label.name" fontFamily="Dialog" fontSize="12" fontStyle="bold" hasLineColor="false" height="20.344114303588867" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#683A00" verticalTextPosition="bottom" visible="true" width="17.72808837890625" x="26.135955810546875" xml:space="preserve" y="4.0">30</y:NodeLabel>
@ -582,8 +582,8 @@ Inverters<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0"
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" anchorX="-22.56775183821742" anchorY="-48.88515038132459" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="36.688228607177734" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="center" ratio="0.5" textColor="#000000" upX="-0.33027906195516804" upY="0.9438833303083672" verticalTextPosition="bottom" visible="true" width="57.61639404296875" x="-89.06825945702936" xml:space="preserve" y="-67.91463895907565">switch to
grid tie<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEnabled="true" distance="2.0" distanceRelativeToEdge="true" mode="side_slider"/></y:LabelModel><y:ModelParameter><y:RotatedSliderEdgeLabelModelParameter invertingSign="false" ratio="0.5" segment="0"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" placement="center" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
<y:EdgeLabel alignment="center" anchorX="-22.56775183821742" anchorY="-5.777885637190593" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="36.688228607177734" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="center" ratio="0.5" textColor="#000000" upX="-0.33027906195516804" upY="0.9438833303083672" verticalTextPosition="bottom" visible="true" width="57.61639404296875" x="-89.06825945702936" xml:space="preserve" y="-24.807374214941643">switch to
grid tie<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEnabled="true" distance="2.0" distanceRelativeToEdge="true" mode="side_slider"/></y:LabelModel><y:ModelParameter><y:RotatedSliderEdgeLabelModelParameter invertingSign="true" ratio="0.5" segment="0"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" placement="center" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
@ -641,10 +641,13 @@ Inverters<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEn
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:Path sx="0.0" sy="0.0" tx="21.046829314938122" ty="-45.01680098318991">
<y:Point x="874.0493776621389" y="435.20661887776083"/>
<y:Point x="715.6320547466846" y="519.7002048682145"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" anchorX="-30.708326355243116" anchorY="86.75687917585009" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.344114303588867" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" upX="0.935215143023455" upY="0.3540799856806631" verticalTextPosition="bottom" visible="true" width="50.236328125" x="-48.49600469839221" xml:space="preserve" y="86.75687917585009">open K2<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEnabled="true" distance="2.0" distanceRelativeToEdge="true" mode="side_slider"/></y:LabelModel><y:ModelParameter><y:RotatedSliderEdgeLabelModelParameter invertingSign="true" ratio="0.5" segment="0"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
<y:EdgeLabel alignment="center" anchorX="-95.81164490935862" anchorY="32.19070766070081" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.344114303588867" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" upX="0.47060708552926384" upY="0.8823428874590944" verticalTextPosition="bottom" visible="true" width="50.236328125" x="-140.13731172251363" xml:space="preserve" y="32.19070766070081">open K2<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEnabled="true" distance="2.0" distanceRelativeToEdge="true" mode="side_slider"/></y:LabelModel><y:ModelParameter><y:RotatedSliderEdgeLabelModelParameter invertingSign="true" ratio="0.5000000000000019" segment="1"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
@ -656,7 +659,7 @@ Inverters<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEn
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" anchorX="-91.4265773429479" anchorY="31.058243169626735" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.344114303588867" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="center" ratio="0.5" textColor="#000000" upX="0.5320320765153349" upY="0.8467241992282852" verticalTextPosition="bottom" visible="true" width="50.236328125" x="-133.9628920467579" xml:space="preserve" y="31.058243169626735">open K2<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEnabled="true" distance="2.0" distanceRelativeToEdge="true" mode="side_slider"/></y:LabelModel><y:ModelParameter><y:RotatedSliderEdgeLabelModelParameter invertingSign="false" ratio="0.5" segment="1"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" placement="center" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
<y:EdgeLabel alignment="center" anchorX="-58.091523252820025" anchorY="-22.344127643221327" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.344114303588867" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="center" ratio="0.5" textColor="#000000" upX="-1.2246467991473532E-16" upY="1.0" verticalTextPosition="bottom" visible="true" width="50.236328125" x="-108.32785137782002" xml:space="preserve" y="-22.344127643221334">open K2<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEnabled="true" distance="2.0" distanceRelativeToEdge="true" mode="side_slider"/></y:LabelModel><y:ModelParameter><y:RotatedSliderEdgeLabelModelParameter invertingSign="false" ratio="0.5" segment="1"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" placement="center" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
@ -665,11 +668,14 @@ Inverters<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEn
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:Path sx="0.0" sy="0.0" tx="15.152106842485068" ty="-45.029446684529034">
<y:Point x="581.2162667802571" y="365.51788910504735"/>
<y:Point x="528.1404648420541" y="447.09481487826343"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" anchorX="-29.97425394733284" anchorY="101.74082753289088" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="36.688228607177734" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="center" ratio="0.5" textColor="#000000" upX="0.9962292767888588" upY="-0.0867595992887667" verticalTextPosition="bottom" visible="true" width="48.38832092285156" x="-29.97425394733284" xml:space="preserve" y="98.55777152031747">turn off
inverter<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEnabled="true" distance="2.0" distanceRelativeToEdge="true" mode="side_slider"/></y:LabelModel><y:ModelParameter><y:RotatedSliderEdgeLabelModelParameter invertingSign="false" ratio="0.5" segment="1"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" placement="center" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
<y:EdgeLabel alignment="center" anchorX="-11.667059533747079" anchorY="53.20739194515636" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="36.688228607177734" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="center" ratio="0.5" textColor="#000000" upX="0.8382050245156525" upY="0.5453552391576654" verticalTextPosition="bottom" visible="true" width="48.38832092285156" x="-38.055883863066654" xml:space="preserve" y="53.20739194515636">turn off
inverter<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEnabled="true" distance="2.0" distanceRelativeToEdge="true" mode="side_slider"/></y:LabelModel><y:ModelParameter><y:RotatedSliderEdgeLabelModelParameter invertingSign="true" ratio="0.5000000000000003" segment="1"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" placement="center" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
@ -681,7 +687,7 @@ inverter<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEna
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="36.688228607177734" horizontalTextPosition="center" iconTextGap="4" modelName="side_slider" preferredPlacement="center" ratio="0.0" textColor="#000000" verticalTextPosition="bottom" visible="true" width="54.13636779785156" x="-61.48240607634398" xml:space="preserve" y="-30.072393595089466">turn off
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="36.688228607177734" horizontalTextPosition="center" iconTextGap="4" modelName="side_slider" preferredPlacement="center" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="54.13636779785156" x="-110.27792003264085" xml:space="preserve" y="-34.688241946810194">turn off
inverters<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" placement="center" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
@ -694,7 +700,7 @@ inverters<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0"
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" anchorX="-40.1366872375786" anchorY="3.276745889159926" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="36.688228607177734" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="center" ratio="0.5" textColor="#000000" upX="0.9801127226267146" upY="0.19844155548989476" verticalTextPosition="bottom" visible="true" width="54.13636779785156" x="-50.87959227195731" xml:space="preserve" y="3.276745889159926">turn off
<y:EdgeLabel alignment="center" anchorX="-38.68821708305404" anchorY="33.66069253418277" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="36.688228607177734" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="center" ratio="0.5" textColor="#000000" upX="1.0" upY="1.8369701987210297E-16" verticalTextPosition="bottom" visible="true" width="54.13636779785156" x="-38.68821708305405" xml:space="preserve" y="33.66069253418277">turn off
inverters<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEnabled="true" distance="2.0" distanceRelativeToEdge="true" mode="side_slider"/></y:LabelModel><y:ModelParameter><y:RotatedSliderEdgeLabelModelParameter invertingSign="false" ratio="0.5" segment="0"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" placement="center" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
@ -707,7 +713,7 @@ inverters<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEn
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" anchorX="-36.42344224440467" anchorY="-13.753969151122476" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="36.688228607177734" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="center" ratio="0.5" textColor="#000000" upX="0.8900092690299531" upY="0.45594243171783055" verticalTextPosition="bottom" visible="true" width="54.13636779785156" x="-61.106509422527964" xml:space="preserve" y="-13.753969151122476">turn off
<y:EdgeLabel alignment="center" anchorX="-36.34321140162365" anchorY="-38.68821373332315" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="36.688228607177734" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="center" ratio="0.5" textColor="#000000" upX="-1.2246467991473532E-16" upY="1.0" verticalTextPosition="bottom" visible="true" width="54.13636779785156" x="-90.47957919947521" xml:space="preserve" y="-38.68821373332316">turn off
inverters<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEnabled="true" distance="2.0" distanceRelativeToEdge="true" mode="side_slider"/></y:LabelModel><y:ModelParameter><y:RotatedSliderEdgeLabelModelParameter invertingSign="false" ratio="0.5" segment="0"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" placement="center" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
@ -717,11 +723,14 @@ inverters<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEn
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0">
<y:Point x="875.759476311287" y="288.91085524521077"/>
<y:Point x="694.5852254317465" y="500.95688054473624"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" anchorX="-87.60097524632454" anchorY="36.001631487133864" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="36.688228607177734" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="center" ratio="0.5" textColor="#000000" upX="0.7247896869799892" upY="0.6889701805212248" verticalTextPosition="bottom" visible="true" width="54.13636779785156" x="-124.89931834077376" xml:space="preserve" y="36.001631487133864">turn off
inverters<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEnabled="true" distance="2.0" distanceRelativeToEdge="true" mode="side_slider"/></y:LabelModel><y:ModelParameter><y:RotatedSliderEdgeLabelModelParameter invertingSign="false" ratio="0.5" segment="1"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" placement="center" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
<y:EdgeLabel alignment="center" anchorX="-140.36047493803812" anchorY="60.311953252495755" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="36.688228607177734" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="center" ratio="0.5" textColor="#000000" upX="0.7602822957199457" upY="0.6495928192451093" verticalTextPosition="bottom" visible="true" width="54.13636779785156" x="-175.52707071953466" xml:space="preserve" y="60.311953252495755">turn off
inverters<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEnabled="true" distance="2.0" distanceRelativeToEdge="true" mode="side_slider"/></y:LabelModel><y:ModelParameter><y:RotatedSliderEdgeLabelModelParameter invertingSign="false" ratio="0.5000000000000003" segment="1"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" placement="center" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
@ -743,8 +752,8 @@ inverters<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEn
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" anchorX="-2.720348770062401" anchorY="-8.184439221301773" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="36.688228607177734" horizontalTextPosition="center" iconTextGap="4" modelName="custom" ratio="0.5" textColor="#000000" upX="-0.9962292767888588" upY="0.08675959928876727" verticalTextPosition="bottom" visible="true" width="55.108367919921875" x="-44.05141614024578" xml:space="preserve" y="-63.085008739179884">turn off
Inverters<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEnabled="true" distance="2.0" distanceRelativeToEdge="true" mode="side_slider"/></y:LabelModel><y:ModelParameter><y:RotatedSliderEdgeLabelModelParameter invertingSign="true" ratio="0.0" segment="0"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" placement="center" side="left|right" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
<y:EdgeLabel alignment="center" anchorX="38.68824090899216" anchorY="-89.20073479644952" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="36.688228607177734" horizontalTextPosition="center" iconTextGap="4" modelName="custom" ratio="0.5" textColor="#000000" upX="-1.0" upY="-6.123233995736766E-17" verticalTextPosition="bottom" visible="true" width="55.108367919921875" x="2.0000123018144222" xml:space="preserve" y="-144.3091027163714">turn off
Inverters<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEnabled="true" distance="2.0" distanceRelativeToEdge="true" mode="side_slider"/></y:LabelModel><y:ModelParameter><y:RotatedSliderEdgeLabelModelParameter invertingSign="false" ratio="0.4999999999999987" segment="0"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" placement="center" side="left|right" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
@ -817,7 +826,7 @@ Inverters<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEn
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" anchorX="-0.601812832179121" anchorY="-7.106143485136727" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="36.688228607177734" horizontalTextPosition="center" iconTextGap="4" modelName="custom" ratio="0.5" textColor="#000000" upX="-0.9801127226267147" upY="-0.19844155548989376" verticalTextPosition="bottom" visible="true" width="54.13636779785156" x="-36.56041246071141" xml:space="preserve" y="-67.4463554735874">turn off
<y:EdgeLabel alignment="center" anchorX="-1.9999867345154598" anchorY="-8.293373776572935" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="36.688228607177734" horizontalTextPosition="center" iconTextGap="4" modelName="custom" ratio="0.5" textColor="#000000" upX="-1.0" upY="-6.123233995736766E-17" verticalTextPosition="bottom" visible="true" width="54.13636779785156" x="-38.688215341693194" xml:space="preserve" y="-62.4297415744245">turn off
inverters<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEnabled="true" distance="2.0" distanceRelativeToEdge="true" mode="side_slider"/></y:LabelModel><y:ModelParameter><y:RotatedSliderEdgeLabelModelParameter invertingSign="true" ratio="0.5" segment="0"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" placement="center" side="left|right" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
@ -827,10 +836,13 @@ inverters<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEn
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:Path sx="0.0" sy="0.0" tx="-13.677119243026084" ty="44.9895375252479">
<y:Point x="282.63957015025017" y="1624.6814853696424"/>
<y:Point x="402.50587645722584" y="1481.6545842023272"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" anchorX="26.767035729346617" anchorY="-89.72842788087337" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.344114303588867" horizontalTextPosition="center" iconTextGap="4" modelName="custom" ratio="0.5" textColor="#000000" upX="-0.9519459018107578" upY="-0.30626622410201093" verticalTextPosition="bottom" visible="true" width="50.236328125" x="7.400539492075577" xml:space="preserve" y="-143.7814096319475">open K2<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEnabled="true" distance="2.0" distanceRelativeToEdge="true" mode="side_slider"/></y:LabelModel><y:ModelParameter><y:RotatedSliderEdgeLabelModelParameter invertingSign="true" ratio="0.5" segment="1"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" placement="center" side="left|right" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
<y:EdgeLabel alignment="center" anchorX="42.26630925296365" anchorY="-95.71994547025815" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.344114303588867" horizontalTextPosition="center" iconTextGap="4" modelName="custom" ratio="0.5" textColor="#000000" upX="-0.7664335365409145" upY="-0.6423236209772972" verticalTextPosition="bottom" visible="true" width="50.236328125" x="26.67389777947143" xml:space="preserve" y="-147.29025726298894">open K2<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEnabled="true" distance="2.0" distanceRelativeToEdge="true" mode="side_slider"/></y:LabelModel><y:ModelParameter><y:RotatedSliderEdgeLabelModelParameter invertingSign="true" ratio="0.4999999999999997" segment="1"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" placement="center" side="left|right" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
@ -839,10 +851,13 @@ inverters<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEn
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:Path sx="0.0" sy="0.0" tx="19.39352371895791" ty="45.01703178967159">
<y:Point x="770.2707053490758" y="1624.8477957198368"/>
<y:Point x="626.760131473005" y="1451.0534797665293"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" anchorX="-24.296235447239155" anchorY="-84.70970920941136" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.344114303588867" horizontalTextPosition="center" iconTextGap="4" modelName="custom" ratio="0.5" textColor="#000000" upX="-0.8599279843359526" upY="0.5104153815823985" verticalTextPosition="bottom" visible="true" width="50.236328125" x="-67.43210324264501" xml:space="preserve" y="-127.90933359438213">open K2<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEnabled="true" distance="2.0" distanceRelativeToEdge="true" mode="side_slider"/></y:LabelModel><y:ModelParameter><y:RotatedSliderEdgeLabelModelParameter invertingSign="false" ratio="0.5" segment="0"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" placement="center" side="left|right" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
<y:EdgeLabel alignment="center" anchorX="-38.53255297975011" anchorY="-123.76381877365907" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.344114303588867" horizontalTextPosition="center" iconTextGap="4" modelName="custom" ratio="0.5" textColor="#000000" upX="-0.7710894922153189" upY="0.6367267820620722" verticalTextPosition="bottom" visible="true" width="50.236328125" x="-86.20650129732047" xml:space="preserve" y="-162.50052351832747">open K2<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEnabled="true" distance="2.0" distanceRelativeToEdge="true" mode="side_slider"/></y:LabelModel><y:ModelParameter><y:RotatedSliderEdgeLabelModelParameter invertingSign="false" ratio="0.500000000000001" segment="1"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" placement="center" side="left|right" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
@ -854,7 +869,7 @@ inverters<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEn
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="dashed" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" anchorX="-15.439448720854557" anchorY="-96.96505251280405" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.344114303588867" horizontalTextPosition="center" iconTextGap="4" modelName="custom" ratio="0.5" textColor="#000000" upX="-0.9905575559375823" upY="0.13709751410935023" verticalTextPosition="bottom" visible="true" width="55.984375" x="-43.26678350459801" xml:space="preserve" y="-152.42079818349714">K3 opens<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEnabled="true" distance="2.0" distanceRelativeToEdge="true" mode="side_slider"/></y:LabelModel><y:ModelParameter><y:RotatedSliderEdgeLabelModelParameter invertingSign="true" ratio="0.5" segment="1"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" placement="center" side="left|right" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
<y:EdgeLabel alignment="center" anchorX="-2.000030429546655" anchorY="-99.48622193119718" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.344114303588867" horizontalTextPosition="center" iconTextGap="4" modelName="custom" ratio="0.5" textColor="#000000" upX="-1.0" upY="-6.123233995736766E-17" verticalTextPosition="bottom" visible="true" width="55.984375" x="-22.344144733135522" xml:space="preserve" y="-155.47059693119718">K3 opens<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEnabled="true" distance="2.0" distanceRelativeToEdge="true" mode="side_slider"/></y:LabelModel><y:ModelParameter><y:RotatedSliderEdgeLabelModelParameter invertingSign="true" ratio="0.5" segment="1"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" placement="center" side="left|right" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
@ -866,7 +881,7 @@ inverters<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEn
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" anchorX="19.787500125521547" anchorY="-12.849807218343813" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.344114303588867" horizontalTextPosition="center" iconTextGap="4" modelName="custom" ratio="0.5" textColor="#000000" upX="-0.9691516142140085" upY="0.24646530925544397" verticalTextPosition="bottom" visible="true" width="50.236328125" x="-12.310543238741985" xml:space="preserve" y="-61.53642571287216">open K2<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEnabled="true" distance="2.0" distanceRelativeToEdge="true" mode="side_slider"/></y:LabelModel><y:ModelParameter><y:RotatedSliderEdgeLabelModelParameter invertingSign="false" ratio="0.5" segment="0"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" placement="center" side="left|right" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
<y:EdgeLabel alignment="center" anchorX="22.344083874042212" anchorY="-10.243393612998716" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.344114303588867" horizontalTextPosition="center" iconTextGap="4" modelName="custom" ratio="0.5" textColor="#000000" upX="-1.0" upY="-6.123233995736766E-17" verticalTextPosition="bottom" visible="true" width="50.236328125" x="1.9999695704533451" xml:space="preserve" y="-60.479721737998716">open K2<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEnabled="true" distance="2.0" distanceRelativeToEdge="true" mode="side_slider"/></y:LabelModel><y:ModelParameter><y:RotatedSliderEdgeLabelModelParameter invertingSign="false" ratio="0.5" segment="0"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" placement="center" side="left|right" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
@ -878,7 +893,7 @@ inverters<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEn
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" anchorX="33.170622230079175" anchorY="-20.54894307703944" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="36.688228607177734" horizontalTextPosition="center" iconTextGap="4" modelName="custom" ratio="0.5" textColor="#000000" upX="-0.9113818501099502" upY="0.4115618098052396" verticalTextPosition="bottom" visible="true" width="54.13636779785156" x="-22.546824942353155" xml:space="preserve" y="-69.88784611887813">turn off
<y:EdgeLabel alignment="center" anchorX="38.68824303828478" anchorY="-8.293373776572935" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="36.688228607177734" horizontalTextPosition="center" iconTextGap="4" modelName="custom" ratio="0.5" textColor="#000000" upX="-1.0" upY="-6.123233995736766E-17" verticalTextPosition="bottom" visible="true" width="54.13636779785156" x="2.000014431107047" xml:space="preserve" y="-62.4297415744245">turn off
inverters<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEnabled="true" distance="2.0" distanceRelativeToEdge="true" mode="side_slider"/></y:LabelModel><y:ModelParameter><y:RotatedSliderEdgeLabelModelParameter invertingSign="false" ratio="0.5" segment="0"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" placement="center" side="left|right" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
@ -891,7 +906,7 @@ inverters<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEn
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" anchorX="35.867743762755254" anchorY="-15.93315162786348" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="36.688228607177734" horizontalTextPosition="center" iconTextGap="4" modelName="custom" ratio="0.5" textColor="#000000" upX="-0.9691516142140085" upY="0.24646530925544397" verticalTextPosition="bottom" visible="true" width="54.13636779785156" x="-13.031448845807557" xml:space="preserve" y="-68.39949986683459">turn off
<y:EdgeLabel alignment="center" anchorX="38.68819817763108" anchorY="-8.293308863013408" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="36.688228607177734" horizontalTextPosition="center" iconTextGap="4" modelName="custom" ratio="0.5" textColor="#000000" upX="-1.0" upY="-6.123233995736766E-17" verticalTextPosition="bottom" visible="true" width="54.13636779785156" x="1.9999695704533451" xml:space="preserve" y="-62.42967666086497">turn off
inverters<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEnabled="true" distance="2.0" distanceRelativeToEdge="true" mode="side_slider"/></y:LabelModel><y:ModelParameter><y:RotatedSliderEdgeLabelModelParameter invertingSign="false" ratio="0.5" segment="0"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" placement="center" side="left|right" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
@ -914,7 +929,7 @@ inverters<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEn
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:LineStyle color="#000000" type="dashed" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" anchorX="-24.918635525422495" anchorY="21.594702697511707" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.344114303588867" horizontalTextPosition="center" iconTextGap="4" modelName="custom" ratio="0.5" textColor="#000000" upX="0.9937122098932426" upY="0.11196447610330804" verticalTextPosition="bottom" visible="true" width="55.984375" x="-31.18689674226863" xml:space="preserve" y="21.594702697511707">K1 opens<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEnabled="true" distance="2.0" distanceRelativeToEdge="true" mode="side_slider"/></y:LabelModel><y:ModelParameter><y:RotatedSliderEdgeLabelModelParameter invertingSign="false" ratio="0.4999999999999979" segment="0"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" placement="center" side="left|right" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
<y:BendStyle smoothed="false"/>
@ -925,11 +940,14 @@ inverters<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEn
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0">
<y:Point x="55.81460729275682" y="1523.820754474699"/>
<y:Point x="234.58612826807462" y="1404.8536338521308"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="36.688228607177734" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="54.13636779785156" x="67.5676531310973" xml:space="preserve" y="-100.68725537689579">turn off
inverters<y:LabelModel><y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/></y:LabelModel><y:ModelParameter><y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="36.32125205930789" distanceToCenter="true" position="right" ratio="0.5" segment="0"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
<y:EdgeLabel alignment="center" anchorX="88.28491738649134" anchorY="-52.831225794193415" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="36.688228607177734" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" upX="-0.5540104212935304" upY="-0.832509731533611" verticalTextPosition="bottom" visible="true" width="54.13636779785156" x="67.95925639931545" xml:space="preserve" y="-113.36664507338794">turn off
inverters<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEnabled="true" distance="2.0" distanceRelativeToEdge="true" mode="side_slider"/></y:LabelModel><y:ModelParameter><y:RotatedSliderEdgeLabelModelParameter invertingSign="false" ratio="0.5000000000000027" segment="1"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
@ -938,10 +956,13 @@ inverters<y:LabelModel><y:SmartEdgeLabelModel autoRotationEnabled="false" defaul
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:Path sx="0.0" sy="0.0" tx="-12.103675434707498" ty="-0.8973455153978875">
<y:Point x="428.7937432135768" y="368.2394927091691"/>
<y:Point x="500.8846825648616" y="448.27634874029746"/>
</y:Path>
<y:LineStyle color="#000000" type="dashed" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" anchorX="-51.04636249792975" anchorY="85.707351023137" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.344114303588867" horizontalTextPosition="center" iconTextGap="4" modelName="custom" ratio="0.5" textColor="#000000" upX="0.9519459018107576" upY="0.3062662241020115" verticalTextPosition="bottom" visible="true" width="58.68438720703125" x="-69.0194081815676" xml:space="preserve" y="85.707351023137">K3's open<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEnabled="true" distance="2.0" distanceRelativeToEdge="true" mode="side_slider"/></y:LabelModel><y:ModelParameter><y:RotatedSliderEdgeLabelModelParameter invertingSign="false" ratio="0.5000000000000006" segment="0"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" placement="center" side="left|right" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
<y:EdgeLabel alignment="center" anchorX="-0.19439353507226542" anchorY="67.52807422348087" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.344114303588867" horizontalTextPosition="center" iconTextGap="4" modelName="custom" ratio="0.5" textColor="#000000" upX="0.7430274178867984" upY="-0.669260977697398" verticalTextPosition="bottom" visible="true" width="58.68438720703125" x="-0.19439353507226542" xml:space="preserve" y="53.91255239427337">K3's open<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEnabled="true" distance="2.0" distanceRelativeToEdge="true" mode="side_slider"/></y:LabelModel><y:ModelParameter><y:RotatedSliderEdgeLabelModelParameter invertingSign="false" ratio="0.5" segment="1"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" placement="center" side="left|right" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
@ -949,10 +970,13 @@ inverters<y:LabelModel><y:SmartEdgeLabelModel autoRotationEnabled="false" defaul
<edge id="e31" source="n8" target="n2">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0">
<y:Point x="276.3712196468965" y="371.2720580009981"/>
<y:Point x="321.80474594577396" y="464.4058541099475"/>
</y:Path>
<y:LineStyle color="#000000" type="dashed" width="1.0"/>
<y:Arrows source="none" target="delta"/>
<y:EdgeLabel alignment="center" anchorX="7.546056019917529" anchorY="89.21761327127075" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.344114303588867" horizontalTextPosition="center" iconTextGap="4" modelName="custom" ratio="0.5" textColor="#000000" upX="0.9438833303083674" upY="-0.3302790619551677" verticalTextPosition="bottom" visible="true" width="58.68438720703125" x="7.546056019917529" xml:space="preserve" y="82.4983782827727">K3's open<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEnabled="true" distance="2.0" distanceRelativeToEdge="true" mode="side_slider"/></y:LabelModel><y:ModelParameter><y:RotatedSliderEdgeLabelModelParameter invertingSign="false" ratio="0.5" segment="0"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" placement="center" side="left|right" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
<y:EdgeLabel alignment="center" anchorX="-10.230074924814858" anchorY="67.35206739565979" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.344114303588867" horizontalTextPosition="center" iconTextGap="4" modelName="custom" ratio="0.5" textColor="#000000" upX="0.8987592955167517" upY="-0.43844238928533374" verticalTextPosition="bottom" visible="true" width="58.68438720703125" x="-10.230074924814858" xml:space="preserve" y="58.43234531250035">K3's open<y:LabelModel><y:RotatedSliderEdgeLabelModel angle="0.0" autoRotationEnabled="true" distance="2.0" distanceRelativeToEdge="true" mode="side_slider"/></y:LabelModel><y:ModelParameter><y:RotatedSliderEdgeLabelModelParameter invertingSign="false" ratio="0.5" segment="1"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" placement="center" side="left|right" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>

View File

@ -4,3 +4,5 @@ Salimax001 ie-entwicklung@10.2.3.104
Salimax002 ie-entwicklung@10.2.4.29
Salimax003 ie-entwicklung@10.2.4.33
Salimax004 ie-entwicklung@10.2.4.32
Salimax005 ie-entwicklung@10.2.4.36
Salimax006 ie-entwicklung@10.2.4.35

View File

@ -18,5 +18,5 @@ echo -e "\n============================ Deploy ============================\n"
rsync -v \
./bin/Release/$dotnet_version/linux-x64/publish/* \
$username@$salimax_ip:~/salimax
$username@"$salimax_ip":~/salimax

View File

@ -2,6 +2,7 @@ using InnovEnergy.Lib.Devices.Battery48TL.DataTypes;
using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc;
using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.DataTypes;
using InnovEnergy.Lib.Time.Unix;
using InnovEnergy.Lib.Units;
using InnovEnergy.Lib.Utils;
namespace InnovEnergy.App.SaliMax.Ess;
@ -9,12 +10,13 @@ namespace InnovEnergy.App.SaliMax.Ess;
public static class Controller
{
private static readonly UnixTimeSpan MaxTimeWithoutEoc = UnixTimeSpan.FromDays(7); // TODO: move to config
private static readonly Double BatteryHeatingPower = 200.0; // TODO: move to config
public static EssMode SelectControlMode(this StatusRecord s)
{
//return EssMode.OptimizeSelfConsumption;
return s.StateMachine.State != 16 ? EssMode.Off
return s.StateMachine.State != 23 ? EssMode.Off
: s.MustHeatBatteries() ? EssMode.HeatBatteries
: s.MustDoCalibrationCharge() ? EssMode.CalibrationCharge
: s.MustReachMinSoc() ? EssMode.ReachMinSoc
@ -25,9 +27,7 @@ public static class Controller
public static EssControl ControlEss(this StatusRecord s)
{
var mode = s.SelectControlMode();
mode.WriteLine();
var mode = s.SelectControlMode().WriteLine();
if (mode is EssMode.Off or EssMode.NoGridMeter)
return EssControl.Default;
@ -85,7 +85,7 @@ public static class Controller
// find out if we reach the lower or upper Dc limit by comparing the current Dc voltage to the reference voltage
return s.AcDc.Dc.Voltage > s.Config.ReferenceDcBusVoltage
return s.AcDc.Dc.Voltage > s.Config.ReferenceDcLinkVoltageFromAcDc
? control with { PowerCorrection = clampedPowerDelta.ClampMax(-correction), LimitedBy = EssLimit.ChargeLimitedByMaxDcBusVoltage }
: control with { PowerCorrection = clampedPowerDelta.ClampMin(correction), LimitedBy = EssLimit.DischargeLimitedByMinDcBusVoltage };
}
@ -153,7 +153,19 @@ public static class Controller
private static Double MaxBatteryChargePower(this StatusRecord s)
{
return s.Battery.Devices.Sum(b => b.MaxChargePower);
// This introduce a limit when we don't have communication with batteries
// Otherwise the limit will be 0 and the batteries will be not heated
var maxChargePower = s.Config.Devices.BatteryNodes.Length * BatteryHeatingPower;
if (s.Battery.Devices.Count != 0)
{
maxChargePower = s.Battery.Devices.Sum(b => b.MaxChargePower);
}
maxChargePower.W().ToDisplayString().WriteLine(" Max Charge Power");
return maxChargePower;
}
private static Double CurrentPowerSetPoint(this StatusRecord s)

View File

@ -12,7 +12,9 @@ public static class Flow
private static readonly String DownArrowChar = "V";
private static readonly String UpArrowChar = "^";
public static TextBlock Horizontal(Unit amount, Int32 width = 10)
public static TextBlock Horizontal(Unit amount) => Horizontal(amount, 10);
public static TextBlock Horizontal(Unit amount, Int32 width)
{
var label = amount.ToDisplayString();
var arrowChar = amount.Value < 0 ? LeftArrowChar : RightArrowChar;
@ -22,18 +24,20 @@ public static class Flow
return TextBlock.AlignCenterHorizontal(label, arrow, "");
}
public static TextBlock Vertical(Unit amount) => Vertical(amount, 4);
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
[SuppressMessage("ReSharper", "CoVariantArrayConversion")]
public static TextBlock Vertical(Unit amount, Int32 height = 4)
public static TextBlock Vertical(Unit amount, Int32 height)
{
var label = amount.ToDisplayString();
var arrowChar = amount.Value < 0 ? UpArrowChar : DownArrowChar;
var halfArrow = Enumerable.Repeat(arrowChar, height/2);
var lines = halfArrow
.Append(label)
.Concat(halfArrow)
.ToArray(height / 2 * 2 + 1);
.Append(label)
.Concat(halfArrow)
.ToArray(height / 2 * 2 + 1);
return TextBlock.AlignCenterHorizontal(lines);
}

View File

@ -18,15 +18,9 @@ public class CustomLogger : ILogger
_CurrentFileSizeBytes = File.Exists(logFilePath) ? new FileInfo(logFilePath).Length : 0;
}
public IDisposable? BeginScope<TState>(TState state) where TState : notnull
{
throw new NotImplementedException();
}
public IDisposable? BeginScope<TState>(TState state) where TState : notnull => throw new NotImplementedException();
public Boolean IsEnabled(LogLevel logLevel)
{
return true; // Enable logging for all levels
}
public Boolean IsEnabled(LogLevel logLevel) => true; // Enable logging for all levels
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception, String> formatter)
{
@ -43,7 +37,7 @@ public class CustomLogger : ILogger
File.AppendAllText(_LogFilePath, logMessage + Environment.NewLine);
_CurrentFileSizeBytes += logMessage.Length;
Console.WriteLine( logMessage + Environment.NewLine);
Console.WriteLine(logMessage);
}
private void RotateLogFile()

View File

@ -1,4 +1,5 @@
using System.Runtime.InteropServices;
using System.Reactive.Linq;
using System.Reactive.Threading.Tasks;
using Flurl.Http;
using InnovEnergy.App.SaliMax.Ess;
using InnovEnergy.App.SaliMax.SaliMaxRelays;
@ -13,10 +14,10 @@ using InnovEnergy.Lib.Devices.Trumpf.SystemControl.DataTypes;
using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc;
using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.DataTypes;
using InnovEnergy.Lib.Devices.Trumpf.TruConvertDc;
using InnovEnergy.Lib.Devices.Trumpf.TruConvertDc.Control;
using InnovEnergy.Lib.Protocols.Modbus.Channels;
using InnovEnergy.Lib.Time.Unix;
using InnovEnergy.Lib.Units;
using InnovEnergy.Lib.Units.Power;
using InnovEnergy.Lib.Utils;
using static InnovEnergy.Lib.Devices.Trumpf.SystemControl.DataTypes.SystemConfig;
using AcPower = InnovEnergy.Lib.Units.Composite.AcPower;
@ -28,10 +29,7 @@ namespace InnovEnergy.App.SaliMax;
internal static class Program
{
[DllImport("libsystemd.so.0")]
private static extern Int32 sd_notify(Int32 unsetEnvironment, String state);
private const UInt32 UpdateIntervalSeconds = 2;
private static readonly UnixTimeSpan UpdateInterval = UnixTimeSpan.FromSeconds(2);
private static readonly IReadOnlyList<Byte> BatteryNodes;
@ -59,7 +57,7 @@ internal static class Program
BatteryNodes = config
.Devices
.BatteryNodes
.Select(n=>n.ConvertTo<Byte>())
.Select(n => n.ConvertTo<Byte>())
.ToArray(config.Devices.BatteryNodes.Length);
}
@ -82,8 +80,7 @@ internal static class Program
{
"Starting SaliMax".LogInfo();
// Send the initial "service started" message to systemd
var sdNotifyReturn = sd_notify(0, "READY=1");
Watchdog.Ready();
var battery48TlDevices = BatteryNodes
.Select(n => new Battery48TlDevice(BatteriesChannel, n))
@ -99,25 +96,12 @@ internal static class Program
StatusRecord ReadStatus()
{
"Reading AcDC".LogInfo();
var acDc = acDcDevices.Read();
"Reading dcDc".LogInfo();
var dcDc = dcDcDevices.Read();
"Reading battery".LogInfo();
var battery = batteryDevices.Read();
"Reading relays".LogInfo();
var acDc = acDcDevices.Read();
var dcDc = dcDcDevices.Read();
var relays = saliMaxRelaysDevice.Read();
"Reading loadOnAcIsland".LogInfo();
var loadOnAcIsland = acIslandLoadMeter.Read();
"Reading gridMeter".LogInfo();
var gridMeter = gridMeterDevice.Read();
"Reading pvOnDc".LogInfo();
var pvOnDc = amptDevice.Read();
var pvOnAcGrid = AcPowerDevice.Null;
@ -144,7 +128,6 @@ internal static class Program
var loadOnDc = new DcPowerDevice { Power = dcPower} ;
return new StatusRecord
{
AcDc = acDc ?? AcDcDevicesRecord.Null,
@ -177,34 +160,40 @@ internal static class Program
dcDcDevices.Write(r.DcDc);
}
const Int32 delayTime = 10;
Console.WriteLine("press ctrl-c to stop");
Console.WriteLine("press ctrl-C to stop");
while (true)
{
sd_notify(0, "WATCHDOG=1");
await Observable
.Interval(UpdateInterval.ToTimeSpan())
.Select(_ => RunIteration())
.SelectMany(r => UploadCsv(r, UnixTime.Now.RoundTo(UpdateInterval)))
.SelectError()
.ToTask();
}
// var iterations = from _ in Observable.Interval(UpdateInterval.ToTimeSpan())
// let t = UnixTime.Now.RoundTo(UpdateInterval)
// let record = RunIteration()
// from uploaded in UploadCsv(record, t)
// select uploaded;
//
// using var running = iterations.Subscribe();
StatusRecord RunIteration()
{
Watchdog.Alive();
var t = UnixTime.Now;
while (t.Ticks % UpdateIntervalSeconds != 0)
{
await Task.Delay(delayTime);
t = UnixTime.Now;
}
var record = ReadStatus();
if (record.Relays is not null)
record.Relays.ToCsv().LogInfo();
record.ControlConstants();
record.ControlSystemState();
(t + "\n").LogInfo();
$"{record.StateMachine.State}: {record.StateMachine.Message}".LogInfo();
$"Batteries SOC: {record.Battery.Soc}".LogInfo();
var essControl = record.ControlEss().LogInfo();
record.EssControl = essControl;
@ -216,183 +205,23 @@ internal static class Program
WriteControl(record);
PrintTopology(record);
Topology.From(record).WriteLine();
await UploadCsv(record, t);
//record.ToCsv().WriteLine();
//await UploadCsv(record, t);
record.Config.Save();
"===========================================".LogInfo();
return record;
}
// ReSharper disable once FunctionNeverReturns
}
private static void PrintTopology(StatusRecord s)
{
// Power Measurement Values
var gridPower = s.GridMeter is not null ? s.GridMeter!.Ac.Power.Active : 0;
var inverterPower = s.AcDc.Ac.Power.Active;
var islandLoadPower = s.LoadOnAcIsland is not null ? s.LoadOnAcIsland.Ac.Power.Active : 0;
var dcBatteryPower = s.DcDc.Dc.Battery.Power;
var dcdcPower = s.DcDc.Dc.Link.Power;
var pvOnDcPower = s.PvOnDc.Dc!.Power.Value;
// Power Calculated Values
var islandToGridBusPower = inverterPower + islandLoadPower;
var gridLoadPower = s.LoadOnAcGrid is null ? 0: s.LoadOnAcGrid.Power.Active;
TextBlock gridBusColumn;
TextBlock gridBox;
TextBlock totalBoxes;
if (s.GridMeter is not null)
{
var gridPowerByPhase = TextBlock.AlignLeft(s.GridMeter.Ac.L1.Power.Active.ToDisplayString(),
s.GridMeter.Ac.L2.Power.Active.ToDisplayString(),
s.GridMeter.Ac.L3.Power.Active.ToDisplayString());
var gridVoltageByPhase = TextBlock.AlignLeft(s.GridMeter.Ac.L1.Voltage.ToDisplayString(),
s.GridMeter.Ac.L2.Voltage.ToDisplayString(),
s.GridMeter.Ac.L3.Voltage.ToDisplayString());
gridBusColumn = ColumnBox("Pv", "Grid Bus", "Load" , gridVoltageByPhase , gridLoadPower);
gridBox = TextBlock.AlignLeft(gridPowerByPhase).TitleBox("Grid");
}
else
{
gridBusColumn = TextBlock.Spacer(0);
gridBox = TextBlock.Spacer(0);
}
var inverterPowerByPhase = TextBlock.AlignLeft(s.AcDc.Ac.L1.Power.Active.ToDisplayString(),
s.AcDc.Ac.L2.Power.Active.ToDisplayString(),
s.AcDc.Ac.L3.Power.Active.ToDisplayString());
// ReSharper disable once CoVariantArrayConversion
var inverterPowerByAcDc = TextBlock.AlignLeft(s.AcDc.Devices
.Select(s1 => s1.Status.Ac.Power)
.ToArray());
var dcLinkVoltage = TextBlock.AlignCenterHorizontal("",
s.DcDc.Dc.Link.Voltage.ToDisplayString(),
"");
var dc48Voltage = s.DcDc.Dc.Battery.Voltage.ToDisplayString();
var batteryVoltage = s.Battery.Dc.Voltage.Value.RoundToSignificantDigits(3);
var batterySoc = s.Battery.Devices.Any()? s.Battery.Devices.Average(b=>b.Soc).Percent().ToDisplayString() : "0";
var batteryCurrent = s.Battery.Dc.Current.ToDisplayString();
var batteryTemp = s.Battery.Temperature.ToDisplayString();
var batteryHeatingCurrent = s.Battery.HeatingCurrent.ToDisplayString();
var anyBatteryAlarm = s.Battery.Alarms.Any();
var anyBatteryWarning = s.Battery.Warnings.Any();
var islandBusColumn = ColumnBox("Pv", "Island Bus", "Load" , inverterPowerByPhase, islandLoadPower);
var dcBusColumn = ColumnBox("Pv", "Dc Bus", "Load" , dcLinkVoltage, 0, pvOnDcPower);
var gridBusFlow = Flow.Horizontal(gridPower);
var flowGridBusToIslandBus = Flow.Horizontal((ActivePower)islandToGridBusPower);
var flowIslandBusToInverter = Flow.Horizontal(inverterPower);
var flowInverterToDcBus = Flow.Horizontal(inverterPower);
var flowDcBusToDcDc = Flow.Horizontal(dcdcPower);
var flowDcDcToBattery = Flow.Horizontal(dcBatteryPower);
var inverterBox = TextBlock.AlignLeft(inverterPowerByAcDc).TitleBox("AC/DC");
var dcDcBox = TextBlock.AlignLeft(dc48Voltage).TitleBox("DC/DC");
var batteryAvgBox = TextBlock.AlignLeft(batteryVoltage,
batterySoc,
batteryCurrent,
batteryTemp,
batteryHeatingCurrent,
anyBatteryWarning,
anyBatteryAlarm)
.TitleBox("Battery");
//////////////////// Batteries /////////////////////////
IReadOnlyList<TextBlock> batteryBoxes = s.Battery
.Devices
.Select(CreateIndividualBattery)
.ToArray(s.Battery.Devices.Count);
var individualBatteries = batteryBoxes.Any()
? TextBlock.AlignLeft(batteryBoxes)
: TextBlock.Spacer(1);
if (s.GridMeter is not null)
{
totalBoxes = TextBlock.AlignCenterVertical(gridBox,
gridBusFlow,
gridBusColumn,
flowGridBusToIslandBus,
islandBusColumn,
flowIslandBusToInverter,
inverterBox,
flowInverterToDcBus,
dcBusColumn,
flowDcBusToDcDc,
dcDcBox,
flowDcDcToBattery,
batteryAvgBox,
individualBatteries);
}
else
{
totalBoxes = TextBlock.AlignCenterVertical(
islandBusColumn,
flowIslandBusToInverter,
inverterBox,
flowInverterToDcBus,
dcBusColumn,
flowDcBusToDcDc,
dcDcBox,
flowDcDcToBattery,
batteryAvgBox,
individualBatteries);
}
totalBoxes.WriteLine();
}
private static TextBlock CreateIndividualBattery(Battery48TlRecord battery, Int32 i)
{
var batteryWarnings = battery.Warnings.Any();
var batteryAlarms = battery.Alarms.Any();
var content = TextBlock.AlignLeft(battery.Dc.Voltage.ToDisplayString(),
battery.Soc.ToDisplayString(),
battery.Dc.Current.ToDisplayString(),
battery.Temperatures.Cells.Average.ToDisplayString(),
// battery.BusCurrent.ToDisplayString(),
battery.HeatingCurrent.ToDisplayString(),
batteryWarnings,
batteryAlarms);
var box = content.TitleBox($"Battery {i + 1}");
var flow = Flow.Horizontal(battery.Dc.Power);
return TextBlock.AlignCenterVertical(flow, box);
}
private static TextBlock ColumnBox(String pvTitle, String busTitle, String loadTitle, TextBlock dataBox, ActivePower loadPower)
{
return ColumnBox(pvTitle, busTitle, loadTitle, dataBox, loadPower, 0);
}
private static TextBlock ColumnBox(String pvTitle, String busTitle, String loadTitle, TextBlock dataBox, ActivePower loadPower, ActivePower pvPower)
{
var pvBox = TextBlock.FromString(pvTitle).Box();
var pvToBus = Flow.Vertical(pvPower);
var busBox = TextBlock.AlignLeft(dataBox).TitleBox(busTitle);
var busToLoad = Flow.Vertical(loadPower);
var loadBox = TextBlock.FromString(loadTitle).Box();
return TextBlock.AlignCenterHorizontal(pvBox, pvToBus, busBox, busToLoad, loadBox);
}
private static async Task<T?> ResultOrNull<T>(this Task<T> task)
{
@ -407,13 +236,19 @@ internal static class Program
var inverters = r.AcDc.Devices;
var dcDevices = r.DcDc.Devices;
inverters.ForEach(d => d.Control.Dc.MaxVoltage = r.Config.MaxDcBusVoltage);
inverters.ForEach(d => d.Control.Dc.MinVoltage = r.Config.MinDcBusVoltage);
inverters.ForEach(d => d.Control.Dc.ReferenceVoltage = r.Config.ReferenceDcBusVoltage);
inverters.ForEach(d => d.Control.Dc.MaxVoltage = r.Config.MaxDcLinkVoltageFromAcDc);
inverters.ForEach(d => d.Control.Dc.MinVoltage = r.Config.MinDcLinkVoltageFromAcDc);
inverters.ForEach(d => d.Control.Dc.ReferenceVoltage = r.Config.ReferenceDcLinkVoltageFromAcDc);
inverters.ForEach(d => d.Control.Dc.PrechargeConfig = DcPrechargeConfig.PrechargeDcWithInternal);
// dcDevices.ForEach(d => d.Control. Dc.MaxVoltage = r.Config.MaxDcBusVoltage);
// dcDevices.ForEach(d => d.Control. Dc.MinVoltage = r.Config.MinDcBusVoltage);
// dcDevices.ForEach(d => d.Control. Dc.ReferenceVoltage = r.Config.ReferenceDcBusVoltage);
dcDevices.ForEach(d => d.Control.DroopControl.UpperVoltage = r.Config.UpperDcLinkVoltageFromDc);
dcDevices.ForEach(d => d.Control.DroopControl.LowerVoltage = r.Config.LowerDcLinkVoltageFromDc);
dcDevices.ForEach(d => d.Control.DroopControl.ReferenceVoltage = r.Config.ReferenceDcLinkVoltageFromDc);
dcDevices.ForEach(d => d.Control.CurrentControl.MaxBatteryChargingCurrent = r.Config.MaxBatteryChargingCurrent);
dcDevices.ForEach(d => d.Control.CurrentControl.MaxBatteryDischargingCurrent = r.Config.MaxBatteryDischargingCurrent);
dcDevices.ForEach(d => d.Control.VoltageLimits.MaxBatteryVoltage = r.Config.MaxChargeBatteryVoltage);
dcDevices.ForEach(d => d.Control.VoltageLimits.MinBatteryVoltage = r.Config.MinDischargeBatteryVoltage);
dcDevices.ForEach(d => d.Control.ControlMode = DcControlMode.VoltageDroop);
r.DcDc.ResetAlarms();
r.AcDc.ResetAlarms();
@ -483,11 +318,11 @@ internal static class Program
sc.ResetAlarmsAndWarnings = true;
}
private static async Task UploadCsv(StatusRecord status, UnixTime timeStamp)
private static async Task<Boolean> UploadCsv(StatusRecord status, UnixTime timeStamp)
{
var s3Config = status.Config.S3;
if (s3Config is null)
return;
return false;
var csv = status.ToCsv();
var s3Path = timeStamp + ".csv";
@ -500,6 +335,7 @@ internal static class Program
var error = response.GetStringAsync();
Console.WriteLine(error);
}
return true;
}
}

View File

@ -1,6 +1,5 @@
using InnovEnergy.App.SaliMax.Ess;
using InnovEnergy.App.SaliMax.SaliMaxRelays;
using InnovEnergy.Lib.Devices.Battery48TL;
using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc;
using InnovEnergy.Lib.Devices.Trumpf.TruConvertDc;
using static InnovEnergy.Lib.Devices.Trumpf.SystemControl.DataTypes.GridType;
@ -21,13 +20,17 @@ public static class Controller
if (acDcs.NotAvailable())
return 102;
var k4 = acDcs.AllDisabled() ? 0
: acDcs.AllGridTied() ? 1
: acDcs.AllIsland() ? 2
var k4 = acDcs.AllGridTied() ? 0
: acDcs.AllIsland() ? 1
: 4;
if (k4 == 4)
return 103; //Message = "Panic: ACDCs have unequal grid types",
var k5 = acDcs.AllDisabled() ? 0
: acDcs.AllEnabled() ? 1
: 4;
if (k4 == 4 || k5 == 4)
return 103; //Message = "Panic: ACDCs have unequal grid types or power stage",
var nInverters = r.AcDc.Devices.Count;
@ -36,11 +39,11 @@ public static class Controller
var k3 = relays.K3InverterIsConnectedToIslandBus.Take(nInverters).Any(c => c) ? 1 : 0;
// states as defined in states excel sheet
return 1
+ 1*k1
+ 2*k2
+ 4*k3
+ 8*k4;
return 1 * k1
+ 2 * k2
+ 4 * k3
+ 8 * k4
+ 16 * k5;
}
public static Boolean ControlSystemState(this StatusRecord s)
@ -49,19 +52,21 @@ public static class Controller
return s.StateMachine.State switch
{
0 => State0(s),
1 => State1(s),
2 => State2(s),
3 => State3(s),
4 => State4(s),
6 => State6(s),
8 => State8(s),
9 => State9(s),
//10 => State10(s),
12 => State12(s),
13 => State13(s),
15 => State15(s),
16 => State16(s),
17 => State17(s),
18 => State18(s),
21 => State21(s),
19 => State19(s),
22 => State22(s),
23 => State23(s),
24 => State24(s),
28 => State28(s),
29 => State29(s),
101 => State101(s),
102 => State102(s),
@ -76,130 +81,69 @@ public static class Controller
return acDcs.SystemControl == null || acDcs.Devices.Count == 0;
}
private static Boolean NotAvailable(this DcDcDevicesRecord dcDcs)
private static Boolean State0(StatusRecord s)
{
return dcDcs.SystemControl == null || dcDcs.Devices.Count == 0;
}
private static Boolean NotAvailable(this Battery48TlRecords batteries)
{
return batteries.Devices.Count <= 0;
}
private static Boolean State1(StatusRecord s)
{
s.StateMachine.Message = "Inverters are off. Switching to Island Mode.";
s.DcDc.Enable();
s.AcDc.Enable();
s.AcDc.EnableIslandMode();
s.Relays.DisconnectIslandBusFromGrid();
return false;
// => 17
}
private static Boolean State2(StatusRecord s)
{
s.StateMachine.Message = "Inverters are disconnected from Island Bus. Switching to GridTie Mode. C";
s.DcDc.Disable();
s.AcDc.Disable();
s.AcDc.EnableGridTieMode();
s.Relays.ConnectIslandBusToGrid();
return false;
// => 10
}
private static Boolean State4(StatusRecord s)
{
s.StateMachine.Message = "Turning on Inverters";
s.DcDc.Enable();
s.AcDc.Enable();
s.AcDc.EnableGridTieMode();
s.Relays.ConnectIslandBusToGrid();
return false;
// => 12
}
private static Boolean State6(StatusRecord s)
{
s.StateMachine.Message = "Inverters are off. Waiting for them to disconnect from Island Bus.";
s.StateMachine.Message = "Ac/Dc are off. Switching to Island Mode.";
s.DcDc.Disable();
s.AcDc.Disable();
s.AcDc.EnableIslandMode();
s.Relays.DisconnectIslandBusFromGrid();
return true;
return false;
// => 2
// => 8
}
private static Boolean State9(StatusRecord s)
{
s.StateMachine.Message = "Ac/Dc are disconnected from Island Bus. Switching to GridTie Mode.";
s.StateMachine.Message = "Inverters have disconnected from Island Bus. Turning them off.";
s.DcDc.Disable(); // TODO: leave enabled?
s.DcDc.Disable();
s.AcDc.Disable();
s.AcDc.EnableGridTieMode();
s.Relays.DisconnectIslandBusFromGrid();
return true;
return false;
// => 1
}
//
// private static Boolean State10(StatusRecord s)
// {
//
// s.SystemState.Message = "Inverters have disconnected from AcOut. Turning them off.";
//
// s.DcDc.Disable(); // TODO: leave enabled?
// s.AcDc.Disable();
// s.AcDc.EnableGridTieMode();
// s.Relays.DisconnectIslandBusFromGrid();
//
// return true;
//
// // => 12
// }
private static Boolean State12(StatusRecord s)
private static Boolean State1(StatusRecord s)
{
s.StateMachine.Message = "Waiting for Inverters to connect to Island Bus";
s.StateMachine.Message = "Grid Tied mode active, closing k2";
s.DcDc.Disable();
s.AcDc.Disable();
s.AcDc.EnableGridTieMode();
s.Relays.ConnectIslandBusToGrid();
return false;
// => 3
}
private static Boolean State3(StatusRecord s)
{
s.StateMachine.Message = "K2 closed, Turning on Ac/Dc";
s.DcDc.Enable();
s.AcDc.Enable();
s.AcDc.EnableGridTieMode();
s.Relays.ConnectIslandBusToGrid();
return true;
return false;
// => 16
// => 19
}
private static Boolean State13(StatusRecord s)
{
s.StateMachine.Message = "Disconnected from AcIn (K2), awaiting inverters to disconnect from AcOut (K3)";
s.StateMachine.Message = "Ac/Dc are off. Waiting for them to disconnect from Island Bus.";
s.DcDc.Enable();
s.AcDc.Enable();
s.AcDc.EnableGridTieMode();
s.DcDc.Disable();
s.AcDc.Disable();
s.AcDc.EnableIslandMode();
s.Relays.DisconnectIslandBusFromGrid();
return true;
@ -207,32 +151,11 @@ public static class Controller
// => 9
}
private static Boolean State15(StatusRecord s)
private static Boolean State19(StatusRecord s)
{
s.StateMachine.Message = "Grid has been lost, disconnecting AcIn from AcOut (K2)";
s.DcDc.Enable();
s.AcDc.Enable();
s.AcDc.EnableGridTieMode();
s.Relays.DisconnectIslandBusFromGrid();
return true;
// => 13
}
private static Boolean State16(StatusRecord s)
{
// return new
// (
// " Inverter is in grid-tie\n Waiting for K1AcInIsConnectedToGrid to open to leave it",
// AcPowerStageEnable: true,
// DcPowerStageEnable: true,
// GridType.GridTied400V50Hz,
// HighActivePinState.Closed
// );
s.StateMachine.Message = "ESS";
s.StateMachine.Message = "Waiting for Ac/Dc to connect to Island Bus";
s.DcDc.Enable();
s.AcDc.Enable();
@ -241,62 +164,99 @@ public static class Controller
return true;
// => 15
// => 23
}
private static Boolean State17(StatusRecord s)
private static Boolean State23(StatusRecord s)
{
s.StateMachine.Message = "Inverters are in Island Mode. Waiting for them to connect to AcIn.";
s.StateMachine.Message = "ESS";
s.DcDc.Enable();
s.AcDc.Enable();
s.AcDc.EnableIslandMode();
s.Relays.DisconnectIslandBusFromGrid();
s.AcDc.EnableGridTieMode();
s.EnableDcLinkGridTie();
s.Relays.ConnectIslandBusToGrid();
return true;
// => 21
}
private static Boolean State18(StatusRecord s)
{
// return new
// (
// " Didn't succeed to go to Island mode and K1AcInIsConnectedToGrid close\n Turning off power stage of inverter\n Moving to Grid Tie",
// AcPowerStageEnable: false,
// DcPowerStageEnable: false,
// GridType.GridTied400V50Hz,
// HighActivePinState.Open
// );
s.DcDc.Disable();
s.AcDc.Disable();
s.AcDc.EnableIslandMode();
s.Relays.DisconnectIslandBusFromGrid();
return true;
}
private static Boolean State21(StatusRecord s)
{
s.StateMachine.Message = "Island Mode";
s.DcDc.Enable();
s.AcDc.Enable();
s.AcDc.EnableIslandMode();
s.Relays.DisconnectIslandBusFromGrid();
return false;
// => 22
}
private static Boolean State22(StatusRecord s)
{
s.StateMachine.Message = "Grid became available (K1). Turning off inverters.";
s.StateMachine.Message = "K1 opened, switching inverters off";
s.DcDc.Disable();
s.AcDc.Disable();
s.AcDc.EnableGridTieMode();
s.Relays.ConnectIslandBusToGrid();
return true;
// => 6
}
private static Boolean State6(StatusRecord s)
{
s.StateMachine.Message = "Inverters are off, opening K2";
s.DcDc.Disable();
s.AcDc.Disable();
s.AcDc.EnableGridTieMode();
s.Relays.DisconnectIslandBusFromGrid();
return true;
// => 4
}
private static Boolean State4(StatusRecord s)
{
s.StateMachine.Message = "K2 is open, waiting K3 to open";
s.DcDc.Disable();
s.AcDc.Disable();
s.AcDc.EnableGridTieMode();
s.Relays.DisconnectIslandBusFromGrid();
return true;
// => 0
}
private static Boolean State8(StatusRecord s)
{
s.StateMachine.Message = "Ac/Dc are off and in Island Mode.";
s.DcDc.Enable();
s.AcDc.Enable();
s.AcDc.EnableIslandMode();
s.Relays.DisconnectIslandBusFromGrid();
return true;
// => 24
}
private static Boolean State28(StatusRecord s)
{
s.StateMachine.Message = "Island Mode";
s.DcDc.Enable();
s.AcDc.Enable();
s.AcDc.EnableIslandMode();
s.EnableDcLinkIslandMode();
s.Relays.DisconnectIslandBusFromGrid();
return false;
// => 29
}
private static Boolean State29(StatusRecord s)
{
s.StateMachine.Message = "K1 closed, Switching off Inverters and moving to grid tie";
s.DcDc.Disable();
s.AcDc.Disable();
@ -305,12 +265,22 @@ public static class Controller
return false;
// => 6
// => 13
}
private static Boolean State24(StatusRecord s)
{
s.StateMachine.Message = "Inverter are on waiting for k3 to close";
s.DcDc.Enable();
s.AcDc.Enable();
s.AcDc.EnableIslandMode();
s.Relays.DisconnectIslandBusFromGrid();
return false;
// => 28
}
private static Boolean State101(StatusRecord s)
{
@ -324,24 +294,22 @@ public static class Controller
return s.EnableSafeDefaults();
}
private static Boolean State103(StatusRecord s)
{
s.StateMachine.Message = "Panic: ACDCs have unequal grid types";
s.StateMachine.Message = "Panic: ACDCs have unequal grid types or PowerStage";
return s.EnableSafeDefaults();
}
private static Boolean State104(StatusRecord s)
{
s.StateMachine.Message = "Panic: DCDCs not available";
return s.EnableSafeDefaults();
}
// private static Boolean State104(StatusRecord s)
// {
// s.StateMachine.Message = "Panic: DCDCs not available";
// return s.EnableSafeDefaults();
// }
private static Boolean UnknownState(StatusRecord s)
{
// "Unknown System State"
return s.EnableSafeDefaults();
}
@ -352,6 +320,20 @@ public static class Controller
return acDcs.Devices.All(d => !d.Control.PowerStageEnable);
}
private static Boolean AllEnabled(this AcDcDevicesRecord acDcs)
{
return acDcs.Devices.All(d => d.Control.PowerStageEnable);
}
private static Boolean AllTheSame(this AcDcDevicesRecord acDcs)
{
return acDcs.Devices
.Select(d => d.Control.PowerStageEnable)
.Distinct()
.Count() == 1;
}
private static Boolean AllGridTied(this AcDcDevicesRecord acDcs)
{
return acDcs.Devices.All(d => d.Status.ActiveGridType is GridTied380V60Hz)
@ -381,9 +363,12 @@ public static class Controller
private static void Disable(this DcDcDevicesRecord dcDc)
{
dcDc.Devices
.Select(d => d.Control)
.ForAll(c => c.PowerStageEnable = false);
// For Test purpose, The transition from island mode to grid tier and vis versa , may not need to disable Dc/Dc.
// This will keep the Dc link powered.
// dcDc.Devices
// .Select(d => d.Control)
// .ForAll(c => c.PowerStageEnable = false);
}
private static void Enable(this AcDcDevicesRecord acDc)
@ -393,7 +378,6 @@ public static class Controller
.ForAll(c => c.PowerStageEnable = true);
}
private static void Enable(this DcDcDevicesRecord dcDc)
{
dcDc.Devices
@ -402,7 +386,6 @@ public static class Controller
}
private static void EnableGridTieMode(this AcDcDevicesRecord acDc)
{
acDc.Devices
@ -418,6 +401,33 @@ public static class Controller
.ForAll(c => c.Ac.GridType = Island400V50Hz); // TODO: config grid type
}
private static void EnableDcLinkIslandMode(this StatusRecord s)
{
// Dc Link Windows on AcDc +- 60
s.Config.ReferenceDcLinkVoltageFromAcDc = 750;
s.Config.MinDcLinkVoltageFromAcDc = 690;
s.Config.MaxDcLinkVoltageFromAcDc = 810;
// Dc Link Windows on DcDc +-55
s.Config.ReferenceDcLinkVoltageFromDc = 750;
s.Config.UpperDcLinkVoltageFromDc = 55;
s.Config.LowerDcLinkVoltageFromDc = 55;
}
private static void EnableDcLinkGridTie(this StatusRecord s)
{
// Dc Link Windows on DcDc +-30
s.Config.ReferenceDcLinkVoltageFromAcDc = 750;
s.Config.MinDcLinkVoltageFromAcDc = 720;
s.Config.MaxDcLinkVoltageFromAcDc = 780;
// Dc Link Windows on DcDc +-20
s.Config.ReferenceDcLinkVoltageFromDc = 750;
s.Config.UpperDcLinkVoltageFromDc = 20;
s.Config.LowerDcLinkVoltageFromDc = 20;
}
private static void DisconnectIslandBusFromGrid(this RelaysRecord? relays)
{
if (relays is not null)
@ -433,15 +443,16 @@ public static class Controller
private static Boolean EnableSafeDefaults(this StatusRecord s)
{
s.DcDc.Disable();
s.AcDc.Disable();
s.AcDc.EnableGridTieMode();
s.Relays.DisconnectIslandBusFromGrid();
// After some tests, the safe state is switch off inverter and keep the last state of K2 , Dc/Dc and Grid type to avoid conflict.
// s.DcDc.Disable();
s.AcDc.Disable();
// s.AcDc.EnableGridTieMode();
// s.Relays.DisconnectIslandBusFromGrid();
return false;
}
private static DcDcDevicesRecord ResetAlarms(this DcDcDevicesRecord dcDcStatus)
public static DcDcDevicesRecord ResetAlarms(this DcDcDevicesRecord dcDcStatus)
{
var sc = dcDcStatus.SystemControl;
@ -454,7 +465,7 @@ public static class Controller
return dcDcStatus;
}
private static AcDcDevicesRecord ResetAlarms(this AcDcDevicesRecord acDcStatus)
public static AcDcDevicesRecord ResetAlarms(this AcDcDevicesRecord acDcStatus)
{
var sc = acDcStatus.SystemControl;

View File

@ -14,48 +14,52 @@ public class Config //TODO: let IE choose from config files (Json) and connect t
private static readonly JsonSerializerOptions JsonOptions = new() { WriteIndented = true };
public required Double MinSoc { get; set; }
public required UnixTime LastEoc { get; set; }
public required Double PConstant { get; set; }
public required Double GridSetPoint { get; set; }
public required Double BatterySelfDischargePower { get; set; }
public required Double HoldSocZone { get; set; }
public required Double MinSoc { get; set; }
public required UnixTime LastEoc { get; set; }
public required Double PConstant { get; set; }
public required Double GridSetPoint { get; set; }
public required Double BatterySelfDischargePower { get; set; }
public required Double HoldSocZone { get; set; }
public required Double MaxDcLinkVoltageFromAcDc { get; set; }
public required Double MinDcLinkVoltageFromAcDc { get; set; }
public required Double ReferenceDcLinkVoltageFromAcDc { get; set; }
public required Double MaxDcLinkVoltageFromAcDc { get; set; }
public required Double MinDcLinkVoltageFromAcDc { get; set; }
public required Double ReferenceDcLinkVoltageFromAcDc { get; set; }
public required Double LowerDcLinkVoltageFromDc { get; set; }
public required Double ReferenceDcLinkVoltageFromDc { get; set; }
public required Double UpperDcLinkVoltageFromDc { get; set; }
public required Double LowerDcLinkVoltageFromDc { get; set; }
public required Double ReferenceDcLinkVoltageFromDc { get; set; }
public required Double UpperDcLinkVoltageFromDc { get; set; }
public required Double MaxBatteryChargingCurrent { get; set; }
public required Double MaxBatteryDischargingCurrent { get; set; }
public required Double MaxBatteryChargingCurrent { get; set; }
public required Double MaxBatteryDischargingCurrent { get; set; }
public required Double MaxChargeBatteryVoltage { get; set; }
public required Double MinDischargeBatteryVoltage { get; set; }
public required Double MaxChargeBatteryVoltage { get; set; }
public required Double MinDischargeBatteryVoltage { get; set; }
public required DeviceConfig Devices { get; set; }
public required S3Config? S3 { get; set; }
public required DeviceConfig Devices { get; set; }
public required S3Config? S3 { get; set; }
#if DEBUG
public static Config Default => new()
{
MinSoc = 20,
LastEoc = UnixTime.Epoch, // TODO: remove, use new LastEoc feature from BMS
PConstant = .5,
GridSetPoint = 0,
BatterySelfDischargePower = 200,
HoldSocZone = 1, // TODO: find better name,
MinDcBusVoltage = 690,
ReferenceDcBusVoltage = 750,
MaxDcBusVoltage = 810,
LowerDcBusVoltageWindow = 50,
ReferenceDcBusVoltageWindow = 750,
UpperDcBusVoltageWindow = 50,
Devices = new ()
MinSoc = 20,
LastEoc = UnixTime.Epoch, // TODO: remove, use new LastEoc feature from BMS
PConstant = .5,
GridSetPoint = 0,
BatterySelfDischargePower = 200,
HoldSocZone = 1, // TODO: find better name,
MinDcLinkVoltageFromAcDc = 690,
ReferenceDcLinkVoltageFromAcDc = 750,
MaxDcLinkVoltageFromAcDc = 810,
LowerDcLinkVoltageFromDc = 50,
ReferenceDcLinkVoltageFromDc = 750,
UpperDcLinkVoltageFromDc = 50,
MaxBatteryChargingCurrent = 210,
MaxBatteryDischargingCurrent = 210,
MaxChargeBatteryVoltage = 57,
MinDischargeBatteryVoltage = 0,
Devices = new ()
{
TruConvertAcIp = new() { Host = "localhost", Port = 5001},
TruConvertDcIp = new() { Host = "localhost", Port = 5002},

View File

@ -0,0 +1,254 @@
using System.Diagnostics.CodeAnalysis;
using InnovEnergy.App.SaliMax.Ess;
using InnovEnergy.Lib.Devices.Battery48TL;
using InnovEnergy.Lib.Units;
using InnovEnergy.Lib.Units.Power;
using InnovEnergy.Lib.Utils;
using Ac3Bus = InnovEnergy.Lib.Units.Composite.Ac3Bus;
namespace InnovEnergy.App.SaliMax;
public static class Topology
{
public static TextBlock From(this StatusRecord s)
{
// Topology is built up from right to left
// Power Measurement Values
var inverterPower = s.AcDc.Ac.Power.Active;
var islandLoadPower = s.LoadOnAcIsland is not null
? s.LoadOnAcIsland.Ac.Power.Active
: 0; // TODO
var islandTopology = CreateIslandTopology(s, islandLoadPower, inverterPower);
if (s.GridMeter is null) // no grid meter?
return islandTopology; // we're done
var gridBusColumn = GridBusColumn(s);
var gridBox = CreateGridBox(s);
ActivePower islandToGridBusPower = inverterPower + islandLoadPower;
// return TextBlock
// .AlignCenterVertical
// (
// gridBox , Flow.Horizontal(s.GridMeter.Ac.Power.Active),
// gridBusColumn, Flow.Horizontal(islandToGridBusPower),
// islandTopology
// );
var gridTopology = TextBlock
.AlignCenterVertical
(
gridBox,
Flow.Horizontal(s.GridMeter.Ac.Power.Active),
gridBusColumn
);
return TextBlock.AlignCenterVertical
(
gridTopology,
Flow.Horizontal(islandToGridBusPower),
islandTopology
);
}
private static TextBlock CreateIslandTopology(StatusRecord s, ActivePower islandLoadPower, ActivePower inverterPower)
{
var dcBatteryPower = s.DcDc.Dc.Battery.Power;
var dcdcPower = s.DcDc.Dc.Link.Power;
var batteries = CreateBatteryColumn(s);
var dcBusColumn = CreateDcBusColumn(s);
var islandBusColumn = CreateIslandBusColumn(s, islandLoadPower);
var inverterBox = CreateInverterBox(s);
var dcDcBox = CreateDcDcBox(s);
return TextBlock
.AlignCenterVertical
(
islandBusColumn, Flow.Horizontal(inverterPower),
inverterBox , Flow.Horizontal(inverterPower),
dcBusColumn , Flow.Horizontal(dcdcPower),
dcDcBox , Flow.Horizontal(dcBatteryPower),
batteries
);
}
private static TextBlock CreateGridBox(StatusRecord statusRecord)
{
return statusRecord
.GridMeter!
.Ac
.PhasePowersActive()
.TitleBox("Grid");
}
private static TextBlock CreateDcDcBox(StatusRecord s)
{
var dc48Voltage = s.DcDc.Dc.Battery.Voltage.ToDisplayString();
return TextBlock
.AlignLeft(dc48Voltage)
.TitleBox("DC/DC");
}
private static TextBlock CreateInverterBox(StatusRecord s)
{
var inverterAcPhases = s
.AcDc
.Devices
.Select(d => d.Status.Ac.Power)
.ToReadOnlyList();
return TextBlock
.AlignLeft(inverterAcPhases)
.TitleBox("AC/DC");
}
private static TextBlock CreateIslandBusColumn(StatusRecord s, ActivePower islandLoadPower)
{
var islandBusPv = 0.W(); // TODO
return ColumnBox
(
"Pv", islandBusPv,
"Island Bus", s.AcDc.Ac.PhasePowersActive(),
"Load", islandLoadPower
);
}
private static TextBlock CreateDcBusColumn(StatusRecord s)
{
var dcBusLoad = 0.W(); // TODO
var pvOnDcPower = s.PvOnDc.Dc!.Power; // TODO !
var dcLinkVoltage = s.DcDc.Dc.Link.Voltage.ToDisplayString();
return ColumnBox
(
"Pv" , pvOnDcPower,
"Dc Bus", dcLinkVoltage,
"Load" , dcBusLoad
);
}
private static TextBlock CreateBatteryColumn(StatusRecord s)
{
var bat = s.Battery;
var batteryAvgBox = CreateAveragedBatteryBox(bat);
var batteryBoxes = bat
.Devices
.Select(CreateIndividualBattery)
.ToReadOnlyList();
var individualBatteries = batteryBoxes.Any()
? TextBlock.AlignLeft(batteryBoxes)
: TextBlock.Empty;
return TextBlock
.AlignCenterVertical
(
batteryAvgBox //,
//individualBatteries // TODO
);
}
private static TextBlock CreateAveragedBatteryBox(Battery48TlRecords bat)
{
var batteryVoltage = bat.Dc.Voltage.ToDisplayString();
var batterySoc = bat.Devices.Any() ? bat.Devices.Average(b => b.Soc).Percent().ToDisplayString() : "0";
var batteryCurrent = bat.Dc.Current.ToDisplayString();
var batteryTemp = bat.Temperature.ToDisplayString();
var batteryHeatingCurrent = bat.HeatingCurrent.ToDisplayString();
var alarms = bat.Alarms.Count + " Alarms";
var warnings = bat.Warnings.Count + " Warnings";
return TextBlock
.AlignLeft
(
batteryVoltage,
batterySoc,
batteryCurrent,
batteryTemp,
batteryHeatingCurrent,
warnings,
alarms
)
.TitleBox("Battery");
}
private static TextBlock GridBusColumn(StatusRecord s)
{
var gridLoadPower = s.LoadOnAcGrid is not null
? s.LoadOnAcGrid.Power.Active
: 0; // TODO: show that LoadOnAcGrid is actually not available and not 0
return ColumnBox
(
"Pv", 0,
"Grid Bus", s.GridMeter!.Ac.PhaseVoltages(),
"Load", gridLoadPower
);
}
private static TextBlock PhaseVoltages(this Ac3Bus ac)
{
return TextBlock.AlignLeft
(
ac.L1.Voltage.ToDisplayString(),
ac.L2.Voltage.ToDisplayString(),
ac.L3.Voltage.ToDisplayString()
);
}
private static TextBlock PhasePowersActive(this Ac3Bus ac)
{
return TextBlock.AlignLeft
(
ac.L1.Power.Active.ToDisplayString(),
ac.L2.Power.Active.ToDisplayString(),
ac.L3.Power.Active.ToDisplayString()
);
}
private static TextBlock CreateIndividualBattery(Battery48TlRecord battery, Int32 i)
{
var batteryWarnings = battery.Warnings.Any();
var batteryAlarms = battery.Alarms.Any();
var content = TextBlock.AlignLeft
(
battery.Dc.Voltage.ToDisplayString(),
battery.Soc.ToDisplayString(),
battery.Dc.Current.ToDisplayString() + " C/D",
battery.Temperatures.Cells.Average.ToDisplayString(),
battery.BusCurrent.ToDisplayString() + " T",
batteryWarnings,
batteryAlarms,
battery.HeatingCurrent.ToDisplayString() + " H"
);
var box = content.TitleBox($"Battery {i + 1}");
var flow = Flow.Horizontal(battery.Dc.Power);
return TextBlock.AlignCenterVertical(flow, box);
}
[SuppressMessage("ReSharper", "SuggestBaseTypeForParameter")]
private static TextBlock ColumnBox(String pvTitle , ActivePower pvPower,
String busTitle , Object busData,
String loadTitle, ActivePower loadPower)
{
var pvBox = TextBlock.FromString(pvTitle).Box();
var pvToBus = Flow.Vertical(pvPower);
var busBox = TextBlock.AlignLeft(busData).TitleBox(busTitle);
var busToLoad = Flow.Vertical(loadPower);
var loadBox = TextBlock.FromString(loadTitle).Box();
return TextBlock.AlignCenterHorizontal(pvBox, pvToBus, busBox, busToLoad, loadBox);
}
}

View File

@ -0,0 +1,15 @@
using System.Runtime.InteropServices;
namespace InnovEnergy.App.SaliMax;
// https://www.freedesktop.org/software/systemd/man/sd_notify.html
public static class Watchdog
{
// "it is generally recommended to ignore the return value of this call. "
[DllImport("libsystemd.so.0")]
private static extern Int32 sd_notify(Int32 unsetEnvironment, String state);
public static void Ready() => _ = sd_notify(0, "READY=1");
public static void Alive() => _ = sd_notify(0, "WATCHDOG=1");
}

View File

@ -8,7 +8,7 @@ tunnel() {
rPort=$3
lPort=$4
echo -n "localhost:$lPort $name "
echo -n "$name @ $ip mapped to localhost:$lPort "
ssh -nNTL "$lPort:$ip:$rPort" "$host" 2> /dev/null &
until nc -vz 127.0.0.1 $lPort 2> /dev/null
@ -22,23 +22,23 @@ tunnel() {
echo ""
tunnel "Trumpf Inverter (http) " 10.0.2.1 80 7001
tunnel "Trumpf DCDC (http) " 10.0.3.1 80 7002
tunnel "Ext Emu Meter (http) " 10.0.4.1 80 7003
tunnel "Int Emu Meter (http) " 10.0.4.2 80 7004
tunnel "AMPT (http) " 10.0.5.1 8080 7005
tunnel "Doepke (http) " 10.0.6.1 80 7006
tunnel "Trumpf Inverter (http) " 10.0.2.1 80 8001
tunnel "Trumpf DCDC (http) " 10.0.3.1 80 8002
tunnel "Ext Emu Meter (http) " 10.0.4.1 80 8003
tunnel "Int Emu Meter (http) " 10.0.4.2 80 8004
tunnel "AMPT (http) " 10.0.5.1 8080 8005
tunnel "Trumpf Inverter (modbus)" 10.0.2.1 502 5001
tunnel "Trumpf DCDC (modbus) " 10.0.3.1 502 5002
tunnel "Ext Emu Meter (modbus) " 10.0.4.1 502 5003
tunnel "Int Emu Meter " 10.0.4.2 502 5004
tunnel "AMPT (modbus) " 10.0.5.1 502 5005
tunnel "Batteries " 127.0.0.1 6855 5007
tunnel "Adam " 10.0.1.1 502 5006
tunnel "Batteries " 127.0.0.1 6855 5007
echo
echo "press any key to close the tunnels ..."
read -r -n 1 -s
kill $(jobs -p)
echo "done"

View File

@ -24,7 +24,7 @@ public static class Program
app.MapControllers();
// app.MapGet("/", () => Controller.Index());
var webTask = app.RunAsync();
await Task.WhenAll(webTask);
await Task.WhenAll(webTask, updateTask);
}
private static OpenApiInfo OpenApiInfo { get; } = new OpenApiInfo

View File

@ -3,7 +3,7 @@ using InnovEnergy.Lib.Utils;
namespace InnovEnergy.Lib.Channels.Framed;
public class Channel<Tx, Rx> : Connection
public class Channel<Tx, Rx> : Connection
{
private readonly AsyncAction<Tx> _Transmit;
private readonly Async<Rx> _Receive;

View File

@ -19,11 +19,10 @@ public class AmptDevices
{
}
public AmptDevices(ModbusClient modbusClient)
{
_CommunicationUnit = new ModbusDevice<CommunicationUnitRegisters>(modbusClient);
_StringOptimizers = StringOptimizers(modbusClient);
_StringOptimizers = StringOptimizers(modbusClient);
}
public AmptStatus Read()
@ -36,7 +35,7 @@ public class AmptDevices
}
catch (Exception e)
{
Console.WriteLine( "Failed to read Ampt data \n"+ e.Message );
Console.WriteLine("Failed to read Ampt data \n" + e.Message);
// TODO: log
}

View File

@ -7,7 +7,7 @@ using static System.IO.Ports.Parity;
namespace InnovEnergy.Lib.Devices.Battery48TL;
public class Battery48TlDevice: ModbusDevice<Battery48TlRecord>
public class Battery48TlDevice : ModbusDevice<Battery48TlRecord>
{
public const Parity Parity = Odd;
public const Int32 StopBits = 1;

View File

@ -20,7 +20,7 @@ public class Battery48TlDevices
}
catch (Exception e)
{
Console.WriteLine( "Failed to read Battery data \n"+ e.Message );
Console.WriteLine("Failed to read Battery data \n" + e.Message);
// TODO: log
return Battery48TlRecords.Null;

View File

@ -94,24 +94,23 @@ public partial class Battery48TlRecord
if (HasBit(14)) yield return "FUSE : Main fuse blown";
if (HasBit(15)) yield return "HTRE : Battery failed to warm up";
if (HasBit(16)) yield return "TCPE : Temperature sensor failure";
if (HasBit(17)) yield return "STRE :";
if (HasBit(17)) yield return "STRE : Voltage measurement circuit fails";
if (HasBit(18)) yield return "CME : Current sensor failure";
if (HasBit(19)) yield return "HWFL : BMS hardware failure";
if (HasBit(20)) yield return "HWEM : Hardware protection tripped";
if (HasBit(21)) yield return "ThM : Heatsink temperature too high";
if (HasBit(22)) yield return "vsm1 : String voltage too low";
if (HasBit(23)) yield return "vsm2 : Low string voltage failure";
if (HasBit(25)) yield return "vsM2 : String voltage too high";
if (HasBit(27)) yield return "iCM2 : Charge current too high";
if (HasBit(29)) yield return "iDM2 : Discharge current too high";
if (HasBit(31)) yield return "MID2 : String voltage unbalance too high";
if (HasBit(33)) yield return "CCBF : Internal charger hardware failure";
if (HasBit(34)) yield return "AhFL :";
if (HasBit(36)) yield return "TbCM :";
if (HasBit(37)) yield return "BRNF :";
// if (HasBit(34)) yield return "AhFL :"; // This is doesn't exist in the manual
// if (HasBit(36)) yield return "TbCM :"; // This is doesn't exist in the manual
// if (HasBit(37)) yield return "BRNF :"; // This is doesn't exist in the manual
if (HasBit(42)) yield return "HTFS : Heater Fuse Blown";
if (HasBit(43)) yield return "DATA : Parameters out of range";
if (HasBit(45)) yield return "CELL2:";
if (HasBit(45)) yield return "CELL2: Unbalance string voltages";
if (HasBit(46)) yield return "HEBT : Loss of heartbeat";
}
[SuppressMessage("ReSharper", "StringLiteralTypo")]
@ -124,18 +123,20 @@ public partial class Battery48TlRecord
if (HasBit(6) ) yield return "VBm1: Bus voltage low";
if (HasBit(8) ) yield return "VBM1: Bus voltage high";
if (HasBit(10)) yield return "IDM1: Discharge current high";
if (HasBit(22)) yield return "vsm1: String voltage too low";
if (HasBit(24)) yield return "vsM1: String voltage high";
if (HasBit(26)) yield return "iCM1: Charge current high";
if (HasBit(28)) yield return "iDM1: Discharge current high";
if (HasBit(30)) yield return "MID1: String voltages unbalanced";
if (HasBit(32)) yield return "BLPW: Not enough charging power on bus";
if (HasBit(33)) yield return "CCBF : Internal charger hardware failure";
if (HasBit(35)) yield return "Ah_W: String SOC low";
if (HasBit(38)) yield return "MPMM: Midpoint wiring problem";
if (HasBit(39)) yield return "TCMM:";
// if (HasBit(39)) yield return "TCMM:"; // This is doesn't exist in the manual
if (HasBit(40)) yield return "TCdi: Temperature difference between strings high";
if (HasBit(41)) yield return "WMTO:";
if (HasBit(44)) yield return "bit44:";
if (HasBit(46)) yield return "CELL1:";
// if (HasBit(41)) yield return "WMTO:"; // This is doesn't exist in the manual
if (HasBit(44)) yield return "LMPW : String voltages unbalance warning";
if (HasBit(47)) yield return "TOCW : ";
}

View File

@ -30,6 +30,16 @@ public partial class Battery48TlRecord
[InputRegister(1017, Scale = 0.1, Offset = -400)] private Double _TemperaturesCellsRight;
[InputRegister(1003, Scale = 0.1, Offset = -400)] private Double _TemperaturesCellsAverage;
[InputRegister(1054)] private Double _FwVersion;
[InputRegister(1055)] private Double _SerialNum1;
[InputRegister(1056)] private Double _SerialNum2;
[InputRegister(1057)] private Double _SerialNum3;
[InputRegister(1058)] private Double _SerialNum4;
[InputRegister(1059)] private Double _LimpBitMap;
[InputRegister(1060)] private Double _BatteryState1;
[InputRegister(1061)] private Double _BatteryState2;
private LedState ParseLed(LedColor led) => (LedState)((_LedStates >> (Int32)led) & 3);
// public Decimal CellsVoltage { get; init; }

View File

@ -19,7 +19,7 @@ public class Battery48TlRecords
HeatingCurrent = records.Any() ? records.Sum(b => b.HeatingCurrent) : 0;
Dc = empty
? DcBus.FromVoltageCurrent(0, 0)
? DcBus.Null
: DcBus.FromVoltageCurrent
(
records.Average(r => r.Dc.Voltage),

View File

@ -36,7 +36,11 @@ public class DcControl
}
}
public DcPrechargeConfig PrechargeConfig => _Self.DcPrechargeConfig;
public DcPrechargeConfig PrechargeConfig
{
get { return _Self.DcPrechargeConfig; }
set { _Self.DcPrechargeConfig = value; }
}
private Boolean Is480V => _Self.GridType is GridType.GridTied480V60Hz or GridType.Island480V60Hz;

View File

@ -3,7 +3,7 @@ namespace InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.DataTypes;
public enum DcPrechargeConfig : UInt16
{
WaitForExternalPrecharge = 0,
PrechargeDcWithSystemCtl = 1,
PrechargeDcWithInternal = 1,
PrechargeDcWithDcDcs = 2,
PrechargeDcWithSystemCtlAndWait = 3,
}

View File

@ -16,6 +16,19 @@ public class CurrentControl
set => _Self.MaxCurrentChangePerMs = value.Value;
}
public Current MaxBatteryChargingCurrent
{
get => _Self.MaxBatteryChargingCurrent;
set => _Self.MaxBatteryChargingCurrent = value.Value;
}
public Current MaxBatteryDischargingCurrent
{
get => _Self.MaxBatteryDischargingCurrent;
set => _Self.MaxBatteryDischargingCurrent = value.Value;
}
private readonly DcDcRecord _Self;
internal CurrentControl(DcDcRecord self) => _Self = self;
}

View File

@ -20,11 +20,14 @@ public partial class DcDcRecord
[HoldingRegister(4100, Scale = .01)] internal Double MaxBatteryVoltage ;
[HoldingRegister(4101, Scale = .01)] internal Double MinBatteryVoltage ;
[HoldingRegister(4106, Scale = .1)] internal Double MaxBatteryChargingCurrent ;
[HoldingRegister(4109, Scale = .1)] internal Double MaxBatteryDischargingCurrent ;
[HoldingRegister(4112, Scale = .1)] internal Double VccEndPointVoltage ;
[HoldingRegister(4115)] internal Double VccEndPointCurrent ;
[HoldingRegister(4118)] internal Double VccStartPointCurrent ;
[HoldingRegister(4118)] internal Double MaxDcPower ;
[HoldingRegister(4121)] internal Double MaxDcPower ;
[HoldingRegister(4124, Scale = .1)] internal Double MaxVoltageAlarm ;
[HoldingRegister(4127, Scale = .1)] internal Double MinVoltageAlarm ;

View File

@ -9,8 +9,8 @@ public static class UnixTimeSpanExtensions
public static UnixTimeSpan Weeks (this Int32 w) => UnixTimeSpan.FromWeeks (w);
public static UnixTimeSpan Seconds(this UInt32 s) => UnixTimeSpan.FromSeconds(s);
public static UnixTimeSpan Minutes(this UInt32 m) => UnixTimeSpan.FromSeconds(m);
public static UnixTimeSpan Hours (this UInt32 h) => UnixTimeSpan.FromMinutes(h);
public static UnixTimeSpan Days (this UInt32 d) => UnixTimeSpan.FromHours (d);
public static UnixTimeSpan Weeks (this UInt32 w) => UnixTimeSpan.FromDays (w);
public static UnixTimeSpan Minutes(this UInt32 m) => UnixTimeSpan.FromMinutes(m);
public static UnixTimeSpan Hours (this UInt32 h) => UnixTimeSpan.FromHours (h);
public static UnixTimeSpan Days (this UInt32 d) => UnixTimeSpan.FromDays (d);
public static UnixTimeSpan Weeks (this UInt32 w) => UnixTimeSpan.FromWeeks (w);
}

View File

@ -9,6 +9,11 @@ public abstract class Unit
public override String ToString() => $"{Value} {Symbol}";
private static readonly IReadOnlyList<String> Prefix = new[] { "y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Y" };
private static Int32 MaxPrefix { get; } = Prefix.Count - 1;
private static Int32 DefaultIndex { get; } = Prefix.TakeWhile(e => e != "").Count();
public String ToDisplayString()
{
if (Value == 0)
@ -17,14 +22,14 @@ public abstract class Unit
var a = Math.Abs(Value);
var s = Math.Sign(Value);
var i = 8;
var i = DefaultIndex;
while (a >= 10000)
while (a >= 10000 && i < MaxPrefix)
{
a /= 1000;
i++;
}
while (a < 10)
while (a < 10 && i > 0)
{
a *= 1000;
i--;
@ -37,6 +42,6 @@ public abstract class Unit
return $"{r * s} {Prefix[i]}{Symbol}";
}
private static readonly IReadOnlyList<String> Prefix = new[] { "y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Y" };
}

View File

@ -23,6 +23,19 @@ public static class Units
public static Energy Wh (this Double value) => value;
public static Percent Percent(this Double value) => value;
public static Current A (this Int32 value) => value;
public static Voltage V (this Int32 value) => value;
public static ActivePower W (this Int32 value) => value;
public static ReactivePower Var (this Int32 value) => value;
public static ApparentPower Va (this Int32 value) => value;
public static Resistance Ohm (this Int32 value) => value;
public static Frequency Hz (this Int32 value) => value;
public static Angle Rad (this Int32 value) => value;
public static Temperature Celsius(this Int32 value) => value;
public static Energy KWh (this Int32 value) => value * 1000;
public static Energy Wh (this Int32 value) => value;
public static Percent Percent(this Int32 value) => value;
public static String ToCsv(this Object thing)
{
var csvLines = new List<String>();

View File

@ -13,4 +13,6 @@ public static class ArrayExtensions
Array.Fill(ts, element);
return ts;
}
public static IReadOnlyList<T> AsReadOnlyList<T>(this T[] ts) => ts;
}

View File

@ -38,7 +38,6 @@ public static class ConsoleUtils
return t;
}
public static T Write<T>(this T t, ConsoleColor color)
{
var c = Console.ForegroundColor;

View File

@ -277,6 +277,9 @@ public static class EnumerableUtils
public static T[] ToArray<T>(this IEnumerable<T> ts, Int32 n)
{
if (ts is T[] ta)
return ta;
var array = new T[n];
var i = 0;
@ -296,6 +299,8 @@ public static class EnumerableUtils
return ts as T[] ?? ts.ToArray(ts.Count);
}
public static IEnumerable<T> Concat<T>(this IEnumerable<T> ts, T last)
{
foreach (var t in ts)

View File

@ -132,9 +132,11 @@ public static class StringUtils
.SideBySideWith(text, "");
}
public static IReadOnlyList<String> SplitLines(this String s)
public static IReadOnlyList<String> SplitLines(this String? s)
{
return s.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
return s is null
? Array.Empty<String>()
: s.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
}

View File

@ -8,12 +8,18 @@ public class TextBlock
public override String ToString() => _Lines.JoinLines();
public static TextBlock AlignLeft(IReadOnlyList<Object> things)
public static TextBlock Empty { get; } = new TextBlock();
public static TextBlock AlignLeft(IEnumerable<Object> things)
{
var lines = things
.SelectMany(GetLines)
.Unless(String.IsNullOrEmpty)
.ToList();
if (!lines.Any())
return Empty;
var width = lines.Max(l => l.Length);
var alignedLines = lines
@ -27,8 +33,12 @@ public class TextBlock
{
var lines = things
.SelectMany(GetLines)
.Unless(String.IsNullOrEmpty)
.ToList();
if (!lines.Any())
return Empty;
var width = lines.Max(l => l.Length);
var alignedLines = lines
@ -42,8 +52,12 @@ public class TextBlock
{
var lines = things
.SelectMany(GetLines)
.Where(l => !String.IsNullOrEmpty(l))
.ToList();
if (!lines.Any())
return Empty;
var width = lines.Max(l => l.Length);
var alignedLines = lines
@ -53,14 +67,16 @@ public class TextBlock
return new TextBlock(alignedLines);
}
public static TextBlock AlignTop(IReadOnlyList<Object> things)
{
var columns = things
.Select(GetLines)
.Where(c => c.Count > 0)
.ToList();
if (!columns.Any())
return Empty;
var height = columns.Max(l => l.Count);
var alignedLines = Enumerable
@ -76,8 +92,12 @@ public class TextBlock
{
var columns = things
.Select(GetLines)
.Where(c => c.Count > 0)
.ToList();
if (!columns.Any())
return Empty;
var height = columns.Max(l => l.Count);
var alignedLines = Enumerable
@ -93,8 +113,12 @@ public class TextBlock
{
var columns = things
.Select(GetLines)
.Where(c => c.Count > 0)
.ToList();
if (!columns.Any())
return Empty;
var height = columns.Max(l => l.Count);
var alignedLines = Enumerable
@ -105,18 +129,18 @@ public class TextBlock
return new TextBlock(alignedLines);
}
public static TextBlock AlignLeft (params Object[] things) => AlignLeft ((IReadOnlyList<Object>) things);
public static TextBlock AlignRight (params Object[] things) => AlignRight ((IReadOnlyList<Object>) things);
public static TextBlock AlignTop (params Object[] things) => AlignTop ((IReadOnlyList<Object>) things);
public static TextBlock AlignBottom (params Object[] things) => AlignBottom ((IReadOnlyList<Object>) things);
public static TextBlock AlignCenterVertical (params Object[] things) => AlignCenterVertical ((IReadOnlyList<Object>) things);
public static TextBlock AlignCenterHorizontal(params Object[] things) => AlignCenterHorizontal((IReadOnlyList<Object>) things);
public static TextBlock AlignLeft (params Object?[] things) => AlignLeft ((IReadOnlyList<Object>) things);
public static TextBlock AlignRight (params Object?[] things) => AlignRight ((IReadOnlyList<Object>) things);
public static TextBlock AlignTop (params Object?[] things) => AlignTop ((IReadOnlyList<Object>) things);
public static TextBlock AlignBottom (params Object?[] things) => AlignBottom ((IReadOnlyList<Object>) things);
public static TextBlock AlignCenterVertical (params Object?[] things) => AlignCenterVertical ((IReadOnlyList<Object>) things);
public static TextBlock AlignCenterHorizontal(params Object?[] things) => AlignCenterHorizontal((IReadOnlyList<Object>) things);
public static TextBlock FromString(String thing) => AlignLeft(thing);
public TextBlock Box()
{
var width = _Lines.Max(l => l.Length);
var width = _Lines.Any() ? _Lines.Max(l => l.Length) : 0;
var hLine = "".PadRight(width + 2, '─');
var top = "┌" + hLine + "┐";
@ -134,7 +158,7 @@ public class TextBlock
public TextBlock TitleBox(String title)
{
var linesWidth = _Lines.Max(l => l.Length);
var linesWidth = _Lines.Any() ? _Lines.Max(l => l.Length) : 0;
var titleWidth = title.Length;
var width = Math.Max(linesWidth, titleWidth);
@ -175,11 +199,14 @@ public class TextBlock
}
private static IReadOnlyList<String> GetLines(Object t)
private static IReadOnlyList<String> GetLines(Object? t)
{
return t is TextBlock tb
? tb._Lines
: t.ToString()!.SplitLines();
return t switch
{
TextBlock tb => tb._Lines,
null => Array.Empty<String>(),
_ => t.ToString()!.SplitLines()
};
}
private static String Space(Int32 totalWidth)

View File

@ -60,10 +60,16 @@ public static class Utils
return t;
}
// Below does not work ;(
// [DebuggerStepThrough][MethodImpl(AggressiveInlining | AggressiveOptimization)]
// public static R Apply<T, S, R>(this T t, Func<S, R> f) where T : S
// {
// return f(t);
// }
[DebuggerStepThrough][MethodImpl(AggressiveInlining | AggressiveOptimization)]
public static R Apply<T, R>(this T t, Func<T, R> f) => f(t);
[DebuggerStepThrough][MethodImpl(AggressiveInlining | AggressiveOptimization)]
public static R Apply<T1, T2, R>(this (T1 p1, T2 p2) t, Func<T1, T2, R> f) => f(t.p1, t.p2);

2020
rust/VrmGrabberOxidised/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,15 @@
[package]
name = "VrmGrabberOxidised"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rocket = "=0.5.0-rc.3"
reqwest = "0.11.18"
openssl = { version = "0.10", features = ["vendored"]}
serde = {version = "1.0.173", features = ["derive"]}
serde_json = "1.0.103"
handlebars = "5.0.0-beta.1"
urlencoding = "2.1.2"

View File

@ -0,0 +1,11 @@
pub struct Installation {
name: String,
vrm: i64,
ip: String,
identifier: String,
serial: String,
online: String,
last_seen: String,
number_of_batteries: String,
batter_firmware_version: String
}

View File

@ -0,0 +1,168 @@
use rocket::serde;
use handlebars::Handlebars;
// use rocket::serde::__private::de::TagContentOtherField::Content;
use urlencoding::encode;
mod installation;
mod vrm_account;
struct InstallationToHtmlInterface<'a> {
Name: &'a str,
Ip: i64,
Vrm: &'a str,
Identifier: &'a str,
Serial: &'a str,
EscapedName: &'a str,
Online: &'a str,
LastSeen: &'a str,
NumBatteries: &'a str,
BatteryVersion: &'a str,
ServerIp : &'a str,
FirmwareVersion: &'a str,
}
// impl Default for InstallationToHtmlInterface{
// fn default() -> Self {
// Self{
// Name: "",
// Ip: 0,
// Vrm: "",
// Identifier: "",
// Serial: "",
// EscapedName: "",
// Online: "",
// LastSeen: "",
// NumBatteries: "",
// BatteryVersion: "",
// ServerIp : "10.2.0.1",
// FirmwareVersion: "AF09",
// }
// }
// }
#[macro_use] extern crate rocket;
#[get("/")]
async fn index() -> String{
let res = vrm_account::all_installations_request().await;
let json = rocket::serde::Deserialize(res.unwrap().text().await.unwrap());
let source = "<head>
<style>
tbody {
background-color: #e4f0f5;
}
tbody tr:nth-child(odd) {
background-color: #ECE9E9;
}
th, td { /* cell */
padding: 0.75rem;
font-size: 0.9375rem;
}
th { /* header cell */
font-weight: 700;
text-align: left;
color: #272838;
border-bottom: 2px solid #EB9486;
position: sticky;
top: 0;
background-color: #F9F8F8;
}
table {
border-collapse: collapse;
width: 100%;
border: 2px solid rgb(200, 200, 200);
letter-spacing: 1px;
font-family: sans-serif;
font-size: 0.8rem;
position: absolute; top: 0; bottom: 0; left: 0; right: 0;
}
thead th {
border: 1px solid rgb(190, 190, 190);
padding: 5px 10px;
position: sticky;
position: -webkit-sticky;
top: 0px;
background: white;
z-index: 999;
}
td {
text-align: left;
}
#managerTable {
overflow: hidden;
}</style></head>
<div id='managerTable'>
<table>
<tbody>
<tr>
<th>Name</th>
<th>Gui</th>
<th>VRM</th>
<th>Grafana</th>
<th>Identifier</th>
<th>Last Seen</th>
<th>Serial</th>
<th>#Batteries</th>
<th>Firmware-Version</th>
<th>Update</th>
</tr>
{{#inst}}
{{> installations}}
{{/inst}}
</tbody>
</table>
<div id='managerTable'>";
let partialSource= "<tr><td>{{Name}}</td>
<td><a target='_blank' href=http://{{Ip}}>{{online}} {{Ip}}</a></td>
<td><a target='_blank' href=https://vrm.victronenergy.com/installation/{{Vrm}}/dashboard>VRM</a></td>
<td><a target='_blank' href='https://salidomo.innovenergy.ch/d/ENkNRQXmk/installation?refresh=5s&orgId=1&var-Installation={{EscapedName}}&kiosk=tv'>Grafana</a></td>
<td>{{Identifier}}</td>
<td>{{LastSeen}}</td>
<td>{{Serial}}</td>
<td>{{NumBatteries}}</td>
<td>{{BatteryVersion}}</td>
<td><a target='_blank' href=https://{{ServerIp}}/UpdateBatteryFirmware/{{Ip}}/{{NumBatteries}}>⬆️{{FirmwareVersion}}</a></td>
</tr>";
let mut handlebars = Handlebars::new();
handlebars.register_template_string("installations", partialSource);
let mut installsForHtml = Vec::<InstallationToHtmlInterface>::new();
for inst in json["records"] {
let mut installation = InstallationToHtmlInterface {
Name: inst["name"],
Ip: 0, // TODO
Vrm: inst["idSite"].parse::<i64>(),
Identifier: inst["identifier"],
Serial: "", //Todo Grab and parse Details
EscapedName: &encode(inst["name"]).to_string(),
Online: "",
LastSeen: "",
NumBatteries: "",
BatteryVersion: "",
ServerIp : "10.2.0.1",
FirmwareVersion: "AF09",
};
installsForHtml.push(installation)
}
let mut data = installsForHtml;
let result = handlebars.render(source, &data);
return result.unwrap();
}
#[launch]
fn rocket() -> _ {
rocket::build().mount("/", routes![index])
}

View File

@ -0,0 +1,21 @@
use std::ops::Add;
use reqwest::{Error, Response};
// use serde::{Deserialize, Serialize};
const API_ROOT:&str ="https://vrmapi.victronenergy.com/v2";
const USER_ID:&str = "55450";
const TOKEN:&str = "88b36e7226ff7fa7bf231d0f9f98e916f661923c84e494cd27b6bc795ec0074b";
pub async fn all_installations_request() -> Result<Response, Error> {
// use reqwest::header::AUTHORIZATION;
let client = reqwest::Client::new();
let res = client
.get(API_ROOT.to_owned().add("/users/").add(USER_ID).add("/installations"))
.header("Content-Type", "application/json")
.header("x-authorization","Token ".to_owned()+TOKEN)
.send()
.await;
return res;
}

View File

@ -1,11 +1,11 @@
import axios from "axios";
export const axiosConfigWithoutToken = axios.create({
baseURL: "http://localhost:5000/api",
baseURL: "https://localhost:7087",
});
const axiosConfig = axios.create({
baseURL: "http://localhost:5000/api",
baseURL: "https://localhost:7087",
});
axiosConfig.defaults.params = {};