监听OPC服务端,接收订阅标记的数据。通过Java观察者模式,只有当订阅的标记发生改变,系统才会捕捉到改变的数据,并对数据进行操作。
为了更方便的操作OPC,将OPC的配置信息写入application.yml配置文件中。
application.yml
opc:
host: 192.168.1.205
domain:
user: OPCUser
password: 123456
progId: Kepware.KEPServerEX.V6
itemIdList:
- Memorybase.Device01.TAG01
- Memorybase.Device01.TAG02
- Memorybase.Device01.TAG03
SpringUtil.java
将类注入到Spring容器
package com.util;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class SpringUtil implements ApplicationContextAware{
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (SpringUtil.applicationContext == null) {
SpringUtil.applicationContext = applicationContext;
}
}
/**
* 获取applicationContext
* @return
*/
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
/**
* 通过name获取bean
* @param name
* @return
*/
public static Object getBean(String name) {
return getApplicationContext().getBean(name);
}
/**
* 通过class获取bean
* @param <T>
* @param clazz
* @return
*/
public static <T> T getBean(Class<T> clazz) {
return getApplicationContext().getBean(clazz);
}
/**
* 通过name,以及clazz返回指定的bean
* @param <T>
* @param name
* @param clazz
* @return
*/
public static <T> T getBean(String name,Class<T> clazz) {
return getApplicationContext().getBean(name,clazz);
}
}
OpcProperties.java
读取yml配置文件中的值,在需要使用配置的类中通过@Autowried注册用getter()方法获取值
package com.opc;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import java.util.List;
@Configuration
@ConfigurationProperties(prefix = "opc")
public class OpcProperties {
private String host;
private String domain;
private String user;
private String password;
private String progId;
private List<String> itemIdList;
public OpcProperties() {
}
public OpcProperties(String host, String domain, String user, String password, String progId, List<String> itemIdList) {
this.host = host;
this.domain = domain;
this.user = user;
this.password = password;
this.progId = progId;
this.itemIdList = itemIdList;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public String getDomain() {
return domain;
}
public void setDomain(String domain) {
this.domain = domain;
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getProgId() {
return progId;
}
public void setProgId(String progId) {
this.progId = progId;
}
public List<String> getItemIdList() {
return itemIdList;
}
public void setItemIdList(List<String> itemIdList) {
this.itemIdList = itemIdList;
}
}
OpcMap.java
将读到的OPC的数据放入Map集合,方便后面对OPC的数据进行读写操作。
package com.opc;
import java.util.HashMap;
import java.util.Map;
/**
* 操作OPC数据
*/
public class OpcMap {
private static Map<String, Object> opcMap = new HashMap<String, Object>();
/**
* 数据放入opcMap集合
*/
public static void putMap(String itemId, Object value) {
opcMap.put(itemId, value);
}
/**
* 根据opc标签从opcMap集合中读数据
*/
public static Object getMap(String ItemId) {
return opcMap.get(ItemId);
}
}
OpcClient.java
建立OPC客户端,实现连接、异步读取数据等操作
package com.opc;
import com.result.Result;
import org.jinterop.dcom.common.JIException;
import org.openscada.opc.dcom.list.ClassDetails;
import org.openscada.opc.lib.common.ConnectionInformation;
import org.openscada.opc.lib.da.*;
import org.openscada.opc.lib.da.browser.FlatBrowser;
import org.openscada.opc.lib.list.Categories;
import org.openscada.opc.lib.list.Category;
import org.openscada.opc.lib.list.ServerList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.UnknownHostException;
import java.util.Collection;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
public class OpcClient extends Observable {
private Server mServer = null;
private static final Logger LOGGER = LoggerFactory.getLogger(OpcClient.class);
/**
* 连接opc服务
*
* @param host
* @param progId
* @param user
* @param password
* @param domain
* @return
*/
public synchronized boolean connectionServer(String host, String progId, String user, String password, String domain) throws UnknownHostException {
boolean mState = false;
ServerList serverList = null;
try {
// 获取server上的opc server列表
serverList = new ServerList(host, user, password, domain);
// 连接server
final ConnectionInformation connectionInfo = new ConnectionInformation();
connectionInfo.setHost(host);
connectionInfo.setClsid(serverList.getClsIdFromProgId(progId));
connectionInfo.setUser(user);
connectionInfo.setPassword(password);
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
mServer = new Server(connectionInfo, executor);
mServer.connect();
mServer.addStateListener(new ServerConnectionStateListener() {
@Override
public void connectionStateChanged(boolean connected) {
LOGGER.info("connectionStateChanged state = {}", connected);
}
});
mState = true;
} catch (Exception e) {
LOGGER.error(e.getMessage());
} finally {
if (!mState) {
mServer = null;
}
}
return mState;
}
/**
* 断开opc连接
*/
public synchronized void disconnectServer() {
if (mServer == null) {
return;
}
mServer.disconnect();
mServer = null;
}
/**
* 显示server上的opc服务器应用列表
*
* @param host
* @param user
* @param password
* @param domain
*/
public void showAllOPCServer(String host, String user, String password, String domain) {
try {
ServerList serverList = new ServerList(host, user, password, domain);
Collection<ClassDetails> detailList = serverList.listServersWithDetails(new Category[]{Categories.OPCDAServer10, Categories.OPCDAServer20}, new Category[]{});
for (ClassDetails details : detailList) {
LOGGER.info("ClsId=" + details.getClsId() + ",ProgId=" + details.getProgId() + ",Description=" + details.getDescription());
}
} catch (Exception e) {
LOGGER.error(e.getMessage());
}
}
/**
* 检查OPC server 中是否包含指定的监测点列表
*
* @param list
* @return
*/
public boolean checkItemList(List<String> list) {
// 获取OPCSserver上的所有监测点
FlatBrowser flatBrowser = mServer.getFlatBrowser();
if (flatBrowser == null) {
return false;
}
try {
Collection<String> collection = flatBrowser.browse();
return collection.containsAll(list);
} catch (Exception e) {
LOGGER.error(e.getMessage());
}
return false;
}
/**
* 异步读取数据
*
* @param itemIdList
* @param period
*/
public void readObjectList(List<String> itemIdList, int period) {
AccessBase accessBase;
try {
accessBase = new Async20Access(mServer, period, true);
for (String itemId : itemIdList) {
accessBase.addItem(itemId, new DataCallback() {
@Override
public void changed(Item item, ItemState itemState) {
try {
Object value = itemState.getValue().getObject();
setData(itemId, value);
OpcMap.putMap(itemId, value);//监听到的数据放入map集合中
} catch (JIException e) {
LOGGER.error(e.getMessage());
}
}
});
}
while (true) {
accessBase.bind();
}
} catch (Exception e) {
LOGGER.error(e.getMessage());
}
}
/**
* 向OPC中写数据
*
* @param value
*/
public void writeOPC(Object value) {
try {
// Add a new group,添加一个组,这个用来就读值或者写值一次,而不是循环读取或者写入
// 组的名字随意,给组起名字是因为,server可以addGroup也可以removeGroup,读一次值,就先添加组,然后移除组,再读一次就再添加然后删除
Group group = mServer.addGroup("test");
// Add a new item to the group,
// 将一个item加入到组,item名字就是MatrikonOPC Server或者KEPServer上面建的项的名字
Item item = group.addItem("channel01.device01.tag03");
//LOGGER.info("\n向OPC" + item.getId() + "中写入值");
item.write(new JIVariant(value.toString()));
mServer.removeGroup(group, true);
} catch (Exception e) {
e.printStackTrace();
LOGGER.error(e.getMessage());
}
}
/**
* 注册
*
* @param observer
*/
public void subscribe(Observer observer) {
this.addObserver(observer);
}
/**
* 数据变化通知观察者
*
* @param itemId
* @param value
*/
private void setData(String itemId, Object value) {
setChanged();
notifyObservers(new Result(itemId, value));
}
}
UtgardTutorial.java
使用Utgard框架完成对OPC的操作
package com.utgard;
import com.opc.OpcClient;
import com.opc.OpcMap;
import com.opc.OpcProperties;
import com.result.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import java.net.UnknownHostException;
import java.util.Observable;
import java.util.Observer;
/**
* 操作OPC UA
*/
@Configuration
public class UtgardTutorial {
private static final Logger LOGGER = LoggerFactory.getLogger(UtgardTutorial.class);
@Autowired
private OpcProperties opc;
public void start(){
OpcClient opcClient = new OpcClient();
// 显示server上的opc server应用列表
opcClient.showAllOPCServer(opc.getHost(), opc.getUser(), opc.getPassword(), opc.getDomain());
// 连接指定的opc server
try {
boolean ret = opcClient.connectionServer(opc.getHost(), opc.getProgId(), opc.getUser(), opc.getPassword(), opc.getDomain());
if (!ret) {
LOGGER.info("connect opc server fail");
return;
}
ret = opcClient.checkItemList(opc.getItemIdList());
if (!ret) {
LOGGER.info("not contain item list");
return;
}
// 注册回调
opcClient.subscribe(new Observer() {
@Override
public void update(Observable o, Object arg) {
Result result = (Result) arg;
OpcMap.putMap(result.getItemId(),result.getValue());//改变的值放入集合
LOGGER.info("\n----------发送改变的数据----------" + "\nitemId:" + result.getItemId() + "\nvalue:" + result.getValue() + "\n------------------------------");
// 向opc中写入数据
opcClient.writeOPC(result.getValue());
}
});
// 添加监测点数据
opcClient.readObjectList(opc.getItemIdList(), 500);
} catch (UnknownHostException e) {
LOGGER.error(e.getMessage());
}
}
}