栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

从输入事件中读取条形码(Linux,C)

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

从输入事件中读取条形码(Linux,C)

当您应该只考虑该事件

read()
的回报
== sizeofev
,因为我们是从输入设备读取这里。如果返回零,则意味着不再发生任何事件(也许设备已分离?)。如果返回
-1
,请检查
errno
。如果
read()
返回任何其他值,则说明内核驱动程序已经崩溃了,您可以将其视为致命错误。

errno == EINTR
是正常的(发生在传递信号时),这本身不是错误。它不应该在这里发生,但是忽略它(将其视为打ic,而不是错误)是非常安全的。

errno == EAGAIN``O_NONBLOCK
open()
标记中使用时发生,并且尚无新事件可用。

绝对没有理由在

O_NONBLOCK
这里使用。它所做的是使你的代码浪费CPU周期,从返回的每秒几万次
read()
的调用只是返回
-1
errno== EAGAIN
。只需将其放下即可,以便
read()
将仅等待新事件到达并返回它。

请参阅我对input_event结构描述问题的回答。

总而言之,适用于

ev_type == 1 == EV_KEY

  • ev_value == 0
    :释放键(向上键)
  • ev_value == 1
    :按下键(按下键)
  • ev_value == 2
    :自动重复(自动重复键)
  • ev_pre == 1 == KEY_ESC
  • ev_pre == 2 == KEY_1
  • ev_pre == 3 == KEY_2
  • ev_pre == 10 == KEY_9
  • ev_pre == 11 == KEY_0
  • ev_pre == 28 == KEY_ENTER

实际提供的设备的按键

1
2
3
4
5
6
7
8
9
Enter

(请注意,您只显示了按键释放事件;实际上,您应该分别看到两个,一个带有

ev_value == 1
,然后带有一个
ev_value ==0
ev_pre
)。

我尝试过的一个中国人很好,尽管便宜。它具有一本手册,其中包含一些条形码,包括一些可以在条形码格式(和位数)之间切换的手册。我隐约记得使用两个条形码切换到另一种模式,并使用最小音量来发出哔声。即使分离后,它似乎仍保留设置。


这是我将使用哪种实施方式读取条形码的示例。我显然将条形码读取部分拆分为一个单独的文件。

以下代码专用于公共领域(在CC0下许可),因此可以随意使用它。没有任何形式的保证,所以不要怪我有任何破损。(欢迎进行任何错误修复;如果报告了错误,则将进行检查并包含在下面的代码中。建议在下面添加注释;每两天左右都会阅读所有答案中的注释。)

头文件

barpre.h

#ifndef   BARCODE_H#define   BARCODE_H#include <stdlib.h>#include <signal.h>extern volatile sig_atomic_t done;int install_done(const int signum);typedef struct {    int  fd;    volatile int    timeout;    timer_t         timer;} barpre_dev;int barpre_close(barpre_dev *const dev);int barpre_open(barpre_dev *const dev, const char *const device_path);size_t barpre_read(barpre_dev *const dev,         char *const buffer, const size_t length,         const unsigned long maximum_ms);#endif 

以下

barpre.c
文件中的实现当前仅接受数字(0到1),但是添加任何其他必要的键(例如
KEY_A
KEY_Z
)应该很简单。当前的操作忽略了移位,控制等功能,因为据我所知,任何扫描仪都没有提供这些功能。它使用
SIGRTMAX-0
实时信号和每个条形码设备的自定义计时器来读取条形码,因此您需要将其链接到
librt
-lrt
):

