• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 505
  • Last Modified:

Clear Buffer of Data Being Read from a Human Interface Device

My application takes data from an electronic scale that supports the human interface device protocol.  I have found that the response from this device is delayed by a few seconds and think it is because I somehow need to clear the buffer before querying it for the next measurement.  Attached is my code.  I am thinking I need to add some mechanism to clear the buffer before I write to it to request its reading.  Is there a way to clear a buffer such as this?
// Get a handles for reading and writing reports
hWrite=CreateFile(detailData->DevicePath, GENERIC_WRITE, 
                FILE_SHARE_READ|FILE_SHARE_WRITE, 
                (LPSECURITY_ATTRIBUTES)NULL, OPEN_EXISTING, 0,NULL);
 
hRead=CreateFile(detailData->DevicePath, GENERIC_READ,
		 FILE_SHARE_READ|FILE_SHARE_WRITE, 
		 (LPSECURITY_ATTRIBUTES) NULL, OPEN_EXISTING,
		 FILE_FLAG_OVERLAPPED, NULL);
if(hRead==INVALID_HANDLE_VALUE){
	strError = "Error connecting to scale";
	throw strError;
}
 
//Set the members of the overlapped structure.ovlpRead.hEvent = CreateEvent(NULL, TRUE, TRUE, "");
ovlpRead.Offset = 0;
ovlpRead.OffsetHigh = 0;
 
for(i=0; i<hidCapabilities.OutputReportByteLength; i++) report[0]=0;
 
if(!WriteFile(hWrite, report, hidCapabilities.OutputReportByteLength,
	          &dwBytes, NULL)){
	strError = "Unable to access scale.";
	throw strError;
}
 
ReadFile(hRead, report, hidCapabilities.InputReportByteLength, &dwBytes,
		(LPOVERLAPPED) &ovlpRead); 
		
dwResult = WaitForSingleObject(ovlpRead.hEvent, 3000);
switch(dwResult){
	case WAIT_OBJECT_0:
		for(i=1; i<hidCapabilities.OutputReportByteLength; i++){
                    character.Format("%02X", report[i]);
			strWeight+=character.Right(2);
		}
		if(sscanf(strWeight, "%x", &weight)<1){
			strError = "Unable to interpret scale response";
			throw strError;
		}
		break;
 
	case WAIT_TIMEOUT:
		strError = "Timed out while waiting for scale to respond.";
		throw strError;
			
	default:
		strError = "Undefined error while waiting for scale to respond.";
		throw strError;
}

Open in new window

0
david_johns
Asked:
david_johns
  • 5
  • 4
  • 4
  • +1
1 Solution
 
evilrixSenior Software Engineer (Avast)Commented:
Have you tried this?

BOOL WINAPI FlushFileBuffers(
  __in  HANDLE hFile
);

http://msdn2.microsoft.com/en-us/library/aa364439(VS.85).aspx
0
 
Infinity08Commented:
Just a note :

        for(i=0; i<hidCapabilities.OutputReportByteLength; i++) report[0]=0;

That sets report[0] to 0 several times. That's a bit redundant. Did you mean :

        for(i=0; i<hidCapabilities.OutputReportByteLength; i++) report[i]=0;


How is report defined ? I assume that's the buffer you're talking about ?
0
 
evilrixSenior Software Engineer (Avast)Commented:
Re: #20785896
I've just realized, that won't help is it only flushes the write buffer and not the read.
0
The new generation of project management tools

With monday.com’s project management tool, you can see what everyone on your team is working in a single glance. Its intuitive dashboards are customizable, so you can create systems that work for you.

 
evilrixSenior Software Engineer (Avast)Commented:
^^^Unless, of course, you are refering specifically to clearing the write buffer then it might help :)
0
 
evilrixSenior Software Engineer (Avast)Commented:
Incidently, what type is StrError that you throw on error? Is it a CString?

It is normally better in C++ to throw an exception based upon a well established exception object for the framework you are developing with. Normally, in C++, this would be considered std::exception. The reason for this is because the langauge will throw std::exception based objects anyway so it makes catching and handling exceptions simpler. If you are not using the C++ runtime libraries then this is unlikely to be a problem. If; however, you are using MFC, you might want to consider basing you exception object on CException for the same reason, in so far as all other MFC exceptions are based upon this so you only need to worry about catching one exception. If you need a specific exception class you can always sub-class the frameworks base exception object for your own needs. Exceptions should always be caught by reference (preferably const) to the base exception type -- to ensure you get correct polymorphic behavior (and to avoind unnecessary copying).

