petal-user-journey-coordinator 0.1.5__py3-none-any.whl → 0.1.7__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.
@@ -810,22 +810,10 @@ class ESCCalibrationController(BaseTimeoutController):
810
810
  self.logger.error(f"[{message_id}] Unknown throttle type: {throttle_type}")
811
811
  return False
812
812
 
813
- # param5 = 110x where x is motor index
814
- param5 = float(1100 + motor_idx)
815
-
816
- # Build the command message
817
- command_msg = self.mavlink_proxy.master.mav.command_long_encode(
818
- 1, # TODO: investigate best practice
819
- 1, # TODO: investigate best practice
820
- mavutil.mavlink.MAV_CMD_ACTUATOR_TEST,
821
- 0, # confirmation
822
- param1, # Motor value (0-1 or NaN)
823
- timeout, # Timeout in seconds
824
- 0, # Reserved
825
- 0, # Reserved
826
- param5, # Motor mapping (110x)
827
- 0, # Reserved
828
- 0 # Reserved
813
+ command_msg = self.mavlink_proxy.build_motor_value_command(
814
+ motor_idx=motor_idx,
815
+ motor_value=param1,
816
+ timeout=timeout
829
817
  )
830
818
 
831
819
  # Send the command
@@ -833,7 +821,7 @@ class ESCCalibrationController(BaseTimeoutController):
833
821
 
834
822
  action = "maximum throttle" if throttle_type == "maximum" else \
835
823
  "minimum throttle" if throttle_type == "minimum" else "stopped"
836
- self.logger.info(f"[{message_id}] Motor {motor_idx}: {action} (param1={param1}, param5={param5})")
824
+ self.logger.info(f"[{message_id}] Motor {motor_idx}: {action} (motor value={param1}, timeout={timeout}s)")
837
825
 
838
826
  # Small delay between motor commands
839
827
  await asyncio.sleep(0.1)
@@ -1019,22 +1007,11 @@ class ESCForceRunAllController(BaseTimeoutController):
1019
1007
  """Send MAV_CMD_ACTUATOR_TEST command to all motors."""
1020
1008
  try:
1021
1009
  for motor_idx in range(1, self.motor_count + 1):
1022
- # param5 = 110x where x is motor index
1023
- param5 = float(1100 + motor_idx)
1024
-
1025
- # Build the command message
1026
- command_msg = self.mavlink_proxy.master.mav.command_long_encode(
1027
- 1, # TODO: investigate best practice
1028
- 1, # TODO: investigate best practice
1029
- mavutil.mavlink.MAV_CMD_ACTUATOR_TEST,
1030
- 0, # confirmation
1031
- motor_value, # Motor value (0-1 or NaN)
1032
- timeout, # Timeout in seconds
1033
- 0, # Reserved
1034
- 0, # Reserved
1035
- param5, # Motor mapping (110x)
1036
- 0, # Reserved
1037
- 0 # Reserved
1010
+
1011
+ command_msg = self.mavlink_proxy.build_motor_value_command(
1012
+ motor_idx=motor_idx,
1013
+ motor_value=motor_value,
1014
+ timeout=timeout
1038
1015
  )
1039
1016
 
1040
1017
  # Send the command
@@ -1056,21 +1033,11 @@ class ESCForceRunAllController(BaseTimeoutController):
1056
1033
  nan_value = float('nan')
1057
1034
 
1058
1035
  for motor_idx in range(1, self.motor_count + 1):
1059
- param5 = float(1100 + motor_idx)
1060
-
1061
- # Build the command message
1062
- command_msg = self.mavlink_proxy.master.mav.command_long_encode(
1063
- 1, # TODO: investigate best practice
1064
- 1, # TODO: investigate best practice
1065
- mavutil.mavlink.MAV_CMD_ACTUATOR_TEST,
1066
- 0, # confirmation
1067
- nan_value, # Motor value (0-1 or NaN)
1068
- 0.0, # Timeout in seconds
1069
- 0, # Reserved
1070
- 0, # Reserved
1071
- param5, # Motor mapping (110x)
1072
- 0, # Reserved
1073
- 0 # Reserved
1036
+
1037
+ command_msg = self.mavlink_proxy.build_motor_value_command(
1038
+ motor_idx=motor_idx,
1039
+ motor_value=nan_value,
1040
+ timeout=0.0
1074
1041
  )
1075
1042
 
1076
1043
  # Send the command
@@ -1250,22 +1217,11 @@ class ESCForceRunSingleController(BaseTimeoutController):
1250
1217
  async def _send_actuator_test_single_motor(self, motor_value: float, timeout: float) -> bool:
1251
1218
  """Send MAV_CMD_ACTUATOR_TEST command to single motor."""
1252
1219
  try:
1253
- # param5 = 110x where x is motor index
1254
- param5 = float(1100 + self.target_motor)
1255
-
1256
- # Build the command message
1257
- command_msg = self.mavlink_proxy.master.mav.command_long_encode(
1258
- 1, # TODO: investigate best practice
1259
- 1, # TODO: investigate best practice
1260
- mavutil.mavlink.MAV_CMD_ACTUATOR_TEST,
1261
- 0, # confirmation
1262
- motor_value, # Motor value (0-1 or NaN)
1263
- timeout, # Timeout in seconds
1264
- 0, # Reserved
1265
- 0, # Reserved
1266
- param5, # Motor mapping (110x)
1267
- 0, # Reserved
1268
- 0 # Reserved
1220
+
1221
+ command_msg = self.mavlink_proxy.build_motor_value_command(
1222
+ motor_idx=self.target_motor,
1223
+ motor_value=motor_value,
1224
+ timeout=timeout
1269
1225
  )
1270
1226
 
1271
1227
  # Send the command
@@ -1284,23 +1240,13 @@ class ESCForceRunSingleController(BaseTimeoutController):
1284
1240
  try:
1285
1241
  # Use NaN to stop motor
1286
1242
  nan_value = float('nan')
1287
- param5 = float(1100 + self.target_motor)
1288
1243
 
1289
- # Build the command message
1290
- command_msg = self.mavlink_proxy.master.mav.command_long_encode(
1291
- 1, # TODO: investigate best practice
1292
- 1, # TODO: investigate best practice
1293
- mavutil.mavlink.MAV_CMD_ACTUATOR_TEST,
1294
- 0, # confirmation
1295
- nan_value, # Motor value (0-1 or NaN)
1296
- 0.0, # Timeout in seconds
1297
- 0, # Reserved
1298
- 0, # Reserved
1299
- param5, # Motor mapping (110x)
1300
- 0, # Reserved
1301
- 0 # Reserved
1244
+ command_msg = self.mavlink_proxy.build_motor_value_command(
1245
+ motor_idx=self.target_motor,
1246
+ motor_value=nan_value,
1247
+ timeout=0.0
1302
1248
  )
1303
-
1249
+
1304
1250
  # Send the command
1305
1251
  self.mavlink_proxy.send("mav", command_msg)
1306
1252
 
@@ -1447,7 +1393,15 @@ class BasePubSubController(ABC):
1447
1393
  3. Stop publishing when receiving petal-user-journey-coordinator/unsubscribe_<stream_name>
1448
1394
  """
1449
1395
 
1450
- def __init__(self, stream_name: str, mqtt_proxy: MQTTProxy, mavlink_proxy: MavLinkExternalProxy, logger: logging.Logger):
1396
+ def __init__(
1397
+ self,
1398
+ stream_name: str,
1399
+ mqtt_proxy: MQTTProxy,
1400
+ mavlink_proxy: MavLinkExternalProxy,
1401
+ logger: logging.Logger,
1402
+ petal_name: str = "petal-user-journey-coordinator"
1403
+ ):
1404
+ self.petal_name = petal_name
1451
1405
  self.stream_name = stream_name
1452
1406
  self.mqtt_proxy = mqtt_proxy
1453
1407
  self.mavlink_proxy = mavlink_proxy
@@ -1544,7 +1498,7 @@ class BasePubSubController(ABC):
1544
1498
 
1545
1499
  mqtt_message = {
1546
1500
  "messageId": str(uuid.uuid4()),
1547
- "command": f"/petal-user-journey-coordinator/publish_{self.stream_name}",
1501
+ "command": f"/{self.petal_name}/publish_{self.stream_name}",
1548
1502
  "timestamp": datetime.now().isoformat(),
1549
1503
  "payload": publish_payload.model_dump()
1550
1504
  }
@@ -1568,8 +1522,14 @@ class BasePubSubController(ABC):
1568
1522
  class RCChannelsController(BasePubSubController):
1569
1523
  """Controller for streaming RC_CHANNELS MAVLink data."""
1570
1524
 
1571
- def __init__(self, mqtt_proxy: MQTTProxy, mavlink_proxy: MavLinkExternalProxy, logger: logging.Logger):
1572
- super().__init__("rc_value_stream", mqtt_proxy, mavlink_proxy, logger)
1525
+ def __init__(
1526
+ self,
1527
+ mqtt_proxy: MQTTProxy,
1528
+ mavlink_proxy: MavLinkExternalProxy,
1529
+ logger: logging.Logger,
1530
+ petal_name: str = "petal-user-journey-coordinator"
1531
+ ):
1532
+ super().__init__("rc_value_stream", mqtt_proxy, mavlink_proxy, logger, petal_name=petal_name)
1573
1533
  self._latest_sample = None
1574
1534
  self._sample_lock = threading.Lock()
1575
1535
 
@@ -1628,9 +1588,10 @@ class PositionChannelsController(BasePubSubController):
1628
1588
  corner_exclusion_radius: float,
1629
1589
  max_matching_distance: float,
1630
1590
  corner_points: List[Dict[str, float]],
1631
- reference_trajectory: List[Dict[str, float]]
1591
+ reference_trajectory: List[Dict[str, float]],
1592
+ petal_name: str = "petal-user-journey-coordinator"
1632
1593
  ):
1633
- super().__init__("real_time_pose", mqtt_proxy, mavlink_proxy, logger)
1594
+ super().__init__("real_time_pose", mqtt_proxy, mavlink_proxy, logger, petal_name=petal_name)
1634
1595
  self._latest_position = None
1635
1596
  self._latest_attitude = None
1636
1597
  self._sample_lock = threading.Lock()
@@ -1916,8 +1877,14 @@ def _classify_kill_transition(text: str) -> Optional[bool]:
1916
1877
  class KillSwitchController(BasePubSubController):
1917
1878
  """Controller for streaming kill switch status from PX4 STATUSTEXT messages with automatic parameter configuration."""
1918
1879
 
1919
- def __init__(self, mqtt_proxy: MQTTProxy, mavlink_proxy: MavLinkExternalProxy, logger: logging.Logger):
1920
- super().__init__("ks_status_stream", mqtt_proxy, mavlink_proxy, logger)
1880
+ def __init__(
1881
+ self,
1882
+ mqtt_proxy: MQTTProxy,
1883
+ mavlink_proxy: MavLinkExternalProxy,
1884
+ logger: logging.Logger,
1885
+ petal_name: str = "petal-user-journey-coordinator"
1886
+ ):
1887
+ super().__init__("ks_status_stream", mqtt_proxy, mavlink_proxy, logger, petal_name=petal_name)
1921
1888
 
1922
1889
  # Kill switch state tracking
1923
1890
  self.is_killed: Optional[bool] = None # None = unknown
@@ -2007,8 +1974,14 @@ class KillSwitchController(BasePubSubController):
2007
1974
  class MultiFunctionalSwitchAController(BasePubSubController):
2008
1975
  """Controller for streaming Multi-functional Switch A data from MAVLink messages."""
2009
1976
 
2010
- def __init__(self, mqtt_proxy: MQTTProxy, mavlink_proxy: MavLinkExternalProxy, logger: logging.Logger):
2011
- super().__init__("mfs_a_status_stream", mqtt_proxy, mavlink_proxy, logger)
1977
+ def __init__(
1978
+ self,
1979
+ mqtt_proxy: MQTTProxy,
1980
+ mavlink_proxy: MavLinkExternalProxy,
1981
+ logger: logging.Logger,
1982
+ petal_name: str = "petal-user-journey-coordinator"
1983
+ ):
1984
+ super().__init__("mfs_a_status_stream", mqtt_proxy, mavlink_proxy, logger, petal_name=petal_name)
2012
1985
 
2013
1986
  # Multi-functional Switch A state tracking
2014
1987
  self.latest_mfs_a_data: Optional[Dict[str, Any]] = None
@@ -2061,8 +2034,14 @@ class MultiFunctionalSwitchAController(BasePubSubController):
2061
2034
  class MultiFunctionalSwitchBController(BasePubSubController):
2062
2035
  """Controller for streaming Multi-functional Switch B data from MAVLink messages."""
2063
2036
 
2064
- def __init__(self, mqtt_proxy: MQTTProxy, mavlink_proxy: MavLinkExternalProxy, logger: logging.Logger):
2065
- super().__init__("mfs_b_status_stream", mqtt_proxy, mavlink_proxy, logger)
2037
+ def __init__(
2038
+ self,
2039
+ mqtt_proxy: MQTTProxy,
2040
+ mavlink_proxy: MavLinkExternalProxy,
2041
+ logger: logging.Logger,
2042
+ petal_name: str = "petal-user-journey-coordinator"
2043
+ ):
2044
+ super().__init__("mfs_b_status_stream", mqtt_proxy, mavlink_proxy, logger, petal_name=petal_name)
2066
2045
 
2067
2046
  # Multi-functional Switch B state tracking
2068
2047
  self.latest_mfs_b_data: Optional[Dict[str, Any]] = None
@@ -2124,8 +2103,10 @@ class TrajectoryVerificationController:
2124
2103
  rectangle_a: float = 2.0,
2125
2104
  rectangle_b: float = 2.0,
2126
2105
  points_per_edge: int = 10,
2127
- corner_exclusion_radius: float = 0.2
2106
+ corner_exclusion_radius: float = 0.2,
2107
+ petal_name: str = "petal-user-journey-coordinator"
2128
2108
  ):
2109
+ self.petal_name = petal_name
2129
2110
  self.mqtt_proxy = mqtt_proxy
2130
2111
  self.logger = logger
2131
2112
  self.is_active = False
@@ -2730,7 +2711,7 @@ class TrajectoryVerificationController:
2730
2711
 
2731
2712
  mqtt_message = {
2732
2713
  "messageId": str(uuid.uuid4()),
2733
- "command": "/petal-user-journey-coordinator/verify_pos_yaw_directions_results",
2714
+ "command": f"/{self.petal_name}/verify_pos_yaw_directions_results",
2734
2715
  "timestamp": datetime.now().isoformat(),
2735
2716
  "payload": results_payload
2736
2717
  }
@@ -2758,7 +2739,13 @@ class WifiOptitrackConnectivityController:
2758
2739
  Controller for connecting to WiFi and verifying OptiTrack server connectivity.
2759
2740
  """
2760
2741
 
2761
- def __init__(self, mqtt_proxy: "MQTTProxy", logger: logging.Logger):
2742
+ def __init__(
2743
+ self,
2744
+ mqtt_proxy: "MQTTProxy",
2745
+ logger: logging.Logger,
2746
+ petal_name: str = "petal-user-journey-coordinator"
2747
+ ):
2748
+ self.petal_name = petal_name
2762
2749
  self.mqtt_proxy = mqtt_proxy
2763
2750
  self.logger = logger
2764
2751
  self.is_active = False
@@ -3115,7 +3102,7 @@ class WifiOptitrackConnectivityController:
3115
3102
  mqtt_message = {
3116
3103
  "messageId": message_id,
3117
3104
  "deviceId": getattr(self.mqtt_proxy, 'device_id', 'unknown'),
3118
- "command": "petal-user-journey-coordinator/acknowledge",
3105
+ "command": f"{self.petal_name}/acknowledge",
3119
3106
  "timestamp": datetime.now().isoformat(),
3120
3107
  "payload": response_payload.model_dump()
3121
3108
  }
@@ -3387,7 +3374,7 @@ class WifiOptitrackConnectivityController:
3387
3374
  mqtt_message = {
3388
3375
  "messageId": message_id,
3389
3376
  "deviceId": getattr(self.mqtt_proxy, 'device_id', 'unknown'),
3390
- "command": "petal-user-journey-coordinator/set_static_ip_address_ack",
3377
+ "command": f"{self.petal_name}/set_static_ip_address_ack",
3391
3378
  "timestamp": datetime.now().isoformat(),
3392
3379
  "payload": response_payload.model_dump()
3393
3380
  }
@@ -1,57 +1,59 @@
1
1
  from pydantic import BaseModel, Field, field_validator, ValidationError
2
- from typing import Dict, Any, List, Union, Optional, Callable
2
+ from typing import Dict, Any, List, Literal, Union, Optional, Callable
3
3
  from datetime import datetime
4
4
 
5
-
6
5
  class ParameterBaseModel(BaseModel):
7
6
  """Base fields for flight records"""
8
7
  parameter_name: str = Field(..., description="Parameter name")
9
8
  parameter_value: Union[str,int,float] = Field(..., description="Value of the parameter")
9
+ parameter_type: Optional[Literal['UINT8','INT8','UINT16','INT16','UINT32','INT32','UINT64','INT64','REAL32','REAL64']] = Field(default=None, description="Type of the parameter")
10
10
 
11
11
  model_config = {
12
12
  "json_schema_extra": {
13
13
  "example": {
14
14
  "parameter_name": "CA_ROTOR_COUNT",
15
- "parameter_value": 4
15
+ "parameter_value": 4,
16
+ "parameter_type": "UINT8"
16
17
  }
17
18
  }
18
19
  }
19
20
 
20
21
 
21
- class ParameterRequestModel(BaseModel):
22
- """Base fields for flight records"""
23
- parameter_name: str = Field(..., description="Parameter name")
22
+ class ParameterResult(BaseModel):
23
+ """Individual parameter result"""
24
+ name: str = Field(..., description="Name of the parameter")
25
+ value: Optional[Union[str,int,float]] = Field(None, description="Decoded value of the parameter")
26
+ raw: Optional[Union[str,int,float]] = Field(None, description="Raw value of the parameter")
27
+ type: Optional[int] = Field(None, description="Type of the parameter")
28
+ count: Optional[int] = Field(None, description="Total number of parameters")
29
+ index: Optional[int] = Field(None, description="Index of the parameter")
30
+ error: Optional[str] = Field(default=None, description="Error message if setting the parameter failed")
31
+ success: Optional[bool] = Field(default=None, description="Whether setting the parameter was successful")
24
32
 
25
33
  model_config = {
26
34
  "json_schema_extra": {
27
35
  "example": {
28
- "parameter_name": "CA_ROTOR_COUNT"
36
+ "name": "CA_ROTOR_COUNT",
37
+ "value": 4,
38
+ "raw": 4.0,
39
+ "type": 6,
40
+ "count": 1053,
41
+ "index": 65535,
42
+ "error": None,
43
+ "success": True
29
44
  }
30
45
  }
31
46
  }
32
47
 
33
48
 
34
- class MQTTMessage(BaseModel):
35
- """MQTT message model"""
36
- waitResponse: bool = Field(..., description="Whether to wait for a response")
37
- messageId: str = Field(..., description="Unique message ID")
38
- deviceId: str = Field(..., description="Device ID")
39
- command: str = Field(..., description="Command to execute")
40
- timestamp: str = Field(..., description="Timestamp of the message")
41
- payload: Dict[str, Any] = Field(..., description="Message payload")
49
+ class ParameterRequestModel(BaseModel):
50
+ """Base fields for flight records"""
51
+ parameter_name: str = Field(..., description="Parameter name")
42
52
 
43
53
  model_config = {
44
54
  "json_schema_extra": {
45
55
  "example": {
46
- "waitResponse": True,
47
- "messageId": "kkkss8fepn-1756665973142-bptyoj06z",
48
- "deviceId": "Instance-a92c5505-ccdb-4ac7-b0fe-74f4fa5fc5b9",
49
- "command": "Update",
50
- "payload": {
51
- "source": "web-client",
52
- "app": "leaf-fc"
53
- },
54
- "timestamp": "2025-08-31T18:46:13.142Z"
56
+ "parameter_name": "CA_ROTOR_COUNT"
55
57
  }
56
58
  }
57
59
  }
@@ -554,3 +556,81 @@ class SetStaticIpAddressResponse(BaseModel):
554
556
  }
555
557
  }
556
558
  }
559
+
560
+
561
+ class BulkParameterSetRequest(BaseModel):
562
+ """Request model for bulk parameter setting"""
563
+ parameters: List[ParameterBaseModel] = Field(..., description="List of parameters to set")
564
+
565
+ model_config = {
566
+ "json_schema_extra": {
567
+ "example": {
568
+ "parameters": [
569
+ {
570
+ "parameter_name": "CA_ROTOR_COUNT",
571
+ "parameter_value": 4,
572
+ "parameter_type": "UINT8"
573
+ },
574
+ {
575
+ "parameter_name": "VTO_LOITER_ALT",
576
+ "parameter_value": 80.0,
577
+ "parameter_type": "REAL32"
578
+ }
579
+ ]
580
+ }
581
+ }
582
+ }
583
+
584
+
585
+ class BulkParameterGetRequest(BaseModel):
586
+ """Request model for bulk parameter getting"""
587
+ parameter_names: List[str] = Field(..., description="List of parameter names to get")
588
+
589
+ model_config = {
590
+ "json_schema_extra": {
591
+ "example": {
592
+ "parameter_names": [
593
+ "CA_ROTOR_COUNT",
594
+ "VTO_LOITER_ALT"
595
+ ]
596
+ }
597
+ }
598
+ }
599
+
600
+
601
+ class BulkParameterResponse(BaseModel):
602
+ """Response model for bulk parameter setting"""
603
+ success: bool = Field(..., description="Whether all parameters were set successfully")
604
+ results: Dict[str, ParameterResult] = Field(..., description="Results for each parameter set attempt")
605
+ timestamp: str = Field(..., description="Timestamp of the operation")
606
+
607
+ model_config = {
608
+ "json_schema_extra": {
609
+ "example": {
610
+ "success": True,
611
+ "results": {
612
+ "CA_ROTOR_COUNT": {
613
+ "name": "CA_ROTOR_COUNT",
614
+ "value": 4,
615
+ "raw": 4.0,
616
+ "type": 6,
617
+ "count": 1053,
618
+ "index": 65535,
619
+ "error": None,
620
+ "success": True
621
+ },
622
+ "VTO_LOITER_ALT": {
623
+ "name": "VTO_LOITER_ALT",
624
+ "value": 80.0,
625
+ "raw": 80.0,
626
+ "type": 9,
627
+ "count": 1053,
628
+ "index": 1047,
629
+ "error": None,
630
+ "success": True
631
+ }
632
+ },
633
+ "timestamp": "2023-01-01T00:00:00Z"
634
+ }
635
+ }
636
+ }