#define  _POSIX_C_SOURCE 200809L#include <stdlib.h>#include <unistd.h>#include <sys/types.h>#include <sys/stat.h>#include <sys/ioctl.h>#include <fcntl.h>#include <signal.h>#include <time.h>#include <linux/input.h>#include <string.h>#include <stdio.h>#include <errno.h>#define UNUSED          __attribute__((unused))#define TIMEOUT_SIGNAL  (SIGRTMAX-0)volatile sig_atomic_t done = 0;static void handle_done(int signum UNUSED){    done = 1;}int install_done(const int signum){    struct sigaction act;    sigemptyset(&act.sa_mask);    act.sa_handler = handle_done;    act.sa_flags = 0;    if (sigaction(signum, &act, NULL) == -1)        return errno;    return 0;}typedef struct {    int  fd;    volatile int    timeout;    timer_t         timer;} barpre_dev;static void handle_timeout(int signum UNUSED, siginfo_t *info, void *context UNUSED){    if (info && info->si_pre == SI_TIMER && info->si_value.sival_ptr)#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)        __atomic_add_fetch((int *)info->si_value.sival_ptr, 1, __ATOMIC_SEQ_CST);#else        __sync_add_and_fetch((int *)info->si_value.sival_ptr, 1);#endif}static int install_timeouts(void){    static int installed = 0;    if (!installed) {        struct sigaction act;        sigemptyset(&act.sa_mask);        act.sa_sigaction = handle_timeout;        act.sa_flags = SA_SIGINFO;        if (sigaction(TIMEOUT_SIGNAL, &act, NULL) == -1) return errno;        installed = 1; }    return 0;}int barpre_close(barpre_dev *const dev){    int retval = 0;    if (!dev)        return 0;    if (dev->fd != -1)        if (close(dev->fd) == -1) retval = errno;    dev->fd = -1;    if (dev->timer)        if (timer_delete(dev->timer) == -1) if (!retval)     retval = errno;    dev->timer = (timer_t)0;        while (1) {        struct timespec t;        siginfo_t info;        sigset_t s;        t.tv_sec = (time_t)0;        t.tv_nsec = 0L;        sigemptyset(&s);        if (sigtimedwait(&s, &info, &t) != TIMEOUT_SIGNAL) break;        if (info.si_pre != SI_TIMER || !info.si_value.sival_ptr) continue;#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)        __atomic_add_fetch((int *)info.si_value.sival_ptr, 1, __ATOMIC_SEQ_CST);#else        __sync_add_and_fetch((int *)info.si_value.sival_ptr, 1);#endif    }    return errno = retval;}int barpre_open(barpre_dev *const dev, const char *const device_path){    struct sigevent event;    int fd;    if (!dev)        return errno = EINVAL;    dev->fd = -1;    dev->timeout = -1;    dev->timer = (timer_t)0;    if (!device_path || !*device_path)        return errno = EINVAL;    if (install_timeouts())        return errno;    do {        fd = open(device_path, O_RDonLY | O_NOCTTY | O_CLOEXEC);    } while (fd == -1 && errno == EINTR);    if (fd == -1)        return errno;    errno = 0;    if (ioctl(fd, EVIOCGRAB, 1)) {        const int saved_errno = errno;        close(fd);        return errno = (saved_errno) ? errno : EACCES;    }    dev->fd = fd;    memset(&event, 0, sizeof event);    event.sigev_notify = SIGEV_SIGNAL;    event.sigev_signo = TIMEOUT_SIGNAL;    event.sigev_value.sival_ptr = (void *)&(dev->timeout);    if (timer_create(CLOCK_REALTIME, &event, &dev->timer) == -1) {        const int saved_errno = errno;        close(fd);        return errno = (saved_errno) ? errno : EMFILE;    }    return errno = 0;}size_t barpre_read(barpre_dev *const dev,         char *const buffer, const size_t length,         const unsigned long maximum_ms){    struct itimerspec it;    size_t len = 0;    int status = ETIMEDOUT;    if (!dev || !buffer || length < 2 || maximum_ms < 1UL) {        errno = EINVAL;        return (size_t)0;    }        it.it_value.tv_sec = maximum_ms / 1000UL;    it.it_value.tv_nsec = (maximum_ms % 1000UL) * 1000000L;        it.it_interval.tv_sec = 0;    it.it_interval.tv_nsec = 10000000L;    if (timer_settime(dev->timer, 0, &it, NULL) == -1)        return (size_t)0;    #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR >= 7)    __atomic_store_n((int *)&(dev->timeout), 0, __ATOMIC_SEQ_CST);#else    __sync_fetch_and_and((int *)&(dev->timeout), 0);#endif    while (!dev->timeout) {        struct input_event ev;        ssize_t n;        int digit;        n = read(dev->fd, &ev, sizeof ev);        if (n == (ssize_t)-1) { if (errno == EINTR)     continue; status = errno; break;        } else        if (n == sizeof ev) {  if (ev.type != EV_KEY || (ev.value != 1 && ev.value != 2))     continue; switch (ev.pre) { case KEY_0: digit = '0'; break; case KEY_1: digit = '1'; break; case KEY_2: digit = '2'; break; case KEY_3: digit = '3'; break; case KEY_4: digit = '4'; break; case KEY_5: digit = '5'; break; case KEY_6: digit = '6'; break; case KEY_7: digit = '7'; break; case KEY_8: digit = '8'; break; case KEY_9: digit = '9'; break; default:    digit = ''; }  if (digit == '') {     if (!len)         continue;     status = 0;     break; } if (len < length)     buffer[len] = digit; len++; continue;        } else        if (n == (ssize_t)0) { status = ENOENT; break;        } else { status = EIO; break;        }    }        if (len + 1 < length)        buffer[len + 1] = '';    else        buffer[length - 1] = '';        it.it_value.tv_sec = 0;    it.it_value.tv_nsec = 0;    it.it_interval.tv_sec = 0;    it.it_interval.tv_nsec = 0L;    (void)timer_settime(dev->timer, 0, &it, NULL);    errno = status;    return len;}

