增加modubs插件

master
xiwa 2023-09-18 08:35:00 +08:00
parent aa041ae0c4
commit e9bb44221c
29 changed files with 451 additions and 13 deletions

0
.gitee/ISSUE_TEMPLATE.zh-CN.md Normal file → Executable file
View File

0
.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md Normal file → Executable file
View File

0
.gitignore vendored Normal file → Executable file
View File

0
LICENSE Normal file → Executable file
View File

0
README.en.md Normal file → Executable file
View File

0
README.md Normal file → Executable file
View File

7
http-plugin/pom.xml Normal file → Executable file
View File

@ -10,6 +10,7 @@
<modelVersion>4.0.0</modelVersion>
<artifactId>http-plugin</artifactId>
<version>1.0.0</version>
<dependencies>
@ -61,13 +62,13 @@
<pluginInfo>
<id>http-plugin</id>
<bootstrapClass>cc.iotkit.plugins.http.Application</bootstrapClass>
<version>1.0.0</version>
<version>${project.version}</version>
<provider>iita</provider>
<description>http示例插件</description>
<description>http示例插件,配置参数:端口(port)默认9081</description>
<configFileName>application.yml</configFileName>
</pluginInfo>
<prodConfig>
<packageType>jar-outer</packageType>
<packageType>jar</packageType>
</prodConfig>
</configuration>
<executions>

View File

@ -1,5 +1,7 @@
package cc.iotkit.plugins.http.conf;
import cc.iotkit.plugin.core.IPluginConfig;
import cc.iotkit.plugin.core.LocalPluginConfig;
import cc.iotkit.plugin.core.thing.IThingService;
import cc.iotkit.plugins.http.service.FakeThingService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@ -18,4 +20,10 @@ public class BeanConfig {
return new FakeThingService();
}
@Bean
@ConditionalOnProperty(name = "plugin.runMode", havingValue = "dev")
IPluginConfig getPluginConfig(){
return new LocalPluginConfig();
}
}

View File

