图片转换工具开发

This commit is contained in:
2025-06-18 16:36:01 +08:00
parent 2795f725da
commit dbd26ccdc5
13 changed files with 754 additions and 173 deletions

View File

@@ -0,0 +1,72 @@
package com.njcn.common.utils;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
/**
* @author hongawen
* @version 1.0
* @data 2025/6/17 15:17
*/
public class BinaryDataConverter {
/**
* 使用Base64编码转换二进制数据为字符串
* 推荐使用,最安全可靠的方式
*/
public static String toBase64String(byte[] binaryData) {
return Base64.getEncoder().encodeToString(binaryData);
}
/**
* 使用Base64解码字符串为二进制数据
*/
public static byte[] fromBase64String(String base64String) {
return Base64.getDecoder().decode(base64String);
}
/**
* 使用UTF-8编码转换二进制数据为字符串
* 注意:仅适用于文本数据,不适用于二进制数据
*/
public static String toUtf8String(byte[] binaryData) {
return new String(binaryData, StandardCharsets.UTF_8);
}
/**
* 使用UTF-8编码转换字符串为二进制数据
*/
public static byte[] fromUtf8String(String text) {
return text.getBytes(StandardCharsets.UTF_8);
}
/**
* 使用十六进制字符串表示二进制数据
* 适用于需要可读性的场景
*/
public static String toHexString(byte[] binaryData) {
StringBuilder hexString = new StringBuilder();
for (byte b : binaryData) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
}
/**
* 从十六进制字符串转换为二进制数据
*/
public static byte[] fromHexString(String hexString) {
int len = hexString.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4)
+ Character.digit(hexString.charAt(i + 1), 16));
}
return data;
}
}

View File

@@ -0,0 +1,58 @@
package com.njcn.common.utils.images;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* @author hongawen
* @version 1.0
* @data 2025/6/18 11:17
*/
public class BinFileReader {
public static void main(String[] args) {
String filePath = "C:\\Users\\hongawen\\Desktop\\temp\\qrc.bin";
try (InputStream inputStream = new FileInputStream(filePath)) {
printHexDump(inputStream);
} catch (IOException e) {
System.err.println("读取文件时发生错误: " + e.getMessage());
}
}
public static void printHexDump(InputStream stream) throws IOException {
int bytesPerLine = 16;
byte[] buffer = new byte[bytesPerLine];
int bytesRead;
long offset = 0;
while ((bytesRead = stream.read(buffer)) != -1) {
// 打印偏移量 (地址)
System.out.printf("%08X: ", offset);
// 打印十六进制值
StringBuilder hexString = new StringBuilder();
StringBuilder asciiString = new StringBuilder();
for (int i = 0; i < bytesPerLine; i++) {
if (i < bytesRead) {
byte b = buffer[i];
hexString.append(String.format("%02X ", b));
// 将非打印字符替换为 '.'
asciiString.append(isPrintableChar(b) ? (char) b : '.');
} else {
// 用空格填充,如果最后一行不满
hexString.append(" ");
}
if (i == 7) { // 在中间加一个空格,方便阅读
hexString.append(" ");
}
}
System.out.printf("%-50s %s%n", hexString.toString(), asciiString.toString());
offset += bytesRead;
}
}
private static boolean isPrintableChar(byte b) {
return b >= 32 && b <= 126;
}
}

View File

