Programmazione dichiarativa per il controllo deterministico in tempo reale

3

Diciamo che vuoi controllare un motore in tempo reale. Normalmente useresti un microcontrollore o PC con ad es. linguaggio di programmazione c. Quindi useresti un approccio imperativo. Dì al microcontrollore esattamente come fare il controllo del motore. E per il determinismo si usano intervalli di tempo specifici per eseguire il controllo.

La mia domanda è, sarebbe anche possibile utilizzare un approccio dichiarativo con ad es. un linguaggio di programmazione funzionale? Negli algoritmi dichiarativi dico al dispositivo di controllo COME eseguire il controllo del motore. PER ESEMPIO. Voglio usare F #. Quali sono i vantaggi o gli svantaggi di ciò con la programmazione funzionale? Potrebbe essere anche deterministico? Lo useresti per i controlli in tempo reale? Altrimenti, perché non dovresti usarlo?

    
posta CPA 16.03.2016 - 18:00
fonte

3 risposte

4

Un sacco di questo tipo di codice è già molto meno imperativo di quanto si possa pensare. Di solito è molto vicino a un modello di programmazione reattiva funzionale , in cui una funzione viene chiamata in risposta ad un evento esterno, in questo caso un segno di spunta del timer.

In genere, quella funzione riceve una sorta di struttura dati context come argomento, che può utilizzare insieme alla lettura di alcuni registri per determinarne lo stato precedente. In genere modifica alcuni registri per eseguire il proprio compito e modifica il contesto per tracciare il suo stato successivo.

In realtà non è tanto una modifica per restituire un nuovo contesto e registrare lo stato invece di mutare quello vecchio. E abbiamo un confine piuttosto buono in cui sappiamo che il vecchio contesto sta andando fuori dal campo di applicazione, in cui il compilatore può pianificare in modo deterministico alcuni garbage collection.

In realtà sto rimuginando creando un FRP linguaggio utilizzabile sui microcontrollori. È molto più vicino all'hardware di quanto si pensi.

    
risposta data 16.03.2016 - 22:32
fonte
3

C'è una lunga storia di linguaggi di programmazione del flusso di dati per sistemi reattivi. Negli anni Ottanta, sono state sviluppate diverse lingue nell'ambito dell'approccio Programmazione sincrona . Penso che Traduzione del tempo discreto SIMULINK SEGNALARE sarebbe un buon punto di partenza per essere esposto a Simulink, Signal. Inoltre, esiste un caso d'uso del motore DC.

Approccio sincrono

Invece di manipolare i valori, i linguaggi sincroni definiscono equazioni su flussi sincroni. Ad ogni passo del clock logico, gli input vengono letti e propagati istantaneamente a variabili e output locali. L'ipotesi di ritardo zero significa che ci vuole un tempo costante per aggiornare il tuo stato (fino a te per dimostrare che il tempo di esecuzione effettivo si adatta sempre al periodo di tempo fisico richiesto). Ecco un esempio con uno stato interno, in Luster :

node sum (in : int) returns (sum : int);
let
   sum = in + (0 -> pre(sum));
tel;

La freccia separa il codice che viene eseguito la prima volta (inizializzazione, a sinistra) e il codice che viene aggiornato in tutti gli altri istanti (caso generale, a destra). A destra della freccia, è quindi consentito fare riferimento ai valori precedenti di sum : questo è lo scopo di pre .

Con la definizione di cui sopra, possiamo analizzare le dipendenze in anticipo, fare una schedulazione statica del codice e produrre codice C basato su una funzione init e una funzione step , che fondamentalmente sono:

/* local globals */
inline int sum, in, tmp;

/* Interface with other code */
extern int read_in();
extern void write_sum(int);

void sum_init() 
{
   tmp = 0;
}

void sum_step ()
{
   in = read_in(); /* external */
   sum = tmp + in;
   write_sum(sum); /* external */

   /* Update memory */
   tmp = sum;
}