http://www.cplusplus.com/doc/tutorial/exceptions.html
http://msdn2.microsoft.com/en-us/library/yx1b5f5w(VS.80).aspx

-Rx.
0
 
itsmeandnobodyelseCommented:
>>>> for(i=0; i<hidCapabilities.OutputReportByteLength; i++) report[0]=0;
>>>> if(!WriteFile(hWrite, report, hidCapabilities.OutputReportByteLength,
                &dwBytes, NULL)){

Assuming Infinity was right suggesting  "report[0]=0;" must turn to "report[i]=0;", why should you want to write a bunch of 0 bytes to the output device? Or is there something between these both statements thyt you didn't post?

>>>>         ReadFile(hRead, report, hidCapabilities.InputReportByteLength, &dwBytes,
>>>>            (LPOVERLAPPED) &ovlpRead);
>>>>          for(i=1; i<hidCapabilities.OutputReportByteLength; i++){
>>>>                    character.Format("%02X", report[i]);

After reading and waiting for the overlapped event you try to print out bytes from 1 to hidCapabilities.OutputReportByteLength. Why not printing from index 0? Why not printing to the input length which was stored in dwBytes?

 Regards, Alex
0
 
david_johnsAuthor Commented:
Guys,

Great comments.  I have fixed the errors you have found with not zeroing out the entire input report and not reading the entire output report.  As it turned out neither one affected the performance of the function.  The non-initialized input report must not have mattered and for the range I am working in the first character of the output report must always be zero and therefore it didn't make any difference.

I'll look into the exceptions I am throwing.  In general I check for catch(CString error) first, then a catch all catch(...) so I can identify where any "Unknown" errors occur.

Any ideas on clearing the buffer?

Thanks,
David
0
 
evilrixSenior Software Engineer (Avast)Commented:
>> In general I check for catch(CString error) first, then a catch all catch(...) so I can identify where any "Unknown" errors occur.
You only actually need (if you use an exception class based on CException) handlers for CException and, if you include any STL or C++ runtime, std::exception. You shouldn't really have any exceptions that would require catch(...) and since there are limited things you can do with an unknown exception I'd just implement one of these at the highest level required just to gracefully terminate (if you haven't handled it with the other two exception types you've got issues!).

One other thing, always catch by reference (const if possible) and not value, for a number of reasons: -
a) It avoids taking a copy, which is obviously expensive (copying can have other [unwanted] side effects too)
b) It avoids slicing (http://www.cs.ualberta.ca/~hoover/Courses/201/201-New-Notes/lectures/section/slice.htm)
c) It ensures you can handle all exceptions by base class and, if appropriate, get polymorphic behavior

