2015-07-22 6 views
3

Je suis très nouveau sur C# et je crée une classe de port série pour un tableau que j'ai conçu. Dans lequel cette classe contient des méthodes pour ouvrir/fermer un port série connecté à la carte. Il devrait également lire les messages de la carte et écrire des messages de l'interface utilisateur sur la carte (j'utilise une application de formulaires pour entrer et afficher des valeurs).L'indexeur créé pour voir le tableau ne voit pas les modifications apportées au tableau

J'ai lu le tampon d'entrée interne et placez les octets dans mon propre tampon logiciel, lorsqu'un message est terminé, cela invite le formulaire pour analyser le message ...

Pour cela, je l'ai créé un indexeur pointez sur le tableau (à partir du formulaire) et prenez les octets qu'il désire.

uint[] serialPortReceiveBuffer = new uint[3]; 
    public delegate void Del(); 
    Del promptFormAction = Form1.MsgReceived; 

    public void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e) 
    { 
     for (int i = 0; i <= 2; i++) 
     { 
      serialPortReceiveBuffer[i] = (uint)serialPort1.ReadByte(); 
     } 

     promptFormAction(); 
    } 

    public uint this[uint i] 
    { 
     get { return serialPortReceiveBuffer[i]; } 
    } 

c'est le code dans ma classe pcbSerialPort, et le code lié à dans la classe Form1 est la suivante:

public static void MsgReceived() 
    { 
     Form1 _frm = new Form1(); 
     _frm.analyzeIncomingMessage(); 
    } 

    public void analyzeIncomingMessage() 
    { 
     if (PCB[0] == 63) 
     { 
      setBoardDesignator(PCB[1], PCB[2]); 
     } 
    } 

Mon problème est que le lorsque j'utilise l'indexeur pour accéder au serialPortReceiveBuffer, il ne voit pas les modifications que je lui ai apportées en plaçant les octets reçus dans le même tableau. Par exemple, lorsque je reçois une chaîne de mon propre protocole -> "? 10" le tampon est rempli avec [63] [49] [48]

Bien que lorsque j'essaie d'accéder à ce tampon en utilisant l'indexeur, je reçois [0] [0] [0]

S'il vous plaît quelqu'un peut-il vous aider? En outre, je suis conscient qu'il y a probablement quelques autres choses que j'aurais pu faire mieux, donc si vous avez des conseils généraux, ce serait génial. Aussi dans une langue que je peux comprendre. Je suis juste en train de me concentrer sur de nombreux aspects de C#, j'ai fait des logiciels embarqués l'année dernière mais je ne me considérerais pas comme un programmeur compétent.

Merci

+0

Où est 'PCB' défini? Êtes-vous sûr de regarder la même instance? Êtes-vous sûr que l'événement 'DataRecieved' est même déclenché? IIRC, il y a un seuil (nombre d'octets) qui doit être atteint avant que cet événement se déclenche. –

+0

Salut merci de répondre, oui l'événement DataReceived est certainement déclenché en mode débogage je peux voir que mon PCB a envoyé avec succès le message "? 10" et que les valeurs correctes ont été stockées dans le tableau. Je mets un marqueur sur le côté de promptFormAction(); donc je verrais le tableau dans sa forme complète. Ensuite, placez un marqueur dans l'indexeur et appuyez sur play pour voir que le tableau était vide. –

+0

PCB est défini dans la classe Form1 en haut comme ceci: pcbSerialPort PCB = new pcbSerialPort(); –

Répondre

1

À partir de votre code, je ne suis pas tout à fait sûr que l'objet PCB vous travaillez avec votre formulaire est en fait celui qui reçoit les données. Peut-être que vous travaillez avec deux instances différentes, d'autant plus que vous créez une nouvelle instance de Form1 chaque fois que les données arrivent!

(EDIT: De votre commentaire à la question, il est clair que c'est exactement le problème.Suivez ces instructions pour se fermer à ce que vous voulez).

Je vous suggère de revoir votre code pour transmettre le message reçu en tant qu'événement à l'instance de formulaire existante au lieu de la façon dont vous le faites maintenant. Un autre problème que vous pourriez rencontrer sera que les données que vous pensez avoir seront remplacées par le prochain message entrant, si l'événement DataReceived est asynchrone.

Je déclare un événement que l'instance de formulaire peut souscrire, de transmettre les données à analyser dans l'événement:

public class MessageReceivedEventArgs: EventArgs 
{ 
    public MessageReceivedEventArgs(byte[] data) : base() 
    { 
     Data = data; 
    } 

    public byte[] Data 
    { 
     get; 
     private set; 
    } 
} 

public event EventHandler<MessageReceivedEventArgs> MessageReceived; 

Ensuite, je change votre événement DataReceived comme suit:

public void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e) 
{ 
    for (int i = 0; i <= 2; i++) 
    { 
     serialPortReceiveBuffer[i] = (uint)serialPort1.ReadByte(); 
    } 

    byte[] dataCopy = new byte[serialPortReceiveBuffer.Length]; 
    Array.Copy(serialPortReceiveBuffer, dataCopy, dataCopy.Length); 

    promptFormAction(dataCopy); 
} 

