Modello dati

Panoramica

Tutti i dati sono organizzati secondo una gerarchia a tre livelli:

Module
  └─ Property (una o più per modulo)
       └─ Element (uno o più per proprietà)

Connessione e stato iniziale

Trovare il server

Un client web è servito dal server OST stesso, quindi conosce già il proprio host — l’URL WebSocket deve essere derivato dall’URL della pagina, non chiesto all’utente:

URL della paginaURL WebSocket
http://<host>/ws://<host>:9624 (diretto, porta fissa)
https://<host>/wss://<host>/ws/ (porta 443 implicita, tramite un percorso proxy inverso — le installazioni SSL non espongono direttamente la porta 9624)

È una convenzione, non qualcosa imposto dal protocollo — ma tutti i client esistenti la seguono, e un client che invece chiede all’utente di digitare a mano un URL WebSocket risolve un problema che nel caso web non esiste.

Un client non-web (mobile, desktop) non ha un URL di pagina da cui derivare nulla, e ha bisogno che l’host gli venga fornito in altro modo:

  • mDNS/Zeroconf: il server si annuncia sulla rete locale con il tipo di servizio _ostserver_ws._tcp nel dominio local, con un record TXT OstServer=Observatoire Sans Tete. La porta annunciata è sempre 9624. Questo permette a un client di scansionare i server disponibili sulla rete invece di richiedere che l’utente conosca un IP — utile per una prima connessione, o ogni volta che potrebbe esserci più di un server OST nelle vicinanze.
  • Inserimento manuale, memorizzato: sia esso scoperto o digitato, un client dovrebbe mantenere una piccola cronologia dei server già usati (host/porta/sicuro) in modo che riconnettersi in seguito non richieda di ripetere la scoperta o l’inserimento. È una comodità lato client, non parte del protocollo.

In ogni caso, l’URL di base dei media (per immagini/video, vedi la nota “URL dei media” più sotto) segue lo stesso host, con la porta HTTP(S) standard (80/443) anziché 9624 — un client non-web ha bisogno anche di questo host, esattamente per la stessa ragione per cui ha bisogno dell’host WebSocket.

Alla connessione, inviare una richiesta DU (vedi Client → Server).
Il server risponde con un messaggio dump completo (d) contenente tutti i moduli, i dati del controller e gli elenchi di file.

I dati propri del controller (carico del server, configurazioni dei moduli, driver INDI, file system) non fanno parte del modello a moduli descritto in questa pagina — vedi Controller, GlobalDatastore e file.


Moduli

Un modulo rappresenta un sottosistema funzionale (messa a fuoco, guida, camera allsky, ecc.).
Il suo identificatore è il suo nome interno (senza spazi).

CampoTipoDescrizione
infos.modulelabelstringNome visualizzato del modulo
infos.descriptionstringDescrizione del modulo
infos.templatestringSuggerimento di visualizzazione per il frontend (quale rendering usare), es. "focus", "guider" — puramente estetico, può essere assente
infos.librarystringIl tipo del modulo, minuscolo (la libreria da cui è stato caricato, es. "focus")
infos.classnamestringIl tipo del modulo, con maiuscole/minuscole esatte (es. "Focus")
infos.familiesarray di stringCapacità dichiarate da questa istanza di modulo (es. ["guiding"]) — zero, una o più
propertiesobjectMappa nome proprietà → oggetto proprietà
globallovsobjectMappa chiave LOV → oggetto LOV (liste di valori a livello modulo)
profile.namestringNome del profilo attivo
profile.changedboolIndica se il profilo ha modifiche non salvate
Formato wire

Nel formato JSON di rete, properties è abbreviato in p, globallovs in l, e profile in f. I client devono gestire queste chiavi abbreviate.

Tipo, template e famiglia sono 3 cose diverse

È allettante trattare infos.template come l’identità del modulo — non farlo. Sono 3 nozioni indipendenti:

  • il tipo (infos.library/infos.classname) è la vera identità del modulo — la libreria da cui è stato caricato. Due istanze dello stesso tipo (es. due moduli Focus) condividono sempre lo stesso tipo.
  • il template (infos.template) è solo un suggerimento di visualizzazione per il frontend, puramente estetico e del tutto opzionale. Due tipi diversi possono condividere lo stesso template generico (es. tutto ciò che viene renderizzato con "default").
  • la famiglia (infos.families) è una capacità che un modulo dichiara su se stesso, indipendente sia dal tipo che dal template. Un modulo può dichiarare zero, una o più famiglie — esiste per permettere a un client di puntare a “qualsiasi modulo in grado di guidare”, ad esempio, senza doversi preoccupare che il tipo effettivo sia Guider o un’implementazione diversa della stessa capacità.