@ -1,5 +1,11 @@
package cc.iotkit.plugins.http.service;
import cc.iotkit.common.utils.JsonUtils;
import cc.iotkit.plugin.core.IPluginConfig;
import cc.iotkit.plugins.http.conf.HttpConfig;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.gitee.starblues.bootstrap.annotation.AutowiredType;
import com.gitee.starblues.bootstrap.realize.PluginCloseListener;
import com.gitee.starblues.core.PluginCloseType;
import com.gitee.starblues.core.PluginInfo;
@ -11,6 +17,7 @@ import org.springframework.context.support.GenericApplicationContext;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
/**
@ -24,6 +31,12 @@ public class HttpPlugin implements PluginCloseListener {
private PluginInfo pluginInfo;
@Autowired
private HttpVerticle httpVerticle;
@Autowired
private HttpConfig httpConfig;
@Autowired
@AutowiredType(AutowiredType.Type.MAIN_PLUGIN)
private IPluginConfig pluginConfig;
private Vertx vertx;
private CountDownLatch countDownLatch;
@ -33,6 +46,12 @@ public class HttpPlugin implements PluginCloseListener {
public void init() {
vertx = Vertx.vertx();
try {
//获取插件最新配置替换当前配置
Map<String, Object> config = pluginConfig.getConfig(pluginInfo.getPluginId());
log.info("get config:{}", JsonUtils.toJsonString(config));
BeanUtil.copyProperties(config, httpConfig, CopyOptions.create().ignoreNullValue());
httpVerticle.setConfig(httpConfig);
countDownLatch = new CountDownLatch(1);
Future<String> future = vertx.deployVerticle(httpVerticle);
future.onSuccess((s -> {
@ -41,12 +60,12 @@ public class HttpPlugin implements PluginCloseListener {
}));
future.onFailure((e) -> {
countDownLatch.countDown();
log.error("start mqtt plugin failed", e);
log.error("start http plugin failed", e);
});
countDownLatch.await();
future.succeeded();
} catch (Throwable e) {
log.error("start mqtt plugin error.", e);
log.error("start http plugin error.", e);
}
}
@ -55,7 +74,7 @@ public class HttpPlugin implements PluginCloseListener {
try {
httpVerticle.stop();
Future<Void> future = vertx.undeploy(deployedId);
future.onSuccess(unused -> log.info("stop mqtt plugin success"));
future.onSuccess(unused -> log.info("stop http plugin success"));
if (closeType == PluginCloseType.UNINSTALL) {
log.info("插件被卸载了:{}", pluginInfo.getPluginId());
} else if (closeType == PluginCloseType.STOP) {
@ -64,7 +83,7 @@ public class HttpPlugin implements PluginCloseListener {
log.info("插件被升级卸载了:{}", pluginInfo.getPluginId());
}
} catch (Throwable e) {
log.error("stop mqtt plugin error.", e);
log.error("stop http plugin error.", e);
}
}

View File

@ -29,6 +29,7 @@ import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.BodyHandler;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@ -46,9 +47,9 @@ import java.util.UUID;
*/
@Slf4j
@Component
@Data
public class HttpVerticle extends AbstractVerticle implements Handler<RoutingContext> {
@Autowired
private HttpConfig config;
@Autowired
@ -60,9 +61,11 @@ public class HttpVerticle extends AbstractVerticle implements Handler<RoutingCon
private static final Set<String> DEVICE_ONLINE = new HashSet<>();
private HttpServer httpServer;
@Override
public void start() {
HttpServer httpServer = vertx.createHttpServer();
httpServer = vertx.createHttpServer();
Router router = Router.router(vertx);
router.route().handler(BodyHandler.create()).handler(this);
httpServer.requestHandler(router).listen(config.getPort(), ar -> {
@ -74,6 +77,11 @@ public class HttpVerticle extends AbstractVerticle implements Handler<RoutingCon
});
}
@Override
public void stop() throws Exception {
httpServer.close((r) -> log.info("http server close result:{}", r.succeeded()));
}
@Override
public void handle(RoutingContext ctx) {
HttpServerResponse response = ctx.response();

0
http-plugin/src/main/resources/application.yml Normal file → Executable file
View File

View File

@ -0,0 +1,26 @@
[
{
"id": "port",
"name": "端口",
"type": "number",
"value": 9081,
"desc": "http端口默认为9081"
},
{
"id": "a",
"name": "测试参数1",
"type": "radio",
"value": 0,
"desc": "单选参数a",
"options": [
{
"name": "值0",
"value": 0
},
{
"name": "值1",
"value": 11
}
]
}
]

73
modbus-plugin/pom.xml Normal file
View File

@ -0,0 +1,73 @@
<?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-iita-plugins</artifactId>
<groupId>cc.iotkit.plugins</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>modbus-plugin</artifactId>
<dependencies>
<dependency>
<groupId>com.digitalpetri.modbus</groupId>
<artifactId>modbus-master-tcp</artifactId>
<version>1.2.0</version>
</dependency>
</dependencies>
<profiles>
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<plugin.build.mode>dev</plugin.build.mode>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<plugin.build.mode>prod</plugin.build.mode>
</properties>
</profile>
</profiles>
<build>
<plugins>
<plugin>
<groupId>com.gitee.starblues</groupId>
<artifactId>spring-brick-maven-packager</artifactId>
<configuration>
<mode>${plugin.build.mode}</mode>
<pluginInfo>
<id>modbus-plugin</id>
<bootstrapClass>cc.iotkit.plugins.modbus.Application</bootstrapClass>
<version>${project.version}</version>
<provider>iita</provider>
<description>modbus示例插件</description>
<configFileName>application.yml</configFileName>
</pluginInfo>
<prodConfig>
<packageType>jar</packageType>
</prodConfig>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,21 @@
package cc.iotkit.plugins.modbus;
import com.gitee.starblues.bootstrap.SpringPluginBootstrap;
import com.gitee.starblues.bootstrap.annotation.OneselfConfig;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
* @author sjg
*/
@SpringBootApplication(scanBasePackages = {"cc.iotkit.plugin.core", "cc.iotkit.plugins.modbus"})
@OneselfConfig(mainConfigFileName = {"application.yml"})
@EnableConfigurationProperties
@EnableScheduling
public class Application extends SpringPluginBootstrap {
public static void main(String[] args) {
new Application().run(Application.class, args);
}
}

View File

@ -0,0 +1,36 @@
package cc.iotkit.plugins.modbus.conf;
import cc.iotkit.plugin.core.IPluginConfig;
import cc.iotkit.plugin.core.IPluginScript;
import cc.iotkit.plugin.core.LocalPluginConfig;
import cc.iotkit.plugin.core.LocalPluginScript;
import cc.iotkit.plugin.core.thing.IThingService;
import cc.iotkit.plugins.modbus.service.FakeThingService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
/**
* @author sjg
*/
@Component
public class BeanConfig {
@Bean
@ConditionalOnProperty(name = "plugin.runMode", havingValue = "dev")
IThingService getThingService() {
return new FakeThingService();
}
@Bean
@ConditionalOnProperty(name = "plugin.runMode", havingValue = "dev")
IPluginScript getPluginScript() {
return new LocalPluginScript("test.js");
}
@Bean
@ConditionalOnProperty(name = "plugin.runMode", havingValue = "dev")
IPluginConfig getPluginConfig(){
return new LocalPluginConfig();
}
}

View File

@ -0,0 +1,47 @@
package cc.iotkit.plugins.modbus.service;
import cc.iotkit.model.device.DeviceInfo;
import cc.iotkit.model.product.Product;
import cc.iotkit.plugin.core.thing.IThingService;
import cc.iotkit.plugin.core.thing.actions.ActionResult;
import cc.iotkit.plugin.core.thing.actions.IDeviceAction;
import lombok.extern.slf4j.Slf4j;
import java.util.HashMap;
import java.util.Map;
/**
*
*
* @author sjg
*/
@Slf4j
public class FakeThingService implements IThingService {
@Override
public ActionResult post(String pluginId, IDeviceAction action) {
log.info("post action:{}", action);
return ActionResult.builder().code(0).build();
}
@Override
public Product getProduct(String pk) {
return Product.builder()
.productKey("cGCrkK7Ex4FESAwe")
.productSecret("aaaaaaaa")
.build();
}
@Override
public DeviceInfo getDevice(String dn) {
return DeviceInfo.builder()
.productKey("cGCrkK7Ex4FESAwe")
.deviceName(dn)
.build();
}
@Override
public Map<String, ?> getProperty(String dn) {
return new HashMap<>(0);
}
}

View File

@ -0,0 +1,138 @@
package cc.iotkit.plugins.modbus.service;
import cc.iotkit.plugin.core.IPluginScript;
import cc.iotkit.plugin.core.thing.IThingService;
import cc.iotkit.plugin.core.thing.actions.DeviceState;
import cc.iotkit.plugin.core.thing.actions.up.DeviceRegister;
import cc.iotkit.plugin.core.thing.actions.up.DeviceStateChange;
import cc.iotkit.plugin.core.thing.actions.up.PropertyReport;
import cc.iotkit.script.IScriptEngine;
import com.digitalpetri.modbus.master.ModbusTcpMaster;
import com.digitalpetri.modbus.master.ModbusTcpMasterConfig;
import com.digitalpetri.modbus.requests.ReadHoldingRegistersRequest;
import com.digitalpetri.modbus.responses.ReadHoldingRegistersResponse;
import com.fasterxml.jackson.core.type.TypeReference;
import com.gitee.starblues.bootstrap.annotation.AutowiredType;
import com.gitee.starblues.bootstrap.realize.PluginCloseListener;
import com.gitee.starblues.core.PluginCloseType;
import com.gitee.starblues.core.PluginInfo;
import io.netty.buffer.ByteBufUtil;
import io.netty.util.ReferenceCountUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.*;
import java.util.concurrent.CompletableFuture;
/**
* @author sjg
*/
@Slf4j
@Service
public class ModbusPlugin implements PluginCloseListener {
@Autowired
private PluginInfo pluginInfo;
@Autowired
@AutowiredType(AutowiredType.Type.MAIN_PLUGIN)
private IPluginScript pluginScript;
@Autowired
@AutowiredType(AutowiredType.Type.MAIN_PLUGIN)
private IThingService thingService;
private IScriptEngine scriptEngine;
private final ModbusTcpMasterConfig config = new ModbusTcpMasterConfig.Builder("localhost").setPort(502).build();
private ModbusTcpMaster master;
private final Set<Integer> registeredDevice = new HashSet<>();
private final int[] slaves = new int[]{1, 2, 3};
private final Map<Integer, String> DATA_CACHE = new HashMap<>();
@PostConstruct
public void init() {
master = new ModbusTcpMaster(config);
CompletableFuture<ModbusTcpMaster> connect = master.connect();
connect.thenAccept(modbusTcpMaster -> System.out.println("111:" + modbusTcpMaster.getConfig()));
//获取脚本引擎
scriptEngine = pluginScript.getScriptEngine(pluginInfo.getPluginId());
}
@Scheduled(initialDelay = 3000, fixedDelay = 1000)
public void taskRead() {
for (int slave : slaves) {
CompletableFuture<ReadHoldingRegistersResponse> future =
master.sendRequest(new ReadHoldingRegistersRequest(0, 10), slave);
future.thenAccept(response -> {
String rspBytes = ByteBufUtil.hexDump(response.getRegisters());
ReferenceCountUtil.release(response);
log.info("receive Response: " + rspBytes);
//相同数据不处理
if (DATA_CACHE.getOrDefault(slave, "").equals(rspBytes)) {
return;
}
DATA_CACHE.put(slave, rspBytes);
if (!registeredDevice.contains(slave)) {
//第一次读取自动注册设备
thingService.post(pluginInfo.getPluginId(), DeviceRegister.builder()
.id(UUID.randomUUID().toString())
.productKey("cGCrkK7Ex4FESAwe")
.deviceName(String.format("modbus_%d", slave))
.build());
registeredDevice.add(slave);
//并上线
thingService.post(pluginInfo.getPluginId(), DeviceStateChange.builder()
.id(UUID.randomUUID().toString())
.productKey("cGCrkK7Ex4FESAwe")
.deviceName(String.format("modbus_%d", slave))
.state(DeviceState.ONLINE)
.build());
}
//调用脚本解码
Map<String, Object> rst = scriptEngine.invokeMethod(new TypeReference<>() {
}, "decode", rspBytes);
if (rst == null || rst.isEmpty()) {
return;
}
//属性上报
thingService.post(pluginInfo.getPluginId(), PropertyReport.builder()
.id(UUID.randomUUID().toString())
.productKey("cGCrkK7Ex4FESAwe")
.deviceName(String.format("modbus_%d", slave))
.params(rst)
.build());
});
}
}
@Override
public void close(GenericApplicationContext applicationContext, PluginInfo pluginInfo, PluginCloseType closeType) {
try {
master.disconnect();
if (closeType == PluginCloseType.UNINSTALL) {
log.info("插件被卸载了:{}", pluginInfo.getPluginId());
} else if (closeType == PluginCloseType.STOP) {
log.info("插件被关闭了:{}", pluginInfo.getPluginId());
} else if (closeType == PluginCloseType.UPGRADE_UNINSTALL) {
log.info("插件被升级卸载了:{}", pluginInfo.getPluginId());
}
} catch (Throwable e) {
log.error("stop mqtt plugin error.", e);
}
}
}

View File

@ -0,0 +1,5 @@
plugin:
runMode: dev
mainPackage: cc.iotkit.plugin

View File

@ -0,0 +1,24 @@
function hexToByte(hexString) {
if (hexString.length % 2 !== 0) {
throw new Error('Invalid hex string. String must have an even number of characters.');
}
let byteArray = [];
for (let i = 0; i < hexString.length; i += 4) {
byteArray.push(parseInt(hexString.substr(i, 4), 16));
}
return byteArray;
}
this.decode=function(hex){
try{
const bytes=hexToByte(hex);
return {
"rssi":bytes[0],
"powerstate":bytes[1]
};
}catch(e){
return {};
}
}

4
mqtt-plugin/pom.xml Normal file → Executable file
View File

@ -66,13 +66,13 @@
<pluginInfo>
<id>mqtt-plugin</id>
<bootstrapClass>cc.iotkit.plugins.mqtt.Application</bootstrapClass>
<version>1.0.0</version>
<version>${project.version}</version>
<provider>iita</provider>
<description>mqtt示例插件</description>
<configFileName>application.yml</configFileName>
</pluginInfo>
<prodConfig>
<packageType>jar-outer</packageType>
<packageType>jar</packageType>
</prodConfig>
</configuration>
<executions>

View File

@ -1,5 +1,7 @@
package cc.iotkit.plugins.mqtt.conf;
import cc.iotkit.plugin.core.IPluginConfig;
import cc.iotkit.plugin.core.LocalPluginConfig;
import cc.iotkit.plugin.core.thing.IThingService;
import cc.iotkit.plugins.mqtt.service.FakeThingService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@ -18,4 +20,9 @@ public class BeanConfig {
return new FakeThingService();
}
@Bean
@ConditionalOnProperty(name = "plugin.runMode", havingValue = "dev")
IPluginConfig getPluginConfig(){
return new LocalPluginConfig();
}
}

View File

@ -1,5 +1,12 @@
package cc.iotkit.plugins.mqtt.service;
import cc.iotkit.common.utils.JsonUtils;
import cc.iotkit.plugin.core.IPlugin;
import cc.iotkit.plugin.core.IPluginConfig;
import cc.iotkit.plugins.mqtt.conf.MqttConfig;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.gitee.starblues.bootstrap.annotation.AutowiredType;
import com.gitee.starblues.bootstrap.realize.PluginCloseListener;
import com.gitee.starblues.core.PluginCloseType;
import com.gitee.starblues.core.PluginInfo;
@ -11,6 +18,7 @@ import org.springframework.context.support.GenericApplicationContext;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
/**
@ -18,12 +26,18 @@ import java.util.concurrent.CountDownLatch;
*/
@Slf4j
@Service
public class MqttPlugin implements PluginCloseListener {
public class MqttPlugin implements PluginCloseListener, IPlugin {
@Autowired
private PluginInfo pluginInfo;
@Autowired
private MqttVerticle mqttVerticle;
@Autowired
private MqttConfig mqttConfig;
@Autowired
@AutowiredType(AutowiredType.Type.MAIN_PLUGIN)
private IPluginConfig pluginConfig;
private Vertx vertx;
private CountDownLatch countDownLatch;
@ -33,6 +47,11 @@ public class MqttPlugin implements PluginCloseListener {
public void init() {
vertx = Vertx.vertx();
try {
//获取插件最新配置替换当前配置
Map<String, Object> config = pluginConfig.getConfig(pluginInfo.getPluginId());
BeanUtil.copyProperties(config,mqttConfig, CopyOptions.create().ignoreNullValue());
mqttVerticle.setConfig(mqttConfig);
countDownLatch = new CountDownLatch(1);
Future<String> future = vertx.deployVerticle(mqttVerticle);
future.onSuccess((s -> {
@ -68,4 +87,8 @@ public class MqttPlugin implements PluginCloseListener {
}
}
@Override
public Map<String, Object> getLinkInfo(String pk, String dn) {
return null;
}
}

View File

@ -34,6 +34,7 @@ import io.vertx.core.json.JsonObject;
import io.vertx.core.net.PemKeyCertOptions;
import io.vertx.mqtt.*;
import io.vertx.mqtt.messages.codes.MqttSubAckReasonCode;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@ -52,6 +53,7 @@ import java.util.concurrent.ConcurrentHashMap;
*/
@Slf4j
@Component
@Data
public class MqttVerticle extends AbstractVerticle implements Handler<MqttEndpoint> {
private MqttServer mqttServer;
@ -61,7 +63,6 @@ public class MqttVerticle extends AbstractVerticle implements Handler<MqttEndpoi
*/
private static final Map<String, Boolean> MQTT_CONNECT_POOL = new ConcurrentHashMap<>();
@Autowired
private MqttConfig config;
@Autowired

0
mqtt-plugin/src/main/resources/application.yml Normal file → Executable file
View File

1
pom.xml Normal file → Executable file
View File

@ -6,6 +6,7 @@
<modules>
<module>mqtt-plugin</module>
<module>http-plugin</module>
<module>modbus-plugin</module>
</modules>
<parent>