or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-messaging.mdcore-operations.mddevice-discovery.mdindex.mdtag-discovery.mdtime-operations.md

device-discovery.mddocs/

0

# Device Discovery and Information

1

2

Network device discovery and device property retrieval capabilities for system diagnostics, configuration, and network management. These functions enable automatic detection of Ethernet I/P devices and detailed device information gathering.

3

4

## Capabilities

5

6

### Network Device Discovery

7

8

Discover all Ethernet I/P devices on the local network using broadcast discovery packets.

9

10

```python { .api }

11

def Discover():

12

"""

13

Discover Ethernet I/P devices on the network.

14

15

Note: Not available on MicroPython due to socket limitations.

16

17

Returns:

18

Response: Object containing list of discovered devices

19

- Value: List of Device objects with device information

20

- Status: "Success" or error description

21

"""

22

```

23

24

**Usage Examples:**

25

26

```python

27

from pylogix import PLC

28

29

with PLC() as comm:

30

# Discover all devices on the network

31

response = comm.Discover()

32

if response.Status == 'Success':

33

devices = response.Value

34

print(f"Discovered {len(devices)} devices:")

35

36

for device in devices:

37

print(f"\nDevice: {device.IPAddress}")

38

print(f" Product: {device.ProductName}")

39

print(f" Vendor: {device.Vendor}")

40

print(f" Device Type: {device.DeviceType}")

41

print(f" Product Code: {device.ProductCode}")

42

print(f" Revision: {device.Revision}")

43

print(f" Serial Number: {device.SerialNumber}")

44

print(f" Status: 0x{device.Status:04x}")

45

else:

46

print(f"Discovery failed: {response.Status}")

47

```

48

49

### Device Property Retrieval

50

51

Get detailed properties of the currently connected device.

52

53

```python { .api }

54

def GetDeviceProperties():

55

"""

56

Get properties of the device at the specified IP address.

57

58

Returns:

59

Response: Object containing device information

60

- Value: Device object with detailed properties

61

- Status: "Success" or error description

62

"""

63

```

64

65

**Usage Examples:**

66

67

```python

68

with PLC() as comm:

69

comm.IPAddress = '192.168.1.100'

70

71

# Get properties of the connected device

72

response = comm.GetDeviceProperties()

73

if response.Status == 'Success':

74

device = response.Value

75

76

print(f"Device Properties for {device.IPAddress}:")

77

print(f" Product Name: {device.ProductName}")

78

print(f" Vendor: {device.Vendor} (ID: {device.VendorID})")

79

print(f" Device Type: {device.DeviceType} (ID: {device.DeviceID})")

80

print(f" Product Code: {device.ProductCode}")

81

print(f" Revision: {device.Revision}")

82

print(f" Serial Number: {device.SerialNumber}")

83

print(f" Encapsulation Version: {device.EncapsulationVersion}")

84

print(f" Status: 0x{device.Status:04x}")

85

print(f" State: {device.State}")

86

else:

87

print(f"Failed to get device properties: {response.Status}")

88

```

89

90

### Module Property Retrieval

91

92

Get properties of modules installed in specific slots of a chassis-based system.

93

94

```python { .api }

95

def GetModuleProperties(slot):

96

"""

97

Get properties of a module in a specific slot.

98

99

Args:

100

slot (int): Slot number to query (0-based)

101

102

Returns:

103

Response: Object containing module information

104

- Value: Device object representing the module

105

- Status: "Success" or error description

106

"""

107

```

108

109

**Usage Examples:**

110

111

```python

112

with PLC() as comm:

113

comm.IPAddress = '192.168.1.100'

114

115

# Query modules in different slots

116

for slot in range(8): # Check slots 0-7

117

response = comm.GetModuleProperties(slot)

118

if response.Status == 'Success':

119

module = response.Value

120

if module.ProductName: # Module present

121

print(f"Slot {slot}: {module.ProductName}")

122

print(f" Type: {module.DeviceType}")

123

print(f" Vendor: {module.Vendor}")

124

print(f" Product Code: {module.ProductCode}")

125

print(f" Revision: {module.Revision}")

126

print(f" Serial: {module.SerialNumber}")

127

print()

128

else:

129

print(f"Slot {slot}: Empty or error - {response.Status}")

130

```

131

132

### Comprehensive Network Scan

133

134

Perform a complete network scan to map all available devices and their capabilities.

135

136