@@ -1,6 +1,7 @@
package com.njcn.common.utils.images;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.ByteBuffer;
@@ -15,188 +16,81 @@ import java.nio.ByteOrder;
*/
public class ImageConverter {
/**
* 将BufferedImage转换为BMP格式的字节数组
* @param image BufferedImage对象
* @return BMP格式的字节数组
* @throws IOException 如果转换过程中发生IO错误
* 将一个 BufferedImage 对象转换为我们自定义的 BMPF .bin 格式。
*/
public static byte[] convertToBMP(BufferedImage image) throws IOException {
public static byte[] convertToBinFormat(BufferedImage image) throws IOException {
final int targetWidth = 148;
final int targetHeight = 148;
// 1. 创建一个新的、与目标格式(16-bpp)匹配的、固定尺寸的画布
BufferedImage newImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_USHORT_GRAY);
Graphics2D g2d = newImage.createGraphics();
// 2. 用白色(0xFFFF)填充背景
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, targetWidth, targetHeight);
// 3. 将原始图像居中绘制到新画布上
// Graphics2D 会自动处理颜色转换
int x = (targetWidth - image.getWidth()) / 2;
int y = (targetHeight - image.getHeight()) / 2;
g2d.drawImage(image, x, y, null);
g2d.dispose();
// 4. 后续操作基于新的、尺寸和位深度都正确的图像
final int bpp = 16;
// Stride: 每行像素数据的字节数。对于无填充的16bpp图像等于 宽度 * 2
final int stride = targetWidth * (bpp / 8);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (DataOutputStream out = new DataOutputStream(baos)) {
// --- 写入文件头 (严格44字节) ---
// 根据C代码 qrc_bitmap.pData = ...[44/4], 我们确认头部为44字节
ByteBuffer headerBuffer = ByteBuffer.allocate(44);
// 头部使用小端序
headerBuffer.order(ByteOrder.LITTLE_ENDIAN);
// 写入BMP文件头
writeBMPHeader(baos, image.getWidth(), image.getHeight());
// 0-3: 魔术字
headerBuffer.put(new byte[]{'b', 'm', 'p', 'f'});
// 4-5: 宽度
headerBuffer.putShort((short) targetWidth);
// 6-7: 高度
headerBuffer.putShort((short) targetHeight);
// 8-9: 行字节数
headerBuffer.putShort((short) stride);
// 10-11: 位深度
headerBuffer.putShort((short) bpp);
// 写入像素数据
for (int y = image.getHeight() - 1; y >= 0; y--) {
for (int x = 0; x < image.getWidth(); x++) {
int rgb = image.getRGB(x, y);
// 写入BGR格式BMP格式要求
// B
baos.write(rgb & 0xFF);
// G
baos.write((rgb >> 8) & 0xFF);
// R
baos.write((rgb >> 16) & 0xFF);
}
// 行对齐每行必须是4字节的倍数
int padding = (4 - (image.getWidth() * 3) % 4) % 4;
for (int i = 0; i < padding; i++) {
baos.write(0);
// 12-43: 剩余32字节严格参照样本文件(out.txt)填充
headerBuffer.put(new byte[]{
// 12 bytes of zeros
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 20 bytes of specific data from sample
(byte)0x44, (byte)0x3A, (byte)0x5C, (byte)0xD7, (byte)0xD4, (byte)0xB6, (byte)0xAF, (byte)0xBC,
(byte)0xEC, (byte)0xB2, (byte)0xE2, (byte)0xC6, (byte)0xBD, (byte)0xCC, (byte)0xA8, (byte)0x5C,
(byte)0x4C, (byte)0xAB, 0x00, 0x00
});
// 将44字节头部一次性写入
out.write(headerBuffer.array());
// --- 写入像素数据 ---
// 为确保数据布局的绝对正确,放弃直接访问数据缓冲区(getDataBuffer)
// 改为使用逐点扫描(getSample)的方式。这可以避免任何因Java内部内存对齐
// 或行填充(padding)导致的图像数据错位问题。
for (int r = 0; r < targetHeight; r++) {
for (int c = 0; c < targetWidth; c++) {
// getSample(x, y, band) 返回的是一个int, 但其值就是16位的ushort值
int pixelValue = newImage.getRaster().getSample(c, r, 0);
// 依旧使用大端序写入像素数据,这是上次测试得出的结论
out.writeShort((short) pixelValue);
}
}
}
return baos.toByteArray();
}
/**
* 将BMP格式的字节数组转换为BIN格式
* @param bmpData BMP格式的字节数组
* @return BIN格式的字节数组
*/
public static byte[] convertBMPToBIN(byte[] bmpData) {
// 跳过BMP文件头54字节
int offset = 54;
int width = ByteBuffer.wrap(bmpData, 18, 4).order(ByteOrder.LITTLE_ENDIAN).getInt();
int height = ByteBuffer.wrap(bmpData, 22, 4).order(ByteOrder.LITTLE_ENDIAN).getInt();
// 计算每行的字节数(包括对齐)
int rowSize = ((width * 3 + 3) / 4) * 4;
// 创建BIN数据数组
byte[] binData = new byte[width * height];
int binIndex = 0;
// 转换像素数据
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int bmpIndex = offset + (height - 1 - y) * rowSize + x * 3;
// 将BGR转换为灰度值
int b = bmpData[bmpIndex] & 0xFF;
int g = bmpData[bmpIndex + 1] & 0xFF;
int r = bmpData[bmpIndex + 2] & 0xFF;
// 使用加权平均计算灰度值
int gray = (int) (0.299 * r + 0.587 * g + 0.114 * b);
// 二值化阈值设为128
binData[binIndex++] = (byte) (gray > 128 ? 1 : 0);
}
}
return binData;
}
/**
* 将BufferedImage直接转换为BIN格式
* @param image BufferedImage对象
* @return BIN格式的字节数组
* @throws IOException 如果转换过程中发生IO错误
*/
public static byte[] convertToBIN(BufferedImage image) throws IOException {
byte[] bmpData = convertToBMP(image);
return convertBMPToBIN(bmpData);
}
/**
* 写入BMP文件头
* @param out 输出流
* @param width 图片宽度
* @param height 图片高度
* @throws IOException 如果写入过程中发生IO错误
*/
private static void writeBMPHeader(OutputStream out, int width, int height) throws IOException {
// 文件头14字节
out.write('B');
out.write('M');
writeInt(out, 54 + width * height * 3); // 文件大小
writeInt(out, 0); // 保留
writeInt(out, 54); // 数据偏移量
// 信息头40字节
writeInt(out, 40); // 信息头大小
writeInt(out, width); // 宽度
writeInt(out, height); // 高度
writeShort(out, 1); // 颜色平面数
writeShort(out, 24); // 每像素位数
writeInt(out, 0); // 压缩方式
writeInt(out, width * height * 3); // 图像数据大小
writeInt(out, 0); // 水平分辨率
writeInt(out, 0); // 垂直分辨率
writeInt(out, 0); // 使用的颜色数
writeInt(out, 0); // 重要颜色数
}
/**
* 写入一个整数(小端序)
* @param out 输出流
* @param value 要写入的整数
* @throws IOException 如果写入过程中发生IO错误
*/
private static void writeInt(OutputStream out, int value) throws IOException {
out.write(value & 0xFF);
out.write((value >> 8) & 0xFF);
out.write((value >> 16) & 0xFF);
out.write((value >> 24) & 0xFF);
}
/**
* 写入一个短整数(小端序)
* @param out 输出流
* @param value 要写入的短整数
* @throws IOException 如果写入过程中发生IO错误
*/
private static void writeShort(OutputStream out, int value) throws IOException {
out.write(value & 0xFF);
out.write((value >> 8) & 0xFF);
}
/**
* 将BufferedImage保存为BMP文件
* @param image BufferedImage对象
* @param filePath 保存路径
* @throws IOException 如果保存过程中发生IO错误
*/
public static void saveAsBMP(BufferedImage image, String filePath) throws IOException {
byte[] bmpData = convertToBMP(image);
try (FileOutputStream fos = new FileOutputStream(filePath)) {
fos.write(bmpData);
}
}
/**
* 将BufferedImage保存为BIN文件
* @param image BufferedImage对象
* @param filePath 保存路径
* @throws IOException 如果保存过程中发生IO错误
*/
public static void saveAsBIN(BufferedImage image, String filePath) throws IOException {
byte[] binData = convertToBIN(image);
try (FileOutputStream fos = new FileOutputStream(filePath)) {
fos.write(binData);
}
}
/**
* 将BMP数据保存为文件
* @param bmpData BMP格式的字节数组
* @param filePath 保存路径
* @throws IOException 如果保存过程中发生IO错误
*/
public static void saveBMPToFile(byte[] bmpData, String filePath) throws IOException {
try (FileOutputStream fos = new FileOutputStream(filePath)) {
fos.write(bmpData);
}
}
/**
* 将BIN数据保存为文件
* @param binData BIN格式的字节数组
* @param filePath 保存路径
* @throws IOException 如果保存过程中发生IO错误
*/
public static void saveBINToFile(byte[] binData, String filePath) throws IOException {
try (FileOutputStream fos = new FileOutputStream(filePath)) {
fos.write(binData);
}
}
}

