C++ | 程序暂停功能

news2024/11/24 7:58:16

C++ | 程序暂停功能

在这里插入图片描述

文章目录

  • C++ | 程序暂停功能
    • 初衷
      • rosbag 播包暂停功能源码
    • 识别键盘输入(需输入Enter)
    • 识别键盘输入(无需输入Enter)
    • opencv waitKey函数
    • kill 信号
      • 包装成空格命令
    • Reference
    • [C/C++ 获取键盘事件](https://www.runoob.com/w3cnote/c-get-keycode.html "C/C++ 获取键盘事件")
      • >>>>> 欢迎关注公众号【三戒纪元】 <<<<<

初衷

想实现一个像rosbag播包一样的功能,按下空格键,程序就暂停了,可以根据三维界面进行观察
rosbag_play

rosbag 播包暂停功能源码

    while ((paused_ || delayed_ || !time_publisher_.horizonReached()) && node_handle_.ok())
    {
        bool charsleftorpaused = true;
        while (charsleftorpaused && node_handle_.ok())
        {
            ros::spinOnce();

            if (pause_change_requested_)
            {
              processPause(requested_pause_state_, horizon);
              pause_change_requested_ = false;
            }

            switch (readCharFromStdin()){
            case ' ': // 空格暂停
                processPause(!paused_, horizon);
                break;
            case 's': // 单步调试
                if (paused_) {
                    time_publisher_.stepClock();

                    ros::WallDuration shift = ros::WallTime::now() - horizon ;
                    paused_time_ = ros::WallTime::now();

                    time_translator_.shift(ros::Duration(shift.sec, shift.nsec));

                    horizon += shift;
                    time_publisher_.setWCHorizon(horizon);
            
                    (pub_iter->second).publish(m);

                    printTime();
                    return;
                }
                break;
            case 't':
                pause_for_topics_ = !pause_for_topics_;
                break;
            case EOF:
                if (paused_)
                {
                    printTime();
                    time_publisher_.runStalledClock(ros::WallDuration(.1));
                    ros::spinOnce();
                }
                else if (delayed_)
                {
                    printTime();
                    time_publisher_.runStalledClock(ros::WallDuration(.1));
                    ros::spinOnce();
                    // You need to check the rate here too.
                    if(rate_control_sub_ == NULL || (time_publisher_.getTime() - last_rate_control_).toSec() <= options_.rate_control_max_delay) {
                        delayed_ = false;
                        // Make sure time doesn't shift after leaving delay.
                        ros::WallDuration shift = ros::WallTime::now() - paused_time_;
                        paused_time_ = ros::WallTime::now();
         
                        time_translator_.shift(ros::Duration(shift.sec, shift.nsec));

                        horizon += shift;
                        time_publisher_.setWCHorizon(horizon);
                    }
                }
                else
                    charsleftorpaused = false;
            }
        }

        printTime();
        time_publisher_.runClock(ros::WallDuration(.1));
        ros::spinOnce();
    }

这里可以看出rosbag 也是用了2重 while循环进行的,暂停功能在 line 14readCharFromStdin函数,该函数源码:

int Player::readCharFromStdin() {
#ifdef __APPLE__
    fd_set testfd;
    FD_COPY(&stdin_fdset_, &testfd);
#elif !defined(_MSC_VER)
    fd_set testfd = stdin_fdset_;
#endif

#if defined(_MSC_VER)
    DWORD events = 0;
    INPUT_RECORD input_record[1];
    DWORD input_size = 1;
    BOOL b = GetNumberOfConsoleInputEvents(input_handle, &events);
    if (b && events > 0)
    {
        b = ReadConsoleInput(input_handle, input_record, input_size, &events);
        if (b)
        {
            for (unsigned int i = 0; i < events; ++i)
            {
                if (input_record[i].EventType & KEY_EVENT & input_record[i].Event.KeyEvent.bKeyDown)
                {
                    CHAR ch = input_record[i].Event.KeyEvent.uChar.AsciiChar;
                    return ch;
                }
            }
        }
    }
    return EOF;
#else
    timeval tv;
    tv.tv_sec  = 0;
    tv.tv_usec = 0;
    if (select(maxfd_, &testfd, NULL, NULL, &tv) <= 0)
        return EOF;
    return getc(stdin);
#endif
}

核心在line 34 使用了select函数,select函数用于在非阻塞中,当一个套接字或一组套接字有信号时通知你,此处作为延时函数使用。

getc就是获取字符串,如果获取到空格,则暂停。

识别键盘输入(需输入Enter)

#include <stdio.h>
#include <termios.h>
#include <unistd.h>

#include <iostream>

static struct termios initial_settings, new_settings;
static int peek_character = -1;

void init_keyboard(void);
void close_keyboard(void);
int kbhit(void);
int readch(void);

void init_keyboard() {
  tcgetattr(0, &initial_settings);
  new_settings = initial_settings;
  new_settings.c_lflag |= ICANON;
  new_settings.c_lflag |= ECHO;
  new_settings.c_lflag |= ISIG;
  new_settings.c_cc[VMIN] = 1;
  new_settings.c_cc[VTIME] = 0;
  tcsetattr(0, TCSANOW, &new_settings);
}

void close_keyboard() { tcsetattr(0, TCSANOW, &initial_settings); }

int kbhit() {
  unsigned char ch;
  int nread;

  if (peek_character != -1) return 1;
  new_settings.c_cc[VMIN] = 0;
  tcsetattr(0, TCSANOW, &new_settings);
  nread = read(0, &ch, 1);
  new_settings.c_cc[VMIN] = 1;
  tcsetattr(0, TCSANOW, &new_settings);
  if (nread == 1) {
    peek_character = ch;
    return 1;
  }
  return 0;
}

int readch() {
  char ch;
  if (peek_character != -1) {
    ch = peek_character;
    peek_character = -1;
    return ch;
  }
  read(0, &ch, 1);
  return ch;
}

int main() {
  init_keyboard();
  while (1) {
    kbhit();
    printf("\n%d\n", readch());
  }
  close_keyboard();
  return 0;
}

每次输入完空格或者其他按键,必须按回车Enter,所以结果中都有回车(10)

a

97

10
q

113

10
c 

99

10
j

106

10

识别键盘输入(无需输入Enter)

#include <thread>
#include <chrono>
#include <mutex>
#include <condition_variable>
#include<opencv2/opencv.hpp>

int in = -1;

void scanKeyboard()
{
    struct termios new_settings;
    struct termios stored_settings;
    tcgetattr(0,&stored_settings);
    new_settings = stored_settings;
    new_settings.c_lflag &= (~ICANON);
    new_settings.c_cc[VTIME] = 0;
    tcgetattr(0,&stored_settings);
    new_settings.c_cc[VMIN] = 1;
    tcsetattr(0,TCSANOW,&new_settings);
     
    in = getchar();
    std::cout << "00 in: " << in << std::endl;
    tcsetattr(0,TCSANOW,&stored_settings);
    return;
}

int main()
{
      while (1) {
        std::cout << "-- -- -- -- -- -- -- -- -- -- " << std::endl;
        sleep(3);
        scanKeyboard(); // int key = cv::waitKey(30) & 0xff;
        std::cout << "main in: " << in << std::endl;
        std::mutex mtx;
        std::unique_lock<std::mutex> lck(mtx);
        std::condition_variable cond;
        while (cond.wait_for(lck, std::chrono::seconds(2)) ==
               std::cv_status::timeout) {
          std::cout << "\nTime-Out: 2 second:";
          std::cout << "\nPlease enter the input:";
          break;
        }
    }
    std::cout << "out!" << std::endl;
    return 0;
    }

运行程序结果:

 00 in: 32 # 输入空格
main in: 32
-- -- -- -- -- -- -- -- -- -- 
f00 in: 102 # 输入 f
main in: 102
-- -- -- -- -- -- -- -- -- -- 
r00 in: 114  # 输入 r
main in: 114
-- -- -- -- -- -- -- -- -- -- 
a00 in: 97  # 输入 a
main in: 97
-- -- -- -- -- -- -- -- -- -- 
n00 in: 110  # 输入 n
main in: 110
-- -- -- -- -- -- -- -- -- -- 
d00 in: 100  # 输入 d
main in: 100
-- -- -- -- -- -- -- -- -- -- 
y00 in: 121  # 输入 y
main in: 121
-- -- -- -- -- -- -- -- -- --

opencv waitKey函数

使用opencv 的waitKey函数是可以在有限的时间内,监听键盘按键,如果没有按下键盘,则继续,如果按下键盘则特殊处理。

int key = cv::waitKey(5)

但是该函数只能在 OpenCV的GUI界面才可以使用,例如imshow()创建的创建的窗口上,waitKey才是有效的。

而在终端控制台上是没有用的。

kill 信号

回到问题本身,根本就是我通过终端,发送一个信号给程序,程序接收到了,然后改变运行的代码;我再次发送时,程序又接收到了,再恢复代码。

因此,可以通过linux中的kill 命令给程序发送信号,可以自定义 SIGUSR1SIGUSR2,这样程序就可以随时接收到信号了。

具体kill信号及其设置可参考《Linux信号sigaction / signal》篇。具体代码如下

#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <signal.h> /*for signal() and raise()*/
#include <chrono>
#include <thread>
#include <unistd.h>


#include <atomic>

std::atomic<bool> g_pause;

void SigPause(int32_t signum) {
  if (signum != SIGUSR2) {
    return;
  }

  if (!g_pause.load()) {
    g_pause.store(true);
    std::cout << "Pause success. sleep 3 seconds." << std::endl;
  } else {
    g_pause.store(false);
    std::cout << "continue." << std::endl;
  }
}

int main(){
  g_pause.store(false);
  signal(SIGUSR2, SigPause);

  while (1) {
    while (g_pause.load())
    {
      sleep(3);
      std::cout << "--- --- sleep --- ---" << std::endl;
    }
    std::cout << "--- --- work --- ---" << std::endl;
    sleep(3);
  }
  return 0;
}

包装成空格命令

因为发送kill信号没有空格来得方便,所以通过 shell 脚本包装下,实现空格发送,效果如下:

randy_暂停

Reference

Linux信号sigaction / signal

C/C++ 获取键盘事件

>>>>> 欢迎关注公众号【三戒纪元】 <<<<<

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/970623.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

电动汽车电机驱动系统的组成和作用

1.电机驱动系统的作用与组成电动汽车电机驱动系统是新能源汽车的核心技术之一&#xff0c;它的主要任务是按驾驶员的驾驶意图&#xff0c;将动力电池的化学能高效地转化为机械能&#xff0c;经过变速器、驱动轴等机构驱动车轮。电动机驱动系统主要有电动机、功率器件和控制系统…

旅游APP外包开发注意事项

旅游类APP通常具有多种功能&#xff0c;以提供给用户更好的旅行体验。以下分享常见的旅游类APP功能以及在开发和使用这些APP时需要注意的问题&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 常见功能…

C++智能指针之shared_ptr(保姆级教学)

目录 C智能指针之shared_ptr 本文涉及所有程序 工作原理 使用方法 手动初始化 std::make_shared函数&#xff08;C14提出&#xff09; 构造方法初始化 make_shared初始化 使用实例 shared_ptr常规操作 use_count(); unique(); reset(); get(); 指定删除器 移动…

确保浏览安全:使用SSH实施加密SOCKS5

在互联网上保护隐私和安全至关重要。使用SSH&#xff08;Secure Shell&#xff09;创建加密的SOCKS5代理是一种简单且有效的方法&#xff0c;可以确保您的网络流量在传输过程中受到保护。本文将向您介绍如何使用SSH实施加密SOCKS5&#xff0c;以提高您的浏览安全。 1、准备工作…

使用perf_analyzer和model-analyzer测试tritonserver的模型性能超详细完整版

导读 当我们在使用tritonserver部署模型之后&#xff0c;通常需要测试一下模型的服务QPS的能力&#xff0c;也就是1s我们的模型能处理多少的请求&#xff0c;也被称为吞吐量。 测试tritonserver模型服务的QPS通常有两种方法&#xff0c;一种是使用perf_analyzer 来测试&#…

递归/回溯/动规

1 动规-打家劫舍一 你是一个经验丰富的小偷&#xff0c;准备偷沿街的一排房间&#xff0c;每个房间都存有一定的现金&#xff0c;为了防止被发现&#xff0c;你不能偷相邻的两家&#xff0c;即&#xff0c;如果偷了第一家&#xff0c;就不能再偷第二家&#xff1b;如果偷了第二…

计算机竞赛 基于深度学习的中文情感分类 - 卷积神经网络 情感分类 情感分析 情感识别 评论情感分类

文章目录 1 前言2 情感文本分类2.1 参考论文2.2 输入层2.3 第一层卷积层&#xff1a;2.4 池化层&#xff1a;2.5 全连接softmax层&#xff1a;2.6 训练方案 3 实现3.1 sentence部分3.2 filters部分3.3 featuremaps部分3.4 1max部分3.5 concat1max部分3.6 关键代码 4 实现效果4.…

Dubbo2.0

前置 衡量网站性能指标&#xff1a; 响应时间&#xff1a;一次请求花费时间并发数&#xff1a;同时处理请求数 并发连接数&#xff1a;每秒建立总TCP数量请求数&#xff1a;QPS一秒请求数并发用户数&#xff1a;单位时间用户 吞吐量&#xff1a;单位时间处理请求数 QPS&#…

Web前端自动化测试Cypress实践总结

本文主要首先主要介绍了什么是自动化测试&#xff0c;接着对常用的自动化测试框架进行了对比分析&#xff0c;最后&#xff0c;介绍了如果将自动化测试框架Cypress运用在项目中。 一、自动化测试概述 为了保障软件质量&#xff0c;并减少重复性的测试工作&#xff0c;自动化测…

GaussDB数据库SQL系列-层次递归查询

目录 一、前言 二、GuassDB数据库层次递归查询概念 三、GaussDB数据库层次递归查询实验示例 1、创建实验表 2、sys_connect_by_path(col, separator) 3、connect_by_root(col) 4、WITH RECURSIVE 四、递归查询的优缺点 1、优点 2、缺点 五、总结 一、前言 层次递归…

中使用pack局管理器:管理器布置小部件

一、说明 在本教程中&#xff0c;我们将了解如何制作登录 UI。今天的教程将重点介绍如何使用 Tkinter 的pack布局管理器。 二、设计用户界面 什么是布局管理器&#xff1f;创建图形界面时&#xff0c;窗口中的小部件必须具有相对于彼此排列的方式。例如&#xff0c;可以使用微件…

Yolov5的tensorRT加速(python)

地址&#xff1a;https://github.com/wang-xinyu/tensorrtx/tree/master/yolov5 下载yolov5代码 方法一&#xff1a;使用torch2trt 安装torch2trt与tensorRT 参考博客&#xff1a;https://blog.csdn.net/dou3516/article/details/124538557 先从github拉取torch2trt源码 ht…

【C++ 二叉搜索树】

目录 1.什么是二叉搜索树2.构建二叉搜索树2.1首先搭建树的框架2.2搭建搜索树的框架 3.二叉搜索树的插入3.1非递归式插入3.2递归式插入 4.二叉搜索树的查找4.1非递归查找4.2递归查找 5.二叉搜索树的删除5.1非递归删除5.2递归删除 6.整个代码实现 1.什么是二叉搜索树 简单来讲就…

WebDAV之π-Disk派盘 + 小书匠

小书匠是一款功能丰富,强大的知识管理工具。全平台覆盖,离线数据存储,自定义数据服务器,所见即所得的 markdown 编辑体验。 小书匠提供了多种实用的编辑模式,例如:栏编辑、双栏编辑、三栏编辑、全屏写作、全屏阅读等。并且该软件还提供了许多有用的扩展语法,比如Latex公…

【前端demo】CSVJSON转换器 原生实现:CSV转换为JSON,JSON转换为CSV

文章目录 效果过程textareaTooltip提示工具按钮的min-width判断输入是否是CSV或JSONJSON与CSV样例JSON转为CSVCSV转为JSON不足之处 代码HTMLCSSJS 其他demo 效果 效果预览&#xff1a;CSV&JSON Converter (codepen.io) 参照的预览&#xff1a;JSV Converter(gpaiva00.git…

地毯16 CFR 1630/1631安全防火性易燃性测试

地毯的16 CFR 1630/1631安全防火性易燃性测试是一项重要的产品检测认证标准。该测试旨在评估地毯材料的防火性能&#xff0c;以确保其在使用过程中不会引发火灾或加剧火势。测试过程包括对地毯样品进行燃烧测试和燃烧后的评估。 根据16 CFR 1630标准&#xff0c;地毯样品将被暴…

检漏仪和高真空度控制技术在热管漏率和内部真空度测量中的应用

摘要&#xff1a;大量MEMS真空密封件具有小体积、高真空和无外接通气接口的特点&#xff0c;现有的各种检漏技术无法对其进行无损形式的漏率和内部真空度测量。基于压差法和高真空度恒定控制技术&#xff0c;本文提出了解决方案。方案的具体内容是将被测封装器件放置在一个比器…

elementui表格自定义表头的两种方法

表格自定义表头的方式 多选框表头换文字 请查看上篇博客&#xff1a;http://t.csdn.cn/69De2 文字换按钮 render-header render-header方法详情 Table-column Attributes 参数说明类型可选值默认值render-header列标题 Label 区域渲染使用的 FunctionFunction(h, { column, $in…

恒运资本:北向资金流出一定会跌吗?股票涨跌与什么有关?

北向资金被认为是A股商场的风向标&#xff0c;它的动向往往会影响投资者的心情。那么北向资金流出一定会跌吗&#xff1f;股票涨跌与什么有关&#xff1f;恒运资本也为大家准备了相关内容&#xff0c;以供参阅。 北向资金流出一定会跌吗&#xff1f; 北向资金流出并不一定意味…

快速解决 adb server version doesn‘t match this client

这个问题是由于电脑上安装了多个版本的adb工具&#xff0c;客户端和服务端的版本不一致&#xff0c;无法正常通信导致。最快的解决方法就是将Android SDK中adb复制到系统目录下。 操作步骤如下&#xff1a; 1. 查看adb版本和路径 执行adb version&#xff0c;如下&#xff0…