扩展项目
This commit is contained in:
parent
d6844f19bd
commit
6b7e0bfa33
|
|
@ -0,0 +1,73 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>ruoyi</artifactId>
|
||||||
|
<groupId>com.ruoyi</groupId>
|
||||||
|
<version>2.0.0</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>modbus-server</artifactId>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
|
||||||
|
<!-- SpringCloud Ailibaba Nacos -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- SpringCloud Ailibaba Nacos Config -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- SpringCloud Ailibaba Sentinel -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- SpringBoot Web -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Swagger -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.springfox</groupId>
|
||||||
|
<artifactId>springfox-swagger-ui</artifactId>
|
||||||
|
<version>${swagger.fox.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- Ruoyi Common Swagger -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.ruoyi</groupId>
|
||||||
|
<artifactId>ruoyi-common-swagger</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.netty</groupId>
|
||||||
|
<artifactId>netty-all</artifactId>
|
||||||
|
<version>4.1.50.Final</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>repackage</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
package com.sarnath.modbusserver;
|
||||||
|
|
||||||
|
import com.ruoyi.common.swagger.annotation.EnableCustomSwagger2;
|
||||||
|
import com.sarnath.modbusserver.dtuutils.DTUServer;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.cloud.client.SpringCloudApplication;
|
||||||
|
|
||||||
|
@EnableCustomSwagger2
|
||||||
|
@SpringCloudApplication
|
||||||
|
public class SarnathModbusServerApplication
|
||||||
|
{
|
||||||
|
public static void main(String[] args)
|
||||||
|
{
|
||||||
|
SpringApplication.run(SarnathModbusServerApplication.class, args);
|
||||||
|
|
||||||
|
|
||||||
|
new Thread(()->{
|
||||||
|
DTUServer.Start();
|
||||||
|
}).start();
|
||||||
|
System.out.println("(♥◠‿◠)ノ゙ Modbus服务端模块启动成功 ლ(´ڡ`ლ)゙ \n" +
|
||||||
|
"____.___._________ \n" +
|
||||||
|
"\\__ | |\\_ ___ \\ \n" +
|
||||||
|
" / | |/ \\ \\/ \n" +
|
||||||
|
" \\____ |\\ \\____\n" +
|
||||||
|
" / ______| \\______ /\n" +
|
||||||
|
" \\/ \\/ ");
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,420 @@
|
||||||
|
package com.sarnath.modbusserver.dtuutils;
|
||||||
|
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数字转字节数组工具类
|
||||||
|
*/
|
||||||
|
public class BitConverter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 以字节数组的形式返回指定的布尔值
|
||||||
|
* @param data 一个布尔值
|
||||||
|
* @return 长度为 1 的字节数组
|
||||||
|
*/
|
||||||
|
public static byte[] getBytes(boolean data) {
|
||||||
|
byte[] bytes = new byte[1];
|
||||||
|
bytes[0] = (byte) (data ? 1 : 0);
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 以字节数组的形式返回指定的 16 位有符号整数值
|
||||||
|
* @param data 要转换的数字
|
||||||
|
* @return 长度为 2 的字节数组
|
||||||
|
*/
|
||||||
|
public static byte[] getBytes(short data) {
|
||||||
|
byte[] bytes = new byte[2];
|
||||||
|
if (isLittleEndian()) {
|
||||||
|
bytes[0] = (byte) (data & 0xff);
|
||||||
|
bytes[1] = (byte) ((data & 0xff00) >> 8);
|
||||||
|
} else {
|
||||||
|
bytes[1] = (byte) (data & 0xff);
|
||||||
|
bytes[0] = (byte) ((data & 0xff00) >> 8);
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 以字节数组的形式返回指定的 Unicode 字符值
|
||||||
|
* @param data 要转换的字符
|
||||||
|
* @return 长度为 2 的字节数组
|
||||||
|
*/
|
||||||
|
public static byte[] getBytes(char data) {
|
||||||
|
byte[] bytes = new byte[2];
|
||||||
|
if (isLittleEndian()) {
|
||||||
|
bytes[0] = (byte) (data);
|
||||||
|
bytes[1] = (byte) (data >> 8);
|
||||||
|
} else {
|
||||||
|
bytes[1] = (byte) (data);
|
||||||
|
bytes[0] = (byte) (data >> 8);
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 以字节数组的形式返回指定的 32 位有符号整数值
|
||||||
|
* @param data 要转换的数字
|
||||||
|
* @return 长度为 4 的字节数组
|
||||||
|
*/
|
||||||
|
public static byte[] getBytes(int data) {
|
||||||
|
byte[] bytes = new byte[4];
|
||||||
|
if (isLittleEndian()) {
|
||||||
|
bytes[0] = (byte) (data & 0xff);
|
||||||
|
bytes[1] = (byte) ((data & 0xff00) >> 8);
|
||||||
|
bytes[2] = (byte) ((data & 0xff0000) >> 16);
|
||||||
|
bytes[3] = (byte) ((data & 0xff000000) >> 24);
|
||||||
|
} else {
|
||||||
|
bytes[3] = (byte) (data & 0xff);
|
||||||
|
bytes[2] = (byte) ((data & 0xff00) >> 8);
|
||||||
|
bytes[1] = (byte) ((data & 0xff0000) >> 16);
|
||||||
|
bytes[0] = (byte) ((data & 0xff000000) >> 24);
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 以字节数组的形式返回指定的 64 位有符号整数值
|
||||||
|
* @param data 要转换的数字
|
||||||
|
* @return 长度为 8 的字节数组
|
||||||
|
*/
|
||||||
|
public static byte[] getBytes(long data) {
|
||||||
|
byte[] bytes = new byte[8];
|
||||||
|
if (isLittleEndian()) {
|
||||||
|
bytes[0] = (byte) (data & 0xff);
|
||||||
|
bytes[1] = (byte) ((data >> 8) & 0xff);
|
||||||
|
bytes[2] = (byte) ((data >> 16) & 0xff);
|
||||||
|
bytes[3] = (byte) ((data >> 24) & 0xff);
|
||||||
|
bytes[4] = (byte) ((data >> 32) & 0xff);
|
||||||
|
bytes[5] = (byte) ((data >> 40) & 0xff);
|
||||||
|
bytes[6] = (byte) ((data >> 48) & 0xff);
|
||||||
|
bytes[7] = (byte) ((data >> 56) & 0xff);
|
||||||
|
} else {
|
||||||
|
bytes[7] = (byte) (data & 0xff);
|
||||||
|
bytes[6] = (byte) ((data >> 8) & 0xff);
|
||||||
|
bytes[5] = (byte) ((data >> 16) & 0xff);
|
||||||
|
bytes[4] = (byte) ((data >> 24) & 0xff);
|
||||||
|
bytes[3] = (byte) ((data >> 32) & 0xff);
|
||||||
|
bytes[2] = (byte) ((data >> 40) & 0xff);
|
||||||
|
bytes[1] = (byte) ((data >> 48) & 0xff);
|
||||||
|
bytes[0] = (byte) ((data >> 56) & 0xff);
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 以字节数组的形式返回指定的单精度浮点值
|
||||||
|
* @param data 要转换的数字
|
||||||
|
* @return 长度为 4 的字节数组
|
||||||
|
*/
|
||||||
|
public static byte[] getBytes(float data) {
|
||||||
|
return getBytes(Float.floatToIntBits(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 以字节数组的形式返回指定的双精度浮点值
|
||||||
|
* @param data 要转换的数字
|
||||||
|
* @return 长度为 8 的字节数组
|
||||||
|
*/
|
||||||
|
public static byte[] getBytes(double data) {
|
||||||
|
return getBytes(Double.doubleToLongBits(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将指定字符串中的所有字符编码为一个字节序列
|
||||||
|
* @param data 包含要编码的字符的字符串
|
||||||
|
* @return 一个字节数组,包含对指定的字符集进行编码的结果
|
||||||
|
*/
|
||||||
|
public static byte[] getBytes(String data) {
|
||||||
|
return data.getBytes(Charset.forName("UTF-8"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将指定字符串中的所有字符编码为一个字节序列
|
||||||
|
* @param data 包含要编码的字符的字符串
|
||||||
|
* @param charsetName 字符集编码
|
||||||
|
* @return 一个字节数组,包含对指定的字符集进行编码的结果
|
||||||
|
*/
|
||||||
|
public static byte[] getBytes(String data, String charsetName) {
|
||||||
|
return data.getBytes(Charset.forName(charsetName));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回由字节数组转换来的布尔值
|
||||||
|
* @param bytes 字节数组
|
||||||
|
* @return 布尔值
|
||||||
|
*/
|
||||||
|
public static boolean toBoolean(byte[] bytes) {
|
||||||
|
return bytes[0] == 0 ? false : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回由字节数组中的指定的一个字节转换来的布尔值
|
||||||
|
* @param bytes 字节数组
|
||||||
|
* @param startIndex 起始下标
|
||||||
|
* @return 布尔值
|
||||||
|
*/
|
||||||
|
public static boolean toBoolean(byte[] bytes, int startIndex) {
|
||||||
|
return toBoolean(copyFrom(bytes, startIndex, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回由字节数组转换来的 16 位有符号整数
|
||||||
|
* @param bytes 字节数组
|
||||||
|
* @return 由两个字节构成的 16 位有符号整数
|
||||||
|
*/
|
||||||
|
public static short toShort(byte[] bytes) {
|
||||||
|
if (isLittleEndian()) {
|
||||||
|
return (short) ((0xff & bytes[0]) | (0xff00 & (bytes[1] << 8)));
|
||||||
|
} else {
|
||||||
|
return (short) ((0xff & bytes[1]) | (0xff00 & (bytes[0] << 8)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 无符号short整数转换为2字节的byte数组
|
||||||
|
*
|
||||||
|
* @param s
|
||||||
|
* short整数
|
||||||
|
* @return byte数组
|
||||||
|
*/
|
||||||
|
public static byte[] unsignedShortToByte2(int s) {
|
||||||
|
byte[] targets = new byte[2];
|
||||||
|
targets[0] = (byte) (s >> 8 & 0xFF);
|
||||||
|
targets[1] = (byte) (s & 0xFF);
|
||||||
|
return targets;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* byte数组转换为无符号short整数
|
||||||
|
*
|
||||||
|
* @param bytes
|
||||||
|
* byte数组
|
||||||
|
* @return short整数
|
||||||
|
*/
|
||||||
|
public static int byte2ToUnsignedShort(byte[] bytes) {
|
||||||
|
return byte2ToUnsignedShort(bytes, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* byte数组转换为无符号short整数
|
||||||
|
*
|
||||||
|
* @param bytes
|
||||||
|
* byte数组
|
||||||
|
* @param off
|
||||||
|
* 开始位置
|
||||||
|
* @return short整数
|
||||||
|
*/
|
||||||
|
public static int byte2ToUnsignedShort(byte[] bytes, int off) {
|
||||||
|
int high = bytes[off];
|
||||||
|
int low = bytes[off + 1];
|
||||||
|
return (high << 8 & 0xFF00) | (low & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回由字节数组中的指定的两个字节转换来的 16 位有符号整数
|
||||||
|
* @param bytes 字节数组
|
||||||
|
* @param startIndex 起始下标
|
||||||
|
* @return 由两个字节构成的 16 位有符号整数
|
||||||
|
*/
|
||||||
|
public static short toShort(byte[] bytes, int startIndex) {
|
||||||
|
return toShort(copyFrom(bytes, startIndex, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回由字节数组转换来的 Unicode 字符
|
||||||
|
* @param bytes 字节数组
|
||||||
|
* @return 由两个字节构成的字符
|
||||||
|
*/
|
||||||
|
public static char toChar(byte[] bytes) {
|
||||||
|
if (isLittleEndian()) {
|
||||||
|
return (char) ((0xff & bytes[0]) | (0xff00 & (bytes[1] << 8)));
|
||||||
|
} else {
|
||||||
|
return (char) ((0xff & bytes[1]) | (0xff00 & (bytes[0] << 8)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回由字节数组中的指定的两个字节转换来的 Unicode 字符
|
||||||
|
* @param bytes 字节数组
|
||||||
|
* @param startIndex 起始下标
|
||||||
|
* @return 由两个字节构成的字符
|
||||||
|
*/
|
||||||
|
public static char toChar(byte[] bytes, int startIndex) {
|
||||||
|
return toChar(copyFrom(bytes, startIndex, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回由字节数组转换来的 32 位有符号整数
|
||||||
|
* @param bytes 字节数组
|
||||||
|
* @return 由四个字节构成的 32 位有符号整数
|
||||||
|
*/
|
||||||
|
public static int toInt(byte[] bytes) {
|
||||||
|
if (isLittleEndian()) {
|
||||||
|
return (0xff & bytes[0])
|
||||||
|
| (0xff00 & (bytes[1] << 8))
|
||||||
|
| (0xff0000 & (bytes[2] << 16))
|
||||||
|
| (0xff000000 & (bytes[3] << 24));
|
||||||
|
} else {
|
||||||
|
return (0xff & bytes[3])
|
||||||
|
| (0xff00 & (bytes[2] << 8))
|
||||||
|
| (0xff0000 & (bytes[1] << 16))
|
||||||
|
| (0xff000000 & (bytes[0] << 24));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回由字节数组中的指定的四个字节转换来的 32 位有符号整数
|
||||||
|
* @param bytes 字节数组
|
||||||
|
* @param startIndex 起始下标
|
||||||
|
* @return 由四个字节构成的 32 位有符号整数
|
||||||
|
*/
|
||||||
|
public static int toInt(byte[] bytes, int startIndex) {
|
||||||
|
return toInt(copyFrom(bytes, startIndex, 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回由字节数组转换来的 64 位有符号整数
|
||||||
|
* @param bytes 字节数组
|
||||||
|
* @return 由八个字节构成的 64 位有符号整数
|
||||||
|
*/
|
||||||
|
public static long toLong(byte[] bytes) {
|
||||||
|
if (isLittleEndian()) {
|
||||||
|
return (0xffL & (long) bytes[0])
|
||||||
|
| (0xff00L & ((long) bytes[1] << 8))
|
||||||
|
| (0xff0000L & ((long) bytes[2] << 16))
|
||||||
|
| (0xff000000L & ((long) bytes[3] << 24))
|
||||||
|
| (0xff00000000L & ((long) bytes[4] << 32))
|
||||||
|
| (0xff0000000000L & ((long) bytes[5] << 40))
|
||||||
|
| (0xff000000000000L & ((long) bytes[6] << 48))
|
||||||
|
| (0xff00000000000000L & ((long) bytes[7] << 56));
|
||||||
|
} else {
|
||||||
|
return (0xffL & (long) bytes[7])
|
||||||
|
| (0xff00L & ((long) bytes[6] << 8))
|
||||||
|
| (0xff0000L & ((long) bytes[5] << 16))
|
||||||
|
| (0xff000000L & ((long) bytes[4] << 24))
|
||||||
|
| (0xff00000000L & ((long) bytes[3] << 32))
|
||||||
|
| (0xff0000000000L & ((long) bytes[2] << 40))
|
||||||
|
| (0xff000000000000L & ((long) bytes[1] << 48))
|
||||||
|
| (0xff00000000000000L & ((long) bytes[0] << 56));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回由字节数组中的指定的八个字节转换来的 64 位有符号整数
|
||||||
|
* @param bytes 字节数组
|
||||||
|
* @param startIndex 起始下标
|
||||||
|
* @return 由八个字节构成的 64 位有符号整数
|
||||||
|
*/
|
||||||
|
public static long toLong(byte[] bytes, int startIndex) {
|
||||||
|
return toLong(copyFrom(bytes, startIndex, 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回由字节数组转换来的单精度浮点数
|
||||||
|
* @param bytes 字节数组
|
||||||
|
* @return 由四个字节构成的单精度浮点数
|
||||||
|
*/
|
||||||
|
public static float toFloat(byte[] bytes) {
|
||||||
|
return Float.intBitsToFloat(toInt(bytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回由字节数组中的指定的四个字节转换来的单精度浮点数
|
||||||
|
* @param bytes 字节数组
|
||||||
|
* @param startIndex 起始下标
|
||||||
|
* @return 由四个字节构成的单精度浮点数
|
||||||
|
*/
|
||||||
|
public static float toFloat(byte[] bytes, int startIndex) {
|
||||||
|
return Float.intBitsToFloat(toInt(copyFrom(bytes, startIndex, 4)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回由字节数组转换来的双精度浮点数
|
||||||
|
* @param bytes 字节数组
|
||||||
|
* @return 由八个字节构成的双精度浮点数
|
||||||
|
*/
|
||||||
|
public static double toDouble(byte[] bytes) {
|
||||||
|
return Double.longBitsToDouble(toLong(bytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回由字节数组中的指定的八个字节转换来的双精度浮点数
|
||||||
|
* @param bytes 字节数组
|
||||||
|
* @param startIndex 起始下标
|
||||||
|
* @return 由八个字节构成的双精度浮点数
|
||||||
|
*/
|
||||||
|
public static double toDouble(byte[] bytes, int startIndex) {
|
||||||
|
return Double.longBitsToDouble(toLong(copyFrom(bytes, startIndex, 8)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回由字节数组转换来的字符串
|
||||||
|
* @param bytes 字节数组
|
||||||
|
* @return 字符串
|
||||||
|
*/
|
||||||
|
public static String toString(byte[] bytes) {
|
||||||
|
return new String(bytes, Charset.forName("UTF-8"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回由字节数组转换来的字符串
|
||||||
|
* @param bytes 字节数组
|
||||||
|
* @param charsetName 字符集编码
|
||||||
|
* @return 字符串
|
||||||
|
*/
|
||||||
|
public static String toString(byte[] bytes, String charsetName) {
|
||||||
|
return new String(bytes, Charset.forName(charsetName));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 以字符串表示形式返回字节数组的内容
|
||||||
|
* @param bytes 字节数组
|
||||||
|
* @return 字符串形式的 <tt>bytes</tt>
|
||||||
|
*/
|
||||||
|
public static String toHexString(byte[] bytes) {
|
||||||
|
if (bytes == null)
|
||||||
|
return "null";
|
||||||
|
int iMax = bytes.length - 1;
|
||||||
|
if (iMax == -1)
|
||||||
|
return "[]";
|
||||||
|
StringBuilder b = new StringBuilder();
|
||||||
|
b.append('[');
|
||||||
|
for (int i = 0; ; i++) {
|
||||||
|
b.append(String.format("%02x", bytes[i] & 0xFF));
|
||||||
|
if (i == iMax)
|
||||||
|
return b.append(']').toString();
|
||||||
|
b.append(", ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数组拷贝。
|
||||||
|
* @param src 字节数组。
|
||||||
|
* @param off 起始下标。
|
||||||
|
* @param len 拷贝长度。
|
||||||
|
* @return 指定长度的字节数组。
|
||||||
|
*/
|
||||||
|
public static byte[] copyFrom(byte[] src, int off, int len) {
|
||||||
|
// return Arrays.copyOfRange(src, off, off + len);
|
||||||
|
byte[] bits = new byte[len];
|
||||||
|
for (int i = off, j = 0; i < src.length && j < len; i++, j++) {
|
||||||
|
bits[j] = src[i];
|
||||||
|
}
|
||||||
|
return bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断 CPU Endian 是否为 Little
|
||||||
|
* @return 判断结果
|
||||||
|
*/
|
||||||
|
private static boolean isLittleEndian() {
|
||||||
|
return ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
package com.sarnath.modbusserver.dtuutils;
|
||||||
|
|
||||||
|
import io.netty.bootstrap.ServerBootstrap;
|
||||||
|
import io.netty.channel.*;
|
||||||
|
import io.netty.channel.nio.NioEventLoopGroup;
|
||||||
|
import io.netty.channel.socket.SocketChannel;
|
||||||
|
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||||
|
import io.netty.handler.logging.LogLevel;
|
||||||
|
import io.netty.handler.logging.LoggingHandler;
|
||||||
|
|
||||||
|
public class DTUServer {
|
||||||
|
|
||||||
|
static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));
|
||||||
|
public static void Start()
|
||||||
|
{
|
||||||
|
//主工作线程组,设置为1个线程
|
||||||
|
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
|
||||||
|
//工作线程组,默认为内核数*2的线程数
|
||||||
|
EventLoopGroup workerGroup = new NioEventLoopGroup();
|
||||||
|
try {
|
||||||
|
ServerBootstrap b = new ServerBootstrap();
|
||||||
|
b.group(bossGroup, workerGroup)
|
||||||
|
.channel(NioServerSocketChannel.class)
|
||||||
|
.option(ChannelOption.SO_BACKLOG, 100)
|
||||||
|
.childHandler(new ChannelInitializer<SocketChannel>() {
|
||||||
|
@Override
|
||||||
|
public void initChannel(SocketChannel ch) throws Exception {
|
||||||
|
ChannelPipeline p = ch.pipeline();
|
||||||
|
p.addLast(new LoggingHandler(LogLevel.INFO));
|
||||||
|
p.addLast(new DTUServerHandler());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Start the server.
|
||||||
|
ChannelFuture f = b.bind(PORT).sync();
|
||||||
|
// Wait until the server socket is closed.
|
||||||
|
f.channel().closeFuture().sync();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
// Shut down all event loops to terminate all threads.
|
||||||
|
bossGroup.shutdownGracefully();
|
||||||
|
workerGroup.shutdownGracefully();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,137 @@
|
||||||
|
package com.sarnath.modbusserver.dtuutils;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
|
||||||
|
public class DTUServerHandler extends ChannelInboundHandlerAdapter {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(DTUServerHandler.class);
|
||||||
|
|
||||||
|
private byte[] cBuf = new byte[0];
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void channelRead(ChannelHandlerContext ctx, Object msg) {
|
||||||
|
InetSocketAddress ipSocket = (InetSocketAddress )ctx.channel().remoteAddress();
|
||||||
|
String addr = ipSocket.getAddress().getHostAddress();
|
||||||
|
int port = ipSocket.getPort();
|
||||||
|
LOG.info("通道读取,对方位于" + addr + ":" + port);
|
||||||
|
ByteBuf buffer = (ByteBuf)msg;
|
||||||
|
if (buffer != null)
|
||||||
|
{
|
||||||
|
//string msg = BitConverter.ToString(buffer.Array);
|
||||||
|
//LogUtil.Info(msg);
|
||||||
|
// 读取长度
|
||||||
|
int len = buffer.readableBytes();
|
||||||
|
byte[] curBuf = new byte[len];
|
||||||
|
buffer.readBytes(curBuf,0,len);
|
||||||
|
//System.arraycopy(buffer.array(), buffer.arrayOffset(), curBuf, 0, len);
|
||||||
|
String tempMsg = BitConverter.toHexString(curBuf);
|
||||||
|
LOG.info("xxxxxxxxx:"+tempMsg);
|
||||||
|
//
|
||||||
|
Unpacking(curBuf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 递归处理当前包
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="curBuf"></param>
|
||||||
|
private void Unpacking(byte[] curBuf)
|
||||||
|
{
|
||||||
|
// 读取长度
|
||||||
|
int len = curBuf.length;
|
||||||
|
// 定义拼接数据包
|
||||||
|
byte[] revBuf = new byte[len + cBuf.length];
|
||||||
|
// 拼接 缓存数据包
|
||||||
|
System.arraycopy(cBuf, 0, revBuf, 0, cBuf.length);
|
||||||
|
// 拼接 新接收数据包
|
||||||
|
System.arraycopy(curBuf, 0, revBuf, cBuf.length, len);
|
||||||
|
// 使用完重置缓存包
|
||||||
|
cBuf = new byte[0];
|
||||||
|
// 包长判断
|
||||||
|
if (len >= 4)
|
||||||
|
{
|
||||||
|
String tempMsg = BitConverter.toHexString(revBuf);
|
||||||
|
LOG.info("xxxxxxxxx:"+tempMsg);
|
||||||
|
|
||||||
|
String tempMsg2 = BitConverter.toHexString(BitConverter.copyFrom(revBuf, 2, 2));
|
||||||
|
LOG.info("xxxxxxxxx2:"+tempMsg2);
|
||||||
|
LOG.info("xxxxxxxxx2:"+BitConverter.toHexString(BitConverter.getBytes((short) 6)));
|
||||||
|
//2 3 位为整包长度
|
||||||
|
short packageLen = BitConverter.toShort(BitConverter.copyFrom(revBuf, 2, 2));
|
||||||
|
LOG.info("packageLen:"+packageLen);
|
||||||
|
if (packageLen==0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (packageLen > revBuf.length)
|
||||||
|
{
|
||||||
|
// 缓存断包 等待下一个数据包
|
||||||
|
LOG.info("缓存断包!");
|
||||||
|
cBuf = revBuf;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 根据长度 拆包
|
||||||
|
byte[] comBuf = new byte[packageLen];
|
||||||
|
System.arraycopy(revBuf, 0, comBuf, 0, packageLen);
|
||||||
|
// 业务处理
|
||||||
|
DoSomeThing(comBuf);
|
||||||
|
//// 重置缓存包
|
||||||
|
//cBuf = new byte[0];
|
||||||
|
int remLen = revBuf.length - packageLen;
|
||||||
|
if (remLen>0)
|
||||||
|
{
|
||||||
|
LOG.info("粘包处理!");
|
||||||
|
// 重置当前处理包
|
||||||
|
curBuf = new byte[remLen];
|
||||||
|
System.arraycopy(revBuf, packageLen, curBuf, 0, remLen);
|
||||||
|
// 递归处理剩余数据包
|
||||||
|
Unpacking(curBuf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 缓存断包 等待下一个数据包
|
||||||
|
LOG.info("缓存断包!");
|
||||||
|
cBuf = revBuf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void channelReadComplete(ChannelHandlerContext ctx) {
|
||||||
|
ctx.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||||
|
// Close the connection when an exception is raised.
|
||||||
|
cause.printStackTrace();
|
||||||
|
ctx.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DoSomeThing(byte[] comBuf)
|
||||||
|
{
|
||||||
|
String msg = BitConverter.toHexString(comBuf);
|
||||||
|
LOG.info("DoSomeThing:"+msg);
|
||||||
|
|
||||||
|
//int dataLen = BitConverter.ToInt16(comBuf, 2) - 14;
|
||||||
|
//byte[] DataByte = new byte[dataLen];
|
||||||
|
//Array.ConstrainedCopy(comBuf, 14, DataByte, 0, dataLen);
|
||||||
|
|
||||||
|
//// 0 1 头
|
||||||
|
//// 2 3 长度
|
||||||
|
//// 4 5 厂站号
|
||||||
|
//int stationId = BitConverter.ToInt16(comBuf, 4);
|
||||||
|
//// 6 7 通道号
|
||||||
|
//int channelId = BitConverter.ToInt16(comBuf, 6);
|
||||||
|
//int startAddress = BitConverter.ToInt16(comBuf, 11);
|
||||||
|
//int type = Convert.ToInt16(comBuf[9]);
|
||||||
|
//LogUtil.Info("场站号:" + stationId.ToString() + ", 通道号:" + channelId.ToString() + ", 起始地址:" + startAddress+",type:"+ type);
|
||||||
|
//LogUtil.Info(stationId+":"+string.Join("-", GetRaw(DataByte)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
Spring Boot Version: ${spring-boot.version}
|
||||||
|
Spring Application Name: ${spring.application.name}
|
||||||
|
_____ .______. _________
|
||||||
|
/ \ ____ __| _/\_ |__ __ __ ______ / _____/ ______________ __ ___________
|
||||||
|
/ \ / \ / _ \ / __ | | __ \| | \/ ___/ ______ \_____ \_/ __ \_ __ \ \/ // __ \_ __ \
|
||||||
|
/ Y ( <_> ) /_/ | | \_\ \ | /\___ \ /_____/ / \ ___/| | \/\ /\ ___/| | \/
|
||||||
|
\____|__ /\____/\____ | |___ /____//____ > /_______ /\___ >__| \_/ \___ >__|
|
||||||
|
\/ \/ \/ \/ \/ \/ \/
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
# Tomcat
|
||||||
|
server:
|
||||||
|
port: 8710
|
||||||
|
|
||||||
|
# Spring
|
||||||
|
spring:
|
||||||
|
application:
|
||||||
|
# 应用名称
|
||||||
|
name: modbus-server
|
||||||
|
profiles:
|
||||||
|
# 环境配置
|
||||||
|
active: dev
|
||||||
|
cloud:
|
||||||
|
nacos:
|
||||||
|
discovery:
|
||||||
|
# 服务注册地址
|
||||||
|
server-addr: 127.0.0.1:8848
|
||||||
|
config:
|
||||||
|
# 配置中心地址
|
||||||
|
server-addr: 127.0.0.1:8848
|
||||||
|
# 配置文件格式
|
||||||
|
file-extension: yml
|
||||||
|
# 共享配置
|
||||||
|
shared-dataids: application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<configuration scan="true" scanPeriod="60 seconds" debug="false">
|
||||||
|
<!-- 日志存放路径 -->
|
||||||
|
<property name="log.path" value="/home/ruoyi/logs/ruoyi-job" />
|
||||||
|
<!-- 日志输出格式 -->
|
||||||
|
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />:ss} %-5level ${springAppName:-} %thread %logger %msg%n"/>
|
||||||
|
|
||||||
|
<!-- 控制台输出 -->
|
||||||
|
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
|
<encoder>
|
||||||
|
<pattern>${log.pattern}</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<!-- 系统日志输出 -->
|
||||||
|
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
|
<file>${log.path}/info.log</file>
|
||||||
|
<!-- 循环政策:基于时间创建日志文件 -->
|
||||||
|
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||||
|
<!-- 日志文件名格式 -->
|
||||||
|
<fileNamePattern>${log.path}/info.%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||||
|
<!-- 日志最大的历史 60天 -->
|
||||||
|
<maxHistory>60</maxHistory>
|
||||||
|
</rollingPolicy>
|
||||||
|
<encoder>
|
||||||
|
<pattern>${log.pattern}</pattern>
|
||||||
|
</encoder>
|
||||||
|
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||||
|
<!-- 过滤的级别 -->
|
||||||
|
<level>INFO</level>
|
||||||
|
<!-- 匹配时的操作:接收(记录) -->
|
||||||
|
<onMatch>ACCEPT</onMatch>
|
||||||
|
<!-- 不匹配时的操作:拒绝(不记录) -->
|
||||||
|
<onMismatch>DENY</onMismatch>
|
||||||
|
</filter>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
|
<file>${log.path}/error.log</file>
|
||||||
|
<!-- 循环政策:基于时间创建日志文件 -->
|
||||||
|
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||||
|
<!-- 日志文件名格式 -->
|
||||||
|
<fileNamePattern>${log.path}/error.%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||||
|
<!-- 日志最大的历史 60天 -->
|
||||||
|
<maxHistory>60</maxHistory>
|
||||||
|
</rollingPolicy>
|
||||||
|
<encoder>
|
||||||
|
<pattern>${log.pattern}</pattern>
|
||||||
|
</encoder>
|
||||||
|
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||||
|
<!-- 过滤的级别 -->
|
||||||
|
<level>ERROR</level>
|
||||||
|
<!-- 匹配时的操作:接收(记录) -->
|
||||||
|
<onMatch>ACCEPT</onMatch>
|
||||||
|
<!-- 不匹配时的操作:拒绝(不记录) -->
|
||||||
|
<onMismatch>DENY</onMismatch>
|
||||||
|
</filter>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<!-- 系统模块日志级别控制 -->
|
||||||
|
<logger name="com.ruoyi" level="info" />
|
||||||
|
<!-- Spring日志级别控制 -->
|
||||||
|
<logger name="org.springframework" level="warn" />
|
||||||
|
|
||||||
|
<root level="info">
|
||||||
|
<appender-ref ref="console" />
|
||||||
|
</root>
|
||||||
|
|
||||||
|
<!--系统操作日志-->
|
||||||
|
<root level="info">
|
||||||
|
<appender-ref ref="file_info" />
|
||||||
|
<appender-ref ref="file_error" />
|
||||||
|
</root>
|
||||||
|
</configuration>
|
||||||
1
pom.xml
1
pom.xml
|
|
@ -213,6 +213,7 @@
|
||||||
<module>ruoyi-modules</module>
|
<module>ruoyi-modules</module>
|
||||||
<module>ruoyi-api</module>
|
<module>ruoyi-api</module>
|
||||||
<module>ruoyi-common</module>
|
<module>ruoyi-common</module>
|
||||||
|
<module>modbus-server</module>
|
||||||
</modules>
|
</modules>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,380 @@
|
||||||
|
package com.ruoyi.common.core.utils;
|
||||||
|
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数字转字节数组工具类
|
||||||
|
*/
|
||||||
|
public class BitConverter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 以字节数组的形式返回指定的布尔值
|
||||||
|
* @param data 一个布尔值
|
||||||
|
* @return 长度为 1 的字节数组
|
||||||
|
*/
|
||||||
|
public static byte[] getBytes(boolean data) {
|
||||||
|
byte[] bytes = new byte[1];
|
||||||
|
bytes[0] = (byte) (data ? 1 : 0);
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 以字节数组的形式返回指定的 16 位有符号整数值
|
||||||
|
* @param data 要转换的数字
|
||||||
|
* @return 长度为 2 的字节数组
|
||||||
|
*/
|
||||||
|
public static byte[] getBytes(short data) {
|
||||||
|
byte[] bytes = new byte[2];
|
||||||
|
if (isLittleEndian()) {
|
||||||
|
bytes[0] = (byte) (data & 0xff);
|
||||||
|
bytes[1] = (byte) ((data & 0xff00) >> 8);
|
||||||
|
} else {
|
||||||
|
bytes[1] = (byte) (data & 0xff);
|
||||||
|
bytes[0] = (byte) ((data & 0xff00) >> 8);
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 以字节数组的形式返回指定的 Unicode 字符值
|
||||||
|
* @param data 要转换的字符
|
||||||
|
* @return 长度为 2 的字节数组
|
||||||
|
*/
|
||||||
|
public static byte[] getBytes(char data) {
|
||||||
|
byte[] bytes = new byte[2];
|
||||||
|
if (isLittleEndian()) {
|
||||||
|
bytes[0] = (byte) (data);
|
||||||
|
bytes[1] = (byte) (data >> 8);
|
||||||
|
} else {
|
||||||
|
bytes[1] = (byte) (data);
|
||||||
|
bytes[0] = (byte) (data >> 8);
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 以字节数组的形式返回指定的 32 位有符号整数值
|
||||||
|
* @param data 要转换的数字
|
||||||
|
* @return 长度为 4 的字节数组
|
||||||
|
*/
|
||||||
|
public static byte[] getBytes(int data) {
|
||||||
|
byte[] bytes = new byte[4];
|
||||||
|
if (isLittleEndian()) {
|
||||||
|
bytes[0] = (byte) (data & 0xff);
|
||||||
|
bytes[1] = (byte) ((data & 0xff00) >> 8);
|
||||||
|
bytes[2] = (byte) ((data & 0xff0000) >> 16);
|
||||||
|
bytes[3] = (byte) ((data & 0xff000000) >> 24);
|
||||||
|
} else {
|
||||||
|
bytes[3] = (byte) (data & 0xff);
|
||||||
|
bytes[2] = (byte) ((data & 0xff00) >> 8);
|
||||||
|
bytes[1] = (byte) ((data & 0xff0000) >> 16);
|
||||||
|
bytes[0] = (byte) ((data & 0xff000000) >> 24);
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 以字节数组的形式返回指定的 64 位有符号整数值
|
||||||
|
* @param data 要转换的数字
|
||||||
|
* @return 长度为 8 的字节数组
|
||||||
|
*/
|
||||||
|
public static byte[] getBytes(long data) {
|
||||||
|
byte[] bytes = new byte[8];
|
||||||
|
if (isLittleEndian()) {
|
||||||
|
bytes[0] = (byte) (data & 0xff);
|
||||||
|
bytes[1] = (byte) ((data >> 8) & 0xff);
|
||||||
|
bytes[2] = (byte) ((data >> 16) & 0xff);
|
||||||
|
bytes[3] = (byte) ((data >> 24) & 0xff);
|
||||||
|
bytes[4] = (byte) ((data >> 32) & 0xff);
|
||||||
|
bytes[5] = (byte) ((data >> 40) & 0xff);
|
||||||
|
bytes[6] = (byte) ((data >> 48) & 0xff);
|
||||||
|
bytes[7] = (byte) ((data >> 56) & 0xff);
|
||||||
|
} else {
|
||||||
|
bytes[7] = (byte) (data & 0xff);
|
||||||
|
bytes[6] = (byte) ((data >> 8) & 0xff);
|
||||||
|
bytes[5] = (byte) ((data >> 16) & 0xff);
|
||||||
|
bytes[4] = (byte) ((data >> 24) & 0xff);
|
||||||
|
bytes[3] = (byte) ((data >> 32) & 0xff);
|
||||||
|
bytes[2] = (byte) ((data >> 40) & 0xff);
|
||||||
|
bytes[1] = (byte) ((data >> 48) & 0xff);
|
||||||
|
bytes[0] = (byte) ((data >> 56) & 0xff);
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 以字节数组的形式返回指定的单精度浮点值
|
||||||
|
* @param data 要转换的数字
|
||||||
|
* @return 长度为 4 的字节数组
|
||||||
|
*/
|
||||||
|
public static byte[] getBytes(float data) {
|
||||||
|
return getBytes(Float.floatToIntBits(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 以字节数组的形式返回指定的双精度浮点值
|
||||||
|
* @param data 要转换的数字
|
||||||
|
* @return 长度为 8 的字节数组
|
||||||
|
*/
|
||||||
|
public static byte[] getBytes(double data) {
|
||||||
|
return getBytes(Double.doubleToLongBits(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将指定字符串中的所有字符编码为一个字节序列
|
||||||
|
* @param data 包含要编码的字符的字符串
|
||||||
|
* @return 一个字节数组,包含对指定的字符集进行编码的结果
|
||||||
|
*/
|
||||||
|
public static byte[] getBytes(String data) {
|
||||||
|
return data.getBytes(Charset.forName("UTF-8"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将指定字符串中的所有字符编码为一个字节序列
|
||||||
|
* @param data 包含要编码的字符的字符串
|
||||||
|
* @param charsetName 字符集编码
|
||||||
|
* @return 一个字节数组,包含对指定的字符集进行编码的结果
|
||||||
|
*/
|
||||||
|
public static byte[] getBytes(String data, String charsetName) {
|
||||||
|
return data.getBytes(Charset.forName(charsetName));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回由字节数组转换来的布尔值
|
||||||
|
* @param bytes 字节数组
|
||||||
|
* @return 布尔值
|
||||||
|
*/
|
||||||
|
public static boolean toBoolean(byte[] bytes) {
|
||||||
|
return bytes[0] == 0 ? false : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回由字节数组中的指定的一个字节转换来的布尔值
|
||||||
|
* @param bytes 字节数组
|
||||||
|
* @param startIndex 起始下标
|
||||||
|
* @return 布尔值
|
||||||
|
*/
|
||||||
|
public static boolean toBoolean(byte[] bytes, int startIndex) {
|
||||||
|
return toBoolean(copyFrom(bytes, startIndex, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回由字节数组转换来的 16 位有符号整数
|
||||||
|
* @param bytes 字节数组
|
||||||
|
* @return 由两个字节构成的 16 位有符号整数
|
||||||
|
*/
|
||||||
|
public static short toShort(byte[] bytes) {
|
||||||
|
if (isLittleEndian()) {
|
||||||
|
return (short) ((0xff & bytes[0]) | (0xff00 & (bytes[1] << 8)));
|
||||||
|
} else {
|
||||||
|
return (short) ((0xff & bytes[1]) | (0xff00 & (bytes[0] << 8)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回由字节数组中的指定的两个字节转换来的 16 位有符号整数
|
||||||
|
* @param bytes 字节数组
|
||||||
|
* @param startIndex 起始下标
|
||||||
|
* @return 由两个字节构成的 16 位有符号整数
|
||||||
|
*/
|
||||||
|
public static short toShort(byte[] bytes, int startIndex) {
|
||||||
|
return toShort(copyFrom(bytes, startIndex, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回由字节数组转换来的 Unicode 字符
|
||||||
|
* @param bytes 字节数组
|
||||||
|
* @return 由两个字节构成的字符
|
||||||
|
*/
|
||||||
|
public static char toChar(byte[] bytes) {
|
||||||
|
if (isLittleEndian()) {
|
||||||
|
return (char) ((0xff & bytes[0]) | (0xff00 & (bytes[1] << 8)));
|
||||||
|
} else {
|
||||||
|
return (char) ((0xff & bytes[1]) | (0xff00 & (bytes[0] << 8)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回由字节数组中的指定的两个字节转换来的 Unicode 字符
|
||||||
|
* @param bytes 字节数组
|
||||||
|
* @param startIndex 起始下标
|
||||||
|
* @return 由两个字节构成的字符
|
||||||
|
*/
|
||||||
|
public static char toChar(byte[] bytes, int startIndex) {
|
||||||
|
return toChar(copyFrom(bytes, startIndex, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回由字节数组转换来的 32 位有符号整数
|
||||||
|
* @param bytes 字节数组
|
||||||
|
* @return 由四个字节构成的 32 位有符号整数
|
||||||
|
*/
|
||||||
|
public static int toInt(byte[] bytes) {
|
||||||
|
if (isLittleEndian()) {
|
||||||
|
return (0xff & bytes[0])
|
||||||
|
| (0xff00 & (bytes[1] << 8))
|
||||||
|
| (0xff0000 & (bytes[2] << 16))
|
||||||
|
| (0xff000000 & (bytes[3] << 24));
|
||||||
|
} else {
|
||||||
|
return (0xff & bytes[3])
|
||||||
|
| (0xff00 & (bytes[2] << 8))
|
||||||
|
| (0xff0000 & (bytes[1] << 16))
|
||||||
|
| (0xff000000 & (bytes[0] << 24));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回由字节数组中的指定的四个字节转换来的 32 位有符号整数
|
||||||
|
* @param bytes 字节数组
|
||||||
|
* @param startIndex 起始下标
|
||||||
|
* @return 由四个字节构成的 32 位有符号整数
|
||||||
|
*/
|
||||||
|
public static int toInt(byte[] bytes, int startIndex) {
|
||||||
|
return toInt(copyFrom(bytes, startIndex, 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回由字节数组转换来的 64 位有符号整数
|
||||||
|
* @param bytes 字节数组
|
||||||
|
* @return 由八个字节构成的 64 位有符号整数
|
||||||
|
*/
|
||||||
|
public static long toLong(byte[] bytes) {
|
||||||
|
if (isLittleEndian()) {
|
||||||
|
return (0xffL & (long) bytes[0])
|
||||||
|
| (0xff00L & ((long) bytes[1] << 8))
|
||||||
|
| (0xff0000L & ((long) bytes[2] << 16))
|
||||||
|
| (0xff000000L & ((long) bytes[3] << 24))
|
||||||
|
| (0xff00000000L & ((long) bytes[4] << 32))
|
||||||
|
| (0xff0000000000L & ((long) bytes[5] << 40))
|
||||||
|
| (0xff000000000000L & ((long) bytes[6] << 48))
|
||||||
|
| (0xff00000000000000L & ((long) bytes[7] << 56));
|
||||||
|
} else {
|
||||||
|
return (0xffL & (long) bytes[7])
|
||||||
|
| (0xff00L & ((long) bytes[6] << 8))
|
||||||
|
| (0xff0000L & ((long) bytes[5] << 16))
|
||||||
|
| (0xff000000L & ((long) bytes[4] << 24))
|
||||||
|
| (0xff00000000L & ((long) bytes[3] << 32))
|
||||||
|
| (0xff0000000000L & ((long) bytes[2] << 40))
|
||||||
|
| (0xff000000000000L & ((long) bytes[1] << 48))
|
||||||
|
| (0xff00000000000000L & ((long) bytes[0] << 56));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回由字节数组中的指定的八个字节转换来的 64 位有符号整数
|
||||||
|
* @param bytes 字节数组
|
||||||
|
* @param startIndex 起始下标
|
||||||
|
* @return 由八个字节构成的 64 位有符号整数
|
||||||
|
*/
|
||||||
|
public static long toLong(byte[] bytes, int startIndex) {
|
||||||
|
return toLong(copyFrom(bytes, startIndex, 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回由字节数组转换来的单精度浮点数
|
||||||
|
* @param bytes 字节数组
|
||||||
|
* @return 由四个字节构成的单精度浮点数
|
||||||
|
*/
|
||||||
|
public static float toFloat(byte[] bytes) {
|
||||||
|
return Float.intBitsToFloat(toInt(bytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回由字节数组中的指定的四个字节转换来的单精度浮点数
|
||||||
|
* @param bytes 字节数组
|
||||||
|
* @param startIndex 起始下标
|
||||||
|
* @return 由四个字节构成的单精度浮点数
|
||||||
|
*/
|
||||||
|
public static float toFloat(byte[] bytes, int startIndex) {
|
||||||
|
return Float.intBitsToFloat(toInt(copyFrom(bytes, startIndex, 4)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回由字节数组转换来的双精度浮点数
|
||||||
|
* @param bytes 字节数组
|
||||||
|
* @return 由八个字节构成的双精度浮点数
|
||||||
|
*/
|
||||||
|
public static double toDouble(byte[] bytes) {
|
||||||
|
return Double.longBitsToDouble(toLong(bytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回由字节数组中的指定的八个字节转换来的双精度浮点数
|
||||||
|
* @param bytes 字节数组
|
||||||
|
* @param startIndex 起始下标
|
||||||
|
* @return 由八个字节构成的双精度浮点数
|
||||||
|
*/
|
||||||
|
public static double toDouble(byte[] bytes, int startIndex) {
|
||||||
|
return Double.longBitsToDouble(toLong(copyFrom(bytes, startIndex, 8)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回由字节数组转换来的字符串
|
||||||
|
* @param bytes 字节数组
|
||||||
|
* @return 字符串
|
||||||
|
*/
|
||||||
|
public static String toString(byte[] bytes) {
|
||||||
|
return new String(bytes, Charset.forName("UTF-8"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回由字节数组转换来的字符串
|
||||||
|
* @param bytes 字节数组
|
||||||
|
* @param charsetName 字符集编码
|
||||||
|
* @return 字符串
|
||||||
|
*/
|
||||||
|
public static String toString(byte[] bytes, String charsetName) {
|
||||||
|
return new String(bytes, Charset.forName(charsetName));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 以字符串表示形式返回字节数组的内容
|
||||||
|
* @param bytes 字节数组
|
||||||
|
* @return 字符串形式的 <tt>bytes</tt>
|
||||||
|
*/
|
||||||
|
public static String toHexString(byte[] bytes) {
|
||||||
|
if (bytes == null)
|
||||||
|
return "null";
|
||||||
|
int iMax = bytes.length - 1;
|
||||||
|
if (iMax == -1)
|
||||||
|
return "[]";
|
||||||
|
StringBuilder b = new StringBuilder();
|
||||||
|
b.append('[');
|
||||||
|
for (int i = 0; ; i++) {
|
||||||
|
b.append(String.format("%02x", bytes[i] & 0xFF));
|
||||||
|
if (i == iMax)
|
||||||
|
return b.append(']').toString();
|
||||||
|
b.append(", ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数组拷贝。
|
||||||
|
* @param src 字节数组。
|
||||||
|
* @param off 起始下标。
|
||||||
|
* @param len 拷贝长度。
|
||||||
|
* @return 指定长度的字节数组。
|
||||||
|
*/
|
||||||
|
private static byte[] copyFrom(byte[] src, int off, int len) {
|
||||||
|
// return Arrays.copyOfRange(src, off, off + len);
|
||||||
|
byte[] bits = new byte[len];
|
||||||
|
for (int i = off, j = 0; i < src.length && j < len; i++, j++) {
|
||||||
|
bits[j] = src[i];
|
||||||
|
}
|
||||||
|
return bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断 CPU Endian 是否为 Little
|
||||||
|
* @return 判断结果
|
||||||
|
*/
|
||||||
|
private static boolean isLittleEndian() {
|
||||||
|
return ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -11,7 +11,7 @@ import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||||
* @author ruoyi
|
* @author ruoyi
|
||||||
*/
|
*/
|
||||||
@EnableDiscoveryClient
|
@EnableDiscoveryClient
|
||||||
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class })
|
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class })// 关闭自动配置功能
|
||||||
public class RuoYiGatewayApplication
|
public class RuoYiGatewayApplication
|
||||||
{
|
{
|
||||||
public static void main(String[] args)
|
public static void main(String[] args)
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>ruoyi-visual</artifactId>
|
||||||
|
<groupId>com.ruoyi</groupId>
|
||||||
|
<version>2.0.0</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>hystrix-dashboard</artifactId>
|
||||||
|
<dependencies>
|
||||||
|
|
||||||
|
<!-- SpringCloud Ailibaba Nacos -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- SpringCloud Ailibaba Nacos Config -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- SpringCloud Ailibaba Sentinel -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- SpringBoot Web -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.netflix.hystrix</groupId>
|
||||||
|
<artifactId>hystrix-javanica</artifactId>
|
||||||
|
<version>RELEASE</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-netflix-hystrix-dashboard</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>repackage</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.sarnath.hystrix.dashboard;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.cloud.client.SpringCloudApplication;
|
||||||
|
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
|
||||||
|
|
||||||
|
@EnableHystrixDashboard
|
||||||
|
@SpringCloudApplication
|
||||||
|
public class HystrixDashBoardApplication {
|
||||||
|
public static void main(String[] args)
|
||||||
|
{
|
||||||
|
SpringApplication.run(HystrixDashBoardApplication.class, args);
|
||||||
|
System.out.println("(♥◠‿◠)ノ゙ hystrix-dashboard服务模块启动成功 ლ(´ڡ`ლ)゙ \n" +
|
||||||
|
"____.___._________ \n" +
|
||||||
|
"\\__ | |\\_ ___ \\ \n" +
|
||||||
|
" / | |/ \\ \\/ \n" +
|
||||||
|
" \\____ |\\ \\____\n" +
|
||||||
|
" / ______| \\______ /\n" +
|
||||||
|
" \\/ \\/ ");
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
Spring Boot Version: ${spring-boot.version}
|
||||||
|
Spring Application Name: ${spring.application.name}
|
||||||
|
.__ __ .__ .___ .__ ___. .___
|
||||||
|
| |__ ___.__. _______/ |________|__|__ ___ __| _/____ _____| |__\_ |__ _________ _______ __| _/
|
||||||
|
| | < | |/ ___/\ __\_ __ \ \ \/ / ______ / __ |\__ \ / ___/ | \| __ \ / _ \__ \\_ __ \/ __ |
|
||||||
|
| Y \___ |\___ \ | | | | \/ |> < /_____/ / /_/ | / __ \_\___ \| Y \ \_\ ( <_> ) __ \| | \/ /_/ |
|
||||||
|
|___| / ____/____ > |__| |__| |__/__/\_ \ \____ |(____ /____ >___| /___ /\____(____ /__| \____ |
|
||||||
|
\/\/ \/ \/ \/ \/ \/ \/ \/ \/ \/
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
# Tomcat
|
||||||
|
server:
|
||||||
|
port: 8100
|
||||||
|
|
||||||
|
# Spring
|
||||||
|
spring:
|
||||||
|
application:
|
||||||
|
# 应用名称
|
||||||
|
name: hystrix-dashboard
|
||||||
|
profiles:
|
||||||
|
# 环境配置
|
||||||
|
active: dev
|
||||||
|
cloud:
|
||||||
|
nacos:
|
||||||
|
discovery:
|
||||||
|
# 服务注册地址
|
||||||
|
server-addr: 127.0.0.1:8848
|
||||||
|
config:
|
||||||
|
# 配置中心地址
|
||||||
|
server-addr: 127.0.0.1:8848
|
||||||
|
# 配置文件格式
|
||||||
|
file-extension: yml
|
||||||
|
# 共享配置
|
||||||
|
shared-dataids: application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
<module>ruoyi-monitor</module>
|
<module>ruoyi-monitor</module>
|
||||||
|
<module>hystrix-dashboard</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<artifactId>ruoyi-visual</artifactId>
|
<artifactId>ruoyi-visual</artifactId>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue