当前位置: 首页 > news >正文

【开发语言】层次状态机(HSM)介绍

层次状态机(Hierarchical State Machine, HSM),从基本原理、结构设计、实现方法以及如何结合 Qt 进行具体实现等方面进行分析。

1. 层次状态机的基本原理

层次状态机是一种用于管理复杂系统行为的状态机模型,它通过将状态组织成层次结构来简化设计和维护。这种结构使得复杂的逻辑可以分解为更小、更易于管理的部分。

关键概念:
  • 状态(State): 系统在某一时刻所处的条件或模式。
  • 事件(Event): 触发状态转换的信息或信号。
  • 转换(Transition): 从一个状态到另一个状态的迁移过程。
  • 动作(Action): 在进入、离开状态或进行转换时执行的操作。
  • 父状态和子状态:
    • 父状态(Superstate): 包含多个子状态的状态。
    • 子状态(Substate): 属于某个父状态的更具体的状态。
特点:
  • 嵌套结构: 状态可以嵌套在其他状态中,形成层次结构。
  • 继承行为: 子状态可以继承父状态的行为和动作。
  • 事件委托: 事件可以从子状态传递到父状态进行处理。
  • 简化设计: 将复杂的状态逻辑分解为更小的、可管理的部分。

2. 层次状态机的设计

在设计层次状态机时,需要仔细规划状态之间的关系以及如何组织这些状态。以下是一些设计原则和步骤:

设计步骤:
  1. 识别顶级状态:

    • 确定系统的基本操作模式或主要功能。
    • 例如,在一个电梯系统中,顶级状态可能包括“待命”、“运行”和“维护”。
  2. 定义子状态:

    • 对于每个顶级状态,进一步分解为更具体的状态。
    • 例如,“运行”状态可以包含“上升”、“下降”和“停止”等子状态。
  3. 确定事件和转换:

    • 定义可能触发状态转换的事件。
    • 确定每个状态在接收到特定事件时应执行的操作以及如何进行转换。
    • 例如,“上升”状态在接收到“到达楼层”事件时,可以转换到“停止”状态。
  4. 实现继承和委托:

    • 设计父状态的行为,并让子状态继承这些行为。
    • 当子状态无法处理某个事件时,将该事件传递给其父状态进行处理。
  5. 编写动作函数:

    • 实现每个状态的进入(Entry)、离开(Exit)操作以及转换期间的动作(Action)。
    • 例如,在“上升”状态下进入时启动电机,在离开时停止电机。
  6. 定义初始状态和历史状态:

    • 指定每个复合状态的初始子状态。
    • 使用历史状态来记住上次活动的子状态,以便在返回该状态时恢复到之前的状态。
示例:

假设我们设计一个简单的电视遥控器状态机,包含以下状态:

  • 待命(Standby)
  • 运行(Running)
    • 频道选择(Channel Selection)
      • 浏览模式(Browse Mode)
      • 锁定模式(Lock Mode)
    • 音量控制(Volume Control)

事件包括:

  • POWER
  • CHANNEL_UP, CHANNEL_DOWN
  • VOLUME_UP, VOLUME_DOWN
  • MODE_SWITCH
状态图示例:
          +-------------------+|     Standby       |+--------+----------+|POWER|+----v-----+|  Running   |+--+-+------+| |CHANNEL_UP|VOLUME_DOWN/      |      \/       v       \+------------+     +------------+| Browse Mode|     |Volume Ctrl|+------------+     +------------+

3. 层次状态机的实现

在实际编程中,层次状态机可以通过多种方式实现。以下是一个使用 C++ 和 Qt 的具体示例。

使用结构体定义状态和事件

首先,我们定义状态、事件及其处理函数的类型:

