Browse Source

代码合并

xiwa 2 years ago
parent
commit
cd23c577bb
67 changed files with 2375 additions and 92 deletions
  1. 26 5
      data/converters/6260396d67aced2696184053/converter.js
  2. 2 2
      data/init/protocolComponent.json
  3. 20 10
      data/init/spaceDevice.json
  4. 14 0
      iot-common/src/main/java/cc/iotkit/common/Constants.java
  5. 9 14
      iot-components/iot-component-server/src/main/java/cc/iotkit/comps/DeviceMessageHandler.java
  6. 16 8
      iot-components/iot-component-server/src/main/java/cc/iotkit/comps/service/DeviceBehaviourService.java
  7. 5 2
      iot-components/iot-emqx-component/src/main/java/cc/iotkit/comp/emqx/JsScripter.java
  8. 5 2
      iot-components/iot-http-biz-component/src/main/java/cc/iotkit/comp/biz/HttpBizComponent.java
  9. 5 2
      iot-components/iot-mqtt-component/src/main/java/cc/iotkit/comp/mqtt/JsScripter.java
  10. 2 0
      iot-components/iot-mqtt-component/src/main/java/cc/iotkit/comp/mqtt/MqttConfig.java
  11. 8 0
      iot-components/iot-mqtt-component/src/main/java/cc/iotkit/comp/mqtt/MqttVerticle.java
  12. 37 16
      iot-components/iot-mqtt-component/src/main/resources/convert.js
  13. 89 0
      iot-components/iot-websocket-component/pom.xml
  14. 18 0
      iot-components/iot-websocket-component/src/main/java/cc/iotkit/comp/websocket/AbstractDeviceVerticle.java
  15. 111 0
      iot-components/iot-websocket-component/src/main/java/cc/iotkit/comp/websocket/WebSocketDeviceComponent.java
  16. 20 0
      iot-components/iot-websocket-component/src/main/java/cc/iotkit/comp/websocket/client/WebSocketClientConfig.java
  17. 124 0
      iot-components/iot-websocket-component/src/main/java/cc/iotkit/comp/websocket/client/WebSocketClientVerticle.java
  18. 25 0
      iot-components/iot-websocket-component/src/main/java/cc/iotkit/comp/websocket/server/WebSocketServerConfig.java
  19. 145 0
      iot-components/iot-websocket-component/src/main/java/cc/iotkit/comp/websocket/server/WebSocketServerVerticle.java
  20. 72 0
      iot-components/iot-websocket-component/src/main/resources/component.js
  21. 1 0
      iot-components/iot-websocket-component/src/main/resources/component.spi
  22. 55 0
      iot-components/iot-websocket-component/src/main/resources/converter.js
  23. 2 0
      iot-data/iot-data-service/src/main/java/cc/iotkit/data/ISpaceDeviceData.java
  24. 5 0
      iot-data/iot-model/src/main/java/cc/iotkit/model/space/SpaceDevice.java
  25. 2 0
      iot-data/iot-rdb-data-service/src/main/java/cc/iotkit/data/dao/SpaceDeviceRepository.java
  26. 5 0
      iot-data/iot-rdb-data-service/src/main/java/cc/iotkit/data/model/TbSpaceDevice.java
  27. 5 0
      iot-data/iot-rdb-data-service/src/main/java/cc/iotkit/data/service/SpaceDeviceDataImpl.java
  28. 97 0
      iot-data/iot-ts-temporal-service/pom.xml
  29. 5 0
      iot-data/iot-ts-temporal-service/readme.md
  30. 27 0
      iot-data/iot-ts-temporal-service/src/main/java/cc/iotkit/temporal/ts/config/Constants.java
  31. 43 0
      iot-data/iot-ts-temporal-service/src/main/java/cc/iotkit/temporal/ts/config/TsDatasourceConfig.java
  32. 19 0
      iot-data/iot-ts-temporal-service/src/main/java/cc/iotkit/temporal/ts/dao/TsTemplate.java
  33. 15 0
      iot-data/iot-ts-temporal-service/src/main/java/cc/iotkit/temporal/ts/dm/DbField.java
  34. 118 0
      iot-data/iot-ts-temporal-service/src/main/java/cc/iotkit/temporal/ts/dm/FieldParser.java
  35. 161 0
      iot-data/iot-ts-temporal-service/src/main/java/cc/iotkit/temporal/ts/dm/TableManager.java
  36. 24 0
      iot-data/iot-ts-temporal-service/src/main/java/cc/iotkit/temporal/ts/dm/TsField.java
  37. 31 0
      iot-data/iot-ts-temporal-service/src/main/java/cc/iotkit/temporal/ts/model/TsDeviceProperty.java
  38. 33 0
      iot-data/iot-ts-temporal-service/src/main/java/cc/iotkit/temporal/ts/model/TsRuleLog.java
  39. 29 0
      iot-data/iot-ts-temporal-service/src/main/java/cc/iotkit/temporal/ts/model/TsTaskLog.java
  40. 45 0
      iot-data/iot-ts-temporal-service/src/main/java/cc/iotkit/temporal/ts/model/TsThingModelMessage.java
  41. 36 0
      iot-data/iot-ts-temporal-service/src/main/java/cc/iotkit/temporal/ts/model/TsTimeData.java
  42. 33 0
      iot-data/iot-ts-temporal-service/src/main/java/cc/iotkit/temporal/ts/model/TsVirtualDeviceLog.java
  43. 207 0
      iot-data/iot-ts-temporal-service/src/main/java/cc/iotkit/temporal/ts/service/DbStructureDataImpl.java
  44. 105 0
      iot-data/iot-ts-temporal-service/src/main/java/cc/iotkit/temporal/ts/service/DevicePropertyDataImpl.java
  45. 80 0
      iot-data/iot-ts-temporal-service/src/main/java/cc/iotkit/temporal/ts/service/RuleLogDataImpl.java
  46. 82 0
      iot-data/iot-ts-temporal-service/src/main/java/cc/iotkit/temporal/ts/service/TaskLogDataImpl.java
  47. 134 0
      iot-data/iot-ts-temporal-service/src/main/java/cc/iotkit/temporal/ts/service/ThingModelMessageDataImpl.java
  48. 74 0
      iot-data/iot-ts-temporal-service/src/main/java/cc/iotkit/temporal/ts/service/VirtualDeviceLogDataImpl.java
  49. 1 0
      iot-data/pom.xml
  50. 3 1
      iot-rule-engine/src/main/java/cc/iotkit/ruleengine/action/AlertService.java
  51. 2 1
      iot-rule-engine/src/main/java/cc/iotkit/ruleengine/action/DeviceActionService.java
  52. 5 6
      iot-rule-engine/src/main/java/cc/iotkit/ruleengine/action/HttpService.java
  53. 6 5
      iot-rule-engine/src/main/java/cc/iotkit/ruleengine/action/ScriptService.java
  54. 3 1
      iot-rule-engine/src/main/java/cc/iotkit/ruleengine/action/kafka/KafkaService.java
  55. 3 1
      iot-rule-engine/src/main/java/cc/iotkit/ruleengine/action/mqtt/MqttService.java
  56. 3 1
      iot-rule-engine/src/main/java/cc/iotkit/ruleengine/action/tcp/TcpService.java
  57. 10 3
      iot-script-engine/pom.xml
  58. 19 10
      iot-script-engine/src/main/java/cc/iotkit/script/JavaScriptEngine.java
  59. 6 0
      iot-standalone/pom.xml
  60. 4 0
      iot-standalone/src/main/java/cc/iotkit/manager/config/SaTokenConfigure.java
  61. 8 0
      iot-standalone/src/main/java/cc/iotkit/manager/controller/DeviceController.java
  62. 21 0
      iot-standalone/src/main/java/cc/iotkit/manager/controller/SpaceController.java
  63. 26 1
      iot-standalone/src/main/java/cc/iotkit/manager/controller/SpaceDeviceController.java
  64. 5 0
      iot-standalone/src/main/java/cc/iotkit/manager/model/vo/SpaceDeviceVo.java
  65. 12 0
      iot-standalone/src/main/java/cc/iotkit/manager/service/DeviceService.java
  66. 7 1
      iot-standalone/src/main/java/cc/iotkit/manager/service/ThingModelService.java
  67. 5 0
      pom.xml

+ 26 - 5
data/converters/6260396d67aced2696184053/converter.js

@@ -109,7 +109,7 @@ this.encode = function (service,device) {
   var method="thing.service.";
   var topic="/sys/"+service.productKey+"/"+service.deviceName+"/c/service/";
   var params={};
-  
+
   //透传下发
   if(device.transparent){
 	var rst=component.transparentEncode(service,device);
@@ -152,12 +152,29 @@ this.encode = function (service,device) {
 	method+=identifier;
 	topic="/sys/"+service.productKey+"/"+service.deviceName+"/c/deregister";
   }
-  
-  for(var p in service.params){
+  if(type=="property" && identifier=="get"  ){
+	var listParams = []
+	for(var p in service.params){
+	listParams.push(service.params[p]);
+  }
+	return {
+	productKey:service.productKey,
+	deviceName:service.deviceName,
+	mid:deviceMid,
+	content:{
+	  topic:topic,
+	  payload:JSON.stringify({
+		id:deviceMid,
+		method:method,
+		params: listParams
+	  })
+	}
+  }
+  }else{
+   for(var p in service.params){
 	params[p]=service.params[p];
   }
-  
-  return {
+	return {
 	productKey:service.productKey,
 	deviceName:service.deviceName,
 	mid:deviceMid,
@@ -170,4 +187,8 @@ this.encode = function (service,device) {
 	  })
 	}
   }
+
+  }
+ 
+  
 };

+ 2 - 2
data/init/protocolComponent.json

@@ -17,7 +17,7 @@
     "name": "EMQX标准协议组件",
     "type": "device",
     "protocol": "mqtt",
-    "jarFile": "iot-emqx-component-0.4.2-SNAPSHOT.jar",
+    "jarFile": "iot-emqx-component-0.4.3-SNAPSHOT.jar",
     "config": "{\"port\":\"1884\",\"ssl\":false,\"type\":\"client\",\"subscribeTopics\":[\"/sys/+/+/s/#\",\"/sys/client/connected\",\"/sys/client/disconnected\",\"/sys/session/subscribed\",\"/sys/session/unsubscribed\"],\"authPort\":\"8088\",\"broker\":\"127.0.0.1\",\"clientId\":\"test\",\"username\":\"test\",\"password\":\"123\"}",
     "converter": "6260396d67aced2696184053",
     "state": "stopped",
@@ -29,7 +29,7 @@
     "name": "小度音箱接入组件",
     "type": "biz",
     "protocol": "http",
-    "jarFile": "iot-http-biz-component-0.4.2-SNAPSHOT.jar",
+    "jarFile": "iot-http-biz-component-0.4.3-SNAPSHOT.jar",
     "config": "{\"port\":\"8084\"}",
     "converter": "",
     "state": "stopped",

+ 20 - 10
data/init/spaceDevice.json

@@ -6,7 +6,8 @@
     "name": "卧室的ZGW01",
     "homeId": "629e18e96b16ad6a3e158645",
     "spaceId": "629e18fee0dc6d4171e1a021",
-    "addAt": 1654609953349
+    "addAt": 1654609953349,
+    "collect":true
   },
   {
     "id": "629f581ee0dc6d4171e1a028",
@@ -16,7 +17,8 @@
     "homeId": "629e18e96b16ad6a3e158645",
     "spaceId": "629e18fee0dc6d4171e1a021",
     "addAt": 1654609950464,
-    "useAt": 1655653227177
+    "useAt": 1655653227177,
+    "collect":true
   },
   {
     "id": "629f581ce0dc6d4171e1a027",
@@ -26,7 +28,8 @@
     "homeId": "629e18e96b16ad6a3e158645",
     "spaceId": "629e18fee0dc6d4171e1a021",
     "addAt": 1654609948550,
-    "useAt": 1655653294372
+    "useAt": 1655653294372,
+    "collect":false
   },
   {
     "id": "629f581ae0dc6d4171e1a026",
@@ -36,7 +39,8 @@
     "homeId": "629e18e96b16ad6a3e158645",
     "spaceId": "629e18fee0dc6d4171e1a021",
     "addAt": 1654609946750,
-    "useAt": 1655653552172
+    "useAt": 1655653552172,
+    "collect":false
   },
   {
     "id":  "629f5818e0dc6d4171e1a025",
@@ -46,7 +50,8 @@
     "homeId": "629e18e96b16ad6a3e158645",
     "spaceId": "629e18fee0dc6d4171e1a021",
     "addAt": 1654609944061,
-    "useAt": 1655653548172
+    "useAt": 1655653548172,
+    "collect":false
   },
   {
     "id": "629f5734e0dc6d4171e1a024",
@@ -56,7 +61,8 @@
     "homeId": "629e18e96b16ad6a3e158645",
     "spaceId": "629e18fee0dc6d4171e1a021",
     "addAt": 1654609716856,
-    "useAt": 1655653590474
+    "useAt": 1655653590474,
+    "collect":false
   },
   {
     "id": "629e1a1de0dc6d4171e1a022",
@@ -66,7 +72,8 @@
     "homeId": "629e18e96b16ad6a3e158645",
     "spaceId": "629e18fee0dc6d4171e1a021",
     "addAt": 1654528541149,
-    "useAt": 1655653255089
+    "useAt": 1655653255089,
+    "collect":false
   },
   {
     "id": "629db2e1e5a005209d182877",
@@ -76,7 +83,8 @@
     "homeId": "6238a49fecf37861bed7ad11",
     "spaceId": "62794a820b0776663635e636",
     "addAt": 1654502113734,
-    "useAt": 1655653724374
+    "useAt": 1655653724374,
+    "collect":false
   },
   {
     "id": "6280d7cd7e234141ee9d1fd2",
@@ -86,7 +94,8 @@
     "homeId": "6238a49fecf37861bed7ad11",
     "spaceId": "62794a5c0b0776663635e633",
     "addAt": 1652611021619,
-    "useAt": 1655181137254
+    "useAt": 1655181137254,
+    "collect":false
   },
   {
     "id": "6280b17d7e234141ee9d1fcf",
@@ -96,6 +105,7 @@
     "homeId": "6238a49fecf37861bed7ad11",
     "spaceId": "62794a5c0b0776663635e633",
     "addAt": 1652601213676,
-    "useAt": 1655189186348
+    "useAt": 1655189186348,
+    "collect":false
   }
 ]

+ 14 - 0
iot-common/src/main/java/cc/iotkit/common/Constants.java

