|
Ok, then scrap that, I thought that was where you were pointing me to, something like a stack. I'm glad it was not, because those looked very complicated!
In trying to write this down and conceptualize it first, I'm wondering if this is a good start?
uC -->
pc thread1 that does a serialport.readLine() into a string, then locks a global array1, copies into it, unlocks global array1 and then clears the string -->
pc thread2 that monitors global array1, locks it, copies data to local string or strings, clears and then unlocks global array1, parses data, adds unique single item delimiter and locks then copies to global array2, unlocks global array2, clears local string or strings -->
pc thread3 that monitors global array2, locks it, copies data to local string, clears global array2, unlocks global array2 and updates pc rich text boxes, then clears local strings
I believe asynchronous data transfer can be achieved if I use some do while loops, to wait for the locks/unlocks.
What do you think?
|
|
|
|
|
way too complex.
Luc Pattyn [My Articles] Nil Volentibus Arduum
Fed up by FireFox memory leaks I switched to Opera and now CP doesn't perform its paste magic, so links will not be offered. Sorry.
|
|
|
|
|
Ok, then don't look at the code I posted a few hours ago
I'm trying to get it to work, but will try and simplify it also, if I can get it to work. I tried to google for some psuedocode, but didn't find anything.
|
|
|
|
|
Well Luc,
After 2 straight days, I've got it working and working fairly decent. If you want I will post the code, but I'm confident it is still to complex. This is my first attempt at data sharing amongst threads.
I'd love to see an efficient/less complex method if you are willing to share. I'd like to learn to do it the right/efficient way. If you're willing to guide me in the right direction, I'm all ears.
|
|
|
|
|
Why would one need all those threads? All they offer is overhead, bug risks, and performance loss.
You should have created one, not three. And please design before you code, not the other way around.
Luc Pattyn [My Articles] Nil Volentibus Arduum
Fed up by FireFox memory leaks I switched to Opera and now CP doesn't perform its paste magic, so links will not be offered. Sorry.
|
|
|
|
|
I chose multiple threads so that the data could be received asynchronously compared to when it was displayed and so no data was lost ... originally I had a single thread. I know you have serial experience, so if you think a single windows thread can handle the 115000 kbps, I will try and condense the 3 threads into 1.
|
|
|
|
|
You never provided info about baud rates, message length, message frequency, throughput.
On a single core, adding threads does not increase throughput; what it can do, using sufficient buffer space, is cope better with peak loads (it is the buffers who deal with the peaks, the threads are just an easy way to decouple the producer and consumer code).
On a multi-core, adding more threads may or may not help, it depends on a lot of circumstances.
I am guessing your real bottleneck is in the GUI, and then IIRC you are using a TextBox, which is a bad choice for displaying large amounts of line-oriented text. A ListBox is a far better choice, as it does not degrade quadratically under increasing data load. A ListBox holds a list of items, a TextBox concatenates all its data, which gets expensive.
I would use just one thread and a ListBox; some 20 lines of code should do it!
And if I were to need special operations, such as e.g. "save to file", I would add that functionality to the ListBox rather than paying a performance penalty all the time.
Luc Pattyn [My Articles] Nil Volentibus Arduum
Fed up by FireFox memory leaks I switched to Opera and now CP doesn't perform its paste magic, so links will not be offered. Sorry.
|
|
|
|
|
Thanks for the guidance Luc. I will try and condense the code as you've suggested. My code is about 200 lines long for the 3 threads, but it wasn't all in vain as I learned a lot. I was going to use a rich text box so that I could set thresholds and change background colors of each individual box, depending on the value. I would venture that would slow the gui down even more though, so listboxes it is.
|
|
|
|
|
You're welcome.
And you can beautify a ListBox as well, here[^] is an example.
And then, this[^] might interest you.
Luc Pattyn [My Articles] Nil Volentibus Arduum
Fed up by FireFox memory leaks I switched to Opera and now CP doesn't perform its paste magic, so links will not be offered. Sorry.
|
|
|
|
|
Awesome! I will be reading your article. This is great
|
|
|
|
|
Hi Luc,
It isn't 20 lines, but it's pretty fast compared to what I had before, I have the uC push serial data every 10mS and it has no problem keeping up.
What do you think?
public string[] produceParseConsumeSerial(byte serialByte)
{
try
{
startCharacters[0] = '@';
startCharacters[1] = '@';
startCharacters[2] = '@';
endCharacters[0] = '|';
endCharacters[1] = '|';
endCharacters[2] = '|';
valueDelimiter[0] = '\r';
valueDelimiter[1] = '\r';
readLine[readLineIndex] = serialByte;
if (readLine[0] == startCharacters[0])
{
if (readLine[1] == startCharacters[1])
{
if (readLine[2] == startCharacters[2])
{
if ((readLine[readLineIndex] != valueDelimiter[0]) && (readLine[readLineIndex] != endCharacters[0]) && (readLine[readLineIndex] != 0) && (readLine[readLineIndex] != startCharacters[0]))
{
serialPortReadBytes[serialPortReadBytesIndex] = readLine[readLineIndex];
serialPortReadBytesIndex = serialPortReadBytesIndex + 1;
}
if ((readLine[readLineIndex] == valueDelimiter[1]) && (readLine[readLineIndex - 1] == valueDelimiter[0]))
{
int nullCount = 0;
string readLineString = System.Text.ASCIIEncoding.ASCII.GetString(serialPortReadBytes).ToString();
foreach (byte singleByte in serialPortReadBytes)
{
if (singleByte != 0)
{
nullCount = nullCount + 1;
}
}
int serialPortReadBytesCount = serialPortReadBytes.Count();
Array.Clear(serialPortReadBytes, 0, serialPortReadBytesCount);
serialPortReadBytesIndex = 0;
readLineString = readLineString.Remove(nullCount);
if (readLineString != "")
{
completeLineByLineArray.Add(readLineString);
}
readLineString = "";
}
else if ((readLine[readLineIndex] == valueDelimiter[0]))
{
int nullCount = 0;
string readLineString = System.Text.ASCIIEncoding.ASCII.GetString(serialPortReadBytes).ToString();
foreach (byte singleByte in serialPortReadBytes)
{
if (singleByte != 0)
{
nullCount = nullCount + 1;
}
}
int serialPortReadBytesCount = serialPortReadBytes.Count();
Array.Clear(serialPortReadBytes, 0, serialPortReadBytesCount);
serialPortReadBytesIndex = 0;
readLineString = readLineString.Remove(nullCount);
if (readLineString != "")
{
completeLineByLineArray.Add(readLineString);
}
readLineString = "";
}
if ((readLine[readLineIndex] == endCharacters[2]) && (readLine[readLineIndex - 1] == endCharacters[1]) && (readLine[readLineIndex - 2] == endCharacters[0]))
{
string[] delimitedAndParsedArray = new string[completeLineByLineArray.Count];
Array.Copy(completeLineByLineArray.ToArray(), delimitedAndParsedArray, completeLineByLineArray.Count);
completeLineByLineArray.Clear();
int readLineLength = readLine.Count();
Array.Clear(readLine, 0, readLineLength);
readLineIndex = 0;
int serialPortReadBytesCountInitialClear = serialPortReadBytes.Count();
Array.Clear(serialPortReadBytes, 0, serialPortReadBytesCountInitialClear);
serialPortReadBytesIndex = 0;
return delimitedAndParsedArray;
}
}
}
readLineIndex = readLineIndex + 1;
}
else
{
int serialPortReadBytesCount = serialPortReadBytes.Count();
Array.Clear(serialPortReadBytes, 0, serialPortReadBytesCount);
int readLineLength = readLine.Count();
Array.Clear(readLine, 0, readLineLength);
readLineIndex = 0;
}
}
catch (Exception ex)
{
MessageBox.Show("produceParseConsumeSerial() failure " + ex.Message + " " + ex.Data + " " + ex.StackTrace);
}
return null;
}
|
|
|
|
|
turbosupramk3 wrote: What do you think?
I did not study that in any detail, it does look horrible, and I'm flabbergasted. IIRC all data is textual, so I was expecting something that reads one line (hence a string) at once (using SerialPort.ReadLine), then validates it and extracts data. Yours is fiddling with bytes, on and on, in incomprehensible ways; and it is not even complete, where is the serial port access itself?
It would have helped if you had shown an expected string.
It would have helped if your identifiers were shorter, how often does one need to see "read" or "readline"?
It also would have helped if your method had a meaningful name (normally one verb + one noun), I can only guess what produceParseConsumeSerial() is supposed to do, and I can't begin to imagine why it needs to return a string array. A functional comment on top would have been nice.
And I don't like that many nested if statements; it is too hard to understand, and it ruins the layout.
I suggest you scrap all of it, and come up with simple, clean code.
|
|
|
|
|
Ok,
I can try again. It's a method, not a class yet, that is why there is no serial port information as far as opening it, that is done in the method that calls upon produceParseConsumeSerial(). I guess the name is long, I combined the three threads and just named the single thread, the appended names of the three individual threads.
I used ReadByte() instead of ReadLine() and pass the method a byte, each time I receive one, I like doing it that way instead of ReadLine(). You are right, it does need more commenting, and an example line, version 3 will have that. I will probably combine the if statements with && in version 3 as well, you are right, that is something that would make it easier to read.
Thanks for the critique, the next version will be better, I promise. This version is at least functional, and that allows me to continue to improve it while testing functionality.
|
|
|
|
|
As OriginalGriff and Luc already pointed out it's a let's say tricky thing.
When I createed my first program that dealt with searial port I was like "What's up with all this nonsence about SerialPort headeaches?".
I was lucky in that I only recevied a "1", "2", "3" or "X" and I always recieved the whole message.
Later on with some more complex messaging I started to see the "headeaches".
The only way to deal with is corectly is to have and know the message start and end specifications known as preffix and suffix in the "serialport world".
Since that day on, I always create(if possible) the suffix and the prefix for devices let's say a RS232 scanner.
If I'm not in control of the suffix and prefix I ask for them and won't accept a situation with no clear prefix and suffix data.
One needs to know when the message starts and when it ends. How you deal with it(e.g event or background worker as Luc suggested) it's your choice.
But you need to know when a message starts and when it ends. Else it's a gamble. I mean it.
Ex: 3CXXXXXXXXXXX[CR][LF]
In this example 3C marks the start of the message and "\r\n" the end. XXXXXXX is the actual message.
All the best,
Dan
|
|
|
|
|
Hi Dan,
Thanks for the reply. I am fortunate enough to be able to create a prefix and suffix. I have my suffix right now as |||, and I could add a prefix, maybe @@@ ?
Does that sound sufficient, along with 3 threads working asynchronously (mapped out in my other reply) with 2 global arrays to parse the data?
|
|
|
|
|
Yes, sure. It should do the trick.
All the best,
Dan
|
|
|
|
|
Ok, I'm actually a little nervous to try this how does this look? I spent all morning trying to sort this out, but have not tested it yet.
If a total rewrite isn't needed, should I have the threads looping all of the time, with a small wait time at the end? Or have the receive thread spawn the other threads?
Also, is serialPort1.ReadByte(), or is serialPort1.ReadLine() a better option?
Thanks for the help everyone, I've learned a lot!
private void produceSerial()
{
try
{
List serialPortReadLine1 = new List(500);
byte[] readLine = new byte[500];
int readLineIndex = 0;
while(globalVariables.receivingSerial == true)
{
if (serialPort1.BytesToRead > 0)
{
readLine[readLineIndex] = Convert.ToByte(serialPort1.ReadByte());
readLineIndex = readLineIndex + 1;
}
if (readLine[0] == '@')
{
if (readLine[1] == '@')
{
if ((readLine[2] == '@') && (readLine[3] != 0))
{
serialPortReadLine1.Add(readLine[readLineIndex]);
if ((readLine[readLineIndex] == '|') && (readLine[readLineIndex - 1] == '|') && (readLine[readLineIndex - 2] == '|'))
{
lock (globalVariables.linesRead)
{
int elements = globalVariables.linesRead.Count;
for (int i = elements; i < (elements + 1); i++)
{
globalVariables.linesRead[i + 1] = serialPortReadLine1.ToString();
serialPortReadLine1.Clear();
readLineIndex = 0;
}
}
}
}
}
}
}
}
catch (Exception ex)
{
MessageBox.Show("produceSerial() failure " + ex.Message);
}
}
private void parseSerial()
{
try
{
string [] linesUnparsed = new string[500];
while (globalVariables.receivingSerial == true)
{
if (globalVariables.linesRead.Count != 0)
{
lock (globalVariables.linesRead)
{
globalVariables.linesRead.CopyTo(linesUnparsed);
globalVariables.linesRead.Clear();
}
List charactersReplaced = new List(500);
string characterReplaced = "";
foreach (string line in linesUnparsed)
{
characterReplaced = line.Replace("\r", "<>");
charactersReplaced.Add(characterReplaced);
}
char[] splitCharacters = new char[] { '<', '>' };
string[] linesParsed = new string[500];
foreach (string lineUnparsed in linesUnparsed)
{
linesParsed = lineUnparsed.Split(splitCharacters, StringSplitOptions.None);
}
Array.Clear(linesUnparsed, 0, linesUnparsed.Length);
lock (globalVariables.linesParsedArray)
{
foreach (string lineParsed in linesParsed)
{
globalVariables.linesParsedArray.Add(lineParsed);
}
Array.Clear(linesParsed, 0, linesParsed.Length);
}
}
}
}
catch (Exception ex)
{
MessageBox.Show("parseSerial() failure " + ex.Message);
}
}
private void consumeSerial()
{
try
{
string[] dataOutputQueue = new string[500];
while (globalVariables.receivingSerial == true)
{
if (globalVariables.linesParsedArray.Count != 0)
{
lock (globalVariables.lineRead)
{
globalVariables.linesParsedArray.CopyTo(dataOutputQueue);
globalVariables.linesParsedArray.Clear();
}
foreach (string dataOutputLine in dataOutputQueue)
{
if (dataOutputLine.Contains("ps1"))
{
}
if (dataOutputLine.Contains("ps2"))
{
}
}
Array.Clear(dataOutputQueue, 0, dataOutputQueue.Length);
}
}
}
catch (Exception ex)
{
MessageBox.Show("consumeSerial() failure " + ex.Message);
}
}
|
|
|
|
|
I developed some com addin for excel 2003. They did no work in excel 2010. Any one used com addin for UDF in excel 2010?ùThank for your time
|
|
|
|
|
Com-addins in Office are usually specific to that particular version. Simple solution; move your logic to a separate assembly, and create a project for each version that you need to support.
Bastard Programmer from Hell
|
|
|
|
|
thanks, i think it is the only think to do
Has anyone used UDF comm add in with Excel2010...Is it suported?
|
|
|
|
|
The .Net framework provides a way to allow com components to work with the framework itself (which is managed code).
You need to have a look at COM interop[^].
|
|
|
|
|
you send link to Visual Studio .NET 2003 ...?!
|
|
|
|
|
You can use COM interop with all versions of .Net.
This is an msdn link. You can change the version of .Net by choosing from a dropdown on the link next to Visual Studio 2003 mentioned on the website.
|
|
|
|
|
I can not understand way you send link for - Net 2003. Since I was able to make work com interop with excel 2003 but not with excel 2010. Why I need the link to .NET 2003?
|
|
|
|
|
having any idea?
ocr from japenese to english using c# with MODI
|
|
|
|
|