View File

@@ -74,6 +74,13 @@
<version>${druid.version}</version>
</dependency>
<!--mariadb-->
<!-- <dependency>-->
<!-- <groupId>org.mariadb.jdbc</groupId>-->
<!-- <artifactId>mariadb-java-client</artifactId>-->
<!-- <version>3.3.2</version> -->
<!-- </dependency>-->
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>

4
njcn-plugin/README.md Normal file
View File

@@ -0,0 +1,4 @@
# 本模块用于收录各种插件工具
## HttpClient模块
封装RestTemplate用于远程接口访问

View File

@@ -0,0 +1,36 @@
<?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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.njcn</groupId>
<artifactId>njcn-plugin</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>RestTemplate-plugin</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.njcn</groupId>
<artifactId>njcn-common</artifactId>
<version>0.0.1</version>
</dependency>
<!-- Apache HttpClient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,53 @@
package com.njcn.http.config;
import com.njcn.http.util.RestTemplateUtil;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
/**
* @author hongawen
* @version 1.0
* @data 2025/6/17 11:15
*/
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
// 创建连接池管理器
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
// 设置最大连接数
connectionManager.setMaxTotal(200);
// 设置每个主机的最大连接数
connectionManager.setDefaultMaxPerRoute(20);
// 创建HttpClient
CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(connectionManager)
.build();
// 创建HttpComponentsClientHttpRequestFactory
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
// 设置连接超时时间(毫秒)
factory.setConnectTimeout(5000);
// 设置读取超时时间(毫秒)
factory.setReadTimeout(5000);
// 设置连接不够用的等待时间(毫秒)
factory.setConnectionRequestTimeout(2000);
return new RestTemplate(factory);
}
@Bean
@ConditionalOnMissingBean
public RestTemplateUtil restTemplateUtil(RestTemplate restTemplate) {
return new RestTemplateUtil(restTemplate);
}
}