#include <QObject>
#include <QVector>
#include <QDebug>namespace HSMUtilityDef {typedef uint32_t HSM_EVENT;// 定义常见事件const uint32_t MAX_DEPTH = 5;const HSM_EVENT HSME_NULL = 0;const HSM_EVENT HSME_START = 1;const HSM_EVENT HSME_INIT = static_cast<HSM_EVENT>(-3);const HSM_EVENT HSME_ENTRY = static_cast<HSM_EVENT>(-2);const HSM_EVENT HSME_EXIT = static_cast<HSM_EVENT>(-1);// 自定义事件const HSM_EVENT POWER = 100;const HSM_EVENT CHANNEL_UP = 101;const HSM_EVENT CHANNEL_DOWN = 102;const HSM_EVENT VOLUME_UP = 103;const HSM_EVENT VOLUME_DOWN = 104;const HSM_EVENT MODE_SWITCH = 105;
}
定义状态基类

创建一个抽象基类 HSMState,包含处理事件的虚函数:

class HSMState : public QObject {Q_OBJECTpublic:explicit HSMState(HSMState *parent = nullptr) : QObject(parent), m_parent(parent) {}virtual ~HSMState() {}// 处理事件的主要接口virtual HSMUtilityDef::HSM_EVENT handleEvent(HSMUtilityDef::HSM_EVENT event, void* param = nullptr) {switch (event) {case HSMUtilityDef::HSME_ENTRY:onEntry(param);break;case HSMUtilityDef::HSME_EXIT:onExit(param);break;default:return unhandledEvent(event, param);}return HSMUtilityDef::HSME_NULL;}protected:// 进入状态时执行的动作virtual void onEntry(void* param) {}// 离开状态时执行的动作virtual void onExit(void* param) {}// 处理未定义事件的方法virtual HSMUtilityDef::HSM_EVENT unhandledEvent(HSMUtilityDef::HSM_EVENT event, void* param) {qDebug() << "Unhandled event" << event;return event;}// 获取父状态HSMState* parent() const { return m_parent; }private:HSMState *m_parent;
};
定义具体状态类

创建具体的派生状态类,实现特定的逻辑:

// 待命状态(Standby)
class StandbyState : public HSMState {Q_OBJECTpublic:explicit StandbyState(HSMState* parent = nullptr) : HSMState(parent) {}protected:void onEntry(void* param) override {qDebug() << "Entering Standby State";}void onExit(void* param) override {qDebug() << "Exiting Standby State";}HSMUtilityDef::HSM_EVENT unhandledEvent(HSMUtilityDef::HSM_EVENT event, void* param) override {if (event == HSMUtilityDef::POWER) {return static_cast<HSMUtilityDef::HSM_EVENT>(HSMUtilityDef::HSME_INIT);}return HSMState::unhandledEvent(event, param);}
};// 运行状态(Running)
class RunningState : public HSMState {Q_OBJECTpublic:explicit RunningState(HSMState* parent = nullptr) : HSMState(parent) {}protected:void onEntry(void* param) override {qDebug() << "Entering Running State";}void onExit(void* param) override {qDebug() << "Exiting Running State";}HSMUtilityDef::HSM_EVENT unhandledEvent(HSMUtilityDef::HSM_EVENT event, void* param) override {if (event == HSMUtilityDef::POWER) {return static_cast<HSMUtilityDef::HSM_EVENT>(HSMUtilityDef::HSME_EXIT);}return HSMState::unhandledEvent(event, param);}
};// 频道选择状态(Channel Selection)
class ChannelSelectionState : public HSMState {Q_OBJECTpublic:explicit ChannelSelectionState(HSMState* parent = nullptr) : HSMState(parent), m_currentMode(BROWSE_MODE) {}protected:void onEntry(void* param) override {qDebug() << "Entering Channel Selection State";}void onExit(void* param) override {qDebug() << "Exiting Channel Selection State";}HSMUtilityDef::HSM_EVENT unhandledEvent(HSMUtilityDef::HSM_EVENT event, void* param) override {switch (event) {case HSMUtilityDef::CHANNEL_UP:channelUp();break;case HSMUtilityDef::CHANNEL_DOWN:channelDown();break;case HSMUtilityDef::MODE_SWITCH:modeSwitch();break;default:return HSMState::unhandledEvent(event, param);}return HSMUtilityDef::HSME_NULL;}private:enum Mode {BROWSE_MODE,LOCK_MODE};Mode m_currentMode;void channelUp() {if (m_currentMode == BROWSE_MODE) {qDebug() << "Channel Up in Browse Mode";} else if (m_currentMode == LOCK_MODE) {qDebug() << "Channel Up in Lock Mode";}}void channelDown() {if (m_currentMode == BROWSE_MODE) {qDebug() << "Channel Down in Browse Mode";} else if (m_currentMode == LOCK_MODE) {qDebug() << "Channel Down in Lock Mode";}}void modeSwitch() {if (m_currentMode == BROWSE_MODE) {m_currentMode = LOCK_MODE;qDebug() << "Switched to Lock Mode";} else {m_currentMode = BROWSE_MODE;qDebug() << "Switched to Browse Mode";}}
};// 音量控制状态(Volume Control)
class VolumeControlState : public HSMState {Q_OBJECTpublic:explicit VolumeControlState(HSMState* parent = nullptr) : HSMState(parent) {}protected:void onEntry(void* param) override {qDebug() << "Entering Volume Control State";}void onExit(void* param) override {qDebug() << "Exiting Volume Control State";}HSMUtilityDef::HSM_EVENT unhandledEvent(HSMUtilityDef::HSM_EVENT event, void* param) override {switch (event) {case HSMUtilityDef::VOLUME_UP:volumeUp();break;case HSMUtilityDef::VOLUME_DOWN:volumeDown();break;default:return HSMState::unhandledEvent(event, param);}return HSMUtilityDef::HSME_NULL;}private:void volumeUp() {qDebug() << "Volume Up";}void volumeDown() {qDebug() << "Volume Down";}
};
定义层次状态机类

