2016-01-15 50 views
14

Ho un'applicazione che legge hardware diverso tramite rs232. È stato testato e funzionava perfettamente. Per l'applicazione finale avevo bisogno di introdurre un cavo di pochi metri, il che significa che ho convertitori RS485.Timeout della comunicazione seriale su timeout cavo lungo

Quando si esegue la mia applicazione per leggere l'hardware, viene visualizzato un errore di timeout per System.IO.Ports.SerialStream.Read. Ho aumentato il timeout a 20 sec purtroppo non ha risolto il problema

Ho provato diverse applicazioni per leggere l'hardware e hanno funzionato anche con 1 sec di frequenza di lettura.

La comunicazione utilizza il protocollo Modbus, che nella fase attuale presumo sia irrilevante in quanto non arrivo allo stadio per ricevere nulla.

Il mio codice sembra che: prima l'apertura della porta seriale e l'inizializzazione:

//get the right modbus data structure element 
ModBus MB = (ModBus)s[0].sensorData; 

//set up the serial port regarding the data structure's data 
SerialPort sp = new SerialPort(); 
sp.PortName = MB.portName; 
sp.BaudRate = Convert.ToInt32(MB.baudRate); 
sp.DataBits = MB.dataBits; 
sp.Parity = MB.parity; 
sp.StopBits = MB.stopBits; 
//Set time outs 20 sec for now 
sp.ReadTimeout = 20000; 
sp.WriteTimeout = 20000; 

// aggiungete la porta a un elenco a cui si accede dal lettore portList.Add (sp); sp.Open();

Leggi hardware:

//get the right port for com 
SerialPort sp = getRightPort(); 
ModBus MB = getRightModBusStructureelement(); 
try 
    { 
    //Clear in/out buffers: 
    sp.DiscardOutBuffer(); 
    sp.DiscardInBuffer(); 

    //create modbus read message 
    byte[] message = createReadModBusMessage(); 

    try 
     { 
     sp.Write(message, 0, message.Length); 

     // FM.writeErrorLog output included for easier debug 
     FM.writeErrorLog(DateTime.Now + ": ModBus Message Sent"); 
     FM.writeErrorLog(DateTime.Now + ": Read TimeOut = " + sp.ReadTimeout + " Write TimeOut = " + sp.WriteTimeout); 

     int offset = 0, bytesRead; 
     int bytesExpected = response.Length; 

     FM.writeErrorLog(DateTime.Now + ": start read"); 

     while (bytesExpected > 0 && (bytesRead = sp.Read(response, offset, bytesExpected)) > 0) 
      { 
       FM.writeErrorLog(DateTime.Now + ": read - " + offset); 
       offset += bytesRead; 
       bytesExpected -= bytesRead; 
      } 
     } 
     catch (Exception err) 
     { 
      Console.WriteLine("ERROR Modbus Message to serial port ModBus: " + err); 
      FM.writeErrorLog(DateTime.Now + " - " + "ERROR Modbus Message to serial port ModBus: " + err); 
     } 

    } 

Dopo aver provato l'applicazione ho ottenuto il seguente uscita dal ErroLog.txt:

14/01/2016 17:18:17: ModBus Message Sent 
14/01/2016 17:18:17: Read TimeOut = 20000 Write TimeOut = 20000 
14/01/2016 17:18:18: start read 
14/01/2016 17:18:38 - ERROR Modbus Message to serial port ModBus: System.TimeoutException: The operation has timed out. 
    at System.IO.Ports.SerialStream.Read(Byte[] array, Int32 offset, Int32 count, Int32 timeout) 
    at System.IO.Ports.SerialStream.Read(Byte[] array, Int32 offset, Int32 count) 
    at System.IO.Ports.SerialPort.Read(Byte[] buffer, Int32 offset, Int32 count) 
    at ProbReader.SensorReader.modbusReading(List`1 mm, Int32 spCounter) 
14/01/2016 17:18:38: 0 
14/01/2016 17:18:38: 0 

ho aumentato il timeout per 60sec ogni evenienza, ma lo stesso errore:

15/01/2016 11:11:51: ModBus Message Sent 
15/01/2016 11:11:51: Read TimeOut = 60000 Write TimeOut = 60000 
15/01/2016 11:11:51: start read 
15/01/2016 11:12:51 - ERROR Modbus Message to serial port ModBus: System.TimeoutException: The operation has timed out. 
    at System.IO.Ports.SerialStream.Read(Byte[] array, Int32 offset, Int32 count, Int32 timeout) 
    at System.IO.Ports.SerialStream.Read(Byte[] array, Int32 offset, Int32 count) 
    at System.IO.Ports.SerialPort.Read(Byte[] buffer, Int32 offset, Int32 count) 
    at ProbReader.SensorReader.modbusReading(List`1 mm, Int32 spCounter) 
