Tcopen: Konventionen

Erstellt am 30. Okt. 2020  ·  31Kommentare  ·  Quelle: TcOpenGroup/TcOpen

Konventionen Dokument hier

Bitte beteiligen Sie sich an der Diskussion unten

  • Lassen Sie uns die Diskussionen hier behalten, um sie nachzuverfolgen.
  • Schnelle Chats hier:TcOpen Slack

  • [ ] Namenskonventionen für Variablen (VAR, VAR_INPUT, VAR_OUTPUT, VAR_IN_OUT, VAR_INST, TEMP)

  • [ ] Namenskonvention für Methoden
  • [ ] Namenskonvention für Eigenschaften
  • [ ] Namenskonvention für Bausteine ​​(FB, FC, PRG etc.)
discussion

Hilfreichster Kommentar

Ich hätte einen Vorschlag zu Arrays. Es gibt eine formale Anforderung für den Inxton-Compiler, der nur 0-basierte Arrays trans-stapelt. Der Grund ist, Verwirrung bei der Verwendung in C# zu vermeiden.

_array : ARRAY[0..10] OF BOOL; // Transstapel
während
_array : ARRAY[1..10] OF BOOL; // trans-stapelt nicht

Irgendwelche Kommentare dazu?

Alle 31 Kommentare

@mark-lazarides @Roald87 @philippleidig @jozefchmelar lasst uns die Diskussion über Konventionen hier fortsetzen... locker für ein kurzes Gespräch; Diskussionen hier, um die Aktivität zu verfolgen

  • Meiner Meinung nach sollten Eigenschaften wie folgt definiert werden "IsEnabled". Der Name selbst sollte bereits angeben, um welche Art es sich handelt.

  • Ich mag den Rückgabewert der Methode als boolean. Komplexere Datentypen finde ich ungeeignet, weil sie extern instanziiert oder per Referenz zurückgegeben werden müssen.

  • Die Vererbung jeder Basisklasse von "fbComponent" ist für die Verwendung von Inxton oder tc.prober erforderlich?

  • Bei der Typenbezeichnung bin ich persönlich etwas radikal und lasse Präfixe generell weg. Außer Schnittstellen, Referenzen und Zeigern.
    z.B

    Geben Sie Benennung ein


| Blocktyp | Notation | Präfix | Beispiel |
| :------------- | :--------- | :------------ | :------------------------------------------------------------- -- |
| FB/CLASS-Name | PascalFall |Nein | Cyclinder |
| ENUM-Typname | PascalFall |Nein | MachineState.Start |
| INTERFACE-Name | PascalFall | I | ICyclinder |
| Funktionsname | PascalFall |Nein | Add() |
| STRUCT-Name | PascalFall | Nein | Data |
| UNION-Name | PascalFall | Nein | Control |

@philippleidig

  • Meiner Meinung nach sollten Eigenschaften wie folgt definiert werden "IsEnabled". Der Name selbst sollte bereits angeben, um welche Art es sich handelt.

Stimme vollkommen zu.

  • Ich mag den Rückgabewert der Methode als boolean. Komplexere Datentypen finde ich ungeeignet, weil sie extern instanziiert oder per Referenz zurückgegeben werden müssen.

Wir verwenden es wie beschrieben bei unseren Komponenten. Dies ist nützlich, wenn Sie den Status einer Sequenz steuern. In den allermeisten Fällen ist bool ausreichend. Manchmal wäre es schön, mehr Informationen über den Status der Methode zu haben ... aber dies würde eine breitere Diskussion erfordern (vielleicht eine fließende Syntax wie so etwas).

  • Die Vererbung jeder Basisklasse von "fbComponent" ist für die Verwendung von Inxton oder tc.prober erforderlich?

Nein. Dafür gibt es weder spezielle Anforderungen noch Inxton oder tc.prober. Wir verwenden es auf diese Weise. ComponentBase ist eine abstrakte Klasse, die einen öffentlichen Vertrag hat (manuelle Methode usw.), aber einige gemeinsame Funktionen für Komponenten implementieren kann. Ich bin kein großer Fan von Vererbung (ich bevorzuge Komposition), aber in diesem Fall hätte ich gerne eine Option für die Zukunft.

