Azioni

L'editor di azione: identità e posizione, il blocco di script, che cosa uno script può leggere e scrivere, ogni messaggio di convalida, il verbo Esegui e la sua registrazione, chi ha avviato l'ultima esecuzione, che cosa esegue l'azione, Abilita e Disabilita, Copia ed Elimina, e i limiti di un'esecuzione.

Visualizza come Markdown

Un'azione è un blocco di istruzioni JavaScript che legge lo spazio degli indirizzi e scrive variabili di memoria e tag. Le azioni sono la via di scrittura di Logic: girano su richiesta (Esegui), su un attivatore, da un Pulsante di dashboard collegato a esse, oppure all'Avvio o al Completamento di una prova di Process, attraverso un comando di azione Logic. Seleziona un'azione sotto la sezione Actions della colonna di Logic, oppure premi Apri sulla sua riga nella tabella della sua cartella o della radice della sua sezione, per aprirne l'editor (un'azione creata con Nuovo… > Nuova azione non si apre, ma viene evidenziata in quella tabella, vedi Creare e copiare); leggere richiede Visualizzare Logic, modificare Configurare Logic, eseguire Comandare Logic.

L'intestazione mostra il segno dell'azione, il nome, la riga « Uno script che si esegue su richiesta, o che un attivatore esegue al posto dell'operatore, scrivendo variabili e tag. » e una pillola Disabilitata mentre l'azione è spenta. La colonna del modulo contiene IDENTITÀ, SCRIPT e Usato da; la barra contiene ULTIMA ESECUZIONE.

Identità

Campo Che cos'è Valori e valore predefinito Effetto
Nome Il nome visualizzato e l'etichetta sotto cui viene registrata un'esecuzione. Testo libero; un'azione nuova si chiama ActionN; un nome vuoto si salva come Action. Si salva da solo a ogni battuta. Le azioni sono referenziate per identità, quindi rinominare non riscrive nulla e non rompe nulla.
Riga di posizione Dove l'azione è archiviata, a parole: Actions / Startup / Walk setpoint. Sola lettura. Un'azione non ha indirizzo; la riga è un posto, mai qualcosa da scrivere in un'espressione. Si cambia con Sposta in nell'albero.
Descrizione (facoltativa) Testo libero. Vuota all'inizio. Nessuno.

Script

La scheda SCRIPT è l'editor di script con il suggerimento « Istruzioni JavaScript (let, if, for, funzioni). Le variabili di memoria si scrivono per assegnazione (Variables.Setpoint = 21.5;) e i tag con Connector.Driver.Device.Tag.Write(valore). Le letture non passano per un controllo di qualità: verificare .State() e proteggere i vuoti con ?? prima dei calcoli. » Lo script si salva da solo a ogni battuta e viene convalidato ogni mezzo secondo; il messaggio compare sotto il suggerimento. La convalida non esegue mai lo script.

Uno script è normale JavaScript in modalità strict (variabili locali, if, cicli, funzioni, Math, Date, JSON) sullo stesso spazio degli indirizzi e sulla stessa superficie di lettura delle espressioni, con due differenze. Le letture non passano dal controllo di qualità: un vuoto si legge come null, quindi verifica .State() e proteggi l'aritmetica con ??; trattare una lettura cattiva è proprio ciò a cui servono le azioni. E uno script può leggere le variabili di espressione (il loro valore pubblicato); solo un'espressione non può referenziarle.

Un punto dal vivo (un tag, una variabile, un allarme, un canale di Process) è il punto, non il suo valore, quindi leggerne il valore tramite un membro: if (Variables.Start.ValueToBool()), Variables.Level.ValueToReal() > 80, if (Alarms.High.IsActive). Scritto nudo nel test di if, while, do, for o ?:, come operando di !, && o ||, in un confronto o nell'aritmetica (comprese += e le altre assegnazioni composte), conterebbe sempre come vero o non ne leggerebbe mai il valore: if (Variables.Start) { … } eseguirebbe il suo blocco a ogni esecuzione, qualunque valore abbia la variabile. La convalida lo rifiuta e nomina il membro da leggere al suo posto (Che cosa rifiuta la convalida). Un punto passato a una funzione, e qualsiasi membro di un punto, resta come è scritto. Lo stesso vale per uno switch su un punto nudo o un case che ne nomina uno (leggere .Value), per il lato sinistro di ?? (leggere .Value) e per un segnaposto ${…} di un template literal (leggere .ValueToString()). Un'assegnazione composta a una variabile (Variables.Total += 1, -=, ||= e le altre) legge la variabile come il punto prima di scrivere, quindi viene rifiutata con l'assegnazione semplice che funziona: Variables.Total = Variables.Total.ValueToReal() + 1.

Che cosa può scrivere un'azione

Esattamente due costrutti modificano l'impianto, ed entrambi passano dall'unico punto di scrittura registrato:

Scrittura Forma Destinazione
Assegnazione a memoria Variables.Setpoint = 21.5; oppure Variables.Forno.Setpoint = 21.5; Una variabile di memoria, livelli di cartella compresi. Il valore viene convertito al tipo di uscita della variabile e marcato per la ritenzione quando la variabile è ritentiva. Si applica subito, quindi la riga successiva dello stesso script legge ciò che ha appena scritto.
Scrittura di tag Connector.Simulated.Pump.Cmd.Write(true); oppure …Cmd.WriteAndWait(true); Un tag vivo. Il valore è un valore ingegneristico: percorre all'indietro la catena di conversione del tag fino al valore grezzo che il dispositivo accetta, e il punto di scrittura lo consegna alla coda di consegna propria di quel dispositivo, dove i valori dovuti a quel dispositivo partono uno alla volta nell'ordine in cui sono arrivati.

Un tag porta due verbi di scrittura, e lo script sceglie a seconda che gli serva la risposta:

Verbo Argomenti Omessi Restituisce Aspetta l'apparecchiatura
Write(value) Il valore. Non c'è nulla da omettere. Nulla. No. Il valore viene consegnato alla coda del dispositivo e la riga successiva parte.
WriteAndWait(value, timeoutMs, continueOnFailure) Il valore, quanti millisecondi aspettarlo e se proseguire quando non riesce. timeoutMs: al suo posto vale il tempo di attesa proprio del driver. continueOnFailure: true. true soltanto quando l'apparecchiatura ha accolto il valore; false in ogni altro caso, valore superato compreso. Sì, finché non arriva la conferma o non scade il tempo di attesa.

L'attesa avviene sulla corsia delle azioni, quindi un dispositivo lento trattiene soltanto le azioni in coda dietro a questa, e non ritira mai la scrittura: un valore non confermato in tempo è ancora in viaggio, e la sua conferma viene registrata quando arriva. È proprio ciò che la risposta dice, con queste parole: « 'Cmd' non è stato confermato entro 2000 ms; il valore è ancora in viaggio verso l'apparecchiatura. » Con continueOnFailure omesso o true il verbo risponde false e lo script prosegue; con false l'esecuzione finisce a quella riga, nominando la destinazione e il motivo.

Nessuna scrittura interrompe da sola uno script. Una scrittura rifiutata prima che parta qualcosa e una scrittura che l'apparecchiatura rifiuta o non risponde mai falliscono da sole: il resto dello script viene eseguito, e l'esecuzione si chiude come riuscita con scritture non riuscite. Nulla viene nemmeno riportato indietro, quindi ciò che era stato applicato prima di una scrittura fallita resta applicato, e il diario mostra ogni scrittura da vecchio a nuovo sotto l'origine Azione. I motivi con cui si legge un rifiuto:

  • « Scrittura su 'X' rifiutata: il valore non è finito. » (NaN o infinito)
  • « 'Tag' non è materializzato nel connector. » (un dispositivo disabilitato, o un tag che il Connector non porta)
  • « Non è stato possibile convertire 'value' per 'Tag'. »
  • « 'X' è protetta in scrittura da '<chi la tiene>'. » (una ricetta in corso o un interblocco tengono la sorgente)
  • « 'Cmd' non è stato scritto: il dispositivo non è connesso. » (il dispositivo è già noto come fermo, quindi per lui non viene messo nulla in coda)
  • « Scrittura su 'Tag' non riuscita: il Connector non è in esecuzione. »
  • « Variabile 'X' non trovata. » e « 'Variables.X' è una variabile di espressione: viene calcolata, mai scritta. »
  • Qualunque cosa abbiano risposto l'apparecchiatura o il suo driver, con le loro parole, quando il rifiuto è venuto dal filo.

Un valore che una scrittura più recente sullo stesso tag ha superato nella coda del dispositivo non è un guasto: « 'Cmd' è stato sostituito da un valore più recente prima dell'invio. » è esattamente quello che è successo, il valore più recente è quello che l'apparecchiatura riceve, e non se ne riferisce da nessuna parte se non a una WriteAndWait, che risponde false perché questo valore non è quello partito.

Che cosa invece chiude un'esecuzione: un errore dello script stesso (un errore di sintassi, un indirizzo che non nomina nulla, un'eccezione), una WriteAndWait(…, false) la cui scrittura non è riuscita, e i limiti di esecuzione sotto Esegui.