loadedModules-<type> e loadedFamilies-<family> (più sotto) fanno riferimento a infos.library/infos.families in minuscolo; profiles-<Type> fa invece riferimento a infos.classname con maiuscole/minuscole esatte. Usare direttamente il campo corrispondente — mai derivarne uno dall’altro trasformando le maiuscole/minuscole lato client.

Campi interni

infos porta anche diversi campi interni di build/versione (baseGithash, thisversion, startdatetime, e altri) — dettaglio implementativo, non fa parte del contratto stabile lato client, non documentato individualmente qui.


Proprietà

Una proprietà raggruppa elementi collegati. Le proprietà sono mostrate in modo gerarchico tramite level1 e level2.

CampoTipoDescrizione
labelstringEtichetta visualizzata
orderstringOrdine di ordinamento allo stesso livello
level1stringPrimo livello gerarchico (es. nome della scheda)
level2stringSecondo livello gerarchico (es. nome del gruppo)
statusint0 Standby · 1 OK (verde) · 2 Occupato (giallo) · 3 Errore (rosso)
permissionint0 Solo lettura · 1 Solo scrittura · 2 Lettura-scrittura
enabledboolIndica se è possibile interagire con la proprietà
badgeboolIndica se la proprietà è nell’elenco dei preferiti
preicon1stringIcona prima dell’etichetta (nome icona Google Fonts)
preicon2stringSeconda icona prima dell’etichetta
posticon1stringIcona dopo l’etichetta
posticon2stringSeconda icona dopo l’etichetta
showEltsboolIndica se i valori degli elementi devono essere mostrati in linea
hasprofileboolIndica se la proprietà è salvata nei profili
freevaluestringCampo di testo libero arbitrario
ruleintRegola di raggruppamento degli elementi bool: 0 UnoTra (radio) · 1 AlPiùUno · 2 Qualsiasi
elementsobjectMappa nome elemento → oggetto elemento
preicon/posticon: azioni cliccabili, non decorazioni

Un valore non vuoto di preicon1/preicon2/posticon1/posticon2 è il nome dell’icona Google Fonts (Material Icons) da visualizzare — ed è cliccabile: cliccandoci si invia I1/I2/I3/I4 senza valore né parametro, solo un payload vuoto che punta alla proprietà. Una stringa vuota significa che non c’è alcuna icona in quella posizione — nulla da visualizzare, nulla da cliccare, nulla da inviare. Le preicon/posticon a livello di elemento (più sotto) funzionano allo stesso modo, tramite J1/J2. Vedi Azioni sulle icone per il riferimento completo dei messaggi.

permission governa gli elementi editabili, non la visualizzazione del valore

permission: 1 (“solo scrittura”) non significa “nascondere il valore corrente” — significa che la proprietà non ha nulla di significativo da rileggere come input inviabile (un’azione a fuoco e dimentica). Una proprietà il cui unico elemento è di tipo solo-visualizzazione (img, video, light, prg, message) può legittimamente essere permission: 1 — un’anteprima live della camera ne è un esempio reale — e deve comunque essere renderizzata a partire dal proprio valore, indipendentemente da permission. Condizionare l’interattività (mostrare un input, abilitare un invio) a permission, ma condizionare il rendering al tipo dell’elemento, non a permission.

rule ha senso solo quando tutti gli elementi sono bool

rule è presente su ogni proprietà (default 0), ma ha significato — e va considerato — solo quando tutti gli elementi della proprietà sono di tipo bool. Compare anche, in modo inerte, su proprietà i cui elementi sono un mix di altri tipi (es. una coppia di coordinate con due elementi float) — un client dovrebbe ignorarlo in quel caso, invece di provare a renderizzare, ad esempio, due float come un gruppo radio.

badge è impostato dall’autore del modulo, non da un client a runtime

Non esiste alcun messaggio client che modifichi badge — è fisso per proprietà/elemento nella definizione stessa del modulo, non un preferito per utente/sessione che un client può impostare. Una schermata “preferiti” può leggere e aggregare ciò che è già segnato, ma non ha modo di lasciare che un utente marchi qualcosa come preferito da sé.

level1/level2/order sono informativi, non un contratto

