Vue3+Three.js实现

一、环境准备

1. 初始化 Vue3 项目

首先确保你已安装 Node.js(建议 v16+),然后通过 Vite 快速创建 Vue3 项目:

# 创建 Vue3 项目(选 Vue + TypeScript 或 JavaScript 均可)
npm create vite@latest vue3-three-demo -- --template vue
cd vue3-three-demo
npm install

2. 安装核心依赖

需要安装 Three.js 核心库和轨道控制器(Three.js 128+ 版本后控制器单独拆分):

# 安装 Three.js 核心 + 轨道控制器npm install three @tweenjs/tween.js
  • three:Three.js 核心库(3D 渲染核心)
  • @tweenjs/tween.js:可选,用于实现 3D 物体动画(本文会用到)

二、核心概念速览(必懂)

在写代码前,先记住 Three.js 3 个核心要素(缺少一个都无法渲染):

  • 场景(Scene):3D 世界的容器,所有 3D 物体、光照都要放在场景中
  • 相机(Camera):相当于人的眼睛,决定从哪个角度观察场景
  • 渲染器(Renderer):将场景和相机的内容渲染到页面 DOM 元素上
其他常用概念:
  • 几何体(Geometry):3D 物体的形状(如立方体、球体)
  • 材质(Material):3D 物体的外观(颜色、纹理、光泽)
  • 网格(Mesh):几何体 + 材质的组合(真正可渲染的 3D 物体)
  • 光照(Light):让 3D 物体有明暗层次(否则是纯色块)
  • 控制器(OrbitControls):让用户可以拖拽、缩放、旋转场景

三、编写第一个 3D 项目

创建 src/components/ThreeDemo.vue 组件,复制以下代码(含详细注释):

<template>
    <!-- 3D 渲染容器 -->
    <div class="three-container" ref="containerRef"></div>
