这里是一个莫名奇妙的需求,所以网上不容易找到教程,所以是踩了一些坑。
由于不熟悉java,也是磕磕绊绊。
opcua库选择
首先opcua的库我找到了两个:
1.https://github.com/eclipse/milo
https://github.com/eclipse/milo2.GitHub - OPCFoundation/UA-Java-Legacy: This repository is provided by OPC Foundation as legacy support for an Java version for OPC UA.
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,这个库干嘛用的我并不知道,但是这可以在网上搜到一些方案,直接去下载了:
注意我使用的版本,就算是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;
}