Questi campi sono puramente informativi — il backend non li impone mai, e un client è libero di rispettarli parzialmente o per niente. Esprimono solo il modo in cui lo sviluppatore di un modulo ha pensato di organizzare le proprie proprietà. È esattamente ciò che permette a client generici (Rooster, ostnoctua) di adattarsi a qualsiasi modulo senza UI dedicata per modulo.

Il client di riferimento (Rooster) raggruppa le proprietà così, per un client che punti a un layout generico simile:

  1. level1 vuoto → proprietà mostrata fuori da qualsiasi scheda (renderizzata direttamente, ordinata per order).
  2. level1 valorizzato → raggruppata in una scheda chiamata level1. Al suo interno, level2 vuoto → mostrata direttamente sotto la scheda; level2 valorizzato → in un gruppo comprimibile chiamato level2.
  3. L’ordine delle schede (e dei gruppi all’interno di una scheda) non è un campo separato — si deduce dal valore order più piccolo tra le proprietà di quel gruppo.
  4. All’interno di un gruppo, le proprietà sono ordinate per il proprio order.

Due cose da sapere se si costruisce qualcosa di simile:

  • level1/level2 formano una “falsa gerarchia” per scelta di progettazione, non una struttura a due livelli garantita — nulla impedisce a un modulo di lasciare level1 vuoto valorizzando level2 (Rooster non gestisce nemmeno questa combinazione: la proprietà finisce semplicemente nel gruppo “senza level1”, level2 ignorato).
  • level1/level2 sono etichette tradotte, tramite lo stesso meccanismo di label/hint. Usare il valore grezzo della stringa come chiave di stato dell’interfaccia (es. un pannello espanso/compresso) è fragile al cambio di lingua lato client — non un bug, solo da evitare.

Proprietà con griglia (quando hasGrid: true):

CampoTipoDescrizione
hasGridboolLa proprietà contiene dati tabulari
showGridboolLa griglia deve essere mostrata di default
gridLimitintNumero massimo di righe nella griglia
gridheadersstring[]Ordine delle colonne (nomi degli elementi)
gridarray di arrayRighe della griglia; ogni riga è un array di valori nell’ordine di gridheaders

Proprietà con grafico (quando hasGraph: true):

CampoTipoDescrizione
hasGraphboolLa proprietà contiene un grafico — sempre presente, anche quando false
graphTypestring"XY" · "DY" · "SXY" · "SDY" · "PHD" — vedi sotto
graphParamsobjectQuale elemento fornisce quale ruolo del grafico — vedi sotto

Una proprietà con grafico è sempre anche una proprietà a griglia (hasGrid: true, vedi sopra): ogni riga della griglia è un punto dati, e graphParams associa un ruolo astratto del grafico alla chiave dell’elemento (uno dei gridheaders) il cui valore, in quella riga, fornisce quel ruolo.

graphTypeFormaRuoli di graphParams
XYUna sola serie a dispersione, limiti degli assi fissiX, Y, Xmin, Xmax, Ymin, Ymax, più opzionalmente graphColor (sotto)
DYUna sola serie, data/ora sull’asse XD (elemento data/ora), Y, più opzionalmente graphColor (sotto)
SXYPiù serie a dispersione, raggruppate da una chiave di serieS (elemento nome serie), X, Y, più opzionalmente graphColors/axis/mapping (sotto)
SDYPiù serie, data/ora sull’asse X, raggruppate da una chiave di serieS, D, Y, più opzionalmente graphColors/axis/mapping
PHDGrafico di guida dedicato in stile PHD2 (deriva + SNR + RMS)D, RA, DE, pRA, pDE, RSB, RMS — ruoli fissi, specifici per questo uso

Campi opzionali di graphParams:

CampoUsato daDescrizione
graphColorXY, DY{"R": int, "G": int, "B": int} — colore per la serie unica
graphColorsSXY, SDY{"<serie>": {"R": int, "G": int, "B": int}} — colore per serie
axisSXY, SDY{"<nome>": {"min": number, "max": number, "pos": "left"|"right", "text": string}} — uno o più assi Y
mappingSXY, SDY{"<serie>": "<nome asse>"} — su quale voce di axis viene tracciata ciascuna serie
PHD non è un tipo di grafico generico

