Jump to content


Check out our Community Blogs

Register and join over 40,000 other developers!


Recent Status Updates

View All Updates

Photo
- - - - -

Visual C++ Rs-232 Communication Incoming String Analysis Using .net

string

  • Please log in to reply
3 replies to this topic

#1 KunLin

KunLin

    CC Lurker

  • Just Joined
  • Pip
  • 1 posts
  • Programming Language:C, C++, Visual Basic .NET
  • Learning:C++, Visual Basic .NET

Posted 28 June 2012 - 08:49 AM

DISCLAIMER: I am self-teaching VC++ without a textbook (I have prior knowledge of C and limited understanding of C++), just through trial and error and browsing online resources (forums, tutorials), this is my first time ever posting in one. I have been having trouble with analysis of strings, this may seem like a very simple problem I am asking but I would greatly appreciate any help or pointers towards the right drection.

Background: I am writing a program to communicate with a Keyence CV-551 (you don't have to know what that is) using RS-232 protocol through the serial port. I have managed to successfully read and write with the machine however I am not sure how to go about analysing the strings that are being returned to me.

Problem: The data that the machine sends me is in regards to the pass/fail status of parts I am analysing; a pass is depicted with 0 and a fail with 1. Each part has anywhere between 1 to 8 features that can be analysed so a string of data received for a part that has all features p**ed would look like: 0,0,0,0,0. If one feature fails then the whole part fails so an example of a failed part with 7 features could be: 0,1,0,0,0,0,0. I want to be able to analyse these strings so that I know if the part failed or not and if it did, which feature(s) caused the failure. The leftmost value can be considered feature 1, increasing as you move right to feature 8. If the part has less than 8 features I want to be able to return an indicator of that as well. I have been going through the MSDN library for the string class and reading about the various methods however I am having a hard time piecing everything together and figuring out what I should use to complete what needs to be done. My main concerns are taking the commas out of the string, counting how many features there are and displaying which features p**ed and which ones failed. Any and all help is very appreciated, thank you.


This is my code for opening and reading from the serialport:


private: void OpenPort(){
mySerialPort = gcnew SerialPort("COM5");
mySerialPort->Open();
mySerialPort->BaudRate = 9600;
mySerialPort->Parity = Parity::None;
mySerialPort->StopBits = StopBits::One;
mySerialPort->DataBits = 8;
mySerialPort->Handshake = Handshake::None;
mySerialPort->NewLine = "/n";
mySerialPort->DataReceived += gcnew SerialDataReceivedEventHandler(this, &Form1::DataReceivedHandler);
if (!mySerialPort->IsOpen)
mySerialPort->Open();
else
;  //already open
}

private: void DataReceivedHandler(Object^ sender,SerialDataReceivedEventArgs^ e){
SerialPort^ sp = (SerialPort^)sender;
String^ incoming;
while (sp->BytesToRead > 0)
incoming = sp->ReadExisting();
MessageBox::Show(incoming);
HandleData(incoming);
}

private: void HandleData(String^ Data){
//String analysis here
}

  • 0

#2 tpPacino

tpPacino

    CC Regular

  • Member
  • PipPipPip
  • 29 posts
  • Location:Sarajevo, Bosnia and Herzegovina
  • Programming Language:C, Java, C++, PHP, (Visual) Basic, PL/SQL, Pascal, Others
  • Learning:Objective-C, C#, JavaScript, Ruby, Haskell, Others

Posted 28 June 2012 - 09:59 AM

I can see that your main problem is string management. Have you tried converting the System String ^ to std string?
There are some methods on how to convert it...
You will need to include these three files:

#include <string>
#include <msclr\marshal_cppstd.h>
#include <algorithm>
and then use this method:

msclr::interop::marshal_context context;
std::string str = context.marshal_as<std::string>(TestBox->Text);

Also, if you want to erase the commas from the string you can use this(after you converted it to a string):

std::string::iterator end_pos = std::remove(str.begin(), str.end(), ',');
str.erase(end_pos, str.end());

After you do that, you can convert it back to the System String ^ using this simple method:

String^ strNew = gcnew String(str.c_str());
Ispis->Text=String::Format("{0}", strNew);

To check if you have any corrupted components use this:


bool check = false;
int component(0);
for(int i(0); i<str.length(); i++)
if(str[i] != '0') {
check = true;
component = i+1;
}
if(check) // do something
If you got any other issues, please ask questions here. :)
  • 0

#3 BlackRabbit

BlackRabbit

    CodeCall Legend

  • Expert Member
  • PipPipPipPipPipPipPipPip
  • 3871 posts
  • Location:Argentina
  • Programming Language:C, C++, C#, PHP, JavaScript, Transact-SQL, Bash, Others
  • Learning:Java, Others

Posted 28 June 2012 - 11:06 AM

what about reading the incoming string to a feature array, that will give you a organized way to make it

a pseudo code like this might help :


yourdatatype feature = new yourdatatype[MAX_FEATURES];
int feature_idx =0;

loop with strlen as end loop condition