这是一个示例程序

example.c
。您提供输入事件设备(我建议使用符号链接,
/dev/input/by-id/
或者
/dev/input/by-path/
如果您
udev
提供了符号链接,因为事件设备索引在内核版本和硬件启动之间可能不稳定),以及您愿意等待/直到下一个条形码的最大持续时间。

#include <stdlib.h>#include <string.h>#include <signal.h>#include <stdio.h>#include <errno.h>#include "barpre.h"#define BARCODE_MAXLEN  1023int main(int argc, char *argv[]){    barpre_dev    dev;    unsigned long  ms;    int status, exitpre;    if (argc != 3 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {        fprintf(stderr, "n");        fprintf(stderr, "Usage: %s [ -h | --help ]n", argv[0]);        fprintf(stderr, "       %s INPUT-EVENT-DEVICE IDLE-TIMEOUTn", argv[0]);        fprintf(stderr, "n");        fprintf(stderr, "This program reads barpres from INPUT-EVENT-DEVICE,n");        fprintf(stderr, "waiting at most IDLE-TIMEOUT seconds for a new barpre.n");        fprintf(stderr, "The INPUT-EVENT-DEVICE is grabbed, the digits do not appear asn");        fprintf(stderr, "inputs in the machine.n");        fprintf(stderr, "You can at any time end the program by sending it an");        fprintf(stderr, "SIGINT (Ctrl+C), SIGHUP, or SIGTERM signal.n");        fprintf(stderr, "n");        return EXIT_FAILURE;    }    if (install_done(SIGINT) ||        install_done(SIGHUP) ||        install_done(SIGTERM)) {        fprintf(stderr, "Cannot install signal handlers: %s.n", strerror(errno));        return EXIT_FAILURE;    }    {        double value, check;        char dummy;        if (sscanf(argv[2], " %lf %c", &value, &dummy) != 1 || value < 0.001) { fprintf(stderr, "%s: Invalid idle timeout value (in seconds).n", argv[2]); return EXIT_FAILURE;        }        ms = (unsigned long)(value * 1000.0);        check = (double)ms / 1000.0;        if (value < check - 0.001 || value > check + 0.001 || ms < 1UL) { fprintf(stderr, "%s: Idle timeout is too long.n", argv[2]); return EXIT_FAILURE;        }    }    if (barpre_open(&dev, argv[1])) {        fprintf(stderr, "%s: Cannot open barpre input event device: %s.n", argv[1], strerror(errno));        return EXIT_FAILURE;    }    while (1) {        char    pre[BARCODE_MAXLEN + 1];        size_t  len;        if (done) { status = EINTR; break;        }        len = barpre_read(&dev, pre, sizeof pre, ms);        if (errno) { status = errno; break;        }        if (len < (size_t)1) { status = ETIMEDOUT; break;        }        printf("%zu-digit barpre: %sn", len, pre);        fflush(stdout);    }    if (status == EINTR) {        fprintf(stderr, "Signaled to exit. Complying.n");        fflush(stderr);        exitpre = EXIT_SUCCESS;    } else    if (status == ETIMEDOUT) {        fprintf(stderr, "Timed out, no more barpres.n");        fflush(stderr);        exitpre = EXIT_SUCCESS;    } else {        fprintf(stderr, "Error reading input event device %s: %s.n", argv[1], strerror(status));        fflush(stderr);        exitpre = EXIT_FAILURE;    }    if (barpre_close(&dev)) {        fprintf(stderr, "Warning: Error closing input event device %s: %s.n", argv[1], strerror(errno));        fflush(stderr);        exitpre = EXIT_FAILURE;    }    return exitpre;}

如您所见,它只是将条形码打印到标准输出(以及任何对标准错误的错误消息和警告)。要进行编译,我建议使用以下命令

Makefile
(缩进必须使用
Tab
,而不是空格):

CC      := gccCFLAGS  := -Wall -Wextra -O2LDFLAGS := -lrt.PHONY: all cleanall: clean exampleclean:    rm -f example *.o%.o: %.c    $(CC) $(CFLAGS) -c $^example: example.o barpre.o    $(CC) $(CFLAGS) $^ $(LDFLAGS) -o example

要进行编译,请创建上面列出的四个文件,然后运行

make clean example

例如运行

./example /dev/input/event4 5.0

将从中读取条形码

/dev/input/event4
,但会在
Ctrl
+
C
(INT信号),HUP信号,TERM信号处退出,或者在5秒钟内没有条形码出现时退出。

请注意,如果在5秒钟之内只读取了部分条形码,我们确实会得到该部分(可以尝试读取其余部分),但是上面的示例程序将忽略该部分,仅显示超时。

有什么问题吗



转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/408793.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号