Compare commits

...

2 Commits

6 changed files with 318 additions and 345 deletions
+27 -29
View File
@@ -1,10 +1,13 @@
{
// Verwendet IntelliSense zum Ermitteln möglicher Attribute.
// Zeigen Sie auf vorhandene Attribute, um die zugehörigen Beschreibungen anzuzeigen.
// Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Python Debugger: Current File",
"type": "debugpy",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal"
},
{
"name": "Python-Debugger: Aktuelle Datei mit Argumenten",
"type": "debugpy",
@@ -18,24 +21,22 @@
"type": "debugpy",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal",
"justMyCode": true
"console": "integratedTerminal"
},
{
"name": "use easy.dxf",
"name": "getpositions with easy.dxf",
"type": "debugpy",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal",
"justMyCode": true,
"args": [
"--filename",
"easy.dxf",
"-s",
"-d",
"-r",
"-c",
"-w"
"--sensors",
"--dists",
"--rack",
"--console",
"--write"
]
},
{
@@ -44,12 +45,12 @@
"request": "launch",
"program": "${file}",
"console": "integratedTerminal",
"justMyCode": true,
"args": [
"--filename",
"easy.dxf",
"-r",
"-c"
"-c",
"-w"
]
},
{
@@ -58,7 +59,6 @@
"request": "launch",
"program": "${file}",
"console": "integratedTerminal",
"justMyCode": true,
"args": [
"--filename",
"ST_6300_Steuerungstestlayout1_neueBloecke.dxf",
@@ -67,31 +67,29 @@
"-r"
]
},
{
"name": "run routing for easy",
"type": "debugpy",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal",
"justMyCode": true,
"args": [
"--inputfile",
"easy_positions.json"
]
},
{
"name": "draw cable dxf from easy.json",
"type": "debugpy",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal",
"justMyCode": true,
"args": [
"--json",
"easy.json",
"--dxf",
"easy.dxf"
]
},
{
"name": "routing for easy_positions.json",
"type": "debugpy",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal",
"args": [
"--filename",
"easy_positions.json"
]
}
]
}
+1 -1
View File
@@ -1,4 +1,4 @@
@echo off
CALL manage_interpreter.bat activate_interpreter
python %PROJECT_LIB%\drawdxf.py
python %PROJECT_LIB%\drawdxf.py %*
CALL manage_interpreter.bat deactivate_interpreter
+4
View File
@@ -0,0 +1,4 @@
@echo off
CALL manage_interpreter.bat activate_interpreter
python %PROJECT_LIB%\routing.py %*
CALL manage_interpreter.bat deactivate_interpreter
+4 -6
View File
@@ -7,6 +7,7 @@ import sys
import math
import json
import re
from shapely import Point
"""
@@ -132,13 +133,10 @@ def get_rack_positions(msp):
for e in msp.query('LWPOLYLINE[layer=="PRITSCHE_200-60"]'):
#print_polyline(e)
rack_key = f"Rack_{rack_counter}"
ret[rack_key] = dict()
node_counter = 1
ret[rack_key] = list()
for x, y, start_width, end_width, bulge in e.get_points(): # Gibt Tuple (x, y, start_width, end_width, bulge)
node_key = f"Node_{node_counter}"
ret[rack_key][node_key] = [round(x,1), round(y,1)]
node_counter +=1
p = [round(x,1), round(y,1)]
ret[rack_key].append(p)
rack_counter +=1
# iterate over all entities in modelspace
# for e in msp:
+211 -258
View File
@@ -152,8 +152,6 @@ class RackIDs():
def get_rack_names(self) -> list:
return list(self._rack2begend.keys())
def add_point_to_rack(self, point:Point, name:str):
if point in self._point2rack:
self._point2rack[point].append(name)
@@ -199,13 +197,11 @@ class RackIDs():
last = Point(l2.coords[1])
if l1.distance(first) <= self._tol_snap:
snap_point = l1.interpolate(l1.project(first))
self.add_point_to_rack(snap_point, rnames[l1])
if l1.distance(last) <= self._tol_snap:
snap_point = l1.interpolate(l1.project(last))
self.add_point_to_rack(snap_point, rnames[l1])
def rack_is_horizontal(self, name):
[pa, pe] = self._rack2begend[name]
if pa.y == pe.y:
@@ -216,39 +212,52 @@ class RackIDs():
class Anlage():
r"""
Baut eine Anlage besteend aus Kabeltrassen (Racks), Sensoren und Unterverteilern auf.
Ermöglicht die Berechnung der günstigsten kabelwege und gibt die Kabellängen von jedem Sensor zum zugehörigen Unterverteiler aus.
Ermöglicht die Berechnung der günstigsten Kabelwege und gibt die Kabellängen von jedem Sensor zum zugehörigen Unterverteiler aus.
Parameters
----------
G : NetworkX graph
weight : string or function
If this is a string, then edge weights will be accessed via the
edge attribute with this key (that is, the weight of the edge
joining `u` to `v` will be ``G.edges[u, v][weight]``). If no
such edge attribute exists, the weight of the edge is assumed to
be one.
If this is a function, the weight of an edge is the value
returned by the function. The function must accept exactly three
positional arguments: the two endpoints of an edge and the
dictionary of edge attributes for that edge. The function must
return a number.
Returns
-------
distance : dictionary
Dictionary, keyed by source and target, of shortest paths.
Examples
--------
>>> graph = nx.DiGraph()
>>> graph.add_weighted_edges_from(
... [("0", "3", 3), ("0", "1", -5), ("0", "2", 2), ("1", "2", 4), ("2", "3", 1)]
... )
>>> paths = nx.johnson(graph, weight="weight")
>>> paths["0"]["2"]
['0', '1', '2']
>>> # Erstelle Anlage
>>> an = Anlage()
>>> # Füge racks aus Daten hinzu
>>> an.set_racks(rack_segs)
>>> # Verbinde Racks miteinander (ggf. verlängere ungenaue Racks)
>>> an.join_racks()
>>> # Füge Sensoren als Knoten hinzu
>>> an.add_sensors(sensors)
>>> # Verbinde Sensoren mit deren naheliegendsten Racks
>>> an.connect_sensors_to_racks()
>>> # Füge UV hinzu
>>> an.add_distributors(distributors)
>>> # Verbinde UV mit deren naheliegendsten Racks
>>> an.connect_distributor_to_racks()
>>> # Verknüpfe Sensoren mit zugehörigem UV
>>> an.map_distributors_to_sensors(mapping)
>>>
>>> # Initialisiere Graph
>>> G3 = nx.Graph()
>>> # Fülle eben erstellten Graphen mit Daten
>>> pos = an.generate_graph(G3)
>>> # Ermittle kürzeste Wege von Unterverteilern zu zugehörigen Sensoren
>>> paths = an.create_cable_paths(G3)
>>> print(paths['Dist_1-Sens_1']["path_coords"])
... [Point(-1, 9), Point(0, 9), Point(0, 3), Point(0, 1), Point(1, 1)]
>>> print(paths['Dist_1-Sens_2']["path_coords"])
... [Point(-1, 9), Point(0, 9), Point(0, 3), Point(2, 3), Point(2, 4)]
>>> print(paths['Dist_2-Sens_3']["path_coords"])
... [Point(11, 0), Point(10, 0), Point(10, 2), Point(9, 2)]
>>> print(paths['Dist_1-Sens_1']["path_length"])
... 10
"""
def __init__(self, tol_snap=200, snap_step=10, tol_connect=2, tol_connect_step=0.5):
@@ -270,18 +279,44 @@ class Anlage():
# Toleranzen zur Anbindung von Sensoren / Verteilern zu Racks
self._tol_connect = tol_connect
self._connect_step = tol_connect_step
# Infos zum zeichnen des Graphen
self._node_positions = dict()
def set_racks(self, racks:dict[str, list[Point]]):
r"""
Fügt racks aus eingelsener Datei zu Anlage hinzu.
Parameters
----------
racks - dict{"Rack_1-1": [Point(1, 0), Point(2,0), ...]}
"""
return self._racks.add_racks(racks)
def get_racks(self) -> dict:
r"""
Gibt in Anlage enthaltene Racks zurück.
Returns
----------
dict{Point(0,0): ["Rack_1", "Rack_2", ...]}
"""
return self._racks._point2rack
def add_point_to_rack(self, point:Point, rname:str):
r"""
Fügt einen Punkt zu einem angegebenen Rack hinzu.
"""
return self._racks.add_point_to_rack(point, rname)
def get_all_rack_points(self):
r"""
Gibt einer Liste von allen Punkten allen Racks zurück.
Returns
----------
[Point(0,0), Point(1,5), Point(2,6)]
"""
ret = list()
for rname in self._racks.get_rack_names():
ret.extend(self.get_points_from_rack(rname))
@@ -342,60 +377,8 @@ class Anlage():
self._racks.add_rack(pos, onpoint, drackname)
return self._distributors_onpoints
def rack_segmentation(self, racks:dict) -> list[tuple[str, int, LineString]]:
''' Racks werden zu LineString konvertiert. Racks bestehend aus Polylinine werden in einzelne Segmente zerlegt und in Liste gesammelt.
'''
rack_segments = []
for rack_id, nodes in racks.items():
# Sortiere Node_1, Node_2, ...
sorted_keys = sorted(nodes.keys(), key=lambda k: int(k.split("_")[1]))
coords = [tuple(nodes[k]) for k in sorted_keys]
for i in range(len(coords) - 1):
p1, p2 = coords[i], coords[i+1]
line = LineString([p1, p2])
rack_segments.append((rack_id, i, line))
return(rack_segments)
def find_rack_endpoints(self, rack_segments):
''' Endpunkte der Racks-Segmente werden in Points konvertiert und in Liste gesammelt'''
segment_endpoints = []
for rack_id, idx, line in rack_segments:
for pt in [line.coords[0], line.coords[1]]:
segment_endpoints.append((rack_id, idx, Point(pt)))
return(segment_endpoints)
def increase_circle(self, tol, tol_step, line, pt, rack_id, idx, other_rack_id, other_idx, verbindungen, endpoint_pinned):
''' vergrößere Kreis bis Schnittpunkt mit Rack entsteht.
Argumente:
tol, tol_step -- Toleranz und Schittweite
line -- linestring der entlang gelaufen wird
rack_id, idx -- Rack_id und index von dem linestring stammt
pt -- Punkt der Überprüft wird
other_rack_id, other_idx -- Rack zu welchem der zu untersuchende Punkt gehört
verbindungen -- Liste an die angefügt wird und die verbindungspunkte speichert
endpoint_pinned -- Liste, die Rack und index von dem untersuchten Punkt und den neuen angepinnten Punkt speichert
'''
radius = tol_step
while radius <= tol:
circle = pt.buffer(radius)
if circle.intersects(line):
contact = circle.intersection(line)
if contact.geom_type == "Point":
nearest = contact
else:
nearest = nearest_points(pt, contact)[1]
#print(f" 🟡 Kreisberührung bei {nearest} mit {rack_id}_{idx}")
verbindungen.append((rack_id, idx, other_rack_id, other_idx, nearest))
# Füge verschobenen Endpunkt zu Liste hinzu. [Punkt gehört zu Rack_Nr, alter Punkt, neuer Punkt, gepinnt an Target_Rack]
endpoint_pinned.append((other_rack_id, other_idx, pt, nearest, rack_id))
break
radius += tol_step
def join_racks(self):
self._racks.join_racks()
def find_nearest_rack_from_point(self, max_dist, coarse_step, sensor:Point, racks:dict) -> tuple[Point, str]:
# 1. grobe Kandidatensuche
@@ -516,13 +499,9 @@ class Anlage():
return rack_segments_pinned
def generate_connected_racks(self, racks_json:dict[str, list[Point]]) -> dict:
rack_segments = self.rack_segmentation(racks_json)
rack_endpoints = self.find_rack_endpoints(rack_segments) # könnte man hier auch get_racks_borders nehmen?
connected_racks = self.search_connections(rack_segments, rack_endpoints, self._tol_snap, self._snap_step) #Kann man diese Ausgabe jetzt nochmal in sowas wie add_Racks aufrufen um "eingelesene Racks" zu überscheiben?
self._racks.add_racks(connected_racks)
return connected_racks
def get_node_positions(self):
''' Daten werden durch generate_graph() befüllt'''
return self._node_positions
def is_sensor(self, p:Point) -> bool:
if p in self._sensors.values():
@@ -538,11 +517,8 @@ class Anlage():
def generate_graph(self, G:nx.Graph):
points = list()
points.extend(self.get_all_rack_points())
points.extend(self.get_points_from_sensors())
self._nodeids.add_points(points)
for p in points:
@@ -555,10 +531,10 @@ class Anlage():
nid = self._nodeids.get_id(p)
G.add_node(nid, shape=shape) # Knoten für Startpunkt
pos = dict()
for node in G.nodes:
point = self._nodeids.get_point(node)
pos[node] = (point.x, point.y)
self._node_positions[node] = (point.x, point.y)
for rname in self.get_rack_names():
plist = self.get_points_from_rack(rname)
@@ -573,7 +549,7 @@ class Anlage():
else:
color = "black"
G.add_edge(nid_start, nid_end, color=color, weight=start.distance(end))
return pos
return self._node_positions
def map_distributor_to_sensors(self, dname:str, snamen:list[str]):
''' Gibt zu einem Distributor die zugehörigen Sensoren an, die später zugeordnet werden.
@@ -593,8 +569,6 @@ class Anlage():
def create_cable_path(self, G, sname, dname):
quelle = self._nodeids.get_id(self.get_distributor_point(dname))
ziel = self._nodeids.get_id(self.get_sensor_point(sname))
print(self.get_distributor_point(dname), dname, quelle)
print(self.get_sensor_point(sname), sname, ziel)
pfad_nodes = nx.shortest_path(G, source=quelle, target=ziel, weight='weight')
pfad_length = nx.shortest_path_length(G, source=quelle, target=ziel, weight='weight')
@@ -604,7 +578,12 @@ class Anlage():
pfade = dict()
for sname, dname in self._sensor2dist.items():
pfad_nodes, pfad_length = self.create_cable_path(G, sname, dname)
pfade[f"{dname}-{sname}"] = {"pfad": pfad_nodes, "laenge": pfad_length}
pfad_coords = self._nodeids.get_points(pfad_nodes)
pfade[f"{dname}-{sname}"] = {"path_nodes": pfad_nodes,
"path_coords": pfad_coords,
"path_length": pfad_length}
return pfade
def show_node_ids(self):
return self._nodeids.show()
@@ -617,57 +596,16 @@ class Anlage():
class TestLinesweep(unittest.TestCase):
def test_duplicate_points(self):
''' Testet das Nicht-Hinzufügen von doppelten Punkten'''
# Initialisiere die Liste an Knoten
nodeids = NodeIDs()
# Setze gleichen Knoten doppelt
nodeids.add_point(Point(1,1))
nodeids.add_point(Point(1,1))
self.assertEqual(nodeids.size_of(), 1)
# def test_linesweep(self):
''' Prüft ob aus ungeanuen Endpunkten von Racks innerhalb einer Json ein neues Rack-Gerüst mit aufeinander Liegenden
Endpunkten auf Racks erzeugt wird.
'''
tol = 200
tol_step = 10
racks_json_str= '''{
"Rack_1": {
"Node_1": [ 4946.5, 15774.4 ],
"Node_2": [ 4946.5, 3879.4 ]
},
"Rack_2": {
"Node_1": [ 0.1, 57.6 ],
"Node_2": [ 0.1, 3777.6 ],
"Node_3": [ 14755.1, 3777.6 ]
},
"Rack_3": {
"Node_1": [ 185.1, 15865.5 ],
"Node_2": [ 12450.7, 15865.5 ] },
"Rack_4": {
"Node_1": [ 2866.6, 15774.4 ],
"Node_2": [ 2866.6, 3880.4 ]
},
"Rack_5": {
"Node_1": [ 8866.1, 15774.4 ],
"Node_2": [ 8866.1, 3878.4 ]
}}'''
racks_json = json.loads(racks_json_str)
an = Anlage()
connected_racks = an.generate_connected_racks(racks_json)
res_rack_seg = {'Rack_1-0': [Point(4946.5, 15865.5), Point(4946.5, 3777.6)],
'Rack_2-0': [Point(0.1, 57.6), Point(0.1, 3777.6)],
'Rack_2-1': [Point(0.1, 3777.6), Point(14755.1, 3777.6)],
'Rack_3-0': [Point(185.1, 15865.5), Point(12450.7, 15865.5)],
'Rack_4-0': [Point(2866.6, 15865.5), Point(2866.6, 3777.6)],
'Rack_5-0': [Point(8866.1, 15865.5), Point(8866.1, 3777.6)]
}
self.assertEqual(connected_racks, res_rack_seg)
def test_cut_rack_in_segments(self):
''' Teilt Rack aus Polyline in mehrere Segmente automatisch auf.'''
@@ -676,23 +614,27 @@ class TestLinesweep(unittest.TestCase):
'Rack_2': [Point(-5, 5), Point(5, 5)]
}
# Initialisiere Racks
rack = RackIDs()
# Füge Racks aus gegebenen Daten hinzu und teile Rack_1 bestehend aus 3 Punkten in 2 Racks auf
rack.add_racks(racks_data)
self.assertEqual(rack.get_rack_names(), ['Rack_1-1', 'Rack_1-2', 'Rack_2'])
def test_intersect_segments(self):
''' Stellt Schnittpunkte zwischen Racks fest und fügt Schnittpunkt zu Rack hinzu. '''
racks_data = {
'Rack_1': [Point(0, 0), Point(0, 10), Point (10, 10)],
'Rack_2': [Point(-5, 5), Point(5, 5)],
}
# Initialisiere Racks
rack = RackIDs()
# Füge Racks aus gegebenen Daten hinzu und teile Rack_1 bestehend aus 3 Punkten in 2 Racks auf
rack.add_racks(racks_data)
# Verknüpfe Racks mit echten Schniuttpunkten und füge Schnittpunkte (exakt & beinahe) zu jeweiligem Rack hinzu
rack.join_racks()
self.assertEqual(rack.get_points_from_rack("Rack_1-1"), [Point(0, 0), Point(0, 5), Point (0, 10)])
@@ -704,156 +646,167 @@ class TestLinesweep(unittest.TestCase):
'Rack_2': [Point(1, 5), Point(5, 5)],
'Rack_3': [Point(1.5, 7.5), Point(5,7.5)]
}
# Initialisiere Racks
rack = RackIDs()
# Füge Racks aus gegebenen Daten hinzu und teile Rack_1 bestehend aus 3 Punkten in 2 Racks auf
rack.add_racks(racks_data)
# Verknüpfe Racks mit echten Schniuttpunkten und füge Schnittpunkte (exakt & beinahe) zu jeweiligem Rack hinzu
rack.join_racks()
self.assertEqual(rack.get_points_from_rack("Rack_1"), [Point(0, 0), Point(0, 5), Point (0, 10)])
# def test_ids_to_point(self):
# ''' Testet, ob gefragter Punkt auf Racks a, b, c liegt'''
def test_ids_to_point(self):
''' Testet, ob gefragter Punkt auf Racks a, b, c liegt'''
# res_rack_seg = {'Rack_1-0': [Point(1, 0), Point(5, 6)],
# 'Rack_2-0': [Point(1, 8), Point(1, 0)],
# 'Rack_2-1': [Point(0, 10), Point(5, 10)]}
res_rack_seg = {'Rack_1-0': [Point(1, 0), Point(5, 6)],
'Rack_2-0': [Point(1, 8), Point(1, 0)],
'Rack_2-1': [Point(0, 10), Point(5, 10)]}
# point2rack = RackIDs(res_rack_seg)
point2rack = RackIDs(res_rack_seg)
# self.assertEqual(point2rack.get_racks_from_point(Point(1, 0)), ["Rack_1-0", "Rack_2-0"])
# self.assertEqual(point2rack.get_racks_from_point(Point(5, 6)), ["Rack_1-0"])
# self.assertEqual(point2rack.get_points_from_rack("Rack_2-0"), [Point(1, 0), Point(1, 8)])
self.assertEqual(point2rack.get_racks_from_point(Point(1, 0)), ["Rack_1-0", "Rack_2-0"])
self.assertEqual(point2rack.get_racks_from_point(Point(5, 6)), ["Rack_1-0"])
self.assertEqual(point2rack.get_points_from_rack("Rack_2-0"), [Point(1, 0), Point(1, 8)])
# def test_add_point_interim(self):
# ''' Testet das inzufügen und einsortieren eines Zwischenpunktes zwische nRack-Anfang und Rack-Ende'''
def test_add_point_interim(self):
''' Testet das inzufügen und einsortieren eines Zwischenpunktes zwische nRack-Anfang und Rack-Ende'''
# res_rack_seg = {'Rack_1-0': [Point(1, 0), Point(5, 6)],
# 'Rack_2-0': [Point(1, 8), Point(1, 0)],
# 'Rack_2-1': [Point(0, 10), Point(5, 10)]}
res_rack_seg = {'Rack_1-0': [Point(1, 0), Point(5, 6)],
'Rack_2-0': [Point(1, 8), Point(1, 0)],
'Rack_2-1': [Point(0, 10), Point(5, 10)]}
# point2rack = RackIDs(res_rack_seg)
# point2rack.add_point_to_rack(Point(1,4), "Rack_2-0")
point2rack = RackIDs(res_rack_seg)
point2rack.add_point_to_rack(Point(1,4), "Rack_2-0")
# self.assertEqual(point2rack.get_points_from_rack("Rack_2-0"), [Point(1, 0), Point(1,4), Point(1, 8)])
self.assertEqual(point2rack.get_points_from_rack("Rack_2-0"), [Point(1, 0), Point(1,4), Point(1, 8)])
# def test_add_sensor(self):
# ''' Erzeugt Aufpunkt an dem Sensor nähesten Rack und fügt diesen auf Rack ein (sortiert).'''
def test_add_sensor(self):
''' Erzeugt Aufpunkt an dem Sensor nähesten Rack und fügt diesen auf Rack ein (sortiert).'''
rack_segs = {'Rack_1-0': [Point(0, 0), Point(0, 10)],
'Rack_2-0': [Point(10, -2), Point(10, 5)],
'Rack_2-1': [Point(0, 3), Point(10, 3)]}
sensors = {'Sens_1': Point(1, 1),
'Sens_2': Point(2, 4),
'Sens_3': Point(9, 2)}
# rack_segs = {'Rack_1-0': [Point(0, 0), Point(0, 10)],
# 'Rack_2-0': [Point(10, -2), Point(10, 5)],
# 'Rack_2-1': [Point(0, 3), Point(10, 3)]}
an = Anlage()
point2rack = an.set_racks(rack_segs)
an.add_sensors(sensors)
# sensors = {'Sens_1': Point(1, 1),
# 'Sens_2': Point(2, 4),
# 'Sens_3': Point(9, 2)}
plist1 = an.get_points_from_rack("Rack_1-0")
an.connect_sensors_to_racks()
plist2 = an.get_points_from_rack("Rack_1-0")
self.assertEqual(plist1, [Point(0, 0), Point(0, 10)])
self.assertEqual(plist2, [Point(0, 0), Point(0,1), Point(0, 10)])
# an = Anlage()
# point2rack = an.set_racks(rack_segs)
# an.add_sensors(sensors)
def test_generate_graph(self):
'''Generiert einen Graphen in 3 unterschiedlichen Ausbauestufen (nur Racks, Racks+Sensoren, Racks+Sensoren+Unterverteiler)'''
rack_segs = {'Rack_1-0': [Point(0, 0), Point(0, 10)],
'Rack_2-0': [Point(10, -2), Point(10, 5)],
'Rack_2-1': [Point(0, 3), Point(10, 3)]}
# plist1 = an.get_points_from_rack("Rack_1-0")
sensors = {'Sens_1': Point(1, 1),
'Sens_2': Point(2, 4),
'Sens_3': Point(9, 2)}
# an.connect_sensors_to_racks()
# plist2 = an.get_points_from_rack("Rack_1-0")
distributors = {'Dist_1': Point(-1, 9),
'Dist_2': Point(11, 0)}
# self.assertEqual(plist1, [Point(0, 0), Point(0, 10)])
# self.assertEqual(plist2, [Point(0, 0), Point(0,1), Point(0, 10)])
an = Anlage()
an.set_racks(rack_segs)
an.join_racks
G1 = nx.Graph()
pos = an.generate_graph(G1)
nx.draw(G1, pos, with_labels=False, node_size=10, font_size=8)
plt.show()
an.add_sensors(sensors)
an.connect_sensors_to_racks()
G2 = nx.Graph()
pos = an.generate_graph(G2)
edge_colors = [G2[u][v].get('color', 'black') for u, v in G2.edges()]
nx.draw(G2, pos, with_labels=False, node_size=10, font_size=8, edge_color=edge_colors)
plt.show()
an.add_distributors(distributors)
an.connect_distributor_to_racks()
G3 = nx.Graph()
pos = an.generate_graph(G3)
edge_colors = [G3[u][v].get('color', 'black') for u, v in G3.edges()]
nx.draw(G3, pos, with_labels=False, node_size=10, font_size=8, edge_color=edge_colors)
plt.show()
# def test_generate_graph(self):
def test_Wegsuche(self):
''' Erstellt Graphen mit Racks, Sensoren und Unterverteilern und sucht kürzeste Wege von Unterverteiler zu zugehörigen Sensoren'''
rack_segs = {'Rack_1-0': [Point(0, 0), Point(0, 10)],
'Rack_2-0': [Point(10, -2), Point(10, 5)],
'Rack_2-1': [Point(0, 3), Point(10, 3)]}
# rack_segs = {'Rack_1-0': [Point(0, 0), Point(0, 10)],
# 'Rack_2-0': [Point(10, -2), Point(10, 5)],
# 'Rack_2-1': [Point(0, 3), Point(10, 3)]}
sensors = {'Sens_1': Point(1, 1),
'Sens_2': Point(2, 4),
'Sens_3': Point(9, 2)}
# sensors = {'Sens_1': Point(1, 1),
# 'Sens_2': Point(2, 4),
# 'Sens_3': Point(9, 2)}
distributors = {'Dist_1': Point(-1, 9),
'Dist_2': Point(11, 0)}
# distributors = {'Dist_1': Point(-1, 9),
# 'Dist_2': Point(11, 0)}
mapping = {'Dist_1': ['Sens_1', 'Sens_2'],
'Dist_2': ['Sens_3']}
# an = Anlage()
# an.set_racks(rack_segs)
# Erstelle Anlage
an = Anlage()
# Füge racks aus Daten hinzu
an.set_racks(rack_segs)
# Verbinde Racks miteinander (ggf. verlängere ungenaue Racks)
an.join_racks()
# Füge Sensoren als Knoten hinzu
an.add_sensors(sensors)
# Verbinde Sensoren mit deren naheliegendsten Racks
an.connect_sensors_to_racks()
# Füge UV hinzu
an.add_distributors(distributors)
# Verbinde UV mit deren naheliegendsten Racks
an.connect_distributor_to_racks()
# Verknüpfe Sensoren mit zugehörigem UV
an.map_distributors_to_sensors(mapping)
# G1 = nx.Graph()
# pos = an.generate_graph(G1)
# nx.draw(G1, pos, with_labels=False, node_size=10, font_size=8)
# plt.show()
# Initialisiere Graph
G3 = nx.Graph()
# Fülle eben erstellten Graphen mit Daten
pos = an.generate_graph(G3)
# Extrahiere Farb-Informationen der Kanten
edge_colors = [G3[u][v].get('color', 'black') for u, v in G3.edges()]
# Zeiche Graphen und zeige in
nx.draw(G3, pos, with_labels=False, node_size=10, font_size=8, edge_color=edge_colors)
plt.show()
# an.add_sensors(sensors)
# an.connect_sensors_to_racks()
# G2 = nx.Graph()
# pos = an.generate_graph(G2)
# edge_colors = [G2[u][v].get('color', 'black') for u, v in G2.edges()]
# nx.draw(G2, pos, with_labels=False, node_size=10, font_size=8, edge_color=edge_colors)
# plt.show()
# an.add_distributors(distributors)
# an.connect_distributor_to_racks()
# G3 = nx.Graph()
# pos = an.generate_graph(G3)
# edge_colors = [G3[u][v].get('color', 'black') for u, v in G3.edges()]
# nx.draw(G3, pos, with_labels=False, node_size=10, font_size=8, edge_color=edge_colors)
# plt.show()
# Ermittle kürzeste Wege von Unterverteilern zu zugehörigen Sensoren
paths = an.create_cable_paths(G3)
# def test_Wegsuche(self):
# rack_segs = {'Rack_1-0': [Point(0, 0), Point(0, 10)],
# 'Rack_2-0': [Point(10, -2), Point(10, 5)],
# 'Rack_2-1': [Point(0, 3), Point(10, 3)]}
# sensors = {'Sens_1': Point(1, 1),
# 'Sens_2': Point(2, 4),
# 'Sens_3': Point(9, 2)}
# distributors = {'Dist_1': Point(-1, 9),
# 'Dist_2': Point(11, 0)}
# mapping = {'Dist_1': ['Sens_1', 'Sens_2'],
# 'Dist_2': ['Sens_3']}
# an = Anlage()
# an.set_racks(rack_segs)
# an.add_sensors(sensors)
# an.connect_sensors_to_racks()
# an.add_distributors(distributors)
# an.connect_distributor_to_racks()
# an.map_distributors_to_sensors(mapping)
# G3 = nx.Graph()
# pos = an.generate_graph(G3)
# print(G3.nodes)
# print(G3.edges)
# print([(n, nbrdict) for n, nbrdict in G3.adjacency()])
# print(an.show_node_ids())
# edge_colors = [G3[u][v].get('color', 'black') for u, v in G3.edges()]
# nx.draw(G3, pos, with_labels=False, node_size=10, font_size=8, edge_color=edge_colors)
# plt.show()
# paths = an.create_cable_paths(G3)
# self.assertEqual(paths, "")
self.assertEqual(paths['Dist_1-Sens_1']["path_coords"], [Point(-1, 9), Point(0, 9), Point(0, 3), Point(0, 1), Point(1, 1)])
self.assertEqual(paths['Dist_1-Sens_2']["path_coords"], [Point(-1, 9), Point(0, 9), Point(0, 3), Point(2, 3), Point(2, 4)])
self.assertEqual(paths['Dist_2-Sens_3']["path_coords"], [Point(11, 0), Point(10, 0), Point(10, 2), Point(9, 2)])
self.assertEqual(paths['Dist_1-Sens_1']["path_length"], 10)
self.assertEqual(paths['Dist_1-Sens_2']["path_length"], 10)
self.assertEqual(paths['Dist_2-Sens_3']["path_length"], 4)
+79 -59
View File
@@ -7,42 +7,92 @@ import matplotlib.pyplot as plt
import networkx as nx
from shapely.geometry import LineString, Point
from shapely.ops import nearest_points
from plant import Anlage
# Funktionen
def load_json(jsonfilename):
with open(jsonfilename, encoding='utf-8') as fh:
return json.load(fh)
def rack_segmentation(racks):
''' Racks werden zu LineString konvertiert. Racks bestehend aus Polylinine werden in einzelne Segmente zerlegt.'''
rack_segments = []
for rack_id, nodes in racks.items():
# Sortiere Node_1, Node_2, ...
sorted_keys = sorted(nodes.keys(), key=lambda k: int(k.split("_")[1]))
coords = [tuple(nodes[k]) for k in sorted_keys]
for i in range(len(coords) - 1):
p1, p2 = coords[i], coords[i+1]
line = LineString([p1, p2])
rack_segments.append((rack_id, i, line))
def create_plant(racks:dict, sensors:dict, distributors:dict, mapping:dict):
return(rack_segments)
# racks = {'Rack_1-0': [Point(0, 0), Point(0, 10)],
# 'Rack_2-0': [Point(10, -2), Point(10, 5)],
# 'Rack_2-1': [Point(0, 3), Point(10, 3)]}
def rack_endpoints(rack_segments)
''' Endpunkte der Racks werden in Points konvertiert'''
segment_endpoints = []
for rack_id, idx, line in rack_segments:
for pt in [line.coords[0], line.coords[1]]:
segment_endpoints.append((rack_id, idx, Point(pt)))
# sensors = {'Sens_1': Point(1, 1),
# 'Sens_2': Point(2, 4),
# 'Sens_3': Point(9, 2)}
return(segment_endpoints)
# distributors = {'Dist_1': Point(-1, 9),
# 'Dist_2': Point(11, 0)}
def pin_rack_endpoints(rack_segments, segment_endpoints)
'''Erstellung eines Dicts, welches unter dem Key "Rack_id - Index" die Endpunkte der Racks speichert. Endpunkte von Racks innerhalb der Toleranz werden an nahegelegenes Rack gepinnt.'''
# mapping = {'Dist_1': ['Sens_1', 'Sens_2'],
# 'Dist_2': ['Sens_3']}
# "mapping": {
# "UC0101": [
# "BG3241",
# "BG3240",
# "MA0062",
# "FC0062"
# ]
# }
an = Anlage()
# Füge racks aus Daten hinzu
an.set_racks(racks)
# Verbinde Racks miteinander (ggf. verlängere ungenaue Racks)
an.join_racks()
# Füge Sensoren als Knoten hinzu
an.add_sensors(sensors)
# Verbinde Sensoren mit deren naheliegendsten Racks
an.connect_sensors_to_racks()
# Füge UV hinzu
an.add_distributors(distributors)
# Verbinde UV mit deren naheliegendsten Racks
an.connect_distributor_to_racks()
# Verknüpfe Sensoren mit zugehörigem UV
an.map_distributors_to_sensors(mapping)
# Initialisiere Graph
G = nx.Graph()
# Fülle eben erstellten Graphen mit Daten
an.generate_graph(G)
# Ermittle kürzeste Wege von Unterverteilern zu zugehörigen Sensoren
paths = an.create_cable_paths(G)
if args.graph:
draw_graph(G,an)
def draw_graph(G:nx.Graph, an:Anlage):
pos = an.get_node_positions()
edge_colors = [G[u][v].get('color', 'black') for u, v in G.edges()]
nx.draw(G, pos, with_labels=False, node_size=10, font_size=8, edge_color=edge_colors)
plt.show()
def prepare_data(rawdata:dict):
sensors = data["sensors"]
subdists = data["distributors"]
racks = data["racks"]
mapping = data["mapping"]
dracks = dict()
for rname,lp in racks:
if rname not in dracks:
dracks[rname] = list()
dracks[rname].append(Point(lp))
return (sensors, subdists, dracks, mapping)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Berechne Wege von Sensoren zu Verteilern über Kabeltrassen')
parser.add_argument('-i', '--inputfile', action='store', required=True, default="easy_position.json", help='file with all informations about positions gathered from getpositions', metavar='my_positions.json')
parser.add_argument('-f', '--filename', action='store', required=True, default="easy_position.json", help='file with all informations about positions gathered from getpositions', metavar='my_positions.json')
parser.add_argument('-c', '--console', action='store_true', help='Ausgabe auf Konsole')
parser.add_argument('-g', '--graph', action='store_true', help='Zeichnet den Graphen der Anlage')
args = parser.parse_args()
@@ -51,50 +101,20 @@ if __name__ == "__main__":
config_dir = os.environ.get("PROJECT_CFG")
# Pfade zu JSON-Dateien
jsonfilename = args.inputfile
jsonfilename = args.filename
sensors_path = os.path.join(work_dir, jsonfilename)
# Einlesen
rawdata = load_json(sensors_path)
data = load_json(sensors_path)
sensors = data["sensors"]
subdists = data["distributors"]
racks = data["racks"]
(racks, sensors, subdists, mapping) = prepare_data(rawdata)
# 1. Alle Kreuzungspunkte der Racks ermitteln
''' Funktion linesweep_circle wird aufgerufen.
Eingabe für Funktion: Nummerierte Racks aus getpositions.py. Reine Liste aus Koordinaten [((Anfang), (Ende)), ((Anfang), (Ende)), ... ] wird in Funktion erstellt
Racks werden abgelaufen und tatsächliche Schnittpunkte sowie Nahezu-Schnitte erfasst.
Ausgabe von Linesweep Circle: Liste von Endpunkten der Racks aus .dxf & Liste von SP von Rack_a mit Rack_b
plant = create_plant(racks, sensors, subdists, mapping)
# Erstelle Anlage
'''
# 2. Graph aus Racks erstellen
''' Zunächst leeren Graph erstellen.
Endpunkte der Racks wie in .dxf als Knoten hinzufügen. Kanten zwischen den Knoten ebenso hinzufügen.
Mittels ausgabe von linesweep_cirle: weitere Knoten auf Kanten hinzufügen und graph vollständig verknüpfen
Frage: Problematisch, wenn Knoten sehr nahe beieinander liegen also teoretischen "Doppelt" sind? -> lieber Endpunkte von Racks, die über linesweep_circle gefunden werden überschreiben?
Zusätzlich: über shapely und distance funktion sämtliche Distanzen bestimmen und Graph gewichten
'''
# 3. Sensoren mit nächstgelegenen Racks verknüpfen
''' Für jeden Sensor: Sensor pos aus json zu Graph hinzufügen
Für jeden Sensor: shapely.distance aufrufen und distance zu allen Racks bzw. Edges ermitteln. (if distance(a) > distance(b) -> Circle_methode um Sensor und Schnittpunkt zu Rack_b ermitteln)
SP von Sensor mit Rack als Node hinzufügen und Connection zwischen Sensor, Node, Edge herstellen
'''
# 4. Unterverteiler mit nächstegelegenen Racks verknüpfen
''' siehe 3.
'''
# 5. Bestimmung des küzesten Wegs
''' über Graphen-Toolbox z.B. nx wird für jeden Knoten zu dessen zugehörigen Unterverteilung der kürzeste Weg gefunden.
Schreiben der Ergebnisse in eine seperate kabel.json z.B.
'''