我正在构建一种使用键盘模拟设备中的数据的设备驱动程序。
该设备是刷卡,因此其行为如下:
- 用户走上前去,刷卡
- 我得到一串字符(键代码,真的,包括大写字母的修饰键(
- 我不知道我会得到多少个字符
- 我不知道我什么时候得到一些东西
由于我不知道我会得到多少个字符,因此阻止键盘 tty 上的读取没有用 - 我最终会在最后一个字符之后阻止。在 Ruby 中,我正在做的是使用 IO 模块对键盘设备执行异步读取,并使用超时来确定是否已到达数据末尾。这在逻辑上工作正常(即使用户快速刷卡也会比字符之间的发送速率慢(。
问题是有时,我会丢失字符串中间的数据。我的预感是,由于我读取数据太慢,因此发生了某种缓冲区溢出。为了确认这一点,我在每个关键进程之间插入了小等待。较长的等待时间(20ms+(确实会加剧问题。然而,大约 5 毫秒的等待实际上会让它消失吗?我能想到的唯一解释是异步读取本身是昂贵的(因为 Ruby(,并且在没有速率限制的情况下执行它们实际上比以 5ms 延迟执行它们要慢。
这听起来合理吗?关于这可能是什么,还有其他想法吗?
红宝石实际上是JRuby 9000。该机器是Ubuntu LTS 16。
编辑:这是相关代码的片段
private def read_swipe(buffer_size, card_reader_input, pause_between_reads, seconds_to_complete)
limit = Time.now + seconds_to_complete.seconds
swipe_data = ''
begin
start_time = Time.now
sleep pause_between_reads
batch = card_reader_input.read_nonblock(buffer_size)
swipe_data << batch
rescue IO::WaitReadable
IO.select([card_reader_input], nil, nil, 0.5)
retry unless limit < start_time
end while start_time < limit
swipe_data
end
哪里card_reader_input = File.new(event_handle, 'rb')
我不确定 Ruby 代码,但您可以使用linux sysfs
来访问来自键盘"喜欢"设备的字符,如果可行,您可以从 ruby 应用程序中调用C
代码。我已经为条形码阅读器做了这个,以下是代码:
static int init_barcode_com(char* bcr_portname)
{
int fd;
/* Open the file descriptor in non-blocking mode */
fd = open(bcr_portname, O_RDONLY | O_NOCTTY | O_NDELAY);
cout << "Barcode Reader FD: " << fd <<endl;
if (fd == -1)
{
cerr << "ERROR: Cannot open fd for barcode communication with error " << fd <<endl;
}
fcntl(fd, F_SETFL, 0);
/* Set up the control structure */
struct termios toptions;
/* Get currently set options for the tty */
tcgetattr(fd, &toptions);
/* Set custom options */
/* 9600 baud */
cfsetispeed(&toptions, B9600);
cfsetospeed(&toptions, B9600);
/* 8 bits, no parity, no stop bits */
toptions.c_cflag &= ~PARENB;
toptions.c_cflag &= ~CSTOPB;
toptions.c_cflag &= ~CSIZE;
toptions.c_cflag |= CS8;
/* no hardware flow control */
toptions.c_cflag &= ~CRTSCTS;
/* enable receiver, ignore status lines */
toptions.c_cflag |= CREAD | CLOCAL;
/* disable input/output flow control, disable restart chars */
toptions.c_iflag &= ~(IXON | IXOFF | IXANY);
/* disable canonical input, disable echo,
* disable visually erase chars,
* disable terminal-generated signals */
toptions.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
/* disable output processing */
toptions.c_oflag &= ~OPOST;
/* wait for n (in our case its 1) characters to come in before read returns */
/* WARNING! THIS CAUSES THE read() TO BLOCK UNTIL ALL */
/* CHARACTERS HAVE COME IN! */
toptions.c_cc[VMIN] = 0;
/* no minimum time to wait before read returns */
toptions.c_cc[VTIME] = 100;
/* commit the options */
tcsetattr(fd, TCSANOW, &toptions);
/* Wait for the Barcode to reset */
usleep(10*1000);
return fd;
}
static int read_from_barcode_reader(int fd, char* bcr_buf)
{
int i = 0, nbytes = 0;
char buf[1];
/* Flush anything already in the serial buffer */
tcflush(fd, TCIFLUSH);
while (1) {
nbytes = read(fd, buf, 1); // read a char at a time
if (nbytes == -1) {
return -1; // Couldn't read
}
if (nbytes == 0) {
return 0;
}
if (buf[0] == 'n' || buf[0] == 'r') {
return 0;
}
bcr_buf[i] = buf[0];
i++;
}
return 0;
}
现在您不知道要获得多少个字符,您可以使用VMIN
和VTIME
组合来解决您的问题。本文档详细介绍了VMIN
和VTIME
的各种可能性。