Tcopen: Convenciones

Creado en 30 oct. 2020  ·  31Comentarios  ·  Fuente: TcOpenGroup/TcOpen

Documento de convenios aquí

Por favor, contribuya a la discusión a continuación.

  • Dejemos las discusiones aquí para el seguimiento.
  • Charlas rápidas aquí:TcOpen Slack

  • [ ] Convenciones de nomenclatura para variables (VAR, VAR_INPUT, VAR_OUTPUT, VAR_IN_OUT, VAR_INST, TEMP)

  • [ ] Convención de nomenclatura para métodos
  • [ ] Convención de nomenclatura para propiedades
  • [ ] Convención de nomenclatura para bloques (FB, FC, PRG, etc.)
discussion

Comentario más útil

Tendría una sugerencia acerca de las matrices. Existe un requisito formal para el compilador inxton que trans-pile solo matrices basadas en 0. El motivo es evitar la confusión cuando se usa en C#.

_array : ARRAY[0..10] OF BOOL; // trans-pilas
tiempo
_array : ARRAY[1..10] OF BOOL; // no trans-apila

¿Algún comentario sobre esto?

Todos 31 comentarios

@mark-lazarides @Roald87 @philippleidig @jozefchmelar mantengamos la discusión sobre las convenciones aquí... holgura para una charla rápida; discusiones aquí para realizar un seguimiento de la actividad

  • En mi opinión, las propiedades deben definirse de la siguiente manera "IsEnabled". El nombre en sí ya debería indicar de qué tipo es.

  • Me gusta el valor de retorno del método como booleano. Encuentro inapropiados los tipos de datos más complejos, porque tienen que ser instanciados fuera o devueltos por referencia.

  • ¿La herencia de cada clase base de "fbComponent" es necesaria para usar Inxton o tc.prober?

  • Cuando se trata de nombrar tipos, personalmente soy un poco radical y generalmente dejo de lado los prefijos. Excepto para interfaces, referencias y punteros.
    p.ej

    Nombre de tipo


| Tipo de bloque | Notación | Prefijo | Ejemplo |
| :-------------- | :--------- | :------------| :------------------------------------------------- -- |
| Nombre de FB/CLASE | Caso Pascal | No | Cyclinder |
| Nombre del tipo ENUM | Caso Pascal | No | MachineState.Start |
| INTERFAZ nombre | PascalCase | I | ICyclinder |
| FUNCIÓN nombre | Caso Pascal | No | Add() |
| ESTRUCTURA nombre | PascalCase | No | Data |
| Nombre de la UNIÓN | PascalCase | No | Control |

@philippleidig

  • En mi opinión, las propiedades deben definirse de la siguiente manera "IsEnabled". El nombre en sí ya debería indicar de qué tipo es.

Completamente de acuerdo.

  • Me gusta el valor de retorno del método como booleano. Encuentro inapropiados los tipos de datos más complejos, porque tienen que ser instanciados fuera o devueltos por referencia.

Lo usamos como se describe con nuestros componentes. Es útil cuando se controla el estado de una secuencia. En la gran mayoría de los casos, bool es suficiente. A veces sería bueno tener más información sobre el estado del método ... pero esto necesitaría una discusión más amplia (tal vez una sintaxis fluida como algo)

  • ¿La herencia de cada clase base de "fbComponent" es necesaria para usar Inxton o tc.prober?

No. No hay un requisito específico para eso ni Inxton ni tc.prober. Lo usamos de esta manera. ComponentBase es una clase abstracta que tiene algún contrato público (método manual, etc.), pero puede implementar algunas características comunes para los componentes. No soy muy partidario de la herencia (prefiero la composición), pero en este caso me gustaría tener alguna opción abierta para el futuro.

En inxton, si desea recopilar todos los componentes de una colección, puede hacerlo cuando something is copmonent existe un mecanismo para ello.

