I am an embedded system software developer for safety critical systems, so I am fairly new with C# but proficient in C-based languages.
To provide a little background, I have developed a Windows Form that interprets serial data packets sent from my embedded software through the serial port into meaningful debugging information.
What I want to do is display each byte of each packet in a TextBox Control. The textbox control that displays the packet information is actually a second form opened by the first form. Here's the code for the event handler that opens the second form from the first:
private void ShowRawSerialData(object sender, EventArgs e)
{
SendSerialDataToForm = true;
if (SerialStreamDataForm == null)
SerialStreamDataForm = new RawSerialDataForm();
SerialStreamDataForm.Instance.Show();
}
In the above code, the .Instance.Show() directive is a means by which I may open a new form if the form is closed, but not show a new form if the form is already open.
Then, in my serial data received event handler, I do this:
// Get bytes from the serial stream
bytesToRead = IFDSerialPort.BytesToRead;
MsgByteArray = new Byte[bytesToRead];
bytesRead = IFDSerialPort.Read(MsgByteArray, 0, bytesToRead);
// Now MsgByteArray has the bytes read from the serial stream,
// send to raw serial form
if (SendSerialDataToForm == true && SerialStreamDataForm != null)
{
SerialStreamDataForm.UpdateSerialDataStream(MsgByteArray);
}
Where MsgByteArray is the byte array of the serial packet received. And here is the code for UpdateSerialDataStream:
public void UpdateSerialDataStream(Byte[] byteArray)
{
String currentByteString = null;
currentByteString = BitConverter.ToString(byteArray);
currentByteString = "0x" + currentByteString.Replace("-", " 0x") + " ";
if (RawSerialStreamTextBox.InvokeRequired)
{
RawSerialStreamTextBox.Invoke(new SerialTextBoxDelegate(this.UpdateSerialDataStream), new object[] { byteArray });
}
else
{
RawSerialStreamTextBox.Text += currentByteString;
}
RawSerialStreamTextBox.Update();
}
The end result is that the value of RawSerialStreamTextBox.Text is correctly updated with the string I intend on adding to the text box! For example, if I pass the byte array {0x01, 0x7F, 0x7E}, then, through the debugger, I can see that the value of RawSerialStreamTextBox.Text = "0x01 0x7F 0x7E".
The problem is that the text box control itself does not show the newly added text. So even though I can confirm through the debugger that RawSerialStreamTextBox.Text = "0x01 0x7F 0x7E" the text box in Windows does not show "0x01 0x7F 0x7E" but rather, remains blank.
Any ideas for what might be happening here?
I would guess that you are setting the Text property on an instance other than the one that is actually being displayed. A sanity check would be something like
RawSerialStreamTextBox.Visible = false;
Does it disappear?
To simplify a little, I would have UpdateSerialDataStream return a string (or pass a string to an out parameter) so that your Event Handler would, instead, look like this:
// Get bytes from the serial stream
bytesToRead = IFDSerialPort.BytesToRead;
MsgByteArray = new Byte[bytesToRead];
bytesRead = IFDSerialPort.Read(MsgByteArray, 0, bytesToRead);
// Now MsgByteArray has the bytes read from the serial stream,
// send to raw serial form
if (SendSerialDataToForm == true && SerialStreamDataForm != null)
{
RawSerialStreamTextBox.Text = UpdateSerialDataStream(MsgByteArray);
}
And UpdateSerialDataStream would look something like this:
public string UpdateSerialDataStream(Byte[] byteArray)
{
String currentByteString = null;
currentByteString = BitConverter.ToString(byteArray);
currentByteString = "0x" + currentByteString.Replace("-", " 0x") + " ";
return currentByteString;
}
You'd have to move your code handling the display of the form around a little, but this would allow the form already containing the TextBox to handle the update on its own.
Related
Any help will be appreciated!
We have some browser based InfoPath (admin approved) form with code behind.
I've got some business task, that when user types some VAT ID, there is required to check if the VAT ID is valid or not and return info as message box.
OK, I've used some web service call to VIES.
string dicValue = FormMethods.GetIpFieldValue(currentForm, formFields.PoDicIPField);
string countryCode = dicValue.Substring(0, 2);
string vatId = dicValue.Substring(2, (dicValue.Length - 2));
this.DataConnections["WebServices-Query-SOAP-CheckVat"].Execute();
bool isVatValid = FormMethods.CheckVAT(this.DataSources);
string notification = "This VAT is valid.";
if (isVatValid != true)
{
notification = "This VAT is not valid!";
}
Then I've tried to send an alert back to page.
HttpContext currentContext = System.Web.HttpContext.Current;
currentContext.Response.Write("<script type=\"text/javascript\">alert('" + notification + "'); </script>");
OK alert
And my problem starts after the user cliks "OK" at alert (loosing page content).
Page look likes this
Because when InfoPath form loads, it loads context of "FormServer.aspx" page.
When User Clicks on button, InfoPath form gets context of PostBack.FormServer.aspx page.
So I need to:
Store original response OutputStream from load
Send an alert
Rewrite currentContext.Response by original response from load
But when I try to save currentContext.Response.OutputStream in any object (for example byte[]), I'm getting following exception: "Stream was not readable".
byte[] currentContextResponseOutputStreamBytes = null;
using(Stream stream = currentContext.Response.OutputStream)
using (MemoryStream memoryStream = new MemoryStream())
{
stream.Dispose();
int count = 0;
do
{
byte[] buffer = new byte[1024];
count = stream.Read(buffer, 0, 1024);
memoryStream.Write(buffer, 0, count);
}
while (stream.CanRead && count > 0);
currentContextResponseOutputStreamBytes = memoryStream.ToArray();
}
Could you help me please with this task?
Thanks
The problem seems to be pretty trivial here. You first invoke dispose on your stream object and then try to read it. This cannot work, because dispose 'destroys' the stream, so there is nothing more to read. This is why you get the exception.
To get rid of the problem, simply remove the dispose invocation. It should be executed at the end, not at the beginning! Moreover, you don't need it at all, because dispose will be executed automatically at the end of the using block.
I have a simple client-server system sending plain text - though only commands that have been approved. The server is a Python system - and I've confirmed proper connections.
However, the client is C# - in Unity. Searching for examples, I stumbled across this bit of code. It does seem to do what I want, however, only partially:
public String readSocket()
{
if (!socketReady)
return "";
if (theStream.DataAvailable)
return theReader.ReadLine();
return "";
}
The strings I am sending end with \n, but I'm only getting half the message like this:
Message A:
claim_2
Message B:
_20_case
claim_1
I know this probably has to do with how I'm directly reading the line but I cannot find any better examples - strangely enough, everyone seems to point back at this snippet even when multiple people point out the problems.
Can anything be done to fix this bit of code properly?
In case it helps, I'm sending the information (from my Python server) out like this:
action = str(command) + "_" + str(x) + "_" + str(userid) + "_" + str(user)
cfg.GameSendConnection.sendall((action + "\n").encode("utf-8"))
When you do sockets programming, it is important to note that data might not be
available in one piece. In fact, this is exactly what you are seeing. Your
messages are being broken up.
So why does ReadLine not wait until there's a line to read?.
Here's some simple sample code:
var stream = new MemoryStream();
var reader = new StreamReader(stream);
var writer = new StreamWriter(stream) { AutoFlush = true };
writer.Write("foo");
stream.Seek(0, SeekOrigin.Begin);
Console.WriteLine(reader.ReadLine());
Note that there is no newline at the end. Still, the output of this little
snippet is foo.
ReadLine returns the string up to the first line break or until there is no
more data to read. The exception being reading from a stream that has no more
data to read, then it returns null.
When a NetworkStream has its DataAvailable property return true, it has
data. But as mentioned before, there is no guarantee whatsoever about what that
data is. It might be a single byte. Or a part of a message. Or a full message
plus part of the next message. Note that depending on the encoding, it could
even be possible to receive only part of a character. Not all character
encodings have all characters be at most a single byte. This includes UTF-8, which cfg.GameSendConnection.sendall((action + "\n").encode("utf-8")) sends.
How to solve this? Read bytes, not lines. Put them in some buffer. After every
read, check if the buffer contains a newline. If it does, you now have a full
message to handle. Remove the message up to and including the newline from the
buffer and keep appending new data to it until the next newline is received. And
so on.
This is how I process the entire line in my similar application, which is a very simple code, and your code may be different, but you can get the idea.
private string incompleteRecord = "";
public void ReadSocket()
{
if (_networkStream.DataAvailable)
{
var buffer = new byte[8192];
var receivedString = new StringBuilder();
do
{
int numberOfBytesRead = _networkStream.Read(buffer, 0, buffer.Length);
receivedString.AppendFormat("{0}", Encoding.UTF8.GetString(buffer, 0, numberOfBytesRead));
} while (_networkStream.DataAvailable);
var bulkMsg = receivedString.ToString();
// When you receive data from the socket, you can receive any number of messages at a time
// with no guarantee that the last message you receive will be complete.
// You can receive only part of a complete message, with next part coming
// with the next call. So, we need to save any partial messages and add
// them to the beginning of the data next time.
bulkMsg = incompleteRecord + bulkMsg;
// clear incomplete record so it doesn't get processed next time too.
incompleteRecord = "";
// loop though the data breaking it apart into lines by delimiter ("\n")
while (bulkMsg.Length > 0)
{
var newLinePos = bulkMsg.IndexOf("\n");
if (newLinePos > 0)
{
var line = bulkMsg.Substring(0, newLinePos);
// Do whatever you want with your line here ...
// ProcessYourLine(line)
// Move to the next message.
bulkMsg = bulkMsg.Substring(line.Length + 1);
}
else
{
// there are no more newline delimiters
// so we save the rest of the message (if any) for processing with the next batch of received data.
incompleteRecord = bulkMsg;
bulkMsg = "";
}
}
}
}
My existing code:
private void ConvertAndSend_Click(object sender, EventArgs e)
{
if (serialPort.IsOpen)
{
byte[] TxBuffer = new byte[240];
string[] coords = textBox1.Text.Split('\n');
for (int i = 0; i <= coords.Length - 1; i++)
{
if (coords[i].Length > 0)
{
Data = GetValue(coords[i]);
}
}
TxBuffer[0] = 0x5A;
TxBuffer[1] = Instruction;
TxBuffer[2] = (byte)Data.Length;
Data.CopyTo(TxBuffer, 3);
TxBuffer[Data.Length + 3] = 0x2C;
serialPort.Write(TxBuffer, 0, 4 + Data.Length);
}
}
Now I am sending every "Data" in separate "Txbuffer". e.g. if I have more than one "Data", I am going to send more than one "Txbuffer". How can I combine all "Data" into one "Txbuffer" and send at one time?
It isn't exactly "wrong", although a magic number like 240 doesn't win any prizes. You can also use BinaryWriter, pass the SerialPort.BaseStream to its constructor.
Keep in mind that serial ports implement streams, not 'packets'. Just a raw train of bytes with no distinctive beginning and end. Just like TCP. There is no framing protocol unless you create your own. Which you did. It is up to the receiver to turn the stream of bytes back into a frame. That same requirement doesn't exist when you transmit it.
I'm talking serially to a Smart Motor and I'm trying to look for the specific string "# Positon" coming back from the motor. When I see that I want to set the Play button to be enabled (btnPlay.Enabled=true;).
I've tried every way but can't seem to fit it to the following code. What can I place in my code below where I can test the incoming data and then trigger the enable?
The following code works - I just don't know where and how to read for specific information.
private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
// If the com port has been closed, do nothing
if (!comport.IsOpen) return;
// Determain which mode (string or binary) the user is in
if (CurrentDataMode == DataMode.Text)
{
// Read all the data waiting in the buffer
string data = comport.ReadExisting();
// Display the text to the user in the terminal
Log(LogMsgType.Incoming, data);
}
else
{
// Obtain the number of bytes waiting in the port's buffer
int bytes = comport.BytesToRead;
// Create a byte array buffer to hold the incoming data
byte[] buffer = new byte[bytes];
// Read the data from the port and store it in our buffer
comport.Read(buffer, 0, bytes);
// Show the user the incoming data in hex format
Log(LogMsgType.Incoming, ByteArrayToHexString(buffer));
}
}
I'm trying to send the Base64 string of a screenshot to the server via NetworkStream and it appears I'm receiving the full string, problem is it's scrambled...
I assume this has something to do with it being fragmented and put back together? What would be the appropriate way to go about this...
Client Code
byte[] ImageBytes = Generics.Imaging.ImageToByte(Generics.Imaging.Get_ScreenShot_In_Bitmap());
string StreamData = "REMOTEDATA|***|" + Convert.ToBase64String(ImageBytes);
SW.WriteLine(StreamData);
SW.Flush();
Server Code
char[] ByteData = new char[350208];
SR.Read(ByteData, 0, 350208);
string Data = new string(ByteData);
File.WriteAllText("C:\\RecievedText", Data);
Also the size of the sent message and the char array are exactly the same.\
EDIT:
After messing around with it some more I realized the text isnt scrambled but the proper text is trailing the previous stream.. How can I ensure the stream is clear or gets the entire text
It's likely that you're not reading all of the previous response. You have to read in a loop until you get no data, like this:
char[] ByteData = new char[350208];
int totalChars = 0;
int charsRead;
while ((charsRead = SR.Read(ByteData, totalChars, ByteData.Length - totalChars) != 0)
{
totalChars += charsRead;
}
string Data = new string(ByteData, 0, totalChars);
File.WriteAllText("C:\\RecievedText", Data);
The key here is that StreamReader.Read reads up to the maximum number of characters you told it to. If there aren't that many characters immediately available, it will read what's available and return those. The return value tells you how many it read. You have to keep reading until you get the number of characters you want, or until Read returns 0.