Wenn Sie in inxton alle Komponenten in einer Sammlung sammeln möchten, können Sie dies tun, wenn something is copmonent dafür ein Mechanismus vorhanden ist.

Dafür gibt es noch einen weiteren Grund. Wir arbeiten derzeit daran, unsere Basisbibliothek freizugeben, was einige Anforderungen an sie stellt. Ich hoffe, dass ich nächste Woche etwas dazu sagen kann. Um Ihnen mehr Details zu geben.

  • Bei der Typenbezeichnung bin ich persönlich etwas radikal und lasse Präfixe generell weg. Außer Schnittstellen, Referenzen und Zeigern.
    z.B

Geben Sie Benennung ein

Beispiel für Blocktyp-Notationspräfix
FB/CLASS-Name PascalCase Nein Cyclinder
ENUM-Typname PascalCase Nein MachineState.Start
INTERFACE-Name PascalCase I ICyclinder
Funktionsname PascalCase Nein Add()
STRUCT-Name PascalCase Nein Data
UNION-Name PascalFall Nr. Control

Ich bin auch kein Fan von Präfixen. Die Tabelle ähnelt dem System der Präfixe, die wir verwenden ... aber sollten wir uns erneut dafür entscheiden, das loszuwerden, würde mich das nur glücklich machen.

In den meisten Fällen sehe ich keinen Vorteil in der Verwendung von Präfixen. Dem Vorschlag von @philippleidig stimme ich zu.

Ich würde sagen, dass Zeiger und Referenz hier eine Ausnahme bilden.

Ich habe meine Konventionen in PR #5 vorgeschlagen

Elementbenennung und Typbenennung

Ich sehe keinen Vorteil in der Verwendung von Präfixen. Es hilft mir in keinster Weise.

Mitgliedsvariablen

Member-Variablen der Klasse (FB) sollten ausgeblendet sein und mit einem kleinen Namen beginnen
~PaskalVAR{Attribut 'ausblenden'}Trigger: BOOL;{Attribut 'ausblenden'}Zähler : INT;{Attribut 'ausblenden'}analogStatus : AnalogStatus;END_VAR~

@jozefchmelar

In den meisten Fällen sehe ich keinen Vorteil in der Verwendung von Präfixen. Dem Vorschlag von @philippleidig stimme ich zu.

Ich würde sagen, dass Zeiger und Referenz hier eine Ausnahme bilden.
👍
Ich habe meine Konventionen in PR #5 vorgeschlagen

Elementbenennung und Typbenennung

Ich sehe keinen Vorteil in der Verwendung von Präfixen. Es hilft mir in keinster Weise.
👍👍

Mitgliedsvariablen

Member-Variablen der Klasse (FB) sollten ausgeblendet sein und mit einem kleinen Namen beginnen

    VAR
        {attribute 'hide'}
        trigger : BOOL;
        {attribute 'hide'}
        counter : INT;
        {attribute 'hide'}
        analogStatus : AnalogStatus;
    END_VAR
  • Versteckte Variablen sind über Anzeigen nicht sichtbar. Wenn sie also auf der HMI nicht benötigt werden, können sie ausgeblendet werden. Aber wenn wir sie in der HMI sehen müssen, sollten wir das Attribut „Hide“ nicht verwenden.
  • Bei Tc3 wird die Groß-/Kleinschreibung nicht beachtet, sodass trigger (Variablenname) und Trigger (Eigenschaftsname) in Konflikt geraten würden. Wir müssen ihm wie vorgeschlagen _ voranstellen, denke ich

Volle Zustimmung 👍

Es gibt auch das Attribut "conditionalshow". Dies kann jedoch nur in Verbindung mit einer kompilierten Bibliothek verwendet werden.
https://infosys.beckhoff.com/deutsch.php?content=../content/1033/tc3_plc_intro/8095402123.html &id=7685156034373049758
Da ich davon ausgehe, dass wir eine offene Bibliothek bereitstellen werden, würde dies nur bedingt Sinn machen.

