JAVA实现OPC DA遇到的常见问题及解决方法 | 您所在的位置:网站首页 › star–561连接 › JAVA实现OPC DA遇到的常见问题及解决方法 |
前言
由于工作对接工业协议比较多,平时采集少量数据都是采用的modbus协议,但是最近要采集大量数据,modbus就不适用了,所以采用opc形式。期间遇到0x00000005,0x80070005,0x80040153,0x80010111等问题,避免大家踩坑,记录一下解决方法。 为什么使用OPC DA虽然OPC DA从1.0,2.0到3.0经历过几次更新,由于其复杂的环境配置和win的局限性,da的形式也逐渐不建议使用,推出了更加简单方便跨平台的OPC UA,但是老旧的控制系统依然只支持DA这种形式,所以为了兼容过去的DCS,PLC等系统必须支持OPC DA的读写方式。 关于OPC UA如果可以使用UA尽量不要使用DA,推荐Eclipse的milo开源库,后面我会单独出一期聊聊UA相关。 实现方式查找了好久java的OPC DA开源库只有Utgard和Jeasyopc两种 UtgardJeasyopcLinux支持(纯Java编写)不支持 Windows64支持(纯Java编写)不支持用户名密码需要不需要组查询不支持支持压力测试(单线程同步)略快7W点大约在4224ms略慢7W点大约在22540msDCOM通过DCOM实现,必须配置不需要配置现状 作者删库跑路只支持 IA 32,不支持AMD 64其中Jeasyopc是通过调用JCustomOpc.dll,需要配置JCustomOpc.dll文件,而且支持只32位系统。所以选用了Utgard来实现。 OPC Server模拟参考这位大佬:OPCServer:使用KEPServer - ioufev - 博客园 DCOM配置还是参考这位大佬:OPC和DCOM配置 - ioufev - 博客园 Maven依赖 org.jinterop j-interop 3.0.0这是Utgard调用的类库,不过Utgard引用的是2.0.4版本,一定要更换成3.0.0版本,否则会有可能碰到0x80010111错误。 Utgard的类库不建议通过maven坐标方式引入,因为其中有很多bug我们需要做一些简单的修改,建议下载源码,修改完成后通过jar方式引入maven,或者直接复制到项目下。 代码实现具体读写代码我这里就不贴了,可以参考一下上面那位大佬:Java OPC 代码 - ioufev - 博客园 我这里做一下补充 首先是获取主机上的所有OPC Server列表,可以直接获取ClsId和ProgId等信息 ServerList serverList = new ServerList(HOST,USERNAME, PASSWORD,DOMAIN); Collection detailsList = serverList.listServersWithDetails(new Category[] {Categories.OPCDAServer10, Categories.OPCDAServer20 ,Categories.OPCDAServer30}, new Category[] {}); for (final ClassDetails details : detailsList) { log.info("ClsId=" + details.getClsId() + " ProgId=" + details.getProgId() + " Description=" + details.getDescription()); }上文提到的大佬读取数据可以用下面这段进行解析,省的大家再去debug返回的对象结构 private static 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; }获取服务器下所有ITEM列表 Collection items = server.getFlatBrowser().browse(); 遇到的问题 0x00000005原因:用户校验不通过。 解决方法:检查用户名和密码,用户名不要使用全名 如果用户名和密码没有问题,那么就有可能是本地安全策略的问题,可以通过下面方法解决。 1、win+r运行 secpol.msc 2、找到本地策略 - 安全选项 - 网络访问:本地账户的共享和安全模型 3、修改属性为“经典:对本地账户进行身份验证,不改变其本来身份” 0x8001FFFF解决方法:检查dcom配置是否正确,防火墙设置是否正确,按照上面大佬的步骤一步一步重新配置一下。 0x80070005解决方法:这个就是上文提到的不建议maven方式直接引入Utgard,需要修改 org.openscada.opc.lib.da包下的Server.class,开启会话安全项,再原来connect()方法添加两处this.session.useSessionSecurity(true); 而且Utgard的通过ProgId建立连接也有问题,具体为什么获取不到连接我没仔细查找,不过也可以在这个地方做修改实现通过ProgID建立连接。 修改完成如下 public synchronized void connect() throws IllegalArgumentException, UnknownHostException, JIException, AlreadyConnectedException { if (isConnected()) { throw new AlreadyConnectedException(); } final int socketTimeout = Integer.getInteger("rpc.socketTimeout", 0); log.debug(String.format("Socket timeout: %s ", socketTimeout)); try { if (this.connectionInformation.getClsid() != null) { this.session = JISession.createSession( this.connectionInformation.getDomain(), this.connectionInformation.getUser(), this.connectionInformation.getPassword()); this.session.setGlobalSocketTimeout(socketTimeout); this.session.useSessionSecurity(true); this.comServer = new JIComServer( JIClsid.valueOf(this.connectionInformation.getClsid()), this.connectionInformation.getHost(), this.session); } else if (this.connectionInformation.getProgId() != null) { this.session = JISession.createSession( this.connectionInformation.getDomain(), this.connectionInformation.getUser(), this.connectionInformation.getPassword()); this.session.setGlobalSocketTimeout(socketTimeout); ServerList serverList = new ServerList(this.connectionInformation.getHost(), this.connectionInformation.getUser(), this.connectionInformation.getPassword(), this.connectionInformation.getDomain()); String clsIdFromProgId = serverList.getClsIdFromProgId(this.connectionInformation.getProgId()); // this.comServer = new JIComServer(JIProgId.valueOf(this.connectionInformation.getProgId()),this.connectionInformation.getHost(), this.session); this.comServer = new JIComServer( JIClsid.valueOf(clsIdFromProgId), this.connectionInformation.getHost(), this.session); } else { throw new IllegalArgumentException("Neither clsid nor progid is valid!"); } this.server = new OPCServer(this.comServer.createInstance()); this.errorMessageResolver = new ErrorMessageResolver( this.server.getCommon(), this.defaultLocaleID); } catch (final UnknownHostException e) { log.error("Unknown host when connecting to server", e); cleanup(); throw e; } catch (final JIException e) { log.error("Failed to connect to server", e); cleanup(); throw e; } catch (final Throwable e) { log.error("Unknown error", e); cleanup(); throw new RuntimeException(e); } notifyConnectionStateChange(true); } 0x80040154原因:ClsId不正确 解决方法:检查OPCServer的ClsId,或者通过上文提到的获取OPC Server列表的方式获取ClsId 0x80040153原因:系统中有过OPCServer的注册表没有删除干净,所以通过ClsId找不到OPCServer 解决方法:这个问题困扰了我很长时间,因为注册表实在太多,没法找到并删除,而电脑里有很多东西又不想重做系统。可以通过修改org.openscada.opc.lib.list;包下的 listServersWithDetails(final Category[] implemented, final Category[] required)方法处理该异常 修改完成如下 public Collection listServersWithDetails(final Category[] implemented, final Category[] required) throws IllegalArgumentException, UnknownHostException, JIException { Collection resultString = listServers(implemented, required); List result = new ArrayList(resultString.size()); for (String clsId : resultString) { //TODO 注册表没清理干净会报错 ,只做了异常处理 try { result.add(getDetails(clsId)); }catch (JIException e){ logger.error(clsId+":Message not found for errorCode: 0x80040153"); e.printStackTrace(); } } return result; } 0x80010111原因:utgard的协议5.6,但windows 10 2004之后的协议都是5.7 解决方法:使用 j-interop 3.0 org.jinterop j-interop 3.0.0使用j-interop3.0需要再maven中添加repository如下,否则会报错找不到各种包 clojars Clojars https://repo.clojars.org/ Utgard源码我自己项目里用的,已经改好了上文提到的部分,直接复制到自己项目里或者打jar就可以使用 链接:https://pan.baidu.com/s/13vcXF74gym-cvhp_myU9rw?pwd=pnjm 提取码:pnjm J-interop 3.0 jar链接:https://pan.baidu.com/s/1b49Z8BvL-ADhe2YZPZVoEg?pwd=9fdp 提取码:9fdp |
CopyRight 2018-2019 实验室设备网 版权所有 |