Tcopen: Convenções

Criado em 30 out. 2020  ·  31Comentários  ·  Fonte: TcOpenGroup/TcOpen

Documento de convenções aqui

Por favor, contribua para a discussão abaixo

  • Vamos manter as discussões aqui para acompanhamento.
  • Bate-papo rápido aqui:TcOpen Slack

  • [ ] Convenções de nomenclatura para variáveis ​​(VAR, VAR_INPUT, VAR_OUTPUT, VAR_IN_OUT, VAR_INST, TEMP)

  • [ ] Convenção de nomenclatura para métodos
  • [ ] Convenção de nomenclatura para propriedades
  • [ ] Convenção de nomenclatura para blocos (FB, FC, PRG etc.)
discussion

Comentários muito úteis

Eu teria uma sugestão sobre matrizes. Existe um requisito formal para o compilador inxton que transpila apenas arrays baseados em 0. O motivo é evitar a confusão quando usado em C#.

_array : ARRAY[0..10] OF BOOL; //transpila
enquanto
_array : ARRAY[1..10] OF BOOL; // não empilha

Algum comentário sobre isso?

Todos 31 comentários

@mark-lazarides @Roald87 @philippleidig @jozefchmelar vamos manter a discussão sobre convenções aqui... folga para bate-papo rápido; discussões aqui para acompanhar a atividade

  • Na minha opinião, as propriedades devem ser definidas da seguinte forma "IsEnabled". O próprio nome já deve indicar de que tipo é.

  • Eu gosto do valor de retorno do método como booleano. Acho inapropriados tipos de dados mais complexos, porque eles precisam ser instanciados fora ou retornados por referência.

  • A herança de cada classe base de "fbComponent" é necessária para usar Inxton ou tc.prober?

  • Quando se trata de nomenclatura de tipos, eu pessoalmente sou um pouco radical e geralmente deixo de fora os prefixos. Exceto para interfaces, referências e ponteiros.
    por exemplo

    Nomeação de tipos


| Tipo de bloco | Notação | Prefixo | Exemplo |
| :------------- | :------------- | :------------ | :------------------------------------------------- -- |
| Nome FB/CLASSE | PascalCase |Não | Cyclinder |
| Nome do tipo ENUM | PascalCase |Não | MachineState.Start |
| Nome da INTERFACE | PascalCase | I | ICyclinder |
| nome da FUNÇÃO | PascalCase |Não | Add() |
| Nome da ESTRUTURA | PascalCase | Não | Data |
| Nome UNIÃO | PascalCase | Não | Control |

@philippleidig

  • Na minha opinião, as propriedades devem ser definidas da seguinte forma "IsEnabled". O próprio nome já deve indicar de que tipo é.

Concordo plenamente.

  • Eu gosto do valor de retorno do método como booleano. Acho inapropriados tipos de dados mais complexos, porque eles precisam ser instanciados fora ou retornados por referência.

Usamos como descrito com nossos componentes. É útil ao controlar o estado de uma sequência. Na grande maioria dos casos bool é suficiente. Às vezes seria bom ter mais informações sobre o estado do método ... mas isso precisaria de uma discussão mais ampla (talvez sintaxe fluente como algo)

  • A herança de cada classe base de "fbComponent" é necessária para usar Inxton ou tc.prober?

Não. não há requisitos específicos para isso, nem Inxton nem tc.prober. Usamos desta forma. ComponentBase é uma classe abstrata que possui algum contrato público (Método Manual, etc), mas pode implementar alguns recursos comuns para componentes. Não sou muito fã de herança (prefiro composição), mas neste caso gostaria de ter alguma opção aberta para o futuro.

No inxton, se você deseja coletar todos os componentes em uma coleção, você pode fazer isso quando something is copmonent existe um mecanismo para isso.