</template>
<script setup lang="ts">
    import { ref, onMounted, onUnmounted } from 'vue';
    import * as THREE from 'three';
    import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
    import { Tween } from '@tweenjs/tween.js';
    // 核心对象引用
    const containerRef = ref<HTMLDivElement | null>(null);
    let scene: THREE.Scene, camera: THREE.PerspectiveCamera, renderer: THREE.WebGLRenderer;
    let cube: THREE.Mesh, controls: OrbitControls;
    // 初始化 3D 场景
    const initThree = () => {
    if (!containerRef.value) return;
    // 1. 创建场景(容器)
    scene = new THREE.Scene();
    scene.background = new THREE.Color(0xf0f0f0); // 场景背景色(浅灰)
    // 2. 创建相机(透视相机:模拟人眼视角)
    // 参数:视野角度(75°)、宽高比、近裁切面、远裁切面
    camera = new THREE.PerspectiveCamera(
    75,
    containerRef.value.clientWidth / containerRef.value.clientHeight,
    0.1,
    1000
    );
    // 设置相机位置(x, y, z),默认在原点(0,0,0),需要拉开距离才能看到物体
    camera.position.z = 5;
    // 3. 创建渲染器(WebGL 渲染器,高性能)
    renderer = new THREE.WebGLRenderer({ antialias: true }); // 开启抗锯齿
    renderer.setSize(containerRef.value.clientWidth, containerRef.value.clientHeight); // 设置渲染尺寸
    containerRef.value.appendChild(renderer.domElement); // 将渲染画布添加到 DOM
    // 4. 创建 3D 物体(立方体)
    // 几何体:BoxGeometry(宽, 高, 深)
    const geometry = new THREE.BoxGeometry(2, 2, 2);
    // 材质:MeshLambertMaterial(漫反射材质,需要光照才能显示)
    const material = new THREE.MeshLambertMaterial({
    color: 0x42b983, // 基础颜色(绿色)
    wireframe: false, // 是否显示线框(true 可看到几何体结构)
});
    // 网格:几何体 + 材质
    cube = new THREE.Mesh(geometry, material);
    scene.add(cube); // 将立方体添加到场景
    // 5. 添加光照(否则物体是纯色块,无明暗)
    // 环境光:均匀照亮所有物体,无阴影
    const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
    scene.add(ambientLight);
    // 方向光:模拟太阳光,有阴影效果
    const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
    directionalLight.position.set(5, 5, 5); // 光源位置
    scene.add(directionalLight);
    // 6. 添加轨道控制器(允许用户交互:拖拽旋转、滚轮缩放)
    controls = new OrbitControls(camera, renderer.domElement);
    controls.enableDamping = true; // 开启阻尼效果(旋转更顺滑)
    controls.dampingFactor = 0.05;
    // 7. 添加动画(立方体自动旋转)
    const animateCube = () => {
    // 使用 Tween 实现平滑旋转动画
    new Tween(cube.rotation)
    .to({ x: cube.rotation.x + Math.PI * 2, y: cube.rotation.y + Math.PI * 2 }, 5000)
    .repeat(Infinity) // 无限循环
    .start();
};
    animateCube();
    // 8. 监听窗口大小变化(自适应调整)
    window.addEventListener('resize', onWindowResize);
};
    // 窗口大小自适应
    const onWindowResize = () => {
    if (!containerRef.value) return;
    // 更新相机宽高比
    camera.aspect = containerRef.value.clientWidth / containerRef.value.clientHeight;
    camera.updateProjectionMatrix(); // 更新相机投影矩阵
    // 更新渲染器尺寸
    renderer.setSize(containerRef.value.clientWidth, containerRef.value.clientHeight);
};
    // 渲染循环(Three.js 核心,持续渲染场景)
    const renderLoop = () => {
    requestAnimationFrame(renderLoop); // 浏览器帧循环
    controls.update(); // 阻尼效果需要持续更新控制器
    Tween.update(); // 更新动画
    renderer.render(scene, camera); // 渲染场景和相机
};
    // 生命周期钩子:组件挂载时初始化
    onMounted(() => {
    initThree();
    renderLoop();
});
    // 生命周期钩子:组件卸载时清理(防止内存泄漏)
    onUnmounted(() => {
    window.removeEventListener('resize', onWindowResize);
    controls.dispose(); // 销毁控制器
    renderer.dispose(); // 销毁渲染器
    // 清理场景中所有物体
    scene.traverse((obj) => {
    if (obj instanceof THREE.Mesh) {
    obj.geometry.dispose();
    (obj.material as THREE.Material).dispose();
}
});
});
</script>
<style scoped>
    .three-container {
    width: 100vw;
    height: 80vh;
    margin: 20px 0;
}
</style>

2. 在主组件中使用

修改 src/App.vue,引入并使用 ThreeDemo 组件:

<template>
    <div id="app">
        <h1>Vue3 + Three.js 第一个 3D 项目</h1>
        <ThreeDemo />
    </div>
</template>
<script setup lang="ts">
    import ThreeDemo from './components/ThreeDemo.vue';
</script>
<style scoped>
    #app {
    text-align: center;
    padding: 2rem;
}
</style>

四、运行项目

npm run dev
打开浏览器访问 http://localhost:5173 即可

五、进阶优化:添加纹理贴图

如果想让立方体更真实,可以给它添加纹理贴图(替换之前的纯色材质)。

1. 准备纹理图片

在 public 文件夹下放入一张纹理图片(如 texture.jpg,可自行下载)。

2. 修改材质代码

将之前的 MeshLambertMaterial 替换为带纹理的材质:

// 加载纹理(Three.js 内置纹理加载器)
const textureLoader = new THREE.TextureLoader();
const texture = textureLoader.load('/texture.jpg'); // 图片路径(public 下直接写文件名)
// 纹理优化(可选)
texture.wrapS = THREE.RepeatWrapping; // 水平重复
texture.wrapT = THREE.RepeatWrapping; // 垂直重复
texture.repeat.set(2, 2); // 重复次数(x 轴 2 次,y 轴 2 次)
texture.anisotropy = renderer.capabilities.getMaxAnisotropy(); // 各向异性过滤(提升纹理清晰度)
// 带纹理的材质
const material = new THREE.MeshLambertMaterial({
    map: texture, // 纹理贴图
});
重新运行项目,你会看到立方体表面贴上了纹理图片,更具真实感。

六、关键 API 总结(快速查阅)

