# Expressões

> A única linguagem de expressão atrás das variáveis e das condições de alarme, o que ela pode referenciar, a superfície de leitura de cada tipo de ponto, as regras que o avaliador impõe, as mensagens que a validação dá, e como o editor de script e o seletor Procurar… funcionam.

Uma única linguagem de expressão serve a dois lugares da página Logic: o que uma [variável de expressão](logic-variables) calcula e a condição que um [alarme](logic-alarms) vigia. As duas são uma expressão JavaScript avaliada em modo estrito dentro de uma caixa isolada, sem acesso à máquina, e as duas são editadas no mesmo editor de script. Scripts de ação usam o mesmo vocabulário, mas são blocos de instruções; as diferenças deles estão em [Ações](logic-actions).

## O que é uma expressão {#what-an-expression-is}

Uma expressão é exatamente uma expressão JavaScript: literais, aritmética, comparações, operadores lógicos, o condicional `a ? b : c`, operações de texto, cadeias de modelo e chamadas aos embutidos do JavaScript `Math`, `Date` e `JSON`. Instruções (`let`, `if`, laços) pertencem às ações: uma segunda instrução é recusada com "Uma variável guarda uma única expressão. Instruções pertencem às Ações." Atribuições e `++`/`--` em qualquer lugar de uma expressão são recusados com "As expressões são somente leitura. As escritas pertencem às Ações."

A expressão referencia pontos pelo [endereço](logic-address-space) enraizado deles. O que cada raiz entrega à expressão:

| Raiz | O que atravessa até a expressão |
| --- | --- |
| Tags de `Connector`, `Variables`, `Alarms`, os `Channels` de uma unidade do Process | Um objeto ao vivo com a superfície de leitura tipada abaixo. Uma referência nua (`Connector.Simulated.Pump.Flow` sozinha) avalia para o valor do ponto; um alarme nu, para o `IsActive` dele. |
| `System`, `Server`, `Assets`, e o estado da execução, a produtividade, os comandos e a preparação de uma unidade do Process | O valor atual em si: um número, um texto, um booleano ou um instante. Use-o diretamente; não há membro a acrescentar. Um membro de ponto ao vivo acrescentado a ele é recusado: `System.StartTime.Value` dá "'System.StartTime' é um valor: leia-o sem .Value." |

Um ponto ao vivo escrito nu é o ponto, não o valor dele, e só vale pelo valor quando é a expressão inteira. Onde um operador o toma pelo valor, leia-o pelo membro que diz qual valor: `.ValueToBool()` onde ele é testado como verdadeiro ou falso (`Variables.Start.ValueToBool() ? 1 : 0`), `.Value` onde é comparado por igualdade, `.ValueToReal()` onde é ordenado ou entra numa conta (`Variables.Level.ValueToReal() > 80`), e `.IsActive` num alarme. A validação recusa o ponto nu no teste de `?:`, como operando de `!`, `&&` ou `||`, numa comparação (`==`, `!=`, `===`, `!==`, `<`, `>`, `<=`, `>=`) e na aritmética (`+`, `-`, `*`, `/`, `%`, ou um sinal na frente dele), porque ali ele sempre contaria como verdadeiro ou nunca leria o valor: `Variables.Level > 80` dá "'Variables.Level' é o próprio ponto, não o valor dele: uma comparação ou um cálculo nunca lê o valor nele. Leia o valor dele com Variables.Level.ValueToReal()." Um ponto passado a uma função, e qualquer membro de um ponto, fica como foi escrito. A mesma recusa vale para o lado esquerdo de `??`, do qual um ponto nunca sai (leia `.Value`), e para um marcador `${…}` de um template literal, que nunca escreve o valor do ponto no texto (leia `.ValueToString()`).

`System.Now` é o instante da avaliação: toda referência a ele dentro de uma avaliação lê a mesma hora. Ele atravessa como um `Date` do JavaScript, como todo carimbo de tempo, então instantes comparam e subtraem naturalmente: `(System.Now - Connector.Simulated.Pump.Run.SourceTimeStamp()) / 1000` são segundos.

Uma variável de expressão pode referenciar tags, variáveis de memória, alarmes, pontos do Process, pontos de Assets e os diagnósticos de System e Server, mas **nunca outra variável de expressão**: o editor a recusa ("'Variables.X' é uma variável de expressão. Expressões não podem referenciar outras variáveis de expressão; referencie variáveis de memória ou os pontos por baixo delas.") para que os resultados nunca dependam da ordem de avaliação. Uma condição de alarme pode referenciar tudo, menos `Alarms.*` ("As condições de alarme não podem referenciar outros alarmes.") e precisa produzir um booleano.

