这里是一个莫名奇妙的需求,所以网上不容易找到教程,所以是踩了一些坑。

由于不熟悉java,也是磕磕绊绊。

opcua库选择

首先opcua的库我找到了两个:

1.https://github.com/eclipse/milo

opc_ua java连接 opc ua java开发_opc_ua java连接

https://github.com/eclipse/milo

2.GitHub - OPCFoundation/UA-Java-Legacy: This repository is provided by OPC Foundation as legacy support for an Java version for OPC UA.

opc_ua java连接 opc ua java开发_opc_ua java连接

https://github.com/OPCFoundation/UA-Java-Legacy

这里本着新的就是好的原则,使用了milo,网上的教程也挺多,但是就遇到了坑。

因为milo是由更新版本的java进行开发,新版本的特性在旧版本上可不适用!

而appinventor就是一个开发时间很早的项目,支持的java版本是7/8.

在导入编译这一步appinventor拓展开发_香道人的博客-参考文档:https://saitwalshreyash19.medium.com/writing-extensions-for-app-inventor-2-and-kodular-7d20092bff16https://saitwalshreyash19.medium.com/writing-extensions-for-app-inventor-2-and-kodular-7d20092bff16https://islet.blog.csdn.net/article/details/124301948?spm=1001.2014.3001.5502可是一直报错!

后面发现是因为milo使用了lambda特性,导致不支持该库。

所以最后我选择了OPCFoundation这个,可以在发布的tag中直接下载.jar文件,也算比较简单。

库的导入问题

但是就算是这样,还是会出问题,因为该库还依赖了sl4j这个。

由于初步接触java,这个库干嘛用的我并不知道,但是这可以在网上搜到一些方案,直接去下载了:

opc_ua java连接 opc ua java开发_opc_ua java连接_03

注意我使用的版本,就算是opcua也不能使用太新的版本,选择不对就会有编译报错。

然后注意在java文件中导入时这两个库都是需要的:

@SimpleObject(external = true) //外部插件
//这个位置不能放上面!必须在SimpleObject下面
@UsesLibraries(libraries = "opc-ua-stack-1.4.1-224.jar,slf4j-api-1.7.0.jar")

开发流程

这里的import也是不怎么专业,将所有可能需要的都添加进去了:

// opcua
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.InvalidParameterSpecException;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;


import java.util.Locale;
import com.google.appinventor.components.annotations.UsesLibraries;
import org.opcfoundation.ua.application.Client;
import org.opcfoundation.ua.application.SessionChannel;
import org.opcfoundation.ua.builtintypes.LocalizedText;
import org.opcfoundation.ua.builtintypes.NodeId;
import org.opcfoundation.ua.cert.CertificateCheck;
import org.opcfoundation.ua.cert.DefaultCertificateValidator;
import org.opcfoundation.ua.cert.DefaultCertificateValidatorListener;
import org.opcfoundation.ua.cert.PkiDirectoryCertificateStore;
import org.opcfoundation.ua.cert.ValidationResult;
import org.opcfoundation.ua.core.ApplicationDescription;
import org.opcfoundation.ua.core.Attributes;
import org.opcfoundation.ua.core.BrowseDescription;
import org.opcfoundation.ua.core.BrowseDirection;
import org.opcfoundation.ua.core.BrowseResponse;
import org.opcfoundation.ua.core.BrowseResultMask;
import org.opcfoundation.ua.core.Identifiers;
import org.opcfoundation.ua.core.NodeClass;
import org.opcfoundation.ua.core.ReadResponse;
import org.opcfoundation.ua.core.ReadValueId;
import org.opcfoundation.ua.core.TimestampsToReturn;
import org.opcfoundation.ua.common.ServiceResultException;
import org.opcfoundation.ua.transport.security.Cert;
import org.opcfoundation.ua.transport.security.KeyPair;
import org.opcfoundation.ua.transport.security.PrivKey;
import org.opcfoundation.ua.utils.CertificateUtils;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


import static org.opcfoundation.ua.utils.EndpointUtil.selectByMessageSecurityMode;
import static org.opcfoundation.ua.utils.EndpointUtil.selectByProtocol;
import static org.opcfoundation.ua.utils.EndpointUtil.sortBySecurityLevel;

import java.net.InetAddress;
import org.opcfoundation.ua.application.Client;
import org.opcfoundation.ua.core.Attributes;
import org.opcfoundation.ua.core.EndpointDescription;
import org.opcfoundation.ua.core.Identifiers;
import org.opcfoundation.ua.core.MessageSecurityMode;
import org.opcfoundation.ua.core.ReadRequest;
import org.opcfoundation.ua.core.ReadResponse;
import org.opcfoundation.ua.core.ReadValueId;
import org.opcfoundation.ua.core.TimestampsToReturn;
import org.opcfoundation.ua.transport.ServiceChannel;
import org.opcfoundation.ua.transport.security.KeyPair;
//write
import org.opcfoundation.ua.builtintypes.DataValue;
import org.opcfoundation.ua.builtintypes.Variant;
import org.opcfoundation.ua.core.WriteResponse;
import org.opcfoundation.ua.core.WriteValue;
import org.opcfoundation.ua.core.WriteRequest;
import org.opcfoundation.ua.core.RequestHeader;

//thread
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

连接服务器的步骤:

//创建任务类,类似Runnable  
    public static class CallerTask implements Callable<String>{  
        @Override  
        public String call() throws Exception{
            String retStr = "";  
            Client myClient = Client.createClientApplication(null);
            try {
            String url = "opc.tcp://192.168.1.8:4840";
            SessionChannel mySession = myClient.createSessionChannel(url);
            mySession.activate("username", "passwd.");

            mySession.close();
            mySession.closeAsync();
            
            } catch (Exception e1) {
                retStr = "Error";
            }
            return retStr;
        }  
    }

这里使用线程是因为主程序无法使用网络服务。

读操作:

ReadResponse res4 = mySession.Read(null, 500.0, TimestampsToReturn.Source,
new ReadValueId(new NodeId(1, "XXXXX"), Attributes.Value, null, null));
            
retStr = String.valueOf(res4.getResults()[0].getValue());//getStatusCode

写操作:

NodeId nodeId = new NodeId(1, "XXX");
//Bad_TypeMismatch  double类型必须带小数点
DataValue dataValue = new DataValue(new Variant(999.0));
WriteValue writeValue[] = new WriteValue[1];
writeValue[0] = new WriteValue(nodeId, Attributes.Value, null, dataValue); //第三个参数为null才可以使用普通写 
RequestHeader requestHeader = new RequestHeader(nodeId, null, null, null, null, null, null);
WriteRequest writeRequest = new WriteRequest(requestHeader, writeValue);
WriteResponse response = mySession.Write(writeRequest);

retStr = String.valueOf(response.getResults()[0]);//

最后补充一下线程返回值的调用:

public String readNode() throws Exception{
        String retStr = "";
        // Android 4.0 之后不能在主线程中请求HTTP请求
        //创建异步任务  
        FutureTask<String> futureTask = new FutureTask<>(new ReadCallerTask());  
        //启动线程  
        new Thread(futureTask).start();  
        try{  
            //等待任务执行完毕,并返回结果  
            String result = futureTask.get();  
            return  result;
        } catch (ExecutionException e){  
            e.printStackTrace();  
        } 
        return retStr;
    }