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} + +