0

The problem

From the serial port I receive a stream of characters. The stream I receive will be terminated by \n. I receive the serial port data in parts like this:

Serial port Event 1: "123\n456\n789"
Serial port Event 2: "\n0123\n456\n789\n"
Serial port Event 3: "0123\n456\n789\n"

As you can see my stream fluctuates, and this is very normal since I read the serial port with "what is available currently".

My problem is that I want to log this to the UI with a RichTextBox. I cannot use ListBox because I need color and font formatting.

First approach

Before I tried this below and that works very well till the time the messages exceed around 30.000 lines of text. The UI gets unresponsive. This was the code:

uiTextActivity.SelectionFont = new Font(uiTextActivity.SelectionFont, FontStyle.Bold);
uiTextActivity.SelectionColor = LogMsgTypeColor[(int)msgtype];
uiTextActivity.AppendText(msg);
uiTextActivity.ScrollToCaret();

Later on I used this as a quick fix that clears the box after 2000 lines:

if ((int.Parse(uiLabelCounterActivity.Text) + 1) > 2000)
   uiTextBoxResetActivity();

I want to keep a history of around 500lines. But with this quick fix above I lose my log completely when the counter hits 2000.

I think what I need is a circular FIFO TextBox. So I can remove after 500 lines the oldest logs and append the new one.

Second approach

I also tried this (note that in this case below the newest logs entries are on top and the oldest below in the textbox)

msg = msgToPasteWhenComplete + msg;
int c = 0; // c=zero if no newline, 1 for single newline, 2 for 2times newline and so on
int latestTermination = 0;// store the point where \n is found
for (int e = 0; e < msg.Length; e++)
{
    if (msg[e] == '\n')
    {
        c++;
        latestTermination = e+1; 
    }
}

// contains strings all terminated with \n
string msgToPaste = msg.Substring(0, latestTermination);
// contains string that is not complete (not yet terminated with \n)
msgToPasteWhenComplete = msg.Substring(latestTermination, msg.Length - latestTermination);

string previous = msgToPaste + uiTextActivity.Text;

if (previous.Length > maxDisplayTextLength) 
{
    string debugInfo3 = previous.Substring(0, maxDisplayTextLength);
    uiTextActivity.Text = debugInfo3;
}
else
    uiTextActivity.Text = previous;

This works almost very well. The problem with this approach is that the line that comes from the serial port will not be pasted to the UI until the \n is received. This means that whenever communication is slow, I will have to wait for the serial stream to complete the whole line including \n before I can see it... What I want is to see every character directly.

info about serialport

