diff --git a/jsowell-admin/src/test/java/SpringBootTestController.java b/jsowell-admin/src/test/java/SpringBootTestController.java index 30724eea9..f00bcc8e1 100644 --- a/jsowell-admin/src/test/java/SpringBootTestController.java +++ b/jsowell-admin/src/test/java/SpringBootTestController.java @@ -85,6 +85,8 @@ import com.jsowell.wxpay.common.WeChatPayParameter; import com.jsowell.wxpay.dto.AppletTemplateMessageSendDTO; import com.jsowell.wxpay.response.WechatPayRefundRequest; import com.jsowell.wxpay.service.WxAppletRemoteService; +import demo.ChargingPileServerHandler; +import demo.ProtocolUtil; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.RandomStringUtils; import org.junit.Test; @@ -104,6 +106,8 @@ import java.nio.charset.StandardCharsets; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @ActiveProfiles("dev") diff --git a/jsowell-admin/src/test/java/Test.java b/jsowell-admin/src/test/java/Test.java deleted file mode 100644 index ed1a484f1..000000000 --- a/jsowell-admin/src/test/java/Test.java +++ /dev/null @@ -1,85 +0,0 @@ -import com.google.common.collect.Lists; - -import java.util.Comparator; -import java.util.List; -import java.util.stream.Collectors; - -/** - * @program: com.cjml.service.partitem - * @description: - * @author: dc - * @create: 2023-09-09 13:46 - **/ -public class Test { - - public static void main(String[] args) { - List testList = Lists.newArrayList(); - testList.add(new Category("100-50-10", 100, 50, 10)); - testList.add(new Category("110-30-8", 110, 30, 8)); - testList.add(new Category("0-10-12", 0, 10, 12)); - - testList = testList.stream().sorted( - Comparator.comparing(Category::getCategoryGroupSort, Comparator.reverseOrder()) - .thenComparing(Category::getCategorySort, Comparator.reverseOrder()) - .thenComparing(Category::getPartSort) - ) - .collect(Collectors.toList()); - - System.out.println(testList.get(0).getCategoryName()); - System.out.println(testList.get(1).getCategoryName()); - System.out.println(testList.get(2).getCategoryName()); - } - - static class Category { - - private String categoryName; - - private int categoryGroupSort; - - private int categorySort; - - private int partSort; - - public Category() { - } - - public Category(String categoryName, int categoryGroupSort, int categorySort, int partSort) { - this.categoryName = categoryName; - this.categoryGroupSort = categoryGroupSort; - this.categorySort = categorySort; - this.partSort = partSort; - } - - public String getCategoryName() { - return categoryName; - } - - public void setCategoryName(String categoryName) { - this.categoryName = categoryName; - } - - public int getCategoryGroupSort() { - return categoryGroupSort; - } - - public void setCategoryGroupSort(int categoryGroupSort) { - this.categoryGroupSort = categoryGroupSort; - } - - public int getCategorySort() { - return categorySort; - } - - public void setCategorySort(int categorySort) { - this.categorySort = categorySort; - } - - public int getPartSort() { - return partSort; - } - - public void setPartSort(int partSort) { - this.partSort = partSort; - } - } -} diff --git a/jsowell-admin/src/test/java/demo/ChargingPileClient.java b/jsowell-admin/src/test/java/demo/ChargingPileClient.java new file mode 100644 index 000000000..29f2ee4c2 --- /dev/null +++ b/jsowell-admin/src/test/java/demo/ChargingPileClient.java @@ -0,0 +1,80 @@ +package demo; + +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.*; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.handler.codec.bytes.ByteArrayDecoder; +import io.netty.handler.codec.bytes.ByteArrayEncoder; + +public class ChargingPileClient { + private final String host; + private final int port; + + public ChargingPileClient(String host, int port) { + this.host = host; + this.port = port; + } + + public void start() throws Exception { + EventLoopGroup group = new NioEventLoopGroup(); + try { + Bootstrap b = new Bootstrap(); + b.group(group) + .channel(NioSocketChannel.class) + .handler(new ChannelInitializer() { + @Override + public void initChannel(SocketChannel ch) throws Exception { + ChannelPipeline pipeline = ch.pipeline(); + pipeline.addLast(new ByteArrayDecoder()); + pipeline.addLast(new ByteArrayEncoder()); + pipeline.addLast(new ChargingPileClientHandler()); + } + }); + + ChannelFuture f = b.connect(host, port).sync(); + System.out.println("充电桩客户端已连接到服务器"); + f.channel().closeFuture().sync(); + } finally { + group.shutdownGracefully(); + } + } + + public static void main(String[] args) throws Exception { + String host = "localhost"; + int port = 9011; + new ChargingPileClient(host, port).start(); + } +} + +class ChargingPileClientHandler extends ChannelInboundHandlerAdapter { + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) { + byte[] command = (byte[]) msg; + System.out.println("收到服务器指令: " + ProtocolUtil.bytesToHex(command)); + + // 处理工作参数设置指令 + if (command[5] == 0x52) { + String pileId = ProtocolUtil.bytesToHex(command).substring(12, 26); + boolean allowWork = (command[19] == 0x00); + int maxPower = command[20] & 0xFF; + + System.out.println("收到工作参数设置:"); + System.out.println("桩编号: " + pileId); + System.out.println("是否允许工作: " + (allowWork ? "允许" : "不允许")); + System.out.println("最大允许输出功率: " + maxPower + "%"); + + // 模拟设置成功 + byte[] response = ProtocolUtil.createSetWorkParamsResponseFrame(pileId, true); + ctx.writeAndFlush(response); + System.out.println("发送响应: " + ProtocolUtil.bytesToHex(response)); + } + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + cause.printStackTrace(); + ctx.close(); + } +} \ No newline at end of file diff --git a/jsowell-admin/src/test/java/demo/ChargingPileController.java b/jsowell-admin/src/test/java/demo/ChargingPileController.java new file mode 100644 index 000000000..f29477b15 --- /dev/null +++ b/jsowell-admin/src/test/java/demo/ChargingPileController.java @@ -0,0 +1,34 @@ +package demo; + +import org.springframework.web.bind.annotation.*; +import io.netty.channel.ChannelHandlerContext; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +@RestController +@RequestMapping("/api/charging") +public class ChargingPileController { + + private final ChannelHandlerContext chargingPileChannel; + + public ChargingPileController(ChannelHandlerContext chargingPileChannel) { + this.chargingPileChannel = chargingPileChannel; + } + + @PostMapping("/setWorkParams") + public String setWorkParams(@RequestParam String pileId, + @RequestParam boolean allowWork, + @RequestParam int maxPower) { + try { + byte[] command = ProtocolUtil.createSetWorkParamsFrame(pileId, allowWork, maxPower); + CompletableFuture future = ChargingPileServerHandler.sendCommand(chargingPileChannel, command); + byte[] response = future.get(5, TimeUnit.SECONDS); + + // 解析响应 + boolean success = (response[18] == 0x01); + return "设置" + (success ? "成功" : "失败"); + } catch (Exception e) { + return "发送指令失败: " + e.getMessage(); + } + } +} \ No newline at end of file diff --git a/jsowell-admin/src/test/java/demo/ChargingPileServer.java b/jsowell-admin/src/test/java/demo/ChargingPileServer.java new file mode 100644 index 000000000..0ddc9d1b1 --- /dev/null +++ b/jsowell-admin/src/test/java/demo/ChargingPileServer.java @@ -0,0 +1,50 @@ +package demo; + +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.codec.bytes.ByteArrayDecoder; +import io.netty.handler.codec.bytes.ByteArrayEncoder; + +public class ChargingPileServer { + private final int port; + + public ChargingPileServer(int port) { + this.port = port; + } + + public void start() throws Exception { + EventLoopGroup bossGroup = new NioEventLoopGroup(1); + EventLoopGroup workerGroup = new NioEventLoopGroup(); + try { + ServerBootstrap b = new ServerBootstrap(); + b.group(bossGroup, workerGroup) + .channel(NioServerSocketChannel.class) + .childHandler(new ChannelInitializer() { + @Override + public void initChannel(SocketChannel ch) throws Exception { + ChannelPipeline pipeline = ch.pipeline(); + pipeline.addLast(new ByteArrayDecoder()); + pipeline.addLast(new ByteArrayEncoder()); + pipeline.addLast(new ChargingPileServerHandler()); + } + }) + .option(ChannelOption.SO_BACKLOG, 128) + .childOption(ChannelOption.SO_KEEPALIVE, true); + + ChannelFuture f = b.bind(port).sync(); + System.out.println("充电桩服务器启动,监听端口: " + port); + f.channel().closeFuture().sync(); + } finally { + workerGroup.shutdownGracefully(); + bossGroup.shutdownGracefully(); + } + } + + public static void main(String[] args) throws Exception { + int port = 9011; + new ChargingPileServer(port).start(); + } +} \ No newline at end of file diff --git a/jsowell-admin/src/test/java/demo/ChargingPileServerHandler.java b/jsowell-admin/src/test/java/demo/ChargingPileServerHandler.java new file mode 100644 index 000000000..4981ca69d --- /dev/null +++ b/jsowell-admin/src/test/java/demo/ChargingPileServerHandler.java @@ -0,0 +1,31 @@ +package demo; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import java.util.concurrent.CompletableFuture; + +public class ChargingPileServerHandler extends ChannelInboundHandlerAdapter { + private static CompletableFuture responseFuture; + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) { + byte[] response = (byte[]) msg; + System.out.println("接收到充电桩响应: " + ProtocolUtil.bytesToHex(response)); + if (responseFuture != null) { + responseFuture.complete(response); + } + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + cause.printStackTrace(); + ctx.close(); + } + + public static CompletableFuture sendCommand(ChannelHandlerContext ctx, byte[] command) { + responseFuture = new CompletableFuture<>(); + ctx.writeAndFlush(command); + System.out.println("发送指令到充电桩: " + ProtocolUtil.bytesToHex(command)); + return responseFuture; + } +} \ No newline at end of file diff --git a/jsowell-admin/src/test/java/demo/ProtocolUtil.java b/jsowell-admin/src/test/java/demo/ProtocolUtil.java new file mode 100644 index 000000000..ba1774c8c --- /dev/null +++ b/jsowell-admin/src/test/java/demo/ProtocolUtil.java @@ -0,0 +1,77 @@ +package demo; + +import java.nio.ByteBuffer; + +public class ProtocolUtil { + + public static byte[] createSetWorkParamsFrame(String pileId, boolean allowWork, int maxPower) { + ByteBuffer buffer = ByteBuffer.allocate(21); + buffer.put((byte) 0x68); // 起始标志 + buffer.put((byte) 0x0D); // 数据长度 + buffer.putShort((short) 0x0008); // 序列号域 + buffer.put((byte) 0x00); // 加密标志 + buffer.put((byte) 0x52); // 帧类型码 + + // 桩编码 + buffer.put(hexStringToByteArray(pileId)); + + // 是否允许工作 + buffer.put((byte) (allowWork ? 0x00 : 0x01)); + + // 充电桩最大允许输出功率 + buffer.put((byte) maxPower); + + // 计算校验和 + short checksum = calculateChecksum(buffer.array(), 1, 19); + buffer.putShort(checksum); + + return buffer.array(); + } + + public static byte[] createSetWorkParamsResponseFrame(String pileId, boolean success) { + ByteBuffer buffer = ByteBuffer.allocate(20); + buffer.put((byte) 0x68); // 起始标志 + buffer.put((byte) 0x0C); // 数据长度 + buffer.putShort((short) 0x0008); // 序列号域 + buffer.put((byte) 0x00); // 加密标志 + buffer.put((byte) 0x51); // 帧类型码 + + // 桩编码 + buffer.put(hexStringToByteArray(pileId)); + + // 设置结果 + buffer.put((byte) (success ? 0x01 : 0x00)); + + // 计算校验和 + short checksum = calculateChecksum(buffer.array(), 1, 18); + buffer.putShort(checksum); + + return buffer.array(); + } + + public static short calculateChecksum(byte[] data, int offset, int length) { + int sum = 0; + for (int i = offset; i < offset + length; i++) { + sum += (data[i] & 0xFF); + } + return (short) (sum & 0xFFFF); + } + + 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; + } + + public static String bytesToHex(byte[] bytes) { + StringBuilder sb = new StringBuilder(); + for (byte b : bytes) { + sb.append(String.format("%02X ", b)); + } + return sb.toString().trim(); + } +} \ No newline at end of file