功能
核心 API
创建场景
new THREE.Scene()
透视相机
new THREE.PerspectiveCamera(fov, aspect, near, far)
WebGL 渲染器
new THREE.WebGLRenderer({ antialias: true })
立方体几何体
new THREE.BoxGeometry(width, height, depth)
漫反射材质
new THREE.MeshLambertMaterial({ color, map })
3D 物体(网格)
new THREE.Mesh(geometry, material)
环境光
new THREE.AmbientLight(color, intensity)
方向光
new THREE.DirectionalLight(color, intensity)
轨道控制器
new OrbitControls(camera, renderer.domElement)
纹理加载
new THREE.TextureLoader().load(url)
动画循环
requestAnimationFrame(renderLoop)
复杂几何体:尝试 SphereGeometry(球体)、CylinderGeometry(圆柱体)等;
高级材质:MeshPhongMaterial(高光材质)、MeshStandardMaterial(PBR 物理材质);
模型加载:使用 GLTFLoader 加载 3D 模型(.gltf/.glb 文件);
粒子系统:创建大量小物体实现粒子特效(如星空、烟雾);
后期处理:添加阴影、模糊、泛光等特效;
Vue 生态整合:结合 Pinia 管理 3D 状态,结合 Vue Router 实现多场景切换;

 

Spring 官宣正式弃用 RestTemplate

近日,Spring 团队正式宣布:RestTemplate 将在 Spring Framework 7.0 中进入弃用计划,并在后续版本中逐步移除。这意味着伴随开发者十余年的经典 HTTP 客户端,终于要走向历史舞台的终点。

  • Spring Framework 7.0(2025 年 11 月):宣布弃用计划。
  • Spring Framework 7.1(预计 2026 年 11 月):正式标记为@Deprecated。
  • Spring Framework 8.0(时间待定):彻底移除 RestTemplate。

根据 Spring 的维护节奏,RestTemplate 的开源支持会延续到 至少 2029 年,开发者仍有充足的时间完成迁移。

RestTemplate 最早诞生于 Spring Framework 3.0(2009 年),至今已有 15 年历史。它的设计基于“模板式 API”(类似 JdbcTemplate、JmsTemplate),在当时为开发者带来了极大便利。但随着技术生态的发展,它逐渐暴露出以下局限:

1.API 扩展性差: 新功能需要通过方法重载实现,导致 Javadoc 和 IDE 自动补全中充斥大量重复方法,开发体验逐渐恶化。

2.异步支持不足: AsyncRestTemplate 曾试图解决,但依赖 ListenableFuture,在并发组合和资源管理上力不从心。

3.流式处理受限: RestTemplate 默认会读取并关闭整个响应,难以处理 SSE(Server-Sent Events)等流式协议。

4.无法拥抱现代 Java: 在虚拟线程(Virtual Threads)、结构化并发(Structured Concurrency)和 Reactive Streams 成为主流的今天,RestTemplate 的 API 模式已经无法适配。

在 Spring Framework 6.1 引入并不断完善的 RestClient,将取代 RestTemplate 成为传统(阻塞式)HTTP 客户端的首选。

与 RestTemplate 相比,RestClient 带来:

  • 现代化的 Fluent API:链式调用,方法更简洁,IDE 提示更精准。
  • API 版本控制:可通过请求头、路径、Query 参数等方式自动插入 API 版本。
  • 灵活的 HttpMessageConverters:更容易扩展和自定义消息转换器。
  • Http Interface Groups:支持批量配置多个 HTTP 接口客户端,减少重复代码。•RestTestClient:全新的测试客户端,替代 TestRestTemplate,支持集成测试与 Mock 测试统一。

对于已经在用RestTemplate的项目,Spring 提供了平滑迁移方案:RestClient 可以直接包装现有 RestTemplate 实例,让你逐步替换而不是“一刀切”。

另一方面,WebClient 依旧是 Reactive 编程场景下的首选 HTTP 客户端:

  • 支持异步、非阻塞调用。
  • 原生支持流式协议(如 SSE)。
  • 与 Reactor / WebFlux 完美结合。

未来的 Spring HTTP 客户端格局将非常清晰:

  • RestClient:适合大多数同步阻塞式应用。
  • WebClient:适合需要Reactive、异步或流式处理的应用。

