SpringBoot集成OPC协议
1. 使用的OPC server软件
- KEPServer V6,官网下载
- windows10专业版虚拟机,1903版本以前,下载地址https://msdn.itellyou.cn/
2. 使用OPC server软件
安装和使用:https://www.cnblogs.com/ioufev/p/9366877.html
3. DCOM配置
DCOM配置:https://www.cnblogs.com/ioufev/p/9365919.html
4. 代码编写
4.1 jar包
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.7</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<!-- OPC服务 -->
<dependency>
<groupId>org.kohsuke.jinterop</groupId>
<artifactId>j-interop</artifactId>
<version>2.0.5</version>
</dependency>
<dependency>
<groupId>org.openscada.external</groupId>
<artifactId>org.openscada.external.jcifs</artifactId>
<version>1.2.25</version>
</dependency>
<dependency>
<groupId>org.openscada.jinterop</groupId>
<artifactId>org.openscada.jinterop.core</artifactId>
<version>2.1.8</version>
</dependency>
<dependency>
<groupId>org.openscada.jinterop</groupId>
<artifactId>org.openscada.jinterop.deps</artifactId>
<version>1.5.0</version>
</dependency>
<dependency>
<groupId>org.openscada.utgard</groupId>
<artifactId>org.openscada.opc.dcom</artifactId>
<version>1.5.0</version>
</dependency>
<dependency>
<groupId>org.openscada.utgard</groupId>
<artifactId>org.openscada.opc.lib</artifactId>
<version>1.5.0</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.59</version>
</dependency>
4.2 代码
opc:
host: 192.168.51.17 # opcServer ip
domain: #
user: OPCServer # opcServer主机有DCOM权限的用户名
password: 123456 # opcServer主机有DCOM权限的密码
clsId: 7BC0CC8E-482C-47CA-ABDC-0FE7F9C6E729
package com.liming.app.opc;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* @Author:liming.she
* @Date:2023/11/21 17:07
* @Description:
*/
@Configuration
@ConfigurationProperties(prefix = "opc")
@Data
public class OPCConfig {
private String host;
private String domain;
private String user;
private String password;
private String clsId;
}
package com.liming.app.opc.v2;
import com.liming.app.common.cache.CacheSingleton;
import com.liming.app.common.config.ApplicationContextGetBeanHelper;
import com.liming.app.common.config.DeviceNameConfig;
import com.liming.app.common.config.RegisterConfig;
import com.liming.app.common.config.RegisterInfo;
import com.liming.app.modbusTcp.HexUtil;
import com.liming.app.opc.OPCConfig;
import lombok.extern.slf4j.Slf4j;
import org.openscada.opc.lib.common.ConnectionInformation;
import org.openscada.opc.lib.da.Group;
import org.openscada.opc.lib.da.Item;
import org.openscada.opc.lib.da.ItemState;
import org.openscada.opc.lib.da.Server;
import org.openscada.opc.lib.da.browser.FlatBrowser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
/**
* @Author:liming.she
* @Date:2023/11/22 10:47
* @Description: 批量读取items值
*/
@Slf4j
@Component
public class OPCClientV2 implements CommandLineRunner {
@Autowired
private RegisterConfig registerConfig;
@Autowired
private DeviceNameConfig deviceNameConfig;
@Override
public void run(String... args) throws Exception {
OPCConfig opcConfig = ApplicationContextGetBeanHelper.getBean(OPCConfig.class);
while (true){
try {
// 连接server
final ConnectionInformation connectionInfo = new ConnectionInformation();
connectionInfo.setHost(opcConfig.getHost());
connectionInfo.setClsid(opcConfig.getClsId());
connectionInfo.setUser(opcConfig.getUser());
connectionInfo.setPassword(opcConfig.getPassword());
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
Server mServer = new Server(connectionInfo, executor);
mServer.connect();
//读取items
FlatBrowser browser = mServer.getFlatBrowser();
List<String> itemList = new ArrayList<>();
Collection<String> browse = browser.browse();
for (String name : browse) {
//匹配字符串获取想要的itemID
if (startWith(name,deviceNameConfig.getDeviceName()) && startWithout(name,deviceNameConfig.getSystemName())) {
itemList.add(name);
}
}
//读取values
Group group = mServer.addGroup();
String[] items = itemList.toArray(new String[]{});
Map<String, Item> itemResult = group.addItems(items);
Item[] itemArr = new Item[itemList.size()];
for (int i = itemList.size() - 1; i >= 0; i--) {
itemArr[itemList.size()-1-i] = itemResult.get(itemList.get(i));
}
Map<Item, ItemState> resultMap = group.read(true, itemArr);
//业务逻辑 解析并存放在寄存器中(内存)
List<Integer> list = new ArrayList<>();
for (Item item : itemArr) {
ItemState itemState = resultMap.get(item);
int type = itemState.getValue().getType();
Object val = getVal(itemState.getValue());
HexUtil.objectToInt2Arr(type, val, list);
}
List<RegisterInfo> registerInfos = registerConfig.getRegisterInfos();
CacheSingleton cacheSingleton = CacheSingleton.getInstance();
cacheSingleton.addCacheData(registerInfos.get(registerInfos.size()-1).getIdentity(),list);
//todo 存库
mServer.disconnect();
Thread.sleep(1000);
} catch (Exception e) {
log.error("connectServer ERROR:",e);
Thread.sleep(1000);
}
}
}
public boolean startWith(String name,String[] names){
for (String s : names) {
if (name.contains(s)){
return true;
}
}
return false;
}
public boolean startWithout(String name,String[] names){
for (String s : names) {
if (name.contains(s)){
return false;
}
}
return true;
}
public Object getVal(JIVariant var) throws JIException {
Object value;
int type = var.getType();
switch (type) {
case JIVariant.VT_I2:
value = var.getObjectAsShort();
break;
case JIVariant.VT_I4:
value = var.getObjectAsInt();
break;
case JIVariant.VT_I8:
value = var.getObjectAsLong();
break;
case JIVariant.VT_R4:
value = var.getObjectAsFloat();
break;
case JIVariant.VT_R8:
value = var.getObjectAsDouble();
break;
case JIVariant.VT_BSTR:
value = var.getObjectAsString2();
break;
case JIVariant.VT_BOOL:
value = var.getObjectAsBoolean();
break;
case JIVariant.VT_UI2:
case JIVariant.VT_UI4:
value = var.getObjectAsUnsigned().getValue();
break;
case JIVariant.VT_EMPTY:
throw new JIException(JIErrorCodes.JI_VARIANT_IS_NULL, "Variant is Empty.");
case JIVariant.VT_NULL:
throw new JIException(JIErrorCodes.JI_VARIANT_IS_NULL, "Variant is null.");
default:
throw new JIException(JIErrorCodes.JI_VARIANT_IS_NULL, "Unknown Type.");
}
return value;
}
}