【代码修复】IoT:网络组件相关
This commit is contained in:
parent
44b835bd4a
commit
d83af87f9f
|
@ -107,6 +107,7 @@ public class IotProductScriptController {
|
||||||
@PreAuthorize("@ss.hasPermission('iot:product-script:query')")
|
@PreAuthorize("@ss.hasPermission('iot:product-script:query')")
|
||||||
public CommonResult<String> getSampleScript(@RequestParam("type") Integer type) {
|
public CommonResult<String> getSampleScript(@RequestParam("type") Integer type) {
|
||||||
String sample;
|
String sample;
|
||||||
|
// TODO @haohao:要不枚举下?
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 1:
|
case 1:
|
||||||
sample = scriptSamples.getPropertyParserSample();
|
sample = scriptSamples.getPropertyParserSample();
|
||||||
|
@ -118,6 +119,7 @@ public class IotProductScriptController {
|
||||||
sample = scriptSamples.getCommandEncoderSample();
|
sample = scriptSamples.getCommandEncoderSample();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
// TODO @haohao:不支持,返回 error 会不会好点哈?例如说,参数不正确;
|
||||||
sample = "// 不支持的脚本类型";
|
sample = "// 不支持的脚本类型";
|
||||||
}
|
}
|
||||||
return success(sample);
|
return success(sample);
|
||||||
|
|
|
@ -17,6 +17,7 @@ import java.util.List;
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||||
import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*;
|
import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* IoT 插件配置 Service 实现类
|
* IoT 插件配置 Service 实现类
|
||||||
*
|
*
|
||||||
|
|
|
@ -13,6 +13,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
|
||||||
|
// TODO @haohao:应该不用写 spring.factories 拉,因为被 imports 替代啦
|
||||||
/**
|
/**
|
||||||
* IoT 网络组件的通用自动配置类
|
* IoT 网络组件的通用自动配置类
|
||||||
*
|
*
|
||||||
|
|
|
@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.iot.net.component.core.constants;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
|
// TODO @haohao:要不放到 enums 包下;
|
||||||
/**
|
/**
|
||||||
* IoT 设备主题枚举
|
* IoT 设备主题枚举
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -12,6 +13,7 @@ import lombok.Getter;
|
||||||
@Getter
|
@Getter
|
||||||
public enum IotDeviceTopicEnum {
|
public enum IotDeviceTopicEnum {
|
||||||
|
|
||||||
|
// TODO @haohao:SYS_TOPIC_PREFIX、SERVICE_TOPIC_PREFIX、REPLY_SUFFIX 类似这种,要不搞成这个里面的静态变量?不是枚举值
|
||||||
/**
|
/**
|
||||||
* 系统主题前缀
|
* 系统主题前缀
|
||||||
*/
|
*/
|
||||||
|
@ -22,6 +24,7 @@ public enum IotDeviceTopicEnum {
|
||||||
*/
|
*/
|
||||||
SERVICE_TOPIC_PREFIX("/thing/service/", "服务调用主题前缀"),
|
SERVICE_TOPIC_PREFIX("/thing/service/", "服务调用主题前缀"),
|
||||||
|
|
||||||
|
// TODO @haohao:注释时,中英文之间,有个空格;
|
||||||
/**
|
/**
|
||||||
* 设备属性设置主题
|
* 设备属性设置主题
|
||||||
* 请求Topic:/sys/${productKey}/${deviceName}/thing/service/property/set
|
* 请求Topic:/sys/${productKey}/${deviceName}/thing/service/property/set
|
||||||
|
@ -75,6 +78,7 @@ public enum IotDeviceTopicEnum {
|
||||||
private final String topic;
|
private final String topic;
|
||||||
private final String description;
|
private final String description;
|
||||||
|
|
||||||
|
// TODO @haohao:使用 lombok 去除
|
||||||
IotDeviceTopicEnum(String topic, String description) {
|
IotDeviceTopicEnum(String topic, String description) {
|
||||||
this.topic = topic;
|
this.topic = topic;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
|
@ -89,6 +93,7 @@ public enum IotDeviceTopicEnum {
|
||||||
* @return 完整的主题路径
|
* @return 完整的主题路径
|
||||||
*/
|
*/
|
||||||
public static String buildServiceTopic(String productKey, String deviceName, String serviceIdentifier) {
|
public static String buildServiceTopic(String productKey, String deviceName, String serviceIdentifier) {
|
||||||
|
// TODO @haohao:貌似 SYS_TOPIC_PREFIX.getTopic() + productKey + "/" + deviceName 是统一的;
|
||||||
return SYS_TOPIC_PREFIX.getTopic() + productKey + "/" + deviceName +
|
return SYS_TOPIC_PREFIX.getTopic() + productKey + "/" + deviceName +
|
||||||
SERVICE_TOPIC_PREFIX.getTopic() + serviceIdentifier;
|
SERVICE_TOPIC_PREFIX.getTopic() + serviceIdentifier;
|
||||||
}
|
}
|
||||||
|
@ -127,7 +132,7 @@ public enum IotDeviceTopicEnum {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建设备OTA升级主题
|
* 构建设备 OTA 升级主题
|
||||||
*
|
*
|
||||||
* @param productKey 产品Key
|
* @param productKey 产品Key
|
||||||
* @param deviceName 设备名称
|
* @param deviceName 设备名称
|
||||||
|
@ -170,4 +175,5 @@ public enum IotDeviceTopicEnum {
|
||||||
public static String getReplyTopic(String requestTopic) {
|
public static String getReplyTopic(String requestTopic) {
|
||||||
return requestTopic + REPLY_SUFFIX.getTopic();
|
return requestTopic + REPLY_SUFFIX.getTopic();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -11,6 +11,7 @@ import java.util.Map;
|
||||||
* IoT Alink 消息模型
|
* IoT Alink 消息模型
|
||||||
* <p>
|
* <p>
|
||||||
* 基于阿里云 Alink 协议规范实现的标准消息格式
|
* 基于阿里云 Alink 协议规范实现的标准消息格式
|
||||||
|
* @see <a href="https://help.aliyun.com/zh/iot/user-guide/alink-protocol-1">阿里云物联网 —— Alink 协议</a>
|
||||||
*
|
*
|
||||||
* @author haohao
|
* @author haohao
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -12,7 +12,7 @@ import lombok.experimental.Accessors;
|
||||||
* @author haohao
|
* @author haohao
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@Accessors(chain = true)
|
@Accessors(chain = true) // TODO @haohao:貌似不用写 @Accessors(chain = true),我全局加啦,可见 lombok.config
|
||||||
public class IotStandardResponse {
|
public class IotStandardResponse {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -28,13 +28,13 @@ import org.springframework.context.event.EventListener;
|
||||||
*
|
*
|
||||||
* @author haohao
|
* @author haohao
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
|
||||||
@AutoConfiguration
|
@AutoConfiguration
|
||||||
@EnableConfigurationProperties(IotNetComponentEmqxProperties.class)
|
@EnableConfigurationProperties(IotNetComponentEmqxProperties.class)
|
||||||
@ConditionalOnProperty(prefix = "yudao.iot.component.emqx", name = "enabled", havingValue = "true", matchIfMissing = false)
|
@ConditionalOnProperty(prefix = "yudao.iot.component.emqx", name = "enabled", havingValue = "true")
|
||||||
@ComponentScan(basePackages = {
|
@ComponentScan(basePackages = {
|
||||||
"cn.iocoder.yudao.module.iot.net.component.emqx" // 只扫描 EMQX 组件包
|
"cn.iocoder.yudao.module.iot.net.component.emqx" // 只扫描 EMQX 组件包
|
||||||
})
|
}) // TODO @haohao:自动配置后,不需要这个哈。
|
||||||
|
@Slf4j
|
||||||
public class IotNetComponentEmqxAutoConfiguration {
|
public class IotNetComponentEmqxAutoConfiguration {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -42,6 +42,7 @@ public class IotNetComponentEmqxAutoConfiguration {
|
||||||
*/
|
*/
|
||||||
private static final String PLUGIN_KEY = "emqx";
|
private static final String PLUGIN_KEY = "emqx";
|
||||||
|
|
||||||
|
// TODO @haohao:这个是不是要去掉哈。
|
||||||
public IotNetComponentEmqxAutoConfiguration() {
|
public IotNetComponentEmqxAutoConfiguration() {
|
||||||
// 构造函数中不输出日志,移到 initialize 方法中
|
// 构造函数中不输出日志,移到 initialize 方法中
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,6 +64,7 @@ public class IotNetComponentEmqxProperties {
|
||||||
@NotNull(message = "认证端口不能为空")
|
@NotNull(message = "认证端口不能为空")
|
||||||
private Integer authPort;
|
private Integer authPort;
|
||||||
|
|
||||||
|
// TODO @haohao:可以使用 Duration 类型,可读性更好
|
||||||
/**
|
/**
|
||||||
* 重连延迟时间(毫秒)
|
* 重连延迟时间(毫秒)
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -77,4 +78,5 @@ public class IotNetComponentEmqxProperties {
|
||||||
* 默认值:10000 毫秒
|
* 默认值:10000 毫秒
|
||||||
*/
|
*/
|
||||||
private Integer connectionTimeoutMs = 10000;
|
private Integer connectionTimeoutMs = 10000;
|
||||||
|
|
||||||
}
|
}
|
|
@ -82,6 +82,7 @@ public class IotDeviceUpstreamServer {
|
||||||
log.info("[start][开始启动服务]");
|
log.info("[start][开始启动服务]");
|
||||||
|
|
||||||
// 检查 authPort 是否为 null
|
// 检查 authPort 是否为 null
|
||||||
|
// TODO @haohao:authPort 里面搞默认值?包括下面,这个类不搞任何默认值,都交给 emqxProperties
|
||||||
Integer authPort = emqxProperties.getAuthPort();
|
Integer authPort = emqxProperties.getAuthPort();
|
||||||
if (authPort == null) {
|
if (authPort == null) {
|
||||||
log.warn("[start][authPort 为 null,使用默认端口 8080]");
|
log.warn("[start][authPort 为 null,使用默认端口 8080]");
|
||||||
|
|
|
@ -30,6 +30,7 @@ import java.util.Map;
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class IotDeviceMqttMessageHandler {
|
public class IotDeviceMqttMessageHandler {
|
||||||
|
|
||||||
|
// TODO @haohao:下面的,有办法也抽到 IotDeviceTopicEnum 么?想的是,尽量把这些 method、topic、url 统一化;
|
||||||
private static final String PROPERTY_METHOD = "thing.event.property.post";
|
private static final String PROPERTY_METHOD = "thing.event.property.post";
|
||||||
private static final String EVENT_METHOD_PREFIX = "thing.event.";
|
private static final String EVENT_METHOD_PREFIX = "thing.event.";
|
||||||
private static final String EVENT_METHOD_SUFFIX = ".post";
|
private static final String EVENT_METHOD_SUFFIX = ".post";
|
||||||
|
@ -223,6 +224,7 @@ public class IotDeviceMqttMessageHandler {
|
||||||
* @return 设备属性上报请求对象
|
* @return 设备属性上报请求对象
|
||||||
*/
|
*/
|
||||||
private IotDevicePropertyReportReqDTO buildPropertyReportDTO(JSONObject jsonObject, String[] topicParts) {
|
private IotDevicePropertyReportReqDTO buildPropertyReportDTO(JSONObject jsonObject, String[] topicParts) {
|
||||||
|
// TODO @haohao:IotDevicePropertyReportReqDTO 可以考虑链式哈。其它也是,尽量让同类参数在一行;这样,阅读起来更聚焦;
|
||||||
IotDevicePropertyReportReqDTO reportReqDTO = new IotDevicePropertyReportReqDTO();
|
IotDevicePropertyReportReqDTO reportReqDTO = new IotDevicePropertyReportReqDTO();
|
||||||
reportReqDTO.setRequestId(jsonObject.getStr("id"));
|
reportReqDTO.setRequestId(jsonObject.getStr("id"));
|
||||||
reportReqDTO.setProcessId(IotNetComponentCommonUtils.getProcessId());
|
reportReqDTO.setProcessId(IotNetComponentCommonUtils.getProcessId());
|
||||||
|
@ -230,7 +232,7 @@ public class IotDeviceMqttMessageHandler {
|
||||||
reportReqDTO.setProductKey(topicParts[2]);
|
reportReqDTO.setProductKey(topicParts[2]);
|
||||||
reportReqDTO.setDeviceName(topicParts[3]);
|
reportReqDTO.setDeviceName(topicParts[3]);
|
||||||
|
|
||||||
// 只使用标准JSON格式处理属性数据
|
// 只使用标准 JSON格式处理属性数据
|
||||||
JSONObject params = jsonObject.getJSONObject("params");
|
JSONObject params = jsonObject.getJSONObject("params");
|
||||||
if (params == null) {
|
if (params == null) {
|
||||||
log.warn("[buildPropertyReportDTO][消息格式不正确,缺少params字段][jsonObject: {}]", jsonObject);
|
log.warn("[buildPropertyReportDTO][消息格式不正确,缺少params字段][jsonObject: {}]", jsonObject);
|
||||||
|
|
|
@ -61,13 +61,14 @@ public class IotNetComponentHttpAutoConfiguration {
|
||||||
|
|
||||||
// 设置当前组件的核心标识
|
// 设置当前组件的核心标识
|
||||||
// 注意:这里只为当前 HTTP 组件设置 pluginKey,不影响其他组件
|
// 注意:这里只为当前 HTTP 组件设置 pluginKey,不影响其他组件
|
||||||
|
// TODO @haohao:多个会存在冲突的问题哇?
|
||||||
commonProperties.setPluginKey(PLUGIN_KEY);
|
commonProperties.setPluginKey(PLUGIN_KEY);
|
||||||
|
|
||||||
// 将 HTTP 组件注册到组件注册表
|
// 将 HTTP 组件注册到组件注册表
|
||||||
componentRegistry.registerComponent(
|
componentRegistry.registerComponent(
|
||||||
PLUGIN_KEY,
|
PLUGIN_KEY,
|
||||||
SystemUtil.getHostInfo().getAddress(),
|
SystemUtil.getHostInfo().getAddress(),
|
||||||
0, // 内嵌模式固定为 0
|
0, // 内嵌模式固定为 0:自动生成对应的 port 端口号
|
||||||
IotNetComponentCommonUtils.getProcessId());
|
IotNetComponentCommonUtils.getProcessId());
|
||||||
|
|
||||||
log.info("[initialize][IoT HTTP 组件初始化完成]");
|
log.info("[initialize][IoT HTTP 组件初始化完成]");
|
||||||
|
@ -115,4 +116,5 @@ public class IotNetComponentHttpAutoConfiguration {
|
||||||
public IotDeviceDownstreamHandler deviceDownstreamHandler() {
|
public IotDeviceDownstreamHandler deviceDownstreamHandler() {
|
||||||
return new IotDeviceDownstreamHandlerImpl();
|
return new IotDeviceDownstreamHandlerImpl();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,12 +5,13 @@ import io.vertx.ext.web.RoutingContext;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
|
|
||||||
|
// TODO @haohao:待实现,或者不需要?
|
||||||
/**
|
/**
|
||||||
* IoT 设备认证提供者
|
* IoT 设备认证提供者
|
||||||
* <p>
|
* <p>
|
||||||
* 用于 HTTP 设备接入时的身份认证
|
* 用于 HTTP 设备接入时的身份认证
|
||||||
*
|
*
|
||||||
* @author 芋道源码
|
* @author haohao
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class IotDeviceAuthProvider {
|
public class IotDeviceAuthProvider {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package cn.iocoder.yudao.module.iot.net.component.http.upstream.router;
|
package cn.iocoder.yudao.module.iot.net.component.http.upstream.router;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.map.MapUtil;
|
import cn.hutool.core.map.MapUtil;
|
||||||
import cn.hutool.core.util.IdUtil;
|
import cn.hutool.core.util.IdUtil;
|
||||||
import cn.hutool.core.util.ObjUtil;
|
import cn.hutool.core.util.ObjUtil;
|
||||||
|
@ -35,6 +36,8 @@ import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeC
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class IotDeviceUpstreamVertxHandler implements Handler<RoutingContext> {
|
public class IotDeviceUpstreamVertxHandler implements Handler<RoutingContext> {
|
||||||
|
|
||||||
|
// TODO @haohao:你说,咱要不要把 "/sys/:productKey/:deviceName"
|
||||||
|
// + IotDeviceTopicEnum.PROPERTY_POST_TOPIC.getTopic(),也抽到 IotDeviceTopicEnum 的 build 这种?尽量都收敛掉?
|
||||||
/**
|
/**
|
||||||
* 属性上报路径
|
* 属性上报路径
|
||||||
*/
|
*/
|
||||||
|
@ -254,8 +257,8 @@ public class IotDeviceUpstreamVertxHandler implements Handler<RoutingContext> {
|
||||||
return PROPERTY_METHOD;
|
return PROPERTY_METHOD;
|
||||||
}
|
}
|
||||||
|
|
||||||
return EVENT_METHOD_PREFIX +
|
return EVENT_METHOD_PREFIX
|
||||||
(routingContext.pathParams().containsKey("identifier")
|
+ (routingContext.pathParams().containsKey("identifier")
|
||||||
? routingContext.pathParam("identifier")
|
? routingContext.pathParam("identifier")
|
||||||
: "unknown")
|
: "unknown")
|
||||||
+
|
+
|
||||||
|
@ -275,7 +278,6 @@ public class IotDeviceUpstreamVertxHandler implements Handler<RoutingContext> {
|
||||||
.setReportTime(LocalDateTime.now())
|
.setReportTime(LocalDateTime.now())
|
||||||
.setProductKey(productKey)
|
.setProductKey(productKey)
|
||||||
.setDeviceName(deviceName)).setState(IotDeviceStateEnum.ONLINE.getState());
|
.setDeviceName(deviceName)).setState(IotDeviceStateEnum.ONLINE.getState());
|
||||||
|
|
||||||
deviceUpstreamApi.updateDeviceState(reqDTO);
|
deviceUpstreamApi.updateDeviceState(reqDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,8 +313,7 @@ public class IotDeviceUpstreamVertxHandler implements Handler<RoutingContext> {
|
||||||
private Map<String, Object> parsePropertiesFromBody(JsonObject body) {
|
private Map<String, Object> parsePropertiesFromBody(JsonObject body) {
|
||||||
Map<String, Object> properties = MapUtil.newHashMap();
|
Map<String, Object> properties = MapUtil.newHashMap();
|
||||||
JsonObject params = body.getJsonObject("params");
|
JsonObject params = body.getJsonObject("params");
|
||||||
|
if (CollUtil.isEmpty(params)) {
|
||||||
if (params == null) {
|
|
||||||
return properties;
|
return properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,7 +328,6 @@ public class IotDeviceUpstreamVertxHandler implements Handler<RoutingContext> {
|
||||||
properties.put(key, valueObj);
|
properties.put(key, valueObj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return properties;
|
return properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -364,15 +364,13 @@ public class IotDeviceUpstreamVertxHandler implements Handler<RoutingContext> {
|
||||||
private Map<String, Object> parseParamsFromBody(JsonObject body) {
|
private Map<String, Object> parseParamsFromBody(JsonObject body) {
|
||||||
Map<String, Object> params = MapUtil.newHashMap();
|
Map<String, Object> params = MapUtil.newHashMap();
|
||||||
JsonObject paramsJson = body.getJsonObject("params");
|
JsonObject paramsJson = body.getJsonObject("params");
|
||||||
|
if (CollUtil.isEmpty(paramsJson)) {
|
||||||
if (paramsJson == null) {
|
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (String key : paramsJson.fieldNames()) {
|
for (String key : paramsJson.fieldNames()) {
|
||||||
params.put(key, paramsJson.getValue(key));
|
params.put(key, paramsJson.getValue(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -32,6 +32,7 @@ public class IotNetComponentServerConfiguration {
|
||||||
* @return RestTemplate
|
* @return RestTemplate
|
||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
|
// TODO @haohao:貌似要独立一个 restTemplate 的名字?不然容易冲突;
|
||||||
public RestTemplate restTemplate(IotNetComponentServerProperties properties) {
|
public RestTemplate restTemplate(IotNetComponentServerProperties properties) {
|
||||||
return new RestTemplateBuilder()
|
return new RestTemplateBuilder()
|
||||||
.connectTimeout(properties.getUpstreamConnectTimeout())
|
.connectTimeout(properties.getUpstreamConnectTimeout())
|
||||||
|
@ -104,6 +105,7 @@ public class IotNetComponentServerConfiguration {
|
||||||
return new Object();
|
return new Object();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO @haohao:这个是不是木有用呀?
|
||||||
/**
|
/**
|
||||||
* 配置默认的组件实例注册客户端
|
* 配置默认的组件实例注册客户端
|
||||||
*
|
*
|
||||||
|
|
|
@ -7,6 +7,7 @@ import org.springframework.web.bind.annotation.RestController;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
// TODO @haohao:这个是必须的哇?可以考虑基于 spring boot actuator;
|
||||||
/**
|
/**
|
||||||
* 健康检查接口
|
* 健康检查接口
|
||||||
*
|
*
|
||||||
|
|
|
@ -7,14 +7,12 @@ import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotPluginInst
|
||||||
import cn.iocoder.yudao.module.iot.net.component.server.config.IotNetComponentServerProperties;
|
import cn.iocoder.yudao.module.iot.net.component.server.config.IotNetComponentServerProperties;
|
||||||
import cn.iocoder.yudao.module.iot.net.component.server.downstream.IotComponentDownstreamServer;
|
import cn.iocoder.yudao.module.iot.net.component.server.downstream.IotComponentDownstreamServer;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.lang.ProcessHandle;
|
|
||||||
|
|
||||||
|
// TODO @haohao:有办法服用 yudao-module-iot-net-component-core 的么?就是 server,只是一个启动器,没什么特殊的功能;
|
||||||
/**
|
/**
|
||||||
* IoT 组件心跳任务
|
* IoT 组件心跳任务
|
||||||
* <p>
|
* <p>
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- JavaScript 引擎 - 使用 GraalJS 替代 Nashorn -->
|
<!-- JavaScript 引擎 - 使用 GraalJS 替代 Nashorn -->
|
||||||
|
<!-- TODO @haohao:得考虑下,jdk8 可能不支持 graalvm,后续哈;【优先级:低】 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.graalvm.sdk</groupId>
|
<groupId>org.graalvm.sdk</groupId>
|
||||||
<artifactId>graal-sdk</artifactId>
|
<artifactId>graal-sdk</artifactId>
|
||||||
|
|
|
@ -10,6 +10,7 @@ import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
// TODO @haohao:挪到 test 目录下
|
||||||
/**
|
/**
|
||||||
* 脚本使用示例类
|
* 脚本使用示例类
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -63,6 +63,7 @@ public class JsScriptEngine extends AbstractScriptEngine implements AutoCloseabl
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// 创建隔离的临时目录路径
|
// 创建隔离的临时目录路径
|
||||||
|
// TODO @haohao:貌似没用到?
|
||||||
Path tempDirectory = Path.of(System.getProperty("java.io.tmpdir"), "graaljs-" + IdUtil.fastSimpleUUID());
|
Path tempDirectory = Path.of(System.getProperty("java.io.tmpdir"), "graaljs-" + IdUtil.fastSimpleUUID());
|
||||||
|
|
||||||
// 初始化 GraalJS 上下文
|
// 初始化 GraalJS 上下文
|
||||||
|
@ -94,6 +95,7 @@ public class JsScriptEngine extends AbstractScriptEngine implements AutoCloseabl
|
||||||
Source source = getOrCreateSource(script);
|
Source source = getOrCreateSource(script);
|
||||||
|
|
||||||
// 执行脚本并捕获结果,添加超时控制
|
// 执行脚本并捕获结果,添加超时控制
|
||||||
|
// TODO @haohao:通过线程池 + future 会好点?
|
||||||
Value result;
|
Value result;
|
||||||
Thread executionThread = Thread.currentThread();
|
Thread executionThread = Thread.currentThread();
|
||||||
Thread watchdogThread = new Thread(() -> {
|
Thread watchdogThread = new Thread(() -> {
|
||||||
|
@ -236,11 +238,14 @@ public class JsScriptEngine extends AbstractScriptEngine implements AutoCloseabl
|
||||||
if (result.isNumber()) {
|
if (result.isNumber()) {
|
||||||
if (result.fitsInInt()) {
|
if (result.fitsInInt()) {
|
||||||
return result.asInt();
|
return result.asInt();
|
||||||
} else if (result.fitsInLong()) {
|
}
|
||||||
|
if (result.fitsInLong()) {
|
||||||
return result.asLong();
|
return result.asLong();
|
||||||
} else if (result.fitsInFloat()) {
|
}
|
||||||
|
if (result.fitsInFloat()) {
|
||||||
return result.asFloat();
|
return result.asFloat();
|
||||||
} else if (result.fitsInDouble()) {
|
}
|
||||||
|
if (result.fitsInDouble()) {
|
||||||
return result.asDouble();
|
return result.asDouble();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,7 +79,6 @@ public class ScriptEngineFactory implements DisposableBean {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void destroy() {
|
public void destroy() {
|
||||||
// 应用关闭时,释放所有引擎资源
|
|
||||||
log.info("应用关闭,释放所有脚本引擎资源...");
|
log.info("应用关闭,释放所有脚本引擎资源...");
|
||||||
releaseAllEngines();
|
releaseAllEngines();
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
|
// TODO @haohao:搞到 test 里面哈;
|
||||||
/**
|
/**
|
||||||
* GraalJS 脚本引擎示例
|
* GraalJS 脚本引擎示例
|
||||||
* <p>
|
* <p>
|
||||||
|
|
|
@ -56,6 +56,7 @@ public class JsSandbox implements ScriptSandbox {
|
||||||
*/
|
*/
|
||||||
public JsSandbox() {
|
public JsSandbox() {
|
||||||
// 初始化 Java 相关的不安全关键字
|
// 初始化 Java 相关的不安全关键字
|
||||||
|
// TODO @haohao:可以使用 addAll 哈。
|
||||||
Arrays.asList(
|
Arrays.asList(
|
||||||
"java.lang.System",
|
"java.lang.System",
|
||||||
"java.io",
|
"java.io",
|
||||||
|
|
|
@ -40,6 +40,7 @@ public class ScriptServiceImpl implements ScriptService {
|
||||||
try {
|
try {
|
||||||
return engine.execute(script, context);
|
return engine.execute(script, context);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
// TODO @haohao:最好打印一些参数;下面类似的也是
|
||||||
log.error("执行脚本失败: {}", e.getMessage(), e);
|
log.error("执行脚本失败: {}", e.getMessage(), e);
|
||||||
throw new RuntimeException("执行脚本失败: " + e.getMessage(), e);
|
throw new RuntimeException("执行脚本失败: " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ public class ScriptUtils {
|
||||||
return INSTANCE;
|
return INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO @haohao:使用 lombok 简化掉
|
||||||
private ScriptUtils() {
|
private ScriptUtils() {
|
||||||
// 私有构造函数
|
// 私有构造函数
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue