How to delete irrevelant sentence from Richtextbox? - c#

I use GPS data and NMEA sentences.Even I only want to see and save the sentences which begins with "$GNGGA" and "$GNTVG" in my richtextbox, there are other sentences(lines) begining with different codes($GNGLL, $GLGSV, $GPGSV etc). What should I do to only get "$GNGGA" and "$GNTVG" sentences to Richtextbox?
Here is my code;
string[] gParca;
string gKG, gDB, gUydular, gYukseklik, gEnlem, gBoylam, gYataySapma, gKilitlenme, gVelocity, gSaat;
private void GPSVelocity(string NMEA2)
{
gParca = NMEA2.Split(new char[] { ',' });
switch(gParca[0])
{
case "$GNVTG":
gVelocity = gParca[7];
break;
}
private void GPSDataBilgisi(string NMEA)
{
gParca=NMEA.Split(new char[] { ',' });
switch (gParca[0])
{
//Global Positioning System Fix data
case "$GNGGA":
gParca[2] = gParca[2].Replace('.', ',');
gParca[4] = gParca[4].Replace('.', ',');
}
}
private void serialPortGPS_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
GPSDataBilgisi(serialPortGPS.ReadLine());
GPSVelocity(serialPortGPS.ReadLine());
}
private void GPSVel(string NMEA2)
{
if(checkBoxSave.Checked)
{
richTextBoxGPSData.AppendText(NMEA2);
}
}
private void GPSData(string NMEA)
{
if(checkBoxSave.Checked)
{
richTextBoxGPSData.AppendText(NMEA);
}
}

Disclaimer
As per OP's reply to my question, I am assuming serialPortGPS.ReadLine() works exactly like TextReader.ReadLine().
Since you want to filter the input you are getting, add a filter to the function that retrieves the data.
private void serialPortGPS_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
for(int i = 0; i < 2; i++)
{
string line = serialPortGPS.ReadLine();
if(line.StartsWith("$GNGGA"))
GPSDataBilgisi(line);
if(line.StartsWith("$GNVTG"))
GPSVelocity(line);
}
}
If the line starts with anything else (e.g. "HELLOIAMALINE"), neither if-check will pass and the code will not do anything with the line, it just moves on to the next one.
You can remove the switch statements in your code, they are no longer needed (of course keep the logic that is inside them!)
There are some caveats here, because I think your code and intention is a bit vague. If you can clarify any of these, I can update my answer.
Going by your question, I assume you do not want to call ReadLine() exactly twice (once for each method). I infer that you want to read every line individually (because that's how TextReader usually works), check if it starts with a "good" value, and then execute the function that needs it.
My original example specifically reads 2 rows and parses them. If you are instead trying to read all lines from the gps update, change the code as follows:
private void serialPortGPS_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
string line;
while((line = serialPortGPS.ReadLine()) != null)
{
if(line.StartsWith("$GNGGA"))
GPSDataBilgisi(line);
if(line.StartsWith("$GNVTG"))
GPSVelocity(line);
}
}
Part of me wonders if reading directly from serialPortGPS is the correct approach. This is pure speculation, but I would expect your SerialDataReceivedEventArgs e to contain a property with the newly received data. It's not impossible that what you are currently doing is correct, but it is more unusual than using the event args that you receive from the update event.
You could use LINQ to immediately filter out the rows that you need (instead of having to manually iterate over them). However, Textreader implements a pattern where it only processes one line at a time (similar to yield return). If you use LINQ, you're going to end up processing all lines at the same time when you want to filter them. If you need to process line by line (e.g. because the data you're processing has a huge memory footprint), then you should avoid LINQ
If you want an example of LINQ:
List<string> theLinesThatIWant =
allTheLines.Where(line => line.StartsWith("$GNGGA")).ToList()
But like I said, only use this if you are able and willing to have all the data in memory at the same time.

Related

c# unsynchronized append text