Quali guasti l'esecuzione stessa possa riferire discende da chi ha aspettato. Una Write rifiutata prima che parta qualcosa è rifiutata mentre lo script è ancora in corso, quindi l'esecuzione la conta e il riquadro delle attività lo dice; una Write che l'apparecchiatura rifiuta dopo ottiene risposta molto tempo dopo la fine dell'esecuzione, quindi è la console degli Eventi, sotto il nome dell'azione, a portarla, e mai l'esecuzione. Una WriteAndWait è rimasta ad aspettare la risposta, quindi la conta comunque sia andata.

Che cosa rifiuta la convalida

Messaggio Causa
« Lo script è obbligatorio. » L'editor è vuoto.
« Errore dello script: … » Il testo non si analizza.
« Gli indirizzi devono essere statici: 'Root' viene raggiunto con un segmento calcolato. » Variables[name] o qualunque segmento non letterale.
« Indirizzo incompleto '…'. » Un riferimento troppo corto per nominare un punto.
« 'X' non è stato trovato. » Una lettura, o una destinazione di assegnazione, che non si risolve in nulla.
« 'X' è un valore: leggerlo senza .Value. » Un membro di punto dal vivo (.Value, .ValueToBool(), .State(), …) letto su un punto che entra nello script come il suo valore: System.StartTime.Value o Process.Oven_soak.Oven_1.Procedure.Running.ValueToBool(). Il messaggio nomina il membro scritto.
« 'X' è il punto stesso, non il suo valore: come condizione conta sempre come vero. Leggerne il valore con X.ValueToBool(). » Un punto dal vivo scritto nudo dove viene verificato come vero o falso: il test di if, while, do, for o ?:, o un operando di !, && o \|\| (if (Variables.Start)). Su un allarme il messaggio nomina .IsActive.
« 'X' è il punto stesso, non il suo valore: un confronto o un calcolo non ne legge mai il valore. Leggerne il valore con X.ValueToReal(). » Un punto dal vivo scritto nudo in un confronto o nell'aritmetica (Variables.Level > 80, total += Variables.Level). Per ==, !=, === e !== il messaggio nomina .Value, e su un allarme .IsActive. Uno switch su un punto nudo, o un case che ne nomina uno, riceve il messaggio con .Value.
« 'X' è il punto stesso, non il suo valore: non è mai vuoto, quindi ?? lo tiene sempre. Leggerne il valore con X.Value. » Un punto dal vivo scritto nudo a sinistra di ?? (Variables.Level ?? 0). Su un allarme il messaggio nomina .IsActive.
« 'X' è il punto stesso, non il suo valore: scritto dentro un testo non ne mostra mai il valore. Leggerne il valore con X.ValueToString(). » Un punto dal vivo scritto nudo in un segnaposto ${…} di un template literal. Su un allarme il messaggio nomina .IsActive.
« '+=' legge X come il punto stesso, non il suo valore, prima di scrivere. Usare X = X.ValueToReal() + 1. » Un'assegnazione composta a una variabile: +=, -=, *=, /=, %=, **=, quelle bit a bit, &&=, \|\|= e ??=. Il messaggio nomina l'operatore scritto e l'assegnazione semplice che fa ciò che si legge, con il lato destro come è scritto: .ValueToReal() per l'aritmetica, .ValueToInteger() per quelle bit a bit, .ValueToBool() per &&= e \|\|=, .Value per ??=.
« Assegnare alla variabile stessa: Variables.Nome = valore. » Il lato sinistro di = nomina un membro, oppure scende più a fondo di quanto l'indirizzo di una variabile consenta.
« 'X' è una variabile di espressione: viene calcolata, mai scritta. » Assegnare a una variabile di espressione.
« I punti 'Connector' sono di sola lettura. I tag si scrivono con Connector.Driver.Device.Tag.Write(valore). » Assegnare a una radice diversa da Variables (Process ha il suo messaggio).
« I punti di Process sono di sola lettura nelle azioni; i comandi di prova e le scritture di preparazione non sono supportati dagli script. » Assegnare a un punto di Process, o chiamarci Write.
« I comandi di prova di Process non possono essere richiamati dalle azioni; i punti di comando espongono valori di prontezza in sola lettura. » Chiamare Process.<Model>.<Unit>.Commands.Start() o qualunque altro punto di comando; un punto Commands legge soltanto se il comando potrebbe essere eseguito adesso.
« Gli operatori ++ e -- non sono supportati sui punti. Usare Variables.Nome = Variables.Nome.ValueToReal() + 1. » Variables.N++.
« Write(valore) è la scrittura del tag: Connector.Driver.Device.Tag.Write(valore). » Write chiamato su qualcosa che non è l'indirizzo di un tag. La frase nomina il verbo che è stato usato, quindi WriteAndWait riceve la stessa frase con il proprio nome in entrambi i punti.
« Il tempo di attesa di WriteAndWait deve essere un numero di millisecondi: WriteAndWait(valore, 2000). » Il secondo argomento è scritto per esteso e non è un numero.
« Il proseguire in caso di errore di WriteAndWait deve essere true o false: WriteAndWait(valore, 2000, false). » Il terzo argomento è scritto per esteso e non è true né false.
« WriteAndWait accetta al massimo tre argomenti: il valore, il tempo di attesa in millisecondi e se proseguire in caso di errore. » Un quarto argomento.

