Indexer created to view an array does not see changes made to the array
I am very new to C # and I am creating a serial port class for a board I have developed. This class contains methods for opening / closing the serial port connected to the board. It also needs to read messages from the board and write messages from the UI to the board (I am using a forms app to enter and display values).
I read the internal input buffer and put the bytes in my own program buffer, when the message is complete, this will force the form to parse the message ...
For this, I created a pointer to point to an array (from the form) and take the bytes it wants.
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]; }
}
this is the code inside my pcbSerialPort class and the code associated with it in the Form1 class looks like this:
public static void MsgReceived()
{
Form1 _frm = new Form1();
_frm.analyzeIncomingMessage();
}
public void analyzeIncomingMessage()
{
if (PCB[0] == 63)
{
setBoardDesignator(PCB[1], PCB[2]);
}
}
My problem is that when I use the index to access serialPortReceiveBuffer
, it doesn't see the changes I made to it when I put the received bytes in the same array. For example, when I receive my own protocol string -> "? 10", the buffer is filled [63] [49] [48]
Although when I try to access this buffer using the indexer, I get [0] [0] [0]
Please, help? Also, I know there are maybe a few other things I could do better, so if you have general advice that would be great. Also in a language I can understand. I'm just looking back at many aspects of C #, I've been doing embedded software for the past year, but I wouldn't consider myself a standalone programmer.
thank
source to share
From your code, I'm not entirely sure that the object PCB
you are working with in your form is actually the one that receives the data. You may be working with two different instances, especially when you create a new instance Form1
whenever data comes in!
(EDIT: It is clear from your comment on the question that this is exactly the problem. Follow these instructions to close them whatever you want).
I suggest you redesign your code to pass the received message as an event to an existing instance of the form, not how to do it now. Another problem you will run into will be that the data you think you will receive will be overridden by the following message since the event DataReceived
is asynchronous.
I declare an event that an instance of the form can register for, passing the data to be parsed to the event:
public class MessageReceivedEventArgs: EventArgs
{
public MessageReceivedEventArgs(byte[] data) : base()
{
Data = data;
}
public byte[] Data
{
get;
private set;
}
}
public event EventHandler<MessageReceivedEventArgs> MessageReceived;
Then I would change your event DataReceived
like this:
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));
}
Also I would keep serialPortReceiveBuffer
completely private for this class, as I said, you might run into synchronization problems if it doesn't. This is why I copy the array before passing it to the event.
This change allows any subscriber to register for notifications whenever you realize that new data has emerged.
To use this it Form1
should look something like this:
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]);
}
}
}
Another tip for how you handle serial data: you need to decide if you are reading from the serial port in a loop or relying on an event DataReceived
. Putting a loop in an event is not a good idea, as the event can be triggered by returning data while waiting.
What you need to do is create a buffer that receives all the information from the available serial port. If you don't have enough data, don't wait for it. Instead, add to the buffer whenever a DataReceived
message is called and processed when there is enough data.
source to share
I think Thorsten's answer is good and it would be wise to rewrite it along these lines, but as an absolute minimum, if you want it to create a new instance Form1
for every message received, then you need to pass the instance pcbSerialPort
to MessageReceived
and then to the constructor of your class Form1
... Something like:
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);
}
And your static method:
public static void MsgReceived(pcbSerialPort pcb)
{
Form1 _frm = new Form1(pcb);
_frm.analyzeIncomingMessage();
}
And you create for Form1
:
public Form1(pcbSerialPort pcb)
{
PCB = pcb;
}
source to share