@@ -155,6 +155,10 @@ public interface Constants {
          * 设备-服务调用
          */
         String INVOKE_SERVICE = "/{deviceId}/service/{service}/invoke";
+        /**
+         * 设备-属性获取
+         */
+        String INVOKE_SERVICE_PROPERTY_GET = "/{deviceId}/service/property/get";
 
     }
 
@@ -170,6 +174,11 @@ public interface Constants {
          */
         String RECENT_DEVICES = "/myRecentDevices";
 
+        /**
+         * 获取用户当前收藏设备
+         */
+        String GET_COLLECT_DEVICES = "/getCollectDevices";
+
         /**
          * 我的空间设备列表
          */
@@ -195,6 +204,11 @@ public interface Constants {
          */
         String SAVE_DEVICE = "/saveDevice";
 
+        /**
+         * 收藏/取消收藏设备
+         */
+        String COLLECT_DEVICE = "/collectDevice";
+
         /**
          * 获取空间设备信息
          */

+ 9 - 14
iot-components/iot-component-server/src/main/java/cc/iotkit/comps/DeviceMessageHandler.java

@@ -78,10 +78,9 @@ public class DeviceMessageHandler implements IMessageHandler {
     public void onReceive(Map<String, Object> head, String type, String msg, Consumer<ReceiveResult> onResult) {
         executorService.submit(() -> {
             try {
-                Map rst = scriptEngine.invokeMethod(new TypeReference<>() {
+                Map<String, Object> rst = scriptEngine.invokeMethod(new TypeReference<>() {
                 }, "onReceive", head, type, msg);
                 Object objType = rst.get("type");
-                log.info("onReceive script result:{}", objType);
                 if (objType == null) {
                     onResult.accept(null);
                     return;
@@ -99,19 +98,15 @@ public class DeviceMessageHandler implements IMessageHandler {
 
                 switch (objType.toString()) {
                     case "register":
-                        if (action != null && Action.TYPE_ACK.equals(action.getType())) {
-                            doAction(action);
-                        } else {
-                            //注册数据
-                            RegisterInfo regInfo = MessageParser.parseRegisterInfo(data);
-                            if (regInfo == null) {
-                                onResult.accept(null);
-                                return;
-                            }
-                            doRegister(regInfo);
-                            doAction(action);
-                            onResult.accept(new ReceiveResult(regInfo.getProductKey(), regInfo.getDeviceName(), regInfo));
+                        //注册数据
+                        RegisterInfo regInfo = MessageParser.parseRegisterInfo(data);
+                        if (regInfo == null) {
+                            onResult.accept(null);
+                            return;
                         }
+                        doRegister(regInfo);
+                        doAction(action);
+                        onResult.accept(new ReceiveResult(regInfo.getProductKey(), regInfo.getDeviceName(), regInfo));
                         return;
                     case "auth":
                         //设备认证

+ 16 - 8
iot-components/iot-component-server/src/main/java/cc/iotkit/comps/service/DeviceBehaviourService.java

@@ -54,14 +54,15 @@ public class DeviceBehaviourService {
             DeviceInfo deviceInfo = register(null, info);
             //子设备注册
             List<RegisterInfo.SubDevice> subDevices = info.getSubDevices();
-            if (subDevices != null && subDevices.size() != 0) {
-                for (RegisterInfo.SubDevice subDevice : subDevices) {
-                    register(deviceInfo.getDeviceId(),
-                            new RegisterInfo(subDevice.getProductKey(),
-                                    subDevice.getDeviceName(),
-                                    subDevice.getModel(),
-                                    subDevice.getTag(), null));
-                }
+            if (subDevices == null) {
+                return;
+            }
+            for (RegisterInfo.SubDevice subDevice : subDevices) {
+                register(deviceInfo.getDeviceId(),
+                        new RegisterInfo(subDevice.getProductKey(),
+                                subDevice.getDeviceName(),
+                                subDevice.getModel(),
+                                subDevice.getTag(), null));
             }
         } catch (BizException e) {
             log.error("register device error", e);
@@ -175,6 +176,13 @@ public class DeviceBehaviourService {
 
     }
 
+    public boolean isOnline(String productKey,
+                            String deviceName) {
+        DeviceInfo device = deviceInfoData.findByProductKeyAndDeviceName(productKey, deviceName);
+        DeviceInfo deviceInfo = deviceInfoData.findByDeviceId(device.getDeviceId());
+        return deviceInfo.getState().isOnline();
+    }
+
     public void deviceStateChange(String productKey,
                                   String deviceName,
                                   boolean online) {

+ 5 - 2
iot-components/iot-emqx-component/src/main/java/cc/iotkit/comp/emqx/JsScripter.java

@@ -14,6 +14,7 @@ import cc.iotkit.model.device.message.ThingModelMessage;
 import cc.iotkit.model.product.ProductModel;
 import cc.iotkit.script.IScriptEngine;
 import cc.iotkit.script.ScriptEngineFactory;
+import com.fasterxml.jackson.core.type.TypeReference;
 import lombok.Data;
 import lombok.SneakyThrows;
 import lombok.extern.slf4j.Slf4j;
@@ -40,11 +41,13 @@ public class JsScripter implements IScripter {
 
     @SneakyThrows
     public ThingModelMessage decode(TransparentMsg msg) {
-        return scriptEngine.invokeMethod(ThingModelMessage.class, "decode", msg).get(0);
+        return scriptEngine.invokeMethod(new TypeReference<>() {
+        }, "decode", msg);
     }
 
     @SneakyThrows
     public TransparentMsg encode(ThingService<?> service) {
-        return scriptEngine.invokeMethod(TransparentMsg.class, "encode", service).get(0);
+        return scriptEngine.invokeMethod(new TypeReference<>() {
+        }, "encode", service);
     }
 }

+ 5 - 2
iot-components/iot-http-biz-component/src/main/java/cc/iotkit/comp/biz/HttpBizComponent.java

@@ -14,6 +14,7 @@ import cc.iotkit.comp.CompConfig;
 import cc.iotkit.comp.IComponent;
 import cc.iotkit.script.IScriptEngine;
 import cc.iotkit.script.ScriptEngineFactory;
+import com.fasterxml.jackson.core.type.TypeReference;
 import io.vertx.core.MultiMap;
 import io.vertx.core.Vertx;
 import io.vertx.core.http.HttpServer;
@@ -76,13 +77,15 @@ public class HttpBizComponent implements IComponent {
                             String response;
                             try {
                                 HttpContent content =
-                                        scriptEngine.invokeMethod(HttpContent.class,
+                                        scriptEngine.invokeMethod(
+                                                new TypeReference<>() {
+                                                },
                                                 "onReceive",
                                                 httpRequest.method().name(),
                                                 httpRequest.path(),
                                                 httpHeader,
                                                 httpParams,
-                                                body).get(0);
+                                                body);
                                 responseHeader = content.getHeader();
                                 response = content.getContent();
                                 response = response == null ? "" : response;

+ 5 - 2
iot-components/iot-mqtt-component/src/main/java/cc/iotkit/comp/mqtt/JsScripter.java

@@ -14,6 +14,7 @@ import cc.iotkit.model.device.message.ThingModelMessage;
 import cc.iotkit.model.product.ProductModel;
 import cc.iotkit.script.IScriptEngine;
 import cc.iotkit.script.ScriptEngineFactory;
+import com.fasterxml.jackson.core.type.TypeReference;
 import lombok.Data;
 import lombok.SneakyThrows;
 import lombok.extern.slf4j.Slf4j;
@@ -40,12 +41,14 @@ public class JsScripter implements IScripter {
 
     @SneakyThrows
     public ThingModelMessage decode(TransparentMsg msg) {
-        return scriptEngine.invokeMethod(ThingModelMessage.class, "decode", msg).get(0);
+        return scriptEngine.invokeMethod(new TypeReference<>() {
+        }, "decode", msg);
 
     }
 
     @SneakyThrows
     public TransparentMsg encode(ThingService<?> service) {
-        return scriptEngine.invokeMethod(TransparentMsg.class, "encode", service).get(0);
+        return scriptEngine.invokeMethod(new TypeReference<>() {
+        }, "encode", service);
     }
 }

+ 2 - 0
iot-components/iot-mqtt-component/src/main/java/cc/iotkit/comp/mqtt/MqttConfig.java

@@ -22,4 +22,6 @@ public class MqttConfig {
 
     private boolean ssl;
 
+    private boolean useWebSocket;
+
 }

+ 8 - 0
iot-components/iot-mqtt-component/src/main/java/cc/iotkit/comp/mqtt/MqttVerticle.java

@@ -29,6 +29,7 @@ import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 @Slf4j
 public class MqttVerticle extends AbstractVerticle {
@@ -41,6 +42,9 @@ public class MqttVerticle extends AbstractVerticle {
 
     private final Map<String, MqttEndpoint> endpointMap = new HashMap<>();
 
+    // 增加一个客户端连接clientid-连接状态池,避免mqtt关闭的时候走异常断开和mqtt断开的handler,导致多次离线消息
+    private static final Map<String, Boolean> mqttConnectPool = new ConcurrentHashMap<>();
+
     public MqttVerticle(MqttConfig config) {
         this.config = config;
     }
@@ -59,6 +63,7 @@ public class MqttVerticle extends AbstractVerticle {
                             .setKeyPath(config.getSslKey())
                             .setCertPath(config.getSslCert()));
         }
+        options.setUseWebSocket(config.isUseWebSocket());
 
         mqttServer = MqttServer.create(vertx, options);
         mqttServer.endpointHandler(endpoint -> {
@@ -84,6 +89,7 @@ public class MqttVerticle extends AbstractVerticle {
                     }
                     //保存设备与连接关系
                     endpointMap.put(getEndpointKey(r), endpoint);
+                    mqttConnectPool.put(clientId, true);
                 });
             } catch (Throwable e) {
                 log.error("auth failed", e);
@@ -96,6 +102,7 @@ public class MqttVerticle extends AbstractVerticle {
             endpoint.accept(false);
             endpoint.closeHandler((v) -> {
                 log.warn("client connection closed,clientId:{}", clientId);
+                if (mqttConnectPool.get(clientId) == false) return;
                 executor.onReceive(new HashMap<>(), "disconnect", clientId, (r) -> {
                     //删除设备与连接关系
                     endpointMap.remove(getEndpointKey(r));
@@ -105,6 +112,7 @@ public class MqttVerticle extends AbstractVerticle {
                 executor.onReceive(new HashMap<>(), "disconnect", clientId, (r) -> {
                     //删除设备与连接关系
                     endpointMap.remove(getEndpointKey(r));
+                    mqttConnectPool.put(clientId, false);
                 });
             }).subscribeHandler(subscribe -> {
                 List<MqttSubAckReasonCode> reasonCodes = new ArrayList<>();

+ 37 - 16
iot-components/iot-mqtt-component/src/main/resources/convert.js

@@ -152,22 +152,43 @@ this.encode = function (service,device) {
         method+=identifier;
         topic="/sys/"+service.productKey+"/"+service.deviceName+"/c/deregister";
     }
+    if(type=="property" && identifier=="get"  ){
+        var listParams = []
+        for(var p in service.params){
+            listParams.push(service.params[p]);
+        }
+        return {
+            productKey:service.productKey,
+            deviceName:service.deviceName,
+            mid:deviceMid,
+            content:{
+                topic:topic,
+                payload:JSON.stringify({
+                    id:deviceMid,
+                    method:method,
+                    params: listParams
+                })
+            }
+        }
+    }else{
+        for(var p in service.params){
+            params[p]=service.params[p];
+        }
+        return {
+            productKey:service.productKey,
+            deviceName:service.deviceName,
+            mid:deviceMid,
+            content:{
+                topic:topic,
+                payload:JSON.stringify({
+                    id:deviceMid,
+                    method:method,
+                    params:params
+                })
+            }
+        }
 
-    for(var p in service.params){
-        params[p]=service.params[p];
     }
 
-    return {
-        productKey:service.productKey,
-        deviceName:service.deviceName,
-        mid:deviceMid,
-        content:{
-            topic:topic,
-            payload:JSON.stringify({
-                id:deviceMid,
-                method:method,
-                params:params
-            })
-        }
-    }
-};
+
+};

+ 89 - 0
iot-components/iot-websocket-component/pom.xml

@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>iot-components</artifactId>
+        <groupId>cc.iotkit</groupId>
+        <version>0.4.3-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>iot-websocket-component</artifactId>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>io.vertx</groupId>
+            <artifactId>vertx-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.luaj</groupId>
+            <artifactId>luaj-jse</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cc.iotkit</groupId>
+            <artifactId>iot-common</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cc.iotkit</groupId>
+            <artifactId>iot-component-base</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cc.iotkit</groupId>
+            <artifactId>iot-data-service</artifactId>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-shade-plugin</artifactId>
+                <version>3.2.4</version>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>shade</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <artifactSet>
+                        <includes>
+                            <include>io.vertx:vertx-core</include>
+                            <include>org.luaj:luaj-jse</include>
+                        </includes>
+                    </artifactSet>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>11</source>
+                    <target>11</target>
+                    <forceJavacCompilerUse>true</forceJavacCompilerUse>
+                    <useIncrementalCompilation>false</useIncrementalCompilation>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 18 - 0
iot-components/iot-websocket-component/src/main/java/cc/iotkit/comp/websocket/AbstractDeviceVerticle.java

@@ -0,0 +1,18 @@
+package cc.iotkit.comp.websocket;
+
+import cc.iotkit.comp.IMessageHandler;
+import cc.iotkit.converter.DeviceMessage;
+import io.vertx.core.AbstractVerticle;
+import lombok.Data;
+
+@Data
+public abstract class AbstractDeviceVerticle extends AbstractVerticle {
+
+    public static final String TYPE_SERVER = "server";
+    public static final String TYPE_CLIENT = "client";
+
+    protected IMessageHandler executor;
+
+    public abstract DeviceMessage send(DeviceMessage message);
+
+}

+ 111 - 0
iot-components/iot-websocket-component/src/main/java/cc/iotkit/comp/websocket/WebSocketDeviceComponent.java

@@ -0,0 +1,111 @@
+package cc.iotkit.comp.websocket;
+
+import cc.iotkit.common.exception.BizException;
+import cc.iotkit.common.utils.JsonUtil;
+import cc.iotkit.comp.AbstractDeviceComponent;
+import cc.iotkit.comp.CompConfig;
+import cc.iotkit.comp.model.DeviceState;
+import cc.iotkit.comp.websocket.client.WebSocketClientVerticle;
+import cc.iotkit.comp.websocket.server.WebSocketServerVerticle;
+import cc.iotkit.converter.DeviceMessage;
+import io.vertx.core.Future;
+import io.vertx.core.Vertx;
+import lombok.*;
+import lombok.extern.slf4j.Slf4j;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+
+@Slf4j
+public class WebSocketDeviceComponent extends AbstractDeviceComponent {
+
+    private Vertx vertx;
+    private CountDownLatch countDownLatch;
+    private String deployedId;
+    private AbstractDeviceVerticle webSocketVerticle;
+    private String type;
+    private final Map<String, Device> deviceChildToParent = new HashMap<>();
+
+    public void create(CompConfig config) {
+        super.create(config);
+        vertx = Vertx.vertx();
+        type= JsonUtil.parse(config.getOther(), Map.class).get("type").toString();
+        if(AbstractDeviceVerticle.TYPE_CLIENT.equals(type)){
+            webSocketVerticle = new WebSocketClientVerticle(config.getOther());
+        }else{
+            webSocketVerticle = new WebSocketServerVerticle(config.getOther());
+        }
+    }
+
+    public void start() {
+        try {
+            webSocketVerticle.setExecutor(getHandler());
+            countDownLatch = new CountDownLatch(1);
+            Future<String> future = vertx.deployVerticle(webSocketVerticle);
+            future.onSuccess((s -> {
+                deployedId = s;
+                countDownLatch.countDown();
+            }));
+            future.onFailure((e) -> {
+                countDownLatch.countDown();
+                log.error("start websocket component failed", e);
+            });
+            countDownLatch.await();
+            future.succeeded();
+        } catch (Throwable e) {
+            throw new BizException("start websocket component error", e);
+        }
+    }
+
+    @SneakyThrows
+    public void stop() {
+        webSocketVerticle.stop();
+        Future<Void> future = vertx.undeploy(deployedId);
+        future.onSuccess(unused -> log.info("stop websocket component success"));
+    }
+
+    public void destroy() {
+    }
+
+    @Override
+    public void onDeviceStateChange(DeviceState state) {
+        DeviceState.Parent parent = state.getParent();
+        if (parent == null) {
+            return;
+        }
+        Device device = new Device(state.getProductKey(), state.getDeviceName());
+
+        if (DeviceState.STATE_ONLINE.equals(state.getState())) {
+            //保存子设备所属父设备
+            deviceChildToParent.put(device.toString(),
+                    new Device(parent.getProductKey(), parent.getDeviceName())
+            );
+        } else {
+            //删除关系
+            deviceChildToParent.remove(device.toString());
+        }
+
+    }
+
+    @Override
+    public DeviceMessage send(DeviceMessage message) {
+        webSocketVerticle.send(message);
+        return message;
+    }
+
+    @Override
+    public CompConfig getConfig() {
+        return config;
+    }
+
+
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @ToString
+    public static class Device {
+        private String productKey;
+        private String deviceName;
+    }
+
+}

+ 20 - 0
iot-components/iot-websocket-component/src/main/java/cc/iotkit/comp/websocket/client/WebSocketClientConfig.java

@@ -0,0 +1,20 @@
+package cc.iotkit.comp.websocket.client;
+
+import lombok.Data;
+
+@Data
+public class WebSocketClientConfig {
+
+    private int port;
+
+    private String ip;
+
+    private String url;
+
+    private long heartBeatTime;
+
+    private String heartBeatData;
+
+    private boolean ssl;
+
+}

+ 124 - 0
iot-components/iot-websocket-component/src/main/java/cc/iotkit/comp/websocket/client/WebSocketClientVerticle.java

@@ -0,0 +1,124 @@
+package cc.iotkit.comp.websocket.client;
+
+import cc.iotkit.common.exception.BizException;
+import cc.iotkit.common.utils.JsonUtil;
+import cc.iotkit.comp.model.ReceiveResult;
+import cc.iotkit.comp.model.RegisterInfo;
+import cc.iotkit.comp.websocket.AbstractDeviceVerticle;
+import cc.iotkit.converter.DeviceMessage;
+import io.vertx.core.Future;
+import io.vertx.core.http.HttpClient;
+import io.vertx.core.http.HttpClientOptions;
+import io.vertx.core.http.WebSocket;
+import io.vertx.core.http.WebSocketConnectOptions;
+import lombok.*;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+
+@Slf4j
+public class WebSocketClientVerticle extends AbstractDeviceVerticle {
+
+    private HttpClient httpClient;
+
+    private WebSocket webSocketClient;
+
+    private WebSocketClientConfig webSocketConfig;
+
+    private long timerID;
+
+    private final Map<String, Device> devices = new ConcurrentHashMap<>();
+
+    public void setWebSocketClient(WebSocket webSocketClient) {
+        this.webSocketClient = webSocketClient;
+    }
+
+    public WebSocketClientVerticle(String config) {
+        this.webSocketConfig = JsonUtil.parse(config, WebSocketClientConfig.class);
+    }
+
+    public void start() {
+        WebSocketConnectOptions options = new WebSocketConnectOptions().setPort(webSocketConfig.getPort())
+                .setHost(webSocketConfig.getIp()).setURI(webSocketConfig.getUrl()).setSsl(webSocketConfig.isSsl());
+        httpClient = vertx.createHttpClient();
+        httpClient.webSocket(options).onSuccess(ws -> {
+            setWebSocketClient(ws);
+            log.info("webSocket client connect success!");
+            ws.textMessageHandler(data -> {
+                log.info("webSocket client receive msg:" + data);
+                executor.onReceive(new HashMap<>(), null, data, (ret) -> {
+                    if (ret != null && ret.getData() instanceof RegisterInfo) {
+                        executor.onReceive(null, "connected", data, (r) -> {
+                            if (!devices.containsKey(getDeviceKey(r))) {
+                                devices.put(getDeviceKey(r), new Device(r.getDeviceName(), r.getProductKey()));
+                            }
+                        });
+                    }
+                });
+            });
+            ws.closeHandler(e -> {
+                for (String deviceKey : devices.keySet()) {
+                    executor.onReceive(null, "disconnect", deviceKey);
+                }
+                log.warn("client connection closed!");
+            });
+            ws.exceptionHandler(e -> {
+                for (String deviceKey : devices.keySet()) {
+                    executor.onReceive(null, "disconnect", deviceKey);
+                }
+                log.error("webSocket client connect exception!");
+            });
+            if (webSocketConfig.getHeartBeatTime() > 0 && StringUtils.isNotBlank(webSocketConfig.getHeartBeatData())) {
+                timerID = vertx.setPeriodic(webSocketConfig.getHeartBeatTime(), t -> {
+                    if (webSocketClient.isClosed()) {
+                        vertx.cancelTimer(timerID);
+                    }
+                    executor.onReceive(new HashMap<>(), "ping", JsonUtil.toJsonString(webSocketConfig));
+                });
+            }
+        }).onFailure(e -> {
+            log.info("webSocket client connect failed!");
+        });
+    }
+
+    @SneakyThrows
+    public void stop() {
+        vertx.cancelTimer(timerID);
+        for (String deviceKey : devices.keySet()) {
+            executor.onReceive(null, "disconnect", deviceKey);
+        }
+        httpClient.close();
+    }
+
+    @Override
+    public DeviceMessage send(DeviceMessage message) {
+        Object obj = message.getContent();
+        if (!(obj instanceof Map)) {
+            throw new BizException("message content is not Map");
+        }
+        String msgStr = JsonUtil.toJsonString(obj);
+        log.info("send msg payload:{}", msgStr);
+        Future<Void> result = webSocketClient.writeTextMessage(msgStr);
+        result.onFailure(e -> log.error("webSocket client send msg failed", e));
+        return message;
+    }
+
+    private String getDeviceKey(ReceiveResult result) {
+        return getDeviceKey(result.getProductKey(), result.getDeviceName());
+    }
+
+    private String getDeviceKey(String productKey, String deviceName) {
+        return String.format("%s_%s", productKey, deviceName);
+    }
+
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @ToString
+    public static class Device {
+        private String productKey;
+        private String deviceName;
+    }
+}

+ 25 - 0
iot-components/iot-websocket-component/src/main/java/cc/iotkit/comp/websocket/server/WebSocketServerConfig.java

@@ -0,0 +1,25 @@
+package cc.iotkit.comp.websocket.server;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class WebSocketServerConfig {
+
+    private int port;
+
+    private String sslKey;
+
+    private String sslCert;
+
+    private boolean ssl;
+
+    private List<AccessToken> accessTokens;
+
+    @Data
+    public static class AccessToken{
+        private String tokenName;
+        private String tokenStr;
+    }
+}

+ 145 - 0
iot-components/iot-websocket-component/src/main/java/cc/iotkit/comp/websocket/server/WebSocketServerVerticle.java

@@ -0,0 +1,145 @@
+package cc.iotkit.comp.websocket.server;
+
+
+import cc.iotkit.common.exception.BizException;
+import cc.iotkit.common.utils.JsonUtil;
+import cc.iotkit.comp.model.ReceiveResult;
+import cc.iotkit.comp.websocket.AbstractDeviceVerticle;
+import cc.iotkit.converter.DeviceMessage;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.util.JSONPObject;
+import io.vertx.core.Future;
+import io.vertx.core.http.HttpServer;
+import io.vertx.core.http.HttpServerOptions;
+import io.vertx.core.http.ServerWebSocket;
+import io.vertx.core.net.PemKeyCertOptions;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+
+@Slf4j
+public class WebSocketServerVerticle extends AbstractDeviceVerticle {
+
+
+    private HttpServer httpServer;
+
+    private WebSocketServerConfig webSocketConfig;
+
+    private final Map<String, ServerWebSocket> wsClients = new ConcurrentHashMap<>();
+
+    public WebSocketServerVerticle(String config) {
+        this.webSocketConfig = JsonUtil.parse(config, WebSocketServerConfig.class);
+    }
+
+    private Map<String, String> tokens=new HashMap<>();
+
+    @Override
+    public void start() throws Exception {
+        HttpServerOptions options = new HttpServerOptions()
+                .setPort(webSocketConfig.getPort());
+        if (webSocketConfig.isSsl()) {
+            options = options.setSsl(true)
+                    .setKeyCertOptions(new PemKeyCertOptions()
+                            .setKeyPath(webSocketConfig.getSslKey())
+                            .setCertPath(webSocketConfig.getSslCert()));
+        }
+        httpServer = vertx.createHttpServer(options).webSocketHandler(wsClient -> {
+            log.info("webSocket client connect sessionId:{},path={}", wsClient.textHandlerID(), wsClient.path());
+            String deviceKey = wsClient.path().replace("/","");
+            if(StringUtils.isBlank(deviceKey)||deviceKey.split("_").length!=2){
+                log.warn("陌生连接,拒绝");
+                wsClient.reject();
+                return;
+            }
+            wsClient.writeTextMessage("connect succes! please auth!");
+            Map<String,String> deviceKeyObj=new HashMap<>();
+            deviceKeyObj.put("deviceKey",deviceKey);
+            wsClient.textMessageHandler(message -> {
+                HashMap<String,String> msg= JsonUtil.parse(message,HashMap.class);
+                if(wsClients.containsKey(deviceKey)){
+                    executor.onReceive(new HashMap<>(), "", message);
+                }else if(msg!=null&&"auth".equals(msg.get("type"))){
+                    Set<String> tokenKey=tokens.keySet();
+                    for(String key:tokenKey){
+                        if(StringUtils.isNotBlank(msg.get(key))&&tokens.get(key).equals(msg.get(key))){
+                            //保存设备与连接关系
+                            log.info("认证通过");
+                            wsClients.put(deviceKey, wsClient);
+                            wsClient.writeTextMessage("auth succes");
+                            return;
+                        }
+                    }
+                    log.warn("认证失败,拒绝");
+                    wsClient.writeTextMessage("auth fail");
+                    return;
+                }else{
+                    log.warn("认证失败,拒绝");
+                    wsClient.writeTextMessage("auth fail");
+                    return;
+                }
+
+            });
+            wsClient.closeHandler(c -> {
+                log.warn("client connection closed,deviceKey:{}", deviceKey);
+                executor.onReceive(new HashMap<>(), "disconnect", JsonUtil.toJsonString(deviceKeyObj), (r) -> {
+                    //删除设备与连接关系
+                    if(r!=null){
+                        wsClients.remove(getDeviceKey(r));
+                    }
+                });
+            });
+            wsClient.exceptionHandler(ex -> {
+                log.warn("webSocket client connection exception,deviceKey:{}", deviceKey);
+            });
+        }).listen(webSocketConfig.getPort(), server -> {
+            if (server.succeeded()) {
+                log.info("webSocket server is listening on port " + webSocketConfig.getPort());
+                List<WebSocketServerConfig.AccessToken> tokenConfig= webSocketConfig.getAccessTokens();
+                for (WebSocketServerConfig.AccessToken obj:tokenConfig) {
+                    tokens.put(obj.getTokenName(),obj.getTokenStr());
+                }
+            } else {
+                log.error("webSocket server on starting the server", server.cause());
+            }
+        });
+    }
+
+    @Override
+    public void stop() throws Exception {
+        for (String deviceKey : wsClients.keySet()) {
+            Map<String,String> deviceKeyObj=new HashMap<>();
+            deviceKeyObj.put("deviceKey",deviceKey);
+            executor.onReceive(null, "disconnect", JsonUtil.toJsonString(deviceKeyObj));
+        }
+        tokens.clear();
+        httpServer.close(voidAsyncResult -> log.info("close webocket server..."));
+    }
+
+    private String getDeviceKey(ReceiveResult result) {
+        return getDeviceKey(result.getProductKey(), result.getDeviceName());
+    }
+
+    private String getDeviceKey(String productKey, String deviceName) {
+        return String.format("%s_%s", productKey, deviceName);
+    }
+
+    @Override
+    public DeviceMessage send(DeviceMessage message) {
+        ServerWebSocket wsClient = wsClients.get(getDeviceKey(message.getProductKey(), message.getDeviceName()));
+        Object obj = message.getContent();
+        if (!(obj instanceof Map)) {
+            throw new BizException("message content is not Map");
+        }
+        String msgStr = JsonUtil.toJsonString(obj);
+        log.info("send msg payload:{}", msgStr);
+        Future<Void> result = wsClient.writeTextMessage(msgStr);
+        result.onFailure(e -> log.error("webSocket server send msg failed", e));
+        return message;
+    }
+}

+ 72 - 0
iot-components/iot-websocket-component/src/main/resources/component.js

@@ -0,0 +1,72 @@
+var mid=1;
+
+var access_token="";
+
+function getMid(){
+	mid++;
+	if(mid>10000){
+		mid=1;
+	}
+	return mid;
+};
+function getPingData(data){
+	var ping={
+		productKey:"",
+		deviceName:"",
+		content:{
+			id:getMid(),
+			type:data
+		}
+	};
+	return {
+		type:"action",
+		data:{
+			productKey:"",
+			deviceName:"",
+			state:""
+		},
+		action:{
+			type:"ack",
+			content:JSON.stringify(ping)
+		}
+	}
+};
+//必须提供onReceive方法
+this.onReceive=function(head,type,payload){
+	var data=JSON.parse(payload)
+	if(data.type=="auth_required"){
+		var auth={
+			productKey:"",
+			deviceName:"",
+			content:{
+				type:"auth",
+				access_token:access_token
+			}
+		};
+		return {
+			type:"action",
+			data:{
+				productKey:"",
+				deviceName:"",
+				state:""
+			},
+			action:{
+				type:"ack",
+				content:JSON.stringify(auth)
+			}
+		}
+	}else if(data.type=="auth_ok"){
+		return getPingData(data.heartBeatData);
+	}else if(data.type=="pong"){
+		apiTool.log("receive pong!");
+	}else if("ping"==type){
+		return getPingData(data.heartBeatData);
+	}
+	return {
+		productKey:"",
+		deviceName:"",
+		mid:0,
+		content:{
+		}
+	}
+};

+ 1 - 0
iot-components/iot-websocket-component/src/main/resources/component.spi

@@ -0,0 +1 @@
+cc.iotkit.comp.websocket.WebSocketDeviceComponent

+ 55 - 0
iot-components/iot-websocket-component/src/main/resources/converter.js

@@ -0,0 +1,55 @@
+
+var mid=1;
+
+function getMid(){
+	mid++;
+	if(mid>10000){
+		mid=1;
+	}
+	return mid+"";
+}
+
+this.decode = function (msg) {
+	//对msg进行解析,并返回物模型数据
+	var content=msg.content;
+	var type = content.type;
+
+	if (type=="report") {
+		//属性上报
+		return {
+			mid: msg.mid,
+			productKey: msg.productKey,
+			deviceName: msg.deviceName,
+			type:"property",
+			identifier: "report", //属性上报
+			occur: new Date().getTime(), //时间戳,设备上的事件或数据产生的本地时间
+			time: new Date().getTime(), //时间戳,消息上报时间
+			data: content.params,
+		};
+	}
+	return null;
+};
+
+this.encode = function (service,device) {
+	var type=service.type;
+	var identifier=service.identifier;
+	var entityId=service.deviceName;
+	var deviceMid=getMid();
+	var params={};
+	var target={};
+	if("property"==type&&"set"==identifier){
+		var domain=entityId.split(".")[0];
+		var powerstate=service.params.powerstate==1?"turn_on":"turn_off";
+		params.type="call_service";
+		params.domain=domain;
+		params.service=powerstate;
+		target.entity_id=entityId;
+		params.target=target;
+	}
+	return {
+		productKey:service.productKey,
+		deviceName:service.deviceName,
+		mid:deviceMid,
+		content:params
+	}
+};

+ 2 - 0
iot-data/iot-data-service/src/main/java/cc/iotkit/data/ISpaceDeviceData.java

@@ -17,6 +17,8 @@ public interface ISpaceDeviceData extends IOwnedData<SpaceDevice, String> {
 
     List<SpaceDevice> findByUidOrderByUseAtDesc(String uid);
 
+    List<SpaceDevice> findByHomeIdAndCollect(String homeId,boolean collect);
+
     List<SpaceDevice> findByUidOrderByAddAtDesc(String uid);
 
     List<SpaceDevice> findBySpaceIdOrderByAddAtDesc(String spaceId);

+ 5 - 0
iot-data/iot-model/src/main/java/cc/iotkit/model/space/SpaceDevice.java

@@ -63,4 +63,9 @@ public class SpaceDevice implements Owned<String> {
      */
     private Long useAt;
 
+    /**
+     * 是否收藏
+     */
+    private Boolean collect;
+
 }

+ 2 - 0
iot-data/iot-rdb-data-service/src/main/java/cc/iotkit/data/dao/SpaceDeviceRepository.java

@@ -24,6 +24,8 @@ public interface SpaceDeviceRepository extends JpaRepository<TbSpaceDevice, Stri
 
     List<TbSpaceDevice> findByUidOrderByUseAtDesc(String uid);
 
+    List<TbSpaceDevice> findByHomeIdAndCollect(String homeId,boolean collect);
+
     List<TbSpaceDevice> findByUidOrderByAddAtDesc(String uid);
 
     List<TbSpaceDevice> findBySpaceIdOrderByAddAtDesc(String spaceId);

+ 5 - 0
iot-data/iot-rdb-data-service/src/main/java/cc/iotkit/data/model/TbSpaceDevice.java

@@ -63,4 +63,9 @@ public class TbSpaceDevice {
      */
     private Long useAt;
 
+    /**
+     * 是否收藏
+     */
+    private Boolean collect;
+
 }

+ 5 - 0
iot-data/iot-rdb-data-service/src/main/java/cc/iotkit/data/service/SpaceDeviceDataImpl.java

@@ -34,6 +34,11 @@ public class SpaceDeviceDataImpl implements ISpaceDeviceData {
         return SpaceDeviceMapper.toDto(spaceDeviceRepository.findByUidOrderByUseAtDesc(uid));
     }
 
+    @Override
+    public List<SpaceDevice> findByHomeIdAndCollect(String homeId,boolean collect) {
+        return SpaceDeviceMapper.toDto(spaceDeviceRepository.findByHomeIdAndCollect(homeId,collect));
+    }
+
     @Override
     public List<SpaceDevice> findByUidOrderByAddAtDesc(String uid) {
         return SpaceDeviceMapper.toDto(spaceDeviceRepository.findByUidOrderByAddAtDesc(uid));

+ 97 - 0
iot-data/iot-ts-temporal-service/pom.xml

@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>cc.iotkit</groupId>
+        <artifactId>iot-data</artifactId>
+        <version>0.4.3-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>iot-ts-temporal-service</artifactId>
+
+    <properties>
+        <maven.compiler.source>11</maven.compiler.source>
+        <maven.compiler.target>11</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-context</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-jdbc</artifactId>
+        </dependency>
+
+
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-annotations</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.mapstruct</groupId>
+            <artifactId>mapstruct</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-http</artifactId>
+        </dependency>
+
+
+
+        <dependency>
+            <groupId>cc.iotkit</groupId>
+            <artifactId>iot-data-cache</artifactId>
+        </dependency>
+
+
+        <dependency>
+            <groupId>org.jooq</groupId>
+            <artifactId>jooq-meta</artifactId>
+            <version>3.14.15</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.jooq</groupId>
+            <artifactId>jooq</artifactId>
+            <version>3.14.15</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.postgresql</groupId>
+            <artifactId>postgresql</artifactId>
+            <version>42.5.4</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-jdbc</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cc.iotkit</groupId>
+            <artifactId>iot-model</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cc.iotkit</groupId>
+            <artifactId>iot-model</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>cc.iotkit</groupId>
+            <artifactId>iot-temporal-service</artifactId>
+        </dependency>
+
+    </dependencies>
+</project>

+ 5 - 0
iot-data/iot-ts-temporal-service/readme.md

@@ -0,0 +1,5 @@
+### 时序数据库服务接口的TimescaleDB实现
+
+postgrep 14
+
+

+ 27 - 0
iot-data/iot-ts-temporal-service/src/main/java/cc/iotkit/temporal/ts/config/Constants.java

@@ -0,0 +1,27 @@
+/*
+ * +----------------------------------------------------------------------
+ * | Copyright (c) 奇特物联 2021-2022 All rights reserved.
+ * +----------------------------------------------------------------------
+ * | Licensed 未经许可不能去掉「奇特物联」相关版权
+ * +----------------------------------------------------------------------
+ * | Author: xw2sy@163.com
+ * +----------------------------------------------------------------------
+ */
+package cc.iotkit.temporal.ts.config;
+
+public interface Constants {
+
+    /**
+     * 根据产品key获取产品属性超级表名
+     */
+    static String getProductPropertySTableName(String productKey) {
+        return String.format("product_property_%s", productKey.toLowerCase());
+    }
+
+    /**
+     * 根据deviceId获取设备属性表名
+     */
+    static String getDevicePropertyTableName(String deviceId) {
+        return String.format("device_property_%s", deviceId.toLowerCase());
+    }
+}

+ 43 - 0
iot-data/iot-ts-temporal-service/src/main/java/cc/iotkit/temporal/ts/config/TsDatasourceConfig.java

@@ -0,0 +1,43 @@
+/*
+ * +----------------------------------------------------------------------
+ * | Copyright (c) 奇特物联 2021-2022 All rights reserved.
+ * +----------------------------------------------------------------------
+ * | Licensed 未经许可不能去掉「奇特物联」相关版权
+ * +----------------------------------------------------------------------
+ * | Author: xw2sy@163.com
+ * +----------------------------------------------------------------------
+ */
+package cc.iotkit.temporal.ts.config;
+
+import cc.iotkit.temporal.ts.dao.TsTemplate;
+import com.zaxxer.hikari.HikariDataSource;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class TsDatasourceConfig {
+
+    @Value("${spring.ts-datasource.url}")
+    private String url;
+
+    @Value("${spring.ts-datasource.driverClassName}")
+    private String driverClassName;
+
+    @Value("${spring.ts-datasource.username}")
+    private String username;
+
+    @Value("${spring.ts-datasource.password}")
+    private String password;
+
+    @Bean("tsJdbcTemplate")
+    public TsTemplate tdJdbcTemplate() {
+        HikariDataSource dataSource = new HikariDataSource();
+        dataSource.setJdbcUrl(url);
+        dataSource.setUsername(username);
+        dataSource.setPassword(password);
+        dataSource.setDriverClassName(driverClassName);
+        return new TsTemplate(dataSource);
+    }
+
+}

+ 19 - 0
iot-data/iot-ts-temporal-service/src/main/java/cc/iotkit/temporal/ts/dao/TsTemplate.java

@@ -0,0 +1,19 @@
+package cc.iotkit.temporal.ts.dao;
+
+import org.springframework.jdbc.core.JdbcTemplate;
+
+import javax.sql.DataSource;
+
+public class TsTemplate extends JdbcTemplate {
+
+    public TsTemplate() {
+    }
+
+    public TsTemplate(DataSource dataSource) {
+        super(dataSource);
+    }
+
+    public TsTemplate(DataSource dataSource, boolean lazyInit) {
+        super(dataSource, lazyInit);
+    }
+}

+ 15 - 0
iot-data/iot-ts-temporal-service/src/main/java/cc/iotkit/temporal/ts/dm/DbField.java

@@ -0,0 +1,15 @@
+package cc.iotkit.temporal.ts.dm;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class DbField {
+    private String name;
+    private String type;
+
+    private int length;
+}

+ 118 - 0
iot-data/iot-ts-temporal-service/src/main/java/cc/iotkit/temporal/ts/dm/FieldParser.java

@@ -0,0 +1,118 @@
+/*
+ * +----------------------------------------------------------------------
+ * | Copyright (c) 奇特物联 2021-2022 All rights reserved.
+ * +----------------------------------------------------------------------
+ * | Licensed 未经许可不能去掉「奇特物联」相关版权
+ * +----------------------------------------------------------------------
+ * | Author: xw2sy@163.com
+ * +----------------------------------------------------------------------
+ */
+package cc.iotkit.temporal.ts.dm;
+
+
+import cc.iotkit.model.product.ThingModel;
+import org.jooq.DataType;
+import org.jooq.Field;
+import org.jooq.impl.DSL;
+import org.jooq.impl.SQLDataType;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public class FieldParser {
+
+    /**
+     * 物模型到td数据类型映射
+     */
+    private static final Map<String, DataType> TYPE_MAPPING = Map.of(
+            "int32", SQLDataType.INTEGER,
+            "float", SQLDataType.FLOAT,
+            "bool", SQLDataType.INTEGER,
+            "enum", SQLDataType.INTEGER,
+            "text", SQLDataType.NVARCHAR,
+            "date", SQLDataType.DATE
+    );
+
+    /**
+     * td数据类型到物模型映射
+     */
+
+    private static final Map<String, DataType> DB2TYPE_MAPPING = Map.of(
+            "int",SQLDataType.INTEGER,
+            "float",   SQLDataType.FLOAT,
+            "bool", SQLDataType.INTEGER,
+            "char",SQLDataType.NVARCHAR,
+            "date",  SQLDataType.DATE,
+            "timestamptz", SQLDataType.TIMESTAMPWITHTIMEZONE
+    );
+
+
+    private static DataType getFieldType(final String type) {
+        Set<String> keys = DB2TYPE_MAPPING.keySet();
+        String lowerCase = type.toLowerCase();
+        for(String key:keys){
+            if(lowerCase.contains(key)){
+                return DB2TYPE_MAPPING.get(key);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 将物模型字段转换为td字段
+     */
+    public static TsField parse(ThingModel.Property property) {
+        String filedName = property.getIdentifier().toLowerCase();
+        ThingModel.DataType dataType = property.getDataType();
+        String type = dataType.getType();
+
+        //将物模型字段类型映射为td字段类型
+        DataType fType = TYPE_MAPPING.get(type);
+        Object specs = dataType.getSpecs();
+        int len = -1;
+        if (specs instanceof Map) {
+            Object objLen = ((Map<?, ?>) specs).get("length");
+            if (objLen != null) {
+                len = Integer.parseInt(objLen.toString());
+            }
+        }
+
+        return new TsField(filedName, fType, len);
+    }
+
+    /**
+     * 获取物模型中的字段列表
+     */
+    public static List<TsField> parse(ThingModel thingModel) {
+        return thingModel.getModel().getProperties().stream().map(FieldParser::parse).collect(Collectors.toList());
+    }
+
+    /**
+     * 将从库中查出来的字段信息转换为td字段对象
+     */
+    public static List<TsField> parse(List<DbField> rows) {
+        return (List<TsField>) rows.stream().map((r) -> {
+
+            return new TsField(
+                    r.getName(),
+                    getFieldType(r.getType()).length(r.getLength()),r.getLength());
+        }).collect(Collectors.toList());
+    }
+
+    /**
+     * 获取字段字义
+     */
+    public static Field getFieldDefine(TsField field) {
+        int length = field.getLength();
+        DataType type = field.getType();
+
+        if(length>0){
+            type.length(length);
+        }
+        return DSL.field(field.getName(),type);
+
+    }
+
+}

+ 161 - 0
iot-data/iot-ts-temporal-service/src/main/java/cc/iotkit/temporal/ts/dm/TableManager.java

@@ -0,0 +1,161 @@
+/*
+ * +----------------------------------------------------------------------
+ * | Copyright (c) 奇特物联 2021-2022 All rights reserved.
+ * +----------------------------------------------------------------------
+ * | Licensed 未经许可不能去掉「奇特物联」相关版权
+ * +----------------------------------------------------------------------
+ * | Author: xw2sy@163.com
+ * +----------------------------------------------------------------------
+ */
+package cc.iotkit.temporal.ts.dm;
+
+import org.jooq.*;
+import org.jooq.conf.ParamType;
+import org.jooq.impl.DSL;
+import org.jooq.impl.SQLDataType;
+import org.jooq.tools.StringUtils;
+
+import java.util.List;
+
+import static org.jooq.impl.DSL.*;
+
+public class TableManager {
+
+   private static final DSLContext sqlBuilder = DSL.using(SQLDialect.POSTGRES);
+
+    public static DSLContext getSqlBuilder() {
+        return sqlBuilder;
+    }
+
+    /**
+     * 获取创建表sql
+     */
+    public static String getCreateSTableSql(String tbName, List<TsField> fields) {
+        if (fields.size() == 0) {
+            return null;
+        }
+
+        CreateTableColumnStep tableColumnStep = sqlBuilder.createTable(tbName)
+                .column("time", SQLDataType.TIMESTAMPWITHTIMEZONE.nullable(false))
+                .column(field("device_id", SQLDataType.NCHAR.length(50).nullable(false)));
+
+        //生成字段片段
+
+        for (TsField field : fields) {
+            tableColumnStep.column(FieldParser.getFieldDefine(field));
+
+        }
+
+
+        return tableColumnStep.getSQL(ParamType.INLINED);
+
+    }
+
+    public static String getCreateSTableIndexSql(String tbName, String partitionCol) {
+        //根据时间和设备纬度分区
+        String sql = null;
+        if(StringUtils.isBlank(partitionCol)){
+            //  只根据时间分区
+            sql=  String.format(" SELECT create_hypertable('%s', 'time') ;", tbName);
+        }else{
+            sql=  String.format(" SELECT * FROM create_hypertable('%s', 'time'," +
+                    "  partitioning_column => '%s'," +
+                    "  number_partitions => 4" +
+                    ") ;", tbName, partitionCol );
+
+        }
+
+        return sql;
+
+    }
+
+    public static String getCreateTableIndexSql(String tbName) {
+
+        CreateIndexIncludeStep step = sqlBuilder.createIndexIfNotExists(tbName + "_index").on(
+                table(name(tbName)),
+                field(name("device_id")),
+                field(name("time")).desc());
+
+        return step.getSQL(ParamType.INLINED);
+
+    }
+
+    /**
+     * 取正确的表名
+     *
+     * @param name 表象
+     */
+    public static String rightTbName(String name) {
+        return name.toLowerCase().replace("-", "_");
+    }
+
+    /**
+     * 获取表详情的sql
+     */
+    public static String getDescTableSql(String tbName) {
+
+
+      String sql =String.format( " select a.attname as name," +
+              " t.typname as type, " +
+              "a.attlen as length," +
+              " case when a.attnotnull='t' then '1' else '0' end as nullable," +
+              " case when b.pk='t' then '1' else '0' end as isPk " +
+              "from pg_class e, pg_attribute a left join pg_type t on a.atttypid = t.oid " +
+              "left join (select pg_constraint.conname,pg_constraint.contype,pg_attribute.attname as pk " +
+              "from pg_constraint " +
+              " inner join pg_class on pg_constraint.conrelid = pg_class.oid" +
+              " inner join pg_attribute on pg_attribute.attrelid = pg_class.oid " +
+              " and  pg_attribute.attnum = any(pg_constraint.conkey) where contype='p')" +
+              " b on a.attname=b.pk where e.relname = '%s'" +
+              " and a.attnum > 0 and a.attrelid = e.oid and t.typname is not null ;",tbName);
+
+    return sql;
+
+
+
+    }
+
+    /**
+     * 获取添加字段sql
+     */
+    public static String getAddSTableColumnSql(String tbName, List<TsField> fields) {
+
+        AlterTableStep alterTableStep = sqlBuilder.alterTable(tbName);
+
+        AlterTableFinalStep addStep = null;
+        for (TsField o : fields) {
+            addStep = alterTableStep.add(FieldParser.getFieldDefine(o));
+        }
+        return addStep.getSQL();
+    }
+
+    /**
+     * 获取修改字段sql
+     */
+    public static String getModifySTableColumnSql(String tbName, List<TsField> fields) {
+        AlterTableStep alterTableStep = sqlBuilder.alterTable(tbName);
+        AlterTableFinalStep step = null;
+        for (TsField o : fields) {
+            Field fieldDefine = FieldParser.getFieldDefine(o);
+            step = alterTableStep.alterColumn(o.getName()).set(fieldDefine.getDataType());
+        }
+        return step.getSQL();
+    }
+
+    /**
+     * 获取删除字段sql
+     */
+    public static String getDropSTableColumnSql(String tbName, List<TsField> fields) {
+
+        AlterTableStep alterTableStep = sqlBuilder.alterTable(tbName);
+
+
+        AlterTableFinalStep step = null;
+        for (TsField o : fields) {
+            step = alterTableStep.dropColumnIfExists(FieldParser.getFieldDefine(o));
+        }
+
+        return step.getSQL();
+    }
+
+}

+ 24 - 0
iot-data/iot-ts-temporal-service/src/main/java/cc/iotkit/temporal/ts/dm/TsField.java

@@ -0,0 +1,24 @@
+/*
+ * +----------------------------------------------------------------------
+ * | Copyright (c) 奇特物联 2021-2022 All rights reserved.
+ * +----------------------------------------------------------------------
+ * | Licensed 未经许可不能去掉「奇特物联」相关版权
+ * +----------------------------------------------------------------------
+ * | Author: xw2sy@163.com
+ * +----------------------------------------------------------------------
+ */
+package cc.iotkit.temporal.ts.dm;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.jooq.DataType;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class TsField {
+    private String name;
+    private DataType type;
+    private int length;
+}

+ 31 - 0
iot-data/iot-ts-temporal-service/src/main/java/cc/iotkit/temporal/ts/model/TsDeviceProperty.java

@@ -0,0 +1,31 @@
+/*
+ * +----------------------------------------------------------------------
+ * | Copyright (c) 奇特物联 2021-2022 All rights reserved.
+ * +----------------------------------------------------------------------
+ * | Licensed 未经许可不能去掉「奇特物联」相关版权
+ * +----------------------------------------------------------------------
+ * | Author: xw2sy@163.com
+ * +----------------------------------------------------------------------
+ */
+package cc.iotkit.temporal.ts.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class TsDeviceProperty {
+
+    private Date time;
+
+    private String deviceId;
+
+    private String name;
+
+    private Object value;
+
+}

+ 33 - 0
iot-data/iot-ts-temporal-service/src/main/java/cc/iotkit/temporal/ts/model/TsRuleLog.java

@@ -0,0 +1,33 @@
+/*
+ * +----------------------------------------------------------------------
+ * | Copyright (c) 奇特物联 2021-2022 All rights reserved.
+ * +----------------------------------------------------------------------
+ * | Licensed 未经许可不能去掉「奇特物联」相关版权
+ * +----------------------------------------------------------------------
+ * | Author: xw2sy@163.com
+ * +----------------------------------------------------------------------
+ */
+package cc.iotkit.temporal.ts.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class TsRuleLog {
+
+    private Date time;
+
+    private String ruleId;
+
+    private String state1;
+
+    private String content;
+
+    private Boolean success;
+
+}

+ 29 - 0
iot-data/iot-ts-temporal-service/src/main/java/cc/iotkit/temporal/ts/model/TsTaskLog.java

@@ -0,0 +1,29 @@
+/*
+ * +----------------------------------------------------------------------
+ * | Copyright (c) 奇特物联 2021-2022 All rights reserved.
+ * +----------------------------------------------------------------------
+ * | Licensed 未经许可不能去掉「奇特物联」相关版权
+ * +----------------------------------------------------------------------
+ * | Author: xw2sy@163.com
+ * +----------------------------------------------------------------------
+ */
+package cc.iotkit.temporal.ts.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class TsTaskLog {
+
+    private Long time;
+
+    private String taskId;
+
+    private String content;
+
+    private Boolean success;
+
+}

+ 45 - 0
iot-data/iot-ts-temporal-service/src/main/java/cc/iotkit/temporal/ts/model/TsThingModelMessage.java

@@ -0,0 +1,45 @@
+/*
+ * +----------------------------------------------------------------------
+ * | Copyright (c) 奇特物联 2021-2022 All rights reserved.
+ * +----------------------------------------------------------------------
+ * | Licensed 未经许可不能去掉「奇特物联」相关版权
+ * +----------------------------------------------------------------------
+ * | Author: xw2sy@163.com
+ * +----------------------------------------------------------------------
+ */
+package cc.iotkit.temporal.ts.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class TsThingModelMessage {
+
+    private Date time;
+
+    private String mid;
+
+    private String deviceId;
+
+    private String productKey;
+
+    private String deviceName;
+
+    private String uid;
+
+    private String type;
+
+    private String identifier;
+
+    private int code;
+
+    private String data;
+
+    private Long reportTime;
+
+}

+ 36 - 0
iot-data/iot-ts-temporal-service/src/main/java/cc/iotkit/temporal/ts/model/TsTimeData.java

@@ -0,0 +1,36 @@
+/*
+ * +----------------------------------------------------------------------
+ * | Copyright (c) 奇特物联 2021-2022 All rights reserved.
+ * +----------------------------------------------------------------------
+ * | Licensed 未经许可不能去掉「奇特物联」相关版权
+ * +----------------------------------------------------------------------
+ * | Author: xw2sy@163.com
+ * +----------------------------------------------------------------------
+ */
+package cc.iotkit.temporal.ts.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+
+/**
+ * 统计的时间数据
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class TsTimeData {
+
+    /**
+     * 时间
+     */
+    private Date time;
+
+    /**
+     * 数据值
+     */
+    private Object data;
+
+}

+ 33 - 0
iot-data/iot-ts-temporal-service/src/main/java/cc/iotkit/temporal/ts/model/TsVirtualDeviceLog.java

@@ -0,0 +1,33 @@
+/*
+ * +----------------------------------------------------------------------
+ * | Copyright (c) 奇特物联 2021-2022 All rights reserved.
+ * +----------------------------------------------------------------------
+ * | Licensed 未经许可不能去掉「奇特物联」相关版权
+ * +----------------------------------------------------------------------
+ * | Author: xw2sy@163.com
+ * +----------------------------------------------------------------------
+ */
+package cc.iotkit.temporal.ts.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class TsVirtualDeviceLog {
+
+    private Date time;
+
+    private String virtualDeviceId;
+
+    private String virtualDeviceName;
+
+    private int deviceTotal;
+
+    private String result;
+
+}

+ 207 - 0
iot-data/iot-ts-temporal-service/src/main/java/cc/iotkit/temporal/ts/service/DbStructureDataImpl.java

@@ -0,0 +1,207 @@
+/*
+ * +----------------------------------------------------------------------
+ * | Copyright (c) 奇特物联 2021-2022 All rights reserved.
+ * +----------------------------------------------------------------------
+ * | Licensed 未经许可不能去掉「奇特物联」相关版权
+ * +----------------------------------------------------------------------
+ * | Author: xw2sy@163.com
+ * +----------------------------------------------------------------------
+ */
+package cc.iotkit.temporal.ts.service;
+
+import cc.iotkit.common.utils.JsonUtil;
+import cc.iotkit.model.product.ThingModel;
+import cc.iotkit.temporal.IDbStructureData;
+import cc.iotkit.temporal.ts.config.Constants;
+import cc.iotkit.temporal.ts.dao.TsTemplate;
+import cc.iotkit.temporal.ts.dm.DbField;
+import cc.iotkit.temporal.ts.dm.FieldParser;
+import cc.iotkit.temporal.ts.dm.TableManager;
+import cc.iotkit.temporal.ts.dm.TsField;
+import lombok.extern.slf4j.Slf4j;
+import org.jooq.CreateTableColumnStep;
+import org.jooq.DSLContext;
+import org.jooq.SQLDialect;
+import org.jooq.impl.DSL;
+import org.jooq.impl.SQLDataType;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.BeanPropertyRowMapper;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.PostConstruct;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Service
+public class DbStructureDataImpl implements IDbStructureData {
+
+    @Autowired
+    private TsTemplate tsTemplate;
+
+    /**
+     * 根据物模型创建超级表
+     */
+    @Override
+    public void defineThingModel(ThingModel thingModel) {
+        //获取物模型中的属性定义
+        List<TsField> fields = FieldParser.parse(thingModel);
+        String tbName = Constants.getProductPropertySTableName(thingModel.getProductKey());
+        String sql = TableManager.getCreateSTableSql(tbName,
+                fields);
+        if (sql == null) {
+            return;
+        }
+        System.out.println(sql);
+        tsTemplate.execute(sql);
+
+        createHypertable(tbName, "device_id");
+
+    }
+
+    private void createHypertable(String tbName, String partitionCol) {
+        String createSTableIndexSql = TableManager.getCreateSTableIndexSql(tbName, partitionCol);
+        try {
+            System.out.println(createSTableIndexSql);
+
+            tsTemplate.execute(createSTableIndexSql);
+        } catch (Exception e) {
+            log.info("createHypertable error:{}", e.getMessage());
+        }
+
+    }
+
+    /**
+     * 根据物模型更新超级表结构
+     */
+    @Override
+    public void updateThingModel(ThingModel thingModel) {
+        //获取旧字段信息
+        String tbName = Constants.getProductPropertySTableName(thingModel.getProductKey());
+        String sql = TableManager.getDescTableSql(tbName);
+        if (sql == null) {
+            return;
+        }
+
+        tsTemplate.execute(sql);
+
+        List<DbField> fieldsInDb = tsTemplate.query(sql, new BeanPropertyRowMapper<DbField>(DbField.class));
+
+        List<TsField> newFields = FieldParser.parse(thingModel);
+        List<TsField> oldFields = FieldParser.parse(fieldsInDb);
+
+        //对比差异
+
+        //找出修改的字段
+        List<TsField> modifyFields = newFields.stream().filter((f) -> oldFields.stream()
+                        .anyMatch(old ->
+                                old.getName().equals(f.getName()) //字段名相同
+                                        //字段类型或长度不同
+                                        && (!old.getType().equals(f.getType()) || old.getLength() != f.getLength())
+                        ))
+                .collect(Collectors.toList());
+        if (modifyFields.size() > 0) {
+            sql = TableManager.getModifySTableColumnSql(tbName, modifyFields);
+            log.info("modify column:{}", sql);
+
+            tsTemplate.execute(sql);
+
+        }
+
+        //找出新增的字段
+        List<TsField> addFields = newFields.stream().filter((f) -> oldFields.stream()
+                        .noneMatch(old -> old.getName().equals(f.getName())))
+                .collect(Collectors.toList());
+        if (addFields.size() > 0) {
+            sql = TableManager.getAddSTableColumnSql(tbName, addFields);
+            log.info("add column:{}", sql);
+
+            tsTemplate.execute(sql);
+        }
+
+
+        //找出删除的字段
+        List<TsField> dropFields = oldFields.stream().filter((f) ->
+                        !"time".equals(f.getName()) &&
+                                !"device_id".equals(f.getName()) && newFields.stream()
+                                //字段名不是time且没有相同字段名的
+                                .noneMatch(n -> n.getName().equals(f.getName())))
+                .collect(Collectors.toList());
+        if (dropFields.size() > 0) {
+
+            sql = TableManager.getDropSTableColumnSql(tbName, dropFields);
+            log.info("drop column:{}", sql);
+            tsTemplate.execute(sql);
+
+        }
+    }
+
+    /**
+     * 初始化其它数据结构
+     */
+    @Override
+    @PostConstruct
+    public void initDbStructure() {
+        //创建规则日志表
+        DSLContext dslBuilder = DSL.using(SQLDialect.POSTGRES);
+
+        CreateTableColumnStep ruleLogStep = dslBuilder.createTableIfNotExists("rule_log")
+                .column("time", SQLDataType.TIMESTAMPWITHTIMEZONE.nullable(false))
+                .column("state1", SQLDataType.VARCHAR(50))
+                .column("content", SQLDataType.VARCHAR(1024))
+                .column("success", SQLDataType.BOOLEAN)
+                .column("rule_id", SQLDataType.VARCHAR(50).nullable(false));
+        String sql = ruleLogStep.getSQL();
+        System.out.println(sql);
+        tsTemplate.execute(sql);
+        // 按时间和rule_id分区
+        createHypertable("rule_log", "rule_id");
+
+
+        CreateTableColumnStep taskLogStep = dslBuilder.createTableIfNotExists("task_log")
+                .column("time", SQLDataType.TIMESTAMPWITHTIMEZONE.nullable(false))
+                .column("content", SQLDataType.VARCHAR(1024))
+                .column("success", SQLDataType.BOOLEAN)
+                .column("task_id", SQLDataType.VARCHAR(50).nullable(false));
+        String taskLogsql = taskLogStep.getSQL();
+
+        System.out.println(taskLogsql);
+        tsTemplate.execute(taskLogsql);
+        // 按时间和task_id分区
+        createHypertable("task_log", "task_id");
+
+        CreateTableColumnStep thingModelStep = dslBuilder.createTableIfNotExists("thing_model_message")
+                .column("time", SQLDataType.TIMESTAMPWITHTIMEZONE.nullable(false))
+                .column("mid", SQLDataType.NVARCHAR(50))
+                .column("product_key", SQLDataType.NVARCHAR(50))
+                .column("device_name", SQLDataType.NVARCHAR(50))
+                .column("uid", SQLDataType.NVARCHAR(50))
+                .column("type", SQLDataType.NVARCHAR(20))
+                .column("identifier", SQLDataType.NVARCHAR(50))
+                .column("code", SQLDataType.INTEGER)
+                .column("data", SQLDataType.NVARCHAR(1024))
+                .column("report_time", SQLDataType.NVARCHAR(1024))
+                .column("device_id", SQLDataType.NVARCHAR(50));
+
+        String thingModelsql = thingModelStep.getSQL();
+
+        System.out.println(thingModelsql);
+        tsTemplate.execute(thingModelsql);
+        createHypertable("thing_model_message", "device_id");
+
+        //创建虚拟设备日志表
+        CreateTableColumnStep virtualStep = dslBuilder.createTableIfNotExists("virtual_device_log")
+                .column("time", SQLDataType.TIMESTAMPWITHTIMEZONE.nullable(false))
+                .column("virtual_device_name", SQLDataType.NVARCHAR(50))
+                .column("device_total", SQLDataType.INTEGER)
+                .column("result", SQLDataType.NVARCHAR(1024))
+                .column("virtual_device_id", SQLDataType.NVARCHAR(50));
+
+        String virtualsql = virtualStep.getSQL();
+        System.out.println(virtualsql);
+
+        tsTemplate.execute(virtualsql);
+        createHypertable("virtual_device_log", "virtual_device_id");
+
+    }
+}

+ 105 - 0
iot-data/iot-ts-temporal-service/src/main/java/cc/iotkit/temporal/ts/service/DevicePropertyDataImpl.java

@@ -0,0 +1,105 @@
+/*
+ * +----------------------------------------------------------------------
+ * | Copyright (c) 奇特物联 2021-2022 All rights reserved.
+ * +----------------------------------------------------------------------
+ * | Licensed 未经许可不能去掉「奇特物联」相关版权
+ * +----------------------------------------------------------------------
+ * | Author: xw2sy@163.com
+ * +----------------------------------------------------------------------
+ */
+package cc.iotkit.temporal.ts.service;
+
+import cc.iotkit.data.IDeviceInfoData;
+import cc.iotkit.model.device.DeviceInfo;
+import cc.iotkit.model.device.message.DeviceProperty;
+import cc.iotkit.temporal.IDevicePropertyData;
+import cc.iotkit.temporal.ts.config.Constants;
+import cc.iotkit.temporal.ts.dao.TsTemplate;
+import cc.iotkit.temporal.ts.model.TsDeviceProperty;
+import lombok.extern.slf4j.Slf4j;
+import org.jooq.Condition;
+import org.jooq.Field;
+import org.jooq.InsertValuesStepN;
+import org.jooq.Record;
+import org.jooq.conf.ParamType;
+import org.jooq.impl.DSL;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.jdbc.core.BeanPropertyRowMapper;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static org.jooq.impl.DSL.field;
+
+@Slf4j
+@Service
+public class DevicePropertyDataImpl implements IDevicePropertyData {
+
+    @Autowired
+    private TsTemplate tsTemplate;
+    @Autowired
+    @Qualifier("deviceInfoDataCache")
+    private IDeviceInfoData deviceInfoData;
+
+    @Override
+    public List<DeviceProperty> findDevicePropertyHistory(String deviceId, String name, long start, long end) {
+        DeviceInfo device = deviceInfoData.findByDeviceId(deviceId);
+
+        String tbName = Constants.getProductPropertySTableName(device.getProductKey());
+        Condition con = field("time").greaterOrEqual(new Date(start)).and(field("time").lessOrEqual(new Date(end)))
+                .and(DSL.field("device_id").eq(deviceId));
+        String sql = DSL.select(DSL.field("time"), DSL.field("device_id"), DSL.field(name.toLowerCase()).as("value"))
+                .from(tbName).where(con)
+                .getSQL(ParamType.INLINED);
+
+
+        List<TsDeviceProperty> list = tsTemplate.query(sql, new BeanPropertyRowMapper<>(TsDeviceProperty.class));
+
+
+        return list.stream().map(
+                o->{
+                    DeviceProperty deviceProperty = new DeviceProperty();
+                    BeanUtils.copyProperties(o,deviceProperty);
+                    deviceProperty.setTime(o.getTime().getTime());
+                    return deviceProperty;
+                }
+        ).collect(Collectors.toList());
+
+    }
+
+    @Override
+    public void addProperties(String deviceId, Map<String, Object> properties, long time) {
+        DeviceInfo device = deviceInfoData.findByDeviceId(deviceId);
+        if (device == null) {
+            return;
+        }
+        //获取设备旧属性
+        Map<String, Object> oldProperties = deviceInfoData.getProperties(deviceId);
+        //用新属性覆盖
+        oldProperties.putAll(properties);
+
+        List<Field<Object>> fields = new ArrayList<>();
+        List<Object> values = new ArrayList<>();
+
+        fields.add(DSL.field("time"));
+        fields.add(DSL.field("device_id"));
+        values.add(new Date(time));
+        values.add(deviceId);
+        //组织sql
+        oldProperties.forEach((key, val) -> {
+            fields.add(DSL.field(key));
+            values.add(val);
+        });
+        String tbName = Constants.getProductPropertySTableName(device.getProductKey());
+
+        //组织sql
+        InsertValuesStepN<Record> step = DSL.insertInto(DSL.table(tbName), (Collection<? extends Field<Object>>) fields).values(values);
+        String sql = step.getSQL(ParamType.INLINED);
+        tsTemplate.batchUpdate(sql);
+
+    }
+
+}

+ 80 - 0
iot-data/iot-ts-temporal-service/src/main/java/cc/iotkit/temporal/ts/service/RuleLogDataImpl.java

@@ -0,0 +1,80 @@
+/*
+ * +----------------------------------------------------------------------
+ * | Copyright (c) 奇特物联 2021-2022 All rights reserved.
+ * +----------------------------------------------------------------------
+ * | Licensed 未经许可不能去掉「奇特物联」相关版权
+ * +----------------------------------------------------------------------
+ * | Author: xw2sy@163.com
+ * +----------------------------------------------------------------------
+ */
+package cc.iotkit.temporal.ts.service;
+
+import cc.iotkit.model.Paging;
+import cc.iotkit.model.rule.RuleLog;
+import cc.iotkit.temporal.IRuleLogData;
+import cc.iotkit.temporal.ts.dao.TsTemplate;
+//import cc.iotkit.temporal.ts.dm.TableManager;
+import cc.iotkit.temporal.ts.dm.TableManager;
+import cc.iotkit.temporal.ts.model.TsRuleLog;
+import org.jooq.*;
+import org.jooq.conf.ParamType;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.BeanPropertyRowMapper;
+import org.springframework.stereotype.Service;
+
+import java.util.Date;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static org.jooq.impl.DSL.*;
+
+@Service
+public class RuleLogDataImpl implements IRuleLogData {
+
+
+    @Autowired
+    private TsTemplate tsTemplate;
+
+    @Override
+    public void deleteByRuleId(String ruleId) {
+
+        tsTemplate.update("delete from rule_log where rule_id=?", ruleId);
+    }
+
+    @Override
+    public Paging<RuleLog> findByRuleId(String ruleId, int page, int size) {
+
+        SelectForUpdateStep<Record5<Object, Object, Object, Object, Object>> sqlStep = TableManager.getSqlBuilder()
+                .select(field("time"), field("state1"), field("content"), field("success"),
+                        field("rule_id"))
+                .from(table("rule_log"))
+                .where(field("rule_id").eq(ruleId))
+                .orderBy(field("time").desc())
+                .limit(size)
+                .offset((page - 1) * size);
+        List<TsRuleLog> ruleLogs = tsTemplate.query(sqlStep.getSQL(ParamType.INLINED), new BeanPropertyRowMapper<>(TsRuleLog.class), ruleId);
+
+        SelectConditionStep<Record1<Integer>> where = TableManager.getSqlBuilder().selectCount().from(table("rule_log"))
+                .where(field("rule_id").eq(ruleId));
+        Long count = tsTemplate.queryForObject(where.getSQL(ParamType.INLINED), Long.class);
+
+        return new Paging<>(count, ruleLogs.stream().map(r ->
+                new RuleLog(r.getTime().toString(), ruleId, r.getState1(),
+                        r.getContent(), r.getSuccess(), r.getTime().getTime()))
+                .collect(Collectors.toList()));
+    }
+
+    @Override
+    public void add(RuleLog log) {
+        //使用
+
+        InsertValuesStep5<Record, Object, Object, Object, Object, Object> sqlStep = TableManager.getSqlBuilder().insertInto(table("rule_log"),
+                field("time"),
+                field("rule_id"),
+                field("state1"),
+                field("content"), field("success")).values(new Date(),
+                log.getRuleId(), log.getState(), log.getContent(), log.getSuccess());
+
+        tsTemplate.update(sqlStep.getSQL(ParamType.INLINED));
+    }
+}

+ 82 - 0
iot-data/iot-ts-temporal-service/src/main/java/cc/iotkit/temporal/ts/service/TaskLogDataImpl.java

@@ -0,0 +1,82 @@
+/*
+ * +----------------------------------------------------------------------
+ * | Copyright (c) 奇特物联 2021-2022 All rights reserved.
+ * +----------------------------------------------------------------------
+ * | Licensed 未经许可不能去掉「奇特物联」相关版权
+ * +----------------------------------------------------------------------
+ * | Author: xw2sy@163.com
+ * +----------------------------------------------------------------------
+ */
+package cc.iotkit.temporal.ts.service;
+
+import cc.iotkit.model.Paging;
+import cc.iotkit.model.rule.TaskLog;
+import cc.iotkit.temporal.ITaskLogData;
+import cc.iotkit.temporal.ts.dao.TsTemplate;
+//import cc.iotkit.temporal.ts.dm.TableManager;
+import cc.iotkit.temporal.ts.dm.TableManager;
+import cc.iotkit.temporal.ts.model.TsTaskLog;
+import cc.iotkit.temporal.ts.dao.TsTemplate;
+import org.jooq.*;
+import org.jooq.conf.ParamType;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.BeanPropertyRowMapper;
+import org.springframework.stereotype.Service;
+
+import java.util.Date;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static org.jooq.impl.DSL.field;
+import static org.jooq.impl.DSL.table;
+
+@Service
+public class TaskLogDataImpl implements ITaskLogData {
+
+    @Autowired
+    private TsTemplate tsTemplate;
+
+    @Override
+    public void deleteByTaskId(String taskId) {
+        tsTemplate.update("delete from task_log where task_id=?", taskId);
+    }
+
+    @Override
+    public Paging<TaskLog> findByTaskId(String taskId, int page, int size) {
+        SelectForUpdateStep<Record4<Object, Object, Object, Object>> sqlStep = TableManager.getSqlBuilder()
+                .select(field("time"), field("content"), field("success"), field("task_id"))
+                .from(table("task_log"))
+                .where(field("task_id").eq(taskId))
+                .orderBy(field("time").desc())
+                .limit(size)
+                .offset((page - 1) * size);
+
+        // Get the SQL string from the query
+        String sql = sqlStep.getSQL(ParamType.INLINED);
+        List<TsTaskLog> taskLogs = tsTemplate.query(sql, new BeanPropertyRowMapper<>(TsTaskLog.class));
+
+        String whereSql = TableManager.getSqlBuilder().selectCount().from(table("task_log"))
+                .where(field("task_id").eq(taskId)).getSQL(ParamType.INLINED);
+        Long count = tsTemplate.queryForObject(whereSql, new BeanPropertyRowMapper<>(Long.class));
+
+        return new Paging<>(count , taskLogs.stream().map(r ->
+                new TaskLog(r.getTime().toString(), taskId,
+                        r.getContent(), r.getSuccess(), r.getTime()))
+                .collect(Collectors.toList()));
+    }
+
+    @Override
+    public void add(TaskLog log) {
+
+        InsertValuesStep4<Record, Object, Object, Object, Object> sqlStep
+                = TableManager.getSqlBuilder().insertInto(table("tag_log"),
+                field("time"),
+                field("task_id"),
+                field("content"), field("success")).values(
+                        new Date(),
+                log.getTaskId(),
+                log.getContent(), log.getSuccess());
+
+        tsTemplate.update(sqlStep.getSQL(ParamType.INLINED));
+    }
+}

+ 134 - 0
iot-data/iot-ts-temporal-service/src/main/java/cc/iotkit/temporal/ts/service/ThingModelMessageDataImpl.java

@@ -0,0 +1,134 @@
+/*
+ * +----------------------------------------------------------------------
+ * | Copyright (c) 奇特物联 2021-2022 All rights reserved.
+ * +----------------------------------------------------------------------
+ * | Licensed 未经许可不能去掉「奇特物联」相关版权
+ * +----------------------------------------------------------------------
+ * | Author: xw2sy@163.com
+ * +----------------------------------------------------------------------
+ */
+package cc.iotkit.temporal.ts.service;
+
+import cc.iotkit.common.utils.JsonUtil;
+import cc.iotkit.model.Paging;
+import cc.iotkit.model.device.message.ThingModelMessage;
+import cc.iotkit.model.stats.TimeData;
+import cc.iotkit.temporal.IThingModelMessageData;
+import cc.iotkit.temporal.ts.dao.TsTemplate;
+import cc.iotkit.temporal.ts.dm.TableManager;
+import cc.iotkit.temporal.ts.model.TsThingModelMessage;
+import cc.iotkit.temporal.ts.model.TsTimeData;
+import org.apache.commons.lang3.StringUtils;
+import org.jooq.*;
+import org.jooq.conf.ParamType;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.BeanPropertyRowMapper;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import static org.jooq.impl.DSL.field;
+import static org.jooq.impl.DSL.table;
+
+@Service
+public class ThingModelMessageDataImpl implements IThingModelMessageData {
+
+    @Autowired
+    private TsTemplate tsTemplate;
+
+    public Paging<ThingModelMessage> findByTypeAndIdentifier(String deviceId, String type,
+                                                             String identifier,
+                                                             int page, int size) {
+
+
+        Table<Record> table = table("thing_model_message");
+        Condition whereConditions = field("device_id").eq(deviceId);
+        SelectJoinStep<Record9<Object, Object, Object, Object, Object, Object, Object, Object, Object>> step = TableManager.getSqlBuilder().select(field("time"), field("mid"),
+                field("product_key"), field("device_name"), field("type"),
+                field("identifier"), field("code"), field("data"),
+                field("report_time")).from(table);
+
+
+        if (StringUtils.isNotBlank(type)) {
+            whereConditions.and(field("type").eq(type));
+        }
+        if (StringUtils.isNotBlank(identifier)) {
+            whereConditions.and(field("identifier").eq(identifier));
+        }
+
+        String sql = step.where(whereConditions).orderBy(field("time").desc()).limit(size).offset((page - 1) * size).getSQL(ParamType.INLINED);
+
+        List<TsThingModelMessage> ruleLogs = tsTemplate.query(sql,
+                new BeanPropertyRowMapper<>(TsThingModelMessage.class)
+        );
+
+        String countSql = TableManager.getSqlBuilder().selectCount().from(table).where(whereConditions).getSQL(ParamType.INLINED);
+        Long count = tsTemplate.queryForObject(countSql, Long.class);
+
+        return new Paging<>(count, ruleLogs.stream().map(r ->
+                        new ThingModelMessage(r.getTime().toString(), r.getMid(),
+                                deviceId, r.getProductKey(), r.getDeviceName(),
+                                r.getUid(), r.getType(), r.getIdentifier(), r.getCode(),
+                                r.getData(),
+                                r.getTime().getTime(), r.getReportTime()))
+                .collect(Collectors.toList()));
+    }
+
+    @Override
+    public List<TimeData> getDeviceMessageStatsWithUid(String uid, long start, long end) {
+
+        Table<Record> table = table("thing_model_message");
+
+        Condition con = field("time").greaterOrEqual(new Date(start)).and(field("time").lessOrEqual(new Date(end)));
+        if(StringUtils.isNotBlank(uid)){
+            con.and(field("uid").eq(uid));
+        }
+
+        String sql = TableManager.getSqlBuilder().select(field("date_trunc('hour', \"time\")").as("time"),field("count(*)").as("data"))
+                .from(table).where(con).groupBy(field("date_trunc('hour', \"time\")")).orderBy(field("time").asc()).getSQL(ParamType.INLINED);
+
+
+        List<TsTimeData> query = tsTemplate.query(sql, new BeanPropertyRowMapper<>(TsTimeData.class));
+        return query.stream().map(o -> {
+            TimeData timeData = new TimeData();
+            timeData.setData(o.getData());
+            timeData.setTime(o.getTime().getTime());
+            return timeData;
+
+        }).collect(Collectors.toList());
+    }
+
+    @Override
+    public void add(ThingModelMessage msg) {
+        Table<Record> table = table("thing_model_message");
+
+        String sql = TableManager.getSqlBuilder().insertInto(table,
+                        field("time"),
+                        field("device_id"),
+                        field("mid"),
+                        field("product_key"),
+                        field("device_name"),
+                        field("uid"),
+                        field("type"),
+                        field("identifier"),
+                        field("code"),
+                        field("data"), field("report_time"))
+                .values(new Date(msg.getOccurred()), msg.getDeviceId(), msg.getMid(),
+                        msg.getProductKey(), msg.getDeviceName(),
+                        msg.getUid(), msg.getType(),
+                        msg.getIdentifier(), msg.getCode(),
+                        msg.getData() == null ? "{}" : JsonUtil.toJsonString(msg.getData()),
+                        msg.getTime()).getSQL(ParamType.INLINED);
+        tsTemplate.update(sql);
+    }
+
+    @Override
+    public long count() {
+        List<Long> counts = tsTemplate.queryForList("select count(*) from thing_model_message", Long.class);
+        return counts.size() > 0 ? counts.get(0) : 0;
+    }
+}

+ 74 - 0
iot-data/iot-ts-temporal-service/src/main/java/cc/iotkit/temporal/ts/service/VirtualDeviceLogDataImpl.java

@@ -0,0 +1,74 @@
+/*
+ * +----------------------------------------------------------------------
+ * | Copyright (c) 奇特物联 2021-2022 All rights reserved.
+ * +----------------------------------------------------------------------
+ * | Licensed 未经许可不能去掉「奇特物联」相关版权
+ * +----------------------------------------------------------------------
+ * | Author: xw2sy@163.com
+ * +----------------------------------------------------------------------
+ */
+package cc.iotkit.temporal.ts.service;
+
+import cc.iotkit.model.Paging;
+import cc.iotkit.model.device.VirtualDeviceLog;
+import cc.iotkit.temporal.IVirtualDeviceLogData;
+import cc.iotkit.temporal.ts.dao.TsTemplate;
+import cc.iotkit.temporal.ts.dm.TableManager;
+import cc.iotkit.temporal.ts.model.TsVirtualDeviceLog;
+import org.jooq.*;
+import org.jooq.conf.ParamType;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.BeanPropertyRowMapper;
+import org.springframework.stereotype.Service;
+
+import java.util.Date;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static org.jooq.impl.DSL.field;
+import static org.jooq.impl.DSL.table;
+
+@Service
+public class VirtualDeviceLogDataImpl implements IVirtualDeviceLogData {
+
+    @Autowired
+    private TsTemplate tsTemplate;
+
+    @Override
+    public Paging<VirtualDeviceLog> findByVirtualDeviceId(String virtualDeviceId, int page, int size) {
+
+        Table<Record> table = table("virtual_device_log");
+
+        Condition whereConditions = field("virtual_device_id").eq(virtualDeviceId.toLowerCase());
+        DSLContext sqlBuilder = TableManager.getSqlBuilder();
+        String sql = sqlBuilder.select(field("time"), field("virtual_device_id"),
+                        field("virtual_device_name"), field("device_total"), field("result")).from(table).where(whereConditions)
+                .orderBy(field("time").desc()).limit(size).offset((page - 1) * size).getSQL(ParamType.INLINED);
+
+        List<TsVirtualDeviceLog> logs = tsTemplate.query(sql, new BeanPropertyRowMapper<>(TsVirtualDeviceLog.class));
+
+        String countSql = sqlBuilder.selectCount().from(table).where(whereConditions).getSQL(ParamType.INLINED);
+
+        Long count = tsTemplate.queryForObject(countSql, Long.class);
+
+        return new Paging<>(count, logs.stream().map(r ->
+                new VirtualDeviceLog(r.getTime().toString(), virtualDeviceId,
+                        r.getVirtualDeviceName(),
+                        r.getDeviceTotal(), r.getResult(),
+                        r.getTime().getTime()))
+                .collect(Collectors.toList()));
+    }
+
+    @Override
+    public void add(VirtualDeviceLog log) {
+        Table<Record> table = table("virtual_device_log");
+
+        String sql = TableManager.getSqlBuilder().insertInto(table, field("time"), field("virtual_device_id"),
+                        field("virtual_device_name"),
+                        field("device_total"), field("result"))
+                .values(new Date(), log.getVirtualDeviceId(), log.getVirtualDeviceName(),
+                        log.getDeviceTotal(), log.getResult()).getSQL(ParamType.INLINED);
+
+        tsTemplate.update(sql);
+    }
+}

+ 1 - 0
iot-data/pom.xml

@@ -18,6 +18,7 @@
         <module>iot-es-temporal-service</module>
         <module>iot-rdb-data-service</module>
         <module>iot-td-temporal-service</module>
+        <module>iot-ts-temporal-service</module>
     </modules>
 
     <artifactId>iot-data</artifactId>

+ 3 - 1
iot-rule-engine/src/main/java/cc/iotkit/ruleengine/action/AlertService.java

@@ -11,6 +11,7 @@ package cc.iotkit.ruleengine.action;
 
 import cc.iotkit.model.device.message.ThingModelMessage;
 import cc.iotkit.ruleengine.alert.Alerter;
+import com.fasterxml.jackson.core.type.TypeReference;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.SneakyThrows;
@@ -29,7 +30,8 @@ public class AlertService<T extends Alerter> extends ScriptService {
     @SneakyThrows
     public String execute(ThingModelMessage msg) {
         //执行转换脚本
-        Map result = execScript(msg);
+        Map<String, Object> result = execScript(new TypeReference<>() {
+        }, msg);
         if (result == null) {
             log.warn("execScript result is null");
             return "execScript result is null";

+ 2 - 1
iot-rule-engine/src/main/java/cc/iotkit/ruleengine/action/DeviceActionService.java

@@ -51,7 +51,8 @@ public class DeviceActionService {
 
         public String getType() {
             //identifier为set固定为属性设置,其它为服务调用
-            if (ThingModelMessage.ID_PROPERTY_SET.equals(identifier)) {
+            if (ThingModelMessage.ID_PROPERTY_SET.equals(identifier) ||
+                    ThingModelMessage.ID_PROPERTY_GET.equals(identifier)) {
                 return ThingModelMessage.TYPE_PROPERTY;
             }
             return ThingModelMessage.TYPE_SERVICE;

+ 5 - 6
iot-rule-engine/src/main/java/cc/iotkit/ruleengine/action/HttpService.java

@@ -11,6 +11,7 @@ package cc.iotkit.ruleengine.action;
 
 import cc.iotkit.common.utils.JsonUtil;
 import cc.iotkit.model.device.message.ThingModelMessage;
+import com.fasterxml.jackson.core.type.TypeReference;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.SneakyThrows;
@@ -32,15 +33,13 @@ public class HttpService extends ScriptService {
     @SneakyThrows
     public String execute(ThingModelMessage msg) {
         //执行转换脚本
-        Map result = execScript(msg);
-        if (result == null) {
+        HttpData httpData = execScript(new TypeReference<>() {
+        }, msg);
+        if (httpData == null) {
             log.warn("execScript result is null");
             return "execScript result is null";
         }
 
-        HttpData httpData = new HttpData();
-        BeanUtils.populate(httpData, result);
-
         //组装http请求
         String url = this.url + httpData.getPath();
         Request.Builder builder = new Request.Builder();
@@ -57,7 +56,7 @@ public class HttpService extends ScriptService {
                 httpData.getBody().toString());
 
         Request request = builder.method(httpData.getMethod().toUpperCase(), requestBody).build();
-        String requestDataStr = JsonUtil.toJsonString(result);
+        String requestDataStr = JsonUtil.toJsonString(httpData);
         log.info("send http request:{} ,{}", url, requestDataStr);
 
         String responseBody = "";

+ 6 - 5
iot-rule-engine/src/main/java/cc/iotkit/ruleengine/action/ScriptService.java

@@ -18,7 +18,6 @@ import com.fasterxml.jackson.core.type.TypeReference;
 import lombok.Data;
 import lombok.extern.slf4j.Slf4j;
 
-import java.util.Map;
 
 @Slf4j
 @Data
@@ -30,15 +29,17 @@ public class ScriptService {
 
     private IDeviceInfoData deviceInfoData;
 
-    public Map execScript(ThingModelMessage msg) {
+    public void setScript(String script) {
+        scriptEngine.setScript(script);
+    }
+
+    public <T> T execScript(TypeReference<T> type, ThingModelMessage msg) {
         try {
-            scriptEngine.setScript(script);
             //取设备信息
             DeviceInfo deviceInfo = deviceInfoData.findByDeviceId(msg.getDeviceId());
 
             //执行转换脚本
-            return scriptEngine.invokeMethod(new TypeReference<>() {
-            }, "translate", msg, deviceInfo);
+            return scriptEngine.invokeMethod(type, "translate", msg, deviceInfo);
         } catch (Throwable e) {
             log.error("run script error", e);
             return null;

+ 3 - 1
iot-rule-engine/src/main/java/cc/iotkit/ruleengine/action/kafka/KafkaService.java

@@ -6,6 +6,7 @@ import cc.iotkit.ruleengine.action.ScriptService;
 import cc.iotkit.ruleengine.link.LinkFactory;
 import cc.iotkit.ruleengine.link.LinkService;
 import cc.iotkit.ruleengine.link.impl.KafkaLink;
+import com.fasterxml.jackson.core.type.TypeReference;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.extern.slf4j.Slf4j;
@@ -28,7 +29,8 @@ public class KafkaService extends ScriptService implements LinkService {
 
     public String execute(ThingModelMessage msg) {
         //执行转换脚本
-        Map result = execScript(msg);
+        Map<String, Object> result = execScript(new TypeReference<>() {
+        }, msg);
         if (result == null) {
             log.warn("execScript result is null");
             return "execScript result is null";

+ 3 - 1
iot-rule-engine/src/main/java/cc/iotkit/ruleengine/action/mqtt/MqttService.java

@@ -6,6 +6,7 @@ import cc.iotkit.ruleengine.action.ScriptService;
 import cc.iotkit.ruleengine.link.LinkFactory;
 import cc.iotkit.ruleengine.link.LinkService;
 import cc.iotkit.ruleengine.link.impl.MqttClientLink;
+import com.fasterxml.jackson.core.type.TypeReference;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.extern.slf4j.Slf4j;
@@ -31,7 +32,8 @@ public class MqttService extends ScriptService implements LinkService {
 
     public String execute(ThingModelMessage msg) {
         //执行转换脚本
-        Map result = execScript(msg);
+        Map<String, Object> result = execScript(new TypeReference<>() {
+        }, msg);
         if (result == null) {
             log.warn("execScript result is null");
             return "execScript result is null";

+ 3 - 1
iot-rule-engine/src/main/java/cc/iotkit/ruleengine/action/tcp/TcpService.java

@@ -6,6 +6,7 @@ import cc.iotkit.ruleengine.action.ScriptService;
 import cc.iotkit.ruleengine.link.LinkFactory;
 import cc.iotkit.ruleengine.link.LinkService;
 import cc.iotkit.ruleengine.link.impl.TcpClientLink;
+import com.fasterxml.jackson.core.type.TypeReference;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.extern.slf4j.Slf4j;
@@ -27,7 +28,8 @@ public class TcpService extends ScriptService implements LinkService {
 
     public String execute(ThingModelMessage msg) {
         //执行转换脚本
-        Map result = execScript(msg);
+        Map<String, Object> result = execScript(new TypeReference<>() {
+        },msg);
         if (result == null) {
             log.warn("execScript result is null");
             return "execScript result is null";

+ 10 - 3
iot-script-engine/pom.xml

@@ -23,23 +23,25 @@
             <groupId>org.graalvm.sdk</groupId>
             <artifactId>graal-sdk</artifactId>
         </dependency>
+
         <dependency>
             <groupId>org.graalvm.js</groupId>
             <artifactId>js</artifactId>
         </dependency>
+
         <dependency>
             <groupId>org.graalvm.js</groupId>
             <artifactId>js-scriptengine</artifactId>
-
         </dependency>
+
         <dependency>
             <groupId>org.apache.commons</groupId>
             <artifactId>commons-lang3</artifactId>
         </dependency>
 
         <dependency>
-            <groupId>commons-beanutils</groupId>
-            <artifactId>commons-beanutils</artifactId>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
         </dependency>
 
         <dependency>
@@ -47,6 +49,11 @@
             <artifactId>lombok</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>commons-beanutils</groupId>
+            <artifactId>commons-beanutils</artifactId>
+        </dependency>
+
         <dependency>
             <groupId>cc.iotkit</groupId>
             <artifactId>iot-common</artifactId>

+ 19 - 10
iot-script-engine/src/main/java/cc/iotkit/script/JavaScriptEngine.java

@@ -1,13 +1,22 @@
+/*
+ * +----------------------------------------------------------------------
+ * | Copyright (c) 奇特物联 2021-2022 All rights reserved.
+ * +----------------------------------------------------------------------
+ * | Licensed 未经许可不能去掉「奇特物联」相关版权
+ * +----------------------------------------------------------------------
+ * | Author: xw2sy@163.com
+ * +----------------------------------------------------------------------
+ */
 package cc.iotkit.script;
 
 import cc.iotkit.common.utils.JsonUtil;
 import com.fasterxml.jackson.core.type.TypeReference;
+import lombok.extern.slf4j.Slf4j;
 import org.graalvm.polyglot.Context;
 import org.graalvm.polyglot.HostAccess;
 import org.graalvm.polyglot.Value;
 
-import java.util.Objects;
-
+@Slf4j
 public class JavaScriptEngine implements IScriptEngine {
 
     private final Context context = Context.newBuilder("js").allowHostAccess(HostAccess.ALL).build();
@@ -42,22 +51,22 @@ public class JavaScriptEngine implements IScriptEngine {
     public <T> T invokeMethod(TypeReference<T> type, String methodName, Object... args) {
         Value member = jsScript.getMember("invoke");
 
-        if (Objects.isNull(member)) {
-            return null;
-        }
-
+        StringBuilder sbArgs = new StringBuilder("[");
+        //将入参转成json
         for (int i = 0; i < args.length; i++) {
             args[i] = JsonUtil.toJsonString(args[i]);
+            sbArgs.append(i == args.length - 1 ? "," : "").append(args[i]);
         }
+        sbArgs.append("]");
 
         //通过调用invoke方法将目标方法返回结果转成json
         Value rst = member.execute(methodName, args);
-        if (rst == null) {
-            return null;
-        }
 
         String json = rst.asString();
-        if (json == null) {
+        log.info("invoke script {},args:{}, result:{}", methodName, sbArgs, json);
+
+        //没有返回值
+        if (json == null || "null".equals(json)) {
             return null;
         }
 

+ 6 - 0
iot-standalone/pom.xml

@@ -164,6 +164,12 @@
             <artifactId>iot-es-temporal-service</artifactId>
         </dependency>
 
+        <!--打开注释 启用timescale数据库-->
+<!--        <dependency>-->
+<!--            <groupId>cc.iotkit</groupId>-->
+<!--            <artifactId>iot-ts-temporal-service</artifactId>-->
+<!--        </dependency>-->
+
         <!--打开注释 启用tdengine数据库-->
         <!--        <dependency>-->
         <!--            <groupId>cc.iotkit</groupId>-->

+ 4 - 0
iot-standalone/src/main/java/cc/iotkit/manager/config/SaTokenConfigure.java

@@ -38,11 +38,15 @@ public class SaTokenConfigure implements WebMvcConfigurer {
                                 "/space/delSpace/**",
                                 "/space/saveHome/**",
                                 "/space/currentHome/**",
+                                "/space/changCurrentHome/**",
+                                "/space/getUserHomes/**",
                                 "/space/myRecentDevices/**",
                                 "/space/spaces/**",
                                 "/space/myDevices/**",
                                 "/space/findDevice/**",
                                 "/space/addDevice/**",
+                                "/space/collectDevice/**",
+                                "/space/getCollectDevices/**",
                                 "/space/saveDevice",
                                 "/space/removeDevice",
                                 "/space/setOpenUid",

+ 8 - 0
iot-standalone/src/main/java/cc/iotkit/manager/controller/DeviceController.java

@@ -88,6 +88,14 @@ public class DeviceController {
         return new InvokeResult(deviceService.invokeService(deviceId, service, args));
     }
 
+    @PostMapping(Constants.API_DEVICE.INVOKE_SERVICE_PROPERTY_GET)
+    public InvokeResult invokeServicePropertySet(@PathVariable("deviceId") String deviceId,
+                                      @RequestBody List<String> propertyNames) {
+        if (StringUtils.isBlank(deviceId)) {
+            throw new RuntimeException("deviceId/service is blank.");
+        }
+        return new InvokeResult(deviceService.getProperty(deviceId, propertyNames, true));
+    }
     @PostMapping(Constants.API_DEVICE.SET_PROPERTIES)
     public InvokeResult setProperty(@PathVariable("deviceId") String deviceId,
                                     @RequestBody Map<String, Object> args) {

+ 21 - 0
iot-standalone/src/main/java/cc/iotkit/manager/controller/SpaceController.java

@@ -42,6 +42,27 @@ public class SpaceController {
         return homeData.findByUidAndCurrent(AuthUtil.getUserId(), true);
     }
 
+    /**
+     * 取用户所有家庭
+     */
+    @GetMapping("/getUserHomes")
+    public List<Home> getUserHomes() {
+        return homeData.findByUid(AuthUtil.getUserId());
+    }
+
+    /**
+     * 切换用户当前家庭
+     */
+    @PostMapping("/changCurrentHome")
+    public void changCurrentHome(Home home) {
+        Home oldHome=homeData.findByUidAndCurrent(AuthUtil.getUserId(), true);
+        oldHome.setCurrent(false);
+        homeData.save(oldHome);
+        Home newHome=homeData.findById(home.getId());
+        newHome.setCurrent(true);
+        homeData.save(newHome);
+    }
+
     /**
      * 保存家庭信息
      */

+ 26 - 1
iot-standalone/src/main/java/cc/iotkit/manager/controller/SpaceDeviceController.java

@@ -15,6 +15,7 @@ import cc.iotkit.data.*;
 import cc.iotkit.manager.model.vo.FindDeviceVo;
 import cc.iotkit.manager.model.vo.SpaceDeviceVo;
 import cc.iotkit.manager.service.DataOwnerService;
+import cc.iotkit.model.space.Home;
 import cc.iotkit.utils.AuthUtil;
 import cc.iotkit.model.UserInfo;
 import cc.iotkit.model.device.DeviceInfo;
@@ -52,6 +53,8 @@ public class SpaceDeviceController {
     @Qualifier("spaceDataCache")
     private ISpaceData spaceData;
     @Autowired
+    private IHomeData homeData;
+    @Autowired
     private DataOwnerService dataOwnerService;
     @Autowired
     private IUserInfoData userInfoData;
@@ -65,6 +68,27 @@ public class SpaceDeviceController {
         return spaceDevices.stream().map((this::parseSpaceDevice)).collect(Collectors.toList());
     }
 
+    /**
+     * 获取用户收藏设备列表
+     */
+    @GetMapping(Constants.API_SPACE.GET_COLLECT_DEVICES)
+    public List<SpaceDeviceVo> getCollectDevices() {
+        Home home=homeData.findByUidAndCurrent(AuthUtil.getUserId(), true);
+        List<SpaceDevice> spaceDevices = spaceDeviceData.findByHomeIdAndCollect(home.getId(),true);
+        return spaceDevices.stream().map((this::parseSpaceDevice)).collect(Collectors.toList());
+    }
+
+    /**
+     *
+     * 收藏/取消收藏设备
+     */
+    @PostMapping(Constants.API_SPACE.COLLECT_DEVICE)
+    public void collectDevice(SpaceDevice spaceDevice) {
+        SpaceDevice oldSpaceDevice=spaceDeviceData.findByDeviceId(spaceDevice.getDeviceId());
+        oldSpaceDevice.setCollect(spaceDevice.getCollect());
+        spaceDeviceData.save(oldSpaceDevice);
+    }
+
     /**
      * 我的空间设备列表-按空间获取
      *
@@ -107,6 +131,7 @@ public class SpaceDeviceController {
                 .online(state != null && state.isOnline())
                 .property(device.getProperty())
                 .uid(sd.getUid())
+                .collect(sd.getCollect())
                 .build();
     }
 
@@ -173,7 +198,7 @@ public class SpaceDeviceController {
         return findDeviceVo;
     }
 
-    /**
+    /**REMOVE_DEVICE
      * 往指定房间中添加设备
      */
     @PostMapping(Constants.API_SPACE.ADD_DEVICE)

+ 5 - 0
iot-standalone/src/main/java/cc/iotkit/manager/model/vo/SpaceDeviceVo.java

@@ -89,4 +89,9 @@ public class SpaceDeviceVo {
      * 品类名
      */
     private String categoryName;
+
+    /**
+     * 是否收藏
+     */
+    private Boolean collect;
 }

+ 12 - 0
iot-standalone/src/main/java/cc/iotkit/manager/service/DeviceService.java

@@ -27,6 +27,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.stereotype.Service;
 
+import java.util.List;
 import java.util.Map;
 
 @Slf4j
@@ -68,6 +69,17 @@ public class DeviceService {
                 args, ThingModelMessage.TYPE_SERVICE, service);
     }
 
+    /**
+     * 设备属性获取
+     */
+    public String getProperty(String deviceId, List<String> properties,
+                              boolean checkOwner) {
+        DeviceInfo device = getAndCheckDevice(deviceId, checkOwner);
+
+        return send(deviceId, device.getProductKey(), device.getDeviceName(), properties,
+                ThingModelMessage.TYPE_PROPERTY, ThingModelMessage.ID_PROPERTY_GET);
+    }
+
     /**
      * 设备属性设置
      */

+ 7 - 1
iot-standalone/src/main/java/cc/iotkit/manager/service/ThingModelService.java

@@ -11,6 +11,7 @@ package cc.iotkit.manager.service;
 
 import cc.iotkit.common.thing.ThingService;
 import cc.iotkit.data.IThingModelData;
+import cc.iotkit.model.device.message.ThingModelMessage;
 import cc.iotkit.model.product.ThingModel;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
@@ -37,7 +38,12 @@ public class ThingModelService {
             if (properties == null) {
                 return;
             }
-            params = parseProperties(properties, (Map<?, ?>) service.getParams());
+            if(identifier.equals(ThingModelMessage.ID_PROPERTY_GET)){
+                params = service.getParams();
+            }
+            else {
+                params = parseProperties(properties, (Map<?, ?>) service.getParams());
+            }
         } else if (ThingService.TYPE_SERVICE.equals(type)) {
             //服务调用
             Map<String, ThingModel.Service> services = model.serviceMap();

+ 5 - 0
pom.xml

@@ -335,6 +335,11 @@
                 <artifactId>iot-td-temporal-service</artifactId>
                 <version>${project.version}</version>
             </dependency>
+            <dependency>
+                <groupId>cc.iotkit</groupId>
+                <artifactId>iot-ts-temporal-service</artifactId>
+                <version>${project.version}</version>
+            </dependency>
 
             <dependency>
                 <groupId>cc.iotkit</groupId>