|
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
|
|
|
|
|
You can OCR the Japanese, but then you'll have to run that string through a translator, like http://www.microsofttranslator.com/[^]. Look at the "For Developers" link and start reading.
|
|
|
|
|
I'm getting the following error with VS2010 (still training), and, I can create a text file within this directory and as a matter of fact the executable is being created, so why is this error being displayed?
Warning GettingStarted Could not create output directory 'C:\Documents and Settings\Jon\My Documents\Visual Studio 2010\Projects\Training\CreateEventLog\GettingStarted\GettingStarted\bin\Debug\'. Access is denied.
1
Jon
|
|
|
|
|
It appears that your application which resides in the ...\bin\debug directory is attempting to create the folder in which it resides.
The key is in your error message. Cout not create output directory .
My suspicion is that you are using Directory.CreateDirectory instead of File.Create.
If you are using the sample from MSDN (http://msdn.microsoft.com/en-us/library/as2f1fez.aspx[^])
The safe way to do this is to always check if an object already exists before trying to create it.
if(!Directory.Exists "your directory path"))
{
Directory.CreateDirectory("your path");
}
FileStream fs;
if(!File.Exists("your file path"))
{
fs = File.Create("your file path");
}
else
{
fs = new FileStream("your file path", FileMode.Open);
}
if(File.Exists("your file path"))
{
File.Delete("your file path");
}
using(FileStream fs = File.Create("your file path"))
{
... Write stuff to the file here
}
using (FileStream fs = File.OpenRead(path))
{
... Read the contents of the file here
}
This MSDN FileStream[^] library article is a very good one to learn from.
I wasn't, now I am, then I won't be anymore.
|
|
|
|
|
I am new to working with com objects and trying to figure out how to make this work. The code runs and it hits the if statement and even writes out the test message in the if statement. The problem is that when I open the excel file there is no change. It did somehow corrupt one and tell me that it is read only now and it will occasionally pop up the do you want to save this excel file message, but nothing ever shows. I have also tried Value and Value2 as I have seen in various pages, but I just can't find anything that does what I want it to do or any place that even gives good help on how to do it. This is the code I am using. A lot of it is just hardcoded test values so it really should be straight forward.
var excelDocument = new Excel.Application();
Excel.Workbook xlWorkbook = excelDocument.Workbooks.Open(@"c:/score/W5490C - Fresh.xlsx");
Excel._Worksheet xlWorksheet = xlWorkbook.Sheets[1];
Excel.Range xlRange = xlWorksheet.UsedRange;
int rowCount = xlRange.Rows.Count;
int colCount = xlRange.Columns.Count;
//Calculate and record the air losses.
int totalAircraftLostAA = 0;
int totalAircraftLostSA = 0;
ScoreCalculations.CalculateEnemyAirLosses(events, ref totalAircraftLostAA, ref totalAircraftLostSA);
int column = 3;
for (int row = 1; row <= rowCount; row++)
{
if (xlRange.Cells[row, 1].Value2.ToString() == "Aircraft Kills (A-A)")
{
Console.WriteLine(xlWorkbook.Sheets[1]);
Console.WriteLine("write attempted");
xlRange.Cells[row, column].Value2 = totalAircraftLostAA;
}
}
//Calculate and record ally airlosses
ScoreCalculations.CalculateAllyAirLosses(events);
//Calculate and record expended Ordnance
ScoreCalculations.CalculateExpendedOrdnance(events);
//Calculate and record ground targets hit
ScoreCalculations.CalculateNamedGroundTargets(events);
xlWorkbook.Save();
xlWorkbook.Close();
Thanks to anyone that can see what is probably the dumb mistake that I am making.
Jimmy
|
|
|
|
|
if (xlRange.Cells[row, 1].Value2.ToString() == "Aircraft Kills (A-A)")
You cannot compare strings like this, you need to use the String.Equals()[^] method.
Unrequited desire is character building. OriginalGriff
I'm sitting here giving you a standing ovation - Len Goodman
|
|
|
|
|
It seemed to work to get into the if statement at the right place based on what I was doing, but I switched the string.Equals just to be sure and it is still entering the if statement at the correct place, but that did not fix the issue. Thanks for letting me know that it was not proper to check the strings the way I was though. Any other ideas to fix why nothing is actually being written that I can see when I open the excel file?
|
|
|
|
|