Há também outra razão para isso. Estamos trabalhando hoje em open-source nossa biblioteca base, que tem alguns requisitos em relação. Espero ser capaz de chegar a algo na próxima semana. Para lhe dar mais detalhes.

  • Quando se trata de nomenclatura de tipos, eu pessoalmente sou um pouco radical e geralmente deixo de fora os prefixos. Exceto para interfaces, referências e ponteiros.
    por exemplo

Nomeação de tipos

Exemplo de prefixo de notação de tipo de bloco
Nome FB/CLASS PascalCase Não Cyclinder
Nome do tipo ENUM PascalCase Não MachineState.Start
Nome da INTERFACE PascalCase I ICyclinder
Nome da FUNÇÃO PascalCase Não Add()
Nome do STRUCT PascalCase Não Data
Nome UNION PascalCase No Control

Também não é fã de prefixos. A tabela se assemelha ao sistema de prefixos que usamos... mas, novamente, se decidirmos nos livrar do, isso me deixaria feliz.

Na maioria dos casos, não vejo vantagem em usar prefixos. Concordo com a proposta do @philippleidig .

Eu diria que ponteiro e referência são uma exceção aqui.

Eu propus minhas convenções no PR #5

Nomeação de membros e nomeação de tipos

Não vejo vantagem em usar prefixo. Não me ajuda em nada.

Variáveis ​​de membro

As variáveis ​​de membro de classe (FB) devem ser ocultadas e começar com um nome pequeno
~PascalVAR{atributo 'ocultar'}gatilho: BOOL;{atributo 'ocultar'}contador : INT;{atributo 'ocultar'}analogStatus : AnalogStatus;END_VAR~

@jozefchmelar

Na maioria dos casos, não vejo vantagem em usar prefixos. Concordo com a proposta do @philippleidig .

Eu diria que ponteiro e referência são uma exceção aqui.
👍
Eu propus minhas convenções no PR #5

Nomeação de membros e nomeação de tipos

Não vejo vantagem em usar prefixo. Não me ajuda em nada.
👍👍

Variáveis ​​de membro

As variáveis ​​de membro de classe (FB) devem ser ocultadas e começar com um nome pequeno

    VAR
        {attribute 'hide'}
        trigger : BOOL;
        {attribute 'hide'}
        counter : INT;
        {attribute 'hide'}
        analogStatus : AnalogStatus;
    END_VAR
  • variáveis ​​ocultas não serão visíveis nos anúncios, portanto, se não houver necessidade delas na HMI, elas poderão ser ocultadas. Mas se precisarmos vê-los na HMI não devemos usar o atributo 'hide'.
  • Tc3 não diferencia maiúsculas de minúsculas, portanto trigger (nome da variável) e Trigger (nome da propriedade) entrariam em conflito. Vamos precisar prefixá-lo com _ como proposto, eu acho

Concordo plenamente 👍

Há também o atributo "conditionalshow". Mas isso só pode ser usado em conjunto com uma biblioteca compilada.
https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_plc_intro/8095402123.html &id=7685156034373049758
Como presumo que forneceremos uma biblioteca aberta, isso só faria sentido limitado.

_ como prefixo para variáveis ​​de membro é necessário como @PTKu disse.

Geralmente, gosto de seguir as convenções de nomenclatura e a seleção de nomes do C#.

Eu gosto do valor de retorno do método como booleano. Acho inapropriados tipos de dados mais complexos, porque eles precisam ser instanciados fora ou retornados por referência.

@philippleidig o que você quer dizer com valores de retorno? Neste caso, eles são usados ​​como verificações de erros? Normalmente o valor de retorno depende do método. CalculcateArea retornaria um REAL `LREAL`.

Concordo totalmente com a nomenclatura sugerida da variável!

Eu gosto do valor de retorno do método como booleano. Acho inapropriados tipos de dados mais complexos, porque eles precisam ser instanciados fora ou retornados por referência.