También hay otra razón para eso. Estamos trabajando estos días en el código abierto de nuestra biblioteca base, que tiene algunos requisitos al respecto. Espero ser capaz de llegar a algo la próxima semana. Para darte más detalles.

  • Cuando se trata de nombrar tipos, personalmente soy un poco radical y generalmente dejo de lado los prefijos. Excepto para interfaces, referencias y punteros.
    p.ej

Nombre de tipo

Tipo de bloque Notación Prefijo Ejemplo
FB/nombre de CLASE PascalCase No Cyclinder
ENUM tipo nombre PascalCase No MachineState.Start
INTERFAZ nombre PascalCase I ICyclinder
FUNCIÓN nombre PascalCase No Add()
STRUCT nombre PascalCase No Data
UNION nombre PascalCase No Control

Tampoco soy fanático de los prefijos. La tabla se parece al sistema de prefijos que usamos... pero nuevamente, si decidiéramos deshacernos de él, me haría feliz.

En la mayoría de los casos, no veo ningún beneficio en el uso de prefijos. Estoy de acuerdo con la propuesta de @philippleidig .

Diría que el puntero y la referencia son una excepción aquí.

Propuse mis convenciones en PR #5

Denominación de miembros y denominación de tipos

No veo un beneficio en el uso de prefijos. No me ayuda de ninguna manera.

Variables miembro

Las variables miembro de clase (FB) deben estar ocultas y comenzar con un nombre pequeño
~PascalVAR{atributo 'ocultar'}disparador: BOOL;{atributo 'ocultar'}contador: INT;{atributo 'ocultar'}estadoanalógico : estadoanalógico;END_VAR~

@jozefchmelar

En la mayoría de los casos, no veo ningún beneficio en el uso de prefijos. Estoy de acuerdo con la propuesta de @philippleidig .

Diría que el puntero y la referencia son una excepción aquí.
👍
Propuse mis convenciones en PR #5

Denominación de miembros y denominación de tipos

No veo un beneficio en el uso de prefijos. No me ayuda de ninguna manera.
👍👍

Variables miembro

Las variables miembro de clase (FB) deben estar ocultas y comenzar con un nombre pequeño

    VAR
        {attribute 'hide'}
        trigger : BOOL;
        {attribute 'hide'}
        counter : INT;
        {attribute 'hide'}
        analogStatus : AnalogStatus;
    END_VAR
  • las variables ocultas no serán visibles sobre los anuncios, por lo que si no se necesitan en HMI, se pueden ocultar. Pero si necesitamos verlos en HMI, no deberíamos usar el atributo 'ocultar'.
  • Tc3 no distingue entre mayúsculas y minúsculas, por lo que trigger (nombre de variable) y Trigger (nombre de propiedad) entrarían en conflicto. Tendremos que prefijarlo con _ como se propone, supongo

Totalmente de acuerdo 👍

También existe el atributo "show condicional". Pero esto solo se puede usar junto con una biblioteca compilada.
https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_plc_intro/8095402123.html &id=7685156034373049758
Dado que asumo que proporcionaremos una biblioteca abierta, esto solo tendría un sentido limitado.

_ como prefijo para las variables miembro es necesario como dijo @PTKu .

Por lo general, me gusta ceñirme a las convenciones de nomenclatura y la selección de nombres de C#.

Me gusta el valor de retorno del método como booleano. Encuentro inapropiados los tipos de datos más complejos, porque tienen que ser instanciados fuera o devueltos por referencia.

@philippleidig , ¿qué quiere decir con valores de retorno? ¿En este caso se utilizan como comprobaciones de errores? Por lo general, el valor devuelto depende del método. CalculcateArea devolvería un REAL `LREAL`.

¡Totalmente de acuerdo con el nombre de variable sugerido!

Me gusta el valor de retorno del método como booleano. Encuentro inapropiados los tipos de datos más complejos, porque tienen que ser instanciados fuera o devueltos por referencia.

