CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-apache-plc4x--plc4j-api

Central API Module providing core interfaces and abstractions for unified access to industrial programmable logic controllers (PLCs)

Pending
Overview
Eval results
Files

browse-operations.mddocs/

Browse Operations

PLC namespace exploration and tag discovery capabilities for Apache PLC4X Java API.

Capabilities

PlcBrowseRequest

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

PlcBrowseResponse

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);
}

PlcBrowseItem

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

PlcBrowseItemArrayInfo

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

PlcBrowseRequestInterceptor

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

Types

Query and Discovery Types

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);
}

Model Types

public interface ArrayInfo {
    int getSize();
    int getLowerBound();
    int getUpperBound();
}

Browse Query Patterns

Different PLC protocols support different browse query patterns:

Siemens S7

  • * - Browse all items at current level
  • DB* - Browse all data blocks
  • DB1.* - Browse all items in data block 1
  • DB1.DBD* - Browse all double words in data block 1
  • M* - Browse all memory areas
  • I* - Browse all inputs
  • Q* - Browse all outputs

Modbus

  • * - Browse all available registers
  • holding-register:* - Browse all holding registers
  • input-register:* - Browse all input registers
  • coil:* - Browse all coils
  • discrete-input:* - Browse all discrete inputs

OPC UA

  • * - Browse all nodes at current level
  • Objects.* - Browse all objects
  • Types.* - Browse all type definitions
  • Views.* - Browse all views

Browse 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

docs

browse-operations.md

connection-management.md

exception-handling.md

index.md

read-operations.md

subscription-system.md

value-system.md

write-operations.md

tile.json