0
# Browse Operations
1
2
PLC namespace exploration and tag discovery capabilities for Apache PLC4X Java API.
3
4
## Capabilities
5
6
### PlcBrowseRequest
7
8
Interface for building and executing browse requests to explore PLC namespaces and discover available tags.
9
10
```java { .api }
11
/**
12
* Browse request interface for exploring PLC namespaces and discovering tags
13
*/
14
public interface PlcBrowseRequest extends PlcRequest {
15
/**
16
* Execute the browse request asynchronously
17
* @return CompletableFuture containing the browse response
18
*/
19
CompletableFuture<? extends PlcBrowseResponse> execute();
20
21
/**
22
* Execute the browse request with an interceptor for processing results
23
* @param interceptor PlcBrowseRequestInterceptor for custom result processing
24
* @return CompletableFuture containing the browse response
25
*/
26
CompletableFuture<? extends PlcBrowseResponse> executeWithInterceptor(PlcBrowseRequestInterceptor interceptor);
27
28
/**
29
* Get all query names in this browse request
30
* @return LinkedHashSet of query names
31
*/
32
LinkedHashSet<String> getQueryNames();
33
34
/**
35
* Get a specific query by name
36
* @param name Query name
37
* @return PlcQuery object
38
*/
39
PlcQuery getQuery(String name);
40
41
/**
42
* Builder interface for constructing browse requests
43
*/
44
interface Builder extends PlcRequestBuilder {
45
/**
46
* Add a browse query
47
* @param name Logical name for the query
48
* @param query Query string (protocol-specific format)
49
* @return Builder instance for method chaining
50
*/
51
Builder addQuery(String name, String query);
52
53
/**
54
* Build the browse request
55
* @return PlcBrowseRequest instance ready for execution
56
*/
57
PlcBrowseRequest build();
58
}
59
}
60
```
61
62
### PlcBrowseResponse
63
64
Interface for accessing browse response data and discovered items.
65
66
```java { .api }
67
/**
68
* Browse response interface providing access to discovered PLC items
69
*/
70
public interface PlcBrowseResponse extends PlcResponse {
71
/**
72
* Get the originating browse request
73
* @return PlcBrowseRequest that generated this response
74
*/
75
PlcBrowseRequest getRequest();
76
77
/**
78
* Get query names from the response
79
* @return Collection of query names
80
*/
81
Collection<String> getQueryNames();
82
83
/**
84
* Get response code for a specific query
85
* @param name Query name
86
* @return PlcResponseCode indicating success or failure
87
*/
88
PlcResponseCode getResponseCode(String name);
89
90
/**
91
* Get browse items for a specific query
92
* @param name Query name
93
* @return Collection of PlcBrowseItem discovered by the query
94
*/
95
Collection<PlcBrowseItem> getValues(String name);
96
}
97
```
98
99
### PlcBrowseItem
100
101
Interface representing a discovered PLC item with its properties and metadata.
102
103
```java { .api }
104
/**
105
* Interface representing a discovered PLC item
106
*/
107
public interface PlcBrowseItem {
108
/**
109
* Get the tag address for this item
110
* @return String representation of the tag address
111
*/
112
String getTag();
113
114
/**
115
* Get the display name for this item
116
* @return Human-readable name
117
*/
118
String getName();
119
120
/**
121
* Get the data type of this item
122
* @return PlcValueType indicating the data type
123
*/
124
PlcValueType getDataType();
125
126
/**
127
* Check if this item is readable
128
* @return true if item can be read
129
*/
130
boolean isReadable();
131
132
/**
133
* Check if this item is writable
134
* @return true if item can be written
135
*/
136
boolean isWritable();
137
138
/**
139
* Check if this item is subscribable
140
* @return true if item supports subscriptions
141
*/
142
boolean isSubscribable();
143
144
/**
145
* Get children items (for hierarchical structures)
146
* @return Map of child name to PlcBrowseItem
147
*/
148
Map<String, PlcBrowseItem> getChildren();
149
150
/**
151
* Get additional options/properties for this item
152
* @return Map of option names to values
153
*/
154
Map<String, PlcValue> getOptions();
155
156
/**
157
* Get array information if this item is an array
158
* @return List of ArrayInfo describing array dimensions
159
*/
160
List<ArrayInfo> getArrayInfo();
161
}
162
```
163
164
### PlcBrowseItemArrayInfo
165
166
Interface providing array dimension information for browse items.
167
168
```java { .api }
169
/**
170
* Array information for browse items
171
*/
172
public interface PlcBrowseItemArrayInfo {
173
/**
174
* Get the size of this array dimension
175
* @return Array size
176
*/
177
int getSize();
178
179
/**
180
* Get the lower bound index
181
* @return Lower bound (typically 0 or 1)
182
*/
183
int getLowerBound();
184
185
/**
186
* Get the upper bound index
187
* @return Upper bound
188
*/
189
int getUpperBound();
190
}
191
```
192
193
### PlcBrowseRequestInterceptor
194
195
Interface for intercepting and processing browse results.
196
197
```java { .api }
198
/**
199
* Interceptor interface for processing browse results during execution
200
*/
201
public interface PlcBrowseRequestInterceptor {
202
/**
203
* Intercept and process a browse item
204
* @param browseItem The discovered browse item
205
* @return Modified or filtered browse item, or null to exclude
206
*/
207
PlcBrowseItem intercept(PlcBrowseItem browseItem);
208
}
209
```
210
211
**Usage Examples:**
212
213
```java
214
import org.apache.plc4x.java.DefaultPlcDriverManager;
215
import org.apache.plc4x.java.api.PlcConnection;
216
import org.apache.plc4x.java.api.messages.*;
217
import org.apache.plc4x.java.api.model.PlcQuery;
218
import org.apache.plc4x.java.api.types.PlcResponseCode;
219
import org.apache.plc4x.java.api.types.PlcValueType;
220
221
// Basic browse operation
222
PlcDriverManager driverManager = new DefaultPlcDriverManager();
223
try (PlcConnection connection = driverManager.getConnection("s7://192.168.1.200/0/1")) {
224
connection.connect();
225
226
// Check if browsing is supported
227
if (connection.getMetadata().isBrowseSupported()) {
228
// Build browse request
229
PlcBrowseRequest browseRequest = connection.browseRequestBuilder()
230
.addQuery("root", "*") // Browse all items at root level
231
.build();
232
233
PlcBrowseResponse response = browseRequest.execute().get();
234
235
if (response.getResponseCode("root") == PlcResponseCode.OK) {
236
Collection<PlcBrowseItem> items = response.getValues("root");
237
238
System.out.println("Discovered " + items.size() + " items:");
239
for (PlcBrowseItem item : items) {
240
System.out.println("- " + item.getName() +
241
" (" + item.getTag() + ")" +
242
" Type: " + item.getDataType() +
243
" R:" + item.isReadable() +
244
" W:" + item.isWritable() +
245
" S:" + item.isSubscribable());
246
}
247
}
248
} else {
249
System.out.println("Browsing not supported by this connection");
250
}
251
}
252
253
// Hierarchical browsing
254
try (PlcConnection connection = driverManager.getConnection("s7://192.168.1.200/0/1")) {
255
connection.connect();
256
257
PlcBrowseRequest browseRequest = connection.browseRequestBuilder()
258
.addQuery("data_blocks", "DB*") // Browse all data blocks
259
.addQuery("memory_areas", "M*") // Browse memory areas
260
.addQuery("inputs", "I*") // Browse inputs
261
.addQuery("outputs", "Q*") // Browse outputs
262
.build();
263
264
PlcBrowseResponse response = browseRequest.execute().get();
265
266
// Process each query result
267
for (String queryName : response.getQueryNames()) {
268
System.out.println("\n=== " + queryName.toUpperCase() + " ===");
269
270
if (response.getResponseCode(queryName) == PlcResponseCode.OK) {
271
Collection<PlcBrowseItem> items = response.getValues(queryName);
272
273
for (PlcBrowseItem item : items) {
274
printBrowseItemDetails(item, 0);
275
276
// Explore children if available
277
if (!item.getChildren().isEmpty()) {
278
item.getChildren().forEach((childName, childItem) -> {
279
printBrowseItemDetails(childItem, 1);
280
});
281
}
282
}
283
} else {
284
System.out.println("Query failed: " + response.getResponseCode(queryName));
285
}
286
}
287
}
288
289
// Filtered browsing with interceptor
290
try (PlcConnection connection = driverManager.getConnection("modbus-tcp://192.168.1.100:502")) {
291
connection.connect();
292
293
// Create interceptor to filter only writable items
294
PlcBrowseRequestInterceptor writableFilter = browseItem -> {
295
if (browseItem.isWritable()) {
296
return browseItem; // Keep writable items
297
}
298
return null; // Filter out read-only items
299
};
300
301
PlcBrowseRequest browseRequest = connection.browseRequestBuilder()
302
.addQuery("writable_registers", "holding-register:*")
303
.build();
304
305
PlcBrowseResponse response = browseRequest.executeWithInterceptor(writableFilter).get();
306
307
if (response.getResponseCode("writable_registers") == PlcResponseCode.OK) {
308
Collection<PlcBrowseItem> writableItems = response.getValues("writable_registers");
309
310
System.out.println("Found " + writableItems.size() + " writable registers:");
311
for (PlcBrowseItem item : writableItems) {
312
System.out.println("- " + item.getTag() + " (" + item.getDataType() + ")");
313
}
314
}
315
}
316
317
// Array item discovery
318
try (PlcConnection connection = driverManager.getConnection("s7://192.168.1.200/0/1")) {
319
connection.connect();
320
321
PlcBrowseRequest browseRequest = connection.browseRequestBuilder()
322
.addQuery("arrays", "DB1.*[*]") // Browse array items in DB1
323
.build();
324
325
PlcBrowseResponse response = browseRequest.execute().get();
326
327
if (response.getResponseCode("arrays") == PlcResponseCode.OK) {
328
Collection<PlcBrowseItem> arrayItems = response.getValues("arrays");
329
330
for (PlcBrowseItem item : arrayItems) {
331
System.out.println("Array: " + item.getName());
332
System.out.println(" Tag: " + item.getTag());
333
System.out.println(" Type: " + item.getDataType());
334
335
// Display array dimension information
336
List<ArrayInfo> arrayInfo = item.getArrayInfo();
337
if (!arrayInfo.isEmpty()) {
338
System.out.println(" Dimensions:");
339
for (int i = 0; i < arrayInfo.size(); i++) {
340
ArrayInfo dimension = arrayInfo.get(i);
341
System.out.println(" [" + i + "]: " + dimension.getLowerBound() +
342
".." + dimension.getUpperBound() +
343
" (size: " + dimension.getSize() + ")");
344
}
345
}
346
}
347
}
348
}
349
350
// Browse with specific data type filtering
351
try (PlcConnection connection = driverManager.getConnection("s7://192.168.1.200/0/1")) {
352
connection.connect();
353
354
PlcBrowseRequest browseRequest = connection.browseRequestBuilder()
355
.addQuery("all_items", "*")
356
.build();
357
358
PlcBrowseResponse response = browseRequest.execute().get();
359
360
if (response.getResponseCode("all_items") == PlcResponseCode.OK) {
361
Collection<PlcBrowseItem> allItems = response.getValues("all_items");
362
363
// Group items by data type
364
Map<PlcValueType, List<PlcBrowseItem>> itemsByType = allItems.stream()
365
.collect(Collectors.groupingBy(PlcBrowseItem::getDataType));
366
367
itemsByType.forEach((dataType, items) -> {
368
System.out.println("\n" + dataType + " items (" + items.size() + "):");
369
items.forEach(item -> {
370
System.out.println(" - " + item.getName() + " (" + item.getTag() + ")");
371
});
372
});
373
}
374
}
375
376
// Metadata and options exploration
377
try (PlcConnection connection = driverManager.getConnection("s7://192.168.1.200/0/1")) {
378
connection.connect();
379
380
PlcBrowseRequest browseRequest = connection.browseRequestBuilder()
381
.addQuery("detailed", "DB10.*")
382
.build();
383
384
PlcBrowseResponse response = browseRequest.execute().get();
385
386
if (response.getResponseCode("detailed") == PlcResponseCode.OK) {
387
Collection<PlcBrowseItem> items = response.getValues("detailed");
388
389
for (PlcBrowseItem item : items) {
390
System.out.println("\nItem: " + item.getName());
391
System.out.println(" Tag: " + item.getTag());
392
System.out.println(" Type: " + item.getDataType());
393
System.out.println(" Readable: " + item.isReadable());
394
System.out.println(" Writable: " + item.isWritable());
395
System.out.println(" Subscribable: " + item.isSubscribable());
396
397
// Display additional options/metadata
398
Map<String, PlcValue> options = item.getOptions();
399
if (!options.isEmpty()) {
400
System.out.println(" Options:");
401
options.forEach((optionName, optionValue) -> {
402
System.out.println(" " + optionName + ": " + optionValue.getObject());
403
});
404
}
405
}
406
}
407
}
408
409
// Helper method for displaying browse item details
410
private static void printBrowseItemDetails(PlcBrowseItem item, int indentLevel) {
411
String indent = " ".repeat(indentLevel);
412
System.out.printf("%s- %s (%s) [%s] R:%s W:%s S:%s%n",
413
indent,
414
item.getName(),
415
item.getTag(),
416
item.getDataType(),
417
item.isReadable() ? "Y" : "N",
418
item.isWritable() ? "Y" : "N",
419
item.isSubscribable() ? "Y" : "N"
420
);
421
422
// Show array information if present
423
if (!item.getArrayInfo().isEmpty()) {
424
System.out.print(indent + " Array: ");
425
for (ArrayInfo arrayInfo : item.getArrayInfo()) {
426
System.out.printf("[%d..%d] ", arrayInfo.getLowerBound(), arrayInfo.getUpperBound());
427
}
428
System.out.println();
429
}
430
}
431
```
432
433
## Types
434
435
### Query and Discovery Types
436
437
```java { .api }
438
public interface PlcQuery {
439
/**
440
* Get the query string
441
* @return Query string in protocol-specific format
442
*/
443
String getQueryString();
444
}
445
446
public interface PlcDiscoveryRequest extends PlcRequest {
447
CompletableFuture<? extends PlcDiscoveryResponse> execute();
448
}
449
450
public interface PlcDiscoveryResponse extends PlcResponse {
451
PlcDiscoveryRequest getRequest();
452
Collection<PlcDiscoveryItem> getValues();
453
}
454
455
public interface PlcDiscoveryItem {
456
String getProtocolCode();
457
String getTransportCode();
458
String getTransportUrl();
459
String getOptions();
460
String getName();
461
}
462
463
public interface PlcDiscoveryItemHandler {
464
void handle(PlcDiscoveryItem discoveryItem);
465
}
466
```
467
468
### Model Types
469
470
```java { .api }
471
public interface ArrayInfo {
472
int getSize();
473
int getLowerBound();
474
int getUpperBound();
475
}
476
```
477
478
## Browse Query Patterns
479
480
Different PLC protocols support different browse query patterns:
481
482
### Siemens S7
483
- `*` - Browse all items at current level
484
- `DB*` - Browse all data blocks
485
- `DB1.*` - Browse all items in data block 1
486
- `DB1.DBD*` - Browse all double words in data block 1
487
- `M*` - Browse all memory areas
488
- `I*` - Browse all inputs
489
- `Q*` - Browse all outputs
490
491
### Modbus
492
- `*` - Browse all available registers
493
- `holding-register:*` - Browse all holding registers
494
- `input-register:*` - Browse all input registers
495
- `coil:*` - Browse all coils
496
- `discrete-input:*` - Browse all discrete inputs
497
498
### OPC UA
499
- `*` - Browse all nodes at current level
500
- `Objects.*` - Browse all objects
501
- `Types.*` - Browse all type definitions
502
- `Views.*` - Browse all views
503
504
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.