Le tre frasi di WriteAndWait giudicano ciò che il testo stesso dice. Un argomento che l'editor non può risolvere leggendolo (una variabile, un'espressione calcolata) viene giudicato quando la chiamata avviene davvero, e allora la stessa frase chiude l'esecuzione a quella riga.

Le variabili locali di JavaScript (let x = 1; x = x + 1;) non sono toccate da queste regole.

Esegui

Esegui è il verbo iniziale della barra sopra un'azione aperta; è disegnato solo per un ruolo che ha Comandare Logic ed è grigio con « Runtime fermo » mentre la stazione è in sola lettura (il blocco di progettazione non lo rende grigio: eseguire è comandare, non configurare). Quando il ruolo ha anche Configurare Logic, la bozza nell'editor viene salvata per prima e l'esecuzione riguarda la definizione salvata; senza quel permesso, l'esecuzione riguarda la definizione durevole (i campi sono di sola lettura, quindi le due non differiscono mai).

Ogni esecuzione gira sulla corsia delle azioni con un motore di script nuovo, così nessuno stato trapela tra un'esecuzione e l'altra e due script non girano mai insieme: girano uno alla volta, nell'ordine in cui sono stati chiesti. La corsia non è il ciclo da 100 ms. Le espressioni, gli allarmi, le tacitazioni e le scritture che arrivano da altrove proseguono mentre uno script gira, quindi un ciclo può capitare nel mezzo di uno script e due letture dello stesso script possono vedere cicli diversi; in cambio, un'azione che si ferma ad aspettare un dispositivo ritarda soltanto le azioni in coda dietro di lei e nient'altro nella stazione.

