Laden...

Gerätebestand visualisieren, von Geräten mit unterschiedlichen Eigenschaften/InterfaceVersionen

Erstellt von Tien1602 vor 11 Jahren Letzter Beitrag vor 11 Jahren 1.923 Views
T
Tien1602 Themenstarter:in
9 Beiträge seit 2011
vor 11 Jahren
Gerätebestand visualisieren, von Geräten mit unterschiedlichen Eigenschaften/InterfaceVersionen

Hallo Zusammen,

mal eine grundlegende Frage...

Gegeben sind Geräte weltweit verstreut mit verschiedenen Firmware Versionen.

Die Geräte können über USB mit dem Rechner kommuniziert.
Diese Kommunikation hat einen bestimmten Umfang an Kommandos, z.B. "ReadSerialNumber", "WriteVendorName", usw.
Der Umfang der Kommunikation ist mit einer InterfaceVersion gekennzeichnet, welche sich bei allen Geräten auf die gleiche Art auslesen lässt.

Es soll ein Tool in C# erstellt werden, welches die Daten der Geräte visualisiert und mit dem man bestimmte Einstellungen ändern kann.

Nun stellt sich mir die Frage, wie ich am besten das C# Tool erstelle, damit es mit allen im Feld verstreuten InterfaceVersionen umgehen kann.

Es muss zum einen die eigentliche Kommunikation angepasst werden (z.B. gibt es beim einen Gerät 10 Speichereinträge, beim nächsten nur 7, oder ein bestimmter Befehl existiert beim einen und beim anderen nicht)

Zum anderen muss die GUI entsprechend angepasst werden, d.h. es ist eventl. notwendig unterschiedliche Oberflächen zu erstellen.

Hat jemand so etwas schon mal gelöst und hätte ein paar Tipps.

Die wohl einfachste Möglichkeit wäre ein Programm, welches die InterfaceVersion ausliest und dann die für diese Anwendung entsprechende Toolversion startet.
Aber bestimmt gibt es elegantere Wege...

Herzlichen Dank für Eure Tipps
Timo

49.485 Beiträge seit 2005
vor 11 Jahren

Hallo Tien1602,

die spannende Frage ist doch, ob die InterfaceVersion nur Auswirklungen darauf hat, welche Daten jeweils vorhanden sind oder auch darauf, auf welche Weise die Daten zugegriffen wird.

Wenn sich nur der Umfang der Daten ändert, diese aber alle auf die gleiche Weise ausgelesen werden können, reicht im Grunde eine Methode zum Auslesen der Daten, die lediglich die Namen der auszulesenden Werte übergeben bekommt. Du bräuchtest also nur ein Dictionary, in dem der InterfaceVersion die Namen der vorhandene Datenfelder zugeordnet sind.

Anders sieht es aus, wenn die InterfaceVersion Einfluss auf den Code hat, der zum Auslesen verwendet werden muss. Das wäre dann vermutlich ein Fall für das Strategy-Pattern.

Ansonsten habe ich nicht verstanden, wo genau dein Problem liegt. Mehr im Auslesen, mehr in der internen Repräsentation der Daten, mehr in der Visualisierung. Es wäre schön, wenn du das noch weiter eingrenzen würdest.

herbivore

C
112 Beiträge seit 2009
vor 11 Jahren

Moinse,
also ich würde das in etwa folgendermaßen lösen:
Oberfläche mit WPF und exzessive Benutzung von Datenbindung,
Ein Masterinterface liest die passende version aus. Dann wird entschieden, welche konkreten Klassen geladen werden müssen.
Eine Klasse enthält eine Liste mit Instanzen einer Bedienelementklasse, die etwa so aussehen könnte:


public class Bedienelement{
 public Type Datentyp {get;set;}
 public string Display{get;set;}
 public object Value{get;set;}
}

An die GUI wird an ein ItemsControl diese Liste mit Bedienelementen gebunden, über einen TemplateSelector kannst Du auswählen, ob eine Checkbox, ein Textfeld oder was auch immer dargestellt werden muss, entscheidend dafür ist der Datentyp in deinem Bedienelement.
Die Klasse, die die Liste mit den Bedienelementen enthält, kann auf Änderungen bzw. Benutzeraktionen (Wert ändern, Speichern o.ä.) entsprechend reagieren. Oder anhand des Masterinterfaces entscheiden, welche Implementation zum Speichern, Ändern usw. geladen werden muss.
Das mal so als grober Denkansatz.