创建一个管理状态转换的主类 HSM

class HSM : public QObject {Q_OBJECTpublic:explicit HSM(QObject* parent = nullptr) : QObject(parent), m_currentState(nullptr) {}void start(HSMState* initialState) {if (m_currentState == nullptr) {initialize(initialState);}}void processEvent(HSMUtilityDef::HSM_EVENT event, void* param = nullptr) {if (m_currentState != nullptr) {HSMState* nextState = m_currentState;HSMUtilityDef::HSM_EVENT nextEvent = event;// 处理事件,直到返回 HSME_NULLwhile (nextEvent != HSMUtilityDef::HSME_NULL) {nextState = processSingleEvent(nextState, nextEvent, param);nextEvent = nextState->handleEvent(event, param);}}}private:HSMState* m_currentState;void initialize(HSMState* initialState) {if (initialState != nullptr) {// 初始化状态栈QVector<HSMState*> stateStack;while (initialState != nullptr) {stateStack.append(initialState);initialState = initialState->parent();}// 从顶层状态开始初始化for (int i = stateStack.size() - 1; i >= 0; --i) {HSMState* currentState = stateStack[i];currentState->handleEvent(HSMUtilityDef::HSME_ENTRY, nullptr);}m_currentState = stateStack.last();}}HSMState* processSingleEvent(HSMState* currentState, HSMUtilityDef::HSM_EVENT event, void* param) {switch (event) {case HSMUtilityDef::HSME_INIT:return initializeChildStates(currentState);case HSMUtilityDef::HSME_ENTRY:currentState->onEntry(param);break;case HSMUtilityDef::HSME_EXIT:currentState->onExit(param);return processSingleEvent(currentState->parent(), HSMUtilityDef::HSME_EXIT, param);}return currentState;}HSMState* initializeChildStates(HSMState* parentState) {if (parentState == nullptr) {return nullptr;}QVector<HSMState*> childStates = findInitialStates(parentState);for (HSMState* state : childStates) {processSingleEvent(state, HSMUtilityDef::HSME_ENTRY, nullptr);}return childStates.last();}QVector<HSMState*> findInitialStates(HSMState* parentState) const {// 在实际应用中,可能需要更复杂的逻辑来确定初始子状态// 这里简单地假设每个父状态只有一个直接的初始子状态QVector<HSMState*> children;QObjectList childObjects = parentState->children();for (QObject* obj : childObjects) {HSMState* state = qobject_cast<HSMState*>(obj);if (state != nullptr) {children.append(state);}}// 返回第一个子状态作为初始状态return children;}
};
构建和运行状态机

main 函数中构建并运行层次状态机:

#include <QCoreApplication>
#include <QDebug>int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);// 创建状态对象StandbyState* standby = new StandbyState();RunningState* running = new RunningState(standby);ChannelSelectionState* channelSel = new ChannelSelectionState(running);VolumeControlState* volumeCtrl = new VolumeControlState(running);// 构建层次结构running->setParent(standby);channelSel->setParent(running);volumeCtrl->setParent(running);// 创建状态机并启动HSM hsm;hsm.start(standby);// 处理事件hsm.processEvent(HSMUtilityDef::POWER);          // 切换到运行模式hsm.processEvent(HSMUtilityDef::CHANNEL_UP);       // 选择频道向上hsm.processEvent(HSMUtilityDef::MODE_SWITCH);      // 切换到锁定模式hsm.processEvent(HSMUtilityDef::VOLUME_UP);        // 增加音量hsm.processEvent(HSMUtilityDef::POWER);          // 关闭电视return a.exec();
}

4. 使用 Qt 的信号和槽机制增强状态机

Qt 提供了强大的信号和槽机制,可以用来进一步简化状态机的设计和实现。以下是如何将 Qt 的信号和槽与层次状态机结合使用的方法。

修改 HSMState 类以支持信号和槽

HSMState 中添加信号来通知状态转换或动作执行:

#include <QObject>
#include <QVector>
#include <QDebug>namespace HSMUtilityDef {typedef uint32_t HSM_EVENT;// 定义常见事件const uint32_t MAX_DEPTH = 5;const HSM_EVENT HSME_NULL = 0;const HSM_EVENT HSME_START = 1;const HSM_EVENT HSME_INIT = static_cast<HSM_EVENT>(-3);const HSM_EVENT HSME_ENTRY = static_cast<HSM_EVENT>(-2);const HSM_EVENT HSME_EXIT = static_cast<HSM_EVENT>(-1);// 自定义事件const HSM_EVENT POWER = 100;const HSM_EVENT CHANNEL_UP = 101;const HSM_EVENT CHANNEL_DOWN = 102;const HSM_EVENT VOLUME_UP = 103;const HSM_EVENT VOLUME_DOWN = 104;const HSM_EVENT MODE_SWITCH = 105;
}class HSMState : public QObject {Q_OBJECTpublic:explicit HSMState(HSMState *parent = nullptr) : QObject(parent), m_parent(parent) {}virtual ~HSMState() {}// 处理事件的主要接口virtual HSMUtilityDef::HSM_EVENT handleEvent(HSMUtilityDef::HSM_EVENT event, void* param = nullptr) {switch (event) {case HSMUtilityDef::HSME_ENTRY:onEntry(param);break;case HSMUtilityDef::HSME_EXIT:onExit(param);break;default:return unhandledEvent(event, param);}return HSMUtilityDef::HSME_NULL;}signals:// 信号用于通知状态转换或动作执行void stateEntered(HSMState* state);void stateExited(HSMState* state);protected:// 进入状态时执行的动作virtual void onEntry(void* param) {emit stateEntered(this);}// 离开状态时执行的动作virtual void onExit(void* param) {emit stateExited(this);}// 处理未定义事件的方法virtual HSMUtilityDef::HSM_EVENT unhandledEvent(HSMUtilityDef::HSM_EVENT event, void* param) {qDebug() << "Unhandled event" << event;return event;}// 获取父状态HSMState* parent() const { return m_parent; }private:HSMState *m_parent;
};