Un'esecuzione viene interrotta oltre 500 ms (« Lo script è stato interrotto: ha superato il limite di tempo di 500 ms (ciclo infinito?). »), 100.000 istruzioni o 4 MB di memoria, ciascuno con un messaggio che nomina il limite. I 500 ms misurano lo script che gira: il conteggio è sospeso per tutto il tempo in cui una WriteAndWait aspetta un dispositivo e riprende dopo, così uno script corretto non paga mai per un'apparecchiatura lenta. Un'azione disabilitata fallisce senza essere eseguita (« L'azione è disabilitata. »); un runtime fermo risponde « Il runtime della logica non è in esecuzione. »

Il registro delle azioni riferisce « Esecuzione riuscita in 12 ms. » quando ogni scrittura è arrivata, « Esecuzione riuscita in 12 ms, 1 scrittura non riuscita: Cmd: 'Cmd' non è stato scritto: il dispositivo non è connesso. » quando lo script è finito e una scrittura no (con « Esecuzione riuscita in 12 ms, 3 scritture non riuscite, la prima: Cmd: 'Cmd' non è stato scritto: il dispositivo non è connesso. » dal secondo guasto in poi), oppure « Esecuzione non riuscita: <motivo> » quando è l'esecuzione stessa a finire male.

L'esecuzione non riuscita arriva alla console di Eventi, sotto il nome dell'azione insieme a chi l'ha invocata: Manuale per il verbo, Dashboard per un Pulsante, MCP per l'agente, il nome dell'attivatore per un'esecuzione pianificata, e Process · <unit> · <action> per una prova di Process. Viene registrata come avviso la prima volta, riassunta mentre continua a fallire con la stessa causa, e annotata ancora una volta quando rientra, e quella riga di rientro è l'unica voce che un'esecuzione riuscita scriva mai lì. Una scrittura rifiutata arriva alla console allo stesso modo e sotto lo stesso nome, e per la stessa ragione è l'azione a essere nominata e non l'esecuzione: la risposta dell'apparecchiatura può arrivare secondi dopo la fine dell'esecuzione, e non resta più nessuna esecuzione da far fallire. Un'esecuzione che semplicemente riesce, e ogni valore che scrive, va solo alla cattura dettagliata: uno script che scrive a ogni ciclo produce migliaia di valori, e una console piena di quelli è una console in cui nessuno riesce a leggere le notizie della stazione.