对于大多数企业级应用来说,这次调整意味着:

1.新项目:直接使用 RestClient 或 WebClient。

2.老项目:逐步迁移,先用 RestClient 包装 RestTemplate,再逐步替换。

3.测试场景:迁移到 RestTestClient,弃用 TestRestTemplate。

随着 Spring Boot 4.0 的模块化改造,开发者还可以直接通过:

  • spring-boot-starter-restclient
  • spring-boot-starter-webclient

来声明依赖,明确选择 HTTP 客户端类型。

用C#读取Modbus温度传感器

传感器地址表

寄存器地址 类型 功能描述 数据格式 示例值
0x0000 (0)
Input Register (只读)
温度值,高精度(浮点型,双寄存器)
浮点数
25.2°C(IEEE格式)
0x0001 (1)
Input Register (只读)
湿度值(如果是温湿一体设备)
浮点数
60.1%
0x0100 (256)
Holding Register (可读写)
单位配置 (0=°C, 1=°F)
16 位整数
0(摄氏度)
0x0200 (512)
Coil (线圈,可读写)
报警启用标志
1 位布尔值
1(启用报警提示)
0x0300 (768)
Discrete Input (只读)
故障状态指示
1 位布尔值
0(无故障)

安装相关依赖

在C#项目中,需要一个库来实现Modbus通讯功能。推荐使用NModbus,它是一个流行且易用的开源库。

步骤:

  • 1.打开Visual Studio。
  • 2.使用NuGet Package Manager安装NModbus:
Install-Package NModbus

001

设置Modbus通讯

Modbus常见的两种通讯方式是串口(Modbus RTU)TCP(Modbus TCP/IP)。下面以Modbus TCP为例。

代码示例:

using System;
using System.Net.Sockets;
using NModbus;

classProgram
{
    static void Main(string[] args)
    {
        string ipAddress = "192.168.1.100"; // 替换为传感器IP
        int port = 502; // 默认的Modbus TCP端口

        try
        {
            // 建立TCP连接
            using (var tcpClient = new TcpClient(ipAddress, port))
            {
                var factory = new ModbusFactory();
                var master = factory.CreateMaster(tcpClient);

                // 读取温度值(寄存器0地址)
                ushort startAddress = 0; // 温度地址
                ushort[] registers = master.ReadInputRegisters(1, startAddress, 2);

                // 将寄存器数据转换为浮点型
                float temperature = ConvertToFloat(registers[0], registers[1]);
                Console.WriteLine($"当前温度: {temperature} °C");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"错误: {ex.Message}");
        }
    }

    // 双寄存器转浮点数
    static float ConvertToFloat(ushort high, ushort low)
    {
        byte[] data = newbyte[4];
        Buffer.BlockCopy(BitConverter.GetBytes(low), 0, data, 0, 2);
        Buffer.BlockCopy(BitConverter.GetBytes(high), 0, data, 2, 2);
        return BitConverter.ToSingle(data, 0);
    }
}

解析Modbus地址与数据

  • 1.Modbus寄存器:
    温度传感器通常储存在输入寄存器(Input Register)或保持寄存器(Holding Register)。实际使用前,需要查阅设备文档确认其寄存器地址。
  • 2.数据转换:
    常见的温度数据格式包括整型或浮点型,需要在获取后根据传感器需求做相应处理。

调试注意事项

  • 1.确保网络或串口连接正常,并确认IP地址和端口匹配实际设备。
  • 2.校对寄存器地址,如果数据异常需检查设备手册。
  • 3.检查协议类型,确定使用Modbus TCP还是RTU协议。

常见错误处理:

  • 连接超时: 确认防火墙是否拦截。
  • 数据不正确: 检查是否需要字节顺序(Byte Order)调整。

 

 

Java怎么做一个Modbus服务

ModbusTCP 是一种客户端/服务器模式的通信协议。服务器负责监听端口,等待客户端发起请求,并依据请求返回数据或执行操作。例如,读取传感器数据、写入控制命令等,是 ModbusTCP 服务器的常见应用场景。

