diff --git a/fastbee-admin/src/main/resources/application-dev.yml b/fastbee-admin/src/main/resources/application-dev.yml index c73727ba..68a7be53 100644 --- a/fastbee-admin/src/main/resources/application-dev.yml +++ b/fastbee-admin/src/main/resources/application-dev.yml @@ -129,7 +129,7 @@ emqx: ApiSecret: emqx.api.123 # sip 配置 sip: - enabled: true # 是否启用视频监控SIP,true为启用 + enabled: false # 是否启用视频监控SIP,true为启用 ## 本地调试时,绑定网卡局域网IP,设备在同一局域网,设备接入IP填写绑定IP ## 部署服务端时,默认绑定容器IP,设备接入IP填写服务器公网IP ip: 192.168.110.160 # 本服务启动的 IP diff --git a/fastbee-common/src/main/java/com/fastbee/common/constant/FastBeeConstant.java b/fastbee-common/src/main/java/com/fastbee/common/constant/FastBeeConstant.java index ec6d5b7f..579cf11f 100644 --- a/fastbee-common/src/main/java/com/fastbee/common/constant/FastBeeConstant.java +++ b/fastbee-common/src/main/java/com/fastbee/common/constant/FastBeeConstant.java @@ -244,6 +244,10 @@ public interface FastBeeConstant { String TSL_PRE_KEY = "TSL:"; String MODBUS_PRE_KEY = "MODBUS:"; + /** + * 设备级 modbus 缓存 + */ + String MODBUS_DEVICE_PRE_KEY = "MODBUS:DEV:"; /** * modbus缓存指令 diff --git a/fastbee-common/src/main/java/com/fastbee/common/core/redis/RedisKeyBuilder.java b/fastbee-common/src/main/java/com/fastbee/common/core/redis/RedisKeyBuilder.java index 067576fd..a2ad5a0d 100644 --- a/fastbee-common/src/main/java/com/fastbee/common/core/redis/RedisKeyBuilder.java +++ b/fastbee-common/src/main/java/com/fastbee/common/core/redis/RedisKeyBuilder.java @@ -65,6 +65,13 @@ public class RedisKeyBuilder { return FastBeeConstant.REDIS.MODBUS_PRE_KEY + productId; } + /** + * 设备级 modbus 配置缓存 key + */ + public static String buildModbusDeviceKey(Long deviceId) { + return FastBeeConstant.REDIS.MODBUS_DEVICE_PRE_KEY + deviceId; + } + /** * 录像缓存key */ diff --git a/fastbee-iot-data/src/main/java/com/fastbee/iot/data/consumer/DevicePropFetchConsumer.java b/fastbee-iot-data/src/main/java/com/fastbee/iot/data/consumer/DevicePropFetchConsumer.java index 5011158b..307a4931 100644 --- a/fastbee-iot-data/src/main/java/com/fastbee/iot/data/consumer/DevicePropFetchConsumer.java +++ b/fastbee-iot-data/src/main/java/com/fastbee/iot/data/consumer/DevicePropFetchConsumer.java @@ -24,6 +24,7 @@ import com.fastbee.iot.enums.DeviceType; import com.fastbee.iot.model.DeviceStatusVO; import com.fastbee.iot.model.modbus.ModbusPollJob; import com.fastbee.iot.service.IDeviceService; +import com.fastbee.iot.service.IDeviceModbusParamsService; import com.fastbee.iot.service.IModbusJobService; import com.fastbee.iot.service.IModbusParamsService; import com.fastbee.mq.producer.MessageProducer; @@ -73,6 +74,8 @@ public class DevicePropFetchConsumer { private ISessionStore sessionStore; @Resource private IModbusParamsService modbusParamsService; + @Resource + private IDeviceModbusParamsService deviceModbusParamsService; @Value("${server.broker.enabled}") private Boolean enabled; @@ -197,8 +200,9 @@ public class DevicePropFetchConsumer { //如果是网关子设备,则检测子设备是否在特定时间内有数据上报 if (modbusJob.getDeviceType() == DeviceType.SUB_GATEWAY.getCode()) { long deterTime = 300L; //这里默认使用5分钟,如果轮询时间大于5分钟,请在配置参数设置更长时间 - ModbusParams params = modbusParamsService.getModbusParamsByDeviceId(modbusJob.getSubDeviceId()); - if (!Objects.isNull(params)) { + // 优先使用设备级配置,无则回退到产品级配置 + ModbusParams params = deviceModbusParamsService.getEffectiveParams(modbusJob.getSubDeviceId()); + if (!Objects.isNull(params) && !Objects.isNull(params.getDeterTimer())) { deterTime = Long.parseLong(params.getDeterTimer()); } diff --git a/fastbee-open-api/src/main/java/com/fastbee/controller/modbus/DeviceModbusConfigController.java b/fastbee-open-api/src/main/java/com/fastbee/controller/modbus/DeviceModbusConfigController.java new file mode 100644 index 00000000..0147be44 --- /dev/null +++ b/fastbee-open-api/src/main/java/com/fastbee/controller/modbus/DeviceModbusConfigController.java @@ -0,0 +1,76 @@ +package com.fastbee.controller.modbus; + +import com.fastbee.common.annotation.Log; +import com.fastbee.common.core.controller.BaseController; +import com.fastbee.common.core.domain.AjaxResult; +import com.fastbee.common.core.page.TableDataInfo; +import com.fastbee.common.enums.BusinessType; +import com.fastbee.iot.cache.IModbusConfigCache; +import com.fastbee.iot.domain.DeviceModbusConfig; +import com.fastbee.iot.model.modbus.DeviceModbusConfigVO; +import com.fastbee.iot.service.IDeviceModbusConfigService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 设备级 modbus 配置 + * + * @author gpt + */ +@RestController +@RequestMapping("/deviceModbus/config") +@Api(tags = "设备modbus配置") +public class DeviceModbusConfigController extends BaseController { + + @Autowired + private IDeviceModbusConfigService deviceModbusConfigService; + @Autowired + private IModbusConfigCache modbusConfigCache; + + /** + * 查询设备modbus配置列表 + */ + @PreAuthorize("@ss.hasPermi('modbus:config:list')") + @GetMapping("/list") + public TableDataInfo list(DeviceModbusConfig config) { + startPage(); + List list = deviceModbusConfigService.selectDeviceModbusConfigList(config); + return getDataTable(list); + } + + /** + * 批量新增或修改设备modbus配置 + */ + @PreAuthorize("@ss.hasPermi('modbus:config:add')") + @Log(title = "设备modbus配置", businessType = BusinessType.INSERT) + @PostMapping("/addBatch") + @ApiOperation("批量新增或修改设备modbus配置") + public AjaxResult addBatch(@RequestBody DeviceModbusConfigVO vo) { + deviceModbusConfigService.addOrUpDeviceModbusConfigBatch(vo.getConfigList(), vo.getProductId(), vo.getDeviceId(), vo.getDelIds()); + // 刷新设备缓存 + modbusConfigCache.setModbusConfigCacheByDeviceId(vo.getProductId(), vo.getDeviceId()); + return AjaxResult.success(); + } + + /** + * 删除设备modbus配置 + */ + @PreAuthorize("@ss.hasPermi('modbus:config:remove')") + @Log(title = "设备modbus配置", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Long[] ids, + @RequestParam(required = false) Long productId, + @RequestParam(required = false) Long deviceId) { + int rows = deviceModbusConfigService.deleteDeviceModbusConfigByIds(ids); + if (rows > 0 && deviceId != null) { + // 删除后刷新对应设备的寄存器缓存,避免解析仍命中旧配置 + modbusConfigCache.setModbusConfigCacheByDeviceId(productId, deviceId); + } + return toAjax(rows); + } +} diff --git a/fastbee-open-api/src/main/java/com/fastbee/controller/modbus/DeviceModbusParamsController.java b/fastbee-open-api/src/main/java/com/fastbee/controller/modbus/DeviceModbusParamsController.java new file mode 100644 index 00000000..5189008c --- /dev/null +++ b/fastbee-open-api/src/main/java/com/fastbee/controller/modbus/DeviceModbusParamsController.java @@ -0,0 +1,82 @@ +package com.fastbee.controller.modbus; + +import com.fastbee.common.core.controller.BaseController; +import com.fastbee.common.core.domain.AjaxResult; +import com.fastbee.iot.domain.DeviceModbusParams; +import com.fastbee.iot.service.IDeviceModbusParamsService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.util.Arrays; + +/** + * 设备modbus配置参数Controller + * + * @author Sumuxi + * @date 2026/1/14 + */ +@RestController +@RequestMapping("/modbus/device/params") +@Api(tags = "设备modbus配置参数") +public class DeviceModbusParamsController extends BaseController { + + @Resource + private IDeviceModbusParamsService deviceModbusParamsService; + + /** + * 根据设备ID获取设备modbus配置参数 + */ + @PreAuthorize("@ss.hasPermi('modbus:params:query')") + @GetMapping(value = "/getByDeviceId") + @ApiOperation("根据设备ID获取设备modbus配置参数") + public AjaxResult getByDeviceId(@ApiParam("设备ID") @RequestParam Long deviceId) { + return success(deviceModbusParamsService.getByDeviceId(deviceId)); + } + + /** + * 获取设备的有效modbus配置参数(设备级 > 产品级) + */ + @PreAuthorize("@ss.hasPermi('modbus:params:query')") + @GetMapping(value = "/getEffective") + @ApiOperation("获取设备的有效modbus配置参数(设备级优先,回退到产品级)") + public AjaxResult getEffective( + @ApiParam("设备ID") @RequestParam Long deviceId, + @ApiParam("产品ID") @RequestParam Long productId) { + return success(deviceModbusParamsService.getEffectiveParams(deviceId, productId)); + } + + /** + * 新增或更新设备modbus配置参数 + */ + @PreAuthorize("@ss.hasPermi('modbus:params:edit')") + @PostMapping("/addOrUpdate") + @ApiOperation("新增或更新设备modbus配置参数") + public AjaxResult addOrUpdate(@RequestBody DeviceModbusParams params) { + return toAjax(deviceModbusParamsService.addOrUpdate(params)); + } + + /** + * 根据设备ID删除设备modbus配置参数 + */ + @PreAuthorize("@ss.hasPermi('modbus:params:remove')") + @DeleteMapping("/deleteByDeviceId/{deviceId}") + @ApiOperation("根据设备ID删除设备modbus配置参数") + public AjaxResult deleteByDeviceId(@PathVariable Long deviceId) { + return toAjax(deviceModbusParamsService.deleteByDeviceId(deviceId)); + } + + /** + * 批量删除设备modbus配置参数 + */ + @PreAuthorize("@ss.hasPermi('modbus:params:remove')") + @DeleteMapping("/{ids}") + @ApiOperation("批量删除设备modbus配置参数") + public AjaxResult remove(@PathVariable Long[] ids) { + return toAjax(deviceModbusParamsService.removeByIds(Arrays.asList(ids))); + } +} + diff --git a/fastbee-protocol/fastbee-protocol-collect/src/main/java/com/fastbee/modbus/codec/ModbusProtocol.java b/fastbee-protocol/fastbee-protocol-collect/src/main/java/com/fastbee/modbus/codec/ModbusProtocol.java index d82b03e6..370bfed1 100644 --- a/fastbee-protocol/fastbee-protocol-collect/src/main/java/com/fastbee/modbus/codec/ModbusProtocol.java +++ b/fastbee-protocol/fastbee-protocol-collect/src/main/java/com/fastbee/modbus/codec/ModbusProtocol.java @@ -97,22 +97,23 @@ public class ModbusProtocol implements IProtocol { matchSubDev(report, deviceData.getSerialNumber(), slaveId); String serialNumber = report.getSerialNumber(); Long productId = report.getProductId(); + Long deviceId = getDeviceIdBySerial(serialNumber); ModbusCode modbusCode = ModbusCode.getInstance(code); List values = new ArrayList<>(); switch (modbusCode) { case Read01: case Read02: - //起始地址 - String address0102 = getCacheModbusAddress(deviceData.getSerialNumber(), slaveId, code); + //起始地址(从设备级Redis缓存获取) + String address0102 = getCacheModbusAddress(productId, deviceId); //读线圈或读离散型寄存器处理 - handleRead0102(productId, message, address0102, values); + handleRead0102(productId, deviceId, message, address0102, values); break; case Read03: case Read04: - //起始地址 - String address0304 = getCacheModbusAddress(deviceData.getSerialNumber(), slaveId, code); + //起始地址(从设备级Redis缓存获取) + String address0304 = getCacheModbusAddress(productId, deviceId); //读输入、保持寄存器 - handleRead0304(productId, message, address0304, values, hexDump); + handleRead0304(productId, deviceId, message, address0304, values, hexDump); break; case Write06: case Write05: @@ -144,18 +145,19 @@ public class ModbusProtocol implements IProtocol { * 读线圈或读离散型寄存器处理 * * @param productId 产品id + * @param deviceId 设备id(覆盖) * @param message 数据集合 * @param address 起始地址 * @param simpleItemList 返回的物模型值 */ - private void handleRead0102(Long productId, ModbusRtu message, String address, List simpleItemList) { + private void handleRead0102(Long productId, Long deviceId, ModbusRtu message, String address, List simpleItemList) { //字节数 int bitCount = message.getBitCount(); //字节数据集合 byte[] byteDatas = message.getBitData(); String hexDump = ByteBufUtil.hexDump(byteDatas); //处理多个的情况 - Map> modbusConfigMap = modbusConfigCache.getModbusConfigCacheByProductId(productId); + Map> modbusConfigMap = modbusConfigCache.getModbusConfigCacheByDeviceId(productId, deviceId); if (CollectionUtils.isEmpty(modbusConfigMap)) { log.warn("寄存器地址:{},不存在", address); return; @@ -192,17 +194,18 @@ public class ModbusProtocol implements IProtocol { * 处理读保持寄存器/输入寄存器 * * @param productId 产品id + * @param deviceId 设备id(覆盖) * @param message 数据 * @param address 起始地址 * @param simpleItemList 返回的物模型值 * @param sourceStr 原始报文字符串 */ - private void handleRead0304(Long productId, ModbusRtu message, String address, List simpleItemList, String sourceStr) { + private void handleRead0304(Long productId, Long deviceId, ModbusRtu message, String address, List simpleItemList, String sourceStr) { short[] data = message.getData(); int length = data.length; if (length == 1) { //单个寄存器上报情况处理 - List modbusConfig = modbusConfigCache.getSingleModbusConfig(productId, address); + List modbusConfig = getConfigByAddress(productId, deviceId, address); if (CollectionUtils.isEmpty(modbusConfig)) { log.warn("寄存器地址:{},不存在", address); return; @@ -239,7 +242,7 @@ public class ModbusProtocol implements IProtocol { // 原数据字符串(保持完整,不截取) String dataHex = sourceStr.substring(6, sourceStr.length() - 4); //处理多个情况 - Map> modbusConfigMap = modbusConfigCache.getModbusConfigCacheByProductId(productId); + Map> modbusConfigMap = modbusConfigCache.getModbusConfigCacheByDeviceId(productId, deviceId); if (CollectionUtils.isEmpty(modbusConfigMap)) { log.warn("寄存器数据不存在"); return; @@ -373,7 +376,8 @@ public class ModbusProtocol implements IProtocol { private void handleMsgReply(ModbusRtu message, DeviceReport report, ModbusCode code) { //单个寄存器上报情况处理 String address = message.getAddress() + ""; - List modbusConfig = modbusConfigCache.getSingleModbusConfig(report.getProductId(), address); + Long deviceId = getDeviceIdBySerial(report.getSerialNumber()); + List modbusConfig = getConfigByAddress(report.getProductId(), deviceId, address); if (CollectionUtils.isEmpty(modbusConfig)) { log.warn("寄存器地址:{},不存在", address); return; @@ -416,6 +420,30 @@ public class ModbusProtocol implements IProtocol { } } + /** + * 根据设备编号获取设备ID + */ + private Long getDeviceIdBySerial(String serialNumber) { + Device device = deviceMapper.selectDeviceBySerialNumber(serialNumber); + return device == null ? null : device.getDeviceId(); + } + + /** + * 根据地址获取设备优先的配置列表 + */ + private List getConfigByAddress(Long productId, Long deviceId, String address) { + Map> modbusConfigMap = modbusConfigCache.getModbusConfigCacheByDeviceId(productId, deviceId); + if (CollectionUtils.isEmpty(modbusConfigMap)) { + return java.util.Collections.emptyList(); + } + try { + int ad = Integer.parseInt(address); + return modbusConfigMap.getOrDefault(ad, java.util.Collections.emptyList()); + } catch (NumberFormatException e) { + return java.util.Collections.emptyList(); + } + } + @Override public FunctionCallBackBo encode(MQSendMessageBo message) { FunctionCallBackBo callBack = new FunctionCallBackBo(); @@ -484,39 +512,26 @@ public class ModbusProtocol implements IProtocol { } /** - * 获取modbus地址 + * 获取modbus起始地址 + * 从设备级Redis缓存获取配置,返回最小寄存器地址 + * + * @param productId 产品ID + * @param deviceId 设备ID + * @return 起始地址 */ - private String getCacheModbusAddress(String serialNumber, int salveId, int code) { -// String key = RedisKeyBuilder.buildModbusRuntimeCacheKey(serialNumber); -// Set commandSet = redisCache.zRange(key, 0, -1); -// -// // 1. 遍历匹配指令 -// for (String command : commandSet) { -// try { -// Mparams params = ModbusUtils.getModbusParams(command); -// redisCache.zRem(key, command); // 删除缓存 -// if (params.getSlaveId() == salveId && params.getCode() == code) { -// return String.valueOf(params.getAddress()); -// } -// } catch (Exception e) { -// log.warn("解析指令异常: {}", e.getMessage()); -// } -// } - - // 2. 无匹配时查询数据库 + private String getCacheModbusAddress(Long productId, Long deviceId) { try { - Device device = deviceMapper.selectDeviceBySerialNumber(serialNumber); - if (device == null) return "0"; - - List configs = modbusConfigMapper.selectModbusConfigListByProductId(device.getProductId()); - if (CollectionUtils.isEmpty(configs)) return "0"; - - return configs.stream() - .mapToInt(ModbusConfig::getAddress) + // 从设备级Redis缓存获取Modbus配置(key: buildModbusDeviceKey(deviceId)) + Map> modbusConfigMap = modbusConfigCache.getModbusConfigCacheByDeviceId(productId, deviceId); + if (CollectionUtils.isEmpty(modbusConfigMap)) { + return "0"; + } + return modbusConfigMap.keySet().stream() + .mapToInt(Integer::intValue) .min() .orElse(0) + ""; } catch (Exception e) { - log.error("数据库查询异常: {}", e.getMessage()); + log.error("获取Modbus配置缓存异常: {}", e.getMessage()); return "0"; } } diff --git a/fastbee-protocol/fastbee-protocol-collect/src/main/java/com/fastbee/modbustcp/codec/ModbusTcpProtocol.java b/fastbee-protocol/fastbee-protocol-collect/src/main/java/com/fastbee/modbustcp/codec/ModbusTcpProtocol.java index f56584df..b1e6817a 100644 --- a/fastbee-protocol/fastbee-protocol-collect/src/main/java/com/fastbee/modbustcp/codec/ModbusTcpProtocol.java +++ b/fastbee-protocol/fastbee-protocol-collect/src/main/java/com/fastbee/modbustcp/codec/ModbusTcpProtocol.java @@ -20,7 +20,9 @@ import com.fastbee.common.utils.StringUtils; import com.fastbee.common.utils.modbus.BitUtils; import com.fastbee.helper.ModbusProtocolHelper; import com.fastbee.iot.cache.IModbusConfigCache; +import com.fastbee.iot.domain.Device; import com.fastbee.iot.domain.ModbusConfig; +import com.fastbee.iot.mapper.DeviceMapper; import com.fastbee.iot.model.ThingsModels.ThingsModelValueItem; import com.fastbee.iot.service.ISubGatewayService; import com.fastbee.modbustcp.model.ModbusTcp; @@ -57,6 +59,8 @@ public class ModbusTcpProtocol implements IProtocol { private IModbusConfigCache modbusConfigCache; @Resource private ISubGatewayService subGatewayService; + @Resource + private DeviceMapper deviceMapper; @Override public DeviceReport decode(DeviceData deviceData, String clientId) { @@ -79,6 +83,7 @@ public class ModbusTcpProtocol implements IProtocol { matchSubDev(report, deviceData.getSerialNumber(), slaveId); String serialNumber = report.getSerialNumber(); Long productId = report.getProductId(); + Long deviceId = getDeviceIdBySerial(serialNumber); ModbusCode modbusCode = ModbusCode.getInstance(code); List values = new ArrayList<>(); switch (modbusCode) { @@ -87,7 +92,7 @@ public class ModbusTcpProtocol implements IProtocol { //起始地址 ModbusTcp rawModbusTcpRtu = getCacheModbusAddress(deviceData.getSerialNumber(), message.getTransactionIdentifier()); //读线圈或读离散型寄存器处理 - handleRead0102(productId, message, rawModbusTcpRtu, values); + handleRead0102(productId, deviceId, message, rawModbusTcpRtu, values); break; case Read03: case Read04: @@ -98,7 +103,7 @@ public class ModbusTcpProtocol implements IProtocol { String addressStr = sourceData.substring(17, 20); int address = Integer.parseInt(addressStr, 16); //读输入、保持寄存器 - handleRead0304(productId, message, String.valueOf(address), values, hexDump); + handleRead0304(productId, deviceId, message, String.valueOf(address), values, hexDump); break; case Write06: case Write05: @@ -130,18 +135,19 @@ public class ModbusTcpProtocol implements IProtocol { * 读线圈或读离散型寄存器处理 * * @param productId 产品id + * @param deviceId 设备id(覆盖) * @param message 数据集合 * @param modbusTcpRtu 下发报文 * @param simpleItemList 返回的物模型值 */ - private void handleRead0102(Long productId, ModbusTcp message, ModbusTcp modbusTcpRtu, List simpleItemList) { + private void handleRead0102(Long productId, Long deviceId, ModbusTcp message, ModbusTcp modbusTcpRtu, List simpleItemList) { //字节数 int bitCount = message.getBitCount(); //字节数据集合 byte[] byteDatas = message.getBitData(); String hexDump = ByteBufUtil.hexDump(byteDatas); //处理多个的情况 - Map> modbusConfigMap = modbusConfigCache.getModbusConfigCacheByProductId(productId); + Map> modbusConfigMap = modbusConfigCache.getModbusConfigCacheByDeviceId(productId, deviceId); if (CollectionUtils.isEmpty(modbusConfigMap)) { log.warn("寄存器地址:{},不存在", modbusTcpRtu.getAddress()); return; @@ -221,17 +227,18 @@ public class ModbusTcpProtocol implements IProtocol { * 处理读保持寄存器/输入寄存器 * * @param productId 产品id + * @param deviceId 设备id(覆盖) * @param message 数据 * @param address 起始地址 * @param simpleItemList 返回的物模型值 * @param sourceStr 原始报文字符串 */ - private void handleRead0304(Long productId, ModbusTcp message, String address, List simpleItemList, String sourceStr) { + private void handleRead0304(Long productId, Long deviceId, ModbusTcp message, String address, List simpleItemList, String sourceStr) { short[] data = message.getData(); int length = data.length; if (length == 1) { //单个寄存器上报情况处理 - List modbusConfig = modbusConfigCache.getSingleModbusConfig(productId, address); + List modbusConfig = getConfigByAddress(productId, deviceId, address); if (CollectionUtils.isEmpty(modbusConfig)) { log.warn("寄存器地址:{},不存在", address); return; @@ -268,7 +275,7 @@ public class ModbusTcpProtocol implements IProtocol { //原数据字符串 String dataHex = sourceStr.substring(6, sourceStr.length() - 4); //处理多个情况 - Map> modbusConfigMap = modbusConfigCache.getModbusConfigCacheByProductId(productId); + Map> modbusConfigMap = modbusConfigCache.getModbusConfigCacheByDeviceId(productId, deviceId); if (CollectionUtils.isEmpty(modbusConfigMap)) { log.warn("寄存器数据不存在"); return; @@ -329,7 +336,8 @@ public class ModbusTcpProtocol implements IProtocol { private void handleMsgReply(ModbusTcp message, DeviceReport report, ModbusCode code) { //单个寄存器上报情况处理 String address = message.getAddress() + ""; - List modbusConfig = modbusConfigCache.getSingleModbusConfig(report.getProductId(), address); + Long deviceId = getDeviceIdBySerial(report.getSerialNumber()); + List modbusConfig = getConfigByAddress(report.getProductId(), deviceId, address); if (CollectionUtils.isEmpty(modbusConfig)) { log.warn("寄存器地址:{},不存在", address); return; @@ -372,6 +380,24 @@ public class ModbusTcpProtocol implements IProtocol { } } + private Long getDeviceIdBySerial(String serialNumber) { + Device device = deviceMapper.selectDeviceBySerialNumber(serialNumber); + return device == null ? null : device.getDeviceId(); + } + + private List getConfigByAddress(Long productId, Long deviceId, String address) { + Map> modbusConfigMap = modbusConfigCache.getModbusConfigCacheByDeviceId(productId, deviceId); + if (CollectionUtils.isEmpty(modbusConfigMap)) { + return java.util.Collections.emptyList(); + } + try { + int ad = Integer.parseInt(address); + return modbusConfigMap.getOrDefault(ad, java.util.Collections.emptyList()); + } catch (NumberFormatException e) { + return java.util.Collections.emptyList(); + } + } + @Override public FunctionCallBackBo encode(MQSendMessageBo message) { FunctionCallBackBo callBack = new FunctionCallBackBo(); diff --git a/fastbee-protocol/fastbee-protocol-collect/src/main/java/com/fastbee/pakModbus/codec/ModbusRtuPakProtocol.java b/fastbee-protocol/fastbee-protocol-collect/src/main/java/com/fastbee/pakModbus/codec/ModbusRtuPakProtocol.java index 2e2c009a..cf0c0f64 100644 --- a/fastbee-protocol/fastbee-protocol-collect/src/main/java/com/fastbee/pakModbus/codec/ModbusRtuPakProtocol.java +++ b/fastbee-protocol/fastbee-protocol-collect/src/main/java/com/fastbee/pakModbus/codec/ModbusRtuPakProtocol.java @@ -11,7 +11,9 @@ import com.fastbee.common.core.thingsModel.ThingsModelSimpleItem; import com.fastbee.common.utils.DateUtils; import com.fastbee.common.utils.modbus.BitUtils; import com.fastbee.iot.cache.IModbusConfigCache; +import com.fastbee.iot.domain.Device; import com.fastbee.iot.domain.ModbusConfig; +import com.fastbee.iot.mapper.DeviceMapper; import com.fastbee.iot.model.ThingsModels.ThingsModelValueItem; import com.fastbee.pakModbus.model.PakModbusRtu; import com.fastbee.protocol.base.protocol.IProtocol; @@ -80,6 +82,8 @@ public class ModbusRtuPakProtocol implements IProtocol { private ModbusRtuPakEncoder rtuPakEncoder; @Resource private IModbusConfigCache modbusConfigCache; + @Resource + private DeviceMapper deviceMapper; @Override public DeviceReport decode(DeviceData deviceData, String clientId) { @@ -94,11 +98,14 @@ public class ModbusRtuPakProtocol implements IProtocol { return report; } PakModbusRtu message = rtuPakDecoder.decode(buf); + Long deviceId = getDeviceIdBySerial(deviceData.getSerialNumber()); + report.setSerialNumber(deviceData.getSerialNumber()); + report.setProductId(deviceData.getProductId()); List values = new ArrayList<>(); short[] data = message.getData(); for (int i = 0; i < data.length; i++) { int address = message.getAddress() + i; - handleModbusDataType(deviceData.getProductId(), address + "", data[i] + "", values); + handleModbusDataType(deviceData.getProductId(), deviceId, address + "", data[i] + "", values); } report.setClientId(clientId); report.setThingsModelSimpleItem(values); @@ -113,10 +120,10 @@ public class ModbusRtuPakProtocol implements IProtocol { * @param productId * @param values */ - private void handleModbusDataType(Long productId, String address, String value, List values) { + private void handleModbusDataType(Long productId, Long deviceId, String address, String value, List values) { int nValue = Integer.parseInt(value); - List modbusConfig = modbusConfigCache.getSingleModbusConfig(productId, address); + List modbusConfig = getConfigByAddress(productId, deviceId, address); if (CollectionUtils.isEmpty(modbusConfig)) { log.warn("寄存器地址:{},不存在", address); return; @@ -158,6 +165,24 @@ public class ModbusRtuPakProtocol implements IProtocol { } } + private Long getDeviceIdBySerial(String serialNumber) { + Device device = deviceMapper.selectDeviceBySerialNumber(serialNumber); + return device == null ? null : device.getDeviceId(); + } + + private List getConfigByAddress(Long productId, Long deviceId, String address) { + Map> modbusConfigMap = modbusConfigCache.getModbusConfigCacheByDeviceId(productId, deviceId); + if (CollectionUtils.isEmpty(modbusConfigMap)) { + return java.util.Collections.emptyList(); + } + try { + int ad = Integer.parseInt(address); + return modbusConfigMap.getOrDefault(ad, java.util.Collections.emptyList()); + } catch (NumberFormatException e) { + return java.util.Collections.emptyList(); + } + } + @Override public FunctionCallBackBo encode(MQSendMessageBo bo) { diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/cache/IModbusConfigCache.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/cache/IModbusConfigCache.java index 20bfdcad..83829e74 100644 --- a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/cache/IModbusConfigCache.java +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/cache/IModbusConfigCache.java @@ -13,7 +13,6 @@ import java.util.Map; */ public interface IModbusConfigCache { - /** * 获取modbus参数 --返回 Map * @@ -46,4 +45,18 @@ public interface IModbusConfigCache { * @return ModbusConfig */ public List getSingleModbusConfig(Long productId, String identify); + + /** + * 获取设备维度的modbus参数(设备优先,产品兜底) + * + * @param productId 产品id(兜底用) + * @param deviceId 设备id(优先) + * @return address -> configs + */ + Map> getModbusConfigCacheByDeviceId(Long productId, Long deviceId); + + /** + * 刷新设备维度缓存 + */ + Map> setModbusConfigCacheByDeviceId(Long productId, Long deviceId); } diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/cache/impl/ModbusConfigCacheImpl.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/cache/impl/ModbusConfigCacheImpl.java index 5d441339..0ff70581 100644 --- a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/cache/impl/ModbusConfigCacheImpl.java +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/cache/impl/ModbusConfigCacheImpl.java @@ -7,6 +7,7 @@ import com.fastbee.common.core.redis.RedisKeyBuilder; import com.fastbee.common.utils.StringUtils; import com.fastbee.iot.cache.IModbusConfigCache; import com.fastbee.iot.domain.ModbusConfig; +import com.fastbee.iot.service.IDeviceModbusConfigService; import com.fastbee.iot.service.IModbusConfigService; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; @@ -27,6 +28,8 @@ public class ModbusConfigCacheImpl implements IModbusConfigCache { @Resource private IModbusConfigService modbusConfigService; @Resource + private IDeviceModbusConfigService deviceModbusConfigService; + @Resource private RedisCache redisCache; /** @@ -104,4 +107,37 @@ public class ModbusConfigCacheImpl implements IModbusConfigCache { return modbusConfigList; } + @Override + public Map> getModbusConfigCacheByDeviceId(Long productId, Long deviceId) { + // 现在仅按设备配置解析,不再回落到产品模板 + if (deviceId == null) { + return new HashMap<>(); + } + String modbusKey = RedisKeyBuilder.buildModbusDeviceKey(deviceId); + Map map = redisCache.hashEntity(modbusKey); + if (!CollectionUtils.isEmpty(map)) { + Map> resultMap = new HashMap<>(); + for (Map.Entry entry : map.entrySet()) { + List modbusConfigList = JSON.parseArray(entry.getValue(), ModbusConfig.class); + resultMap.put(Integer.parseInt(entry.getKey()), modbusConfigList); + } + return resultMap; + } + return setModbusConfigCacheByDeviceId(productId, deviceId); + } + + @Override + public Map> setModbusConfigCacheByDeviceId(Long productId, Long deviceId) { + String modbusKey = RedisKeyBuilder.buildModbusDeviceKey(deviceId); + Map resultMap = new HashMap<>(); + // 仅使用设备维度的寄存器配置,不再合并产品模板 + List deviceConfigs = deviceModbusConfigService.selectDeviceConfigs(productId, deviceId); + Map> listMap = deviceConfigs.stream().collect(Collectors.groupingBy(ModbusConfig::getAddress)); + for (Map.Entry> entry : listMap.entrySet()) { + String valueStr = JSONObject.toJSONString(entry.getValue()); + resultMap.put(entry.getKey() + "", valueStr); + } + redisCache.hashPutAll(modbusKey, resultMap); + return listMap; + } } diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/domain/DeviceModbusConfig.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/domain/DeviceModbusConfig.java new file mode 100644 index 00000000..dc9ce954 --- /dev/null +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/domain/DeviceModbusConfig.java @@ -0,0 +1,27 @@ +package com.fastbee.iot.domain; + +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * @description: 设备级 modbus 配置对象 iot_device_modbus_config, + * 继承产品级 modbus 配置对象 iot_modbus_config,允许设备对产品模板做覆盖 + * + * @author Sumuxi + * @date 2026/1/14 + */ +@ApiModel(value = "DeviceModbusConfig", description = "设备modbus配置 iot_device_modbus_config") +@EqualsAndHashCode(callSuper = true) +@Data +@TableName("iot_device_modbus_config") +public class DeviceModbusConfig extends ModbusConfig { + + private static final long serialVersionUID = 1L; + + /** 所属设备id */ + @ApiModelProperty(value = "所属设备id") + private Long deviceId; +} diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/domain/DeviceModbusParams.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/domain/DeviceModbusParams.java new file mode 100644 index 00000000..63ceca6b --- /dev/null +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/domain/DeviceModbusParams.java @@ -0,0 +1,29 @@ +package com.fastbee.iot.domain; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * @description: 设备modbus配置参数对象 iot_device_modbus_params, + * 继承产品modbus配置参数对象 iot_modbus_params + * + * @author Sumuxi + * @date 2026/1/14 + */ +@ApiModel(value = "DeviceModbusParams", description = "设备modbus配置参数 iot_device_modbus_params") +@EqualsAndHashCode(callSuper = true) +@Data +@TableName("iot_device_modbus_params") +public class DeviceModbusParams extends ModbusParams { + + private static final long serialVersionUID = 1L; + + /** 所属设备id */ + @ApiModelProperty(value = "所属设备id") + @TableField("device_id") + private Long deviceId; +} diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/mapper/DeviceModbusConfigMapper.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/mapper/DeviceModbusConfigMapper.java new file mode 100644 index 00000000..a9d537aa --- /dev/null +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/mapper/DeviceModbusConfigMapper.java @@ -0,0 +1,44 @@ +package com.fastbee.iot.mapper; + +import com.fastbee.iot.domain.DeviceModbusConfig; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 设备级 modbus 配置 Mapper + */ +public interface DeviceModbusConfigMapper { + + DeviceModbusConfig selectDeviceModbusConfigById(Long id); + + List selectDeviceModbusConfigList(DeviceModbusConfig config); + + int insertDeviceModbusConfig(DeviceModbusConfig config); + + int updateDeviceModbusConfig(DeviceModbusConfig config); + + int deleteDeviceModbusConfigById(Long id); + + int deleteDeviceModbusConfigByIds(Long[] ids); + + List selectShortListByDeviceId(DeviceModbusConfig config); + + DeviceModbusConfig selectByIdentify(@Param("deviceId") Long deviceId, @Param("identity") String identity); + + /** + * 批量插入设备modbus配置 + * + * @param list 配置列表 + * @return 插入行数 + */ + int insertModbusConfigBatch(@Param("list") List list); + + /** + * 批量更新设备modbus配置 + * + * @param list 配置列表 + * @return 更新行数 + */ + int updateModbusConfigBatch(@Param("list") List list); +} diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/mapper/DeviceModbusParamsMapper.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/mapper/DeviceModbusParamsMapper.java new file mode 100644 index 00000000..1bc2821a --- /dev/null +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/mapper/DeviceModbusParamsMapper.java @@ -0,0 +1,31 @@ +package com.fastbee.iot.mapper; + +import com.fastbee.framework.mybatis.mapper.BaseMapperX; +import com.fastbee.iot.domain.DeviceModbusParams; +import org.apache.ibatis.annotations.Param; + +/** + * 设备modbus配置参数Mapper接口 + * + * @author Sumuxi + * @date 2026/1/14 + */ +public interface DeviceModbusParamsMapper extends BaseMapperX { + + /** + * 根据设备id获取modbus配置参数 + * + * @param deviceId 设备id + * @return DeviceModbusParams + */ + DeviceModbusParams getByDeviceId(@Param("deviceId") Long deviceId); + + /** + * 根据设备id删除配置 + * + * @param deviceId 设备id + * @return 影响行数 + */ + int deleteByDeviceId(@Param("deviceId") Long deviceId); +} + diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/model/modbus/DeviceModbusConfigVO.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/model/modbus/DeviceModbusConfigVO.java new file mode 100644 index 00000000..cb7cc359 --- /dev/null +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/model/modbus/DeviceModbusConfigVO.java @@ -0,0 +1,25 @@ +package com.fastbee.iot.model.modbus; + +import com.fastbee.iot.domain.DeviceModbusConfig; +import lombok.Data; + +import java.util.List; + +/** + * 设备级 modbus 配置批量提交 VO + */ +@Data +public class DeviceModbusConfigVO { + + /** 产品id */ + private Long productId; + + /** 设备id */ + private Long deviceId; + + /** modbus配置列表 */ + private List configList; + + /** 删除的id集合 */ + private Long[] delIds; +} diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/IDeviceModbusConfigService.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/IDeviceModbusConfigService.java new file mode 100644 index 00000000..2088d0d7 --- /dev/null +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/IDeviceModbusConfigService.java @@ -0,0 +1,50 @@ +package com.fastbee.iot.service; + +import com.fastbee.iot.domain.DeviceModbusConfig; +import com.fastbee.iot.domain.ModbusConfig; + +import java.util.List; + +/** + * 设备级 modbus 配置 Service + */ +public interface IDeviceModbusConfigService { + + DeviceModbusConfig selectDeviceModbusConfigById(Long id); + + List selectDeviceModbusConfigList(DeviceModbusConfig config); + + int insertDeviceModbusConfig(DeviceModbusConfig config); + + int updateDeviceModbusConfig(DeviceModbusConfig config); + + int deleteDeviceModbusConfigByIds(Long[] ids); + + int deleteDeviceModbusConfigById(Long id); + + /** + * 批量新增或更新设备级配置 + */ + void addOrUpDeviceModbusConfigBatch(List configList, Long productId, Long deviceId, Long[] delIds); + + /** + * 获取设备级简要字段 + */ + List selectShortListByDeviceId(DeviceModbusConfig config); + + /** + * 根据设备和标识符获取配置 + */ + DeviceModbusConfig selectByIdentify(Long deviceId, String identity); + + /** + * 合并设备覆盖后的最终配置列表(转为 ModbusConfig 供解析和缓存) + */ + List selectMergedConfig(Long productId, Long deviceId); + + /** + * 仅设备维度的配置列表(转为 ModbusConfig 供解析和缓存) + */ + List selectDeviceConfigs(Long productId, Long deviceId); +} + diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/IDeviceModbusParamsService.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/IDeviceModbusParamsService.java new file mode 100644 index 00000000..9668780b --- /dev/null +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/IDeviceModbusParamsService.java @@ -0,0 +1,102 @@ +package com.fastbee.iot.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.fastbee.iot.domain.DeviceModbusParams; +import com.fastbee.iot.domain.ModbusParams; + +import java.util.List; + +/** + * 设备modbus配置参数Service接口 + * + * @author Sumuxi + * @date 2026/1/14 + */ +public interface IDeviceModbusParamsService extends IService { + + /** + * 根据设备id获取modbus配置参数 + * + * @param deviceId 设备id + * @return DeviceModbusParams + */ + DeviceModbusParams getByDeviceId(Long deviceId); + + /** + * 根据设备id获取modbus配置参数(带缓存) + * + * @param deviceId 设备id + * @return DeviceModbusParams + */ + DeviceModbusParams getByDeviceIdWithCache(Long deviceId); + + /** + * 获取设备的有效modbus配置参数(自动获取productId) + * 如果设备级配置存在则返回设备级,否则返回产品级默认配置 + * 推荐使用此方法,便于平滑切换 + * + * @param deviceId 设备id + * @return ModbusParams + */ + ModbusParams getEffectiveParams(Long deviceId); + + /** + * 获取设备的有效modbus配置参数 + * 如果设备级配置存在则返回设备级,否则返回产品级默认配置 + * + * @param deviceId 设备id + * @param productId 产品id + * @return ModbusParams + */ + ModbusParams getEffectiveParams(Long deviceId, Long productId); + + /** + * 新增设备modbus配置参数 + * + * @param params 设备modbus配置参数 + * @return 是否成功 + */ + Boolean insertParams(DeviceModbusParams params); + + /** + * 新增或更新设备modbus配置参数 + * + * @param params 设备modbus配置参数 + * @return 是否成功 + */ + boolean addOrUpdate(DeviceModbusParams params); + + /** + * 修改设备modbus配置参数 + * + * @param params 设备modbus配置参数 + * @return 是否成功 + */ + Boolean updateParams(DeviceModbusParams params); + + /** + * 根据设备id删除配置 + * + * @param deviceId 设备id + * @return 是否成功 + */ + Boolean deleteByDeviceId(Long deviceId); + + /** + * 批量删除设备modbus配置参数 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否成功 + */ + Boolean deleteByIds(Long[] ids, Boolean isValid); + + /** + * 根据设备id列表查询配置列表 + * + * @param deviceIds 设备id列表 + * @return 配置列表 + */ + List selectByDeviceIds(List deviceIds); +} + diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/impl/DeviceModbusConfigServiceImpl.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/impl/DeviceModbusConfigServiceImpl.java new file mode 100644 index 00000000..b6b731f2 --- /dev/null +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/impl/DeviceModbusConfigServiceImpl.java @@ -0,0 +1,164 @@ +package com.fastbee.iot.service.impl; + +import com.fastbee.common.exception.ServiceException; +import com.fastbee.common.utils.DateUtils; +import com.fastbee.common.utils.SecurityUtils; +import com.fastbee.iot.domain.DeviceModbusConfig; +import com.fastbee.iot.domain.ModbusConfig; +import com.fastbee.iot.mapper.DeviceModbusConfigMapper; +import com.fastbee.iot.mapper.ModbusConfigMapper; +import com.fastbee.iot.service.IDeviceModbusConfigService; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; + +import javax.annotation.Resource; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 设备级 modbus 配置 Service 实现 + */ +@Service +public class DeviceModbusConfigServiceImpl implements IDeviceModbusConfigService { + + @Resource + private DeviceModbusConfigMapper deviceModbusConfigMapper; + @Resource + private ModbusConfigMapper modbusConfigMapper; + + @Override + public DeviceModbusConfig selectDeviceModbusConfigById(Long id) { + return deviceModbusConfigMapper.selectDeviceModbusConfigById(id); + } + + @Override + public List selectDeviceModbusConfigList(DeviceModbusConfig config) { + return deviceModbusConfigMapper.selectDeviceModbusConfigList(config); + } + + @Override + public int insertDeviceModbusConfig(DeviceModbusConfig config) { + config.setCreateTime(DateUtils.getNowDate()); + return deviceModbusConfigMapper.insertDeviceModbusConfig(config); + } + + @Override + public int updateDeviceModbusConfig(DeviceModbusConfig config) { + config.setUpdateTime(DateUtils.getNowDate()); + return deviceModbusConfigMapper.updateDeviceModbusConfig(config); + } + + @Override + public int deleteDeviceModbusConfigByIds(Long[] ids) { + return deviceModbusConfigMapper.deleteDeviceModbusConfigByIds(ids); + } + + @Override + public int deleteDeviceModbusConfigById(Long id) { + return deviceModbusConfigMapper.deleteDeviceModbusConfigById(id); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void addOrUpDeviceModbusConfigBatch(List configList, Long productId, Long deviceId, Long[] delIds) { + if (Objects.isNull(productId)) { + throw new ServiceException("产品id不能为空"); + } + if (Objects.isNull(deviceId)) { + throw new ServiceException("设备id不能为空"); + } + if (CollectionUtils.isEmpty(configList)) { + throw new ServiceException("modbus配置数据不能为空"); + } + if (delIds != null && delIds.length > 0) { + this.deleteDeviceModbusConfigByIds(delIds); + } + List insertModbusConfigs = new ArrayList<>(); + List updateModbusConfigs = new ArrayList<>(); + for (DeviceModbusConfig config : configList) { + config.setProductId(productId); + config.setDeviceId(deviceId); + if (Objects.isNull(config.getId())) { + config.setCreateBy(SecurityUtils.getUsername()); + config.setCreateTime(DateUtils.getNowDate()); + //this.insertDeviceModbusConfig(config); + insertModbusConfigs.add(config); + } else { + config.setUpdateBy(SecurityUtils.getUsername()); + config.setUpdateTime(DateUtils.getNowDate()); + //this.updateDeviceModbusConfig(config); + updateModbusConfigs.add(config); + } + } + if (!CollectionUtils.isEmpty(insertModbusConfigs)) { + deviceModbusConfigMapper.insertModbusConfigBatch(insertModbusConfigs); + } + if (!CollectionUtils.isEmpty(updateModbusConfigs)) { + deviceModbusConfigMapper.updateModbusConfigBatch(updateModbusConfigs); + } + } + + @Override + public List selectShortListByDeviceId(DeviceModbusConfig config) { + return deviceModbusConfigMapper.selectShortListByDeviceId(config); + } + + @Override + public DeviceModbusConfig selectByIdentify(Long deviceId, String identity) { + return deviceModbusConfigMapper.selectByIdentify(deviceId, identity); + } + + @Override + public List selectMergedConfig(Long productId, Long deviceId) { + List deviceList = selectShortListByDeviceId(buildShortConfig(deviceId, productId, null)); + List productList = Collections.emptyList(); + if (productId != null) { + ModbusConfig p = new ModbusConfig(); + p.setProductId(productId); + productList = new ArrayList<>(Optional.ofNullable(modbusConfigMapper.selectShortListByProductId(p)).orElse(Collections.emptyList())); + } + Map merged = new LinkedHashMap<>(); + // 产品模板 + for (ModbusConfig config : productList) { + merged.put(config.getIdentifier(), config); + } + // 设备覆盖(同 identifier 覆盖产品) + for (DeviceModbusConfig deviceConfig : deviceList) { + merged.put(deviceConfig.getIdentifier(), convert(deviceConfig)); + } + return new ArrayList<>(merged.values()); + } + + private DeviceModbusConfig buildShortConfig(Long deviceId, Long productId, Integer type) { + DeviceModbusConfig config = new DeviceModbusConfig(); + config.setDeviceId(deviceId); + config.setProductId(productId); + config.setType(type); + return config; + } + + /** + * 用于兼容 cache 逻辑的转换 + */ + private ModbusConfig convert(DeviceModbusConfig config) { + ModbusConfig target = new ModbusConfig(); + BeanUtils.copyProperties(config, target); + return target; + } + + @Override + public List selectDeviceConfigs(Long productId, Long deviceId) { + List deviceList = selectShortListByDeviceId(buildShortConfig(deviceId, productId, null)); + return deviceList.stream().map(this::convert).collect(Collectors.toList()); + } + + /** + * 供单元测试或特殊场景调用,避免直接依赖 service 造成循环 + */ + protected List selectProductShortList(ModbusConfig config) { + return modbusConfigMapper.selectShortListByProductId(config); + } +} + diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/impl/DeviceModbusParamsServiceImpl.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/impl/DeviceModbusParamsServiceImpl.java new file mode 100644 index 00000000..3bed1295 --- /dev/null +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/impl/DeviceModbusParamsServiceImpl.java @@ -0,0 +1,131 @@ +package com.fastbee.iot.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.fastbee.common.utils.DateUtils; +import com.fastbee.common.utils.SecurityUtils; +import com.fastbee.iot.domain.Device; +import com.fastbee.iot.domain.DeviceModbusParams; +import com.fastbee.iot.domain.ModbusParams; +import com.fastbee.iot.mapper.DeviceMapper; +import com.fastbee.iot.mapper.DeviceModbusParamsMapper; +import com.fastbee.iot.service.IDeviceModbusParamsService; +import com.fastbee.iot.service.IModbusParamsService; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +/** + * 设备modbus配置参数Service业务层处理 + * + * @author Sumuxi + * @date 2026/1/14 + */ +@Service +public class DeviceModbusParamsServiceImpl extends ServiceImpl + implements IDeviceModbusParamsService { + + private static final String CACHE_NAME = "DeviceModbusParams"; + + @Resource + private IModbusParamsService modbusParamsService; + + @Resource + private DeviceMapper deviceMapper; + + @Override + public DeviceModbusParams getByDeviceId(Long deviceId) { + return baseMapper.getByDeviceId(deviceId); + } + + @Override + @Cacheable(cacheNames = CACHE_NAME, key = "#deviceId") + public DeviceModbusParams getByDeviceIdWithCache(Long deviceId) { + return baseMapper.getByDeviceId(deviceId); + } + + @Override + public ModbusParams getEffectiveParams(Long deviceId) { + // 优先返回设备级配置 + DeviceModbusParams deviceParams = this.getByDeviceId(deviceId); + if (Objects.nonNull(deviceParams)) { + return deviceParams; + } + // 查询设备获取productId,回退到产品级默认配置 + Device device = deviceMapper.selectDeviceByDeviceId(deviceId); + if (Objects.nonNull(device) && Objects.nonNull(device.getProductId())) { + return modbusParamsService.getModbusParamsByProductId(device.getProductId()); + } + return null; + } + + @Override + public ModbusParams getEffectiveParams(Long deviceId, Long productId) { + // 优先返回设备级配置 + DeviceModbusParams deviceParams = this.getByDeviceId(deviceId); + if (Objects.nonNull(deviceParams)) { + return deviceParams; + } + // 返回产品级默认配置 + return modbusParamsService.getModbusParamsByProductId(productId); + } + + @Override + public Boolean insertParams(DeviceModbusParams params) { + params.setCreateBy(SecurityUtils.getUsername()); + params.setCreateTime(DateUtils.getNowDate()); + return this.save(params); + } + + @Override + @CacheEvict(cacheNames = CACHE_NAME, key = "#params.deviceId") + public boolean addOrUpdate(DeviceModbusParams params) { + Long deviceId = params.getDeviceId(); + DeviceModbusParams existing = this.getByDeviceId(deviceId); + if (Objects.nonNull(existing)) { + params.setId(existing.getId()); + params.setUpdateBy(SecurityUtils.getUsername()); + params.setUpdateTime(DateUtils.getNowDate()); + } else { + params.setCreateBy(SecurityUtils.getUsername()); + params.setCreateTime(DateUtils.getNowDate()); + } + return this.saveOrUpdate(params); + } + + @Override + @CacheEvict(cacheNames = CACHE_NAME, key = "#params.deviceId") + public Boolean updateParams(DeviceModbusParams params) { + params.setUpdateBy(SecurityUtils.getUsername()); + params.setUpdateTime(DateUtils.getNowDate()); + return this.updateById(params); + } + + @Override + @CacheEvict(cacheNames = CACHE_NAME, key = "#deviceId") + public Boolean deleteByDeviceId(Long deviceId) { + return baseMapper.deleteByDeviceId(deviceId) > 0; + } + + @Override + @CacheEvict(cacheNames = CACHE_NAME, allEntries = true) + public Boolean deleteByIds(Long[] ids, Boolean isValid) { + if (isValid) { + // 可以在此添加业务校验逻辑 + } + return this.removeByIds(Arrays.asList(ids)); + } + + @Override + public List selectByDeviceIds(List deviceIds) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.in(DeviceModbusParams::getDeviceId, deviceIds); + return baseMapper.selectList(queryWrapper); + } +} + diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/impl/ModbusConfigServiceImpl.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/impl/ModbusConfigServiceImpl.java index 11847954..bd72c037 100644 --- a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/impl/ModbusConfigServiceImpl.java +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/impl/ModbusConfigServiceImpl.java @@ -109,7 +109,7 @@ public class ModbusConfigServiceImpl implements IModbusConfigService { assert !Objects.isNull(productId) : "产品id不能为空"; assert !CollectionUtils.isEmpty(modbusConfigList) : "modbus配置数据不能为空"; if (!CollectionUtils.isEmpty(Arrays.asList(ids))) { - //要删除的id集合不为空,则先进行删除操作 + // 要删除的id集合不为空,则先进行删除操作 this.deleteModbusConfigByIds(ids); } for (ModbusConfig config : modbusConfigList) { diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/impl/SubGatewayServiceImpl.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/impl/SubGatewayServiceImpl.java index a18916c1..7fedad7a 100644 --- a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/impl/SubGatewayServiceImpl.java +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/impl/SubGatewayServiceImpl.java @@ -10,6 +10,7 @@ import com.fastbee.iot.mapper.SubGatewayMapper; import com.fastbee.iot.model.gateWay.GateSubDeviceVO; import com.fastbee.iot.model.gateWay.SubDeviceAddVO; import com.fastbee.iot.model.gateWay.SubDeviceListVO; +import com.fastbee.iot.service.IDeviceModbusParamsService; import com.fastbee.iot.service.IDeviceService; import com.fastbee.iot.service.IModbusParamsService; import com.fastbee.iot.service.ISubGatewayService; @@ -33,6 +34,8 @@ public class SubGatewayServiceImpl implements ISubGatewayService { private SubGatewayMapper gatewayMapper; @Resource private IModbusParamsService modbusParamsService; + @Resource + private IDeviceModbusParamsService deviceModbusParamsService; /** * 查询网关与子设备关联 @@ -125,7 +128,8 @@ public class SubGatewayServiceImpl implements ISubGatewayService { List list = new ArrayList<>(); if (subDeviceIds.length > 0) { for (Long subDeviceId : subDeviceIds) { - ModbusParams modbusParams = modbusParamsService.getModbusParamsByDeviceId(subDeviceId); + // 优先使用设备级配置,无则回退到产品级配置 + ModbusParams modbusParams = deviceModbusParamsService.getEffectiveParams(subDeviceId); SubGateway subGateway = new SubGateway(); subGateway.setSubDeviceId(subDeviceId); subGateway.setGwDeviceId(gwDeviceId); diff --git a/fastbee-service/fastbee-iot-service/src/main/resources/mapper/iot/DeviceModbusConfigMapper.xml b/fastbee-service/fastbee-iot-service/src/main/resources/mapper/iot/DeviceModbusConfigMapper.xml new file mode 100644 index 00000000..6c47155c --- /dev/null +++ b/fastbee-service/fastbee-iot-service/src/main/resources/mapper/iot/DeviceModbusConfigMapper.xml @@ -0,0 +1,406 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + select id, + device_id, + product_id, + identifier, + slave, + address, + is_readonly, + data_type, + quantity, + type, + bit_order, + sort, + del_flag, + create_by, + create_time, + update_by, + update_time, + remark + from iot_device_modbus_config + + + + + + + + insert into iot_device_modbus_config + + + id, + + + device_id, + + + product_id, + + + identifier, + + + slave, + + + address, + + + is_readonly, + + + data_type, + + + quantity, + + + type, + + + del_flag, + + + create_by, + + + create_time, + + + update_by, + + + update_time, + + + remark, + + + sort, + + + bit_order, + + + + + #{id}, + + + #{deviceId}, + + + #{productId}, + + + #{identifier}, + + + #{slave}, + + + #{address}, + + + #{isReadonly}, + + + #{dataType}, + + + #{quantity}, + + + #{type}, + + + #{delFlag}, + + + #{createBy}, + + + #{createTime}, + + + #{updateBy}, + + + #{updateTime}, + + + #{remark}, + + + #{sort}, + + + #{bitOrder}, + + + + + + update iot_device_modbus_config + + + device_id = #{deviceId}, + + + product_id = #{productId}, + + + identifier = #{identifier}, + + + slave = #{slave}, + + + address = #{address}, + + + is_readonly = #{isReadonly}, + + + data_type = #{dataType}, + + + quantity = #{quantity}, + + + type = #{type}, + + + del_flag = #{delFlag}, + + + create_by = #{createBy}, + + + create_time = #{createTime}, + + + update_by = #{updateBy}, + + + update_time = #{updateTime}, + + + remark = #{remark}, + + + sort = #{sort}, + + + bit_order = #{bitOrder}, + + + where id = #{id} + + + + delete + from iot_device_modbus_config + where id = #{id} + + + + delete from iot_device_modbus_config where id in + + #{id} + + + + + + + + + + insert into iot_device_modbus_config ( + device_id, product_id, identifier, slave, address, is_readonly, + data_type, quantity, type, bit_order, sort, del_flag, + create_by, create_time, update_by, update_time, remark + ) values + + ( + #{item.deviceId}, #{item.productId}, #{item.identifier}, #{item.slave}, #{item.address}, #{item.isReadonly}, + #{item.dataType}, #{item.quantity}, #{item.type}, #{item.bitOrder}, #{item.sort}, #{item.delFlag}, + #{item.createBy}, #{item.createTime}, #{item.updateBy}, #{item.updateTime}, #{item.remark} + ) + + + + + + update iot_device_modbus_config + + + + when id = #{item.id} then #{item.deviceId} + + + + + when id = #{item.id} then #{item.productId} + + + + + when id = #{item.id} then #{item.identifier} + + + + + when id = #{item.id} then #{item.slave} + + + + + when id = #{item.id} then #{item.address} + + + + + when id = #{item.id} then #{item.isReadonly} + + + + + when id = #{item.id} then #{item.dataType} + + + + + when id = #{item.id} then #{item.quantity} + + + + + when id = #{item.id} then #{item.type} + + + + + when id = #{item.id} then #{item.bitOrder} + + + + + when id = #{item.id} then #{item.sort} + + + + + when id = #{item.id} then #{item.updateBy} + + + + + when id = #{item.id} then #{item.updateTime} + + + + + when id = #{item.id} then #{item.remark} + + + + where id in + + #{item.id} + + + + diff --git a/fastbee-service/fastbee-iot-service/src/main/resources/mapper/iot/DeviceModbusParamsMapper.xml b/fastbee-service/fastbee-iot-service/src/main/resources/mapper/iot/DeviceModbusParamsMapper.xml new file mode 100644 index 00000000..3f300543 --- /dev/null +++ b/fastbee-service/fastbee-iot-service/src/main/resources/mapper/iot/DeviceModbusParamsMapper.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + select id, device_id, product_id, poll_type, poll_length, slave_id, status_deter, deter_timer, + create_by, create_time, update_by, update_time, remark + from iot_device_modbus_params + + + + + + delete from iot_device_modbus_params where device_id = #{deviceId} + + + + diff --git a/sql/fastbee-v2.5.sql b/sql/fastbee-v2.5.sql index c94e2a89..eb4a8c50 100644 --- a/sql/fastbee-v2.5.sql +++ b/sql/fastbee-v2.5.sql @@ -801,6 +801,38 @@ CREATE TABLE `iot_modbus_config` ( -- Records of iot_modbus_config -- ---------------------------- +-- ---------------------------- +-- Table structure for iot_device_modbus_config +-- ---------------------------- +DROP TABLE IF EXISTS `iot_device_modbus_config`; +CREATE TABLE `iot_device_modbus_config` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '业务id', + `device_id` bigint(20) NOT NULL COMMENT '所属设备id', + `product_id` bigint(20) NOT NULL COMMENT '所属产品id', + `identifier` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '关联属性', + `slave` int(10) NULL DEFAULT NULL COMMENT '从机地址', + `address` int(10) NOT NULL COMMENT '寄存器地址', + `is_readonly` tinyint(1) NOT NULL COMMENT '是否只读(0-否,1-是)', + `data_type` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'modbus数据类型', + `quantity` int(2) NULL DEFAULT NULL COMMENT '读取个数', + `type` tinyint(1) NOT NULL COMMENT '寄存器类型 1-IO寄存器 2-数据寄存器', + `bit_order` int(2) NULL DEFAULT NULL COMMENT 'bit位排序', + `sort` int(6) NULL DEFAULT NULL COMMENT '排序', + `del_flag` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)', + `create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '创建者', + `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', + `update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '更新者', + `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', + `remark` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '备注', + PRIMARY KEY (`id`) USING BTREE, + KEY `idx_device_id` (`device_id`) USING BTREE COMMENT '设备id索引', + KEY `idx_product_id` (`product_id`) USING BTREE COMMENT '产品id索引' +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '设备modbus配置表' ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of iot_device_modbus_config +-- ---------------------------- + -- ---------------------------- -- Table structure for iot_modbus_job -- ---------------------------- @@ -848,6 +880,33 @@ CREATE TABLE `iot_modbus_params` ( -- Records of iot_modbus_params -- ---------------------------- +-- ---------------------------- +-- Table structure for iot_device_modbus_params +-- ---------------------------- +DROP TABLE IF EXISTS `iot_device_modbus_params`; +CREATE TABLE `iot_device_modbus_params` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '业务id', + `device_id` bigint(20) NOT NULL COMMENT '设备id', + `product_id` bigint(20) NOT NULL COMMENT '产品id', + `poll_type` tinyint(1) NULL DEFAULT NULL COMMENT '是否启动云端轮训(0-云端轮训,1-边缘采集)', + `slave_id` int(3) NULL DEFAULT NULL COMMENT '默认的子设备地址', + `status_deter` tinyint(1) NULL DEFAULT NULL COMMENT '子设备状态判断方式 1-设备数据 2- 网关', + `deter_timer` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '设备数据来判断子设备状态的时长(s)', + `poll_length` int(3) NULL DEFAULT NULL COMMENT '批量读取的个数', + `create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '创建者', + `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', + `update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '更新者', + `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', + `remark` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '备注', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE KEY `uk_device_id` (`device_id`) USING BTREE COMMENT '设备唯一索引', + KEY `idx_product_id` (`product_id`) USING BTREE COMMENT '产品id索引' +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_unicode_ci COMMENT = '设备modbus配置参数' ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of iot_device_modbus_params +-- ---------------------------- + -- ---------------------------- -- Table structure for iot_product -- ---------------------------- diff --git a/sql/fastbee2.4-upgrade-v2.5.sql b/sql/fastbee2.4-upgrade-v2.5.sql index 914bddeb..b5ace803 100644 --- a/sql/fastbee2.4-upgrade-v2.5.sql +++ b/sql/fastbee2.4-upgrade-v2.5.sql @@ -73,3 +73,54 @@ ALTER TABLE `iot_firmware` ALTER TABLE `iot_things_model_translate` ADD COLUMN `product_id` bigint(20) NULL DEFAULT NULL COMMENT '产品ID'; +-- 设备级modbus配置表(用于设备覆盖产品模板) +CREATE TABLE IF NOT EXISTS `iot_device_modbus_config` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '业务id', + `device_id` bigint(20) NOT NULL COMMENT '所属设备id', + `product_id` bigint(20) NOT NULL COMMENT '所属产品id(兜底物模型)', + `identifier` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '关联属性', + `slave` int(10) DEFAULT NULL COMMENT '从机地址', + `address` int(10) NOT NULL COMMENT '寄存器地址', + `is_readonly` tinyint(1) NOT NULL COMMENT '是否只读(0-否,1-是)', + `data_type` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT 'modbus数据类型', + `quantity` int(2) DEFAULT NULL COMMENT '读取个数', + `type` tinyint(1) NOT NULL COMMENT '寄存器类型 1-IO寄存器 2-数据寄存器', + `bit_order` int(2) DEFAULT NULL COMMENT 'bit位排序', + `sort` int(6) DEFAULT NULL COMMENT '排序', + `del_flag` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)', + `create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '' COMMENT '创建者', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '' COMMENT '更新者', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `remark` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '备注', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE KEY `uk_device_identifier` (`device_id`,`identifier`), + KEY `idx_device_address_bit` (`device_id`,`address`,`bit_order`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci ROW_FORMAT=DYNAMIC COMMENT='设备级modbus配置表(覆盖产品模板)'; + +-- ---------------------------- +-- 设备级modbus配置参数表 iot_device_modbus_params +-- 继承自产品级 iot_modbus_params,用于设备级别的个性化配置 +-- @author Sumuxi +-- @date 2026/1/14 +-- ---------------------------- + +DROP TABLE IF EXISTS `iot_device_modbus_params`; +CREATE TABLE `iot_device_modbus_params` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '业务id', + `device_id` bigint(20) NOT NULL COMMENT '设备id', + `product_id` bigint(20) NOT NULL COMMENT '产品id', + `poll_type` tinyint(1) NULL DEFAULT NULL COMMENT '是否启动云端轮训(0-云端轮训,1-边缘采集)', + `slave_id` int(3) NULL DEFAULT NULL COMMENT '默认的子设备地址', + `status_deter` tinyint(1) NULL DEFAULT NULL COMMENT '子设备状态判断方式 1-设备数据 2- 网关', + `deter_timer` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '设备数据来判断子设备状态的时长(s)', + `poll_length` int(3) NULL DEFAULT NULL COMMENT '批量读取的个数', + `create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '创建者', + `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', + `update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '更新者', + `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', + `remark` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '备注', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE KEY `uk_device_id` (`device_id`) USING BTREE COMMENT '设备唯一索引', + KEY `idx_product_id` (`product_id`) USING BTREE COMMENT '产品id索引' +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_unicode_ci COMMENT = '设备modbus配置参数' ROW_FORMAT = DYNAMIC; diff --git a/sql/yack.sql b/sql/yack.sql index 7fba1e5c..e2c810e9 100644 --- a/sql/yack.sql +++ b/sql/yack.sql @@ -3537,4 +3537,58 @@ CREATE TABLE `qrtz_simprop_triggers` `bool_prop_2` varchar(1) DEFAULT NULL COMMENT 'Boolean类型的trigger的第二个参数', PRIMARY KEY (`sched_name`, `trigger_name`, `trigger_group`) USING BTREE, CONSTRAINT `QRTZ_SIMPROP_TRIGGERS_ibfk_1` FOREIGN KEY (`sched_name`, `trigger_name`, `trigger_group`) REFERENCES `qrtz_triggers` (`sched_name`, `trigger_name`, `trigger_group`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='同步机制的行锁表'; \ No newline at end of file +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='同步机制的行锁表'; + + + +-- 设备级modbus配置表(用于设备覆盖产品模板) +CREATE TABLE IF NOT EXISTS `iot_device_modbus_config` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '业务id', + `device_id` bigint(20) NOT NULL COMMENT '所属设备id', + `product_id` bigint(20) NOT NULL COMMENT '所属产品id(兜底物模型)', + `identifier` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '关联属性', + `slave` int(10) DEFAULT NULL COMMENT '从机地址', + `address` int(10) NOT NULL COMMENT '寄存器地址', + `is_readonly` tinyint(1) NOT NULL COMMENT '是否只读(0-否,1-是)', + `data_type` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT 'modbus数据类型', + `quantity` int(2) DEFAULT NULL COMMENT '读取个数', + `type` tinyint(1) NOT NULL COMMENT '寄存器类型 1-IO寄存器 2-数据寄存器', + `bit_order` int(2) DEFAULT NULL COMMENT 'bit位排序', + `sort` int(6) DEFAULT NULL COMMENT '排序', + `del_flag` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)', + `create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '' COMMENT '创建者', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '' COMMENT '更新者', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `remark` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '备注', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE KEY `uk_device_identifier` (`device_id`,`identifier`), + KEY `idx_device_address_bit` (`device_id`,`address`,`bit_order`) + ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci ROW_FORMAT=DYNAMIC COMMENT='设备级modbus配置表(覆盖产品模板)'; + +-- ---------------------------- +-- 设备级modbus配置参数表 iot_device_modbus_params +-- 继承自产品级 iot_modbus_params,用于设备级别的个性化配置 +-- @author Sumuxi +-- @date 2026/1/14 +-- ---------------------------- + +DROP TABLE IF EXISTS `iot_device_modbus_params`; +CREATE TABLE `iot_device_modbus_params` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '业务id', + `device_id` bigint(20) NOT NULL COMMENT '设备id', + `product_id` bigint(20) NOT NULL COMMENT '产品id', + `poll_type` tinyint(1) NULL DEFAULT NULL COMMENT '是否启动云端轮训(0-云端轮训,1-边缘采集)', + `slave_id` int(3) NULL DEFAULT NULL COMMENT '默认的子设备地址', + `status_deter` tinyint(1) NULL DEFAULT NULL COMMENT '子设备状态判断方式 1-设备数据 2- 网关', + `deter_timer` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '设备数据来判断子设备状态的时长(s)', + `poll_length` int(3) NULL DEFAULT NULL COMMENT '批量读取的个数', + `create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '创建者', + `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', + `update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '更新者', + `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', + `remark` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '备注', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE KEY `uk_device_id` (`device_id`) USING BTREE COMMENT '设备唯一索引', + KEY `idx_product_id` (`product_id`) USING BTREE COMMENT '产品id索引' +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_unicode_ci COMMENT = '设备modbus配置参数' ROW_FORMAT = DYNAMIC; diff --git a/vue/src/api/iot/deviceModbusConfig.js b/vue/src/api/iot/deviceModbusConfig.js new file mode 100644 index 00000000..9613e70d --- /dev/null +++ b/vue/src/api/iot/deviceModbusConfig.js @@ -0,0 +1,28 @@ +import request from '@/utils/request'; + +// 查询设备modbus配置列表 +export function listDeviceConfig(query) { + return request({ + url: '/deviceModbus/config/list', + method: 'get', + params: query, + }); +} + +// 批量新增或更新设备modbus配置 +export function addDeviceBatch(data) { + return request({ + url: '/deviceModbus/config/addBatch', + method: 'post', + data: data, + }); +} + +// 删除设备modbus配置 +export function delDeviceConfig(ids) { + return request({ + url: '/deviceModbus/config/' + ids, + method: 'delete', + }); +} + diff --git a/vue/src/utils/map.js b/vue/src/utils/map.js index cd22d52d..32f4deaa 100644 --- a/vue/src/utils/map.js +++ b/vue/src/utils/map.js @@ -1,19 +1,18 @@ export function loadBMap() { - return new Promise(function(resolve, reject) { + return new Promise(function (resolve, reject) { if (typeof BMap !== 'undefined') { resolve(BMap) return true } - window.onBMapCallback = function() { + window.onBMapCallback = function () { resolve(BMap) } // 使用https协议需要添加一下meta标签 var protocolStr = document.location.protocol; - if(protocolStr == "https:") - { + if (protocolStr == "https:") { let meta = document.createElement('meta') meta.httpEquiv = 'Content-Security-Policy' - meta.content ='upgrade-insecure-requests' + meta.content = 'upgrade-insecure-requests' meta.onerror = reject document.head.appendChild(meta) } @@ -21,7 +20,7 @@ export function loadBMap() { let script = document.createElement('script') script.type = 'text/javascript' script.src = - 'http://api.map.baidu.com/api?v=2.0&ak='+ process.env.VUE_APP_BAI_DU_AK +'&s=1&__ec_v__=20190126&callback=onBMapCallback' + 'http://api.map.baidu.com/api?v=2.0&ak=' + process.env.VUE_APP_BAI_DU_AK + '&s=1&__ec_v__=20190126&callback=onBMapCallback' script.onerror = reject document.head.appendChild(script) }) diff --git a/vue/src/views/iot/device/device-edit.vue b/vue/src/views/iot/device/device-edit.vue index b0d42052..de02a136 100644 --- a/vue/src/views/iot/device/device-edit.vue +++ b/vue/src/views/iot/device/device-edit.vue @@ -1,5 +1,5 @@