_ als Präfix für Member-Variablen ist notwendig, wie @PTKu sagte.

Generell halte ich mich gerne an die Namenskonventionen und Namenswahl von C#.

Ich mag den Rückgabewert der Methode als boolean. Komplexere Datentypen finde ich ungeeignet, weil sie extern instanziiert oder per Referenz zurückgegeben werden müssen.

@philippleidig was meinst du mit Rückgabewerten? In diesem Fall werden sie als Fehlerprüfungen verwendet? Normalerweise hängt der Rückgabewert von der Methode ab. CalculcateArea würde ein REAL `LREAL` zurückgeben.

Stimme der vorgeschlagenen Variablenbenennung voll und ganz zu!

Ich mag den Rückgabewert der Methode als boolean. Komplexere Datentypen finde ich ungeeignet, weil sie extern instanziiert oder per Referenz zurückgegeben werden müssen.

@philippleidig was meinst du mit Rückgabewerten? In diesem Fall werden sie als Fehlerprüfungen verwendet? Normalerweise hängt der Rückgabewert von der Methode ab. CalculcateArea würde ein REAL``LREAL zurückgeben.

@ Roald87 Die Idee wäre, dass die Methode einer Komponente, die eine Aktion ausführt, "true" zurückgibt, wenn die Aktion abgeschlossen ist (MoveToHome(), wenn der Home-Sensor / die Position erreicht ist). Dies verhindert bei Bedarf andere Rückgabetypen nicht.

Stimme der vorgeschlagenen Variablenbenennung voll und ganz zu!

Ich hätte einen Vorschlag zu Arrays. Es gibt eine formale Anforderung für den Inxton-Compiler, der nur 0-basierte Arrays trans-stapelt. Der Grund ist, Verwirrung bei der Verwendung in C# zu vermeiden.

_array : ARRAY[0..10] OF BOOL; // Transstapel
während
_array : ARRAY[1..10] OF BOOL; // trans-stapelt nicht

Irgendwelche Kommentare dazu?

Gleiches für TwinCAT HMI (TE2000)

Gleiches für TwinCAT HMI (TE2000)

Wäre sehr praktisch, wenn die Arrays tatsächlich mit dem HMI synchronisiert wären!

@philippleidig || @Roald87 würde jemand von euch PR-Konventionen über Arrays dann pls ... Ich mag es einfach, mehr Mitwirkende im Repo zu sehen :).

Ich hätte einen Vorschlag zu Arrays. Es gibt eine formale Anforderung für den Inxton-Compiler, der nur 0-basierte Arrays trans-stapelt. Der Grund ist, Verwirrung bei der Verwendung in C# zu vermeiden.

_array : ARRAY[0..10] OF BOOL; // Transstapel
während
_array : ARRAY[1..10] OF BOOL; // trans-stapelt nicht

Irgendwelche Kommentare dazu?

Aufgrund der Art und Weise, wie strukturierte Textschleifen funktionieren, ziehe ich es vor, SPS-Arrays mit einer Dimension von 1..X zu belassen. Dadurch ist der Code überall in der SPS besser lesbar. Ich denke, wir sollten Code schreiben, der auf der SPS immer attraktiv und wartbar ist. Wenn wir Shims benötigen, damit es mit Code-Bits von Drittanbietern besser funktioniert, können wir das separat verwalten.

// Declaration
NUMBER_OF_DRIVES : INT := 10;
drives  : ARRAY[1..NUMBER_OF_DRIVES] OF I_Drive;

// now in the code
FOR i := 1 to NUMBER_OF_DRIVES DO
   drives[i].SomethingCool();
END_FOR

// Compared to

// Declaration
drives : ARRAY[0..(NUMBER_OF_DRIVES -1) ] OF I_Drive;
// Code
FOR i := 0 to (NUMBER_OF_DRIVES -1) DO
   drives[i].SomethingCool();