相关文章:

【开发语言】层次状态机(HSM)介绍

层次状态机&#xff08;Hierarchical State Machine, HSM&#xff09;&#xff0c;从基本原理、结构设计、实现方法以及如何结合 Qt 进行具体实现等方面进行分析。 1. 层次状态机的基本原理 层次状态机是一种用于管理复杂系统行为的状态机模型&#xff0c;它通过将状态组织成…...

03-13、SpringCloud Alibaba第十三章,升级篇,服务降级、熔断和限流Sentinel

SpringCloud Alibaba第十三章&#xff0c;升级篇&#xff0c;服务降级、熔断和限流Sentinel 一、Sentinel概述 1、Sentinel是什么 随着微服务的流行&#xff0c;服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点&#xff0c;从流量控制、熔断降级、系统负载保…...

【k8s 深入学习之 event 聚合】event count累记聚合(采用 Patch),Message 聚合形成聚合 event(采用Create)

参考 15.深入k8s:Event事件处理及其源码分析 - luozhiyun - 博客园event 模块总览 EventRecorder:是事件生成者,k8s组件通过调用它的方法来生成事件;EventBroadcaster:事件广播器,负责消费EventRecorder产生的事件,然后分发给broadcasterWatcher;broadcasterWatcher:用…...

leetcode104.二叉树的最大深度