>> Any ideas on clearing the buffer?
Um, sorry - no :(
0
 
itsmeandnobodyelseCommented:
>>>> and not reading the entire output report.
Actually, I asked for somewhat different. I asked why you want to write zero bytes to the output device? How can you expect to get a valid response for that?

I further asked why you were printing the report buffer from 1 to hidCapabilities.OutputReportByteLength, though it must be from 0 to dwlen? How can you make any statements on the quality of a communication (performance) if you actually don't evaluate the right variables?

>>>> I have found that the response from this device is delayed by a few seconds
You have an overlapped event with a timeout of 3 seconds (what seems long for me). That means the 'few seconds' you were talking of actually must be less than 3 seconds, or the timeout would apply.

Generally, when making an overlapped read, you have to care for more than the completion of the event only (where you miss to handle the case WAIT_ABANDONED). The MSDN says:

------------------------------------------------------
If hFile is opened with FILE_FLAG_OVERLAPPED and lpOverlapped is not NULL, the read operation starts at the offset that is specified in the OVERLAPPED structure, and ReadFile may return before the read operation is complete. In this scenario, ReadFile returns FALSE and the GetLastError function returns ERROR_IO_PENDING, which allows the calling process to continue while the read operation completes. The event specified in the OVERLAPPED structure is set to the signaled state when the read operation is complete, and then the caller must adjust the position of the file pointer.
-----------------------------------------------------

You neither check the return of ReadFile nor check whether the io operation was complete.  Furthermore, the number of bytes received you must retrieve by calling GetOverlappedResult cause the actual read operation wasn't made by the ReadFile but by an asynchronously invoked thread. If the operation really was completed you (manually) must set the 'file pointer' to its new position (SetFilePointer).

Below sample code from MSDN.

Regards, Alex


OVERLAPPED gOverlapped;
 
// set up overlapped structure fields
gOverLapped.Offset     = 0; 
gOverLapped.OffsetHigh = 0; 
gOverLapped.hEvent     = hEvent; 
 
// verify that sizeof(inBuffer <= nBytestoRead)
 
// attempt an asynchronous read operation
bResult = ReadFile(hFile, &inBuffer, nBytesToRead, &nBytesRead, 
    &gOverlapped) ; 
 
// if there was a problem, or the async. operation is still pending. 
if (!bResult) 
{ 
    // deal with the error code 
    switch (dwError = GetLastError()) 
    { 
        case ERROR_HANDLE_EOF: 
        { 
            // we have reached the end of the file 
            // during the call to ReadFile 
 
            // code to handle that 
        } 
 
        case ERROR_IO_PENDING: 
        { 
            // asynchronous i/o is still in progress 
 
            // do something else for a while 
            GoDoSomethingElse() ; 
 
            // check on the results of the asynchronous read 
            bResult = GetOverlappedResult(hFile, &gOverlapped, 
                &nBytesRead, FALSE) ; 
 
            // if there was a problem ... 
            if (!bResult) 
            { 
                // deal with the error code 
                switch (dwError = GetLastError()) 
                { 
                    case ERROR_HANDLE_EOF: 
                    { 
                        // we have reached the end of
                        // the file during asynchronous
                        // operation
                    } 
 
                    // deal with other error cases 
                }   //end switch (dwError = GetLastError()) 
             } 
        } // end case 
 
        // deal with other error cases, such as the default 
 
    } // end switch (dwError = GetLastError()) 
 } // end if

Open in new window

0
 
david_johnsAuthor Commented:
Alex,

Guess I didn't get across my intent.  I had already fixed both the report I was writing to the device and how I was reading the responce.  The device didn't seem to care as long as the input request had a null for the first character of the array.  In the output it didn't seem to make a difference in the interpreted value.  As I said, it was likely because the first character was always the same (perhaps always 0) and when I get the weight I subtract the tare value from the current value so it resolves to the same weight in the end.

When I say a few seconds I mean like 10-30 seconds, not 3.  That is why I felt there must have been a buffer that I needed to clear before I received the present measurement.

I have adjusted the code as the MSDN code suggests and it seems to be working better.  Give it a check over and see if you agree with my implementation now.
for(i=0; i<hidCapabilities.OutputReportByteLength; i++) report[i]=0;
 
if(!WriteFile(hWrite, report, hidCapabilities.OutputReportByteLength,
			    &dwBytes, NULL)){
	strError = "Unable to access scale.";
	throw strError;
}
 
bRead = ReadFile(hRead, report, hidCapabilities.InputReportByteLength, &dwBytes,
				    (LPOVERLAPPED) &ovlpRead); 
 
if(!bRead){
	switch(GetLastError()){
		case ERROR_IO_PENDING: 
			dwResult = WaitForSingleObject(ovlpRead.hEvent, 3000);
			switch(dwResult){
				case WAIT_OBJECT_0:
					bRead = TRUE;
					break;
 
				case WAIT_TIMEOUT:
					strError = "Timed out while waiting for scale to respond.";
					throw strError;
				
				default:
					strError = "Undefined error while waiting for scale to respond.";
					throw strError;
			}
			break;
 
		default:
			strError = "Undefined error while reading from scale.";
			throw strError;
	}
}
if(bRead){
	for(i=0; i<hidCapabilities.OutputReportByteLength; i++){
		character.Format("%02X", report[i]);
		strWeight+=character.Right(2);
	}
	if(sscanf(strWeight, "%x", &weight)<1){
		strError = "Unable to interpret scale response";
		throw strError;
	}
}

Open in new window

0
 
itsmeandnobodyelseCommented:
>>>> Give it a check over and see if you agree with my implementation now.

Yes it is better now though I still have some problems with some code parts and the assumptions the code seems to make:

1. It seems that you always open the device both for write and read for each request.
For what purpose do you open for writing? Sending zero bytes to the device hardly makes any advantage and can seriously disturb a proper connection if the device can't handle that. And why didn't you open the device only once and store the handles as class members? I don't see any closing. If you don't properly finish a previous connection, then you easily can get a bad synchronization.

2. What is the value of hidCapabilities.OutputReportByteLength?
'OutputReportByteLength' doesn't seem a proper name for a buffer length needed for I/O with a scale device? Didn't you mixup some length variables?

3. Why are you sending zero bytes to the device? Just to test whether writing is ok?
I would recommend against it. the CreateFile and ReadFile will return with a specific error if the device wasn't  ready.

4. I am missing the call of GetOverlappedResult()
As told above, the dwlen hardly is filled properly as the read was made asynchronously. You need to get the result length by a call to GetOverlappedResult(). If successful you should print out the buffer from 0 to the dwlen-1. Post the hex string you got and compare it with the specs you hopefully have. If the first byte is a 0 byte (???) it either should be documented like that or there is some error.

5. After successfully reading you need to manually set the file pointer for the next read by calling the SetFilePointer. The next time you were calling the function you should *not* call the CreateFile again but use the handles stored with the first call.

Regards, Alex
0
 
david_johnsAuthor Commented:
Alex,

1. I just consolidated the code by putting the CreateFile call at the top.  It is actually in a completely different function that is only called once.  The remainder of the code is in a function that is called on an interval grab new measurements from the device.

2. When you read on MSDN about communicating with HID's I think you will find that my use of the OutputReportByteLength is correct.  When I connect to the device I query it for its HIDP_CAPS structure, which tells you how long its input and output "reports" are.

3. I actually don't have the specification for the device.  I contacted the manufacturer and they were unwilling to give me any information about it.  I just played around with it for a while and was able to figure out how to work with it, at least mostly.  

4. I can see your point on using the GetOverlappedResult call, but the disadvantage of that is you cannot specify a timeout interval.  At some point I think I want my function to cut and run.  With GetOverlappedResult you can either specify to return immediately whether or not the data has been received or wait indefinitely until it is received.  MSDN suggests that inside GetOverlappedResult it is just issuing a call to WaitForSingleObject or some other synronization function on the event specified in the OVERLAPPED structure anyway.  Do you really think there is a big advantage to calling that function instead of what I am doing?

5. I don't think that SetFilePointer will work since as MSDN states "You cannot use the SetFilePointer function with a handle to a non-seeking device such as a pipe or a communications device".  I would assume that a device like this would be considered a non-seeking device.
0
 
itsmeandnobodyelseCommented:
Your last answer cleared most points.

A few are still open, yet.

You can call the GetOverlappedResult without wait when you successfully waited for the overlapped event with timeout as you do now. It actually doesn't return the result but only the length of the last read. If you are right it should be equal to OutputReportByteLength. If there is a difference something should be cleared. When calling GetOverlappedResult you should be able to see whether the device has sent all bytes or whether there is still some parts of the message pending. If the device wasn't fast enough to send you all the bytes of the report at once some of your issues could be explained with.

I still were missing some kind of hand-shake or even some kind of a request you were sending to the device. I wonder how the device 'knows' what to send.

I still don't know why you were sending zero bytes to the device.

I still don't know why you weren't evaluating the first byte sent. Note, if the device sends a length first (what is quite usual), it is the first 4 bytes or the first 2 bytes but rarely a 1 byte offset. If it sends a flag indicating what kind of buffer follows, it could be a 1 byte flag but actually that would be a very strange interface and different to that I know of. You shouldn't ignore the flag but read it and verify that it was always the same. It also could be '0' or 0 for the first part, '1' or 1 for the second part, and so on. If so, the flag may give you an indication whether to read more or whether the message fully was sent.

>>>> You cannot use the SetFilePointer function with a handle to a non-seeking device
ok.


>>>> I contacted the manufacturer and they were unwilling to give me any information about it.
That is strange either. Did they sell software themselves for it? You might search for the device in the net. I would bet there are specs for it.

Regards, Alex



 

 
0
 
david_johnsAuthor Commented:
Alex,
Good idea on waiting for the event then calling the function with the wait argument set to FALSE.  I can do that.

Like I said I don't have the specification for what I am supposed to send to the device.  I just know from playing with it that if I send it a report with report[0]=0 it works.  When you pointed out the mistake in my for loop I did change it so it is sending report[i]=0, but this did not make any difference.

After you pointed out that I was not reading the first byte I changed it as shown on line 37 of the revised code above.

Thanks for your help.

David
0

Featured Post

The new generation of project management tools

With monday.com’s project management tool, you can see what everyone on your team is working in a single glance. Its intuitive dashboards are customizable, so you can create systems that work for you.

  • 5
  • 4
  • 4
  • +1
Tackle projects and never again get stuck behind a technical roadblock.
Join Now