CameraParamsViewModel.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621
  1. using Cognex.VisionPro;
  2. using MaterialDesignThemes.Wpf;
  3. using Prism.Commands;
  4. using Prism.Events;
  5. using Prism.Ioc;
  6. using Prism.Mvvm;
  7. using Prism.Regions;
  8. using Prism.Services.Dialogs;
  9. using System;
  10. using System.Collections.Generic;
  11. using System.Collections.ObjectModel;
  12. using System.Drawing;
  13. using System.Linq;
  14. using System.Threading;
  15. using System.Threading.Channels;
  16. using System.Threading.Tasks;
  17. using System.Windows;
  18. using System.Windows.Threading;
  19. using Team.FFFeederService.Interfaces;
  20. using TeamAAS_VP;
  21. using TeamAAS_VP.Controls;
  22. using TeamAAS_VP.Core;
  23. using TeamAAS_VP.Data;
  24. using TeamAAS_VP.Enums;
  25. using TeamAAS_VP.Events;
  26. using TeamAAS_VP.Interfaces;
  27. using TeamAAS_VP.Models;
  28. using TeamAAS_VP.Models.Lights;
  29. using TeamAAS_VP.Resources.Languages;
  30. using TeamAAS_VP.Services;
  31. using static NPOI.HSSF.Util.HSSFColor;
  32. namespace TeamAAS_VP.ViewModels.Product
  33. {
  34. public class CameraParamsViewModel : BindableBase, IConfirmNavigationRequest
  35. {
  36. IRegionManager _regionManager;
  37. IEventAggregator _eventAggregator;
  38. IContainerProvider _container;
  39. IDialogService _dialogService;
  40. IConfigService _configService;
  41. IRobotService _robotService;
  42. ICameraService _cameraService;
  43. IFeederService _feederService;
  44. IProductService _productService;
  45. IRemoteCommandService _remoteCommandService;
  46. ISystemDatabaseService _systemDatabaseService;
  47. ILightManagerService _lightManagerService;
  48. // 创建Channel
  49. private readonly Channel<(int channel, int brightness)> _brightnessChannel;
  50. private CancellationTokenSource _cancellationTokenSource;
  51. #region 属性
  52. private ProductModel _SelectProduct;
  53. /// <summary>
  54. /// 选中的产品
  55. /// </summary>
  56. public ProductModel SelectProduct
  57. {
  58. get { return _SelectProduct; }
  59. set { SetProperty(ref _SelectProduct, value); }
  60. }
  61. private ProcedureModel _SelectProcedure;
  62. /// <summary>
  63. /// 选中的流程
  64. /// </summary>
  65. public ProcedureModel SelectProcedure
  66. {
  67. get { return _SelectProcedure; }
  68. set { SetProperty(ref _SelectProcedure, value); }
  69. }
  70. private bool _IsHaveFeeder=false;
  71. public bool IsHaveFeeder
  72. {
  73. get { return _IsHaveFeeder; }
  74. set { SetProperty(ref _IsHaveFeeder, value); }
  75. }
  76. private bool _IsHaveRobot = false;
  77. public bool IsHaveRobot
  78. {
  79. get { return _IsHaveRobot; }
  80. set { SetProperty(ref _IsHaveRobot, value); }
  81. }
  82. private IRobot _Robot;
  83. public IRobot Robot
  84. {
  85. get { return _Robot; }
  86. set { SetProperty(ref _Robot, value); }
  87. }
  88. private ICamera _Camera;
  89. public ICamera Camera
  90. {
  91. get { return _Camera; }
  92. set { SetProperty(ref _Camera, value); }
  93. }
  94. public IVoiceCoilMotorFeeder Feeder { get; set; }
  95. private ICogImage _Image;
  96. public ICogImage Image
  97. {
  98. get { return _Image; }
  99. set { SetProperty(ref _Image, value); }
  100. }
  101. private string _Message;
  102. public string Message
  103. {
  104. get { return _Message; }
  105. set { SetProperty(ref _Message, value); }
  106. }
  107. private float _ExposureTime;
  108. /// <summary>
  109. /// 相机曝光时间
  110. /// </summary>
  111. public float ExposureTime
  112. {
  113. get { return _ExposureTime; }
  114. set { SetProperty(ref _ExposureTime, value);
  115. SelectProcedure.ExposureTime = value;
  116. if (Camera != null)
  117. {
  118. try
  119. {
  120. Camera.SetExposureTime(value);
  121. }
  122. catch (Exception)
  123. {
  124. }
  125. }
  126. }
  127. }
  128. private float _Gain;
  129. /// <summary>
  130. /// 相机增益
  131. /// </summary>
  132. public float Gain
  133. {
  134. get { return _Gain; }
  135. set { SetProperty(ref _Gain, value);
  136. SelectProcedure.Gain=value;
  137. if (Camera != null)
  138. {
  139. try
  140. {
  141. Camera.SetGain(value);
  142. }
  143. catch (Exception)
  144. {
  145. }
  146. }
  147. }
  148. }
  149. private bool _IsGrap=true;
  150. public bool IsGrap
  151. {
  152. get { return _IsGrap; }
  153. set { SetProperty(ref _IsGrap, value); }
  154. }
  155. #endregion
  156. #region 命令
  157. private DelegateCommand _NextCommand;
  158. public DelegateCommand NextCommand =>
  159. _NextCommand ?? (_NextCommand = new DelegateCommand(ExecuteNextCommand));
  160. private DelegateCommand _BackCommand;
  161. public DelegateCommand BackCommand =>
  162. _BackCommand ?? (_BackCommand = new DelegateCommand(ExecuteBackCommand));
  163. private DelegateCommand _LoadedCommand;
  164. public DelegateCommand LoadedCommand =>
  165. _LoadedCommand ?? (_LoadedCommand = new DelegateCommand(ExecuteLoadedCommand));
  166. private DelegateCommand<double?> _LightValueChangedCommand;
  167. public DelegateCommand<double?> LightValueChangedCommand =>
  168. _LightValueChangedCommand ?? (_LightValueChangedCommand = new DelegateCommand<double?>(ExecuteLightValueChangedCommand));
  169. private DelegateCommand _StartGrabbingCommand;
  170. public DelegateCommand StartGrabbingCommand =>
  171. _StartGrabbingCommand ?? (_StartGrabbingCommand = new DelegateCommand(ExecuteStartGrabbingCommand).ObservesProperty(() => IsGrap));
  172. private DelegateCommand _StopGrabbingCommand;
  173. public DelegateCommand StopGrabbingCommand =>
  174. _StopGrabbingCommand ?? (_StopGrabbingCommand = new DelegateCommand(ExecuteStopGrabbingCommand).ObservesProperty(() => IsGrap));
  175. private DelegateCommand _LightValueAddCommand;
  176. public DelegateCommand LightValueAddCommand =>
  177. _LightValueAddCommand ?? (_LightValueAddCommand = new DelegateCommand(ExecuteLightValueAddCommand));
  178. private DelegateCommand _LightValueSubCommand;
  179. public DelegateCommand LightValueSubCommand =>
  180. _LightValueSubCommand ?? (_LightValueSubCommand = new DelegateCommand(ExecuteLightValueSubCommand));
  181. private DelegateCommand<object> _SetChannelBrightnessCommand;
  182. public DelegateCommand<object> SetChannelBrightnessCommand =>
  183. _SetChannelBrightnessCommand ?? (_SetChannelBrightnessCommand = new DelegateCommand<object>(ExecuteSetChannelBrightnessCommand));
  184. private DelegateCommand<object> _TurnOnCommand;
  185. public DelegateCommand<object> TurnOnCommand =>
  186. _TurnOnCommand ?? (_TurnOnCommand = new DelegateCommand<object>(ExecuteTurnOnCommand));
  187. private DelegateCommand<object> _TurnOffCommand;
  188. public DelegateCommand<object> TurnOffCommand =>
  189. _TurnOffCommand ?? (_TurnOffCommand = new DelegateCommand<object>(ExecuteTurnOffCommand));
  190. #endregion
  191. #region 事件
  192. #endregion
  193. public CameraParamsViewModel(IRegionManager regionManager, IEventAggregator ea, IContainerProvider container, IDialogService dialogService,
  194. IConfigService configService, IRobotService robotService, ICameraService cameraService, IFeederService feederService, IProductService productService,
  195. IRemoteCommandService remoteCommandService, ISystemDatabaseService systemDatabaseService, ILightManagerService lightManagerService)
  196. {
  197. _regionManager = regionManager;
  198. _eventAggregator = ea;
  199. _container = container;
  200. _dialogService = dialogService;
  201. _configService = configService;
  202. _robotService = robotService;
  203. _cameraService = cameraService;
  204. _feederService = feederService;
  205. _systemDatabaseService = systemDatabaseService;
  206. //订阅权限登录事件
  207. _eventAggregator.GetEvent<UserLoginNotification>().Subscribe(LoginChange);
  208. _productService = productService;
  209. _remoteCommandService = remoteCommandService;
  210. _lightManagerService = lightManagerService;
  211. // 创建有界Channel,容量为1,只保留最新的亮度值
  212. _brightnessChannel = Channel.CreateBounded<(int channel, int brightness)>(
  213. new BoundedChannelOptions(1) // 容量为1
  214. {
  215. // 当Channel满时,丢弃最旧的值,写入新的值
  216. FullMode = BoundedChannelFullMode.DropOldest,
  217. // 允许多个UI线程写入(虽然通常只有一个)
  218. SingleWriter = false,
  219. // 只有一个消费者读取并发送到设备
  220. SingleReader = true
  221. });
  222. _cancellationTokenSource = new CancellationTokenSource();
  223. // 启动消费者任务
  224. StartDeviceConsumer();
  225. }
  226. #region 方法
  227. // 消费者:从Channel读取并发送到设备
  228. private async void StartDeviceConsumer()
  229. {
  230. try
  231. {
  232. while (await _brightnessChannel.Reader.WaitToReadAsync(_cancellationTokenSource.Token))
  233. {
  234. while (_brightnessChannel.Reader.TryRead(out var obj))
  235. {
  236. // 实际写入(底层方法可能不支持取消)
  237. var success = await _lightManagerService.SetGlobalChannelBrightnessAsync(obj.channel, obj.brightness);
  238. }
  239. }
  240. }
  241. catch (OperationCanceledException)
  242. {
  243. // 正常结束
  244. }
  245. finally
  246. {
  247. _brightnessChannel.Writer.Complete();
  248. }
  249. }
  250. /// <summary>
  251. /// 下一步
  252. /// </summary>
  253. async void ExecuteNextCommand()
  254. {
  255. SelectProcedure.ExposureTime = ExposureTime;
  256. SelectProcedure.Gain = Gain;
  257. if (Camera != null)
  258. {
  259. Camera.StopGrabbing();
  260. Camera.ImageCallbackEvent -= Camera_ImageCallbackEvent;
  261. }
  262. //判断Toolblock是否存在
  263. if (SelectProcedure.ToolBlock==null)
  264. {
  265. var view = new WaitingControl();
  266. //show the dialog
  267. var task = DialogHost.Show(view, "RootDialog", null, null,null);
  268. await _productService.LoadProcedureVisionToolsAsync(SelectProduct.ID, SelectProcedure.CameraId, SelectProcedure.Name);
  269. if (DialogHost.IsDialogOpen("RootDialog"))
  270. {
  271. DialogHost.Close("RootDialog");
  272. }
  273. await task;
  274. }
  275. NavigationParameters param = new NavigationParameters();
  276. param.Add("SelectProduct", SelectProduct);
  277. param.Add("SelectedProcedure", SelectProcedure);
  278. if (SelectProcedure.VisionModel == Enums.VisionModel.Simple)
  279. {
  280. _regionManager.RequestNavigate("ProductRegionContext", "SimpleProcedure", param);
  281. }
  282. else
  283. {
  284. _regionManager.RequestNavigate("ProductRegionContext", "AdvancedProcedure", param);
  285. }
  286. }
  287. /// <summary>
  288. /// 上一步
  289. /// </summary>
  290. void ExecuteBackCommand()
  291. {
  292. if (Camera != null)
  293. {
  294. Camera.StopGrabbing();
  295. Camera.ImageCallbackEvent -= Camera_ImageCallbackEvent;
  296. }
  297. NavigationParameters param = new NavigationParameters();
  298. param.Add("SelectProduct", SelectProduct);
  299. param.Add("SelectedProcedure", SelectProcedure);
  300. _regionManager.RequestNavigate("ProductRegionContext", "ProcessBasics", param);
  301. }
  302. async void ExecuteLoadedCommand()
  303. {
  304. if (_systemDatabaseService.GetCurrentUser().userPart == Enums.UserPart.Operator)
  305. {
  306. IsAllowEdit = false;
  307. }
  308. else
  309. {
  310. IsAllowEdit = true;
  311. }
  312. }
  313. private void Camera_ImageCallbackEvent(ICogImage image,TimeSpan totaltime,string errormessage)
  314. {
  315. Image = image;
  316. App.Current.Dispatcher.Invoke(new
  317. Action(() =>
  318. {
  319. if (errormessage != null && !string.IsNullOrEmpty(errormessage))
  320. {
  321. Message = $"{Lang.耗时}:{totaltime.TotalMilliseconds.ToString("F1")} ms Error:{errormessage}";
  322. }
  323. else
  324. {
  325. Message = $"{Lang.耗时}:{totaltime.TotalMilliseconds.ToString("F1")} ms";
  326. }
  327. }));
  328. }
  329. /// <summary>
  330. /// 光源亮度调整时
  331. /// </summary>
  332. async void ExecuteLightValueChangedCommand(double? lightvalue)
  333. {
  334. try
  335. {
  336. if (lightvalue == null) return;
  337. if (Feeder == null || !Feeder.IsConnected) return;
  338. await Feeder.SetLightValueAsync((ushort)lightvalue);
  339. }
  340. catch (Exception ex)
  341. {
  342. LogHelper.WriteLogError(Lang.产品实时图像界面Feeder光源亮度更改时出错, ex);
  343. }
  344. }
  345. /// <summary>
  346. /// 为指定通道应用亮度设置(仅更新模型并在可能时尝试通知硬件)。
  347. /// </summary>
  348. /// <param name="channel">要设置的通道配置。</param>
  349. async void ExecuteSetChannelBrightnessCommand(object obj)
  350. {
  351. try
  352. {
  353. if (!(obj is Tuple<object, double, double> parameter)) return;
  354. if (!(parameter.Item1 is ChannelConfig channel)) return;
  355. int newValue = (int)parameter.Item2;
  356. // 尝试写入Channel(非阻塞)
  357. try
  358. {
  359. // 如果Channel满了(有旧值),DropOldest策略会自动丢弃旧值
  360. await _brightnessChannel.Writer.WriteAsync(
  361. (channel.GlobalIndex, newValue),
  362. _cancellationTokenSource.Token);
  363. }
  364. catch (OperationCanceledException)
  365. {
  366. // 正常取消,无需处理
  367. }
  368. catch (ChannelClosedException)
  369. {
  370. // Channel已关闭
  371. }
  372. }
  373. catch (Exception ex)
  374. {
  375. LogHelper.WriteLogError(Lang.产品实时图像界面设置光源通道亮度时出错, ex);
  376. }
  377. }
  378. /// <summary>
  379. /// 关闭光源
  380. /// </summary>
  381. /// <param name="parameter"></param>
  382. async void ExecuteTurnOffCommand(object parameter)
  383. {
  384. try
  385. {
  386. if (!(parameter is ChannelConfig channel)) return;
  387. await _lightManagerService.TurnOffGlobalChannelAsync(channel.GlobalIndex);
  388. }
  389. catch (Exception ex)
  390. {
  391. LogHelper.WriteLogError(Lang.关闭通道光源时出错, ex);
  392. }
  393. }
  394. /// <summary>
  395. /// 打开光源
  396. /// </summary>
  397. /// <param name="parameter"></param>
  398. async void ExecuteTurnOnCommand(object parameter)
  399. {
  400. try
  401. {
  402. if (!(parameter is ChannelConfig channel)) return;
  403. await _lightManagerService.TurnOnGlobalChannelAsync(channel.GlobalIndex);
  404. }
  405. catch (Exception ex)
  406. {
  407. LogHelper.WriteLogError(Lang.打开通道光源时出错, ex);
  408. }
  409. }
  410. void ExecuteLightValueSubCommand()
  411. {
  412. SelectProcedure.LightValue--;
  413. }
  414. void ExecuteLightValueAddCommand()
  415. {
  416. SelectProcedure.LightValue++;
  417. }
  418. /// <summary>
  419. /// 停止采集
  420. /// </summary>
  421. void ExecuteStopGrabbingCommand()
  422. {
  423. IsGrap = false;
  424. if (Camera != null)
  425. Camera.StopGrabbing();
  426. IsGrap = true;
  427. }
  428. /// <summary>
  429. /// 开始采集
  430. /// </summary>
  431. void ExecuteStartGrabbingCommand()
  432. {
  433. IsGrap = false;
  434. if (Camera != null)
  435. Camera.StartGrabbing();
  436. IsGrap = true;
  437. }
  438. #endregion
  439. #region 继承
  440. /// <summary>
  441. /// 确认导航请求时调用。此方法允许您在导航之前执行一些操作。
  442. /// </summary>
  443. /// <param name="navigationContext"></param>
  444. /// <param name="continuationCallback"></param>
  445. public void ConfirmNavigationRequest(NavigationContext navigationContext, Action<bool> continuationCallback)
  446. {
  447. continuationCallback(true);
  448. }
  449. /// <summary>
  450. /// 接收导航请求时调用。传入导航参数,包含有关导航目标的信息。
  451. /// </summary>
  452. /// <param name="navigationContext"></param>
  453. /// <exception cref="NotImplementedException"></exception>
  454. public async void OnNavigatedTo(NavigationContext navigationContext)
  455. {
  456. try
  457. {
  458. SelectProduct = navigationContext.Parameters.GetValue<ProductModel>("SelectProduct");
  459. SelectProcedure = navigationContext.Parameters.GetValue<ProcedureModel>("SelectedProcedure");
  460. if (SelectProcedure.RobotId != Guid.Empty)
  461. {
  462. IsHaveRobot = true;
  463. Robot = _robotService.GetRobot(SelectProcedure.RobotId);
  464. if (Robot != null)
  465. {
  466. Robot.EnterDebugMode = true;
  467. }
  468. }
  469. else
  470. {
  471. IsHaveRobot = false;
  472. }
  473. if (SelectProcedure.FeederId != Guid.Empty)
  474. {
  475. IsHaveFeeder = true;
  476. Feeder = _feederService.GetFeeder(SelectProcedure.FeederId);
  477. if (Feeder.IsConnected && !_remoteCommandService.IsExecuting)
  478. {
  479. await Feeder.OpenLightAsync();
  480. await Feeder.SetLightValueAsync((ushort)SelectProcedure.LightValue);
  481. }
  482. }
  483. else
  484. {
  485. IsHaveFeeder = false;
  486. }
  487. Camera = _cameraService.GetCamera(SelectProcedure.CameraId);
  488. if (SelectProcedure.ExposureTime == 0)
  489. {
  490. SelectProcedure.ExposureTime = 5000;
  491. }
  492. ExposureTime = SelectProcedure.ExposureTime;
  493. Gain = SelectProcedure.Gain;
  494. if (Camera != null && !_remoteCommandService.IsExecuting)
  495. {
  496. Camera.SetExposureTime(ExposureTime);
  497. Camera.SetGain(Gain);
  498. Camera.ImageCallbackEvent += Camera_ImageCallbackEvent;
  499. //Camera.Grab();
  500. }
  501. }
  502. catch (Exception ex)
  503. {
  504. LogHelper.WriteLogError(Lang.相机流程界面进入时出错, ex);
  505. }
  506. }
  507. /// <summary>
  508. /// 是否允许导航到此视图模型。
  509. /// </summary>
  510. /// <param name="navigationContext"></param>
  511. /// <returns></returns>
  512. public bool IsNavigationTarget(NavigationContext navigationContext)
  513. {
  514. return true;
  515. }
  516. /// <summary>
  517. /// 导航离开此视图模型时调用。您可以在此处执行清理操作或保存状态。
  518. /// </summary>
  519. /// <param name="navigationContext"></param>
  520. public void OnNavigatedFrom(NavigationContext navigationContext)
  521. {
  522. if (Robot!=null)
  523. {
  524. Robot.EnterDebugMode = false;
  525. }
  526. ExecuteStopGrabbingCommand();
  527. }
  528. #endregion
  529. private bool _IsAllowEdit = false;
  530. public bool IsAllowEdit
  531. {
  532. get { return _IsAllowEdit; }
  533. set { SetProperty(ref _IsAllowEdit, value); }
  534. }
  535. private void LoginChange(Models.User user)
  536. {
  537. if (user.userPart == Enums.UserPart.Operator)
  538. {
  539. IsAllowEdit = false;
  540. }
  541. else
  542. {
  543. IsAllowEdit = true;
  544. }
  545. }
  546. }
  547. }