```python

137

def network_scan_and_analysis():

138

"""Perform comprehensive network device analysis."""

139

140

with PLC() as comm:

141

# Discover all devices

142

discovery_response = comm.Discover()

143

if discovery_response.Status != 'Success':

144

print(f"Network discovery failed: {discovery_response.Status}")

145

return None

146

147

devices = discovery_response.Value

148

print(f"Network Scan Results: {len(devices)} devices found\n")

149

150

device_analysis = []

151

152

for i, device in enumerate(devices, 1):

153

print(f"Device {i}: {device.IPAddress}")

154

print(f" Product: {device.ProductName}")

155

print(f" Vendor: {device.Vendor}")

156

print(f" Type: {device.DeviceType}")

157

print(f" Revision: {device.Revision}")

158

159

# Try to connect and get detailed properties

160

comm.IPAddress = device.IPAddress

161

detailed_response = comm.GetDeviceProperties()

162

163

device_info = {

164

'ip_address': device.IPAddress,

165

'product_name': device.ProductName,

166

'vendor': device.Vendor,

167

'device_type': device.DeviceType,

168

'product_code': device.ProductCode,

169

'revision': device.Revision,

170

'serial_number': device.SerialNumber,

171

'can_connect': detailed_response.Status == 'Success',

172

'is_plc': False,

173

'slot_count': 0,

174

'modules': []

175

}

176

177

# Check if this is a PLC by looking for common PLC device types

178

plc_types = ['Programmable Logic Controller', 'ControlLogix', 'CompactLogix']

179

if any(plc_type.lower() in device.DeviceType.lower() for plc_type in plc_types):

180

device_info['is_plc'] = True

181

print(" -> Identified as PLC")

182

183

# If it's a PLC, check for modules in slots

184

if detailed_response.Status == 'Success':

185

modules_found = 0

186

for slot in range(16): # Check up to 16 slots

187

module_response = comm.GetModuleProperties(slot)

188

if module_response.Status == 'Success':

189

module = module_response.Value

190

if module.ProductName:

191

modules_found += 1

192

device_info['modules'].append({

193

'slot': slot,

194

'product_name': module.ProductName,

195

'device_type': module.DeviceType,

196

'product_code': module.ProductCode

197

})

198

print(f" Slot {slot}: {module.ProductName}")

199

200

device_info['slot_count'] = modules_found

201

if modules_found > 0:

202

print(f" -> Found {modules_found} modules")

203

204

device_analysis.append(device_info)

205

print()

206

207

# Summary analysis

208

print("=== Network Analysis Summary ===")

209

plc_count = sum(1 for d in device_analysis if d['is_plc'])

210

connectable_count = sum(1 for d in device_analysis if d['can_connect'])

211

212

print(f"Total devices: {len(device_analysis)}")

213

print(f"PLCs identified: {plc_count}")

214

print(f"Connectable devices: {connectable_count}")

215

216

if plc_count > 0:

217

print("\nPLC Details:")

218

for device in device_analysis:

219

if device['is_plc']:

220

print(f" {device['ip_address']}: {device['product_name']}")

221

if device['modules']:

222

print(f" Modules: {len(device['modules'])}")

223

224

return device_analysis

225

226

# Usage

227

network_devices = network_scan_and_analysis()

228

```

229

230

### Device Information Analysis

231

232

Analyze device information to understand network topology and capabilities.

233

234

```python

235

def analyze_device_capabilities(device):

236

"""Analyze device capabilities based on properties."""

237

238

capabilities = {

239

'is_plc': False,

240

'is_io_module': False,

241

'is_drive': False,

242

'is_hmi': False,

243

'supports_messaging': False,

244

'ethernet_capable': True, # All discovered devices are Ethernet I/P

245

'vendor_category': 'Unknown'

246

}

247

248

# Device type analysis

249

device_type_lower = device.DeviceType.lower()

250

251

if 'programmable logic controller' in device_type_lower or 'plc' in device_type_lower:

252

capabilities['is_plc'] = True

253

capabilities['supports_messaging'] = True

254

elif 'discrete i/o' in device_type_lower or 'analog i/o' in device_type_lower:

255

capabilities['is_io_module'] = True

256

elif 'drive' in device_type_lower or 'motor' in device_type_lower:

257

capabilities['is_drive'] = True

258

elif 'human-machine interface' in device_type_lower or 'hmi' in device_type_lower:

259

capabilities['is_hmi'] = True

260

capabilities['supports_messaging'] = True

261

262

# Vendor analysis

263

vendor_lower = device.Vendor.lower()

264

if 'rockwell' in vendor_lower or 'allen-bradley' in vendor_lower:

265

capabilities['vendor_category'] = 'Rockwell Automation'

266

elif 'schneider' in vendor_lower:

267

capabilities['vendor_category'] = 'Schneider Electric'

268

elif 'siemens' in vendor_lower:

269

capabilities['vendor_category'] = 'Siemens'

270

271

return capabilities

272

273

def detailed_device_report(plc_ip):

274

"""Generate detailed device report."""

275

276

with PLC() as comm:

277

comm.IPAddress = plc_ip

278

279

# Get device properties

280

response = comm.GetDeviceProperties()

281

if response.Status != 'Success':

282

print(f"Cannot connect to device at {plc_ip}: {response.Status}")

283

return None

284

285

device = response.Value

286

capabilities = analyze_device_capabilities(device)

287

288

print(f"=== Device Report for {plc_ip} ===")

289

print(f"Product Name: {device.ProductName}")

290

print(f"Vendor: {device.Vendor} ({capabilities['vendor_category']})")

291

print(f"Device Type: {device.DeviceType}")

292

print(f"Product Code: {device.ProductCode}")

293

print(f"Firmware Revision: {device.Revision}")

294

print(f"Serial Number: {device.SerialNumber}")

295

print(f"Device Status: 0x{device.Status:04x}")

296

print(f"Device State: {device.State}")

297

print(f"Encapsulation Version: {device.EncapsulationVersion}")

298

299

print(f"\nCapabilities:")

300

print(f" PLC: {'Yes' if capabilities['is_plc'] else 'No'}")

301

print(f" I/O Module: {'Yes' if capabilities['is_io_module'] else 'No'}")

302

print(f" Drive: {'Yes' if capabilities['is_drive'] else 'No'}")

303

print(f" HMI: {'Yes' if capabilities['is_hmi'] else 'No'}")

304

print(f" Messaging Support: {'Yes' if capabilities['supports_messaging'] else 'No'}")

305

306

# If it's a PLC, get additional information

307

if capabilities['is_plc']:

308

print(f"\nPLC-Specific Information:")

309

310

# Try to get tag count

311

try:

312

tag_response = comm.GetTagList(allTags=False) # Controller tags only

313

if tag_response.Status == 'Success':

314

controller_tags = len(tag_response.Value)

315

print(f" Controller Tags: {controller_tags}")

316

317

# Try to get programs

318

program_response = comm.GetProgramsList()

319

if program_response.Status == 'Success':

320

programs = program_response.Value

321

print(f" Programs: {len(programs)}")

322

if programs:

323

for program in programs[:5]: # Show first 5

324

print(f" - {program}")

325

if len(programs) > 5:

326

print(f" ... and {len(programs) - 5} more")

327

328

except Exception as e:

329

print(f" Could not retrieve PLC details: {e}")

330

331

# Check for modules

332

print(f"\n Module Scan:")

333

modules_found = 0

334

for slot in range(8):

335

module_response = comm.GetModuleProperties(slot)

336

if module_response.Status == 'Success':

337

module = module_response.Value

338

if module.ProductName:

339

modules_found += 1

340

print(f" Slot {slot}: {module.ProductName} (Rev {module.Revision})")

341

342

if modules_found == 0:

343

print(f" No modules found or standalone PLC")

344

345

return {

346

'device': device,

347

'capabilities': capabilities,

348

'ip_address': plc_ip

349

}

350

351

# Usage

352

device_report = detailed_device_report('192.168.1.100')

353

```

354

355

### Error Handling for Discovery Operations

356

357

Discovery operations may fail due to network issues, device compatibility, or access restrictions.

358

359

