ENUM als Schleifenvariable gute oder schlechte Idee?

Zuviel Werbung?
-> Hier kostenlos registrieren
Ich bekomme es nicht Dynamisch in der PLC hin.


Wie machst du das?
Bisher ergibt es gar keinen Sinn.

Meine Idee ist folgende:
Es gibt Standardalarme, die um eine projektspezifische Quelle erweitert werden.
Meine Kollegen nehmen dann beispielsweise den Motor-Baustein und übergeben diesem per Schnittstelle die SourceInfo.

Wenn der sName (Translation Key) jedoch hardcoded übergeben werden muss, ergeben sich etliche Fehlermöglichkeiten.
Genau daran hängen wir gerade auch.

Und ich habe auch noch keine vernünftige Möglichkeit gefunden, außer die Strings hard codiert einzugeben und dann immer weiter nach unten durchzuschleifen.

Wenn man die Events weitergeben könnte, dann wären Schreibfehler ausgeschlossen.

Aber ich habe noch keine Möglichkeit gefunden auf den Key eines Events zuzugreifen. Die übersetzten Texte in der SPS zu bekommen, kein Problem, aber der Key geht irgendwie nicht.

Es gibt da den FB_TcDetail, der könnte es eventuell schaffen, aber der holt sich das scheinbar asynchron, dann muss man auf den evtl. auch noch warten. Außerdem hab ich es nicht auf die Reihe bekommen ihn zum laufen zu bringen.
 
Ich habe mein altes Projekt, das ich zur Supportanfrage genutzt habe, noch einmal herausgesucht (August 2025).

Da bin ich auch bis zum FB_TcDetail gekommen und ich erinnere mich: Damit bekommt man nicht den Translation Key. Was auch inkonsequent ist.

Beispiel:

Code:
IF xRequestText THEN

    xRequestText := FALSE; 

    HRESULT := fbRequestEventText.Request(
                                  eventClass:= Global.TC_EVENTS.TranslationEventClass.Door.uuidEventClass,
                                  nEventId:= Global.TC_EVENTS.TranslationEventClass.Door.nEventId,
                                  nLangId:= nLngId);
END_IF

Mit nLangId gibt man die Lcid ein (1031, 1033).

Meine Idee war, einfach „0” zu übergeben um den Key zu erfragen.
Dann erhält man den Text jedoch einfach in „de-de”. :rolleyes:

Vielleicht liest das ja jemand bei Beckhoff und irgendwann machen sie ein Update davon.
Mitteilen werden sie es mir nicht. ;)

Workaround wäre es vll. eine Sprache zu nehmen die niemals vorkommt und dort den gleichen Text wie den Key einzugeben.
Aber das muss ich dann auch erstmal meinen Kollegen klar machen.

PS:
Tatsächlich handelt es sich um FB_RequestEventText und nicht um FB_RequestEventDetails.
Letzteren habe ich auch nicht zum laufen gebracht.
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
Hab grade noch einen Workaround gefunden, der vermutlich auch nicht optimal ist, aber das beste, das ich bisher gefunden habe:

Von der Event-Class mit den Sources (hat bei mir den Namen "RE_Sources") wird ja auch ein Enum angelegt:
1769689434148.png

Wenn dieser Enum noch das Attribut 'to_string' hätte wäre schon alles gut, hat er aber nicht.
Wenn man sich aber einen eigenen Enum im Projekt anlegt, dann kann man sich die Definition rüberkopieren (ja, manuell) und den eigenen Enum mit dem Passenden Attribut versehen.
1769689618643.png

Im Alarmhandler dann über die Funktion "TO_STRING" in den gewünschten String umwandeln:
1769689697602.png

Ja, ist etwas doof mit dem manuellen kopieren, aber man bekommt es wenigstens direkt in der Entwicklung mit. Wenn man in der Source-Liste einen neuen Eintrag erstellt, das kopieren vergisst, dann wird man direkt merken, dass man den neuen Eintrag nicht nutzen kann.
 