A differenza di XY/DY/SXY/SDY, i cui ruoli sono abbastanza generici da poter guidare un unico componente grafico riutilizzabile, PHD ha ruoli fissi e specifici per un grafico di guida in stile PHD2 (usato dal modulo guider). Un client che costruisce un componente grafico generico per gli altri quattro tipi avrà probabilmente bisogno di un’implementazione dedicata per PHD, o dovrà tralasciarlo — è quello che fa attualmente Rooster (solo SXY/SDY/XY/DY, PHD non gestito).

Rendere i grafici come componenti dinamici, non come immagini generate

I dati di griglia di una proprietà con grafico cambiano nel tempo (gc/gu/gd/gr, o un uc/ap/aa fresco) — quindi vanno trattati come qualsiasi altro elemento di UI dinamico: disegnarli con un componente grafico/canvas che si ridisegna a ogni aggiornamento, come fa Rooster con Chart.js. Non renderizzarli lato server o lato client in un’immagine statica (uno snapshot PNG, un grafico renderizzato dal server) per poi semplicemente sostituire l’<img> — è visibilmente peggio di un grafico reale (nessun aggiornamento fluido, nessuna interattività, sfarfallio/ricaricamento visibile a ogni punto), e butta via tutto il senso di aver ricevuto i dati di griglia in diretta sulla connessione.

Formato wire

Nel formato JSON di rete, elements è abbreviato in e.


Elementi

Un elemento contiene un unico valore tipizzato. Esistono 11 tipi di elementi.

Campi comuni (tutti gli elementi, nel dump completo)

CampoTipoDescrizione
typestringTipo dell’elemento (vedi elenco più sotto)
labelstringEtichetta visualizzata
orderstringOrdine di ordinamento all’interno della proprietà
hintstringTesto di aiuto / tooltip
autoupdateboolIndicatore interno del backend; ignorato dai client
badgeboolIndica se l’elemento è nell’elenco dei preferiti
directeditboolIndica se questo elemento può essere scritto da solo via SV — vedi sotto
preiconstringIcona prima dell’elemento — azione cliccabile, vedi nota sopra (opzionale)
posticonstringIcona dopo l’elemento — azione cliccabile, vedi nota sopra (opzionale)
Aggiornamenti parziali

Nei messaggi di aggiornamento ee e ea, gli elementi contengono solo il loro valore — senza i campi di metadati. I client devono conservare i metadati dell’ultimo dump completo.

Omissione del campo

directedit può essere del tutto assente per gli elementi in cui non si applica — considerare un’assenza come false. Questo alleggerisce i dump delle proprietà che non ne hanno bisogno.

Significato di directedit

true significa che questo elemento può essere aggiornato da solo, indipendentemente dagli altri elementi della proprietà — inviare SV per esso (funziona anche SA). false significa che il client deve inviare tutti gli elementi della proprietà insieme — usare sempre SA, mai SV per quell’elemento da solo. Un esempio significativo: coordinate RA/Dec, dove entrambi i valori devono essere dati insieme.

Il backend lo impone: un SV su un elemento con directedit: false viene rifiutato con un errore nel log del modulo e non viene applicato nulla. SA è sempre accettato.

Scegliere il valore in base al comportamento dell’elemento, non a quanto «sembra legato» ai vicini:

  • true per un elemento indipendente — un bool che può essere vero indipendentemente dai fratelli (pulsante d’azione come start/abort/loop, interruttore) deve avere directedit: true. Un valore simile riflette spesso un’attività in corso e più valori possono essere veri insieme (es. un ciclo di acquisizione in corso mentre si richiede un focus out). Dichiararlo false affermerebbe a torto che gli elementi vanno inviati in gruppo, e un client che segue questa pagina invierebbe SA, aggirando i moduli che gestiscono solo SV.
  • false solo per un vero gruppo — elementi che hanno senso solo insieme: coordinate RA/Dec, o un vettore di switch INDI riprodotto uno a uno.

Per un gruppo false di elementi bool con regola esclusiva (OneOfMany o AtMostOne), selezionare un elemento significa inviargli true e tutti i fratelli false nello stesso SA — mai rinviare i valori correnti dei fratelli, altrimenti due elementi finiscono true.


Tipo: int

CampoTipoDescrizione
valueintValore intero corrente
minintValore minimo consentito
maxintValore massimo consentito
stepintPasso di incremento
formatstringStringa di formattazione per la visualizzazione
sliderint0 nessuno slider · 1 solo slider · 2 slider + inserimento valore
listOfValuesobjectOpzionale: mappa {"chiave": "Etichetta"} dei valori consentiti
globallovstringOpzionale: chiave che fa riferimento a un LOV di modulo o di controller
lovScopestring"module" o "controller" — dove è definito il globallov
lovConstrainedboolIndica se il valore deve appartenere al LOV
nullableboolIndica se l’elemento può essere lasciato vuoto (il frontend deve offrire una scelta “nessuno”)