Grüße
Christian

T
Tien1602 Themenstarter:in
9 Beiträge seit 2011
vor 11 Jahren

Danke schon mal für die Antworten!

@herbivore
Zum einen der Hinweis, dass ich das Interface zw. Gerät und Rechner komplett selbst im Griff habe. D.h. ich arbeite gerade an der ersten InterfaceVersion. Wie sich die Sache entwickelt wird sich über die Zeit ergeben. Allerdings ist es schon denkbar, dass sich auch der Datenaufbau ändert, je nach Wünschen des Kunden. Z.B. geben wir eine Positionsinfo aus. Nun kann es sein, dass der Kunde irgendwann mal die Auflösung geändert haben will. Oder ein zusätzlichen Bit dazu kommt, ob das Signal gestört ist usw. Der Möglichkeiten sind viele...

D.h. es muss davon ausgegangen werden, das sowohl der Umfang, als auch die Art der Interpretation und damit der Code zw. Kommunikation und Darstellung sich von Version zu Version unterscheidet. Und dann natürlich auch die Darstellung.

Ich tendiere gerade zu einer ähnlichen Lösung wie es Christian vorgeschlagen hat. Allerdings weniger "intelligent". Ich würde einen WPF Framework aufbauen, welches die generellen Anteile (z.B. Connect zum Gerät usw.) abdeckt. Beim Connect wird die InterfaceVersion eingelesen und dann in Abhängigkeit davon in einem Frame Steuerelement die entsprechende GUI anzeigt, welche dann auf die InterfaceVersions typische DLL zugreift. D.h. es gibt je Version eine komplette Oberfläche und eine DLL.

Die Umschaltung würde ich über verschiedene Namespaces realisieren, so dass ich beim einführen einer neuen Version die Dateien "einfach" kopieren würde und nur den Namespace und die geänderte Funktionalität ändern müsste.

Ist halt wenig Modular und es gibt viele Codeanteile, die mehrfach vorkommen, aber so ist die Trennung der Versionen einfach und strikt. Einzig das Bugfixing wird dann aufwendig.

Grüße Timo

F
10.010 Beiträge seit 2004
vor 11 Jahren

Ist halt wenig Modular und es gibt viele Codeanteile, die mehrfach vorkommen, aber so ist die Trennung der Versionen einfach und strikt.

Was hat das eine mit dem anderen zu tun?

Nur weil du für jedes Interface ( oder jedes wirklich andere Interface ) eine eigene Klasse machst, heisst das ja nicht das Du Code duplizieren musst.
Wozu gibt es Vererbung und Hilfsklassen?

T
Tien1602 Themenstarter:in
9 Beiträge seit 2011
vor 11 Jahren

Ok, ich hab auch schon an Vererbung gedacht. Nur komm ich da irgendwie nicht auf eine Lösung. Eventl. kann mir ja einer von Euch die Augen öffnen.

Lassen wir mal die GUI Geschichte weg und betrachten nur die Datenhaltung:

Ich habe eine Klasse "ClsDeviceData", deren Instanz die ausgelesenen bzw. zu schreibenden Daten eines Gerätes speichert. Um auf die Daten später zugreifen zu können wird dieses Objekt in ein XML File serialisiert.
Um im XML eine Struktur aufzubauen und natürlich auch um den Zugriff etwas zu strukturieren besteht die Klasse "DeviceData" im wesentlichen nur aus weiteren Objekten, z.B. "ConfugurationData" oder "IdentificationData".

Beispielcode:



  [Serializable]
  public class ClsDeviceData
  {
    [XmlElement]
    public ClsIdData IdentificationData = new ClsIdData();

    [XmlElement]
    public ClsConfigurationData ConfigurationData = new ClsConfigurationData();

    // Hier könnten in späteren Versionen weitere Elemente dazu kommen.
   
  }
  

  [Serializable]
  public class ClsIdData
  {
    [XmlElement]
    public ClsIdDataContentDetail ManufacturingDate = new ClsIdDataContentDetail() 
      { ReadRequest = "2Bh,0Eh,04h,80h", WriteRequestPrefix = "2Bh,8Eh,80h" };

    // Hier könnte in anderen Versionen der initialisierte Request sich ändern.
    // Oder es könnten weitere Id Daten dazu kommen.

    [XmlElement]
    public ClsIdDataContentDetail SerialNumber = new ClsIdDataContentDetail() 
      { ReadRequest = "2Bh,0Eh,04h,81h", WriteRequestPrefix = "2Bh,8Eh,81h" };
    
    // [...]
  }
  
  
  [Serializable]
  public class ClsIdDataContentDetail
  {
    private string m_Content;
    private string m_ReadRequest;
    private string m_WriteRequestPrefix;

    [XmlElement]
    public string Content 
    { 
      get { return m_Content; } 
      set { m_Content = value; } 
    }

    [XmlIgnore]
    public string ReadRequest 
    { 
      get { return m_ReadRequest; } 
      set { m_ReadRequest = value; } 
    }

    [XmlIgnore]
    public string WriteRequestPrefix 
    { 
      get { return m_WriteRequestPrefix; } 
      set { m_WriteRequestPrefix = value; } 
    }
    
    public string ConvertToHexString()
    {
      // Konvertiert Inhalt von m_Content in einen HexString...
      return HexString
    }

    // Hier könnten neue Convert Funktionen dazu kommen, 
    // die Konvertierung könnte sich ändern oder es könnten 
    // weitere Elemente dazu kommen.  
  }


Wenn sich nie etwas ändert funktioniert das ja auch ganz gut.

Die Abhängigkeiten bzgl. InterfaceVersionen stecken aber nun verteilt in den verschiedenen Klassen.

Wie ich nun die "ClsDeviceData" Klasse (Wurzel) versionsspezifisch vererbe ist mir klar. D.h. ich erzeugte eine abgeleitete Klasse von "ClsDeviceData", benenne diese für die Representation der InterfaceVersion 2 als "ClsDeviceDataV2" und füge z.B. ein weiteres Objekt (=XML Element) hinzu.

Nun kann ich nach Auslesen der InterfaceVersion == 2 aus dem Gerät eine Instanz der korrekten Klasse "ClsDeviceDataV2" erzeugen und die Daten im korrekten Format speichern.

Soweit so gut, aber was mach ich, wenn für die InterfaceVersion 2 z.B. in der Klasse "ClsIdData" noch ein weiterer IdString hinzukommt?
Dann muss ich ja die "Innere" Klasse ebenfalls vererben - hm, ich muss dass mal probieren ob und wie das geht.

Aber vielleicht hat ja jemand von Euch einen Profi Tipp

F
10.010 Beiträge seit 2004
vor 11 Jahren

Codekonventionen für C# (C#-Programmierhandbuch)

Soweit so gut, aber was mach ich, wenn für die InterfaceVersion 2 z.B. in der Klasse "ClsIdData" noch ein weiterer IdString hinzukommt?

Dann leitest du eben IdData2 von IdData ab und weist sie zu.

T
Tien1602 Themenstarter:in
9 Beiträge seit 2011
vor 11 Jahren

So, ich hab das ganze mit Vererbung aufgebaut. Soweit so gut.
Ich kann wunderbar in den vererbten Klassen neue Elemente hinzufügen, diese serialisieren und deserialisieren.

Leider gibt es ein Problem wenn ich ein Element der Basisklasse überschreiben will so bekomme ich beim Serialisieren folgende Fehlermeldung:

Fehlermeldung:
Member ClsDeviceDataV2.Tools of type Device.ClsToolsV2 hides base class member ClsDeviceData.Tools of type Device.ClsTools. Use XmlElementAttribute or XmlAttributeAttribute to specify a new name.

Nach etwas Suche im Netz trifft vermutlich folgende Antwort auf mein Problem zu:

Member Hiding and XML Serialization

Wenn ich das richtig sehe müsste ich mir meine eigene Serialisierung schreiben.

Hm... Ich glaub ich nutze nur die untersten Objekte mit Veerbung und kopiere, falls sich was ändert doch Code... so langsam hab ich keine Zeit mehr.
Oder hat jemand noch eine Idee?

T
Tien1602 Themenstarter:in
9 Beiträge seit 2011
vor 11 Jahren

Um es vielleicht etwas besser zu verdeutlichen:

In Version1 wird folgende XML Datei per XML-Serialisierung geschrieben (natürlich mit Inhalten):