Hab grade noch einen Workaround gefunden, der vermutlich auch nicht optimal ist, aber das beste, das ich bisher gefunden habe:
Das ist ziemlich nah, an dem, was ich heute früh gezeigt hatte. Ich nehme die Vorteile des TC2-Eventloggers mit in den TC3-Eventlogger. Dadurch erübrigt sich die Problemstellung für mich. Möglicherweise habt ihr das übersehen, weil es der letzte Beitrag auf der Seite 2 ist und nahezu zeitgleich mit dem Nachfolgepost kam.
 
Das ist ziemlich nah, an dem, was ich heute früh gezeigt hatte. Ich nehme die Vorteile des TC2-Eventloggers mit in den TC3-Eventlogger. Dadurch erübrigt sich die Problemstellung für mich. Möglicherweise habt ihr das übersehen, weil es der letzte Beitrag auf der Seite 2 ist und nahezu zeitgleich mit dem Nachfolgepost kam.
Das Konzept passt nicht so recht zu unserem System, denn die Nummern müssen immer stimmen.
Das muss ich einigen Kollegen erklären, die zum Teil an ganz anderen Anlagentypen arbeiten.

Hinzu kommt, dass ich mich endlich von „Nummern” lösen und mit Namen arbeiten möchte.

Aber vielen Dank für den Vorschlag!

Das Merkwürdige ist ja:

Erweitert man einen Event mit .AddEventReferenceEx(*HierEventEinfügen*), dann wird das sofort sprachlich aufgelöst.
Bei der SourceInfo geht das aber nicht. Er braucht die GUID (oder uuid), die Event-ID und dann noch den Translation-Key.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Erweitert man einen Event mit .AddEventReferenceEx(*HierEventEinfügen*), dann wird das sofort sprachlich aufgelöst.
Bei der SourceInfo geht das aber nicht. Er braucht die GUID (oder uuid), die Event-ID und dann noch den Translation-Key.

Ich denke auch, dass das der richtige Weg ist. Man muss dann eben für jedes Gerät / Ort ein eigenes Event haben.

Dem SourceInfo.sName vergebe ich den InstanzPfad des Bausteins. So kann ich beim debuggen die Quelle schnell finden. Das muss der Bediener dann nicht unbedingt sehen.
 
Hinzu kommt, dass ich mich endlich von „Nummern” lösen und mit Namen arbeiten möchte.
Das kannst Du. Dann musst du das Konzept nur ein klein wenig ändern und die Namen je Instanziierungsebene und -tiefe miteinander verketten.
Also, von meinem Beispiel ausgehend:

'System'
'System.Remote'
'System.Environment'
'System.EtherCATDiagnostics.1'
'System.EtherCATDiagnostics.2'
'System.EtherCATDiagnostics.3'
'LoadAreaA'
'LoadAreaA.ProcessCtrl'
'LoadAreaA.LoadHandling'
'LoadAreaB'
'LoadAreaB.ProcessCtrl'
'LoadAreaB.LoadHandling'

Ob mit ohne ohne Punkte, spielt keine Rolle. So bleibst Du flexibel für echte Objektorientierung über mehrere Ebenen.

Ich denke auch, dass das der richtige Weg ist. Man muss dann eben für jedes Gerät / Ort ein eigenes Event haben.
Das war die Schwäche des TC2-Eventloggers.

