操作系统:windows10 版本1909
注意:与kepserver的通讯对windows版本的要求很高,目前所了解到的必须是1909版本以前,或者1903版本,windows11 使用21H2的版本。
原因是:微软给修复了docm的安全漏洞,发了补丁的原因,导致了一些新版本的windows不支持opcda的写法。
开始
1、第一步:创建一个新的windows用户,用于给访问kepserver使用。
用户名:OPCServer 【自定义即可】
密码:123456 【自定义即可】
将新增的用户添加到DOCM组
然后把新增用户移出普通用户组【Users就是刚刚新增的用户】
添加用户步骤完成
2、配置防火墙
找到DCOM-IN
上面两个DOCM-IN都是一样的操作。
3、创建OPC程序规则:允许程序OPCEnum
新建入站规则
然后一直下一步,直到【名称】,然后点击完成
4、添加 OPC 服务器程序的规则:允许程序 KEPServer的server_runtime
新建入站规则--程序--找到server_runtime.exe
其他步骤同上一个规则一样即可。
5、组件服务
6、OpcEnum的安全选项
因为自定义的编辑都是一样的,所以我下面只演示一个。
7、组件服务:KEPServer的安全选项
和上面一样,这三个都点自定义,编辑中把新增的用户添加进去,把允许的权限全部打开
8、本地安全策略
配置工作全部完成
下面是JAVA代码
所需依赖
org.openscada.utgard org.openscada.opc.lib1.5.0 org.bouncycastle bcprov-jdk15onorg.bouncycastle bcprov-jdk15on1.65 org.openscada.utgard org.openscada.opc.dcom1.5.0
连接配置
@Configuration @ConfigurationProperties(prefix = "kep-server") @Slf4j @Data public class KepServerConfig implements Serializable { private String host; private String domain; private String user; private String password; private String clsid; @Bean(name = "opcServer") public Server server() { ConnectionInformation connectionInformation = new ConnectionInformation(); connectionInformation.setHost(host); connectionInformation.setDomain(domain); connectionInformation.setUser(user); connectionInformation.setPassword(password); connectionInformation.setClsid(clsid); // 连接到服务 Server server = new Server(connectionInformation, null); try { server.connect(); log.info("opc 服务端连接成功........"); } catch (UnknownHostException e) { log.error("opc 地址错误:", e); } catch (JIException e) { log.error("opc 连接失败:", e); }catch (AlreadyConnectedException e) { log.error("opc 已连接:", e); } return server; } }
【注意:为了不误人子弟,下面的读取方法是有BUG的,正确代码有空修改,当你看见这句话时,还没有修改】
批量读取kepserver中的数据
package com.hangcheng.opc.common.scheduledTasks; import com.hangcheng.opc.common.constant.CommonConstant; import com.hangcheng.opc.common.constant.DictTagsConstant; import com.hangcheng.opc.common.constant.KepServerConstant; import com.hangcheng.opc.common.constant.RedisKeyConstant; import com.hangcheng.opc.common.pojo.DataTags; import com.hangcheng.opc.common.pojo.EarlyWarning; import com.hangcheng.opc.common.utils.opc.OpcTypeConversionUtils; import com.hangcheng.opc.common.utils.redis.RedisCacheUtils; import com.hangcheng.opc.service.EarlyWarningService; import com.hangcheng.opc.service.TagsDataService; import lombok.extern.slf4j.Slf4j; import org.jinterop.dcom.core.JIVariant; import org.openscada.opc.dcom.da.OPCSERVERSTATE; import org.openscada.opc.lib.da.*; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.*; import java.util.stream.Collectors; @Service @Slf4j public class OpcDaClient { @Resource private TagsDataService tagsDataService; @Resource @Qualifier("opcServer") private Server server; @Resource private RedisCacheUtils redisCacheUtils; @Resource private EarlyWarningService earlyWarningService; /** * PLC批量读操作 */ public void configureTasks() { //下面这段是为了解决断线重连的问题 try { // 这里会出现空是应为 OPCServer 为 null,但是单独拿OPCServer来判断null并不严谨,因为还有一些其他报错会导致OPCServer为空, // 并不是只有连接失败导致,所以这里进行多次判断 if (server.getServerState() == null) { log.info("OPCServer为空,尝试一次连接"); // 尝试一次连接,一般情况连接失败就直接被下面的catch捕获了 server.connect(); } else if (server.getServerState() != null && server.getServerState().getServerState().equals(OPCSERVERSTATE.OPC_STATUS_RUNNING)) { log.info("与服务端的连接是正常的,继续执行业务代码"); } else { return; } // 创建组 Group group = server.addGroup(); // 转数组 String[] items = keyList.toArray(new String[]{}); // 把所有的标记添加到组里 MapitemResult = group.addItems(items); // map转set,避免重复 Set - itemSet = new HashSet<>(itemResult.values()); // 创建一个 itemSet 大小的数组 Item[] itemArr = new Item[itemSet.size()]; // 将 itemSet集合 复制到 itemArr数组 itemSet.toArray(itemArr); // 一次性读取全部标记的值,非常滴爽 Map
- resultMap = group.read(true, itemArr); // resultMap 就是读出来的所有数据 } catch (Exception e) { e.printStackTrace(); } } }
写操作
/** * PLC批量写操作 * * @param tagValueMap 指定标识与指定值集合 */ public void writePlcValues(MaptagValueMap) { try { // 添加一个Group Group group = server.addGroup(); Map itemIdAndRWMap = redisCacheUtils.hmEntries(RedisKeyConstant.ITEM_ID_AND_R_W_MAP); Iterator iterator = tagValueMap.keySet().iterator(); while (iterator.hasNext()) { String tag = iterator.next(); String key = itemIdAndRWMap.get(tag); // 如果标识不存在那么代表传参有误,直接去除即可 if (key == null || !key.equals(RedisKeyConstant.R_W)) { iterator.remove(); } } int i = 0; WriteRequest[] writeRequests = new WriteRequest[tagValueMap.size()]; for (Map.Entry entry : tagValueMap.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); WriteRequest writeRequest = new WriteRequest(group.addItem(key), new JIVariant(value)); writeRequests[i++] = writeRequest; } group.write(writeRequests); } catch (Exception e) { throw new RuntimeException(e); } }
完毕,剩下的再补,没时间写了