架构图
IBOOT系统除了可以部署在云服务器作为软网关也可以嵌入到硬件(工控机)里面作为边缘网关。
多环境说明
- 本地开发环境:dev, 本地开发时需要修改application-dev.properties文件中的信息
- 测试环境:test, 测试环境安装部署时修改application-test.properties文件中的信息
- 生产环境:prod, 正式环境安装部署时修改application-prod.properties文件中的信息
通过修改application.properties文件中的spring.profiles.active的值来切换环境,默认是dev环境。
properties
# 修改application.properties文件,将配置spring.profiles.active的值改成dev|test|prod
spring.profiles.active=dev|test|prod
注意: 由于线上环境使用nginx做反向代理, 所以test、prod环境的server.servlet.context-path属性设置为/api, 开发环境的server.servlet.context-path属性设置为/,然后前端则由vite3的http服务做代理。
js
// vite.config.js文件中的server配置, 此配置只有在本地开发时才需要修改, 线上环境不需要修改
// 线上环境需要修改docs/nginx/iot.conf或iot-https.conf文件中的反向代理等配置参数
server: {
proxy: {
'^/api/*': {
changeOrigin: true,
target: 'http://localhost:8085',
rewrite: (path) => {
return path.replace(/^\/api/, '')
}
},
},
},
如果需要修改server.servlet.context-path属性的值, 请修改docs/nginx/iot.conf或iot-https.conf文件中的反向代理等配置参数。
项目结构
iboot项目由maven的多模块组成, 主要分为以下几个模块:
java
iboot
├── bootstrap # 项目启动模块,包含启动类和核心配置
├── framework # 框架核心模块,包含通用工具和基础组件
├── iboot-plugin # 插件模块集合
│ ├── iboot-code # 代码生成器插件
│ ├── iboot-protocol # 协议驱动支持
│ │ ├── protocol-mqtt # MQTT协议支持
│ │ ├── protocol-dtu # DTU协议支持
│ │ ├── protocol-modbus # Modbus协议支持
│ ├── iboot-SaToken # SaToken权限认证插件
│ ├── iboot-shiro # Shiro权限认证插件
│ ├── iboot-knife4j # API文档插件
│ ├── iboot-amqp # 消息队列插件(商业)
│ ├── iboot-datasource # 多数据源插件(商业)
│ ├── iboot-gb28181 # GB28181插件(商业)
│ ├── iboot-le5le # 乐吾乐大屏插件(商业)
│ ├── iboot-message # 消息通知插件(商业)
│ ├── iboot-open # 开放接口插件(商业)
│ ├── iboot-script # 脚本引擎插件(商业)
│ ├── iboot-oauth2 # OAuth2认证插件(商业)
- bootstrap: 项目启动模块, 包含项目的启动类和配置文件以及业务模块等. 包含的业务模块有:
- core包: 项目的核心模块, 包含系统的基础功能 如: 登录、菜单、授权、角色、用户、部门、字典、文件、日志、系统配置等
- iot包: 项目的物联网相关模块, 包含物联网功能 如: 设备管理、数据采集、规则引擎、告警等
- framework: 整个项目的框架层, 包含项目的通用工具类、常量类、枚举类、spring自动配置、api接口等
- iboot-plugin: 项目的插件模块, 包含项目的插件类、插件接口等(通过在bootstrap模块的pom.xml文件引入插件来实现插件的功能)
- iboot-code: 此插件下面包含代码生成器的支持, 目前支持mybatis-plus代码生成器
- iboot-protocol: 此插件提供对协议驱动的支持, 目前开源版支持mqtt、dtu(modbus)、modbus等协议
- iboot-SaToken: 此插件提供系统的权限、认证支持(和iboot-shiro插件二选一)
- iboot-shiro: 此插件提供系统的权限、认证支持(和iboot-SaToken插件二选一)
- iboot-knife4j: 此插件下面包含knife4j的api文档支持, 主要是对openapi的支持
- iboot-amqp(商业): 此插件下面包含消息队列的支持, 目前支持rabbitmq消息队列
- iboot-datasource(商业): 此插件下面包含多数据源的支持, 主要是采集数据的存储支持, 目前支持mysql和TDengine时序数据库
- iboot-gb28181(商业): 此插件下面包含gb28181的支持, 主要是gb28181设备的接入支持, 目前支持gb28181设备的接入和管理
- iboot-le5le(商业): 此插件提供对乐吾乐大屏和组态的支持
- iboot-message(商业): 此插件提供对告警消息的支持, 目前支持邮件、短信等消息发送
- iboot-open(商业): 此插件提供对外接口的支持, 通过此插件提供北向接口实现, 目前支持mqtt数据转发
- iboot-script(商业): 此插件提供对脚本的支持, 目前支持javascript脚本并提供模型数据的数据转换以及数据过滤(数据存储和mqtt转发)
- iboot-oauth2(商业): 此插件提供北向http协议接口的认证支持, 可以调用系统的任何接口
多模块
iboot是多maven模块的架构, 新增模块是有两种方式:
- 直接在bootstrap项目的module包下面新增一个业务模块包(目前已包含core和iot), 然后在新的模块中编写代码即可
- 通过在iboot-plugin项目的下面新增一个插件(maven模块), 然后在新的模块中编写代码, 然后在bootstrap项目的pom.xml文件中引入插件即可
新增模块
在bootstrap项目的module包下面新增模块, 例如新增一个demo模块
java
//1. 在bootstrap项目下面的module包新增一个demo包, 项目结构如下:
bootstrap
├── src
│ ├── main
│ │ ├── java
│ │ │ └── com.iteaj.iboot.module.demo
│ │ │ ├── service
│ │ │ │ ├── impl
│ │ │ │ │ └── DemoServiceImpl.java // 模块的服务实现类
│ │ │ │ └── IDemoService.java // 模块的服务接口类
│ │ │ ├── controller
│ │ │ │ └── DemoController.java // 模块的控制器类
│ │ │ ├── mapper
│ │ │ │ └── IDemoMapper.java // 模块的mapper类
│ │ │ ├── entity
│ │ │ │ └── DemoEntity.java // 模块的实体类
│ │ │ ├── DemoAutoConfiguration.java // 模块的自动配置类
// 2. 然后在demo模块包下面新增一个spring的自动配置类
package com.iteaj.iboot.module.demo;
import com.iteaj.framework.spi.admin.Module;
@AutoConfiguration
@MapperScan({"com.iteaj.iboot.module.demo.mapper"})
@ComponentScan(basePackages = "com.iteaj.iboot.module.demo")
public class DemoAutoConfiguration {
// 根据需要创建一个module类用来声明此模块
// 此module声明可以在菜单中显示, 用于控制模块的菜单权限
@Bean
public Module demoModule() {
return new Module("demo", "demo模块");
}
}
// 3. 然后在bootstrap项目的启动类{@code IBootApplication}上面导入配置类
@ImportAutoConfiguration(value = {
IotAutoConfiguration.class, // 物联网模块
DemoAutoConfiguration.class, // 新增的demo模块
// 以下是IBoot的核心配置(必须启用)
CoreAutoConfiguration.class, // 系统管理模块
})
@SpringBootApplication
public class IBootApplication {
public static void main(String[] args) {
SpringApplication.run(IBootApplication.class, args);
}
}
新增插件
- 在iboot-plugin项目的下面新增一个插件(maven项目), 例如新增一个demo插件, 项目结构如下:
java
// 注:包名最好以com.iteaj.iboot.plugin开头
iboot-plugin
├── iboot-demo
│ ├── src
│ │ ├── main
│ │ │ ├── java
│ │ │ │ └── com.iteaj.iboot.plugin.demo
│ │ │ │ ├── service
│ │ │ │ │ ├── IDemoService.java // 服务接口定义
│ │ │ │ │ └── impl
│ │ │ │ │ └── DemoServiceImpl.java // 服务接口实现
│ │ │ │ ├── controller
│ │ │ │ │ └── DemoController.java // 插件的控制器类
│ │ │ │ ├── mapper
│ │ │ │ │ └── IDemoMapper.java // 插件的mapper类
│ │ │ │ ├── entity
│ │ │ │ │ └── DemoEntity.java // 插件的实体类
│ │ │ │ ├── DemoAutoConfiguration.java // 插件的自动配置类
│ │ │ └── resources
│ │ │ └── META-INF
│ │ │ └── spring
│ │ │ ├── org.springframework.boot.autoconfigure.AutoConfiguration.imports
// 2. 为了告诉spring自动导入插件的自动配置类则需要在src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件中添加插件的自动配置类
com.iteaj.iboot.plugin.demo.DemoAutoConfiguration
// 3. 编写DemoAutoConfiguration类
package com.iteaj.iboot.plugin.demo;
import com.iteaj.framework.spi.admin.Module;
import com.iteaj.framework.security.OrderFilterChainDefinition;
@MapperScan({"com.iteaj.iboot.plugin.demo.mapper"})
@ComponentScan(basePackages = "com.iteaj.iboot.plugin.demo")
public class DemoAutoConfiguration {
// 根据需要创建一个module类用来声明此模块
// 此module声明可以在菜单中显示, 用于控制模块的菜单权限
@Bean
public Module demoModule() {
return new Module("demo", "demo模块");
}
// 如果此插件需要添加匿名访问的接口
@Bean
@Order(value = 58)
public OrderFilterChainDefinition demoFilterChainDefinition() {
return new OrderFilterChainDefinition()
.addAnon("/demo/a", "/demo/b/**");
}
}
// 4. 然后在bootstrap项目的pom.xml文件中引入依赖
<dependency>
<groupId>com.iteaj.iboot</groupId>
<artifactId>iboot-demo</artifactId>
<version>${project.version}</version>
</dependency>
设备状态事件
iboot平台支持监听设备上线/离线事件, 设备状态事件是由netty框架或者平台自动触发
java
/**
* 设备状态监听器
* @see CombinedEventListener 用于监听netty的tcp事件
* @see IotListener 用于监听非netty事件的设备状态, 如网关子设备的上线/离线事件等(有平台实现)
* @see ApplicationReadyListener 用于监听系统启动就绪事件, 做一些初始化操作
* @author iteaj
*/
@Component
public class DeviceStatusListener implements CombinedEventListener, ApplicationReadyListener, IotListener<RealtimeStatus> {
private final IotCacheManager cacheManager;
private final IDeviceService deviceService;
private final FrameworkProperties properties;
private Logger logger = LoggerFactory.getLogger(getClass());
public DeviceStatusListener(IotCacheManager cacheManager, IDeviceService deviceService, FrameworkProperties properties) {
this.cacheManager = cacheManager;
this.deviceService = deviceService;
this.properties = properties;
}
@Override
public void online(String source, FrameworkComponent component) {
// 设备上线事件
DeviceProtocolSupplier supplier = ProtocolSupplierManager.get(component.getMessageClass());
if(supplier != null) {
RealtimeStatus device = cacheManager.get(supplier.getProtocol().getCode(), source);
if(device != null) {
device.setStatus(DeviceStatus.online);
deviceService.updateDeviceStatus(device);
} else {
logger.warn("更新设备状态失败 设备不存在 - 设备编号: {} - 协议/驱动: {}", source, supplier.getDesc());
}
} else {
logger.warn("更新设备状态失败 协议不存在 - 设备编号: {} - 协议类: {}", source, component.getMessageClass().getSimpleName());
}
}
@Override
public void offline(String source, FrameworkComponent component) {
// 设备掉线事件
DeviceProtocolSupplier supplier = ProtocolSupplierManager.get(component.getMessageClass());
if(supplier != null) {
RealtimeStatus device = cacheManager.get(supplier.getProtocol().getCode(), source);
if(device != null) {
device.setStatus(DeviceStatus.offline);
deviceService.updateDeviceStatus(device);
} else {
logger.warn("更新设备状态失败 设备不存在 - 设备编号: {} - 协议码: {}", source, supplier.getProtocol().getCode());
}
} else {
logger.warn("更新设备状态失败 协议不存在 - 设备编号: {} - 协议类: {}", source, component.getMessageClass().getSimpleName());
}
}
@Override
public void started(ApplicationContext context) {
if(!properties.isCluster()) {
// 系统重启时 更新所有的设备状态到离线
deviceService.update(Wrappers.<Device>lambdaUpdate()
.set(Device::getStatus, DeviceStatus.offline)
.set(Device::getSwitchTime, new Date()));
}
}
@Override
public boolean isEventMatcher(IotEventType eventType) {
return IotEventType.DeviceStatus == eventType;
}
@Override
public void onIotEvent(IotEventType eventType, RealtimeStatus value) {
deviceService.updateDeviceStatus(value);
}
@Override
public int getOrder() {
return Integer.MIN_VALUE + 1000;
}
}
平台启动就绪事件
平台启动就绪事件是由spring框架触发的, 当spring容器启动完毕后会触发此事件, 可以在此事件中做一些初始化操作, 如初始化设备状态等, 可以通过实现{@code ApplicationReadyListener}接口来监听平台启动就绪事件
java
/**
* 应用启动就绪监听
*/
@FunctionalInterface
public interface ApplicationReadyListener extends EventListener, Ordered {
/**
* 启动完成处理
* @param context
*/
void started(ApplicationContext context);
@Override
default int getOrder() {
return Integer.MIN_VALUE + 100000;
}
}
// 平台以实现如下:
/**
* iot模块监听应用重启时的初始化监听
*/
@Component
public class IotRestartListener implements ApplicationReadyListener {}
/**
* 系统采集任务(事件源)初始化监听
*/
@Service
public class CollectTaskListenerService implements ApplicationReadyListener {}
@Component
public class EventSourceCollectService implements ApplicationReadyListener {}
/**
* 网关子设备的在线状态触发任务
*/
public class DeviceStatusModelApiManager implements Runnable, ApplicationReadyListener {}
websocket推送事件
此事件用于给前端推送实时采集数据、设备状态、告警事件
java
/**
* 将采集的数据实时推送到前端
*/
public class RealtimePushListener implements WebSocketServerListener, EventGroupCollectListener
, SignalCollectListener, DeviceStatusListener, ModelAttrListener {
// 省略具体代码, 具体实现可以参考源码
}