Does Serial.find Clears Buffer If It Can't Find Anything - c++

I'm trying to look for keywords in serial buffer in Arduino.
if (Serial.find("SOMETHING"))
{
// do something
}
else if (Serial.find("SOMETHING ELSE"))
{
// do another thing
}
But only the first if works. Even if I send "SOMETHINGELSE" it doesn't check at all. Does find function clear buffer completely even if it can't find anything ? If yes, what can i do in this situation?

Serial.find(); reads Serial buffer and removes every single byte from it, up to the point where it can find specified by you String or Character.
If you use it in an conditional statement like in your example, it will always find "SOMETHING" even if "SOMETHING ELSE" exist because everything up to the point of "SOMETHING" is removed from buffer ( if "SOMETHING" actually arrived before "SOMETHING ELSE" ).
If we assume that your data arrives in order SOMETHING and then SOMETHING ELSE, your Serial buffer will look like this: SOMETHING ELSESOMETHING
in which case:
It will find "SOMETHING" and stop in there as first condition to meet is to search for this word exactly.
I assumed that you don't actually mean to send "SOMETHING" so lets say that first String to look for is StringA and then StringB. Your buffer then will look like this: StringBStringA however based on your conditional statement it will still only find StringA. This will happen because StringA still exist in buffer and now when first condition is checked you basically ask to search for StringA and by doing this you are removing StringB using Serial.find(StringA) - it simply skips StringB because its not aware that you are going to ask about it in your else if later on.
Solution to your problem depends on data that you expect to receive. You can tag beginning of data that you are awaiting for with some specific character or sequence of characters:
For example lets assume that you await for String type of data. Before you send it to your Serial put each message in a specific format like $START$SOMETHING$
You can then use this to find first command that starts with your tag and load content of the message to String so you can compare it with expected results using conditional statement.
Note!!! Code below will stop on first message with $START$ tag so if you want to look into your Serial buffer for other messages you don't want to break while(Serial.available > 0) and use arrays to store each result.
char myCharacter;
String myIncomingData;
if(Serial.find("$START$"))
{
while (Serial.available() > 0) {
// Reads byte of Serial at the time
myCharacter = Serial.read();
// Stops at the end of data
if (myCharacter == "$") {
break;
}
// Adds each character to String with your data
myIncomingData += myCharacter;
}
if (myIncomingData == "SOMETHING") {
// Do whatever you like to with your data
} else if (myIncomingData == "SOMETHING ELSE") {
// Do whatever you like to with your data
}
I would use this solution only if you want to use Serial.find(), Im sure that you can get your results in many different ways as well, at the end you can always go through entire 64 bytes of you buffer byte by byte using your own code :D.

Related

C++ Winsock Download File Cut off HTTP Header

I'm downloading the bytes of a file from the web using winsock2. so good so far.
I have the problem that I download my bytes including the http header which I don't need and which causes troubles in my files bytecodes.
Example:
I know I can find the position where the header is ending by finding "\r\n\r\n".
But somehow I can't find or at least cut it... :(
int iResponseBytes = 0;
ofstream ofDownloadedFile;
ofDownloadedFile.open(pathonclient, ios::binary);
do {
iResponseBytes = recv(this->Socket, responseBuffer, pageBufferSize, 0);
if (iResponseBytes > 0) // if bytes received
{
ofDownloadedFile.write(responseBuffer, pageBufferSize);
}
else if (iResponseBytes == 0) //Done
{
break;
}
else //fail
{
cout << "Error while downloading" << endl;
break;
}
} while (iResponseBytes > 0);
I tried searching the array / the pointer using strncmp etc.
Hopefully someone can help me.
Best greetings
You have no guarantees, whatsoever, that the \r\n\r\n sequence will be received completely within a single recv() call.
For example, the first recv() call could end up reading everything up until the first two characters of the sequence, \r\n, then your code runs around the loop again, and the second time recv() gets called it receives the remaining \r\n for the initial two bytes received (followed by the first part of the actual content). A small possibility that this might happen, but it cannot be ignored, and must be correctly handled.
If your goal is to trim everything up until the \r\n\r\n, your current approach is not going to work very well.
Instead, what you should do is invest some time studying how file stream buffering actually works. Pontificate, for a moment, how std::istream/std::ostream read/write large chunks of data at a time, but they provide a character-oriented interface. std::istream, for example, reads a buffer's full of file data at a time, placing it into an internal buffer, which your code can then retrieve one character at a time (if it wishes to). How does that work? Think about it.
To do this correctly, you need to implement the same algorithm yourself: recv() from the socket a buffer at a time, then provide a byte-oriented interface, to return the received contents one byte at a time.
Then, the main code becomes a simple loop, reading the streamed socket contents one byte at a time, at which point discarding everything up until the code sees \r\n\r\n becomes trivial (although there are still a few non-obvious gotchas in doing this right, but that can be a new question).
Of course, once the \r\n\r\n gets processed, it is certainly possible to optimize things going forward, by flushing out whatever's still buffered internally, to the output file, and then resume reading from the socket a whole buffer-at-a-time, and copying it to the output file without burning CPU cycles dealing with the byte-oriented interface.

Read text lines using QDataStream or QTextStream or neither from tcpsocket?

I am creating a simple TCP server, and have a slot/function built which reads incoming text from a client (telnet connection) on a TCP socket. I've used the Fortune code examples to help me, but had to remove QDataStream since it didn't work as expected.
I want my readCommand function to collect incoming characters from a telnet client connection, and once it finds a newline or return to remove the typed command from the input buffer, remove /n and /r, add it to my string list (commandList), and then echo the commands (seperate function). Here's what I've got so far:
void MyServer::readCommand()
{
inBuffer += tcpSocketPtr->readAll();
// While newline is present, extract the command
int nextNewlinePos;
while ((nextNewlinePos = inBuffer.indexOf('\n')) > -1) {
commandList << inBuffer.left(nextNewlinePos);
inBuffer.remove(0,nextNewlinePos);
// Get rid of /n /r if present somehow
}
if (commandList.size() > 0)
{
echoCommand();
}
}
Before I start stripping /n and /r etc. manually, my gut is telling me there is a better way to do this. Is QTextStream the way to go? Can someone provide a simple(r) alternative to what I am trying to achieve?
You could simply use the readLine(...) variants:
qint64 QIODevice::readLine(char * data, qint64 maxSize)
QByteArray QIODevice::readLine(qint64 maxSize = 0)
Note, read line functionality would be invoked on your QTcpSocket instance, and not the QDataStream. See the documentation for the precise details here inline:
A terminating '\0' byte is always appended to data, so maxSize must be
larger than 1. Data is read until either of the following conditions
are met: The first '\n' character is read. maxSize - 1 bytes are read.
The end of the device data is detected.
This way, you would need to work such an issue around, and the code could become a lot simpler.
One further note that may be helpful:
bool QIODevice::canReadLine() const [virtual]
Basically, this method would guarantee you do not try to run a line before that is available. This can come to handy in certain scenarios.

String issue with assert on erase

I am developing a program in C++, using the string container , as in std::string to store network data from the socket (this is peachy), I receive the data in a maximum possible 1452 byte frame at a time, the protocol uses a header that contains information about the data area portion of the packets length, and header is a fixed 20 byte length. My problem is that a string is giving me an unknown debug assertion, as in , it asserts , but I get NO message about the string. Now considering I can receive more than a single packet in a frame at a any time, I place all received data into the string , reinterpret_cast to my data struct, calculate the total length of the packet, then copy the data portion of the packet into a string for regex processing, At this point i do a string.erase, as in mybuff.Erase(totalPackLen); <~ THIS is whats calling the assert, but totalpacklen is less than the strings size.
Is there some convention I am missing here? Or is it that the std::string really is an inappropriate choice here? Ty.
Fixed it on my own. Rolled my own VERY simple buffer with a few C calls :)
int ret = recv(socket,m_buff,0);
if(ret > 0)
{
BigBuff.append(m_buff,ret);
while(BigBuff.size() > 16){
Header *hdr = reinterpret_cast<Header*>(&BigBuff[0]);
if(ntohs(hdr->PackLen) <= BigBuff.size() - 20){
hdr->PackLen = ntohs(hdr->PackLen);
string lData;
lData.append(BigBuff.begin() + 20,BigBuff.begin() + 20 + hdr->PackLen);
Parse(lData); //regex parsing helper function
BigBuff.erase(hdr->PackLen + 20); //assert here when len is packlen is 235 and string len is 1458;
}
}
}
From the code snippet you provided it appears that your packet comprises a fixed-length binary header followed by a variable length ASCII string as a payload. Your first mistake is here:
BigBuff.append(m_buff,ret);
There are at least two problems here:
1. Why the append? You presumably have dispatched with any previous messages. You should be starting with a clean slate.
2. Mixing binary and string data can work, but more often than not it doesn't. It is usually better to keep the binary and ASCII data separate. Don't use std::string for non-string data.
Append adds data to the end of the string. The very next statement after the append is a test for a length of 16, which says to me that you should have started fresh. In the same vein you do that reinterpret cast from BigBuff[0]:
Header *hdr = reinterpret_cast<Header*>(&BigBuff[0]);
Because of your use of append, you are perpetually dealing with the header from the first packet received rather than the current packet. Finally, there's that erase:
BigBuff.erase(hdr->PackLen + 20);
Many problems here:
- If the packet length and the return value from recv are consistent the very first call will do nothing (the erase is at but not past the end of the string).
- There is something very wrong if the packet length and the return value from recv are not consistent. It might mean, for example, that multiple physical frames are needed to form a single logical frame, and that in turn means you need to go back to square one.
- Suppose the physical and logical frames are one and the same, you're still going about this all wrong. As noted, the first time around you are erasing exactly nothing. That append at the start of the loop is exactly what you don't want to do.
Serialization oftentimes is a low-level concept and is best treated as such.
Your comment doesn't make sense:
BigBuff.erase(hdr->PackLen + 20); //assert here when len is packlen is 235 and string len is 1458;
BigBuff.erase(hdr->PackLen + 20) will erase from hdr->PackLen + 20 onwards till the end of the string. From the description of the code - seems to me that you're erasing beyond the end of the content data. Here's the reference for std::string::erase() for you.
Needless to say that std::string is entirely inappropriate here, it should be std::vector.