private void promptForAction(byte[] data) 
{ 
    if (MessageReceived != null) 
     MessageReceived(this, new MessageReceivedEventArgs(data)); 
} 

Aussi, je garderais le serialPortReceiveBuffer totalement privé à cette classe, comme je l'ai dit, vous pouvez rencontrer des problèmes de synchronisation si vous ne le faites pas. C'est pourquoi je copie le tableau avant de le passer à l'événement.

Cette modification permet à tout abonné de s'enregistrer pour des notifications chaque fois que vous vous rendez compte que de nouvelles données sont arrivées.Pour utiliser cela, Form1 devrait ressembler à ceci (à peu près);

public class Form1 
{ 
    pcbSerialPort PCB; // The name of that class I don't know from your code 

    public Form1() 
    { 
     PCB = new pcbSerialPort(); 
     PCB.MessageReceived += MessageReceived; 
    } 

    private void MessageReceived(object sender, pcbSerialPort.MessageReceivedEventArgs e) 
    { 
     analyzeIncomingMessage(e.Data); 
    } 

    private void analyzeIncomingMessage(byte[] data) 
    { 
     if (data[0] == 63) 
     { 
      setBoardDesignator(data[1], data[2]); 
     } 
    } 
} 

Un autre conseil sur la façon dont vous gérez les données de série: Vous devez décider si vous avez lu à partir d'un port série dans une boucle ou si vous comptez sur l'événement DataReceived. Mettre une boucle dans l'événement n'est pas une bonne idée, car l'événement peut être appelé lors de l'arrivée des données à nouveau pendant que vous attendez.

Ce que vous devez faire est de créer un tampon qui prend toutes les informations du port série disponible. Si vous n'avez pas assez de données, ne pas attendre pour cela. Au lieu de cela, ajouter à la mémoire tampon à chaque fois que DataReceived est appelée et gérer un message lorsque suffisamment de données sont présentes.

+0

merci @ThorstenDittmar en essayant d'obtenir ma tête autour d'elle, d'abord je vais essayer de l'implémenter dans mon code. Je rencontre un problème avec 'byte [] dataCopy = serialPortReceiveBuffer.Copy();' déclaration il sais qu'il n'y a pas de surcharge pour la méthode de copie –

+0

Ah, ok. Je vais le faire différemment et mettre à jour ma réponse dans un instant. –

+0

Erreur accessibilité Incohérence: type de paramètre 'WindowsFormsApplication1.pcbSerialPort.MessageReceivedEventArgs' est moins accessible que la méthode 'WindowsFormsApplication1.Form1.MessageReceived (objet, WindowsFormsApplication1.pcbSerialPort.MessageReceivedEventArgs)' \t D: \ Users \ jbrown \ Documents \ Home travail \ HMI Design \ HMI Design \ HMI_071-02 \ Form1.cs HMI_071-02 –

0

Je pense que la réponse de Thorsten est bonne et il serait très sensé de la redessiner selon ces lignes, mais comme un minimum absolu, si vous voulez l'avoir tel que vous créez une nouvelle instance pour chaque message reçu, alors vous devrez passer l'instance de pcbSerialPort à MessageReceived puis au constructeur de votre classe Form1. Quelque chose comme:

Action<pcbSerialPort> promptFormAction = Form1.MsgReceived; 

public void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e) 
{ 
    // as Thorsten noted, you need to rethink this loop anyway 
    // what if there aren't at least three bytes to read? 
    for (int i = 0; i <= 2; i++) 
    { 
     serialPortReceiveBuffer[i] = (uint)serialPort1.ReadByte(); 
    } 

    promptFormAction(this); 
} 

Et votre méthode statique:

public static void MsgReceived(pcbSerialPort pcb) 
{ 
    Form1 _frm = new Form1(pcb); 
    _frm.analyzeIncomingMessage(); 
} 

Et vous constructeur de Form1:

public Form1(pcbSerialPort pcb) 
{ 
    PCB = pcb; 
} 
+0

Je suppose que nous sommes d'accord sur le fait qu'il y a beaucoup de raisons ** pas ** de le faire à sa manière, l'une d'elles étant que la classe UI indépendante a soudainement une connexion à l'interface utilisateur comme le besoin de coder le 'Form1 Méthode .MsgReceived'. –

+0

@ThorstenDittmar: Je suppose (peut-être) que la pensée de l'OP était dans le sens de pouvoir injecter une action pour gérer les données reçues. Bien sûr, il est codé en dur ici et dans le code original de l'OP, mais vous pouvez faire une propriété 'promptFormAction', ou le définir avec un argument constructeur et supprimer le couplage codé en dur dans l'interface utilisateur. –

+0

Je ne veux pas avoir à créer une nouvelle instance de Form1 chaque fois qu'un message est reçu. Je faisais juste ça parce que je ne connaissais pas d'autre moyen. J'apprends, mais lire sur les choses est différent de les mettre en œuvre. Là où on me donne un exemple, quand je l'essaie, ça ne marche pas forcément –