plexus-python 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. plexus/__init__.py +31 -0
  2. plexus/__main__.py +4 -0
  3. plexus/adapters/__init__.py +122 -0
  4. plexus/adapters/base.py +409 -0
  5. plexus/adapters/ble.py +257 -0
  6. plexus/adapters/can.py +439 -0
  7. plexus/adapters/can_detect.py +174 -0
  8. plexus/adapters/mavlink.py +642 -0
  9. plexus/adapters/mavlink_detect.py +192 -0
  10. plexus/adapters/modbus.py +622 -0
  11. plexus/adapters/mqtt.py +350 -0
  12. plexus/adapters/opcua.py +607 -0
  13. plexus/adapters/registry.py +206 -0
  14. plexus/adapters/serial_adapter.py +547 -0
  15. plexus/buffer.py +257 -0
  16. plexus/cameras/__init__.py +57 -0
  17. plexus/cameras/auto.py +239 -0
  18. plexus/cameras/base.py +189 -0
  19. plexus/cameras/picamera.py +171 -0
  20. plexus/cameras/usb.py +143 -0
  21. plexus/cli.py +783 -0
  22. plexus/client.py +465 -0
  23. plexus/config.py +169 -0
  24. plexus/connector.py +666 -0
  25. plexus/deps.py +246 -0
  26. plexus/detect.py +1238 -0
  27. plexus/importers/__init__.py +25 -0
  28. plexus/importers/rosbag.py +778 -0
  29. plexus/sensors/__init__.py +118 -0
  30. plexus/sensors/ads1115.py +164 -0
  31. plexus/sensors/adxl345.py +179 -0
  32. plexus/sensors/auto.py +290 -0
  33. plexus/sensors/base.py +412 -0
  34. plexus/sensors/bh1750.py +102 -0
  35. plexus/sensors/bme280.py +241 -0
  36. plexus/sensors/gps.py +317 -0
  37. plexus/sensors/ina219.py +149 -0
  38. plexus/sensors/magnetometer.py +239 -0
  39. plexus/sensors/mpu6050.py +162 -0
  40. plexus/sensors/sht3x.py +139 -0
  41. plexus/sensors/spi_scan.py +164 -0
  42. plexus/sensors/system.py +261 -0
  43. plexus/sensors/vl53l0x.py +109 -0
  44. plexus/streaming.py +743 -0
  45. plexus/tui.py +642 -0
  46. plexus_python-0.1.0.dist-info/METADATA +470 -0
  47. plexus_python-0.1.0.dist-info/RECORD +50 -0
  48. plexus_python-0.1.0.dist-info/WHEEL +4 -0
  49. plexus_python-0.1.0.dist-info/entry_points.txt +2 -0
  50. plexus_python-0.1.0.dist-info/licenses/LICENSE +190 -0
