由于之前用PHP+WorkMan开发的新能源汽车充电管理系统,(有兴趣的可以查看这里),公司说这技术淘汰了,需要用java重新架构一套,适应市场需求。 毕竟入职时候,说自己php、java都能撸出冒火星,这个任务无可厚非放在我的身上了。 经
由于之前用PHP+WorkMan开发的新能源汽车充电管理系统,(有兴趣的可以查看这里),公司说这技术淘汰了,需要用java重新架构一套,适应市场需求。
毕竟入职时候,说自己php、java都能撸出冒火星,这个任务无可厚非放在我的身上了。
经过小组讨论,决定架构用:SpringBoot+mybatis+Mysql+Netty+Redis+Uni-app
有什么不明白地方,大家一起交流:150-5606-9927
硬件跟服务器通讯
硬件厂商提供的充电桩和充电运营管理系统之间的通信接口采用基于 tcp/IP Socket 的通信方式实现,按照长连接工作模式。
我们使用Netty跟硬件桩子(TCP/IP)通讯
应用层报文桢格式
起始标志 | 数据长度 | 序列号域 | 加密标志 | 桢类型标志 | 消息体 | 桢校验域 |
1字节 | 1字节 | 2字节 | 1字节 | 1字节 | N字节 | 2字节 |
数据结构定义说明:
桢校验域:从序列号域到数据域的 CRC 校验,校验多项式为 0x180D,低字节在前,高字节在后,
数据格式包括BCD码、BIN码、ASCII,BIN码均为低位在前高位在后。协议中小数值均乘倍率(保留小数点位数)上送平台(例如:电压为225.1,保留一位小数,上送到平台值为2251,即0x8CB)。CP56Time2a格式如下:
Miliseconds(D7-D0) | ||
Miliseconds(D15-D8) | ||
IV(D7) | RES1 | Minutes(D5-D0) |
SU(D7) | RES2 | Hours(D4-D0) |
DAY of WEEK | DAY of MONTH(D4-D0) | |
RES3 | Month(D3-D0) | |
RES4 | Years(D6-D0) |
有了充电桩硬件厂商提供的数据协议,
我们开始写Netty服务器端,贴出核心代码
package com.icojoo.chargeNetty.tcpIp;import io.netty.bootstrap.ServerBootstrap;import io.netty.channel.Channel;import io.netty.channel.ChannelFuture;import io.netty.channel.ChannelOption;import io.netty.channel.EventLoopGroup;import io.netty.channel.NIO.NioEventLoopGroup;import io.netty.channel.socket.nio.NiOServerSocketChannel;import io.netty.util.concurrent.Future;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Component;import java.util.Map;import java.util.concurrent.ConcurrentHashMap;@Slf4j@Componentpublic class NettyTcpServer { private EventLoopGroup boss = new NioEventLoopGroup(1); private EventLoopGroup worker = new NioEventLoopGroup(); @Autowired ServerChannelInitializer serverChannelInitializer; @Value("${netty.tcp.client.port}") private Integer port; private Channel channel; public static Map map = new ConcurrentHashMap(); public ChannelFuture start() { // 启动类 ServerBootstrap serverBootstrap = new ServerBootstrap(); //组配置,初始化ServerBootstrap的线程组 serverBootstrap.group(boss, worker) //构造channel通道工厂 bossGroup的通道,只是负责连接 .channel(NioServerSocketChannel.class) //设置通道处理者ChannelHandlerWorkerGroup的处理器 .childHandler(serverChannelInitializer) //socket参数,当服务器请求处理程全满时,用于临时存放已完成三次握手请求的队列的最大长度。如果未设置或所设置的值小于1,Java将使用默认值50。 .option(ChannelOption.SO_BACKLOG, 1024) //启用心跳保活机制,tcp,默认2小时发一次心跳 .childOption(ChannelOption.SO_KEEPALIVE, true); //Future:异步任务的生命周期,可用来获取任务结果 // 绑定端口 开启监听 同步等待 ChannelFuture channelFuture1 = serverBootstrap.bind(port).syncUninterruptibly(); if (channelFuture1 != null && channelFuture1.isSuccess()) { // 获取通道 channel = channelFuture1.channel(); log.info("Netty tcp server start success,port={}",port); }else { log.error("Netty tcp server start fail"); } return channelFuture1; } public void destroy(){ if (channel != null) { channel.close(); } try { Future> future = worker.shutdownGracefully().await(); if (!future.isSuccess()) { log.error("netty tcp workerGroup shutdown fail,{}",future.cause()); } } catch (InterruptedException e) { log.error(e.toString()); } log.info("Netty tcp server shutdown success"); }}
package com.icojoo.chargeNetty.tcpIp;import io.netty.channel.ChannelInitializer;import io.netty.channel.ChannelPipeline;import io.netty.channel.socket.SocketChannel;import io.netty.handler.codec.bytes.ByteArrayDecoder;import io.netty.handler.codec.bytes.ByteArrayEncoder;import io.netty.handler.timeout.IdleStateHandler;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import java.util.concurrent.TimeUnit;@Componentpublic class ServerChannelInitializer extends ChannelInitializer { @Autowired ServerChannelHandler serverChannelHandler; @Override protected void initChannel(SocketChannel socketChannel) throws Exception { ChannelPipeline pipeline = socketChannel.pipeline(); //IdleStateHandler心跳机制,如果超时触发Handle中userEventTrigger()方法 pipeline.addLast("idleStateHandler", new IdleStateHandler(15,0,0, TimeUnit.MINUTES)); // 字符串编解码器 pipeline.addLast( new ByteArrayDecoder(), new ByteArrayEncoder() ); // 自定义Handler pipeline.addLast("serverChannelHandler",serverChannelHandler); }}
package com.icojoo.chargeNetty.tcpIp;import com.icojoo.chargeNetty.service.ITBookService;import com.icojoo.chargeNetty.utils.Crc3Util;import com.icojoo.chargeNetty.utils.JuDianUtil;import com.icojoo.chargeNetty.utils.RedisUtil;import io.netty.channel.ChannelHandler;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.SimpleChannelInboundHandler;import io.netty.handler.timeout.IdleState;import io.netty.handler.timeout.IdleStateEvent;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;@Slf4j@Component@ChannelHandler.Sharablepublic class ServerChannelHandler extends SimpleChannelInboundHandler { @Autowired private ITBookService itBookService; @Autowired RedisUtil redisUtil; @Override protected void channelRead0(ChannelHandlerContext channelHandlerContext, byte[] msg) throws Exception { String vv=JuDianUtil.bytesToHexString(msg); log.info("Netty tcp server receive--- message: {}", vv);// 效验//String nn="000000010361002001101300020a3232303432320000011920168010203000000000"; byte[] bb= JuDianUtil.hexStringToBytes(nn); log.info("Netty tcp server receive message------: {}", Crc3Util.getCRC3(bb,null));// String cmd = JuDianUtil.getMsGCmd(msg);// String pileCode = JuDianUtil.getPileNum(msg);//// log.info("Netty tcp: {}", cmd);// log.info("Netty tcp: {}", pileCode);// itBookService.save(new TBook("1231", (String) msg));// redisUtil.set("123",new TBook(1,"1231", (String) msg),60);// channelHandlerContext.writeAndFlush(JuDianUtil.hexStringToBytes("680c000000020361002001101300c17f")).syncUninterruptibly(); } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { super.channelActive(ctx); log.info("tcp client "+getRemoteAddress(ctx)+" connect success"); NettyTcpServer.map.put(getIPString(ctx),ctx.channel()); } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { // 删除Channel Map中失效的Client log.info("tcp client "+getRemoteAddress(ctx)+" 删除Channel"); NettyTcpServer.map.remove(getIPString(ctx)); ctx.close(); }掉线移除// @Override// public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {//// log.info("tcp client "+getRemoteAddress(ctx)+" handlerRemoved");// NettyTcpServer.map.remove(getIPString(ctx));// ctx.close();// } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { super.exceptionCaught(ctx, cause); // 发生异常 关闭连接 log.error("引擎{}的通道发生异常,断开连接",getRemoteAddress(ctx)); ctx.close(); } @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { String socketString = ctx.channel().remoteAddress().toString(); if (evt instanceof IdleStateEvent) { IdleStateEvent event = (IdleStateEvent) evt; if (event.state()== IdleState.READER_IDLE) { log.info("Client: "+socketString+" READER_IDLE读超时"); ctx.disconnect(); }else if (event.state()==IdleState.WRITER_IDLE){ log.info("Client: "+socketString+" WRITER_IDLE写超时"); ctx.disconnect(); }else if (event.state()==IdleState.ALL_IDLE){ log.info("Client: "+socketString+" ALL_IDLE总超时"); ctx.disconnect(); } } } public String getRemoteAddress(ChannelHandlerContext channelHandlerContext){ String socketString = ""; socketString = channelHandlerContext.channel().remoteAddress().toString(); return socketString; } public String getIPString(ChannelHandlerContext channelHandlerContext){ String ipString = ""; String socketString = channelHandlerContext.channel().remoteAddress().toString(); int colonAt = socketString.indexOf(":"); ipString = socketString.substring(1,colonAt); return ipString; }}
CRC 校验工具类
package com.icojoo.chargeNetty.utils;public class Crc3Util { public static String getCRC3(byte[] data,String order) { byte[] crc16_h = { (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40 }; byte[] crc16_l = { (byte) 0x00, (byte) 0xC0, (byte) 0xC1, (byte) 0x01, (byte) 0xC3, (byte) 0x03, (byte) 0x02, (byte) 0xC2, (byte) 0xC6, (byte) 0x06, (byte) 0x07, (byte) 0xC7, (byte) 0x05, (byte) 0xC5, (byte) 0xC4, (byte) 0x04, (byte) 0xCC, (byte) 0x0C, (byte) 0x0D, (byte) 0xCD, (byte) 0x0F, (byte) 0xCF, (byte) 0xCE, (byte) 0x0E, (byte) 0x0A, (byte) 0xCA, (byte) 0xCB, (byte) 0x0B, (byte) 0xC9, (byte) 0x09, (byte) 0x08, (byte) 0xC8, (byte) 0xD8, (byte) 0x18, (byte) 0x19, (byte) 0xD9, (byte) 0x1B, (byte) 0xDB, (byte) 0xDA, (byte) 0x1A, (byte) 0x1E, (byte) 0xDE, (byte) 0xDF, (byte) 0x1F, (byte) 0xDD, (byte) 0x1D, (byte) 0x1C, (byte) 0xDC, (byte) 0x14, (byte) 0xD4, (byte) 0xD5, (byte) 0x15, (byte) 0xD7, (byte) 0x17, (byte) 0x16, (byte) 0xD6, (byte) 0xD2, (byte) 0x12, (byte) 0x13, (byte) 0xD3, (byte) 0x11, (byte) 0xD1, (byte) 0xD0, (byte) 0x10, (byte) 0xF0, (byte) 0x30, (byte) 0x31, (byte) 0xF1, (byte) 0x33, (byte) 0xF3, (byte) 0xF2, (byte) 0x32, (byte) 0x36, (byte) 0xF6, (byte) 0xF7, (byte) 0x37, (byte) 0xF5, (byte) 0x35, (byte) 0x34, (byte) 0xF4, (byte) 0x3C, (byte) 0xFC, (byte) 0xFD, (byte) 0x3D, (byte) 0xFF, (byte) 0x3F, (byte) 0x3E, (byte) 0xFE, (byte) 0xFA, (byte) 0x3A, (byte) 0x3B, (byte) 0xFB, (byte) 0x39, (byte) 0xF9, (byte) 0xF8, (byte) 0x38, (byte) 0x28, (byte) 0xE8, (byte) 0xE9, (byte) 0x29, (byte) 0xEB, (byte) 0x2B, (byte) 0x2A, (byte) 0xEA, (byte) 0xEE, (byte) 0x2E, (byte) 0x2F, (byte) 0xEF, (byte) 0x2D, (byte) 0xED, (byte) 0xEC, (byte) 0x2C, (byte) 0xE4, (byte) 0x24, (byte) 0x25, (byte) 0xE5, (byte) 0x27, (byte) 0xE7, (byte) 0xE6, (byte) 0x26, (byte) 0x22, (byte) 0xE2, (byte) 0xE3, (byte) 0x23, (byte) 0xE1, (byte) 0x21, (byte) 0x20, (byte) 0xE0, (byte) 0xA0, (byte) 0x60, (byte) 0x61, (byte) 0xA1, (byte) 0x63, (byte) 0xA3, (byte) 0xA2, (byte) 0x62, (byte) 0x66, (byte) 0xA6, (byte) 0xA7, (byte) 0x67, (byte) 0xA5, (byte) 0x65, (byte) 0x64, (byte) 0xA4, (byte) 0x6C, (byte) 0xAC, (byte) 0xAD, (byte) 0x6D, (byte) 0xAF, (byte) 0x6F, (byte) 0x6E, (byte) 0xAE, (byte) 0xAA, (byte) 0x6A, (byte) 0x6B, (byte) 0xAB, (byte) 0x69, (byte) 0xA9, (byte) 0xA8, (byte) 0x68, (byte) 0x78, (byte) 0xB8, (byte) 0xB9, (byte) 0x79, (byte) 0xBB, (byte) 0x7B, (byte) 0x7A, (byte) 0xBA, (byte) 0xBE, (byte) 0x7E, (byte) 0x7F, (byte) 0xBF, (byte) 0x7D, (byte) 0xBD, (byte) 0xBC, (byte) 0x7C, (byte) 0xB4, (byte) 0x74, (byte) 0x75, (byte) 0xB5, (byte) 0x77, (byte) 0xB7, (byte) 0xB6, (byte) 0x76, (byte) 0x72, (byte) 0xB2, (byte) 0xB3, (byte) 0x73, (byte) 0xB1, (byte) 0x71, (byte) 0x70, (byte) 0xB0, (byte) 0x50, (byte) 0x90, (byte) 0x91, (byte) 0x51, (byte) 0x93, (byte) 0x53, (byte) 0x52, (byte) 0x92, (byte) 0x96, (byte) 0x56, (byte) 0x57, (byte) 0x97, (byte) 0x55, (byte) 0x95, (byte) 0x94, (byte) 0x54, (byte) 0x9C, (byte) 0x5C, (byte) 0x5D, (byte) 0x9D, (byte) 0x5F, (byte) 0x9F, (byte) 0x9E, (byte) 0x5E, (byte) 0x5A, (byte) 0x9A, (byte) 0x9B, (byte) 0x5B, (byte) 0x99, (byte) 0x59, (byte) 0x58, (byte) 0x98, (byte) 0x88, (byte) 0x48, (byte) 0x49, (byte) 0x89, (byte) 0x4B, (byte) 0x8B, (byte) 0x8A, (byte) 0x4A, (byte) 0x4E, (byte) 0x8E, (byte) 0x8F, (byte) 0x4F, (byte) 0x8D, (byte) 0x4D, (byte) 0x4C, (byte) 0x8C, (byte) 0x44, (byte) 0x84, (byte) 0x85, (byte) 0x45, (byte) 0x87, (byte) 0x47, (byte) 0x46, (byte) 0x86, (byte) 0x82, (byte) 0x42, (byte) 0x43, (byte) 0x83, (byte) 0x41, (byte) 0x81, (byte) 0x80, (byte) 0x40 }; int crc = 0x0000ffff; int ucCRCHi = 0x00ff; int ucCRCLo = 0x00ff; int iIndex; for (int i = 0; i < data.length; ++i) { iIndex = (ucCRCLo ^ data[i]) & 0x00ff; ucCRCLo = ucCRCHi ^ crc16_h[iIndex]; ucCRCHi = crc16_l[iIndex]; } crc = ((ucCRCHi & 0x00ff) << 8) | (ucCRCLo & 0x00ff) & 0xffff; //高低位互换,输出符合相关工具对Modbus CRC16的运算 if("H".equals(order)) { crc = ( (crc & 0xFF00) >> 8) | ( (crc & 0x00FF ) << 8); } return String.fORMat("%04X", crc); }}
2.5.1、总后台管理
权限管理、管理员管理、角色管理、系统基本管理、互联互通管理、支付管理、短信通知管理。
2.5.2、用户管理
APP、小程序会员管理、代理商管理、商户管理、公司账户管理。
2.5.3、设备管理
站场网站管理、设备管理、功率监控、故障管理、告警管理、摄像头管理。
2.5.4、订单管理
充电订单、充值订单、卡充电单、退款订单等
2.5.5、财务管理
平台流水、代理商流水、代理商提现、商户流水、商户提现、会员流水、电子发票自助开票管理
2.5.6、数据分析
订单分析、会员分析、财务分析、运营数据分析等
2.6、App、小程序端功能点
2.6.1、首页
充电站列表、导航、当前价格、距离远近、设备是否有空闲、是否有停车位、停车收费标准等。
2.6.2、充电
扫码充电、插枪充电、刷卡充电、充满推送、远程停充、占位费计算、充电订单等。
2.6.3、会员中心
显示余额、充值、充电卡、积分、优惠券、每日签到、故障申报、发票申领、联系客服。
代理商或商户:收益统计、佣金查看、提现、收支流水等
2.7、数据大屏
可视化数据大屏数据 通过地图、柱状图、折线图、饼图把场站位置、场站数量。充电桩的类型、充电桩状态、当月/当日充值金额,当月/当日充电金额,电站告警等相对复杂、抽象的数据通过可视的方式以一种人们更容易理解的、更直观的方式来展示出来,将数据转换成图或表等。将业务的关键指标以可视化的方式展示到一个或多个LED屏幕上,不仅使业务人员能够从复杂的业务数据中快速、直接地找到重要数据,而且能对决策者起到辅助作用。
来源地址:https://blog.csdn.net/jiyehang/article/details/128786810
--结束END--
本文标题: SpringBoot+Mybatis+Mysql+Netty+Redis+Uni-app开发的充电桩小程序管理后台
本文链接: https://www.lsjlt.com/news/518293.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
2024-10-23
2024-10-22
2024-10-22
2024-10-22
2024-10-22
2024-10-22
2024-10-22
2024-10-22
2024-10-22
2024-10-22
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0