if ( incoming[loop_idx] = ',' ) { feature[feature_idx] = strExtraction(); feature_idx++; };

end loop

here you got feature_idx features captured, and organized in an easy to handle feature's array, which in fact, you can have in the object as a buffer, in form of an array of received features ;)

sorry for the pseudocode, but since i don' recall string split in c++ ( to array, of course you have strchr or sscanf, but they required either looping or fixed input so i just went old school )

my question is ... you really get string ? i mean, not binary 0 and 1 ? extrange for a port communication, i mean, it is very different the chars '0' and '1' to the binary values 0 and 1, weird isn't it ?
  • 0

#4 kernelcoder

kernelcoder

    CC Devotee

  • Expert Member
  • PipPipPipPipPipPip
  • 990 posts
  • Location:Dhaka
  • Programming Language:C, Java, C++, C#, Visual Basic .NET
  • Learning:Objective-C, PHP, Python, Delphi/Object Pascal

Posted 28 June 2012 - 09:14 PM

First let us examine the port opening part:
private: void OpenPort(){
mySerialPort = gcnew SerialPort("COM5");
mySerialPort->Open();
mySerialPort->BaudRate = 9600;
mySerialPort->Parity = Parity::None;
mySerialPort->StopBits = StopBits::One;
mySerialPort->DataBits = 8;
mySerialPort->Handshake = Handshake::None; // Note I removed // mySerialPort->NewLine = "/n";
mySerialPort->DataReceived += gcnew SerialDataReceivedEventHandler(this, &Form1::DataReceivedHandler);
if (!mySerialPort->IsOpen)
mySerialPort->Open();
}
The first question is is your DataReceivedHandler get called? I mean are you getting data? If no, you need to know about BaudDate, Parity, StopBits, DataBits & Handkshake mechanism is using by the other end (the device that is sending data). If you don't know that, my suggestion is try with every combination of values for all those properties to open the port and track for which combination you are getting data from the port. However, if there is a handshaking protocol exist to communicate with the Keyence CV-551, you must need to know that. Otherwise you will not be able to communicate with that device properly. I'm going to tell a bit details about this handshaking mechanism: Handshaking is a pre-condition that should be met between two parties before the actual communication starts. This is something like sharing coded sentence between two espionage agents before starting to share their actual information. As example, in our serial-port case, it may be something like after opening the serial port, 1) the receiver first send a 1 byte 0x01 value, 2) after receiving ox01 the device send 0x00 over the serial port to sender. 3) and the device starts sending actual data over the serial port. --- So eventually we need to know the handshaking mechanism if there is anything exist with that Keyence CV-551.

Second point is that you should not use SerialPort::ReadExisting method to read data. The caution about this method from MSDN is that: "This method returns the contents of the stream and internal buffer of the SerialPort object as a string. This method does not use a time-out. Note that this method can leave trailing lead bytes in the internal buffer, which makes the BytesToRead value greater than zero.". I think it is better to use BytesToRead/Read property-method pair to read data.

Third point is that the DataReceived event handle is fired on a thread from thread-pool as soon as a data is arrived there in the input stream. There is no time specification about when that event will be fired as there is a condition exist with the tread-pool. So when there is a free thread exist in thread pool and there is enough data in the stream, that event may fire. And the important part is that the thread is not the same one your DataReceived event handler will get called. It will be actually different thread each time. So there is a chance that two or more DataReceived event handler will be executed at the same time on two or more threads. So you have to be careful about if any shared data if you use in that method. Or, we can use a locking mechanism, let access on that shared data only to one thread at a time. Well, don't worry, that is easy with .NET -- We need to use Monitor class for that.

private: void DataReceivedHandler(Object^ sender,SerialDataReceivedEventArgs^ e)
{
Monitor::Enter(this);
try
{
while (mySerialPort->BytesToRead > 0) {
int count = Math::Min(mySerialPort->BytesToRead, mySerialPort->ReadBufferSize);
if (count > 0) {
array<unsigned char>^ data = gcnew array<unsigned char>(count); 
mySerialPort->Read(data, 0, count);
array<Char>^asciiChars = gcnew array<Char>(mySerialPort->Encoding->GetCharCount(data, 0, data->Length ));
mySerialPort->Encoding->GetChars( data, 0, data->Length, asciiChars, 0 );
String^ incoming = gcnew String( asciiChars );
HandleData(incoming);
}
}
}catch(Exception^ exc) { }
finally
{
Monitor::Exit(this);
}
}



Now your HanldeData method. You want to handle each 8 bytes segment at a time, right. So you need to keep a variable to keep the modulo of 8 data bytes. Here is the code.

private: String^ previousData; // you need to create it inside constructor as previousData = gcnew String^()

private: void HandleData(String^ data)
{
previousData += data;
while (previousData->Length > 8) {
String^ part = previousData->Remove(0, 8);
for(int i = 0; i < part->Length; i++) {
if (part[i] == '0') {
// Do the if part
}
else {
// Do the else
}
}

}
}

  • 0





Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download