MainForm.cs 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877
  1. using System;
  2. using System.Drawing;
  3. using System.Drawing.Imaging;
  4. using System.IO;
  5. using System.Runtime.InteropServices;
  6. using System.Threading;
  7. using System.Windows.Forms;
  8. using MVSDK_Net;
  9. using VM.Core;
  10. using VM.PlatformSDKCS;
  11. using HuarayVMCore;
  12. using HuarayVMCore.Camera;
  13. using HuarayVMCore.Core;
  14. using HuarayVMCore.VisionMaster;
  15. using ImageConverter = HuarayVMCore.Core.ImageConverter;
  16. namespace HuarayVMDemo.Forms
  17. {
  18. /// <summary>
  19. /// 主窗体。
  20. /// 集成华睿相机采集和海康VisionMaster视觉检测的演示界面。
  21. /// </summary>
  22. public partial class MainForm : Form
  23. {
  24. #region 字段
  25. /// <summary>华睿相机实例</summary>
  26. private HuarayCamera _camera;
  27. /// <summary>VM桥接实例</summary>
  28. private VMBridge _vmBridge;
  29. /// <summary>枚举到的设备列表</summary>
  30. private System.Collections.Generic.List<CameraDeviceInfo> _deviceList;
  31. /// <summary>是否正在连续执行检测</summary>
  32. private bool _isContinuousRunning = false;
  33. /// <summary>连续执行线程</summary>
  34. private Thread _continuousThread;
  35. /// <summary>UI更新用的同步锁</summary>
  36. private readonly object _uiLock = new object();
  37. /// <summary>跨线程更新PictureBox的委托</summary>
  38. private delegate void UpdateImageDelegate(PictureBox pic, Bitmap bmp);
  39. /// <summary>跨线程更新Control文本的委托</summary>
  40. private delegate void UpdateTextDelegate(Control ctrl, string text);
  41. /// <summary>跨线程更新ToolStripItem文本的委托</summary>
  42. private delegate void UpdateToolStripItemTextDelegate(ToolStripItem ctrl, string text);
  43. /// <summary>跨线程追加日志的委托</summary>
  44. private delegate void AppendLogDelegate(string text);
  45. #endregion
  46. #region 构造函数
  47. /// <summary>
  48. /// 构造函数
  49. /// </summary>
  50. public MainForm()
  51. {
  52. InitializeComponent();
  53. _camera = new HuarayCamera();
  54. _vmBridge = new VMBridge();
  55. // 注册相机事件
  56. _camera.FrameReceived += OnFrameReceived;
  57. _camera.ConnectionChanged += OnConnectionChanged;
  58. _camera.StreamError += OnStreamError;
  59. // 配置日志助手
  60. LogHelper.EnableConsoleOutput = false;
  61. LogHelper.EnableFileOutput = true;
  62. LogHelper.MinLogLevel = LogLevel.Debug;
  63. // 重定向日志到UI
  64. LogHelper.LogInfo("系统集成初始化完成");
  65. AppendLog("系统初始化完成,请枚举设备并连接相机");
  66. }
  67. #endregion
  68. #region 相机事件处理
  69. /// <summary>
  70. /// 帧接收事件处理(异步回调取图模式)
  71. /// </summary>
  72. private void OnFrameReceived(object sender, FrameReceivedEventArgs e)
  73. {
  74. if (IsDisposed || !IsHandleCreated)
  75. return;
  76. // 在UI线程显示图像
  77. if (chkContinuousDisplay.Checked)
  78. {
  79. try
  80. {
  81. Bitmap bmp = ImageConverter.ToBitmap(e);
  82. UpdatePictureBox(picCameraView, bmp);
  83. }
  84. catch (Exception ex)
  85. {
  86. LogHelper.LogError("图像显示失败: " + ex.Message);
  87. }
  88. }
  89. // 如果正在连续执行VM检测,将图像送入VM
  90. if (_isContinuousRunning && _vmBridge.IsLoaded)
  91. {
  92. try
  93. {
  94. // 转换为VM图像数据并送入
  95. ImageBaseData imageData = ImageConverter.ToImageBaseData(e);
  96. _vmBridge.SetImageData(
  97. txtFlowName.Text,
  98. txtImageSourceName.Text,
  99. imageData);
  100. // 执行流程
  101. _vmBridge.RunProcedure(txtFlowName.Text);
  102. // 获取结果图像并显示
  103. ImageBaseData resultImage = _vmBridge.GetProcedureOutputImage(
  104. txtFlowName.Text, "ImageData");
  105. if (resultImage != null)
  106. {
  107. Bitmap resultBmp = ImageBaseDataToBitmap(resultImage);
  108. UpdatePictureBox(picVMResultView, resultBmp);
  109. }
  110. UpdateStatusLabel(lblRunStatus, "检测完成 - 帧#" + e.FrameNumber);
  111. }
  112. catch (VMBridgeException ex)
  113. {
  114. AppendLog("VM检测异常: " + ex.Message);
  115. }
  116. catch (Exception ex)
  117. {
  118. AppendLog("图像处理异常: " + ex.Message);
  119. }
  120. }
  121. }
  122. /// <summary>
  123. /// 连接状态变化事件处理
  124. /// </summary>
  125. private void OnConnectionChanged(object sender, CameraConnectionEventArgs e)
  126. {
  127. string status = e.IsConnected ? "已连接" : "已断开";
  128. AppendLog("相机连接状态: " + status);
  129. UpdateStatusLabel(tsslStatus, "相机" + status);
  130. if (!e.IsConnected)
  131. {
  132. UpdateUIState(false, false);
  133. }
  134. }
  135. /// <summary>
  136. /// 流异常事件处理
  137. /// </summary>
  138. private void OnStreamError(object sender, CameraStreamErrorEventArgs e)
  139. {
  140. AppendLog("流异常: " + e.ErrorMessage);
  141. }
  142. #endregion
  143. #region 相机控制按钮事件
  144. /// <summary>
  145. /// 枚举设备按钮
  146. /// </summary>
  147. private void btnRefreshDevices_Click(object sender, EventArgs e)
  148. {
  149. try
  150. {
  151. cmbDeviceList.Items.Clear();
  152. AppendLog("正在枚举设备...");
  153. _deviceList = HuarayCamera.EnumerateDevices();
  154. if (_deviceList.Count == 0)
  155. {
  156. AppendLog("未找到任何相机设备");
  157. return;
  158. }
  159. foreach (var dev in _deviceList)
  160. {
  161. string display = string.Format("[{0}] {1} ({2})",
  162. dev.InterfaceType, dev.ModelName, dev.SerialNumber);
  163. cmbDeviceList.Items.Add(display);
  164. }
  165. cmbDeviceList.SelectedIndex = 0;
  166. AppendLog(string.Format("找到 {0} 个设备", _deviceList.Count));
  167. }
  168. catch (Exception ex)
  169. {
  170. AppendLog("枚举设备失败: " + ex.Message);
  171. LogHelper.LogError("枚举设备失败", ex);
  172. }
  173. }
  174. /// <summary>
  175. /// 连接相机按钮
  176. /// </summary>
  177. private void btnConnectCamera_Click(object sender, EventArgs e)
  178. {
  179. if (cmbDeviceList.SelectedIndex < 0 || _deviceList == null || _deviceList.Count == 0)
  180. {
  181. MessageBox.Show("请先枚举设备并选择一个相机", "提示");
  182. return;
  183. }
  184. try
  185. {
  186. int index = cmbDeviceList.SelectedIndex;
  187. CameraDeviceInfo devInfo = _deviceList[index];
  188. AppendLog("正在连接相机: " + devInfo.ModelName);
  189. _camera.Open(devInfo);
  190. AppendLog("相机连接成功: " + devInfo.ModelName + " (SN: " + devInfo.SerialNumber + ")");
  191. // 读取当前参数显示到界面
  192. txtExposure.Text = _camera.GetExposureTime().ToString("F1");
  193. txtGain.Text = _camera.GetGain().ToString("F2");
  194. UpdateUIState(true, false);
  195. UpdateStatusLabel(tsslStatus, "相机已连接");
  196. }
  197. catch (Exception ex)
  198. {
  199. AppendLog("连接相机失败: " + ex.Message);
  200. LogHelper.LogError("连接相机失败", ex);
  201. }
  202. }
  203. /// <summary>
  204. /// 断开相机按钮
  205. /// </summary>
  206. private void btnDisconnectCamera_Click(object sender, EventArgs e)
  207. {
  208. try
  209. {
  210. if (_camera.IsGrabbing)
  211. {
  212. _camera.StopGrabbing();
  213. }
  214. _camera.Close();
  215. AppendLog("相机已断开");
  216. UpdateUIState(false, false);
  217. picCameraView.Image = null;
  218. UpdateStatusLabel(tsslStatus, "相机已断开");
  219. }
  220. catch (Exception ex)
  221. {
  222. AppendLog("断开相机失败: " + ex.Message);
  223. }
  224. }
  225. /// <summary>
  226. /// 设置曝光时间
  227. /// </summary>
  228. private void btnSetExposure_Click(object sender, EventArgs e)
  229. {
  230. try
  231. {
  232. double exposure = double.Parse(txtExposure.Text);
  233. _camera.SetDoubleFeatureValue("ExposureTime", exposure);
  234. AppendLog(string.Format("曝光时间已设置: {0:F1} μs", exposure));
  235. }
  236. catch (FormatException)
  237. {
  238. MessageBox.Show("请输入有效的数字", "提示");
  239. }
  240. catch (Exception ex)
  241. {
  242. AppendLog("设置曝光失败: " + ex.Message);
  243. }
  244. }
  245. /// <summary>
  246. /// 设置增益
  247. /// </summary>
  248. private void btnSetGain_Click(object sender, EventArgs e)
  249. {
  250. try
  251. {
  252. double gain = double.Parse(txtGain.Text);
  253. _camera.SetGain(gain);
  254. AppendLog(string.Format("增益已设置: {0:F2}", gain));
  255. }
  256. catch (FormatException)
  257. {
  258. MessageBox.Show("请输入有效的数字", "提示");
  259. }
  260. catch (Exception ex)
  261. {
  262. AppendLog("设置增益失败: " + ex.Message);
  263. }
  264. }
  265. /// <summary>
  266. /// 触发模式切换
  267. /// </summary>
  268. private void cmbTriggerMode_SelectedIndexChanged(object sender, EventArgs e)
  269. {
  270. if (!_camera.IsOpened)
  271. return;
  272. try
  273. {
  274. if (cmbTriggerMode.SelectedIndex == 0)
  275. {
  276. // 连续采集
  277. _camera.SetTriggerMode(TriggerMode.Continuous);
  278. btnSoftwareTrigger.Enabled = false;
  279. AppendLog("触发模式: 连续采集");
  280. }
  281. else
  282. {
  283. // 软触发
  284. _camera.SetTriggerMode(TriggerMode.Software, TriggerSource.Software);
  285. btnSoftwareTrigger.Enabled = true;
  286. AppendLog("触发模式: 软触发");
  287. }
  288. }
  289. catch (Exception ex)
  290. {
  291. AppendLog("设置触发模式失败: " + ex.Message);
  292. }
  293. }
  294. /// <summary>
  295. /// 开始采集
  296. /// </summary>
  297. private void btnStartGrabbing_Click(object sender, EventArgs e)
  298. {
  299. try
  300. {
  301. // 设置触发模式
  302. if (cmbTriggerMode.SelectedIndex == 0)
  303. {
  304. _camera.SetTriggerMode(TriggerMode.Continuous);
  305. }
  306. else
  307. {
  308. _camera.SetTriggerMode(TriggerMode.Software, TriggerSource.Software);
  309. }
  310. // 设置缓冲区数量
  311. _camera.SetBufferCount(30);
  312. // 开始采集
  313. _camera.StartGrabbing();
  314. AppendLog("开始采集图像");
  315. btnStartGrabbing.Enabled = false;
  316. btnStopGrabbing.Enabled = true;
  317. UpdateStatusLabel(tsslStatus, "正在采集...");
  318. }
  319. catch (Exception ex)
  320. {
  321. AppendLog("开始采集失败: " + ex.Message);
  322. LogHelper.LogError("开始采集失败", ex);
  323. }
  324. }
  325. /// <summary>
  326. /// 停止采集
  327. /// </summary>
  328. private void btnStopGrabbing_Click(object sender, EventArgs e)
  329. {
  330. try
  331. {
  332. _camera.StopGrabbing();
  333. AppendLog("停止采集");
  334. btnStartGrabbing.Enabled = true;
  335. btnStopGrabbing.Enabled = false;
  336. UpdateStatusLabel(tsslStatus, "已停止采集");
  337. }
  338. catch (Exception ex)
  339. {
  340. AppendLog("停止采集失败: " + ex.Message);
  341. }
  342. }
  343. /// <summary>
  344. /// 软触发
  345. /// </summary>
  346. private void btnSoftwareTrigger_Click(object sender, EventArgs e)
  347. {
  348. try
  349. {
  350. _camera.TriggerSoftware();
  351. AppendLog("软触发已发送");
  352. }
  353. catch (Exception ex)
  354. {
  355. AppendLog("软触发失败: " + ex.Message);
  356. }
  357. }
  358. #endregion
  359. #region VisionMaster控制按钮事件
  360. /// <summary>
  361. /// 浏览方案文件
  362. /// </summary>
  363. private void btnBrowseSolution_Click(object sender, EventArgs e)
  364. {
  365. using (OpenFileDialog ofd = new OpenFileDialog())
  366. {
  367. ofd.Filter = "VM方案文件 (*.sol)|*.sol|所有文件 (*.*)|*.*";
  368. ofd.Title = "选择VisionMaster方案文件";
  369. if (ofd.ShowDialog() == DialogResult.OK)
  370. {
  371. txtSolutionPath.Text = ofd.FileName;
  372. }
  373. }
  374. }
  375. /// <summary>
  376. /// 加载VM方案
  377. /// </summary>
  378. private void btnLoadSolution_Click(object sender, EventArgs e)
  379. {
  380. if (string.IsNullOrEmpty(txtSolutionPath.Text))
  381. {
  382. MessageBox.Show("请先选择方案文件", "提示");
  383. return;
  384. }
  385. try
  386. {
  387. AppendLog("正在加载VM方案: " + txtSolutionPath.Text);
  388. _vmBridge.LoadSolution(txtSolutionPath.Text, "");
  389. // 设置图像输入模块为SDK模式
  390. _vmBridge.SetImageSourceToSDK(
  391. txtFlowName.Text,
  392. txtImageSourceName.Text,
  393. chkOutMono8.Checked);
  394. AppendLog("VM方案加载成功,图像输入已设置为SDK模式");
  395. UpdateStatusLabel(tsslStatus, "VM方案已加载");
  396. btnLoadSolution.Enabled = false;
  397. btnUnloadSolution.Enabled = true;
  398. btnRunOnce.Enabled = true;
  399. btnStartContinuous.Enabled = true;
  400. }
  401. catch (VMBridgeException ex)
  402. {
  403. AppendLog("加载VM方案失败: " + ex.Message);
  404. MessageBox.Show("加载VM方案失败:\n" + ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
  405. }
  406. catch (Exception ex)
  407. {
  408. AppendLog("加载VM方案异常: " + ex.Message);
  409. MessageBox.Show("加载VM方案异常:\n" + ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
  410. }
  411. }
  412. /// <summary>
  413. /// 卸载VM方案
  414. /// </summary>
  415. private void btnUnloadSolution_Click(object sender, EventArgs e)
  416. {
  417. try
  418. {
  419. // 先停止连续执行
  420. if (_isContinuousRunning)
  421. {
  422. StopContinuousRun();
  423. }
  424. _vmBridge.UnloadSolution();
  425. AppendLog("VM方案已卸载");
  426. btnLoadSolution.Enabled = true;
  427. btnUnloadSolution.Enabled = false;
  428. btnRunOnce.Enabled = false;
  429. btnStartContinuous.Enabled = false;
  430. picVMResultView.Image = null;
  431. UpdateStatusLabel(tsslStatus, "VM方案已卸载");
  432. }
  433. catch (Exception ex)
  434. {
  435. AppendLog("卸载VM方案异常: " + ex.Message);
  436. }
  437. }
  438. /// <summary>
  439. /// 单次执行(采图+检测)
  440. /// </summary>
  441. private void btnRunOnce_Click(object sender, EventArgs e)
  442. {
  443. if (!_camera.IsOpened)
  444. {
  445. MessageBox.Show("请先连接相机", "提示");
  446. return;
  447. }
  448. if (!_vmBridge.IsLoaded)
  449. {
  450. MessageBox.Show("请先加载VM方案", "提示");
  451. return;
  452. }
  453. try
  454. {
  455. UpdateStatusLabel(lblRunStatus, "正在采图...");
  456. // 采图(同步方式)
  457. FrameReceivedEventArgs frame = _camera.GetOneFrame(5000);
  458. if (frame == null)
  459. {
  460. AppendLog("采图超时,未获取到图像");
  461. UpdateStatusLabel(lblRunStatus, "采图超时");
  462. return;
  463. }
  464. AppendLog(string.Format("采图成功: {0}x{1}, 帧#{2}", frame.Width, frame.Height, frame.FrameNumber));
  465. // 显示采集图像
  466. Bitmap srcBmp = ImageConverter.ToBitmap(frame);
  467. UpdatePictureBox(picCameraView, srcBmp);
  468. UpdateStatusLabel(lblRunStatus, "正在执行检测...");
  469. // 转换为VM图像数据
  470. ImageBaseData imageData = ImageConverter.ToImageBaseData(frame);
  471. // 送入VM
  472. _vmBridge.SetImageData(
  473. txtFlowName.Text,
  474. txtImageSourceName.Text,
  475. imageData);
  476. // 执行流程
  477. _vmBridge.RunProcedure(txtFlowName.Text);
  478. AppendLog("VM检测执行完成");
  479. // 获取结果图像
  480. try
  481. {
  482. ImageBaseData resultImage = _vmBridge.GetProcedureOutputImage(
  483. txtFlowName.Text, "ImageData");
  484. if (resultImage != null)
  485. {
  486. Bitmap resultBmp = ImageBaseDataToBitmap(resultImage);
  487. UpdatePictureBox(picVMResultView, resultBmp);
  488. AppendLog("已获取检测结果图像");
  489. }
  490. }
  491. catch (VMBridgeException ex)
  492. {
  493. AppendLog("获取结果图像失败: " + ex.Message);
  494. }
  495. UpdateStatusLabel(lblRunStatus, "检测完成");
  496. }
  497. catch (Exception ex)
  498. {
  499. AppendLog("单次执行失败: " + ex.Message);
  500. LogHelper.LogError("单次执行失败", ex);
  501. UpdateStatusLabel(lblRunStatus, "检测失败");
  502. }
  503. }
  504. /// <summary>
  505. /// 开始连续执行
  506. /// </summary>
  507. private void btnStartContinuous_Click(object sender, EventArgs e)
  508. {
  509. if (!_camera.IsOpened)
  510. {
  511. MessageBox.Show("请先连接相机", "提示");
  512. return;
  513. }
  514. if (!_vmBridge.IsLoaded)
  515. {
  516. MessageBox.Show("请先加载VM方案", "提示");
  517. return;
  518. }
  519. if (_isContinuousRunning)
  520. return;
  521. _isContinuousRunning = true;
  522. // 确保相机在连续采集模式
  523. if (!_camera.IsGrabbing)
  524. {
  525. cmbTriggerMode.SelectedIndex = 0; // 切换到连续采集
  526. try
  527. {
  528. _camera.SetTriggerMode(TriggerMode.Continuous);
  529. _camera.SetBufferCount(30);
  530. _camera.StartGrabbing();
  531. btnStartGrabbing.Enabled = false;
  532. btnStopGrabbing.Enabled = true;
  533. }
  534. catch (Exception ex)
  535. {
  536. AppendLog("启动采集失败: " + ex.Message);
  537. _isContinuousRunning = false;
  538. return;
  539. }
  540. }
  541. btnStartContinuous.Enabled = false;
  542. btnStopContinuous.Enabled = true;
  543. btnRunOnce.Enabled = false;
  544. AppendLog("开始连续检测");
  545. UpdateStatusLabel(lblRunStatus, "连续检测中...");
  546. UpdateStatusLabel(tsslStatus, "连续检测运行中");
  547. }
  548. /// <summary>
  549. /// 停止连续执行
  550. /// </summary>
  551. private void btnStopContinuous_Click(object sender, EventArgs e)
  552. {
  553. StopContinuousRun();
  554. }
  555. /// <summary>
  556. /// 停止连续执行(内部方法)
  557. /// </summary>
  558. private void StopContinuousRun()
  559. {
  560. _isContinuousRunning = false;
  561. btnStartContinuous.Enabled = true;
  562. btnStopContinuous.Enabled = false;
  563. btnRunOnce.Enabled = true;
  564. AppendLog("已停止连续检测");
  565. UpdateStatusLabel(lblRunStatus, "已停止连续检测");
  566. UpdateStatusLabel(tsslStatus, "连续检测已停止");
  567. }
  568. #endregion
  569. #region 日志和通用按钮事件
  570. /// <summary>
  571. /// 清空日志
  572. /// </summary>
  573. private void btnClearLog_Click(object sender, EventArgs e)
  574. {
  575. txtLog.Clear();
  576. }
  577. #endregion
  578. #region 窗体事件
  579. /// <summary>
  580. /// 窗体关闭事件
  581. /// </summary>
  582. private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
  583. {
  584. try
  585. {
  586. // 停止连续执行
  587. if (_isContinuousRunning)
  588. {
  589. StopContinuousRun();
  590. }
  591. // 停止采集
  592. if (_camera != null && _camera.IsGrabbing)
  593. {
  594. _camera.StopGrabbing();
  595. }
  596. // 关闭相机
  597. if (_camera != null && _camera.IsOpened)
  598. {
  599. _camera.Close();
  600. }
  601. // 卸载VM方案
  602. if (_vmBridge != null && _vmBridge.IsLoaded)
  603. {
  604. _vmBridge.UnloadSolution();
  605. }
  606. // 释放资源
  607. _camera?.Dispose();
  608. _vmBridge?.Dispose();
  609. LogHelper.LogInfo("系统资源已释放,程序退出");
  610. }
  611. catch (Exception ex)
  612. {
  613. LogHelper.LogError("程序退出时异常", ex);
  614. }
  615. }
  616. #endregion
  617. #region 辅助方法
  618. /// <summary>
  619. /// 更新PictureBox图像(线程安全)
  620. /// </summary>
  621. private void UpdatePictureBox(PictureBox pic, Bitmap bmp)
  622. {
  623. if (pic.InvokeRequired)
  624. {
  625. pic.BeginInvoke(new UpdateImageDelegate(UpdatePictureBox), pic, bmp);
  626. return;
  627. }
  628. Bitmap oldBmp = pic.Image as Bitmap;
  629. pic.Image = bmp;
  630. oldBmp?.Dispose();
  631. }
  632. /// <summary>
  633. /// 更新控件文本(线程安全,Control版本)
  634. /// </summary>
  635. private void UpdateStatusLabel(Control ctrl, string text)
  636. {
  637. if (ctrl.InvokeRequired)
  638. {
  639. ctrl.BeginInvoke(new UpdateTextDelegate(UpdateStatusLabel), ctrl, text);
  640. return;
  641. }
  642. ctrl.Text = text;
  643. }
  644. /// <summary>
  645. /// 更新状态栏文本(线程安全,ToolStripItem版本)
  646. /// </summary>
  647. private void UpdateStatusLabel(ToolStripItem ctrl, string text)
  648. {
  649. if (InvokeRequired)
  650. {
  651. BeginInvoke(new UpdateToolStripItemTextDelegate(UpdateStatusLabel), ctrl, text);
  652. return;
  653. }
  654. ctrl.Text = text;
  655. }
  656. /// <summary>
  657. /// 追加日志(线程安全)
  658. /// </summary>
  659. private void AppendLog(string text)
  660. {
  661. if (txtLog.InvokeRequired)
  662. {
  663. txtLog.BeginInvoke(new AppendLogDelegate(AppendLog), text);
  664. return;
  665. }
  666. string logLine = string.Format("[{0:HH:mm:ss.fff}] {1}", DateTime.Now, text);
  667. txtLog.AppendText(logLine + Environment.NewLine);
  668. if (chkAutoScroll.Checked)
  669. {
  670. txtLog.SelectionStart = txtLog.TextLength;
  671. txtLog.ScrollToCaret();
  672. }
  673. // 同时写入文件日志
  674. LogHelper.LogInfo(text);
  675. }
  676. /// <summary>
  677. /// 更新UI控件状态
  678. /// </summary>
  679. private void UpdateUIState(bool cameraConnected, bool isGrabbing)
  680. {
  681. if (InvokeRequired)
  682. {
  683. BeginInvoke(new Action(() => UpdateUIState(cameraConnected, isGrabbing)));
  684. return;
  685. }
  686. btnConnectCamera.Enabled = !cameraConnected;
  687. btnDisconnectCamera.Enabled = cameraConnected;
  688. btnRefreshDevices.Enabled = !cameraConnected;
  689. cmbDeviceList.Enabled = !cameraConnected;
  690. btnSetExposure.Enabled = cameraConnected;
  691. btnSetGain.Enabled = cameraConnected;
  692. cmbTriggerMode.Enabled = cameraConnected;
  693. btnStartGrabbing.Enabled = cameraConnected && !isGrabbing;
  694. btnStopGrabbing.Enabled = cameraConnected && isGrabbing;
  695. }
  696. /// <summary>
  697. /// 将VM的ImageBaseData转换为Bitmap用于显示
  698. /// </summary>
  699. private Bitmap ImageBaseDataToBitmap(ImageBaseData imageData)
  700. {
  701. if (imageData == null)
  702. return null;
  703. // 通过反射获取ImageBaseData的内部数据
  704. // ImageBaseData可能不直接暴露byte[],需要通过反射或已知接口获取
  705. // VM SDK中ImageBaseData通常包含: Data(byte[]), Width, Height, PixelFormat等
  706. var type = imageData.GetType();
  707. // 尝试获取Data属性
  708. var dataProp = type.GetProperty("Data");
  709. var widthProp = type.GetProperty("Width") ?? type.GetProperty("ImageWidth");
  710. var heightProp = type.GetProperty("Height") ?? type.GetProperty("ImageHeight");
  711. var formatProp = type.GetProperty("PixelFormat");
  712. if (dataProp == null)
  713. {
  714. // 如果无法通过反射获取,尝试使用VM SDK提供的方式
  715. // VM SDK可能提供GetImage方法或直接可以转换为Bitmap
  716. return null;
  717. }
  718. byte[] data = dataProp.GetValue(imageData) as byte[];
  719. int width = (int)(widthProp?.GetValue(imageData) ?? 0);
  720. int height = (int)(heightProp?.GetValue(imageData) ?? 0);
  721. int pixelFormat = (int)(formatProp?.GetValue(imageData) ?? 0);
  722. if (data == null || width <= 0 || height <= 0)
  723. return null;
  724. // 根据像素格式转换为Bitmap
  725. // PixelFormatF.MONO8 = 0x01080001
  726. // PixelFormatF.BGR8 = 0x02180003
  727. if (pixelFormat == 0x01080001 || pixelFormat == (int)VMPixelFormat.VM_PIXEL_MONO_08)
  728. {
  729. return ImageConverter.Mono8ToBitmap(data, width, height);
  730. }
  731. else
  732. {
  733. // 默认按BGR8处理
  734. return ImageConverter.BGR8ToBitmap(data, width, height);
  735. }
  736. }
  737. #endregion
  738. private void button1_Click(object sender, EventArgs e)
  739. {
  740. _camera.SetTriggerMode(TriggerMode.Software, TriggerSource.Software);
  741. // 设置缓冲区数量
  742. _camera.SetBufferCount(30);
  743. // 开始采集
  744. _camera.StartGrabbing();
  745. Thread.Sleep(500);//等待4s取图
  746. _camera.TriggerSoftware();
  747. }
  748. }
  749. }