15/01/2016 11:12:51: 0 
15/01/2016 11:12:51: 0 

Ho provato alcuni modi diversi di leggere la porta seriale, penso che il metodo attuale sembra il migliore che è il ciclo while nel mio codice di lettura.

Non ho incluso il resto del mio codice come prima e penso che sia irrilevante.

+0

Non vedo alcuna prova di controllo del flusso hardware come RTS/CTS. Sarebbe stata la mia prima scelta anche prima di un controllo del flusso del software che sospetto non sia affidabile nella tua situazione attuale – MickyD

+0

Inoltre, il tuo codice precedente non mostra in realtà _what values_ che stai usando per _parity; bit di dati; baud; fermare i bit; _ e così via. Qualche possibilità di scaricare i valori in 'MB'? – MickyD

+0

Hai mai pensato di utilizzare un convertitore da COM a IP? Quindi potresti ricevere i tuoi dati attraverso una presa TCP e potresti gestire una lunga distanza. – etalon11

risposta

1

Come ho menzionato nella mia domanda, ero in grado di leggere l'hardware con altri software, quindi doveva essere un errore del software. Dopo aver indagato su tutte le possibili variabili che potevo manipolare alla porta seriale, mi è venuta l'idea di girare la stretta di mano e lasciare che fosse sempre accettata.

Un po 'di scavo mi dà il seguente codice aumentando il tempo di scrittura e lettura. E 'risolto il mio problema:

      sp.ReadTimeout = 60000; 
          sp.WriteTimeout = 60000; 

          sp.DtrEnable = true; 
          sp.RtsEnable = true; 
          sp.Handshake = Handshake.None; 

Spero che si aiuta per altri in futuro, e grazie per aiutare tutti e fatica.

2

Supponendo che questo non è un problema/cavo lungo hardware, potrebbe essere in grado di fare qualcosa nel codice per gestire l'errore:

È necessario creare un gestore di errore "giusto" per quanto si crea getRightPort:

SerialPort sp = getRightPort(); 

presumere che vi sia un Collection di elementi porta seriale all'interno e si ritorna quella giusta, nel caso di questo SerialPort ha errore, assicuratevi di ricreare l'errore SerialPortobject con le stesse impostazioni:

catch (Exception err) 
{ 
    Console.WriteLine("ERROR Modbus Message to serial port ModBus: " + err); 
    FM.writeErrorLog(DateTime.Now + " - " + "ERROR Modbus Message to serial port ModBus: " + err); 
    reinitRightPort(sp); //add this 
} 

E il metodo reinitRightPort(); potrebbe essere simile a come si prima avvia, con piccole differenze come:

  1. Non è necessario aggiungere questo nel List<SerialPort> più
  2. di non fare dichiarare SerialPort nel metodo, si ottiene dall'argomento di input.
  3. Può essere migliore per introdurre una certa validità di controllo in ingresso per evitare errori in seguito noti
  4. Forse si può chiudere la connessione precedente, solo per assicurarsi che la porta può essere utilizzato dal nuovo SerialPortobject.

Qualcosa di simile a questo:

private void reinitRightPort(SerialPort sp){ //get SerialPort from outside of method 
    //Add whatever necessary here, to close all the current connections 
    //Plus all the error handlings 
    if (sp == null) //Read 3. 
     return; 
    sp.Close(); //Read 4. close the current connections 

    //get the right modbus data structure element 
    ModBus MB = (ModBus)s[0].sensorData; 

    //set up the serial port regarding the data structure's data 
    sp = new SerialPort(); //Read 2. sp is from outside the method, but use new keyword still 
    sp.PortName = MB.portName; 
    sp.BaudRate = Convert.ToInt32(MB.baudRate); 
    sp.DataBits = MB.dataBits; 
    sp.Parity = MB.parity; 
    sp.StopBits = MB.stopBits; 
    //Set time outs 20 sec for now 
    sp.ReadTimeout = 20000; 
    sp.WriteTimeout = 20000; 

    //portList.Add(sp); Read 1. no need to add this! It is already there! 
    sp.Open(); 
} 

