/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package com.eas.opc.da; import com.eas.opc.da.Item.State; import com.eas.opc.da.dcom.IOPCAsyncIO2; import com.eas.opc.da.dcom.IOPCAsyncIO2.ReadResult; import com.eas.opc.da.dcom.IOPCGroupStateMgt; import com.eas.opc.da.dcom.IOPCGroupStateMgt.GroupState; import com.eas.opc.da.dcom.IOPCItemMgt; import com.eas.opc.da.dcom.IOPCSyncIO; import com.eas.opc.da.dcom.OPCDataListener; import com.eas.opc.da.dcom.OPCITEMDEF; import com.eas.opc.da.dcom.OPCITEMRESULT; import com.eas.opc.da.dcom.OPCITEMSTATE; import com.eas.opc.da.dcom.ResultTable; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import org.jinterop.dcom.common.JIException; import org.jinterop.dcom.core.JIVariant; /** * * @author pk */ public class Group { private String name; private boolean active; private int requestedUpdateRate; private int revisedUpdateRate; private int clientHandle; private int serverHandle; private Integer timeBias; private Float percentDeadBand; private int localeID; protected final IOPCGroupStateMgt groupStateMgt; protected final IOPCItemMgt itemMgt; protected final IOPCSyncIO syncIO; protected final IOPCAsyncIO2 asyncIO; private final List<Item> knownItems = new ArrayList<>(); private final Map<Integer, Item> knownItemsByClientHandle = new HashMap<>(); private final List<DataListener> dataListeners = new ArrayList<>(); private final InternalDataListener internalListener = new InternalDataListener(); protected Group(String name, boolean active, int requestedUpdateRate, int revisedUpdateRate, int clientHandle, int serverHandle, Integer timeBias, Float percentDeadBand, int localeID, IOPCGroupStateMgt groupStateMgt) throws JIException { this.name = name; this.active = active; this.requestedUpdateRate = requestedUpdateRate; this.revisedUpdateRate = revisedUpdateRate; this.clientHandle = clientHandle; this.serverHandle = serverHandle; this.timeBias = timeBias; this.percentDeadBand = percentDeadBand; this.localeID = localeID; this.groupStateMgt = groupStateMgt; this.itemMgt = new IOPCItemMgt(groupStateMgt.queryInterface(IOPCItemMgt.IID_IOPCItemMgt)); this.syncIO = new IOPCSyncIO(groupStateMgt.queryInterface(IOPCSyncIO.IID_IOPCSyncIO)); this.asyncIO = new IOPCAsyncIO2(groupStateMgt.queryInterface(IOPCAsyncIO2.IID_IOPCAsyncIO2)); this.asyncIO.addDataListener(internalListener); } public void addDataListener(DataListener l) { synchronized (dataListeners) { dataListeners.add(l); } } public void removeDataListener(DataListener l) { synchronized (dataListeners) { dataListeners.remove(l); } } public boolean isActive() { return active; } public void setActive(boolean active) throws JIException { if (this.active != active) { final boolean oldValue = this.active; this.active = active; try { updateStateOnServer(); } catch (JIException ex) { this.active = oldValue; throw ex; } } } public int getClientHandle() { return clientHandle; } public void setClientHandle(int clientHandle) throws JIException { if (this.clientHandle != clientHandle) { final int oldValue = this.clientHandle; this.clientHandle = clientHandle; try { updateStateOnServer(); } catch (JIException ex) { this.clientHandle = oldValue; throw ex; } } } public int getLocaleID() { return localeID; } public void setLocaleID(int localeID) throws JIException { if (this.localeID != localeID) { final int oldValue = this.localeID; this.localeID = localeID; try { updateStateOnServer(); } catch (JIException ex) { this.localeID = oldValue; throw ex; } } } public String getName() { return name; } public void setName(String name) throws JIException { groupStateMgt.setName(name); this.name = name; } public Float getPercentDeadBand() { return percentDeadBand; } public void setPercentDeadBand(Float percentDeadBand) throws JIException { if (this.percentDeadBand == null && percentDeadBand != null || this.percentDeadBand != null && !this.percentDeadBand.equals(percentDeadBand)) { final Float oldValue = this.percentDeadBand; this.percentDeadBand = percentDeadBand; try { updateStateOnServer(); } catch (JIException ex) { this.percentDeadBand = oldValue; throw ex; } } } public int getRequestedUpdateRate() { return requestedUpdateRate; } public void setRequestedUpdateRate(int requestedUpdateRate) throws JIException { if (this.requestedUpdateRate != requestedUpdateRate) { final int oldValue = this.requestedUpdateRate; this.requestedUpdateRate = requestedUpdateRate; try { updateStateOnServer(); } catch (JIException ex) { this.requestedUpdateRate = oldValue; throw ex; } } } public int getRevisedUpdateRate() { return revisedUpdateRate; } public int getServerHandle() { return serverHandle; } public Integer getTimeBias() { return timeBias; } public void setTimeBias(Integer timeBias) throws JIException { if (this.timeBias == null && timeBias != null || this.timeBias != null && !this.timeBias.equals(timeBias)) { final Integer oldValue = this.timeBias; this.timeBias = timeBias; try { updateStateOnServer(); } catch (JIException ex) { this.timeBias = oldValue; throw ex; } } } public void updateState() throws JIException { final GroupState state = groupStateMgt.getState(); name = state.getName(); clientHandle = state.getClientHandle(); localeID = state.getLocaleId(); percentDeadBand = state.getPercentDeadband(); serverHandle = state.getServerHandle(); timeBias = state.getTimeBias(); revisedUpdateRate = state.getUpdateRate(); } public int[] addItems(Item... items) throws JIException { synchronized (this) { for (Item i : items) { if (knownItems.contains(i)) { throw new IllegalArgumentException(String.format("Item %s is already added.", i.getItemID())); } } } final OPCITEMDEF[] itemsDefs = buildItemsDefs(items); final ResultTable<OPCITEMDEF, OPCITEMRESULT> result = itemMgt.addItems(itemsDefs); final int[] errors = new int[items.length]; for (int i = 0; i < items.length; i++) { errors[i] = result.errorCode(itemsDefs[i]); final OPCITEMRESULT itemResult = result.get(itemsDefs[i]); items[i].setAccessRights(itemResult.getAccessRights()); items[i].setCanonicalDataType(DataType.getDataType(itemResult.getCanonicalDataType())); items[i].setReserved(itemResult.getReserved()); items[i].setServerHandle(itemResult.getServerHandle()); } synchronized (this) { for (Item i : items) { knownItems.add(i); knownItemsByClientHandle.put(i.getClientHandle(), i); i.setGroup(this); } } return errors; } public int[] validateItems(Item... items) throws JIException { final OPCITEMDEF[] itemsDefs = buildItemsDefs(items); final ResultTable<OPCITEMDEF, OPCITEMRESULT> result = itemMgt.validateItems(itemsDefs, false); final int[] errors = new int[items.length]; for (int i = 0; i < items.length; i++) { errors[i] = result.errorCode(itemsDefs[i]); final OPCITEMRESULT itemResult = result.get(itemsDefs[i]); items[i].setAccessRights(itemResult.getAccessRights()); items[i].setCanonicalDataType(DataType.getDataType(itemResult.getCanonicalDataType())); items[i].setReserved(itemResult.getReserved()); items[i].setServerHandle(itemResult.getServerHandle()); } return errors; } public int[] removeItems(Item... items) throws JIException { synchronized (this) { for (Item i : items) { if (!knownItems.contains(i)) { throw new IllegalArgumentException(String.format("Item %s is not added.", i.getItemID())); } } } final Integer[] serverHandles = new Integer[items.length]; for (int i = 0; i < items.length; i++) { serverHandles[i] = items[i].getServerHandle(); } final Map<Integer, Integer> result = itemMgt.removeItems(serverHandles); final int[] errors = new int[items.length]; for (int i = 0; i < items.length; i++) { errors[i] = result.get(items[i].getServerHandle()); } synchronized (this) { for (Item i : items) { knownItems.remove(i); knownItemsByClientHandle.remove(i.getClientHandle()); } } return errors; } public int[] removeAllItems() throws JIException { Item[] items; synchronized (this) { items = new Item[knownItems.size()]; items = knownItems.toArray(items); } return removeItems(items); } public Item[] getItems() { Item[] result; synchronized (this) { result = new Item[knownItems.size()]; result = knownItems.toArray(result); } return result; } public Item.State[] read(DataSource ds, Item... items) throws JIException { final int[] serverHandles = new int[items.length]; synchronized (this) { for (int i = 0; i < items.length; i++) { if (!knownItemsByClientHandle.containsKey(items[i].getClientHandle())) { throw new IllegalArgumentException("Group does not contain item " + items[i]); } else if (items[i].getServerHandle() == null) { throw new IllegalArgumentException("" + items[i] + " has not been added to any group."); } else { serverHandles[i] = items[i].getServerHandle(); } } } final ResultTable<Integer, OPCITEMSTATE> out = syncIO.read(ds.getValue(), serverHandles); final Item.State[] result = new Item.State[items.length]; for (int i = 0; i < items.length; i++) { final OPCITEMSTATE state = out.get(items[i].getServerHandle()); result[i] = new Item.State(items[i], state.getTimeStamp().getTime(), state.getQuality(), (short) 0, state.getDataValue().getObject(), out.errorCode(items[i].getServerHandle())); } return result; } public Item.State[] read(DataSource ds) throws JIException { Item[] items; synchronized (this) { items = new Item[knownItems.size()]; items = knownItems.toArray(items); } return read(ds, items); } public void readRawHistory(Date startTime, Date endTime, int numValues, boolean withBounds, Item... items) { final Integer[] serverHandles = new Integer[items.length]; synchronized (this) { for (int i = 0; i < items.length; i++) { if (!knownItemsByClientHandle.containsKey(items[i].getClientHandle())) { throw new IllegalArgumentException("Group does not contain item " + items[i]); } else if (items[i].getServerHandle() == null) { throw new IllegalArgumentException("" + items[i] + " has not been added to any group."); } else { serverHandles[i] = items[i].getServerHandle(); } } } } public int[] write(Object... data) throws JIException { if (data.length % 2 != 0) { throw new IllegalArgumentException("syntax: write(item1, value1, item2, value2, ..."); } final int[] serverHandles = new int[data.length / 2]; final JIVariant[] values = new JIVariant[serverHandles.length]; for (int i = 0; i < serverHandles.length; i++) { if (!(data[i] instanceof Item)) { throw new IllegalArgumentException("syntax: write(item1, value1, item2, value2, ..."); } Item item = (Item) data[i * 2]; if (item.getServerHandle() == null || item.getGroup() != this) { throw new IllegalArgumentException("" + item + " not in this group."); } serverHandles[i] = item.getServerHandle(); values[i] = new JIVariant(data[i * 2 + 1], false); } final Map<Integer, Integer> result = syncIO.write(serverHandles, values); final int[] errors = new int[serverHandles.length]; for (int i = 0; i < serverHandles.length; i++) { errors[i] = result.get(serverHandles[i]); } return errors; } public AsyncOperationInfo readAsync(int transactionID, Item... items) throws JIException { final int[] serverHandles = new int[items.length]; synchronized (this) { for (int i = 0; i < items.length; i++) { if (!knownItemsByClientHandle.containsKey(items[i].getClientHandle())) { throw new IllegalArgumentException("Group does not contain item " + items[i]); } else if (items[i].getServerHandle() == null) { throw new IllegalArgumentException("" + items[i] + " has not been added to any group."); } else { serverHandles[i] = items[i].getServerHandle(); } } } final ReadResult r = asyncIO.read(serverHandles, transactionID); final int[] errors = new int[serverHandles.length]; for (int i = 0; i < serverHandles.length; i++) { errors[i] = r.getErrorCodes().get(serverHandles[i]); } return new AsyncOperationInfo(r.getCancelId(), errors); } public AsyncOperationInfo writeAsync(int transactionID, Object... data) throws JIException { if (data.length % 2 != 0) { throw new IllegalArgumentException("syntax: writeAsync(transid, item1, value1, item2, value2, ..."); } final int[] serverHandles = new int[data.length / 2]; final JIVariant[] values = new JIVariant[serverHandles.length]; for (int i = 0; i < serverHandles.length; i++) { if (!(data[i] instanceof Item)) { throw new IllegalArgumentException("syntax: writeAsync(transid, item1, value1, item2, value2, ..."); } Item item = (Item) data[i * 2]; if (item.getServerHandle() == null || item.getGroup() != this) { throw new IllegalArgumentException("" + item + " not in this group."); } serverHandles[i] = item.getServerHandle(); values[i] = new JIVariant(data[i * 2 + 1], false); } final ReadResult r = asyncIO.write(serverHandles, values, transactionID); final int[] errors = new int[serverHandles.length]; for (int i = 0; i < serverHandles.length; i++) { errors[i] = r.getErrorCodes().get(serverHandles[i]); } return new AsyncOperationInfo(r.getCancelId(), errors); } public AsyncOperationInfo refreshAsync(DataSource ds, int transactionID) throws JIException { final int cancelID = asyncIO.refresh2(ds.getValue(), transactionID); return new AsyncOperationInfo(cancelID, new int[0]); } public void cancelAsync(AsyncOperationInfo info) throws JIException { asyncIO.cancel2(info.getCancelID()); } public boolean isSubscriptionEnabled() throws JIException { return asyncIO.getEnable(); } public void setSubscriptionEnabled(boolean enabled) throws JIException { asyncIO.setEnable(enabled); } @Override public String toString() { return String.format("[OPC DA group %s, clientHandle=%d, serverHandle=%d]", name, clientHandle, serverHandle); } protected void cleanup() throws JIException { synchronized (this) { asyncIO.removeDataListener(internalListener); removeAllItems(); } } private void updateStateOnServer() throws JIException { final GroupState state = new GroupState(); state.setActive(active); state.setClientHandle(clientHandle); state.setLocaleId(localeID); state.setName(name); state.setPercentDeadband(percentDeadBand); state.setServerHandle(serverHandle); state.setTimeBias(timeBias); state.setUpdateRate(requestedUpdateRate); this.revisedUpdateRate = groupStateMgt.setState(state); } private OPCITEMDEF[] buildItemsDefs(Item[] items) { final OPCITEMDEF[] itemsDefs = new OPCITEMDEF[items.length]; for (int i = 0; i < items.length; i++) { itemsDefs[i] = new OPCITEMDEF(); itemsDefs[i].setAccessPath(items[i].getAccessPath()); itemsDefs[i].setActive(items[i].isActive()); itemsDefs[i].setClientHandle(items[i].getClientHandle()); itemsDefs[i].setItemID(items[i].getItemID()); itemsDefs[i].setRequestedDataType(items[i].getRequestedDataType().getTypeID()); itemsDefs[i].setReserved(items[i].getReserved()); } return itemsDefs; } public static class AsyncOperationInfo { private final int[] errors; private final int cancelID; public AsyncOperationInfo(int cancelID, int[] errors) { this.errors = errors; this.cancelID = cancelID; } public int getCancelID() { return cancelID; } public int[] getErrors() { return errors; } } private class InternalDataListener implements OPCDataListener { private DataListener[] getListeners() { DataListener[] result; synchronized (Group.this.dataListeners) { result = new DataListener[Group.this.dataListeners.size()]; result = Group.this.dataListeners.toArray(result); } return result; } private Item.State[] getResult(Integer[] clientHandles, Object[] values, Short[] qualities, Date[] timeStamps, Integer[] errors) { final List<State> states = new ArrayList<>(clientHandles.length); for (int i = 0; i < clientHandles.length; i++) { final Item item = Group.this.knownItemsByClientHandle.get(clientHandles[i]); if (item != null) { states.add(new Item.State(item, timeStamps == null ? null : timeStamps[i], qualities == null ? 0 : qualities[i], (short) 0, values == null ? null : values[i], errors == null ? 0 : errors[i])); } } State[] result = new Item.State[states.size()]; result = states.toArray(result); return result; } public void dataChanged(Integer transid, Integer group, Integer masterQuality, Integer masterError, Integer[] clientHandles, Object[] values, Short[] qualities, Date[] timeStamps, Integer[] errors) { final DataListener[] listeners = getListeners(); final Item.State[] result = getResult(clientHandles, values, qualities, timeStamps, errors); for (DataListener l : listeners) { l.dataChanged(transid, Group.this, masterQuality, masterError, result); } } public void readCompleted(Integer transid, Integer group, Integer masterQuality, Integer masterError, Integer[] clientHandles, Object[] values, Short[] qualities, Date[] timeStamps, Integer[] errors) { final DataListener[] listeners = getListeners(); final Item.State[] result = getResult(clientHandles, values, qualities, timeStamps, errors); for (DataListener l : listeners) { l.readCompleted(transid, Group.this, masterQuality, masterError, result); } } public void writeCompleted(Integer transid, Integer group, Integer masterError, Integer[] clientHandles, Integer[] errors) { final DataListener[] listeners = getListeners(); final Item.State[] result = getResult(clientHandles, null, null, null, errors); for (DataListener l : listeners) { l.writeCompleted(transid, Group.this, masterError, result); } } public void cancelCompleted(Integer transid, Integer group) { final DataListener[] listeners = getListeners(); for (DataListener l : listeners) { l.cancelCompleted(transid, Group.this); } } } }