2012-12-06 31 views
20

Qui ho letto un sacco di domande su come leggere i dati dalle porte seriali usando la classe .NET SerialPort ma nessuno degli approcci consigliati è risultato del tutto efficiente per me.Qual è il modo corretto di leggere una porta seriale utilizzando .NET framework?

Ecco il codice che sto usando per ora:

SerialPort port = new SerialPort("COM1"); 
port.DataReceived += new SerialDataReceivedEventHandler(MyDataReceivedHandler); 

E il gestore di eventi:

void MyDataReceivedHandler(object sender, SerialDataReceivedEventArgs e) 
{ 
    int count = port.BytesToRead; 
    byte[] ByteArray = new byte[count]; 
    port.Read(ByteArray, 0, count); 
} 

Ma mi mancano ancora alcuni dati a volte. Ho provato un modo diverso di leggere i dati nel gestore dell'evento ma senza fortuna.

Poiché .NET 4.5 offre nuove possibilità per eseguire alcune operazioni asincrone, come con il metodo ReadAsync che sembra essere utilizzabile su un flusso SerialPort, sono curioso di vedere quale sarebbe l'approccio consigliato per gestire questi casi.

+3

La tua domanda è davvero vaga. "Mi mancano ancora alcuni dati a volte" è come dire al tuo meccanico "la mia auto a volte fa un rumore strano" e mi chiede quanto costa rimediare. Forse [questa risposta] (http://stackoverflow.com/a/2966357/62576) a una domanda correlata potrebbe aiutare. Tuttavia, penso che tu debba essere molto più specifico se vuoi aiuto; così com'è, non c'è molto che non può essere risolta guardando altre domande su questo argomento. –

+0

Hai ragione, metterò un esempio reale per illustrare meglio cosa sta succedendo ... –

+3

Implementare un protocollo di comunicazione seriale su RS232 o 485 è un compito davvero molto difficile. Richiede MOLTA esperienza. L'ho implementato fin dai bei vecchi tempi di DOS e ancora mi sono bloccato in alcune insidie ​​comuni. Perché non provi a trovare un'offerta affidabile di terze parti per la gestione delle comunicazioni seriali? Probabilmente avranno risolto tutti i tanti, molti, molti bug che ti cadranno in te stesso :-) Se questo è un esercizio, poi vai avanti, dà grandi soddisfazioni per finalmente padroneggiare le comunicazioni seriali! :-) – Loudenvier

risposta

14

Potrebbe provare qualcosa di simile, per esempio penso che quello che si vogliono utilizzare è la port.ReadExisting() Metodo

class SerialPortProgram 
{ 
    // Create the serial port with basic settings 
    private SerialPort port = new SerialPort("COM1", 
     9600, Parity.None, 8, StopBits.One); 
    [STAThread] 
    static void Main(string[] args) 
    { 
     // Instatiate this 
     class new SerialPortProgram(); 
    } 

    private SerialPortProgram() 
    { 
     Console.WriteLine("Incoming Data:"); 
     // Attach a method to be called when there 
     // is data waiting in the port's buffer 
     port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived); 
     // Begin communications 
     port.Open(); 
     // Enter an application loop to keep this thread alive 
     Application.Run(); 
    } 

    private void port_DataReceived(object sender, SerialDataReceivedEventArgs e) 
    { 
     // Show all the incoming data in the port's buffer 
     Console.WriteLine(port.ReadExisting()); 
    } 
} 

O si vuole farlo in base a quello che stavi cercando a che fare, si può provare questo

public class MySerialReader : IDisposable 
{ 
    private SerialPort serialPort; 
    private Queue<byte> recievedData = new Queue<byte>(); 

    public MySerialReader() 
    { 
    serialPort = new SerialPort(); 
    serialPort.Open(); 
    serialPort.DataReceived += serialPort_DataReceived; 
    } 

    void serialPort_DataReceived(object s, SerialDataReceivedEventArgs e) 
    { 
    byte[] data = new byte[serialPort.BytesToRead]; 
    serialPort.Read(data, 0, data.Length); 
    data.ToList().ForEach(b => recievedData.Enqueue(b)); 
    processData(); 
    } 

    void processData() 
    { 
    // Determine if we have a "packet" in the queue 
    if (recievedData.Count > 50) 
    { 
     var packet = Enumerable.Range(0, 50).Select(i => recievedData.Dequeue()); 
    } 
    } 

    public void Dispose() 
    { 
     if (serialPort != null) 
     { 
      serialPort.Dispose(); 
     } 
    } 
+1

Grazie per il tuo aiuto, alla fine sono andato con 'ReadExisting' utilizzato con una raccolta di blocchi. Sto ancora pensando di provare la nuova funzione 'ReadAsync' però ... –

+0

non il metodo processData() richiede un mutex? Voglio dire, l'evento datareceived potrebbe attivarsi di nuovo quando processData() è ancora in esecuzione? non è vero? Quindi potremmo finire con due (o anche più?) Thread eseguiti all'interno del metodo processData() .... –

+0

Non l'ho ancora provato e basandomi su ciò che Yannick ha menzionato nel suo commento sopra, penso che sarebbe non fare la differenza se dovesse procedere con la lettura in modo asincrono – MethodMan

4

ho utilizzato codice simile a @MethodMan ma ho dovuto tenere traccia dei dati della porta seriale mandava e cercare un carattere di terminazione per sapere quando la porta seriale è stato fatto l'invio dati.

private string buffer { get; set; } 
private SerialPort _port { get; set; } 

public Port() 
{ 
    _port = new SerialPort(); 
    _port.DataReceived += new SerialDataReceivedEventHandler(dataReceived); 
    buffer = string.Empty; 
} 

private void dataReceived(object sender, SerialDataReceivedEventArgs e) 
{  
    buffer += _port.ReadExisting(); 

    //test for termination character in buffer 
    if (buffer.Contains("\r\n")) 
    { 
     //run code on data received from serial port 
    } 
} 
0

Penso che non utilizzare un SerialDataReceivedEvent. Timer è uno dei migliori scegliere, e dopo un tempo costante, per ricevere i dati:

For i = 1 To Me.objSerialPort.BytesToRead 
    strReceiveData &= Convert.ToChar(Me.objSerialPort.ReadByte) 
Next i 
Problemi correlati