@philippleidig , ¿qué quiere decir con valores de retorno? ¿En este caso se utilizan como comprobaciones de errores? Por lo general, el valor devuelto depende del método. CalculcateArea devolvería un REAL``LREAL .

@ Roald87 la idea sería que el método de un componente que realiza una acción devolvería 'verdadero' cuando se completa la acción (MoveToHome() cuando se alcanza el sensor/posición de inicio). Esto no impide otros tipos de devolución cuando sea necesario.

¡Totalmente de acuerdo con el nombre de variable sugerido!

Tendría una sugerencia acerca de las matrices. Existe un requisito formal para el compilador inxton que trans-pile solo matrices basadas en 0. El motivo es evitar la confusión cuando se usa en C#.

_array : ARRAY[0..10] OF BOOL; // trans-pilas
tiempo
_array : ARRAY[1..10] OF BOOL; // no trans-apila

¿Algún comentario sobre esto?

Lo mismo para TwinCAT HMI (TE2000)

Lo mismo para TwinCAT HMI (TE2000)

¡Sería muy conveniente tener las matrices sincronizadas con la HMI!

@philippleidig || @ Roald87 , ¿alguno de ustedes tiene convenciones de relaciones públicas sobre arreglos? Por favor... Simplemente me gusta ver más colaboradores en el repositorio :).

Tendría una sugerencia acerca de las matrices. Existe un requisito formal para el compilador inxton que trans-pile solo matrices basadas en 0. El motivo es evitar la confusión cuando se usa en C#.

_array : ARRAY[0..10] OF BOOL; // trans-pilas
tiempo
_array : ARRAY[1..10] OF BOOL; // no trans-apila

¿Algún comentario sobre esto?

Debido a la forma en que funciona el bucle de texto estructurado, prefiero mantener las matrices de PLC con dimensiones 1..X. Como resultado, el código es más fácil de leer en cualquier parte del PLC. Creo que deberíamos estar escribiendo código que sea atractivo y mantenible en el PLC siempre. Si necesitamos correcciones de compatibilidad para que funcione mejor en fragmentos de código de terceros, podemos gestionarlo por separado.

// 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

Me gusta el valor de retorno del método como booleano. Encuentro inapropiados los tipos de datos más complejos, porque tienen que ser instanciados fuera o devueltos por referencia.

Creo que los métodos deberían devolver lo que sea razonable para el método. El nombre del método debería ayudarlo a comprender eso.

es decir

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 la idea sería que el método de un componente que realiza una acción devolvería 'verdadero' cuando se completa la acción (MoveToHome() cuando se alcanza el sensor/posición de inicio). Esto no impide otros tipos de devolución cuando sea necesario.

Realmente no me gusta el enfoque de llamar repetidamente a un método público, donde ese método ejecuta la funcionalidad repetidamente hasta que regresa correctamente. Hay un solo caso que creo que es aceptable (si hay un método de tipo "Ejecutar" en una interfaz, pero creo que también hay mejores formas de que funcione).

Los problemas con eso;

  • Puede/está creando rutas de ejecución en una clase que podría llamarse simultáneamente. Es decir
atEnd :=  axis.GoToEnd();
atBeginning := axis.GoToBeginning();
  • Las clases deben administrar su estado internamente. Los métodos se pueden usar para realizar solicitudes y modificaciones de ese estado, y las propiedades se pueden usar para acceder al estado o establecer un estado simple también.
  • ¿Cómo nombra el método para que quede claro cómo y por qué se usa de esa manera? eje.GoToEndTrueWhenComplete()?
  • ¿Qué pasa si el estado subyacente de la clase cambia de una manera que modifica la forma en que se ejecuta la llamada?
  • Las condiciones de carrera se pueden generar más fácilmente. Si llamamos repetidamente a .Enable() en algo, pero obtenemos una falla para un solo escaneo, entonces .Enable() puede habilitarse de todos modos usando el enfoque "continuar llamando hasta que esté listo". En su lugar, debería ser .RequestEnable: BOOL, que indica si las condiciones subyacentes en el punto de la solicitud son correctas (lo que permite que el código de llamada retroceda correctamente en ese punto). Si se puede realizar la solicitud, el código de llamada puede monitorear .IsEnabled e .InError para que se completen.