Denke objektorientiert: Wenn Du ein Gerät 50x hast, dann braucht Du im TC3 Eventlogger jede Meldung nur einmal anlegen. Im TC2 Eventlogger musstest Du für jede Meldung 50 Meldungen anlegen. Lediglich auf der Code-Ebene musste man das nicht.
Individualisieren kannst Du dann über Felder (BMK's und Werte) und die Source-Id (Gerätebezeichnung).
 
Zuletzt bearbeitet:
Das war die Schwäche des TC2-Eventloggers.

Denke objektorientiert: Wenn Du ein Gerät 50x hast, dann braucht Du im TC3 Eventlogger jede Meldung nur einmal anlegen. Im TC2 Eventlogger musstest Du für jede Meldung 50 Meldungen anlegen. Lediglich auf der Code-Ebene musste man das nicht.
Individualisieren kannst Du dann über Felder (BMK's und Werte) und die Source-Id (Gerätebezeichnung).

Ich kenne den TC2-Eventlogger nicht aber ich denke, dass man da für jede Fehlermeldung ein eigenes Event brauchte. Beim TC3 brauche ich ja nur noch die Herkunft der Meldung.

Wenn ich also einen Zylinderbaustein mit 10 Meldungen habe, brauche ich für jeden Zylinder noch mal ein Event indem dann der Textverweis auf die Übersetzung z.B. "Hub-Zylinder hinten rechts" steht
So hat man dann nicht 100 Events mit 100 Eventtexten zum übersetzen die sich dann nur am Ort unterscheiden, sondern 20 Events (10 Events für die Meldungen und je 10 Events für den Ort). Man muss eben bei den Fehlermeldungen dann die Platzhalter nutzen und dort den Ort einsetzen.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Man muss eben bei den Fehlermeldungen dann die Platzhalter nutzen und dort den Ort einsetzen.
Ja, dafür ist die SourceInfo-Class, weil die Übersetzbar ist. Felder eignen sich dafür nicht, die haben andere Aufgaben. Irgendwie habe ich trotz gründlicher Recherche scheinbar immer noch nicht verstanden, was für ein Problem ihr genau habt.
 
Ich habe den Tc3_EventLogger.FB_TcAlarm bei mir um eine Cyclic-Methode ergänzt und führe noch einige Aktionen im FB_Init aus.

Die Cyclic-Methode kümmert sich nur darum, dass der Event erstellt wird. Dort kann man dann auch klassisch den Binär-Alarm auslösen oder quittieren usw.

Das sieht jetzt wie folgt aus:

Code:
fbEvent.Cyclic(
    xRaise:= FALSE,
    xAck:= FALSE,
    stEventEntry:= Global.TC_EVENTS.AlarmEventClass.PUMP,
    stLocalizedSourceInfo:= Global.TC_EVENTS.SourceInfoEventClass.ORT_DER_PUMPE,
    sSourceInfoName:= 'ORT_DER_PUMPE');

Das Problem hierbei ist, dass wenn die SourceInfo übersetzbar sein soll, dann muss ich die SourceInfo als String übergeben. Da TwinCAT es mit dem Event alleine nicht schafft.

Schöner wäre folgender Code:

Code:
fbEvent.Cyclic(
    xRaise:= FALSE,
    xAck:= FALSE,
    stEventEntry:= Global.TC_EVENTS.AlarmEventClass.PUMP,
    stLocalizedSourceInfo:= Global.TC_EVENTS.SourceInfoEventClass.ORT_DER_PUMPE);

Hierbei muss man nichts doppelt pflegen und erhält vom Compiler Änderungen an jeder EventClass um die Ohren geworfen.
Wenn sich bei der aktuellen Variante der Key ändert (aus welchem Grund auch immer), bekommt man das nicht einmal mit.
 
Hierbei muss man nichts doppelt pflegen und erhält vom Compiler Änderungen an jeder EventClass um die Ohren geworfen.
Wenn sich bei der aktuellen Variante der Key ändert (aus welchem Grund auch immer), bekommt man das nicht einmal mit.
Ich denke, Du hast Dich etwas verrannt.

Da Du im gesamten Projekt nur eine einzige SourceInfo-Eventclass brauchtst, kannst Du die doch fest an stLocalizedSourceInfo verdrahten. Nur die sSourceInfoName musst Du individualisieren, und die kannst Du, wenn Du objektorientiert vorgehst, einmal als Parameter an Deinem Objekt "Pumpe" festlegen.

Also:
C-ähnlich:
fbPumpe(
    in_Source := 'ORT_DER_PUMPE'
   ...
);

Und dann innerhalb des Objektes an jedem einzelnen Event:

C-ähnlich:
fbEvent.Cyclic(
    xRaise:= FALSE,
    xAck:= FALSE,
    stEventEntry:= Global.TC_EVENTS.AlarmEventClass.PUMP,
    stLocalizedSourceInfo:= Global.TC_EVENTS.SourceInfoEventClass,
    sSourceInfoName:= in_Source);

Deine SourceInfoEventClass kannst Du Projektweit einsetzen.

Allerdings muss ich einschränken, dass ich nicht genau weiß, wie Du stLocalizedSourceInfo und sSourceInfoName in Deiner Cyclic-Methode verwendest. Ich habe dazu nur Vermutungen.

PS. Ich entwerfe auch Architekturen, die jede Menge Zeit einsparen können.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
fbEvent.Cyclic( xRaise:= FALSE, xAck:= FALSE, stEventEntry:= Global.TC_EVENTS.AlarmEventClass.PUMP, stLocalizedSourceInfo:= Global.TC_EVENTS.SourceInfoEventClass, sSourceInfoName:= in_Source);
Deine SourceInfoEventClass kannst Du Projektweit einsetzen.

Wie reagiert hier den Compiler in Bibliotheken, wenn es die Klasse noch gar nicht gibt? Bspw. wissen Komponentenbilbiotheken ja noch nicht über die Struktur der späteren Maschine. Man könnte zwar als Konvention einen einheitlichen Namen der Eventclass festlegen, aber dennoch gibt es die Eventclass (noch) nicht in der Bibliothek...
 
Wie reagiert hier den Compiler in Bibliotheken, wenn es die Klasse noch gar nicht gibt? Bspw. wissen Komponentenbilbiotheken ja noch nicht über die Struktur der späteren Maschine. Man könnte zwar als Konvention einen einheitlichen Namen der Eventclass festlegen, aber dennoch gibt es die Eventclass (noch) nicht in der Bibliothek...
Bisher musste ich eine Bibliothek nicht compilieren, ich habe die referenziert und erst dann wurde das Gesamt-Projekt übersetzt. Und erst dann wurden Bibliotheksfehler zu einem Problem.
Ist das jetzt nur eine Vermutung von Dir? Oder bist Du auf das Problem schon konkret gestoßen?

Ich kenne das Problem mit fehlenden Eventklassen und bediene mich eines Tricks:

C-ähnlich:
    {IF defined (UseEventLoggerV2)}
    fbLog.ig_EventClass        :=     TC_EVENT_CLASSES.FB_System_EventClass;
    {END_IF}
    fbLog(
        in_Port                := 110,
        in_SourceId            := in_EvLogSourceId,
        in_EvLogPriority    := in_EvLogPriority,
        in_NumOfEvents        := cnMaxLogEvents,
        ib_UnLockAlarms        := stErrors.bShow,
        ip_EventData        := ADR(astEventData),
        );

Der ist aus der Tatsache entstanden, dass mein Code sowohl den TC2 als auch den TC3-Eventlogger umschaltbar unterstützt. Ich kann also mit Software-Updates jederzeit zurück in ältere Anlagen, die mit der Version 4024 laufen.
 
Ist das jetzt nur eine Vermutung von Dir? Oder bist Du auf das Problem schon konkret gestoßen?

Erstmal nur eine Vermutung rein vom Lesen. Ich komme aber auch nicht zeitnah zum testen.

Eventuell könnte man über den Konstruktor den Klassennamen übergeben und diesen mit einem Standardwert belegen, sodass dieser in der Anwendersoftware nicht angegeben werden muss, aber angepasst werden kann. Oder über eine Parameterliste in der Bibliothek...
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich denke, Du hast Dich etwas verrannt.

Da Du im gesamten Projekt nur eine einzige SourceInfo-Eventclass brauchtst, kannst Du die doch fest an stLocalizedSourceInfo verdrahten. Nur die sSourceInfoName musst Du individualisieren, und die kannst Du, wenn Du objektorientiert vorgehst, einmal als Parameter an Deinem Objekt "Pumpe" festlegen.

Also:
C-ähnlich:
fbPumpe(
    in_Source := 'ORT_DER_PUMPE'
   ...
);

Und dann innerhalb des Objektes an jedem einzelnen Event:

C-ähnlich:
fbEvent.Cyclic(
    xRaise:= FALSE,
    xAck:= FALSE,
    stEventEntry:= Global.TC_EVENTS.AlarmEventClass.PUMP,
    stLocalizedSourceInfo:= Global.TC_EVENTS.SourceInfoEventClass,
    sSourceInfoName:= in_Source);

Deine SourceInfoEventClass kannst Du Projektweit einsetzen.

Allerdings muss ich einschränken, dass ich nicht genau weiß, wie Du stLocalizedSourceInfo und sSourceInfoName in Deiner Cyclic-Methode verwendest. Ich habe dazu nur Vermutungen.

PS. Ich entwerfe auch Architekturen, die jede Menge Zeit einsparen können.

Die SourceInfo möchte die EventClass-GUID/UUID, die EventID und den Key. So habe ich es zumindest verstanden.

Wenn es sich allerdings um die EventCLASS GUID/UUID und die ID handelt, hast du recht. Dann könnte man das global definieren und einmal verwenden und nur noch die Keys übergeben.

Das fesselt mich aber zu sehr an einem festen Konstrukt, sodass es genau diese EventClass (z.B. LocalizedSourceInfo) geben muss.

Meine Erfahrung:
Erzwinge ich diesen festen Aufbau, dann wird er falsch benutzt oder die Kollegen nutzen ihn überhaupt nicht und entwickeln etwas Eigenes.
Die Gründe dafür können vielfältig sein. (Baustelle, Zeitmangel, Workaround ...)

Das Problem ist nicht das Verschachteln von LocalizedEventClass + Event, sondern der hardcodierte Key.

Der Compiler kann nicht prüfen, ob es diesen Key überhaupt gibt, er schluckt ihn einfach.

Und im Zweifel funktioniert die Übersetzung nicht – fertig. Das ist das Problem.

PS:
Wenn ich mit .AddEventReferenceEx() eine Referenz zu einem anderen Event hinzufüge, löst er es sprachlich korrekt auf.

Ich benötige nicht einmal einen Translation Key! Falls es das Event, das ich referenzieren möchte, nicht mehr gibt, sagt mir der Compiler das.

Vielleicht hat ja jemand in der LocalizedSourceInfo herum gebastelt oder der Übersetzer hat etwas versaubeutelt. Auf jeden Fall sehe ich beim Kompilieren, dass es nicht funktionieren kann.

Die SourceInfo verhält sich halt ganz anders. Der Key muss nicht existieren, das ist ihm egal.


Für mich sehe ich gerade folgende zwei Möglichkeiten:

Entweder ich einige mich darauf, dass der Ort der Meldung dem eigentlichen Eventtext hinzugefügt wird. Das funktioniert immer und der Compiler schimpft, wenn nicht.

Alternativ mache ich es technisch richtig und trage den Ort in SourceInfo ein. Allerdings muss ich mich darum kümmern, dass der Key existiert. Das muss ich gegebenenfalls prüfen. Mir fehlt gerade die Kreativität, um zu überlegen, wie ich das gescheit automatisieren kann.
Ich möchte mich nicht auf eine stumpfe Regel wie „Halt dich ans Konzept und dann geht es” verlassen. Es soll schon reproduzierbar bleiben.
 
Zuletzt bearbeitet:
Zurück
Oben