{"id":1150,"date":"2026-09-17T09:10:58","date_gmt":"2026-09-17T12:10:58","guid":{"rendered":"https:\/\/www.pascalscada.com\/?page_id=1150"},"modified":"2026-09-17T09:36:58","modified_gmt":"2026-09-17T12:36:58","slug":"numeric-expression-tag","status":"publish","type":"page","link":"https:\/\/www.pascalscada.com\/pb\/numeric-expression-tag\/","title":{"rendered":"Tag de express\u00e3o num\u00e9rica"},"content":{"rendered":"<h5>Introdu\u00e7\u00e3o<\/h5>\n<p>O <img decoding=\"async\" alt=\"\" src=\"https:\/\/www.pascalscada.com\/wp-content\/uploads\/2026\/09\/TNumericExprTag.png\"> <strong><code>TNumericExprTag<\/code><\/strong> \u00e9 um tag num\u00e9rico <strong>virtual<\/strong>: ele n\u00e3o tem driver nem endere\u00e7o, e o seu valor \u00e9 o resultado de uma <strong>express\u00e3o<\/strong> calculada a partir de outros tags. Sempre que um dos tags de origem muda, a express\u00e3o \u00e9 reavaliada e, se o resultado for diferente, o <code>TNumericExprTag<\/code> muda tamb\u00e9m \u2014 notificando os controles da <a href=\"\/pb\/hmi-control-library-hcl\/\">HCl<\/a>, os <code>TTagBit<\/code> e o c\u00f3digo ligados a ele, exatamente como um tag de comunica\u00e7\u00e3o faria.<\/p>\n<p>Serve para tudo que \u00e9 <em>derivado<\/em> de valores do equipamento e que n\u00e3o vale a pena programar no CLP nem em c\u00f3digo na aplica\u00e7\u00e3o:<\/p>\n<ul>\n<li>uma vaz\u00e3o total como soma de duas linhas (<code>A+B<\/code>);<\/li>\n<li>um percentual (<code>A\/B*100<\/code>), uma diferen\u00e7a de temperatura (<code>A-B<\/code>), uma pot\u00eancia (<code>A*B\/1000<\/code>);<\/li>\n<li>um alarme ou estado combinado (<code>ifthen((A&gt;80) and (B=1), 1, 0)<\/code>);<\/li>\n<li>uma grandeza f\u00edsica a partir de uma medi\u00e7\u00e3o (<code>sqrt(A)*12.5<\/code>).<\/li>\n<\/ul>\n<p>Ele fica na paleta <strong>PascalSCADA Tags<\/strong>. Para convers\u00f5es de escala simples de <strong>um<\/strong> tag, um <a href=\"\/pb\/scale-processors\/\">processador de escala<\/a> \u00e9 mais adequado \u2014 a express\u00e3o entra quando o resultado depende de mais de um tag ou de l\u00f3gica.<\/p>\n<h5>Propriedades<\/h5>\n<table>\n<thead>\n<tr>\n<th>Propriedade<\/th>\n<th>Descri\u00e7\u00e3o<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>A<\/code> \u2026 <code>J<\/code><\/td>\n<td>At\u00e9 dez tags num\u00e9ricos de origem (qualquer <code>TPLCNumber<\/code>: <code>TPLCTagNumber<\/code>, <code>TPLCBlockElement<\/code>, <code>TPLCStructItem<\/code>, <code>TTagBit<\/code> ou outro <code>TNumericExprTag<\/code>). Cada um entra na express\u00e3o pela sua letra.<\/td>\n<\/tr>\n<tr>\n<td><code>Expression<\/code><\/td>\n<td>A express\u00e3o. Alter\u00e1-la recalcula o valor na hora.<\/td>\n<\/tr>\n<tr>\n<td><code>ScaleProcessor<\/code><\/td>\n<td>Escala aplicada ao resultado \u2014 <code>ValueRaw<\/code> \u00e9 o resultado da express\u00e3o, <code>Value<\/code> \u00e9 ele escalado.<\/td>\n<\/tr>\n<tr>\n<td><code>LastEvalutionError<\/code><\/td>\n<td><code>OK<\/code>, ou a mensagem do \u00faltimo erro de avalia\u00e7\u00e3o (sintaxe, vari\u00e1vel n\u00e3o ligada, tipo inv\u00e1lido). Somente leitura.<\/td>\n<\/tr>\n<tr>\n<td><code>Value<\/code>, <code>ValueRaw<\/code><\/td>\n<td>O resultado. <strong>Somente leitura na pr\u00e1tica<\/strong>: escrever nele n\u00e3o faz nada al\u00e9m de disparar <code>OnWriteFail<\/code>.<\/td>\n<\/tr>\n<tr>\n<td><code>OnValueChangeFirst<\/code>, <code>OnValueChangeLast<\/code><\/td>\n<td>Disparados quando o resultado muda (antes e depois de notificar os controles).<\/td>\n<\/tr>\n<tr>\n<td><code>OnReadFail<\/code><\/td>\n<td>Disparado quando a avalia\u00e7\u00e3o falha.<\/td>\n<\/tr>\n<tr>\n<td><code>OnWriteFail<\/code><\/td>\n<td>Disparado quando algu\u00e9m tenta escrever no tag.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>As vari\u00e1veis entram na express\u00e3o com o <strong><code>Value<\/code><\/strong> do tag de origem \u2014 ou seja, j\u00e1 escalado. Se precisar do valor bruto, ligue um <code>TTagBit<\/code>\/<code>TPLCTagNumber<\/code> sem escala ou fa\u00e7a a conta inversa na express\u00e3o.<\/p>\n<h5>Sintaxe da express\u00e3o<\/h5>\n<p>O avaliador \u00e9 o <code>TFPExpressionParser<\/code> do Free Pascal, com as fun\u00e7\u00f5es matem\u00e1ticas e booleanas ligadas, mais uma fun\u00e7\u00e3o pr\u00f3pria do PascalSCADA:<\/p>\n<table>\n<thead>\n<tr>\n<th>Categoria<\/th>\n<th>Dispon\u00edvel<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Operadores<\/td>\n<td><code>+ - * \/ ^<\/code> (pot\u00eancia), <code>mod<\/code>, par\u00eanteses<\/td>\n<\/tr>\n<tr>\n<td>Compara\u00e7\u00e3o<\/td>\n<td><code>= &lt;&gt; &lt; &gt; &lt;= &gt;=<\/code><\/td>\n<\/tr>\n<tr>\n<td>L\u00f3gica<\/td>\n<td><code>and<\/code>, <code>or<\/code>, <code>not<\/code>, <code>xor<\/code>; <code>true<\/code>, <code>false<\/code><\/td>\n<\/tr>\n<tr>\n<td>Bits<\/td>\n<td><code>shl<\/code>, <code>shr<\/code><\/td>\n<\/tr>\n<tr>\n<td>Condicional<\/td>\n<td><strong><code>ifthen(condi\u00e7\u00e3o, seVerdadeiro, seFalso)<\/code><\/strong> \u2014 os dois valores devem ser num\u00e9ricos<\/td>\n<\/tr>\n<tr>\n<td>Matem\u00e1tica<\/td>\n<td><code>abs<\/code>, <code>sqr<\/code>, <code>sqrt<\/code>, <code>round<\/code>, <code>trunc<\/code>, <code>int<\/code>, <code>frac<\/code>, <code>exp<\/code>, <code>ln<\/code>, <code>log<\/code>, <code>sin<\/code>, <code>cos<\/code>, <code>arctan<\/code>, <code>pi<\/code><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Regras que v\u00eam do avaliador e valem a pena saber:<\/p>\n<ul>\n<li><strong>Uma compara\u00e7\u00e3o sozinha vale 1 ou 0<\/strong>: <code>Expression = 'A &gt; 5'<\/code> \u00e9 um tag booleano perfeito para um <code>THMICheckBox<\/code> ou uma zona.<\/li>\n<li>O resultado pode ser inteiro ou real; ambos viram o <code>Double<\/code> do tag sem perda. Um resultado <strong>texto<\/strong> (<code>'\"abc\"'<\/code>) \u00e9 erro.<\/li>\n<li>As letras das vari\u00e1veis n\u00e3o diferenciam mai\u00fasculas (<code>a+b<\/code> = <code>A+B<\/code>), mas prefira mai\u00fasculas para casar com as propriedades.<\/li>\n<li>Uma vari\u00e1vel usada na express\u00e3o sem tag ligado (<code>A+B<\/code> com <code>B = nil<\/code>) \u00e9 erro de avalia\u00e7\u00e3o, n\u00e3o zero.<\/li>\n<li>Divis\u00e3o por zero \u00e9 <strong>erro de avalia\u00e7\u00e3o<\/strong> (&#8220;division by zero&#8221; em <code>LastEvalutionError<\/code>, valor anterior mantido) \u2014 proteja na express\u00e3o: <code>ifthen(B=0, 0, A\/B)<\/code>.<\/li>\n<\/ul>\n<p>Exemplos:<\/p>\n<pre><code>A + B\nA \/ B * 100\n(A - 32) * 5 \/ 9\nifthen(A &gt; B, A, B)                       -- m\u00e1ximo\nifthen((A &gt; 80) and (B = 1), 1, 0)        -- alarme combinado\nifthen(B = 0, 0, A \/ B)                   -- divis\u00e3o protegida\nround(A \/ 10) * 10                        -- arredonda para a dezena\n(A shr 4) mod 16                          -- nibble alto de um byte\nsqrt(sqr(A) + sqr(B))                     -- m\u00f3dulo de um vetor\n<\/code><\/pre>\n<h5>Quando o valor \u00e9 recalculado<\/h5>\n<p>O <code>TNumericExprTag<\/code> n\u00e3o tem <code>RefreshTime<\/code>: ele \u00e9 <strong>dirigido a eventos<\/strong>. A express\u00e3o \u00e9 avaliada:<\/p>\n<ul>\n<li>quando qualquer tag ligado em <code>A<\/code>\u2026<code>J<\/code> muda de valor (<code>OnValueChange<\/code> da origem) ou falha uma escrita;<\/li>\n<li>quando <code>Expression<\/code> ou uma das vari\u00e1veis \u00e9 alterada;<\/li>\n<li>ao terminar de carregar o form\/datamodule.<\/li>\n<\/ul>\n<p>Se o resultado for igual ao anterior, nada \u00e9 notificado \u2014 um <code>ifthen(A&gt;80, 1, 0)<\/code> s\u00f3 dispara <code>OnValueChange<\/code> quando cruza o limite, n\u00e3o a cada leitura de <code>A<\/code>. Por isso ele \u00e9 barato: dez tags de origem lidos a 100 ms custam dez avalia\u00e7\u00f5es por ciclo, todas na thread principal, e s\u00f3 as mudan\u00e7as reais se propagam.<\/p>\n<p>O <code>ClockMonotonicTimeStamp<\/code> do tag \u00e9 o instante da \u00faltima mudan\u00e7a de resultado.<\/p>\n<h5>Erros<\/h5>\n<p>Quando a avalia\u00e7\u00e3o falha \u2014 sintaxe errada, vari\u00e1vel sem tag, resultado texto \u2014 o tag:<\/p>\n<ol>\n<li>guarda a mensagem em <code>LastEvalutionError<\/code>;<\/li>\n<li><strong>mant\u00e9m o valor anterior<\/strong> (n\u00e3o vira zero);<\/li>\n<li>dispara <code>OnReadFail<\/code>, o que faz os controles ligados mostrarem o selo de falha de comunica\u00e7\u00e3o, como se o tag tivesse parado de comunicar.<\/li>\n<\/ol>\n<p>Corrigida a express\u00e3o (ou ligada a vari\u00e1vel que faltava), a pr\u00f3xima avalia\u00e7\u00e3o com sucesso limpa o erro (<code>LastEvalutionError = 'OK'<\/code>) e o selo some. Em tempo de projeto, olhe <code>LastEvalutionError<\/code> no Object Inspector logo depois de digitar a express\u00e3o: o erro aparece ali na hora.<\/p>\n<p>Se um tag de origem for destru\u00eddo, a vari\u00e1vel correspondente volta a <code>nil<\/code> sozinha \u2014 e a express\u00e3o passa a falhar por vari\u00e1vel n\u00e3o ligada, em vez de acessar um objeto morto.<\/p>\n<h5>Encadeando express\u00f5es<\/h5>\n<p>Como o <code>TNumericExprTag<\/code> \u00e9 um <code>TPLCNumber<\/code>, ele pode ser vari\u00e1vel de <strong>outro<\/strong> <code>TNumericExprTag<\/code>. Isso permite dividir uma conta grande em etapas nomeadas (<code>Vazao_Total = A+B+C<\/code>, <code>Percentual_Linha1 = A\/B*100<\/code> com <code>B = Vazao_Total<\/code>) e reaproveitar o resultado intermedi\u00e1rio em v\u00e1rios lugares. Evite ciclos (um tag que dependa, direta ou indiretamente, de si mesmo): a avalia\u00e7\u00e3o entra em loop de notifica\u00e7\u00f5es.<\/p>\n<p>Ele tamb\u00e9m pode ser origem de um <a href=\"\/pb\/tags\/#TTagBit\"><code>TTagBit<\/code><\/a> (com <code>UseRawValue<\/code> conforme o caso) e de qualquer controle da HCl.<\/p>\n<h5>Exemplo passo a passo<\/h5>\n<p>Mostrar a efici\u00eancia de um motor a partir de pot\u00eancia el\u00e9trica e mec\u00e2nica lidas do CLP, com alarme abaixo de 85 %:<\/p>\n<ol>\n<li>Tags de comunica\u00e7\u00e3o <code>Pot_Eletrica<\/code> e <code>Pot_Mecanica<\/code> (<code>TPLCTagNumber<\/code>, <code>pttFloat<\/code>).<\/li>\n<li>Um <code>TNumericExprTag<\/code> chamado <code>Eficiencia<\/code>: <code>A = Pot_Mecanica<\/code>, <code>B = Pot_Eletrica<\/code>, <code>Expression = 'ifthen(B = 0, 0, A \/ B * 100)'<\/code>.<\/li>\n<li>Um <code>THMILabel<\/code> em <code>Eficiencia<\/code> com <code>NumberFormat = '#0.0'<\/code> e <code>Sufix = ' %'<\/code>.<\/li>\n<li>Outro <code>TNumericExprTag<\/code> chamado <code>Eficiencia_Baixa<\/code>: <code>A = Eficiencia<\/code>, <code>Expression = 'ifthen(A &lt; 85, 1, 0)'<\/code>.<\/li>\n<li>Um <code>THMIColorPropertyConnector<\/code> em <code>Eficiencia_Baixa<\/code> colorindo o painel de vermelho quando vale 1 \u2014 ou um <a href=\"\/pb\/event-and-alarm-loggers\/\"><code>THMIAlarmLogger<\/code><\/a> registrando o alarme.<\/li>\n<\/ol>\n<p>Nenhuma linha de c\u00f3digo, e nada foi acrescentado ao programa do CLP.<\/p>\n<h5>Exemplos relacionados<\/h5>\n<ul>\n<li><a href=\"https:\/\/github.com\/fluisgirardi\/pascalscada_v0\/tree\/master\/examples\/laz_numericexpr\"><code>examples\/laz_numericexpr<\/code><\/a> \u2014 dois <code>TPLCTagNumber<\/code> movidos por <code>THMITrackBar<\/code> e um <code>TNumericExprTag<\/code> com <code>Expression = 'A+B'<\/code> mostrado em <code>THMILabel<\/code>; o lugar para experimentar as express\u00f5es da tabela acima.<\/li>\n<\/ul>","protected":false},"excerpt":{"rendered":"<p>Introdu\u00e7\u00e3o O TNumericExprTag \u00e9 um tag num\u00e9rico virtual: ele n\u00e3o tem driver nem endere\u00e7o, e o seu valor \u00e9 o resultado de uma express\u00e3o calculada a partir de outros tags. &#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-1150","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/www.pascalscada.com\/pb\/wp-json\/wp\/v2\/pages\/1150","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.pascalscada.com\/pb\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.pascalscada.com\/pb\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.pascalscada.com\/pb\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.pascalscada.com\/pb\/wp-json\/wp\/v2\/comments?post=1150"}],"version-history":[{"count":6,"href":"https:\/\/www.pascalscada.com\/pb\/wp-json\/wp\/v2\/pages\/1150\/revisions"}],"predecessor-version":[{"id":1241,"href":"https:\/\/www.pascalscada.com\/pb\/wp-json\/wp\/v2\/pages\/1150\/revisions\/1241"}],"wp:attachment":[{"href":"https:\/\/www.pascalscada.com\/pb\/wp-json\/wp\/v2\/media?parent=1150"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}