def generate_outputs(self, finalize_config=True):
print(" 正在执行 generate_outputs()...")
if not self.tx_limit_entries:
print(" 无 TX 限幅数据可输出")
return
# === Step 0: 记录变更摘要 ===
changes = {
"added_ranges": set(),
"removed_ranges": set(),
"modified_ranges": set(), # 可留空,后续可扩展
"other_additions": [], # 其他文本类变更(如 fallback 变更)
"other_deletions": []
}
# 获取当前 used_ranges(去重宏名)
current_used_ranges = set(entry["range_macro"] for entry in self.tx_limit_entries)
# 读取旧的 used_ranges(如果 config 存在)
previous_used_ranges = set()
if "used_ranges" in self.config:
try:
previous_used_ranges = set(self.config["used_ranges"])
except Exception as e:
print(f" 解析旧 used_ranges 失败: {e}")
# 对比变化
changes["added_ranges"] = current_used_ranges - previous_used_ranges
changes["removed_ranges"] = previous_used_ranges - current_used_ranges
if changes["added_ranges"]:
print(f" 新增 RANGE 宏 ({len(changes['added_ranges'])}):")
for r in sorted(changes["added_ranges"]):
print(f" + {r}")
if changes["removed_ranges"]:
print(f" 删除 RANGE 宏 ({len(changes['removed_ranges'])}):")
for r in sorted(changes["removed_ranges"]):
print(f" - {r}")
# === Step 1: 使用 "HT" 分类 entries ===
normal_entries = []
ht_entries = []
for e in self.tx_limit_entries:
macro = e.get("rate_set_macro", "")
if "HT" in macro:
ht_entries.append(e)
else:
normal_entries.append(e)
print(f" 自动分类结果:")
print(f" ├─ Normal 模式(不含 HT): {len(normal_entries)} 条")
print(f" └─ HT 模式(含 HT): {len(ht_entries)} 条")
# === Step 2: 构建 g_tx_limit_normal 结构(按 bw 排序)===
def build_normal_structure(entries):
grouped = defaultdict(list)
for e in entries:
bw = str(e["bw"])
grouped[bw].append(e)
result = []
for bw in ["20", "40", "80", "160"]:
if bw in grouped:
sorted_entries = sorted(grouped[bw], key=lambda x: (x["ch_start"], x["encoded_power"]))
result.append((bw, sorted_entries))
return result
normal_struct = build_normal_structure(normal_entries)
# === Step 3: 构建 g_tx_limit_ht 结构(严格顺序)===
def build_ht_structure(entries):
groups = defaultdict(list)
for e in entries:
bw = str(e["bw"])
if "EXT4" in e["rate_set_macro"]:
level = "ext4"
elif "EXT" in e["rate_set_macro"]:
level = "ext"
else:
level = "base"
groups[(level, bw)].append(e)
order = [
("base", "20"), ("base", "40"),
("ext", "20"), ("ext", "40"),
("ext4", "20"), ("ext4", "40")
]
segments = []
active_segment_count = sum(1 for key in order if key in groups)
for idx, (level, bw) in enumerate(order):
key = (level, bw)
if key not in groups:
continue
seg_entries = sorted(groups[key], key=lambda x: (x["ch_start"], x["encoded_power"]))
count = len(seg_entries)
header_flags = f"CLM_DATA_FLAG_WIDTH_{bw} | CLM_DATA_FLAG_MEAS_COND"
if idx < active_segment_count - 1:
header_flags += " | CLM_DATA_FLAG_MORE"
if level != "base":
header_flags += " | CLM_DATA_FLAG_FLAG2"
segment = {
"header_flags": header_flags,
"count": count,
"entries": seg_entries
}
if level == "ext":
segment["flag2"] = "CLM_DATA_FLAG2_RATE_TYPE_EXT"
elif level == "ext4":
segment["flag2"] = "CLM_DATA_FLAG2_RATE_TYPE_EXT4"
segments.append(segment)
return segments
ht_segments = build_ht_structure(ht_entries)
# === Step 4: fallback range 和 CHANNEL_SET 自动创建逻辑 ===
channel_set_comment = "Fallback 2.4GHz channel set "
old_fallback = self.config.get("fallback_range_macro", "UNKNOWN")
if self.global_ch_min is not None and self.global_ch_max is not None:
fallback_range_macro = f"RANGE_2G_20M_{self.global_ch_min}_{self.global_ch_max}"
fallback_ch_start = self.global_ch_min
fallback_ch_end = self.global_ch_max
if old_fallback != fallback_range_macro:
changes["other_deletions"].append(f"fallback range: {old_fallback}")
changes["other_additions"].append(f"fallback range: {fallback_range_macro}")
print(f" fallback range 变更: {old_fallback} → {fallback_range_macro}")
# 待修改
print(f" 正在设置监管 fallback 范围: {fallback_range_macro}")
fallback_channel_set_id = 1
self.channel_set_map[fallback_range_macro] = fallback_channel_set_id
print(f" 已绑定监管 fallback: {fallback_range_macro} → CHANNEL_SET_{fallback_channel_set_id}")
else:
fallback_range_macro = "RANGE_2G_20M_1_11"
fallback_ch_start = 1
fallback_ch_end = 11
fallback_channel_set_id = 1
self.channel_set_map[fallback_range_macro] = fallback_channel_set_id
print(" 未检测到有效的 2.4G 信道范围,使用默认 fallback: RANGE_2G_20M_1_11 → CHANNEL_SET_1")
# 待修改
# === Step 5: 渲染上下文集合 ===
# === 遍历 locale_targets 生成多个 locale 数据块 ===
for idx, target in enumerate(self.config.get("locale_targets", [])):
enum = target["enum"]
table = target["table"]
suffix = target.get("suffix", "UNKNOWN")
assigned_locale = target["assigned_locale"]
locale_id_safe = assigned_locale.replace('-', '_')
display_name = suffix
context_tables = {
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"locale_name": locale_id_safe,
"locale_display_name": display_name,
"normal_table": normal_struct,
"ht_segments": ht_segments,
"fallback_encoded_eirp": 30,
"fallback_range_macro": fallback_range_macro,
"fallback_ch_start": fallback_ch_start,
"fallback_ch_end": fallback_ch_end,
"fallback_channel_set_id": fallback_channel_set_id,
"channel_set_comment": "Fallback 2.4GHz channel set ",
}
# 确保输出目录存在
output_dir = Path(self.output_dir)
output_dir.mkdir(parents=True, exist_ok=True)
#待修改
# 分析 tx_limit_table.c 的变更
output_path = output_dir / "tx_limit_table.c"
template_path = "templates/tx_limit_table_2_4G.c.j2"
# 待修改
# 读取原始文件内容(如果存在)
original_lines = []
file_existed = output_path.exists()
if file_existed:
try:
original_lines = output_path.read_text(encoding='utf-8').splitlines()
except Exception as e:
print(f" 无法读取旧文件 {output_path}: {e}")
# 生成新内容
try:
new_content = self.render_from_template_string(
template_path=template_path,
context=context_tables
)
new_lines = new_content.splitlines()
except Exception as e:
print(f" 模板渲染失败 ({template_path}): {e}")
raise
# 比较差异并决定是否写入
if not file_existed:
print(f" 将创建新文件: {output_path}")
elif original_lines != new_lines:
print(f" 检测到变更,将更新文件: {output_path}")
# 标记该文件更新
changes["other_additions"].append(f"更新了 {output_path.name}")
else:
print(f" 文件内容未变,跳过写入: {output_path}")
# 即使不写也要继续后续流程
# 写入新内容(除非完全一致且已存在)
if not file_existed or original_lines != new_lines:
try:
output_path.write_text(new_content, encoding='utf-8')
print(f" 已写入 → {output_path}")
except Exception as e:
print(f" 写入文件失败 {output_path}: {e}")
raise
#待修改
# === Step 6: 实际渲染其他模板文件 ===
try:
self.render_from_template(
"templates/clm_locale.c.j2",
context_clm,
str(output_dir / f"locale_{self.locale_name.lower()}.c")
)
print(f" 生成 locale 文件 → locale_{self.locale_name.lower()}.c")
self.render_from_template(
"templates/clm_macros.h.j2",
context_tables,
str(output_dir / "clm_macros.h")
)
print(f" 生成宏定义头文件 → clm_macros.h")
except Exception as e:
print(f" 模板渲染失败: {e}")
raise
# 待修改
# === Step 7: 添加到 used_ranges 和 used_ranges_count 到 config.json(仅当 finalize_config=True)===
if finalize_config:
used_range_macros = sorted(set(entry["range_macro"] for entry in self.tx_limit_entries))
self.used_ranges = used_range_macros
# 更新主配置字段(直接赋值 list 和 int)
self.config["used_ranges"] = used_range_macros
self.config["used_ranges_count"] = len(used_range_macros)
try:
# 写回文件
with open(self.config_file_path, 'w', encoding='utf-8') as f:
json.dump(self.config, f, indent=4, ensure_ascii=False)
f.flush()
os.fsync(f.fileno()) # 确保落盘
print(f" 已添加 'used_ranges' 到 config: {self.config_file_path}")
print(f" 共 {len(used_range_macros)} 个唯一 RANGE 宏被使用:")
for macro in used_range_macros:
print(f" - {macro}")
self.last_config = str(self.config_file_path)
except Exception as e:
print(f" 写入 config 失败: {e}")
else:
print(" 跳过 used_ranges 生成 (finalize_config=False)")
# === Final Step: 保存 channel_set 映射配置 ===
self.save_channel_set_map_to_config()
# === Log Changes ===
total_entries = len(self.tx_limit_entries)
self.log_changes_to_file(
changes=changes,
locale_id=self.locale_name,
total_entries=total_entries
)
#待修改
# 最终总结
print(f" 所有输出文件生成完成。")
print(f" 输出路径: {self.output_dir}")
print(f" 功率表名称: {self.locale_display_name} ({self.locale_name})")
# 待修改这样吗