From 63a905c01f020b5b6ea9599100f6cce74ee6ec14 Mon Sep 17 00:00:00 2001
From: jay <75509151@qq.com>
Date: Sat, 25 Feb 2023 21:59:10 +0800
Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20covert=E8=84=9A=E6=9C=AC?=
=?UTF-8?q?=20js=E8=A7=A3=E6=9E=90?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../iot-component-converter/pom.xml | 16 ++
.../java/cc/iotkit/converter/CovertUtils.java | 47 +++++
.../converter/GraalJsScriptConverter.java | 77 ++++++++
.../src/main/resources/graaljsconverter.js | 173 ++++++++++++++++++
pom.xml | 19 ++
5 files changed, 332 insertions(+)
create mode 100644 iot-components/iot-component-converter/src/main/java/cc/iotkit/converter/CovertUtils.java
create mode 100644 iot-components/iot-component-converter/src/main/java/cc/iotkit/converter/GraalJsScriptConverter.java
create mode 100644 iot-components/iot-mqtt-component/src/main/resources/graaljsconverter.js
diff --git a/iot-components/iot-component-converter/pom.xml b/iot-components/iot-component-converter/pom.xml
index e0ef658e..fe14368c 100755
--- a/iot-components/iot-component-converter/pom.xml
+++ b/iot-components/iot-component-converter/pom.xml
@@ -33,6 +33,22 @@
iot-model
+
+
+ org.graalvm.sdk
+ graal-sdk
+
+
+
+ org.graalvm.js
+ js
+
+
+ org.graalvm.js
+ js-scriptengine
+
+
+
\ No newline at end of file
diff --git a/iot-components/iot-component-converter/src/main/java/cc/iotkit/converter/CovertUtils.java b/iot-components/iot-component-converter/src/main/java/cc/iotkit/converter/CovertUtils.java
new file mode 100644
index 00000000..542fae43
--- /dev/null
+++ b/iot-components/iot-component-converter/src/main/java/cc/iotkit/converter/CovertUtils.java
@@ -0,0 +1,47 @@
+package cc.iotkit.converter;
+
+import org.graalvm.polyglot.Value;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class CovertUtils {
+
+ private static final Map, Map> FIELD_CACHE = new ConcurrentHashMap<>();
+ private static final Map, Map> SETTER_CACHE = new ConcurrentHashMap<>();
+
+ public static void copyProperties(Object javaObj, Value jsObj) {
+ Map fieldMap = FIELD_CACHE.computeIfAbsent(javaObj.getClass(), clazz -> {
+ Map fields = new ConcurrentHashMap<>();
+ for (Field field : clazz.getDeclaredFields()) {
+ fields.put(field.getName(), field);
+ }
+ return fields;
+ });
+ Map setterMap = SETTER_CACHE.computeIfAbsent(javaObj.getClass(), clazz -> {
+ Map setters = new ConcurrentHashMap<>();
+ for (Method method : clazz.getDeclaredMethods()) {
+ if (method.getName().startsWith("set") && method.getParameterCount() == 1) {
+ String propName = method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4);
+ setters.put(propName, method);
+ }
+ }
+ return setters;
+ });
+ for (String propName : jsObj.getMemberKeys()) {
+ try {
+ Field field = fieldMap.get(propName);
+ Method setter = setterMap.get(propName);
+ if (field != null && setter != null) {
+ Class> propType = field.getType();
+ Object propValue = jsObj.getMember(propName).as(propType);
+ setter.invoke(javaObj, propValue);
+ }
+ } catch (Exception e) {
+ // ignore errors
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/iot-components/iot-component-converter/src/main/java/cc/iotkit/converter/GraalJsScriptConverter.java b/iot-components/iot-component-converter/src/main/java/cc/iotkit/converter/GraalJsScriptConverter.java
new file mode 100644
index 00000000..07f35e2a
--- /dev/null
+++ b/iot-components/iot-component-converter/src/main/java/cc/iotkit/converter/GraalJsScriptConverter.java
@@ -0,0 +1,77 @@
+/*
+ * +----------------------------------------------------------------------
+ * | Copyright (c) 奇特物联 2021-2022 All rights reserved.
+ * +----------------------------------------------------------------------
+ * | Licensed 未经许可不能去掉「奇特物联」相关版权
+ * +----------------------------------------------------------------------
+ * | Author: xw2sy@163.com
+ * +----------------------------------------------------------------------
+ */
+package cc.iotkit.converter;
+
+import cc.iotkit.common.thing.ThingService;
+import cc.iotkit.common.utils.JsonUtil;
+import cc.iotkit.model.device.message.ThingModelMessage;
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.beanutils.BeanUtils;
+
+import javax.script.Invocable;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+import javax.script.ScriptException;
+import java.util.Map;
+import org.graalvm.polyglot.*;
+
+@Slf4j
+@Data
+public class GraalJsScriptConverter implements IConverter {
+
+
+ private final Context context = Context.newBuilder("js").allowHostAccess(true).build();
+
+ private Object scriptObj;
+
+ private Value decoder;
+ private Value encoder;
+
+ public void setScript(String script) {
+
+ Value myFunctions = context.eval("js",String.format("new (function () {\n%s})()", script));
+ // 调用JavaScript函数
+ decoder = myFunctions.getMember("decode");
+ encoder = myFunctions.getMember("encode");
+
+ }
+
+ public ThingModelMessage decode(DeviceMessage msg) {
+ try {
+// String msgJson = JsonUtil.toJsonString(msg);
+ Value rst = decoder.execute(msg);
+ ThingModelMessage modelMessage = new ThingModelMessage();
+ CovertUtils.copyProperties(modelMessage, rst);
+ return modelMessage;
+ } catch (Throwable e) {
+ log.error("execute decode script error", e);
+ }
+ return null;
+ }
+
+
+ @Override
+ public DeviceMessage encode(ThingService> service, Device device) {
+ try {
+ Value rst = encoder.execute(service,device);
+ DeviceMessage modelMessage = rst.as(DeviceMessage.class);
+ return modelMessage;
+ } catch (Throwable e) {
+ log.error("execute encode script error", e);
+ }
+ return null;
+ }
+
+ @Override
+ public void putScriptEnv(String key, Object value) {
+ context.getBindings("js").putMember(key, value);
+ }
+}
diff --git a/iot-components/iot-mqtt-component/src/main/resources/graaljsconverter.js b/iot-components/iot-mqtt-component/src/main/resources/graaljsconverter.js
new file mode 100644
index 00000000..7182174a
--- /dev/null
+++ b/iot-components/iot-mqtt-component/src/main/resources/graaljsconverter.js
@@ -0,0 +1,173 @@
+
+var mid=1;
+
+function getMid(){
+ mid++;
+ if(mid>10000){
+ mid=1;
+ }
+ return mid+"";
+}
+
+this.decode = function (msg) {
+ //对msg进行解析,并返回物模型数据
+ var content=msg.getContent();
+ var topic = content.topic;
+ var payload = content.payload;
+ var identifier = topic.substring(topic.lastIndexOf("/") + 1);
+
+ //透传上报
+ if(topic.endsWith("/event/rawReport")){
+ var rst= component.transparentDecode(payload.params);
+ if(!rst){
+ return null;
+ }
+ rst.occur=new Date().getTime();
+ rst.time=new Date().getTime();
+ return rst;
+ }
+
+ if (topic.endsWith("/property/post")) {
+ //属性上报
+ return {
+ mid: msg.mid,
+ productKey: msg.productKey,
+ deviceName: msg.deviceName,
+ type:"property",
+ identifier: "report", //属性上报
+ occur: new Date().getTime(), //时间戳,设备上的事件或数据产生的本地时间
+ time: new Date().getTime(), //时间戳,消息上报时间
+ data: payload,
+ };
+ } else if (topic.indexOf("/event/") > 0) {
+ //事件上报
+ return {
+ mid: msg.mid,
+ productKey: msg.productKey,
+ deviceName: msg.deviceName,
+ type:"event",
+ identifier: identifier,
+ occur: new Date().getTime(),
+ time: new Date().getTime(),
+ data: payload,
+ };
+ }else if(topic.endsWith("/service/property/set_reply")){
+ //属性设置回复
+ return {
+ mid: msg.mid,
+ productKey: msg.productKey,
+ deviceName: msg.deviceName,
+ type:"property",
+ identifier: identifier,
+ occur: new Date().getTime(),
+ time: new Date().getTime(),
+ code: payload.code
+ };
+ }else if(topic.endsWith("/config/set_reply")){
+ //设备配置设置回复
+ return {
+ mid: msg.mid,
+ productKey: msg.productKey,
+ deviceName: msg.deviceName,
+ type:"config",
+ identifier: "set_reply",
+ occur: new Date().getTime(),
+ time: new Date().getTime(),
+ code: payload.code
+ };
+ }else if(topic.endsWith("/config/get")){
+ //设备配置获取
+ return {
+ mid: msg.mid,
+ productKey: msg.productKey,
+ deviceName: msg.deviceName,
+ type:"config",
+ identifier: "get",
+ occur: new Date().getTime(),
+ time: new Date().getTime(),
+ data: {},
+ };
+ } else if (topic.endsWith("_reply")) {
+ //服务回复
+ return {
+ mid: msg.mid,
+ productKey: msg.productKey,
+ deviceName: msg.deviceName,
+ type:"service",
+ identifier: identifier,
+ occur: new Date().getTime(),
+ time: new Date().getTime(),
+ code: payload.code,
+ data: payload.data,
+ };
+ }
+ return null;
+};
+
+this.encode = function (service,device) {
+ var deviceMid=getMid();
+ var method="thing.service.";
+ var topic="/sys/"+service.productKey+"/"+service.deviceName+"/c/service/";
+ var params={};
+
+ //透传下发
+ if(device.transparent){
+ var rst=component.transparentEncode(service,device);
+ topic="/sys/"+rst.productKey+"/"+rst.deviceName+"/c/service/rawSend";
+ params.model=rst.content.model;
+ params.deviceName=rst.content.deviceName;
+ params.data=rst.content.data;
+
+ return {
+ productKey:rst.productKey,
+ deviceName:rst.deviceName,
+ mid:rst.mid,
+ content:{
+ topic:topic,
+ payload:JSON.stringify({
+ id:rst.mid,
+ method:method+"rawSend",
+ params:params
+ })
+ }
+ }
+
+ }
+
+ var type=service.type;
+ var identifier=service.identifier;
+
+ if(type=="property"){
+ method+="property."+identifier;
+ topic+="property/"+identifier;
+ }else if(type=="service"){
+ method+=identifier;
+ topic+=identifier;
+ }else if(type=="config"){
+ //设备配置下发
+ method+=identifier;
+ topic="/sys/"+service.productKey+"/"+service.deviceName+"/c/config/"+identifier;
+ }else if(type="lifetime"){
+ //子设备注销下发
+ method+=identifier;
+ topic="/sys/"+service.productKey+"/"+service.deviceName+"/c/deregister";
+ }
+
+ 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
+ })
+ }
+ }
+};
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index f83ed53e..d553b8c5 100755
--- a/pom.xml
+++ b/pom.xml
@@ -31,6 +31,7 @@
11
4.2.2
1.30.0
+ 21.1.0
@@ -328,6 +329,24 @@
${project.version}
+
+
+ org.graalvm.sdk
+ graal-sdk
+ ${graalvm.version}
+
+
+ org.graalvm.js
+ js
+ ${graalvm.version}
+ runtime
+
+
+ org.graalvm.js
+ js-scriptengine
+ ${graalvm.version}
+
+