<?xml version="1.0"?>
<DeviceData>
  <Tools>
    <DiagTool />
    <MicroControllerFlasherInterface />
    <HallSensorInterface />
  </Tools>
  <MetaData>
    <SerializeTimeStamp>2012-06-29 15:40:43</SerializeTimeStamp>
    <XmlFileVersion>1</XmlFileVersion>
    <Comment>Comment</Comment>
  </MetaData>
  <IdentificationData>
    <VendorName />
    <CustomerNumber />
    <ProductVersion />
    <ManufacturingDate />
    <SerialNumber />
    <OrderNumber />
    <BootloaderIdentificationNumber />
    <BootloaderVersion />
    <ApplicationSoftwareIdentificationNumber />
    <ApplicationSoftwareVersion />
    <InterfaceVersion />
    <TestProgressIndicator />
  </IdentificationData>
  <ConfigurationData />
  <EventManagment>
    <SensorStatus>
      <SensorStatus>unknown</SensorStatus>
    </SensorStatus>
    <EventMemory Location="0">
      <EventStatus>unknown</EventStatus>
    </EventMemory>
    <EventMemory Location="1">
      <EventStatus>unknown</EventStatus>
    </EventMemory>
    <EventMemory Location="2">
      <EventStatus>unknown</EventStatus>
    </EventMemory>
    <EventMemory Location="3">
      <EventStatus>unknown</EventStatus>
    </EventMemory>
    <EventMemory Location="4">
      <EventStatus>unknown</EventStatus>
    </EventMemory>
    <EventMemory Location="5">
      <EventStatus>unknown</EventStatus>
    </EventMemory>
    <EventMemory Location="6">
      <EventStatus>unknown</EventStatus>
    </EventMemory>
    <EventMemory Location="7">
      <EventStatus>unknown</EventStatus>
    </EventMemory>
  </EventManagment>
  <MeasurementData />
</DeviceData>

Nun soll ein neues Tool dazu kommen. Die XML Datei sollte dann so aussehen:


<?xml version="1.0"?>
<DeviceData>
  <Tools>
    <EinNeuesTool>
      <Name>Ein neues Tool</Name>
    </EinNeuesTool>
    <DiagTool />
    <MicroControllerFlasherInterface />
    <HallSensorInterface />
  </Tools>
  <MetaData>
    <SerializeTimeStamp>2012-06-29 15:42:02</SerializeTimeStamp>
    <XmlFileVersion>2</XmlFileVersion>
    <Comment>Comment</Comment>
  </MetaData>
  <IdentificationData>
    <VendorName />
    <CustomerNumber />
    <ProductVersion />
    <ManufacturingDate />
    <SerialNumber />
    <OrderNumber />
    <BootloaderIdentificationNumber />
    <BootloaderVersion />
    <ApplicationSoftwareIdentificationNumber />
    <ApplicationSoftwareVersion />
    <InterfaceVersion />
    <TestProgressIndicator />
  </IdentificationData>
  <ConfigurationData />
  <EventManagment>
    <SensorStatus>
      <SensorStatus>unknown</SensorStatus>
    </SensorStatus>
    <EventMemory Location="0">
      <EventStatus>unknown</EventStatus>
    </EventMemory>
    <EventMemory Location="1">
      <EventStatus>unknown</EventStatus>
    </EventMemory>
    <EventMemory Location="2">
      <EventStatus>unknown</EventStatus>
    </EventMemory>
    <EventMemory Location="3">
      <EventStatus>unknown</EventStatus>
    </EventMemory>
    <EventMemory Location="4">
      <EventStatus>unknown</EventStatus>
    </EventMemory>
    <EventMemory Location="5">
      <EventStatus>unknown</EventStatus>
    </EventMemory>
    <EventMemory Location="6">
      <EventStatus>unknown</EventStatus>
    </EventMemory>
    <EventMemory Location="7">
      <EventStatus>unknown</EventStatus>
    </EventMemory>
  </EventManagment>
  <MeasurementData />
</DeviceData>

Um das zu erreichen müsste ich ja folgendes schreiben:


  [XmlRoot(ElementName = "DeviceData")]
  public class ClsDeviceDataV2 : ClsDeviceData
  {
    [XmlElement]
    public new ClsToolsV2 Tools = new ClsToolsV2();
  }


  [Serializable]
  public class ClsToolsV2:ClsTools
  {
    [XmlElement]
    public ClsTool EinNeuesTool = new ClsTool();
  }

Und genau dann gibt es die Fehlermeldung.
Kopiere ich den gesamten Code von V1 und erweitere ihn geht es natürlich - aber das ist nicht im Sinne des Erfinders.