Tipo: float

Stessi campi di int, con value, min, max, step in virgola mobile.

Tipo: bool

CampoTipoDescrizione
valuebooltrue o false

Tipo: string

CampoTipoDescrizione
valuestringValore testo
listOfValuesobjectOpzionale: mappa {"chiave": "Etichetta"} dei valori consentiti
globallovstringOpzionale: chiave che fa riferimento a un LOV
lovScopestring"module" o "controller"
lovConstrainedboolIndica se il valore deve appartenere al LOV
nullableboolIndica se l’elemento può essere lasciato vuoto (il frontend deve offrire una scelta “nessuno”)

Tipo: date

CampoTipoDescrizione
valueobject{"year": int, "month": int (1-12), "day": int (1-31)}

Tipo: time

CampoTipoDescrizione
valueobject{"hh": int, "mm": int, "ss": int, "ms": int}
usemsboolIndica se i millisecondi devono essere mostrati

Tipo: datetime

CampoTipoDescrizione
valueobject{"year": int, "month": int, "day": int, "hh": int, "mm": int, "ss": int, "ms": int}

Tipo: img

Il campo value è un oggetto contenente i metadati dell’immagine:

CampoTipoDescrizione
urljpegstringPercorso relativo verso l’immagine JPEG (da usare con l’URL di base /ostmedia/)
urlfitsstringPercorso relativo verso il file FITS
urlthumbnailstringPercorso relativo verso la miniatura
urloverlaystringPercorso relativo verso l’immagine di sovrapposizione
channelsintNumero di canali colore
width / heightintDimensioni dell’immagine in pixel
snrfloatRapporto segnale/rumore
hfravgfloatHFR medio (raggio a mezzo flusso) delle stelle rilevate
hfravgdevfloatDeviazione standard di hfravg
starsintNumero di stelle rilevate
issolvedboolIndica se la risoluzione di campo è riuscita
solverra / solverdefloatCoordinate RA/DEC risolte
solverorientationfloatAngolo di rotazione del campo risolto: dal Nord verso l’alto dell’immagine (gradi), rilevante solo se issolved
solverpixscalefloatScala del pixel risolta (arcsec/pixel), rilevante solo se issolved
solverfieldwidth / solverfieldheightfloatDimensione del campo risolta (minuti d’arco), rilevante solo se issolved
samplingfloatScala del pixel nominale (arcsec/pixel), calcolata dalla focale/riduttore dell’ottica e dalla dimensione del pixel della fotocamera del modulo — sempre disponibile una volta configurate fotocamera e ottica, indipendentemente da qualsiasi risoluzione (a differenza dei campi solver* sopra)
min / max / mean / median / stddevfloat[]Statistiche per canale (vedi sotto)
histogramarrayDati dell’istogramma per canale (vedi sotto)
alternatesstring[]Opzionale: versioni alternative dell’immagine
showstatsboolImpostato dallo sviluppatore del modulo: indica se i campi di statistiche sopra sono significativi per questa immagine e vanno mostrati (un modulo può impostarlo a false se non li calcola, o se non si applicano)

Statistiche per canale: min/max/mean/median/stddev sono array a 3 posizioni fisse, indipendentemente dal numero reale di canali — indice 0 = canale unico (immagine mono) o rosso (R), 1 = verde (G), 2 = blu (B). Solo le prime channels posizioni sono significative; oltre, i valori sono a zero e privi di significato (un’immagine mono ha valorizzato solo l’indice 0).

Istogramma: struttura dei bin

histogram segue la stessa logica per canale (un array per canale, solo le prime channels posizioni usate), ma con due particolarità da conoscere prima di usarlo:

  • Ogni bin è a sua volta incapsulato in un array di un elemento: histogram[canale][bin] vale [frequenza], non direttamente frequenza — un artefatto dell’attuale serializzazione lato backend, non un errore di lettura lato client.
  • Il valore di pixel rappresentato da ogni bin non viene trasmesso direttamente, solo la frequenza lo è. Il numero di bin varia da un’immagine all’altra (histogram[canale].length); il valore di pixel del bin i si ricalcola da min/max dello stesso canale:
    larghezza_bin = (max[canale] - min[canale]) / (numero_di_bin - 1)
    valore(i)     = min[canale] + i × larghezza_bin
