Constamment la lecture à partir d'un port série avec un thread d'arrière-plan
Que le port de communication série asynchrone, j'ai compris tôt dans mon projet impliquant la communication avec une interface RS 232 de l'appareil que je vais avoir un thread d'arrière-plan constamment la lecture du port pour les données reçues. Maintenant, je suis en utilisant IronPython (.NET 4.0) et j'ai donc accès à la nappe de classe SerialPort construit dans .NET. Cela me permet d'écrire du code comme ceci:
self.port = System.IO.Ports.SerialPort('COM1', 9600, System.IO.Ports.Parity.None, 8, System.IO.Ports.StopBits.One)
self.port.Open()
reading = self.port.ReadExisting() #grabs all bytes currently sitting in the input buffer
Assez Simple. Mais comme je l'ai mentionné, je veux être constamment vérifier ce port pour les nouvelles données qu'il arrive. Idéalement, j'aurais l'OS dire - moi à tout moment il y a de données en attente. Whaddaya savez, mes prières ont été exaucées, il y a un DataReceived
événement!
self.port.DataReceived += self.OnDataReceived
def OnDataReceived(self, sender, event):
reading = self.port.ReadExisting()
...
Dommage que ce soit inutile, bien que, parce que cet événement n'est pas garanti d'être soulevé!
La DataReceived événement n'est pas garanti d'être soulevées pour chaque octet reçu.
Donc, retour à l'écriture d'un thread d'écoute, puis. J'ai accompli ce assez rapidement avec un BackgroundWorker
qui appelle simplement port.ReadExisting()
encore et encore. Il lit les octets comme ils viennent, et quand il voit une fin de ligne (\r\n
), il place ce qu'elle a lu dans un linebuffer
. Puis d'autres parties de mon programme d'oeil à la linebuffer
pour voir s'il y a des lignes complètes en attente d'être utilisés.
Maintenant, c'est un classique producteur-consommateur le problème, évidemment. Le producteur est le BackgroundWorker
, de placer des lignes complètes en linebuffer
. Le consommateur est un code qui consomme ces lignes à partir de la linebuffer
aussi vite que possible.
Toutefois, le consommateur est en quelque sorte inefficace. Actuellement, il est constamment vérifier la linebuffer
, obtenir déçu à chaque fois de trouver vide; mais chaque fois dans un certain temps de trouver une ligne d'attente à l'intérieur. Quelle est la meilleure façon d'optimiser, ce afin que le consommateur ne se réveille quand il y a une ligne disponibles? De cette façon, le consommateur n'est pas à la faire tourner en permanence accès à la linebuffer
, ce qui peut entraîner certains problèmes de concurrence.
Aussi, si il est plus simple/la meilleure façon de lire en permanence à partir d'un port série, je suis ouvert aux suggestions!
OriginalL'auteur Aphex | 2010-11-12
Vous devez vous connecter pour publier un commentaire.
Je ne vois pas pourquoi vous ne pouvez pas utiliser la
DataReceived
événement. Comme les docs de l'étatTout ce qui est dit, c'est que vous n'êtes pas assuré d'obtenir un événement distinct pour chaque octet de données. Vous devrez vérifier et voir si il y a plus d'un octet disponible sur le port à l'aide de la
BytesToRead
propriété et lire le nombre d'octets.Yeap,
ReadExisting
devrait faire ce que vous voulez. Mais assurez-vous que vous lisez la documentation soigneusement,ReadExisting
ne garantit pas à vous donner tous les octets, mais il reste ", Lit-tous de suite d'octets disponibles, basées sur le codage".Donc, un moyen infaillible de lecture de tous octets disponibles dans le buffer à ce point dans le temps serait de
port.Read(buffer, 0, port.BytesToRead)
, je présume?OriginalL'auteur heavyd
Il n'est pas nécessaire d'avoir un coup de fil qui interroge le port série.
Je suggère d'utiliser SerialPort.BaseStream.BeginRead(...) de la méthode. Il est beaucoup mieux que d'essayer d'utiliser l'événement dans la classe SerialPort. L'appel à BeginRead retourne immédiatement et registres asynchrone de callback qui est appelée une fois la lecture terminée. Dans la méthode de rappel, vous appelez EndRead et qui renvoie le nombre d'octets lus dans le tampon. Le BaseStream(SerialStream) hérite de Flux et suit la tendance générale .Net Flux, ce qui est très utile.
Mais il est important de garder à l'esprit que c'est une .Net thread qui appelle la fonction de rappel, si vous avez besoin de processus de données plus rapide, ou passer tout le levage lourd pour celui de votre fils. Je vous suggère fortement de lire le lien suivant, en particulier la section Remarques.
http://msdn.microsoft.com/en-us/library/system.io.stream.beginread.aspx
OriginalL'auteur coder
Pourquoi ne pas votre producteur de déclencher un événement lorsqu'une ligne complète est placée dans le
LineBuffer
Aussi, rom de lire les docs, je crois qu'il est simplement en train de dire que la lecture de 100 octets != 100 événements ont - il ne dit pas: les événements ne peuvent pas être invoqué.
LineAvailable
événement que j'ai enregistrer le consommateur. Cependant, le problème que j'ai rencontré était que le consommateur n'a pas pu afficher la ligne sur l'INTERFACE utilisateur, parce que le consommateur a été réveillé par leLineAvailable
événement, qui est soulevée parDataReceived
, qui s'exécute sur un non-thread d'INTERFACE utilisateur.En bref, le consommateur finit par se réveiller sur un non-thread de l'INTERFACE utilisateur et ne peuvent pas accéder à des éléments de l'INTERFACE (et oui, j'ai essayé d'utiliser
Dispatcher.Invoke
, mais pour une raison que je ne pouvais pas comprendre, le consommateur ne pouvait pas trouver le répartiteur fil pour invoquer sur -Thread.GetData(Thread.GetNamedDataSlot("WPF_DISPATCHER"))
retournénull
).Essayez de le faire Si
<UIControl>.InvokeRequired Then UIControl.Invoke(Delegate, Params)
voir msdn.microsoft.com/en-us/library/ms171728%28VS.80%29.aspxHmmm sory jut remarqué que vous êtes en utilisant WPF Répartiteur dont je suis familier avec le commentaire ci-dessus peuvent ne pas s'appliquer.
OriginalL'auteur Basic
Voici le code VB qui exprime mon opinion sur la façon dont cela devrait être fait:
Pour ce qu'elle vaut, code très similaire à ce qui a traité des données du port série à près de 1 mbps.
Avec la serrure???? Je pense que je ne comprends pas votre question.
OriginalL'auteur dbasnett