好友
阅读权限25
听众
最后登录1970-1-1
|
本帖最后由 AflyExeed 于 2024-5-23 11:24 编辑
上次开源的是一个扫码点餐系统,这个是毕业之前做的一个毕设,稍微整理一下就开源出来供大家学习了,有吾友说要拿去开饭店,但其实这个项目只能用来学习而已,并不能商业化,毕竟没有对接支付
然后本次是开源一个物联网系统,上班之后业余时间写了这个,基于大名鼎鼎的若依框架二开的,可以拿来学习一下Netty通信、动态大屏等开发。项目写完也就写完了,不如开源出来大家互相学习,也为论坛做出点贡献
各位师哥师姐可以给小的点个star吗
源码仓库:https://gitee.com/ah-f/AflyExceedIot-backend
项目介绍
本项目是用于企业工厂设备监控,统计设备稼动率、监控设备实时运行状况等。 通过物联网终端设备(DTU)接入平台,配置好设备参数将设备添加到平台即可对设备运行状态监控。
项目分为这四部分
项目模块 | 仓库地址 | 前端 | https://gitee.com/ah-f/AflyExceedIot-front | 后端 | https://gitee.com/ah-f/AflyExceedIot-backend | 设备网关服务 | https://gitee.com/ah-f/AflyExceedIot-gateway | wx小程序 | https://gitee.com/ah-f/AflyExceedIot-miniprogram |
项目整体架构
物联网终端(DUT): 是一种专为物联网(Internet of Things, IoT)应用设计的无线数据传输设备。其核心功能在于实现现场设备(如传感器、控制器、仪表等)与远程服务器之间的数据透明传输,即在不同通信协议和网络架构之间建立起可靠的桥接,确保数据能够在不同的网络环境中高效、稳定地交换。 本项目对接的终端设备是一种携带四个继电器的设备,通过4G无线网络实时发送当前四位继电器的状态发送到设备网关服务,可以将继电器的状态规定为运行、关机、报警、待机四种状态,这样就可以在后端知道当前设备状态进行监控,计算获取当前设备稼动率。系统介绍- 基于若依管理后台进行二次开发。
- 基于Netty框架实现物联网设备网关服务
- 基于Echarts实现动态实时大屏
- 前端采用Vue3、Element UI。
- 后端采用Spring Boot、Spring Security、Redis & Jwt。
- 权限认证使用Jwt,支持多终端认证系统。
- 支持加载动态权限菜单,多方式轻松权限控制。
- 封装异步任务管理,提高系统并发。
功能介绍- 设备管理:对DTU设备进行管理。
- 动态大屏:实时显示设备当前状态、设备位置、设备监控、设备稼动率统计。
- 设备地图:展示设备地理位置分布等信息。
- 数据记录:对设备数据进行统计、查看、分析。
- 菜单管理:配置系统菜单,操作权限,按钮权限标识等。
- 角色管理:角色菜单权限分配、设置角色按机构进行数据范围权限划分。
- 登录日志:系统登录日志记录查询包含登录异常。
- 服务监控:监视当前系统CPU、内存、磁盘、堆栈等相关信息。
- 缓存监控:对系统的缓存信息查询,命令统计等。
- 用户管理:用户是系统操作者,该功能主要完成系统用户配置。
Netty核心代码
[Java] 纯文本查看 复制代码 @Slf4j
@Component("nettyServer")
public class NettyServer {
@Resource
private CustomerChannelInitializer channelInitializer;
//配置服务端NIO线程组
private final EventLoopGroup parentGroup = new NioEventLoopGroup(); //NioEventLoopGroup extends MultithreadEventLoopGroup Math.max(1, SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));
private final EventLoopGroup childGroup = new NioEventLoopGroup();
private Channel channel;
public ChannelFuture bing(InetSocketAddress address) {
ChannelFuture channelFuture = null;
try {
ServerBootstrap b = new ServerBootstrap();
b.group(parentGroup, childGroup)
.channel(NioServerSocketChannel.class) //非阻塞模式
.option(ChannelOption.SO_BACKLOG, 128)
.childHandler(channelInitializer);
channelFuture = b.bind(address).syncUninterruptibly();
channel = channelFuture.channel();
} catch (Exception e) {
log.error(e.getMessage());
} finally {
if (null != channelFuture && channelFuture.isSuccess()) {
log.info("device netty server start done. ");
} else {
log.error("device netty server start error. ");
}
}
return channelFuture;
}
public void destroy() {
if (null == channel) return;
channel.close();
parentGroup.shutdownGracefully();
childGroup.shutdownGracefully();
}
public Channel getChannel() {
return channel;
}
}
处理设备消息核心逻辑:
[Java] 纯文本查看 复制代码 public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//接收msg消息
log.info(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + " 服务端接收到消息:" + msg);
long currentTimeMillis = System.currentTimeMillis();
//通知客户端链消息发送成功
String str = "服务端收到:" + new Date() + " " + msg + "\r\n";
SocketChannel channel = (SocketChannel) ctx.channel();
String channelId = channel.id().toString();
//处理发送注册码
if (msg.toString().charAt(0) != 'D') {
// 设备上线
poolTaskExecutor.execute(() -> {
Device dev = deviceService.getOne(new QueryWrapper<Device>().lambda()
.eq(Device::getNumber, msg.toString()));
if (dev == null){
//设备不存在不处理
log.info("该设备还未添加或者其他数据不做处理");
return;
}
CacheUtil.cacheClientChannel.put(channelId, channel);
CacheUtil.deviceChannelMap.put(channelId, msg.toString());
Device device = Device.builder().number(msg.toString())
.status(1)
.lastOnline(LocalDateTime.ofInstant(new Date().toInstant(), ZoneId.systemDefault()))
.build();
Boolean result = deviceService.updateDeviceStatus(device);
if (result) {
log.info("修改设备{}在线成功,时间{}", msg, new Date().toInstant());
} else {
log.info("修改设备{}在线失败,时间{}", msg, new Date().toInstant());
}
//判断当前设备是否是波动下线 是的话进行数据补偿
if (CacheUtil.deviceOfflineMap.containsKey(msg.toString())) {
Long lastTime = CacheUtil.deviceOfflineMap.get(msg.toString());
if (currentTimeMillis - lastTime < 1000 * 60 * 15) { //间隔小于15分钟进行数据补偿
Transaction one = transactionMapper.selectOne(new QueryWrapper<Transaction>().lambda()
.eq(Transaction::getDeviceNo, msg.toString())
.eq(Transaction::getFromType, "1")
.orderByDesc(Transaction::getId)
.last("limit 1")); //获取上一次数据内容
long times = (currentTimeMillis - lastTime) / (1000 * 60 * 3);
List<Transaction> transactions = new ArrayList<>();
for (long i = 0; i < times; i++) {
one.setTime(one.getTime().plusMinutes(3 * (i + 1))); //时间增加三分钟
transactions.add(one);
}
if (transactionService.saveBatch(transactions)) {//批量添加
//删除缓存中波动设备
CacheUtil.deviceOfflineMap.remove(msg.toString());
}
}
}
});
} else {
//获取设备号
String deviceNo = CacheUtil.deviceChannelMap.get(channelId);
poolTaskExecutor.execute(() -> { //数据保存到数据库
transactionService.save(Transaction.builder().deviceNo(deviceNo)
.data(msg.toString().substring(1))
.fromType("1")
.time(LocalDateTime.ofInstant(new Date().toInstant(), ZoneId.systemDefault())).build());
});
}
ctx.writeAndFlush(str);
}
[size=1.25em]
启动- 环境要求:JDK 1.8+、Maven 3.3+、MySQL 5.7+、redis 6.0+(mysql、redis 自行安装)。
- 导入项目:将项目导入到IDE中,需要在application.yml中配置好数据库连接。
- 编译项目:执行mvn clean install命令,编译成功后会在target目录下生成.jar。
- 启动项目: redis、mysql环境搭建好,配置文件配置好即可启动项目。 首先启动网关服务AflyExceedIot-gateway 然后启动管理后台后端项目,直接对admin模块启动即可 最后启动后台前端项目即可
- 模拟设备: 找到后端模块afly-analog-device 打开Startup.java可以创建模拟设备
系统图片
系统管理员账号:admin/admin123 |
免费评分
-
查看全部评分
本帖被以下淘专辑推荐:
- · 精品工具盒|主题: 2339, 订阅: 1050
|
发帖前要善用【论坛搜索】功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。 |
|
|
|
|