Does there exist an "unput" for std::ostream like there exists "unget" for std::istream?

I have some parsing code that allows for escape sequences to be entered into a string of text:
// In a file or large, multi-line string ...
my_parameter="A setting for the parameter\nthat contains \"escape sequence\" characters"
When I parse it, I handle the backslashes and add the appropriate character to the string that I am building using a std::ostringstream instance. Line feeds, quotes, backslashes and such all work fine. However, I was contemplating whether or not to allow the \b sequence, and went looking to see if I can "unput" the last character from my ostringstream like you can "unget" from any std::istream. Can you do such a thing? If the function does not exist, is there a simple way to push the write position back one character and simply have the next character read overwrite it?
This is not mission-critical or anything like that, but I was curious if anyone else has come across this before.
Streams are an awful lot like the mail. each message sent on a stream is like a letter, and messages can be queued into buffers, which are like mailboxes.
If you were responsible for both putting messages into and taking messages out of a mail-box, then you could certainly know that a letter you just put there is still there for you to take back. Of course, you probably wouldn't go to the trouble of putting it in a mailbox at all, since you own both ends.
If instead, you are putting a letter in your girlfriends mailbox, you don't really have much control of when she will check her mailbox and take out all of the letters. it could be that she's sitting by the door and will snatch the letter right up and read it as soon as it passes through the slot.
More likely, you're actually delivering the letter to a box owned by the post-office (the operating system). Although many such receptacles are just bins, and mail carrier checks it once per day, It could be that the slot is connected directly to a sorting machine, and the letter gets delivered the instant you drop it.
In a streaming interface with concurrency, there is no general way to retake ownership of a write once written. If you need that, you should place an intermediate buffer between you and the stream, and flush it out to the stream only when you know for sure that you are ready.
You may use seekp to set the position of cursor in the stream (see: http://cplusplus.com/reference/iostream/ostream/seekp/).
If you may want to take back a character, don't send it until you're sure you won't want to take it back. You could implement your own "allow takebacks" logic thus:
int pending_ch = -1;
void output_char(int ch)
{
if (pending_ch >= 0)
putch(F, ch);
pending_ch = ch;
}
void unput_char(void)
{
pending_ch = -1;
}
void force_put_char(void)
{
output_char(-1);
}
A bit clunky, but that general approach can be useful for delaying output to a stream.
You could simply input a backspace character yourself. I've done it in the Windows Console, at least. This should ensure that the correct behaviour is observed regardless of the destination.

fscanf multiple lines [c++]

I am reading in a file with multiple lines of data like this:
:100093000202C4C0E0E57FB40005D0E0020C03B463
:1000A3000105D0E0022803B40205D0E0027C03027C
:1000B30002E3C0E0E57FB40005D0E0020C0BB4011D
I am reading in values byte by byte and storing them in an array.
fscanf_s(in_file,"%c", &sc); // start code
fscanf_s(in_file,"%2X", &iByte_Count); // byte count
fscanf_s(in_file,"%4X", &iAddr); // 2 byte address
fscanf_s(in_file,"%2X", &iRec_Type); // record type
for(int i=0; i<iByte_Count; i++)
{
fscanf_s(in_file,"%2X", &iData[i]);
iArray[(iMaskedAddr/16)][iMaskedNumMove+3+i]=iData[i];
}
fscanf_s(in_file,"%2X", &iCkS);
This is working great except when I get to the end of the first line. I need this to repeat until I get to the end of the file but when I put this in a loop it craps out.
Can I force the position to the begining of the next line?
I know I can use a stream and all that but I am dealing with this method.
Thanks for the help
My suggestion is to dump fscanf_s and use either fgets or std::getline.
That said, your issue is handling the newlines, and the next beginning of record token, the ':'.
One method is to use fscanf_s("%c") until the ':' character is read or the end of file is reached:
char start_of_record;
do
{
fscanf_s(infile, "%c", &start_of_record);
} while (!feof(infile) && (start_of_record != ':'));
// Now process the header....
The data the OP is reading is a standard format for transmitting binary data, usually for downloading into Flash Memories and EPROMs.
Your topic clear states that you are using C++ so, if I may, I suggest you use the correct STL stream manipulators.
To read line-by-line, you can use ifstream::getline. But again, you are not reading the file line by line, you are reading it field by field. So, you should try using ifstream::read, which lets you choose the amount of bytes to read from the stream.
UPDATE:
While doing an unrelated search over the net, I found out about a library called IOF which may help you with this task. Check it out.