@philippleidig no está familiarizado con TwinCAT HMI. ¿Cómo se manejan las matrices no basadas en 0 allí?

@mark-lazarides

Creo que los métodos deberían devolver lo que sea razonable para el método. El nombre del método debería ayudarlo a comprender eso.
👍

La concurrencia y las condiciones de carrera son preocupaciones razonables. En mi humilde opinión, estos problemas deben abordarse tanto como sea posible a nivel de los componentes, pero lo que es más importante, a nivel de coordinación al consumir los componentes. Los métodos del componente deben llamarse desde primitivos similares a controladores de estado implementados correctamente (ya sea CASE simple, IF, ELSIF o un secuenciador/selector/iterador más complejo) que evitarían llamadas simultáneas de métodos en conflicto de la misma instancia de un componente. .

Algo como esto debería evitarse en el código de consumidor del componente.
~alFinal := eje.IrAlFinal();alInicio := eje.IrAlInicio();~

El executing methods que devuelve true cuando termina permite un uso declarativo limpio.

Lo que tengo en mente es algo como esto:

~~~
VAR
_estado: INT;

END_VAR

CASO _estado DE
0:
SI(eje.MoveAbsolute(Posición: 100.0)) ENTONCES
_estado := 1;
TERMINARA SI;
1:
SI(eje.MoveRelative(Posición: 100.0)) ENTONCES
_estado := 2;
TERMINARA SI;
2:
SI(eje.MoveAbsolute(Posición: 300.0)) ENTONCES
_estado := 3;
TERMINARA SI;
3:
_estado := 0;
END_CASE
~~~

Esto podría reducirse a

~~~
VAR
_estado: INT;

END_VAR

CASO _estado DE
0:
Await(axis.MoveAbsolute(Posición: 100.0),1);
1:
Await(axis.MoveRelative(Posición: 100.0),2);
2:
Await(axis.MoveAbsolute(Posición: 300.0),3);
3:
Espera (verdadero, 0);

END_CASE

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

MÉTODO Esperar
VAR_ENTRADA
hecho: BOOL
estado siguiente: INT;
END_VAR
SI (hecho) ENTONCES
_estado := estado siguiente;

TERMINARA SI;

~~~
editar: supongo que el componente se usa en una sola tarea de plc

Pone restricciones en el consumo del código. Nuestros componentes no deben estar sujetos a errores si el consumidor los usa en un orden incorrecto. Deben responder bien a toda interacción. No necesariamente "funcionarán" (es decir, llamar a res := axis.MoveTo(Position:=100); antes de que axis.Enable() no funcione), pero el código de consumo debe recibir suficiente información en todos los puntos para comprender dónde está el problema.

Todavía no se lee bien para mí tampoco. No puede leer axis.MoveAbsolute(syx) y comprender que debe llamarse cíclicamente. Solo entendería eso si supiera que nuestro estilo idiomático lo requiere de usted y en ese punto creo que hemos fallado en crear algo muy útil.

También diría que, independientemente de si los errores se pueden eliminar, aún son más probables. Con el enfoque de llamada de método cíclico, puede crear un método que verifique si el estado del objeto está listo para ser llamado, luego llamar al método cíclico y luego monitorear el otro estado del objeto para asegurarse de que no haya sucedido nada mientras tanto. O realiza la solicitud, que le indica si tiene éxito o no, y luego supervisa el estado para que se complete. Puede registrar una devolución de llamada para eso como parte de la solicitud si lo desea, que es un enfoque OO decente para esto y reduce más llamadas/interrogaciones cíclicas.