@philippleidig o que você quer dizer com valores de retorno? Neste caso, eles são usados ​​como verificações de erros? Normalmente o valor de retorno depende do método. CalculcateArea retornaria um REAL``LREAL .

@Roald87 a ideia seria que o método de um componente que executa uma ação retornaria 'true' quando a ação for concluída (MoveToHome() quando o sensor/posição inicial for atingido). Isso não impede outros tipos de retorno quando necessário.

Concordo totalmente com a nomenclatura sugerida da variável!

Eu teria uma sugestão sobre matrizes. Existe um requisito formal para o compilador inxton que transpila apenas arrays baseados em 0. O motivo é evitar a confusão quando usado em C#.

_array : ARRAY[0..10] OF BOOL; //transpila
enquanto
_array : ARRAY[1..10] OF BOOL; // não empilha

Algum comentário sobre isso?

O mesmo para TwinCAT HMI (TE2000)

O mesmo para TwinCAT HMI (TE2000)

Seria muito conveniente ter os arrays em sincronia com a IHM de fato!

@philippleidig || @Roald87 , qualquer um de vocês faria convenções de relações públicas sobre arrays, então pls... Eu gosto de ver mais contribuidores no repositório :).

Eu teria uma sugestão sobre matrizes. Existe um requisito formal para o compilador inxton que transpila apenas arrays baseados em 0. O motivo é evitar a confusão quando usado em C#.

_array : ARRAY[0..10] OF BOOL; //transpila
enquanto
_array : ARRAY[1..10] OF BOOL; // não empilha

Algum comentário sobre isso?

Devido à maneira como o loop de texto estruturado funciona, prefiro manter os arrays PLC dimensionados 1..X. Como resultado, o código é mais fácil de ler em qualquer lugar do PLC. Acho que deveríamos estar sempre escrevendo código atraente e sustentável no PLC. Se precisarmos de calços para fazê-lo funcionar melhor em bits de código de terceiros, podemos gerenciar isso separadamente.

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

Eu gosto do valor de retorno do método como booleano. Acho inapropriados tipos de dados mais complexos, porque eles precisam ser instanciados fora ou retornados por referência.

Eu acho que os métodos devem retornar o que for razoável para o método. O nome do método deve ajudá-lo a entender isso.

ou seja

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 a ideia seria que o método de um componente que executa uma ação retornaria 'true' quando a ação for concluída (MoveToHome() quando o sensor/posição inicial for atingido). Isso não impede outros tipos de retorno quando necessário.

Eu realmente não gosto da abordagem de chamar repetidamente um método público, onde esse método está executando a funcionalidade repetidamente até retornar correto. Há um único caso que eu acho que é aceitável (se houver um método do tipo "Execute" em uma interface, mas também existem maneiras melhores para que isso funcione também, acredito).

Os problemas com isso;

  • Você pode/está criando caminhos de execução em uma classe que poderia ser chamada simultaneamente. Ou seja
atEnd :=  axis.GoToEnd();
atBeginning := axis.GoToBeginning();
  • As classes devem gerenciar seu estado internamente. Os métodos podem ser usados ​​para fazer solicitações e modificações desse estado, e as propriedades podem ser usadas para acessar o status ou definir um estado simples também.
  • Como você nomeia o método para que fique claro como e por que ele está sendo usado dessa maneira? axis.GoToEndTrueWhenComplete()?
  • E se o estado subjacente da classe mudar de uma maneira que modifique a maneira como a chamada está sendo executada?
  • As condições de corrida podem ser geradas mais facilmente. Se chamarmos .Enable() repetidamente em algo, mas obtivermos uma falha em uma única varredura, então .Enable() pode ser Habilitado de qualquer maneira usando a abordagem "continuar chamando até terminar". Em vez disso, deve ser .RequestEnable : BOOL, que indica se as condições subjacentes no ponto da solicitação estão corretas (permitindo que o código de chamada retorne normalmente nesse ponto). Se a solicitação puder ser feita, o código de chamada poderá monitorar .IsEnabled e .InError para conclusão.

