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

spring状态机

1、概述

        Spring State Machine 是一个用于处理状态机逻辑的框架,它提供了一种简洁的方法来定义状

态、转换以及在状态变更时触发的动作。

概念

  • 状态  State  :一个状态机至少要包含两个状态。例如自动门的例子,有 open 和 closed 两个状态。

  • 事件  Event   :事件就是执行某个操作的触发条件或者口令。对于自动门,“按下开门按钮”就是一个事件。

  • 动作  Action   :事件发生以后要执行动作。例如事件是“按开门按钮”,动作是“开门”。编程的时候,一个 Action一般就对应一个函数。

  • 转换  Transition  :也就是从一个状态变化为另一个状态。例如“开门过程”就是一个转换。

  • 守卫(Guard) :一种条件逻辑,用于决定是否可以进行某个状态转换。守卫可以基于应用程序的当前状态或其他条件来确定转换是否应该发生。

状态机

        有限状态机(Finite-state machine,FSM),又称有限状态自动机,简称状态机,是表示有限

个状态以及在这些状态之间的转移和动作等行为的数学模型。FSM是一种算法思想,简单而言,有

限状态机由一组状态、一个初始状态、输入和根据输入及现有状态转换为下一个状态的转换函数组

成。其作用主要是描述对象在它的生命周期内所经历的状态序列,以及如何响应来自外界的各种事

件。

地址

官方地址:

Spring Statemachine

文档地址

Spring Statemachine - Reference Documentation

github地址

https://github.com/spring-projects/spring-statemachine

2、状态机图

        做需求时,需要了解以下六种元素:起始、终止、现态、次态(目标状态)、动作、条件,

我们就可以完成一个状态机图了,以订单为例:通过支付事件,订单状态从待支付状态转换为待发货状态。

  1. 现态:是指当前所处的状态。待支付。
  2. 条件:又称为“事件”,当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移。支付事件。
  3. 动作:条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。状态转换为待发货。
  4. 次态:条件满足后要迁往的新状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了。待发货。

注意事项:

  1. 避免把某个“程序动作”当作是一种“状态”来处理。那么如何区分“动作”和“状态”?“动作”是不稳定的,即使没有条件的触发,“动作”一旦执行完毕就结束了;而“状态”是相对稳定的,如果没有外部条件的触发,一个状态会一直持续下去。
  2. 状态划分时漏掉一些状态,导致跳转逻辑不完整。所以在设计状态机时,我们需要反复的查看设计的状态图或者状态表,最终达到一种牢不可破的设计方案。

3、代码

3.1 依赖

 <!--spring 状态机start-->
<dependency><groupId>org.springframework.statemachine</groupId><artifactId>spring-statemachine-core</artifactId><version>${spring-statemachine.version}</version>
</dependency>
<!-- redis持久化状态机 -->
<dependency><groupId>org.springframework.statemachine</groupId><artifactId>spring-statemachine-data-redis</artifactId><version>${spring-statemachine.version}</version>
</dependency>
<!--spring 状态机end-->
<!--redis start-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--redis end-->

3.2 定义状态和事件

package com.ybw.constant;/*** 状态** @author weixiansheng* @version V1.0* @className RegStatusEnum* @date 2023/12/26**/
public enum RegStatusEnum {// 未连接UNCONNECTED,// 已连接CONNECTED,// 注册中REGISTERING,// 已注册REGISTERED;
}
package com.ybw.constant;/*** 事件** @author weixiansheng* @version V1.0* @className RegEventEnum* @date 2023/12/26**/
public enum RegEventEnum {// 连接CONNECT,// 注册REGISTER,// 注册成功REGISTER_SUCCESS,// 注册失败REGISTER_FAILED,// 注销UN_REGISTER;
}

3.3 配置状态机

创建一个配置类来配置状态机。在这个配置中,我们定义状态转换逻辑。