END_FOR

Ich mag den Rückgabewert der Methode als boolean. Komplexere Datentypen finde ich ungeeignet, weil sie extern instanziiert oder per Referenz zurückgegeben werden müssen.

Ich denke, Methoden sollten das zurückgeben, was für die Methode angemessen ist. Der Name der Methode soll Ihnen helfen, das zu verstehen.

dh

IF NOT piece.PassesValidation() THEN
 LogError('Piece does not pass validation');
END_IF

// OR

IF sequence.Finished THEN
  axis.Disable(); // No return type necessary.
  state := WaitForAxisDisabled;
END_IF

@ Roald87 Die Idee wäre, dass die Methode einer Komponente, die eine Aktion ausführt, "true" zurückgibt, wenn die Aktion abgeschlossen ist (MoveToHome(), wenn der Home-Sensor / die Position erreicht ist). Dies verhindert bei Bedarf andere Rückgabetypen nicht.

Ich mag den Ansatz des wiederholten Aufrufens einer öffentlichen Methode wirklich nicht, bei der diese Methode die Funktionalität wiederholt ausführt, bis sie korrekt zurückgegeben wird. Es gibt einen einzigen Fall, den ich für akzeptabel halte (wenn es eine Methode vom Typ "Execute" auf einer Schnittstelle gibt, aber es gibt meiner Meinung nach auch bessere Möglichkeiten, damit dies funktioniert).

Die Probleme damit;

  • Sie können / erstellen Ausführungspfade für eine Klasse, die gleichzeitig aufgerufen werden könnte. Dh
atEnd :=  axis.GoToEnd();
atBeginning := axis.GoToBeginning();
  • Klassen sollten ihren Zustand intern verwalten. Methoden können verwendet werden, um Anforderungen und Änderungen an diesem Status vorzunehmen, und Eigenschaften können verwendet werden, um auf den Status zuzugreifen oder auch einen einfachen Status festzulegen.
  • Wie benennen Sie die Methode, damit klar wird, wie und warum sie auf diese Weise verwendet wird? Achse.GoToEndTrueWhenComplete()?
  • Was ist, wenn sich der zugrunde liegende Zustand der Klasse in einer Weise ändert, die die Art und Weise ändert, wie dieser Aufruf ausgeführt wird?
  • Rennbedingungen können einfacher generiert werden. Wenn wir wiederholt .Enable() für etwas aufrufen, aber einen Fehler für einen einzelnen Scan erhalten, kann .Enable() trotzdem aktiviert werden, indem der Ansatz "Weiterrufen, bis es fertig ist" verwendet wird. Dies sollte stattdessen .RequestEnable : BOOL sein, was angibt, ob die zugrunde liegenden Bedingungen zum Zeitpunkt der Anforderung korrekt sind (wodurch der aufrufende Code an diesem Punkt problemlos zurückgreifen kann). Wenn die Anforderung gestellt werden kann, kann der aufrufende Code .IsEnabled und .InError auf den Abschluss überwachen.

@philippleidig mit TwinCAT HMI nicht vertraut. Wie werden dort nicht 0-basierte Arrays behandelt?

@mark-lazarides

Ich denke, Methoden sollten das zurückgeben, was für die Methode angemessen ist. Der Name der Methode soll Ihnen helfen, das zu verstehen.
👍

Nebenläufigkeit und Race-Conditions sind alles vernünftige Bedenken. IMHO Diese Probleme sollten so weit wie möglich auf der Ebene der Komponenten angegangen werden, vor allem aber auf der Ebene der Koordination beim Verbrauch der Komponenten. Die Komponentenmethoden sollten aus ordnungsgemäß implementierten zustandscontrollerähnlichen Grundelementen (sei es einfache CASE, IF, ELSIF oder komplexere Sequenzer/Selektoren/Iteratoren) aufgerufen werden, die gleichzeitige Aufrufe von widersprüchlichen Methoden derselben Instanz einer Komponente verhindern würden .