View File

@@ -0,0 +1,291 @@
package com.njcn.http.util;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.*;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import java.io.File;
import java.util.Map;
/**
* @author hongawen
* @version 1.0
* @data 2025/6/17 11:18
*/
@Slf4j
public class RestTemplateUtil {
private final RestTemplate restTemplate;
public RestTemplateUtil(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
/**
* GET请求
*/
public <T> T get(String url, Class<T> responseType) {
return get(url, null, null, responseType);
}
/**
* GET请求带参数
*/
public <T> T get(String url, Map<String, Object> params, Class<T> responseType) {
return get(url, params, null, responseType);
}
/**
* GET请求带参数和请求头
*/
public <T> T get(String url, Map<String, Object> params, HttpHeaders headers, Class<T> responseType) {
try {
// 构建URL带参数
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
if (params != null) {
params.forEach(builder::queryParam);
}
// 构建请求头
HttpHeaders requestHeaders = new HttpHeaders();
if (headers != null) {
requestHeaders.addAll(headers);
}
// 发送请求
HttpEntity<?> requestEntity = new HttpEntity<>(requestHeaders);
ResponseEntity<T> response = restTemplate.exchange(
builder.build().encode().toUri(),
HttpMethod.GET,
requestEntity,
responseType
);
return response.getBody();
} catch (Exception e) {
log.error("GET请求异常: {}", e.getMessage(), e);
throw new RuntimeException("GET请求异常", e);
}
}
/**
* POST请求JSON数据
*/
public <T> T postJson(String url, Object body, Class<T> responseType) {
return postJson(url, body, null, responseType);
}
/**
* POST请求JSON数据带请求头
*/
public <T> T postJson(String url, Object body, HttpHeaders headers, Class<T> responseType) {
try {
// 构建请求头
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setContentType(MediaType.APPLICATION_JSON);
if (headers != null) {
requestHeaders.addAll(headers);
}
// 发送请求
HttpEntity<?> requestEntity = new HttpEntity<>(body, requestHeaders);
ResponseEntity<T> response = restTemplate.exchange(
url,
HttpMethod.POST,
requestEntity,
responseType
);
return response.getBody();
} catch (Exception e) {
log.error("POST请求异常: {}", e.getMessage(), e);
throw new RuntimeException("POST请求异常", e);
}
}
/**
* POST请求表单数据
*/
public <T> T postForm(String url, Map<String, Object> formData, Class<T> responseType) {
return postForm(url, formData, null, responseType);
}
/**
* POST请求表单数据带请求头
*/
public <T> T postForm(String url, Map<String, Object> formData, HttpHeaders headers, Class<T> responseType) {
try {
// 构建表单数据
MultiValueMap<String, Object> requestBody = new LinkedMultiValueMap<>();
if (formData != null) {
formData.forEach((key, value) -> requestBody.add(key, value));
}
// 构建请求头
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
if (headers != null) {
requestHeaders.addAll(headers);
}
// 发送请求
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(requestBody, requestHeaders);
ResponseEntity<T> response = restTemplate.exchange(
url,
HttpMethod.POST,
requestEntity,
responseType
);
return response.getBody();
} catch (Exception e) {
log.error("POST表单请求异常: {}", e.getMessage(), e);
throw new RuntimeException("POST表单请求异常", e);
}
}
/**
* PUT请求
*/
public <T> T put(String url, Object body, Class<T> responseType) {
return put(url, body, null, responseType);
}
/**
* PUT请求带请求头
*/
public <T> T put(String url, Object body, HttpHeaders headers, Class<T> responseType) {
try {
// 构建请求头
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setContentType(MediaType.APPLICATION_JSON);
if (headers != null) {
requestHeaders.addAll(headers);
}
// 发送请求
HttpEntity<?> requestEntity = new HttpEntity<>(body, requestHeaders);
ResponseEntity<T> response = restTemplate.exchange(
url,
HttpMethod.PUT,
requestEntity,
responseType
);
return response.getBody();
} catch (Exception e) {
log.error("PUT请求异常: {}", e.getMessage(), e);
throw new RuntimeException("PUT请求异常", e);
}
}
/**
* DELETE请求
*/
public <T> T delete(String url, Class<T> responseType) {
return delete(url, null, responseType);
}
/**
* DELETE请求带请求头
*/
public <T> T delete(String url, HttpHeaders headers, Class<T> responseType) {
try {
// 构建请求头
HttpHeaders requestHeaders = new HttpHeaders();
if (headers != null) {
requestHeaders.addAll(headers);
}
// 发送请求
HttpEntity<?> requestEntity = new HttpEntity<>(requestHeaders);
ResponseEntity<T> response = restTemplate.exchange(
url,
HttpMethod.DELETE,
requestEntity,
responseType
);
return response.getBody();
} catch (Exception e) {
log.error("DELETE请求异常: {}", e.getMessage(), e);
throw new RuntimeException("DELETE请求异常", e);
}
}
/**
* 文件上传
*/
public <T> T uploadFile(String url, File file, String fileParamName, Map<String, Object> params, Class<T> responseType) {
try {
// 构建表单数据
MultiValueMap<String, Object> requestBody = new LinkedMultiValueMap<>();
requestBody.add(fileParamName, new FileSystemResource(file));
if (params != null) {
params.forEach(requestBody::add);
}
// 构建请求头
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
// 发送请求
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(requestBody, requestHeaders);
ResponseEntity<T> response = restTemplate.exchange(
url,
HttpMethod.POST,
requestEntity,
responseType
);
return response.getBody();
} catch (Exception e) {
log.error("文件上传异常: {}", e.getMessage(), e);
throw new RuntimeException("文件上传异常", e);
}
}
/**
* 文件上传
*/
public ResponseEntity<String> uploadFile(String url, File file) {
try {
// 构建表单数据
MultiValueMap<String, Object> requestBody = new LinkedMultiValueMap<>();
requestBody.add("file", new FileSystemResource(file));
// 构建请求头
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
// 发送请求
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(requestBody, requestHeaders);
return restTemplate.exchange(
url,
HttpMethod.POST,
requestEntity,
String.class
);
} catch (Exception e) {
log.error("文件上传异常: {}", e.getMessage(), e);
throw new RuntimeException("文件上传异常", e);
}
}
/**
* 文件下载
*/
public byte[] downloadFile(String url) {
try {
ResponseEntity<byte[]> response = restTemplate.getForEntity(url, byte[].class);
return response.getBody();
} catch (Exception e) {
log.error("文件下载异常: {}", e.getMessage(), e);
throw new RuntimeException("文件下载异常", e);
}
}
}

View File

@@ -0,0 +1,2 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.njcn.http.config.RestTemplateConfig

23
njcn-plugin/pom.xml Normal file
View File

@@ -0,0 +1,23 @@
<?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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.njcn</groupId>
<artifactId>BasicDependVersion</artifactId>
<version>1.0.0</version>
</parent>
<packaging>pom</packaging>
<artifactId>njcn-plugin</artifactId>
<modules>
<module>RestTemplate-plugin</module>
</modules>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>

View File

@@ -0,0 +1,140 @@
package com.njcn.web.utils;
/**
* @author hongawen
* @version 1.0
* @data 2025/2/25 13:24
*/
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
public class ThreePhaseUnbalance {
// 定义复数类
static class Complex {
BigDecimal real; // 复数的实部
BigDecimal imag; // 复数的虚部
// 构造函数,初始化复数的实部和虚部
public Complex(BigDecimal real, BigDecimal imag) {
this.real = real;
this.imag = imag;
}
// 复数加法
public Complex add(Complex other) {
return new Complex(this.real.add(other.real), this.imag.add(other.imag));
}
// 复数乘法
public Complex multiply(Complex other) {
BigDecimal real = this.real.multiply(other.real).subtract(this.imag.multiply(other.imag));
BigDecimal imag = this.real.multiply(other.imag).add(this.imag.multiply(other.real));
return new Complex(real, imag);
}
// 复数除以一个实数
public Complex divide(BigDecimal divisor) {
return new Complex(this.real.divide(divisor, MathContext.DECIMAL128),
this.imag.divide(divisor, MathContext.DECIMAL128));
}
// 计算复数的模(幅值)
public BigDecimal magnitude() {
return BigDecimal.valueOf(Math.sqrt(real.multiply(real).add(imag.multiply(imag)).doubleValue()));
}
}
// 将幅值和角度(角度制)转换为复数
public static Complex fromPolar(BigDecimal magnitude, BigDecimal angleDegrees) {
double angleRadians = Math.toRadians(angleDegrees.doubleValue()); // 将角度转换为弧度
BigDecimal real = magnitude.multiply(BigDecimal.valueOf(Math.cos(angleRadians))); // 计算实部
BigDecimal imag = magnitude.multiply(BigDecimal.valueOf(Math.sin(angleRadians))); // 计算虚部
return new Complex(real, imag);
}
// 计算三相电压的对称分量(零序、正序、负序)
public static Complex[] calculateSymmetricalComponents(Complex va, Complex vb, Complex vc) {
// 120度相位因子 a = e^(j120°) = -0.5 + j * sqrt(3)/2
Complex a = new Complex(BigDecimal.valueOf(-0.5), BigDecimal.valueOf(Math.sqrt(3) / 2));
// 240度相位因子 a^2 = e^(j240°) = -0.5 - j * sqrt(3)/2
Complex a2 = a.multiply(a);
// 零序分量 V0 = (Va + Vb + Vc) / 3
Complex v0 = va.add(vb).add(vc).divide(BigDecimal.valueOf(3));
// 正序分量 V1 = (Va + a * Vb + a^2 * Vc) / 3
Complex v1 = va.add(a.multiply(vb)).add(a2.multiply(vc)).divide(BigDecimal.valueOf(3));
// 负序分量 V2 = (Va + a^2 * Vb + a * Vc) / 3
Complex v2 = va.add(a2.multiply(vb)).add(a.multiply(vc)).divide(BigDecimal.valueOf(3));
// 返回零序、正序、负序分量
return new Complex[]{v0, v1, v2};
}
// 计算三相电压的不平衡度
public static void calculateUnbalance(Complex v0, Complex v1, Complex v2) {
// 计算零序、正序、负序分量的幅值
BigDecimal v0Mag = v0.magnitude(); // 零序分量幅值
BigDecimal v1Mag = v1.magnitude(); // 正序分量幅值
BigDecimal v2Mag = v2.magnitude(); // 负序分量幅值
// 计算不平衡度
BigDecimal zeroSequenceUnbalance = v1Mag.compareTo(BigDecimal.ZERO) != 0 ?
v0Mag.divide(v1Mag, 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)) : BigDecimal.ZERO;
BigDecimal negativeSequenceUnbalance = v1Mag.compareTo(BigDecimal.ZERO) != 0 ?
v2Mag.divide(v1Mag, 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)) : BigDecimal.ZERO;
BigDecimal positiveSequenceUnbalance = BigDecimal.valueOf(100); // 正序电压不平衡度通常为100%
// 输出结果保留2位小数
System.out.printf("零序电压不平衡度: %.2f%%\n", zeroSequenceUnbalance.setScale(2, RoundingMode.HALF_UP).doubleValue());
System.out.printf("负序电压不平衡度: %.2f%%\n", negativeSequenceUnbalance.setScale(2, RoundingMode.HALF_UP).doubleValue());
System.out.printf("正序电压不平衡度: %.2f%%\n", positiveSequenceUnbalance.setScale(2, RoundingMode.HALF_UP).doubleValue());
}
public static void main(String[] args) {
// 输入三相电压的幅值和相位角(角度制)
System.out.println("请输入A相电压的幅值单位V");
BigDecimal vaMagnitude = new BigDecimal(57.74);
System.out.println("请输入A相电压的相位角单位");
BigDecimal vaAngle = new BigDecimal(0);
System.out.println("请输入B相电压的幅值单位V");
BigDecimal vbMagnitude =new BigDecimal(57.74*0.9);
System.out.println("请输入B相电压的相位角单位");
BigDecimal vbAngle = new BigDecimal(-122);
System.out.println("请输入C相电压的幅值单位V");
BigDecimal vcMagnitude =new BigDecimal(57.74);
System.out.println("请输入C相电压的相位角单位");
BigDecimal vcAngle = new BigDecimal(118);
// 将幅值和角度转换为复数
Complex va = fromPolar(vaMagnitude, vaAngle);
Complex vb = fromPolar(vbMagnitude, vbAngle);
Complex vc = fromPolar(vcMagnitude, vcAngle);
// 计算对称分量
Complex[] symmetricalComponents = calculateSymmetricalComponents(va, vb, vc);
Complex v0 = symmetricalComponents[0]; // 零序分量
Complex v1 = symmetricalComponents[1]; // 正序分量
Complex v2 = symmetricalComponents[2]; // 负序分量
// 输出对称分量
System.out.printf("零序分量: %.2f + j%.2f\n", v0.real.setScale(2, RoundingMode.HALF_UP).doubleValue(),
v0.imag.setScale(2, RoundingMode.HALF_UP).doubleValue());
System.out.printf("正序分量: %.2f + j%.2f\n", v1.real.setScale(2, RoundingMode.HALF_UP).doubleValue(),
v1.imag.setScale(2, RoundingMode.HALF_UP).doubleValue());
System.out.printf("负序分量: %.2f + j%.2f\n", v2.real.setScale(2, RoundingMode.HALF_UP).doubleValue(),
v2.imag.setScale(2, RoundingMode.HALF_UP).doubleValue());
// 计算并输出不平衡度
calculateUnbalance(v0, v1, v2);
}
}

View File

@@ -11,6 +11,7 @@
<module>njcn-common</module>
<module>njcn-springboot</module>
<module>njcn-db</module>
<module>njcn-plugin</module>
</modules>
<packaging>pom</packaging>
<name>版本控制项目</name>