Exemple de port série comms code à l'aide d'API Asynchrone .net 4.5?
Quelqu'un peut-il m'indiquer un exemple qui utilise le .net 4.5 API Asynchrone (async, attendent, tâche<>, ReadAsync, etc) pour faire des communications en série?
J'ai essayé d'adapter un événement piloté de série de l'échantillon, et les ai toutes sortes d'horribles comportement - "port en cours d'utilisation par une autre application" des erreurs, VS2013 débogueur de lever des exceptions et des blocages qui habituellement nécessitent un redémarrage du PC pour récupérer à partir.
modifier
J'ai écrit mon propre exemple à partir de zéro. C'est un simple projet Winforms qui écrit dans la fenêtre de Sortie. Trois boutons sur la forme - Ouvrir le Port, à Proximité du Port, et de Lire les Données. Le ReadDataAsync les appels de méthode SerialPort.BaseStream.ReadAsync.
Comme de maintenant, il va lire des données à partir du port, mais je suis en cours d'exécution dans les problèmes de décision robuste.
Par exemple, si je débranchez le câble série, ouvrez le port, puis cliquez sur Lire les Données deux fois, je vais obtenir un Système.IO.IOException (que je sorte de s'attendre), mais mon application cesse de répondre.
Pire, quand j'essaie d'arrêter mon programme, VS2013 lance une "Arrêter le Débogage en cours de dialogue", qui ne se termine jamais, et je ne peux même pas tuer VS partir du Gestionnaire de Tâches. Avoir à redémarrer mon PC à chaque fois que cela se produit.
Pas bon.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Ports;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
private SerialPort _serialPort;
public Form1()
{
InitializeComponent();
}
private void openPortbutton_Click(object sender, EventArgs e)
{
try
{
if (_serialPort == null )
_serialPort = new SerialPort("COM3", 9600, Parity.None, 8, StopBits.One);
if (!_serialPort.IsOpen)
_serialPort.Open();
Console.Write("Open...");
}
catch(Exception ex)
{
ClosePort();
MessageBox.Show(ex.ToString());
}
}
private void closePortButton_Click(object sender, EventArgs e)
{
ClosePort();
}
private async void ReadDataButton_Click(object sender, EventArgs e)
{
try
{
await ReadDataAsync();
}
catch (Exception ex)
{
ClosePort();
MessageBox.Show(ex.ToString(), "ReadDataButton_Click");
}
}
private async Task ReadDataAsync()
{
byte[] buffer = new byte[4096];
Task<int> readStringTask = _serialPort.BaseStream.ReadAsync(buffer, 0, 100);
if (!readStringTask.IsCompleted)
Console.WriteLine("Waiting...");
int bytesRead = await readStringTask;
string data = Encoding.ASCII.GetString(buffer, 0, bytesRead);
Console.WriteLine(data);
}
private void ClosePort()
{
if (_serialPort == null) return;
if (_serialPort.IsOpen)
_serialPort.Close();
_serialPort.Dispose();
_serialPort = null;
Console.WriteLine("Close");
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
ClosePort();
}
}
}
Les autres, de SORTE postes implique que je peux utiliser des choses comme SerialPort.BaseStream.ReadAsync, etc. Je suis l'appel de cette méthode. Vous dites que cela ne fonctionnera pas?
Eh bien, si jamais de l'aide de l'underliying flux avec SerialPort, mais je soupçonne qu'il ne fonctionnera pas correctement (peut-être que je me trompe), je l'ai utilisé pendant des années le SerialPort et de l'utiliser avec l'Écriture et de la Lecture les fonctions et les DataReceived événement devrait être suffisant et déjà asynchrone.
L'exemple de projet que j'ai commencé avec utilise la DataRecieved événement. Je trouve l'INTERFACE utilisateur se bloque quand il y a beaucoup de données, donc je suis à la recherche d'une approche plus rigoureuse.
À l'aide de
BaseStream.ReadAsync
est exactement la bonne façon d'utiliser les ports série .NET, si vous êtes obligé d'utiliser la BCL classe SerialPort. (Encore mieux, c'est d'utiliser l'API Win32.) J'ai un blog à venir sur ce sujet, après mon superviseur approuvé.
OriginalL'auteur Tom Bushell | 2014-04-22
Vous devez vous connecter pour publier un commentaire.
J'utiliserais
TaskCompletionSource<>
pour envelopperSerialDataReceivedEvent
, quelque chose comme ça (non testé):BytesToRead
n'est pas exacte. La plupart des ports série ces jours sont sur USB, pas le bus PCI. Sur USB, envoi d'octets individuels jusqu'à l'hôte est très, de gaspillage, de sorte que le conducteur ne sait pas l'appareil a données dans sa mémoire tampon de lecture jusqu'à ce que vous en fait essayez de le lire. En outre il ya une tonne de conditions de course. Si vous avez reçu un octet et l'encadrement d'erreur, vous obtiendrez des événements sur les deux fils et les gestionnaires de la course, ou même exécuter en même temps exact. Et aucun moyen de connaître l'ordre d'arrivé.Oh voici un fait amusant, j'ai découvert: la Vérification de
BytesToRead
efface tous les indicateurs d'erreur... Rien de tel qu'un peu de silence défaut de vous faire rayer votre tête.bon point sur la mise en mémoire tampon et les indicateurs d'erreur, tks. Concernant les courses, je peux voir que, éventuellement, passe dans le code ci-dessus. Dans mon projet passe-temps, c'était un thread avec la sérialisation contexte de synchronisation (quelque chose comme ceci). Donc pas de lectures simultanées. Le même raisonnement pourrait s'appliquer à un thread d'INTERFACE utilisateur.
Même lorsque vous utilisez un mutex pour empêcher les gestionnaires d'événements en cours d'exécution simultanément, ils peuvent toujours se produire, parce que chacun est sur son propre thread de travail. C'est ce que je voulais dire par la course.
actuellement j'ai utilisé
BlockingCollection
et consommateur/producteur de la logique, de sorte que toutes les lectures se produire sur le même thread, indépendamment de l'endroit oùDataReceived
est déclenché. L'ordre des événements n'a pas d'importance, que tout ce que je fais estport.Read(BytesToRead)
.OriginalL'auteur noseratio
J'ai eu des problèmes similaires, la fermeture d'un SerialPort du thread d'INTERFACE utilisateur. Le blog MSDN suivant suggère que c'est à cause d'un blocage entre le thread d'INTERFACE utilisateur et le thread natif de faire la clôture.
http://blogs.msdn.com/b/bclteam/archive/2006/10/10/top-5-serialport-tips-_5b00_kim-hamilton_5d00_.aspx
Mettre le fermer dans une tâche séparée fixe pour moi. (La méthode est implémentée dans un Protocole de classe de conteneur dans mon projet et a appelé lors d'une INTERFACE utilisateur bouton est cliqué, la IDispose interface de l'appeler ou de la fenêtre principale est fermée.)
... et puis dans mon INTERFACE de commande ...
...et le mien est un Prolifique USB vers RS232 câble.
Est "serialStream" une instance de votre Protocole de classe de conteneur?
Tom - Oui, c'est un modbus RTU maître de la couche 1 de la classe. La couche 2 (adresse et CRC) et de la couche 7 (codes de fonction) ont leurs propres classes, de sorte qu'il peut être étendu pour prendre en charge ASCII et modbus TCP plus tard dans le projet. La couche 2 est encore un peu glitch en raison du peu de délai d'attente et asynchrone comportement de la classe SerialPort combiné avec la moins-que-l'interaction transparente avec la clé USB (le CDC classe d'appareil est idéal pour le streaming de données en vrac, mais pas tellement utile pour les petits, de temps délimité par des paquets de modbus RTU - HID aurait été plus approprié, je pense).
Mes exigences sont beaucoup plus simple, mais je suis déjà affaire avec la classe SerialPort plus compliqué que ce que j'attendais. Et ont également eu de mauvaises expériences avec Win32 API série, et FTDI USB serial pilotes. Windows + Série != amusant.
OriginalL'auteur Evil Dog Pie
Je pense qu'une bonne partie de votre problème est d'avoir le déclencheur de l'utilisateur
ReadDataAsync
, et lui permettant d'être déclenchée alors qu'il y a encore une lecture en cours (à partir de votre description).La bonne façon de le faire est de commencer une lecture lorsque le port série est ouvert, et dans le gestionnaire d'achèvement pour le lire, d'en commencer un autre (enfin, vérifiez que la fin n'était pas causé par la fermeture du port).
Lectures simultanées à partir d'un port série sont inutile de toute façon, parce que vous ne pouvez pas contrôler l'ordre dans lequel les données entrantes seront transmises à compléter routines.
Lorsque vous débranchez le câble série", cela signifie que vous avez le port série connecté à votre ordinateur, mais pas à n'importe quoi sur l'autre extrémité? Ou vous de débrancher le port série de l'ordinateur (typique avec USB ports série)?
Je suis en tirant la série du câble au connecteur série sur l'instrument que je suis en train de contrôle. Je laisse la clé USB de série de l'adaptateur secteur branché à l'ordinateur. Je fais ceci pour éviter toutes les données à venir, et de la force de la ReadAsync attendre.
Ce chipset est la clé USB de série? FTDI? PL2303? Avez-vous mis à jour les pilotes pour qui? Ce que vous avez des sons comme un fil coincé dans un pilote en mode noyau d'appel.
Juste essayé de mettre à jour les pilotes dans le Gestionnaire de Périphériques, mais n'a pas trouvé quelque chose de plus récent (bien que les pilotes actuels sont datées de 2009...hmmm). Ne sais pas de chipset...pilote montre que ATEN USB to Serial Bridge, à partir Prolifique Technologies.
OriginalL'auteur Ben Voigt