URL media

Tutti i percorsi immagine/video sono relativi. Aggiungere il prefisso http(s)://hostname/ostmedia/ per ottenere l’URL completo.
Esempio: "urljpeg": "Focus/frame.jpeg" → http://hostname/ostmedia/Focus/frame.jpeg

I media sono serviti separatamente dalla porta WebSocket

Questo hostname non è deducibile dal protocollo — nessun campo del dump lo fornisce, e non è l’host della connessione WebSocket appena stabilita. Nel deployment di riferimento (vedi Installazione pulita), i media sono serviti da un server web separato (nginx, porta 80 di default, 443 dietro SSL) — una porta completamente diversa dal server WebSocket (9624). Un client deve già conoscere, o far configurare all’utente, l’hostname del server OST indipendentemente dall’URL WebSocket, e costruire gli URL dei media usando la porta HTTP di quell’hostname — non l’origine dei propri file, né la porta WebSocket. Un client che si limita ad anteporre un percorso /ostmedia/ nudo (risolto rispetto a dove sono serviti i propri file) otterrà immagini rotte.

Tipo: video

CampoTipoDescrizione
valueobject{"url": "percorso/relativo/verso/video.mp4"}

Nei messaggi di aggiornamento parziale (ea), l’elemento video può contenere url direttamente a livello dell’elemento invece che annidato in value.

Tipo: light

CampoTipoDescrizione
valueint0 Standby · 1 OK · 2 Avviso · 3 Errore

Tipo: prg

CampoTipoDescrizione
valueobject{"value": float (0–100), "dynlabel": string}
prgtypestring"bar" o "spinner"

Tipo: message (non ancora implementato nel backend attuale)

Riservato per la visualizzazione di messaggi/log.


Liste di valori (LOV)

I LOV forniscono un insieme di valori consentiti per gli elementi di tipo int, float e string.

LOV locale — integrata nell’elemento:

"listOfValues": {
  "1": "Opzione Uno",
  "2": "Opzione Due"
}

LOV globale — referenziata per chiave:

"globallov": "myCameraModels",
"lovScope": "module"

Il LOV stesso è definito a livello modulo (globallovs) o controller (lovs):

"myCameraModels": {
  "label": "Modelli di camera",
  "type": "string",
  "values": {
    "ASI294MC": "ZWO ASI 294 MC",
    "ASI533MC": "ZWO ASI 533 MC"
  }
}

LOV integrati del controller:

ChiaveContenutoDisponibilità
loadedModulesTutti i moduli caricati: {nomeModulo: etichetta}Sempre
loadedModules-<type>Moduli di un dato tipo, corrispondente a infos.library (es. loadedModules-focus)Una volta caricato un modulo di quel tipo
loadedFamilies-<family>Moduli che dichiarano una data famiglia, corrispondente a infos.families (es. loadedFamilies-guiding elenca ogni modulo caricato in grado di guidare, qualunque sia il suo tipo effettivo)Una volta caricato un modulo che dichiara quella famiglia
profiles-<Type>Profili disponibili per un tipo di modulo, corrispondente esattamente a infos.classname — da notare l’uso delle maiuscole, es. profiles-FocusUna volta salvato un profilo di quel tipo

Controllo degli accessi

La risposta dump contiene i valori grant-server e grant-client.

grant-server è la politica di accesso propria del server, fissa (impostata all’avvio del server, uguale per ogni client):

ValoreSignificato
"0"Accesso completo lettura-scrittura, nessuna autenticazione richiesta — grant-client vale sempre "1"
"1"Accesso completo in sola lettura, nessuna autenticazione richiesta; l’accesso in scrittura dipende dai diritti propri dell’utente autenticato — grant-client vale "0" o "1"
"2"Nessun accesso senza autenticarsi; l’accesso in lettura e in scrittura dipende dai diritti propri dell’utente autenticato — grant-client vale "-1", "0" o "1"

grant-client è la concessione attuale propria di questa connessione, che può cambiare dopo un LO (login) riuscito:

ValoreSignificato
"1"Accesso completo lettura-scrittura
"0"Solo lettura
"-1"Accesso negato (autenticazione richiesta)

Se grant-client vale "-1", non viene inviato alcun dato di modulo. Usare il messaggio LO (login) per autenticarsi.