添加 covert脚本 js解析
parent
e3461e1082
commit
63a905c01f
|
@ -33,6 +33,22 @@
|
||||||
<artifactId>iot-model</artifactId>
|
<artifactId>iot-model</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!--javascript运行环境-->
|
||||||
|
<dependency>
|
||||||
|
<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>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
|
@ -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<Class<?>, Map<String, Field>> FIELD_CACHE = new ConcurrentHashMap<>();
|
||||||
|
private static final Map<Class<?>, Map<String, Method>> SETTER_CACHE = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public static void copyProperties(Object javaObj, Value jsObj) {
|
||||||
|
Map<String, Field> fieldMap = FIELD_CACHE.computeIfAbsent(javaObj.getClass(), clazz -> {
|
||||||
|
Map<String, Field> fields = new ConcurrentHashMap<>();
|
||||||
|
for (Field field : clazz.getDeclaredFields()) {
|
||||||
|
fields.put(field.getName(), field);
|
||||||
|
}
|
||||||
|
return fields;
|
||||||
|
});
|
||||||
|
Map<String, Method> setterMap = SETTER_CACHE.computeIfAbsent(javaObj.getClass(), clazz -> {
|
||||||
|
Map<String, Method> 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
19
pom.xml
19
pom.xml
|
@ -31,6 +31,7 @@
|
||||||
<java.version>11</java.version>
|
<java.version>11</java.version>
|
||||||
<vertx.version>4.2.2</vertx.version>
|
<vertx.version>4.2.2</vertx.version>
|
||||||
<sa-token.version>1.30.0</sa-token.version>
|
<sa-token.version>1.30.0</sa-token.version>
|
||||||
|
<graalvm.version>21.1.0</graalvm.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
|
@ -328,6 +329,24 @@
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!--javascript运行环境-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.graalvm.sdk</groupId>
|
||||||
|
<artifactId>graal-sdk</artifactId>
|
||||||
|
<version>${graalvm.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.graalvm.js</groupId>
|
||||||
|
<artifactId>js</artifactId>
|
||||||
|
<version>${graalvm.version}</version>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.graalvm.js</groupId>
|
||||||
|
<artifactId>js-scriptengine</artifactId>
|
||||||
|
<version>${graalvm.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue