1.PLC和OPC
- PLC:
- 西门子的S7 300
- OPC:
2.连接测试
- MatrikonOPC
- KEPServer V6
- 配置OPCserver
3.通信实现
- 最重要参考:Java OPC client开发踩坑记
- Utgard
- JeasyOPC
4.实现过程
- 先恶补了一下OPC的概念:
- 先使用MatrikonOPC模拟实现:
- OPC测试常用的OPCClient和OPCServer软件推荐
- 我的目的就是写一个类似的Java版的Client来连接OPC Server
- 使用Matrikon OPC Server Simulation
- 关于OPC UA
- 支持的西门子PLC至少是s7-1500,
- 我的s7-300是没法用的,所以就不需要搜集OPC UA的资料了
- 关于Utgard
- utgard是一个开源的项目,基于j-interop做的,用于和OPC SERVER通讯。
- j-interop是纯java封装的用于COM/DCOM通讯的开源项目,这样就不必使用JNI
- 关于JeasyOPC
- JeasyOPC源码下载
- 借助一个dll库来实现的和OPCServer的通信,但是JCustomOpc.dll,,太老了,而且支持只32位系统
- 实现
- 当然选Utgard
- 过程就是把需要的jar包找到,
- 然后复制编程指导里的读写代码,读就是启动线程一直监控读值,写就是直接写
- 测试:
- 参考OPC_Client里的例子
- 关于配置文件的代码直接复制用了
- 例子实际也用不到,试了试,,因为实际只需要读写变量就可以了
- 问题:
- 在虚拟机里用localhost一直报错,要写固定IP才行
- 需要下载一个bcprov-jdk16-146.jar包,因为报安全算法错误
- 位置:"C:\Program Files\Java\jdk1.8.0_92\jre\lib\ext\bcprov-jdk16-146.jar"
- java.security位置:"C:\Program Files\Java\jdk1.8.0_92\jre\lib\security\java.security"
- 在java.security最后添加一句:security.provider.x=org.bouncycastle.jce.provider.BouncyCastleProvider
5.代码
下载:链接: 百度网盘 ,密码: x7f1
截图:
读值
package tcb;
import java.util.concurrent.Executors;
import org.jinterop.dcom.common.JIException;
import org.openscada.opc.lib.common.ConnectionInformation;
import org.openscada.opc.lib.da.AccessBase;
import org.openscada.opc.lib.da.DataCallback;
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.SyncAccess;
public class UtgardTutorial1 {
public static void main(String[] args) throws Exception {
// 连接信息
final ConnectionInformation ci = new ConnectionInformation();
ci.setHost("192.168.0.1");
ci.setDomain("");
ci.setUser("OPCUser");
ci.setPassword("123456");
// MatrikonOPC Server
// ci.setClsid("F8582CF2-88FB-11D0-B850-00C0F0104305");
// final String itemId = "u.u";//项的名字按实际
// KEPServer
ci.setClsid("7BC0CC8E-482C-47CA-ABDC-0FE7F9C6E729");
final String itemId = "u.u.u";// 项的名字按实际
// final String itemId = "通道 1.设备 1.标记 1";
// create a new server
final Server server = new Server(ci, Executors.newSingleThreadScheduledExecutor());
try {
// connect to server
server.connect();
// add sync access, poll every 500 ms
final AccessBase access = new SyncAccess(server, 500);
access.addItem(itemId, new DataCallback() {
@Override
public void changed(Item item, ItemState state) {
System.out.println("-----" + state);
}
});
// start reading
access.bind();
// wait a little bit
Thread.sleep(10 * 1000);
// stop reading
access.unbind();
} catch (final JIException e) {
System.out.println(String.format("%08X: %s", e.getErrorCode(), server.getErrorMessage(e.getErrorCode())));
}
}
}
写值
package tcb;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.jinterop.dcom.common.JIException;
import org.jinterop.dcom.core.JIVariant;
import org.openscada.opc.lib.common.ConnectionInformation;
import org.openscada.opc.lib.da.AccessBase;
import org.openscada.opc.lib.da.DataCallback;
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.SyncAccess;
public class UtgardTutorial2 {
public static void main(String[] args) throws Exception {
// 连接信息
final ConnectionInformation ci = new ConnectionInformation();
ci.setHost("192.168.0.1");
ci.setDomain("");
ci.setUser("OPCUser");
ci.setPassword("123456");
// MatrikonOPC Server
// ci.setClsid("F8582CF2-88FB-11D0-B850-00C0F0104305");
// final String itemId = "u.u";//项的名字按实际
// KEPServer
ci.setClsid("7BC0CC8E-482C-47CA-ABDC-0FE7F9C6E729");
final String itemId = "u.u.u";// 项的名字按实际
// final String itemId = "通道 1.设备 1.标记 1";
// create a new server
final Server server = new Server(ci, Executors.newSingleThreadScheduledExecutor());
try {
// connect to server
server.connect();
// add sync access, poll every 500 ms
final AccessBase access = new SyncAccess(server, 500);
access.addItem(itemId, new DataCallback() {
@Override
public void changed(Item item, ItemState state) {
// also dump value
try {
if (state.getValue().getType() == JIVariant.VT_UI4) {
System.out.println("<<< " + state + " / value = " + state.getValue().getObjectAsUnsigned().getValue());
} else {
System.out.println("<<< " + state + " / value = " + state.getValue().getObject());
}
} catch (JIException e) {
e.printStackTrace();
}
}
});
// Add a new group
final Group group = server.addGroup("test");
// Add a new item to the group
final Item item = group.addItem(itemId);
// start reading
access.bind();
// add a thread for writing a value every 3 seconds
//写入线程,至于为什么要用线程,要一直写,,如果只写一次就不用写线程了
ScheduledExecutorService writeThread = Executors.newSingleThreadScheduledExecutor();
writeThread.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
final JIVariant value = new JIVariant("24");//一直写入24
try {
System.out.println(">>> " + "writing value " + "24");
item.write(value);
} catch (JIException e) {
e.printStackTrace();
}
}
}, 5, 3, TimeUnit.SECONDS);
// wait a little bit
Thread.sleep(20 * 1000);
writeThread.shutdownNow();
// stop reading
access.unbind();
} catch (final JIException e) {
System.out.println(String.format("%08X: %s", e.getErrorCode(), server.getErrorMessage(e.getErrorCode())));
}
}
}