给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;3示例 2&#xff1a; 输入&#xff1a;root [1,null,2] 输出…...

蓝桥杯2117砍竹子(简单易懂 包看包会版)

问题描述 这天, 小明在砍竹子, 他面前有 n 棵竹子排成一排, 一开始第 i 棵竹子的 高度为 hi​. 他觉得一棵一棵砍太慢了, 决定使用魔法来砍竹子。魔法可以对连续的一 段相同高度的竹子使用, 假设这一段竹子的高度为 H, 那么 用一次魔法可以 把这一段竹子的高度都变为 ⌊H2⌋…...

LCD与lvgl

LCD与lvgl 目录 LCD与lvgl 回顾 LCD 的驱动层讲解 1、LCD 的常见接口 2、我们的 LCD 的参数 3、LCD 的设备树说明 4、LCD 的设备树说明 5、如何移植 LCD 的驱动(重点) LCD 的应用层开发 1:LCD 应用开发->界面开发的方法 2:LVGL 模拟器安装 3:LVGL 工程创建和…...

SpringBoot 赋能:精铸超稳会员制医疗预约系统,夯实就医数据根基

1绪论 1.1开发背景 传统的管理方式都在使用手工记录的方式进行记录&#xff0c;这种方式耗时&#xff0c;而且对于信息量比较大的情况想要快速查找某一信息非常慢&#xff0c;对于会员制医疗预约服务信息的统计获取比较繁琐&#xff0c;随着网络技术的发展&#xff0c;采用电脑…...

android studio 读写文件操作(应用场景二)

android studio版本&#xff1a;2023.3.1 patch2 例程&#xff1a;readtextviewIDsaveandread 本例程是个过渡例程&#xff0c;如果单是实现下图的目的有更简单的方法&#xff0c;但这个方法是下一步工作的基础&#xff0c;所以一定要做。 例程功能&#xff1a;将两个textvi…...

小尺寸低功耗蓝牙模块在光伏清扫机器人上的应用

一、引言 随着可再生能源的迅速发展&#xff0c;光伏发电系统的清洁与维护变得越来越重要。光伏清扫机器人通过自动化技术提高了清洁效率&#xff0c;而蓝牙模组的集成为这些设备提供了更为智能的管理和控制方案。 二、蓝牙模组的功能与实现&#xff1a; 蓝牙模组ANS-BT103M…...

防火墙有什么作用