```python

360

def robust_device_discovery(target_ip=None, timeout=10):

361

"""Robust device discovery with comprehensive error handling."""

362

363

results = {

364

'network_devices': [],

365

'target_device': None,

366

'errors': [],

367

'warnings': []

368

}

369

370

with PLC() as comm:

371

comm.SocketTimeout = timeout

372

373

# Network discovery

374

try:

375

print("Performing network discovery...")

376

discovery_response = comm.Discover()

377

378

if discovery_response.Status == 'Success':

379

devices = discovery_response.Value

380

results['network_devices'] = devices

381

print(f"Network discovery successful: {len(devices)} devices found")

382

else:

383

error_msg = f"Network discovery failed: {discovery_response.Status}"

384

results['errors'].append(error_msg)

385

print(error_msg)

386

387

# Check for common issues

388

if 'micropython' in discovery_response.Status.lower():

389

results['warnings'].append("Network discovery not available on MicroPython")

390

elif 'socket' in discovery_response.Status.lower():

391

results['warnings'].append("Network discovery requires socket permissions")

392

393

except Exception as e:

394

error_msg = f"Network discovery exception: {e}"

395

results['errors'].append(error_msg)

396

print(error_msg)

397

398

# Target device query

399

if target_ip:

400

try:

401

print(f"Querying target device {target_ip}...")

402

comm.IPAddress = target_ip

403

404

device_response = comm.GetDeviceProperties()

405

if device_response.Status == 'Success':

406

results['target_device'] = device_response.Value

407

print(f"Target device query successful")

408

else:

409

error_msg = f"Target device query failed: {device_response.Status}"

410

results['errors'].append(error_msg)

411

print(error_msg)

412

413

except Exception as e:

414

error_msg = f"Target device query exception: {e}"

415

results['errors'].append(error_msg)

416

print(error_msg)

417

418

# Summary

419

print(f"\nDiscovery Summary:")

420

print(f" Network devices found: {len(results['network_devices'])}")

421

print(f" Target device: {'Found' if results['target_device'] else 'Not found'}")

422

print(f" Errors: {len(results['errors'])}")

423

print(f" Warnings: {len(results['warnings'])}")

424

425

if results['errors']:

426

print("Errors encountered:")

427

for error in results['errors']:

428

print(f" - {error}")

429

430

if results['warnings']:

431

print("Warnings:")

432

for warning in results['warnings']:

433

print(f" - {warning}")

434

435

return results

436

437

# Usage

438

discovery_results = robust_device_discovery(target_ip='192.168.1.100')

439

```

440

441

### Device Status Interpretation

442

443

Understand device status codes and states for diagnostics.

444

445

```python

446

def interpret_device_status(device):

447

"""Interpret device status codes and provide diagnostic information."""

448

449

status_info = {

450

'status_code': device.Status,

451

'state_code': device.State,

452

'interpretation': {},

453

'recommendations': []

454

}

455

456

# Common status bit interpretations (device-dependent)

457

status = device.Status

458

459

if status == 0x0000:

460

status_info['interpretation']['status'] = "Normal operation"

461

elif status & 0x0001:

462

status_info['interpretation']['status'] = "Minor fault present"

463

status_info['recommendations'].append("Check device diagnostics")

464

elif status & 0x0002:

465

status_info['interpretation']['status'] = "Major fault present"

466

status_info['recommendations'].append("Device requires attention")

467

elif status & 0x0004:

468

status_info['interpretation']['status'] = "Configuration error"

469

status_info['recommendations'].append("Check device configuration")

470

471

# Device state interpretation

472

state = device.State

473

state_meanings = {

474

0: "Non-existent",

475

1: "Device operational",

476

2: "Standby",

477

3: "Major fault",

478

4: "Configuration/setup mode",

479

5: "Not configured"

480

}

481

482

status_info['interpretation']['state'] = state_meanings.get(state, f"Unknown state ({state})")

483

484

return status_info

485

486

# Usage in device reporting

487

def enhanced_device_report(plc_ip):

488

"""Enhanced device report with status interpretation."""

489

490

with PLC() as comm:

491

comm.IPAddress = plc_ip

492

response = comm.GetDeviceProperties()

493

494

if response.Status == 'Success':

495

device = response.Value

496

status_info = interpret_device_status(device)

497

498

print(f"Device Status Analysis for {plc_ip}:")

499

print(f" Status Code: 0x{device.Status:04x}")

500

print(f" Status Meaning: {status_info['interpretation'].get('status', 'Unknown')}")

501

print(f" Device State: {device.State}")

502

print(f" State Meaning: {status_info['interpretation'].get('state', 'Unknown')}")

503

504

if status_info['recommendations']:

505

print(f" Recommendations:")

506

for rec in status_info['recommendations']:

507

print(f" - {rec}")

508

else:

509

print(f"Cannot get device status for {plc_ip}: {response.Status}")

510

511

# Usage

512

enhanced_device_report('192.168.1.100')

513

```