RemoteCommandService.cs 90 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989
  1. using Cognex.VisionPro;
  2. using Cognex.VisionPro.ToolBlock;
  3. using MathNet.Numerics.LinearAlgebra;
  4. using Microsoft.CodeAnalysis.Scripting;
  5. using Org.BouncyCastle.Tls;
  6. using Prism.Events;
  7. using Prism.Ioc;
  8. using Prism.Regions;
  9. using System;
  10. using System.Collections.Generic;
  11. using System.IO;
  12. using System.Linq;
  13. using System.Reflection;
  14. using System.Text;
  15. using System.Threading;
  16. using System.Threading.Tasks;
  17. using System.Windows.Shapes;
  18. using Team.FFFeederService;
  19. using Team.FFFeederService.Interfaces;
  20. using TeamAAS_VP.Core;
  21. using TeamAAS_VP.Data;
  22. using TeamAAS_VP.Enums;
  23. using TeamAAS_VP.Events;
  24. using TeamAAS_VP.Interfaces;
  25. using TeamAAS_VP.Models;
  26. using TeamAAS_VP.Models.Calibration;
  27. using TeamAAS_VP.Models.Feeder;
  28. using TeamAAS_VP.Models.Product;
  29. using TeamAAS_VP.Resources.Languages;
  30. using System.ComponentModel;
  31. using System.Collections.Concurrent;
  32. namespace TeamAAS_VP.Services
  33. {
  34. /// <summary>
  35. /// Lightweight migration of Management.ExecuteRemoteCommand into a dedicated service.
  36. /// This service depends on existing domain services and will call into them.
  37. /// Only the core helpers used by the old ExecuteRemoteCommand are migrated (LoadProduct, GetCurrentProductName, GetCurrentProductID, GetCurrentProductSpeed, etc.).
  38. /// </summary>
  39. public class RemoteCommandService : IRemoteCommandService, INotifyPropertyChanged
  40. {
  41. private readonly IEventAggregator _eventAggregator;
  42. private readonly IProductService _productService;
  43. private readonly IConfigService _configService;
  44. private readonly ICalibrationService _calibrationService;
  45. private readonly IFeederService _feederService;
  46. private readonly ICameraService _cameraService;
  47. private readonly IRobotService _robotService;
  48. private readonly IContainerProvider _container;
  49. private readonly IRegionManager _regionManager;
  50. private readonly ISystemDatabaseService _systemDatabaseService;
  51. /// <summary>
  52. /// 计时器任务取消令牌
  53. /// </summary>
  54. private CancellationTokenSource _cycleTimingCts;
  55. /// <summary>
  56. /// 清料任务取消令牌字典,Key为任务编号
  57. /// </summary>
  58. private readonly ConcurrentDictionary<int, CancellationTokenSource> _clearMaterialCtsDict = new ConcurrentDictionary<int, CancellationTokenSource>();
  59. // 字段
  60. private int _activeCount;
  61. private int _visionActiveCount;
  62. private readonly ConcurrentDictionary<Guid, Task> _runningTasks = new ConcurrentDictionary<Guid, Task>();
  63. // 实现 INotifyPropertyChanged
  64. public event PropertyChangedEventHandler PropertyChanged;
  65. private void OnPropertyChanged(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
  66. // 公开属性(只读)
  67. public bool IsExecuting => Volatile.Read(ref _activeCount) > 0;
  68. public bool IsExecutingVisionCommand => Volatile.Read(ref _visionActiveCount) > 0;
  69. // 可选事件(如果不想用 INotifyPropertyChanged)
  70. public event EventHandler ExecutionStateChanged;
  71. /// <summary>
  72. /// 计时器计时通知事件
  73. /// </summary>
  74. public event EventHandler<TimeSpan> CycleTimingTicked;
  75. public RemoteCommandService(IEventAggregator eventAggregator, IRegionManager regionManager, IProductService productService, IConfigService configService, ICalibrationService calibrationService,
  76. IFeederService feederService, ICameraService cameraService, IRobotService robotService, IContainerProvider container, ISystemDatabaseService systemDatabaseService)
  77. {
  78. _eventAggregator = eventAggregator;
  79. _regionManager = regionManager;
  80. _productService = productService;
  81. _configService = configService;
  82. _calibrationService = calibrationService;
  83. _feederService = feederService;
  84. _cameraService = cameraService;
  85. _robotService = robotService;
  86. _container = container;
  87. _systemDatabaseService = systemDatabaseService;
  88. }
  89. public void Dispose() { }
  90. // 通用包裹器:包装并追踪任意异步工作
  91. private async Task<T> TrackExecutionAsync<T>(Func<CancellationToken, Task<T>> work, bool isVision, CancellationToken ct)
  92. {
  93. // create id for this run (可用于日志/诊断)
  94. var id = Guid.NewGuid();
  95. // 增加计数器并通知
  96. Interlocked.Increment(ref _activeCount);
  97. if (isVision) Interlocked.Increment(ref _visionActiveCount);
  98. OnPropertyChanged(nameof(IsExecuting));
  99. OnPropertyChanged(nameof(IsExecutingVisionCommand));
  100. ExecutionStateChanged?.Invoke(this, EventArgs.Empty);
  101. try
  102. {
  103. // 记录 task(便于外部查询)
  104. var task = work(ct);
  105. _runningTasks.TryAdd(id, task);
  106. var result = await task.ConfigureAwait(false);
  107. return result;
  108. }
  109. finally
  110. {
  111. // 减少计数器并通知
  112. Interlocked.Decrement(ref _activeCount);
  113. if (isVision) Interlocked.Decrement(ref _visionActiveCount);
  114. // 移除记录(如果仍存在)
  115. _runningTasks.TryRemove(id, out _);
  116. OnPropertyChanged(nameof(IsExecuting));
  117. OnPropertyChanged(nameof(IsExecutingVisionCommand));
  118. ExecutionStateChanged?.Invoke(this, EventArgs.Empty);
  119. }
  120. }
  121. public async Task ExecuteAsync(string command, CancellationToken cancellationToken, Action<string> callback)
  122. {
  123. var items = (command ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.None).Select(s => s.Trim()).ToArray();
  124. if (items.Length == 0) return;
  125. //触发命令的第一个参数作为命令类型
  126. string triggerCommand = items[0];
  127. //剩余参数作为命令参数
  128. string[] commandParams = items.Skip(1).ToArray();
  129. string response = string.Empty;
  130. //根据命令类型执行不同的命令
  131. switch (triggerCommand)
  132. {
  133. case "LoadProduct":
  134. if (commandParams.Length < 1)
  135. {
  136. callback?.Invoke("InvalidParameters");
  137. return;
  138. }
  139. response = await TrackExecutionAsync(async ct =>
  140. {
  141. var ok = await LoadProductAsync(commandParams[0], ct);
  142. return ok ? "OK" : "NG";
  143. }, isVision: false, cancellationToken);
  144. callback?.Invoke($"LoadProduct,{response}");
  145. break;
  146. case "GetProductName":
  147. //如果没有参数,则获取当前产品名称
  148. if (commandParams.Length < 1)
  149. {
  150. response = await TrackExecutionAsync(async ct =>
  151. {
  152. return await GetCurrentProductNameAsync(ct);
  153. }, isVision: false, cancellationToken);
  154. if (string.IsNullOrEmpty(response))
  155. {
  156. callback?.Invoke("GetProductName,NG");
  157. }
  158. else
  159. {
  160. callback?.Invoke($"GetProductName,{response}");
  161. }
  162. break;
  163. }
  164. //如果有参数,则通过产品编号获取产品名称
  165. else if (commandParams.Length == 1)
  166. {
  167. if (!int.TryParse(commandParams[0], out int productNumber))
  168. {
  169. callback?.Invoke("InvalidParameters");
  170. return;
  171. }
  172. response = await TrackExecutionAsync(async ct =>
  173. {
  174. return await GetProductNameByNumberAsync(productNumber, ct);
  175. }, isVision: false, cancellationToken);
  176. if (string.IsNullOrEmpty(response))
  177. {
  178. callback?.Invoke($"GetProductName,NG");
  179. }
  180. else
  181. {
  182. callback?.Invoke($"GetProductName,{response}");
  183. }
  184. break;
  185. }
  186. else
  187. {
  188. callback?.Invoke("InvalidParameters");
  189. return;
  190. }
  191. case "GetProductID":
  192. //如果没有参数,则获取当前产品编号
  193. if (commandParams.Length < 1)
  194. {
  195. response = await TrackExecutionAsync(async ct =>
  196. {
  197. int productNum = await GetCurrentProductNumberAsync(ct);
  198. return productNum.ToString();
  199. }, isVision: false, cancellationToken);
  200. if (response == "-1")
  201. {
  202. callback?.Invoke("GetProductID,NG");
  203. }
  204. else
  205. {
  206. callback?.Invoke($"GetProductID,{response}");
  207. }
  208. break;
  209. }
  210. //如果有参数,则通过产品名称获取产品编号
  211. else if (commandParams.Length == 1)
  212. {
  213. response = await TrackExecutionAsync(async ct =>
  214. {
  215. int productNum = await GetProductNumberByNameAsync(commandParams[0], ct);
  216. return productNum.ToString();
  217. }, isVision: false, cancellationToken);
  218. if (response == "-1")
  219. {
  220. callback?.Invoke($"GetProductID,NG");
  221. }
  222. else
  223. {
  224. callback?.Invoke($"GetProductID,{response}");
  225. }
  226. break;
  227. }
  228. else
  229. {
  230. callback?.Invoke("InvalidParameters");
  231. return;
  232. }
  233. case "GetRobotPointsCountById":
  234. if (commandParams.Length < 2 || !int.TryParse(commandParams[0], out int robotNumber) || !int.TryParse(commandParams[1], out int pointNumber))
  235. {
  236. callback?.Invoke("InvalidParameters");
  237. return;
  238. }
  239. response = await TrackExecutionAsync(async ct =>
  240. {
  241. return await GetRobotPointInfoAsync(robotNumber, pointNumber, ct);
  242. }, isVision: false, cancellationToken);
  243. if (string.IsNullOrEmpty(response))
  244. {
  245. callback?.Invoke($"GetRobotPointsCountById,NG");
  246. }
  247. else
  248. {
  249. callback?.Invoke($"GetRobotPointsCountById,{response}");
  250. }
  251. break;
  252. case "GetPalletPoint":
  253. if (commandParams.Length < 2 || !int.TryParse(commandParams[0], out int robotNumber1) || !int.TryParse(commandParams[1], out int palletNumber1) || !int.TryParse(commandParams[2], out int pointNumber1))
  254. {
  255. callback?.Invoke("InvalidParameters");
  256. return;
  257. }
  258. response = await TrackExecutionAsync(async ct =>
  259. {
  260. return await GetRobotPalletPointInfoAsync(robotNumber1, palletNumber1, pointNumber1, ct);
  261. }, isVision: false, cancellationToken);
  262. if (string.IsNullOrEmpty(response))
  263. {
  264. callback?.Invoke($"GetPalletPoint,NG");
  265. }
  266. else
  267. callback?.Invoke($"GetPalletPoint,{response}");
  268. break;
  269. case "CycleTime":
  270. if (commandParams.Length < 1)
  271. {
  272. callback?.Invoke("InvalidParameters");
  273. return;
  274. }
  275. if (int.Parse(commandParams[0]) == 1)
  276. {
  277. response = await TrackExecutionAsync(async ct =>
  278. {
  279. bool cycleResult = await StartCycleTimingAsync(ct);
  280. return cycleResult ? "OK" : "NG";
  281. }, isVision: false, cancellationToken);
  282. callback?.Invoke($"CycleTime,{response}");
  283. break;
  284. }
  285. else
  286. {
  287. response = await TrackExecutionAsync(async ct =>
  288. {
  289. bool cycleResult = await StopCycleTimingAsync(ct);
  290. return cycleResult ? "OK" : "NG";
  291. }, isVision: false, cancellationToken);
  292. callback?.Invoke($"CycleTime,{response}");
  293. break;
  294. }
  295. case "Record":
  296. if (commandParams.Length < 1)
  297. {
  298. callback?.Invoke("InvalidParameters");
  299. return;
  300. }
  301. response = await TrackExecutionAsync(async ct =>
  302. {
  303. bool recordResult = await CreateProductionCountAsync(commandParams[0].Trim(), ct);
  304. return recordResult ? "OK" : "NG";
  305. }, isVision: false, cancellationToken);
  306. callback?.Invoke($"Record,{response}");
  307. break;
  308. case "RecordQuery":
  309. if (commandParams.Length < 1)
  310. {
  311. callback?.Invoke("InvalidParameters");
  312. return;
  313. }
  314. response = await TrackExecutionAsync(async ct =>
  315. {
  316. int recordCount = await GetCurrentProductionCountAsync(commandParams[0].Trim(), ct);
  317. return recordCount.ToString();
  318. }, isVision: false, cancellationToken);
  319. callback?.Invoke($"RecordQuery,{response}");
  320. break;
  321. case "RecordReset":
  322. if (commandParams.Length < 1)
  323. {
  324. callback?.Invoke("InvalidParameters");
  325. return;
  326. }
  327. response = await TrackExecutionAsync(async ct =>
  328. {
  329. bool recordResetResult = await ResetProductionCountAsync(commandParams[0].Trim(), ct);
  330. return recordResetResult ? "OK" : "NG";
  331. }, isVision: false, cancellationToken);
  332. callback?.Invoke($"RecordReset,{response}");
  333. break;
  334. case "Err":
  335. if (commandParams.Length < 2)
  336. {
  337. callback?.Invoke("InvalidParameters");
  338. return;
  339. }
  340. response = await TrackExecutionAsync(async ct =>
  341. {
  342. bool errResult = await CreateAlarmRecordAsync(commandParams[0].Trim(), commandParams[1].Trim(), ct);
  343. return errResult ? "OK" : "NG";
  344. }, isVision: false, cancellationToken);
  345. callback?.Invoke($"Err,{response}");
  346. break;
  347. case "Info":
  348. if (commandParams.Length < 1)
  349. {
  350. callback?.Invoke("InvalidParameters");
  351. return;
  352. }
  353. response = await TrackExecutionAsync(async ct =>
  354. {
  355. bool infoResult = await CreateOperationLogAsync(commandParams[0].Trim(), ct);
  356. return infoResult ? "OK" : "NG";
  357. }, isVision: false, cancellationToken);
  358. callback?.Invoke($"Info,{response}");
  359. break;
  360. case "GetGlobalParam":
  361. if (commandParams.Length == 1)
  362. {
  363. response = await TrackExecutionAsync(async ct =>
  364. {
  365. return await GetGlobalParameterAsync(int.Parse(commandParams[0]), -1, ct);
  366. }, isVision: false, cancellationToken);
  367. if (string.IsNullOrEmpty(response))
  368. {
  369. callback?.Invoke("GetGlobalParam,NG");
  370. }
  371. else
  372. {
  373. callback?.Invoke($"GetGlobalParam,{response}");
  374. }
  375. break;
  376. }
  377. else if (commandParams.Length == 2)
  378. {
  379. response = await TrackExecutionAsync(async ct =>
  380. {
  381. return await GetGlobalParameterAsync(int.Parse(commandParams[0]), int.Parse(commandParams[1]), ct);
  382. }, isVision: false, cancellationToken);
  383. if (string.IsNullOrEmpty(response))
  384. {
  385. callback?.Invoke("GetGlobalParam,NG");
  386. }
  387. else
  388. {
  389. callback?.Invoke($"GetGlobalParam,{response}");
  390. }
  391. break;
  392. }
  393. else
  394. {
  395. callback?.Invoke("InvalidParameters");
  396. return;
  397. }
  398. case "GetSpeed":
  399. response = await TrackExecutionAsync(async ct =>
  400. {
  401. return await GetOperationSpeedAsync(ct);
  402. }, isVision: false, cancellationToken);
  403. if (string.IsNullOrEmpty(response))
  404. {
  405. callback?.Invoke("GetSpeed,NG");
  406. return;
  407. }
  408. else
  409. callback?.Invoke($"GetSpeed,{response}");
  410. break;
  411. case "GetToolParam":
  412. if (commandParams.Length < 2)
  413. {
  414. callback?.Invoke("InvalidParameters");
  415. return;
  416. }
  417. response = await TrackExecutionAsync(async ct =>
  418. {
  419. return await GetToolParamAsync(commandParams[0], int.Parse(commandParams[1]), ct);
  420. }, isVision: false, cancellationToken);
  421. if (string.IsNullOrEmpty(response))
  422. {
  423. callback?.Invoke("GetToolParam,NG");
  424. return;
  425. }
  426. else
  427. callback?.Invoke($"GetToolParam,{response}");
  428. break;
  429. case "GetProcedureParam":
  430. if (commandParams.Length < 1)
  431. {
  432. callback?.Invoke("InvalidParameters");
  433. return;
  434. }
  435. response = await TrackExecutionAsync(async ct =>
  436. {
  437. return await GetProcessParamAsync(commandParams[0], ct);
  438. }, isVision: false, cancellationToken);
  439. if (string.IsNullOrEmpty(response))
  440. {
  441. callback?.Invoke("GetProcedureParam,NG");
  442. return;
  443. }
  444. else
  445. callback?.Invoke($"GetProcedureParam,{response}");
  446. break;
  447. case "StartClearMaterial":
  448. if (commandParams.Length < 1)
  449. {
  450. callback?.Invoke("InvalidParameters");
  451. return;
  452. }
  453. response = await TrackExecutionAsync(async ct =>
  454. {
  455. bool startClearResult = await ExecuteClearMaterialTaskAsync(int.Parse(commandParams[0]), ct);
  456. return startClearResult ? "OK" : "NG";
  457. }, isVision: false, cancellationToken);
  458. callback?.Invoke($"StartClearMaterial,{response}");
  459. break;
  460. case "StopClearMaterial":
  461. if (commandParams.Length < 1)
  462. {
  463. callback?.Invoke("InvalidParameters");
  464. return;
  465. }
  466. response = await TrackExecutionAsync(async ct =>
  467. {
  468. bool stopClearResult = await StopClearMaterialTaskAsync(int.Parse(commandParams[0]), ct);
  469. return stopClearResult ? "OK" : "NG";
  470. }, isVision: false, cancellationToken);
  471. callback?.Invoke($"StopClearMaterial,{response}");
  472. break;
  473. //获取机器人是否进入调试模式
  474. case "RobotIsDebugMode":
  475. if (commandParams.Length < 1)
  476. {
  477. callback?.Invoke("InvalidParameters");
  478. return;
  479. }
  480. response = await TrackExecutionAsync(async ct =>
  481. {
  482. bool isDebugMode = await IsRobotInDebugModeAsync(int.Parse(commandParams[0]), ct);
  483. return isDebugMode ? "1" : "0";
  484. }, isVision: false, cancellationToken);
  485. callback?.Invoke($"RobotIsDebugMode,{response}");
  486. break;
  487. default:
  488. //获取当前产品
  489. var currentProduct = _productService.GetCurrentProduct();
  490. if (currentProduct == null) return;
  491. // 查找对应的流程
  492. var procedure = currentProduct.CameraProcedures
  493. .SelectMany(cp => cp.ProcedureModels)
  494. .FirstOrDefault(p => p.TriggerCommand == triggerCommand);
  495. if (procedure == null) return;
  496. // 假设 ExecuteFeederVisionTaskAsync 返回 (bool, string[])
  497. var visionResult = await TrackExecutionAsync(async ct =>
  498. {
  499. var r = await ExecuteVisionTaskAsync(procedure, commandParams, ct);
  500. // 如果需要转换类型,包装成 Tuple<bool, string[]>
  501. return Tuple.Create(r.IsSucceed, r.Response);
  502. }, isVision: true, cancellationToken);
  503. if (visionResult.Item1)
  504. {
  505. foreach (var item in visionResult.Item2)
  506. {
  507. callback?.Invoke($"{triggerCommand},OK,{item}");
  508. }
  509. }
  510. else
  511. {
  512. string msg = string.Empty;
  513. if (visionResult.Item2 != null && visionResult.Item2.Length>0)
  514. {
  515. msg = visionResult.Item2[0];
  516. }
  517. callback?.Invoke($"{triggerCommand},NG,{msg}");
  518. }
  519. break;
  520. }
  521. }
  522. //加载产品
  523. public async Task<bool> LoadProductAsync(string productName, CancellationToken cancellationToken)
  524. {
  525. //检查是否存在该名称的产品
  526. bool exists = _productService.ContainsProductByName(productName);
  527. if (exists)
  528. {
  529. //获取产品名称对应的产品
  530. var product = _productService.GetAllProducts().FirstOrDefault(p => p.Name == productName);
  531. //加载产品
  532. await _productService.LoadProductAsync(product.ID);
  533. //设置为当前产品
  534. _productService.SetCurrentProduct(product.ID);
  535. return true;
  536. }
  537. //获取产品编号对应的产品
  538. if (int.TryParse(productName, out int productNumber))
  539. {
  540. var product = _productService.GetAllProducts().FirstOrDefault(p => p.NumberCode == productNumber);
  541. if (product != null)
  542. {
  543. //加载产品
  544. await _productService.LoadProductAsync(product.ID);
  545. //设置为当前产品
  546. _productService.SetCurrentProduct(product.ID);
  547. return true;
  548. }
  549. }
  550. return false;
  551. }
  552. //通过产品编号获取产品名称
  553. public Task<string> GetProductNameByNumberAsync(int productNumber, CancellationToken cancellationToken)
  554. {
  555. var product = _productService.GetAllProducts().FirstOrDefault(p => p.NumberCode == productNumber);
  556. if (product != null)
  557. {
  558. return Task.FromResult(product.Name);
  559. }
  560. return Task.FromResult(string.Empty);
  561. }
  562. //获取当前的产品名称
  563. public Task<string> GetCurrentProductNameAsync(CancellationToken cancellationToken)
  564. {
  565. var currentProduct = _productService.GetCurrentProduct();
  566. if (currentProduct != null)
  567. {
  568. return Task.FromResult(currentProduct.Name);
  569. }
  570. return Task.FromResult(string.Empty);
  571. }
  572. //通过产品名称获取产品编号
  573. public Task<int> GetProductNumberByNameAsync(string productName, CancellationToken cancellationToken)
  574. {
  575. //检查是否存在该名称的产品
  576. bool exists = _productService.ContainsProductByName(productName);
  577. if (exists)
  578. {
  579. //获取产品名称对应的产品
  580. var product = _productService.GetAllProducts().FirstOrDefault(p => p.Name == productName);
  581. return Task.FromResult(product.NumberCode);
  582. }
  583. return Task.FromResult(-1);
  584. }
  585. //获取当前的产品编号
  586. public Task<int> GetCurrentProductNumberAsync(CancellationToken cancellationToken)
  587. {
  588. var currentProduct = _productService.GetCurrentProduct();
  589. if (currentProduct != null)
  590. {
  591. return Task.FromResult(currentProduct.NumberCode);
  592. }
  593. return Task.FromResult(-1);
  594. }
  595. //通过机器人编号和点位编号获取机器人点位信息
  596. public Task<string> GetRobotPointInfoAsync(int robotNumber, int pointNumber, CancellationToken cancellationToken)
  597. {
  598. //获取当前产品
  599. var currentProduct = _productService.GetCurrentProduct();
  600. if (currentProduct == null) return Task.FromResult(string.Empty);
  601. //获取机器人编号对应的机器人信息
  602. var robot = _configService.GetAllRobots().FirstOrDefault(r => r.RobotNo == robotNumber);
  603. if (robot == null) return Task.FromResult(string.Empty);
  604. //获取机器人点位信息
  605. var robotPoint = currentProduct.RobotPoint.Robots.FirstOrDefault(rp => rp.Id == robot.Id);
  606. if (robotPoint == null) return Task.FromResult(string.Empty);
  607. //获取点位信息
  608. var point = robotPoint.Points.FirstOrDefault(p => p.Number == pointNumber);
  609. if (point == null) return Task.FromResult(string.Empty);
  610. return Task.FromResult(point.ToString());
  611. }
  612. // <summary>
  613. /// 通过机器人编号和托盘编号、点编号获取机器人托盘点位信息
  614. /// </summary>
  615. /// <param name="robotNumber">机器人编号</param>
  616. /// <param name="palletNumber">托盘编号</param>
  617. /// <param name="pointNumber">点编号</param>
  618. /// <param name="cancellationToken"></param>
  619. /// <returns></returns>
  620. public Task<string> GetRobotPalletPointInfoAsync(int robotNumber, int palletNumber, int pointNumber, CancellationToken cancellationToken)
  621. {
  622. // 获取当前产品
  623. var currentProduct = _productService.GetCurrentProduct();
  624. if (currentProduct == null) return Task.FromResult(string.Empty);
  625. //获取机器人编号对应的机器人信息
  626. var robot = _configService.GetAllRobots().FirstOrDefault(r => r.RobotNo == robotNumber);
  627. if (robot == null) return Task.FromResult(string.Empty);
  628. //获取机器人点位信息
  629. var robotPoint = currentProduct.RobotPoint.Robots.FirstOrDefault(rp => rp.Id == robot.Id);
  630. if (robotPoint == null) return Task.FromResult(string.Empty);
  631. //获取托盘
  632. var pallet = robotPoint.Pallets.FirstOrDefault(p => p.PalletNo == palletNumber);
  633. if (pallet == null) return Task.FromResult(string.Empty);
  634. PalletTool palletTool = new PalletTool(pallet);
  635. if (pointNumber > palletTool.Points.Length || pointNumber < 1) return Task.FromResult(string.Empty);
  636. var pos = palletTool.Points[pointNumber - 1];
  637. string response = $"{robotNumber},{palletNumber},{palletTool.Points.Length},{pos.Number},{pos.X:F3},{pos.Y:F3},{pos.Z:F3},{pos.U:F3},{pos.V:F3},{pos.W:F3},{pos.Local},{pos.Tool},{pos.Hand}";
  638. return Task.FromResult(response);
  639. }
  640. //开始节拍计时
  641. public Task<bool> StartCycleTimingAsync(CancellationToken cancellationToken)
  642. {
  643. //TODO: 实现节拍计时开始逻辑
  644. //如果当前已经在计时,则取消之前的计时,然后重新开始计时
  645. if (_cycleTimingCts != null)
  646. {
  647. _cycleTimingCts.Cancel();
  648. _cycleTimingCts.Dispose();
  649. }
  650. _cycleTimingCts = new CancellationTokenSource();
  651. var token = _cycleTimingCts.Token;
  652. Task.Run(async () =>
  653. {
  654. var startTime = DateTime.UtcNow;
  655. while (!token.IsCancellationRequested)
  656. {
  657. var elapsed = DateTime.UtcNow - startTime;
  658. OnCycleTimingTicked(elapsed);
  659. await Task.Delay(100, token); // 每100毫秒更新一次
  660. }
  661. }, token);
  662. return Task.FromResult(true);
  663. }
  664. //停止节拍计时
  665. public Task<bool> StopCycleTimingAsync(CancellationToken cancellationToken)
  666. {
  667. if (_cycleTimingCts != null)
  668. {
  669. _cycleTimingCts.Cancel();
  670. _cycleTimingCts.Dispose();
  671. _cycleTimingCts = null;
  672. }
  673. return Task.FromResult(true);
  674. }
  675. //创建一条生产计数,传入计数项
  676. public Task<bool> CreateProductionCountAsync(string countItem, CancellationToken cancellationToken)
  677. {
  678. _systemDatabaseService.RecordProductionAsync(new ProductionRecord
  679. {
  680. Category = countItem,
  681. ProductName = _productService.GetCurrentProduct()?.Name ?? "Unknown",
  682. Quantity = 1,
  683. Timestamp = DateTime.UtcNow
  684. });
  685. return Task.FromResult(true);
  686. }
  687. //获取当前生产计数
  688. public async Task<int> GetCurrentProductionCountAsync(string countItem, CancellationToken cancellationToken)
  689. {
  690. //获取当前产品
  691. var currentProduct = _productService.GetCurrentProduct();
  692. if (currentProduct == null) return 0;
  693. //获取分类集合
  694. var records = _systemDatabaseService.GetProductCategoriesAsync(currentProduct.Name);
  695. //计算指定分类的总产能
  696. int totalCount =await _systemDatabaseService.GetTotalProductionAsync(currentProduct.Name, new List<string> { countItem });
  697. return totalCount;
  698. }
  699. //重置生产计数
  700. public async Task<bool> ResetProductionCountAsync(string countItem, CancellationToken cancellationToken)
  701. {
  702. //获取当前产品
  703. var currentProduct = _productService.GetCurrentProduct();
  704. if (currentProduct == null) return false;
  705. await _systemDatabaseService.ResetProductionAsync(currentProduct.Name);
  706. return true;
  707. }
  708. //创建一条报警记录
  709. public Task<bool> CreateAlarmRecordAsync(string alarmCode, string alarmDescription, CancellationToken cancellationToken)
  710. {
  711. SendTaskMessage(alarmDescription, MessageLevel.Error);
  712. _systemDatabaseService.RecordAlarmAsync(new AlarmRecord
  713. {
  714. Code = alarmCode,
  715. Message = alarmDescription,
  716. Source = "Unknown",
  717. Time = DateTime.UtcNow
  718. });
  719. return Task.FromResult(true);
  720. }
  721. //创建一条运行信息记录
  722. public Task<bool> CreateOperationLogAsync(string logMessage, CancellationToken cancellationToken)
  723. {
  724. SendTaskMessage(logMessage, MessageLevel.Info);
  725. return Task.FromResult(true);
  726. }
  727. //获取当前产品指定编号的全局参数
  728. public Task<string> GetGlobalParameterAsync(int parameterNumber, int itemIndex, CancellationToken cancellationToken)
  729. {
  730. //获取当前产品
  731. var currentProduct = _productService.GetCurrentProduct();
  732. if (currentProduct == null) return Task.FromResult(string.Empty);
  733. if (currentProduct.GlobalParamListCollection == null) return Task.FromResult(string.Empty);
  734. //获取产品全局参数
  735. var globalParam = currentProduct.GlobalParamListCollection.FirstOrDefault(p => p.Number == parameterNumber);
  736. if (globalParam == null) return Task.FromResult(string.Empty);
  737. string response = string.Empty;
  738. if (itemIndex == 1)
  739. {
  740. response = $"{globalParam.Number},{globalParam.Name},{globalParam.Param1}";
  741. }
  742. else if (itemIndex == 2)
  743. {
  744. response = $"{globalParam.Number},{globalParam.Name},{globalParam.Param2}";
  745. }
  746. else if (itemIndex == 3)
  747. {
  748. response = $"{globalParam.Number},{globalParam.Name},{globalParam.Param3}";
  749. }
  750. else if (itemIndex == 4)
  751. {
  752. response = $"{globalParam.Number},{globalParam.Name},{globalParam.Param4}";
  753. }
  754. else
  755. {
  756. response = globalParam.ToString();
  757. }
  758. return Task.FromResult(response);
  759. }
  760. //获取当前产品的运行速度
  761. public Task<string> GetOperationSpeedAsync(CancellationToken cancellationToken)
  762. {
  763. //获取当前产品
  764. var currentProduct = _productService.GetCurrentProduct();
  765. if (currentProduct == null) return Task.FromResult(string.Empty);
  766. string response = $"{currentProduct.Speed},{currentProduct.Accel},{currentProduct.Speeds},{currentProduct.Accels},{Convert.ToInt16(currentProduct.Power)},{Convert.ToInt16(currentProduct.IsUserUpCamera)}";
  767. return Task.FromResult(response);
  768. }
  769. //获取末端工具信息,传入流程触发命令和工具编号
  770. public Task<string> GetToolParamAsync(string triggerCommand, int toolNumber, CancellationToken cancellationToken)
  771. {
  772. //获取当前产品
  773. var currentProduct = _productService.GetCurrentProduct();
  774. if (currentProduct == null) return Task.FromResult(string.Empty);
  775. //查找对应的流程
  776. ProcedureModel procedure = currentProduct.CameraProcedures
  777. .SelectMany(cp => cp.ProcedureModels)
  778. .FirstOrDefault(p => p.TriggerCommand == triggerCommand);
  779. if (procedure == null || procedure.ToolInfo == null) return Task.FromResult(string.Empty);
  780. //查找对应的工具
  781. var tool = procedure.ToolInfo.FirstOrDefault(t => t.Number == toolNumber);
  782. if (tool == null) return Task.FromResult(string.Empty);
  783. if (tool.Tool == null) return Task.FromResult(string.Empty);
  784. string response = $"{tool.Tool.Number},{tool.Tool.X:F3},{tool.Tool.Y:F3},{tool.OffsetX:F3},{tool.OffsetY:F3},{tool.OffsetZ:F3},{tool.OffsetU:F3}";
  785. return Task.FromResult(response);
  786. }
  787. //获取流程参数信息,传入流程触发命令
  788. public Task<string> GetProcessParamAsync(string triggerCommand, CancellationToken cancellationToken)
  789. {
  790. //获取当前产品
  791. var currentProduct = _productService.GetCurrentProduct();
  792. if (currentProduct == null) return Task.FromResult(string.Empty);
  793. //查找对应的流程
  794. ProcedureModel procedure = currentProduct.CameraProcedures
  795. .SelectMany(cp => cp.ProcedureModels)
  796. .FirstOrDefault(p => p.TriggerCommand == triggerCommand);
  797. if (procedure == null || procedure.ToolInfo == null) return Task.FromResult(string.Empty);
  798. StringBuilder response = new StringBuilder();
  799. response.Append($"{procedure.WaitPhoto}");
  800. if (procedure.UserDefineParameters != null)
  801. {
  802. //附加用户自定义参数
  803. //遍历所有用户自定义参数,以分号分隔
  804. foreach (var param in procedure.UserDefineParameters)
  805. {
  806. response.Append("," + param.ToString());
  807. }
  808. }
  809. return Task.FromResult(response.ToString());
  810. }
  811. //执行清料任务
  812. public async Task<bool> ExecuteClearMaterialTaskAsync(int taskNumber, CancellationToken cancellationToken)
  813. {
  814. //获取清料任务
  815. var feederClearWork=_configService.GetClearanceTaskByNumber(taskNumber);
  816. if (feederClearWork == null)
  817. {
  818. return false;
  819. }
  820. IVoiceCoilMotorFeeder Feeder = _feederService.GetFeeder(feederClearWork.FeederId);
  821. if (!Feeder.IsConnected)
  822. {
  823. return false;
  824. }
  825. //如果当前已有该任务在执行,则返回失败
  826. if (_clearMaterialCtsDict.ContainsKey(feederClearWork.TaskCode))
  827. {
  828. return false;
  829. }
  830. _clearMaterialCtsDict[feederClearWork.TaskCode] = new CancellationTokenSource();
  831. if (cancellationToken != null)
  832. {
  833. //将外部取消令牌与内部取消令牌关联
  834. cancellationToken.Register(() =>
  835. {
  836. if (_clearMaterialCtsDict.ContainsKey(feederClearWork.TaskCode))
  837. {
  838. _clearMaterialCtsDict[feederClearWork.TaskCode].Cancel();
  839. }
  840. });
  841. }
  842. var token = _clearMaterialCtsDict[feederClearWork.TaskCode].Token;
  843. for (int i = 0; i < feederClearWork.ClearCount; i++)
  844. {
  845. //1.Feeder振动之前,打开挡板
  846. await Feeder.OutputAsync((int)feederClearWork.BeforeOutput + 1, 0);
  847. await Task.Delay(200, token);
  848. if ((cancellationToken != null && cancellationToken.IsCancellationRequested) || (token!=null && token.IsCancellationRequested))
  849. {
  850. await Feeder.StopOutputAsync();
  851. if (_clearMaterialCtsDict.ContainsKey(feederClearWork.TaskCode))
  852. {
  853. _clearMaterialCtsDict[feederClearWork.TaskCode].Cancel();
  854. _clearMaterialCtsDict.TryRemove(feederClearWork.TaskCode, out _);
  855. }
  856. return false;
  857. }
  858. //2.执行配置的Feeder振动任务
  859. for (int j = 0; j < feederClearWork.VibrationCount; j++)
  860. {
  861. await ExecuteFeederWorks(Feeder, feederClearWork.FeederWorks, token, true);
  862. if ((cancellationToken != null && cancellationToken.IsCancellationRequested) || (token != null && token.IsCancellationRequested))
  863. {
  864. await Feeder.StopOutputAsync();
  865. if (_clearMaterialCtsDict.ContainsKey(feederClearWork.TaskCode))
  866. {
  867. _clearMaterialCtsDict[feederClearWork.TaskCode].Cancel();
  868. _clearMaterialCtsDict.TryRemove(feederClearWork.TaskCode, out _);
  869. }
  870. return false;
  871. }
  872. //如果料仓停止了,则打开料仓运动
  873. if (!await Feeder.GetOutputStatusAsync())
  874. {
  875. await Feeder.OutputAsync((int)feederClearWork.BeforeOutput + 1, 0);
  876. }
  877. if ((cancellationToken != null && cancellationToken.IsCancellationRequested) || (token != null && token.IsCancellationRequested))
  878. {
  879. await Feeder.StopOutputAsync();
  880. if (_clearMaterialCtsDict.ContainsKey(feederClearWork.TaskCode))
  881. {
  882. _clearMaterialCtsDict[feederClearWork.TaskCode].Cancel();
  883. _clearMaterialCtsDict.TryRemove(feederClearWork.TaskCode, out _);
  884. }
  885. return false;
  886. }
  887. }
  888. if ((cancellationToken != null && cancellationToken.IsCancellationRequested) || (token != null && token.IsCancellationRequested))
  889. {
  890. await Feeder.StopOutputAsync();
  891. if (_clearMaterialCtsDict.ContainsKey(feederClearWork.TaskCode))
  892. {
  893. _clearMaterialCtsDict[feederClearWork.TaskCode].Cancel();
  894. _clearMaterialCtsDict.TryRemove(feederClearWork.TaskCode, out _);
  895. }
  896. return false;
  897. }
  898. //3.Feeder振动之后,关闭挡板
  899. await Feeder.StopOutputAsync();
  900. await Task.Delay(300, token);
  901. if ((cancellationToken != null && cancellationToken.IsCancellationRequested) || (token != null && token.IsCancellationRequested))
  902. {
  903. await Feeder.StopOutputAsync();
  904. if (_clearMaterialCtsDict.ContainsKey(feederClearWork.TaskCode))
  905. {
  906. _clearMaterialCtsDict[feederClearWork.TaskCode].Cancel();
  907. _clearMaterialCtsDict.TryRemove(feederClearWork.TaskCode, out _);
  908. }
  909. return false;
  910. }
  911. }
  912. //3.Feeder振动之后,关闭挡板
  913. await Feeder.StopOutputAsync();
  914. await Task.Delay(300, token);
  915. if ((cancellationToken != null && cancellationToken.IsCancellationRequested) || (token != null && token.IsCancellationRequested))
  916. {
  917. await Feeder.StopOutputAsync();
  918. if (_clearMaterialCtsDict.ContainsKey(feederClearWork.TaskCode))
  919. {
  920. _clearMaterialCtsDict[feederClearWork.TaskCode].Cancel();
  921. _clearMaterialCtsDict.TryRemove(feederClearWork.TaskCode, out _);
  922. }
  923. return false;
  924. }
  925. if (_clearMaterialCtsDict.ContainsKey(feederClearWork.TaskCode))
  926. {
  927. _clearMaterialCtsDict[feederClearWork.TaskCode].Cancel();
  928. _clearMaterialCtsDict.TryRemove(feederClearWork.TaskCode, out _);
  929. }
  930. return true;
  931. }
  932. //终止清料任务
  933. public Task<bool> StopClearMaterialTaskAsync(int taskNumber, CancellationToken cancellationToken)
  934. {
  935. //获取清料任务
  936. var feederClearWork = _configService.GetClearanceTaskByNumber(taskNumber);
  937. if (feederClearWork == null)
  938. {
  939. return Task.FromResult(false);
  940. }
  941. if (_clearMaterialCtsDict.ContainsKey(feederClearWork.TaskCode))
  942. {
  943. _clearMaterialCtsDict[feederClearWork.TaskCode].Cancel();
  944. _clearMaterialCtsDict.TryRemove(feederClearWork.TaskCode, out _);
  945. }
  946. return Task.FromResult(true);
  947. }
  948. /// <summary>
  949. /// 获取机器人是否进入调试模式
  950. /// </summary>
  951. /// <param name="cancellationToken"></param>
  952. /// <returns></returns>
  953. public Task<bool> IsRobotInDebugModeAsync(int robotNumber, CancellationToken cancellationToken)
  954. {
  955. var robotInfo = _configService.GetAllRobots().FirstOrDefault(r => r.RobotNo == robotNumber);
  956. if (robotInfo == null)
  957. {
  958. return Task.FromResult(false);
  959. }
  960. var robot = _robotService.GetRobot(robotInfo.Id);
  961. if (robot != null) return Task.FromResult(robot.EnterDebugMode);
  962. return Task.FromResult(false);
  963. }
  964. /// <summary>
  965. /// 执行视觉任务
  966. /// </summary>
  967. /// <param name="triggerCommand">流程触发命令</param>
  968. /// <param name="doVibrateFirst">是否先振动</param>
  969. /// <param name="doCaptureFirst">是否先拍照</param>
  970. /// <param name="robotCurrentPosition">机器人当前点位</param>
  971. /// <param name="visionToolInputParams">视觉工具输入参数</param>
  972. /// <param name="cancellationToken"></param>
  973. /// <returns></returns>
  974. public async Task<(bool IsSucceed, string[] Response)> ExecuteVisionTaskAsync(ProcedureModel procedure, string[] items, CancellationToken cancellationToken)
  975. {
  976. //获取当前产品
  977. var currentProduct = _productService.GetCurrentProduct();
  978. //检测当前流程的本地是否存在流程执行脚本文件,如果存在则调用脚本执行视觉任务
  979. string scriptFilePath = FilePath.ProductsPath + "//" + currentProduct.Name + "//" + procedure.CameraName + "//" + procedure.Name + ".cs";
  980. if (File.Exists(scriptFilePath))
  981. {
  982. //调用脚本执行视觉任务
  983. try
  984. {
  985. string script = FileHelper.ReadFile(scriptFilePath);
  986. SendTaskMessage(Lang.执行视觉处理脚本, MessageLevel.Debug);
  987. Tuple<bool, string[]> rstn = null;
  988. var rst1 = await ScriptHelper.ExecuteVisionScriptingAsync(script, _regionManager, _eventAggregator, _container, procedure, items, rst =>
  989. {
  990. rstn = rst;
  991. });
  992. if (!rst1.Item1)
  993. {
  994. SendTaskMessage(Lang.视觉处理脚本出错, MessageLevel.Error);
  995. foreach (var item in rst1.Item2)
  996. {
  997. SendTaskMessage(item, MessageLevel.Error);
  998. }
  999. return (false, new string[] { "ScriptError" });
  1000. }
  1001. return (rstn.Item1, rstn.Item2);
  1002. }
  1003. catch (Exception ex)
  1004. {
  1005. SendTaskMessage(ex.Message, MessageLevel.Error);
  1006. LogHelper.WriteLogError("执行视觉处理脚本时出错!", ex);
  1007. return (false, new string[] { "ScriptException" });
  1008. }
  1009. }
  1010. else
  1011. {
  1012. //正常执行模块,每次拍照,会返回所有的结果数据
  1013. IVoiceCoilMotorFeeder Feeder = null;
  1014. if (procedure.FeederId != Guid.Empty)
  1015. {
  1016. Feeder = _feederService.GetFeeder(procedure.FeederId);
  1017. return await ExecuteFeederVisionTaskAsync(Feeder, procedure, items, cancellationToken);
  1018. }
  1019. else
  1020. {
  1021. var result = await ExecuteNormalVisionTaskAsync(procedure, items, cancellationToken);
  1022. return (result.IsSucceed, new string[] { result.Response });
  1023. }
  1024. }
  1025. }
  1026. /// <summary>
  1027. /// 更新计时器通知
  1028. /// </summary>
  1029. /// <param name="elapsed"></param>
  1030. private void OnCycleTimingTicked(TimeSpan elapsed)
  1031. {
  1032. CycleTimingTicked?.Invoke(this, elapsed);
  1033. }
  1034. private void SendTaskMessage(string msg, MessageLevel level)
  1035. {
  1036. App.Current.Dispatcher.Invoke(() =>
  1037. {
  1038. _eventAggregator.GetEvent<TaskMessageNotification>().Publish(new Models.MessageStruct() { Message = msg, level = level });
  1039. });
  1040. }
  1041. #region 执行视觉任务辅助方法
  1042. /// <summary>
  1043. /// 执行Feeder视觉引导任务
  1044. /// </summary>
  1045. /// <param name="Feeder"></param>
  1046. /// <param name="procedure"></param>
  1047. /// <param name="items"></param>
  1048. /// <param name="cancellationToken"></param>
  1049. /// <returns></returns>
  1050. public async Task<(bool IsSucceed, string[] Response)> ExecuteFeederVisionTaskAsync(IVoiceCoilMotorFeeder feeder, ProcedureModel procedure, string[] items, CancellationToken cancellationToken)
  1051. {
  1052. // 如果输入的items中是否包含Vibration和NoVibration,这两个分别对应是否先振动和是否先拍照
  1053. bool doVibrateFirst = items != null && items.Contains("VibrateFirst");
  1054. bool doCaptureFirst = items != null && items.Contains("CaptureFirst");
  1055. if (doVibrateFirst && doCaptureFirst) doCaptureFirst = false; // 如果两个都包含,则以振动优先为准
  1056. // 触发命令传入的输入终端
  1057. Dictionary<string, string> InputTerminal = null;
  1058. // 解析触发命令传入的输入终端参数
  1059. //判断传入参数中是否存在“SetInputs”
  1060. if (items != null && items.Length > 1 && items[0] == "SetInputs")
  1061. {
  1062. //依次从索引1开始解析参数,格式为 key=value,直到遇到非key=value格式的参数为止
  1063. InputTerminal = new Dictionary<string, string>();
  1064. for (int i = 1; i < items.Length; i++)
  1065. {
  1066. var pair = items[i].Split('=');
  1067. if (pair.Length == 2)
  1068. {
  1069. InputTerminal[pair[0]] = pair[1];
  1070. }
  1071. else
  1072. {
  1073. break; // 遇到非key=value格式的参数,停止解析
  1074. }
  1075. }
  1076. }
  1077. //执行Feeder相机拍照前的任务
  1078. await ExecuteFeederForPhotoBefore(feeder, procedure, doVibrateFirst, doCaptureFirst, cancellationToken);
  1079. // 如果遇到取消令牌,则直接返回失败
  1080. if (cancellationToken != null && cancellationToken.IsCancellationRequested)
  1081. {
  1082. await feeder.CloseLightAsync();
  1083. return (false, new string[] { "FeederTaskCancelled" });
  1084. }
  1085. DateTime nowtime = DateTime.Now;
  1086. //拍照失败时,需要多次拍照
  1087. for (int i = 0; i < procedure.FeederFailLimit; i++)
  1088. {
  1089. if (cancellationToken != null && cancellationToken.IsCancellationRequested)
  1090. {
  1091. await feeder.CloseLightAsync();
  1092. return (false, new string[] { "VisionTaskCancelled" });
  1093. }
  1094. SendTaskMessage(Lang.开始执行视觉流程.Replace("{0}", $"{i + 1}").Replace("{1}", procedure.Name), MessageLevel.Info);
  1095. // 1. 执行相机拍照并运行视觉工具
  1096. bool visionSucceed = ExecutePhoto(procedure, InputTerminal, out CogToolBlockTerminalCollection outputCollection);
  1097. if (!visionSucceed)
  1098. {
  1099. SendTaskMessage(Lang.视觉任务执行失败, MessageLevel.Error);
  1100. continue;
  1101. }
  1102. if (!outputCollection.Contains("Found"))
  1103. {
  1104. SendTaskMessage(Lang.未找到视觉输出结果, MessageLevel.Error);
  1105. return (false, new string[] { "NoFoundOutput" });
  1106. }
  1107. procedure.LastBlobArea = Convert.ToDouble(outputCollection["BlobTotalArea"].Value);
  1108. // 2. 获取视觉输出结果
  1109. bool Found = (bool)(outputCollection["Found"].Value);
  1110. if (!Found)
  1111. {
  1112. SendTaskMessage(Lang.拍照NG, MessageLevel.Debug);
  1113. await ExecuteFeederForPhotoLater(feeder, procedure, cancellationToken);
  1114. continue;
  1115. }
  1116. SendTaskMessage(Lang.拍照OK, MessageLevel.Debug);
  1117. if (procedure.ProcedureOutputs == null)
  1118. {
  1119. SendTaskMessage(Lang.视觉流程未配置输出项, MessageLevel.Alarm);
  1120. return (false, new string[] { "NoOutputConfigured" });
  1121. }
  1122. // 3. 获取视觉输出结果
  1123. LogHelper.WriteLogInfo("开始获取视觉输出结果");
  1124. var (IsSucceed, ErrMsg, Output) = GetVisionOutput(procedure, outputCollection, null);
  1125. if (!IsSucceed)
  1126. {
  1127. SendTaskMessage(Lang.获取视觉输出结果失败, MessageLevel.Error);
  1128. return (false, new string[] { ErrMsg });
  1129. }
  1130. // 4. 组织返回结果
  1131. int outputPointCount = 0;//输出点的数量
  1132. List<double[]> currentPoints = new List<double[]>(); //点位数据
  1133. List<string> outputItems = new List<string>(); //多项输出结果集合
  1134. foreach (var output in procedure.ProcedureOutputs)
  1135. {
  1136. if (Output.ContainsKey(output.Name))
  1137. {
  1138. var (IsPoint, Value) = Output[output.Name];
  1139. if (IsPoint)
  1140. {
  1141. //Value是点位数据,类型:List<string[]>,string[]是一个点位,里面是X、Y,U等数据
  1142. //将所有点位数据和单个点位数据中的坐标之间都用逗号分隔,多个点位之间用逗号分隔
  1143. List<string[]> points = Value as List<string[]>;
  1144. List<string> allpoints = new List<string>();
  1145. for (int j = 0; j < points.Count; j++)
  1146. {
  1147. //如果当前流程开启了剔除重复点功能,则进行剔除
  1148. if (procedure.IsExcludeNearPoint && procedure.LastRunPoints != null && procedure.LastRunPoints.Count > 0)
  1149. {
  1150. //当前点的坐标数据
  1151. double px = double.Parse(points[j][0]);
  1152. double py = double.Parse(points[j][1]);
  1153. double pu = double.Parse(points[j][2]);
  1154. //检查当前点是否与上次运行的点过于接近
  1155. //使用linq来检测上一次的点位集合中,是否存在某个点与当前点的x,y,u的差值小于设定的阈值
  1156. bool isNear = procedure.LastRunPoints.Any(lp =>
  1157. Math.Abs(lp[0] - px) < procedure.ExcludeNearPointThreshol &&
  1158. Math.Abs(lp[1] - py) < procedure.ExcludeNearPointThreshol &&
  1159. Math.Abs(lp[2] - pu) < procedure.ExcludeNearPointThreshol);
  1160. if (isNear) continue; //如果存在接近的点,则跳过当前点,不进行添加
  1161. }
  1162. //单个点位内坐标之间逗号分隔
  1163. allpoints.Add(string.Join(",", points[j]));
  1164. //如果输出模式是AllAtOnce或者BatchedUntilTerminator,则需要检查是否达到最大点位数量限制
  1165. if (procedure.OutputPointMode == OutputPointMode.AllAtOnce || procedure.OutputPointMode == OutputPointMode.BatchedUntilTerminator)
  1166. {
  1167. //如果当前总点位数量已经达到最大限制,则停止添加更多点位
  1168. if (allpoints.Count >= procedure.OutputPointCountMax)
  1169. {
  1170. break;
  1171. }
  1172. }
  1173. currentPoints.Add(new double[] { double.Parse(points[j][0]), double.Parse(points[j][1]), double.Parse(points[j][2]) });
  1174. }
  1175. //多个点位之间逗号分隔
  1176. string pointstr = allpoints.Count + "," + string.Join(",", allpoints);
  1177. if (procedure.OutputPointMode == OutputPointMode.UntilTerminator || procedure.OutputPointMode == OutputPointMode.BatchedUntilTerminator)
  1178. {
  1179. outputItems.AddRange(allpoints);
  1180. }
  1181. else
  1182. {
  1183. outputItems.Add(pointstr);
  1184. }
  1185. outputPointCount += allpoints.Count;
  1186. }
  1187. else
  1188. {
  1189. string itemstr = Value != null ? Value.ToString() : string.Empty;
  1190. outputItems.Add(itemstr);
  1191. }
  1192. }
  1193. }
  1194. procedure.LastRunPoints = currentPoints;
  1195. //关闭Feeder灯光
  1196. if (feeder.IsConnected)
  1197. {
  1198. await feeder.CloseLightAsync();
  1199. }
  1200. //如果输出模式是AllAtOnce或者BatchedUntilTerminator,则分次发送数据
  1201. if (procedure.OutputPointMode == OutputPointMode.UntilTerminator || procedure.OutputPointMode == OutputPointMode.BatchedUntilTerminator)
  1202. {
  1203. //将outputItems按照procedure.OutputSeparator进行拼接,得到最终返回结果
  1204. string finalResponse = string.Join(procedure.OutputSeparator, outputItems);
  1205. return (true, outputItems.ToArray());
  1206. }
  1207. else
  1208. {
  1209. //否则,一次发送所有数据
  1210. //将outputItems按照procedure.OutputSeparator进行拼接,得到最终返回结果
  1211. string finalResponse = string.Join(procedure.OutputSeparator, outputItems);
  1212. return (true, new string[] { finalResponse });
  1213. }
  1214. }
  1215. //关闭Feeder灯光
  1216. if (feeder.IsConnected)
  1217. {
  1218. await feeder.CloseLightAsync();
  1219. }
  1220. SendTaskMessage(Lang.多次拍照失败, MessageLevel.Alarm);
  1221. return (false, new string[] { "VisionFailed" });
  1222. }
  1223. /// <summary>
  1224. /// 执行普通视觉任务
  1225. /// </summary>
  1226. /// <param name="procedure"></param>
  1227. /// <param name="items"></param>
  1228. /// <param name="cancellationToken"></param>
  1229. /// <returns></returns>
  1230. public async Task<(bool IsSucceed, string Response)> ExecuteNormalVisionTaskAsync(ProcedureModel procedure, string[] items, CancellationToken cancellationToken)
  1231. {
  1232. // 开始执行前的时间点
  1233. DateTime startTime = DateTime.Now;
  1234. try
  1235. {
  1236. // 触发命令传入的输入终端
  1237. Dictionary<string, string> InputTerminal = null;
  1238. // 解析触发命令传入的输入终端参数
  1239. //判断传入参数中是否存在“SetInputs”
  1240. if (items != null && items.Length>1 && items[0] == "SetInputs")
  1241. {
  1242. InputTerminal = items.Skip(1)
  1243. .Select(x => x.Split('='))
  1244. .Where(y => y.Length == 2)
  1245. .ToDictionary(z => z[0], z => z[1]);
  1246. }
  1247. // 机器人当前坐标
  1248. double[] currentpos = null;
  1249. // 判断传入参数长度是否3,并且是否都是double
  1250. if (items != null && items.Length >= 3 && double.TryParse(items[0], out double _x) && double.TryParse(items[1], out double _y) && double.TryParse(items[2], out double _u))
  1251. {
  1252. currentpos = new double[] { _x, _y, _u };
  1253. }
  1254. //拍照失败时,需要多次拍照
  1255. for (int i = 0; i < procedure.FeederFailLimit; i++)
  1256. {
  1257. if (cancellationToken != null && cancellationToken.IsCancellationRequested)
  1258. {
  1259. return (false, "VisionTaskCancelled");
  1260. }
  1261. // 1. 执行相机拍照并运行视觉工具
  1262. LogHelper.WriteLogInfo("开始执行相机拍照并运行视觉工具");
  1263. bool visionSucceed = ExecutePhoto(procedure, InputTerminal, out CogToolBlockTerminalCollection outputCollection);
  1264. if (!visionSucceed)
  1265. {
  1266. SendTaskMessage(Lang.视觉任务执行失败, MessageLevel.Error);
  1267. continue;
  1268. }
  1269. if (!outputCollection.Contains("Found"))
  1270. {
  1271. SendTaskMessage(Lang.未找到视觉输出结果, MessageLevel.Error);
  1272. return (false, "NoFoundOutput");
  1273. }
  1274. bool Found = (bool)(outputCollection["Found"].Value);
  1275. if (!Found)
  1276. {
  1277. SendTaskMessage(Lang.拍照NG, MessageLevel.Debug);
  1278. continue;
  1279. }
  1280. SendTaskMessage(Lang.拍照OK, MessageLevel.Debug);
  1281. // 2. 获取视觉输出结果
  1282. LogHelper.WriteLogInfo("开始获取视觉输出结果");
  1283. var (IsSucceed, ErrMsg, Output) = GetVisionOutput(procedure, outputCollection, currentpos);
  1284. if (!IsSucceed)
  1285. {
  1286. SendTaskMessage(Lang.获取视觉输出结果失败, MessageLevel.Error);
  1287. return (false, ErrMsg);
  1288. }
  1289. // 3. 组织返回结果
  1290. List<string> outputItems = new List<string>(); //多项输出结果集合
  1291. foreach (var output in procedure.ProcedureOutputs)
  1292. {
  1293. if (Output.ContainsKey(output.Name))
  1294. {
  1295. var (IsPoint, Value) = Output[output.Name];
  1296. if (IsPoint)
  1297. {
  1298. //Value是点位数据,类型:List<string[]>,string[]是一个点位,里面是X、Y,U等数据
  1299. //将所有点位数据和单个点位数据中的坐标之间都用逗号分隔,多个点位之间用逗号分隔
  1300. List<string[]> points = Value as List<string[]>;
  1301. List<string> allpoints = new List<string>();
  1302. for (int j = 0; j < points.Count; j++)
  1303. {
  1304. //单个点位内坐标之间逗号分隔
  1305. allpoints.Add(string.Join(",", points[j]));
  1306. }
  1307. //多个点位之间逗号分隔
  1308. string pointstr = allpoints.Count + "," + string.Join(",", allpoints);
  1309. outputItems.Add(pointstr);
  1310. }
  1311. else
  1312. {
  1313. string itemstr = Value != null ? Value.ToString() : string.Empty;
  1314. outputItems.Add(itemstr);
  1315. }
  1316. }
  1317. }
  1318. //将outputItems按照procedure.OutputSeparator进行拼接,得到最终返回结果
  1319. string finalResponse = string.Join(procedure.OutputSeparator, outputItems);
  1320. return (true, finalResponse);
  1321. }
  1322. return (false, "VisionFailed");
  1323. }
  1324. catch (Exception ex)
  1325. {
  1326. SendTaskMessage(ex.Message, MessageLevel.Error);
  1327. LogHelper.WriteLogError("执行普通视觉任务时出错!", ex);
  1328. return (false, "Exception");
  1329. }
  1330. }
  1331. /// <summary>
  1332. /// 执行相机拍照
  1333. /// </summary>
  1334. /// <param name="procedure"></param>
  1335. /// <param name="InputTerminal">输入终端参数</param>
  1336. /// <param name="outputCollection">输出终端结果</param>
  1337. /// <returns></returns>
  1338. public bool ExecutePhoto(ProcedureModel procedure, Dictionary<string, string> InputTerminal, out CogToolBlockTerminalCollection outputCollection)
  1339. {
  1340. try
  1341. {
  1342. // 1. 采集图像
  1343. var camera = _cameraService.GetCamera(procedure.CameraId);
  1344. bool succed = camera.SetExposureTime(procedure.ExposureTime);
  1345. if (!succed)
  1346. {
  1347. SendTaskMessage($"相机曝光设置失败!", MessageLevel.Error);
  1348. }
  1349. succed = camera.SetGain(procedure.Gain);
  1350. if (!succed)
  1351. {
  1352. SendTaskMessage($"相机增益设置失败!", MessageLevel.Error);
  1353. }
  1354. DateTime nowtime = DateTime.Now;
  1355. LogHelper.WriteLogInfo("开始采集图像");
  1356. var image = camera.Grab();
  1357. if (image == null)
  1358. {
  1359. SendTaskMessage(Lang.图像采集失败, MessageLevel.Error);
  1360. outputCollection = null;
  1361. return false;
  1362. }
  1363. procedure.ToolBlock.Inputs["InputImage"].Value = image;
  1364. LogHelper.WriteLogInfo($"采集图像完成;用时:{(DateTime.Now - nowtime).Milliseconds}ms");
  1365. // 2. 为ToolBlock传入其他输入终端
  1366. if (InputTerminal != null)
  1367. {
  1368. LogHelper.WriteLogInfo($"为ToolBlock传入输入终端");
  1369. foreach (var item in InputTerminal)
  1370. {
  1371. if (procedure.ToolBlock.Inputs.Contains(item.Key))
  1372. {
  1373. LogHelper.WriteLogInfo($"传入[{item.Key}]={item.Value}");
  1374. procedure.ToolBlock.Inputs[item.Key].Value = item.Value;
  1375. }
  1376. else
  1377. {
  1378. LogHelper.WriteLogInfo($"创建并传入[{item.Key}]={item.Value}");
  1379. procedure.ToolBlock.Inputs.Add(new CogToolBlockTerminal(item.Key, item.Value));
  1380. }
  1381. }
  1382. }
  1383. // 3. 运行视觉工具
  1384. LogHelper.WriteLogInfo("开始运行视觉工具");
  1385. procedure.ToolBlock.Run(); //运行ToolBlock
  1386. if (procedure.ToolBlock.RunStatus.Result == CogToolResultConstants.Accept)
  1387. {
  1388. SendTaskMessage(Lang.视觉流程执行耗时.Replace("{0}", procedure.Name).Replace("{1}", $"{procedure.ToolBlock.RunStatus.ProcessingTime:F1}"), MessageLevel.Info);
  1389. outputCollection = procedure.ToolBlock.Outputs; //获取输出终端集合
  1390. DatabaseHelper.AddCameraRecords(_productService.GetCurrentProduct().Name, procedure.Name, outputCollection);
  1391. ICogRecord record = null;
  1392. foreach (CogToolBlockTerminal item in outputCollection)
  1393. {
  1394. if (item.Value is ICogRecord)
  1395. record = item.Value as ICogRecord;
  1396. }
  1397. _eventAggregator.GetEvent<RenderUpdateNotification>().Publish(new ShowRender()
  1398. {
  1399. CameraName = procedure.CameraName,
  1400. Id = procedure.CameraId,
  1401. Image = (ICogImage)procedure.ToolBlock.Inputs["InputImage"].Value,
  1402. Graphic = outputCollection["Graphic"].Value as CogGraphicCollection,
  1403. Record = record,
  1404. Result = (bool)(outputCollection["Found"].Value),
  1405. IsShow = true,
  1406. IsSaveImage = procedure.IsSaveImage,
  1407. SaveImageModel = procedure.SaveImageModel,
  1408. SaveImagePathModel = procedure.SaveImagePathModel,
  1409. SavePath = procedure.SavePath,
  1410. IsCompress = procedure.IsCompress,
  1411. });
  1412. return true;
  1413. }
  1414. else
  1415. {
  1416. SendTaskMessage(Lang.视觉流程执行出错耗时.Replace("{0}", procedure.Name).Replace("{1}", $"{procedure.ToolBlock.RunStatus.ProcessingTime:F1}"), MessageLevel.Error);
  1417. SendTaskMessage(procedure.ToolBlock.RunStatus.Message, MessageLevel.Error);
  1418. outputCollection = null;
  1419. _eventAggregator.GetEvent<RenderUpdateNotification>().Publish(new ShowRender()
  1420. {
  1421. CameraName = procedure.CameraName,
  1422. Id = procedure.CameraId,
  1423. Image = (ICogImage)procedure.ToolBlock.Inputs["InputImage"].Value,
  1424. Graphic = new CogGraphicCollection(),
  1425. Result = false,
  1426. IsShow = true,
  1427. IsSaveImage = procedure.IsSaveImage,
  1428. SaveImageModel = procedure.SaveImageModel,
  1429. SaveImagePathModel = procedure.SaveImagePathModel,
  1430. SavePath = procedure.SavePath
  1431. });
  1432. return false;
  1433. }
  1434. }
  1435. catch (Exception ex)
  1436. {
  1437. SendTaskMessage(ex.Message, MessageLevel.Error);
  1438. LogHelper.WriteLogError("执行相机取图并执行视觉工具组时出错!", ex);
  1439. outputCollection = null;
  1440. return false;
  1441. }
  1442. }
  1443. /// <summary>
  1444. /// 获取需要输出项
  1445. /// </summary>
  1446. /// <param name="procedure"></param>
  1447. /// <param name="outputCollection"></param>
  1448. /// <param name="IsCheckLastPoints"></param>
  1449. /// <returns>是否成功,错误返回内容,<项的名称,<是否是点位数据,值>></returns>
  1450. public (bool IsSucceed, string ErrMsg, Dictionary<string, (bool IsPoint, object Value)> Output) GetVisionOutput(ProcedureModel procedure, CogToolBlockTerminalCollection outputCollection, double[] robotPos)
  1451. {
  1452. RobotBrand robotBrand = RobotBrand.Default;
  1453. var robot = _configService.GetRobot(procedure.RobotId);
  1454. if (robot != null)
  1455. {
  1456. robotBrand = robot.RobotBrand;
  1457. }
  1458. List<string> SendResults = new List<string>();
  1459. //<输出项的名称,<是否点位,每项输出集合值>>
  1460. Dictionary<string, (bool IsPoint, object Value)> ResultCollection = new Dictionary<string, (bool IsPoint, object Value)>();
  1461. foreach (var output in procedure.ProcedureOutputs) //遍历配置的所有需要输出项
  1462. {
  1463. //根据输出类型进行区分输出
  1464. if (output.ValueType == typeof(string))
  1465. {
  1466. //需要点位转换时,进行点位提取
  1467. if (output.IsPointTran)
  1468. {
  1469. if (outputCollection[output.Name].Value == null || string.IsNullOrEmpty(outputCollection[output.Name].Value.ToString()))
  1470. {
  1471. //如果需要输出的项的值为空,即点位数据为空,则输出点位的个数为0
  1472. ResultCollection.Add(output.Name, (true, null));
  1473. continue;
  1474. }
  1475. string[] strpoints = outputCollection[output.Name].Value.ToString().Split(';');
  1476. //存储当前输出点位集合
  1477. List<string[]> currentPointsForFeeder = new List<string[]>();
  1478. //获取相机校准
  1479. var calib = _calibrationService.GetCalibration(procedure.CalibrationId);
  1480. if (calib == null)
  1481. {
  1482. SendTaskMessage(Lang.没有相机校准信息, MessageLevel.Alarm);
  1483. return (false, "NotCalibrations", null);
  1484. }
  1485. // 判断相机安装方式,如果是J4移动相机或者XYZ平台时,则需要判断robotPos不能为null
  1486. if ((calib.CameraMount == CameraMount.MobileJ4 || calib.CameraMount == CameraMount.MobileDown_XYPlatform) && robotPos == null)
  1487. {
  1488. SendTaskMessage(Lang.机器人当前位置不能为空, MessageLevel.Alarm);
  1489. return (false, "RobotPositionNull", null);
  1490. }
  1491. List<string> SendDtat = new List<string>();
  1492. List<string> points = new List<string>();
  1493. //遍历所有的输出点位
  1494. foreach (var pos in strpoints)
  1495. {
  1496. //先将pos按照逗号进行分隔,拿到数组,判断是否至少包含3个元素,并且前三个元素可以转换为double,确保是有效的点位数据
  1497. if (string.IsNullOrEmpty(pos))
  1498. {
  1499. continue;
  1500. }
  1501. //先将pos按照逗号进行分隔,拿到数组
  1502. var posParts = pos.Split(',');
  1503. if (posParts.Length < 3 ||
  1504. !double.TryParse(posParts[0], out double _pixel_x) ||
  1505. !double.TryParse(posParts[1], out double _pixel_y) ||
  1506. !double.TryParse(posParts[2], out double _pixel_u))
  1507. {
  1508. SendTaskMessage(string.Format(Lang.点位数据格式错误, pos), MessageLevel.Alarm);
  1509. return (false, "PointDataFormatError", null);
  1510. }
  1511. //转换点位-像素转换成机器人绝对坐标
  1512. var calibResult = _calibrationService.ConvertPixelToPosition((_pixel_x, _pixel_y, _pixel_u), robotPos, calib, robotBrand);
  1513. if (!calibResult.IsSucceed)
  1514. {
  1515. return (false, "CalibrationFailed", null);
  1516. }
  1517. //截取posParts数组从索引3开始到结尾的所有元素,得到新新的数组extraInfoArray
  1518. string[] extraInfoArray = posParts.Skip(3).ToArray();
  1519. //将rpos转换为字符串数组,保留3位小数
  1520. string[] strings = new string[] { $"{calibResult.X:F3}", $"{calibResult.Y:F3}", $"{calibResult.U:F3}" };
  1521. List<string> rposString = new List<string>(strings);
  1522. if (extraInfoArray.Length > 0)
  1523. {
  1524. rposString.AddRange(extraInfoArray);
  1525. }
  1526. currentPointsForFeeder.Add(rposString.ToArray());
  1527. }
  1528. ResultCollection[output.Name] = (true, currentPointsForFeeder);
  1529. }
  1530. //直接输出字符串
  1531. else
  1532. {
  1533. if (outputCollection[output.Name].Value == null || string.IsNullOrEmpty(outputCollection[output.Name].Value.ToString()))
  1534. {
  1535. ResultCollection.Add(output.Name, (false, null));
  1536. continue;
  1537. }
  1538. ResultCollection[output.Name] = (false, outputCollection[output.Name].Value.ToString());
  1539. }
  1540. }
  1541. else
  1542. {
  1543. if (outputCollection[output.Name].Value == null)
  1544. {
  1545. ResultCollection.Add(output.Name, (false, null));
  1546. }
  1547. else
  1548. {
  1549. ResultCollection[output.Name] = (false, outputCollection[output.Name].Value.ToString());
  1550. }
  1551. }
  1552. }
  1553. return (true, string.Empty, ResultCollection);
  1554. }
  1555. /// <summary>
  1556. /// Feeder拍照前,执行Feeder
  1557. /// </summary>
  1558. /// <param name="Feeder">Feeder</param>
  1559. /// <param name="procedure">流程</param>
  1560. /// <param name="doVibrateFirst">是否先振动</param>
  1561. /// <param name="doCaptureFirst">是否先拍照</param>
  1562. /// <param name="cancellationToken"></param>
  1563. /// <returns></returns>
  1564. public async Task ExecuteFeederForPhotoBefore(IVoiceCoilMotorFeeder Feeder, ProcedureModel procedure, bool doVibrateFirst, bool doCaptureFirst, CancellationToken cancellationToken)
  1565. {
  1566. // 1. 设置Feeder光源亮度、打开光源
  1567. if (Feeder.FeederBrand == FeederBrand.AFAG)
  1568. {
  1569. await Feeder.SetLightValueAsync((ushort)procedure.LightValue);
  1570. }
  1571. else
  1572. {
  1573. //Feeder光源亮度设置
  1574. if (procedure.LightValue > 3)
  1575. {
  1576. var light = await Feeder.GetLightBrightnessAsync();
  1577. if (procedure.LightValue != light)
  1578. {
  1579. await Feeder.SetLightValueAsync((ushort)procedure.LightValue);
  1580. }
  1581. //获取光源的超时时间
  1582. int lightTimeout = await Feeder.GetLightTimeoutAsync();
  1583. if (lightTimeout < 10000)
  1584. {
  1585. await Feeder.SetLightTimeOutAsync(10000);
  1586. }
  1587. //打开光源
  1588. await Feeder.OpenLightAsync();
  1589. }
  1590. }
  1591. if (doCaptureFirst)//如果不振动,等待光源稳定时间
  1592. {
  1593. if (procedure.WaitPhoto > 0) await Task.Delay(procedure.WaitPhoto);
  1594. return;
  1595. }
  1596. //先振后拍时
  1597. if (procedure.VibrationModel == VibrationModel.VibrationPhoto || doVibrateFirst)
  1598. {
  1599. if (procedure.FeederWorks != null && procedure.FeederWorks.FeederWorkList != null)
  1600. {
  1601. bool flog = false;
  1602. //如果上次的Blob面积小于设定的最小值,则触发供料
  1603. if (procedure.LastBlobArea <= procedure.MinArea)
  1604. {
  1605. flog = true;
  1606. SendTaskMessage(Lang.执行料仓供料, MessageLevel.Info);
  1607. await ExecuteFeederStockWorks(Feeder, procedure.FeederStockWorks, cancellationToken);
  1608. if (procedure.WaitFeederStop > 0) await Task.Delay(procedure.WaitFeederStop);
  1609. SendTaskMessage(Lang.料仓供料结束, MessageLevel.Info);
  1610. }
  1611. if (!flog)
  1612. {
  1613. //执行配置的Feeder振动任务
  1614. await ExecuteFeederWorks(Feeder, procedure.FeederWorks, cancellationToken, false);
  1615. if (procedure.WaitFeederStop > 0) await Task.Delay(procedure.WaitFeederStop);
  1616. }
  1617. }
  1618. }
  1619. else
  1620. {
  1621. if (procedure.LightValue > 3)
  1622. {
  1623. if (procedure.WaitPhoto > 0) await Task.Delay(procedure.WaitPhoto);
  1624. }
  1625. //如果上次的Blob面积小于设定的最小值,则触发供料
  1626. if (procedure.LastBlobArea <= procedure.MinArea)
  1627. {
  1628. SendTaskMessage(Lang.执行料仓供料, MessageLevel.Info);
  1629. await ExecuteFeederStockWorks(Feeder, procedure.FeederStockWorks, cancellationToken);
  1630. if (procedure.WaitFeederStop > 0) await Task.Delay(procedure.WaitFeederStop);
  1631. SendTaskMessage(Lang.料仓供料结束, MessageLevel.Info);
  1632. }
  1633. }
  1634. }
  1635. /// <summary>
  1636. /// Feeder拍照后,拍照失败时,执行Feeder
  1637. /// </summary>
  1638. /// <param name="Feeder"></param>
  1639. /// <param name="procedure"></param>
  1640. /// <returns></returns>
  1641. public async Task ExecuteFeederForPhotoLater(IVoiceCoilMotorFeeder Feeder, ProcedureModel procedure, CancellationToken cancellationToken)
  1642. {
  1643. //根据斑点面积决定是否料仓供料
  1644. if (procedure.LastBlobArea <= procedure.MinArea)
  1645. {
  1646. SendTaskMessage(Lang.执行料仓供料, MessageLevel.Info);
  1647. await ExecuteFeederStockWorks(Feeder, procedure.FeederStockWorks, cancellationToken);
  1648. if (cancellationToken != null && cancellationToken.IsCancellationRequested) return;
  1649. if (procedure.WaitFeederStop > 0) await Task.Delay(procedure.WaitFeederStop, cancellationToken);
  1650. SendTaskMessage(Lang.料仓供料结束, MessageLevel.Info);
  1651. }
  1652. else
  1653. {
  1654. if (procedure.FeederWorks != null && procedure.FeederWorks.FeederWorkList != null)
  1655. {
  1656. SendTaskMessage(Lang.执行Feeder振动, MessageLevel.Info);
  1657. //执行配置的Feeder振动任务
  1658. await ExecuteFeederWorks(Feeder, procedure.FeederWorks, cancellationToken);
  1659. if (cancellationToken != null && cancellationToken.IsCancellationRequested) return;
  1660. if (procedure.WaitFeederStop > 0) await Task.Delay(procedure.WaitFeederStop, cancellationToken);
  1661. SendTaskMessage(Lang.Feeder振动结束, MessageLevel.Info);
  1662. }
  1663. }
  1664. }
  1665. /// <summary>
  1666. /// 执行Feeder料仓供料及振动集合
  1667. /// </summary>
  1668. /// <param name="feeder"></param>
  1669. /// <param name="works"></param>
  1670. /// <returns></returns>
  1671. public async Task ExecuteFeederStockWorks(IVoiceCoilMotorFeeder feeder, FeederStockWorks works, CancellationToken cancellationToken, bool isUpdateParame = true)
  1672. {
  1673. //同时执行料仓供料和Feeder振动任务
  1674. var stockTask = ExecuteFeederStockWorksOnly(feeder, works.FeederStockWorkList.ToArray(), cancellationToken);
  1675. var vibrationTask = ExecuteFeederWorkOnly(feeder, works.FeederWorkList.ToArray(), cancellationToken);
  1676. await Task.WhenAll(stockTask, vibrationTask);
  1677. }
  1678. /// <summary>
  1679. /// 执行料仓振动任务
  1680. /// </summary>
  1681. /// <param name="feeder"></param>
  1682. /// <param name="works"></param>
  1683. /// <param name="cancellationToken"></param>
  1684. /// <returns></returns>
  1685. public async Task ExecuteFeederStockWorksOnly(IVoiceCoilMotorFeeder feeder, FeederStockWork[] works, CancellationToken cancellationToken)
  1686. {
  1687. //执行配置的料仓振动任务
  1688. if (feeder.FeederBrand == FeederBrand.AFAG)
  1689. {
  1690. foreach (var stockwork in works)
  1691. {
  1692. if (stockwork.RegisterIndex == 0)
  1693. {
  1694. if (stockwork.Duration > 0) await Task.Delay(stockwork.Duration, cancellationToken);
  1695. }
  1696. else if (stockwork.RegisterIndex == 1)
  1697. {
  1698. //if (BgModbusTcpCommunicate != null)
  1699. //{
  1700. // BgModbusTcpCommunicate.Slave.DataStore.HoldingRegisters[stockwork.Address] = 1;
  1701. // await Task.Delay(stockwork.Duration);
  1702. // BgModbusTcpCommunicate.Slave.DataStore.HoldingRegisters[stockwork.Address] = 0;
  1703. //}
  1704. }
  1705. if (cancellationToken != null && cancellationToken.IsCancellationRequested)
  1706. {
  1707. return;
  1708. }
  1709. }
  1710. }
  1711. else
  1712. {
  1713. foreach (var stockwork in works)
  1714. {
  1715. if (stockwork.Function == VibrationFunction.Delay)
  1716. {
  1717. if (stockwork.Duration > 0) await Task.Delay(stockwork.Duration, cancellationToken);
  1718. }
  1719. else
  1720. {
  1721. await feeder.OutputAsync(((int)stockwork.Function) + 1, stockwork.Duration);
  1722. if (stockwork.Duration > 0) await Task.Delay(stockwork.Duration, cancellationToken);
  1723. }
  1724. if (cancellationToken != null && cancellationToken.IsCancellationRequested)
  1725. {
  1726. return;
  1727. }
  1728. }
  1729. }
  1730. }
  1731. /// <summary>
  1732. /// 执行Feeder振动任务
  1733. /// </summary>
  1734. /// <param name="feeder"></param>
  1735. /// <param name="feederwork"></param>
  1736. /// <param name="cancellationToken"></param>
  1737. /// <param name="isUpdateParame"></param>
  1738. /// <returns></returns>
  1739. public async Task ExecuteFeederWorkOnly(IVoiceCoilMotorFeeder feeder, FeederWork[] works, CancellationToken cancellationToken, bool isUpdateParame = true)
  1740. {
  1741. if (works == null)
  1742. {
  1743. return;
  1744. }
  1745. if (feeder.FeederBrand == FeederBrand.AFAG)
  1746. {
  1747. //执行配置的Feeder振动任务
  1748. foreach (var feederwork in works)
  1749. {
  1750. if (feederwork.ConfigIndex < 0)
  1751. {
  1752. continue;
  1753. }
  1754. if (feederwork.ConfigIndex == 0)
  1755. {
  1756. if (feederwork.Duration > 0) await Task.Delay(feederwork.Duration, cancellationToken);
  1757. }
  1758. else
  1759. {
  1760. ((AfagFeeder)feeder).LoadConfiguration(feederwork.ConfigIndex);
  1761. ((AfagFeeder)feeder).SetIntensity(feederwork.Intensity);
  1762. ((AfagFeeder)feeder).SetDirection(feederwork.Direction);
  1763. if (feederwork.Duration > 0) await Task.Delay(feederwork.Duration, cancellationToken);
  1764. if (cancellationToken != null && cancellationToken.IsCancellationRequested)
  1765. {
  1766. return;
  1767. }
  1768. ((AfagFeeder)feeder).SetIntensity(0);
  1769. }
  1770. if (cancellationToken != null && cancellationToken.IsCancellationRequested)
  1771. {
  1772. return;
  1773. }
  1774. }
  1775. }
  1776. else
  1777. {
  1778. //执行配置的Feeder振动任务
  1779. foreach (var feederwork in works)
  1780. {
  1781. if (feederwork.Function == VibrationFunction.Delay)
  1782. {
  1783. if (feederwork.Duration > 0) await Task.Delay(feederwork.Duration, cancellationToken);
  1784. }
  1785. else
  1786. {
  1787. if (feederwork.SingleAction != null && isUpdateParame)
  1788. {
  1789. await feeder.SetShakeParametersAsync((int)feederwork.Function, feederwork.SingleAction);
  1790. }
  1791. await feeder.StartAction((int)feederwork.Function, feederwork.Duration);
  1792. if (feederwork.Duration > 0) await Task.Delay(feederwork.Duration, cancellationToken);
  1793. }
  1794. if (cancellationToken != null && cancellationToken.IsCancellationRequested)
  1795. {
  1796. return;
  1797. }
  1798. }
  1799. }
  1800. }
  1801. /// <summary>
  1802. /// 执行Feeder振动集合
  1803. /// </summary>
  1804. /// <param name="feeder"></param>
  1805. /// <param name="works"></param>
  1806. /// <param name="cancellationToken"></param>
  1807. /// <param name="isUpdateParame"></param>
  1808. /// <returns></returns>
  1809. public async Task ExecuteFeederWorks(IVoiceCoilMotorFeeder feeder, FeederWorks works, CancellationToken cancellationToken, bool isUpdateParame = true)
  1810. {
  1811. //执行配置的Feeder振动任务
  1812. if (feeder.FeederBrand == FeederBrand.AFAG)
  1813. {
  1814. foreach (var feederwork in works.FeederWorkList)
  1815. {
  1816. if (feederwork.ConfigIndex < 0)
  1817. {
  1818. continue;
  1819. }
  1820. if (feederwork.ConfigIndex == 0)
  1821. {
  1822. await Task.Delay(feederwork.Duration, cancellationToken);
  1823. }
  1824. else
  1825. {
  1826. ((AfagFeeder)feeder).LoadConfiguration(feederwork.ConfigIndex);
  1827. ((AfagFeeder)feeder).SetIntensity(feederwork.Intensity);
  1828. ((AfagFeeder)feeder).SetDirection(feederwork.Direction);
  1829. await Task.Delay(feederwork.Duration, cancellationToken);
  1830. if (cancellationToken != null && cancellationToken.IsCancellationRequested)
  1831. {
  1832. return;
  1833. }
  1834. ((AfagFeeder)feeder).SetIntensity(0);
  1835. }
  1836. if (cancellationToken != null && cancellationToken.IsCancellationRequested)
  1837. {
  1838. return;
  1839. }
  1840. }
  1841. }
  1842. else
  1843. {
  1844. foreach (var feederwork in works.FeederWorkList)
  1845. {
  1846. if (feederwork.Function == VibrationFunction.Delay)
  1847. {
  1848. if (feederwork.Duration > 0) await Task.Delay(feederwork.Duration, cancellationToken);
  1849. }
  1850. else
  1851. {
  1852. if (feederwork.SingleAction != null && isUpdateParame)
  1853. {
  1854. await feeder.SetShakeParametersAsync((int)feederwork.Function, feederwork.SingleAction);
  1855. }
  1856. await feeder.StartAction((int)feederwork.Function, feederwork.Duration);
  1857. if (feederwork.Duration > 0) await Task.Delay(feederwork.Duration, cancellationToken);
  1858. }
  1859. if (cancellationToken != null && cancellationToken.IsCancellationRequested)
  1860. {
  1861. return;
  1862. }
  1863. }
  1864. }
  1865. }
  1866. #endregion
  1867. }
  1868. }