feat:新增数据监控页面

plp
HuangHuiKang 1 month ago
parent 0ca1282d55
commit ba743c9a26

@ -35,6 +35,8 @@
<dm8.jdbc.version>8.1.3.140</dm8.jdbc.version>
<kingbase.jdbc.version>8.6.0</kingbase.jdbc.version>
<opengauss.jdbc.version>5.1.0</opengauss.jdbc.version>
<taosdata.version>3.7.8</taosdata.version>
<!-- 消息队列 -->
<rocketmq-spring.version>2.3.1</rocketmq-spring.version>
<!-- 服务保障相关 -->
@ -287,6 +289,12 @@
<version>${kingbase.jdbc.version}</version>
</dependency>
<dependency>
<groupId>com.taosdata.jdbc</groupId>
<artifactId>taos-jdbcdriver</artifactId>
<version>${taosdata.version}</version>
</dependency>
<!-- Job 定时任务相关 -->
<dependency>
<groupId>cn.iocoder.boot</groupId>

@ -48,5 +48,6 @@ public interface ErrorCodeConstants {
ErrorCode OPC_CONNECT_FAILURE_DOES_NOT_EXIST= new ErrorCode(1_003_000_000, "OPC连接失败,请检测url及账号密码是否正确");
ErrorCode OPC_CLOSE_CONNECT_FAILURE= new ErrorCode(1_003_000_000, "OPC断开连接失败");
ErrorCode OPC_PARAMETER_DOES_NOT_EXIST= new ErrorCode(1_003_000_000, "连接关闭参数不存在");
ErrorCode CREATE_TDENGINE_FAILURE= new ErrorCode(1_003_000_000, "创建Tdengine子表失败");
}

@ -58,6 +58,11 @@
<artifactId>yudao-spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>com.taosdata.jdbc</groupId>
<artifactId>taos-jdbcdriver</artifactId>
</dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-excel</artifactId>

@ -6,9 +6,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.DevicePageReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.DeviceRespVO;
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.DeviceSaveReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.*;
import cn.iocoder.yudao.module.iot.controller.admin.devicecontactmodel.vo.DeviceContactModelPageReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.devicemodelattribute.vo.DeviceModelAttributePageReqVO;
import cn.iocoder.yudao.module.iot.dal.dataobject.device.DeviceAttributeDO;
@ -126,6 +124,18 @@ public class DeviceController {
}
@GetMapping("/lineDevicePage")
@Operation(summary = "获得产线设备分页")
@PreAuthorize("@ss.hasPermission('iot:device:query')")
public CommonResult<PageResult<LineDeviceRespVO>> lineDevicePage(@Valid LineDeviceRequestVO pageReqVO) {
PageResult<LineDeviceRespVO> pageResult = deviceService.lineDevicePage(pageReqVO);
return success(pageResult);
}
// ==================== 子表(设备属性) ====================
@ -169,4 +179,8 @@ public class DeviceController {
return success(deviceService.getDeviceAttribute(id));
}
}

@ -0,0 +1,17 @@
package cn.iocoder.yudao.module.iot.controller.admin.device.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.sql.DataSource;
@Configuration
public class TDengineConfig {
@Bean("tdengineJdbcTemplate")
public JdbcTemplate tdengineJdbcTemplate(DataSource dataSource) {
// 此处的dataSource会自动注入上面在yml中配置的TDengine数据源
return new JdbcTemplate(dataSource);
}
}

@ -0,0 +1,34 @@
package cn.iocoder.yudao.module.iot.controller.admin.device.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import java.time.LocalDateTime;
@Data
public class LineDeviceRequestVO extends PageParam {
@Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "26404")
private Long id;
@Schema(description = "产线编码")
private String lineNode;
@Schema(description = "产线名称")
private String lineName;
@Schema(description = "设备编码")
private String deviceCode;
@Schema(description = "设备名称")
private String deviceName;
@Schema(description = "状态 1-在线 2-离线")
private String status;
@Schema(description = "采集时间")
private LocalDateTime collectionTime;
}

