Ankündigung

Einklappen
Keine Ankündigung bisher.

Ü - Übertragen Flag

Einklappen
Dieser Beitrag wurde beantwortet.
X
X
 
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

    #16
    Zitat von DirtyHarry Beitrag anzeigen
    Naja, das habe ich schon in #6 geschrieben...

    ["So wie ich das bisher gesehen habe, musst du das Ü-Flag an einem Eingang auch setzen, wenn das KO beim Init ein Read-Telegramm versenden soll (zusätzlich zum I-Flag)."
    Indirekt schon (Danke!), konnte ich aber nicht zuordnen, da das I-Flag bei meinem Problem nicht gesetzt ist. Die Inits macht das MDT-Logikmodul ohne gesetztem I-Flag.

    Gruß

    lopiuh
    Zuletzt geändert von lopiuh; 09.02.2025, 08:21.

    Kommentar


      #17
      Zitat von Klaus Gütter Beitrag anzeigen
      eigentlich ein eigenes Flag "Read On Init"
      Stellt sich nur die Frage, was sagt die KNX-Spec zum Verhalten des Ü-Flags: Unterdrücken der Leseanforderung bei gesetzem I-Flag oder nur das Unterdrücken der normalen Sendevorgänge?


      Und wenn letzteres, warum werden dann eigentlich "alle" Geräte zertifiziert, wenn sie doch nicht der Spezifikation entsprechen?
      Gruß Andreas

      -----------------------------------------------------------
      Immer wieder benötigt: KNX-Grundlagen PDF Englisch, PDF Deutsch oder
      Deutsche Version im KNX-Support.

      Kommentar


        #18
        Zitat von DirtyHarry Beitrag anzeigen
        Stellt sich nur die Frage, was sagt die KNX-Spec zum Verhalten des Ü-Flags:
        Ich denk, es gibt auch ein paar Freiheiten und man hat nicht alles bis ins kleinste Detail vorgedacht.
        Gruß
        Florian

        Kommentar


          #19
          Zitat von Klaus Gütter Beitrag anzeigen
          In der KNX-Spec gibt es für die Situation "Lesen beim Hochstarten" eigentlich ein eigenes Flag "Read On Init"
          Klaus, wenn Du das schon ansprichst : Wie soll denn dieses Flag interpretiert werden?
          1. Bisher hatte ich das so verstanden, dass man das Flag an einem KO setzen kann und dann sendet das KO - ohne dass das Applikaitonsprogramm dafür extra angepasst werden muss - einfach ein Read, sobald der KNX-Stack des Gerätes initialisiert ist.
          2. Es wäre auch denkbar, dass der KNX-Stack für System B eine neue Mehode wie knxReadOnInit haben sollte, die vom Applikationsprogramm irgendwann aufgerufen wird, wenn die Applikation soweit ist, Werte zu empfangen. Und das neue I-Flag erlaubt eben das Senden dieses Reads oder eben nicht.
          Falls Punkt 1 beim Design gemeint war, verstehe ich es nicht, denn dann fehlt mir eine Zeitkomponente - wenn das Applikationsprogramm z.B. 5 Sekunden braucht, um irgendwelche Hardware oder Libs zu initialisieren, dann verpasst es vielleicht die Antworten, die der KNX-Stack sofort nach dem Neustart angefordert hat.
          Und falls Punkt 2 gemeint ist, dann muss man ja im Applikationsprogramm die neue Methode aufrufen, also das Applikationsprogramm anpassen. Aber wenn man das machen muss, kann man auch Parameter einführen, die den User bestimmen lassen, ob und wann beim Startup gelesen werden soll.

          Bisher hab ich im OpenKNX-Team die Meinung vertreten, dass man das I-Flag möglichst nicht nutzen sollte, weil es keinerlei Vorteile bringt. Aber vielleicht steckt ja noch irgendwas dahinter, dass ich einfach nur nicht durchdrungen habe?

          Gruß, Waldemar
          ​​​​​​​
          OpenKNX www.openknx.de

          Kommentar


            #20
            Hallo Waldemar,

            aus 3/5/1
            image.png

            Gemäß 3/4/1 3.3.2​ sendet dann der Group Object Server in der AIL das GroupValue_Read (und sollte dann doch auch bereit sein, die Response zu empfangen). Wann genau der GO Server soweit "freigegeben" ist, um das zu tun, könnte durchaus von der Applikation beeinflusst werden. So etwas ist typischerweise nicht spezifiziert, da implementierungsabhängig.

            Davon abgesehen könne ein random delay beim Starten sehr sinnvoll sein, damit nicht bei Busspannungswiederkehr alles geflutet wird.

            Gruß, Klaus
            Zuletzt geändert von Klaus Gütter; 10.02.2025, 06:46.

            Kommentar


              #21
              Zitat von mumpf Beitrag anzeigen
              Heeres Ziel, viel Spaß damit (ist nicht ironisch gemeint, ist wirklich anspruchsvoll und macht Spaß). Ich hab das mit Standardgeräten nicht geschafft und u.A. deswegen das OpenKNX-Logikmodul gemacht .
              So, ich habe ich mir eine Matrix erstellt (in LO Calc) und alle Geräte in den Spalten der Zeile 1 und ebenfalls in der ersten Zelle der Spalte A eingefügt. Dann habe ich "1" für "Muss vor dem Gerät in der Spalte" starten und "-1" für "Muss nach dem Gerät in der Spalte" starten in die Schnittmengenzelle eingetragen.

              Der Code, um die Startreihenfolge (Gruppen der Geräte, die jeweils gleichzeitig starten können) und Reihenfolge der Gruppen habe ich mir von ChatGPT erstellen lassen, s.u.

              Jetzt habe ich meine geordnete Liste und vorher die tatsächliche Startdauer der einzelnen Geräte gemessen. Mal gucken, ob ich damit was optimiert bekommen habe 😁🤩


              Code:
              # -*- coding: utf-8 -*-
              import pandas as pd
              
              def read_csv_and_build_graph(csv_file):
                  """
                  Liest die CSV-Datei ein und erstellt einen gerichteten Graphen.
                  
                  Da die Matrix symmetrisch ist, wird für jedes Geräte-Paar (i, j) mit i < j sowohl
                  die obere rechte als auch die untere linke Hälfte ausgewertet:
                    - Falls df[i,j] == 1 oder df[j,i] == -1, dann muss das Gerät in Zeile i vor
                      dem Gerät in Spalte j starten.
                    - Falls df[i,j] == -1 oder df[j,i] == 1, dann muss das Gerät in Zeile j vor
                      dem Gerät in Spalte i starten.
                  Werte 0, 2, -2, 99 und -99 werden ignoriert.
                  """
                  df = pd.read_csv(csv_file, index_col=0)
                  devices = list(df.index)
                  n = len(devices)
                  
                  # Graph: Jeder Knoten (Gerät) hat eine Liste von direkten Nachfolgern.
                  graph = {device: [] for device in devices}
                  # In-Degree: Anzahl der eingehenden Kanten (harte Abhängigkeiten)
                  in_degree = {device: 0 for device in devices}
                  
                  for i in range(n):
                      for j in range(i+1, n):
                          try:
                              value_upper = int(df.iloc[i, j])
                          except Exception:
                              value_upper = None
                          try:
                              value_lower = int(df.iloc[j, i])
                          except Exception:
                              value_lower = None
                          
                          dependency = None
                          # Prüfe, ob (i, j) die Abhängigkeit signalisiert: Gerät[i] vor Gerät[j]
                          if (value_upper == 1) or (value_lower == -1):
                              dependency = (devices[i], devices[j])
                          # Alternativ: Prüfe, ob (i, j) das umgekehrte Signal liefert: Gerät[j] vor Gerät[i]
                          elif (value_upper == -1) or (value_lower == 1):
                              dependency = (devices[j], devices[i])
                          
                          if dependency:
                              src, tgt = dependency
                              if tgt not in graph[src]:
                                  graph[src].append(tgt)
                                  in_degree[tgt] += 1
                  return graph, in_degree
              
              def compute_start_groups(graph, in_degree):
                  """
                  Ermittelt die Startgruppen mittels topologischer Sortierung.
                  
                  Alle Geräte ohne eingehende Kanten (In-Degree == 0) werden gruppiert.
                  Falls zyklische Abhängigkeiten vorliegen, bleiben einige Geräte übrig.
                  
                  Rückgabe:
                    - groups: Liste von Gruppen (Listen von Geräten), die ohne Zyklus ermittelt wurden.
                    - remaining_nodes: Menge der Geräte, die in Zyklen liegen.
                  """
                  groups = []
                  nodes_remaining = set(graph.keys())
                  
                  while nodes_remaining:
                      zero_in_degree = [node for node in nodes_remaining if in_degree[node] == 0]
                      if not zero_in_degree:
                          break  # Es gibt keine Knoten mit In-Degree 0 → Zyklische Abhängigkeiten
                      groups.append(zero_in_degree)
                      for node in zero_in_degree:
                          nodes_remaining.remove(node)
                          for child in graph[node]:
                              in_degree[child] -= 1
                  return groups, nodes_remaining
              
              def tarjan_scc(graph, nodes):
                  """
                  Implementiert den Tarjan-Algorithmus zur Ermittlung stark zusammenhängender
                  Komponenten (SCC) im Subgraphen, der nur die Knoten aus 'nodes' enthält.
                  
                  Rückgabe: Eine Liste von SCCs, wobei jede SCC eine Liste von Geräten darstellt.
                  """
                  index = 0
                  indices = {}
                  lowlink = {}
                  stack = []
                  on_stack = set()
                  sccs = []
                  
                  def strongconnect(v):
                      nonlocal index
                      indices[v] = index
                      lowlink[v] = index
                      index += 1
                      stack.append(v)
                      on_stack.add(v)
                      
                      for w in graph[v]:
                          if w not in nodes:
                              continue
                          if w not in indices:
                              strongconnect(w)
                              lowlink[v] = min(lowlink[v], lowlink[w])
                          elif w in on_stack:
                              lowlink[v] = min(lowlink[v], indices[w])
                              
                      if lowlink[v] == indices[v]:
                          scc = []
                          while True:
                              w = stack.pop()
                              on_stack.remove(w)
                              scc.append(w)
                              if w == v:
                                  break
                          sccs.append(scc)
                  
                  for v in nodes:
                      if v not in indices:
                          strongconnect(v)
                  return sccs
              
              def main():
                  csv_file = "Geräteanlaufzeiten_chart_neu2.csv"  # CSV-Datei mit der Matrix
                  graph, in_degree = read_csv_and_build_graph(csv_file)
                  groups, remaining_nodes = compute_start_groups(graph, in_degree)
                  
                  # Erstelle eine Zuordnung: Gerät (gekürzte ID – nur die erste Zeile) -> Gruppe
                  device_to_group = {}
                  for group_num, group in enumerate(groups, start=1):
                      for device in group:
                          id_only = device.splitlines()[0]
                          device_to_group[id_only] = f"Gruppe {group_num}"
                  
                  # Falls Geräte übrig bleiben, liegen zyklische Abhängigkeiten vor.
                  cycle_details = []
                  if remaining_nodes:
                      sccs = tarjan_scc(graph, remaining_nodes)
                      cycle_index = 1
                      for scc in sccs:
                          # Eine SCC wird als Zyklus gewertet, wenn mehr als ein Gerät enthalten ist
                          # oder ein einzelnes Gerät einen Self-Loop hat.
                          is_cycle = (len(scc) > 1) or (len(scc) == 1 and scc[0] in graph[scc[0]])
                          if is_cycle:
                              label = f"Cycle {cycle_index}"
                              cycle_index += 1
                              cycle_ids = [n.splitlines()[0] for n in scc]
                              cycle_details.append((label, cycle_ids))
                              for device in scc:
                                  id_only = device.splitlines()[0]
                                  device_to_group[id_only] = label
                  
                  # Ausgabe der Geräte mit zugeordneter Gruppe
                  print("Gerät und zugeordnete Gruppe:")
                  print("{:<15}{}".format("ID", "Gruppe"))
                  for device, group in device_to_group.items():
                      print("{:<15}{}".format(device, group))
                  
                  # Ausgabe der Konfliktgruppen (Zyklen), falls vorhanden
                  if cycle_details:
                      print("\nKonflikt: Geräte in zyklischen Abhängigkeiten:")
                      for label, devices in cycle_details:
                          print(f"{label}: {', '.join(devices)}")
                  else:
                      print("\nKeine zyklischen Abhängigkeiten gefunden.")
              
              if __name__ == "__main__":
                  main()
              ​
              Zuletzt geändert von lopiuh; 13.02.2025, 21:32.

              Kommentar

              Lädt...
              X