@@ -0,0 +1,192 @@
1
+ """
2
+ MAVLink auto-detection.
3
+
4
+ Scans for MAVLink-capable connections: UDP ports, serial flight controllers,
5
+ and TCP endpoints.
6
+
7
+ Usage:
8
+ from plexus.adapters.mavlink_detect import scan_mavlink
9
+
10
+ connections = scan_mavlink()
11
+ for conn in connections:
12
+ print(f"{conn.connection_string} ({conn.transport})")
13
+ """
14
+
15
+ import glob
16
+ import logging
17
+ import os
18
+ import socket
19
+ from dataclasses import dataclass
20
+ from typing import List
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+ # Common MAVLink UDP ports
25
+ MAVLINK_UDP_PORTS = [14550, 14551]
26
+
27
+ # Common MAVLink TCP ports (SITL, companion computers)
28
+ MAVLINK_TCP_PORTS = [5760, 5762]
29
+
30
+ # Known flight controller USB vendor IDs
31
+ FC_USB_VIDS = {
32
+ "2dae": "Holybro (Pixhawk)",
33
+ "1209": "ArduPilot / Generic FC",
34
+ "26ac": "3DR (Pixhawk 1)",
35
+ "0483": "STMicro (many FCs)",
36
+ "1fc9": "NXP (FMUK66)",
37
+ "3162": "CubePilot",
38
+ }
39
+
40
+
41
+ @dataclass
42
+ class DetectedMAVLink:
43
+ """Information about a detected MAVLink connection."""
44
+ connection_string: str
45
+ transport: str # "udp", "tcp", "serial"
46
+ description: str
47
+ is_available: bool # True if the connection was verified
48
+
49
+
50
+ def _scan_udp() -> List[DetectedMAVLink]:
51
+ """Scan for MAVLink UDP listeners on standard ports."""
52
+ detected = []
53
+
54
+ for port in MAVLINK_UDP_PORTS:
55
+ sock = None
56
+ try:
57
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
58
+ sock.settimeout(0.1)
59
+ sock.bind(("0.0.0.0", port))
60
+ # Port is free — no one is sending here yet, but it's available
61
+ detected.append(DetectedMAVLink(
62
+ connection_string=f"udpin:0.0.0.0:{port}",
63
+ transport="udp",
64
+ description=f"UDP port {port} (available to listen)",
65
+ is_available=True,
66
+ ))
67
+ except OSError:
68
+ # Port in use — something may already be sending MAVLink here
69
+ detected.append(DetectedMAVLink(
70
+ connection_string=f"udpin:0.0.0.0:{port}",
71
+ transport="udp",
72
+ description=f"UDP port {port} (in use — possible MAVLink source)",
73
+ is_available=True,
74
+ ))
75
+ finally:
76
+ if sock:
77
+ sock.close()
78
+
79
+ return detected
80
+
81
+
82
+ def _scan_tcp() -> List[DetectedMAVLink]:
83
+ """Scan for MAVLink TCP endpoints on localhost."""
84
+ detected = []
85
+
86
+ for port in MAVLINK_TCP_PORTS:
87
+ sock = None
88
+ try:
89
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
90
+ sock.settimeout(0.5)
91
+ result = sock.connect_ex(("127.0.0.1", port))
92
+ if result == 0:
93
+ detected.append(DetectedMAVLink(
94
+ connection_string=f"tcp:127.0.0.1:{port}",
95
+ transport="tcp",
96
+ description=f"TCP port {port} (SITL or companion)",
97
+ is_available=True,
98
+ ))
99
+ except (OSError, socket.error):
100
+ pass
101
+ finally:
102
+ if sock:
103
+ sock.close()
104
+
105
+ return detected
106
+
107
+
108
+ def _scan_serial() -> List[DetectedMAVLink]:
109
+ """Scan serial ports for known flight controller USB VIDs."""
110
+ detected = []
111
+
112
+ # Check /sys/bus/usb for known FC vendor IDs (Linux)
113
+ usb_path = "/sys/bus/usb/devices"
114
+ fc_serial_paths = set()
115
+
116
+ if os.path.isdir(usb_path):
117
+ try:
118
+ for entry in os.listdir(usb_path):
119
+ vid_path = os.path.join(usb_path, entry, "idVendor")
120
+ try:
121
+ with open(vid_path, "r") as f:
122
+ vid = f.read().strip().lower()
123
+ except (OSError, IOError):
124
+ continue
125
+
126
+ if vid in FC_USB_VIDS:
127
+ # Look for tty sub-device
128
+ dev_dir = os.path.join(usb_path, entry)
129
+ for tty in glob.glob(os.path.join(dev_dir, "**", "tty", "tty*"), recursive=True):
130
+ tty_name = os.path.basename(tty)
131
+ dev_path = f"/dev/{tty_name}"
132
+ if os.path.exists(dev_path):
133
+ fc_serial_paths.add((dev_path, FC_USB_VIDS[vid]))
134
+ except OSError:
135
+ pass
136
+
137
+ for dev_path, fc_name in sorted(fc_serial_paths):
138
+ detected.append(DetectedMAVLink(
139
+ connection_string=dev_path,
140
+ transport="serial",
141
+ description=f"Flight controller: {fc_name}",
142
+ is_available=True,
143
+ ))
144
+
145
+ # Fallback: check common serial paths for FC-like devices
146
+ if not detected:
147
+ fc_patterns = ["/dev/ttyACM*", "/dev/ttyUSB*"]
148
+ for pattern in fc_patterns:
149
+ for device_path in sorted(glob.glob(pattern)):
150
+ detected.append(DetectedMAVLink(
151
+ connection_string=device_path,
152
+ transport="serial",
153
+ description="Serial port (possible flight controller)",
154
+ is_available=False, # Not verified
155
+ ))
156
+
157
+ return detected
158
+
159
+
160
+ def scan_mavlink() -> List[DetectedMAVLink]:
161
+ """
162
+ Scan for MAVLink connections on the system.
163
+
164
+ Checks:
165
+ 1. UDP ports 14550, 14551
166
+ 2. TCP ports 5760, 5762 on localhost
167
+ 3. Serial ports with known flight controller USB VIDs
168
+
169
+ Returns:
170
+ List of detected MAVLink connections
171
+ """
172
+ detected = []
173
+
174
+ # UDP ports
175
+ try:
176
+ detected.extend(_scan_udp())
177
+ except Exception as e:
178
+ logger.debug(f"Error scanning MAVLink UDP ports: {e}")
179
+
180
+ # TCP ports
181
+ try:
182
+ detected.extend(_scan_tcp())
183
+ except Exception as e:
184
+ logger.debug(f"Error scanning MAVLink TCP ports: {e}")
185
+
186
+ # Serial ports
187
+ try:
188
+ detected.extend(_scan_serial())
189
+ except Exception as e:
190
+ logger.debug(f"Error scanning MAVLink serial ports: {e}")
191
+
192
+ return detected