防火墙的作用&#xff1a;1. 提供网络安全防护&#xff1b;2. 实施访问控制和流量过滤&#xff1b;3. 检测和阻止恶意攻击&#xff1b;4. 保护内部网络免受未经授权的访问&#xff1b;5. 监控网络流量和安全事件&#xff1b;6. 支持虚拟专用网络&#xff08;VPN&#xff09;。防…...

MongoDB-BSON 协议与类型

前言&#xff1a; MongoDB 是一个高性能、无模式的 NoSQL 数据库&#xff0c;广泛应用于大数据处理和实时数据存储。作为一个数据库系统&#xff0c;MongoDB 的核心之一就是其使用的 BSON&#xff08;Binary JSON&#xff09;格式&#xff0c;它用于存储数据以及在客户端和数据…...

学习threejs,使用VideoTexture实现视频Video更新纹理

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️VideoTexture 视频纹理 二、…...

怎么获取键值对的键的数值?

问&#xff1a; 通过paelData.cardMap.C0002112可以获取到Cooo2112里面的数据&#xff0c;但是有时候接口返回的不是C0002112而是C0002093或者其他值&#xff0c;请问我该怎么写&#xff1f; 后端返回的数据是这样的&#xff1a; cardMap: { C0002112: { name: Item 1, va…...

数据结构排序算法详解

数据结构排序算法详解 1、冒泡排序&#xff08;Bubble Sort&#xff09;2、选择排序&#xff08;Selection Sort&#xff09;2、插入排序&#xff08;Insertion Sort&#xff09; 1、冒泡排序&#xff08;Bubble Sort&#xff09; 原理&#xff1a;越小的元素会慢慢“浮”到数…...

在Linux设置postgresql开机自启动,创建一个文件 postgresql-15.service

在Linux设置postgresql开机自启动&#xff0c;创建一个文件 postgresql-15.service 在Linux设置postgresql开机自启动&#xff0c;创建一个文件 postgresql-15.service1. 创建 systemd 服务文件2. 编辑服务文件3. 保存并退出4. 重新加载 systemd 配置5. 启动 PostgreSQL 服务6.…...

【kafka】消息队列的认识,Kafka与RabbitMQ的简单对比

什么是消息队列&#xff1f; 消息队列&#xff08;Message Queue&#xff0c;简称 MQ&#xff09;是一个在不同应用程序、系统或服务之间传递数据的机制。 它允许系统间异步地交换信息&#xff0c;而无需直接交互&#xff0c;确保消息的可靠传输。 想象一下&#xff0c;你正在…...

ProjectSend 身份认证绕过漏洞复现(CVE-2024-11680)

0x01 产品描述: ProjectSend 是一个开源文件共享网络应用程序,旨在促进服务器管理员和客户端之间的安全、私密文件传输。它是一款相当流行的应用程序,被更喜欢自托管解决方案而不是 Google Drive 和 Dropbox 等第三方服务的组织使用。0x02 漏洞描述: ProjectSend r1720 之前…...

Android笔记(三十四):onCreate执行Handler.post在onResume后才能执行?

背景 偶然发现一个点&#xff0c;就是在onCreate执行Handler.post在onResume后才执行&#xff0c;以下是测试代码 多次运行的结果一致&#xff0c;为什么execute runnable不是在onCreate和onResume之间执行的呢&#xff0c;带着疑问撸了一遍Activity启动流程 关键源码分析 …...

关闭模组的IP过滤功能

关闭模组的IP过滤功能 关闭模组的IP过滤功能 本脚本用于关闭模组的IP过滤功能&#xff0c;关闭后&#xff0c; 源地址不是终端IP的数据包&#xff0c;也可以被模组转发给网络 关闭模组的IP过滤功能 cat > /usr/bin/ipfilter << "EOF"echo -e "ATCFUN…...

算法分析与设计复习笔记

