12-08-2020 02:41 PM - edited 12-08-2020 02:55 PM
Hello,
I have a test case where I need to read text coming across a serial Bus (RS-232). This text is actually the text output by an embedded PC when it boots up. I then need to parse that text output for certain tokens. I am trying to develop a general approach to this problem. Here is my approach:
Any pointers here to help me along, or anything you think is missing? For the function ComToFile, I'm not sure what to use for the parameter "termination byte" because I don't know yet what the text looks like in its entirety. Is there a default value to feed into that function to not use that method?
NOTE: A UI is not required, this is for an automated test. Also, I'm debating whether I should write the boot text to a file and then parse the tokens from that file as I've done, Or is it best to just store it in a large buffer within the program and parse that buffer?
#include <formatio.h>
#include<stdio.h>
#include<string.h>
#include<rs232.h>
#define PORT_NUMBER 1
#define BUFFER_SIZE 10000
int main (void)
{
char buffer[BUFFER_SIZE];
int bytes = 100;
int fileHandle;
int status;
if (0 > (OpenComConfig(PORT_NUMBER, "COM1", 115200, 0, 7, 1, 5000, 512))) //Opens connection to COM port, closes program if error code returned
{
printf("Error: COM port could not be opened\n");
return -1;
}
fileHandle = OpenFile ("BootText.txt", VAL_READ_WRITE, VAL_TRUNCATE, VAL_ASCII);
status = SetComTime(PORT_NUMBER, 10);
bytes = ComToFile(PORT_NUMBER, fileHandle, 2000, 0x0D);
return 0;
}
Thanks
Solved! Go to Solution.
12-09-2020 04:37 PM
The trouble with your mock-up is that it will terminate on the first carriage return and then your application will just be done. What if your machine outputs many strings, all terminated the same?
What you really need is a function that's always listening. Take a look at InstallComCallback. This will fire each time you have certain comm triggers.
There's a great example project that shows you how:
samples\rs232\commcallback.cws
12-14-2020 11:32 AM
Hello,
Thanks for the feedback. I've updated my mock-up to execute a while loop. My only question is whether this works the way I think it does. I believe that this will read from the serial port on a loop and write to the file whenever a carriage return is encountered. When the number of bytes read equals zero (end of transmission), the program will close.
I hope that this doesn't overwrite the file contents on every iteration of the loop, but rather appends to the file contents with each call to ComToFile. I opened the file handle in the "truncate" mode, which I believe means how it will behave with the file since the last time it was opened.
Please let me know if you see any issues with this latest approach.
#include <formatio.h>
#include<stdio.h>
#include<string.h>
#include<rs232.h>
#define PORT_NUMBER 1
#define BUFFER_SIZE 1000
int main (void)
{
int bytes = 100;
int fileHandle;
int status;
if (0 > (OpenComConfig(PORT_NUMBER, "COM1", 115200, 0, 7, 1, 5000, 512))) //Opens connection to COM port, closes program if error code returned
{
printf("Error: COM port could not be opened\n");
return -1;
}
fileHandle = OpenFile ("BootText.txt", VAL_READ_WRITE, VAL_TRUNCATE, VAL_ASCII);
status = SetComTime(PORT_NUMBER, 10);
while ((bytes = ComToFile(PORT_NUMBER, fileHandle, BUFFER_SIZE, 0x0D)) != 0);
return 0;
}
12-14-2020 02:15 PM
There are some issues with your code. On the call to OpenFile, pay closer attention to the action parameter. You'll need append.
action | int | Specifies whether to delete the old contents of the file and whether to force the file pointer to the end of the file before each write operation. action is meaningful only if read/writeMode is VAL_READ_WRITE or VAL_WRITE_ONLY. After you perform read operations, the file pointer points to the byte that follows the last byte read. action values are as follows:
|
Your code works enough for proof-of-concept, but in practice this wouldn't be a functional program. Blocking loops prevent other callback functions in the same thread from firing.
12-14-2020 02:58 PM
Hello,
Thanks for confirming. I've updated the mode to "append." As for what you said here: "
.Blocking loops prevent other callback functions in the same thread from firing.
Can you elaborate on what that means? In my program the only loop present is the while loop that does the serial buffer read/write to file. What callback functions will be blocked? FYI, I am ultimately looking to call this from a TestStand sequence during an automated test.
12-14-2020 03:15 PM - edited 12-14-2020 03:16 PM
It sounds like you're new to the CVI platform. To get acquainted with how it works, have a look at how its callback system works. A callback fires upon the triggering of a control on a UIR (GUI). Though in your example, there is no associated GUI, which is fine.
If your CVI application were to need GUI controls, then those associated functions would not fire if execution were inside a for/while loop (until that loop finished).
If you don't have any other controls and main() is your own application function, then sure your code would get the job done.
If you need any other functions to run "simultaneously", then I would again suggest a separate comm callback.
12-14-2020 03:40 PM
Yes, I am somewhat new to CVI. As you note, this is my own application, so not applicable here. But I will make sure to learn how the callback system works in CVI. Thanks!
12-14-2020 03:42 PM
FYI, below is the final "mock-up" code for any interested:
#include <formatio.h>
#include<stdio.h>
#include<string.h>
#include<rs232.h>
#define COM_PORT 1
#define BUFFER_SIZE 1000
int main (void)
{
int bytes = 100;
int timeOut = 10;
static int fileHandle;
int status;
/* Open and configure COM port, close program if error code returned */
if ((OpenComConfig(COM_PORT, "COM1", 115200, 0, 7, 1, 512, 512)) < 0)
{
printf("Error: COM port could not be opened\n");
return -1;
}
/* Turn off Hardware handshaking */
SetCTSMode (COM_PORT, LWRS_HWHANDSHAKE_OFF);
/* Set timeout value */
status = SetComTime(COM_PORT, timeOut);
/* Make sure Serial buffers are empty */
FlushInQ (COM_PORT);
FlushOutQ (COM_PORT);
/* Open handle to log file */
fileHandle = OpenFile ("BootText.txt", VAL_READ_WRITE, VAL_APPEND, VAL_ASCII);
/* Read buffer and write to file when newline encountered */
/* Terminate loop when no bytes are present */
while ((bytes = ComToFile(COM_PORT, fileHandle, BUFFER_SIZE, 0x0D)) > 0)
continue;
/* Close the open COM port and log file before exiting */
CloseCom(COM_PORT);
CloseFile(fileHandle);
return 0;
}
06-03-2021 10:33 AM
Hello Electrolund,
Thank you for the information. I need to clarify something. My test case is fairly simple. I'm looking to build a DLL that I can call from TestStand. The test case is that I will reboot my UUT and then read all the data that comes across the serial bus. I don't know exactly how much data will come across, but I plan to read it into a buffer and then scan the buffer for certain tokens that indicate a successful boot. I had written my code to first call OpenComConfig(). Then, after the UUT has been reset I will call the below function that I wrote. The below function is a wrapper around the CVI function ComRd().
int readComPort(int comPort, char *buf, size_t byteCount, char * errBuf);
int bytesRead = 0;
int count = 0;
while(bytesRead < byteCount)
{
count = ComRd(comPort, buf , byteCount);
if (count < 0)
{
checkRS232Err (status, errBuf);
break;
}
bytesRead += count;
}
This function will read the port until the total bytes expected are read. My problem with this approach is that in my case I don't know how many bytes to expect. I just want to keep reading until there is nothing left at the port. I suppose I could make my exit condition "while count > 0".
My question is, does it make sense to have a callback fucntion instead? There is no GUI event here to repsond to. I looked at the commcallback example and I'm trying to understand how it works. Is there a callback event that will execute if there is simply data at the port to read? Maybe that is the way to go, that ComRd() will execute whenever data is present at the port.
Do you think my current approach is fine or should I use a callback function? Also, if I do use a callback I'm a little confused about how to implement that and what event to trigger off. Is there an event for the case where data is ready at the COM port?
Thanks!
06-03-2021 11:15 AM - edited 06-03-2021 11:16 AM
Regarding your example code, there are several undefined variables, but I assume this is just pseudo code.
Regardless, yes you can make a DLL around this code. I have done something very similar, wrapping my own specific functions around the CVI RS232 library functions into a DLL that can then be more easily used by a host application. This works well.
Please have a good look at the NI Example Finder in your LabWindows Help menu. I learned about 75% of what I know about CVI in these examples! Here are several good DLL example projects:
Regarding implementation for your RS232 project, I would recommend using callbacks. Callbacks are the backbone of the CVI platform. They allow you to take advantage of event-based triggering.
InstallComCallback will give you all the options you need. As you suspected, there are special events with this, like bytes received or buffer size. Pay close attention to the included sample project in the help. This will teach you faster than I can type it here!
One final thought is to consider your device. If you don't know the number of bytes received, maybe you know about the protocol? What's the terminating character, any? What's the starting character, any? Is there a byte that defines the number of proceeding characters? There's usually some definition of the packets your receive, from which you can devise the best callback event.
Good luck!