#!/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=null,IsUseVar=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}")