package com.ybw.config;import com.ybw.constant.RegEventEnum;
import com.ybw.constant.RegStatusEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.config.EnableStateMachine;
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;import java.util.EnumSet;/*** @author weixiansheng* @version V1.0* @className StateMachineConfig* @date 2023/12/25**/
@Configuration
@EnableStateMachine
@Slf4j
public class StateMachineConfig extends EnumStateMachineConfigurerAdapter<RegStatusEnum, RegEventEnum> {/*** 配置状态** @param states* @methodName: configure* @return: void* @author: weixiansheng* @date: 2023/12/25**/@Overridepublic void configure(StateMachineStateConfigurer<RegStatusEnum, RegEventEnum> states) throws Exception {states.withStates()// 定义初始状态.initial(RegStatusEnum.UNCONNECTED)// 定义状态机状态.states(EnumSet.allOf(RegStatusEnum.class));}/*** 配置状态转换事件关系** @param transitions* @methodName: configure* @return: void* @author: weixiansheng* @date: 2023/12/25**/@Overridepublic void configure(StateMachineTransitionConfigurer<RegStatusEnum, RegEventEnum> transitions) throws Exception {transitions// 1.连接事件// 未连接 -> 已连接.withExternal().source(RegStatusEnum.UNCONNECTED).target(RegStatusEnum.CONNECTED).event(RegEventEnum.CONNECT).and()// 2.注册事件// 已连接 -> 注册中.withExternal().source(RegStatusEnum.CONNECTED).target(RegStatusEnum.REGISTERING).event(RegEventEnum.REGISTER).and()// 3.注册成功事件// 注册中 -> 已注册.withExternal().source(RegStatusEnum.REGISTERING).target(RegStatusEnum.REGISTERED).event(RegEventEnum.REGISTER_SUCCESS).and()// 5.注销事件// 已连接 -> 未连接.withExternal().source(RegStatusEnum.CONNECTED).target(RegStatusEnum.UNCONNECTED).event(RegEventEnum.UN_REGISTER).and()// 注册中 -> 未连接.withExternal().source(RegStatusEnum.REGISTERING).target(RegStatusEnum.UNCONNECTED).event(RegEventEnum.UN_REGISTER).and()// 已注册 -> 未连接.withExternal().source(RegStatusEnum.REGISTERED).target(RegStatusEnum.UNCONNECTED).event(RegEventEnum.UN_REGISTER);}
}

3.4 状态机的转换事件配置

package com.ybw.config;import com.alibaba.fastjson2.JSON;
import com.ybw.constant.RegEventEnum;
import com.ybw.entity.Order;
import lombok.extern.slf4j.Slf4j;
import org.springframework.messaging.Message;
import org.springframework.statemachine.annotation.OnTransition;
import org.springframework.statemachine.annotation.WithStateMachine;/*** 状态机的转换事件配置** @author weixiansheng* @version V1.0* @className StateMachineEventConfig* @date 2023/12/26**/
@WithStateMachine
@Slf4j
public class StateMachineEventConfig {/*** 连接事件** @param message* @methodName: connect* @return: void* @author: weixiansheng* @date: 2023/12/26**/@OnTransition(source = "UNCONNECTED", target = "CONNECTED")public void connect(Message<RegEventEnum> message) {Order order = (Order) message.getHeaders().get("order");log.info("///");log.info("连接事件, 未连接 -> 已连接,order:{}", JSON.toJSONString(order));log.info("///");}/*** 注册事件** @param message* @methodName: register* @return: void* @author: weixiansheng* @date: 2023/12/26**/@OnTransition(source = "CONNECTED", target = "REGISTERING")public void register(Message<RegEventEnum> message) {log.info("///");log.info("注册事件, 已连接 -> 注册中");log.info("///");}/*** 注册成功事件** @param message* @methodName: registerSuccess* @return: void* @author: weixiansheng* @date: 2023/12/26**/@OnTransition(source = "REGISTERING", target = "REGISTERED")public void registerSuccess(Message<RegEventEnum> message) {log.info("///");log.info("注册成功事件, 注册中 -> 已注册");log.info("///");}/*** 注销事件** @param message* @methodName: unRegister* @return: void* @author: weixiansheng* @date: 2023/12/26**/@OnTransition(source = "REGISTERED", target = "UNCONNECTED")public void unRegister(Message<RegEventEnum> message) {log.info("///");log.info("注销事件, 已注册 -> 未连接");log.info("///");}
}

3.6 Redis持久化

持久化到redis中,在分布式系统中使用。

package com.ybw.config;import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.statemachine.data.redis.RedisStateMachineContextRepository;
import org.springframework.statemachine.data.redis.RedisStateMachinePersister;
import org.springframework.statemachine.persist.RepositoryStateMachinePersist;/*** 持久化** @author weixiansheng* @version V1.0* @className Persist* @date 2023/12/26**/
@Configuration
@Slf4j
public class Persist<E, S> {@Resourceprivate RedisConnectionFactory redisConnectionFactory;/*** 持久化到redis中,在分布式系统中使用** @methodName: getRedisPersister* @return: org.springframework.statemachine.data.redis.RedisStateMachinePersister<E, S>* @author: weixiansheng* @date: 2023/12/26**/@Bean(name = "stateMachineRedisPersister")public RedisStateMachinePersister<E, S> getRedisPersister() {RedisStateMachineContextRepository<E, S> repository = new RedisStateMachineContextRepository<>(redisConnectionFactory);RepositoryStateMachinePersist<E, S> p = new RepositoryStateMachinePersist<>(repository);return new RedisStateMachinePersister<>(p);}
}

3.6 测试

package com.ybw.state;import com.ybw.constant.RegEventEnum;
import com.ybw.constant.RegStatusEnum;
import com.ybw.entity.Order;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.data.redis.RedisStateMachinePersister;
import org.springframework.statemachine.persist.StateMachinePersister;/*** @author weixiansheng* @version V1.0* @className StateTest* @date 2023/12/26**/
@SpringBootTest
@Slf4j
public class StateTest {@Resourceprivate StateMachine<RegStatusEnum, RegEventEnum> stateMachine;@Resource(name = "stateMachineRedisPersister")private RedisStateMachinePersister<RegStatusEnum, RegEventEnum> persister;/*** @MethodName: testState* @Description:* @Param: []* @Return: void* @Author: ybwei* @Date: 2020/3/26**/@Testpublic void testState() {try {Order order = Order.builder().id(1L).name("张三").status(RegStatusEnum.CONNECTED).build();stateMachine.start();//尝试恢复状态机状态(read)persister.restore(stateMachine, order.getId().toString());Message<RegEventEnum> message = MessageBuilder.withPayload(RegEventEnum.CONNECT).setHeader("order", order).build();stateMachine.sendEvent(message);//持久化状态机状态(write)persister.persist(stateMachine, order.getId().toString());} catch (Exception e) {log.error("testState error:", e);}
//        stateMachine.sendEvent(RegEventEnum.REGISTER);
//        stateMachine.sendEvent(RegEventEnum.REGISTER_SUCCESS);
//        stateMachine.sendEvent(RegEventEnum.UN_REGISTER);
//
//        stateMachine.sendEvent(RegEventEnum.CONNECT);}
}