The serial data am I getting from the SerialDataReceivedEvent, in that event I use comport.ReadExisting() to have a non-blocking event. The serial data comes from my embedded programming board that has analog readings. The analog readings supply me with 20 lines per second, each line containing 20 chars. I need the raw data to be read in a user interface where it has to be filtered to other textboxes depending on the prefix of the serial message (like err goes to error , warn goes to warning textbox, but they all go to the activity log. The activity log is what this question is about.

4

2 に答える 2

0

私はうまくいく解決策を見つけました、ここに私のコードがあります:

private const int maxDisplayTextLength = 5000;
private string splitString = "";
private int activityCount = 0;
private string errorMessageBox = "";
private bool errorMessageBoxNeedUpdate = true;
private int errorMessageBoxCount = 0;
private string filteredMessageBox = "";
private int filteredMessageCount = 0;
private bool filteredMessageBoxNeedUpdate = true;
private string activityLogFilterKeyword = "Warning";

string logHelperStringMaxSizeLimiter(string input)
{
    // check if our buffer isn't getting bigger than our specified max. length
    if (input.Length > maxDisplayTextLength)
    {
        // discard the oldest data in our buffer (FIFO) so we have room for our newest values
        input = input.Substring(input.Length - maxDisplayTextLength);
    }
    return input;
}

private void logHelperIncoming(string msg)
{
    msg = msg.Replace('\0', '\n'); // remove \0 NULL characters as they have special meanings in C# RichTextBox

    // add the new received string to our buffer
    splitString += msg;

    // filter out special strings 
    int searchIndexStart = splitString.Length - msg.Length;
    for (int e = searchIndexStart; e < splitString.Length; e++)
    {
        if (splitString[e] == '\n')
        {
            activityCount++;

            string subString = splitString.Substring(searchIndexStart, e - searchIndexStart) + "\n";
            searchIndexStart += e - searchIndexStart + 1; // update searchindex for next search
            string filterError = "error";
            // filter messages that start with error
            if (subString.Length > filterError.Length) // for this filter, the length should be at least length of error
            {
                if (String.Compare(subString, 0, filterError, 0, 4, true) == 0)
                {
                    errorMessageBox += subString;
                    errorMessageBoxNeedUpdate = true;
                    errorMessageBoxCount++;
                }
            }
            // filter messages that start with XXX
            if (subString.Length > activityLogFilterKeyword.Length && activityLogFilterKeyword.Length != 0) // for this filter, the length should be at least length of error
            {
                if (String.Compare(subString, 0, activityLogFilterKeyword, 0, activityLogFilterKeyword.Length, true) == 0)
                {
                    filteredMessageBox += subString;
                    filteredMessageBoxNeedUpdate = true;
                    filteredMessageCount++;
                }
            }
        }
    }
}

// outputs to main activity textbox
private void Log(LogMsgType msgtype, string msg)
{
    if (msgtype == LogMsgType.Incoming || msgtype == LogMsgType.Normal)
    {
        logHelperIncoming(msg);
    }
    else if (msgtype == LogMsgType.Outgoing)
    {

    }

    splitString = logHelperStringMaxSizeLimiter(splitString);
    filteredMessageBox = logHelperStringMaxSizeLimiter(filteredMessageBox);
    errorMessageBox = logHelperStringMaxSizeLimiter(errorMessageBox);

    uiTextActivity.Invoke(new EventHandler(delegate
    {
        // time to post our updated buffer to the UI
        uiTextActivity.Text = splitString;
        uiTextActivity.Update();
        // scroll to the newest data only if user has no focus on the 
        uiTextActivity.SelectionStart = uiTextActivity.TextLength; // make sure view is to the latest
        uiTextActivity.ScrollToCaret();
        uiLabelCounterActivity.Text = activityCount.ToString();

        if (errorMessageBoxNeedUpdate)
        {
            errorMessageBoxNeedUpdate = false;
            uiTextActivity.SelectionColor = Color.Red;
            // time to post our updated buffer to the UI
            uiTextboxErrorLog.Text = errorMessageBox;
            // scroll to the newest data only if user has no focus on the 
            uiTextboxErrorLog.SelectionStart = uiTextboxErrorLog.TextLength; // make sure view is to the latest
            uiTextboxErrorLog.ScrollToCaret();
            uiLabelCounterError.Text = errorMessageBoxCount.ToString();

        }
        if (filteredMessageBoxNeedUpdate)
        {
            filteredMessageBoxNeedUpdate = false;
            // time to post our updated buffer to the UI
            uiTextboxFilteredLog.Text = filteredMessageBox;
            // scroll to the newest data only if user has no focus on the 
            uiTextboxFilteredLog.SelectionStart = uiTextboxFilteredLog.TextLength; // make sure view is to the latest
            uiTextboxFilteredLog.ScrollToCaret();
            uiLabelCounterFiltered.Text = filteredMessageCount.ToString();
        }
        // extract value from filter and store to filter
        activityLogFilterKeyword = uiTextboxFilterKeyword.Text;

    }));
}
于 2012-06-24T17:25:20.103 に答える
0

うまくいく特定のコードのチャンクを提供できればいいのですが、しばらくの間、この種の入力処理を行う必要はありませんでした。

そうは言っても、入力をQueueMSDNリファレンス)オブジェクトにバッファリングしてから、タイマーでキューをポーリングするか、他のイベント(多分OnChanged?)に応答することで、パフォーマンスが向上する可能性があります。

于 2012-06-24T09:40:37.757 に答える