Central API Module providing core interfaces and abstractions for unified access to industrial programmable logic controllers (PLCs)
—
PLC namespace exploration and tag discovery capabilities for Apache PLC4X Java API.
Interface for building and executing browse requests to explore PLC namespaces and discover available tags.
/**
* Browse request interface for exploring PLC namespaces and discovering tags
*/
public interface PlcBrowseRequest extends PlcRequest {
/**
* Execute the browse request asynchronously
* @return CompletableFuture containing the browse response
*/
CompletableFuture<? extends PlcBrowseResponse> execute();
/**
* Execute the browse request with an interceptor for processing results
* @param interceptor PlcBrowseRequestInterceptor for custom result processing
* @return CompletableFuture containing the browse response
*/
CompletableFuture<? extends PlcBrowseResponse> executeWithInterceptor(PlcBrowseRequestInterceptor interceptor);
/**
* Get all query names in this browse request
* @return LinkedHashSet of query names
*/
LinkedHashSet<String> getQueryNames();
/**
* Get a specific query by name
* @param name Query name
* @return PlcQuery object
*/
PlcQuery getQuery(String name);
/**
* Builder interface for constructing browse requests
*/
interface Builder extends PlcRequestBuilder {
/**
* Add a browse query
* @param name Logical name for the query
* @param query Query string (protocol-specific format)
* @return Builder instance for method chaining
*/
Builder addQuery(String name, String query);
/**
* Build the browse request
* @return PlcBrowseRequest instance ready for execution
*/
PlcBrowseRequest build();
}
}Interface for accessing browse response data and discovered items.
/**
* Browse response interface providing access to discovered PLC items
*/
public interface PlcBrowseResponse extends PlcResponse {
/**
* Get the originating browse request
* @return PlcBrowseRequest that generated this response
*/
PlcBrowseRequest getRequest();
/**
* Get query names from the response
* @return Collection of query names
*/
Collection<String> getQueryNames();
/**
* Get response code for a specific query
* @param name Query name
* @return PlcResponseCode indicating success or failure
*/
PlcResponseCode getResponseCode(String name);
/**
* Get browse items for a specific query
* @param name Query name
* @return Collection of PlcBrowseItem discovered by the query
*/
Collection<PlcBrowseItem> getValues(String name);
}Interface representing a discovered PLC item with its properties and metadata.
/**
* Interface representing a discovered PLC item
*/
public interface PlcBrowseItem {
/**
* Get the tag address for this item
* @return String representation of the tag address
*/
String getTag();
/**
* Get the display name for this item
* @return Human-readable name
*/
String getName();
/**
* Get the data type of this item
* @return PlcValueType indicating the data type
*/
PlcValueType getDataType();
/**
* Check if this item is readable
* @return true if item can be read
*/
boolean isReadable();
/**
* Check if this item is writable
* @return true if item can be written
*/
boolean isWritable();
/**
* Check if this item is subscribable
* @return true if item supports subscriptions
*/
boolean isSubscribable();
/**
* Get children items (for hierarchical structures)
* @return Map of child name to PlcBrowseItem
*/
Map<String, PlcBrowseItem> getChildren();
/**
* Get additional options/properties for this item
* @return Map of option names to values
*/
Map<String, PlcValue> getOptions();
/**
* Get array information if this item is an array
* @return List of ArrayInfo describing array dimensions
*/
List<ArrayInfo> getArrayInfo();
}Interface providing array dimension information for browse items.
/**
* Array information for browse items
*/
public interface PlcBrowseItemArrayInfo {
/**
* Get the size of this array dimension
* @return Array size
*/
int getSize();
/**
* Get the lower bound index
* @return Lower bound (typically 0 or 1)
*/
int getLowerBound();
/**
* Get the upper bound index
* @return Upper bound
*/
int getUpperBound();
}Interface for intercepting and processing browse results.
/**
* Interceptor interface for processing browse results during execution
*/
public interface PlcBrowseRequestInterceptor {
/**
* Intercept and process a browse item
* @param browseItem The discovered browse item
* @return Modified or filtered browse item, or null to exclude
*/
PlcBrowseItem intercept(PlcBrowseItem browseItem);
}Usage Examples:
import org.apache.plc4x.java.DefaultPlcDriverManager;
import org.apache.plc4x.java.api.PlcConnection;
import org.apache.plc4x.java.api.messages.*;
import org.apache.plc4x.java.api.model.PlcQuery;
import org.apache.plc4x.java.api.types.PlcResponseCode;
import org.apache.plc4x.java.api.types.PlcValueType;
// Basic browse operation
PlcDriverManager driverManager = new DefaultPlcDriverManager();
try (PlcConnection connection = driverManager.getConnection("s7://192.168.1.200/0/1")) {
connection.connect();
// Check if browsing is supported
if (connection.getMetadata().isBrowseSupported()) {
// Build browse request
PlcBrowseRequest browseRequest = connection.browseRequestBuilder()
.addQuery("root", "*") // Browse all items at root level
.build();
PlcBrowseResponse response = browseRequest.execute().get();
if (response.getResponseCode("root") == PlcResponseCode.OK) {
Collection<PlcBrowseItem> items = response.getValues("root");
System.out.println("Discovered " + items.size() + " items:");
for (PlcBrowseItem item : items) {
System.out.println("- " + item.getName() +
" (" + item.getTag() + ")" +
" Type: " + item.getDataType() +
" R:" + item.isReadable() +
" W:" + item.isWritable() +
" S:" + item.isSubscribable());
}
}
} else {
System.out.println("Browsing not supported by this connection");
}
}
// Hierarchical browsing
try (PlcConnection connection = driverManager.getConnection("s7://192.168.1.200/0/1")) {
connection.connect();
PlcBrowseRequest browseRequest = connection.browseRequestBuilder()
.addQuery("data_blocks", "DB*") // Browse all data blocks
.addQuery("memory_areas", "M*") // Browse memory areas
.addQuery("inputs", "I*") // Browse inputs
.addQuery("outputs", "Q*") // Browse outputs
.build();
PlcBrowseResponse response = browseRequest.execute().get();
// Process each query result
for (String queryName : response.getQueryNames()) {
System.out.println("\n=== " + queryName.toUpperCase() + " ===");
if (response.getResponseCode(queryName) == PlcResponseCode.OK) {
Collection<PlcBrowseItem> items = response.getValues(queryName);
for (PlcBrowseItem item : items) {
printBrowseItemDetails(item, 0);
// Explore children if available
if (!item.getChildren().isEmpty()) {
item.getChildren().forEach((childName, childItem) -> {
printBrowseItemDetails(childItem, 1);
});
}
}
} else {
System.out.println("Query failed: " + response.getResponseCode(queryName));
}
}
}
// Filtered browsing with interceptor
try (PlcConnection connection = driverManager.getConnection("modbus-tcp://192.168.1.100:502")) {
connection.connect();
// Create interceptor to filter only writable items
PlcBrowseRequestInterceptor writableFilter = browseItem -> {
if (browseItem.isWritable()) {
return browseItem; // Keep writable items
}
return null; // Filter out read-only items
};
PlcBrowseRequest browseRequest = connection.browseRequestBuilder()
.addQuery("writable_registers", "holding-register:*")
.build();
PlcBrowseResponse response = browseRequest.executeWithInterceptor(writableFilter).get();
if (response.getResponseCode("writable_registers") == PlcResponseCode.OK) {
Collection<PlcBrowseItem> writableItems = response.getValues("writable_registers");
System.out.println("Found " + writableItems.size() + " writable registers:");
for (PlcBrowseItem item : writableItems) {
System.out.println("- " + item.getTag() + " (" + item.getDataType() + ")");
}
}
}
// Array item discovery
try (PlcConnection connection = driverManager.getConnection("s7://192.168.1.200/0/1")) {
connection.connect();
PlcBrowseRequest browseRequest = connection.browseRequestBuilder()
.addQuery("arrays", "DB1.*[*]") // Browse array items in DB1
.build();
PlcBrowseResponse response = browseRequest.execute().get();
if (response.getResponseCode("arrays") == PlcResponseCode.OK) {
Collection<PlcBrowseItem> arrayItems = response.getValues("arrays");
for (PlcBrowseItem item : arrayItems) {
System.out.println("Array: " + item.getName());
System.out.println(" Tag: " + item.getTag());
System.out.println(" Type: " + item.getDataType());
// Display array dimension information
List<ArrayInfo> arrayInfo = item.getArrayInfo();
if (!arrayInfo.isEmpty()) {
System.out.println(" Dimensions:");
for (int i = 0; i < arrayInfo.size(); i++) {
ArrayInfo dimension = arrayInfo.get(i);
System.out.println(" [" + i + "]: " + dimension.getLowerBound() +
".." + dimension.getUpperBound() +
" (size: " + dimension.getSize() + ")");
}
}
}
}
}
// Browse with specific data type filtering
try (PlcConnection connection = driverManager.getConnection("s7://192.168.1.200/0/1")) {
connection.connect();
PlcBrowseRequest browseRequest = connection.browseRequestBuilder()
.addQuery("all_items", "*")
.build();
PlcBrowseResponse response = browseRequest.execute().get();
if (response.getResponseCode("all_items") == PlcResponseCode.OK) {
Collection<PlcBrowseItem> allItems = response.getValues("all_items");
// Group items by data type
Map<PlcValueType, List<PlcBrowseItem>> itemsByType = allItems.stream()
.collect(Collectors.groupingBy(PlcBrowseItem::getDataType));
itemsByType.forEach((dataType, items) -> {
System.out.println("\n" + dataType + " items (" + items.size() + "):");
items.forEach(item -> {
System.out.println(" - " + item.getName() + " (" + item.getTag() + ")");
});
});
}
}
// Metadata and options exploration
try (PlcConnection connection = driverManager.getConnection("s7://192.168.1.200/0/1")) {
connection.connect();
PlcBrowseRequest browseRequest = connection.browseRequestBuilder()
.addQuery("detailed", "DB10.*")
.build();
PlcBrowseResponse response = browseRequest.execute().get();
if (response.getResponseCode("detailed") == PlcResponseCode.OK) {
Collection<PlcBrowseItem> items = response.getValues("detailed");
for (PlcBrowseItem item : items) {
System.out.println("\nItem: " + item.getName());
System.out.println(" Tag: " + item.getTag());
System.out.println(" Type: " + item.getDataType());
System.out.println(" Readable: " + item.isReadable());
System.out.println(" Writable: " + item.isWritable());
System.out.println(" Subscribable: " + item.isSubscribable());
// Display additional options/metadata
Map<String, PlcValue> options = item.getOptions();
if (!options.isEmpty()) {
System.out.println(" Options:");
options.forEach((optionName, optionValue) -> {
System.out.println(" " + optionName + ": " + optionValue.getObject());
});
}
}
}
}
// Helper method for displaying browse item details
private static void printBrowseItemDetails(PlcBrowseItem item, int indentLevel) {
String indent = " ".repeat(indentLevel);
System.out.printf("%s- %s (%s) [%s] R:%s W:%s S:%s%n",
indent,
item.getName(),
item.getTag(),
item.getDataType(),
item.isReadable() ? "Y" : "N",
item.isWritable() ? "Y" : "N",
item.isSubscribable() ? "Y" : "N"
);
// Show array information if present
if (!item.getArrayInfo().isEmpty()) {
System.out.print(indent + " Array: ");
for (ArrayInfo arrayInfo : item.getArrayInfo()) {
System.out.printf("[%d..%d] ", arrayInfo.getLowerBound(), arrayInfo.getUpperBound());
}
System.out.println();
}
}public interface PlcQuery {
/**
* Get the query string
* @return Query string in protocol-specific format
*/
String getQueryString();
}
public interface PlcDiscoveryRequest extends PlcRequest {
CompletableFuture<? extends PlcDiscoveryResponse> execute();
}
public interface PlcDiscoveryResponse extends PlcResponse {
PlcDiscoveryRequest getRequest();
Collection<PlcDiscoveryItem> getValues();
}
public interface PlcDiscoveryItem {
String getProtocolCode();
String getTransportCode();
String getTransportUrl();
String getOptions();
String getName();
}
public interface PlcDiscoveryItemHandler {
void handle(PlcDiscoveryItem discoveryItem);
}public interface ArrayInfo {
int getSize();
int getLowerBound();
int getUpperBound();
}Different PLC protocols support different browse query patterns:
* - Browse all items at current levelDB* - Browse all data blocksDB1.* - Browse all items in data block 1DB1.DBD* - Browse all double words in data block 1M* - Browse all memory areasI* - Browse all inputsQ* - Browse all outputs* - Browse all available registersholding-register:* - Browse all holding registersinput-register:* - Browse all input registerscoil:* - Browse all coilsdiscrete-input:* - Browse all discrete inputs* - Browse all nodes at current levelObjects.* - Browse all objectsTypes.* - Browse all type definitionsViews.* - Browse all viewsBrowse operations provide a powerful way to discover and explore PLC namespaces without prior knowledge of the tag structure, making it ideal for debugging, development, and dynamic tag discovery scenarios.
Install with Tessl CLI
npx tessl i tessl/maven-org-apache-plc4x--plc4j-api