mehrer Geräte unterstützen (Vererbung/Polymorhismus)



  • tagchen leute

    wie im titel erwähnt, möcht ich in meiner MFC App verschiedene Geräte unterstützen.
    Diese werden per serieller Schnittstelle konfiguriert.
    Sie unterscheiden sich im wesentlichen durch unterschiedliche Parameternummern.

    ich stell mir das so vor:

    auf Dialog Ebene kennt die App nur:

    Geraet* pG;   // p auf ein generisches oder abstraktes Gerät
    

    erst auf Klassenebene der Geräte müssen die Parameternummern dann bekannt sein.
    also alles geräte spezifische hat auf Dialog ebene nix zu suchen.

    idealerweise während der Laufzeit kann ich dann umschalten:

    Geraet A();
    Geraet B();
    
    pG = &A;  // bzw. pG = &B;
    

    ist dieses Kozept so weit ok? gibts spezielles zu beachten?

    jetzt zu den parameternummern:
    es ist ein satz von ca 300 Parametern, wobei die Nummern von Gerät zu Gerät nicht gleich sind.
    ziel ist es:

    WritePara(PAR01, iValue);
    

    aufzurufen.
    wie würdet ihr diese einbinden?

    ich sehe die Möglichkeit mit einer

    #define
    

    -liste
    oder ein

    enum Parameternummern{ ... };
    

    was passiert bei den #define wenn das gleiche Parameterkürzel redefiniert wird (Bsp: #define ID 33, und anderes Gerät #define ID 44 )? defines sind sozusagen global..
    bei den enums noch die frage ist ob ich sie dann innerhalb der klasse definieren oder ausserhalb? innerhalb der Klasse hab ich keine Probleme mit redefinition.
    das problem ist also dass einige Kürzel zwar gleich (bei beiden Geräten) sind aber auf eine andere Nummer zeigen. andere Kürzel gibt bei einem Gerät und beim anderen nicht.
    aus diesem Grund möchte ich für jedes Gerät eine eigene Parameter liste führen.

    wie seht ihr das? 😕

    gruss



  • mathias_f schrieb:

    ...

    Bevor ich darauf antworte einige Anmerkungen/Fragen:

    1. Wenn du eine abstrakte Basisklasse verwendest, nützt dir das ganze auch nur, wenn du die Daten über diese Schnittstelle verwenden kannst. Sprich: Klassenspezifische Konstanten brechen hier das Konzept wieder auf, sofern du keinen Mechanismus verwendest, wie du diese Kopplung reduzierst.

    Beispielsweise: Führen einer Liste aller möglichen Parameter zu einem Gerät (z.B. könnte ein Eintrag aus der Nummer, einer Kurzbeschreibung [Könnte beispielsweise ein Titel in der GUI stellen] und einer Langbeschreibung [Könnte beispielsweise einen Tooltip in der GUI stellen]). Diese werden zur Laufzeit ausgewertet, und über diese Einträge die Zuordnung zu Settern/Gettern gemacht (bzw. direkt in den Parameterelement halten).

    z.B. std::vector<GeraeteParameter>, std::map<int, GeraeteParameter>... die man dann auch über die Basisschnittstelle abfragen kann.

    2. define sollte man nur mit Bedacht einsetzen. Konstanten sollten eher mit const oder enum definiert werden. Beides kann auf Klassenebene geschehen.

    3. Das mit dem Austausch eines Gerätes zur Laufzeit ist durchaus Gang und Gäbe (Sprich grundsätzlich spricht nichts gegen das Konzept, sofern du dies über eine allgemeine Schnittstelle umsetzen kannst).

    cu André



  • danke für deine antwort.

    es wird möglich sein die schnittstellen gleich zu halten, indem ich eine Abstraktionsebene einfüge:
    d.h auf ebene der dialoge kommt bei

    int GetDeviceID()
    

    bei jeder abgeleiteten klasse ein int zurück.

    wo es sich unterscheidet:
    funktionskörper GeraetA::GetDeviceID() und funktionskörper GeraetB::GetDeviceID(). Denn dort sind andere Parameternummern nötig.
    es wird also GeraetA::readPara(NR_ID_GERAET_A) bzw. GeraetB::readPara(NR_ID_GERAET_B) aufgerufen.
    bisher wars so, dass im Dialog Code, gerätespezifisch readPara() aufgerufen wurde.

    die möglichkeit mit std:vector<> habe ich tatsächlich noch nicht in betracht gezogen.
    habe ich das richtig verstanden?:
    jede Geräte Klasse hätte eine

    typedef std::vector<vParameter> vParameterListe
    

    wobei:

    typedef std::vector<int, CString, CString> vParameter;
    

    mit Nummer, Kürzel, längere Beschreibung.

    habe das gefühl der vector ist zuviel des guten. brauche keinerlei dynamik..
    Probleme seh ich v.a wie initialisier ich das ganze und wie komm ich vom kürzel auf die nummer ohne durch den vec zu loopen?

    andere möglichkeit Klasse Parameterliste:
    http://www.iag.uni-hannover.de/~bothmer/strudel/source/Parameterliste.cpp
    scheint mir auch oversized..

    tendiere zu klassen-internen enums.
    was dabei unschön ist: beim intellisense wird mir einhaufen Params aufgeführt.
    lösung wäre zPARAM1, zPARAM2, .. damits alphabetisch eher ende steht.
    oder kann ichs anders einbinden?
    schön wäre so:

    pGeraet->Parameterliste.PARAM1..PARAM300
    

    was fürn objekt kann ch da nehmen für Parameterliste?

    g



  • mathias_f schrieb:

    d.h auf ebene der dialoge kommt bei

    int GetDeviceID()
    

    bei jeder abgeleiteten klasse ein int zurück.

    Wenn dieses int wieder zur Sonderfallbehandlung (wie switch-Blöcken oder anderen Masken) führt, hast du nichts gewonnen.

    mathias_f schrieb:

    die möglichkeit mit std:vector<> habe ich tatsächlich noch nicht in betracht gezogen.
    habe ich das richtig verstanden?: jede Geräte Klasse hätte eine

    typedef std::vector<vParameter> vParameterListe
    

    wobei:

    typedef std::vector<int, CString, CString> vParameter;
    

    mit Nummer, Kürzel, längere Beschreibung.

    habe das gefühl der vector ist zuviel des guten. brauche keinerlei dynamik..
    Probleme seh ich v.a wie initialisier ich das ganze und wie komm ich vom kürzel auf die nummer ohne durch den vec zu loopen?

    Ich bin ein Mensch der gerne Sonderbehandlungen gegen 0 tendieren lässt. Das Ideal wäre also, wenn ich unabhängig von verwendeten Gerät die gleiche Maske, die gleiche Logik etc. verwendet werden kann. Ob dies in deinen Fall möglich ist kann ich nicht sagen. Ich tendiere persönlich aber - wenn möglich - dazu Sonderfälle möglichst an einer Stelle zu behandeln und nicht in die UI etc. durchzuschleifen.

    Ich weiß wie gesagt weder wie deine Maske aufgebaut ist, noch welche Parameterwerte zulässig wären. Gehen wir vom Ideal aus: Die Parameter sind alle vom gleichen Typ (wenn auch anders Nummeriert), und zudem werden diese Listenartig bearbeitet. Nach meinen Konzept brauch die Maske die konkreten Parameter selbst garnicht zu kennen, alle Informationen werden aus der "Geräteschnittstelle" ausgelesen (Und nur das konkrete Gerät kennt als einziges seine Interna).

    Sprich: Das konkrete Gerät initialisiert - da auf dieser Basis auch die Daten bekannt sind - eine Parameterliste. Die Maske lädt nun über die Schnittstelle die Parameter aus, iteriert über diese und merkt sich zu jedem Eintrag die "id". Texte wie Beschreibungen etc. liest sie ebenfalls aus eben diesen Einträgen.

    Je nach dem wie häufig du die Maske öffnest würde ich die Parameterinformationen und die Parameterwerte entweder zusammenschmeißen oder trennen (Informationen statisch - da pro Gerät nur einmal nötig, Parameterwertse selbst wiederum immer auf Objektbasis).

    Beispiel (Weder überprüft noch sonderlich hübsch):

    #include <string>
    #include <map>
    #include <vector>
    
    class DeviceParamInfo {
      private:
        int paramNo;
        std::string name;
        std::string description;
        int standardValue;
      public:
        DeviceParam(int paramNo, std::string const & name,
          std::string const & description)
        : paramNo(paramNo),
          name(name),
          description(description),
          standardValue(standardValue);
        {}
    
        int GetParamNo() const
        { return paramNo; };
        std::string GetName() const
        { return name; };
        std::string GetDescription() const
        { return description; };
        int GetStandardValue() const
        { return standardValue; }
    };
    
    class IDevice
    {
      public:
        virtual ~IDevice() {};
        virtual std::vector<DeviceParamInfo> const & GetParams() const = 0;
        virtual void SetValue(int paramNo, int value) = 0;
        virtual int GetValue(int paramNo) = 0;
    };
    
    class DeviceA : public IDevice
    {
      private:
        std::map<int, int> values;
        static std::vector<DeviceParamInfo> const & Params()
        {
          static std::vector<int, DeviceParamInfo> params;
          if(params.empty()) {
              // Dies ist nun z.B. Gerätespezifisch
              params.push_back(DeviceParamInfo(1, "X1", "X1blabla", 0));
              params.push_back(DeviceParamInfo(2, "X2", "X2blabla", 0));
              params.push_back(DeviceParamInfo(3, "X3", "X3blabla", 20));
          }
          return params;
        }
      public:
        DeviceA()
        : values()
        {
          std::vector<DeviceParamInfo> const & params = Params();
          BOOST_FOREACH(DeviceParamInfo const & param, params)
              values[param.GetParamNo()] = param.GetStandardValue();
        }
    
        virtual std::vector<int, DeviceParamInfo> const & GetParams() const
        { return Params(); }
        virtual void SetValue(int paramNo, int value)
        { values[paramNo] = value; } // Besser: Vor Setzen auf Existenz prüfen
        virtual int GetValue(int paramNo) = 0
        { return values[paramNo]; } // Besser: Vor Lesen auf Existenz prüfen
    };
    
    // PseudoCode
    // (In der Maske, ich nehme hier mal eine Tabellarische Liste an)
    // a) Parameter des konkreten Gerätes ermitteln [GetParams()]
    // b) Für jeden Parameter einen Listeneintrag machen (dort eine versteckte Zeile mit paramNo), mit dem Parameternamen/-beschreibung als Read only-Feld, und dem Wert zum editeren.
    // Zum zurückschreiben:
    // a) Jede Tabellenzeile durchlaufen
    // b) Über hinterlegte paramNo den Parameter setzen
    

    cu André



  • danke für den guten vorschlag und deine ausführungen.

    werde es mir anschauen. ung ggf einbauen.

    Bem: (auch für andere leser)

    Bei DeviceParam gibts noch DeviceParamInfo, ich denke ist das gleiche gemeint (siehe konstruktor)

    weitere Möglichkeit hinsichtlich der Parameterliste:
    http://www.iag.uni-hannover.de/~bothmer/strudel/source/
    oder
    http://www.iag.uni-hannover.de/~bothmer/strudel/source/Parameterliste.cpp

    gruss



  • mathias_f schrieb:

    Bei DeviceParam gibts noch DeviceParamInfo, ich denke ist das gleiche gemeint (siehe konstruktor)

    Ja, Ich hatte nachträglich den Namen geändert. Vorher war im DeviceParam selbst der Parameterwert enthalten (und nun ist es eher Informativ "Welche Parameter existieren, und deren Beschreibung", daher die Umbenennung). Wie gesagt gibt es hierzu aber viele mögliche Umsetzungen, welche am sinnvollsten (und saubersten) ist, hängt von dem Anwendungsfall ab.


Anmelden zum Antworten