 执行结果:

第一次执行

[INFO ] 2023-12-26 17:35:20.563 [main] o.s.s.support.AbstractStateMachine - Got null context, resetting to initial state, clearing extended state and machine id
[INFO ] 2023-12-26 17:35:20.593 [main] c.ybw.config.StateMachineEventConfig - ///
[INFO ] 2023-12-26 17:35:20.688 [main] c.ybw.config.StateMachineEventConfig - 连接事件, 未连接 -> 已连接,order:{"id":1,"name":"张三","status":"CONNECTED"}
[INFO ] 2023-12-26 17:35:20.690 [main] c.ybw.config.StateMachineEventConfig - ///

第二次执行,没有日志打印。

修改代码

将RegEventEnum.CONNECT改为RegEventEnum.REGISTER

@Test
public void testState() {try {Order order = Order.builder().id(1L).name("张三").status(RegStatusEnum.CONNECTED).build();stateMachine.start();//尝试恢复状态机状态(read)persister.restore(stateMachine, order.getId().toString());Message<RegEventEnum> message = MessageBuilder.withPayload(RegEventEnum.REGISTER).setHeader("order", order).build();stateMachine.sendEvent(message);//持久化状态机状态(write)persister.persist(stateMachine, order.getId().toString());} catch (Exception e) {log.error("testState error:", e);}
}

第三次执行,打印日志

[INFO ] 2023-12-26 17:38:31.307 [main] c.ybw.config.StateMachineEventConfig - ///
[INFO ] 2023-12-26 17:38:31.307 [main] c.ybw.config.StateMachineEventConfig - 注册事件, 已连接 -> 注册中
[INFO ] 2023-12-26 17:38:31.307 [main] c.ybw.config.StateMachineEventConfig - ///

 总结:

  • 事件发生后,会执行StateMachineEventConfig相关逻辑。
  • 持久化状态机状态。
  • 相同的事件再次发生,不会执行StateMachineEventConfig相关逻辑。

3.7 源码地址

share: 分享仓库 - Gitee.com

相关文章:

spring状态机

1、概述 Spring State Machine 是一个用于处理状态机逻辑的框架&#xff0c;它提供了一种简洁的方法来定义状 态、转换以及在状态变更时触发的动作。 概念 状态 &#xff08; State &#xff09; &#xff1a;一个状态机至少要包含两个状态。例如自动门的例子&#xff0c;有 …...

K8S异常处理

一、概述 1、k8s有时候会报错The connection to the server ip:6443 was refused - did you specify the right host or port &#xff0c;本文档提供几种可能产生该报错的原因和排障思路。 二、发现问题 使用任意Kubectl 命令会报错&#xff1a;The connection to the serv…...

【挑战全网最易懂】深度强化学习 --- 零基础指南

深度强化学习介绍、概念 强化学习介绍离散场景&#xff0c;使用行为价值方法连续场景&#xff0c;使用概率分布方法实时反馈连续场景&#xff1a;使用概率分布 行为价值方法 强化学习六要素设计奖励函数设计评论家策略学习与优化 算法路径深度 Q 网络 DQN演员-评论家算法&…...

WPF RelativeSource

RelativeSource 类在 WPF 中提供了以下几种模式&#xff1a; RelativeSource Self&#xff1a;指定当前元素作为相对源。可以在当前元素的属性中绑定到自身的属性。 示例&#xff1a; <TextBlock Text"{Binding Text, RelativeSource{RelativeSource Self}}" /&…...

centos 安装 配置 zsh

centos 编译安装 zsh 和 配置 oh-my-zsh 下载 wget https://jaist.dl.sourceforge.net/project/zsh/zsh/5.9/zsh-5.9.tar.xz依赖 yum install ncurses-devel安装zsh 执行&#xff1a; tar -xvf zsh-5.9.tar.xz cd zsh-5.9 ./configure --prefix/usr/local/zsh5.9 make &am…...

git 常用基本命令, reset 回退撤销commit,解决gitignore无效,忽略记录或未记录远程仓库的文件,删除远程仓库文件

git 基本命令 reset 撤销commit https://blog.csdn.net/a704397849/article/details/135220091 idea 中 rest 撤销commit过程如下&#xff1a; Git -> Rest Head… 在To Commit中的HEAD后面加上^&#xff0c;点击Reset即可撤回最近一次的尚未push的commit Reset Type 有三…...

Vue Echarts 多折线图只有一条X轴(合并X轴数据并去重排序) - 附完整示例

echarts&#xff1a;一个基于 JavaScript 的开源可视化图表库。 目录 效果 一、介绍 1、官方文档&#xff1a;Apache ECharts 2、官方示例 二、准备工作 1、安装依赖包 2、示例版本 三、使用步骤 1、在单页面引入 echarts 2、指定容器并设置容器宽高 3、数据处理&am…...

WPF+Halcon 培训项目实战(6):目标匹配助手

文章目录 前言相关链接项目专栏模板匹配助手简单使用金字塔级别参数自动选择应用插入代码 总结 前言 为了更好地去学习WPFHalcon&#xff0c;我决定去报个班学一下。原因无非是想换个工作。相关的教学视频来源于下方的Up主的提供的教程。这里只做笔记分享&#xff0c;想要源码…...

Linux管理LVM逻辑卷

目录 一、LVM逻辑卷介绍 1. 概述 2. LVM基本术语 2.1 PV&#xff08;Physical Volume&#xff0c;物理卷&#xff09; 2.2 VG (Volume Group&#xff0c;卷组&#xff09; 2.3 LV (Logical Volume&#xff0c;逻辑卷&#xff09; 3. 常用的磁盘命令 4. 查看系统信息的命…...

vue如何实现局部刷新?

应用场景&#xff1a; 比如你要切换tap栏实现刷新下面form表单等&#xff0c;相当于刷新页面。 如何使用如下&#xff1a; <div v-if"isReloadData"> 比如你想刷新那个位置就把 v-if"isReloadData"写到那个标签上 </div> 在data中定义刷新标…...

C语言,指针链表详解解说及代码示例

C语言&#xff0c;指针链表详解解说及代码示例 指针链表是一种常用的数据结构&#xff0c;用于存储和组织数据。它由一系列节点组成&#xff0c;每个节点包含数据和一个指向下一个节点的指针。通过这种方式&#xff0c;可以动态地添加、删除和访问节点&#xff0c;实现灵活的数…...

6、LLaVA

简介 LLaVA官网 LLaVA使用Vicuna(LLaMA-2)作为LLM f ϕ ( ⋅ ) f_\phi() fϕ​(⋅)&#xff0c;使用预训练的CLIP图像编码器 ViT-L/14 g ( X v ) g(X_v) g(Xv​)。 输入图像 X v X_v Xv​&#xff0c;首先获取feature Z v g ( X v ) Z_vg(X_v) Zv​g(Xv​)。考虑到最后一…...

SpringMVC核心处理流程梳理

1、处理流程图展示 当我拿出这张图&#xff0c;阁下又该如何应对呢&#xff1f;执行流程是不是一目了然了。【记住一句话&#xff1a;所有的注解都只是一个标签或者标记&#xff0c;最终都是反射找到具体方法上面的注解标记&#xff0c;然后找到类、属性、方法扩展自己想要的功…...

go 语言程序设计第2章--程序结构

2.1 名称 如果一个实体在函数中声明&#xff0c;它只在函数局部有效。如果声明在函数外&#xff0c;它将对包里面所有源文件可见。 实体第一个字母的大小写决定其可见性是否跨包。如果名称以大写字母开头&#xff0c;它是导出的&#xff0c;意味着它对包外是可见和可访问的。包…...

JavaScript基础知识点总结:从零开始学习JavaScript(五)

如果大家感感兴趣也可以去看&#xff1a; &#x1f389;博客主页&#xff1a;阿猫的故乡 &#x1f389;系列专栏&#xff1a;JavaScript专题栏 &#x1f389;ajax专栏&#xff1a;ajax知识点 &#x1f389;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收藏✍️留言 学习…...

Intel FPGA 技术开放日

概要 时间&#xff1a;2023.11.14 全天 &#xff08; 9:00 - 16: 20&#xff09; 地点&#xff1a;北京望京. 凯悦酒店 主题内容&#xff1a;分享交流了Intel FPGA 产品技术优势和落地实践方案。 会议的议程 开场致词&#xff1a; FPGA业务&#xff0c;是几年前intel收购而…...

分享72个Python爬虫源码总有一个是你想要的

分享72个Python爬虫源码总有一个是你想要的 学习知识费力气&#xff0c;收集整理更不易。 知识付费甚欢喜&#xff0c;为咱码农谋福利。 链接&#xff1a;https://pan.baidu.com/s/1v2P4l5R6KT2Ul-oe2SF8cw?pwd6666 提取码&#xff1a;6666 项目名称 10 photo websites…...

Mybatis 动态 SQL - foreach

动态SQL的另一个常见需求是需要迭代一个集合&#xff0c;通常用于构建IN条件。例如&#xff1a; <select id"selectPostIn" resultType"domain.blog.Post">SELECT *FROM POST P<where><foreach item"item" index"index&quo…...

编程笔记 GOLANG基础 001 为什么要学习Go语言

编程笔记 GOLANG基础 001 为什么要学Go语言 一、推荐学习的计算机程序设计语言&#xff08;一&#xff09;、前端设计与编程&#xff1a;htmlcssjavascripttypescript&#xff08;二&#xff09;、C/C语言&#xff08;三&#xff09;、Go语言&#xff08;四&#xff09;、Pytho…...

OrientDB使用教程:全面了解图数据库

图数据库在当今数据处理领域中扮演着越来越重要的角色&#xff0c;而OrientDB作为一种多模型的数据库&#xff0c;具有图数据库、文档数据库和对象数据库的特性&#xff0c;为应对不同场景提供了灵活的解决方案。本教程将简要介绍OrientDB的使用&#xff0c;包括基本概念、安装…...

[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解

突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 ​安全措施依赖问题​ GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

PHP和Node.js哪个更爽?

先说结论&#xff0c;rust完胜。 php&#xff1a;laravel&#xff0c;swoole&#xff0c;webman&#xff0c;最开始在苏宁的时候写了几年php&#xff0c;当时觉得php真的是世界上最好的语言&#xff0c;因为当初活在舒适圈里&#xff0c;不愿意跳出来&#xff0c;就好比当初活在…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建

制造业采购供应链管理是企业运营的核心环节&#xff0c;供应链协同管理在供应链上下游企业之间建立紧密的合作关系&#xff0c;通过信息共享、资源整合、业务协同等方式&#xff0c;实现供应链的全面管理和优化&#xff0c;提高供应链的效率和透明度&#xff0c;降低供应链的成…...

对WWDC 2025 Keynote 内容的预测

借助我们以往对苹果公司发展路径的深入研究经验&#xff0c;以及大语言模型的分析能力&#xff0c;我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际&#xff0c;我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测&#xff0c;聊作存档。等到明…...

高等数学(下)题型笔记(八)空间解析几何与向量代数

目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...

Unit 1 深度强化学习简介

Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库&#xff0c;例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体&#xff0c;比如 SnowballFight、Huggy the Do…...

CMake控制VS2022项目文件分组

我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...

有限自动机到正规文法转换器v1.0

1 项目简介 这是一个功能强大的有限自动机&#xff08;Finite Automaton, FA&#xff09;到正规文法&#xff08;Regular Grammar&#xff09;转换器&#xff0c;它配备了一个直观且完整的图形用户界面&#xff0c;使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...

JAVA后端开发——多租户

数据隔离是多租户系统中的核心概念&#xff0c;确保一个租户&#xff08;在这个系统中可能是一个公司或一个独立的客户&#xff09;的数据对其他租户是不可见的。在 RuoYi 框架&#xff08;您当前项目所使用的基础框架&#xff09;中&#xff0c;这通常是通过在数据表中增加一个…...

Python Ovito统计金刚石结构数量

大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...