插入排序 1. void insert_sort(int A[ ],int n) 2. { 3. int a,i,j; 4. for (i1;i<n;i) { 5. a A[ i ]; 6. j i – 1; 7. while (j>0 && A[j]>a) { 8. A[ j…...

vue-amap 高德地图

vue-amap是一套基于Vue 2/vue3和高德地图的地图组件 vue-amap 高德地图2.0版本的对应vue3...

Numpy基础练习

import numpy as np 1.创建一个长度为10的一维全为0的ndarray对象,然后让第5个元素等于1 n np.zeros(10,dtypenp.int32) n[4] 12.创建一个元素从10到49的ndarray对象 n np.arrange(10,50)3.将第2题的所有元素位置反转 n[::-1]使用np.random.random创建一个10*10的ndarray对象…...

一番赏小程序定制开发,打造全新抽赏体验平台

随着盲盒的热潮来袭&#xff0c;作为传统的潮玩方式一番赏也再次受到了大家的关注&#xff0c;市场热度不断上升&#xff01; 一番赏能够让玩家百分百中奖&#xff0c;商品种类丰富、收藏价值高&#xff0c;拥有各种IP&#xff0c;从而吸引着各个圈子的粉丝玩家&#xff0c;用…...

【前端】将vue的方法挂载到window上供全局使用,也方便跟原生js做交互

【前端】将vue的方法挂载到window上供全局使用&#xff0c;也方便跟原生js做交互 <template><div><el-button click"start">调用方法</el-button></div> </template> <script> // import { JScallbackProc } from ./JScal…...

Oracle查询优化:高效实现仅查询前10条记录的方法与实践

在 Oracle 中&#xff0c;实现仅查询前10条记录的四种方法 1. 使用 ROWNUM 查询 ROWNUM 是 Oracle 中的伪列&#xff0c;用于限制返回的行数。 SELECT * FROM table_name WHERE condition AND ROWNUM < 10;condition&#xff1a;查询条件。ROWNUM < 10&#xff1a;限制…...

go语言编译问题

go编译 goproxy地址 阿里云 https://mirrors.aliyun.com/goproxy/七牛云 https://goproxy.cn/开源版 https://goproxy.io/nexus社区 https://gonexus.dev/启用 Go Modules 功能 go env -w GO111MODULEon配置 GOPROXY 环境变量&#xff0c;以下三选一 七牛 CDN go env -w …...

mobi文件转成pdf

将 MOBI 文件转换为 PDF 格式通常涉及两个步骤&#xff1a; 解析 MOBI 文件&#xff1a;需要提取 MOBI 文件的内容&#xff08;文本、图片等&#xff09;。将提取的内容转换为 PDF&#xff1a;将 MOBI 文件的内容渲染到 PDF 格式。 可用工具 kindleunpack 或 mobi&#xff1…...

MobaXterm解决中文显示乱码问题

1 问题 打开MobaXterm时&#xff0c;会显示中文乱码。 2 解决方法 右键点击会话&#xff0c;在弹出菜单中选择“编辑会话”&#xff0c;如下&#xff1a; 选择终端字体设置&#xff0c;如下&#xff1a; 字符集换成ISO-8859-1&#xff0c;如下&#xff1a; 网上有说用…...

西门子 SINAMICS G120 变频器借助 ProfiNet 转 EtherCAT 实现与汇川 H5U 通讯实例

一&#xff0e; 案例背景 随着智能制造理念的推进&#xff0c;设备之间的协同工作变得越来越重要。例如&#xff0c;在机器人自动化焊接生产线中&#xff0c;电机驱动的焊接机器人需要与其他设备协同工作&#xff0c;这就要求负责电机控制的变频器和控制整个生产线流程的PLC能…...

流媒体之linux下离线部署FFmpeg 和 SRS

前言 用户对网络做了限制&#xff0c;只能访问指定的网址&#xff0c;和没网没啥区别&#xff0c;导致无法连接外网&#xff0c;无法获取安装包&#xff0c;还有一些编译需要的开源工具 用户需要用平台查看库房的海康摄像头实时监控&#xff0c;只能在库房里一台纯净的ubantu…...