Nota: Dopo averlo fatto e assicurarsi che la porta sta funzionando bene, per quanto possibile, si potrebbe anche combinare il metodo reinitRightPort di cui sopra con il tuo vero inizializzazione con un po ' piccole modifiche Ma la prima cosa che potresti voler fare è far sì che la tua porta seriale funzioni in errore.

But if the error source comes from hardware/long cable issue (such as cable placing in the RS232 or RS485, or voltage drop due to long cable, or incompatible hardware: that is, in short, nothing to do with coding at all), then, unfortunately the solution cannot come from code as well. You got to find the real hardware issue.

5

Se si utilizza centinaia di metri di cavo seriale (che è un problema di ingegneria hardware in sé) allora vi consiglio vivamente di avere un ricetrasmettitore adeguato su entrambe le estremità del cavo. Il cavo stesso deve essere schermato EMC e di alta qualità. Lunghe tirature di cavi non schermati possono essere vittime di fluttuazioni di tensione indotte che possono danneggiare apparecchiature che non sono progettate per gestire cavi lunghi.

Anche con un buon cavo si avranno ancora notevoli cadute di tensione e effetti induttivi/capacitivi che potrebbero impedire la comunicazione a velocità di trasmissione più elevate. Corri al baud rate più basso con cui puoi farcela.

+0

le centinaia di metri di cavo erano inevitabili. Il cavo è di buona qualità, la caduta di tensione non dovrebbe essere un problema, come ho detto, sono stato in grado di leggere l'hardware con altri software. Gestito per risolvere il problema rimuovendo il tremolio della mano. Alla rimozione intendo che l'ho impostato per essere vero. – Daniel

4

Supponendo che si tratti di un problema hardware (e suppongo che sia, ho dovuto risolvere anche un problema simile), consiglierei di sostituire le centinaia di metri di cavo seriale con un dispositivo server (via ethernet) accanto al dispositivo seriale. Il device server può emulare una porta COM sul PC e quindi mantenere il cavo seriale breve.

Purtroppo tali server sono un po 'più costoso di un paio di metri di cavo ..

+1

@MickyD, Ethernet è stato progettato pensando a cavi lunghi. I cavi seriali generalmente non lo sono. Gli adattatori da seriale a ethernet sono una soluzione valida a questo problema. La distanza è ancora lunga ma Ethernet lo gestisce meglio grazie alla sua costruzione a doppino intrecciato ed è disponibile in forma schermata. – Wossname

+0

@Wossname Oh, capisco cosa intendete ora voi, convertitori seriale-ethernet, in modo tale che il dispositivo non abbia idea che il cavo sia stato sostituito con ethernet. Il mio male hehe. qui +1 :) – MickyD

3

prega di notare che una differenza tra RS232 e RS485 è che RS232 è full duplex, mentre RS485 è solo la metà duplex. Poiché modbus è un protocollo di tipo risposta di richiesta, questo non è un problema, ma è importante sapere.

A causa di ciò RS232 -> RS485 deve sapere quando accendere il trasmettitore RS485. Questo può essere fatto in modo diverso su diversi convertitori e può anche essere configurabile. Questo potrebbe essere fatto con le linee di controllo aggiuntive che RS232 ha ma RS485 manca. RTS/CTS. Se configurato in modo errato, il trasmettitore del risponditore (in attesa di una richiesta di risposta) potrebbe essere acceso e quindi non può ricevere nulla.

Un esempio in un manuale è http://ftc.beijer.se/files/C125728B003AF839/992C59EC02C66E00C12579C60051484E/westermo_ug_6617-2203_mdw-45.pdf Questo è un modello popolare in Svezia che ha tre modalità di funzionamento. Si può accendere/spegnere il trasmettitore semplicemente dati in arrivo, controllati dal perno RTS sul connettore DB9-RS232 o hanno il trasmettitore sempre (per RS422 che hanno fili separati in ciascuna direzione) http://screencast.com/t/KrkEw13J8

Questo è difficile perché alcuni produttori non stampano come funziona realmente. È anche estremamente facile ottenere il cablaggio sbagliato perché non è chiaro cosa sia DCE e DTE.