La barra ULTIMA ESECUZIONE mostra quando l'azione è stata eseguita l'ultima volta (oppure « Mai eseguita. »), « Durata: N ms », chi l'ha avviata (qui sotto), l'ultimo errore in una nota di attenzione, le scritture non riuscite di quell'esecuzione in una nota di attenzione tutta loro (la stessa frase che portava il registro delle attività, così un'esecuzione riuscita che ha comunque lasciato un setpoint dov'era lo dice anche qui), e il promemoria « Le esecuzioni avvengono sulla corsia delle azioni, una alla volta. Solo un'esecuzione non riuscita arriva alla console Eventi; un'esecuzione riuscita, e ogni valore che scrive, resta nella cattura dettagliata. Gli attivatori richiamano le azioni secondo la pianificazione. » La barra appartiene all'azione viva: un salvataggio automatico non la svuota mai; un riavvio del runtime sì.

Avviata da

Sotto la durata, la riga « Avviata da: … » dice che cosa ha eseguito l'azione l'ultima volta:

Che cosa l'ha eseguita La riga dice
Esegui in questa pagina « Avviata da: Esecuzione da questa pagina »
Un Pulsante di dashboard « Avviata da: Un pulsante di una dashboard »
L'agente (action_run) « Avviata da: Agente »
Un attivatore Il nome dell'attivatore: « Avviata da: Heater off when too hot ».
Un comando di azione Logic di una prova di Process L'unità e il gruppo di azioni: « Avviata da: Process · Oven 1 · After soak ».

Usato da