Oggi sembra esserci un rinnovato interesse per la programmazione reattiva funzionale. Anche se ci sono alcune ricerche su FRP in tempo reale , il FRP generalmente fornito non offre lo stesso tipo di garanzie statiche richieste nei sistemi embedded / critici:

  • utilizzo della memoria costante: la memoria viene allocata staticamente e completamente determinata in anticipo (nessun GC, grazie). I valori passati sono accessibili solo tramite gli operatori di ritardo (ad esempio pre ), quindi il numero di operatori di ritardo nidificati indica il numero di versioni precedenti di una variabile da conservare, che è noto staticamente. Potresti immaginare di costruire una finestra scorrevole di memoria per osservare il flusso infinito descritto dalle tue equazioni.

  • tempo di esecuzione costante: il tempo di esecuzione nel caso peggiore di ogni passo temporale è molto più facile da calcolare.

  • analisi di causalità: l'ordine delle operazioni è noto in anticipo. Quando si progettano sistemi reattivi distribuiti, è possibile garantire che non si verificherà un deadlock.

  • I linguaggi sincroni usano anche gli orologi, che determinano quando deve avvenire un calcolo: puoi scrivere x when b per avere un flusso in cui gli unici valori calcolati esistono quando il flusso dati booleano b valuta true (e nota che b è un flusso di dati stesso, che può anche avere un orologio).

    Nel linguaggio Segnale , che consente di descrivere unità di calcolo distribuite, gli orologi consentono ad esempio di emettere solo le informazioni di sincronizzazione necessarie tra unità separate, asincrone: se trasmetti un contatore di tick tra due sistemi e le relazioni di clock ti dicono che il flusso A è presente ogni 5 tick e flusso B ogni 7 tick, non è necessario trasmettere anche il reale flusso di clock booleano di A e B : il ricevitore sa esattamente quando leggere un valore per A e uno per B , basato solo sulla spunta. Gli orologi formano relazioni booleane e possono a volte essere dedotti implicitamente dallo stato conosciuto di altri orologi.

Quelle lingue sono sviluppate in contesti industriali, generalmente come parte di una più ampia catena di strumenti: cerca SCADE, ecc. A seconda dell'utilizzo, potresti preferire usare i compilatori accademici.

Che dire di F #?

F # è raccolta dalla spazzatura, per quanto ne so. Io tendo a pensare che puoi eseguire i controller in un ambiente raccolto, se sei abbastanza attento e il tuo dispositivo non richiede tempi precisi. Personalmente modificherei la variabile esistente per evitare di allocare memoria durante l'esecuzione, in modo che il consumo effettivo di memoria nel ciclo di controllo sia zero.

Se la memoria viene continuamente allocata (e quindi raccolta, si spera), si potrebbe avere, in media, un utilizzo costante della memoria con possibili pause GC. Il garbage-collector non è in tempo reale (anche se esistono GC in tempo reale), ma se il periodo fisico è abbastanza grande, avrai una bassa probabilità di perdere una scadenza.

Se manca una scadenza è fuori questione, usa un codice generato dalle lingue sopra, o scrivine uno a mano se hai solo bisogno di un semplice ciclo di controllo. Utilizza un linguaggio di basso livello come C o Ada (magari all'interno di un sistema operativo Real-Time o direttamente sul microcontrollore).

    
risposta data 17.03.2016 - 01:26
fonte
2

Sì, a condizione che la garbage collection non interferisca con i requisiti in tempo reale e disponga di memoria e potenza del processore sufficienti.

Le lingue imperative ti consentono di avere un controllo più preciso sulle risorse di calcolo che vengono utilizzate (proprio perché stai descrivendo come ). Potresti aver bisogno di questo controllo preciso se stai utilizzando motori con richieste in tempo reale. Le lingue dichiarative comunemente estrapolano questi dettagli da te, in base alla progettazione.

Se hai intenzione di andare con la purezza funzionale (ossia la piena immutabilità), potresti dover essere molto intelligente con le tue strutture dati per ottenere prestazioni sufficientemente buone (cfr. Okasaki ).

Si noti che l'uso di F # richiede di avere un ecosistema che supporti F #; come minimo, dovrai supportare il .NET Compact Framework . Potresti anche essere interessato a Netduino .

Potresti ottenere il meglio da entrambi i mondi scrivendo un piccolo DSL in un linguaggio imperativo (può essere un piccolo insieme di funzioni) che ottimizza il controllo del motore ma ti offre comunque le capacità dichiarative che desideri.

    
risposta data 16.03.2016 - 18:12
fonte