一.OPC
1.使用的OPC server軟件:
模擬仿真用的 MatrikonOPCSimulation(50M),https://pan.baidu.com/share/init?surl=9rcHEBGSWGMSkRo1kEY6fQ,密碼: mcur
項目使用KEPServer V6(450M,中文),https://pan.baidu.com/share/init?surl=3Bc0gfGxCWo8fddTt-ut-w,密碼: ykj2
推薦使用KEPServer V6,配置DCOM步驟清晰。
2.使用OPC server軟件:
MatrikonOPCSimulation:https://www.cnblogs.com/ioufev/p/9366426.html
KEPServer V6:https://www.cnblogs.com/ioufev/p/9366877.html
3.配置Server和Client:
OPC和DCOM配置:https://www.cnblogs.com/ioufev/p/9365919.html
注:1.使用 MatrikonOPCSimulation,要額外下載OPC運行庫。
2. 創建用戶時,電腦管理內無“本地用戶和組”,原因為:Windows版本為家庭版,升級Windows版本為企業版或專業版。
3.在配置組件服務時,無創建的用戶時,可以通過添加得到。
二.編寫OPC Client
下載添加jar包,https://pan.baidu.com/s/1MLynD_iYceWpWxVIQ0e7Pw ,密碼: ermn,可以從此項目找到所有jar包。
讀取數據
import java.util.concurrent.Executors; import org.jinterop.dcom.common.JIException; import org.jinterop.dcom.core.JIString; 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.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"); // 電腦IP ci.setDomain(""); // 域,為空就行 ci.setUser("OPCUser"); // 電腦上自己建好的用戶名 ci.setPassword("123456"); // 用戶名的密碼 // 使用MatrikonOPC Server的配置 // ci.setClsid("F8582CF2-88FB-11D0-B850-00C0F0104305"); // MatrikonOPC的注冊表ID,可以在“組件服務”里看到 // final String itemId = "u.u"; // 項的名字按實際 // 使用KEPServer的配置 ci.setClsid("7BC0CC8E-482C-47CA-ABDC-0FE7F9C6E729"); // KEPServer的注冊表ID,可以在“組件服務”里看到 final String itemId = "u.u.u"; // 項的名字按實際,沒有實際PLC,用的模擬器:simulator // final String itemId = "通道 1.設備 1.標記 1"; // 啟動服務 final Server server = new Server(ci, Executors.newSingleThreadScheduledExecutor()); try { // 連接到服務 server.connect(); // add sync access, poll every 500 ms,啟動一個同步的access用來讀取地址上的值,線程池每500ms讀值一次 // 這個是用來循環讀值的,只讀一次值不用這樣 final AccessBase access = new SyncAccess(server, 500); // 這是個回調函數,就是讀到值后執行這個打印,是用匿名類寫的,當然也可以寫到外面去 access.addItem(itemId, new DataCallback() { @Override public void changed(Item item, ItemState itemState) { int type = 0; try { type = itemState.getValue().getType(); // 類型實際是數字,用常量定義的 } catch (JIException e) { e.printStackTrace(); } System.out.println("監控項的數據類型是:-----" + type); System.out.println("監控項的時間戳是:-----" + itemState.getTimestamp().getTime()); System.out.println("監控項的詳細信息是:-----" + itemState); // 如果讀到是short類型的值 if (type == JIVariant.VT_I2) { short n = 0; try { n = itemState.getValue().getObjectAsShort(); } catch (JIException e) { e.printStackTrace(); } System.out.println("-----short類型值: " + n); } // 如果讀到是字符串類型的值 if(type == JIVariant.VT_BSTR) { // 字符串的類型是8 JIString value = null; try { value = itemState.getValue().getObjectAsString(); } catch (JIException e) { e.printStackTrace(); } // 按字符串讀取 String str = value.getString(); // 得到字符串 System.out.println("-----String類型值: " + str); } } }); // start reading,開始讀值 access.bind(); // wait a little bit,有個10秒延時 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()))); } } }
讀取數值與寫入數值
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"); // 電腦IP ci.setDomain(""); // 域,為空就行 ci.setUser("OPCUser"); // 用戶名,配置DCOM時配置的 ci.setPassword("123456"); // 密碼 // 使用MatrikonOPC Server的配置 // ci.setClsid("F8582CF2-88FB-11D0-B850-00C0F0104305"); // MatrikonOPC的注冊表ID,可以在“組件服務”里看到 // final String itemId = "u.u"; // 項的名字按實際 // 使用KEPServer的配置 ci.setClsid("7BC0CC8E-482C-47CA-ABDC-0FE7F9C6E729"); // KEPServer的注冊表ID,可以在“組件服務”里看到 final String itemId = "u.u.u"; // 項的名字按實際,沒有實際PLC,用的模擬器:simulator // 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,啟動一個同步的access用來讀取地址上的值,線程池每500ms讀值一次 // 這個是用來循環讀值的,只讀一次值不用這樣 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) { // 如果讀到的值類型時UnsignedInteger,即無符號整形數值 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,添加一個組,這個用來就讀值或者寫值一次,而不是循環讀取或者寫入 // 組的名字隨意,給組起名字是因為,server可以addGroup也可以removeGroup,讀一次值,就先添加組,然后移除組,再讀一次就再添加然后刪除 final Group group = server.addGroup("test"); // Add a new item to the group, // 將一個item加入到組,item名字就是MatrikonOPC Server或者KEPServer上面建的項的名字比如:u.u.TAG1,PLC.S7-300.TAG1 final Item item = group.addItem(itemId); // start reading,開始循環讀值 access.bind(); // add a thread for writing a value every 3 seconds // 寫入一次就是item.write(value),循環寫入就起個線程一直執行item.write(value) ScheduledExecutorService writeThread = Executors.newSingleThreadScheduledExecutor(); writeThread.scheduleWithFixedDelay(new Runnable() { @Override public void run() { final JIVariant value = new JIVariant("24"); // 寫入24 try { System.out.println(">>> " + "寫入值: " + "24"); item.write(value); } catch (JIException e) { e.printStackTrace(); } } }, 5, 3, TimeUnit.SECONDS); // 啟動后5秒第一次執行代碼,以后每3秒執行一次代碼 // wait a little bit ,延時20秒 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()))); } } }