Once you have the OPC Gateway running, it's possible access the OPC Legacy server from any system using pure Java code. Here's a sample that shows how to do that.
import com.ergotech.util.SimulationManager;
import com.ergotech.util.TargetLicenseManager;
import com.ergotech.vib.exceptions.BadParameterException;
import com.ergotech.vib.exceptions.VIBUpdateFailedException;
import com.ergotech.vib.servers.SimpleDataSource;
import com.ergotech.vib.servers.opc.opcclient.OPC;
import com.ergotech.vib.utils.DataSourceContainer;
import com.ergotech.vib.valueobjects.ValueChangedEvent;
import com.ergotech.vib.valueobjects.ValueObjectInterface;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
public class OPCDemo {
public static void main(String[] args) throws InterruptedException {
OPCDemo opcDemo = new OPCDemo();
// Configure the license, required for OPC component to function.
opcDemo.setupLicense();
// Disable simulation mode to connect to the actual OPC server.
SimulationManager.getSimulationManager().setSimulating(false);
// Initialize and start the OPC setup.
opcDemo.setupOpc();
// Keep the application running for 5 minutes to receive updates from the OPC server.
Thread.sleep(1000 * 60 * 5);
}
public void setupLicense() {
try (InputStream license = Files.newInputStream(Path.of("resources/MIX.trg"))) {
// Initialize the license manager with the appropriate target.
final TargetLicenseManager manager = new TargetLicenseManager("MIX",
license,
OPCDemo.class.getClassLoader());
// Set the license manager for components.
SimpleDataSource.setClassLicenseManager(manager);
} catch (Exception e) {
// Handle any failure during license setup.
throw new RuntimeException("Failed to setup license", e);
}
}
public void setupOpc() {
DataSourceContainer container;
try {
// Create a new OPC container to hold the OPC components.
container = DataSourceContainer.getRootContainer().createNewChildContainer("OPCExample");
} catch (BadParameterException e) {
// Occurs if a container with the same name already exists.
throw new RuntimeException("Failed to create OPC container", e);
}
OPC opc = new OPC();
try {
// Assign a unique, case-insensitive name to the OPC component.
opc.setName("OPC");
// Assign the host and item to connect to in the OPC server.
opc.setHost("localhost"); // the host that is running the OPCGateway
opc.setItem("GEMToolSimulator.VIDs.SVIDs.Temperature");
} catch (BadParameterException e) {
// Handle invalid configuration for the OPC component.
throw new RuntimeException("Invalid OPC configuration", e);
}
try {
// Add the configured OPC component to the container.
container.addComponent(opc, opc.getName());
} catch (BadParameterException e) {
// Occurs if a component with the same name is already added to the container.
throw new RuntimeException("Failed to add OPC component to container", e);
}
// Register a listener to handle value change events from the OPC server.
opc.addValueChangedListener(new ValueChangedCallback());
try {
// Initialize and start the OPC component.
opc.init();
opc.start();
} catch (BadParameterException | VIBUpdateFailedException e) {
// Handle any failure during initialization or startup.
throw new RuntimeException("Failed to start OPC component", e);
}
// Access current values as various types
// Force the server to register for value updates even though there are no listeners
opc.setAutoSuspend(SimpleDataSource.AUTOSUSPEND_NEVER);
// read the data
System.out.println("Current value as String: " + opc.getValueObject().getStringValue());
System.out.println("Current value as Double: " + opc.getValueObject().getDoubleValue());
System.out.println("Current value as Float: " + opc.getValueObject().getFloatValue());
System.out.println("Current value as Integer: " + opc.getValueObject().getIntValue());
System.out.println("Current value as Long: " + opc.getValueObject().getLongValue());
System.out.println("Current value as Boolean: " + opc.getValueObject().getBoolValue());
}
// Callback to process value changes from the OPC component.
public static class ValueChangedCallback {
// Any object with a method "valueInput(ValueChangedEvent) can be used a callback
public void valueInput(ValueChangedEvent valueChangedEvent) {
// Logic to handle changes in OPC data, triggered whenever the OPC value changes.
ValueObjectInterface valueObject = valueChangedEvent.getValueObject();
System.out.println("Value Changed: " + valueObject + " [quality: " + valueObject.getQuality() + "]");
}
}
}
The OPC DA standard is optimized for a subscription model. Here's an example of reading multiple servers. Each will update when the value changes at the server and the updated value will be stored in a HashMap. Multiple listeners/HashMaps could be used to build "groups" of data, especially if you're looking to use ErgoTech's "TriggeredHistorical" component to periodically save these straight to a database.
import com.ergotech.util.SimulationManager;
import com.ergotech.util.TargetLicenseManager;
import com.ergotech.vib.exceptions.BadParameterException;
import com.ergotech.vib.exceptions.VIBUpdateFailedException;
import com.ergotech.vib.servers.SimpleDataSource;
import com.ergotech.vib.servers.opc.opcclient.OPC;
import com.ergotech.vib.utils.DataSourceContainer;
import com.ergotech.vib.valueobjects.ValueChangedEvent;
import com.ergotech.vib.valueobjects.ValueObjectInterface;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
public class OPCDemo {
// HashMap to store the latest values for each item
private HashMap itemValueMap = new HashMap<>();
public static void main(String[] args) throws InterruptedException {
OPCDemo opcDemo = new OPCDemo();
// Configure the license, required for OPC component to function.
opcDemo.setupLicense();
// Disable simulation mode to connect to the actual OPC server.
SimulationManager.getSimulationManager().setSimulating(false);
// Initialize and start the OPC setup.
opcDemo.setupOpc();
// Keep the application running for 5 minutes to receive updates from the OPC server.
Thread.sleep(1000 * 60 * 5);
}
public void setupLicense() {
try (InputStream license = Files.newInputStream(Path.of("resources/MIX.trg"))) {
// Initialize the license manager with the appropriate target.
final TargetLicenseManager manager = new TargetLicenseManager("MIX",
license,
OPCDemo.class.getClassLoader());
// Set the license manager for components.
SimpleDataSource.setClassLicenseManager(manager);
} catch (Exception e) {
// Handle any failure during license setup.
throw new RuntimeException("Failed to setup license", e);
}
}
public void setupOpc() {
DataSourceContainer container;
try {
// Create a new OPC container to hold the OPC components.
container = DataSourceContainer.getRootContainer().createNewChildContainer("OPCExample");
} catch (BadParameterException e) {
// Occurs if a container with the same name already exists.
throw new RuntimeException("Failed to create OPC container", e);
}
// List of items to monitor
String[] items = {
"GEMToolSimulator.VIDs.SVIDs.Temperature",
"GEMToolSimulator.VIDs.SVIDs.Pressure",
"GEMToolSimulator.VIDs.SVIDs.Humidity"
};
// Create a single listener instance
ValueChangedCallback listener = new ValueChangedCallback();
for (String item : items) {
OPC opc = new OPC();
try {
// Assign a unique, case-insensitive name to the OPC component.
opc.setName("OPC_" + item);
// Assign the host and item to connect to in the OPC server.
opc.setHost("localhost"); // the host that is running the OPCGateway
opc.setItem(item);
} catch (BadParameterException e) {
// Handle invalid configuration for the OPC component.
throw new RuntimeException("Invalid OPC configuration", e);
}
try {
// Add the configured OPC component to the container.
container.addComponent(opc, opc.getName());
} catch (BadParameterException e) {
// Occurs if a component with the same name is already added to the container.
throw new RuntimeException("Failed to add OPC component to container", e);
}
// Register the same listener to handle value change events from the OPC server.
opc.addValueChangedListener(listener);
try {
// Initialize and start the OPC component.
opc.init();
opc.start();
} catch (BadParameterException | VIBUpdateFailedException e) {
// Handle any failure during initialization or startup.
throw new RuntimeException("Failed to start OPC component", e);
}
// Force the server to register for value updates even though there are no listeners
opc.setAutoSuspend(SimpleDataSource.AUTOSUSPEND_NEVER);
}
// Access current values from the HashMap
for (String item : items) {
ValueObjectInterface valueObject = itemValueMap.get(item);
if (valueObject != null) {
System.out.println("Current value of " + item + " as String: " + valueObject.getStringValue());
System.out.println("Current value of " + item + " as Double: " + valueObject.getDoubleValue());
System.out.println("Current value of " + item + " as Float: " + valueObject.getFloatValue());
System.out.println("Current value of " + item + " as Integer: " + valueObject.getIntValue());
System.out.println("Current value of " + item + " as Long: " + valueObject.getLongValue());
System.out.println("Current value of " + item + " as Boolean: " + valueObject.getBoolValue());
} else {
System.out.println("No value yet for " + item);
}
}
}
// Callback to process value changes from the OPC component.
public class ValueChangedCallback {
// Any object with a method "valueInput(ValueChangedEvent)" can be used as a callback
public void valueInput(ValueChangedEvent valueChangedEvent) {
// Logic to handle changes in OPC data, triggered whenever the OPC value changes.
ValueObjectInterface valueObject = valueChangedEvent.getValueObject();
OPC opc = (OPC) valueChangedEvent.getSource();
String itemName = opc.getItem();
// Update the HashMap with the new value
itemValueMap.put(itemName, valueObject);
System.out.println("Value Changed for item " + itemName + ": " + valueObject + " [quality: " + valueObject.getQuality() + "]");
}
}
}