diff --git a/springboot/fastbee-common/src/main/java/com/fastbee/common/constant/FastBeeConstant.java b/springboot/fastbee-common/src/main/java/com/fastbee/common/constant/FastBeeConstant.java index d14fab2c..9df8f917 100644 --- a/springboot/fastbee-common/src/main/java/com/fastbee/common/constant/FastBeeConstant.java +++ b/springboot/fastbee-common/src/main/java/com/fastbee/common/constant/FastBeeConstant.java @@ -163,6 +163,11 @@ public interface FastBeeConstant { /**固件版本key*/ String FIRMWARE_VERSION = "device:firmware:"; + /** + * 设备信息 + */ + String DEVICE_MSG = "device:msg:"; + /**采集点变更记录缓存key*/ String COLLECT_POINT_CHANGE = "collect:point:change:"; /**属性下发回调*/ diff --git a/springboot/fastbee-common/src/main/java/com/fastbee/common/core/redis/RedisKeyBuilder.java b/springboot/fastbee-common/src/main/java/com/fastbee/common/core/redis/RedisKeyBuilder.java index fbc745d1..32659b0c 100644 --- a/springboot/fastbee-common/src/main/java/com/fastbee/common/core/redis/RedisKeyBuilder.java +++ b/springboot/fastbee-common/src/main/java/com/fastbee/common/core/redis/RedisKeyBuilder.java @@ -81,4 +81,11 @@ public class RedisKeyBuilder { return FastBeeConstant.REDIS.DEVICE_MESSAGE_ID; } + /** + * 缓存产品id,设备编号,协议编号 + */ + public static String buildDeviceMsgCacheKey(String serialNumber){ + return FastBeeConstant.REDIS.DEVICE_MSG + serialNumber; + } + } diff --git a/springboot/fastbee-common/src/main/java/com/fastbee/common/core/thingsModel/SceneThingsModelItem.java b/springboot/fastbee-common/src/main/java/com/fastbee/common/core/thingsModel/SceneThingsModelItem.java new file mode 100644 index 00000000..03569d83 --- /dev/null +++ b/springboot/fastbee-common/src/main/java/com/fastbee/common/core/thingsModel/SceneThingsModelItem.java @@ -0,0 +1,38 @@ +package com.fastbee.common.core.thingsModel; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; + +/** + * 物模型值的项 + * + * @author kerwincui + * @date 2021-12-16 + */ +@AllArgsConstructor +@Builder +@Data +public class SceneThingsModelItem +{ + /** 物模型唯一标识符 */ + private String id; + + /** 物模型值 */ + private String value; + + /** 类型:1=属性, 2=功能,3=事件, 4=设备升级,5=设备上线,6=设备下线 ,*/ + private int type; + + /** 脚本ID */ + private String stripId; + + /** 场景ID*/ + private Long sceneId; + + /** 产品ID */ + private Long productId; + + /** 设备编号 */ + private String DeviceNumber; +} diff --git a/springboot/fastbee-gateway/fastbee-mq/src/main/java/com/fastbee/mq/mqttClient/MqttService.java b/springboot/fastbee-gateway/fastbee-mq/src/main/java/com/fastbee/mq/mqttClient/MqttService.java index 71eacdb7..be3d9a37 100644 --- a/springboot/fastbee-gateway/fastbee-mq/src/main/java/com/fastbee/mq/mqttClient/MqttService.java +++ b/springboot/fastbee-gateway/fastbee-mq/src/main/java/com/fastbee/mq/mqttClient/MqttService.java @@ -4,8 +4,11 @@ import com.fastbee.common.constant.FastBeeConstant; import com.fastbee.common.core.mq.DeviceReportBo; import com.fastbee.common.enums.ServerType; import com.fastbee.common.utils.DateUtils; +import com.fastbee.common.utils.StringUtils; import com.fastbee.common.utils.gateway.mq.TopicsPost; import com.fastbee.common.utils.gateway.mq.TopicsUtils; +import com.fastbee.iot.ruleEngine.MsgContext; +import com.fastbee.iot.ruleEngine.RuleProcess; import com.fastbee.mq.redischannel.producer.MessageProducer; import com.fastbee.mq.service.IDeviceReportMessageService; import com.fastbee.mq.service.IMessagePublishService; @@ -17,6 +20,7 @@ import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.util.Arrays; +import java.util.Objects; @Component @@ -28,6 +32,9 @@ public class MqttService { @Resource private IDeviceReportMessageService deviceReportMessageService; + @Resource + private RuleProcess ruleProcess; + public void subscribe(MqttAsyncClient client) throws MqttException { @@ -48,6 +55,18 @@ public class MqttService { log.info("接收消息主题 : " + topic); log.info("接收消息Qos : " + mqttMessage.getQos()); log.info("接收消息内容 : " + message); + + //这里默认设备编号长度超过9位 + String[] split = topic.split("/"); + String clientId = Arrays.stream(split).filter(imei -> imei.length() > 9).findFirst().get(); + // 规则引擎脚本处理,完成后返回结果 + MsgContext context = ruleProcess.processRuleScript(clientId, 1, topic, message); + if (!Objects.isNull(context) && StringUtils.isNotEmpty(context.getPayload()) + && StringUtils.isNotEmpty(context.getTopic())) { + topic = context.getTopic(); + message = context.getPayload(); + } + String serialNumber = topicsUtils.parseSerialNumber(topic); Long productId = topicsUtils.parseProductId(topic); String name = topicsUtils.parseTopicName(topic); diff --git a/springboot/fastbee-gateway/fastbee-mq/src/main/java/com/fastbee/mq/ruleEngine/SceneContext.java b/springboot/fastbee-gateway/fastbee-mq/src/main/java/com/fastbee/mq/ruleEngine/SceneContext.java new file mode 100644 index 00000000..6f69a7ad --- /dev/null +++ b/springboot/fastbee-gateway/fastbee-mq/src/main/java/com/fastbee/mq/ruleEngine/SceneContext.java @@ -0,0 +1,553 @@ +package com.fastbee.mq.ruleEngine; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.fastbee.common.core.mq.InvokeReqDto; +import com.fastbee.common.core.redis.RedisCache; +import com.fastbee.common.core.redis.RedisKeyBuilder; +import com.fastbee.common.core.thingsModel.SceneThingsModelItem; +import com.fastbee.common.core.thingsModel.ThingsModelSimpleItem; +import com.fastbee.common.exception.ServiceException; +import com.fastbee.common.utils.DateUtils; +import com.fastbee.common.utils.StringUtils; +import com.fastbee.common.utils.spring.SpringUtils; +import com.fastbee.iot.model.ScriptTemplate; +import com.fastbee.iot.model.ThingsModels.ValueItem; +import com.fastbee.iot.service.IDeviceService; +import com.fastbee.iot.service.IDeviceUserService; +import com.fastbee.mq.service.IFunctionInvoke; +import lombok.Data; +import org.apache.commons.collections4.CollectionUtils; + +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import static java.util.regex.Pattern.compile; + +@Data +public class SceneContext { + + /** + * 上报信息的设备编号 + */ + private String deviceNum; + + /** + * 上报信息的设备所属产品ID + */ + private Long productId; + + /** + * 上报信息的设备信息类型 1=属性, 2=功能,3=事件,4=设备升级,5=设备上线,6=设备下线 + */ + private int type; + + /** + * 上报的物模型集合 + */ + private List thingsModelSimpleItems; + + /** + * 触发成功的物模型集合,保留给告警记录 + */ + private List sceneThingsModelItems; + + + private static IFunctionInvoke functionInvoke = SpringUtils.getBean(IFunctionInvoke.class); + + private static IDeviceService deviceService = SpringUtils.getBean(IDeviceService.class); + +// private static IAlertService alertService = SpringUtils.getBean(IAlertService.class); + +// private static NotifySendService notifySendService = SpringUtils.getBean(NotifySendService.class); + + private static RedisCache redisCache = SpringUtils.getBean(RedisCache.class); + +// private static AlertLogMapper alertLogMapper = SpringUtils.getBean(AlertLogMapper.class); + + private static IDeviceUserService deviceUserService = SpringUtils.getBean(IDeviceUserService.class); + + public SceneContext(String deviceNum, Long productId, int type, List thingsModelSimpleItems) { + this.deviceNum = deviceNum; + this.productId = productId; + this.type = type; + this.thingsModelSimpleItems = thingsModelSimpleItems; + } + + /** + * 处理规则脚本 + * + * @return + */ + private boolean process(String json) throws InterruptedException { + System.out.println("------------------[规则引擎执行...]---------------------"); + ScriptTemplate scriptTemplate = JSON.parseObject(json, ScriptTemplate.class); + if (scriptTemplate.getPurpose() == 2) { + // 触发器,检查静默时间 + if (!checkSilent(scriptTemplate.getSilent(), scriptTemplate.getSceneId())) { + // 触发条件为不满足时,返回true + if (scriptTemplate.getCond() == 3) { + return true; + } + return false; + } + + // 触发器 + if (scriptTemplate.getSource() == 1) { + // 设备触发 + return deviceTrigger(scriptTemplate); + } else if (scriptTemplate.getSource() == 3) { + // 产品触发 + return productTrigger(scriptTemplate); + } + + } /*else if (scriptTemplate.getPurpose() == 3) { + // 执行动作,延迟执行,线程休眠 delay x 1000毫秒 + Thread.sleep(scriptTemplate.getDelay() * 1000); + + if (scriptTemplate.getSource() == 4) { + // 告警 + this.alert(scriptTemplate.getDelay(), scriptTemplate.getSceneId()); + } else if (scriptTemplate.getSource() == 1 || scriptTemplate.getSource() == 3) { + // 下发指令 + this.send(scriptTemplate); + } + + // 更新静默时间 + this.updateSilent(scriptTemplate.getSilent(), scriptTemplate.getSceneId()); + }*/ + return false; + } + + /*** + * 设备触发脚本处理 + * @param scriptTemplate 解析后的Json脚本数据 + * @return + */ + private boolean deviceTrigger(ScriptTemplate scriptTemplate) { + // 判断定制触发(执行一次)或设备上报 + boolean isDeviceReport = StringUtils.isEmpty(deviceNum) ? false : true; + if (isDeviceReport) { + // 1. 匹配设备编号 + boolean matchDeviceNum = Arrays.asList(scriptTemplate.getDeviceNums().split(",")).contains(deviceNum); + if (scriptTemplate.getType() < 4) { + // 2.匹配物模型标识符 + ThingsModelSimpleItem matchItem = null; + if (thingsModelSimpleItems != null) { + for (ThingsModelSimpleItem item : thingsModelSimpleItems) { + if (item.getId().equals(scriptTemplate.getId())) { + matchItem = item; + break; + } + } + } + if (matchDeviceNum && matchItem != null) { + // 记录结果 + if (sceneThingsModelItems == null) { + sceneThingsModelItems = new ArrayList<>(); + } + SceneThingsModelItem sceneItem = new SceneThingsModelItem(scriptTemplate.getId(), matchItem.getValue(), type, + scriptTemplate.getScriptId(), scriptTemplate.getSceneId(), scriptTemplate.getProductId(), deviceNum); + sceneThingsModelItems.add(sceneItem); + // 3.设备上报值匹配 + boolean isMatch = matchValue(scriptTemplate.getOperator(), scriptTemplate.getValue(), matchItem.getValue()); + if (isMatch) { + return true; + } + } + + } else { + // 上线,下线 + if (matchDeviceNum && scriptTemplate.getType() == type) { + // 记录结果 + if (sceneThingsModelItems == null) { + sceneThingsModelItems = new ArrayList<>(); + } + SceneThingsModelItem sceneItem = new SceneThingsModelItem(type == 5 ? "online" : "offline", type == 5 ? "1" : "0", type, + scriptTemplate.getScriptId(), scriptTemplate.getSceneId(), scriptTemplate.getProductId(), deviceNum); + sceneThingsModelItems.add(sceneItem); + // 记录结果 + return true; + } + } + } else { + // 定时触发/执行一次 + int resultCount = 0; + // 3.查询设备最新上报值去匹配 + for (String num : Arrays.asList(scriptTemplate.getDeviceNums().split(","))) { + // 数组类型,key去除前缀,值从逗号分隔的字符串获取 + String id = ""; + String value = ""; + int index = 0; + if (scriptTemplate.getId().startsWith("array_")) { + id = scriptTemplate.getId().substring(9); + index = Integer.parseInt(scriptTemplate.getId().substring(6, 8)); + } else { + id = scriptTemplate.getId(); + } + String key = RedisKeyBuilder.buildTSLVCacheKey(scriptTemplate.getProductId(), num); + String cacheValue = redisCache.getCacheMapValue(key, id); + if (StringUtils.isEmpty(cacheValue)) { + continue; + } + ValueItem valueItem = JSON.parseObject(cacheValue, ValueItem.class); + if (scriptTemplate.getId().startsWith("array_")) { + String[] values = valueItem.getValue().split(","); + value = values[index]; + } else { + value = valueItem.getValue(); + } + boolean isMatch = matchValue(scriptTemplate.getOperator(), scriptTemplate.getValue(), value); + if (isMatch) { + // 记录结果 + if (sceneThingsModelItems == null) { + sceneThingsModelItems = new ArrayList<>(); + } + SceneThingsModelItem sceneItem = new SceneThingsModelItem(scriptTemplate.getId(), value, type, + scriptTemplate.getScriptId(), scriptTemplate.getSceneId(), scriptTemplate.getProductId(), num); + sceneThingsModelItems.add(sceneItem); + resultCount++; + } + } + // 任意设备匹配成功返回true + return resultCount > 0 ? true : false; + } + return false; + } + + /*** + * 产品触发脚本处理 + * @param scriptTemplate + * @return + */ + private boolean productTrigger(ScriptTemplate scriptTemplate) { + // 判断定制触发(执行一次)或设备上报 + boolean isDeviceReport = StringUtils.isEmpty(deviceNum) ? false : true; + if (isDeviceReport) { + // 匹配产品编号 + boolean matchProductId = scriptTemplate.getProductId().equals(productId); + if (scriptTemplate.getType() < 4) { + // 匹配物模型标识符 + ThingsModelSimpleItem matchItem = null; + if (thingsModelSimpleItems != null) { + for (ThingsModelSimpleItem item : thingsModelSimpleItems) { + if (item.getId().equals(scriptTemplate.getId())) { + matchItem = item; + break; + } + } + } + if (matchProductId && matchItem != null) { + // 记录结果 + if (sceneThingsModelItems == null) { + sceneThingsModelItems = new ArrayList<>(); + } + SceneThingsModelItem sceneItem = new SceneThingsModelItem(scriptTemplate.getId(), matchItem.getValue(), type, + scriptTemplate.getScriptId(), scriptTemplate.getSceneId(), scriptTemplate.getProductId(), deviceNum); + sceneThingsModelItems.add(sceneItem); + // 设备上报值匹配 + boolean isMatch = matchValue(scriptTemplate.getOperator(), scriptTemplate.getValue(), matchItem.getValue()); + if (isMatch) { + return true; + } + } + + } else { + // 上线,下线 + if (matchProductId && scriptTemplate.getType() == type) { + // 记录结果 + if (sceneThingsModelItems == null) { + sceneThingsModelItems = new ArrayList<>(); + } + SceneThingsModelItem sceneItem = new SceneThingsModelItem(type == 5 ? "online" : "offline", type == 5 ? "1" : "0", type, + scriptTemplate.getScriptId(), scriptTemplate.getSceneId(), scriptTemplate.getProductId(), deviceNum); + sceneThingsModelItems.add(sceneItem); + // 记录结果 + return true; + } + } + } else { + // 定时触发/执行一次 + int resultCount = 0; + // 查询设备最新上报值去匹配 + String[] deviceNums = deviceService.getDeviceNumsByProductId(scriptTemplate.getProductId()); + for (String num : deviceNums) { + // 数组类型,key去除前缀,值从逗号分隔的字符串获取 + String id = ""; + String value = ""; + int index = 0; + if (scriptTemplate.getId().startsWith("array_")) { + id = scriptTemplate.getId().substring(9); + index = Integer.parseInt(scriptTemplate.getId().substring(6, 8)); + } else { + id = scriptTemplate.getId(); + } + String key = RedisKeyBuilder.buildTSLVCacheKey(scriptTemplate.getProductId(), num); + String cacheValue = redisCache.getCacheMapValue(key, id); + if (StringUtils.isEmpty(cacheValue)) { + continue; + } + ValueItem valueItem = JSON.parseObject(cacheValue, ValueItem.class); + if (scriptTemplate.getId().startsWith("array_")) { + String[] values = valueItem.getValue().split(","); + value = values[index]; + } else { + value = valueItem.getValue(); + } + boolean isMatch = matchValue(scriptTemplate.getOperator(), scriptTemplate.getValue(), value); + if (isMatch) { + // 记录结果 + if (sceneThingsModelItems == null) { + sceneThingsModelItems = new ArrayList<>(); + } + SceneThingsModelItem sceneItem = new SceneThingsModelItem(scriptTemplate.getId(), value, type, + scriptTemplate.getScriptId(), scriptTemplate.getSceneId(), scriptTemplate.getProductId(), num); + sceneThingsModelItems.add(sceneItem); + resultCount++; + } + } + // 任意设备匹配成功返回true + return resultCount > 0 ? true : false; + } + return false; + } + + + /** + * 执行动作,下发指令 + * + * @param scriptTemplate + */ + private void send(ScriptTemplate scriptTemplate) { + String[] deviceNumbers = null; + if (scriptTemplate.getSource() == 1) { + // 下发给指定设备 + deviceNumbers = scriptTemplate.getDeviceNums().split(","); + + } else if (scriptTemplate.getSource() == 3) { + // 下发给产品下所有设备 + deviceNumbers = deviceService.getDeviceNumsByProductId(scriptTemplate.getProductId()); + } + for (String deviceNum : deviceNumbers) { + InvokeReqDto reqDto = new InvokeReqDto(); + reqDto.setProductId(scriptTemplate.getProductId()); + reqDto.setSerialNumber(deviceNum); + reqDto.setModelName(""); + reqDto.setType(1); + reqDto.setIdentifier(scriptTemplate.getId()); + Map params = new HashMap<>(); + params.put(scriptTemplate.getId(), scriptTemplate.getValue()); + reqDto.setRemoteCommand(params); + reqDto.setValue(new JSONObject(reqDto.getRemoteCommand())); + functionInvoke.invokeNoReply(reqDto); + } + } + + /** + * 执行动作,告警处理 + * + * @param sceneId 场景ID + * @param delay 延时(单位秒,90秒内) + */ + /*private void alert(int delay, Long sceneId) { + for (SceneThingsModelItem sceneThingsModelItem : sceneThingsModelItems) { + // 查询设备信息 + Device device = deviceService.selectDeviceBySerialNumber(sceneThingsModelItem.getDeviceNumber()); + Optional.ofNullable(device).orElseThrow(() -> new ServiceException("告警推送,设备不存在" + "[{" + sceneThingsModelItem.getDeviceNumber() + "}]")); + // 获取场景相关的告警参数,告警必须要是启动状态 + List sceneSendVOList = alertService.listByAlertIds(sceneId); + if (CollectionUtils.isEmpty(sceneSendVOList)) { + return; + } + // 获取告警推送参数 + AlertPushParams alertPushParams = new AlertPushParams(); + alertPushParams.setDeviceName(device.getDeviceName()); + alertPushParams.setSerialNumber(sceneThingsModelItem.getDeviceNumber()); + // 获取设备所属及分享用户信息 + List deviceUserList = deviceUserService.selectDeviceUserByDeviceId(device.getDeviceId()); + if (CollectionUtils.isNotEmpty(deviceUserList)) { + alertPushParams.setUserPhoneSet(deviceUserList.stream().map(DeviceUser::getPhonenumber).filter(StringUtils::isNotEmpty).collect(Collectors.toSet())); + alertPushParams.setUserIdSet(deviceUserList.stream().map(DeviceUser::getUserId).collect(Collectors.toSet())); + } + String address; + if (StringUtils.isNotEmpty(device.getNetworkAddress())) { + address = device.getNetworkAddress(); + } else if (StringUtils.isNotEmpty(device.getNetworkIp())) { + address = device.getNetworkIp(); + } else if (Objects.nonNull(device.getLongitude()) && Objects.nonNull(device.getLatitude())) { + address = device.getLongitude() + "," + device.getLatitude(); + } else { + address = "未知地点"; + } + alertPushParams.setAddress(address); + alertPushParams.setAlertTime(DateUtils.parseDateToStr(DateUtils.YY_MM_DD_HH_MM_SS, new Date())); + List alertLogList = new ArrayList<>(); + // 获取告警关联模版id + for (AlertSceneSendVO alertSceneSendVO : sceneSendVOList) { + List alertNotifyTemplateList = alertService.listAlertNotifyTemplate(alertSceneSendVO.getAlertId()); + alertPushParams.setAlertName(alertSceneSendVO.getAlertName()); + for (AlertNotifyTemplate alertNotifyTemplate : alertNotifyTemplateList) { + alertPushParams.setNotifyTemplateId(alertNotifyTemplate.getNotifyTemplateId()); + notifySendService.alertSend(alertPushParams); + } + AlertLog alertLog = getAlertLog(alertSceneSendVO, device, sceneThingsModelItem); + alertLogList.add(alertLog); + } + // 保存告警日志 + if (CollectionUtils.isNotEmpty(alertLogList)) { + alertLogMapper.insertAlertLogBatch(alertLogList); + } + } + } + + *//** + * 组装告警日志 + * + * @param alertSceneSendVO + * @return com.fastbee.iot.domain.AlertLog + * @param: device + *//* + private AlertLog getAlertLog(AlertSceneSendVO alertSceneSendVO, Device device, SceneThingsModelItem sceneThingsModelItem) { + AlertLog alertLog = new AlertLog(); + alertLog.setAlertName(alertSceneSendVO.getAlertName()); + alertLog.setAlertLevel(alertSceneSendVO.getAlertLevel()); + alertLog.setSerialNumber(sceneThingsModelItem.getDeviceNumber()); + alertLog.setProductId(sceneThingsModelItem.getProductId()); + alertLog.setDeviceName(device.getDeviceName()); + // 统一未处理 + alertLog.setStatus(2); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("id", sceneThingsModelItem.getId()); + jsonObject.put("value", sceneThingsModelItem.getValue()); + jsonObject.put("remark", ""); + alertLog.setDetail(jsonObject.toJSONString()); + alertLog.setCreateTime(new Date()); + return alertLog; + }*/ + + /** + * 检查静默周期物模型值是否匹配 + * + * @param operator 操作符 + * @param triggerValue 触发值 + * @param value 上报的值 + * @return + */ + private boolean matchValue(String operator, String triggerValue, String value) { + boolean result = false; + // 操作符比较 + switch (operator) { + case "=": + result = value.equals(triggerValue); + break; + case "!=": + result = !value.equals(triggerValue); + break; + case ">": + if (isNumeric(value) && isNumeric(triggerValue)) { + result = Double.parseDouble(value) > Double.parseDouble(triggerValue); + } + break; + case "<": + if (isNumeric(value) && isNumeric(triggerValue)) { + result = Double.parseDouble(value) < Double.parseDouble(triggerValue); + } + break; + case ">=": + if (isNumeric(value) && isNumeric(triggerValue)) { + result = Double.parseDouble(value) >= Double.parseDouble(triggerValue); + } + break; + case "<=": + if (isNumeric(value) && isNumeric(triggerValue)) { + result = Double.parseDouble(value) <= Double.parseDouble(triggerValue); + } + break; + case "between": + // 比较值用英文中划线分割 - + String[] triggerValues = triggerValue.split("-"); + if (isNumeric(value) && isNumeric(triggerValues[0]) && isNumeric(triggerValues[1])) { + result = Double.parseDouble(value) >= Double.parseDouble(triggerValues[0]) && Double.parseDouble(value) <= Double.parseDouble(triggerValues[1]); + } + break; + case "notBetween": + // 比较值用英文中划线分割 - + String[] trigValues = triggerValue.split("-"); + if (isNumeric(value) && isNumeric(trigValues[0]) && isNumeric(trigValues[1])) { + result = Double.parseDouble(value) <= Double.parseDouble(trigValues[0]) || Double.parseDouble(value) >= Double.parseDouble(trigValues[1]); + } + break; + case "contain": + result = value.contains(triggerValue); + break; + case "notContain": + result = !value.contains(triggerValue); + break; + default: + break; + } + return result; + } + + /** + * 检查静默时间 + * + * @param silent + * @param sceneId + * @return + */ + private boolean checkSilent(int silent, Long sceneId) { + if (silent == 0 || sceneId == 0) { + return true; + } + // silent:scene_场景编号 + String key = "silent:" + "scene_" + sceneId; + Calendar calendar = Calendar.getInstance(); + // 查询静默截止时间 + Long expireTime = redisCache.getCacheObject(key); + if (expireTime == null) { + // 添加场景静默时间 + calendar.add(Calendar.MINUTE, silent); + redisCache.setCacheObject(key, calendar.getTimeInMillis()); + return true; + } else { + Long NowTimestamp = Calendar.getInstance().getTimeInMillis(); + if (NowTimestamp > expireTime) { + return true; + } + return false; + } + } + + /** + * 更新静默时间 + * + * @param sceneId + * @param silent + */ + private void updateSilent(int silent, Long sceneId) { + if (silent == 0 || sceneId == 0) { + return; + } + // 更新场景静默时间 + String key = "silent:" + "scene_" + sceneId; + Calendar calendar = Calendar.getInstance(); + calendar.add(Calendar.MINUTE, silent); + redisCache.setCacheObject(key, calendar.getTimeInMillis()); + } + + /** + * 判断字符串是否为整数或小数 + */ + private boolean isNumeric(String str) { + Pattern pattern = compile("[0-9]*\\.?[0-9]+"); + Matcher isNum = pattern.matcher(str); + if (!isNum.matches()) { + return false; + } + return true; + } + +} diff --git a/springboot/fastbee-open-api/src/main/java/com/fastbee/data/controller/SceneController.java b/springboot/fastbee-open-api/src/main/java/com/fastbee/data/controller/SceneController.java new file mode 100644 index 00000000..ea26b1fd --- /dev/null +++ b/springboot/fastbee-open-api/src/main/java/com/fastbee/data/controller/SceneController.java @@ -0,0 +1,126 @@ +package com.fastbee.data.controller; + +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.domain.model.LoginUser; +import com.fastbee.common.core.page.TableDataInfo; +import com.fastbee.common.enums.BusinessType; +import com.fastbee.common.utils.poi.ExcelUtil; +import com.fastbee.iot.domain.Scene; +import com.fastbee.iot.service.ISceneService; +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 javax.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * 场景联动Controller + * + * @author kerwincui + * @date 2022-01-13 + */ +@Api(tags = "场景联动") +@RestController +@RequestMapping("/iot/scene") +public class SceneController extends BaseController +{ + @Autowired + private ISceneService sceneService; + + /** + * 查询场景联动列表 + */ + @ApiOperation("查询场景联动列表") + @PreAuthorize("@ss.hasPermi('iot:scene:list')") + @GetMapping("/list") + public TableDataInfo list(Scene scene) + { + startPage(); + List list = sceneService.selectSceneList(scene); + return getDataTable(list); + } + + /** + * 导出场景联动列表 + */ + @ApiOperation("导出场景联动列表") + @PreAuthorize("@ss.hasPermi('iot:scene:export')") + @Log(title = "场景联动", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, Scene scene) + { + List list = sceneService.selectSceneList(scene); + ExcelUtil util = new ExcelUtil(Scene.class); + util.exportExcel(response, list, "场景联动数据"); + } + + /** + * 获取场景联动详细信息 + */ + @ApiOperation("获取场景联动详细信息") + @PreAuthorize("@ss.hasPermi('iot:scene:query')") + @GetMapping(value = "/{sceneId}") + public AjaxResult getInfo(@PathVariable("sceneId") Long sceneId) + { + return AjaxResult.success(sceneService.selectSceneBySceneId(sceneId)); + } + + /** + * 新增场景联动 + */ + @ApiOperation("新增场景联动") + @PreAuthorize("@ss.hasPermi('iot:scene:add')") + @Log(title = "场景联动", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody Scene scene) + { + LoginUser loginUser = getLoginUser(); + if (loginUser == null || loginUser.getUser() == null) { + return error("请登录后重试!"); + } + scene.setUserId(loginUser.getUser().getUserId()); + scene.setUserName(loginUser.getUser().getUserName()); + return toAjax(sceneService.insertScene(scene)); + } + + /** + * 修改场景联动 + */ + @ApiOperation("修改场景联动") + @PreAuthorize("@ss.hasPermi('iot:scene:edit')") + @Log(title = "场景联动", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody Scene scene) + { + return toAjax(sceneService.updateScene(scene)); + } + + /** + * 删除场景联动 + */ + @ApiOperation("删除场景联动") + @PreAuthorize("@ss.hasPermi('iot:scene:remove')") + @Log(title = "场景联动", businessType = BusinessType.DELETE) + @DeleteMapping("/{sceneIds}") + public AjaxResult remove(@PathVariable Long[] sceneIds) + { + return toAjax(sceneService.deleteSceneBySceneIds(sceneIds)); + } + + /** + * 修改场景联动状态 + */ + @ApiOperation("修改场景联动状态") + @PreAuthorize("@ss.hasPermi('iot:scene:edit')") + @Log(title = "场景联动", businessType = BusinessType.UPDATE) + @PutMapping("/updateStatus") + public AjaxResult updateStatus(@RequestBody Scene scene) + { + return toAjax(sceneService.updateStatus(scene)); + } +} diff --git a/springboot/fastbee-open-api/src/main/java/com/fastbee/data/controller/ScriptController.java b/springboot/fastbee-open-api/src/main/java/com/fastbee/data/controller/ScriptController.java new file mode 100644 index 00000000..b49a044a --- /dev/null +++ b/springboot/fastbee-open-api/src/main/java/com/fastbee/data/controller/ScriptController.java @@ -0,0 +1,107 @@ +package com.fastbee.data.controller; + +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.common.utils.poi.ExcelUtil; +import com.fastbee.iot.domain.Script; +import com.fastbee.iot.service.IScriptService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * 规则引擎脚本Controller + * + * @author lizhuangpeng + * @date 2023-07-01 + */ +@RestController +@RequestMapping("/iot/script") +public class ScriptController extends BaseController +{ + @Autowired + private IScriptService scriptService; + + /** + * 查询规则引擎脚本列表 + */ + @PreAuthorize("@ss.hasPermi('iot:script:list')") + @GetMapping("/list") + public TableDataInfo list(Script ruleScript) + { + startPage(); + List diff --git a/vue/src/views/iot/scene/device-list.vue b/vue/src/views/iot/scene/device-list.vue new file mode 100644 index 00000000..c5d95fb6 --- /dev/null +++ b/vue/src/views/iot/scene/device-list.vue @@ -0,0 +1,180 @@ + + + + + diff --git a/vue/src/views/iot/scene/index.vue b/vue/src/views/iot/scene/index.vue new file mode 100644 index 00000000..42664903 --- /dev/null +++ b/vue/src/views/iot/scene/index.vue @@ -0,0 +1,1602 @@ + + + + + diff --git a/vue/src/views/iot/scene/log.vue b/vue/src/views/iot/scene/log.vue new file mode 100644 index 00000000..30402495 --- /dev/null +++ b/vue/src/views/iot/scene/log.vue @@ -0,0 +1,253 @@ + + + diff --git a/vue/src/views/iot/scene/product-list.vue b/vue/src/views/iot/scene/product-list.vue new file mode 100644 index 00000000..d82ba7d5 --- /dev/null +++ b/vue/src/views/iot/scene/product-list.vue @@ -0,0 +1,147 @@ + + + diff --git a/vue/src/views/iot/scene/script.vue b/vue/src/views/iot/scene/script.vue new file mode 100644 index 00000000..8b07ea3d --- /dev/null +++ b/vue/src/views/iot/scene/script.vue @@ -0,0 +1,384 @@ + + +