¡@mark-lazarides correcto! I execute methods (llamémoslos así) no debería implementar la lógica cíclica. Asumí que lo que hemos estado discutiendo anteriormente es que nos aseguremos de que lo que debe ejecutarse de manera cíclica se coloque en el cuerpo de FB o en un método Cyclic ; que debe llamarse en algún lugar apropiado en el programa del consumidor.

está bien. Entonces, ¿por qué estamos llamando al método cíclicamente? Sigo pensando que los argumentos originales se mantienen. El método debe hacer un trabajo. Empieza algo (y por lo tanto reporta el éxito de eso) o consigue algo (y lo devuelve).

está bien. Entonces, ¿por qué estamos llamando al método cíclicamente? Sigo pensando que los argumentos originales se mantienen. El método debe hacer un trabajo. Empieza algo (y por lo tanto reporta el éxito de eso) o consigue algo (y lo devuelve).

Sin objeciones Mark, no necesitamos llamar a los métodos de ejecución cíclicamente, pero no debería ser un problema si lo hacemos.

No veo por qué un método no puede devolver el resultado de la operación.

Creo que deberíamos idear algunos componentes más complejos y crear prototipos de estas ideas (el pistón neumático no es lo suficientemente complejo para esta discusión). Creo que podríamos comenzar con el accionamiento/eje.

De acuerdo, Pedro.

¡Debido al nombre del hilo, pensé que estábamos apuntando a esto como una convención! Disculpas si he entendido mal.

Chris tiene un bloque de eje básico como relaciones públicas en este momento : lo he comentado, pero necesita más atención.

@mark-lazarides no se necesitan disculpas Mark, estamos aquí para discutir libremente, mañana echaré un vistazo a las relaciones públicas...

@philippleidig @Roald87 @dhullett08 Discusión sobre el diseño de componentes también aquí

Un par de sugerencias aleatorias tomadas de los documentos de PLCopen.

plcopen_codificación_directrices_versión_1.0.pdf

constantes
Deben estar en MAYÚSCULAS para que sean fácilmente identificables

Longitud de nombre aceptable
Mínimo de 4 caracteres máximo de 24 caracteres?

Un par de sugerencias aleatorias tomadas de los documentos de PLCopen.

plcopen_codificación_directrices_versión_1.0.pdf

Gracias por el enlace

constantes
Deben estar en MAYÚSCULAS para que sean fácilmente identificables

👍

Longitud de nombre aceptable
Mínimo de 4 caracteres máximo de 24 caracteres?

Los nombres más largos no deberían ser un problema hasta que expresen la intención, 24 caracteres deberían ser suficientes, sin embargo, no pondría un límite en el máximo. caracteres. Los nombres demasiado cortos son realmente sospechosos, deberían tener más de 4 caracteres.

@Seversonic sus comentarios agregados al documento...

continuación de la discusión aquí #11

Realmente no me gustan tus Convenciones, ¡pero creo que tu Proyecto es bastante interesante!
personalmente prefiero una forma más clásica
Bloque de función FB_ fb
Método M_Add()
P_Parameter Prop

Hola, @PeterZerlauth y gracias. Es un poco laborioso ponerse de acuerdo sobre las convenciones ya que estamos a medio camino entre el PLC y la ingeniería de software clásica.

Aquí está la encuesta desde el principio de las discusiones:

TcOpen.Survey.Result.pdf

Además de eso, hubo una discusión aquí y en Slack Channel, también podría haber algo en @dhullett08 TcOpen repo.

Había un sentimiento general (o al menos yo lo interpreto de esa manera) de que deberíamos abandonar los prefijos si no brindan información útil o si el IDE moderno brinda la información que transmitíamos con los prefijos en el pasado.

Entiendo que se trata de preferencias personales y que realmente no hay una forma correcta o incorrecta de hacerlo. Sólo tenemos que estar de acuerdo en algo.

Cerrando aquí, la discusión continúa aquí: https://github.com/TcOpenGroup/TcOpen/discussions/11

¿Fue útil esta página
0 / 5 - 0 calificaciones