尝试通过创建模拟串行设备来测试 C 代码
Trying to test C code by creating a simulated serial device
我希望以前没有人问过这个问题,我已经检查过并偶然发现 this 但它对我尝试进行的自动化测试并没有真正帮助。
我想创建一个能够创建串行设备的简单测试脚本,允许我 "echo 'w' > device" 并从中获取值 X。
手动使用 socat 创建多个点并没有多大帮助,因为我的代码只能查看一个端口,而不是 slave/master 样式的端口。
我已经包含了我的函数,它与串行端口交互,并与实际的物理硬件一起工作!
int interactWithPort(char* portID, char BW, char* offOn){
speed_t baud = B9600; // baud rate
int fd = open(portID, (BW == 'B') ? O_RDWR : O_WRONLY); //Open the port with the correct RW settings
struct termios settings; // structure for the settings that will be used for the port
tcgetattr(fd, &settings);
cfsetospeed(&settings, baud); // baud rate
settings.c_cflag &= ~PARENB; // no parity
settings.c_cflag &= ~CSTOPB; // 1 stop bit
settings.c_cflag &= ~CSIZE;
settings.c_cflag |= CS8 | CLOCAL; // 8 bits
settings.c_lflag = ICANON; // canonical mode
settings.c_oflag &= ~OPOST; // raw output
tcsetattr(fd, TCSANOW, &settings); // apply the settings
tcflush(fd, TCOFLUSH);
fcntl(fd, F_SETFL, 0); // apply file control operations
// Initialize file descriptor sets
fd_set read_fds, write_fds, except_fds;
FD_ZERO(&read_fds);
FD_ZERO(&write_fds);
FD_ZERO(&except_fds);
FD_SET(fd, &read_fds);
// Set timeout to 1.0 seconds
struct timeval timeout;
timeout.tv_sec = 3; // Read timeout of around 3 seconds, any more than this and something has went wrong
timeout.tv_usec = 1;
int w = 0;
if(BW == 'W'){
w = (int) write(fd, offOn, 1);
}else{
w = (int) write(fd, "w", 1); // writes to the port a w or a 1/0 depending on function
}
//If theres an error in writing to the scales then tell us!
if(w < 0)
fprintf(stderr, "Error writting to device: %s\n", portID);
//If we flip switch to water then return as it's worked
if(BW == 'W') return w;
// Wait for input to become ready or until the time out; the first parameter is
// 1 more than the largest file descriptor in any of the sets
if (select(fd + 1, &read_fds, &write_fds, &except_fds, &timeout) == 1) {
//This buffer holds the data from the serial port
char buffer[32]; //Could reduce this to around 18 in length but better to have more buffering room
// fd is ready for reading
u_short n = (u_short) read(fd, buffer, sizeof(buffer)); //Reads the length of the serial data
buffer[n] = 0;
char * result = deblank(buffer);
close(fd); // close the connection
return atoi(result); // convert the result to a number and cast to be short
}
else
fprintf(stderr, "Timeout error\n");
return 0; // timeout or error
}
我尝试了一些 python 脚本,但它仍然存在与 slave/master 相同的问题,无法从所需地址读取,但可以毫无问题地写入。
我玩过类似这样的 python 脚本,但它的 slave/master 设置仍然不适合我:
import os
import pty
import serial
import time
master, slave = pty.openpty()
s_name = os.ttyname(slave)
ser = serial.Serial(s_name, baudrate=9600)
print(os.ttyname(slave))
print(os.ttyname(master))
# Create a symbolic link for the C program to use!
src = s_name
dst = "/home/nathan/test"
# This creates a symbolic link on python in tmp directory
os.symlink(src, dst)
# To Write to the device to get a reading
# ser.write('w'.encode('ascii'))
# To read from the device
while(True):
if "w" in str(os.read(master, 1000)):
ser.write("100".encode('ascii'))
print("worked")
break
time.sleep(1)
print(os.read(slave, 1000))
# Clean up by deleting the temp link that we used
os.unlink(dst)
... it still has the same problem of slave/master and can't read from the desired address, but can write to it no problem.
您的代码缺少重要的初始化。
应指定输入波特率:
cfsetispeed(&settings, baud);
启用接收器需要 CREAD 标志:
settings.c_cflag |= CREAD;
指定串口为非控制终端也很常见:
fd = open(portID, O_RDWR | O_NOCTTY);
为了一致的逻辑(即匹配你的其他测试),条件应该是
fd = open(portID, (BW == 'W' ? : O_WRONLY : O_RDWR) | O_NOCTTY);
在 write() 和 select() 之间,一个 tcdrain 操作(等待所有输出写入已传输)将是适当的:
tcdrain(fd);
大图不明显,好像打开、初始化、关闭串口只是为了读写一次;即所有这些都在一个过程中。
为了模块化,这通常应分为程序的至少三个部分。
您似乎使用了阻塞规范模式,但只输出一个字节(即行终止符在哪里?)。
总的来说,您的代码无法检查系统调用中的 return 代码是否存在错误情况。由于您的程序未按预期运行,您可能忽略了重要信息。
我希望以前没有人问过这个问题,我已经检查过并偶然发现 this 但它对我尝试进行的自动化测试并没有真正帮助。
我想创建一个能够创建串行设备的简单测试脚本,允许我 "echo 'w' > device" 并从中获取值 X。
手动使用 socat 创建多个点并没有多大帮助,因为我的代码只能查看一个端口,而不是 slave/master 样式的端口。
我已经包含了我的函数,它与串行端口交互,并与实际的物理硬件一起工作!
int interactWithPort(char* portID, char BW, char* offOn){
speed_t baud = B9600; // baud rate
int fd = open(portID, (BW == 'B') ? O_RDWR : O_WRONLY); //Open the port with the correct RW settings
struct termios settings; // structure for the settings that will be used for the port
tcgetattr(fd, &settings);
cfsetospeed(&settings, baud); // baud rate
settings.c_cflag &= ~PARENB; // no parity
settings.c_cflag &= ~CSTOPB; // 1 stop bit
settings.c_cflag &= ~CSIZE;
settings.c_cflag |= CS8 | CLOCAL; // 8 bits
settings.c_lflag = ICANON; // canonical mode
settings.c_oflag &= ~OPOST; // raw output
tcsetattr(fd, TCSANOW, &settings); // apply the settings
tcflush(fd, TCOFLUSH);
fcntl(fd, F_SETFL, 0); // apply file control operations
// Initialize file descriptor sets
fd_set read_fds, write_fds, except_fds;
FD_ZERO(&read_fds);
FD_ZERO(&write_fds);
FD_ZERO(&except_fds);
FD_SET(fd, &read_fds);
// Set timeout to 1.0 seconds
struct timeval timeout;
timeout.tv_sec = 3; // Read timeout of around 3 seconds, any more than this and something has went wrong
timeout.tv_usec = 1;
int w = 0;
if(BW == 'W'){
w = (int) write(fd, offOn, 1);
}else{
w = (int) write(fd, "w", 1); // writes to the port a w or a 1/0 depending on function
}
//If theres an error in writing to the scales then tell us!
if(w < 0)
fprintf(stderr, "Error writting to device: %s\n", portID);
//If we flip switch to water then return as it's worked
if(BW == 'W') return w;
// Wait for input to become ready or until the time out; the first parameter is
// 1 more than the largest file descriptor in any of the sets
if (select(fd + 1, &read_fds, &write_fds, &except_fds, &timeout) == 1) {
//This buffer holds the data from the serial port
char buffer[32]; //Could reduce this to around 18 in length but better to have more buffering room
// fd is ready for reading
u_short n = (u_short) read(fd, buffer, sizeof(buffer)); //Reads the length of the serial data
buffer[n] = 0;
char * result = deblank(buffer);
close(fd); // close the connection
return atoi(result); // convert the result to a number and cast to be short
}
else
fprintf(stderr, "Timeout error\n");
return 0; // timeout or error
}
我尝试了一些 python 脚本,但它仍然存在与 slave/master 相同的问题,无法从所需地址读取,但可以毫无问题地写入。
我玩过类似这样的 python 脚本,但它的 slave/master 设置仍然不适合我:
import os
import pty
import serial
import time
master, slave = pty.openpty()
s_name = os.ttyname(slave)
ser = serial.Serial(s_name, baudrate=9600)
print(os.ttyname(slave))
print(os.ttyname(master))
# Create a symbolic link for the C program to use!
src = s_name
dst = "/home/nathan/test"
# This creates a symbolic link on python in tmp directory
os.symlink(src, dst)
# To Write to the device to get a reading
# ser.write('w'.encode('ascii'))
# To read from the device
while(True):
if "w" in str(os.read(master, 1000)):
ser.write("100".encode('ascii'))
print("worked")
break
time.sleep(1)
print(os.read(slave, 1000))
# Clean up by deleting the temp link that we used
os.unlink(dst)
... it still has the same problem of slave/master and can't read from the desired address, but can write to it no problem.
您的代码缺少重要的初始化。
应指定输入波特率:
cfsetispeed(&settings, baud);
启用接收器需要 CREAD 标志:
settings.c_cflag |= CREAD;
指定串口为非控制终端也很常见:
fd = open(portID, O_RDWR | O_NOCTTY);
为了一致的逻辑(即匹配你的其他测试),条件应该是
fd = open(portID, (BW == 'W' ? : O_WRONLY : O_RDWR) | O_NOCTTY);
在 write() 和 select() 之间,一个 tcdrain 操作(等待所有输出写入已传输)将是适当的:
tcdrain(fd);
大图不明显,好像打开、初始化、关闭串口只是为了读写一次;即所有这些都在一个过程中。
为了模块化,这通常应分为程序的至少三个部分。
您似乎使用了阻塞规范模式,但只输出一个字节(即行终止符在哪里?)。
总的来说,您的代码无法检查系统调用中的 return 代码是否存在错误情况。由于您的程序未按预期运行,您可能忽略了重要信息。