SerialPort s = new SerialPort("COM32");
public MainWindow()
{
InitializeComponent();
s.DataReceived += dataAction;
}
private void dataAction(object sender, SerialDataReceivedEventArgs e)
{
var data = s.ReadLine();
File.AppendAllText(recordAllSerialPortPath, data);
if (data == "ABCD")
s.WriteLine("aaaa");
if(data=="aaa")
File.AppendAllText(loggerPath, data);
}
I get data from serial port, and I want to write all the data to recordAllSerialPortPath.
additionally I want to answer each line on diffrent way (write to SerailPort or write to logger).
It's most important to answer fast , and the problem now with my code it the append text to recordAllSerialPortPath takes time and i do it synchronized , so my code wait until the line write to recordAllSerialPortPath and then continue to answer.
If I move the "record" to the end of function that not help because the next line will wait until I finish to record this line.
How can I record each line but not wait to this in my code.
I know I can do Task.run(()=>File.AppendAllText(recordAllSerialPortPath, data)); But i not sure if it's the right sulution.
I will be happy get some way to fix my problem (advantages and disadvantages)

Comparing to previous string gets me stuck in a loop

Ok, so I have a program that checks a twitch url for whenever someone new follows the channel by comparing a certain string is different from a "temp" string that I use for reference. But instead of only outputting a message every time the string is different it gets stuck in a loop of outputting the latest follower and then second latest follower then latest follower again etc.
What am I missing? Also, is there a better way of checking if a certain string is updated?
private void DonationListen()
{
try
{
followers = this.donationClient.DownloadString("https://api.twitch.tv/kraken/channels/" + channel.Trim() + "/follows");
donationTimer.Interval = 10000;
donationTimer.Elapsed += new ElapsedEventHandler(CheckUpdates);
donationTimer.Start();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private void CheckUpdates(object source, ElapsedEventArgs e)
{
donationTimer.Stop();
int startIndex = followers.IndexOf("display_name\":") + 15;
int endIndex = followers.IndexOf(",\"logo", startIndex);
prevFollower = followers.Substring(startIndex, (endIndex - 1) - startIndex);
if (firstRun == true)
{
temp = prevFollower;
}
else if (prevFollower != temp)
{
//New follower detected
temp = prevFollower;
if (updateFollower != null)
{
updateFollower(prevFollower);
}
}
else
{
//Follower is the same as before
}
firstRun = false;
DonationListen();
}
I'm thinking it might have something to do with the downloadstring trying to get a new string from the url but failing since it's currently being updated and therefore the CheckUpdates doesn't have correct information or something?
Without a good code example, it is difficult to know for sure what the problem is. So we are left inspecting the code you did show us.
Based on that, it appears to me as though your "loop" is being caused by repeatedly subscribing to the same event.
In your DonationListen() method, you have this statement:
donationTimer.Elapsed += new ElapsedEventHandler(CheckUpdates);
In the CheckUpdates() method (i.e. the handler you are subscribing), you have this statement (as the very last statement):
DonationListen();
In other words, every time the timer's Elapsed event is raised, you add another event handler instance to the event. For every handler you add, the CheckUpdates() method will be called.
Again, without a good code example, it is difficult to know for sure what the best fix would be. But given the code that is here, it appears to me that you could just remove that last statement from the CheckUpdates() method, as the DonationListen() method does not appear to do anything that needs doing again.

General Best practice for Event handling (methods)

So far I was putting all code that was reaction to event directly into event handling method.
Yesterday I saw somebody somewhere mentioning that only minimum of code should go there.
Is that true ? Or whats the best practice ?
e.g. which one of the examples is better from program-smooth-working point of view, and why, if you may:
Fig1:
private void MainForm_DragDrop(object sender, DragEventArgs e)
{
var DropPosX = e.X;
string[] s = (string[])e.Data.GetData(DataFormats.FileDrop, false);
for (int i = 0; i < s.Length; i++)
{
CheckFile(s[i])
LoadFile(s[i]);
// ..big chunk of code..
}
// ..big chunk of code..
}
Fig2:
DoDragDrop(int[] s, int DropPosX)
{
for (int i = 0; i < s.Length; i++)
{
CheckFile(s[i])
LoadFile(s[i]);
// ..big chunk of code..
}
// ..big chunk of code..
}
private void MainForm_DragDrop(object sender, DragEventArgs e)
{
var DropPosX = e.X;
string[] s = (string[])e.Data.GetData(DataFormats.FileDrop, false);
DoDragDrop(s, DropPos);
}
..or even
Fig3:
int DropPosX;
string[] s;
DoDragDrop()
{
for (int i = 0; i < s.Length; i++)
{
CheckFile(s[i])
LoadFile(s[i]);
// ...
}
// ...
}
private void MainForm_DragDrop(object sender, DragEventArgs e)
{
DropPosX = e.X;
s = (string[])e.Data.GetData(DataFormats.FileDrop, false);
DoDragDrop();
}
Most event handlers essentially take the following two actions
Gather relevant data from the event, possible just that the event happened
Perform an action using the gathered data
It sounds like that person was suggesting that you break up these logical operations into 2 separate methods. This is sound reasoning but it is also an issue of style. It doesn't make your program more or less correct to do this. Although I generally find that code is more testable if you take this approach.
Specific to this sample though. I would not use instance variables in this case. The items being dragged and the position are relevant to the DoDragDrop method and should be passed as arguments. If that data needs to be persisted then DoDragDrop should be the one to set the instance values.
private void MainForm_DragDrop(object sender, DragEventArgs e)
{
int position = e.X;
string[] items = (string[])e.Data.GetData(DataFormats.FileDrop, false);
DoDragDrop(positon, items);
}
Yes you should try to keep a minimum in there.
It can get a bit messy if big chunk of code is say twiddling about with a load of internals (e.g. form controls, but you aim for as much as you can outside of the eventhandler.
All depends on what the big chunk of code is, but even a local private method is better than a huge lump of code in and eventHandler.
If say you were grabbing UI properties to store them in another class. Add a method to it, that takes them as arguments.
If it's a lot of UI stuff, look at a UserControl.
The main reason to do it, is testing UI is a major pain, so the less logic in the UI, the easier the job.

C# How to place serial input data into a queue and de-que using a backgroundworker

I need to take incoming data from a serial port place the 'ReadExisting' string into a queue, and then de-que back into the user interface.
During the de-que process I will be formatting the incoming string to remove unwanted characters, add LineFeeds (there are no EOL characters in the incoming), etc. and post various parts of the string into several controls (listbox, textbox) for viewing.
This is as far as I have been able to get to create a string (RxString) from incoming data:
private void serialPort1_DataReceived(object sender,
System.IO.Ports.SerialDataReceivedEventArgs e)
{
RxString = serialPort1.ReadExisting();
this.Invoke(new EventHandler(DisplayText));
}
I am able to get the incoming data, but when I try to format it and display it some of the data that comes in gets lost or dropped or something and the text gets unreadable. If I don't format the text & send it straight to a listbox the data is all there, but not useable because of all the extra code characters.
I'd like to handle the (DisplayText) using a backgroundworker so the serial data can come in to a queue so as to not be lost while the backgroundworker handles placing the info on screen. Unless theres a better way.
I assume that the data comes one line at the time, so you could always use the Queue-class and use the method Enqueue to add the incoming data.
And to get an item from the Background-worker, just use Dequeue
Here's a link to the MSDN-article about the Queue-class
http://msdn.microsoft.com/en-us/library/7977ey2c.aspx
Example:
private Queue<string> data = new Queue<string>();
private void Rx_GetData(e)
{
var rxString = e.ReadExisting();
data.Enqueue(rxString);
}
private void BackgroundWorker_DoWork()
{
while(rxConn.IsConnected) // Haven't worked with serial connections, so I don't know the proper property here..
{
if(data.Count > 0)
{
var str = data.Dequeue();
FormatString(str);
}
Thread.Sleep(10);
}
}

C# and Winforms TextBox control: How do I get the text change?

I have an event handler for the TextBox.TextChanged event on a form of mine. In order to support undo, I'd like to figure out exactly what has changed in the TextBox, so that I can undo the change if the user asks for it. (I know the builtin textbox supports undo, but I'd like to have a single undo stack for the whole application)
Is there a reasonable way to do that? If not, is there a better way of supporting such an undo feature?
EDIT: Something like the following seems to work -- are there any better ideas? (It's times like this that I really wish .NET had something like the STL's std::mismatch algorithm...
class TextModification
{
private string _OldValue;
public string OldValue
{
get
{
return _OldValue;
}
}
private string _NewValue;
public string NewValue
{
get
{
return _NewValue;
}
}
private int _Position;
public int Position
{
get
{
return _Position;
}
}
public TextModification(string oldValue, string newValue, int position)
{
_OldValue = oldValue;
_NewValue = newValue;
_Position = position;
}
public void RevertTextbox(System.Windows.Forms.TextBox tb)
{
tb.Text = tb.Text.Substring(0, Position) + OldValue + tb.Text.Substring(Position + NewValue.Length);
}
}
private Stack<TextModification> changes = new Stack<TextModification>();
private string OldTBText = "";
private bool undoing = false;
private void Undoit()
{
if (changes.Count == 0)
return;
undoing = true;
changes.Pop().RevertTextbox(tbFilter);
OldTBText = tbFilter.Text;
undoing = false;
}
private void UpdateUndoStatus(TextBox caller)
{
int changeStartLocation = 0;
int changeEndTBLocation = caller.Text.Length;
int changeEndOldLocation = OldTBText.Length;
while (changeStartLocation < Math.Min(changeEndOldLocation, changeEndTBLocation) &&
caller.Text[changeStartLocation] == OldTBText[changeStartLocation])
changeStartLocation++;
while (changeEndTBLocation > 1 && changeEndOldLocation > 1 &&
caller.Text[changeEndTBLocation-1] == OldTBText[changeEndOldLocation-1])
{
changeEndTBLocation--;
changeEndOldLocation--;
}
changes.Push(new TextModification(
OldTBText.Substring(changeStartLocation, changeEndOldLocation - changeStartLocation),
caller.Text.Substring(changeStartLocation, changeEndTBLocation - changeStartLocation),
changeStartLocation));
OldTBText = caller.Text;
}
private void tbFilter_TextChanged(object sender, EventArgs e)
{
if (!undoing)
UpdateUndoStatus((TextBox)sender);
}
You might be better off using the Enter and Leave events instead. When entering, store the current text in a class variable, then when leaving compare the new text to the old.
Yes, don't tie it directly to the textbox. Your forms' state should be in some model object somewhere that isn't directly tied to the form (MVC is one way to do this, MVVM is another). By decoupling them like that, you can compare the new textbox value to the current model value whenever a change request comes in.
Actually, all I can think of is having some kind of collection where you store different string versions (so you can undo many times, not just once).
I would store the reference to TextBox's collections in TextBox.Tag, so it is straightforward to store/use it.
Last but not least, you update your collection of strings during the event TextChange. With no much work, you can maintain a full history, gettinjg the previous value from your own structure.
This is probably overkill for what you're trying to accomplish, but CSLA support n-level undo. CSLA is a great business objects framework written by Rocky Lhotka. The business objects handle the undo history and it flows to the UI through data binding.
Switching your app to use CSLA would be a big commitment, but another option would be to look through the freely available source code to see how he implemented it.
I am actually making an own Syntax-Highlight-System so I also need to know the changed text.
My solution is to watch for an enter or space or an depositioning of the cursor.
As WinForms provide the Keydown event I used the KeyEventArguments (e) and converted them to a char.
After that I storage the char into a string like :
string i="";
i+=convertedToChar; // convertedToChar = kc.ConvertToString(e.KeyData)
And as soon as there is a enter or space or depositioning - "event" I delete the string.
Result:
If a user enters a few chars and hit space I am able to read the last chars (till the last space).
An advantage would be the fact that you can use any delimiter char for that (as soon as they are storaged and provided by e.KeyCode)
However I hope that this is a solution for everybody watching this after 9years :D.
It´s never too late.

Categories