## A superfície de leitura de um ponto {#the-read-surface-of-a-point}

Uma tag ao vivo oferece:

| Membro | Devolve | Significado |
| --- | --- | --- |
| `.Value` | valor tipado | O valor de engenharia, depois da [cadeia de conversão](connector-value-pipeline) inteira da tag |
| `.ValueRaw` | valor tipado | O valor bruto normalizado para o tipo declarado da tag, antes da escala |
| `.ValueObject` | qualquer coisa | O valor exatamente como o driver o produziu |
| `.ValueScaled`, `.ValueCalibrated`, `.ValueLimited`, `.ValueRounded` | número | O valor depois dos estágios de Escala, Calibração, Limites e Decimais |
| `.ValueToBool()`, `.ValueToInteger()`, `.ValueToReal()`, `.ValueToString()`, `.ValueToDateTime()` | valor convertido | O valor de engenharia convertido para aquela categoria; nenhum valor continua nenhum valor |
| `.State()` | booleano | Qualidade: verdadeiro enquanto a leitura é boa |
| `.SourceTimeStamp()` | Date | Quando o dispositivo produziu a leitura, se o protocolo dele a data (OPC UA); senão, o instante em que o Connector a recebeu, o mesmo de `.ApplicationTimeStamp()` |
| `.ApplicationTimeStamp()` | Date | Quando o Connector a recebeu |
| `.Name`, `.EngineeringUnit` | texto | O nome e a unidade da tag |

Uma variável oferece `.Value`, as cinco conversões `.ValueTo*()`, `.State()`, `.ApplicationTimeStamp()`, `.Name` e `.EngineeringUnit`. Um alarme oferece `.IsActive`, `.IsAcked`, `.CurrentState`, `.State()` (se a condição dele pôde ser avaliada na última varredura), `.ActiveSince()`, `.LastTransitionAt()` e `.Name`. Um canal do Process oferece a superfície de tag quando está vinculado a uma tag, e a superfície de variável quando está vinculado a uma variável; um canal sem vínculo não oferece nada. Nada mais num objeto ao vivo é alcançável: um membro fora desta superfície lê como indefinido, e chamar um falha na validação. O seletor Procurar… lista esses mesmos membros sob cada ponto, então nenhum deles precisa ser digitado de memória.

## As regras que o avaliador impõe {#the-rules-the-evaluator-enforces}

Tanto no editor, enquanto você digita, quanto de novo a cada varredura de 100 ms:

- **A porteira da qualidade.** Antes de avaliar, todo ponto referenciado é resolvido e atualizado. Qualquer ponto ausente, Ruim ou sem valor faz o resultado ser uma lacuna de qualidade ruim sem rodar a expressão, então uma tag offline nunca consegue fabricar uma amostra e `null` nunca chega ao motor para virar `0`. Uma tag cuja leitura calou, sem leitura nova durante o intervalo de leitura mais 10 segundos, vale aqui como uma leitura que falhou: Ruim, a menos que a sua política para [um dispositivo que não respondeu](connector-value-pipeline#the-device-did-not-answer) apresente um substituto.
- **Resultados não finitos são lacunas.** NaN ou infinito (divisão por zero, aritmética indefinida) nunca vira uma amostra.
- **Proteção contra descontrole.** Uma avaliação é abortada além de 250 ms, 5.000 instruções ou 4 MB de memória de script, e conta como lacuna de qualidade ruim. Um motor é mantido por texto de expressão, então nada vaza de outra expressão.
- **Conversão.** Um resultado bem-sucedido é convertido para o [tipo de saída](logic-variables#output-types) da variável; uma condição de alarme é convertida para booleano, e um resultado que não converte mantém o estado do alarme.
- **Erros são lacunas, não exceções.** Um erro de execução no momento da varredura produz uma lacuna e uma entrada de diário (a primeira falha, resumos enquanto ela se repete, uma linha quando ela se recupera); a varredura segue com a próxima variável.

## O que a validação recusa {#what-validation-refuses}

O editor valida o rascunho a cada meio segundo e mostra o primeiro problema abaixo do valor ou da condição:

| Mensagem | Causa |
| --- | --- |
| A expressão é obrigatória. / A condição é obrigatória. | O editor está vazio. |
| Erro na expressão: … | O texto não é interpretável. |
| Uma variável guarda uma única expressão. Instruções pertencem às Ações. | Mais de uma instrução, ou uma instrução que não é uma expressão. |
| As expressões são somente leitura. As escritas pertencem às Ações. | Uma atribuição ou `++`/`--`. |
| Os endereços precisam ser estáticos: 'Root' é acessado com um segmento calculado. | `Variables[name]` ou qualquer segmento que não seja literal. |
| Endereço incompleto 'Connector.Simulated'. | Uma cadeia curta demais para nomear um ponto sob a raiz dela. |
| Não foi possível encontrar 'X'. | O endereço não resolve para nada. |
| 'X' é uma variável de expressão. … | Uma referência a outra variável de expressão. |
| 'X' é um valor: leia-o sem .Value. | Um membro de ponto ao vivo (`.Value`, `.State()`, …) acrescentado a um ponto que entra como o próprio valor (`System.StartTime.Value`). |
| 'X' é o próprio ponto, não o valor dele: como condição ele sempre conta como verdadeiro. Leia o valor dele com X.ValueToBool(). | Um ponto ao vivo escrito nu onde é testado como verdadeiro ou falso: o teste de `?:`, ou um operando de `!`, `&&` ou `\|\|` (`!Variables.Start`). Num alarme a mensagem nomeia `.IsActive`. |
| 'X' é o próprio ponto, não o valor dele: uma comparação ou um cálculo nunca lê o valor nele. Leia o valor dele com X.ValueToReal(). | Um ponto ao vivo escrito nu numa comparação ou na aritmética (`Variables.Level > 80`, `Variables.Level * 2`). Para `==`, `!=`, `===` e `!==` a mensagem nomeia `.Value`, e num alarme `.IsActive`. |
| 'X' é o próprio ponto, não o valor dele: ele nunca está vazio, então ?? sempre fica com ele. Leia o valor dele com X.Value. | Um ponto ao vivo escrito nu à esquerda de `??` (`Variables.Level ?? 0`). Num alarme a mensagem nomeia `.IsActive`. |
| 'X' é o próprio ponto, não o valor dele: escrito dentro de um texto ele nunca mostra o valor. Leia o valor dele com X.ValueToString(). | Um ponto ao vivo escrito nu num marcador `${…}` de um template literal. Num alarme a mensagem nomeia `.IsActive`. |
| As condições de alarme não podem referenciar outros alarmes. | Uma referência a `Alarms.*` numa condição. |
| A condição precisa resultar em um booleano, mas ela produziu 'x'. | A avaliação de teste de uma condição produziu um valor presente que não converte para booleano. |
| Erro na expressão: um ponto ao vivo não pode ser usado direto aqui. Leia-o pelo membro .Value dele (por exemplo Connector.Driver.Device.Tag.Value). | Um objeto ao vivo usado onde um primitivo é necessário (`Math.round(Variables.Level)`). |
| Erro na expressão: `<mensagem do motor>` | A avaliação de teste lançou um erro: um identificador desconhecido, um membro bloqueado chamado como função. |

A avaliação de teste roda com os valores atuais, seja qual for a qualidade, então uma mensagem reflete a expressão, e não uma entrada Ruim; uma entrada Ruim aparece no cartão PONTOS REFERENCIADOS.

## O editor de script {#the-script-editor}

Toda expressão, condição e script da página é editado no mesmo cartão: um rótulo de grupo (EXPRESSÃO, CONDIÇÃO ou SCRIPT DA AÇÃO), um botão **Procurar…**, o editor, uma dica e a mensagem de validação. O editor é o CodeMirror com realce de JavaScript, um histórico de desfazer e autocompletar de símbolos de endereço: nomes de raiz num símbolo isolado, depois os filhos do caminho digitado até ali, com os membros incluídos assim que o símbolo nomeia um ponto ([digitar um endereço](logic-address-space#entering-an-address)). O texto é empurrado para o rascunho com atraso e salvo sozinho. As caixas de expressão e de condição têm no mínimo 56 px de altura, e a caixa de script de ação tem 160 px. Quando o motor do editor não consegue iniciar, a caixa se degrada para uma área de texto simples, que ainda lê e escreve, sem realce, sem completar e sem histórico; o Procurar… então acrescenta o endereço escolhido no fim, em vez de no cursor.

O **Procurar…** abre um diálogo ("Procurar: inserir em Expressão") com a árvore do espaço de endereços inteiro, até o nível de membro; selecione uma linha e pressione **Inserir**, ou clique duas vezes nela, para pôr o endereço dela no cursor. Cada editor é dono do próprio diálogo, então dois editores numa página nunca compartilham uma seleção.

## O que as expressões não fazem {#what-expressions-do-not-do}

Elas não escrevem nada, não chamam comandos de execução do Process, não guardam estado entre avaliações, não conseguem definir funções compartilhadas entre expressões, e não são avaliadas sob demanda: a varredura as publica a cada 100 ms e o editor mostra a prévia do rascunho duas vezes por segundo.
