openubmc-bingo 0.6.45__py3-none-any.whl → 0.6.99__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.
- bmcgo/__init__.py +1 -1
- bmcgo/bmcgo.py +9 -3
- bmcgo/bmcgo_config.py +16 -0
- bmcgo/cli/cli.py +72 -21
- bmcgo/codegen/__init__.py +1 -1
- bmcgo/codegen/lua/codegen.py +2 -2
- bmcgo/codegen/lua/script/check_intfs.py +1 -0
- bmcgo/codegen/lua/script/dto/options.py +1 -0
- bmcgo/codegen/lua/script/gen_db_json.py +4 -3
- bmcgo/codegen/lua/script/gen_rpc_msg_json.py +78 -11
- bmcgo/codegen/lua/script/model_consistency_check.py +1 -1
- bmcgo/codegen/lua/script/render_utils/db_lua.py +5 -6
- bmcgo/codegen/lua/script/render_utils/model_lua.py +5 -1
- bmcgo/codegen/lua/script/template.py +5 -0
- bmcgo/codegen/lua/script/utils.py +50 -8
- bmcgo/codegen/lua/templates/apps/Makefile +2 -2
- bmcgo/codegen/lua/templates/apps/client.lua.mako +1 -1
- bmcgo/codegen/lua/templates/apps/model.lua.mako +4 -3
- bmcgo/codegen/lua/templates/apps/service.lua.mako +1 -1
- bmcgo/codegen/lua/templates/apps/utils/mdb_intf.lua.mako +4 -0
- bmcgo/codegen/lua/templates/new_app_v2/CMakeLists.txt.mako +26 -0
- bmcgo/codegen/lua/templates/new_app_v2/conanfile.py.mako +9 -0
- bmcgo/codegen/lua/v1/script/render_utils/db_lua.py +5 -6
- bmcgo/codegen/lua/v1/script/render_utils/model_lua.py +13 -1
- bmcgo/codegen/lua/v1/templates/apps/client.lua.mako +1 -1
- bmcgo/codegen/lua/v1/templates/apps/local_db.lua.mako +0 -4
- bmcgo/codegen/lua/v1/templates/apps/message.lua.mako +3 -0
- bmcgo/codegen/lua/v1/templates/apps/model.lua.mako +3 -0
- bmcgo/codegen/lua/v1/templates/apps/utils/mdb_intf.lua.mako +6 -4
- bmcgo/component/analysis/analysis.py +9 -4
- bmcgo/component/analysis/dep-rules.json +20 -8
- bmcgo/component/analysis/dep_node.py +2 -0
- bmcgo/component/analysis/intf_validation.py +8 -7
- bmcgo/component/analysis/sr_validation.py +5 -4
- bmcgo/component/busctl_log_parse/busctl_log_parser.py +809 -0
- bmcgo/component/busctl_log_parse/mock_data_save.py +170 -0
- bmcgo/component/busctl_log_parse/test_data_save.py +49 -0
- bmcgo/component/component_helper.py +29 -0
- bmcgo/component/coverage/incremental_cov.py +5 -0
- bmcgo/component/fixture/__init__.py +29 -0
- bmcgo/component/fixture/auto_case_generator.py +490 -0
- bmcgo/component/fixture/busctl_type_converter.py +1081 -0
- bmcgo/component/fixture/common_config.py +15 -0
- bmcgo/component/fixture/dbus_gateway.py +669 -0
- bmcgo/component/fixture/dbus_library.py +250 -0
- bmcgo/component/fixture/dbus_mock_utils.py +514 -0
- bmcgo/component/fixture/dbus_response_handler.py +138 -0
- bmcgo/component/fixture/dbus_signature.py +110 -0
- bmcgo/component/template_v2/conanbase.py.mako +1 -5
- bmcgo/component/test.py +69 -10
- bmcgo/error_analyzer/__init__.py +0 -0
- bmcgo/error_analyzer/case_matcher.py +114 -0
- bmcgo/error_analyzer/log_parser.py +128 -0
- bmcgo/error_analyzer/unified_error_analyzer.py +359 -0
- bmcgo/error_cases/cases.yml +59 -0
- bmcgo/error_cases/cases_template_valid.json +71 -0
- bmcgo/error_cases/conanfile.py +58 -0
- bmcgo/frame.py +0 -4
- bmcgo/functional/analysis.py +18 -12
- bmcgo/functional/bmc_studio_action.py +21 -10
- bmcgo/functional/check.py +86 -42
- bmcgo/functional/conan_index_build.py +1 -1
- bmcgo/functional/config.py +22 -18
- bmcgo/functional/csr_build.py +63 -34
- bmcgo/functional/deploy.py +4 -3
- bmcgo/functional/diff.py +51 -34
- bmcgo/functional/full_component.py +16 -5
- bmcgo/functional/hpm_signer.py +484 -0
- bmcgo/functional/new.py +8 -2
- bmcgo/functional/schema_valid.py +111 -15
- bmcgo/functional/upgrade.py +6 -6
- bmcgo/misc.py +1 -0
- bmcgo/tasks/task_build_conan.py +27 -6
- bmcgo/tasks/task_build_rootfs_img.py +120 -83
- bmcgo/tasks/task_buildgppbin.py +30 -13
- bmcgo/tasks/task_buildhpm_ext4.py +5 -3
- bmcgo/tasks/task_download_buildtools.py +20 -11
- bmcgo/tasks/task_download_dependency.py +29 -20
- bmcgo/tasks/task_hpm_envir_prepare.py +32 -53
- bmcgo/tasks/task_packet_to_supporte.py +12 -4
- bmcgo/tasks/task_prepare.py +1 -1
- bmcgo/tasks/task_sign_and_pack_hpm.py +15 -7
- bmcgo/utils/component_version_check.py +4 -4
- bmcgo/utils/config.py +3 -0
- bmcgo/utils/fetch_component_code.py +148 -17
- bmcgo/utils/install_manager.py +2 -2
- bmcgo/utils/installations/base_installer.py +10 -27
- bmcgo/utils/installations/install_plans/studio.yml +3 -0
- bmcgo/utils/mapping_config_patch.py +5 -4
- bmcgo/utils/tools.py +49 -7
- {openubmc_bingo-0.6.45.dist-info → openubmc_bingo-0.6.99.dist-info}/METADATA +1 -1
- {openubmc_bingo-0.6.45.dist-info → openubmc_bingo-0.6.99.dist-info}/RECORD +95 -74
- bmcgo/tasks/download_buildtools_hm.py +0 -124
- {openubmc_bingo-0.6.45.dist-info → openubmc_bingo-0.6.99.dist-info}/WHEEL +0 -0
- {openubmc_bingo-0.6.45.dist-info → openubmc_bingo-0.6.99.dist-info}/entry_points.txt +0 -0
- {openubmc_bingo-0.6.45.dist-info → openubmc_bingo-0.6.99.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,1081 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# coding: utf-8
|
|
3
|
+
# Copyright (c) 2024 Huawei Technologies Co., Ltd.
|
|
4
|
+
# openUBMC is licensed under Mulan PSL v2.
|
|
5
|
+
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
6
|
+
# You may obtain a copy of Mulan PSL v2 at:
|
|
7
|
+
# http://license.coscl.org.cn/MulanPSL2
|
|
8
|
+
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
|
9
|
+
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
|
10
|
+
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
11
|
+
# See the Mulan PSL v2 for more details.
|
|
12
|
+
import re
|
|
13
|
+
import logging
|
|
14
|
+
from dbus_next import Variant
|
|
15
|
+
from bmcgo.component.fixture.dbus_signature import DBusSignature
|
|
16
|
+
|
|
17
|
+
# 配置日志
|
|
18
|
+
logging.basicConfig(
|
|
19
|
+
level=logging.INFO,
|
|
20
|
+
format='%(asctime)s - %(levelname)s - %(name)s - %(message)s'
|
|
21
|
+
)
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class BusCtlTypeConverter:
|
|
26
|
+
"""
|
|
27
|
+
BusCtl类型转换器,提供将BusCtl日志字符串转换为正确D-Bus类型的功能
|
|
28
|
+
支持所有D-Bus标准数据类型,保持与DBusTypeConverter相同的对外接口
|
|
29
|
+
"""
|
|
30
|
+
@staticmethod
|
|
31
|
+
def dbus_string_to_type(type_value_str):
|
|
32
|
+
"""
|
|
33
|
+
将BusCtl日志中的字符串转换为正确的D-Bus类型
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
type_value_str: BusCtl日志中的字符串,格式:类型 值
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
转换后的Python原生类型或Variant
|
|
40
|
+
"""
|
|
41
|
+
if type_value_str is None:
|
|
42
|
+
return None
|
|
43
|
+
# 去除首尾的引号和空格
|
|
44
|
+
value_str = str(type_value_str).strip()
|
|
45
|
+
# 首先尝试转换基本类型
|
|
46
|
+
basic_result = BusCtlTypeConverter._convert_basic_type(value_str)
|
|
47
|
+
if basic_result is not None:
|
|
48
|
+
return basic_result
|
|
49
|
+
# 处理对象路径类型
|
|
50
|
+
if value_str.startswith('OBJECT_PATH '):
|
|
51
|
+
try:
|
|
52
|
+
value_str = BusCtlTypeConverter._trim_trailing_semicolon(value_str)
|
|
53
|
+
path = value_str[13:].strip('"\'')
|
|
54
|
+
return path # dbus-next使用普通字符串表示对象路径
|
|
55
|
+
except (IndexError, ValueError):
|
|
56
|
+
pass
|
|
57
|
+
# 处理签名类型
|
|
58
|
+
if value_str.startswith('SIGNATURE '):
|
|
59
|
+
try:
|
|
60
|
+
value_str = BusCtlTypeConverter._trim_trailing_semicolon(value_str)
|
|
61
|
+
sig = value_str[10:].strip('"\'')
|
|
62
|
+
return sig # dbus-next使用普通字符串表示签名
|
|
63
|
+
except (IndexError, ValueError):
|
|
64
|
+
pass
|
|
65
|
+
# 处理复杂类型
|
|
66
|
+
|
|
67
|
+
# 处理圆括号结构体类型 (ss) {STRING "A";STRING "B";}
|
|
68
|
+
if re.match(r'^\([^)]+\)\s*\{', value_str):
|
|
69
|
+
return BusCtlTypeConverter._convert_struct_with_no_quote(value_str)
|
|
70
|
+
# 1. 字典/映射类型 - 优先处理,因为它是特殊的数组格式
|
|
71
|
+
if value_str.startswith('ARRAY "{'):
|
|
72
|
+
# 专门处理busctl格式: ARRAY "{ss}" {...}
|
|
73
|
+
# 找到签名结束的引号位置
|
|
74
|
+
first_quote = value_str.find('"')
|
|
75
|
+
second_quote = value_str.find('"', first_quote + 1)
|
|
76
|
+
if second_quote != -1:
|
|
77
|
+
# 从签名结束引号之后查找数组内容的开始花括号
|
|
78
|
+
content_start = value_str.find('{', second_quote + 1)
|
|
79
|
+
if content_start != -1:
|
|
80
|
+
# 找到匹配的结束花括号
|
|
81
|
+
end_brace_pos = value_str.rfind('};')
|
|
82
|
+
if end_brace_pos != -1 and end_brace_pos > content_start:
|
|
83
|
+
# 提取完整的数组内容(包含花括号)
|
|
84
|
+
array_content = value_str[content_start:end_brace_pos + 2]
|
|
85
|
+
# 检查是否包含DICT_ENTRY
|
|
86
|
+
if 'DICT_ENTRY' in array_content:
|
|
87
|
+
# 调用字典转换函数
|
|
88
|
+
result = BusCtlTypeConverter._convert_dictionary_type(value_str)
|
|
89
|
+
# 如果结果是None(空字典),返回空字典而不是None
|
|
90
|
+
return result if result is not None else {}
|
|
91
|
+
else:
|
|
92
|
+
# 检查是否为空字典数组(去除空白后只有{}或{;})
|
|
93
|
+
stripped_content = array_content.strip()
|
|
94
|
+
# 移除所有空白字符(包括换行、空格等)后检查
|
|
95
|
+
normalized = re.sub(r'\s+', '', stripped_content)
|
|
96
|
+
# 匹配各种可能的空数组格式:{}, {;}, { };, { };}, 等
|
|
97
|
+
flag_norma = normalized in ['{}', '{;}', '{;};', '{};']
|
|
98
|
+
flag_start_end = normalized.startswith('{') and normalized.endswith('}')
|
|
99
|
+
flag_length = len(normalized) <= 5
|
|
100
|
+
if flag_norma or (flag_start_end and flag_length):
|
|
101
|
+
# 空字典数组,返回空字典
|
|
102
|
+
return {}
|
|
103
|
+
# 更精确地处理结构体数组格式: ARRAY "(ss)" { ... }
|
|
104
|
+
if value_str.startswith('ARRAY "('):
|
|
105
|
+
result = BusCtlTypeConverter._handle_struct_array(value_str)
|
|
106
|
+
if isinstance(result, list) and len(result) == 0:
|
|
107
|
+
logger.warning(f"结构体数组解析结果为空,输入字符串长度: {len(value_str)}")
|
|
108
|
+
return result
|
|
109
|
+
# 字节数组类型 - BusCtl格式可能与dbus-monitor不同
|
|
110
|
+
if value_str.startswith('ARRAY "y" '):
|
|
111
|
+
try:
|
|
112
|
+
result = BusCtlTypeConverter._convert_array_of_bytes(value_str)
|
|
113
|
+
if result is not None:
|
|
114
|
+
return result
|
|
115
|
+
except Exception as e:
|
|
116
|
+
logger.error(f"字节数组解析错误: {e}")
|
|
117
|
+
pass
|
|
118
|
+
# 普通数组类型放最后处理 - 支持嵌套结构
|
|
119
|
+
if value_str.startswith('ARRAY '):
|
|
120
|
+
try:
|
|
121
|
+
result = BusCtlTypeConverter._convert_array_type(value_str)
|
|
122
|
+
if result is not None:
|
|
123
|
+
return result
|
|
124
|
+
except Exception as e:
|
|
125
|
+
logger.error(f"数组解析错误: {e}")
|
|
126
|
+
pass
|
|
127
|
+
# 3. 结构体类型
|
|
128
|
+
if value_str.startswith('STRUCT '):
|
|
129
|
+
try:
|
|
130
|
+
result = BusCtlTypeConverter._convert_struct_type(value_str)
|
|
131
|
+
if result is not None:
|
|
132
|
+
return result
|
|
133
|
+
except Exception as e:
|
|
134
|
+
logger.error(f"结构体解析错误: {e}", exc_info=True)
|
|
135
|
+
pass
|
|
136
|
+
# 4. 变体类型
|
|
137
|
+
if value_str.startswith('VARIANT '):
|
|
138
|
+
explicit_sig = None
|
|
139
|
+
try:
|
|
140
|
+
# 先尝试直接从VARIANT描述中提取显式签名,例如:VARIANT "s" STRING "foo";
|
|
141
|
+
match = re.match(r'^VARIANT\s+"([^"]+)"\s*(.*)$', value_str, re.DOTALL)
|
|
142
|
+
variant_payload = None
|
|
143
|
+
if match:
|
|
144
|
+
explicit_sig = match.group(1).strip()
|
|
145
|
+
variant_payload = match.group(2).strip()
|
|
146
|
+
# 处理特殊格式:VARIANT "v" <userdata>; 表示无法序列化的 Variant
|
|
147
|
+
if variant_payload.startswith('<userdata>'):
|
|
148
|
+
# 对于 <userdata>,创建一个 Variant("v", Variant("v", None))
|
|
149
|
+
# 或者创建一个 Variant("v", Variant("s", "<userdata>"))
|
|
150
|
+
# 这里我们使用一个字符串作为占位符
|
|
151
|
+
inner_variant = Variant("s", "<userdata>")
|
|
152
|
+
return Variant("v", inner_variant)
|
|
153
|
+
# 如果 payload 为空或仍然是 { ... } 包裹的内容,则回退到统一的内容提取逻辑
|
|
154
|
+
if (not variant_payload
|
|
155
|
+
or variant_payload.startswith('{')
|
|
156
|
+
or variant_payload.startswith('VARIANT ')):
|
|
157
|
+
variant_payload = BusCtlTypeConverter.extract_content_from_type(value_str, "VARIANT")
|
|
158
|
+
else:
|
|
159
|
+
variant_payload = BusCtlTypeConverter.extract_content_from_type(value_str, "VARIANT")
|
|
160
|
+
# 检查提取的内容是否仍然是 <userdata>
|
|
161
|
+
if variant_payload and variant_payload.strip().startswith('<userdata>'):
|
|
162
|
+
inner_variant = Variant("s", "<userdata>")
|
|
163
|
+
return Variant(explicit_sig or "v", inner_variant)
|
|
164
|
+
|
|
165
|
+
converted_value = BusCtlTypeConverter.dbus_string_to_type(variant_payload)
|
|
166
|
+
signature = explicit_sig or DBusSignature.get_dbus_signature(converted_value)
|
|
167
|
+
return Variant(signature, converted_value)
|
|
168
|
+
except Exception as e:
|
|
169
|
+
logger.error(f"变体解析错误: {e}")
|
|
170
|
+
pass
|
|
171
|
+
# 默认返回原始字符串
|
|
172
|
+
return value_str
|
|
173
|
+
|
|
174
|
+
@staticmethod
|
|
175
|
+
def extract_content_from_type(value_str, type_name):
|
|
176
|
+
"""
|
|
177
|
+
公共函数:提取指定类型数据结构中的内容部分,去除类型名称和可选的类型签名。
|
|
178
|
+
支持两种格式:
|
|
179
|
+
1. 带大括号的格式:'TYPE_NAME "signature" { content }'
|
|
180
|
+
2. VARIANT特殊格式:'VARIANT "signature" content'
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
value_str: 包含数据结构内容的字符串
|
|
184
|
+
type_name: 数据结构类型名称,如'STRUCT'、'ARRAY'、'DICT_ENTRY'、'VARIANT'等
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
str: 提取出的内容字符串,如果无法提取则返回None
|
|
188
|
+
"""
|
|
189
|
+
import re
|
|
190
|
+
# 先尝试匹配带大括号的标准格式
|
|
191
|
+
pattern = r'{}\s*(?:"[^"]*")?\s*\{{'.format(type_name)
|
|
192
|
+
match = re.search(pattern, value_str)
|
|
193
|
+
if match:
|
|
194
|
+
# 处理带大括号的格式,使用括号平衡算法找到匹配的右花括号
|
|
195
|
+
# match.end() 是匹配结束的位置(即 { 之后),所以 match.end() - 1 是 { 的位置
|
|
196
|
+
start_brace = match.end() - 1
|
|
197
|
+
if start_brace != -1:
|
|
198
|
+
# 使用括号平衡算法找到匹配的右花括号
|
|
199
|
+
brace_count = 1
|
|
200
|
+
end_brace = start_brace + 1
|
|
201
|
+
in_quotes = False
|
|
202
|
+
quote_char = None
|
|
203
|
+
while end_brace < len(value_str) and brace_count > 0:
|
|
204
|
+
char = value_str[end_brace]
|
|
205
|
+
# 处理引号
|
|
206
|
+
if char in ['"', "'"] and (end_brace == 0 or value_str[end_brace - 1] != '\\'):
|
|
207
|
+
if in_quotes and char == quote_char:
|
|
208
|
+
in_quotes = False
|
|
209
|
+
quote_char = None
|
|
210
|
+
elif not in_quotes:
|
|
211
|
+
in_quotes = True
|
|
212
|
+
quote_char = char
|
|
213
|
+
elif not in_quotes:
|
|
214
|
+
if char == '{':
|
|
215
|
+
brace_count += 1
|
|
216
|
+
elif char == '}':
|
|
217
|
+
brace_count -= 1
|
|
218
|
+
end_brace += 1
|
|
219
|
+
if brace_count == 0:
|
|
220
|
+
# 找到了匹配的右花括号,提取内容(不包括花括号本身)
|
|
221
|
+
extracted = value_str[start_brace + 1:end_brace - 1].strip()
|
|
222
|
+
return extracted
|
|
223
|
+
else:
|
|
224
|
+
logger.warning(f"extract_content_from_type: 括号不平衡,brace_count={ \
|
|
225
|
+
brace_count}, end_brace={end_brace}, value_str长度={len(value_str)}")
|
|
226
|
+
elif type_name == "VARIANT":
|
|
227
|
+
# 特殊处理VARIANT类型,没有大括号的格式
|
|
228
|
+
# 匹配VARIANT后跟可选的签名,然后提取剩余内容
|
|
229
|
+
variant_pattern = r'VARIANT\s*(?:"[^"]*")?\s*(.*?);?$'
|
|
230
|
+
variant_match = re.search(variant_pattern, value_str)
|
|
231
|
+
if variant_match:
|
|
232
|
+
return variant_match.group(1).strip()
|
|
233
|
+
else:
|
|
234
|
+
logger.warning(f"extract_content_from_type: 正则匹配失败,pattern={pattern}, value_str前200字符={value_str[:200]}")
|
|
235
|
+
return None
|
|
236
|
+
|
|
237
|
+
@staticmethod
|
|
238
|
+
def compare_dbus_objects(obj1, obj2):
|
|
239
|
+
"""
|
|
240
|
+
比较两个 D-Bus 对象是否相等
|
|
241
|
+
|
|
242
|
+
在dbus-next中,我们比较Python原生类型
|
|
243
|
+
"""
|
|
244
|
+
# 对于变体类型特殊处理
|
|
245
|
+
if isinstance(obj1, Variant) and isinstance(obj2, Variant):
|
|
246
|
+
return BusCtlTypeConverter.compare_dbus_objects(obj1.value, obj2.value)
|
|
247
|
+
elif isinstance(obj1, Variant):
|
|
248
|
+
return BusCtlTypeConverter.compare_dbus_objects(obj1.value, obj2)
|
|
249
|
+
elif isinstance(obj2, Variant):
|
|
250
|
+
return BusCtlTypeConverter.compare_dbus_objects(obj1, obj2.value)
|
|
251
|
+
|
|
252
|
+
# 检查类型是否匹配
|
|
253
|
+
if type(obj1) != type(obj2):
|
|
254
|
+
# 处理数值类型的兼容性(例如int和float)
|
|
255
|
+
if isinstance(obj1, (int, float)) and isinstance(obj2, (int, float)):
|
|
256
|
+
return abs(obj1 - obj2) < 1e-10 # 浮点数比较需要容差
|
|
257
|
+
return False
|
|
258
|
+
# 对于基本类型,直接比较
|
|
259
|
+
if isinstance(obj1, (int, float, str, bool)):
|
|
260
|
+
if isinstance(obj1, float) and isinstance(obj2, float):
|
|
261
|
+
return abs(obj1 - obj2) < 1e-10 # 浮点数比较需要容差
|
|
262
|
+
return obj1 == obj2
|
|
263
|
+
# 对于列表/数组类型,递归比较每个元素
|
|
264
|
+
elif isinstance(obj1, list):
|
|
265
|
+
if len(obj1) != len(obj2):
|
|
266
|
+
return False
|
|
267
|
+
|
|
268
|
+
# 空列表直接返回 True
|
|
269
|
+
if len(obj1) == 0:
|
|
270
|
+
return True
|
|
271
|
+
|
|
272
|
+
# 特殊处理:如果两个列表都只包含字符串,进行集合比较(忽略顺序)
|
|
273
|
+
# 这对于字符串数组(as类型)很有用
|
|
274
|
+
if (all(isinstance(x, str) for x in obj1) and
|
|
275
|
+
all(isinstance(x, str) for x in obj2)):
|
|
276
|
+
# 对于字符串数组,使用集合比较
|
|
277
|
+
return set(obj1) == set(obj2)
|
|
278
|
+
|
|
279
|
+
# 对于其他类型的列表,逐个比较元素(保持顺序)
|
|
280
|
+
for i in range(len(obj1)):
|
|
281
|
+
if not BusCtlTypeConverter.compare_dbus_objects(obj1[i], obj2[i]):
|
|
282
|
+
return False
|
|
283
|
+
return True
|
|
284
|
+
# 对于字典类型,递归比较每个键值对
|
|
285
|
+
elif isinstance(obj1, dict):
|
|
286
|
+
if len(obj1) != len(obj2):
|
|
287
|
+
return False
|
|
288
|
+
for key in obj1:
|
|
289
|
+
if key not in obj2:
|
|
290
|
+
return False
|
|
291
|
+
if not BusCtlTypeConverter.compare_dbus_objects(obj1[key], obj2[key]):
|
|
292
|
+
return False
|
|
293
|
+
return True
|
|
294
|
+
# 对于元组/结构体类型,递归比较每个元素
|
|
295
|
+
elif isinstance(obj1, tuple):
|
|
296
|
+
if len(obj1) != len(obj2):
|
|
297
|
+
return False
|
|
298
|
+
for i in range(len(obj1)):
|
|
299
|
+
if not BusCtlTypeConverter.compare_dbus_objects(obj1[i], obj2[i]):
|
|
300
|
+
return False
|
|
301
|
+
return True
|
|
302
|
+
# 对于其他类型,尝试字符串比较
|
|
303
|
+
else:
|
|
304
|
+
return str(obj1) == str(obj2)
|
|
305
|
+
|
|
306
|
+
@staticmethod
|
|
307
|
+
def _get_value(value_str):
|
|
308
|
+
"""
|
|
309
|
+
从字符串中提取值,通过空格分割,返回分割后的第二个部分,分割后去掉首尾空格
|
|
310
|
+
|
|
311
|
+
Args:
|
|
312
|
+
value_str: 包含值的字符串,格式:类型 值 比如'string "Requestor"'
|
|
313
|
+
|
|
314
|
+
Returns:
|
|
315
|
+
提取出的值,字符串类型
|
|
316
|
+
"""
|
|
317
|
+
# 处理带引号的字符串
|
|
318
|
+
parts = value_str.split(' ', 1)
|
|
319
|
+
if len(parts) < 2:
|
|
320
|
+
# 如果没有空格分隔,返回原字符串
|
|
321
|
+
return value_str.strip()
|
|
322
|
+
return parts[1].strip() # 使用空格分割并去掉首尾空格
|
|
323
|
+
|
|
324
|
+
@staticmethod
|
|
325
|
+
def _convert_basic_type(type_value_str):
|
|
326
|
+
"""
|
|
327
|
+
转换D-Bus基本类型
|
|
328
|
+
|
|
329
|
+
Args:
|
|
330
|
+
type_value_str: 要转换的基本类型字符串,格式:类型 值,比如STRING "Slot";
|
|
331
|
+
|
|
332
|
+
Returns:
|
|
333
|
+
转换后的Python原生类型,如果无法转换则返回None
|
|
334
|
+
"""
|
|
335
|
+
# 先处理末尾分号
|
|
336
|
+
type_value_str = type_value_str.strip()
|
|
337
|
+
if type_value_str.endswith(';'):
|
|
338
|
+
type_value_str = type_value_str[:-1].strip()
|
|
339
|
+
value_str = BusCtlTypeConverter._get_value(type_value_str)
|
|
340
|
+
# 1. 字节类型: "BYTE 1" -> 1 (Python int)
|
|
341
|
+
if type_value_str.upper().startswith('BYTE '):
|
|
342
|
+
try:
|
|
343
|
+
# BusCtl中字节值可能是十六进制或十进制
|
|
344
|
+
if value_str.startswith('0x'):
|
|
345
|
+
return int(value_str, 16)
|
|
346
|
+
return int(value_str)
|
|
347
|
+
except (IndexError, ValueError):
|
|
348
|
+
pass
|
|
349
|
+
# 2. 字符串类型处理
|
|
350
|
+
if type_value_str.upper().startswith('STRING '):
|
|
351
|
+
try:
|
|
352
|
+
string_value = value_str
|
|
353
|
+
flag_dpuble = string_value.startswith('"') and string_value.endswith('"')
|
|
354
|
+
flag_single = string_value.startswith("'") and string_value.endswith("'")
|
|
355
|
+
# 处理带引号的情况
|
|
356
|
+
if flag_dpuble or flag_single:
|
|
357
|
+
string_value = string_value[1:-1]
|
|
358
|
+
# 处理转义字符
|
|
359
|
+
string_value = string_value.replace(r'\\n', '\n').replace(r'\\t', '\t').replace(r'\\\\', '\\')
|
|
360
|
+
return string_value
|
|
361
|
+
except (IndexError, ValueError):
|
|
362
|
+
pass
|
|
363
|
+
# 3. 布尔类型: "BOOLEAN true" -> True
|
|
364
|
+
if type_value_str.upper().startswith('BOOLEAN '):
|
|
365
|
+
bool_value = value_str.lower()
|
|
366
|
+
return bool_value == 'true'
|
|
367
|
+
elif type_value_str.lower() == 'true':
|
|
368
|
+
return True
|
|
369
|
+
elif type_value_str.lower() == 'false':
|
|
370
|
+
return False
|
|
371
|
+
# 4. 整数类型
|
|
372
|
+
# 4.1 有符号16位整数
|
|
373
|
+
if type_value_str.upper().startswith('INT16 '):
|
|
374
|
+
try:
|
|
375
|
+
# 处理十六进制表示
|
|
376
|
+
if value_str.startswith('0x'):
|
|
377
|
+
return int(value_str, 16)
|
|
378
|
+
return int(value_str)
|
|
379
|
+
except (IndexError, ValueError):
|
|
380
|
+
pass
|
|
381
|
+
# 4.2 无符号16位整数
|
|
382
|
+
elif type_value_str.upper().startswith('UINT16 '):
|
|
383
|
+
try:
|
|
384
|
+
# 处理十六进制表示
|
|
385
|
+
if value_str.startswith('0x'):
|
|
386
|
+
return int(value_str, 16)
|
|
387
|
+
return int(value_str)
|
|
388
|
+
except (IndexError, ValueError):
|
|
389
|
+
pass
|
|
390
|
+
# 4.3 有符号32位整数
|
|
391
|
+
elif type_value_str.upper().startswith('INT32 '):
|
|
392
|
+
try:
|
|
393
|
+
# 处理十六进制表示
|
|
394
|
+
if value_str.startswith('0x'):
|
|
395
|
+
return int(value_str, 16)
|
|
396
|
+
return int(value_str)
|
|
397
|
+
except (IndexError, ValueError):
|
|
398
|
+
pass
|
|
399
|
+
# 4.4 无符号32位整数
|
|
400
|
+
elif type_value_str.upper().startswith('UINT32 '):
|
|
401
|
+
try:
|
|
402
|
+
# 处理十六进制表示
|
|
403
|
+
if value_str.startswith('0x'):
|
|
404
|
+
return int(value_str, 16)
|
|
405
|
+
return int(value_str)
|
|
406
|
+
except (IndexError, ValueError):
|
|
407
|
+
pass
|
|
408
|
+
# 4.5 有符号64位整数
|
|
409
|
+
elif type_value_str.upper().startswith('INT64 '):
|
|
410
|
+
try:
|
|
411
|
+
# 处理十六进制表示
|
|
412
|
+
if value_str.startswith('0x'):
|
|
413
|
+
return int(value_str, 16)
|
|
414
|
+
return int(value_str)
|
|
415
|
+
except (IndexError, ValueError):
|
|
416
|
+
pass
|
|
417
|
+
# 4.6 无符号64位整数
|
|
418
|
+
elif type_value_str.upper().startswith('UINT64 '):
|
|
419
|
+
try:
|
|
420
|
+
# 处理十六进制表示
|
|
421
|
+
if value_str.startswith('0x'):
|
|
422
|
+
return int(value_str, 16)
|
|
423
|
+
return int(value_str)
|
|
424
|
+
except (IndexError, ValueError):
|
|
425
|
+
pass
|
|
426
|
+
# 5. 浮点数类型
|
|
427
|
+
# 5.1 double类型
|
|
428
|
+
if type_value_str.upper().startswith('DOUBLE '):
|
|
429
|
+
try:
|
|
430
|
+
return float(value_str)
|
|
431
|
+
except (IndexError, ValueError):
|
|
432
|
+
pass
|
|
433
|
+
# 5.2 float类型
|
|
434
|
+
elif type_value_str.upper().startswith('FLOAT '):
|
|
435
|
+
try:
|
|
436
|
+
return float(value_str)
|
|
437
|
+
except (IndexError, ValueError):
|
|
438
|
+
pass
|
|
439
|
+
# 无法识别为基本类型
|
|
440
|
+
return None
|
|
441
|
+
|
|
442
|
+
@staticmethod
|
|
443
|
+
def _convert_struct_with_no_quote(value_str):
|
|
444
|
+
"""
|
|
445
|
+
转换不带STRUCT关键字的结构体字符串为dbus-next可识别的格式,只是有圆括号
|
|
446
|
+
例如: "(ss) {STRING "A";STRING "B";}" -> ["A", "B"]
|
|
447
|
+
支持嵌套结构,如 "(ssayay) { STRING "A"; ARRAY "y" { BYTE 1; }; }"
|
|
448
|
+
"""
|
|
449
|
+
import re
|
|
450
|
+
# 找到第一个花括号的位置
|
|
451
|
+
brace_start = value_str.find('{')
|
|
452
|
+
if brace_start == -1:
|
|
453
|
+
return value_str
|
|
454
|
+
|
|
455
|
+
# 使用括号平衡算法提取完整的花括号内容
|
|
456
|
+
brace_balance = 0
|
|
457
|
+
in_quotes = False
|
|
458
|
+
quote_char = None
|
|
459
|
+
brace_end = -1
|
|
460
|
+
for i in range(brace_start, len(value_str)):
|
|
461
|
+
char = value_str[i]
|
|
462
|
+
# 处理引号状态
|
|
463
|
+
if char in ['"', "'"] and (i == 0 or value_str[i-1] != '\\'):
|
|
464
|
+
if in_quotes and char == quote_char:
|
|
465
|
+
in_quotes = False
|
|
466
|
+
quote_char = None
|
|
467
|
+
elif not in_quotes:
|
|
468
|
+
in_quotes = True
|
|
469
|
+
quote_char = char
|
|
470
|
+
continue
|
|
471
|
+
# 只在非引号内处理括号
|
|
472
|
+
if not in_quotes:
|
|
473
|
+
if char == '{':
|
|
474
|
+
brace_balance += 1
|
|
475
|
+
elif char == '}':
|
|
476
|
+
brace_balance -= 1
|
|
477
|
+
if brace_balance == 0:
|
|
478
|
+
brace_end = i
|
|
479
|
+
break
|
|
480
|
+
if brace_end == -1:
|
|
481
|
+
return value_str
|
|
482
|
+
# 提取花括号内的内容(不包括花括号本身)
|
|
483
|
+
inner_content = value_str[brace_start + 1:brace_end].strip()
|
|
484
|
+
if not inner_content:
|
|
485
|
+
return []
|
|
486
|
+
# 使用智能分割函数分割字段(考虑嵌套结构和引号)
|
|
487
|
+
fields = BusCtlTypeConverter._split_elements_by_semicolon(inner_content)
|
|
488
|
+
# 转换每个字段
|
|
489
|
+
struct_fields = []
|
|
490
|
+
for field in fields:
|
|
491
|
+
field = field.strip()
|
|
492
|
+
if field:
|
|
493
|
+
converted_field = BusCtlTypeConverter.dbus_string_to_type(field)
|
|
494
|
+
struct_fields.append(converted_field)
|
|
495
|
+
return struct_fields
|
|
496
|
+
|
|
497
|
+
@staticmethod
|
|
498
|
+
def _split_elements_by_semicolon(content):
|
|
499
|
+
"""
|
|
500
|
+
智能分割带分号的内容,考虑括号嵌套和引号状态
|
|
501
|
+
|
|
502
|
+
Args:
|
|
503
|
+
content: 需要分割的内容字符串
|
|
504
|
+
|
|
505
|
+
Returns:
|
|
506
|
+
分割后的元素列表
|
|
507
|
+
"""
|
|
508
|
+
if not content:
|
|
509
|
+
return []
|
|
510
|
+
|
|
511
|
+
elements = []
|
|
512
|
+
current_element = []
|
|
513
|
+
brace_count = 0 # 花括号计数 {}
|
|
514
|
+
paren_count = 0 # 圆括号计数 ()
|
|
515
|
+
bracket_count = 0 # 方括号计数 []
|
|
516
|
+
in_quotes = False
|
|
517
|
+
for i, char in enumerate(content):
|
|
518
|
+
# 处理引号状态
|
|
519
|
+
if char == '"' and (i == 0 or content[i-1] != '\\'):
|
|
520
|
+
in_quotes = not in_quotes
|
|
521
|
+
# 只在非引号内处理括号计数
|
|
522
|
+
if not in_quotes:
|
|
523
|
+
if char == '{':
|
|
524
|
+
brace_count += 1
|
|
525
|
+
elif char == '}':
|
|
526
|
+
brace_count -= 1
|
|
527
|
+
elif char == '(':
|
|
528
|
+
paren_count += 1
|
|
529
|
+
elif char == ')':
|
|
530
|
+
paren_count -= 1
|
|
531
|
+
elif char == '[':
|
|
532
|
+
bracket_count += 1
|
|
533
|
+
elif char == ']':
|
|
534
|
+
bracket_count -= 1
|
|
535
|
+
# 当遇到分号且所有括号已匹配且不在引号内时,分割元素
|
|
536
|
+
if char == ';' and brace_count == 0 and paren_count == 0 and bracket_count == 0 and not in_quotes:
|
|
537
|
+
# 不包含分号本身
|
|
538
|
+
element = ''.join(current_element).strip()
|
|
539
|
+
if element:
|
|
540
|
+
elements.append(element)
|
|
541
|
+
current_element = []
|
|
542
|
+
else:
|
|
543
|
+
# 不是分隔符时才添加字符
|
|
544
|
+
current_element.append(char)
|
|
545
|
+
# 处理最后一个元素
|
|
546
|
+
if current_element:
|
|
547
|
+
element = ''.join(current_element).strip()
|
|
548
|
+
if element:
|
|
549
|
+
elements.append(element)
|
|
550
|
+
return elements
|
|
551
|
+
|
|
552
|
+
# 然后修复 _handle_struct_array 方法
|
|
553
|
+
@staticmethod
|
|
554
|
+
def _handle_struct_array(value_str):
|
|
555
|
+
"""
|
|
556
|
+
value_str: 包含结构体数组内容的字符串,格式为 'ARRAY "(ss)" {...}'
|
|
557
|
+
示例字符串:"ARRAY \"(ss)\" { (ss) { STRING \"a\"; STRING \"b\"; }; (ss) { STRING \"c\"; STRING \"d\"; }; };"
|
|
558
|
+
return_type: 转换后的dbus-next结构体数组
|
|
559
|
+
"""
|
|
560
|
+
try:
|
|
561
|
+
# 提取ARRAY大括号内的内容
|
|
562
|
+
content = BusCtlTypeConverter.extract_content_from_type(value_str, 'ARRAY')
|
|
563
|
+
if not content:
|
|
564
|
+
logger.warning(f"无法从结构体数组中提取内容,输入前500字符: {value_str[:500]}")
|
|
565
|
+
return []
|
|
566
|
+
|
|
567
|
+
# 专门处理结构体数组:每个元素格式是 (signature) { ... };
|
|
568
|
+
# 使用括号平衡算法找到每个结构体元素的边界
|
|
569
|
+
elements = []
|
|
570
|
+
i = 0
|
|
571
|
+
struct_count = 0
|
|
572
|
+
while i < len(content):
|
|
573
|
+
# 跳过空白字符
|
|
574
|
+
while i < len(content) and content[i].isspace():
|
|
575
|
+
i += 1
|
|
576
|
+
if i >= len(content):
|
|
577
|
+
break
|
|
578
|
+
|
|
579
|
+
# 查找结构体开始:应该是 (signature) {
|
|
580
|
+
if content[i] == '(':
|
|
581
|
+
# 找到匹配的右括号
|
|
582
|
+
paren_end = i + 1
|
|
583
|
+
paren_count = 1
|
|
584
|
+
while paren_end < len(content) and paren_count > 0:
|
|
585
|
+
if content[paren_end] == '(':
|
|
586
|
+
paren_count += 1
|
|
587
|
+
elif content[paren_end] == ')':
|
|
588
|
+
paren_count -= 1
|
|
589
|
+
paren_end += 1
|
|
590
|
+
|
|
591
|
+
# 跳过空白字符,查找左花括号
|
|
592
|
+
brace_start = paren_end
|
|
593
|
+
while brace_start < len(content) and content[brace_start].isspace():
|
|
594
|
+
brace_start += 1
|
|
595
|
+
if brace_start < len(content) and content[brace_start] == '{':
|
|
596
|
+
# 找到匹配的右花括号
|
|
597
|
+
brace_end = brace_start + 1
|
|
598
|
+
brace_count = 1
|
|
599
|
+
in_quotes = False
|
|
600
|
+
quote_char = None
|
|
601
|
+
while brace_end < len(content) and brace_count > 0:
|
|
602
|
+
char = content[brace_end]
|
|
603
|
+
|
|
604
|
+
# 处理引号
|
|
605
|
+
if char in ['"', "'"] and (brace_end == 0 or content[brace_end-1] != '\\'):
|
|
606
|
+
if in_quotes and char == quote_char:
|
|
607
|
+
in_quotes = False
|
|
608
|
+
quote_char = None
|
|
609
|
+
elif not in_quotes:
|
|
610
|
+
in_quotes = True
|
|
611
|
+
quote_char = char
|
|
612
|
+
elif not in_quotes:
|
|
613
|
+
if char == '{':
|
|
614
|
+
brace_count += 1
|
|
615
|
+
elif char == '}':
|
|
616
|
+
brace_count -= 1
|
|
617
|
+
|
|
618
|
+
brace_end += 1
|
|
619
|
+
|
|
620
|
+
# 如果括号平衡没有归零,说明没有找到匹配的右花括号
|
|
621
|
+
if brace_count > 0:
|
|
622
|
+
logger.warning(f"结构体数组元素括号不平衡: brace_count={brace_count}, 位置={i}, brace_end={brace_end}")
|
|
623
|
+
break
|
|
624
|
+
|
|
625
|
+
# 查找分号:可能在 brace_end-1 之后(即 }; 的情况),也可能在跳过空白后
|
|
626
|
+
# 首先检查 brace_end-1 之后是否有分号(}; 的情况)
|
|
627
|
+
semicolon_pos = -1
|
|
628
|
+
if brace_end > 0 and brace_end <= len(content):
|
|
629
|
+
# 检查 brace_end-1 位置之后是否有分号
|
|
630
|
+
check_pos = brace_end - 1 # 这是 } 的位置
|
|
631
|
+
# 跳过 } 本身,检查之后是否有分号
|
|
632
|
+
next_pos = check_pos + 1
|
|
633
|
+
while next_pos < len(content) and content[next_pos].isspace():
|
|
634
|
+
next_pos += 1
|
|
635
|
+
if next_pos < len(content) and content[next_pos] == ';':
|
|
636
|
+
semicolon_pos = next_pos
|
|
637
|
+
|
|
638
|
+
# 如果没找到,尝试从 brace_end 开始查找
|
|
639
|
+
if semicolon_pos == -1:
|
|
640
|
+
semicolon_pos = brace_end
|
|
641
|
+
while semicolon_pos < len(content) and content[semicolon_pos].isspace():
|
|
642
|
+
semicolon_pos += 1
|
|
643
|
+
if semicolon_pos < len(content) and content[semicolon_pos] == ';':
|
|
644
|
+
pass # 找到了
|
|
645
|
+
else:
|
|
646
|
+
semicolon_pos = -1
|
|
647
|
+
|
|
648
|
+
if semicolon_pos >= 0:
|
|
649
|
+
# 提取完整的结构体元素(包括分号)
|
|
650
|
+
struct_element = content[i:semicolon_pos + 1].strip()
|
|
651
|
+
if struct_element:
|
|
652
|
+
elements.append(struct_element)
|
|
653
|
+
struct_count += 1
|
|
654
|
+
i = semicolon_pos + 1
|
|
655
|
+
else:
|
|
656
|
+
# 没有找到分号,可能是最后一个元素,尝试提取结构体元素
|
|
657
|
+
if brace_count == 0:
|
|
658
|
+
struct_element = content[i:brace_end].strip()
|
|
659
|
+
if struct_element:
|
|
660
|
+
elements.append(struct_element)
|
|
661
|
+
struct_count += 1
|
|
662
|
+
i = brace_end # 继续处理,不要 break
|
|
663
|
+
else:
|
|
664
|
+
break
|
|
665
|
+
else:
|
|
666
|
+
break
|
|
667
|
+
else:
|
|
668
|
+
# 没有找到左花括号,跳过这个字符
|
|
669
|
+
i += 1
|
|
670
|
+
else:
|
|
671
|
+
# 不是结构体开始,跳过这个字符
|
|
672
|
+
i += 1
|
|
673
|
+
|
|
674
|
+
if len(elements) == 0:
|
|
675
|
+
logger.warning(f"结构体数组分割结果为空,content长度={len(content)}, 前500字符={content[:500]}")
|
|
676
|
+
|
|
677
|
+
# 转换每个结构体元素
|
|
678
|
+
converted_elements = []
|
|
679
|
+
for idx, elem in enumerate(elements):
|
|
680
|
+
elem = elem.strip()
|
|
681
|
+
# 确保elem是完整的结构体格式
|
|
682
|
+
if elem and '(' in elem and '{' in elem:
|
|
683
|
+
# 递归转换每个结构体
|
|
684
|
+
try:
|
|
685
|
+
converted_value = BusCtlTypeConverter.dbus_string_to_type(elem)
|
|
686
|
+
converted_elements.append(converted_value)
|
|
687
|
+
except Exception as e:
|
|
688
|
+
logger.error(f"转换第 {idx + 1} 个结构体元素时出错: {e}", exc_info=True)
|
|
689
|
+
# 对于单个元素的错误,跳过该元素继续处理
|
|
690
|
+
|
|
691
|
+
# 确保始终返回列表类型
|
|
692
|
+
return converted_elements
|
|
693
|
+
|
|
694
|
+
except Exception as e:
|
|
695
|
+
logger.error(f"处理结构体数组时出错: {e}", exc_info=True)
|
|
696
|
+
|
|
697
|
+
# 错误情况下也返回空列表而不是原始字符串
|
|
698
|
+
return []
|
|
699
|
+
|
|
700
|
+
@staticmethod
|
|
701
|
+
def _trim_trailing_semicolon(value_str):
|
|
702
|
+
"""
|
|
703
|
+
移除字符串末尾的分号(如果存在)
|
|
704
|
+
|
|
705
|
+
Args:
|
|
706
|
+
value_str: 输入字符串
|
|
707
|
+
|
|
708
|
+
Returns:
|
|
709
|
+
移除分号后的字符串
|
|
710
|
+
"""
|
|
711
|
+
if value_str.endswith(';'):
|
|
712
|
+
return value_str[:-1].strip()
|
|
713
|
+
return value_str
|
|
714
|
+
|
|
715
|
+
@staticmethod
|
|
716
|
+
def _convert_dictionary_type(value_str):
|
|
717
|
+
"""
|
|
718
|
+
转换字典类型
|
|
719
|
+
|
|
720
|
+
Args:
|
|
721
|
+
value_str: 字典类型的字符串表示
|
|
722
|
+
|
|
723
|
+
Returns:
|
|
724
|
+
转换后的Python字典对象,如果无法转换则返回None
|
|
725
|
+
"""
|
|
726
|
+
try:
|
|
727
|
+
# 直接使用括号平衡算法提取DICT_ENTRY内容,因为正则表达式无法处理嵌套结构
|
|
728
|
+
dict_entries = BusCtlTypeConverter._extract_dict_entries_with_bracket_balance(value_str)
|
|
729
|
+
|
|
730
|
+
result_dict = {}
|
|
731
|
+
for entry in dict_entries:
|
|
732
|
+
# DICT_ENTRY格式: DICT_ENTRY "signature" { key; value; }
|
|
733
|
+
# 需要跳过 DICT_ENTRY "signature" { 部分,提取key和value
|
|
734
|
+
|
|
735
|
+
# 找到第一个不在引号内的大括号,这是内容开始的位置
|
|
736
|
+
content_start = -1
|
|
737
|
+
in_quotes = False
|
|
738
|
+
quote_char = None
|
|
739
|
+
|
|
740
|
+
for i, char in enumerate(entry):
|
|
741
|
+
# 处理引号状态
|
|
742
|
+
if char in ['"', "'"] and (i == 0 or entry[i-1] != '\\'):
|
|
743
|
+
if in_quotes and char == quote_char:
|
|
744
|
+
in_quotes = False
|
|
745
|
+
quote_char = None
|
|
746
|
+
elif not in_quotes:
|
|
747
|
+
in_quotes = True
|
|
748
|
+
quote_char = char
|
|
749
|
+
continue
|
|
750
|
+
|
|
751
|
+
# 只在非引号内查找大括号
|
|
752
|
+
if not in_quotes and char == '{':
|
|
753
|
+
content_start = i + 1
|
|
754
|
+
break
|
|
755
|
+
|
|
756
|
+
if content_start == -1:
|
|
757
|
+
continue
|
|
758
|
+
|
|
759
|
+
# 提取大括号内的内容(去掉最后的};或})
|
|
760
|
+
content_end = entry.rfind('}')
|
|
761
|
+
if content_end == -1:
|
|
762
|
+
continue
|
|
763
|
+
# 提取内容,去掉最后的};或}
|
|
764
|
+
inner_content = entry[content_start:content_end].strip()
|
|
765
|
+
# 使用智能分割函数分割key和value
|
|
766
|
+
# 在字典中,key和value之间用分号分隔,但要注意嵌套结构
|
|
767
|
+
brace_count = 0
|
|
768
|
+
paren_count = 0
|
|
769
|
+
bracket_count = 0
|
|
770
|
+
in_quotes = False
|
|
771
|
+
semicolon_pos = -1
|
|
772
|
+
|
|
773
|
+
# 查找第一个不在括号内且不在引号内的分号作为key-value分隔符
|
|
774
|
+
for i, char in enumerate(inner_content):
|
|
775
|
+
# 处理引号状态
|
|
776
|
+
if char in ['"', "'"] and (i == 0 or inner_content[i-1] != '\\'):
|
|
777
|
+
if in_quotes and char == quote_char:
|
|
778
|
+
in_quotes = False
|
|
779
|
+
quote_char = None
|
|
780
|
+
elif not in_quotes:
|
|
781
|
+
in_quotes = True
|
|
782
|
+
quote_char = char
|
|
783
|
+
continue
|
|
784
|
+
|
|
785
|
+
if not in_quotes:
|
|
786
|
+
if char == '{':
|
|
787
|
+
brace_count += 1
|
|
788
|
+
elif char == '}':
|
|
789
|
+
brace_count -= 1
|
|
790
|
+
elif char == '(':
|
|
791
|
+
paren_count += 1
|
|
792
|
+
elif char == ')':
|
|
793
|
+
paren_count -= 1
|
|
794
|
+
elif char == '[':
|
|
795
|
+
bracket_count += 1
|
|
796
|
+
elif char == ']':
|
|
797
|
+
bracket_count -= 1
|
|
798
|
+
|
|
799
|
+
# 找到有效的分隔符(第一个不在嵌套结构内的分号)
|
|
800
|
+
if char == ';' and brace_count == 0 and paren_count == 0 and bracket_count == 0 and not in_quotes:
|
|
801
|
+
semicolon_pos = i
|
|
802
|
+
break # 找到第一个有效分号就停止
|
|
803
|
+
|
|
804
|
+
if semicolon_pos == -1:
|
|
805
|
+
# 如果没有找到分号,尝试使用_split_elements_by_semicolon
|
|
806
|
+
elements = BusCtlTypeConverter._split_elements_by_semicolon(inner_content)
|
|
807
|
+
if len(elements) >= 2:
|
|
808
|
+
key_line = elements[0].strip()
|
|
809
|
+
value_content = '; '.join(elements[1:]).strip()
|
|
810
|
+
else:
|
|
811
|
+
continue
|
|
812
|
+
else:
|
|
813
|
+
# 找到了有效的分号分隔符
|
|
814
|
+
key_line = inner_content[:semicolon_pos].strip()
|
|
815
|
+
value_content = inner_content[semicolon_pos + 1:].strip()
|
|
816
|
+
|
|
817
|
+
# 处理key和value
|
|
818
|
+
converted_key = BusCtlTypeConverter.dbus_string_to_type(key_line)
|
|
819
|
+
converted_value = BusCtlTypeConverter.dbus_string_to_type(value_content)
|
|
820
|
+
|
|
821
|
+
# 确保值不是原始字符串,除非它确实是字符串类型
|
|
822
|
+
if isinstance(converted_value, str):
|
|
823
|
+
# 检查是否是嵌套的数组或字典格式
|
|
824
|
+
if converted_value.strip().startswith('ARRAY ') or converted_value.strip().startswith('DICT_ENTRY '):
|
|
825
|
+
# 尝试再次转换
|
|
826
|
+
converted_value = BusCtlTypeConverter.dbus_string_to_type(converted_value)
|
|
827
|
+
|
|
828
|
+
# 如果转换后的值是空列表,但原始value_content是字典数组格式,应该转换为空字典
|
|
829
|
+
if isinstance(converted_value, list) and len(converted_value) == 0:
|
|
830
|
+
if value_content.strip().startswith('ARRAY "{'):
|
|
831
|
+
# 这是空字典数组,应该返回空字典
|
|
832
|
+
converted_value = {}
|
|
833
|
+
|
|
834
|
+
# 确保key不是None
|
|
835
|
+
if converted_key is None:
|
|
836
|
+
logger.warning(f"无法转换字典key: {key_line}")
|
|
837
|
+
continue
|
|
838
|
+
result_dict[converted_key] = converted_value
|
|
839
|
+
|
|
840
|
+
return result_dict if result_dict else None
|
|
841
|
+
except Exception as e:
|
|
842
|
+
logger.error(f"busctl字典转换错误: {e}", exc_info=True)
|
|
843
|
+
return None
|
|
844
|
+
|
|
845
|
+
@staticmethod
|
|
846
|
+
def _extract_dict_entries_with_bracket_balance(value_str):
|
|
847
|
+
"""
|
|
848
|
+
提取所有的DICT_ENTRY条目,确保提取完整条目
|
|
849
|
+
使用括号平衡算法处理嵌套结构,正确处理引号内的内容
|
|
850
|
+
"""
|
|
851
|
+
result = []
|
|
852
|
+
pos = 0
|
|
853
|
+
while pos < len(value_str):
|
|
854
|
+
# 查找下一个DICT_ENTRY
|
|
855
|
+
dict_entry_start = value_str.find('DICT_ENTRY', pos)
|
|
856
|
+
if dict_entry_start == -1:
|
|
857
|
+
break
|
|
858
|
+
# 从DICT_ENTRY开始查找第一个不在引号内的大括号
|
|
859
|
+
brace_start = -1
|
|
860
|
+
in_quotes = False
|
|
861
|
+
quote_char = None
|
|
862
|
+
for i in range(dict_entry_start, len(value_str)):
|
|
863
|
+
char = value_str[i]
|
|
864
|
+
# 处理引号状态
|
|
865
|
+
if char in ['"', "'"] and (i == 0 or value_str[i - 1] != '\\'):
|
|
866
|
+
if in_quotes and char == quote_char:
|
|
867
|
+
in_quotes = False
|
|
868
|
+
quote_char = None
|
|
869
|
+
elif not in_quotes:
|
|
870
|
+
in_quotes = True
|
|
871
|
+
quote_char = char
|
|
872
|
+
continue
|
|
873
|
+
# 只在非引号内查找大括号
|
|
874
|
+
if not in_quotes and char == '{':
|
|
875
|
+
brace_start = i
|
|
876
|
+
break
|
|
877
|
+
if brace_start == -1:
|
|
878
|
+
pos = dict_entry_start + 10 # 跳过当前DICT_ENTRY,继续查找
|
|
879
|
+
continue
|
|
880
|
+
# 计算括号平衡,正确处理引号
|
|
881
|
+
brace_count = 1
|
|
882
|
+
end_brace_pos = brace_start + 1
|
|
883
|
+
in_quotes = False
|
|
884
|
+
quote_char = None
|
|
885
|
+
while end_brace_pos < len(value_str):
|
|
886
|
+
char = value_str[end_brace_pos]
|
|
887
|
+
# 处理引号状态
|
|
888
|
+
if char in ['"', "'"] and (end_brace_pos == 0 or value_str[end_brace_pos - 1] != '\\'):
|
|
889
|
+
if in_quotes and char == quote_char:
|
|
890
|
+
in_quotes = False
|
|
891
|
+
quote_char = None
|
|
892
|
+
elif not in_quotes:
|
|
893
|
+
in_quotes = True
|
|
894
|
+
quote_char = char
|
|
895
|
+
end_brace_pos += 1
|
|
896
|
+
continue
|
|
897
|
+
# 只在非引号内处理大括号
|
|
898
|
+
if not in_quotes:
|
|
899
|
+
if char == '{':
|
|
900
|
+
brace_count += 1
|
|
901
|
+
elif char == '}':
|
|
902
|
+
brace_count -= 1
|
|
903
|
+
# 当括号平衡时,我们找到了匹配的结束大括号
|
|
904
|
+
if brace_count == 0:
|
|
905
|
+
break
|
|
906
|
+
end_brace_pos += 1
|
|
907
|
+
# 确保找到了匹配的结束大括号
|
|
908
|
+
if brace_count == 0:
|
|
909
|
+
# 提取完整的DICT_ENTRY条目(包含结束的};)
|
|
910
|
+
# 查找是否还有分号和结束大括号
|
|
911
|
+
if end_brace_pos + 1 < len(value_str) and value_str[end_brace_pos + 1] == ';':
|
|
912
|
+
dict_entry = value_str[dict_entry_start:end_brace_pos + 2]
|
|
913
|
+
else:
|
|
914
|
+
dict_entry = value_str[dict_entry_start:end_brace_pos + 1]
|
|
915
|
+
result.append(dict_entry)
|
|
916
|
+
pos = end_brace_pos + 2 # 继续查找下一个DICT_ENTRY
|
|
917
|
+
else:
|
|
918
|
+
# 没有找到匹配的括号,跳过这个DICT_ENTRY
|
|
919
|
+
pos = dict_entry_start + 10
|
|
920
|
+
return result
|
|
921
|
+
|
|
922
|
+
@staticmethod
|
|
923
|
+
def _find_matching_brace(text, start_pos):
|
|
924
|
+
"""
|
|
925
|
+
查找匹配的结束大括号,处理引号和嵌套括号
|
|
926
|
+
"""
|
|
927
|
+
depth = 0
|
|
928
|
+
in_quote = False
|
|
929
|
+
quote_char = None
|
|
930
|
+
for i in range(start_pos, len(text)):
|
|
931
|
+
char = text[i]
|
|
932
|
+
# 处理引号
|
|
933
|
+
if char in ['"', "'"] and (i == 0 or text[i-1] != '\\'):
|
|
934
|
+
if in_quote and char == quote_char:
|
|
935
|
+
in_quote = False
|
|
936
|
+
elif not in_quote:
|
|
937
|
+
in_quote = True
|
|
938
|
+
quote_char = char
|
|
939
|
+
continue
|
|
940
|
+
# 在引号内,跳过括号处理
|
|
941
|
+
if in_quote:
|
|
942
|
+
continue
|
|
943
|
+
# 处理括号
|
|
944
|
+
if char == '{':
|
|
945
|
+
depth += 1
|
|
946
|
+
elif char == '}':
|
|
947
|
+
depth -= 1
|
|
948
|
+
if depth == 0:
|
|
949
|
+
return i
|
|
950
|
+
return -1 # 没有找到匹配的括号
|
|
951
|
+
|
|
952
|
+
@staticmethod
|
|
953
|
+
def _convert_array_type(value_str):
|
|
954
|
+
"""
|
|
955
|
+
转换D-Bus数组类型,不包括字节数组
|
|
956
|
+
|
|
957
|
+
Args:
|
|
958
|
+
value_str: 包含数组内容的字符串
|
|
959
|
+
|
|
960
|
+
Returns:
|
|
961
|
+
转换后的Python列表对象,如果无法转换则返回None
|
|
962
|
+
"""
|
|
963
|
+
# 对于简单的ARRAY格式,使用通用函数提取内容
|
|
964
|
+
if value_str.startswith('ARRAY ') and '{' in value_str:
|
|
965
|
+
elements = BusCtlTypeConverter._extract_and_split_content(value_str, 'ARRAY')
|
|
966
|
+
if elements is None:
|
|
967
|
+
return []
|
|
968
|
+
# 转换每个元素
|
|
969
|
+
converted_elements = []
|
|
970
|
+
for elem in elements:
|
|
971
|
+
converted_elements.append(BusCtlTypeConverter.dbus_string_to_type(elem))
|
|
972
|
+
return converted_elements
|
|
973
|
+
# 对于没有ARRAY关键字的简单花括号格式
|
|
974
|
+
start_brace = value_str.find('{')
|
|
975
|
+
end_brace = value_str.rfind('}')
|
|
976
|
+
if start_brace == -1 or end_brace == -1 or start_brace >= end_brace:
|
|
977
|
+
return [] # 格式不正确,返回空列表
|
|
978
|
+
# 提取大括号内的内容并去除首尾空白
|
|
979
|
+
content = value_str[start_brace + 1:end_brace].strip()
|
|
980
|
+
if not content:
|
|
981
|
+
return [] # 空数组
|
|
982
|
+
# 使用分割函数
|
|
983
|
+
elements = BusCtlTypeConverter._split_elements_by_semicolon(content)
|
|
984
|
+
# 转换每个元素
|
|
985
|
+
converted_elements = []
|
|
986
|
+
for elem in elements:
|
|
987
|
+
converted_elements.append(BusCtlTypeConverter.dbus_string_to_type(elem))
|
|
988
|
+
return converted_elements
|
|
989
|
+
|
|
990
|
+
@staticmethod
|
|
991
|
+
def _convert_struct_type(value_str):
|
|
992
|
+
"""
|
|
993
|
+
转换D-Bus结构体类型
|
|
994
|
+
|
|
995
|
+
Args:
|
|
996
|
+
value_str: 包含结构体内容的字符串,格式为 'STRUCT {...}' 或 'STRUCT "ysss" {...}'
|
|
997
|
+
|
|
998
|
+
Returns:
|
|
999
|
+
转换后的Python列表对象,如果无法转换则返回None
|
|
1000
|
+
"""
|
|
1001
|
+
# 使用通用函数提取内容
|
|
1002
|
+
elements = BusCtlTypeConverter._extract_and_split_content(value_str, 'STRUCT')
|
|
1003
|
+
if elements is None:
|
|
1004
|
+
return None
|
|
1005
|
+
|
|
1006
|
+
# 转换每个元素
|
|
1007
|
+
converted_elements = []
|
|
1008
|
+
for elem in elements:
|
|
1009
|
+
converted_elem = BusCtlTypeConverter.dbus_string_to_type(elem)
|
|
1010
|
+
if converted_elem is not None:
|
|
1011
|
+
converted_elements.append(converted_elem)
|
|
1012
|
+
# 返回列表,对应dbus-next中的结构体表示
|
|
1013
|
+
return converted_elements
|
|
1014
|
+
|
|
1015
|
+
|
|
1016
|
+
|
|
1017
|
+
@staticmethod
|
|
1018
|
+
def _convert_array_of_bytes(value_str):
|
|
1019
|
+
"""转换字节数组类型的值"""
|
|
1020
|
+
try:
|
|
1021
|
+
# 假设输入格式为 "ARRAY "y" { BYTE 1; BYTE 2; BYTE 3; };" 或类似格式
|
|
1022
|
+
# 直接提取大括号内的内容
|
|
1023
|
+
start_index = value_str.find('{')
|
|
1024
|
+
end_index = value_str.rfind('}')
|
|
1025
|
+
if start_index == -1 or end_index == -1 or start_index >= end_index:
|
|
1026
|
+
# 如果找不到有效格式,尝试处理空数组
|
|
1027
|
+
if '{ }' in value_str or '{}' in value_str:
|
|
1028
|
+
return b''
|
|
1029
|
+
raise ValueError(f"Invalid byte array format: {value_str}")
|
|
1030
|
+
# 提取大括号内的内容,去掉前后空格
|
|
1031
|
+
content = value_str[start_index + 1:end_index].strip()
|
|
1032
|
+
# 如果内容为空,返回空字节数组
|
|
1033
|
+
if not content:
|
|
1034
|
+
return b''
|
|
1035
|
+
# 按分号分割元素
|
|
1036
|
+
elements = [elem.strip() for elem in content.split(';') if elem.strip()]
|
|
1037
|
+
# 提取每个BYTE值并转换为整数
|
|
1038
|
+
valid_values = []
|
|
1039
|
+
for elem in elements:
|
|
1040
|
+
# 简化逻辑:使用空格分割并取第二个元素(数字部分)
|
|
1041
|
+
parts = elem.split()
|
|
1042
|
+
if len(parts) >= 2 and parts[0].upper() == 'BYTE':
|
|
1043
|
+
try:
|
|
1044
|
+
# 直接取空格分割后的第二个部分作为数字
|
|
1045
|
+
byte_value = int(parts[1])
|
|
1046
|
+
# 确保值在有效范围内
|
|
1047
|
+
if 0 <= byte_value <= 255:
|
|
1048
|
+
valid_values.append(byte_value)
|
|
1049
|
+
else:
|
|
1050
|
+
logging.warning(f"Byte value {byte_value} out of range, clamping to 0-255")
|
|
1051
|
+
valid_values.append(max(0, min(255, byte_value)))
|
|
1052
|
+
except ValueError:
|
|
1053
|
+
logging.warning(f"Invalid byte value format: {elem}")
|
|
1054
|
+
valid_values.append(0)
|
|
1055
|
+
else:
|
|
1056
|
+
logging.warning(f"Unexpected element format in byte array: {elem}")
|
|
1057
|
+
valid_values.append(0)
|
|
1058
|
+
# 转换为bytes对象返回
|
|
1059
|
+
return bytes(valid_values)
|
|
1060
|
+
except Exception as e:
|
|
1061
|
+
logging.error(f"Error converting byte array: {str(e)}")
|
|
1062
|
+
return b''
|
|
1063
|
+
|
|
1064
|
+
@staticmethod
|
|
1065
|
+
def _extract_and_split_content(value_str, type_name):
|
|
1066
|
+
"""
|
|
1067
|
+
公共函数:提取并分割指定类型的数据结构内容,针对复杂类型(STRUCT、ARRAY),用最外层的;进行分割分割成独立的数据
|
|
1068
|
+
|
|
1069
|
+
Args:
|
|
1070
|
+
value_str: 包含数据结构内容的字符串
|
|
1071
|
+
type_name: 数据结构类型名称,如'STRUCT'、'ARRAY'
|
|
1072
|
+
|
|
1073
|
+
Returns:
|
|
1074
|
+
分割后的元素列表,如果无法提取则返回None
|
|
1075
|
+
"""
|
|
1076
|
+
# 提取大括号内的内容
|
|
1077
|
+
content = BusCtlTypeConverter.extract_content_from_type(value_str, type_name)
|
|
1078
|
+
if not content:
|
|
1079
|
+
return []
|
|
1080
|
+
# 使用之前创建的元素分割函数
|
|
1081
|
+
return BusCtlTypeConverter._split_elements_by_semicolon(content)
|