So etwas sollte im Consumer-Code der Komponente verhindert werden
~atEnd := Achse.GoToEnd();atBeginning := axis.GoToBeginning();~

Das executing methods , das true zurückgibt, wenn es fertig ist, ermöglicht eine saubere deklarative Verwendung.

Was mir vorschwebt ist so etwas:

~~~
VAR
_state : INT;

END_VAR

FALL_Zustand VON
0:
IF(axis.MoveAbsolute(Position: 100.0)) THEN
_state := 1;
END_IF;
1:
IF(axis.MoveRelative(Position: 100.0)) THEN
_zustand := 2;
END_IF;
2:
IF(axis.MoveAbsolute(Position: 300.0)) THEN
_zustand := 3;
END_IF;
3:
_zustand := 0;
END_CASE
~~~

Dies könnte reduziert werden

~~~
VAR
_state : INT;

END_VAR

FALL_Zustand VON
0:
Warten (axis.MoveAbsolute (Position: 100.0), 1);
1:
Warten (axis.MoveRelative (Position: 100,0), 2);
2:
Warten (axis.MoveAbsolute (Position: 300.0), 3);
3:
Warten (true, 0);

END_CASE

==================================

METHODE Warten
VAR_INPUT
fertig : BOOL
nextState : INT;
END_VAR
WENN (erledigt) DANN
_state := nextState;

END_IF;

~~~
Bearbeiten: Ich gehe davon aus, dass die Komponente in einer einzelnen SPS-Aufgabe verwendet wird

Es schränkt das Konsumieren des Codes ein. Unsere Komponenten sollten nicht fehleranfällig sein, wenn der Verbraucher sie in einer falschen Reihenfolge verwendet. Sie sollten gut auf alle Interaktionen reagieren. Sie "funktionieren" nicht unbedingt (dh das Aufrufen res := axis.MoveTo(Position:=100); vor axis.Enable() würde nicht funktionieren), aber der verbrauchende Code sollte an allen Stellen genügend Informationen erhalten, um zu verstehen, wo das Problem liegt.

Es liest sich für mich auch immer noch nicht gut. Sie können axis.MoveAbsolute(syx) nicht lesen und verstehen, dass es zyklisch aufgerufen werden muss. Sie würden das nur verstehen, wenn Sie wüssten, dass unser idiomatischer Stil das von Ihnen verlangt, und an diesem Punkt denke ich, dass wir es versäumt haben, etwas sehr Brauchbares zu schaffen.

Ich würde auch sagen, dass unabhängig davon, ob Fehler ausgeschlossen werden können, sie immer noch wahrscheinlicher sind. Mit dem Ansatz des zyklischen Methodenaufrufs könnten Sie eine Methode erstellen, die prüft, ob der Status des Objekts aufgerufen werden kann, dann die zyklische Methode aufrufen und dann den anderen Status des Objekts überwachen, um sicherzustellen, dass in der Zwischenzeit nichts passiert ist. Oder Sie stellen die Anfrage, die Ihnen mitteilt, ob sie erfolgreich war oder nicht, und überwachen dann den Status auf Fertigstellung. Sie können dafür einen Rückruf als Teil der Anfrage registrieren, wenn Sie möchten, was ein anständiger OO-Ansatz dafür ist und mehr zyklische Anrufe / Abfragen reduziert.

@mark-lazarides richtig! I execute methods (nennen wir sie so) sollte keine zyklische Logik implementieren. Ich habe angenommen, was wir zuvor besprochen haben, dass wir sicherstellen, dass das, was auf zyklische Weise ausgeführt werden muss, entweder im Körper von FB oder in einer Cyclic -Methode platziert wird; das sollte an geeigneter Stelle im Consumer-Programm aufgerufen werden.

OK. Warum rufen wir die Methode also zyklisch auf? Ich denke immer noch, dass die ursprünglichen Argumente bestehen. Die Methode sollte einen Job erledigen. Entweder etwas anfangen (und deshalb den Erfolg davon melden) oder etwas bekommen (und es zurückgeben).

