Files
ACP/convert_ats_to_acp.py
T
2026-08-06 14:04:28 +08:00

444 lines
18 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
将极电子程序 .ats 文件转换为 ACP 项目 .ACP 文件
- 设备 FullName 映射
- 方法名映射
- 参数格式转换(.ats → .ACP
- IOBoardGroup 只保留台架序号参数
"""
import json
import glob
import os
import uuid
import copy
# ============================================================
# 1. 设备 FullName 映射
# ============================================================
DEVICE_MAPPING = {
# 相同设备(无需改 FullName)
"TSMasterCAN.CAN": "TSMasterCAN.CAN",
"Command.Delay": "Command.Delay",
"Command.CommandMath": "Command.CommandMath",
"DeviceCommand.Device.PW8001": "DeviceCommand.Device.PW8001",
# 需要映射的设备
"DeviceCommand.Device.IOBoardCard": "DeviceCommand.Device.IOBoardGroup",
"DeviceCommand.Device.IT6724C": "DeviceCommand.Devices.IT6720",
"DeviceCommand.Device.SQ0090G1D1": "DeviceCommand.Devices.Chroma61800", # 三相交流源 → Chroma61800
"DeviceCommand.Device.PSB11500_60": "DeviceCommand.Devices.S7200", # 交流源/负载 → S7200 直流源载一体机
"DeviceCommand.Device.MSO44B": "DeviceCommand.Devices.TektronixMSO",
"DeviceCommand.Device.EA3040_40C": "DeviceCommand.Devices.ANEVH80", # 水冷机/负载 → ANEVH80
"DeviceCommand.Device.Chroma_63206A": "DeviceCommand.Devices.ANEVH80", # 电子负载 → ANEVH80
# 暂无对应设备,保持原 FullName(方法名使用占位符)
"DeviceCommand.Device.WS_68070": "DeviceCommand.Device.WS_68070",
"DeviceCommand.Device.AFV33030": "DeviceCommand.Device.AFV33030",
"DeviceCommand.Device.DAQ970A": "DeviceCommand.Device.DAQ970A",
"DeviceCommand.Device.XcpDevice": "DeviceCommand.Device.XcpDevice",
"ATS.Commands.CurveMonitorCommands": "ATS.Commands.CurveMonitorCommands",
}
# ============================================================
# 2. 方法名映射 {(旧FullName): {旧方法名: 新方法名}}
# ============================================================
# --- IOBoardCard → IOBoardGroup ---
IOBOARD_METHOD_MAP = {
"WriteMultiIO": "批量写输出开关",
"WriteSingleIO": "写输出开关",
}
# --- IT6724C → IT6720 ---
IT6720_METHOD_MAP = {
"RemoteMode": "切换远程控制模式",
"DCOutput_OFF": "设置输出开关", # 参数值 false
"OFF": "设置输出开关", # 参数值 false
"ON": "设置输出开关", # 参数值 true
"SetVoltage": "设置输出电压",
"SetCurrent": "设置输出电流",
}
# --- PSB11500_60 → S7200 (直流源载一体机) ---
S7200_METHOD_MAP = {
"Set_RemoteMode": "设置为远程模式",
"Set_SourceMode_SetVoltage": "设置电压",
"Set_SourceMode_SetCurrent": "设置CC模式电流",
"Set_SourceMode_SetPower": "设置正向功率阈值",
"Set_SinkMode_SetVoltage": "设置电压",
"Set_SinkMode_SetCurrent": "设置CC模式电流",
"Set_SinkMode_SetPower": "设置反向功率阈值",
"Set_DC_Input": "设置电压",
"Set_DC_Output": "设置电压",
"LoadOn": "设置通道开关", # 开启负载
"LoadOff": "设置通道开关", # 关闭负载
"Get_ActualVoltage": "查询实时电压",
"Get_ActualCurrent": "查询实时电流",
"Get_ActualPower": "查询功率",
"MeasureVoltage": "查询实时电压",
"MeasureCurrent": "查询实时电流",
"MeasurePower": "查询功率",
}
# --- SQ0090G1D1 → Chroma61800 (交流源) ---
CHROMA61800_METHOD_MAP = {
"RemoteMode": "设置输出开关Async", # true=远程
"ON": "设置输出开关Async", # true=开启
"OFF": "设置输出开关Async", # false=关闭
"SetVoltage": "设置交流电压Async",
"SetFrequency": "设置频率Async",
"SetOutputMode": "设置单三相模式Async",
"SetPhaseAngle": "设置开机角度Async",
"SetIndividualVoltage": "设置交流电压Async",
"MeasureVoltage": "读取交流电压Async",
"MeasureCurrent": "读取交流电流Async",
}
# --- EA3040_40C → ANEVH80 ---
ANEVH80_METHOD_MAP = {
"Set_RemoteMode": "占位符",
"Set_Voltage": "占位符",
"Set_Current": "占位符",
"DCOutput_ON": "占位符",
"DCOutput_OFF": "占位符",
}
# --- Chroma_63206A → ANEVH80 ---
CHROMA63206A_METHOD_MAP = {
"SetModeCC": "占位符",
"SetModeCV": "占位符",
"SetModeCR": "占位符",
"ON": "占位符",
"OFF": "占位符",
"SetVoltageL1": "占位符",
"SetVoltageL2": "占位符",
"SetCurrentL1": "占位符",
"SetCurrentL2": "占位符",
"SetResistanceL1": "占位符",
"SetResistanceL2": "占位符",
"SetCurrentSlewRatePositive": "占位符",
"SetCurrentSlewRateNegative": "占位符",
"MeasureVoltage": "占位符",
"MeasureCurrent": "占位符",
"MeasurePower": "占位符",
}
# --- MSO44B → TektronixMSO ---
TEKTRONIX_METHOD_MAP = {
"Acquire_Run": "设置采集状态",
"Acquire_Stop": "设置采集状态",
"ClearStatus": "清除状态",
"Reset": "清除状态",
"Set_AcquireMode": "设置采集模式",
"Set_ChannelState": "设置通道显示开关",
"Set_ChannelScale": "设置通道垂直刻度",
"Set_ChannelUnit": "设置通道垂直刻度",
"Set_ChannelCoupling": "设置通道垂直刻度",
"Set_ChannelProbe": "设置通道垂直刻度",
"Set_ChannelBandwidth": "设置通道垂直刻度",
"Set_HorizontalScale": "设置水平刻度",
"Set_RecordLength": "设置记录长度",
"Set_StopAfter": "设置采集状态",
"Set_TriggerMode": "设置采集模式",
"Set_TriggerType_Edge": "设置采集模式",
"Set_TriggerEdgeSource": "设置测量源",
"Set_TriggerEdgeSlope": "设置测量类型",
"Set_TriggerLevel": "设置通道垂直位置",
"Set_WaveformSource": "设置测量源",
"Setup_Measurement": "开启测量项",
"Get_MeasurementValue": "查询测量项当前值",
"Save_Screenshot": "保存屏幕截图",
"Save_Waveform": "保存屏幕截图",
}
# --- PW8001 → PW8001 (同设备,方法名需适配) ---
PW8001_METHOD_MAP = {
"Set_TestMode_WIDE": "设置测试模式_WIDE",
"Set_TestMode_IEC": "设置测试模式_IEC",
"QueryVoltageWithRatio": "查询电压_含变比",
"QueryCurrentWithRatio": "查询电流_含变比",
"QueryPowerWithoutRatio": "查询功率_不含变比",
"QueryTotalPowerP123": "查询总功率P123",
"QueryVoltageTHD": "查询电压THD",
"QueryCurrentTHD": "查询电流THD",
}
# --- WS_68070 (占位符) ---
WS68070_METHOD_MAP = {
"Get_ThreePhase_Frequency": "占位符_GetFrequency",
"Get_ThreePhase_MaxCurrent": "占位符_GetMaxCurrent",
"Get_ThreePhase_MaxVoltage": "占位符_GetMaxVoltage",
"Get_ThreePhase_SumActivePower": "占位符_GetSumActivePower",
"Set_ATE_Load_A_Current": "占位符_SetLoadCurrent",
"Set_ATE_Load_CurrentMode": "占位符_SetLoadMode",
"Set_ATE_Load_Load": "占位符_SetLoad",
"Set_ATE_Load_PowerMode": "占位符_SetPowerMode",
"Set_ATE_Load_Uninstall": "占位符_SetUninstall",
"Set_ThreePhase_SumActivePower": "占位符_SetSumActivePower",
}
# --- AFV33030 (占位符) ---
AFV33030_METHOD_MAP = {
"Set_General_OutputVoltage": "占位符_SetOutputVoltage",
"Set_ThreePhase_OutputVoltage": "占位符_SetThreePhaseVoltage",
}
# --- DAQ970A (占位符) ---
DAQ970A_METHOD_MAP = {
"Set_DCDefaultCurrent": "占位符_SetCurrent",
"Set_DCDefaultVoltage": "占位符_SetVoltage",
}
# --- XcpDevice (占位符) ---
XCP_METHOD_MAP = {
"Connect": "占位符_Connect",
"Disconnect": "占位符_Disconnect",
"ReadMemory": "占位符_ReadMemory",
"WriteMemory": "占位符_WriteMemory",
}
# --- CurveMonitorCommands (占位符) ---
CURVE_MONITOR_METHOD_MAP = {
"CAN曲线记录_启动": "占位符_StartCANRecord",
"CAN曲线记录_停止": "占位符_StopCANRecord",
"设备参数记录_启动": "占位符_StartDeviceRecord",
"设备参数记录_停止": "占位符_StopDeviceRecord",
}
# 汇总所有方法映射
METHOD_MAPPING = {
"DeviceCommand.Device.IOBoardCard": IOBOARD_METHOD_MAP,
"DeviceCommand.Device.IT6724C": IT6720_METHOD_MAP,
"DeviceCommand.Device.PSB11500_60": S7200_METHOD_MAP,
"DeviceCommand.Device.SQ0090G1D1": CHROMA61800_METHOD_MAP,
"DeviceCommand.Device.EA3040_40C": ANEVH80_METHOD_MAP,
"DeviceCommand.Device.Chroma_63206A": CHROMA63206A_METHOD_MAP,
"DeviceCommand.Device.MSO44B": TEKTRONIX_METHOD_MAP,
"DeviceCommand.Device.PW8001": PW8001_METHOD_MAP,
"DeviceCommand.Device.WS_68070": WS68070_METHOD_MAP,
"DeviceCommand.Device.AFV33030": AFV33030_METHOD_MAP,
"DeviceCommand.Device.DAQ970A": DAQ970A_METHOD_MAP,
"DeviceCommand.Device.XcpDevice": XCP_METHOD_MAP,
"ATS.Commands.CurveMonitorCommands": CURVE_MONITOR_METHOD_MAP,
}
# ============================================================
# 3. 格式转换函数
# ============================================================
def convert_param_ats_to_acp(param):
"""将 .ats 参数格式转换为 .ACP 参数格式"""
new_param = {}
new_param["ID"] = param.get("ID", str(uuid.uuid4()))
new_param["IsVisible"] = param.get("IsVisible", True)
new_param["IsEditable"] = True # .ACP 新增字段
new_param["Name"] = param.get("Name", "")
new_param["Type"] = param.get("Type", "")
new_param["Category"] = param.get("Category", 0)
new_param["Value"] = param.get("Value", None)
new_param["LowerLimit"] = param.get("LowerLimit", None)
new_param["UpperLimit"] = param.get("UpperLimit", None)
new_param["Result"] = param.get("Result", True)
new_param["IsUseVar"] = param.get("IsUseVar", False)
new_param["VariableName"] = param.get("VariableName", None)
new_param["VariableID"] = param.get("VariableID", None)
# 不包含: IsGlobal, InitialValue, IsSave, IsOutputToReport
return new_param
def convert_ioboard_params(params, old_method_name):
"""IOBoardGroup 参数处理:
根据 IOBoardGroup 的方法签名重新生成参数
写输出开关(台架序号, 站号, 开始地址, data)
批量写输出开关(台架序号, 站号, 开始地址, datas)
只保留台架序号参数(使用公共变量),其他参数先不写
"""
int_type = "System.Int32, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
byte_type = "System.Byte, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
ushort_type = "System.UInt16, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
bool_type = "System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
bool_arr_type = "System.Boolean[], System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
ct_type = "System.Threading.CancellationToken, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
# 台架序号参数(使用公共变量,Value=nullIsUseVar=true
station_param = {
"ID": str(uuid.uuid4()),
"IsVisible": True,
"IsEditable": True,
"Name": "台架序号",
"Type": int_type,
"Category": 0,
"Value": None,
"LowerLimit": None,
"UpperLimit": None,
"Result": True,
"IsUseVar": True,
"VariableName": "台架序号",
"VariableID": None
}
new_params = [station_param]
# 根据方法类型添加对应的参数(值设为 null,用户后续填写)
if "批量" in old_method_name or "Multi" in old_method_name:
# 批量写输出开关: 台架序号, 站号, 开始地址, datas
new_params.append(make_param("站号", byte_type, None))
new_params.append(make_param("开始地址", ushort_type, None))
new_params.append(make_param("datas", bool_arr_type, None))
else:
# 写输出开关: 台架序号, 站号, 开始地址, data
new_params.append(make_param("站号", byte_type, None))
new_params.append(make_param("开始地址", ushort_type, None))
new_params.append(make_param("data", bool_type, None))
return new_params
def make_param(name, type_str, value):
"""创建一个 ACP 格式的参数"""
return {
"ID": str(uuid.uuid4()),
"IsVisible": True,
"IsEditable": True,
"Name": name,
"Type": type_str,
"Category": 0,
"Value": value,
"LowerLimit": None,
"UpperLimit": None,
"Result": True,
"IsUseVar": False,
"VariableName": None,
"VariableID": None
}
def convert_method(method, old_full_name):
"""转换 Method 对象"""
if not method:
return None
new_method = {}
old_name = method.get("Name", "")
# 映射 FullName
new_full_name = DEVICE_MAPPING.get(old_full_name, old_full_name)
new_method["Name"] = old_name
new_method["FullName"] = new_full_name
# 映射方法名
method_map = METHOD_MAPPING.get(old_full_name, {})
if old_name in method_map:
new_method["Name"] = method_map[old_name]
# 转换参数(不包含 DeviceID)
old_params = method.get("Parameters", [])
if old_full_name == "DeviceCommand.Device.IOBoardCard":
new_method["Parameters"] = convert_ioboard_params(old_params, old_name)
else:
new_method["Parameters"] = [convert_param_ats_to_acp(p) for p in old_params]
return new_method
def convert_step(step):
"""转换单个 Step"""
new_step = {}
new_step["ID"] = step.get("ID", str(uuid.uuid4()))
new_step["IsUsed"] = step.get("IsUsed", True)
new_step["Index"] = step.get("Index", 0)
new_step["Name"] = step.get("Name", "")
new_step["StepType"] = step.get("StepType", "方法")
# 转换 Method
old_method = step.get("Method")
if old_method:
old_full_name = old_method.get("FullName", "")
new_step["Method"] = convert_method(old_method, old_full_name)
else:
new_step["Method"] = None
# 转换 SubProgram(嵌套的子程序)
sub_program = step.get("SubProgram")
if sub_program and isinstance(sub_program, dict):
new_step["SubProgram"] = convert_program(sub_program)
else:
new_step["SubProgram"] = None
# 循环相关
new_step["LoopCount"] = step.get("LoopCount", None)
new_step["LoopStartStepId"] = step.get("LoopStartStepId", None)
# 跳转相关
new_step["OKExpression"] = step.get("OKExpression", None)
new_step["GotoSettingString"] = step.get("GotoSettingString", "")
new_step["OKGotoStepID"] = step.get("OKGotoStepID", None)
new_step["NGGotoStepID"] = step.get("NGGotoStepID", None)
new_step["Description"] = step.get("Description", None)
return new_step
def convert_program(program):
"""转换整个 ProgramVM 结构"""
new_program = {}
new_program["ID"] = program.get("ID", str(uuid.uuid4()))
# 转换 StepCollection
old_steps = program.get("StepCollection", [])
new_program["StepCollection"] = [convert_step(s) for s in old_steps]
# ErrorStepCollection
new_program["ErrorStepCollection"] = []
# 转换 Parameters(顶层参数)
old_params = program.get("Parameters", [])
new_program["Parameters"] = [convert_param_ats_to_acp(p) for p in old_params]
return new_program
def convert_file(input_path, output_path):
"""转换单个文件"""
with open(input_path, encoding='utf-8-sig') as f:
data = json.load(f)
result = convert_program(data)
with open(output_path, 'w', encoding='utf-8') as f:
json.dump(result, f, ensure_ascii=False, indent=2)
# ============================================================
# 4. 主程序
# ============================================================
if __name__ == "__main__":
input_dir = r"C:\Users\kk\Desktop\ACP\极电子程序"
output_dir = r"D:\ACP\测试项\ACP输出"
os.makedirs(output_dir, exist_ok=True)
files = glob.glob(os.path.join(input_dir, "*.ats"))
print(f"找到 {len(files)} 个 .ats 文件")
success = 0
failed = 0
for f in files:
basename = os.path.splitext(os.path.basename(f))[0]
output_path = os.path.join(output_dir, f"{basename}.ACP")
try:
convert_file(f, output_path)
print(f" ✓ {basename}.ats → {basename}.ACP")
success += 1
except Exception as e:
print(f" ✗ {basename}.ats 失败: {e}")
failed += 1
print(f"\n转换完成: 成功 {success}, 失败 {failed}")
print(f"输出目录: {output_dir}")