@ -0,0 +1,35 @@
package cn.iocoder.yudao.module.iot.controller.admin.device.vo;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 产线设备分页返回 Resq VO")
@Data
public class LineDeviceRespVO {
@Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "26404")
private Long id;
@Schema(description = "产线编码")
private String lineNode;
@Schema(description = "产线名称")
private String lineName;
@Schema(description = "设备编码")
private String deviceCode;
@Schema(description = "设备名称")
private String deviceName;
@Schema(description = "状态 1-在线 2-离线")
private String status;
@Schema(description = "采集时间")
private LocalDateTime collectionTime;
}

@ -17,12 +17,12 @@ public class DeviceModelRespVO {
// @ExcelProperty("ID")
private Long id;
@Schema(description = "分类编码", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("分类编码")
@Schema(description = "模型编码", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("模型编码")
private String code;
@Schema(description = "分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
@ExcelProperty("分类名称")
@Schema(description = "模型名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
@ExcelProperty("模型名称")
private String name;
@Schema(description = "通讯协议", requiredMode = Schema.RequiredMode.REQUIRED)

@ -5,9 +5,14 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.DevicePageReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.LineDeviceRequestVO;
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.LineDeviceRespVO;
import cn.iocoder.yudao.module.iot.dal.dataobject.device.DeviceDO;
import com.alibaba.excel.util.StringUtils;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.Arrays;
import java.util.List;
@ -88,4 +93,7 @@ public interface DeviceMapper extends BaseMapperX<DeviceDO> {
.eqIfPresent(DeviceDO::getIsEnable, reqVO.getIsEnable())
.orderByDesc(DeviceDO::getId));
}
IPage<LineDeviceRespVO> lineDevicePage(Page<LineDeviceRespVO> page, @Param("pageReqVO") LineDeviceRequestVO pageReqVO);
}

@ -1,11 +1,14 @@
package cn.iocoder.yudao.module.iot.dal.mysql.devicemodel;
import java.util.*;
import java.util.stream.Collectors;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.iot.dal.dataobject.device.DeviceDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.devicemodel.DeviceModelDO;
import com.alibaba.excel.util.StringUtils;
import org.apache.ibatis.annotations.Mapper;
import cn.iocoder.yudao.module.iot.controller.admin.devicemodel.vo.*;
@ -18,13 +21,29 @@ import cn.iocoder.yudao.module.iot.controller.admin.devicemodel.vo.*;
public interface DeviceModelMapper extends BaseMapperX<DeviceModelDO> {
default PageResult<DeviceModelDO> selectPage(DeviceModelPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<DeviceModelDO>()
.eqIfPresent(DeviceModelDO::getCode, reqVO.getCode())
LambdaQueryWrapperX<DeviceModelDO> deviceModelDOLambdaQueryWrapperX = new LambdaQueryWrapperX<>();
deviceModelDOLambdaQueryWrapperX.eqIfPresent(DeviceModelDO::getCode, reqVO.getCode())
.likeIfPresent(DeviceModelDO::getName, reqVO.getName())
.eqIfPresent(DeviceModelDO::getProtocol, reqVO.getProtocol())
.eqIfPresent(DeviceModelDO::getRemark, reqVO.getRemark())
.betweenIfPresent(DeviceModelDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(DeviceModelDO::getId));
.orderByDesc(DeviceModelDO::getCreateTime);
// 单独处理 ids 条件
if (StringUtils.isNotBlank(reqVO.getIds())) {
List<Long> idList = Arrays.stream(reqVO.getIds().split(","))
.map(String::trim)
.map(Long::valueOf)
.collect(Collectors.toList());
deviceModelDOLambdaQueryWrapperX.in(DeviceModelDO::getId, idList);
}
return selectPage(reqVO, deviceModelDOLambdaQueryWrapperX);
}
default List<DeviceModelDO> select() {

@ -6,6 +6,8 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.DevicePageReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.DeviceSaveReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.LineDeviceRequestVO;
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.LineDeviceRespVO;
import cn.iocoder.yudao.module.iot.controller.admin.devicecontactmodel.vo.DeviceContactModelPageReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.devicemodelattribute.vo.DeviceModelAttributePageReqVO;
import cn.iocoder.yudao.module.iot.dal.dataobject.device.DeviceDO;
@ -118,4 +120,6 @@ public interface DeviceService {
Boolean connectDevice(DeviceSaveReqVO createReqVO);
Long copyDevice(Long id);
PageResult<LineDeviceRespVO> lineDevicePage(LineDeviceRequestVO pageReqVO);
}

@ -6,9 +6,10 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.common.util.opc.OpcUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.DevicePageReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.DeviceSaveReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.LineDeviceRequestVO;
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.LineDeviceRespVO;
import cn.iocoder.yudao.module.iot.controller.admin.devicecontactmodel.vo.DeviceContactModelPageReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.devicemodelattribute.vo.DeviceModelAttributePageReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.mqttdatarecord.vo.MqttDataRecordPageReqVO;
@ -27,8 +28,13 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.device.DeviceDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.device.DeviceAttributeDO;
import cn.iocoder.yudao.module.iot.dal.mysql.device.DeviceMapper;
import cn.iocoder.yudao.module.iot.dal.mysql.device.DeviceAttributeMapper;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
@ -66,6 +72,12 @@ public class DeviceServiceImpl implements DeviceService {
@Resource
private MqttDataRecordMapper mqttDataRecordMapper;
@Resource
@Qualifier("tdengineJdbcTemplate")
private JdbcTemplate tdengineJdbcTemplate;
@Resource
private TDengineService tdengineService;
@Override
@Transactional(rollbackFor = Exception.class)
@ -100,10 +112,60 @@ public class DeviceServiceImpl implements DeviceService {
}
deviceContactModelMapper.insertBatch(contactModelList);
//创建时序数据库
// createTDengine(device.getId());
tdengineService.initDatabaseAndTable(device.getId());
// 返回
return device.getId();
}
@DS("tdengine")
public void createTDengine(Long id) {
try {
// 测试TDengine连接
String testSQL = "SELECT 1";
tdengineJdbcTemplate.queryForObject(testSQL, Integer.class);
System.out.println("TDengine连接正常");
} catch (Exception e) {
throw new RuntimeException("无法连接到TDengine请检查数据源配置", e);
}
try {
// 创建数据库 - 使用更兼容的语法[1,6](@ref)
String createDbSQL = "CREATE DATABASE IF NOT EXISTS besure KEEP 365 DURATION 30";
tdengineJdbcTemplate.execute(createDbSQL);
// 使用数据库
tdengineJdbcTemplate.execute("USE besure");
// 创建超级表
String createSuperTableSQL = "CREATE STABLE IF NOT EXISTS device_data (" +
"ts TIMESTAMP, " +
"query_data NCHAR(2048)" +
") TAGS (device_id BIGINT)";
tdengineJdbcTemplate.execute(createSuperTableSQL);
// 创建子表
String tableName = "d_" + id;
String createTableSql = String.format(
"CREATE TABLE IF NOT EXISTS %s USING device_data TAGS(%d)",
tableName, id);
tdengineJdbcTemplate.execute(createTableSql);
System.out.println("TDengine表创建成功: " + tableName);
} catch (Exception e) {
System.err.println("TDengine操作失败: " + e.getMessage());
e.printStackTrace();
throw exception(CREATE_TDENGINE_FAILURE);
}
}
//@Scheduled(cron="0/5 * * * * ? ") //每1秒执行一次
public void updateDeviceStatus(){
List<DeviceDO> list = deviceMapper.selectList();
@ -317,6 +379,22 @@ public class DeviceServiceImpl implements DeviceService {
}
@Override
public PageResult<LineDeviceRespVO> lineDevicePage(LineDeviceRequestVO pageReqVO) {
Page<LineDeviceRespVO> page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());
IPage<LineDeviceRespVO> lineDeviceRespVO = deviceMapper.lineDevicePage(page,pageReqVO);
List<LineDeviceRespVO> records = lineDeviceRespVO.getRecords();
PageResult<LineDeviceRespVO> lineDeviceRespVOPageResult = new PageResult<>(lineDeviceRespVO.getRecords(), lineDeviceRespVO.getTotal());
return lineDeviceRespVOPageResult;
}
private void validateDeviceAttributeExists(Long id) {
if (deviceAttributeMapper.selectById(id) == null) {
throw exception(DEVICE_ATTRIBUTE_NOT_EXISTS);

@ -0,0 +1,48 @@
package cn.iocoder.yudao.module.iot.service.device;
import com.baomidou.dynamic.datasource.annotation.DS;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class TDengineService {
@Resource
private JdbcTemplate tdengineJdbcTemplate;
@DS("tdengine")
public void testConnection() {
String testSQL = "SELECT SERVER_STATUS()";
tdengineJdbcTemplate.queryForObject(testSQL, Integer.class);
System.out.println("TDengine连接正常");
}
@DS("tdengine")
public void initDatabaseAndTable(Long id) {
// 1. 创建数据库使用正确的TDengine语法
// 注意KEEP 必须大于或等于 3 倍的 DURATION[6](@ref),建议调整
String createDbSQL = "CREATE DATABASE IF NOT EXISTS besure KEEP 365 DURATION 30";
tdengineJdbcTemplate.execute(createDbSQL);
// 2. 使用数据库
tdengineJdbcTemplate.execute("USE besure");
// 3. 创建超级表
String createSuperTableSQL = "CREATE STABLE IF NOT EXISTS device_data (" +
"ts TIMESTAMP, " +
"query_data NCHAR(2048)" +
") TAGS (device_id BIGINT)";
tdengineJdbcTemplate.execute(createSuperTableSQL);
// 4. 创建子表
String tableName = "d_" + id;
String createTableSql = String.format(
"CREATE TABLE IF NOT EXISTS %s USING device_data TAGS(%d)",
tableName, id);
tdengineJdbcTemplate.execute(createTableSql);
System.out.println("TDengine表创建成功: " + tableName);
}
}

@ -9,4 +9,39 @@
文档可见https://www.iocoder.cn/MyBatis/x-plugins/
-->
<select id="lineDevicePage"
resultType="cn.iocoder.yudao.module.iot.controller.admin.device.vo.LineDeviceRespVO">
select
mo.id,
mo.code as lineNode,
mo.name as lineName,
iod.device_code as deviceCode,
iod.device_name as deviceName,
iod.status
from mes_organization mo
left join iot_device iod on mo.machine_id = iod.id
where mo.deleted = 0
and mo.machine_id is not null
<if test="pageReqVO.lineNode != null and pageReqVO.lineNode != ''">
and mo.code like concat(concat('%', #{pageReqVO.lineNode}), '%')
</if>
<if test="pageReqVO.lineName != null and pageReqVO.lineName != ''">
and mo.name like concat(concat('%', #{pageReqVO.lineName}), '%')
</if>
<if test="pageReqVO.deviceCode != null and pageReqVO.deviceCode != ''">
and iod.device_code like concat(concat('%', #{pageReqVO.deviceCode}), '%')
</if>
<if test="pageReqVO.deviceName != null and pageReqVO.deviceName != ''">
and iod.device_name like concat(concat('%', #{pageReqVO.deviceName}), '%')
</if>
<if test="pageReqVO.status != null and pageReqVO.status != ''">
and iod.status like concat(concat('%', #{pageReqVO.status}), '%')
</if>
order by mo.create_time desc
</select>
</mapper>

@ -105,6 +105,17 @@ public class DeviceServiceImplTest extends BaseDbUnitTest {
assertServiceException(() -> deviceService.deleteDevice(Collections.singletonList(id)), DEVICE_NOT_EXISTS);
}
@Test
public void testCreateTDengine() {
// 准备参数
Long id = 12313L;
deviceService.createTDengine(id);
}
@Test
@Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
public void testGetDevicePage() {

@ -55,6 +55,20 @@ spring:
# url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.master.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT # MySQL Connector/J 5.X 连接的示例
username: root
password: ngsk0809
tdengine:
name: tdengine
url: jdbc:TAOS-RS://192.168.5.5:6041/besure?charset=UTF-8&locale=en_US.UTF-8
username: root
password: taosdata
driver-class-name: com.taosdata.jdbc.rs.RestfulDriver # TDengine REST驱动
# TDengine 专用连接池配置
druid:
initial-size: 1
max-active: 20 # TDengine 连接数不需要太多
validation-query: SELECT SERVER_STATUS() # 使用 TDengine 兼容的验证语句[5](@ref)
connection-error-retry-attempts: 1 # 减少重试次数
break-after-acquire-failure: true
fail-fast: true
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
redis:

Loading…
Cancel
Save