这是我项目的代码,算是真实业务实现,记录备份一下实现思路
因为业务就是简单的获取数据然后保存,所以还是容易理解的
1.任务详情
Java实现OPC通信的代码实现
最终实现对西门子的S7-300的PLC变量的值读写
2.实现情况
使用utgard实现通信:Java实现OPC通信
OPCserver:KEPserver6
制定变量列表:规定名称和类型
说明
PC和PLC通信,PC用TAG1之类的地址,PLC用BD1.INT05之类的地址,个人总结的表格,方便说明
OPCServer上定义的地址名称
硬件PLC上的地址
数据类型
说明:这个地址做什么用的
TAG1
DB1.INT00
short
心跳
TAG2
DB1.INT02
short
控制字
TAG3
DB1.STRING4,10
string
箱体信息
TAG4
DB1.D14
float
结果
地址定义在OPCServer上,实际截图
制定交互流程:控制字的状态
心跳变量:TAG1,表示软件和硬件(这里是PLC)连接状态,这个是自己随便定义的,我的是:PLC一直写1,PC收到1写2,5秒内一直收不到1就是断开了。
控制字(状态变量):TAG2,作用是作为一个控制字,控制整个交互的流程,当是不同值时,PLC和PC要进行指定的读写动作。
这是我一开始画的
这是后来画的,就是PC和PLC的交互,比较清晰了
实际的PLC地址编辑界面截图
3. 代码实现
按照官方例子写了一个通信类:通过配置信息,启动server,添加要读取的地址
配置信息是单独一个类:从配置文件读取IP什么的
然后是回调函数:某个地址,读到了某个值要怎么做,,在这就2个回调,心跳TAG1:读到1写入2,5秒超时,TAG2:是某个值时,怎么做。。。
读值和写值可以单独算一个方法类:我用到的数据类型就是几个,short,string,Long Array,对应写了几个方法
OPCServer上的地址配置也算是一个单独的类:我写在配置文件了,当时犹豫是不是放到数据库,感觉差别不大
Utgard有两种数据访问方式——直接通过item的read/write方法或者使用AccessBase(读取数据)
心跳TAG1:PC写2,PLC写1,逻辑是PC收到1写2,超时提示
控制字TAG2:PLC写0,1,3,4,5,6/7,PC写2,8
PC在写TAG2=1时,同时写入TAG3值
PLC在写TAG2=6/7时,同时写入TAG4值
读线程:每隔500ms读一次值,读到值后执行回调方法DataCallback()
不用另起线程,直接用这个Access线程循环读取控制字的值,回调方法对应调用item的read/write方法读写值
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();
4.代码:OPCserver连接配置类
import java.io.IOException;
import java.util.Properties;
import org.openscada.opc.lib.common.ConnectionInformation;
/**
* 配置文件工具类
*/
public final class OPCConfiguration {
private final static ConnectionInformation ci;
private final static Properties prop;
public final static String CONFIG_USERNAME = "username";
public final static String CONFIG_PASSWORD = "password";
public final static String CONFIG_HOST = "host";
public final static String CONFIG_DOMAIN = "domain";
public final static String CONFIG_CLSID = "clsid";
public final static String CONFIG_PROGID = "progid";
private final static String CONFIG_FILE_NAME = "opc.properties";
/**
* 加载配置文件
*/
static {
ci = new ConnectionInformation();
prop = new Properties();
try {
prop.load(OPCConfiguration.class.getClassLoader().getResourceAsStream(CONFIG_FILE_NAME));
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 通过名字获得配置的值
*/
public static String getEntryValue(String name) {
return prop.getProperty(name);
}
/**
* 获得包含ClsId的连接信息
*/
public static ConnectionInformation getCLSIDConnectionInfomation() {
ci.setProgId(null);
getConnectionInfomation();
ci.setClsid(prop.getProperty(CONFIG_CLSID));
return ci;
}
/**
* 获得包含ProgId的连接信息
*/
public static ConnectionInformation getPROGIDConnectionInfomation() {
ci.setClsid(null);
getConnectionInfomation();
ci.setProgId(prop.getProperty(CONFIG_PROGID));
return ci;
}
/**
* 获得基础的连接信息
*/
private static void getConnectionInfomation() {
ci.setHost(prop.getProperty(CONFIG_HOST));
ci.setDomain(prop.getProperty(CONFIG_DOMAIN));
ci.setUser(prop.getProperty(CONFIG_USERNAME));
ci.setPassword(prop.getProperty(CONFIG_PASSWORD));
}
}
5.代码:通信实现类
package cn.com.tcb.assembly.management.listener;
import static cn.com.tcb.assembly.management.listener.OPCConfiguration.getCLSIDConnectionInfomation;
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.Group;
import org.openscada.opc.lib.da.Item;
import org.openscada.opc.lib.da.Server;
import org.openscada.opc.lib.da.SyncAccess;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 功能:OPC通信线程 描述:通过循环读取心跳和状态控制字,按照商议好的交互流程读写变量
*/
public class OPCComm {
private static Logger logger = LoggerFactory.getLogger(OPCComm.class);
private Item item_heartbeat;
private Item item_status;
private Item item_ordernum;
private Item item_sn;
private Item item_boxnum;
private Item item_abnormal;
private Item item_finish;
private Item item_result;
private Server server;
/**
* 单例模式
*/
private static class SingletonHolder {
static final OPCComm doOPCComm = new OPCComm();
}
public static OPCComm getInstance() {
return SingletonHolder.doOPCComm;
}
/**
* 启动server 创建一个监控线程 创建一个写入线程
*/
public void init() throws Exception {
// 加载配置文件
final ConnectionInformation ci = getCLSIDConnectionInfomation();
// 创建server
final Server server = new Server(ci, Executors.newSingleThreadScheduledExecutor());
try {
// 启动server
server.connect();
logger.info("This is {} message.", "OPCserver connect success");
this.server = server;
// 同步读取,500ms一次
final AccessBase access = new SyncAccess(server, 500);
access.addItem(OPCElement.ITEMID_HEARTBEAT, new DataCallBack_HeartBeat());
access.addItem(OPCElement.ITEMID_STATUS, new DataCallBack_Status());
// 添加一个组
final Group group = server.addGroup("sew");
item_heartbeat = group.addItem(OPCElement.ITEMID_HEARTBEAT);
item_status = group.addItem(OPCElement.ITEMID_STATUS);
item_ordernum = group.addItem(OPCElement.ITEMID_ORDERNUM);
item_sn = group.addItem(OPCElement.ITEMID_SN);
item_boxnum = group.addItem(OPCElement.ITEMID_BOXNUM);
item_abnormal = group.addItem(OPCElement.ITEMID_ABNORMAL);
item_finish = group.addItem(OPCElement.ITEMID_FINISH);
item_result = group.addItem(OPCElement.ITEMID_RESULT);
// start reading
access.bind();
} catch (final JIException e) {
System.out.println(String.format("%08X: %s", e.getErrorCode(), server.getErrorMessage(e.getErrorCode())));
}
}
public Item getItem_heartbeat() {
return item_heartbeat;
}
public Item getItem_status() {
return item_status;
}
public Item getItem_ordernum() {
return item_ordernum;
}
public Item getItem_sn() {
return item_sn;
}
public Item getItem_boxnum() {
return item_boxnum;