Getpositions angepasst, sodass Jsons geschrieben werden. Ausserdem Anpassung, sodass Leichen (=Bloecke ohne pos) nicht erfasst werden
This commit is contained in:
Vendored
+16
-1
@@ -32,7 +32,22 @@
|
|||||||
"easy.dxf",
|
"easy.dxf",
|
||||||
"-s",
|
"-s",
|
||||||
"-d",
|
"-d",
|
||||||
"-r"
|
"-r",
|
||||||
|
"-c"
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "use easy.dxf, nur Pritsche",
|
||||||
|
"type": "debugpy",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${file}",
|
||||||
|
"console": "integratedTerminal",
|
||||||
|
"justMyCode": true,
|
||||||
|
"args": [
|
||||||
|
"--filename",
|
||||||
|
"easy.dxf",
|
||||||
|
"-r",
|
||||||
|
"-c"
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,3 +8,11 @@ MOTOR
|
|||||||
[Pritsche]
|
[Pritsche]
|
||||||
PRITSCHE_100-60-SCHRAFF
|
PRITSCHE_100-60-SCHRAFF
|
||||||
PRITSCHE_200-60
|
PRITSCHE_200-60
|
||||||
|
|
||||||
|
[Unterverteiler]
|
||||||
|
Breite = 320
|
||||||
|
Hoehe = 350
|
||||||
|
|
||||||
|
[Sensor_Marker]
|
||||||
|
Breite = 80
|
||||||
|
Hoehe = 90
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
[Eingänge]
|
||||||
|
0-0_ILS_EINGANG
|
||||||
|
|
||||||
|
[Ausgänge]
|
||||||
|
AUSGANG
|
||||||
|
MOTOR
|
||||||
|
|
||||||
|
[Pritsche]
|
||||||
|
PRITSCHE_100-60-SCHRAFF
|
||||||
|
PRITSCHE_200-60
|
||||||
|
|
||||||
|
[Unterverteiler]
|
||||||
|
Breite = 320
|
||||||
|
Hoehe = 350
|
||||||
|
|
||||||
|
[Sensor_Marker]
|
||||||
|
Breite = 80
|
||||||
|
Hoehe = 90
|
||||||
Binary file not shown.
+23
-42
@@ -27,39 +27,6 @@ def write_results(jsnResults, outdir, filename):
|
|||||||
fh.write(jsnResults)
|
fh.write(jsnResults)
|
||||||
print("done")
|
print("done")
|
||||||
|
|
||||||
|
|
||||||
def get_world_position_of_attribute(insert, attrib):
|
|
||||||
"""
|
|
||||||
Berechnet die echte Weltposition eines Block-Attributs (ATTRIB)
|
|
||||||
unter Berücksichtigung von Skalierung, Drehung und Blockverschiebung.
|
|
||||||
|
|
||||||
:param insert: Blockreferenz-Entity (INSERT)
|
|
||||||
:param attrib: Attribut-Entity (ATTRIB)
|
|
||||||
:return: Vector (x, y, z) in Weltkoordinaten
|
|
||||||
"""
|
|
||||||
# Lokale Position des Attributs
|
|
||||||
local_pos = attrib.dxf.insert
|
|
||||||
|
|
||||||
# Blockeinfügepunkt
|
|
||||||
block_pos = insert.dxf.insert
|
|
||||||
|
|
||||||
# Blockskalierung (Standard = 1.0)
|
|
||||||
xscale = getattr(insert.dxf, 'xscale', 1.0)
|
|
||||||
yscale = getattr(insert.dxf, 'yscale', 1.0)
|
|
||||||
|
|
||||||
# Rotation des Blocks (in Grad)
|
|
||||||
rotation_deg = insert.dxf.rotation
|
|
||||||
|
|
||||||
# Transformation aufbauen: Skalierung -> Rotation -> Translation
|
|
||||||
m = (
|
|
||||||
Matrix44.scale(xscale, yscale, 1) @
|
|
||||||
Matrix44.z_rotate(rotation_deg * math.pi / 180) @
|
|
||||||
Matrix44.translate(block_pos.x, block_pos.y, block_pos.z)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Lokale Position transformieren
|
|
||||||
return m.transform(local_pos)
|
|
||||||
|
|
||||||
def merge_two_dicts(x, y):
|
def merge_two_dicts(x, y):
|
||||||
z = x.copy()
|
z = x.copy()
|
||||||
z.update(y)
|
z.update(y)
|
||||||
@@ -96,16 +63,22 @@ def get_input_positions(msp: ezdxf.document.Drawing.modelspace):
|
|||||||
#print(f"-- coord {attrib.dxf.text} --: {attrib.dxf.insert}")
|
#print(f"-- coord {attrib.dxf.text} --: {attrib.dxf.insert}")
|
||||||
if attrib.dxf.tag == "REALE_POSITION" and attrib.dxf.text == "x":
|
if attrib.dxf.tag == "REALE_POSITION" and attrib.dxf.text == "x":
|
||||||
#print(f"-- coord real --: {attrib.dxf.insert}")
|
#print(f"-- coord real --: {attrib.dxf.insert}")
|
||||||
world_pos = get_world_position_of_attribute(insert, attrib)
|
pos = attrib.dxf.insert #Position Ecke unten links von "x"-Marker auslesen
|
||||||
#print(f"-- world --: {world_pos}")
|
|
||||||
ld["block_pos"] = attrib.dxf.insert
|
|
||||||
ld["world_pos"] = world_pos
|
|
||||||
|
|
||||||
if not id == "":
|
# Hoehe und Breite von "x" addieren, um Mittelpunkt zu finden
|
||||||
if not id in allIds:
|
breite_marker = config.getfloat("Sensor_Marker", "Breite")
|
||||||
allIds[id] = ld
|
hoehe_marker = config.getfloat("Sensor_Marker", "Hoehe")
|
||||||
|
pos_midx = pos.x + breite_marker*0.5
|
||||||
|
pos_midy = pos.y + hoehe_marker*0.5
|
||||||
|
|
||||||
|
ld["pos"] = (pos_midx, pos_midy)
|
||||||
|
|
||||||
|
# Nur wenn eine ID vorhanden ist, und eine gültige Position existiert
|
||||||
|
if id and "pos" in ld and isinstance(ld["pos"], tuple) and len(ld["pos"]) == 2:
|
||||||
|
if id in allIds:
|
||||||
|
allIds[id] = merge_two_dicts(allIds[id], ld) #Kombiniert alle infos aus dxf und "pos"
|
||||||
else:
|
else:
|
||||||
allIds[id] = merge_two_dicts(allIds[id], ld)
|
allIds[id] = ld
|
||||||
return allIds
|
return allIds
|
||||||
|
|
||||||
|
|
||||||
@@ -172,7 +145,7 @@ def get_rack_positions(msp):
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
def to_json(d, pretty: bool = True) -> str:
|
def to_json(d, pretty: bool = True) -> str:
|
||||||
return json.dumps(d, indent=2 if pretty else None, default=str)
|
return json.dumps(d, indent=2 if pretty else None, ensure_ascii=False, default=str) #ensure_ascii false für darstellung von "ue"
|
||||||
|
|
||||||
def get_dxf_file(filepath):
|
def get_dxf_file(filepath):
|
||||||
"""hole das dxf file
|
"""hole das dxf file
|
||||||
@@ -199,6 +172,7 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
out_dir = os.environ.get('PROJECT_DATA')
|
out_dir = os.environ.get('PROJECT_DATA')
|
||||||
work_dir = os.environ.get('PROJECT_WORK')
|
work_dir = os.environ.get('PROJECT_WORK')
|
||||||
|
config_dir = os.environ.get("PROJECT_CFG")
|
||||||
filename = args.filename
|
filename = args.filename
|
||||||
|
|
||||||
doc = get_dxf_file(os.path.join(work_dir, filename)) # type: ignore
|
doc = get_dxf_file(os.path.join(work_dir, filename)) # type: ignore
|
||||||
@@ -208,18 +182,25 @@ if __name__ == '__main__':
|
|||||||
res_dist = dict()
|
res_dist = dict()
|
||||||
res_rac = dict()
|
res_rac = dict()
|
||||||
if args.sensors or args.dists or args.rack:
|
if args.sensors or args.dists or args.rack:
|
||||||
|
|
||||||
|
config = configparser.ConfigParser(allow_no_value=True)
|
||||||
|
config.read(os.path.join(config_dir, "getpositions.cfg"))
|
||||||
|
|
||||||
if args.sensors:
|
if args.sensors:
|
||||||
res_pos = get_input_positions(msp)
|
res_pos = get_input_positions(msp)
|
||||||
if args.console:
|
if args.console:
|
||||||
print(to_json(res_pos))
|
print(to_json(res_pos))
|
||||||
|
write_results(to_json(res_pos), work_dir, "sensors.json")
|
||||||
if args.dists:
|
if args.dists:
|
||||||
res_dist = get_subdistributor_positions(msp)
|
res_dist = get_subdistributor_positions(msp)
|
||||||
if args.console:
|
if args.console:
|
||||||
print(to_json(res_dist))
|
print(to_json(res_dist))
|
||||||
|
write_results(to_json(res_dist), work_dir, "subdistributors.json")
|
||||||
if args.rack:
|
if args.rack:
|
||||||
res_rac = get_rack_positions(msp)
|
res_rac = get_rack_positions(msp)
|
||||||
if args.console:
|
if args.console:
|
||||||
print(to_json(res_rac))
|
print(to_json(res_rac))
|
||||||
|
write_results(to_json(res_rac), work_dir, "racks.json")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
parser.print_help()
|
parser.print_help()
|
||||||
|
|||||||
+24
-16
@@ -3,30 +3,36 @@ from dacite import from_dict
|
|||||||
from typing import List
|
from typing import List
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Sensor:
|
class Punkt:
|
||||||
id: str
|
|
||||||
x: float
|
x: float
|
||||||
y: float
|
y: float
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Aktor:
|
class Ziel:
|
||||||
|
#Sensor oder Aktor
|
||||||
id: str
|
id: str
|
||||||
x: float
|
pos: Punkt
|
||||||
y: float
|
|
||||||
|
@dataclass
|
||||||
|
class Quelle:
|
||||||
|
#Unterverteiler
|
||||||
|
id: str
|
||||||
|
pos: Punkt
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Pritsche:
|
class Pritsche:
|
||||||
id: str
|
id: str
|
||||||
x: float
|
cords: List[Punkt]
|
||||||
y: float
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Anlage:
|
class Anlage:
|
||||||
name: str
|
name: str
|
||||||
sensoren: List[Sensor]
|
sensoren: List[Ziel]
|
||||||
aktoren: List[Aktor]
|
aktoren: List[Ziel]
|
||||||
|
unterverteiler: List[Quelle]
|
||||||
kabelpritschen: List[Pritsche]
|
kabelpritschen: List[Pritsche]
|
||||||
|
|
||||||
def to_json(self, pretty: bool = True) -> str:
|
def to_json(self, pretty: bool = True) -> str:
|
||||||
@@ -42,19 +48,21 @@ class Anlage:
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
||||||
json_string = '''{
|
json_string = '''{
|
||||||
"name": "H&M",
|
"name": "H&M",
|
||||||
"sensoren": [
|
"sensoren": [
|
||||||
{"id": "AP4321", "x": 14, "y": 50},
|
{"id": "S1", "pos": {"x": 3, "y": 5}}
|
||||||
{"id": "AP4322", "x": 22, "y": 100}
|
|
||||||
],
|
],
|
||||||
"aktoren": [
|
"aktoren": [
|
||||||
{"id": "AP4321", "x": 14, "y": 50},
|
{"id": "A1", "pos": {"x": 3, "y": 5}}
|
||||||
{"id": "AP4322", "x": 22, "y": 100}
|
],
|
||||||
|
"unterverteiler": [
|
||||||
|
{"id": "U1", "pos": {"x": 3, "y": 5}}
|
||||||
],
|
],
|
||||||
"kabelpritschen": [
|
"kabelpritschen": [
|
||||||
{"id": "p1", "x": 1, "y": 0},
|
{"id": "p1", "cords": [{"x": 3, "y": 5},{"x": 3, "y": 5}]},
|
||||||
{"id": "p2", "x": 22, "y": 10}
|
{"id": "p2", "cords": [{"x": 3, "y": 5},{"x": 3, "y": 5}]}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
'''
|
'''
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
contourpy==1.3.1
|
||||||
|
cycler==0.12.1
|
||||||
|
et-xmlfile==1.1.0
|
||||||
|
ezdxf==1.4.0
|
||||||
|
fonttools==4.57.0
|
||||||
|
kiwisolver==1.4.8
|
||||||
|
numpy==1.26.1
|
||||||
|
openpyxl==3.1.2
|
||||||
|
packaging==24.2
|
||||||
|
pillow==11.1.0
|
||||||
|
PyMuPDF==1.25.5
|
||||||
|
pyparsing==3.2.3
|
||||||
|
PySide6==6.9.0
|
||||||
|
PySide6_Addons==6.9.0
|
||||||
|
PySide6_Essentials==6.9.0
|
||||||
|
python-dateutil==2.8.2
|
||||||
|
pytz==2023.3.post1
|
||||||
|
shiboken6==6.9.0
|
||||||
|
six==1.16.0
|
||||||
|
typing_extensions==4.13.1
|
||||||
|
tzdata==2023.3
|
||||||
+133
@@ -0,0 +1,133 @@
|
|||||||
|
import os
|
||||||
|
import json
|
||||||
|
import argparse
|
||||||
|
import heapq
|
||||||
|
import math
|
||||||
|
|
||||||
|
# Hilfsfunktionen
|
||||||
|
def load_json(filepath):
|
||||||
|
with open(filepath, 'r', encoding='utf-8') as f:
|
||||||
|
return json.load(f)
|
||||||
|
|
||||||
|
def parse_pos(pos_str):
|
||||||
|
""" Konvertiert '(x, y)' oder '(x, y, z)' in ein Tuple """
|
||||||
|
try:
|
||||||
|
return tuple(map(float, pos_str.strip('()').split(',')))
|
||||||
|
except Exception:
|
||||||
|
raise ValueError(f"Ungültiges Positionsformat: {pos_str}")
|
||||||
|
|
||||||
|
def distance(p1, p2):
|
||||||
|
""" Euklidische Distanz in 2D """
|
||||||
|
return math.sqrt((p1[0]-p2[0])**2 + (p1[1]-p2[1])**2)
|
||||||
|
|
||||||
|
def add_edge(graph, node1, node2, dist):
|
||||||
|
""" Fügt eine Kante zwischen zwei Knoten im Graphen hinzu """
|
||||||
|
if node1 not in graph:
|
||||||
|
graph[node1] = []
|
||||||
|
if node2 not in graph:
|
||||||
|
graph[node2] = []
|
||||||
|
graph[node1].append((node2, dist))
|
||||||
|
graph[node2].append((node1, dist))
|
||||||
|
|
||||||
|
def project_point_on_segment(p, a, b):
|
||||||
|
"""Projektion eines Punktes p auf ein Liniensegment a-b"""
|
||||||
|
ax, ay = a
|
||||||
|
bx, by = b
|
||||||
|
px, py = p
|
||||||
|
|
||||||
|
dx = bx - ax
|
||||||
|
dy = by - ay
|
||||||
|
if dx == dy == 0:
|
||||||
|
return a
|
||||||
|
|
||||||
|
t = ((px - ax) * dx + (py - ay) * dy) / (dx * dx + dy * dy)
|
||||||
|
t = max(0, min(1, t)) # Begrenze t auf [0,1]
|
||||||
|
return (ax + t * dx, ay + t * dy)
|
||||||
|
|
||||||
|
def dijkstra(graph, start):
|
||||||
|
""" Dijkstra-Algorithmus, um die kürzesten Wege im Graphen zu berechnen """
|
||||||
|
distances = {node: float('inf') for node in graph}
|
||||||
|
distances[start] = 0
|
||||||
|
priority_queue = [(0, start)] # (Distanz, Knoten)
|
||||||
|
|
||||||
|
while priority_queue:
|
||||||
|
current_distance, current_node = heapq.heappop(priority_queue)
|
||||||
|
|
||||||
|
if current_distance > distances[current_node]:
|
||||||
|
continue
|
||||||
|
|
||||||
|
for neighbor, weight in graph[current_node]:
|
||||||
|
distance = current_distance + weight
|
||||||
|
|
||||||
|
if distance < distances[neighbor]:
|
||||||
|
distances[neighbor] = distance
|
||||||
|
heapq.heappush(priority_queue, (distance, neighbor))
|
||||||
|
|
||||||
|
return distances
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser(description='Berechne Wege von Sensoren zu Verteilern über Kabeltrassen')
|
||||||
|
parser.add_argument('-c', '--console', action='store_true', help='Ausgabe auf Konsole')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# Umgebungsvariablen
|
||||||
|
work_dir = os.environ.get("PROJECT_WORK")
|
||||||
|
config_dir = os.environ.get("PROJECT_CFG")
|
||||||
|
|
||||||
|
# Pfade zu JSON-Dateien
|
||||||
|
sensors_path = os.path.join(work_dir, "sensors.json")
|
||||||
|
subdist_path = os.path.join(work_dir, "subdistributors.json")
|
||||||
|
racks_path = os.path.join(work_dir, "racks.json")
|
||||||
|
|
||||||
|
# Einlesen
|
||||||
|
sensors = load_json(sensors_path)
|
||||||
|
subdists = {k: parse_pos(v) for k, v in load_json(subdist_path).items()}
|
||||||
|
racks = load_json(racks_path)
|
||||||
|
|
||||||
|
# Graph erstellen
|
||||||
|
graph = {}
|
||||||
|
|
||||||
|
# Sensoren zu Kabeltrassen verbinden
|
||||||
|
for sensor_id, sensor_info in sensors.items(): #über alle Sensoren und alle deren Infos laufen
|
||||||
|
sensor_pos = tuple(sensor_info['pos']) #sensor position als tuple übergeben
|
||||||
|
for rack in racks:
|
||||||
|
for segment_start, segment_end in zip(rack[:-1], rack[1:]):
|
||||||
|
# Berechne Distanz von Sensor zur Kabeltrasse
|
||||||
|
px, py = project_point_on_segment(sensor_pos, segment_start, segment_end)
|
||||||
|
dist = distance(sensor_pos, (px, py))
|
||||||
|
add_edge(graph, sensor_id, f"rack_{rack}", dist)
|
||||||
|
|
||||||
|
# Unterverteiler zu Kabeltrassen verbinden
|
||||||
|
for uc_id, uc_pos in subdists.items():
|
||||||
|
for rack in racks:
|
||||||
|
for segment_start, segment_end in zip(rack[:-1], rack[1:]):
|
||||||
|
# Berechne Distanz von UC zur Kabeltrasse
|
||||||
|
px, py = project_point_on_segment(uc_pos, segment_start, segment_end)
|
||||||
|
dist = distance(uc_pos, (px, py))
|
||||||
|
add_edge(graph, uc_id, f"rack_{rack}", dist)
|
||||||
|
|
||||||
|
# Sensor zu UC verbinden (Routing von Sensoren zu den zugehörigen Unterverteilern)
|
||||||
|
for sensor_id, sensor_info in sensors.items():
|
||||||
|
subdist_id = None
|
||||||
|
if 'KENNZEICHNUNG' in sensor_info:
|
||||||
|
for uc_id in subdists:
|
||||||
|
if uc_id in sensor_info['KENNZEICHNUNG']:
|
||||||
|
subdist_id = uc_id
|
||||||
|
break
|
||||||
|
if subdist_id:
|
||||||
|
# Verbinde den Sensor mit dem zugehörigen Unterverteiler
|
||||||
|
sensor_pos = tuple(sensor_info['pos'])
|
||||||
|
uc_pos = subdists[subdist_id]
|
||||||
|
dist = distance(sensor_pos, uc_pos)
|
||||||
|
add_edge(graph, sensor_id, subdist_id, dist)
|
||||||
|
|
||||||
|
# Berechnung der kürzesten Wege mit Dijkstra
|
||||||
|
routing_result = {}
|
||||||
|
for sensor_id in sensors:
|
||||||
|
distances = dijkstra(graph, sensor_id)
|
||||||
|
routing_result[sensor_id] = distances
|
||||||
|
|
||||||
|
if args.console:
|
||||||
|
print(json.dumps(routing_result, indent=2))
|
||||||
Reference in New Issue
Block a user