APS7100Controller.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708
  1. using System;
  2. using APS7100TestTool.Models;
  3. using APS7100TestTool.Services;
  4. namespace APS7100TestTool.Controllers
  5. {
  6. /// <summary>
  7. /// APS-7100 AC 可编程电源控制器
  8. ///
  9. /// ⚠ 重要说明:APS7100 使用完整的、层级严格的 SCPI 命令树
  10. /// 核心根节点:MEAS / SOUR / OUTP / STAT / DATA
  11. ///
  12. /// 命令特点:
  13. /// 1. 几乎都带二级/三级节点
  14. /// 2. 不支持简写的无子节点查询(如 OUTP? 不行,必须 OUTP:STAT?)
  15. /// 3. 测量命令必须走 SCALar 路径
  16. /// </summary>
  17. public class APS7100Controller : IPowerSupplyController
  18. {
  19. private readonly ScpiDevice _device;
  20. public DeviceType DeviceType => DeviceType.APS7100;
  21. public DeviceInfo DeviceInfo => DeviceInfo.GetDeviceInfo(DeviceType.APS7100);
  22. public APS7100Controller(ScpiDevice device)
  23. {
  24. _device = device ?? throw new ArgumentNullException(nameof(device));
  25. }
  26. #region IEEE 488.2 标准命令
  27. /// <summary>
  28. /// 获取设备识别信息
  29. /// ⚠ APS7100 在收到任何 SCPI 命令后会自动进入远程模式
  30. /// </summary>
  31. public string GetIdentification()
  32. {
  33. string result = _device.Query("*IDN?");
  34. // APS7100 收到 SCPI 命令后自动进入远程模式
  35. _isInRemoteMode = true;
  36. return result;
  37. }
  38. /// <summary>
  39. /// 重置设备到出厂状态
  40. /// </summary>
  41. public void Reset()
  42. {
  43. _device.SendCommand("*RST");
  44. System.Threading.Thread.Sleep(1000);
  45. }
  46. /// <summary>
  47. /// 清除状态寄存器
  48. /// </summary>
  49. public void ClearStatus()
  50. {
  51. _device.SendCommand("*CLS");
  52. }
  53. /// <summary>
  54. /// 等待所有挂起操作完成
  55. /// </summary>
  56. public void WaitComplete()
  57. {
  58. _device.SendCommand("*WAI");
  59. }
  60. /// <summary>
  61. /// 查询操作完成状态
  62. /// </summary>
  63. public bool QueryOperationComplete()
  64. {
  65. string result = _device.Query("*OPC?");
  66. return result.Trim() == "1";
  67. }
  68. /// <summary>
  69. /// 触发设备
  70. /// </summary>
  71. public void Trigger()
  72. {
  73. _device.SendCommand("*TRG");
  74. }
  75. /// <summary>
  76. /// 自检
  77. /// </summary>
  78. public int SelfTest()
  79. {
  80. string result = _device.Query("*TST?");
  81. return int.TryParse(result.Trim(), out int code) ? code : -1;
  82. }
  83. /// <summary>
  84. /// 保存设置到指定位置
  85. /// </summary>
  86. public void SaveSettings(int location)
  87. {
  88. _device.SendCommand($"*SAV {location}");
  89. }
  90. /// <summary>
  91. /// 从指定位置恢复设置
  92. /// </summary>
  93. public void RecallSettings(int location)
  94. {
  95. _device.SendCommand($"*RCL {location}");
  96. }
  97. /// <summary>
  98. /// 查询事件状态寄存器
  99. /// </summary>
  100. public int QueryEventStatusRegister()
  101. {
  102. string result = _device.Query("*ESR?");
  103. return int.TryParse(result.Trim(), out int value) ? value : 0;
  104. }
  105. /// <summary>
  106. /// 设置事件状态使能寄存器
  107. /// </summary>
  108. public void SetEventStatusEnable(int mask)
  109. {
  110. _device.SendCommand($"*ESE {mask}");
  111. }
  112. /// <summary>
  113. /// 查询状态字节
  114. /// </summary>
  115. public int QueryStatusByte()
  116. {
  117. string result = _device.Query("*STB?");
  118. return int.TryParse(result.Trim(), out int value) ? value : 0;
  119. }
  120. /// <summary>
  121. /// 设置服务请求使能
  122. /// </summary>
  123. public void SetServiceRequestEnable(int mask)
  124. {
  125. _device.SendCommand($"*SRE {mask}");
  126. }
  127. #endregion
  128. #region SYSTEM 系统命令
  129. // 内部状态跟踪,避免重复发送 SYST:REM 导致 SCPI Error
  130. private bool _isInRemoteMode = false;
  131. /// <summary>
  132. /// 进入远程控制模式
  133. /// ⚠ APS7100 如果已经是远程模式,再次发送 SYST:REM 会报错
  134. /// </summary>
  135. public void SetRemoteMode()
  136. {
  137. // 如果已知在远程模式,跳过
  138. if (_isInRemoteMode) return;
  139. try
  140. {
  141. _device.SendCommand("SYST:REM");
  142. _isInRemoteMode = true;
  143. }
  144. catch
  145. {
  146. // 如果报错,可能已经在远程模式,标记状态并忽略
  147. _isInRemoteMode = true;
  148. }
  149. }
  150. /// <summary>
  151. /// 返回本地控制模式
  152. /// </summary>
  153. public void SetLocalMode()
  154. {
  155. try
  156. {
  157. _device.SendCommand("SYST:COMM:RLST LOCAL");
  158. _isInRemoteMode = false;
  159. }
  160. catch
  161. {
  162. // 忽略错误
  163. _isInRemoteMode = false;
  164. }
  165. }
  166. /// <summary>
  167. /// 锁定/解锁前面板按键
  168. /// ⚠ APS7100 使用 KLOC 命令,不是 RWLOCK
  169. /// </summary>
  170. public void SetPanelLock(bool locked)
  171. {
  172. _device.SendCommand(locked ? "SYST:KLOC ON" : "SYST:KLOC OFF");
  173. }
  174. /// <summary>
  175. /// 查询系统错误
  176. /// </summary>
  177. public string QueryError()
  178. {
  179. return _device.Query("SYST:ERR?");
  180. }
  181. /// <summary>
  182. /// 清除系统错误队列
  183. /// </summary>
  184. public void ClearErrors()
  185. {
  186. // 循环读取直到没有错误
  187. try
  188. {
  189. for (int i = 0; i < 10; i++) // 最多清除 10 个错误
  190. {
  191. string error = _device.Query("SYST:ERR?");
  192. if (error.StartsWith("0") || error.Contains("No error"))
  193. break;
  194. }
  195. }
  196. catch { }
  197. }
  198. /// <summary>
  199. /// 查询 SCPI 版本
  200. /// </summary>
  201. public string QueryVersion()
  202. {
  203. return _device.Query("SYST:VERS?");
  204. }
  205. /// <summary>
  206. /// 查询当前是否为远程模式
  207. /// </summary>
  208. public bool IsRemoteMode()
  209. {
  210. // 返回内部跟踪的状态
  211. // 如果能通讯就说明至少连接正常
  212. try
  213. {
  214. _device.Query("*IDN?");
  215. return _isInRemoteMode;
  216. }
  217. catch
  218. {
  219. return false;
  220. }
  221. }
  222. #endregion
  223. #region OUTPUT 输出控制
  224. /// <summary>
  225. /// 设置输出开关
  226. /// ⚠ APS7100 必须使用 OUTPut:STATe,不支持 OUTP ON
  227. /// </summary>
  228. public void SetOutput(bool enable)
  229. {
  230. _device.SendCommand(enable ? "OUTP:STAT ON" : "OUTP:STAT OFF");
  231. }
  232. /// <summary>
  233. /// 获取输出状态
  234. /// ⚠ APS7100 必须使用 OUTPut:STATe?,不支持 OUTP?
  235. /// </summary>
  236. public bool GetOutputState()
  237. {
  238. string result = _device.Query("OUTP:STAT?");
  239. return result.Trim() == "1" || result.ToUpper().Contains("ON");
  240. }
  241. /// <summary>
  242. /// 清除输出保护状态
  243. /// </summary>
  244. public void ClearOutputProtection()
  245. {
  246. _device.SendCommand("OUTP:PROT:CLE");
  247. }
  248. #endregion
  249. #region SOURCE 源设置 - 电压
  250. /// <summary>
  251. /// 设置输出电压 (V)
  252. /// </summary>
  253. public void SetVoltage(double voltage)
  254. {
  255. _device.SendCommand($"SOUR:VOLT {voltage}");
  256. }
  257. /// <summary>
  258. /// 获取电压设定值 (V)
  259. /// </summary>
  260. public double GetVoltage()
  261. {
  262. string result = _device.Query("SOUR:VOLT?");
  263. return double.TryParse(result.Trim(), out double value) ? value : 0;
  264. }
  265. /// <summary>
  266. /// 设置电压量程
  267. /// ⚠ APS7100 量程选项:R155 / R310 / R600 / AUTO
  268. /// ❌ 不支持 LOW / HIGH
  269. /// </summary>
  270. public void SetVoltageRange(APS7100VoltageRange range)
  271. {
  272. string rangeStr = range switch
  273. {
  274. APS7100VoltageRange.R155 => "R155",
  275. APS7100VoltageRange.R310 => "R310",
  276. APS7100VoltageRange.R600 => "R600",
  277. APS7100VoltageRange.Auto => "AUTO",
  278. _ => "AUTO"
  279. };
  280. _device.SendCommand($"SOUR:VOLT:RANG {rangeStr}");
  281. }
  282. /// <summary>
  283. /// 获取电压量程
  284. /// </summary>
  285. public APS7100VoltageRange GetVoltageRangeAPS()
  286. {
  287. string result = _device.Query("SOUR:VOLT:RANG?").Trim().ToUpper();
  288. return result switch
  289. {
  290. "R155" or "155" => APS7100VoltageRange.R155,
  291. "R310" or "310" => APS7100VoltageRange.R310,
  292. "R600" or "600" => APS7100VoltageRange.R600,
  293. "AUTO" => APS7100VoltageRange.Auto,
  294. _ => APS7100VoltageRange.Auto
  295. };
  296. }
  297. /// <summary>
  298. /// 设置电压量程(接口兼容方法)
  299. /// ⚠ 注意:VoltageRange.Low 映射为 R155,High 映射为 R310
  300. /// </summary>
  301. public void SetVoltageRange(VoltageRange range)
  302. {
  303. // 接口兼容:Low -> R155, High -> R310
  304. SetVoltageRange(range == VoltageRange.Low ? APS7100VoltageRange.R155 : APS7100VoltageRange.R310);
  305. }
  306. /// <summary>
  307. /// 获取电压量程(接口兼容方法)
  308. /// </summary>
  309. public VoltageRange GetVoltageRange()
  310. {
  311. var range = GetVoltageRangeAPS();
  312. return range == APS7100VoltageRange.R155 ? VoltageRange.Low : VoltageRange.High;
  313. }
  314. #endregion
  315. #region SOURCE 源设置 - 频率
  316. /// <summary>
  317. /// 设置输出频率 (Hz)
  318. /// 常用值:50 / 60 / 400 Hz
  319. /// </summary>
  320. public void SetFrequency(double frequency)
  321. {
  322. _device.SendCommand($"SOUR:FREQ {frequency}");
  323. }
  324. /// <summary>
  325. /// 获取频率设定值 (Hz)
  326. /// </summary>
  327. public double GetFrequency()
  328. {
  329. string result = _device.Query("SOUR:FREQ?");
  330. return double.TryParse(result.Trim(), out double value) ? value : 50;
  331. }
  332. #endregion
  333. #region SOURCE 源设置 - 相位
  334. /// <summary>
  335. /// 设置输出相位 (度)
  336. /// 用于多相或并机控制
  337. /// </summary>
  338. public void SetPhase(double degrees)
  339. {
  340. _device.SendCommand($"SOUR:PHAS {degrees}");
  341. }
  342. /// <summary>
  343. /// 获取相位设定值 (度)
  344. /// </summary>
  345. public double GetPhase()
  346. {
  347. string result = _device.Query("SOUR:PHAS?");
  348. return double.TryParse(result.Trim(), out double value) ? value : 0;
  349. }
  350. #endregion
  351. #region SOURCE 源设置 - 电流限制
  352. /// <summary>
  353. /// 设置电流限值 (A)
  354. /// ⚠ APS7100 只有电流限制,没有"电流设定"概念
  355. /// 必须使用 SOURce:CURRent:LIMit
  356. /// </summary>
  357. public void SetCurrent(double current)
  358. {
  359. _device.SendCommand($"SOUR:CURR:LIM:RMS {current}");
  360. }
  361. /// <summary>
  362. /// 设置电流限值 (A) - 显式方法
  363. /// </summary>
  364. public void SetCurrentLimit(double current)
  365. {
  366. SetCurrent(current);
  367. }
  368. /// <summary>
  369. /// 获取电流限值 (A)
  370. /// ⚠ 必须使用 SOURce:CURRent:LIMit?
  371. /// ❌ SOUR:CURR? 不可用
  372. /// </summary>
  373. public double GetCurrent()
  374. {
  375. string result = _device.Query("SOUR:CURR:LIM:RMS?");
  376. return double.TryParse(result.Trim(), out double value) ? value : 0;
  377. }
  378. /// <summary>
  379. /// 获取电流限值 (A) - 显式方法
  380. /// </summary>
  381. public double GetCurrentLimit()
  382. {
  383. return GetCurrent();
  384. }
  385. #endregion
  386. #region MEASURE 测量命令
  387. /// <summary>
  388. /// 测量实际输出电压 (V RMS)
  389. /// ⚠ 必须使用 MEASure:SCALar 路径
  390. /// </summary>
  391. public double MeasureVoltage()
  392. {
  393. string result = _device.Query("MEAS:SCAL:VOLT?");
  394. return double.TryParse(result.Trim(), out double value) ? value : 0;
  395. }
  396. /// <summary>
  397. /// 测量实际输出电流 (A RMS)
  398. /// ⚠ 必须使用 MEASure:SCALar 路径
  399. /// </summary>
  400. public double MeasureCurrent()
  401. {
  402. string result = _device.Query("MEAS:SCAL:CURR?");
  403. return double.TryParse(result.Trim(), out double value) ? value : 0;
  404. }
  405. /// <summary>
  406. /// 测量实际频率 (Hz)
  407. /// ⚠ 必须使用 MEASure:SCALar 路径
  408. /// </summary>
  409. public double MeasureFrequency()
  410. {
  411. string result = _device.Query("MEAS:SCAL:FREQ?");
  412. return double.TryParse(result.Trim(), out double value) ? value : 0;
  413. }
  414. /// <summary>
  415. /// 测量有功功率 P (W)
  416. /// ⚠ 必须使用完整路径 MEASure:SCALar:POWer:AC:REAL?
  417. /// ❌ MEAS:POW? 不可用
  418. /// </summary>
  419. public double MeasurePower()
  420. {
  421. string result = _device.Query("MEAS:SCAL:POW:AC:REAL?");
  422. return double.TryParse(result.Trim(), out double value) ? value : 0;
  423. }
  424. /// <summary>
  425. /// 测量有功功率 P (W) - 别名
  426. /// </summary>
  427. public double MeasureRealPower()
  428. {
  429. return MeasurePower();
  430. }
  431. /// <summary>
  432. /// 测量视在功率 S (VA)
  433. /// </summary>
  434. public double MeasureApparentPower()
  435. {
  436. string result = _device.Query("MEAS:SCAL:POW:AC:APP?");
  437. return double.TryParse(result.Trim(), out double value) ? value : 0;
  438. }
  439. /// <summary>
  440. /// 测量功率因数 PF
  441. /// ⚠ 使用 MEASure:SCALar:POWer:AC:PFAC?
  442. /// ❌ MEAS:PF? 不可用
  443. /// </summary>
  444. public double MeasurePowerFactor()
  445. {
  446. string result = _device.Query("MEAS:SCAL:POW:AC:PFAC?");
  447. return double.TryParse(result.Trim(), out double value) ? value : 0;
  448. }
  449. #endregion
  450. #region INITIATE 触发/执行命令
  451. /// <summary>
  452. /// 立即执行(启动 Sequence / Simulation / 瞬态测试)
  453. /// </summary>
  454. public void InitiateImmediate()
  455. {
  456. _device.SendCommand("INIT:IMM");
  457. }
  458. /// <summary>
  459. /// 立即执行瞬态
  460. /// </summary>
  461. public void InitiateTransient()
  462. {
  463. _device.SendCommand("INIT:IMM:TRAN");
  464. }
  465. #endregion
  466. #region STATUS 状态命令
  467. /// <summary>
  468. /// 查询操作状态寄存器
  469. /// </summary>
  470. public int QueryOperationStatus()
  471. {
  472. string result = _device.Query("STAT:OPER?");
  473. return int.TryParse(result.Trim(), out int value) ? value : 0;
  474. }
  475. /// <summary>
  476. /// 查询可疑状态寄存器
  477. /// </summary>
  478. public int QueryQuestionableStatus()
  479. {
  480. string result = _device.Query("STAT:QUES?");
  481. return int.TryParse(result.Trim(), out int value) ? value : 0;
  482. }
  483. #endregion
  484. #region DATA/TRACE 序列和模拟命令
  485. /// <summary>
  486. /// 清除序列数据
  487. /// 用于:电压跌落、频率扫变、IEC测试波形等
  488. /// </summary>
  489. public void SequenceClear()
  490. {
  491. _device.SendCommand("DATA:SEQ:CLE");
  492. }
  493. /// <summary>
  494. /// 存储序列到指定位置
  495. /// </summary>
  496. public void SequenceStore(int index)
  497. {
  498. _device.SendCommand($"DATA:SEQ:STOR {index}");
  499. }
  500. /// <summary>
  501. /// 从指定位置调用序列
  502. /// </summary>
  503. public void SequenceRecall(int index)
  504. {
  505. _device.SendCommand($"DATA:SEQ:REC {index}");
  506. }
  507. /// <summary>
  508. /// 清除模拟数据
  509. /// </summary>
  510. public void SimulationClear()
  511. {
  512. _device.SendCommand("DATA:SIM:CLE");
  513. }
  514. /// <summary>
  515. /// 存储模拟到指定位置
  516. /// </summary>
  517. public void SimulationStore(int index)
  518. {
  519. _device.SendCommand($"DATA:SIM:STOR {index}");
  520. }
  521. /// <summary>
  522. /// 从指定位置调用模拟
  523. /// </summary>
  524. public void SimulationRecall(int index)
  525. {
  526. _device.SendCommand($"DATA:SIM:REC {index}");
  527. }
  528. #endregion
  529. #region 波形设置(APS7100 不支持传统波形)
  530. /// <summary>
  531. /// 设置波形 - APS7100 不支持传统波形设置
  532. /// ⚠ APS7100 使用 DATA:SEQuence 和 DATA:SIMulation 进行
  533. /// 电压跌落、频率扫变、IEC测试波形,不是 SINE/SQUARE
  534. /// </summary>
  535. public void SetWaveform(Waveform waveform)
  536. {
  537. // APS7100 不支持传统的 SOUR:WAVE 命令
  538. // 它的"波形"是通过序列和模拟功能实现的
  539. throw new NotSupportedException(
  540. "APS7100 不支持传统波形设置。" +
  541. "请使用 SequenceStore/SequenceRecall 或 SimulationStore/SimulationRecall 进行波形模拟。");
  542. }
  543. /// <summary>
  544. /// 获取当前波形 - APS7100 不支持
  545. /// </summary>
  546. public Waveform GetWaveform()
  547. {
  548. throw new NotSupportedException(
  549. "APS7100 不支持传统波形查询。输出始终为正弦波。");
  550. }
  551. #endregion
  552. #region 自定义命令
  553. /// <summary>
  554. /// 发送自定义 SCPI 命令
  555. /// </summary>
  556. public void SendCustomCommand(string command)
  557. {
  558. _device.SendCommand(command);
  559. }
  560. /// <summary>
  561. /// 发送自定义 SCPI 查询
  562. /// </summary>
  563. public string SendCustomQuery(string command)
  564. {
  565. return _device.Query(command);
  566. }
  567. #endregion
  568. }
  569. #region APS7100 专用枚举
  570. /// <summary>
  571. /// APS7100 电压量程
  572. /// ⚠ 注意:与其他设备不同,APS7100 使用 R155/R310/R600/AUTO
  573. /// </summary>
  574. public enum APS7100VoltageRange
  575. {
  576. /// <summary>0-155V 量程</summary>
  577. R155,
  578. /// <summary>0-310V 量程</summary>
  579. R310,
  580. /// <summary>0-600V 量程(如果支持)</summary>
  581. R600,
  582. /// <summary>自动量程</summary>
  583. Auto
  584. }
  585. #endregion
  586. #region 通用枚举(接口兼容)
  587. /// <summary>
  588. /// 电压量程(通用接口)
  589. /// </summary>
  590. public enum VoltageRange
  591. {
  592. /// <summary>低档量程</summary>
  593. Low,
  594. /// <summary>高档量程</summary>
  595. High
  596. }
  597. /// <summary>
  598. /// 波形类型(通用接口)
  599. /// ⚠ 注意:APS7100 不支持波形设置,仅用于接口兼容
  600. /// </summary>
  601. public enum Waveform
  602. {
  603. /// <summary>正弦波</summary>
  604. Sine,
  605. /// <summary>方波</summary>
  606. Square,
  607. /// <summary>三角波</summary>
  608. Triangle
  609. }
  610. #endregion
  611. }