Come posso ridurre la crescita della mia comunicazione seriale basata sul callback?

3

Sto comunicando con un dispositivo collegato al computer tramite com port. Il dispositivo accetta determinati comandi predefiniti per interagire con esso. Sto essenzialmente creando un'API più astratta su quella fornita dal dispositivo.

Ho incapsulato le caratteristiche del Command s che uso, in modo che possa semplicemente lanciare quegli oggetti nella mia classe Connection , che gestisce la porta com.

connection.Send(new FooCommand());

connection poi

  • sposta ICommand in un oggetto SerialPort utilizzato internamente
  • ascolta SerialPort.DataReceived per il risultato atteso
  • invia il prossimo ICommand (se ce n'è uno)

Fin qui tutto bene.

Per alcuni ICommand , il dispositivo restituisce valori significativi (e non riconosce semplicemente la ricezione). Questi possono essere gestiti in callback opzionali Action<> , che vengono passati al costruttore.

connection.Send(new BarCommand(response => Console.WriteLine(response)));

Funziona bene solo per ottenere un valore, ma diventa complicato per comunicazioni più complesse che inviano più istruzioni inviate che dipendono l'una dall'altra. Per illustrare, supponiamo che il valore restituito di un comando possa essere necessario per inviarne un altro.

La soluzione banale è mettere la seconda chiamata nella risposta callback del primo.

connection.Send(new BarCommand(response => connection.Send(new BazCommand(response)));

Questo diventa rapidamente disordinato. Una soluzione più leggibile potrebbe essere quella di memorizzare la risposta in una variabile.

var baz = new BazCommand();
connection.Send(new BarCommand(response => baz.Parameter = response));
connection.Send(baz)

Ora però BazCommand deve essere mutabile , perché sarà completato (con il valore necessario per la sua proprietà parameter ) solo dopo è passato al metodo connection.Send() . Significa anche che connection.Send() deve valutare il suo parametro pigramente.

Anche in questo semplice esempio, l'ordine delle operazioni è già un po 'oscurato.

La situazione peggiora se l'invio di BazCommand debba dipendere dalla risposta. Lambdas non è adatto per questo tipo di codice. È possibile utilizzare un metodo.

connection.Send(new BarCommand(responseHandler));

private void responseHandler(bool response) {
    if(response) connection.Send(baz)

Questo porta a dozzine di metodi come responseHandler che essenzialmente gestiscono le diverse transizioni di stato del mio oggetto (non una cosa negativa di per sé), che amplifica il codice e riduce la leggibilità .

Non ho molta esperienza con C # e ho bisogno di una guida su come migliorare il codice di comunicazione. È un caso d'uso per async / await ? Mi permetterebbe di scrivere codice come il seguente?

bool response = connection.Send(new BarCommand);
/* stop here until response is actually set to a value comming from the device */
connection.Send(new BazCommand(response))

o

bool response = connection.Send(new BarCommand);
/* stop here until response is actually set to a value comming from the device */
if (response) connection.Send(new BazCommand())
    
posta surface 05.10.2017 - 14:16
fonte

1 risposta

2

I'm not too experienced with C# and need some guidance how to improve the communication code. Is this a use case for async/await? Would it allow me to write code like the following?

Questo è davvero uno scenario molto applicabile per l'utilizzo async / await.

public static class CommandHelper
{
    public static async Task<T> SendAsync<T>(this SerialConnection connection, ICommand command)
    {
        var tcs = new TaskCompletionSource<T>();
        command.DataReceived += (sender, data) => tcs.SetResult((T)data);
        connection.Send(command);
        return await tcs.Task;
    }
}

Con questo sarai in grado di 'attendere' ogni chiamata seriale e metterli insieme in una catena come in questo modo

var welcomeResponse = await connection.SendAsync<WelcomeResponse>(new WelcomeCommand());
var registrationReply = await connection.SendAsync<RegistrationReply>(new RegistrationCommand(welcomeResponse.Name));

Hai un'idea. Async / await pulisce davvero questi scenari in cui altrimenti attenderei i dati usando un gestore di eventi.

    
risposta data 05.10.2017 - 15:36
fonte

Leggi altre domande sui tag