@philippleidig não familiarizado com o TwinCAT HMI. Como as matrizes não baseadas em 0 são tratadas lá?

@mark-lazarides

Eu acho que os métodos devem retornar o que for razoável para o método. O nome do método deve ajudá-lo a entender isso.
👍

Concorrência e condições de corrida são todas preocupações razoáveis. IMHO Esses problemas devem ser abordados tanto quanto possível no nível dos componentes, mas mais importante no nível de coordenação ao consumir os componentes. Os métodos do componente devem ser chamados de dentro de primitivas do tipo controlador de estado implementadas adequadamente (seja CASE simples, IF, ELSIF ou sequenciador/seletor/iterador mais complexo) que impediriam chamadas simultâneas de métodos conflitantes da mesma instância de um componente .

Algo assim deve ser evitado no código do consumidor do componente
~atEnd := axis.GoToEnd();atBeginning := axis.GoToBeginning();~

O executing methods retornando true quando concluído permite o uso declarativo limpo.

O que tenho em mente é algo assim:

~~~
VAR
_estado: INT;

END_VAR

CASO _estado DE
0:
SE(eixo.MoveAbsoluto(Posição: 100,0)) ENTÃO
_estado := 1;
FIM SE;
1:
SE(eixo.MoverRelativo(Posição: 100,0)) ENTÃO
_estado := 2;
FIM SE;
2:
SE(eixo.MoveAbsoluto(Posição: 300,0)) ENTÃO
_estado := 3;
FIM SE;
3:
_estado := 0;
END_CASE
~~~

Isso poderia ser reduzido a

~~~
VAR
_estado: INT;

END_VAR

CASO _estado DE
0:
Await(axis.MoveAbsolute(Posição: 100.0),1);
1:
Await(axis.MoveRelative(Posição: 100.0),2);
2:
Await(axis.MoveAbsolute(Posição: 300.0),3);
3:
Aguardar(verdadeiro,0);

END_CASE

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

MÉTODO Aguardar
VAR_INPUT
feito: BOOL
próximoEstado: INT;
END_VAR
SE (feito) ENTÃO
_state := nextState;

FIM SE;

~~~
edit: suponho que o componente seja usado em uma tarefa de plc único

Ele coloca restrições no consumo do código. Nossos componentes não devem estar sujeitos a erros se o consumidor os utilizar em um pedido incorreto. Eles devem responder bem a todas as interações. Eles não necessariamente "funcionarão" (ou seja, chamar res := axis.MoveTo(Position:=100); antes axis.Enable() não funcionaria), mas o código consumidor deve receber informações suficientes em todos os pontos para entender onde está o problema.

Ele ainda não lê bem para mim também. Você não pode ler axis.MoveAbsolute(syx) e entender que ele precisa ser chamado ciclicamente. Você só entenderia isso se soubesse que nosso estilo idiomático exigia isso de você e, nesse ponto, acho que falhamos em criar algo muito útil.

Eu também diria que, independentemente de os erros poderem ser fatorados, eles ainda são mais prováveis. Com a abordagem de chamada de método cíclico, você pode criar um método que verifica se o estado do objeto está pronto para ser chamado, chamar o método cíclico e monitorar o outro estado do objeto para garantir que nada tenha acontecido nesse ínterim. Ou você faz a solicitação, que informa se foi bem-sucedida ou não, e monitora o status para conclusão. Você pode registrar um retorno de chamada para isso como parte da solicitação, se desejar, o que é uma abordagem OO decente para isso e reduz mais chamadas/interrogatórios ciclicamente.

@mark-lazarides correto! Eu execute methods (vamos chamá-los assim) não deveria implementar lógica cíclica. Eu assumi o que discutimos anteriormente que garantimos que o que precisa ser executado de maneira cíclica seria colocado no corpo do FB ou em um método Cyclic ; que deve ser chamado em algum lugar apropriado no programa do consumidor.

