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

使用springboot-3.4.1搭建一个netty服务并且WebSocket消息通知(适用于设备直连操作,以及回复操作)

引入最新版本

<!--websocket-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

启动类加入

//netty 协议服务端口启动
NettyTcpHandler.start();
package com.cqcloud.platform.handler;import com.cqcloud.platform.service.IotMqttService;
import com.cqcloud.platform.service.impl.IotMqttServiceImpl;
import org.springframework.stereotype.Component;import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;/*** @author weimeilayer@gmail.com ✨* @date 💓💕 2022年9月23日 🐬🐇 💓💕*/
@Component
public class NettyTcpHandler {/*** IoT设备协议端口*/private static int PORT = 1883;/*** 使用方法在启动类* 加上 NettyTcpHandler.start();* @throws Exception*/public static void start() throws Exception {final NioEventLoopGroup bossGroup = new NioEventLoopGroup();final NioEventLoopGroup workerGroup = new NioEventLoopGroup();try {ServerBootstrap bootstrap = new ServerBootstrap();// 创建 IotPushService 实例IotMqttService iotPushService = new IotMqttServiceImpl();bootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) {ChannelPipeline pipeline = ch.pipeline();// 添加处理器,处理所有连接的业务逻辑pipeline.addLast(new TcpMqttServerHandler(iotPushService));}});// 绑定端口并启动ChannelFuture future = bootstrap.bind(PORT).sync();// 等待服务器关闭future.channel().closeFuture().sync();} finally {// 优雅地关闭线程池workerGroup.shutdownGracefully();bossGroup.shutdownGracefully();}}
}
package com.cqcloud.platform.handler;import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;import cn.hutool.json.JSONObject;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;/*** @author weimeilayer@gmail.com ✨* @date 💓💕 2022年10月06日 🐬🐇 💓💕*/
@Slf4j
@Component
public class TcpEventHandler {// 使用 BiConsumer 来处理两个参数private final static Map<String, BiConsumer<String, String>> eventActions = new HashMap<>();public void registerEventAction(String eventCode, BiConsumer<String, String> action) {// 动态注册事件处理逻辑eventActions.put(eventCode, action);}public static void handleEvent(String evt, String imei, String reportContent) {// 根据事件类型找到对应的处理逻辑,并执行eventActions.getOrDefault(evt, TcpEventHandler::handleUnknownEvent).accept(imei, reportContent);}private static void handleUnknownEvent(String imei, String reportContent) {// 处理未知事件的逻辑System.out.println("imei: " + imei + ", 报告内容: " + reportContent);}public static void handleAlarm(String imei, String reportContent) {log.info("内容: {}", reportContent);// 获取目标用户列表(包括固定的用户 ID)List<String> targetUsers = buildTargetUsersList();// 构建消息并发送sendAlarmMessage(targetUsers, imei, reportContent);}private static List<String> buildTargetUsersList() {List<String> targetUsers = new ArrayList<>();targetUsers.add("1");targetUsers.add("2");//实际根据业务查询数据targetUsers.add("26967563820859392");return targetUsers;}private static void sendAlarmMessage(List<String> targetUsers, String imei, String reportContent) {// 将目标用户列表转换为逗号分隔的字符串String users = String.join(",", targetUsers);// 构建 JSON 消息JSONObject obj = new JSONObject();obj.set("imei", imei);obj.set("message", reportContent);obj.set("userId", users);// 发送消息WebSocketHandler.sendMessageToUser(users, obj.toString());}
}
package com.cqcloud.platform.handler;import java.util.ArrayList;
import java.util.List;
import java.util.Optional;import com.cqcloud.platform.service.IotMqttService;import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;/*** 物联网云平台设备协议* @author weimeilayer@gmail.com ✨* @date 💓💕 2022年9月23日 🐬🐇 💓💕*/
public class TcpMqttServerHandler extends SimpleChannelInboundHandler<ByteBuf>  {// 接口注入private final IotMqttService iotPushService;public TcpMqttServerHandler(IotMqttService iotPushService) {this.iotPushService = iotPushService;}@Overrideprotected void channelRead0(ChannelHandlerContext ctx, ByteBuf in) throws Exception {byte[] byteArray;if (in.readableBytes() <= 0) {in.release();return;}byteArray = new byte[in.readableBytes()];in.readBytes(byteArray);if (byteArray.length <= 0) {in.release();return;}// 将消息传递给 iotPushServiceiotPushService.pushMessageArrived(byteArray);// 下发指令,假设返回的是多个指令List<String> externalValues = extractExternalValue("deviceId");// 转换为十六进制字符串String hexString = bytesToHex(byteArray);System.out.println("来自于物联网云平台设备协议的数据: " + hexString);// 使用 Optional 判断外部值// 使用 Optional 判断外部值,如果不为空则逐一处理每条指令Optional.ofNullable(externalValues).ifPresent(values -> {values.forEach(value -> {// 提取外部数据值并发送System.out.println("来自于物联网云平台设备协议的1885端口的提取外部数据值: " + value);sendResponse(ctx, value);});});}// 辅助方法:将字节数组转换为十六进制字符串private static String bytesToHex(byte[] bytes) {StringBuilder hexString = new StringBuilder();for (byte b : bytes) {String hex = Integer.toHexString(0xFF & b);if (hex.length() == 1) {hexString.append('0'); // 确保每个字节都为两位}hexString.append(hex);}return hexString.toString().toUpperCase(); // 返回大写格式}// 发送响应的统一辅助方法private void sendResponse(ChannelHandlerContext ctx, String hexResponse) {byte[] responseBytes = hexStringToByteArray(hexResponse);ByteBuf responseBuffer = Unpooled.copiedBuffer(responseBytes);ctx.writeAndFlush(responseBuffer);}// 将响应消息转换为字节数组public static byte[] hexStringToByteArray(String s) {int len = s.length();byte[] data = new byte[len / 2];for (int i = 0; i < len; i += 2) {data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16));}return data;}// 查询数据库当前设备号下需要下发的命令private List<String> extractExternalValue(String deviceId) {//这里自行查询数据库数据库,这里只模拟一个list集合ArrayList<Object> list = new ArrayList<>();// 如果记录不为空,获取最新记录的 externalValuelist.stream().findFirst() // 获取最新的一条记录.map(latestRecord -> {// 处理最新记录的逻辑return ""; // 需要返回的值是 externalValue});// 获取最新的一条记录return null; // 如果没有找到,返回 null}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {// 打印异常堆栈跟踪,便于调试和错误排查cause.printStackTrace();// 关闭当前的通道,释放相关资源ctx.close();}
}
package com.cqcloud.platform.handler;import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;import org.springframework.stereotype.Component;import cn.hutool.json.JSONUtil;
import io.micrometer.common.util.StringUtils;
import jakarta.websocket.OnClose;
import jakarta.websocket.OnError;
import jakarta.websocket.OnMessage;
import jakarta.websocket.OnOpen;
import jakarta.websocket.Session;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;/*** @author weimeilayer@gmail.com ✨* @date 💓💕 2022年4月12日 🐬🐇 💓💕*/
@Component
@ServerEndpoint("/websocket/{username}")
public class WebSocketHandler {public static synchronized int getOnlineCount() {return onlineCount;}public static synchronized void addOnlineCount() {WebSocketHandler.onlineCount++;}public static synchronized void subOnlineCount() {WebSocketHandler.onlineCount--;}// 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。private static int onlineCount = 0;// 根据名字存储websocket对象CopyOnWriteArraySet线程安全set,ConcurrentHashMap线程安全mappublic static Map<String, CopyOnWriteArraySet<WebSocketHandler>> webSocketMap = new ConcurrentHashMap<>();// 与某个客户端的连接会话,需要通过它来给客户端发送数据public Session session;// 心跳时间,长时间没心跳踢掉连接public long heartBeatTime;// 初次连接时间,用于控制连接时间过长,踢掉连接public long beginTime;/*** 用户名称*/public String username;/*** 发送消息* @param username* @param message*/public static void sendMessageToUser(String username, String message) {// 检查用户名是否在 map 中存在if (webSocketMap.containsKey(username)) {// 获取该用户的 WebSocketHandler 集合CopyOnWriteArraySet<WebSocketHandler> userHandlers = webSocketMap.get(username);// 遍历该用户的所有连接(每个用户可能有多个 WebSocket 连接)for (WebSocketHandler handler : userHandlers) {// 通过 WebSocketHandler 实例发送消息handler.sendMessageOne(message, username);}} else {System.out.println("并无在线用户: " + username);}}/*** 连接建立成功调用的方法*/@OnOpenpublic void onOpen(@PathParam("username") String username, Session session) {this.username = username;this.session = session;this.heartBeatTime = System.currentTimeMillis();this.beginTime = System.currentTimeMillis();// 登陆用户必须按照用户id 格式登陆if (!"server".equals(username) && username.split(",").length < 3) {return;}// 将用户添加到websocket,支持单用户多出链接if (webSocketMap.containsKey(username)) {webSocketMap.get(username).add(this);} else {CopyOnWriteArraySet websocketSet = new CopyOnWriteArraySet();websocketSet.add(this);webSocketMap.put(username, websocketSet);addOnlineCount(); // 在线数加1}//注释掉 会退出Map<String, Object> messageMap = new ConcurrentHashMap<>();messageMap.put("type", "0");messageMap.put("message", username + "加入8000端口的的当前在线人数为" + getOnlineCount());messageMap.put("to", "all");messageMap.put("users", webSocketMap.keySet());messageMap.put("username", "server");sendMessageAll(JSONUtil.toJsonStr(messageMap));}/*** 发送消息给所有用户** @param message* @throws IOException*/public void sendMessageAll(String message) {for (String key : webSocketMap.keySet()) {for (WebSocketHandler websocket : webSocketMap.get(key)) {websocket.session.getAsyncRemote().sendText(message);}}}/*** 连接关闭调用的方法*/@OnClosepublic void onClose() {if (StringUtils.isNotEmpty(this.username)) {try {if (this.session.isOpen()) {this.session.close();// 强制关闭}webSocketMap.get(username).remove(this);// 删除链接if (webSocketMap.get(username).isEmpty()) {webSocketMap.remove(username);subOnlineCount(); // 在线数减1// 刷新用户列表Map<String, Object> messageMap = new ConcurrentHashMap<>();messageMap.put("type", 0);messageMap.put("message", username + "退出!当前在线人数为" + getOnlineCount());messageMap.put("users", webSocketMap.keySet());sendMessageAll(JSONUtil.toJsonStr(messageMap));}} catch (Exception e) {System.err.println("关闭连接出错 : " + e.getLocalizedMessage());}}}/*** 收到客户端消息后调用的方法** @param message 客户端发送过来的消息*/@OnMessagepublic void onMessage(String message) {// 刷新心跳时间this.heartBeatTime = System.currentTimeMillis();// 群发消息cn.hutool.json.JSONObject messageJson = JSONUtil.parseObj(message);Object type = messageJson.get("type");// 消息类型Object toUser = messageJson.get("to");// 接收对象// 心跳检测if ("999".equals(type)) {Map<String, Object> messageMap = new ConcurrentHashMap<>();messageMap.put("type", "1");messageMap.put("message", "pong");messageMap.put("username", "服务器");messageMap.put("to", this.username);sendMessageOne(JSONUtil.toJsonStr(messageMap), this.username);return;}// 发送消息if ("All".equalsIgnoreCase(type + "")) {sendMessageAll(message);}else {sendMessageOne(message, toUser + "");}}/*** 发生错误时调用*/@OnErrorpublic void onError(Throwable error) {error.printStackTrace();}/*** 发送消息** @param message* @throws IOException*/public void sendMessage(String message){//this.session.getBasicRemote().sendText(message);//同步this.session.getAsyncRemote().sendText(message);// 异步}/*** 发送消息给指定用户** @param message* @param toUserName*/public void sendMessageOne(String message, String toUserName) {webSocketMap.keySet().forEach(e -> {if (e.equals(toUserName)) {webSocketMap.get(e).forEach(f -> {try {f.session.getAsyncRemote().sendText(message);} catch (Exception e2) {f.session.getAsyncRemote().sendText(message);}});}});}
}
package com.cqcloud.platform.service;/*** @author weimeilayer@gmail.com* @date 💓💕2022年9月8日🐬🐇💓💕*/
public interface IotMqttService {/*** 扩展传输原文* @param message*/void pushMessageArrived(byte[] message);
}
package com.cqcloud.platform.service.impl;import org.springframework.stereotype.Service;import com.cqcloud.platform.service.IotMqttService;import lombok.AllArgsConstructor;/*** @author weimeilayer@gmail.com* @date 💓💕2022年9月8日🐬🐇💓💕*/
@Service
@AllArgsConstructor
public class IotMqttServiceImpl implements IotMqttService {/*** 获取拓展接口原文值* @param message*/@Overridepublic void pushMessageArrived(byte[] message) {// 拓展方法TcpEventHandler.handleAlarm("设备号","告警信息");}
}
package com.cqcloud.platform.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;/*** @author weimeilayer@gmail.com ✨* @date 💓💕 2022年4月12日 🐬🐇 💓💕*/
@Configuration
public class WebSocketConfig {/*** 注入ServerEndpointExporter, 这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint* @return*/@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
进行批量用户的id进行下发webscoket信息

相关文章:

使用springboot-3.4.1搭建一个netty服务并且WebSocket消息通知(适用于设备直连操作,以及回复操作)

引入最新版本 <!--websocket--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId> </dependency>启动类加入 //netty 协议服务端口启动 NettyTcpHandler.start()…...

4. 设计模式分类

4.1 创建型模式 这类模式提供创建对象的机制,能够提升已有代码的灵活性和可复用性。 序 号 类 型 业务场景 实现要点 1 工 厂 方 法 多种类型商品不同接口,统一发奖服 务搭建场景 定义一个创建对象的接口,让其子类自 己决定实例化哪一个工厂类,工厂模式 使其创建过程延迟…...

Hive分区值的插入

对于Hive分区表&#xff0c;在我们插入数据的时候需要指定对应的分区值&#xff0c;而这里就会涉及很多种情况。比如静态分区插入、动态分区插入、提供的分区值和分区字段类型不一致&#xff0c;或者提供的分区值是NULL的情况&#xff0c;下面我们依次来展现下不同情况下的表现…...

【多个图片合并成PDF】

因工作安排,小编最近参加了几场学术会议,被多名业界大佬的汇报所震撼。当然也不是白来的,好东西要留存下来回来分享给科室。因此,小编变成了幻灯片专职摄影师,参会的同时对着大牛的PPT就是一顿咔咔咔。回来后,面对手机里数百张照片却犯了难,就这样一张张发到群里么?还是…...

Flutter动画(三)内建显式动画Widget

常见的内建显式动画Widget&#xff1a; ListenableBuilder&#xff1a; AnimatedBuilder AnimatedWidget AlignTransition DecoratedBoxTransition DefaultTextStyleTransition PositionedTransition RelativePositionedTransition RotationTransition ScaleTransiti…...

本地运行打包好的dist

首先输入打包命令 每个人设置不一样 一般人 是npm run build如果不知道可以去package.json里去看。 打包好文件如下 命令行输入 :npm i -g http-server 进入到dist目录下输入 命令cmd 输入 http-server 成功...

什么是Layer Normalization?

一、概念 前面的文章中&#xff0c;我们介绍了Batch Normalization。BN的目的是使得每个batch的输入数据在每个维度上的均值为0、方差为1&#xff08;batch内&#xff0c;数据维度A的所有数值均值为0、方差为1&#xff0c;维度B、C等以此类推&#xff09;&#xff0c;这是由于神…...

17. Threejs案例-Three.js创建多个立方体

17. Threejs案例-Three.js创建多个立方体 实现效果 知识点 WebGLRenderer (WebGL渲染器) WebGLRenderer 是 Three.js 中用于渲染 WebGL 场景的核心类。它负责将场景中的对象渲染到画布上。 构造器 new THREE.WebGLRenderer(parameters) 参数类型描述parametersObject可选…...

RK3568 Android14 打开蓝牙时默认同意

1、最近给一个项目做了一款基础功能的自动测试&#xff0c;在打开蓝牙时&#xff0c;有一个是否同意的提示框要去掉&#xff0c;即默认同意打开蓝牙。 2、路径&#xff1a; packages/apps/Settings/src/com/android/settings/bluetooth/RequestPermissionActivity.java// Sho…...

多模态视频大模型Aria在Docker部署

多模态视频大模型Aria在Docker部署 契机 ⚙ 闲逛HuggingFace的时候发现一个25.3B的多模态大模型&#xff0c;支持图片和视频。刚好我有H20的GPU所以部署来看看效果&#xff0c;因为我的宿主机是cuda-12.1所以为了防止环境污染采用docker部署&#xff0c;通过一系列的披荆斩棘…...

Ant-Design-Vue 全屏下拉日期框无法显示,能显示后小屏又位置错乱

问题1&#xff1a;在全屏后 日期选择器的下拉框无法显示。 解决&#xff1a;在Ant-Design-Vue的文档中&#xff0c;很多含下拉框的组件都有一个属性 getPopupContainer可以用来指定弹出层的挂载节点。 在该组件上加上 getPopupContainer 属性,给挂载到最外层盒子上。 <temp…...

AMR移动机器人赋能制造业仓储自动化升级

在当今制造业的激烈竞争中&#xff0c;智能化、数字化已成为企业转型升级的关键路径。一家制造业巨头&#xff0c;凭借其庞大的生产体系和多个仓库资源&#xff0c;正以前所未有的决心和行动力&#xff0c;在制造业智能化浪潮中勇立潮头&#xff0c;开启了降本增效的新篇章。这…...

【PHP项目实战】活动报名系统

目录 项目介绍 开发语言 后端 前端 项目截图&#xff08;部分&#xff09; 首页 列表 详情 个人中心 后台管理 项目演示 项目介绍 本项目是一款基于手机浏览器的活动报名系统。它提供了一个方便快捷的活动报名解决方案&#xff0c;无需下载和安装任何APP&#xff0c…...

【HarmonyOS】Component组件引入报错 does not meet UI component syntax.

【HarmonyOS】Component组件引入报错 一、问题背景 有时会碰到引入组件时&#xff0c;无法import引入组件&#xff0c;导致引入的组件报错。 或者提示does not meet UI component syntax. &#xff08;不符合UI组件语法。&#xff09; 如下图所示&#xff0c;在引入组件时&a…...

vue3项目最新eslint9+prettier+husky+stylelint+vscode配置

一、eslint9和prettier通用配置 安装必装插件 ESlint9.x pnpm add eslintlatest -DESlint配置 vue 规则 , typescript解析器 pnpm add eslint-plugin-vue typescript-eslint -DESlint配置 JavaScript 规则 pnpm add eslint/js -D配置所有全局变量 globals pnpm add globa…...

备赛蓝桥杯--算法题目(3)

1. 2的幂 231. 2 的幂 - 力扣&#xff08;LeetCode&#xff09; class Solution { public:bool isPowerOfTwo(int n) {return n>0&&n(n&(-n));} }; 2. 3的幂 326. 3 的幂 - 力扣&#xff08;LeetCode&#xff09; class Solution { public:bool isPowerOfT…...

CSS中要注意的样式效果

1. 应用过渡效果 transition: var(--aa); 2.告诉浏览器元素可能会发生变换&#xff0c;从而优化性能。 will-change: transform; 3.使元素不响应鼠标事件。 pointer-events: none; 4.隐藏水平方向上的溢出内容 overflow-x: hidden; 5.定义一个元素的宽度和高度之间的比…...

【NIPS2024】Unique3D:从单张图像高效生成高质量的3D网格

背景&#xff08;现有方法的不足&#xff09;&#xff1a; 基于Score Distillation Sampling &#xff08;SDS&#xff09;的方法&#xff1a;从大型二维扩散模型中提取3D知识&#xff0c;生成多样化的3D结果&#xff0c;但存在每个案例长时间优化问题/不一致问题。 目前通过微…...

使用Kubernetes部署Spring Boot项目

目录 前提条件 新建Spring Boot项目并编写一个接口 新建Maven工程 导入 Spring Boot 相关的依赖 启动项目 编写Controller 测试接口 构建镜像 打jar包 新建Dockerfile文件 Linux目录准备 上传Dockerfile和target目录到Linux 制作镜像 查看镜像 测试镜像 上传镜…...

基于VTX356语音识别合成芯片的智能语音交互闹钟方案

一、方案概述 本方案旨在利用VTX356语音识别合成芯片强大的语音处理能力&#xff0c;结合蓝牙功能、APP或小程序&#xff0c;打造一款功能全面且智能化程度高的闹钟产品。除了基本的时钟显示和闹钟提醒功能外&#xff0c;还拥有正计时、倒计时、日程安排、重要日提醒以及番茄钟…...

JavaSec-RCE

简介 RCE(Remote Code Execution)&#xff0c;可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景&#xff1a;Groovy代码注入 Groovy是一种基于JVM的动态语言&#xff0c;语法简洁&#xff0c;支持闭包、动态类型和Java互操作性&#xff0c…...

安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件

在选煤厂、化工厂、钢铁厂等过程生产型企业&#xff0c;其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进&#xff0c;需提前预防假检、错检、漏检&#xff0c;推动智慧生产运维系统数据的流动和现场赋能应用。同时&#xff0c;…...

质量体系的重要

质量体系是为确保产品、服务或过程质量满足规定要求&#xff0c;由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面&#xff1a; &#x1f3db;️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限&#xff0c;形成层级清晰的管理网络&#xf…...

c#开发AI模型对话

AI模型 前面已经介绍了一般AI模型本地部署&#xff0c;直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型&#xff0c;但是目前国内可能使用不多&#xff0c;至少实践例子很少看见。开发训练模型就不介绍了&am…...

Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)

参考官方文档&#xff1a;https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java&#xff08;供 Kotlin 使用&#xff09; 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...

代码随想录刷题day30

1、零钱兑换II 给你一个整数数组 coins 表示不同面额的硬币&#xff0c;另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额&#xff0c;返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带…...

Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档&#xff09;&#xff0c;如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下&#xff0c;风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...

Razor编程中@Html的方法使用大全

文章目录 1. 基础HTML辅助方法1.1 Html.ActionLink()1.2 Html.RouteLink()1.3 Html.Display() / Html.DisplayFor()1.4 Html.Editor() / Html.EditorFor()1.5 Html.Label() / Html.LabelFor()1.6 Html.TextBox() / Html.TextBoxFor() 2. 表单相关辅助方法2.1 Html.BeginForm() …...

Python 训练营打卡 Day 47

注意力热力图可视化 在day 46代码的基础上&#xff0c;对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...

Modbus RTU与Modbus TCP详解指南

目录 1. Modbus协议基础 1.1 什么是Modbus? 1.2 Modbus协议历史 1.3 Modbus协议族 1.4 Modbus通信模型 🎭 主从架构 🔄 请求响应模式 2. Modbus RTU详解 2.1 RTU是什么? 2.2 RTU物理层 🔌 连接方式 ⚡ 通信参数 2.3 RTU数据帧格式 📦 帧结构详解 🔍…...