本指南环境:
系统 : win7
JDK: 1.8.0_201
Maven: 3.3.9
IDE : IntelliJ IDEA2017
NODEJS: npm 6.14.5
docker: 19.03.1
win10家庭版和win7安装docker
https://blog.csdn.net/vitaair/article/details/80894890
#在开发模式下进行的
# Docker Toolbox 默认IP 是192.168.99.100
# Added by DC3
192.168.99.100 dc3.com
192.168.99.100 dc3-mysql
192.168.99.100 dc3-redis
192.168.99.100 dc3-mongo
192.168.99.100 dc3-rabbitmq
192.168.99.100 dc3-register
192.168.99.100 dc3-nginx
# dev
192.168.99.100 dc3-auth
127.0.0.1 dc3-manager
127.0.0.1 dc3-data
所以将在docker中部署的改为192.168.99.100
在IDEA中启动的 127.0.0.1即可
测试驱动是时需保证以上基础服务开启状态
启动 ModbusTcpDriverApplication
启动成功在驱动页面 看到ModbusDriver 状态在线
查看属性【驱动属性】【位号属性】
点击【驱动属性页面】 查看需要的配置的属性,点击【位号属性页面】查看监控点需要配置的属性
在 application.yml可修改相应的说明:
【驱动属性】
driver-attribute:
- displayName: 主机
name: host
type: string
value: localhost
description: Modbus IP
- displayName: 端口
name: port
type: int
value: 502
description: Modbus Port
【位号属性】
point-attribute:
- displayName: 从站编号
name: slaveId
type: int
value: 1
description: 从站编号
- displayName: 功能码
name: functionCode
type: int
value: 1
description: 功能码 [1、2、3、4]
- displayName: 偏移量
name: offset
type: int
value: 0
description: 偏移量
添加模板
【模板页面】
新建模板 选中ModbusTcp驱动
配置驱动属性
【驱动配置页面】
ModbusTcp驱动需要配置主机和端口号
需要新增2条
所属模板 3中创建的模板
属性 需要配置的主机和端口号
**单条只能配置一项属性**
位号
【位号页面】
位号即监控点名称
在此新建需要监控的点位,并对点位的读写属性/转换等进行设置
此处监控点位并没有绑定设备/驱动,所有驱动的设置相同
分组
新建设备分组
设备
新建设备 绑定目标驱动
位号配置
需要选择在5中新建的位号,绑定7设备
配置位号属性
一条记录只能配置单一属性,对于 ModbusTcp需要配置 从站ID/偏移量/功能码 三个属性,需要新建3条记录,一次配置
从站编号: 即modbus从站的编号
功能码: 1(读取线圈状态) 2(读取输入状态) 3(读取保存寄存器) 4(读取输入寄存器) ...
偏移量:监控点的地址
Posman批量导入
json.文件
[
{
//驱动名称
"serviceName": "dc3-driver-modbus-tcp",
"profiles": [
{
//驱动模板名称
"name": "Modbus-Tcp-Local",
"share": true,
//驱动属性配置
"driverConfig": {
"host": "127.0.0.1",
"port": "502"
},
//监控点处理配置
"points": [
{
"name": "Modbus-Tcp-Point-1",
"type": "string",
"rw": 0,
"base": 0,
"minimum": -999999,
"maximum": 999999,
"multiple": 1,
"accrue": false,
"format": "%.3f",
"unit": "-"
},
{
"name": "Modbus-Tcp-Point-2",
"type": "boolean",
"rw": 0,
"base": 0,
"minimum": -999999,
"maximum": 999999,
"multiple": 1,
"accrue": false,
"format": "%.3f",
"unit": "-"
},
{
"name": "Modbus-Tcp-Point-3",
"type": "int",
"rw": 0,
"base": 0,
"minimum": -999999,
"maximum": 999999,
"multiple": 1,
"accrue": false,
"format": "%.3f",
"unit": "-"
},
{
"name": "Modbus-Tcp-Point-4",
"type": "int",
"rw": 0,
"base": 0,
"minimum": -999999,
"maximum": 999999,
"multiple": 1,
"accrue": false,
"format": "%.3f",
"unit": "-"
}
],
"groups": [
{
//组名
"name": "设备分组1",
"devices": [
{
//设备名
"name": "Modbus模拟设备1",
"pointConfig": {
//监控点属性配置
"Modbus-Tcp-Point-1": {
"functionCode": 3,
"slaveId": 1,
"offset":1
},
"Modbus-Tcp-Point-2": {
"functionCode": 3,
"slaveId": 1,
"offset":2
},
"Modbus-Tcp-Point-3": {
"functionCode": 3,
"slaveId": 1,
"offset":3
},
"Modbus-Tcp-Point-4": {
"functionCode": 4,
"slaveId": 1,
"offset":3
}
}
}
]
}
]
}
]
}
]
*模拟工具使用MatrikonOPC Server和KEPServerEX 6 *
步骤与ModbusTcp相同
启动 OpcDaApplication
启动成功在驱动页面 看到OpcDaDriver dc3-driver-opc-da 状态在线
查看属性【驱动属性】【位号属性】
点击【驱动属性页面】 查看需要的配置的属性,点击【位号属性页面】查看监控点需要配置的属性
在 application.yml可修改相应的说明:
【数据读取】
schedule:
【默认实现30S插入mongo】
read:
enable: true
corn: '0/30 * * * * ?'
【自定义需要自己实现】
custom:
enable: true
corn: '0/5 * * * * ?'
【驱动属性】
driver-attribute:
- displayName: 主机
name: host
type: string
value: localhost
description: Opc Da Host
- displayName: CLSID
name: clsId
type: string
value: F8582CF2-88FB-11D0-B850-00C0F0104305
description: Opc Da Server CLAID
- displayName: 用户名
name: username
type: string
value: dc3
description: Opc Da UserName
- displayName: 密码
name: password
type: string
value: dc3dc3
description: Opc Da Passward
【位号属性】
point-attribute:
- displayName: 分组
name: group
type: string
value: GROUP
description: 分组名称
- displayName: 位号
name: tag
type: string
value: TAG
description: 位号名称
添加模板
【模板页面】
新建驱动模板
配置驱动属性
【驱动配置页面】
OPC-UA驱动需要配置四个属性
主机 OPC服务器所在主机
用户名 OPC服务器所在主机电脑用户名
密码 OPC服务器所在主机电脑用户名
CLSID OPC服务器在系统中的注册标识,到windowns 注册列表中找
*用户名必须有OPC相应操作权限*
位号
【位号页面】同上
位号即监控点名称
在此新建需要监控的点位,并对点位的读写属性/转换等进行设置
此处监控点位并没有绑定设备/驱动,所有驱动的设置相同
是对原始值得转换
分组
新建设备分组
设备
新建设备 绑定目标驱动
位号配置
一个点位需要配置一下两个属性
位号 在OPC服务器中的名称
分组 可自定义
Posman批量导入
导入方式相同
json.文件【MatrikonOPC Server】
[
{
"serviceName": "dc3-driver-opc-da",
"profiles": [
{
"name": "OPC-DA-Local",
"share": true,
"driverConfig": {
"clsId": "F8582CF2-88FB-11D0-B850-00C0F0104305",
"password": "123",
"host":"127.0.0.1",
"username":"administrator"
},
"points": [
{
"name": "随机数1",
"type": "int",
"rw": 0,
"base": 0,
"minimum": -999999,
"maximum": 999999,
"multiple": 1,
"accrue": false,
"format": "%.3f",
"unit": "-"
},
{
"name": "随机数2",
"type": "int",
"rw": 0,
"base": 0,
"minimum": -999999,
"maximum": 999999,
"multiple": 1,
"accrue": false,
"format": "%.3f",
"unit": "-"
},
{
"name": "随机数3",
"type": "int",
"rw": 0,
"base": 0,
"minimum": -999999,
"maximum": 999999,
"multiple": 1,
"accrue": false,
"format": "%.3f",
"unit": "-"
},
{
"name": "随机数4",
"type": "int",
"rw": 0,
"base": 0,
"minimum": -999999,
"maximum": 999999,
"multiple": 1,
"accrue": false,
"format": "%.3f",
"unit": "-"
}
],
"groups": [
{
"name": "OPC-DA-默认组",
"devices": [
{
"name": "OPC模拟设备001",
"pointConfig": {
"随机数1": {
"group": "tag",
"tag": "tag.随机数1"
},
"随机数2": {
"group": "tag",
"tag": "tag.随机数2"
},
"随机数3": {
"group": "tag",
"tag": "tag.随机数3"
},
"随机数4": {
"group": "tag",
"tag": "tag.随机数4"
}
}
}
]
}
]
}
]
}
]
json.文件【KEPServerEX 6】
[
{
"serviceName": "dc3-driver-opc-da",
"profiles": [
{
"name": "OPC-DA-Local_02",
"share": true,
"driverConfig": {
"clsId": "7BC0CC8E-482C-47CA-ABDC-0FE7F9C6E729",
"password": "123",
"host":"127.0.0.1",
"username":"administrator"
},
"points": [
{
"name": "kepserver1",
"type": "int",
"rw": 0,
"base": 0,
"minimum": -999999,
"maximum": 999999,
"multiple": 1,
"accrue": false,
"format": "%.3f",
"unit": "-"
},
{
"name": "kepserver2",
"type": "int",
"rw": 0,
"base": 0,
"minimum": -999999,
"maximum": 999999,
"multiple": 1,
"accrue": false,
"format": "%.3f",
"unit": "-"
},
{
"name": "kepserver3",
"type": "int",
"rw": 0,
"base": 0,
"minimum": -999999,
"maximum": 999999,
"multiple": 1,
"accrue": false,
"format": "%.3f",
"unit": "-"
},
{
"name": "kepserver4",
"type": "int",
"rw": 0,
"base": 0,
"minimum": -999999,
"maximum": 999999,
"multiple": 1,
"accrue": false,
"format": "%.3f",
"unit": "-"
}
],
"groups": [
{
"name": "OPC-DA-默认组",
"devices": [
{
"name": "OPC模拟设备002",
"pointConfig": {
"kepserver1": {
"group": "kep",
"tag": "channel1.device1.point1"
},
"kepserver2": {
"group": "kep",
"tag": "channel1.device1.point2"
},
"kepserver3": {
"group": "kep",
"tag": "channel1.device1.point3"
},
"kepserver4": {
"group": "kep",
"tag": "channel1.device1.point4"
}
}
}
]
}
]
}
]
}
]
异常
1.连接中断,读取异常
https://gitee.com/pnoker/iot-dc3/issues/I1OUS4
可取消对连接的销毁
read方法
try {
Item item = getItem(server, pointInfo);
return readItem(item);
} finally {
//注解此处
// server.dispose();
}
2.在连接KEPServerEX 6时第一轮读数org.jinterop.dcom.core.VariantBody$EMPTY
之后会回复正常(暂未解决)
模拟工具使用Prosys OPC UA Simulation Server
Windows版链接:https://pan.baidu.com/s/1xXxziod-b8fMuNDj_wzsLQ
提取码:evaw
安装完毕打开如图启动成功
查看驱动连接属性
OPC-UA 驱动属性配置有三项 端口/主机/路径如图
OPC-UA 位号属性如上图需要配置命名空间和位号两个属性
通过网页插入,可参考ModbusTcp按步骤操作,步骤相同,不过是驱动属性和位号属性配置不同
OPC-UA驱动需要配置三项: 主机 端口 路径
每个位号都需配置两项: 命名空间 位号
批量导入方法也与ModbusTcp相同,一下给出json文件内容
[
{
"serviceName": "dc3-driver-opc-ua",
"profiles": [
{
"name": "OPC-UA-Local",
"share": true,
"driverConfig": {
"host": "127.0.0.1",
"port": "53530",
"path": "/OPCUA/SimulationServer"
},
"points": [
{
"name": "OpcUa-Point-1",
"type": "int",
"rw": 0,
"base": 0,
"minimum": -999999,
"maximum": 999999,
"multiple": 1,
"accrue": false,
"format": "%.3f",
"unit": "-"
},
{
"name": "OpcUa-Point-2",
"type": "boolean",
"rw": 0,
"base": 0,
"minimum": -999999,
"maximum": 999999,
"multiple": 1,
"accrue": false,
"format": "%.3f",
"unit": "-"
},
{
"name": "OpcUa-Point-3",
"type": "int",
"rw": 0,
"base": 0,
"minimum": -999999,
"maximum": 999999,
"multiple": 1,
"accrue": false,
"format": "%.3f",
"unit": "-"
},
{
"name": "OpcUa-Point-4",
"type": "long",
"rw": 0,
"base": 0,
"minimum": -999999,
"maximum": 999999,
"multiple": 1,
"accrue": false,
"format": "%.3f",
"unit": "-"
},
{
"name": "OpcUa-Point-5",
"type": "float",
"rw": 0,
"base": 0,
"minimum": -999999,
"maximum": 999999,
"multiple": 1,
"accrue": false,
"format": "%.3f",
"unit": "-"
}
],
"groups": [
{
"name": "OPC-UA-默认组",
"devices": [
{
"name": "OPC模拟设备003",
"pointConfig": {
"OpcUa-Point-1": {
"namespace": "5",
"tag": "OpcUa-Point-1"
},
"OpcUa-Point-2": {
"namespace": "5",
"tag": "OpcUa-Point-2"
},
"OpcUa-Point-3": {
"namespace": "5",
"tag": "OpcUa-Point-3"
},
"OpcUa-Point-4": {
"namespace": "5",
"tag": "OpcUa-Point-4"
},
"OpcUa-Point-5": {
"namespace": "5",
"tag": "OpcUa-Point-5"
}
}
}
]
}
]
}
]
}
]
网页配置不在描述,可看其他驱动详细配置
MQTT驱动与其他驱动配置略有不同,MQTT是发布——订阅模式,broker利用的是rabbitmq
/iot-dc3/dc3/dependences/rabbitmq/Dockerfile
在创建rabbitmq镜像时,设置了默认用户名密码
ENV RABBITMQ_DEFAULT_USER dc3
ENV RABBITMQ_DEFAULT_PASS dc3
在MqttDriverApplication配置文件如下
driver:
name: MqttDriver
description: @project.description@
schedule:
read:
#修改为false,发布订阅模式,为true则会出现值‘nil’
enable: false
corn: '0/30 * * * * ?'
custom:
enable: true
corn: '0/5 * * * * ?'
# driver-attribute,此属性无用,但是不配置会抛
# "profile(" + profileId + ") driver info does not exist" 异常
driver-attribute:
- displayName: 冗余
name: default
type: string
value: default
description: 冗余配置
# 点位属性配置 两个
# 若点位,只需要读,而不需要写则可以不配置
point-attribute:
# 驱动下发给设备(驱动发布主题/设备订阅主题)
- displayName: 指令Topic
name: commandTopic
type: string
value: commandTopic
description: 测点/设备接收下行指令的Mqtt主题
# MQTT消息质量
- displayName: 指令Qos
name: commandQos
type: int
value: 2
description: 测点/设备接收下行指令的Mqtt主题的Qos
# 这里是MQTT客户端的配置连接rabbit broker
mqtt:
username: dc3
password: dc3
url: tcp://dc3-rabbitmq:1883
qos:
- 0
#默认情况下,驱动订阅的主题时
# mqtt/group/device/#
# 在默认情况下,设备上报的主题则必须
# mqtt/group/device/自定义
# mqtt/group/device/自定义/自定义/....
# 驱动端才能收到设备端发布的内容
topics:
- mqtt/group/device/#
client:
id: dc3-mqtt-client
default:
topic: dc3-mqtt-topic
qos: 2
receive:
enable: true
keep-alive: 5
completion-timeout: 3000
server:
port: 8701
spring:
application:
name: @project.artifactId@
logging:
level:
com.dc3.common.sdk: DEBUG
com.dc3: DEBUG
file:
name: dc3/logs/driver/mqtt/${spring.application.name}.log
[
{
"serviceName": "dc3-driver-mqtt",
"profiles": [
{
"name": "MQTT-Local",
"share": true,
"driverConfig": {
"default":"default"
},
"points": [
{
"name": "MQTT-Point-1",
"type": "int",
"rw": 0,
"base": 0,
"minimum": -999999,
"maximum": 999999,
"multiple": 1,
"accrue": false,
"format": "%.3f",
"unit": "-"
},
{
"name": "MQTT-Point-2",
"type": "int",
"rw": 0,
"base": 0,
"minimum": -999999,
"maximum": 999999,
"multiple": 1,
"accrue": false,
"format": "%.3f",
"unit": "-"
},
{
"name": "MQTT-Point-3",
"type": "int",
"rw": 0,
"base": 0,
"minimum": -999999,
"maximum": 999999,
"multiple": 1,
"accrue": false,
"format": "%.3f",
"unit": "-"
}
{
"name": "MQTT-Point-4",
"type": "int",
"rw": 0,
"base": 0,
"minimum": -999999,
"maximum": 999999,
"multiple": 1,
"accrue": false,
"format": "%.3f",
"unit": "-"
},
],
"groups": [
{
"name": "MQTT-默认组",
"devices": [
{
"name": "MQTT模拟设备001",
"pointConfig": {
"MQTT-Point-3": {
"commandTopic": "mqtt/group/device/write/003",
"commandQos": "2"
},
"MQTT-Point-4": {
"commandTopic": "mqtt/group/device/write/004",
"commandQos": "2"
}
}
}
]
}
]
}
]
}
]
上述导入了4个点位 MQTT-Point-1和2只读,没有位号配置 3 4 读写
主题都是“mqtt/group/device/write”,即驱动端在此主题发布信息,设备端订阅此主题
设备(发布)——》Rabbit《——(订阅)驱动
# 默认情况下 设备发布消息的物模型如下
# id获取方式可参考API使用postman获取
{
# 设备ID
deviceId:1,
#点位ID
pointId:1,
# 值
value:"1212"
}
#查询设备信息
#请求API(POST) http://localhost:8400/manager/device/list
#请求参数
{
"name": "MQTT模拟设备001",
"page": {
"current": 1,
"orders": [
{
"column": "id",
"asc": false
}
]
}
}
#返回
{
"ok": true,
"message": "ok",
"data": {
"records": [
{
#设备ID
"id": 1,
"description": "批量导入:新增",
"createTime": "2020-07-25 10:20:47",
"updateTime": "2020-07-25 10:20:47",
"name": "MQTT模拟设备001",
#驱动ID
"profileId": 1,
"groupId": 1
}
],
"total": 1,
"size": 20,
"current": 1,
"orders": [
{
"column": "id",
"asc": false
},
{
"column": "create_time",
"asc": false
}
],
"optimizeCountSql": true,
"hitCount": false,
"searchCount": true,
"pages": 1
}
}
#查询点位ID
#API(post) http://localhost:8400/manager/point/list
#请求参数
{
"profileId": 1,
"page": {
"current": 1,
"orders": [
{
"column": "id",
"asc": false
}
]
}
}
#返回
{
"ok": true,
"message": "ok",
"data": {
"records": [
{
#点位ID
"id": 4,
"description": "批量导入:新增",
"createTime": "2020-07-25 10:20:46",
"updateTime": "2020-07-25 10:20:46",
# 点位名称
"name": "MQTT-Point-4",
"type": "int",
"rw": 0,
"base": 0,
"minimum": -999999,
"maximum": 999999,
"multiple": 1,
"accrue": false,
"format": "%.3f",
"unit": "-",
"profileId": 1
},
],
}
}
可在数据页面查询发布的消息
#在本案例中MQTT-Point-1和2并没有配置下发属性,会返回
{
"ok": false,
"message": "point(1) info does not exist"
}
#只能对点位3 4 下发操作
#请求参数
[
{
"deviceId": 1,
"pointId": 3,
"value": "34"
},
{
"deviceId": 1,
"pointId": 4,
"value": "34"
}
]
JAVA模拟设备
package com.dc3.driver.mqtt;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.api.R;
import com.dc3.common.bean.driver.PointValue;
import com.dc3.common.model.Point;
import com.google.gson.JsonObject;
import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.UUID;
/**
*
* @ClassName DeviceTest
* @Description TODO
* @Author Mr.Luan
* @Date 2020/7/25 11:35
* @Version 1.0
**/
public class DeviceTest {
private static String pubtopic="mqtt/group/device/update"; //发布主题
private static String subtopic ="mqtt/group/device/write/#";
private static String broker="tcp://192.168.1.111:1883";
private static String username="dc3";
private static String password="dc3";
private static String clientId= String.valueOf(UUID.randomUUID());
private static MqttClient mqttClient;
private static void initConnection() throws MqttException {
if(mqttClient==null)
mqttClient= new MqttClient( broker, clientId, new MemoryPersistence());
MqttConnectOptions connectOptions=new MqttConnectOptions();
connectOptions.setCleanSession(false);
connectOptions.setUserName(username);
connectOptions.setPassword(password.toCharArray());
connectOptions.setConnectionTimeout(10);
connectOptions.setKeepAliveInterval(20);
mqttClient.setCallback(new MqttCallback() {
public void messageArrived(String topicName, MqttMessage message) throws Exception {
//subscribe后得到的消息会执行到这里面
// 获取驱动下发的消息
System.out.println(topicName+"---"+message.toString());
}
public void deliveryComplete(IMqttDeliveryToken token) {
//publish后会执行到这里
// System.out.println("发布完毕");
}
public void connectionLost(Throwable cause) {
//连接丢失后,一般在这里面进行重连
try {
initConnection();
}catch (Exception e){
e.printStackTrace();
}
}
});
//建立连接
mqttClient.connect(connectOptions);
//订阅
mqttClient.subscribe(subtopic);
}
private static void PushMsg(String topic ,String msg) {
MqttMessage message=new MqttMessage( msg.getBytes());
//设置消息的服务质量
message.setRetained(true);
//发布消息
try {
mqttClient.publish(topic,message);
} catch (MqttException e) {
System.out.println("发送消息失败");
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
Random random=new Random();
List list=new ArrayList<>();
PointValue pv1=new PointValue();
pv1.setDeviceId(1l);
pv1.setPointId(1l);
list.add(pv1);
PointValue pv2=new PointValue();
pv2.setDeviceId(1l);
pv2.setPointId(2l);
list.add(pv2);
PointValue pv3=new PointValue();
pv3.setDeviceId(1l);
pv3.setPointId(3l);
list.add(pv3);
PointValue pv4=new PointValue();
pv4.setDeviceId(1l);
pv4.setPointId(4l);
list.add(pv4);
try {
initConnection();
while (true){
for (PointValue pv:list) {
pv.setValue(random.nextInt(1000)+"");
PushMsg(pubtopic, JSON.toJSONString(pv));
}
Thread.sleep(5000);
}
} catch (MqttException e) {
e.printStackTrace();
}
}
}