1、电表和modbus设备控制完成
parent
5fed47a2c8
commit
94b39b4f6d
|
@ -16,4 +16,6 @@ public class TcpClientConfig {
|
|||
private String host;
|
||||
|
||||
private int port;
|
||||
|
||||
private int interval;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package cc.iotkit.plugins.dlt645.service;
|
||||
|
||||
import cc.iotkit.common.utils.JsonUtils;
|
||||
import cc.iotkit.plugin.core.thing.IThingService;
|
||||
import cc.iotkit.plugin.core.thing.actions.DeviceState;
|
||||
import cc.iotkit.plugin.core.thing.actions.IDeviceAction;
|
||||
|
@ -57,6 +56,8 @@ public class TcpClientVerticle extends AbstractVerticle {
|
|||
|
||||
private int connectState = 0;
|
||||
|
||||
private long timerID;
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
log.info("init start");
|
||||
|
@ -74,10 +75,8 @@ public class TcpClientVerticle extends AbstractVerticle {
|
|||
options.setReconnectAttempts(Integer.MAX_VALUE);
|
||||
options.setReconnectInterval(20000L);
|
||||
netClient = vertx.createNetClient(options);
|
||||
log.info("start1 connect->" + config.getHost() + ":" + config.getPort());
|
||||
netClient.connect(config.getPort(), config.getHost())
|
||||
.onComplete(result -> {
|
||||
System.out.println("connect result:" + JsonUtils.toJsonString(result));
|
||||
if (result.succeeded()) {
|
||||
connectState = 2;
|
||||
log.info("connect dlt645 server success");
|
||||
|
@ -91,7 +90,7 @@ public class TcpClientVerticle extends AbstractVerticle {
|
|||
Object func = ret.get(DLT645Analysis.FUN);
|
||||
DLT645FunCode funCode = DLT645FunCode.decodeEntity((byte) func);
|
||||
if (funCode.isError()) {
|
||||
log.error("message erroe:{}", hexStr);
|
||||
log.info("message erroe:{}", hexStr);
|
||||
return;
|
||||
}
|
||||
//获取设备地址
|
||||
|
@ -100,26 +99,31 @@ public class TcpClientVerticle extends AbstractVerticle {
|
|||
ByteUtils.byteInvertedOrder(adrrTmp, addr);
|
||||
//获取数据
|
||||
byte[] dat = (byte[]) ret.get(DLT645Analysis.DAT);
|
||||
String strAddr=ByteUtils.byteArrayToHexString(addr,false);
|
||||
DLT645V2007Data dataEntity = new DLT645V2007Data();
|
||||
dataEntity.decodeValue(dat, DLT645Analysis.din2entity);
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("p" + dataEntity.getKey(), dataEntity.getValue());//数据标识
|
||||
thingService.post(pluginInfo.getPluginId(),
|
||||
applyPkDn(PropertyReport.builder()
|
||||
PropertyReport.builder().deviceName(strAddr).productKey("PwMfpXmp4ZWkGahn")
|
||||
.params(params)
|
||||
.build()
|
||||
)
|
||||
);
|
||||
}).closeHandler(res -> {
|
||||
connectState = 0;
|
||||
vertx.cancelTimer(timerID);
|
||||
log.info("dlt645 tcp connection closed!");
|
||||
stateChange(DeviceState.OFFLINE);
|
||||
}
|
||||
).exceptionHandler(res -> {
|
||||
connectState = 0;
|
||||
vertx.cancelTimer(timerID);
|
||||
log.info("dlt645 tcp connection exce!");
|
||||
stateChange(DeviceState.OFFLINE);
|
||||
});
|
||||
timerID = vertx.setPeriodic(config.getInterval(), t -> {
|
||||
readDataTask();
|
||||
});
|
||||
} else {
|
||||
connectState = 0;
|
||||
log.info("connect dlt645 tcp error", result.cause());
|
||||
|
@ -132,7 +136,6 @@ public class TcpClientVerticle extends AbstractVerticle {
|
|||
;
|
||||
}
|
||||
|
||||
@Scheduled(initialDelay = 5, fixedRate = 60, timeUnit = TimeUnit.SECONDS)
|
||||
private void readDataTask() {
|
||||
log.info("readData:" + socket);
|
||||
if (socket != null) {
|
||||
|
@ -146,6 +149,8 @@ public class TcpClientVerticle extends AbstractVerticle {
|
|||
if (netClient != null) {
|
||||
netClient.close();
|
||||
}
|
||||
vertx.cancelTimer(timerID);
|
||||
connectState = 0;
|
||||
super.stop();
|
||||
}
|
||||
|
||||
|
@ -160,7 +165,7 @@ public class TcpClientVerticle extends AbstractVerticle {
|
|||
|
||||
private IDeviceAction applyPkDn(IDeviceAction action) {
|
||||
action.setProductKey("BRD3x4fkKxkaxXFt");
|
||||
action.setDeviceName("123456789123");
|
||||
action.setDeviceName("WG123456");
|
||||
return action;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,5 +3,6 @@ plugin:
|
|||
mainPackage: cc.iotkit.plugin
|
||||
|
||||
tcp:
|
||||
host: xxx
|
||||
host: 25on621889.goho.co
|
||||
port: 43161
|
||||
interval: 10000
|
||||
|
|
|
@ -3,14 +3,21 @@
|
|||
"id": "host",
|
||||
"name": "服务端ip",
|
||||
"type": "text",
|
||||
"value": "0.0.0.0",
|
||||
"value": "25on621889.goho.co",
|
||||
"desc": "服务端ip"
|
||||
},
|
||||
{
|
||||
"id": "port",
|
||||
"name": "服务端端口",
|
||||
"type": "number",
|
||||
"value": 1111,
|
||||
"value": 43161,
|
||||
"desc": "服务端端口"
|
||||
},
|
||||
{
|
||||
"id": "interval",
|
||||
"name": "采集频率",
|
||||
"type": "number",
|
||||
"value": 10000,
|
||||
"desc": "采集频率"
|
||||
}
|
||||
]
|
|
@ -0,0 +1,64 @@
|
|||
<?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>hydrovalve-plugin</artifactId>
|
||||
|
||||
<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>
|
||||
<version>${spring-brick-package.version}</version>
|
||||
<configuration>
|
||||
<mode>${plugin.build.mode}</mode>
|
||||
<pluginInfo>
|
||||
<id>hydrovalve-plugin</id>
|
||||
<bootstrapClass>cc.iotkit.plugins.hydrovalve.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>
|
|
@ -0,0 +1,22 @@
|
|||
package cc.iotkit.plugins.hydrovalve;
|
||||
|
||||
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:tfd
|
||||
* @Date:2024/1/8 14:57
|
||||
*/
|
||||
@SpringBootApplication(scanBasePackages = {"cc.iotkit.plugin.core", "cc.iotkit.plugins.hydrovalve"})
|
||||
@OneselfConfig(mainConfigFileName = {"application.yml"})
|
||||
@EnableConfigurationProperties
|
||||
@EnableScheduling
|
||||
public class Application extends SpringPluginBootstrap {
|
||||
|
||||
public static void main(String[] args) {
|
||||
new Application().run(Application.class, args);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package cc.iotkit.plugins.hydrovalve.analysis;
|
||||
|
||||
/**
|
||||
* @Author:tfd
|
||||
* @Date:2024/1/9 15:41
|
||||
*/
|
||||
public abstract class ModBusAnalysis {
|
||||
/**
|
||||
* 编码:将实体打包成报文
|
||||
*
|
||||
* @param entity 实体
|
||||
* @return 数据报文
|
||||
*/
|
||||
public abstract byte[] packCmd4Entity(ModBusEntity entity);
|
||||
|
||||
/**
|
||||
* 解包:将报文解码成实体
|
||||
*
|
||||
* @param arrCmd 报文
|
||||
* @return 实体
|
||||
*/
|
||||
public abstract ModBusEntity unPackCmd2Entity(byte[] arrCmd);
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package cc.iotkit.plugins.hydrovalve.analysis;
|
||||
|
||||
/**
|
||||
* @Author:tfd
|
||||
* @Date:2024/1/9 15:45
|
||||
*/
|
||||
public class ModBusConstants {
|
||||
public static final String MODE = "modbusMode";
|
||||
public static final String MODE_ASCII = "ASCII";
|
||||
public static final String MODE_RTU = "RTU";
|
||||
public static final String MODE_TCP = "TCP";
|
||||
|
||||
/**
|
||||
* ModBus的报文结构
|
||||
*/
|
||||
public static final String SN = "sn";
|
||||
public static final String ADDR = "devAddr";
|
||||
public static final String FUNC = "func";
|
||||
public static final String DATA = "data";
|
||||
|
||||
/**
|
||||
* 寄存器地址和数量
|
||||
*/
|
||||
public static final String REG_ADDR = "regAddr";
|
||||
public static final String REG_CNT = "regCnt";
|
||||
|
||||
public static final String REG_HOLD_STATUS = "regHoldStatus";
|
||||
|
||||
//读多个保持寄存器
|
||||
public static final byte FUN_CODE3 = 0x03;
|
||||
|
||||
//写单个保持寄存器
|
||||
public static final byte FUN_CODE6 = 0x06;
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package cc.iotkit.plugins.hydrovalve.analysis;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* @Author:tfd
|
||||
* @Date:2024/1/9 15:44
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class ModBusEntity {
|
||||
/**
|
||||
* 流水号
|
||||
*/
|
||||
private int sn = 0;
|
||||
|
||||
/**
|
||||
* 地址
|
||||
*/
|
||||
private byte devAddr = 0x01;
|
||||
|
||||
/**
|
||||
* 功能码
|
||||
*/
|
||||
private byte func = 0x01;
|
||||
|
||||
/**
|
||||
* 数据域
|
||||
*/
|
||||
private byte[] data = new byte[0];
|
||||
|
||||
/**
|
||||
* 出错信息
|
||||
*/
|
||||
private int errCode = 0;
|
||||
|
||||
/**
|
||||
* 出错信息
|
||||
*/
|
||||
private String errMsg = "";
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
package cc.iotkit.plugins.hydrovalve.analysis;
|
||||
|
||||
/**
|
||||
* @Author:tfd
|
||||
* @Date:2024/1/9 15:48
|
||||
*/
|
||||
public class ModBusError {
|
||||
static final String err01 = "err=01:非法的功能码";
|
||||
static final String err02 = "err=02:非法的数据地址";
|
||||
static final String err03 = "err=03:非法的数据值";
|
||||
static final String err04 = "err=04:服务器故障";
|
||||
static final String err05 = "err=05:确认。";
|
||||
static final String err06 = "err=06:服务器繁忙";
|
||||
static final String err10 = "err=10:网关故障:网关路经是无效的";
|
||||
static final String err11 = "err=11:网关故障:目标设备没有响应";
|
||||
|
||||
/**
|
||||
* 获取出错信息
|
||||
* @param code 出错代码
|
||||
* @return 出错信息
|
||||
*/
|
||||
static String getError(int code) {
|
||||
if (code == 1) {
|
||||
return err01;
|
||||
}
|
||||
if (code == 2) {
|
||||
return err02;
|
||||
}
|
||||
if (code == 3) {
|
||||
return err03;
|
||||
}
|
||||
if (code == 4) {
|
||||
return err04;
|
||||
}
|
||||
if (code == 5) {
|
||||
return err05;
|
||||
}
|
||||
if (code == 6) {
|
||||
return err06;
|
||||
}
|
||||
if (code == 10) {
|
||||
return err10;
|
||||
}
|
||||
if (code == 11) {
|
||||
return err11;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,171 @@
|
|||
package cc.iotkit.plugins.hydrovalve.analysis;
|
||||
|
||||
import cc.iotkit.common.utils.StringUtils;
|
||||
import cc.iotkit.plugins.hydrovalve.utils.ByteUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* @Author:tfd
|
||||
* @Date:2024/1/9 15:53
|
||||
*/
|
||||
@Slf4j
|
||||
public class ModBusRtuAnalysis extends ModBusAnalysis {
|
||||
/**
|
||||
* 校验CRC16
|
||||
*
|
||||
* @param arrCmd
|
||||
* @return
|
||||
*/
|
||||
public static int getCRC16(byte[] arrCmd) {
|
||||
int iSize = arrCmd.length - 2;
|
||||
|
||||
// 检查:帧长度
|
||||
if (iSize < 2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wCrcMathematics = 0xA001;
|
||||
|
||||
int usCrc16 = 0x00;
|
||||
|
||||
//16位的CRC寄存器
|
||||
int byteCrc16Lo = 0xFF;
|
||||
int byteCrc16Hi = 0xFF;
|
||||
//临时变量
|
||||
int byteSaveHi = 0x00;
|
||||
int byteSaveLo = 0x00;
|
||||
|
||||
//CRC多项式码的寄存器
|
||||
int byteCl = wCrcMathematics % 0x100;
|
||||
int byteCh = wCrcMathematics / 0x100;
|
||||
|
||||
for (int i = 0; i < iSize; i++) {
|
||||
byteCrc16Lo &= 0xFF;
|
||||
byteCrc16Hi &= 0xFF;
|
||||
byteSaveHi &= 0xFF;
|
||||
byteSaveLo &= 0xFF;
|
||||
|
||||
byteCrc16Lo ^= arrCmd[i]; //每一个数据与CRC寄存器进行异或
|
||||
for (int k = 0; k < 8; k++) {
|
||||
byteCrc16Lo &= 0xFF;
|
||||
byteCrc16Hi &= 0xFF;
|
||||
|
||||
byteSaveHi = byteCrc16Hi;
|
||||
byteSaveLo = byteCrc16Lo;
|
||||
byteCrc16Hi /= 2; //高位右移一位
|
||||
byteCrc16Lo /= 2; //低位右移一位
|
||||
if ((byteSaveHi & 0x01) == 0x01) //如果高位字节最后一位为1
|
||||
{
|
||||
byteCrc16Lo |= 0x80; //则低位字节右移后前面补1
|
||||
} //否则自动补0
|
||||
if ((byteSaveLo & 0x01) == 0x01) //如果高位字节最后一位为1,则与多项式码进行异或
|
||||
{
|
||||
byteCrc16Hi ^= byteCh;
|
||||
byteCrc16Lo ^= byteCl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
usCrc16 = (byteCrc16Hi & 0xff) * 0x100 + (byteCrc16Lo & 0xff);
|
||||
|
||||
return usCrc16;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解码
|
||||
*
|
||||
* @param arrCmd 报文
|
||||
* @return 是否成功
|
||||
*/
|
||||
@Override
|
||||
public ModBusEntity unPackCmd2Entity(byte[] arrCmd) {
|
||||
ModBusEntity entity = new ModBusEntity();
|
||||
|
||||
int iSize = arrCmd.length;
|
||||
if (iSize < 4) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 地址码
|
||||
byte byAddr = arrCmd[0];
|
||||
entity.setDevAddr(byAddr);
|
||||
|
||||
// 功能码
|
||||
byte byFun = arrCmd[1];
|
||||
entity.setFunc(byFun);
|
||||
|
||||
// 数据域
|
||||
int iDataSize = iSize - 4;
|
||||
entity.setData(new byte[iDataSize]);
|
||||
byte[] arrData = entity.getData();
|
||||
System.arraycopy(arrCmd, 2, arrData, 0, iDataSize);
|
||||
|
||||
// 校验CRC
|
||||
int wCrc16OK = getCRC16(arrCmd);
|
||||
byte crcH = (byte) (wCrc16OK & 0xff);
|
||||
byte crcL = (byte) ((wCrc16OK & 0xff00) >> 8);
|
||||
if (arrCmd[arrCmd.length - 1] == crcL && arrCmd[arrCmd.length - 2] == crcH) {
|
||||
return entity;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 编码
|
||||
*
|
||||
* @return 编码是否成功
|
||||
*/
|
||||
@Override
|
||||
public byte[] packCmd4Entity(ModBusEntity entity) {
|
||||
int iSize = entity.getData().length;
|
||||
|
||||
byte[] arrCmd = new byte[iSize + 4];
|
||||
|
||||
// 地址码
|
||||
arrCmd[0] = entity.getDevAddr();
|
||||
|
||||
// 功能码
|
||||
arrCmd[1] = entity.getFunc();
|
||||
|
||||
// 数据域
|
||||
System.arraycopy(entity.getData(), 0, arrCmd, 2, iSize);
|
||||
|
||||
// 校验CRC
|
||||
int wCrc16 = getCRC16(arrCmd);
|
||||
arrCmd[arrCmd.length - 2] = (byte) (wCrc16 % 0x100);
|
||||
arrCmd[arrCmd.length - 1] = (byte) (wCrc16 / 0x100);
|
||||
|
||||
return arrCmd;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// String hexString = "0103020457FB7A";
|
||||
String hexString = "01060001000119CA";
|
||||
ModBusRtuAnalysis a=new ModBusRtuAnalysis();
|
||||
ModBusEntity b=a.unPackCmd2Entity(ByteUtils.hexStrToBinaryStr(hexString));
|
||||
int lenth=b.getData()[0];
|
||||
byte[] val = new byte[lenth];
|
||||
System.arraycopy(b.getData(), 1, val, 0, lenth);
|
||||
log.info("ret:"+Integer.parseInt(ByteUtils.BinaryToHexString(val,false), 16));
|
||||
ModBusEntity c=new ModBusEntity();
|
||||
c.setDevAddr((byte) 1);
|
||||
c.setFunc(ModBusConstants.FUN_CODE3);
|
||||
Integer dz=0;
|
||||
Integer dzsl=10;
|
||||
String a1=StringUtils.leftPad(dz.toHexString(dz),4,'0')+StringUtils.leftPad(dz.toHexString(dzsl),4,'0');
|
||||
c.setData(ByteUtils.hexStrToBinaryStr(a1));
|
||||
byte[] d=a.packCmd4Entity(c);
|
||||
log.info("ret1:"+ByteUtils.BinaryToHexString(d,false));
|
||||
ModBusEntity e=new ModBusEntity();
|
||||
e.setDevAddr((byte) 1);
|
||||
e.setFunc(ModBusConstants.FUN_CODE6);
|
||||
Integer dze=0;
|
||||
Integer dzsle=1;
|
||||
String a1e=StringUtils.leftPad(dz.toHexString(dze),4,'0')+StringUtils.leftPad(dz.toHexString(dzsle),4,'0');
|
||||
e.setData(ByteUtils.hexStrToBinaryStr(a1e));
|
||||
byte[] f=a.packCmd4Entity(e);
|
||||
log.info("rete:"+ByteUtils.BinaryToHexString(f,false));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package cc.iotkit.plugins.hydrovalve.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.hydrovalve.service.FakeThingService;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @Author:tfd
|
||||
* @Date:2024/1/8 14:58
|
||||
*/
|
||||
@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("script.js");
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = "plugin.runMode", havingValue = "dev")
|
||||
IPluginConfig getPluginConfig(){
|
||||
return new LocalPluginConfig();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package cc.iotkit.plugins.hydrovalve.conf;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @Author:tfd
|
||||
* @Date:2024/1/8 15:04
|
||||
*/
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "hydrovalve")
|
||||
public class HydrovalveConfig {
|
||||
|
||||
private String host;
|
||||
|
||||
private int port;
|
||||
|
||||
private int interval;
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package cc.iotkit.plugins.hydrovalve.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:tfd
|
||||
* @Date:2024/1/8 14:58
|
||||
*/
|
||||
@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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package cc.iotkit.plugins.hydrovalve.service;
|
||||
|
||||
import cc.iotkit.common.utils.StringUtils;
|
||||
import cc.iotkit.plugin.core.thing.IDevice;
|
||||
import cc.iotkit.plugin.core.thing.actions.ActionResult;
|
||||
import cc.iotkit.plugin.core.thing.actions.down.DeviceConfig;
|
||||
import cc.iotkit.plugin.core.thing.actions.down.PropertyGet;
|
||||
import cc.iotkit.plugin.core.thing.actions.down.PropertySet;
|
||||
import cc.iotkit.plugin.core.thing.actions.down.ServiceInvoke;
|
||||
import cc.iotkit.plugins.hydrovalve.analysis.ModBusConstants;
|
||||
import cc.iotkit.plugins.hydrovalve.analysis.ModBusEntity;
|
||||
import cc.iotkit.plugins.hydrovalve.analysis.ModBusRtuAnalysis;
|
||||
import cc.iotkit.plugins.hydrovalve.utils.ByteUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @Author:tfd
|
||||
* @Date:2024/1/10 11:06
|
||||
*/
|
||||
@Service
|
||||
public class ModBusDevice implements IDevice {
|
||||
|
||||
@Autowired
|
||||
private ModbusVerticle modbusVerticle;
|
||||
|
||||
ModBusRtuAnalysis analysis=new ModBusRtuAnalysis();
|
||||
|
||||
@Override
|
||||
public ActionResult config(DeviceConfig action) {
|
||||
return ActionResult.builder().code(0).reason("").build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult propertyGet(PropertyGet action) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult propertySet(PropertySet action) {
|
||||
ModBusEntity read=new ModBusEntity();
|
||||
String devAddr=action.getDeviceName().split("_")[1];
|
||||
read.setFunc(ModBusConstants.FUN_CODE6);
|
||||
read.setDevAddr(Byte.parseByte(devAddr));
|
||||
Integer addr=0;
|
||||
for (Map.Entry<String, ?> entry : action.getParams().entrySet()) {
|
||||
int val = Integer.parseInt((String) entry.getValue());
|
||||
String a1= StringUtils.leftPad(addr.toHexString(addr),4,'0')+StringUtils.leftPad(addr.toHexString(val),4,'0');
|
||||
read.setData(ByteUtils.hexStrToBinaryStr(a1));
|
||||
byte[] msg = analysis.packCmd4Entity(read);
|
||||
modbusVerticle.sendMsg(msg);
|
||||
}
|
||||
return ActionResult.builder().code(0).reason("success").build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult serviceInvoke(ServiceInvoke action) {
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
package cc.iotkit.plugins.hydrovalve.service;
|
||||
|
||||
import cc.iotkit.common.utils.JsonUtils;
|
||||
import cc.iotkit.plugin.core.IPluginConfig;
|
||||
import cc.iotkit.plugin.core.IPluginScript;
|
||||
import cc.iotkit.plugin.core.thing.IThingService;
|
||||
import cc.iotkit.plugins.hydrovalve.conf.HydrovalveConfig;
|
||||
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;
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.core.Vertx;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @Author:tfd
|
||||
* @Date:2024/1/8 14:57
|
||||
*/
|
||||
@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;
|
||||
|
||||
@Autowired
|
||||
@AutowiredType(AutowiredType.Type.MAIN_PLUGIN)
|
||||
private IPluginConfig pluginConfig;
|
||||
|
||||
@Autowired
|
||||
private HydrovalveConfig modbusConfig;
|
||||
|
||||
@Autowired
|
||||
private ModbusVerticle modbusVerticle;
|
||||
|
||||
private Vertx vertx;
|
||||
private String deployedId;
|
||||
|
||||
@PostConstruct
|
||||
public void init(){
|
||||
vertx = Vertx.vertx();
|
||||
try {
|
||||
//获取插件最新配置替换当前配置
|
||||
Map<String, Object> config = pluginConfig.getConfig(pluginInfo.getPluginId());
|
||||
BeanUtil.copyProperties(config, modbusConfig, CopyOptions.create().ignoreNullValue());
|
||||
modbusVerticle.setModbusConfig(modbusConfig);
|
||||
|
||||
Future<String> future = vertx.deployVerticle(modbusVerticle);
|
||||
future.onSuccess((s -> {
|
||||
deployedId = s;
|
||||
log.info("modbus plugin started success,config:"+ JsonUtils.toJsonString(modbusConfig));
|
||||
}));
|
||||
future.onFailure(Throwable::printStackTrace);
|
||||
} catch (Throwable e) {
|
||||
log.error("modbus plugin error", e);
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public void close(GenericApplicationContext applicationContext, PluginInfo pluginInfo, PluginCloseType closeType) {
|
||||
log.info("plugin close,type:{},pluginId:{}", closeType, pluginInfo.getPluginId());
|
||||
if (deployedId != null) {
|
||||
CountDownLatch wait = new CountDownLatch(1);
|
||||
Future<Void> future = vertx.undeploy(deployedId);
|
||||
future.onSuccess(unused -> {
|
||||
log.info("modbus plugin stopped success");
|
||||
wait.countDown();
|
||||
});
|
||||
future.onFailure(h -> {
|
||||
log.info("modbus plugin stopped failed");
|
||||
h.printStackTrace();
|
||||
wait.countDown();
|
||||
});
|
||||
wait.await(5, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,182 @@
|
|||
package cc.iotkit.plugins.hydrovalve.service;
|
||||
|
||||
import cc.iotkit.common.utils.StringUtils;
|
||||
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.plugins.hydrovalve.analysis.ModBusConstants;
|
||||
import cc.iotkit.plugins.hydrovalve.analysis.ModBusEntity;
|
||||
import cc.iotkit.plugins.hydrovalve.analysis.ModBusRtuAnalysis;
|
||||
import cc.iotkit.plugins.hydrovalve.conf.HydrovalveConfig;
|
||||
import cc.iotkit.plugins.hydrovalve.utils.ByteUtils;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import com.gitee.starblues.bootstrap.annotation.AutowiredType;
|
||||
import com.gitee.starblues.core.PluginInfo;
|
||||
import io.vertx.core.AbstractVerticle;
|
||||
import io.vertx.core.buffer.Buffer;
|
||||
import io.vertx.core.net.NetClient;
|
||||
import io.vertx.core.net.NetClientOptions;
|
||||
import io.vertx.core.net.NetSocket;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @Author:tfd
|
||||
* @Date:2024/1/9 17:06
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class ModbusVerticle extends AbstractVerticle {
|
||||
@Getter
|
||||
@Setter
|
||||
private HydrovalveConfig modbusConfig;
|
||||
|
||||
private NetClient netClient;
|
||||
|
||||
private NetSocket socket;
|
||||
|
||||
@Autowired
|
||||
@AutowiredType(AutowiredType.Type.MAIN_PLUGIN)
|
||||
private IThingService thingService;
|
||||
|
||||
ModBusRtuAnalysis analysis=new ModBusRtuAnalysis();
|
||||
|
||||
@Autowired
|
||||
private PluginInfo pluginInfo;
|
||||
|
||||
private int connectState = 0;
|
||||
|
||||
private long timerID;
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
log.info("init start");
|
||||
}
|
||||
|
||||
@Scheduled(initialDelay = 2, fixedRate = 5, timeUnit = TimeUnit.SECONDS)
|
||||
public void initClient() {
|
||||
if (connectState > 0) {
|
||||
return;
|
||||
}
|
||||
connectState = 1;
|
||||
NetClientOptions options = new NetClientOptions();
|
||||
options.setReconnectAttempts(Integer.MAX_VALUE);
|
||||
options.setReconnectInterval(20000L);
|
||||
netClient = vertx.createNetClient(options);
|
||||
netClient.connect(modbusConfig.getPort(), modbusConfig.getHost())
|
||||
.onComplete(result -> {
|
||||
if (result.succeeded()) {
|
||||
connectState = 2;
|
||||
log.info("connect modbus slave success");
|
||||
socket = result.result();
|
||||
thingService.post(pluginInfo.getPluginId(), DeviceRegister.builder()
|
||||
.id(UUID.randomUUID().toString())
|
||||
.productKey("PYWH4r8xBzsfn3XB")
|
||||
.deviceName(String.format("modbus_%d", 1))
|
||||
.build());
|
||||
stateChange(DeviceState.ONLINE,String.format("modbus_%d", 1));
|
||||
socket.handler(data -> {
|
||||
String hexStr = ByteUtils.BinaryToHexString(data.getBytes(), false);
|
||||
log.info("modbus received message:{}", hexStr);
|
||||
//获取功能码
|
||||
if(0x03==data.getBytes()[1]){
|
||||
ModBusEntity ret = analysis.unPackCmd2Entity(data.getBytes());
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("devSwith" , Integer.parseInt(ByteUtils.BinaryToHexString(getData(ret.getData()),false)));//数据标识
|
||||
thingService.post(pluginInfo.getPluginId(),
|
||||
PropertyReport.builder().deviceName(String.format("modbus_%d", ret.getDevAddr())).productKey("PwMfpXmp4ZWkGahn")
|
||||
.params(params)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
}).closeHandler(res -> {
|
||||
connectState = 0;
|
||||
vertx.cancelTimer(timerID);
|
||||
log.info("modbus tcp connection closed!");
|
||||
stateChange(DeviceState.OFFLINE,String.format("modbus_%d", 1));
|
||||
}
|
||||
).exceptionHandler(res -> {
|
||||
connectState = 0;
|
||||
vertx.cancelTimer(timerID);
|
||||
log.info("modbus tcp connection exce!");
|
||||
stateChange(DeviceState.OFFLINE,String.format("modbus_%d", 1));
|
||||
});
|
||||
timerID = vertx.setPeriodic(modbusConfig.getInterval(), t -> {
|
||||
readDataTask();
|
||||
});
|
||||
} else {
|
||||
connectState = 0;
|
||||
log.info("connect modbus tcp error", result.cause());
|
||||
}
|
||||
})
|
||||
.onFailure(e -> {
|
||||
log.error("modbus connect failed", e);
|
||||
connectState = 0;
|
||||
})
|
||||
;
|
||||
}
|
||||
|
||||
private void readDataTask() {
|
||||
log.info("readData:" + socket);
|
||||
if (socket != null) {
|
||||
ModBusEntity read=new ModBusEntity();
|
||||
read.setFunc(ModBusConstants.FUN_CODE3);
|
||||
read.setDevAddr((byte) 1);
|
||||
Integer addr=1;
|
||||
Integer length=1;
|
||||
String a1= StringUtils.leftPad(addr.toHexString(addr),4,'0')+StringUtils.leftPad(addr.toHexString(length),4,'0');
|
||||
read.setData(ByteUtils.hexStrToBinaryStr(a1));
|
||||
byte[] msg = analysis.packCmd4Entity(read);
|
||||
sendMsg(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() throws Exception {
|
||||
if (netClient != null) {
|
||||
netClient.close();
|
||||
}
|
||||
vertx.cancelTimer(timerID);
|
||||
connectState = 0;
|
||||
super.stop();
|
||||
}
|
||||
|
||||
private byte[] getData(byte[] data) {
|
||||
int lenth=data[0];
|
||||
byte[] val = new byte[lenth];
|
||||
System.arraycopy(data, 1, val, 0, lenth);
|
||||
return val;
|
||||
}
|
||||
|
||||
private void stateChange(DeviceState state,String deviceName) {
|
||||
thingService.post(pluginInfo.getPluginId(),
|
||||
DeviceStateChange.builder()
|
||||
.id(IdUtil.simpleUUID())
|
||||
.state(state).productKey("PYWH4r8xBzsfn3XB").deviceName(deviceName)
|
||||
.time(System.currentTimeMillis())
|
||||
.build());
|
||||
}
|
||||
|
||||
public void sendMsg(byte[] msg) {
|
||||
log.info("modbus send msg data:{}", ByteUtils.BinaryToHexString(msg,false));
|
||||
Buffer data = Buffer.buffer(msg);
|
||||
socket.write(data, r -> {
|
||||
if (r.succeeded()) {
|
||||
log.info("modbus msg send success:{}", ByteUtils.BinaryToHexString(msg,false));
|
||||
} else {
|
||||
log.error("modbus msg send failed", r.cause());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package cc.iotkit.plugins.hydrovalve.utils;
|
||||
|
||||
/**
|
||||
* @Author:tfd
|
||||
* @Date:2024/1/8 15:15
|
||||
*/
|
||||
public class ByteUtils {
|
||||
/**
|
||||
* 将十六进制的字符串转换成字节数组
|
||||
*
|
||||
* @param hexString
|
||||
* @return
|
||||
*/
|
||||
public static byte[] hexStrToBinaryStr(String hexString) {
|
||||
|
||||
if (hexString==null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
hexString = hexString.replaceAll(" ", "");
|
||||
int len = hexString.length();
|
||||
int index = 0;
|
||||
byte[] bytes = new byte[len / 2];
|
||||
while (index < len) {
|
||||
String sub = hexString.substring(index, index + 2);
|
||||
bytes[index/2] = (byte)Integer.parseInt(sub,16);
|
||||
index += 2;
|
||||
}
|
||||
return bytes;
|
||||
}catch (Exception e){
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字节数组转换成十六进制的字符串
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static String BinaryToHexString(byte[] bytes,boolean isBalank) {
|
||||
String hexStr = "0123456789ABCDEF";
|
||||
String result = "";
|
||||
String hex = "";
|
||||
Boolean feStart=true;
|
||||
for (byte b : bytes) {
|
||||
hex = String.valueOf(hexStr.charAt((b & 0xF0) >> 4));
|
||||
hex += String.valueOf(hexStr.charAt(b & 0x0F));
|
||||
if("FE".equals(hex) && feStart){
|
||||
continue;
|
||||
}else {
|
||||
feStart=false;
|
||||
}
|
||||
result += hex + (isBalank?" ":"");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
plugin:
|
||||
runMode: prod
|
||||
mainPackage: cc.iotkit.plugin
|
||||
|
||||
hydrovalve:
|
||||
host: 25on621889.goho.co
|
||||
port: 38807
|
||||
interval: 20000
|
|
@ -0,0 +1,23 @@
|
|||
[
|
||||
{
|
||||
"id": "host",
|
||||
"name": "服务端ip",
|
||||
"type": "text",
|
||||
"value": "25on621889.goho.co",
|
||||
"desc": "服务端ip"
|
||||
},
|
||||
{
|
||||
"id": "port",
|
||||
"name": "服务端端口",
|
||||
"type": "number",
|
||||
"value": 38807,
|
||||
"desc": "服务端端口"
|
||||
},
|
||||
{
|
||||
"id": "interval",
|
||||
"name": "采集频率",
|
||||
"type": "number",
|
||||
"value": 20000,
|
||||
"desc": "采集频率"
|
||||
}
|
||||
]
|
Loading…
Reference in New Issue