flexCommunicator 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.
- flexCommunicator/__init__.py +0 -0
- flexCommunicator/clientLibraries/__init__.py +0 -0
- flexCommunicator/clientLibraries/flcpy/__init__.py +0 -0
- flexCommunicator/clientLibraries/flcpy/action/__init__.py +0 -0
- flexCommunicator/clientLibraries/flcpy/action/action.py +85 -0
- flexCommunicator/clientLibraries/flcpy/action/action_client.py +70 -0
- flexCommunicator/clientLibraries/flcpy/action/goal_handle.py +50 -0
- flexCommunicator/clientLibraries/flcpy/codec/ROS2Codec.py +227 -0
- flexCommunicator/clientLibraries/flcpy/codec/__init__.py +0 -0
- flexCommunicator/clientLibraries/flcpy/commands/__init__.py +0 -0
- flexCommunicator/clientLibraries/flcpy/commands/commandProcessor.py +113 -0
- flexCommunicator/clientLibraries/flcpy/commands/composer.py +25 -0
- flexCommunicator/clientLibraries/flcpy/communication/CommunicationManager.py +579 -0
- flexCommunicator/clientLibraries/flcpy/communication/__init__.py +0 -0
- flexCommunicator/clientLibraries/flcpy/contracts/__init__.py +0 -0
- flexCommunicator/clientLibraries/flcpy/contracts/contractVariable.py +93 -0
- flexCommunicator/clientLibraries/flcpy/flexCloud/__init__.py +0 -0
- flexCommunicator/clientLibraries/flcpy/flexCloud/flexCloudConfiguration.py +16 -0
- flexCommunicator/clientLibraries/flcpy/flexCloud/flexCloudManager.py +162 -0
- flexCommunicator/clientLibraries/flcpy/flexCloud/flexCloudOrchestrator.py +248 -0
- flexCommunicator/clientLibraries/flcpy/flexCloud/flexCloudVariable.py +130 -0
- flexCommunicator/clientLibraries/flcpy/flexNode.py +892 -0
- flexCommunicator/clientLibraries/flcpy/fmi/__init__.py +0 -0
- flexCommunicator/clientLibraries/flcpy/fmi/fmuNode.py +166 -0
- flexCommunicator/clientLibraries/flcpy/fmi/node.py +360 -0
- flexCommunicator/clientLibraries/flcpy/knowledge/KnowledgeManager.py +288 -0
- flexCommunicator/clientLibraries/flcpy/knowledge/__init__.py +0 -0
- flexCommunicator/clientLibraries/flcpy/logging/LoggingAndTracking.py +159 -0
- flexCommunicator/clientLibraries/flcpy/logging/__init__.py +0 -0
- flexCommunicator/clientLibraries/flcpy/messages/StandardizedMessages.py +64 -0
- flexCommunicator/clientLibraries/flcpy/messages/__init__.py +0 -0
- flexCommunicator/clientLibraries/flcpy/node.py +486 -0
- flexCommunicator/clientLibraries/flcpy/security/__init__.py +0 -0
- flexCommunicator/clientLibraries/flcpy/security/security.py +189 -0
- flexCommunicator/clientLibraries/flcpy/service/__init__.py +0 -0
- flexCommunicator/clientLibraries/flcpy/service/client.py +58 -0
- flexCommunicator/clientLibraries/flcpy/service/service.py +67 -0
- flexCommunicator/clientLibraries/flcpy/transport/PropertyBinding.py +108 -0
- flexCommunicator/clientLibraries/flcpy/transport/PropertyRegistry.py +142 -0
- flexCommunicator/clientLibraries/flcpy/transport/__init__.py +0 -0
- flexCommunicator/clientLibraries/flcpy/utils/Timer.py +22 -0
- flexCommunicator/clientLibraries/flcpy/utils/__init__.py +0 -0
- flexCommunicator/clientLibraries/flcpy/utils/auxiliary.py +261 -0
- flexCommunicator/clientLibraries/flcpy/utils/constants.py +276 -0
- flexCommunicator/clientLibraries/flcpy/utils/decorators.py +70 -0
- flexCommunicator/clientLibraries/flcpy/validityFrame/__init__.py +0 -0
- flexCommunicator/clientLibraries/flcpy/validityFrame/runtimeMonitor.py +101 -0
- flexCommunicator/clientLibraries/flcpy/validityFrame/validityFrameManager.py +958 -0
- flexcommunicator-0.1.0.dist-info/METADATA +568 -0
- flexcommunicator-0.1.0.dist-info/RECORD +53 -0
- flexcommunicator-0.1.0.dist-info/WHEEL +5 -0
- flexcommunicator-0.1.0.dist-info/licenses/LICENSE +201 -0
- flexcommunicator-0.1.0.dist-info/top_level.txt +1 -0
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#**************************************************************************
|
|
2
|
+
# * Copyright (C) 2025-present Bert Van Acker (B.MKR) <bva.bmkr@gmail.com>
|
|
3
|
+
# *
|
|
4
|
+
# * This file is part of the flexIA project.
|
|
5
|
+
# *
|
|
6
|
+
# * flexIA can not be copied and/or distributed without the express
|
|
7
|
+
# * permission of Bert Van Acker
|
|
8
|
+
# *************************************************************************
|
|
9
|
+
|
|
10
|
+
class action(object):
|
|
11
|
+
def __init__(self, name="tbd", request=None, response=None,feedback=None,callback=None,requestProperty=None, responseProperty=None,feedbackProperty=None):
|
|
12
|
+
self._name = name
|
|
13
|
+
self._request = request
|
|
14
|
+
self._response = response
|
|
15
|
+
self._feedback = feedback
|
|
16
|
+
self._requestProperty = requestProperty
|
|
17
|
+
self._responseProperty = responseProperty
|
|
18
|
+
self._feedbackProperty = feedbackProperty
|
|
19
|
+
self._callback = callback
|
|
20
|
+
|
|
21
|
+
@property
|
|
22
|
+
def name(self):
|
|
23
|
+
return self._name
|
|
24
|
+
|
|
25
|
+
@name.setter
|
|
26
|
+
def name(self, value):
|
|
27
|
+
self._name = value
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def request(self):
|
|
31
|
+
return self._request
|
|
32
|
+
|
|
33
|
+
@request.setter
|
|
34
|
+
def request(self, value):
|
|
35
|
+
self._request = value
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def response(self):
|
|
39
|
+
return self._response
|
|
40
|
+
|
|
41
|
+
@response.setter
|
|
42
|
+
def response(self, value):
|
|
43
|
+
self._response = value
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def feedback(self):
|
|
47
|
+
return self._feedback
|
|
48
|
+
|
|
49
|
+
@feedback.setter
|
|
50
|
+
def feedback(self, value):
|
|
51
|
+
self._feedback = value
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def requestProperty(self):
|
|
55
|
+
return self._requestProperty
|
|
56
|
+
|
|
57
|
+
@requestProperty.setter
|
|
58
|
+
def requestProperty(self, value):
|
|
59
|
+
self._requestProperty = value
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def responseProperty(self):
|
|
63
|
+
return self._responseProperty
|
|
64
|
+
|
|
65
|
+
@responseProperty.setter
|
|
66
|
+
def responseProperty(self, value):
|
|
67
|
+
self._responseProperty = value
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def feedbackProperty(self):
|
|
71
|
+
return self._feedbackProperty
|
|
72
|
+
|
|
73
|
+
@feedbackProperty.setter
|
|
74
|
+
def feedbackProperty(self, value):
|
|
75
|
+
self._feedbackProperty = value
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def callback(self):
|
|
79
|
+
return self._callback
|
|
80
|
+
|
|
81
|
+
@callback.setter
|
|
82
|
+
def callback(self, value):
|
|
83
|
+
self._callback = value
|
|
84
|
+
|
|
85
|
+
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#**************************************************************************
|
|
2
|
+
# * Copyright (C) 2025-present Bert Van Acker (B.MKR) <bva.bmkr@gmail.com>
|
|
3
|
+
# *
|
|
4
|
+
# * This file is part of the flexIA project.
|
|
5
|
+
# *
|
|
6
|
+
# * flexIA can not be copied and/or distributed without the express
|
|
7
|
+
# * permission of Bert Van Acker
|
|
8
|
+
# *************************************************************************
|
|
9
|
+
import time
|
|
10
|
+
import threading
|
|
11
|
+
class ation_client(object):
|
|
12
|
+
def __init__(self, name="tbd", action=None,logger=None,set_data=None):
|
|
13
|
+
self._name = name
|
|
14
|
+
self._action = action
|
|
15
|
+
self._isWaitingforAction = False
|
|
16
|
+
self._cv = threading.Condition()
|
|
17
|
+
self._logger = logger
|
|
18
|
+
self._set_data = set_data
|
|
19
|
+
self._done_callback = None
|
|
20
|
+
self._feedback_callback = None
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def action(self):
|
|
24
|
+
return self._action
|
|
25
|
+
|
|
26
|
+
@action.setter
|
|
27
|
+
def action(self, value):
|
|
28
|
+
self._action = value
|
|
29
|
+
|
|
30
|
+
def call_async(self,request):
|
|
31
|
+
self._logger.info("Async action <"+self.action.name+"> called.")
|
|
32
|
+
self._set_data(data_name=self._action.requestProperty, data=request)
|
|
33
|
+
self._isWaitingforAction = True
|
|
34
|
+
|
|
35
|
+
def spin_until_future_complete(self, timeout=5.0):
|
|
36
|
+
# Wait for response or timeout
|
|
37
|
+
with self._cv:
|
|
38
|
+
if not self._cv.wait_for(lambda: not self._isWaitingforAction, timeout=timeout):
|
|
39
|
+
self._isWaitingforAction = False
|
|
40
|
+
self._logger.info("Async action <" + self.action.name + "> called failed, no response received within timeout ["+timeout.__str__()+" sec.].")
|
|
41
|
+
return -1
|
|
42
|
+
|
|
43
|
+
self._isWaitingforAction = False
|
|
44
|
+
return self._action.response
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def response_callback(self,topic,response):
|
|
48
|
+
#set response
|
|
49
|
+
self.action.response.instantiate(json_str=response)
|
|
50
|
+
self._isWaitingforAction = False
|
|
51
|
+
|
|
52
|
+
#DONE CALLBACK
|
|
53
|
+
if self._done_callback is not None:
|
|
54
|
+
self._done_callback(self.action.response)
|
|
55
|
+
|
|
56
|
+
def feedback_callback(self,topic,feedback):
|
|
57
|
+
#set response
|
|
58
|
+
self.action.feedback.instantiate(json_str=feedback)
|
|
59
|
+
|
|
60
|
+
#DONE CALLBACK
|
|
61
|
+
if self._feedback_callback is not None:
|
|
62
|
+
self._feedback_callback(self.action.feedback)
|
|
63
|
+
|
|
64
|
+
def add_done_callback(self,callback=None):
|
|
65
|
+
self._done_callback = callback
|
|
66
|
+
|
|
67
|
+
def add_feedback_callback(self,callback=None):
|
|
68
|
+
self._feedback_callback = callback
|
|
69
|
+
|
|
70
|
+
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#**************************************************************************
|
|
2
|
+
# * Copyright (C) 2025-present Bert Van Acker (B.MKR) <bva.bmkr@gmail.com>
|
|
3
|
+
# *
|
|
4
|
+
# * This file is part of the flexIA project.
|
|
5
|
+
# *
|
|
6
|
+
# * flexIA can not be copied and/or distributed without the express
|
|
7
|
+
# * permission of Bert Van Acker
|
|
8
|
+
# *************************************************************************
|
|
9
|
+
from flexCommunicator.clientLibraries.flcpy.utils.constants import *
|
|
10
|
+
import jsonpickle
|
|
11
|
+
class goal_handle(object):
|
|
12
|
+
def __init__(self, name="tbd", action=None,logger=None):
|
|
13
|
+
self._name = name
|
|
14
|
+
self._action = action
|
|
15
|
+
self._logger = logger
|
|
16
|
+
|
|
17
|
+
@property
|
|
18
|
+
def action(self):
|
|
19
|
+
return self._action
|
|
20
|
+
|
|
21
|
+
@action.setter
|
|
22
|
+
def action(self, value):
|
|
23
|
+
self._action = value
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def request(self):
|
|
27
|
+
return self._action.request
|
|
28
|
+
|
|
29
|
+
@request.setter
|
|
30
|
+
def request(self, value):
|
|
31
|
+
self._action.request = value
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def response(self):
|
|
35
|
+
return self._action.response
|
|
36
|
+
|
|
37
|
+
@response.setter
|
|
38
|
+
def response(self, value):
|
|
39
|
+
self._action.response = value
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def feedback(self):
|
|
43
|
+
return self._action.feedback
|
|
44
|
+
|
|
45
|
+
@feedback.setter
|
|
46
|
+
def feedback(self,value):
|
|
47
|
+
self._action.feedback = value
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
#**************************************************************************
|
|
2
|
+
# * Copyright (C) 2025-present Bert Van Acker (B.MKR) <bva.bmkr@gmail.com>
|
|
3
|
+
# *
|
|
4
|
+
# * This file is part of the flexIA project.
|
|
5
|
+
# *
|
|
6
|
+
# * flexIA can not be copied and/or distributed without the express
|
|
7
|
+
# * permission of Bert Van Acker
|
|
8
|
+
# *************************************************************************
|
|
9
|
+
import base64
|
|
10
|
+
import json
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
from typing import Any, Dict, Optional, Union
|
|
13
|
+
import numpy as np
|
|
14
|
+
import cv2
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class DecodedImageMessage:
|
|
18
|
+
frame: Optional[np.ndarray]
|
|
19
|
+
encoding: str
|
|
20
|
+
metadata: Dict[str, Any]
|
|
21
|
+
stamp_sec: Optional[int]
|
|
22
|
+
stamp_nanosec: Optional[int]
|
|
23
|
+
frame_id: Optional[str]
|
|
24
|
+
height: Optional[int]
|
|
25
|
+
width: Optional[int]
|
|
26
|
+
ros_encoding: Optional[str]
|
|
27
|
+
is_bigendian: Optional[int]
|
|
28
|
+
step: Optional[int]
|
|
29
|
+
raw_bytes: bytes
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class ROS2ImageDecoder:
|
|
33
|
+
def decode_message(
|
|
34
|
+
self,
|
|
35
|
+
payload: Union[str, bytes, Dict[str, Any]],
|
|
36
|
+
) -> Optional[DecodedImageMessage]:
|
|
37
|
+
|
|
38
|
+
data = self._parse_payload(payload)
|
|
39
|
+
if data is None:
|
|
40
|
+
return None
|
|
41
|
+
|
|
42
|
+
payload_obj = self._extract_payload_obj(data)
|
|
43
|
+
if not payload_obj:
|
|
44
|
+
return None
|
|
45
|
+
|
|
46
|
+
encoding = payload_obj.get("encoding", "")
|
|
47
|
+
raw_b64 = payload_obj.get("data", "")
|
|
48
|
+
metadata = payload_obj.get("metadata", {}) or {}
|
|
49
|
+
|
|
50
|
+
if not raw_b64:
|
|
51
|
+
return None
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
raw_bytes = base64.b64decode(raw_b64)
|
|
55
|
+
except Exception as exc:
|
|
56
|
+
print(f"Failed to base64-decode image payload: {exc}")
|
|
57
|
+
return None
|
|
58
|
+
|
|
59
|
+
frame = self._decode_image_bytes(
|
|
60
|
+
raw_bytes=raw_bytes,
|
|
61
|
+
payload_encoding=encoding,
|
|
62
|
+
metadata=metadata,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
stamp = metadata.get("stamp", {}) or {}
|
|
66
|
+
|
|
67
|
+
return DecodedImageMessage(
|
|
68
|
+
frame=frame,
|
|
69
|
+
encoding=encoding,
|
|
70
|
+
metadata=metadata,
|
|
71
|
+
stamp_sec=stamp.get("sec"),
|
|
72
|
+
stamp_nanosec=stamp.get("nanosec"),
|
|
73
|
+
frame_id=metadata.get("frame_id"),
|
|
74
|
+
height=metadata.get("height"),
|
|
75
|
+
width=metadata.get("width"),
|
|
76
|
+
ros_encoding=metadata.get("encoding"),
|
|
77
|
+
is_bigendian=metadata.get("is_bigendian"),
|
|
78
|
+
step=metadata.get("step"),
|
|
79
|
+
raw_bytes=raw_bytes,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
def _parse_payload(
|
|
83
|
+
self,
|
|
84
|
+
payload: Union[str, bytes, Dict[str, Any]],
|
|
85
|
+
) -> Optional[Dict[str, Any]]:
|
|
86
|
+
|
|
87
|
+
if isinstance(payload, dict):
|
|
88
|
+
return payload
|
|
89
|
+
|
|
90
|
+
if isinstance(payload, bytes):
|
|
91
|
+
try:
|
|
92
|
+
payload = payload.decode("utf-8")
|
|
93
|
+
except UnicodeDecodeError as exc:
|
|
94
|
+
print(f"Failed to decode MQTT payload as UTF-8: {exc}")
|
|
95
|
+
return None
|
|
96
|
+
|
|
97
|
+
if isinstance(payload, str):
|
|
98
|
+
try:
|
|
99
|
+
return json.loads(payload)
|
|
100
|
+
except json.JSONDecodeError as exc:
|
|
101
|
+
print(f"Failed to parse JSON payload: {exc}")
|
|
102
|
+
return None
|
|
103
|
+
|
|
104
|
+
return None
|
|
105
|
+
|
|
106
|
+
def _extract_payload_obj(self, data: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
|
107
|
+
"""
|
|
108
|
+
Supports:
|
|
109
|
+
|
|
110
|
+
1. {"_payload": {...}}
|
|
111
|
+
2. {"payload": {"_payload": {...}}}
|
|
112
|
+
3. {"payload": {...}}
|
|
113
|
+
4. {"encoding": "...", "data": "..."}
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
if "_payload" in data and isinstance(data["_payload"], dict):
|
|
117
|
+
return data["_payload"]
|
|
118
|
+
|
|
119
|
+
if "payload" in data and isinstance(data["payload"], dict):
|
|
120
|
+
nested = data["payload"]
|
|
121
|
+
|
|
122
|
+
if "_payload" in nested and isinstance(nested["_payload"], dict):
|
|
123
|
+
return nested["_payload"]
|
|
124
|
+
|
|
125
|
+
if "encoding" in nested and "data" in nested:
|
|
126
|
+
return nested
|
|
127
|
+
|
|
128
|
+
if "encoding" in data and "data" in data:
|
|
129
|
+
return data
|
|
130
|
+
|
|
131
|
+
return None
|
|
132
|
+
|
|
133
|
+
def _decode_image_bytes(
|
|
134
|
+
self,
|
|
135
|
+
raw_bytes: bytes,
|
|
136
|
+
payload_encoding: str,
|
|
137
|
+
metadata: Dict[str, Any],
|
|
138
|
+
) -> Optional[np.ndarray]:
|
|
139
|
+
|
|
140
|
+
if payload_encoding in (
|
|
141
|
+
"image_jpeg_base64",
|
|
142
|
+
"image_jpg_base64",
|
|
143
|
+
"image_png_base64",
|
|
144
|
+
):
|
|
145
|
+
arr = np.frombuffer(raw_bytes, dtype=np.uint8)
|
|
146
|
+
frame_bgr = cv2.imdecode(arr, cv2.IMREAD_COLOR)
|
|
147
|
+
|
|
148
|
+
if frame_bgr is None:
|
|
149
|
+
print("OpenCV failed to decode compressed image.")
|
|
150
|
+
return None
|
|
151
|
+
|
|
152
|
+
return frame_bgr
|
|
153
|
+
|
|
154
|
+
if payload_encoding == "image_raw_base64":
|
|
155
|
+
return self._decode_raw_image(raw_bytes, metadata)
|
|
156
|
+
|
|
157
|
+
print(f"Unsupported payload encoding: {payload_encoding}")
|
|
158
|
+
return None
|
|
159
|
+
|
|
160
|
+
def _decode_raw_image(
|
|
161
|
+
self,
|
|
162
|
+
raw_bytes: bytes,
|
|
163
|
+
metadata: Dict[str, Any],
|
|
164
|
+
) -> Optional[np.ndarray]:
|
|
165
|
+
|
|
166
|
+
height = metadata.get("height")
|
|
167
|
+
width = metadata.get("width")
|
|
168
|
+
ros_encoding = metadata.get("encoding")
|
|
169
|
+
step = metadata.get("step")
|
|
170
|
+
|
|
171
|
+
if not height or not width or not ros_encoding:
|
|
172
|
+
print("Missing raw image metadata: height, width, or encoding.")
|
|
173
|
+
return None
|
|
174
|
+
|
|
175
|
+
channels_by_encoding = {
|
|
176
|
+
"mono8": 1,
|
|
177
|
+
"rgb8": 3,
|
|
178
|
+
"bgr8": 3,
|
|
179
|
+
"rgba8": 4,
|
|
180
|
+
"bgra8": 4,
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
channels = channels_by_encoding.get(ros_encoding)
|
|
184
|
+
|
|
185
|
+
if channels is None:
|
|
186
|
+
print(f"Unsupported raw ROS image encoding: {ros_encoding}")
|
|
187
|
+
return None
|
|
188
|
+
|
|
189
|
+
arr = np.frombuffer(raw_bytes, dtype=np.uint8)
|
|
190
|
+
|
|
191
|
+
expected_row_bytes = width * channels
|
|
192
|
+
|
|
193
|
+
if step is None:
|
|
194
|
+
step = expected_row_bytes
|
|
195
|
+
|
|
196
|
+
expected_total_bytes = height * step
|
|
197
|
+
|
|
198
|
+
if arr.size < expected_total_bytes:
|
|
199
|
+
print(
|
|
200
|
+
f"Raw image buffer too small: got {arr.size} bytes, "
|
|
201
|
+
f"expected at least {expected_total_bytes} bytes."
|
|
202
|
+
)
|
|
203
|
+
return None
|
|
204
|
+
|
|
205
|
+
arr = arr[:expected_total_bytes]
|
|
206
|
+
|
|
207
|
+
rows = arr.reshape((height, step))
|
|
208
|
+
image_data = rows[:, :expected_row_bytes]
|
|
209
|
+
|
|
210
|
+
if channels == 1:
|
|
211
|
+
return image_data.reshape((height, width))
|
|
212
|
+
|
|
213
|
+
frame = image_data.reshape((height, width, channels))
|
|
214
|
+
|
|
215
|
+
if ros_encoding == "rgb8":
|
|
216
|
+
return cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
|
|
217
|
+
|
|
218
|
+
if ros_encoding == "bgr8":
|
|
219
|
+
return frame
|
|
220
|
+
|
|
221
|
+
if ros_encoding == "rgba8":
|
|
222
|
+
return cv2.cvtColor(frame, cv2.COLOR_RGBA2BGR)
|
|
223
|
+
|
|
224
|
+
if ros_encoding == "bgra8":
|
|
225
|
+
return cv2.cvtColor(frame, cv2.COLOR_BGRA2BGR)
|
|
226
|
+
|
|
227
|
+
return None
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#**************************************************************************
|
|
2
|
+
# * Copyright (C) 2025-present Bert Van Acker (B.MKR) <bva.bmkr@gmail.com>
|
|
3
|
+
# *
|
|
4
|
+
# * This file is part of the flexIA project.
|
|
5
|
+
# *
|
|
6
|
+
# * flexIA can not be copied and/or distributed without the express
|
|
7
|
+
# * permission of Bert Van Acker
|
|
8
|
+
# *************************************************************************
|
|
9
|
+
from flexCommunicator.clientLibraries.flcpy.security.security import verify_signature
|
|
10
|
+
from flexCommunicator.clientLibraries.flcpy.utils.constants import *
|
|
11
|
+
import threading
|
|
12
|
+
import json
|
|
13
|
+
class commandProcessor(object):
|
|
14
|
+
def __init__(self,name="cmdProcessor",logger=None,verbose=False):
|
|
15
|
+
"""Initialize a Logger component.
|
|
16
|
+
|
|
17
|
+
Parameters
|
|
18
|
+
----------
|
|
19
|
+
name : string
|
|
20
|
+
name of the property components
|
|
21
|
+
|
|
22
|
+
verbose : bool
|
|
23
|
+
component verbose execution
|
|
24
|
+
|
|
25
|
+
See Also
|
|
26
|
+
--------
|
|
27
|
+
..
|
|
28
|
+
|
|
29
|
+
Examples
|
|
30
|
+
--------
|
|
31
|
+
>> cmdProcessor = commandProcessor(name="customProcessor",verbose=False)
|
|
32
|
+
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
# --- logger configuration ---
|
|
36
|
+
self._name = name
|
|
37
|
+
self._verbose = verbose
|
|
38
|
+
self._logger = logger
|
|
39
|
+
# Set to store received command IDs.
|
|
40
|
+
self._received_command_ids = set()
|
|
41
|
+
self._command_ids_lock = threading.Lock()
|
|
42
|
+
|
|
43
|
+
self._logger.info("Command processor initialized")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def process_command(self,command):
|
|
47
|
+
"""Process a command """
|
|
48
|
+
action = None
|
|
49
|
+
data = {}
|
|
50
|
+
|
|
51
|
+
# DECODE INCOMMING COMMAND
|
|
52
|
+
try:
|
|
53
|
+
command = json.loads(command)
|
|
54
|
+
except:
|
|
55
|
+
command = GENERIC_STATUS.INCORRECT_CMD
|
|
56
|
+
|
|
57
|
+
# CHECK COMMAND CORRECTNESS (SIGNATURE)
|
|
58
|
+
signature_valid = verify_signature(command=command)
|
|
59
|
+
|
|
60
|
+
try:
|
|
61
|
+
#CHECK IF COMMAND IS ALREADY RECEIVED
|
|
62
|
+
if not self.is_command_already_received(command):
|
|
63
|
+
if command["cmd"] == FLEXCLOUD_COMMANDS.UNKNOWN:
|
|
64
|
+
if self._logger is not None: self._logger.error(COMMAND_TYPE.UNKNOWN+" command received. Please verify the command type")
|
|
65
|
+
return action,data
|
|
66
|
+
elif command["cmd"] == FLEXCLOUD_COMMANDS.PING:
|
|
67
|
+
if self._logger is not None: self._logger.info(COMMAND_TYPE.PING+" command received.")
|
|
68
|
+
if self._verbose: print(FLEXCLOUD_COMMANDS.PING+" command received.")
|
|
69
|
+
action = FLEXCLOUD_COMMANDS.PING
|
|
70
|
+
data = {}
|
|
71
|
+
return signature_valid,action,data
|
|
72
|
+
elif command["cmd"] == FLEXCLOUD_COMMANDS.UPDATE_TRANSPORT:
|
|
73
|
+
if self._logger is not None: self._logger.info(FLEXCLOUD_COMMANDS.UPDATE_TRANSPORT+" command received.")
|
|
74
|
+
if self._verbose: print(FLEXCLOUD_COMMANDS.UPDATE_TRANSPORT+" command received.")
|
|
75
|
+
action = FLEXCLOUD_COMMANDS.UPDATE_TRANSPORT
|
|
76
|
+
data = command["params"]
|
|
77
|
+
return signature_valid,action,data
|
|
78
|
+
else:
|
|
79
|
+
if self._logger is not None: self._logger.warning(FLEXCLOUD_WARNINGS.WARN_UNKNOWN_FLEXCLOUD_COMMAND)
|
|
80
|
+
return signature_valid,action,data
|
|
81
|
+
|
|
82
|
+
#TODO: extend with other accepted commands
|
|
83
|
+
|
|
84
|
+
except Exception as e:
|
|
85
|
+
if self._logger is not None: self._logger.error("Failed to process command.")
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def is_command_already_received(self,command):
|
|
89
|
+
"""
|
|
90
|
+
Checks if a command has already been received by inspecting its unique 'id' field.
|
|
91
|
+
|
|
92
|
+
This function uses a global set to keep track of command IDs that have already been processed.
|
|
93
|
+
If the command has not been received before, its ID is added to the set.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
command (dict): The command dictionary. Must include an 'id' key.
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
bool: True if the command was already received, False if it is new.
|
|
100
|
+
|
|
101
|
+
Raises:
|
|
102
|
+
ValueError: If the command does not contain an 'id' field.
|
|
103
|
+
"""
|
|
104
|
+
command_id = command.get("id")
|
|
105
|
+
if command_id is None:
|
|
106
|
+
raise ValueError("Command does not contain an 'id' field.")
|
|
107
|
+
|
|
108
|
+
with self._command_ids_lock:
|
|
109
|
+
if command_id in self._received_command_ids:
|
|
110
|
+
return True
|
|
111
|
+
else:
|
|
112
|
+
self._received_command_ids.add(command_id)
|
|
113
|
+
return False
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#**************************************************************************
|
|
2
|
+
# * Copyright (C) 2025-present Bert Van Acker (B.MKR) <bva.bmkr@gmail.com>
|
|
3
|
+
# *
|
|
4
|
+
# * This file is part of the flexIA project.
|
|
5
|
+
# *
|
|
6
|
+
# * flexIA can not be copied and/or distributed without the express
|
|
7
|
+
# * permission of Bert Van Acker
|
|
8
|
+
# *************************************************************************
|
|
9
|
+
import time
|
|
10
|
+
import uuid
|
|
11
|
+
from flexCommunicator.clientLibraries.flcpy.security.security import *
|
|
12
|
+
def compose_command(cmd_type, params, priority=5, expiry_seconds=60):
|
|
13
|
+
current_time = int(time.time())
|
|
14
|
+
|
|
15
|
+
_command = {
|
|
16
|
+
"id": str(uuid.uuid4()),
|
|
17
|
+
"cmd": cmd_type,
|
|
18
|
+
"priority": priority, # Add command priority (e.g., 1=highest, 10=lowest)
|
|
19
|
+
"expiry": current_time + expiry_seconds, # Command expiry timestamp
|
|
20
|
+
"params": params,
|
|
21
|
+
"timestamp": current_time,
|
|
22
|
+
"nonce": uuid.uuid4().hex
|
|
23
|
+
}
|
|
24
|
+
_command["signature"] = compute_signature(_command)
|
|
25
|
+
return _command
|