OK. Então, por que estamos chamando o método ciclicamente? Eu ainda acho que os argumentos originais permanecem. O método deve fazer um trabalho. Ou comece algo (e, portanto, relate o sucesso disso) ou obtenha algo (e devolva).

OK. Então, por que estamos chamando o método ciclicamente? Eu ainda acho que os argumentos originais permanecem. O método deve fazer um trabalho. Ou comece algo (e, portanto, relate o sucesso disso) ou obtenha algo (e devolva).

Sem objeções Mark, não precisamos chamar métodos execute ciclicamente, mas não deve ser um problema se o fizermos.

Não vejo por que um método não pôde retornar o resultado da operação.

Acho que devemos criar alguns componentes mais complexos e prototipar essas ideias (pistão pneumático não é complexo o suficiente para esta discussão) acho que poderíamos começar com acionamento/eixo.

Acordado, Pedro.

Devido ao nome do tópico, pensei que estávamos visando isso como uma convenção! Desculpas se eu tiver entendido errado.

Chris tem um bloco de eixo básico como PR no momento - eu comentei sobre isso, mas precisa de mais atenção.

@mark-lazarides não precisa de desculpas Mark, estamos aqui para discutir livremente, vou dar uma olhada no PR amanhã...

@philippleidig @ Roald87 @dhullett08 Discussão sobre design de componentes também aqui

Algumas sugestões aleatórias retiradas dos documentos do PLCopen.

plcopen_coding_guidelines_version_1.0.pdf

Constantes
Devem estar em letras maiúsculas para que sejam facilmente identificáveis

Comprimento do nome aceitável
Mínimo de 4 caracteres máximo de 24 caracteres?

Algumas sugestões aleatórias retiradas dos documentos do PLCopen.

plcopen_coding_guidelines_version_1.0.pdf

Obrigado pelo link

Constantes
Devem estar em letras maiúsculas para que sejam facilmente identificáveis

👍

Comprimento do nome aceitável
Mínimo de 4 caracteres máximo de 24 caracteres?

Nomes mais longos não devem ser um problema até que expressem a intenção, 24 caracteres devem ser suficientes, no entanto, eu não colocaria limite no máximo. personagens. Nomes muito curtos são realmente suspeitos, eles devem ter mais de 4 caracteres.

@Severonic suas observações adicionadas ao documento...

discussão cont. aqui #11

Eu realmente não gosto de suas convenções, mas acho seu projeto bastante interessante!
pessoalmente prefiro uma forma mais clássica
Bloco de função FB_fb
Método M_Add()
P_Parameter Prop

Olá, @PeterZerlauth e obrigado. É um pouco trabalhoso concordar com as convenções, pois estamos a meio caminho entre o PLC e a engenharia de software clássica.

Aqui está a enquete do início das discussões:

TcOpen.Survey.Result.pdf

Além disso, houve uma discussão aqui e no Slack Channel, também pode haver algo no repositório @dhullett08 TcOpen.

Havia um sentimento geral (ou pelo menos eu interpreto dessa maneira) de que devemos abandonar os prefixos se eles não fornecerem informações úteis ou o IDE moderno fornecer as informações que transmitimos com os prefixos no passado.

Eu entendo que isso é sobre preferências pessoais e realmente não há maneira certa ou errada de fazer isso. Só temos que concordar em algo.

Fechando aqui a discussão continua aqui: https://github.com/TcOpenGroup/TcOpen/discussions/11

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

runtimevic picture runtimevic  ·  8Comentários

runtimevic picture runtimevic  ·  12Comentários

KindDragon picture KindDragon  ·  11Comentários

kalebpederson picture kalebpederson  ·  12Comentários

jasonmalinowski picture jasonmalinowski  ·  19Comentários