Java 语言因其跨平台性和丰富的类库,成为构建 ModbusTCP 服务器的一种高效选择。借助第三方库如 JamodModbus4J,我们可以快速实现协议的解析与响应。

1.添加依赖

选择一个成熟的 Modbus 库,例如 Modbus4J,将其引入项目。以下是 Maven 配置:

<dependency>
  <groupId>com.ghgande</groupId>
  <artifactId>modbus4j</artifactId>
  <version>3.0.7</version>
</dependency>

2.编写代码

下面是一段简单的 ModbusTCP 服务器实现代码:

import com.serotonin.modbus4j.base.ModbusUtils;
import com.serotonin.modbus4j.ip.tcp.TcpSlave;
import com.serotonin.modbus4j.processimage.BasicProcessImage;

publicclass ModbusTCPServer {
    public static void main(String[] args) {
        try {
            // 创建服务器实例
            TcpSlave server = new TcpSlave(false);
            server.setPort(502);

            // 构建模拟设备数据
            BasicProcessImage spi = new BasicProcessImage(1);
            spi.setHoldingRegister(0, 12345); // 模拟寄存器数据
            server.addProcessImage(spi);

            // 启动服务
            server.start();
            System.out.println("ModbusTCP 服务器已启动,监听端口 502");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

3.运行程序并测试

运行后,使用 Modbus Poll 或其他客户端工具测试通信功能,可验证读取寄存器数据的正确性。

001

测试结果如下:

Request: Read Holding Registers
Response: [12345]

Java 实现 ModbusTCP 服务器非常简单,通过像 Modbus4J 这样的第三方库,无需深入底层协议解析,即可快速构建稳定可靠的工业通信服务器。同时,你可以根据具体需求扩展功能,例如加入多线程处理、日志记录等。

Modbus 资料总结

Modbus 是工业通信设备中最常用的协议之一,网关向 Modbus 设备发送命令以获取设备的值。

在 Modbus 网络上的所有设备中,通常只有一个主设备,它会向其他从设备发送不同的命令。Modbus 主设备会向网络上的所有设备广播消息,但只有分配了 Modbus 地址来接受命令的设备才会接受该命令,其余设备将拒绝该命令。基本的 Modbus 命令可以指示 RTU 更改其某个寄存器中的值、控制或读取 I/O 端口,以及命令设备发回其寄存器中包含的一个或多个值。

在工业环境中使用 Modbus 的主要原因是:

  • 它是专为工业应用而开发的
  • 它是公开发布的,免版税
  • 它易于部署和维护
  • 它移动原始位或字,而不会对供应商施加太多限制
  • 在 Modbus 协议上轻松获得 SCADA 系统

Modbus 协议有多种类型:

  • Modbus TCP
  • Modbus RTU
  • Modbus ASCII
  • Modbus Plus
  • Modbus Daniels
  • Modbus Tek-Air
  • Modbus Omniflow

其中最流行的是 Modbus RTU 和 Modbus TCP/IP。

ModbusRTU 是一种串行通信协议,可连接同一网络上的不同设备,并使它们之间可以进行通信。

ModbusTCP 涵盖使用 TCP/IP 协议通过“内部网”或“互联网”环境进行 Modbus 通信。目前,该协议最常见的用途是将 PLC、I/O 模块和网关通过以太网连接到其他简单现场总线或 I/O 网络。

总会有这样一个问题:为什么使用面向连接的 TCP/IP 协议而不是面向数据报的 UDP。主要原因是通过将单个“通信”隔离在可以识别、取消和监督的连接中来保持对单个“通信”的控制,而无需对客户端和服务器应用程序执行特定操作。这使该机制能够容忍网络性能变化,并且还提供了添加防火墙和代理等安全功能的空间。

MODBUS/TCP/IP 处理两种不同的情况。在协议层面上,连接很容易被识别。单个连接可用于执行多个独立通信。此外,TCP/IP 允许大量并发连接,因此用户决定重新使用旧连接或重新连接到常用连接。

MODBUS 内存模型/MODBUS 存储模型

MODBUS 具有独特的寻址模式。Modbus 设备会将其中的每个值存储在特定地址。例如,功率计将仅在 Modbus 地址 40001 处存储 Volt A-N 值。

有四种 Modbus 数据类型:

Modbus 数据类型 数据格式和通用名称 地址起始位置
线圈状态 位,二进制值 00001 此类数据可由 I/O 系统提供。
离散输入状态 二进制值 10001 此类型数据可由应用层更改。
输入寄存器 二进制值 30001 此类数据可由 I/O 系统提供。
保持寄存器 模拟值 40001 此类数据可由应用层更改。

MODBUS RTU 如何工作?

  • Modbus通过设备之间的串行线路传输。最简单的设置是使用一条串行电缆连接两个设备(主设备和从设备)上的串行端口。
  • 数据以一系列 1 和 0(称为位)的形式发送。每个位都以电压的形式发送。0以正电压的形式发送,1以负电压的形式发送。位发送得非常快。典型的传输速度为 9600 波特(每秒位数)。

Modbus TCP 如何工作?

可以使用网关上的以太网端口连接 Modbus 设备。我们可以使用任何标准 Modbus 扫描仪进行查询,以从 Modbus 设备中提取值。所有请求都通过注册端口 502 上的 TCP/IP 发送。

Modbus RTU、TCP 和 ASCII 之间有什么区别?

Modbus 协议定义了一个独立于底层通信层的协议数据单元 (PDU)。 Modbus RTU 是最常用的,它是 PDU 的二进制表示,在 PDU 之前有寻址,末尾附加有 CRC。Modbus ASCII 是使用所有可打印字符(通常是两倍的字节数)表示的相同 PDU。Modbus TCP 本质上与 Modbus RTU 完全相同,只是 CRC 不在应用层字节字符串中,而留给 TCP 层自动处理。RTU 数据包的 TCP 封装中还有一些额外的寻址字节。

无论协议变体如何,功能代码、寄存器编号和寻址都是相同的。寄存器类型相同,即为 Modbus 设备定义了相同的数据块。

OSI 模型上的 MODBUS 协议

标准 Modbus 中如何存储数据?

  • 从设备中的信息存储在四个不同的表中。
  • 每个表有 9999 个数据点,可以存储不同的值。

每个线圈有 1 位,并给出 x0000 和 x9999 之间的数据地址。

每个寄存器是 1 个字 = 16 位 = 2 个字节,并且数据地址在 x0000 和 x9999 之间。

数字40001、10000等应作为位置的地址。这些数字永远不会显示在实际消息中。实际地址将是存储数字的偏移量。因此,如果设备具有 Modbus 的 ADU(应用程序定义单元)寻址,则地址 0001 将位于偏移量 40001 处,而对于 PDU(协议定义单元),它将位于偏移量 40002 处。

Modbus 中的从属 ID 或设备 ID

网络中的每个设备都必须分配一个唯一的单元地址,该地址可以介于 1 和 255 之间。当主设备请求数据时,它发送的第一个字节是设备/从属地址。因此,只有在发送第一个字节后,从属设备才会决定响应或忽略。

Modbus 映射

Modbus 映射是支持 Modbus 作为通信协议的任何特定设备的简单点列表。

Modbus 映射应包含以下信息:

  • 设备读取和存储什么类型的数据(例如温度或压力)
  • 数据存储在哪个地址(例如 40001 处的电压 A-N)
  • 数据存储的格式是什么(例如位、UINT16、SINT16 等)
  • 如果需要,提供点的工程单位。
  • 有关设备是否具有 ADU 或 PDU 寻址的信息。

大多数设备都预装了 Modbus 寄存器。在某些情况下,制造商还允许操作员根据自己的要求配置设备。

MODBUS 错误检查

MODBUS 网络采用两种错误检查方法:奇偶校验

  • 数据字符帧的奇偶校验(偶校验、奇校验或无奇偶校验)
  • 消息帧内的帧校验(RTU 模式下的循环冗余校验或 ASCII 模式下的纵向冗余校验)。

奇偶校验

MODBUS 设备可以配置为偶校验或奇校验,或无奇偶校验。这决定了字符数据帧的奇偶校验位的设置方式。如果选择偶校验或奇校验,则计算每个字符帧数据部分中的 1 位数量。RTU 模式下的每个字符包含 8 位。然后,奇偶校验位将设置为 0 或 1,从而导致 1 位总数为偶数(偶校验)或奇数(奇校验)。

帧检查

LRC 纵向冗余校验(仅限 ASCII 模式)在 ASCII 传输模式下,字符帧包含一个 LRC 字段,作为 CRLF 字符之前的最后一个字段。此字段包含两个 ASCII 字符,表示除起始冒号字符和结束 CR LF 字符对之外的所有字段的纵向冗余计算结果。

CRC 错误检查(仅限 RTU 模式)

RTU 模式消息帧包括一种基于循环冗余校验 (CRC) 的错误检查方法。消息帧的错误检查字段包含一个 16 位值(两个 8 位字节),其中包含对消息内容执行的循环冗余校验 (CRC) 计算的结果。

Modbus 定义的功能代码:

主设备发送的第二个字节是功能代码。此数字告诉从设备要访问哪个表以及是否从表中读取或写入。

功能代码 操作 表名称
01 (十六进制 01) 读取 离散输出线圈
05 (十六进制 05) 写入单个 离散输出线圈
15 (十六进制 0F) 写入多个 离散输出线圈
02 (十六进制 02) 读取 离散输入触点
04 (十六进制 04) 读取 模拟输入寄存器
03 (十六进制 03) 读取 模拟输出保持寄存器
06 (十六进制 06) 写入单个 模拟输出保持寄存器
16 (十六进制 10) 写入多个 模拟输出保持寄存器

Modbus 异常代码

代码 名称 含义
01 非法功能 查询中收到的功能代码不是服务器(或从属设备)允许的操作。这可能是因为功能代码仅适用于较新的设备,并且未在所选单元中实现。它也可能表明服务器(或从属设备)处于错误状态,无法处理此类请求,例如因为它未配置并被要求返回寄存器值。
02 非法数据地址 查询中收到的数据地址不是服务器(或从属设备)允许的地址。更具体地说,参考编号和传输长度的组合无效。对于具有 100 个寄存器的控制器,PDU 将第一个寄存器的地址设为 0,将最后一个寄存器的地址设为 99。如果提交的请求的起始寄存器地址为 96,寄存器数量为 4,则该请求将成功(至少在地址方面)对寄存器 96、97、98、99 进行操作。如果提交的请求的起始寄存器地址为 96,寄存器数量为 5,则该请求将失败,并出现异常代码 0x02“非法数据地址”,因为它尝试对寄存器 96、97、98、99 和 100 进行操作,而没有地址为 100 的寄存器。
03 非法数据值 查询数据字段中包含的值不是服务器(或从属设备)允许的值。这表明复杂请求的其余部分的结构存在错误,例如隐含的长度不正确。它具体并不意味着提交存储在寄存器中的数据项具有超出应用程序预期的值,因为 MODBUS 协议不知道任何特定寄存器的任何特定值的意义。
04 从设备故障 当服务器(或从设备)尝试执行请求的操作时发生不可恢复的错误。
05 确认 专门与编程命令结合使用。服务器(或从设备)已接受请求并正在处理它,但这样做需要很长时间。返回此响应是为了防止客户端(或主设备)中发生超时错误。客户端(或主设备)接下来可以发出轮询程序完成消息来确定处理是否完成。
06 从设备忙 专门与编程命令结合使用。服务器(或从设备)正在处理长时间的程序命令。客户端(或主设备)应在服务器(或从设备)空闲时重新传输该消息。
08 内存奇偶校验错误 专门与功能代码 20 和 21 以及引用类型 6 结合使用,表示扩展文件区域未能通过一致性检查。服务器(或从设备)尝试读取记录文件,但检测到内存中的奇偶校验错误。客户端(或主设备)可以重试该请求,但服务器(或从设备)设备上可能需要服务。
0A 网关路径不可用 专门与网关结合使用,表示网关无法分配从输入端口到输出端口的内部通信路径来处理请求。通常意味着网关配置不正确或过载。
0B 网关目标设备响应失败 专门与网关结合使用,表示未从目标设备获得响应。通常意味着该设备不在网络上。

版权声明

123118
 
Copyright © 2008-2021 lanxinbase.com Rights Reserved. | 粤ICP备14086738号-3 |