Il riquadro Usato da elenca tutto ciò che nella stazione esegue l'azione (« Tutto ciò che nella stazione esegue questa azione. »), letto quando l'azione viene aperta: una riga per ogni comando di Process che la avvia, per ogni attivatore collegato a lei e per ogni Pulsante di dashboard che la esegue.

Genere Posizione Nome
Comando di Process Il modello. Il gruppo di azioni e il momento: After soak · Completamento.
Attivatore Vuoto. L'attivatore, sotto le sue cartelle.
Pulsante La dashboard. La scritta del Pulsante.

Senza nulla, dice « Niente nella stazione esegue questa azione. »; quando i riferimenti non si possono leggere, « Questa azione potrebbe essere ancora in uso. Riprovare prima di eliminarla o rinominarla. » con Riprova. Il riquadro legge soltanto, così ogni ruolo lo vede, anche mentre la stazione è bloccata in ingegneria. Leggerlo prima di disabilitare o eliminare un'azione: un comando di Process la cui azione è sparita non esegue nulla e lo dice sulla propria pagina.

Leggere la prova che l'ha avviata

Un'azione che una prova di Process avvia al Completamento legge quella prova sotto il ramo LastRun dell'unità, che la contiene già quando l'azione gira; una avviata all'Avvio legge i metadati della prova che parte sotto Procedure.Metadata. I punti di Process si leggono come valori semplici, senza .Value dopo, a differenza delle variabili, e un punto che la prova non contiene si legge null, quindi proteggerlo:

if (Process.Oven_soak.Oven_1.LastRun.Verdict === "Ok") {
  Variables.Last_serial = Process.Oven_soak.Oven_1.LastRun.Metadata.Serial;
}
const peak = Process.Oven_soak.Oven_1.LastRun.Evaluations.Soak_check.Temperature_in_range.MaxValue;
if (peak !== null) {
  Variables.Last_peak = peak;
}

Gli indirizzi si scrivono per intero, come ogni indirizzo di uno script: un indirizzo non si può comporre a partire da una variabile.

Comandi

Comando Che cosa fa Grigio quando Non disegnato quando
Esegui Salva la bozza (con Configurare Logic) e mette in coda un'esecuzione. Il runtime è fermo. Il ruolo non ha Comandare Logic.
Abilita / Disabilita Commuta Abilitata; un'azione disabilitata rifiuta Esegui e lascia inattivo ogni attivatore collegato a lei, e un comando di Process che la avvia è rifiutato ogni volta e resta Da rivedere. La stazione è bloccata o in sola lettura. Il ruolo non ha Configurare Logic.
Copia Duplica l'azione nella radice Actions con un nome libero ed evidenzia la copia nella tabella di quella radice invece di aprirla (Creare e copiare); la copia non ha alcuna esecuzione registrata né alcun attivatore collegato. La stazione è bloccata o in sola lettura. Il ruolo non ha Configurare Logic.
Elimina azione Chiede « Eliminare 'X'? Questo rimuove l'azione. L'operazione non è reversibile. » Un attivatore collegato a lei resta senza collegamento, non viene eliminato; un comando di Process che la esegue resta, non esegue nulla e resta Da rivedere. La stazione è bloccata o in sola lettura. Il ruolo non ha Configurare Logic.

Il pannello della radice Actions elenca ogni azione con il suo stato nella colonna Valore: « Abilitata · ultima esecuzione <quando> », con « · errore » quando l'ultima esecuzione è fallita, « · scrittura non riuscita » quando è riuscita e una sua scrittura no, oppure « Disabilitata ».

Che cosa non fanno le azioni

Non inviano i comandi di prova di Process, non preparano una procedura e non aggiungono un commento a una prova (quei punti sono valori di prontezza e di preparazione, in sola lettura); non tacitano gli allarmi; non girano in parallelo; non conservano stato tra un'esecuzione e l'altra e non condividono funzioni; e non scattano per un cambio di valore o per una condizione, ma solo con Esegui, con l'intervallo di un attivatore, con un pulsante di dashboard, con l'agente o con il comando di una prova di Process. Una prova di Process non attende mai l'azione che avvia, e il risultato dell'azione non viene mai scritto nella prova.