OK. Warum rufen wir die Methode also zyklisch auf? Ich denke immer noch, dass die ursprünglichen Argumente bestehen. Die Methode sollte einen Job erledigen. Entweder etwas anfangen (und deshalb den Erfolg davon melden) oder etwas bekommen (und es zurückgeben).

Keine Einwände Mark, wir müssen execute-Methoden nicht zyklisch aufrufen, aber es sollte kein Problem sein, wenn wir es tun.

Ich verstehe nicht, warum eine Methode das Ergebnis der Operation nicht zurückgeben konnte.

Ich denke, wir sollten uns einige komplexere Komponenten einfallen lassen und diese Ideen prototypisieren (pneumatischer Kolben ist für diese Diskussion nicht komplex genug). Ich denke, wir könnten mit Antrieb/Achse beginnen.

Einverstanden, Peter.

Aufgrund des Thread-Namens dachte ich, wir zielen darauf als Konvention ab! Entschuldigung, falls ich das falsch verstanden haben sollte.

Chris hat im Moment einen einfachen Achsenblock als PR - ich habe ihn kommentiert, aber es braucht mehr Augen darauf.

@mark-lazarides, keine Entschuldigung nötig Mark, wir sind hier, um frei zu diskutieren, ich werde morgen einen Blick auf PR werfen ...

@philippleidig @Roald87 @dhullett08 Diskussion über Komponentendesign auch hier

Ein paar zufällige Vorschläge aus den PLCopen-Dokumenten.

plcopen_coding_guidelines_version_1.0.pdf

Konstanten
Sollte in GROSSBUCHSTABEN geschrieben sein, damit sie leicht identifizierbar sind

Akzeptable Namenslänge
Mindestens 4 Zeichen, maximal 24 Zeichen?

Ein paar zufällige Vorschläge aus den PLCopen-Dokumenten.

plcopen_coding_guidelines_version_1.0.pdf

Danke für den Link

Konstanten
Sollte in GROSSBUCHSTABEN geschrieben sein, damit sie leicht identifizierbar sind

👍

Akzeptable Namenslänge
Mindestens 4 Zeichen, maximal 24 Zeichen?

Längere Namen sollten kein Problem sein, bis sie die Absicht ausdrücken, 24 Zeichen sollten ausreichen, aber ich würde die maximale Anzahl nicht einschränken. Zeichen. Zu kurze Namen sind in der Tat verdächtig, sie sollten länger als 4 Zeichen sein.

@Seversonic Ihre Bemerkungen wurden dem Dokument hinzugefügt ...

Diskussion Forts. hier #11

Ich mag deine Conventions wirklich nicht, aber ich finde dein Projekt ziemlich interessant!
Ich persönlich bevorzuge einen klassischeren Weg
FB_ fb-Funktionsblock
M_Add() Methode
P_Parameter Prop

Hallo, @PeterZerlauth und danke. Es ist etwas mühsam, sich auf die Konventionen zu einigen, da wir uns auf halbem Weg zwischen SPS und klassischem Software-Engineering befinden.

Hier ist die Umfrage vom frühen Beginn der Diskussionen:

TcOpen.Umfrage.Ergebnis.pdf

Darüber hinaus gab es hier und im Slack Channel eine Diskussion, vielleicht gibt es auch etwas in @dhullett08 TcOpen Repo.

Es gab ein allgemeines Gefühl (oder zumindest interpretiere ich es so), dass wir Präfixe aufgeben sollten, wenn sie keine nützlichen Informationen liefern oder die moderne IDE die Informationen bereitstellt, die wir in der Vergangenheit mit den Präfixen übermittelt haben.

Ich verstehe, dass es hier um persönliche Vorlieben geht, und es gibt wirklich keinen richtigen oder falschen Weg, dies zu tun. Wir müssen uns nur auf etwas einigen.

Abschließend wird die Diskussion hier fortgesetzt: https://github.com/TcOpenGroup/TcOpen/discussions/11

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen