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() + "]"); } } }