Management.cs 239 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240
  1. using Cognex.VisionPro.ToolBlock;
  2. using MathNet.Numerics.LinearAlgebra;
  3. using NPOI.SS.Formula.Functions;
  4. using NPOI.Util;
  5. using OpenCvSharp;
  6. using Prism.Events;
  7. using Prism.Ioc;
  8. using Prism.Mvvm;
  9. using Prism.Regions;
  10. using SqlSugar;
  11. using System;
  12. using System.Collections.Generic;
  13. using System.Collections.ObjectModel;
  14. using System.Drawing;
  15. using System.IO;
  16. using System.IO.Compression;
  17. using System.Linq;
  18. using System.Text;
  19. using System.Threading;
  20. using System.Threading.Tasks;
  21. using System.Windows.Media;
  22. using Team.FFFeederService.Interfaces;
  23. using TeamAAS_VP.Core.PLCs;
  24. using TeamAAS_VP.Core.ScrewDriver;
  25. using TeamAAS_VP.Enums;
  26. using TeamAAS_VP.Events;
  27. using TeamAAS_VP.Interfaces;
  28. using TeamAAS_VP.Models;
  29. using TeamAAS_VP.Resources.Languages;
  30. using TouchSocket.Core;
  31. using TouchSocket.Sockets;
  32. namespace TeamAAS_VP.Core
  33. {
  34. public class Management : BindableBase
  35. {
  36. #region 字段
  37. IRegionManager _regionManager;
  38. IEventAggregator _eventAggregator;
  39. IContainerProvider _container;
  40. IConfigService _configService;
  41. IRobotService _robotService;
  42. ICameraService _cameraService;
  43. IFeederService _feederService;
  44. IPlcService _plcService;
  45. ILightManagerService _lightManagerService;
  46. IProductService _productService;
  47. ICalibrationService _calibrationService;
  48. IRemoteCommandService _remoteCommandService;
  49. ISystemDatabaseService _systemDatabaseService;
  50. IMesService _mesService;
  51. Timer yieldtimer;
  52. private DateTime StartTime = DateTime.Now;
  53. // Pressure collection
  54. /// <summary>
  55. /// 压力采集信号控制CTS
  56. /// </summary>
  57. private CancellationTokenSource[] _pressureCts = new CancellationTokenSource[10];
  58. /// <summary>
  59. /// 压力采集超时CTS
  60. /// </summary>
  61. private CancellationTokenSource[] _pressureTimeoutCts = new CancellationTokenSource[10];
  62. /// <summary>
  63. /// 压力采集CTS
  64. /// </summary>
  65. private CancellationTokenSource[] _pressureLinkedCts = new CancellationTokenSource[10];
  66. /// <summary>
  67. /// 压力数据集合
  68. /// </summary>
  69. private List<(DateTime Timestamp, float Value)>[] _pressureSamples = new List<(DateTime, float)>[10];
  70. /// <summary>
  71. /// 采样间隔毫秒
  72. /// </summary>
  73. private int _pressureSampleIntervalMs = 50; //采样间隔毫秒
  74. /// <summary>
  75. /// 超时自动停止秒数
  76. /// </summary>
  77. private int _pressureMaxDurationSeconds = 30; //超时自动停止秒数
  78. DateTime[] StartCtqTime = new DateTime[3];//开始采集Ctq数据的时间
  79. /// <summary>
  80. /// 上次执行上传csv的日期
  81. /// </summary>
  82. private DateTime _lastCompressDate = DateTime.MinValue;
  83. /// <summary>
  84. /// 当天是否上传过csv
  85. /// </summary>
  86. private bool _hasCompressedToday = false;
  87. #endregion
  88. #region 属性
  89. private System.Windows.Media.Brush _StatusBackground;
  90. public System.Windows.Media.Brush StatusBackground
  91. {
  92. get { return _StatusBackground; }
  93. set { SetProperty(ref _StatusBackground, value); }
  94. }
  95. private System.Windows.Media.Brush _RunModelBackground;
  96. public System.Windows.Media.Brush RunModelBackground
  97. {
  98. get { return _RunModelBackground; }
  99. set { SetProperty(ref _RunModelBackground, value); }
  100. }
  101. private System.Windows.Media.Brush _ManualModeBackground;
  102. public System.Windows.Media.Brush ManualModeBackground
  103. {
  104. get { return _ManualModeBackground; }
  105. set { SetProperty(ref _ManualModeBackground, value); }
  106. }
  107. private System.Windows.Media.Brush _ClearModeBackground;
  108. public System.Windows.Media.Brush ClearModeBackground
  109. {
  110. get { return _ClearModeBackground; }
  111. set { SetProperty(ref _ClearModeBackground, value); }
  112. }
  113. private ObservableCollection<ShowRender> _Renders;
  114. /// <summary>
  115. /// 显示界面
  116. /// </summary>
  117. public ObservableCollection<ShowRender> Renders
  118. {
  119. get { return _Renders; }
  120. set { SetProperty(ref _Renders, value); }
  121. }
  122. public BgCommunicate BgCommunicate { get; private set; }
  123. public BgModbusTcpCommunicate BgModbusTcpCommunicate { get; private set; }
  124. private ObservableCollection<StatusInfo> _Status = new ObservableCollection<StatusInfo>();
  125. /// <summary>
  126. /// 状态栏集合
  127. /// </summary>
  128. public ObservableCollection<StatusInfo> Status
  129. {
  130. get { return _Status; }
  131. set { SetProperty(ref _Status, value); }
  132. }
  133. private Int64 _TotalQuantity;
  134. /// <summary>
  135. /// 总产量
  136. /// </summary>
  137. public Int64 TotalQuantity
  138. {
  139. get { return _TotalQuantity; }
  140. set { SetProperty(ref _TotalQuantity, value); }
  141. }
  142. private Int64 _TotalQuantityToday;
  143. /// <summary>
  144. /// 当天的总产量
  145. /// </summary>
  146. public Int64 TotalQuantityToday
  147. {
  148. get { return _TotalQuantityToday; }
  149. set { SetProperty(ref _TotalQuantityToday, value); }
  150. }
  151. private Int64 _CurrentUPH;
  152. /// <summary>
  153. /// 当前的UPH
  154. /// </summary>
  155. public Int64 CurrentUPH
  156. {
  157. get { return _CurrentUPH; }
  158. set { SetProperty(ref _CurrentUPH, value); }
  159. }
  160. private XYD_ScrewDriver _ScrewDriver;
  161. public XYD_ScrewDriver ScrewDriver
  162. {
  163. get { return _ScrewDriver; }
  164. set { SetProperty(ref _ScrewDriver, value); }
  165. }
  166. public Dictionary<string, object> BackupObject { get; set; } = new Dictionary<string, object>();
  167. private double _CTtime;
  168. /// <summary>
  169. /// ct
  170. /// </summary>
  171. public double CTtime
  172. {
  173. get { return _CTtime; }
  174. set { SetProperty(ref _CTtime, value); }
  175. }
  176. private ObservableCollection<DataFormCtq> _dfc = new ObservableCollection<DataFormCtq>();
  177. /// <summary>
  178. /// CTQ数据集合
  179. /// </summary>
  180. public ObservableCollection<DataFormCtq> DFC
  181. {
  182. get { return _dfc; }
  183. set { SetProperty(ref _dfc, value); }
  184. }
  185. private ObservableCollection<DataFormCtq> _dfc2 = new ObservableCollection<DataFormCtq>();
  186. /// <summary>
  187. /// CTQ数据集合
  188. /// </summary>
  189. public ObservableCollection<DataFormCtq> DFC2
  190. {
  191. get { return _dfc2; }
  192. set { SetProperty(ref _dfc2, value); }
  193. }
  194. private Int64 _ThrowNumber;
  195. /// <summary>
  196. /// 抛料数
  197. /// </summary>
  198. public Int64 ThrowNumber
  199. {
  200. get { return _ThrowNumber; }
  201. set { SetProperty(ref _ThrowNumber, value); }
  202. }
  203. private Int64 _NgNumber;
  204. /// <summary>
  205. /// ng数量
  206. /// </summary>
  207. public Int64 NgNumber
  208. {
  209. get { return _NgNumber; }
  210. set { SetProperty(ref _NgNumber, value); }
  211. }
  212. private double _Yield;
  213. /// <summary>
  214. /// 良率
  215. /// </summary>
  216. public double Yield
  217. {
  218. get { return _Yield; }
  219. set { SetProperty(ref _Yield, value); }
  220. }
  221. private ObservableCollection<ProductModel> _ProductList;
  222. public ObservableCollection<ProductModel> ProductList
  223. {
  224. get { return _ProductList; }
  225. set { SetProperty(ref _ProductList, value); }
  226. }
  227. public static int uphhour = 0;
  228. public static int uphnumber = 0;
  229. #endregion
  230. #region 命令
  231. #endregion
  232. #region 事件
  233. #endregion
  234. public Management(IRegionManager regionManager, IEventAggregator ea, IContainerProvider container, IConfigService configService,
  235. IRobotService robotService, ICameraService cameraService, IFeederService feederService, IPlcService plcService, IProductService productService,
  236. ICalibrationService calibrationService, IRemoteCommandService remoteCommandService, ISystemDatabaseService systemDatabaseService, ILightManagerService lightManagerService,
  237. IMesService mesService)
  238. {
  239. _regionManager = regionManager;
  240. _eventAggregator = ea;
  241. _container = container;
  242. _configService = configService;
  243. _robotService = robotService;
  244. _cameraService = cameraService;
  245. _feederService = feederService;
  246. _plcService = plcService;
  247. _calibrationService = calibrationService;
  248. yieldtimer = new Timer(DoYieldTime, null, 10000, 1000);
  249. Renders = new ObservableCollection<ShowRender>();
  250. _configService = configService;
  251. _productService = productService;
  252. _remoteCommandService = remoteCommandService;
  253. _systemDatabaseService = systemDatabaseService;
  254. _lightManagerService = lightManagerService;
  255. _mesService = mesService;
  256. _eventAggregator.GetEvent<CurrentStatusNotification>().Subscribe(CurrentRunStatus);
  257. InitPressure();
  258. }
  259. #region 初始化硬件模块
  260. /// <summary>
  261. /// 初始化Feeder
  262. /// </summary>
  263. /// <returns></returns>
  264. public async Task<(bool IsSucceed, string Msg)> InitFeeders()
  265. {
  266. var feeders = _configService.GetAllFeeders();
  267. StringBuilder sb = new StringBuilder();
  268. foreach (var item in feeders)
  269. {
  270. _feederService.CreateFeeder(item.Id, item.FeederNo, item.FeederName, item.IP, item.Port, item.FeederBrand);
  271. var feeder = _feederService.GetFeeder(item.Id);
  272. Status.Add(new StatusInfo(item.Id, item.FeederName + $"{Lang.未连接}", new SolidColorBrush(Colors.Red)));
  273. try
  274. {
  275. feeder.FeederConnectionStatusChanged += Management_FeederConnectionStatusChanged;
  276. await feeder.ConnectAsync(); //连接Feeder
  277. //SendTaskMessage($"{item.FeederName}已连接!", MessageLevel.Debug);
  278. }
  279. catch (Exception ex)
  280. {
  281. sb.AppendLine($"{item.FeederName}:{Lang.断开连接}![{ex.Message}]");
  282. SendTaskMessage($"{item.FeederName}{Lang.断开连接}!", MessageLevel.Alarm);
  283. }
  284. }
  285. if (sb.Length == 0)
  286. {
  287. return (true, "");
  288. }
  289. else
  290. {
  291. return (false, sb.ToString());
  292. }
  293. }
  294. /// <summary>
  295. /// 初始化机器人
  296. /// </summary>
  297. /// <returns></returns>
  298. public async Task<(bool IsSucceed, string Msg)> InitRobots()
  299. {
  300. var robots = _configService.GetAllRobots();
  301. StringBuilder sb = new StringBuilder();
  302. foreach (var item in robots)
  303. {
  304. _robotService.CreateRobot(item.Id, item);
  305. try
  306. {
  307. Status.Add(new StatusInfo(item.Id, item.RobotName + $"{Lang.未连接}", new SolidColorBrush(Colors.Red)));
  308. var robot = _robotService.GetRobot(item.Id);
  309. robot.ReceivedEvent += Management_ReceivedEvent;
  310. robot.SendEvent += Management_SendEvent;
  311. robot.ConnectedEvent += Management_ConnectedEvent;
  312. robot.DisconnectedEvent += Management_DisconnectedEvent;
  313. await robot.ConnectAsync(); //连接Robot
  314. SendTaskMessage($"{item.RobotName}{Lang.已连接}!", MessageLevel.Debug);
  315. }
  316. catch (Exception ex)
  317. {
  318. sb.AppendLine($"{item.RobotName}:{Lang.断开连接}![{ex.Message}]");
  319. SendTaskMessage($"{item.RobotName}{Lang.断开连接}!", MessageLevel.Alarm);
  320. }
  321. }
  322. if (sb.Length == 0)
  323. {
  324. return (true, "");
  325. }
  326. else
  327. {
  328. return (false, sb.ToString());
  329. }
  330. }
  331. /// <summary>
  332. /// 初始化PLC
  333. /// </summary>
  334. /// <returns></returns>
  335. public async Task<(bool IsSucceed, string Msg)> InitPlc()
  336. {
  337. var plcs = _configService.GetAllPlcs();
  338. StringBuilder sb = new StringBuilder();
  339. foreach (var item in plcs)
  340. {
  341. try
  342. {
  343. _plcService.CreatePlc(item.Id, item);
  344. var plc = _plcService.GetPlc(item.Id);
  345. Status.Add(new StatusInfo(item.Id, item.Name + $"{Lang.未连接}", new SolidColorBrush(Colors.Red)));
  346. plc.ConnectChangedEvent += Management_PlcConnectChangedEvent;
  347. await plc.ConnectAsync(); //连接PLC
  348. //SendTaskMessage($"{item.Name}{Lang.已连接}!", MessageLevel.Debug);
  349. }
  350. catch (Exception ex)
  351. {
  352. sb.AppendLine($"{item.Name}:{Lang.断开连接}![{ex.Message}]");
  353. SendTaskMessage($"{item.Name}{Lang.断开连接}!", MessageLevel.Alarm);
  354. }
  355. }
  356. try
  357. {
  358. await WriteSystemParameterToPlcAsync(_configService.GetSystemConfiguration());
  359. RegisterPlcMonitorAddress();
  360. }
  361. catch (Exception ex)
  362. {
  363. LogHelper.WriteLogError("为PLC写入系统参数和订阅变量时出错", ex);
  364. }
  365. if (sb.Length == 0)
  366. {
  367. return (true, "");
  368. }
  369. else
  370. {
  371. return (false, sb.ToString());
  372. }
  373. }
  374. /// <summary>
  375. /// 初始化相机
  376. /// </summary>
  377. /// <returns></returns>
  378. public async Task<(bool IsSucceed, string Msg)> InitCameras()
  379. {
  380. var cameras = _configService.GetAllCameras();
  381. StringBuilder sb = new StringBuilder();
  382. foreach (var item in cameras)
  383. {
  384. try
  385. {
  386. // Renders.Add(new ShowRender() { Id = item.Id, CameraName = item.CameraName });
  387. _cameraService.CreateCamera(item.Id, item);
  388. var camera = _cameraService.GetCamera(item.Id);
  389. Status.Add(new StatusInfo(item.Id, item.CameraName + $"{Lang.未连接}", new SolidColorBrush(Colors.Red)));
  390. camera.CameraConnectChangedEvent += Management_CameraConnectChangedEvent;
  391. try
  392. {
  393. if (await camera.OpenDeviceAsync())
  394. {
  395. SendTaskMessage($"{item.CameraName}{Lang.已连接}!", MessageLevel.Debug);
  396. }
  397. else
  398. {
  399. SendTaskMessage($"{item.CameraName}{Lang.连接失败}!", MessageLevel.Alarm);
  400. sb.AppendLine($"{item.CameraName}:{Lang.连接失败}!");
  401. }
  402. }
  403. catch (Exception ex)
  404. {
  405. SendTaskMessage($"{item.CameraName}{ex.Message}!", MessageLevel.Alarm);
  406. sb.AppendLine($"{item.CameraName}:{ex.Message}!");
  407. }
  408. }
  409. catch (Exception ex)
  410. {
  411. SendTaskMessage($"{item.CameraName}{Lang.连接失败}!", MessageLevel.Alarm);
  412. sb.AppendLine($"{item.CameraName}:{Lang.连接失败}!:{ex.Message}");
  413. }
  414. }
  415. //_container.Resolve<ShowVisionRenderViewModel>().Renders = Renders;
  416. if (sb.Length == 0)
  417. {
  418. return (true, "");
  419. }
  420. else
  421. {
  422. return (false, sb.ToString());
  423. }
  424. }
  425. /// <summary>
  426. /// 初始化光源控制器
  427. /// </summary>
  428. /// <returns></returns>
  429. public async Task<(bool IsSucceed, string Msg)> InitLightControllers()
  430. {
  431. var lightcontrollers = _configService.GetAllLightControllers();
  432. StringBuilder sb = new StringBuilder();
  433. foreach (var item in lightcontrollers)
  434. {
  435. try
  436. {
  437. var lishtCtr = await _lightManagerService.RegisterControllerAsync(item.Id, item);
  438. //var lightcontroller = _cameraService.GetLightController(item.Id);
  439. //await lightcontroller.ConnectAsync(); //连接光源控制器
  440. //SendTaskMessage($"{item.LightControllerName}{Lang.已连接}!", MessageLevel.Debug);
  441. }
  442. catch (Exception ex)
  443. {
  444. //sb.AppendLine($"{item.LightControllerName}:{Lang.断开连接}![{ex.Message}]");
  445. //SendTaskMessage($"{item.LightControllerName}{Lang.断开连接}!", MessageLevel.Alarm);
  446. }
  447. }
  448. if (lightcontrollers.Count > 0)
  449. {
  450. var result = await _lightManagerService.ConnectAllAsync();
  451. if (result)
  452. {
  453. return (true, "");
  454. }
  455. else
  456. {
  457. return (false, "Light Controller Connect Failed!");
  458. }
  459. }
  460. return (true, "");
  461. //if (sb.Length == 0)
  462. //{
  463. // return (true, "");
  464. //}
  465. //else
  466. //{
  467. // return (false, sb.ToString());
  468. //}
  469. }
  470. /// <summary>
  471. /// 初始化后台TCP服务器通讯
  472. /// </summary>
  473. public void InitBgTcp()
  474. {
  475. var config = _configService.GetBgTcp();
  476. try
  477. {
  478. BgCommunicate = new BgCommunicate(config);
  479. BgCommunicate.ConnectedEvent += BgCommunicate_ConnectedEvent;
  480. BgCommunicate.DisconnectedEvent += BgCommunicate_DisconnectedEvent;
  481. BgCommunicate.ReceivedEvent += BgCommunicate_ReceivedEvent;
  482. BgCommunicate.SendEvent += BgCommunicate_SendEvent;
  483. BgCommunicate.StartListening();
  484. SendTaskMessage($"{Lang.已开启监听端口}:{config.Port}", MessageLevel.Debug);
  485. }
  486. catch (Exception ex)
  487. {
  488. SendTaskMessage($"{Lang.未开启监听端口}:{config.Port}", MessageLevel.Error);
  489. LogHelper.WriteLogError("开启后台服务器通讯时出错!", ex);
  490. }
  491. }
  492. /// <summary>
  493. /// 初始化后台ModbusTCP从站
  494. /// </summary>
  495. public void InitModbusTcp()
  496. {
  497. try
  498. {
  499. var config = _configService.GetBgModbusTcp();
  500. BgModbusTcpCommunicate = new BgModbusTcpCommunicate(config);
  501. if (config.IsEnabled)
  502. {
  503. BgModbusTcpCommunicate.Listen();
  504. SendTaskMessage($"Start Modbus Tcp Slave:{BgModbusTcpCommunicate.IP}:{BgModbusTcpCommunicate.Port}:{BgModbusTcpCommunicate.SlaveID}", MessageLevel.Debug);
  505. }
  506. }
  507. catch (Exception ex)
  508. {
  509. SendTaskMessage($"No Start Modbus Tcp Slave:{ex.Message}", MessageLevel.Alarm);
  510. LogHelper.WriteLogError("开启后台Modbus TCP通讯时出错!", ex);
  511. }
  512. }
  513. /// <summary>
  514. /// 初始化后台脚本
  515. /// </summary>
  516. public void InitBackgroundcript()
  517. {
  518. try
  519. {
  520. if (!Directory.Exists(FilePath.BackgroundScriptPath))
  521. {
  522. Directory.CreateDirectory(FilePath.BackgroundScriptPath);
  523. }
  524. try
  525. {
  526. string[] files = Directory.GetFiles(FilePath.BackgroundScriptPath);
  527. List<string> ScriptFilesCollection = new List<string>();
  528. foreach (string file in files)
  529. {
  530. string fileName = Path.GetFileName(file);
  531. string fileExtension = Path.GetExtension(file);
  532. if (fileExtension == ".cs")
  533. {
  534. ScriptFilesCollection.Add(file);
  535. }
  536. }
  537. foreach (var item in ScriptFilesCollection)
  538. {
  539. try
  540. {
  541. string script = FileHelper.ReadFile(item);
  542. SendTaskMessage($"{Lang.执行脚本}:{item}", MessageLevel.Info);
  543. Task.Factory.StartNew(() =>
  544. {
  545. ScriptHelper.ExecuteScriptingAsync(script, _regionManager, _eventAggregator, _container, rst =>
  546. {
  547. if (!rst.Item1)
  548. {
  549. foreach (var err in rst.Item2)
  550. {
  551. SendTaskMessage($"{Lang.后台脚本出错}:{err}", MessageLevel.Error);
  552. }
  553. }
  554. });
  555. });
  556. }
  557. catch (Exception ex)
  558. {
  559. LogHelper.WriteLogError($"开启脚本时出错:{item}", ex);
  560. }
  561. }
  562. }
  563. catch (Exception ex)
  564. {
  565. LogHelper.WriteLogError("读取本地脚本文件列表时出错", ex);
  566. }
  567. }
  568. catch (Exception ex)
  569. {
  570. LogHelper.WriteLogError("开启后台脚本时出错!", ex);
  571. }
  572. }
  573. /// <summary>
  574. /// 初始化电批
  575. /// </summary>
  576. /// <returns></returns>
  577. public async Task InitScrewDriver()
  578. {
  579. try
  580. {
  581. var screwDriverConfig = _configService.GetScrewDriverConfig();
  582. ScrewDriver = new XYD_ScrewDriver(screwDriverConfig.IPAdress, screwDriverConfig.Port);
  583. Status.Add(new StatusInfo(ScrewDriver.Id, "电批未连接", new SolidColorBrush(Colors.Red)));
  584. ScrewDriver.ConnectStateChangedEvent += ScrewDriver_ConnectStateChangedEvent;
  585. ScrewDriver.SendTimeout = screwDriverConfig.SendTimeout;
  586. screwDriverConfig.ReceiveTimeout = screwDriverConfig.ReceiveTimeout;
  587. var isConnected = await ScrewDriver.ConnectAsync();
  588. if (isConnected)
  589. {
  590. SendTaskMessage($"电批已连接", MessageLevel.Debug);
  591. }
  592. else
  593. {
  594. SendTaskMessage($"电批连接失败", MessageLevel.Error);
  595. }
  596. }
  597. catch (Exception ex)
  598. {
  599. SendTaskMessage($"电批连接失败:{ex.Message}", MessageLevel.Alarm);
  600. LogHelper.WriteLogError("连接电批时出错!", ex);
  601. }
  602. }
  603. #endregion
  604. #region 机器人指令执行
  605. public Dictionary<Guid, CancellationTokenSource> RobotTCPCollection = new Dictionary<Guid, CancellationTokenSource>();
  606. /// <summary>
  607. /// 机器人TCP接收
  608. /// </summary>
  609. /// <param name="arg1"></param>
  610. /// <param name="arg2"></param>
  611. /// <param name="arg3"></param>
  612. private async void Management_ReceivedEvent(Guid robotid, object arg2, TouchSocket.Sockets.ReceivedDataEventArgs e)
  613. {
  614. try
  615. {
  616. if (!RobotTCPCollection.ContainsKey(robotid))
  617. RobotTCPCollection.Add(robotid, new CancellationTokenSource());
  618. else
  619. {
  620. if (RobotTCPCollection[robotid].IsCancellationRequested)
  621. {
  622. RobotTCPCollection[robotid] = new CancellationTokenSource();
  623. }
  624. }
  625. var robot = await _robotService.GetRobotAsync(robotid);
  626. //从服务器收到信息。但是一般byteBlock和requestInfo会根据适配器呈现不同的值。
  627. var buffer = e.ByteBlock.Span.ToString(robot.GetEncoding());
  628. SendTaskMessage($"{robot.Name}Received:{buffer}", MessageLevel.Info);
  629. var _ = _remoteCommandService.ExecuteAsync(buffer, RobotTCPCollection[robotid].Token, async msg =>
  630. {
  631. try
  632. {
  633. if (robot.ConnectType == TCPConnectType.Client)
  634. {
  635. if (robot.IsConnected)
  636. {
  637. await robot.SendAsync($"{msg}");
  638. }
  639. }
  640. else
  641. {
  642. var cient = (ITcpSessionClient)arg2;
  643. if (cient.Online)
  644. {
  645. await robot.SendAsync(cient, $"{msg}");
  646. }
  647. }
  648. }
  649. catch (Exception ex)
  650. {
  651. LogHelper.WriteLogError("TCP发送数据时出错!", ex);
  652. SendTaskMessage(ex.Message, MessageLevel.Alarm);
  653. }
  654. });
  655. }
  656. catch (Exception ex)
  657. {
  658. LogHelper.WriteLogError("处理机器人接收的命令时出错!", ex);
  659. SendTaskMessage(ex.Message, MessageLevel.Alarm);
  660. }
  661. }
  662. #endregion
  663. #region 机器人&Feeder&相机连接状态事件
  664. /// <summary>
  665. /// 机器人发送事件
  666. /// </summary>
  667. /// <param name="arg1"></param>
  668. /// <param name="arg2"></param>
  669. /// <param name="arg3"></param>
  670. /// <exception cref="NotImplementedException"></exception>
  671. private void Management_SendEvent(Guid robotid, object arg2, string arg3)
  672. {
  673. var r = _robotService.GetRobot(robotid);
  674. SendTaskMessage($"{r.Name}Send:{arg3}", MessageLevel.Info);
  675. }
  676. /// <summary>
  677. /// 机器人断开连接时
  678. /// </summary>
  679. /// <param name="arg1"></param>
  680. /// <param name="arg2"></param>
  681. /// <param name="arg3"></param>
  682. /// <exception cref="NotImplementedException"></exception>
  683. private void Management_DisconnectedEvent(Guid robotid, object arg2, ClosedEventArgs arg3)
  684. {
  685. if (RobotTCPCollection.ContainsKey(robotid))
  686. {
  687. RobotTCPCollection[robotid].Cancel();
  688. }
  689. var r = _configService.GetRobot(robotid);
  690. if (r.ConnectType == TCPConnectType.Server)
  691. {
  692. ITcpSessionClient socketClient = arg2 as ITcpSessionClient;
  693. SendTaskMessage($"{r.RobotName}[{socketClient.Id}:{socketClient.IP}]{Lang.断开连接}!", MessageLevel.Error);
  694. var robot = _robotService.GetRobot(robotid);
  695. if (robot.TcpService.Count < 1)
  696. {
  697. var sta = Status.FirstOrDefault(s => s.ID == robotid);
  698. App.Current.Dispatcher.Invoke(new
  699. Action(() =>
  700. {
  701. sta.Message = $"{r.RobotName}{Lang.未连接}";
  702. sta.Background = new SolidColorBrush(Colors.Red);
  703. }));
  704. }
  705. }
  706. else
  707. {
  708. SendTaskMessage($"{r.RobotName}{Lang.断开连接}!", MessageLevel.Error);
  709. var sta = Status.FirstOrDefault(s => s.ID == robotid);
  710. App.Current.Dispatcher.Invoke(new
  711. Action(() =>
  712. {
  713. sta.Message = $"{r.RobotName}{Lang.断开连接}";
  714. sta.Background = new SolidColorBrush(Colors.Red);
  715. }));
  716. }
  717. }
  718. /// <summary>
  719. /// 机器人连接成功时
  720. /// </summary>
  721. /// <param name="arg1"></param>
  722. /// <param name="arg2"></param>
  723. /// <param name="arg3"></param>
  724. /// <exception cref="NotImplementedException"></exception>
  725. private void Management_ConnectedEvent(Guid robotid, object arg2, ConnectedEventArgs arg3)
  726. {
  727. var r = _configService.GetRobot(robotid);
  728. if (r.ConnectType == TCPConnectType.Server)
  729. {
  730. ITcpSessionClient socketClient = arg2 as ITcpSessionClient;
  731. SendTaskMessage($"{r.RobotName}[{socketClient.Id}:{socketClient.IP}]{Lang.已连接}!", MessageLevel.Debug);
  732. var robot = _robotService.GetRobot(robotid);
  733. if (robot.TcpService.Count > 0)
  734. {
  735. var sta = Status.FirstOrDefault(s => s.ID == robotid);
  736. App.Current.Dispatcher.Invoke(new
  737. Action(() =>
  738. {
  739. sta.Message = $"{r.RobotName}{Lang.已连接}";
  740. sta.Background = new SolidColorBrush(Colors.Green);
  741. }));
  742. }
  743. }
  744. else
  745. {
  746. SendTaskMessage($"{r.RobotName}{Lang.已连接}!", MessageLevel.Debug);
  747. var sta = Status.FirstOrDefault(s => s.ID == robotid);
  748. App.Current.Dispatcher.Invoke(new
  749. Action(() =>
  750. {
  751. sta.Message = $"{r.RobotName}{Lang.已连接}";
  752. sta.Background = new SolidColorBrush(Colors.Green);
  753. }));
  754. }
  755. }
  756. /// <summary>
  757. /// Feeder连接状态改变时
  758. /// </summary>
  759. /// <param name="sender"></param>
  760. /// <param name="state"></param>
  761. private void Management_FeederConnectionStatusChanged(object sender, bool state)
  762. {
  763. IFeeder feeder = sender as IFeeder;
  764. var sta = Status.FirstOrDefault(s => s.ID == feeder.Id);
  765. if (state)
  766. {
  767. App.Current.Dispatcher.Invoke(new
  768. Action(() =>
  769. {
  770. sta.Message = $"{feeder.Name}{Lang.已连接}";
  771. sta.Background = new SolidColorBrush(Colors.Green);
  772. }));
  773. SendTaskMessage($"{feeder.Name}{Lang.已连接}!", MessageLevel.Debug);
  774. }
  775. else
  776. {
  777. App.Current.Dispatcher.Invoke(new
  778. Action(() =>
  779. {
  780. sta.Message = $"{feeder.Name}{Lang.未连接}";
  781. sta.Background = new SolidColorBrush(Colors.Red);
  782. }));
  783. SendTaskMessage($"{feeder.Name}{Lang.断开连接}!", MessageLevel.Error);
  784. }
  785. }
  786. /// <summary>
  787. /// 相机连接或者断开时
  788. /// </summary>
  789. /// <param name="id"></param>
  790. /// <param name="state"></param>
  791. private void Management_CameraConnectChangedEvent(Guid id, bool state)
  792. {
  793. var sta = Status.FirstOrDefault(s => s.ID == id);
  794. var camera = _configService.GetCamera(id);
  795. if (sta != null && camera != null)
  796. {
  797. if (state)
  798. {
  799. App.Current.Dispatcher.Invoke(new Action(() =>
  800. {
  801. sta.Message = $"{camera.CameraName}{Lang.已连接}";
  802. sta.Background = new SolidColorBrush(Colors.Green);
  803. }));
  804. }
  805. else
  806. {
  807. App.Current.Dispatcher.Invoke(new Action(() =>
  808. {
  809. sta.Message = $"{camera.CameraName}{Lang.断开连接}";
  810. sta.Background = new SolidColorBrush(Colors.Red);
  811. }));
  812. }
  813. }
  814. }
  815. /// <summary>
  816. /// PLC连接状态改变事件
  817. /// </summary>
  818. /// <param name="plc"></param>
  819. /// <param name="isconnected"></param>
  820. private async void Management_PlcConnectChangedEvent(object sender, bool isconnected)
  821. {
  822. IPlc plc = sender as IPlc;
  823. var sta = Status.FirstOrDefault(s => s.ID == plc.Id);
  824. if (plc.IsConnected)
  825. {
  826. SendTaskMessage($"{plc.Name}{Lang.已连接}!", MessageLevel.Debug);
  827. if (sta == null) return;
  828. App.Current.Dispatcher.Invoke(new
  829. Action(() =>
  830. {
  831. sta.Message = $"{plc.Name}{Lang.已连接}";
  832. sta.Background = new SolidColorBrush(Colors.Green);
  833. }));
  834. try
  835. {
  836. await WriteSystemParameterToPlcAsync(_configService.GetSystemConfiguration());
  837. await WriteParameterToPlcExAsync();
  838. }
  839. catch (Exception ex)
  840. {
  841. LogHelper.WriteLogError("PLC连接成功时,写入系统参数和点位信息时出错!", ex);
  842. }
  843. }
  844. else
  845. {
  846. SendTaskMessage($"{plc.Name}{Lang.断开连接}!", MessageLevel.Error);
  847. if (sta == null) return;
  848. App.Current.Dispatcher.Invoke(new
  849. Action(() =>
  850. {
  851. sta.Message = $"{plc.Name}{Lang.未连接}";
  852. sta.Background = new SolidColorBrush(Colors.Red);
  853. }));
  854. }
  855. }
  856. /// <summary>
  857. /// 电批连接状态改变时
  858. /// </summary>
  859. /// <param name="screwDevice"></param>
  860. /// <param name="state"></param>
  861. private void ScrewDriver_ConnectStateChangedEvent(object screwDevice, bool state)
  862. {
  863. try
  864. {
  865. XYD_ScrewDriver screwDriver = screwDevice as XYD_ScrewDriver;
  866. if (state)
  867. {
  868. SendTaskMessage($"电批{Lang.已连接}!", MessageLevel.Debug);
  869. var sta = Status.FirstOrDefault(s => s.ID == screwDriver.Id);
  870. App.Current.Dispatcher.Invoke(new
  871. Action(() =>
  872. {
  873. sta.Message = $"电批{Lang.已连接}";
  874. sta.Background = new SolidColorBrush(Colors.Green);
  875. }));
  876. }
  877. else
  878. {
  879. SendTaskMessage($"电批{Lang.未连接}!", MessageLevel.Error);
  880. var sta = Status.FirstOrDefault(s => s.ID == screwDriver.Id);
  881. App.Current.Dispatcher.Invoke(new
  882. Action(() =>
  883. {
  884. sta.Message = $"电批{Lang.未连接}";
  885. sta.Background = new SolidColorBrush(Colors.Red);
  886. }));
  887. }
  888. }
  889. catch (Exception ex)
  890. {
  891. LogHelper.WriteLogError("电批连接状态改变时出错了", ex);
  892. }
  893. }
  894. #endregion
  895. #region 后台服务器通讯事件
  896. private void BgCommunicate_SendEvent(ITcpSessionClient client, string msg)
  897. {
  898. SendTaskMessage($"{Lang.服务器}[{client.ServicePort}] Send To {Lang.客户端}[{client.Id}:{client.IP}]:{msg}", MessageLevel.Info);
  899. }
  900. public Dictionary<string, CancellationTokenSource> BgCommunicateollection = new Dictionary<string, CancellationTokenSource>();
  901. private async void BgCommunicate_ReceivedEvent(ITcpSessionClient client, string msg)
  902. {
  903. try
  904. {
  905. if (!BgCommunicateollection.ContainsKey(client.Id))
  906. {
  907. BgCommunicateollection.Add(client.Id, new CancellationTokenSource());
  908. }
  909. SendTaskMessage($"{Lang.服务器}[{client.ServicePort}]ReceiveFor{Lang.客户端}[{client.Id}:{client.IP}]:{msg}", MessageLevel.Info);
  910. var _ = _remoteCommandService.ExecuteAsync(msg, BgCommunicateollection[client.Id].Token, async response =>
  911. {
  912. try
  913. {
  914. if (!client.Online)
  915. {
  916. return;
  917. }
  918. await BgCommunicate.SendAsync(client, $"{response}");
  919. }
  920. catch (Exception ex)
  921. {
  922. LogHelper.WriteLogError("后台TCP发送数据时出错!", ex);
  923. SendTaskMessage(ex.Message, MessageLevel.Alarm);
  924. }
  925. });
  926. }
  927. catch (Exception ex)
  928. {
  929. LogHelper.WriteLogError("处理后台服务器接收的命令时出错!", ex);
  930. SendTaskMessage(ex.Message, MessageLevel.Alarm);
  931. }
  932. }
  933. private void BgCommunicate_DisconnectedEvent(ITcpSessionClient client, ClosedEventArgs e)
  934. {
  935. SendTaskMessage($"{Lang.服务器}[{client.ServicePort}]{Lang.客户端}[{client.Id}:{client.IP}]{Lang.断开连接}!", MessageLevel.Alarm);
  936. if (BgCommunicateollection.ContainsKey(client.Id))
  937. {
  938. BgCommunicateollection[client.Id].Cancel();
  939. }
  940. }
  941. private void BgCommunicate_ConnectedEvent(ITcpSessionClient client, ConnectedEventArgs e)
  942. {
  943. SendTaskMessage($"{Lang.服务器}[{client.ServicePort}]{Lang.客户端}[{client.Id}:{client.IP}]{Lang.连接成功}!", MessageLevel.Debug);
  944. }
  945. #endregion
  946. #region PLC
  947. /// <summary>
  948. /// 将系统参数写入PLC
  949. /// </summary>
  950. /// <param name="systemConfiguration"></param>
  951. /// <returns></returns>
  952. public async Task WriteSystemParameterToPlcAsync(SystemConfiguration systemConfiguration)
  953. {
  954. var addressConfig = _configService.GetPlcAddresses();
  955. var plc = _plcService.GetPlcByNumber(addressConfig.PlcNo) as OpcUaClientPLC;
  956. if (plc == null || !plc.IsConnected) return;
  957. Dictionary<string, object> nodeValues = new Dictionary<string, object>();
  958. nodeValues.Add(addressConfig.Out_WorkMode.Address, (Int16)systemConfiguration.WorkMode);
  959. nodeValues.Add(addressConfig.Out_PickPointNum.Address, (Int16)systemConfiguration.PickPointNum);
  960. nodeValues.Add(addressConfig.Out_DowmCameraNum.Address, (Int16)systemConfiguration.DowmCameraNum);
  961. nodeValues.Add(addressConfig.Out_UpCameraPickNum.Address, (Int16)systemConfiguration.UpCameraPickNum);
  962. nodeValues.Add(addressConfig.Out_UpCameraPutNum.Address, (Int16)systemConfiguration.UpCameraPutNum);
  963. nodeValues.Add(addressConfig.Out_UPCameracheckNum.Address, (Int16)systemConfiguration.UPCameracheckNum);
  964. nodeValues.Add(addressConfig.Out_FixedCameraPickNum.Address, (Int16)systemConfiguration.FixedCameraPickNum);
  965. nodeValues.Add(addressConfig.Out_FixedCameraPutNum.Address, (Int16)systemConfiguration.FixedCameraPutNum);
  966. nodeValues.Add(addressConfig.Out_FixedCameracheckNum.Address, (Int16)systemConfiguration.FixedCameracheckNum);
  967. nodeValues.Add(addressConfig.Out_ScrewTeachNum.Address, (Int16)systemConfiguration.ScrewTeachNum);
  968. var res = await plc.WriteNodesAsync(nodeValues);
  969. }
  970. /// <summary>
  971. /// 注册需要监控的PLC地址
  972. /// </summary>
  973. public void RegisterPlcMonitorAddress()
  974. {
  975. var addressConfig = _configService.GetPlcAddresses();
  976. var plc = _plcService.GetPlcByNumber(addressConfig.PlcNo) as OpcUaClientPLC;
  977. if (plc == null || !plc.IsConnected) return;
  978. //PLC地址数组
  979. List<string> monitorNodeIds = new List<string>()
  980. {
  981. addressConfig.In_DowmCameraExe.Address,
  982. addressConfig.In_UpCameraPickExe.Address,
  983. addressConfig.In_UpCameraPutExe.Address,
  984. addressConfig.In_UPCameracheckExe.Address,
  985. addressConfig.In_FixedCameraPickExe.Address,
  986. addressConfig.In_FixedCameraPutExe.Address,
  987. addressConfig.In_FixedCameracheckExe.Address,
  988. addressConfig.In_RequestPosition.Address,
  989. addressConfig.In_ScrewTeachExe.Address,
  990. addressConfig.In_QrCodePhotoRequest.Address,
  991. addressConfig.In_PickINC.Address,
  992. addressConfig.In_Throw.Address,
  993. // addressConfig.RunMode.Address,
  994. addressConfig.ManualMode.Address,
  995. addressConfig.AutoRunning.Address,
  996. addressConfig.ClearMode.Address,
  997. // addressConfig.Fault.Address,
  998. };
  999. for (int i = 0; i < 10; i++)
  1000. {
  1001. monitorNodeIds.Add(string.Format(addressConfig.In_MesCheck.Address, i));
  1002. }
  1003. for (int i = 0; i < 7; i++)
  1004. {
  1005. monitorNodeIds.Add(string.Format(addressConfig.In_WorkStart.Address, i));
  1006. monitorNodeIds.Add(string.Format(addressConfig.In_WorkDone.Address, i));
  1007. }
  1008. //订阅PLC地址变化
  1009. //plc.SubscribeNodes("AutoSensor", monitorNodeIds, PlcCommandTrigger, OpcUaSubscriptionMode.CustomPolling);
  1010. // 1. 初始订阅(使用原生模式)
  1011. plc.SubscribeNodes("AutoSensor", monitorNodeIds, PlcCommandTrigger, OpcUaSubscriptionMode.CustomPolling);
  1012. // 2. 监听重连事件,重连成功后自动重新订阅
  1013. plc.ConnectChangedEvent -= Plc_ConnectChanged; // 防止重复注册
  1014. plc.ConnectChangedEvent += Plc_ConnectChanged;
  1015. // 定义重连处理方法(写在同一个类里即可)
  1016. void Plc_ConnectChanged(object sender, bool isConnected)
  1017. {
  1018. if (isConnected)
  1019. {
  1020. // 重连成功后,重新订阅节点
  1021. plc.SubscribeNodes("AutoSensor", monitorNodeIds, PlcCommandTrigger, OpcUaSubscriptionMode.CustomPolling);
  1022. }
  1023. }
  1024. }
  1025. /// <summary>
  1026. /// 将产品的参数统一写入PLC
  1027. /// </summary>
  1028. /// <returns></returns>
  1029. public async Task WriteParameterToPlcExAsync()
  1030. {
  1031. await WritePositionToPlcAsync();// 写入点位信息给PLC
  1032. await WriteParameterToPlcAsync();// 写入组装上下限给PLC
  1033. await WriteProductLoadModeToPLC();//把当前产品型号,写入PLC
  1034. }
  1035. /// <summary>
  1036. /// 写入点位信息给PLC
  1037. /// </summary>
  1038. /// <returns></returns>
  1039. public async Task WritePositionToPlcAsync()
  1040. {
  1041. var addressConfig = _configService.GetPlcAddresses();
  1042. var plc = _plcService.GetPlcByNumber(addressConfig.PlcNo) as OpcUaClientPLC;
  1043. if (plc == null || !plc.IsConnected) return;
  1044. var current = _productService.GetCurrentProduct();
  1045. if (current == null) return;
  1046. if (current.PickCameraPoints != null && current.PickCameraPoints.Count > 0)
  1047. {
  1048. foreach (var item in current.PickCameraPoints)
  1049. {
  1050. await item.WriteToPlcAddress(addressConfig.Out_PickCamera, plc);
  1051. }
  1052. }
  1053. if (current.PickPoints != null && current.PickPoints.Count > 0)
  1054. {
  1055. foreach (var item in current.PickPoints)
  1056. {
  1057. await item.WriteToPlcAddress(addressConfig.Out_Pick, plc);
  1058. }
  1059. }
  1060. if (current.SecPosCameraPoints != null && current.SecPosCameraPoints.Count > 0)
  1061. {
  1062. foreach (var item in current.SecPosCameraPoints)
  1063. {
  1064. await item.WriteToPlcAddress(addressConfig.Out_SecPosCamera, plc);
  1065. }
  1066. }
  1067. if (current.ScrewCameraPoints != null && current.ScrewCameraPoints.Count > 0)
  1068. {
  1069. foreach (var item in current.ScrewCameraPoints)
  1070. {
  1071. await item.WriteToPlcAddress(addressConfig.Out_ScrewCamera, plc);
  1072. }
  1073. }
  1074. if (current.ScrewPoints != null && current.ScrewPoints.Count > 0)
  1075. {
  1076. foreach (var item in current.ScrewPoints)
  1077. {
  1078. await item.WriteToPlcAddressEx(addressConfig.Out_Screw, plc);
  1079. }
  1080. await plc.WriteNodeAsync(addressConfig.Out_WorkNum.Address, (Int16)current.ScrewPoints.Count);
  1081. }
  1082. if (current.CheckCameraPoints != null && current.CheckCameraPoints.Count > 0)
  1083. {
  1084. foreach (var item in current.CheckCameraPoints)
  1085. {
  1086. await item.WriteToPlcAddress(addressConfig.Out_CheckCamera, plc);
  1087. }
  1088. }
  1089. if (current.RemovePoints != null && current.RemovePoints.Count > 0)
  1090. {
  1091. foreach (var item in current.RemovePoints)
  1092. {
  1093. await item.WriteToPlcAddress(addressConfig.Out_RemovePos, plc);
  1094. }
  1095. await plc.WriteNodeAsync(addressConfig.Out_RemovePos_Num.Address, (Int16)current.RemovePoints.Count);
  1096. }
  1097. if (current.PressurePlacePoints != null && current.PressurePlacePoints.Count > 0)
  1098. {
  1099. foreach (var item in current.PressurePlacePoints)
  1100. {
  1101. await item.WriteToPlcAddress(addressConfig.Out_PressurePlace, plc);
  1102. }
  1103. }
  1104. if (current.IndependentCamerPoints != null && current.IndependentCamerPoints.Count > 0)
  1105. {
  1106. foreach (var item in current.IndependentCamerPoints)
  1107. {
  1108. await item.WriteToPlcAddress(addressConfig.Out_IndependentCamer, plc);
  1109. }
  1110. }
  1111. if (current.QrCodePoints != null && current.QrCodePoints.Count > 0)
  1112. {
  1113. foreach (var item in current.QrCodePoints)
  1114. {
  1115. await item.WriteToPlcAddress(addressConfig.Out_QrCodeCamera, plc);
  1116. }
  1117. }
  1118. }
  1119. /// <summary>
  1120. /// 写入组装上下限给PLC
  1121. /// </summary>
  1122. /// <returns></returns>
  1123. public async Task WriteParameterToPlcAsync()
  1124. {
  1125. var addressConfig = _configService.GetPlcAddresses();
  1126. var plc = _plcService.GetPlcByNumber(addressConfig.PlcNo) as OpcUaClientPLC;
  1127. if (plc == null || !plc.IsConnected) return;
  1128. // 获取当前产品
  1129. var currentProduct = _productService.GetCurrentProduct();
  1130. if (currentProduct == null) return;
  1131. try
  1132. {
  1133. if (currentProduct.GlobalParamEx == null) currentProduct.GlobalParamEx = new Models.Product.ProductGlobalParamEx();
  1134. if (currentProduct.GlobalParamEx.AssemblyPressure == null) currentProduct.GlobalParamEx.AssemblyPressure = new PressureModel();
  1135. if (currentProduct.GlobalParamEx.DownPressure == null) currentProduct.GlobalParamEx.DownPressure = new PressureModel();
  1136. await plc.WriteNodeAsync(addressConfig.PUT_Down_Time.Address, currentProduct.GlobalParamEx.AssemblyPressure.PressureTime);
  1137. await plc.WriteNodeAsync(addressConfig.PUT_WetOut_Num.Address, currentProduct.GlobalParamEx.AssemblyPressure.PressureNum);
  1138. await plc.WriteNodeAsync(addressConfig.PUT_WetOut_Num_max.Address, currentProduct.GlobalParamEx.AssemblyPressure.PressureNumMax);
  1139. await plc.WriteNodeAsync(addressConfig.Down_Time.Address, currentProduct.GlobalParamEx.DownPressure.PressureTime);
  1140. await plc.WriteNodeAsync(addressConfig.Down_WetOut_Num.Address, currentProduct.GlobalParamEx.DownPressure.PressureNum);
  1141. await plc.WriteNodeAsync(addressConfig.Down_WetOut_Num_max.Address, currentProduct.GlobalParamEx.DownPressure.PressureNumMax);
  1142. }
  1143. catch (Exception)
  1144. {
  1145. }
  1146. }
  1147. /// <summary>
  1148. /// 将当前产品类型写入PLC
  1149. /// </summary>
  1150. /// <param name="mode"></param>
  1151. public async Task WriteProductLoadModeToPLC(int modeIndex)
  1152. {
  1153. var addressConfig = _configService.GetPlcAddresses();
  1154. var plc = _plcService.GetPlcByNumber(addressConfig.PlcNo) as OpcUaClientPLC;
  1155. if (plc == null || !plc.IsConnected) return;
  1156. await plc.WriteNodeAsync(addressConfig.Out_ProductLoadMode.Address, (Int16)modeIndex);
  1157. }
  1158. /// <summary>
  1159. /// 将当前产品类型写入PLC
  1160. /// </summary>
  1161. /// <param name="mode"></param>
  1162. public async Task WriteProductLoadModeToPLC()
  1163. {
  1164. var addressConfig = _configService.GetPlcAddresses();
  1165. var plc = _plcService.GetPlcByNumber(addressConfig.PlcNo) as OpcUaClientPLC;
  1166. if (plc == null || !plc.IsConnected) return;
  1167. var current = _productService.GetCurrentProduct();
  1168. if (current == null) return;
  1169. var qMes = await _systemDatabaseService.QueryProductFromMesAsync(current.Name);
  1170. if (qMes == null) return;
  1171. await plc.WriteNodeAsync(addressConfig.Out_ProductLoadMode.Address, (Int16)qMes.ProductLoadMode);
  1172. }
  1173. /// <summary>
  1174. /// 固定向上相机拍照结果
  1175. /// </summary>
  1176. OpenCvSharp.Point3f[] DowmCameraResults = new OpenCvSharp.Point3f[10];
  1177. /// <summary>
  1178. /// 移动向下相机拍照结果
  1179. /// </summary>
  1180. OpenCvSharp.Point3f[] UpCameraPutResults = new OpenCvSharp.Point3f[10];
  1181. /// <summary>
  1182. /// 取料移动相机拍照结果
  1183. /// </summary>
  1184. OpenCvSharp.Point3f UpCameraPickResult = new OpenCvSharp.Point3f();
  1185. /// <summary>
  1186. /// 最终组装位置
  1187. /// </summary>
  1188. OpenCvSharp.Point3f PlaceActualCoord = new OpenCvSharp.Point3f();
  1189. /// <summary>
  1190. /// 零件SN
  1191. /// </summary>
  1192. string PartSN = "";
  1193. /// <summary>
  1194. /// 产品SN
  1195. /// </summary>
  1196. string[] ProductMainSN = new string[10];//产品主sn
  1197. /// <summary>
  1198. /// 工单信息
  1199. /// </summary>
  1200. string[] wo = new string[10];
  1201. string[] model_num = new string[10];//产品model_num
  1202. string[] color = new string[10];//产品color
  1203. string[] ppid = new string[10];//产品ppid
  1204. string[] revision = new string[10];//产品revision
  1205. /// <summary>
  1206. /// 复检结果
  1207. /// </summary>
  1208. Dictionary<string, string> gapDict = new Dictionary<string, string>();
  1209. string[] dis = new string[10];
  1210. /// <summary>
  1211. /// 定位结果
  1212. /// </summary>
  1213. Dictionary<string, string> DISDict = new Dictionary<string, string>();
  1214. List<string> fileresult { get; set; } = new List<string>();
  1215. /// <summary>
  1216. /// 二次定位相机的工具坐标
  1217. /// </summary>
  1218. ToolCoord DownTool = new ToolCoord();
  1219. double _FixedUpCameraAngle = 0;
  1220. double _MoveCameraAngle = 0;
  1221. bool _LastDowmCameraExe = false;
  1222. bool _LastScrewTeachExe = false;
  1223. bool _LastUpCameraPickExe = false;
  1224. bool _LastUpCameraPutExe = false;
  1225. bool _LastUPCameracheckExe = false;
  1226. bool _LastFixedCameraPickExe = false;
  1227. bool _LastFixedCameraPutExe = false;
  1228. bool _LastFixedCameracheckExe = false;
  1229. public bool _LastAutoRunning = false;
  1230. bool _LastManualMode = false;
  1231. bool _LastClearMode = false;
  1232. bool _LastThrowExe = false;
  1233. bool _LastRunModel = false;
  1234. bool _LastFault = false;
  1235. Int16[] _LastWorkStart = new Int16[7];
  1236. Int16 _LastQrCodePhotoRequest = 0;
  1237. Int16[] _LastWorkDone = new Int16[7];
  1238. Int16 _LastRequestPosition = 0;
  1239. Int16 _LastPickINC = 0;
  1240. Int16[] _LastMesCheck = new Int16[10];
  1241. DateTime[] _LastMesCheckTime = new DateTime[10];
  1242. double[] currentPressure = new double[10];//当前压力
  1243. /// <summary>
  1244. /// PLC命令触发时
  1245. /// </summary>
  1246. /// <param name="tuple"></param>
  1247. private async void PlcCommandTrigger((string key, string nodeId, object value) tuple)
  1248. {
  1249. try
  1250. {
  1251. if (tuple.key != "AutoSensor")
  1252. {
  1253. return;
  1254. }
  1255. var addressConfig = _configService.GetPlcAddresses();
  1256. var plc = _plcService.GetPlcByNumber(addressConfig.PlcNo) as OpcUaClientPLC;
  1257. if (plc == null || !plc.IsConnected) return;
  1258. // 获取当前产品
  1259. var currentProduct = _productService.GetCurrentProduct();
  1260. if (currentProduct == null)
  1261. {
  1262. return;
  1263. }
  1264. ////获取产品编码
  1265. //_productService.CurrentProductCode = plc.ReadNode<string>(string.Format(addressConfig.In_Code.Address, 0));
  1266. ////如果产品编码为空,则以当前的时间戳作为编码
  1267. //if (string.IsNullOrEmpty(_productService.CurrentProductCode))
  1268. //{
  1269. // _productService.CurrentProductCode = DateTime.Now.ToString("yyyyMMddHHmmssfff");
  1270. //}
  1271. bool isDryMode = plc.ReadNode<bool>(addressConfig.In_DryMode.Address);
  1272. // 触发下相机拍照--下相机拍屏幕,预定位
  1273. if (addressConfig.In_DowmCameraExe.Address.Contains(tuple.nodeId))
  1274. {
  1275. if (tuple.value is bool state)
  1276. {
  1277. if (_LastDowmCameraExe != state)
  1278. {
  1279. _LastDowmCameraExe = state;
  1280. }
  1281. else
  1282. {
  1283. return;
  1284. }
  1285. if (state)
  1286. {
  1287. SendTaskMessage($"触发下相机拍照...", MessageLevel.Debug);
  1288. //相机的编号
  1289. Int16 cameraIndex = plc.ReadNode<Int16>(addressConfig.In_DowmCameraNum.Address);
  1290. SendTaskMessage($"编号:{cameraIndex}", MessageLevel.Info);
  1291. // 获取机器人当前坐标
  1292. var robot = _robotService.GetAllRobots().FirstOrDefault();
  1293. if (robot == null)
  1294. {
  1295. SendTaskMessage($"未找到机器人,无法执行上相机锁附/组装拍照视觉任务", MessageLevel.Alarm);
  1296. await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)2);
  1297. return;
  1298. }
  1299. var currentPosition = robot.GetRobotPos();
  1300. var current = _productService.GetCurrentProduct();
  1301. //如果下相机拍照的次数是1,则判断用时拍照的相机是几个
  1302. int downCameraNum = _configService.GetSystemConfiguration().DowmCameraNum;
  1303. if (downCameraNum == 1)
  1304. {
  1305. //如果下相机2的流程id为空,则表示只有一个下相机
  1306. if (currentProduct.FixedUpCamera2ProcedureId == Guid.Empty)
  1307. {
  1308. //获取固定向上相机1的流程
  1309. var process = _productService.GetCurrentProductProcedureModelById(currentProduct.FixedUpCamera1ProcedureId);
  1310. if (process == null)
  1311. {
  1312. SendTaskMessage($"固定向上相机1视觉流程,流程ID:{currentProduct.FixedUpCamera1ProcedureId}", MessageLevel.Alarm);
  1313. await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)2);
  1314. return;
  1315. }
  1316. // 执行视觉任务
  1317. var _cts = new CancellationTokenSource();
  1318. var visionResult = await _remoteCommandService.ExecutePhotoGetSinglePoint(process, null, new double[] { currentPosition.X, currentPosition.Y, currentPosition.U }, _cts.Token, cameraIndex, null);
  1319. if (isDryMode)
  1320. {
  1321. await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)1);
  1322. return;
  1323. }
  1324. if (visionResult.IsSucceed)
  1325. {
  1326. //float toolX = (float)(visionResult.X - currentPosition.X);
  1327. //float toolY = (float)(visionResult.Y - currentPosition.Y);
  1328. DowmCameraResults[0] = new OpenCvSharp.Point3f((float)visionResult.X, (float)visionResult.Y, (float)visionResult.U);
  1329. //DownTool = new PointF(toolX, toolY);
  1330. //以LCD的的上边中心创建工具坐标
  1331. DownTool.ComputeTool(currentPosition.X, currentPosition.Y, currentPosition.U, DowmCameraResults[0].X, DowmCameraResults[0].Y);
  1332. await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)1);
  1333. return;
  1334. }
  1335. else
  1336. {
  1337. if (isDryMode)
  1338. {
  1339. await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)1);
  1340. return;
  1341. }
  1342. await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)2);
  1343. return;
  1344. }
  1345. }
  1346. else
  1347. {
  1348. //获取固定向上相机1的流程
  1349. var process1 = _productService.GetCurrentProductProcedureModelById(currentProduct.FixedUpCamera1ProcedureId);
  1350. if (process1 == null)
  1351. {
  1352. SendTaskMessage($"固定向上相机1视觉流程,流程ID:{currentProduct.FixedUpCamera1ProcedureId}", MessageLevel.Alarm);
  1353. await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)2);
  1354. return;
  1355. }
  1356. //获取固定向上相机2的流程
  1357. var process2 = _productService.GetCurrentProductProcedureModelById(currentProduct.FixedUpCamera2ProcedureId);
  1358. if (process2 == null)
  1359. {
  1360. SendTaskMessage($"固定向上相机1视觉流程,流程ID:{currentProduct.FixedUpCamera2ProcedureId}", MessageLevel.Alarm);
  1361. await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)2);
  1362. return;
  1363. }
  1364. // 执行视觉任务
  1365. var _cts = new CancellationTokenSource();
  1366. var task1 = _remoteCommandService.ExecutePhotoGetSinglePoint(process1, null, null, _cts.Token, 1, null);
  1367. var task2 = _remoteCommandService.ExecutePhotoGetSinglePoint(process2, null, null, _cts.Token, 2, null);
  1368. var visionResults = await Task.WhenAll(task1, task2);
  1369. if (isDryMode)
  1370. {
  1371. await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)1);
  1372. return;
  1373. }
  1374. if (visionResults[0].IsSucceed && visionResults[1].IsSucceed)
  1375. {
  1376. float toolX = (float)(visionResults[0].X + visionResults[1].X) / 2;
  1377. float toolY = (float)(visionResults[0].Y + visionResults[1].Y) / 2;
  1378. float deltaX = (float)(visionResults[1].X - visionResults[0].X);
  1379. float deltaY = (float)(visionResults[1].Y - visionResults[0].Y);
  1380. float distance = (float)Math.Sqrt(deltaX * deltaX + deltaY * deltaY);
  1381. SendTaskMessage($"屏幕长度: {distance:F4}", MessageLevel.Info);
  1382. dis[5] = distance.ToString();
  1383. _FixedUpCameraAngle = Common.CalculateAngleBetweenPointsDegrees(new PointF((float)visionResults[0].X, (float)visionResults[0].Y), new PointF((float)visionResults[1].X, (float)visionResults[1].Y));
  1384. SendTaskMessage($"屏幕中心点位:{toolX:F3},{toolY:F4},{_FixedUpCameraAngle:F4}", MessageLevel.Info);
  1385. SendTaskMessage($"当前机器人点位:{currentPosition.X:F3},{currentPosition.Y:F4},{currentPosition.U:F4}", MessageLevel.Info);
  1386. DowmCameraResults[0] = new OpenCvSharp.Point3f(toolX, toolY, (float)_FixedUpCameraAngle);
  1387. //DownTool = new PointF(toolX, toolY);
  1388. //以LCD的的上边中心创建工具坐标
  1389. DownTool.ComputeTool(currentPosition.X, currentPosition.Y, currentPosition.U, DowmCameraResults[0].X, DowmCameraResults[0].Y);
  1390. SendTaskMessage($"屏幕Tool:{DownTool.X:F3},{DownTool.Y:F4}", MessageLevel.Info);
  1391. if (current.Reference_coordinate.Count > 0)
  1392. {
  1393. float offsetX = toolX - current.Reference_coordinate[0].X_Position;
  1394. float offsetY = toolY - current.Reference_coordinate[0].Y_Position;
  1395. float offsetAngle = (float)_FixedUpCameraAngle - current.Reference_coordinate[0].U_Position;
  1396. SendTaskMessage($"屏幕偏移量:X:{offsetX},Y:{offsetY},U:{offsetAngle}", MessageLevel.Debug);
  1397. foreach (var item in current.RemovePoints)
  1398. {
  1399. item.X_Position += offsetX;
  1400. item.Y_Position += offsetY;
  1401. item.U_Position += offsetAngle;
  1402. await item.WriteToPlcAddress(addressConfig.Out_RemovePos, plc);
  1403. }
  1404. }
  1405. await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)1);
  1406. return;
  1407. }
  1408. else
  1409. {
  1410. await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)2);
  1411. return;
  1412. }
  1413. }
  1414. }
  1415. else if (downCameraNum == 2)
  1416. {
  1417. if (isDryMode)
  1418. {
  1419. await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)1);
  1420. return;
  1421. }
  1422. //如果下相机2的流程id为空,则表示只有一个下相机
  1423. if (currentProduct.FixedUpCamera2ProcedureId == Guid.Empty)
  1424. {
  1425. //获取固定向上相机1的流程
  1426. //var process = _productService.GetCurrentProductProcedureModelById(currentProduct.FixedUpCamera1ProcedureId);
  1427. //if (process == null)
  1428. //{
  1429. // SendTaskMessage($"固定向上相机1视觉流程,流程ID:{currentProduct.FixedUpCamera1ProcedureId}", MessageLevel.Alarm);
  1430. // await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)2);
  1431. // return;
  1432. //}
  1433. //// 执行视觉任务
  1434. //var _cts = new CancellationTokenSource();
  1435. //var visionResult = await _remoteCommandService.ExecutePhotoGetSinglePoint(process, null, new double[] { currentPosition.X, currentPosition.Y, currentPosition.U }, _cts.Token);
  1436. //if (visionResult.IsSucceed)
  1437. //{
  1438. // float toolX = (float)(visionResult.X - currentPosition.X);
  1439. // float toolY = (float)(visionResult.Y - currentPosition.Y);
  1440. // DowmCameraResults[cameraIndex] = new PointF(toolX, toolX);
  1441. // if (cameraIndex == downCameraNum)
  1442. // {
  1443. // //求取多个DowmCameraResults的中心点
  1444. // DownTool = new PointF(toolX, toolX);
  1445. // }
  1446. // await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)1);
  1447. // return;
  1448. //}
  1449. //else
  1450. //{
  1451. // await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)2);
  1452. // return;
  1453. //}
  1454. //暂时没有应用场景,不支持
  1455. await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)2);
  1456. return;
  1457. }
  1458. else
  1459. {
  1460. //获取固定向上相机1的流程
  1461. var process1 = _productService.GetCurrentProductProcedureModelById(currentProduct.FixedUpCamera1ProcedureId);
  1462. if (process1 == null)
  1463. {
  1464. SendTaskMessage($"固定向上相机1视觉流程,流程ID:{currentProduct.FixedUpCamera1ProcedureId}", MessageLevel.Alarm);
  1465. await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)2);
  1466. return;
  1467. }
  1468. //获取固定向上相机2的流程
  1469. var process2 = _productService.GetCurrentProductProcedureModelById(currentProduct.FixedUpCamera2ProcedureId);
  1470. if (process2 == null)
  1471. {
  1472. SendTaskMessage($"固定向上相机1视觉流程,流程ID:{currentProduct.FixedUpCamera2ProcedureId}", MessageLevel.Alarm);
  1473. await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)2);
  1474. return;
  1475. }
  1476. // 执行视觉任务
  1477. var _cts = new CancellationTokenSource();
  1478. var task1 = _remoteCommandService.ExecutePhotoGetSinglePoint(process1, null, new double[] { currentPosition.X, currentPosition.Y, currentPosition.U }, _cts.Token, 1, null);
  1479. var task2 = _remoteCommandService.ExecutePhotoGetSinglePoint(process2, null, new double[] { currentPosition.X, currentPosition.Y, currentPosition.U }, _cts.Token, 2, null);
  1480. var visionResults = await Task.WhenAll(task1, task2);
  1481. if (isDryMode)
  1482. {
  1483. await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)1);
  1484. return;
  1485. }
  1486. if (visionResults[0].IsSucceed && visionResults[1].IsSucceed)
  1487. {
  1488. float toolX = (float)(visionResults[0].X + visionResults[1].X) / 2;
  1489. float toolY = (float)(visionResults[0].Y + visionResults[1].Y) / 2;
  1490. _FixedUpCameraAngle = Common.CalculateAngleBetweenPointsDegrees(new PointF((float)visionResults[0].X, (float)visionResults[0].Y), new PointF((float)visionResults[1].X, (float)visionResults[1].Y));
  1491. DowmCameraResults[cameraIndex] = new OpenCvSharp.Point3f(toolX, toolY, (float)_FixedUpCameraAngle);
  1492. //DownTool = new PointF(toolX, toolY);
  1493. await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)1);
  1494. return;
  1495. }
  1496. else
  1497. {
  1498. await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)2);
  1499. return;
  1500. }
  1501. }
  1502. }
  1503. else
  1504. {
  1505. //暂时没有应用场景,不支持
  1506. await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)2);
  1507. return;
  1508. }
  1509. }
  1510. else
  1511. {
  1512. SendTaskMessage($"触发下相机拍照信号已关闭", MessageLevel.Alarm);
  1513. await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)0);
  1514. }
  1515. }
  1516. }
  1517. // 触发下相机拍照披头
  1518. if (addressConfig.In_ScrewTeachExe.Address.Contains(tuple.nodeId))
  1519. {
  1520. if (tuple.value is bool state)
  1521. {
  1522. if (_LastScrewTeachExe != state)
  1523. {
  1524. _LastScrewTeachExe = state;
  1525. }
  1526. else
  1527. {
  1528. return;
  1529. }
  1530. if (state)
  1531. {
  1532. SendTaskMessage($"触发下相机拍照...", MessageLevel.Debug);
  1533. //相机的编号
  1534. Int16 cameraIndex = plc.ReadNode<Int16>(addressConfig.In_DowmCameraNum.Address);
  1535. SendTaskMessage($"编号:{cameraIndex}", MessageLevel.Info);
  1536. }
  1537. else
  1538. {
  1539. SendTaskMessage($"触发下相机拍照信号已关闭", MessageLevel.Alarm);
  1540. await plc.WriteNodeAsync(addressConfig.Out_ScrewTeachOk.Address, (Int16)0);
  1541. }
  1542. }
  1543. }
  1544. // 触发上相机取料拍照
  1545. else if (addressConfig.In_UpCameraPickExe.Address.Contains(tuple.nodeId))
  1546. {
  1547. if (tuple.value is bool state)
  1548. {
  1549. if (_LastUpCameraPickExe != state)
  1550. {
  1551. _LastUpCameraPickExe = state;
  1552. }
  1553. else
  1554. {
  1555. return;
  1556. }
  1557. if (state)
  1558. {
  1559. SendTaskMessage($"触发上相机取料拍照...", MessageLevel.Debug);
  1560. //相机的编号
  1561. Int16 cameraIndex = plc.ReadNode<Int16>(addressConfig.In_UpCameraPickNum.Address);
  1562. SendTaskMessage($"编号:{cameraIndex}", MessageLevel.Info);
  1563. // 获取机器人当前坐标
  1564. var robot = _robotService.GetAllRobots().FirstOrDefault();
  1565. if (robot == null)
  1566. {
  1567. SendTaskMessage($"未找到机器人,无法执行上相机锁附/组装拍照视觉任务", MessageLevel.Alarm);
  1568. await plc.WriteNodeAsync(addressConfig.Out_UpCameraPickStatus.Address, (Int16)2);
  1569. return;
  1570. }
  1571. var currentPosition = robot.GetRobotPos();
  1572. //如果上相机2的流程id为空,则表示只有一个下相机
  1573. if (currentProduct.MoveDownCamer2PickPhotoProcedureId == Guid.Empty)
  1574. {
  1575. //获取移动向下相机1的流程
  1576. var process = _productService.GetCurrentProductProcedureModelById(currentProduct.MoveDownCamer1PickPhotoProcedureId);
  1577. if (process == null)
  1578. {
  1579. SendTaskMessage($"移动向下相机1视觉流程,流程ID:{currentProduct.MoveDownCamer1PickPhotoProcedureId}", MessageLevel.Alarm);
  1580. await plc.WriteNodeAsync(addressConfig.Out_UpCameraPickStatus.Address, (Int16)2);
  1581. return;
  1582. }
  1583. // 执行视觉任务
  1584. var _cts = new CancellationTokenSource();
  1585. var visionResult = await _remoteCommandService.ExecutePhotoGetSinglePoint(process, null, new double[] { currentPosition.X, currentPosition.Y, currentPosition.U }, _cts.Token, cameraIndex, null);
  1586. if (isDryMode)
  1587. {
  1588. await plc.WriteNodeAsync(addressConfig.Out_UpCameraPickStatus.Address, (Int16)1);
  1589. return;
  1590. }
  1591. if (visionResult.IsSucceed)
  1592. {
  1593. currentProduct.PickPoints[0].X_Position = (float)visionResult.X;
  1594. currentProduct.PickPoints[0].Y_Position = (float)visionResult.Y;
  1595. //currentProduct.PickPoints[0].U_Position += (float)visionResult.U;
  1596. await currentProduct.ScrewPoints[0].WriteToPlcAddress(addressConfig.Out_Pick, plc);
  1597. await plc.WriteNodeAsync(addressConfig.Out_UpCameraPickStatus.Address, (Int16)1);
  1598. return;
  1599. }
  1600. else
  1601. {
  1602. await plc.WriteNodeAsync(addressConfig.Out_UpCameraPickStatus.Address, (Int16)2);
  1603. return;
  1604. }
  1605. }
  1606. else
  1607. {
  1608. //获取固定向上相机1的流程
  1609. var process1 = _productService.GetCurrentProductProcedureModelById(currentProduct.MoveDownCamer1PickPhotoProcedureId);
  1610. if (process1 == null)
  1611. {
  1612. SendTaskMessage($"固定向上相机1视觉流程,流程ID:{currentProduct.MoveDownCamer1PickPhotoProcedureId}", MessageLevel.Alarm);
  1613. await plc.WriteNodeAsync(addressConfig.Out_UpCameraPickStatus.Address, (Int16)2);
  1614. return;
  1615. }
  1616. //获取固定向上相机2的流程
  1617. var process2 = _productService.GetCurrentProductProcedureModelById(currentProduct.MoveDownCamer2PickPhotoProcedureId);
  1618. if (process2 == null)
  1619. {
  1620. SendTaskMessage($"固定向上相机1视觉流程,流程ID:{currentProduct.MoveDownCamer2PickPhotoProcedureId}", MessageLevel.Alarm);
  1621. await plc.WriteNodeAsync(addressConfig.Out_UpCameraPickStatus.Address, (Int16)2);
  1622. return;
  1623. }
  1624. // 执行视觉任务
  1625. var _cts = new CancellationTokenSource();
  1626. var task1 = _remoteCommandService.ExecutePhotoGetSinglePoint(process1, null, new double[] { currentPosition.X, currentPosition.Y, currentPosition.U }, _cts.Token, 1, null);
  1627. var task2 = _remoteCommandService.ExecutePhotoGetSinglePoint(process2, null, new double[] { currentPosition.X, currentPosition.Y, currentPosition.U }, _cts.Token, 2, null);
  1628. var visionResults = await Task.WhenAll(task1, task2);
  1629. if (isDryMode)
  1630. {
  1631. await plc.WriteNodeAsync(addressConfig.Out_UpCameraPickStatus.Address, (Int16)1);
  1632. return;
  1633. }
  1634. if (visionResults[0].IsSucceed && visionResults[1].IsSucceed)
  1635. {
  1636. float tool1X = (float)(visionResults[0].X - visionResults[0].X);
  1637. float tool1Y = (float)(visionResults[0].Y - visionResults[0].Y);
  1638. float tool2X = (float)(visionResults[1].X - visionResults[1].X);
  1639. float tool2Y = (float)(visionResults[1].Y - visionResults[1].Y);
  1640. float toolX = (tool1X + tool2X) / 2;
  1641. float toolY = (tool1Y + tool2Y) / 2;
  1642. //计算两个点的线角度
  1643. currentProduct.PickPoints[0].X_Position = (float)toolX;
  1644. currentProduct.PickPoints[0].Y_Position = (float)toolY;
  1645. //currentProduct.PickPoints[0].U_Position += (float)visionResult.U;
  1646. await currentProduct.ScrewPoints[0].WriteToPlcAddress(addressConfig.Out_Pick, plc);
  1647. await plc.WriteNodeAsync(addressConfig.Out_UpCameraPickStatus.Address, (Int16)1);
  1648. return;
  1649. }
  1650. else
  1651. {
  1652. await plc.WriteNodeAsync(addressConfig.Out_UpCameraPickStatus.Address, (Int16)2);
  1653. return;
  1654. }
  1655. }
  1656. }
  1657. else
  1658. {
  1659. SendTaskMessage($"触发上相机取料拍照信号已关闭", MessageLevel.Alarm);
  1660. await plc.WriteNodeAsync(addressConfig.Out_UpCameraPickStatus.Address, (Int16)0);
  1661. }
  1662. }
  1663. }
  1664. // 上相机锁附/组装拍照
  1665. else if (addressConfig.In_UpCameraPutExe.Address.Contains(tuple.nodeId))
  1666. {
  1667. if (tuple.value is bool state)
  1668. {
  1669. if (_LastUpCameraPutExe != state)
  1670. {
  1671. _LastUpCameraPutExe = state;
  1672. }
  1673. else
  1674. {
  1675. return;
  1676. }
  1677. if (state)
  1678. {
  1679. SendTaskMessage($"触发上相机锁附/组装拍照...", MessageLevel.Debug);
  1680. //相机的编号
  1681. Int16 cameraIndex = plc.ReadNode<Int16>(addressConfig.In_UpCameraPutNum.Address);
  1682. SendTaskMessage($"编号:{cameraIndex}", MessageLevel.Info);
  1683. // 获取机器人当前坐标
  1684. var robot = _robotService.GetAllRobots().FirstOrDefault();
  1685. if (robot == null)
  1686. {
  1687. SendTaskMessage($"未找到机器人,无法执行上相机锁附/组装拍照视觉任务", MessageLevel.Alarm);
  1688. await plc.WriteNodeAsync(addressConfig.Out_UpCameraPutStatus.Address, (Int16)2);
  1689. return;
  1690. }
  1691. var currentPosition = robot.GetRobotPos();
  1692. //如果上相机2的流程id为空,则表示只有一个上相机
  1693. if (currentProduct.MoveDownCamera2LockPhotoProcedureId == Guid.Empty)
  1694. {
  1695. //获取移动向下相机1的流程
  1696. var process = _productService.GetCurrentProductProcedureModelById(currentProduct.MoveDownCamera1LockPhotoProcedureId);
  1697. if (process == null)
  1698. {
  1699. SendTaskMessage($"移动向下相机1视觉流程,流程ID:{currentProduct.MoveDownCamera1LockPhotoProcedureId}", MessageLevel.Alarm);
  1700. await plc.WriteNodeAsync(addressConfig.Out_UpCameraPutStatus.Address, (Int16)2);
  1701. return;
  1702. }
  1703. // 执行视觉任务
  1704. var _cts = new CancellationTokenSource();
  1705. var visionResult = await _remoteCommandService.ExecutePhotoGetSinglePoint(process, null, new double[] { currentPosition.X, currentPosition.Y, currentPosition.U }, _cts.Token, cameraIndex, null);
  1706. if (isDryMode)
  1707. {
  1708. await plc.WriteNodeAsync(addressConfig.Out_UpCameraPutStatus.Address, (Int16)1);
  1709. return;
  1710. }
  1711. if (visionResult.IsSucceed)
  1712. {
  1713. UpCameraPutResults[cameraIndex] = new OpenCvSharp.Point3f((float)visionResult.X, (float)visionResult.Y, (float)visionResult.U);
  1714. await plc.WriteNodeAsync(addressConfig.Out_UpCameraPutStatus.Address, (Int16)1);
  1715. return;
  1716. }
  1717. else
  1718. {
  1719. await plc.WriteNodeAsync(addressConfig.Out_UpCameraPutStatus.Address, (Int16)2);
  1720. return;
  1721. }
  1722. }
  1723. else
  1724. {
  1725. //获取移动向下相机1的流程
  1726. var process1 = _productService.GetCurrentProductProcedureModelById(currentProduct.MoveDownCamera1LockPhotoProcedureId);
  1727. if (process1 == null)
  1728. {
  1729. SendTaskMessage($"固定向上相机1视觉流程,流程ID:{currentProduct.MoveDownCamera1LockPhotoProcedureId}", MessageLevel.Alarm);
  1730. await plc.WriteNodeAsync(addressConfig.Out_UpCameraPutStatus.Address, (Int16)2);
  1731. return;
  1732. }
  1733. //获取移动向下相机2的流程
  1734. var process2 = _productService.GetCurrentProductProcedureModelById(currentProduct.MoveDownCamera2LockPhotoProcedureId);
  1735. if (process2 == null)
  1736. {
  1737. SendTaskMessage($"固定向上相机1视觉流程,流程ID:{currentProduct.MoveDownCamera2LockPhotoProcedureId}", MessageLevel.Alarm);
  1738. await plc.WriteNodeAsync(addressConfig.Out_UpCameraPutStatus.Address, (Int16)2);
  1739. return;
  1740. }
  1741. // 执行视觉任务
  1742. var _cts = new CancellationTokenSource();
  1743. var task1 = _remoteCommandService.ExecutePhotoGetSinglePoint(process1, null, new double[] { currentPosition.X, currentPosition.Y, currentPosition.U }, _cts.Token, 1, null);
  1744. var task2 = _remoteCommandService.ExecutePhotoGetSinglePoint(process2, null, new double[] { currentPosition.X, currentPosition.Y, currentPosition.U }, _cts.Token, 2, null);
  1745. var visionResults = await Task.WhenAll(task1, task2);
  1746. if (isDryMode)
  1747. {
  1748. await plc.WriteNodeAsync(addressConfig.Out_UpCameraPutStatus.Address, (Int16)1);
  1749. return;
  1750. }
  1751. if (visionResults[0].IsSucceed && visionResults[1].IsSucceed)
  1752. {
  1753. float centerX = (float)(visionResults[0].X + visionResults[1].X) / 2;
  1754. float centerY = (float)(visionResults[0].Y + visionResults[1].Y) / 2;
  1755. _MoveCameraAngle = Common.CalculateAngleBetweenPointsDegrees(new PointF((float)visionResults[0].X, (float)visionResults[0].Y), new PointF((float)visionResults[1].X, (float)visionResults[1].Y));
  1756. UpCameraPutResults[cameraIndex] = new OpenCvSharp.Point3f(centerX, centerY, (float)_MoveCameraAngle);
  1757. await plc.WriteNodeAsync(addressConfig.Out_UpCameraPutStatus.Address, (Int16)1);
  1758. return;
  1759. }
  1760. else
  1761. {
  1762. await plc.WriteNodeAsync(addressConfig.Out_UpCameraPutStatus.Address, (Int16)2);
  1763. return;
  1764. }
  1765. }
  1766. }
  1767. else
  1768. {
  1769. SendTaskMessage($"触发上相机锁附/组装拍照信号已关闭", MessageLevel.Alarm);
  1770. await plc.WriteNodeAsync(addressConfig.Out_UpCameraPutStatus.Address, (Int16)0);
  1771. }
  1772. }
  1773. }
  1774. // 上相机检测拍照
  1775. else if (addressConfig.In_UPCameracheckExe.Address.Contains(tuple.nodeId))
  1776. {
  1777. if (tuple.value is bool state)
  1778. {
  1779. if (_LastUPCameracheckExe != state)
  1780. {
  1781. _LastUPCameracheckExe = state;
  1782. }
  1783. else
  1784. {
  1785. return;
  1786. }
  1787. if (state)
  1788. {
  1789. SendTaskMessage($"触发上相机检测拍照...", MessageLevel.Debug);
  1790. //相机的编号
  1791. Int16 cameraIndex = plc.ReadNode<Int16>(addressConfig.In_UPCameracheckNum.Address);
  1792. if (isDryMode)
  1793. {
  1794. await plc.WriteNodeAsync(addressConfig.Out_UPCameracheckStatus.Address, (Int16)1);
  1795. return;
  1796. }
  1797. //视觉流程ID
  1798. Guid procedureId = Guid.Empty;
  1799. //获取锁付拍照的点位
  1800. var pos = currentProduct.CheckCameraPoints.FirstOrDefault(p => p.Number == cameraIndex);
  1801. if (pos != null)
  1802. {
  1803. procedureId = pos.VisionProcessID;
  1804. }
  1805. // 获取流程
  1806. var process = _productService.GetCurrentProductProcedureModelById(procedureId);
  1807. if (process == null)
  1808. {
  1809. SendTaskMessage(string.Format("未找到点位:{0}的视觉流程:{1}", cameraIndex, procedureId), MessageLevel.Alarm);
  1810. await plc.WriteNodeAsync(addressConfig.Out_UPCameracheckStatus.Address, (Int16)2);
  1811. return;
  1812. }
  1813. // 执行视觉任务
  1814. var _cts = new CancellationTokenSource();
  1815. var visionResult = await _remoteCommandService.ExecutePhotoGetSinglePoint(process, null, null, _cts.Token, cameraIndex, null);
  1816. if (visionResult.IsSucceed)
  1817. {
  1818. await plc.WriteNodeAsync(addressConfig.Out_UPCameracheckStatus.Address, (Int16)1);
  1819. SendTaskMessage($"点位{cameraIndex}检测OK", MessageLevel.Debug);
  1820. }
  1821. else
  1822. {
  1823. await plc.WriteNodeAsync(addressConfig.Out_UPCameracheckStatus.Address, (Int16)2);
  1824. SendTaskMessage($"点位{cameraIndex}检测NG", MessageLevel.Error);
  1825. }
  1826. await plc.WriteNodeAsync(addressConfig.Out_UPCameracheckStatus.Address, (Int16)1);
  1827. }
  1828. else
  1829. {
  1830. SendTaskMessage($"触发上相机检测拍照信号已关闭", MessageLevel.Alarm);
  1831. await plc.WriteNodeAsync(addressConfig.Out_UPCameracheckStatus.Address, (Int16)0);
  1832. }
  1833. }
  1834. }
  1835. // 固定相机取料拍照--屏幕拍照,取上边沿中心(防呆作用)
  1836. else if (addressConfig.In_FixedCameraPickExe.Address.Contains(tuple.nodeId))
  1837. {
  1838. if (tuple.value is bool state)
  1839. {
  1840. if (_LastFixedCameraPickExe != state)
  1841. {
  1842. _LastFixedCameraPickExe = state;
  1843. }
  1844. else
  1845. {
  1846. return;
  1847. }
  1848. if (state)
  1849. {
  1850. SendTaskMessage($"触发固定相机取料拍照...", MessageLevel.Debug);
  1851. //相机的编号
  1852. //Int16 cameraIndex = plc.ReadNode<Int16>(addressConfig.In_FixedCameraPickNum.Address);
  1853. if (currentProduct.IndependentCamer1AuxProcedureId == Guid.Empty)
  1854. {
  1855. await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPickStatus.Address, (Int16)2);
  1856. return;
  1857. }
  1858. if (currentProduct.IndependentCamer2AuxProcedureId == Guid.Empty)
  1859. {
  1860. await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPickStatus.Address, (Int16)2);
  1861. return;
  1862. }
  1863. if (currentProduct.IndependentCamer3AuxProcedureId == Guid.Empty)
  1864. {
  1865. await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPickStatus.Address, (Int16)2);
  1866. return;
  1867. }
  1868. if (currentProduct.IndependentCamer4AuxProcedureId == Guid.Empty)
  1869. {
  1870. await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPickStatus.Address, (Int16)2);
  1871. return;
  1872. }
  1873. //获取四个独立相机的流程id
  1874. var process1 = _productService.GetCurrentProductProcedureModelById(currentProduct.IndependentCamer1AuxProcedureId);
  1875. var process2 = _productService.GetCurrentProductProcedureModelById(currentProduct.IndependentCamer2AuxProcedureId);
  1876. var process3 = _productService.GetCurrentProductProcedureModelById(currentProduct.IndependentCamer3AuxProcedureId);
  1877. var process4 = _productService.GetCurrentProductProcedureModelById(currentProduct.IndependentCamer4AuxProcedureId);
  1878. // 执行视觉任务
  1879. var _cts = new CancellationTokenSource();
  1880. var task1 = _remoteCommandService.ExecutePhotoGetSinglePoint(process1, null, null, _cts.Token, 1, null);
  1881. var task2 = _remoteCommandService.ExecutePhotoGetSinglePoint(process2, null, null, _cts.Token, 2, null);
  1882. var task3 = _remoteCommandService.ExecutePhotoGetSinglePoint(process3, null, null, _cts.Token, 3, null);
  1883. var task4 = _remoteCommandService.ExecutePhotoGetSinglePoint(process4, null, null, _cts.Token, 4, null);
  1884. var visionResults = await Task.WhenAll(task1, task2, task3, task4);
  1885. if (isDryMode)
  1886. {
  1887. await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPickStatus.Address, (Int16)1);
  1888. return;
  1889. }
  1890. if (visionResults[0].IsSucceed && visionResults[1].IsSucceed && visionResults[2].IsSucceed && visionResults[3].IsSucceed)
  1891. {
  1892. Vector<double> p1 = Vector<double>.Build.Dense(new double[] { visionResults[0].X, visionResults[0].Y });
  1893. Vector<double> p2 = Vector<double>.Build.Dense(new double[] { visionResults[1].X, visionResults[1].Y });
  1894. Vector<double> p3 = Vector<double>.Build.Dense(new double[] { visionResults[2].X, visionResults[2].Y });
  1895. Vector<double> p4 = Vector<double>.Build.Dense(new double[] { visionResults[3].X, visionResults[3].Y });
  1896. //var center = RectangleCenterCalculator.CalculateByDirectMethod(new List<Vector<double>> { p1, p2, p3, p4 });
  1897. var center = RectangleCenterCalculator.CalculateOptimalCenter(new List<Vector<double>> { p1, p2, p3, p4 });//0215修改,不考虑点顺序
  1898. //UpCameraPutResults[1].X = (float)center.Center[0];
  1899. //UpCameraPutResults[1].Y = (float)center.Center[1];
  1900. //UpCameraPutResults[1].Z = (float)center.MajorAxisAngle;
  1901. UpCameraPutResults[1].X = ((float)center.TopLeft[0] + (float)center.TopRight[0]) / 2;
  1902. UpCameraPutResults[1].Y = ((float)center.TopLeft[1] + (float)center.TopRight[1]) / 2;
  1903. UpCameraPutResults[1].Z = (float)center.MajorAxisAngle;
  1904. SendTaskMessage($"LCD拍照结果:X={UpCameraPutResults[1].X},Y={UpCameraPutResults[1].Y},Angle={UpCameraPutResults[1].Z}", MessageLevel.Info);
  1905. var dist1 = RectangleCenterCalculator.Distance(center.TopLeft, center.TopRight);//长1
  1906. var dist2 = RectangleCenterCalculator.Distance(center.TopRight, center.BottomRight);//宽1
  1907. var dist3 = RectangleCenterCalculator.Distance(center.BottomLeft, center.BottomRight);//长2
  1908. var dist4 = RectangleCenterCalculator.Distance(center.TopLeft, center.BottomLeft);//宽2
  1909. LogHelper.WriteLogInfo($"dist1={dist1};dist2={dist2};dist3={dist3};dist4={dist4};");
  1910. if (currentProduct.GlobalParamListCollection.Count >= 4)
  1911. {
  1912. var param = currentProduct.GlobalParamListCollection.FirstOrDefault(p => p.Number == 2);
  1913. if (param != null)//长
  1914. {
  1915. var length = Double.Parse(param.Param1);
  1916. var limtDown = Double.Parse(param.Param2);
  1917. var LimtUp = Double.Parse(param.Param3);
  1918. if (!(((length + limtDown) <= dist1) && (dist1 <= (length + LimtUp)) && (((length + limtDown) <= dist3) && (dist3 <= (length + LimtUp)))))
  1919. {
  1920. SendTaskMessage($"屏幕长度超范围:长1={dist1},长2={dist3},para1={length},para2={limtDown},para3={LimtUp}", MessageLevel.Alarm);
  1921. await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPickStatus.Address, (Int16)2);
  1922. return;
  1923. }
  1924. SendTaskMessage($"屏幕长度防呆:长1={dist1},长2={dist3},para1={length},para2={limtDown},para3={LimtUp}", MessageLevel.Info);
  1925. }
  1926. param = currentProduct.GlobalParamListCollection.FirstOrDefault(p => p.Number == 3);
  1927. if (param != null)//宽
  1928. {
  1929. var length = Double.Parse(param.Param1);
  1930. var limtDown = Double.Parse(param.Param2);
  1931. var LimtUp = Double.Parse(param.Param3);
  1932. if (!(((length + limtDown) <= dist2) && (dist2 <= (length + LimtUp)) && (((length + limtDown) <= dist4) && (dist4 <= (length + LimtUp)))))
  1933. {
  1934. SendTaskMessage($"屏幕宽度超范围:宽1={dist2},宽2={dist4},para1={length},para2={limtDown},para3={LimtUp}", MessageLevel.Alarm);
  1935. await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPickStatus.Address, (Int16)2);
  1936. return;
  1937. }
  1938. SendTaskMessage($"屏幕宽度防呆:宽1={dist2},宽2={dist4},para1={length},para2={limtDown},para3={LimtUp}", MessageLevel.Info);
  1939. }
  1940. if (currentProduct.GlobalParamListCollection.Count >= 7)
  1941. {
  1942. param = currentProduct.GlobalParamListCollection.FirstOrDefault(p => p.Number == 4);
  1943. if (param != null)//X防呆
  1944. {
  1945. var Limt_val = Double.Parse(param.Param1);
  1946. var limtDown = Double.Parse(param.Param2);
  1947. var LimtUp = Double.Parse(param.Param3);
  1948. var dist = UpCameraPutResults[1].X - UpCameraPutResults[0].X;
  1949. if (!(((Limt_val + limtDown) <= dist) && (dist <= (Limt_val + LimtUp))))
  1950. {
  1951. SendTaskMessage($"贴合X防呆超限:X实际差值={dist},X允许差值={Limt_val},下限={limtDown},上限={LimtUp}", MessageLevel.Info);
  1952. await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPickStatus.Address, (Int16)2);
  1953. return;
  1954. }
  1955. SendTaskMessage($"贴合X防呆:X实际差值={dist},X允许差值={Limt_val},下限={limtDown},上限={LimtUp}", MessageLevel.Info);
  1956. }
  1957. param = currentProduct.GlobalParamListCollection.FirstOrDefault(p => p.Number == 5);
  1958. if (param != null)//Y防呆
  1959. {
  1960. var Limt_val = Double.Parse(param.Param1);
  1961. var limtDown = Double.Parse(param.Param2);
  1962. var LimtUp = Double.Parse(param.Param3);
  1963. var dist = UpCameraPutResults[1].Y - UpCameraPutResults[0].Y;
  1964. if (!(((Limt_val + limtDown) <= dist) && (dist <= (Limt_val + LimtUp))))
  1965. {
  1966. SendTaskMessage($"贴合Y防呆超限:Y实际差值={dist},Y允许差值={Limt_val},下限={limtDown},上限={LimtUp}", MessageLevel.Info);
  1967. await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPickStatus.Address, (Int16)2);
  1968. return;
  1969. }
  1970. SendTaskMessage($"贴合Y防呆:Y实际差值={dist},Y允许差值={Limt_val},下限={limtDown},上限={LimtUp}", MessageLevel.Info);
  1971. }
  1972. param = currentProduct.GlobalParamListCollection.FirstOrDefault(p => p.Number == 6);
  1973. if (param != null)//U防呆
  1974. {
  1975. var Limt_val = Double.Parse(param.Param1);
  1976. var limtDown = Double.Parse(param.Param2);
  1977. var LimtUp = Double.Parse(param.Param3);
  1978. var dist = UpCameraPutResults[1].Z - UpCameraPutResults[0].Z;
  1979. if (!(((Limt_val + limtDown) <= dist) && (dist <= (Limt_val + LimtUp))))
  1980. {
  1981. SendTaskMessage($"贴合U防呆超限:U实际差值={dist},X允许差值={Limt_val},下限={limtDown},上限={LimtUp}", MessageLevel.Info);
  1982. await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPickStatus.Address, (Int16)2);
  1983. return;
  1984. }
  1985. SendTaskMessage($"贴合U防呆:U实际差值={dist},X允许差值={Limt_val},下限={limtDown},上限={LimtUp}", MessageLevel.Info);
  1986. }
  1987. }
  1988. }
  1989. }
  1990. else
  1991. {
  1992. await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPickStatus.Address, (Int16)2);
  1993. return;
  1994. }
  1995. await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPickStatus.Address, (Int16)1);
  1996. }
  1997. else
  1998. {
  1999. SendTaskMessage($"触发固定相机取料拍照信号已关闭", MessageLevel.Alarm);
  2000. await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPickStatus.Address, (Int16)0);
  2001. }
  2002. }
  2003. }
  2004. // 固定相机锁附/组装拍照--外壳拍照
  2005. else if (addressConfig.In_FixedCameraPutExe.Address.Contains(tuple.nodeId))
  2006. {
  2007. if (tuple.value is bool state)
  2008. {
  2009. if (_LastFixedCameraPutExe != state)
  2010. {
  2011. _LastFixedCameraPutExe = state;
  2012. }
  2013. else
  2014. {
  2015. return;
  2016. }
  2017. if (state)
  2018. {
  2019. SendTaskMessage($"触发固定相机锁附/组装拍照...", MessageLevel.Debug);
  2020. //相机的编号
  2021. //Int16 cameraIndex = plc.ReadNode<Int16>(addressConfig.In_FixedCameraPutNum.Address);
  2022. if (currentProduct.IndependentCamer1ProductProcedureId == Guid.Empty)
  2023. {
  2024. await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPutStatus.Address, (Int16)2);
  2025. return;
  2026. }
  2027. if (currentProduct.IndependentCamer2ProductProcedureId == Guid.Empty)
  2028. {
  2029. await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPutStatus.Address, (Int16)2);
  2030. return;
  2031. }
  2032. if (currentProduct.IndependentCamer3ProductProcedureId == Guid.Empty)
  2033. {
  2034. await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPutStatus.Address, (Int16)2);
  2035. return;
  2036. }
  2037. if (currentProduct.IndependentCamer4ProductProcedureId == Guid.Empty)
  2038. {
  2039. await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPutStatus.Address, (Int16)2);
  2040. return;
  2041. }
  2042. //获取四个独立相机的流程id
  2043. var process1 = _productService.GetCurrentProductProcedureModelById(currentProduct.IndependentCamer1ProductProcedureId);
  2044. var process2 = _productService.GetCurrentProductProcedureModelById(currentProduct.IndependentCamer2ProductProcedureId);
  2045. var process3 = _productService.GetCurrentProductProcedureModelById(currentProduct.IndependentCamer3ProductProcedureId);
  2046. var process4 = _productService.GetCurrentProductProcedureModelById(currentProduct.IndependentCamer4ProductProcedureId);
  2047. // 执行视觉任务
  2048. var _cts = new CancellationTokenSource();
  2049. var task1 = _remoteCommandService.ExecutePhotoGetSinglePoint(process1, null, null, _cts.Token, 1, null);
  2050. var task2 = _remoteCommandService.ExecutePhotoGetSinglePoint(process2, null, null, _cts.Token, 2, null);
  2051. var task3 = _remoteCommandService.ExecutePhotoGetSinglePoint(process3, null, null, _cts.Token, 3, null);
  2052. var task4 = _remoteCommandService.ExecutePhotoGetSinglePoint(process4, null, null, _cts.Token, 4, null);
  2053. var visionResults = await Task.WhenAll(task1, task2, task3, task4);
  2054. if (isDryMode)
  2055. {
  2056. await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPutStatus.Address, (Int16)1);
  2057. return;
  2058. }
  2059. if (visionResults[0].IsSucceed && visionResults[1].IsSucceed && visionResults[2].IsSucceed && visionResults[3].IsSucceed)
  2060. {
  2061. Vector<double> p1 = Vector<double>.Build.Dense(new double[] { visionResults[0].X, visionResults[0].Y });
  2062. Vector<double> p2 = Vector<double>.Build.Dense(new double[] { visionResults[1].X, visionResults[1].Y });
  2063. Vector<double> p3 = Vector<double>.Build.Dense(new double[] { visionResults[2].X, visionResults[2].Y });
  2064. Vector<double> p4 = Vector<double>.Build.Dense(new double[] { visionResults[3].X, visionResults[3].Y });
  2065. //var center = RectangleCenterCalculator.CalculateByDirectMethod(new List<Vector<double>> { p1, p2, p3, p4 });
  2066. var center = RectangleCenterCalculator.CalculateOptimalCenter(new List<Vector<double>> { p1, p2, p3, p4 });//0215修改,不考虑点顺序
  2067. //UpCameraPutResults[0].X = (float)center.Center[0];
  2068. //UpCameraPutResults[0].Y = (float)center.Center[1];
  2069. UpCameraPutResults[0].X = ((float)center.TopLeft[0] + (float)center.TopRight[0]) / 2;
  2070. UpCameraPutResults[0].Y = ((float)center.TopLeft[1] + (float)center.TopRight[1]) / 2;
  2071. UpCameraPutResults[0].Z = (float)center.MajorAxisAngle;
  2072. SendTaskMessage($"产品拍照结果:X={UpCameraPutResults[0].X},Y={UpCameraPutResults[0].Y},Angle={UpCameraPutResults[0].Z}", MessageLevel.Info);
  2073. var dist1 = RectangleCenterCalculator.Distance(center.TopLeft, center.TopRight);//长1
  2074. var dist2 = RectangleCenterCalculator.Distance(center.TopRight, center.BottomRight);//宽1
  2075. var dist3 = RectangleCenterCalculator.Distance(center.BottomLeft, center.BottomRight);//长2
  2076. var dist4 = RectangleCenterCalculator.Distance(center.TopLeft, center.BottomLeft);//宽2
  2077. LogHelper.WriteLogInfo($"dist1={dist1};dist2={dist2};dist3={dist3};dist4={dist4};");
  2078. dis[0] = dist1.ToString();
  2079. dis[1] = dist2.ToString();
  2080. dis[2] = dist3.ToString();
  2081. dis[3] = dist4.ToString();
  2082. if (currentProduct.GlobalParamListCollection.Count >= 4)
  2083. {
  2084. var param = currentProduct.GlobalParamListCollection.FirstOrDefault(p => p.Number == 0);
  2085. if (param != null)//长
  2086. {
  2087. var length = Double.Parse(param.Param1);
  2088. var limtDown = Double.Parse(param.Param2);
  2089. var LimtUp = Double.Parse(param.Param3);
  2090. if (!(((length + limtDown) <= dist1) && (dist1 <= (length + LimtUp)) && (((length + limtDown) <= dist3) && (dist3 <= (length + LimtUp)))))
  2091. {
  2092. SendTaskMessage($"外壳长度超范围:长1={dist1},长2={dist3},para1={length},para2={limtDown},para3={LimtUp}", MessageLevel.Alarm);
  2093. await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPutStatus.Address, (Int16)2);
  2094. return;
  2095. }
  2096. SendTaskMessage($"外壳长度防呆:长1={dist1},长2={dist3},para1={length},para2={limtDown},para3={LimtUp}", MessageLevel.Info);
  2097. }
  2098. param = currentProduct.GlobalParamListCollection.FirstOrDefault(p => p.Number == 1);
  2099. if (param != null)//宽
  2100. {
  2101. var length = Double.Parse(param.Param1);
  2102. var limtDown = Double.Parse(param.Param2);
  2103. var LimtUp = Double.Parse(param.Param3);
  2104. if (!(((length + limtDown) <= dist2) && (dist2 <= (length + LimtUp)) && (((length + limtDown) <= dist4) && (dist4 <= (length + LimtUp)))))
  2105. {
  2106. SendTaskMessage($"外壳宽度超范围:宽1={dist2},宽2={dist4},para1={length},para2={limtDown},para3={LimtUp}", MessageLevel.Alarm);
  2107. await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPutStatus.Address, (Int16)2);
  2108. return;
  2109. }
  2110. SendTaskMessage($"外壳宽度防呆:宽1={dist2},宽2={dist4},para1={length},para2={limtDown},para3={LimtUp}", MessageLevel.Info);
  2111. }
  2112. }
  2113. SendTaskMessage($"固定相机锁附/组装拍照检测OK", MessageLevel.Debug);
  2114. SendTaskMessage($"固定相机流程1结果:{visionResults[0].dis}", MessageLevel.Debug);
  2115. SendTaskMessage($"固定相机流程2结果:{visionResults[1].dis}", MessageLevel.Debug);
  2116. SendTaskMessage($"固定相机流程3结果:{visionResults[2].dis}", MessageLevel.Debug);
  2117. SendTaskMessage($"固定相机流程4结果:{visionResults[3].dis}", MessageLevel.Debug);
  2118. DISDict.Clear();
  2119. //DISDict.Add("DownDis1", visionResults[0].dis.Split(',')[1]);
  2120. //DISDict.Add("LeftDis1", visionResults[0].dis.Split(',')[2]);
  2121. //DISDict.Add("LeftDis2", visionResults[1].dis.Split(',')[1]);
  2122. //DISDict.Add("UpDis1", visionResults[1].dis.Split(',')[2]);
  2123. //DISDict.Add("DownDis2", visionResults[2].dis.Split(',')[1]);
  2124. //DISDict.Add("RightDis1", visionResults[2].dis.Split(',')[2]);
  2125. //DISDict.Add("UpDis2", visionResults[3].dis.Split(',')[1]);
  2126. //DISDict.Add("RightDis2", visionResults[3].dis.Split(',')[2]);
  2127. }
  2128. else
  2129. {
  2130. SendTaskMessage($"固定相机锁附/组装拍照检测NG", MessageLevel.Error);
  2131. SendTaskMessage($"固定相机流程1结果:{visionResults[0].dis}", MessageLevel.Debug);
  2132. SendTaskMessage($"固定相机流程2结果:{visionResults[1].dis}", MessageLevel.Debug);
  2133. SendTaskMessage($"固定相机流程3结果:{visionResults[2].dis}", MessageLevel.Debug);
  2134. SendTaskMessage($"固定相机流程4结果:{visionResults[3].dis}", MessageLevel.Debug);
  2135. DISDict.Clear();
  2136. //DISDict.Add("DownDis1", visionResults[0].dis.Split(',')[1]);
  2137. //DISDict.Add("LeftDis1", visionResults[0].dis.Split(',')[2]);
  2138. //DISDict.Add("LeftDis2", visionResults[1].dis.Split(',')[1]);
  2139. //DISDict.Add("UpDis1", visionResults[1].dis.Split(',')[2]);
  2140. //DISDict.Add("DownDis2", visionResults[2].dis.Split(',')[1]);
  2141. //DISDict.Add("RightDis1", visionResults[2].dis.Split(',')[2]);
  2142. //DISDict.Add("UpDis2", visionResults[3].dis.Split(',')[1]);
  2143. //DISDict.Add("RightDis2", visionResults[3].dis.Split(',')[2]);
  2144. await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPutStatus.Address, (Int16)2);
  2145. return;
  2146. }
  2147. await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPutStatus.Address, (Int16)1);
  2148. }
  2149. else
  2150. {
  2151. SendTaskMessage($"触发固定相机锁附/组装拍照信号已关闭", MessageLevel.Alarm);
  2152. await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPutStatus.Address, (Int16)0);
  2153. }
  2154. }
  2155. }
  2156. // 固定相机检测拍照
  2157. else if (addressConfig.In_FixedCameracheckExe.Address.Contains(tuple.nodeId))
  2158. {
  2159. if (tuple.value is bool state)
  2160. {
  2161. if (_LastFixedCameracheckExe != state)
  2162. {
  2163. _LastFixedCameracheckExe = state;
  2164. }
  2165. else
  2166. {
  2167. return;
  2168. }
  2169. if (state)
  2170. {
  2171. SendTaskMessage($"触发固定相机检测拍照...", MessageLevel.Debug);
  2172. //相机的编号
  2173. //Int16 cameraIndex = plc.ReadNode<Int16>(addressConfig.In_FixedCameracheckNum.Address);
  2174. if (currentProduct.IndependentCamer1CheckProcedureId == Guid.Empty)
  2175. {
  2176. await plc.WriteNodeAsync(addressConfig.Out_FixedCameracheckStatus.Address, (Int16)2);
  2177. return;
  2178. }
  2179. if (currentProduct.IndependentCamer2CheckProcedureId == Guid.Empty)
  2180. {
  2181. await plc.WriteNodeAsync(addressConfig.Out_FixedCameracheckStatus.Address, (Int16)2);
  2182. return;
  2183. }
  2184. if (currentProduct.IndependentCamer3CheckProcedureId == Guid.Empty)
  2185. {
  2186. await plc.WriteNodeAsync(addressConfig.Out_FixedCameracheckStatus.Address, (Int16)2);
  2187. return;
  2188. }
  2189. if (currentProduct.IndependentCamer4CheckProcedureId == Guid.Empty)
  2190. {
  2191. await plc.WriteNodeAsync(addressConfig.Out_FixedCameracheckStatus.Address, (Int16)2);
  2192. return;
  2193. }
  2194. //获取四个独立相机的流程id
  2195. var process1 = _productService.GetCurrentProductProcedureModelById(currentProduct.IndependentCamer1CheckProcedureId);
  2196. var process2 = _productService.GetCurrentProductProcedureModelById(currentProduct.IndependentCamer2CheckProcedureId);
  2197. var process3 = _productService.GetCurrentProductProcedureModelById(currentProduct.IndependentCamer3CheckProcedureId);
  2198. var process4 = _productService.GetCurrentProductProcedureModelById(currentProduct.IndependentCamer4CheckProcedureId);
  2199. // 执行视觉任务(复检结果gap)
  2200. var _cts = new CancellationTokenSource();
  2201. var task1 = _remoteCommandService.ExecutePhotoGetDistance(process1, null, null, _cts.Token, 1, null);
  2202. var task2 = _remoteCommandService.ExecutePhotoGetDistance(process2, null, null, _cts.Token, 2, null);
  2203. var task3 = _remoteCommandService.ExecutePhotoGetDistance(process3, null, null, _cts.Token, 3, null);
  2204. var task4 = _remoteCommandService.ExecutePhotoGetDistance(process4, null, null, _cts.Token, 4, null);
  2205. var visionResults = await Task.WhenAll(task1, task2, task3, task4);
  2206. if (isDryMode)
  2207. {
  2208. await plc.WriteNodeAsync(addressConfig.Out_FixedCameracheckStatus.Address, (Int16)1);
  2209. return;
  2210. }
  2211. for (int i = 0; i < visionResults.Length; i++)
  2212. {
  2213. if (visionResults[i].distance.Length != 0)
  2214. { }
  2215. else
  2216. {
  2217. visionResults[i].distance = $"{i + 1},999";
  2218. }
  2219. }
  2220. if (visionResults[0].IsSucceed && visionResults[1].IsSucceed && visionResults[2].IsSucceed && visionResults[3].IsSucceed)
  2221. {
  2222. SendTaskMessage($"固定相机检测OK", MessageLevel.Debug);
  2223. SendTaskMessage($"流程1结果:{visionResults[0].distance}", MessageLevel.Debug);
  2224. SendTaskMessage($"流程2结果:{visionResults[1].distance}", MessageLevel.Debug);
  2225. SendTaskMessage($"流程3结果:{visionResults[2].distance}", MessageLevel.Debug);
  2226. SendTaskMessage($"流程4结果:{visionResults[3].distance}", MessageLevel.Debug);
  2227. gapDict.Clear();
  2228. gapDict.Add("Gap1", visionResults[0].distance.Split(',')[1]);
  2229. gapDict.Add("Gap2", visionResults[1].distance.Split(',')[1]);
  2230. gapDict.Add("Gap3", visionResults[2].distance.Split(',')[1]);
  2231. gapDict.Add("Gap4", visionResults[3].distance.Split(',')[1]);
  2232. //gapDict.Add("Gap5", visionResults[1].distance.Split(',')[2]);
  2233. //gapDict.Add("Gap6", visionResults[3].distance.Split(',')[2]);
  2234. //gapString = $"{visionResults[0].distance.Split(',')[1]},{visionResults[1].distance.Split(',')[1]},{visionResults[2].distance.Split(',')[1]},{visionResults[3].distance.Split(',')[1]}";
  2235. await plc.WriteNodeAsync(addressConfig.Out_FixedCameracheckStatus.Address, (Int16)1);
  2236. return;
  2237. }
  2238. else
  2239. {
  2240. string res1 = "", res2 = "", res3 = "", res4 = "", res5 = "", res6 = "";
  2241. res1 = visionResults[0].distance.Split(',')[1];
  2242. res2 = visionResults[1].distance.Split(',')[1];
  2243. res3 = visionResults[2].distance.Split(',')[1];
  2244. res4 = visionResults[3].distance.Split(',')[1];
  2245. //res5 = visionResults[1].distance.Split(',')[2];
  2246. //res6 = visionResults[3].distance.Split(',')[2];
  2247. SendTaskMessage($"固定相机检测NG", MessageLevel.Error);
  2248. gapDict.Clear();
  2249. gapDict.Add("Gap1", res1);
  2250. gapDict.Add("Gap2", res2);
  2251. gapDict.Add("Gap3", res3);
  2252. gapDict.Add("Gap4", res4);
  2253. //gapDict.Add("Gap5", res5);
  2254. //gapDict.Add("Gap6", res6);
  2255. //gapString = $"{res1},{res2},{res3},{res4}";
  2256. await plc.WriteNodeAsync(addressConfig.Out_FixedCameracheckStatus.Address, (Int16)2);
  2257. return;
  2258. }
  2259. }
  2260. else
  2261. {
  2262. SendTaskMessage($"触发固定相机检测拍照信号已关闭", MessageLevel.Alarm);
  2263. await plc.WriteNodeAsync(addressConfig.Out_FixedCameracheckStatus.Address, (Int16)0);
  2264. }
  2265. }
  2266. }
  2267. // 多相机坐标合并
  2268. else if (addressConfig.In_RequestPosition.Address.Contains(tuple.nodeId))
  2269. {
  2270. if (tuple.value is Int16 state)
  2271. {
  2272. if (_LastRequestPosition != state)
  2273. {
  2274. _LastRequestPosition = state;
  2275. }
  2276. else
  2277. {
  2278. return;
  2279. }
  2280. if (state == 1)
  2281. {
  2282. SendTaskMessage($"收到多相机坐标合并请求,开始合并...", MessageLevel.Debug);
  2283. if (isDryMode)//空跑模式
  2284. {
  2285. var point1 = currentProduct.ScrewPoints[0].Clone();
  2286. SendTaskMessage($"空跑模式点:1,{point1.X_Position:F3},{point1.Y_Position:F4},{point1.U_Position:F4}", MessageLevel.Info);
  2287. await point1.WriteToPlcAddress(addressConfig.Out_Screw, plc);
  2288. await plc.WriteNodeAsync(addressConfig.Out_WorkNum.Address, (Int16)1);
  2289. await plc.WriteNodeAsync(addressConfig.Out_CameraDone.Address, (Int16)1);
  2290. return;
  2291. }
  2292. // 获取机器人当前坐标
  2293. var robot = _robotService.GetAllRobots().FirstOrDefault();
  2294. if (robot == null)
  2295. {
  2296. SendTaskMessage($"未找到机器人,无法执行上相机锁附/组装拍照视觉任务", MessageLevel.Alarm);
  2297. await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)2);
  2298. return;
  2299. }
  2300. var currentPosition = robot.GetRobotPos();
  2301. //已经通过下相机拍屏幕两角获取--DownTool
  2302. //以LCD的中线创建工具坐标
  2303. //ToolCoord toolCoord = new ToolCoord();
  2304. //toolCoord.ComputeTool(currentPosition.X, currentPosition.Y, currentPosition.U, UpCameraPutResults[1].X, UpCameraPutResults[1].Y);
  2305. //添加新的补偿算法--20260213
  2306. double offset_X = 0, offset_Y = 0, offset_U = 0;
  2307. var current = _productService.GetCurrentProduct();
  2308. if (current.ScrewPoints == null || current.ScrewPoints.Count <= 0)
  2309. {
  2310. SendTaskMessage($"未找到任务贴合补偿参数", MessageLevel.Alarm);
  2311. await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)2);
  2312. return;
  2313. }
  2314. offset_X = current.ScrewPoints[0].X_Offset;
  2315. offset_Y = current.ScrewPoints[0].Y_Offset;
  2316. offset_U = current.ScrewPoints[0].U_Offset;
  2317. SendTaskMessage($"放料补偿:X={offset_X},Y={offset_Y},U={offset_U}", MessageLevel.Info);
  2318. //利用补偿偏差算出,补偿后的壳体中心,p_new
  2319. ToolCoord toolCoord2 = new ToolCoord();
  2320. toolCoord2.SetTool(offset_X, offset_Y);
  2321. var p_new_Vector = Vector<double>.Build.Dense(new double[] { UpCameraPutResults[0].X, UpCameraPutResults[0].Y });
  2322. var p_new = toolCoord2.GetToolnCoord(p_new_Vector, UpCameraPutResults[0].Z);
  2323. SendTaskMessage($"放料原点:1,{UpCameraPutResults[0].X:F3},{UpCameraPutResults[0].Y:F4},{UpCameraPutResults[0].Z:F4}", MessageLevel.Info);
  2324. SendTaskMessage($"放料补偿点:2,{p_new[0]:F3},{p_new[1]:F4},{UpCameraPutResults[0].Z:F4}", MessageLevel.Info);
  2325. //计算放料角度--带入下相机的角度
  2326. double angle = currentPosition.U - DowmCameraResults[0].Z + UpCameraPutResults[0].Z + offset_U;
  2327. //将产品的中心点坐标转换为Tool0下的坐标
  2328. var point = currentProduct.ScrewPoints[0].Clone();
  2329. //var pn = Vector<double>.Build.Dense(new double[] { UpCameraPutResults[0].X, UpCameraPutResults[0].Y });
  2330. var pn = p_new;
  2331. //var p0 = toolCoord.GetTool0Coord(pn, angle);
  2332. var p0 = DownTool.GetTool0Coord(pn, angle);//下相机拍照tool,动态Tool
  2333. point.X_Position = (float)p0[0];
  2334. point.Y_Position = (float)p0[1];
  2335. point.U_Position = (float)angle;
  2336. PlaceActualCoord = new OpenCvSharp.Point3f(point.X_Position, point.Y_Position, point.U_Position);
  2337. //SendTaskMessage($"点:1,{point.X_Position:F3},{point.Y_Position:F4},{point.U_Position:F4}", MessageLevel.Info);
  2338. SendTaskMessage($"目标点:3,{point.X_Position:F3},{point.Y_Position:F4},{point.U_Position:F4}", MessageLevel.Info);
  2339. await point.WriteToPlcAddressEx2(addressConfig.Out_Screw, plc);//补偿参数提前代入计算,直接发最终点位
  2340. await plc.WriteNodeAsync(addressConfig.Out_WorkNum.Address, (Int16)1);
  2341. await plc.WriteNodeAsync(addressConfig.Out_CameraDone.Address, (Int16)1);
  2342. //原计算方法--20260213
  2343. ////计算放料角度
  2344. //double angle = currentPosition.U - UpCameraPutResults[1].Z + UpCameraPutResults[0].Z;
  2345. ////将产品的中心点坐标转换为Tool0下的坐标
  2346. //var point = currentProduct.ScrewPoints[0].Clone();
  2347. //var pn = Vector<double>.Build.Dense(new double[] { UpCameraPutResults[0].X, UpCameraPutResults[0].Y });
  2348. //var p0 = toolCoord.GetTool0Coord(pn, angle);
  2349. //point.X_Position = (float)p0[0];
  2350. //point.Y_Position = (float)p0[1];
  2351. //point.U_Position = (float)angle;
  2352. //PlaceActualCoord = new OpenCvSharp.Point3f(point.X_Position, point.Y_Position, point.U_Position);
  2353. //SendTaskMessage($"点:1,{point.X_Position:F3},{point.Y_Position:F4},{point.U_Position:F4}", MessageLevel.Info);
  2354. //await point.WriteToPlcAddress(addressConfig.Out_Screw, plc);
  2355. //await plc.WriteNodeAsync(addressConfig.Out_WorkNum.Address, (Int16)1);
  2356. //await plc.WriteNodeAsync(addressConfig.Out_CameraDone.Address, (Int16)1);
  2357. }
  2358. else
  2359. {
  2360. SendTaskMessage($"多相机坐标合并请求已结束或被取消,state={state}", MessageLevel.Alarm);
  2361. await plc.WriteNodeAsync(addressConfig.Out_CameraDone.Address, (Int16)0);
  2362. }
  2363. }
  2364. }
  2365. // 螺丝供料器螺丝递减
  2366. else if (addressConfig.In_PickINC.Address.Contains(tuple.nodeId))
  2367. {
  2368. if (tuple.value is Int16 state)
  2369. {
  2370. if (_LastPickINC != state)
  2371. {
  2372. _LastPickINC = state;
  2373. }
  2374. else
  2375. {
  2376. return;
  2377. }
  2378. if (state != 0)
  2379. {
  2380. SendTaskMessage($"螺丝供料器...", MessageLevel.Info);
  2381. await plc.WriteNodeAsync(addressConfig.In_PickINC.Address, (Int16)0);
  2382. }
  2383. }
  2384. }
  2385. // PLC请求二维码拍照
  2386. else if (addressConfig.In_QrCodePhotoRequest.Address.Contains(tuple.nodeId))
  2387. {
  2388. if (tuple.value is Int16 state)
  2389. {
  2390. if (_LastQrCodePhotoRequest != state)
  2391. {
  2392. _LastQrCodePhotoRequest = state;
  2393. }
  2394. else
  2395. {
  2396. return;
  2397. }
  2398. if (state == 1)
  2399. {
  2400. //相机的编号
  2401. //Int16 cameraIndex = plc.ReadNode<Int16>(addressConfig.In_QrCodeCamera_ScanNum.Address);
  2402. //SendTaskMessage(string.Format("扫码编号", cameraIndex), MessageLevel.Info);
  2403. Guid qrCodeProcedureId = currentProduct.QrCodeProcedureId;
  2404. //视觉流程ID
  2405. var qrCodeProcess = _productService.GetCurrentProductProcedureModelById(qrCodeProcedureId);
  2406. if (qrCodeProcess == null)
  2407. {
  2408. SendTaskMessage($"未找到二维码视觉流程,流程ID: {qrCodeProcedureId}", MessageLevel.Alarm);
  2409. await plc.WriteNodeAsync(addressConfig.Out_QrCodePhotoResault.Address, (Int16)2);
  2410. return;
  2411. }
  2412. SendTaskMessage($"开始执行二维码扫码视觉流程", MessageLevel.Debug);
  2413. var _cts = new CancellationTokenSource();
  2414. await _remoteCommandService.SetLightBeforePhoto(qrCodeProcess);
  2415. var qrCodeResult = _remoteCommandService.ExecutePhoto(1, qrCodeProcess, out CogToolBlockTerminalCollection outputCollection);
  2416. if (qrCodeResult && outputCollection.Contains("QR"))
  2417. {
  2418. PartSN = outputCollection["QR"].Value as string;
  2419. if (!string.IsNullOrEmpty(PartSN))
  2420. {
  2421. await plc.WriteNodeAsync(addressConfig.Out_PartCode.Address, PartSN);
  2422. SendTaskMessage($"二维码扫码成功:{PartSN}", MessageLevel.Info);
  2423. var stationConfig = _configService.GetDeviceInfo(0);
  2424. if (stationConfig.EnableMES)
  2425. {
  2426. (bool isSuccess, string response) = await _mesService.StationGetEx2Async(0, PartSN, "", 2);//询问零件码,是否可以使用
  2427. if (!isSuccess)//如果二维码已使用,则返回false
  2428. {
  2429. await plc.WriteNodeAsync(addressConfig.Out_QrCodePhotoResault.Address, (Int16)3);
  2430. return;
  2431. }
  2432. }
  2433. await plc.WriteNodeAsync(addressConfig.Out_QrCodePhotoResault.Address, (Int16)1);
  2434. }
  2435. else
  2436. {
  2437. PartSN = "";
  2438. SendTaskMessage("二维码扫码失败", MessageLevel.Alarm);
  2439. await plc.WriteNodeAsync(addressConfig.Out_QrCodePhotoResault.Address, (Int16)2);
  2440. return;
  2441. }
  2442. }
  2443. else
  2444. {
  2445. PartSN = "";
  2446. SendTaskMessage("二维码扫码失败", MessageLevel.Alarm);
  2447. await plc.WriteNodeAsync(addressConfig.Out_QrCodePhotoResault.Address, (Int16)2);
  2448. return;
  2449. }
  2450. }
  2451. else
  2452. {
  2453. SendTaskMessage("二维码拍照点位发送信号已关闭", MessageLevel.Alarm);
  2454. await plc.WriteNodeAsync(addressConfig.Out_QrCodePhotoResault.Address, (Int16)0);
  2455. }
  2456. }
  2457. }
  2458. // 抛料计数
  2459. else if (addressConfig.In_Throw.Address.Contains(tuple.nodeId))
  2460. {
  2461. if (tuple.value is bool state)
  2462. {
  2463. if (_LastThrowExe != state)
  2464. {
  2465. _LastThrowExe = state;
  2466. }
  2467. else
  2468. {
  2469. return;
  2470. }
  2471. if (state)
  2472. {
  2473. SendTaskMessage("收到抛料信号", MessageLevel.Info);
  2474. await _systemDatabaseService.RecordThrowNumberAsync(new ThrowRecord()
  2475. {
  2476. DayTime = DateTime.Now,
  2477. ThrowCount = 1
  2478. });
  2479. }
  2480. }
  2481. }
  2482. //切换成自动运行模式
  2483. else if (addressConfig.AutoRunning.Address.Contains(tuple.nodeId))
  2484. {
  2485. if (tuple.value is bool state)
  2486. {
  2487. if (_LastAutoRunning != state)
  2488. {
  2489. _LastAutoRunning = state;
  2490. }
  2491. else
  2492. {
  2493. return;
  2494. }
  2495. if (state)
  2496. {
  2497. SendTaskMessage("收到切换成自动模式", MessageLevel.Info);
  2498. }
  2499. else
  2500. {
  2501. SendTaskMessage("收到关闭自动模式", MessageLevel.Info);
  2502. }
  2503. CurrentRunStatus();
  2504. }
  2505. }
  2506. //切换成维修模式
  2507. else if (addressConfig.ManualMode.Address.Contains(tuple.nodeId))
  2508. {
  2509. if (tuple.value is bool state)
  2510. {
  2511. if (_LastManualMode != state)
  2512. {
  2513. _LastManualMode = state;
  2514. }
  2515. else
  2516. {
  2517. return;
  2518. }
  2519. if (state)
  2520. {
  2521. ClearModeBackground = System.Windows.Media.Brushes.White;
  2522. RunModelBackground = System.Windows.Media.Brushes.White;
  2523. ManualModeBackground = System.Windows.Media.Brushes.Orange;
  2524. SendTaskMessage("收到切换成维修模式", MessageLevel.Info);
  2525. }
  2526. }
  2527. }
  2528. //切换成流线模式
  2529. else if (addressConfig.ClearMode.Address.Contains(tuple.nodeId))
  2530. {
  2531. if (tuple.value is bool state)
  2532. {
  2533. if (_LastClearMode != state)
  2534. {
  2535. _LastClearMode = state;
  2536. }
  2537. else
  2538. {
  2539. return;
  2540. }
  2541. if (state)
  2542. {
  2543. ClearModeBackground = System.Windows.Media.Brushes.Yellow;
  2544. RunModelBackground = System.Windows.Media.Brushes.White;
  2545. ManualModeBackground = System.Windows.Media.Brushes.White;
  2546. SendTaskMessage("收到切换成流线模式", MessageLevel.Info);
  2547. }
  2548. }
  2549. }
  2550. else
  2551. {
  2552. // 过站检查
  2553. for (int i = 0; i < 10; i++)
  2554. {
  2555. if (string.Format(addressConfig.In_MesCheck.Address, i).Contains(tuple.nodeId))
  2556. {
  2557. if (tuple.value is Int16 state)
  2558. {
  2559. if (_LastMesCheck[i] != state)
  2560. {
  2561. _LastMesCheck[i] = state;
  2562. }
  2563. else
  2564. {
  2565. return;
  2566. }
  2567. var code = plc.ReadNode<string>(string.Format(addressConfig.In_Code.Address, i));
  2568. string comp = PartSN;
  2569. SendTaskMessage($"产品sn:{code},辅料码:{comp}", MessageLevel.Info);
  2570. //询问mes
  2571. if (state == 1)
  2572. {
  2573. //获取过站时间
  2574. _LastMesCheckTime[i] = (await _mesService.GetServerTimeAsync(i, code, 2)).Now;
  2575. //获取当前工位的配置
  2576. var stationConfig = _configService.GetDeviceInfo(i);
  2577. if (stationConfig.EnableDoubleMes)
  2578. {
  2579. SendTaskMessage($"收到{i}:{stationConfig.DeviceName}过站检查请求(当站是否生产)(当前需要双MES验证)...", MessageLevel.Debug);
  2580. LogHelper.WriteLogMes($"【mes开始QUERY】");
  2581. SendTaskMessage($"mes开始QUERY", MessageLevel.Info);
  2582. (bool isSuccess, string response) = await _mesService.StationGetExAsync(i, code, PartSN, 2);//询问mes
  2583. if (isSuccess)
  2584. {
  2585. LogHelper.WriteLogMes($"询问mes成功");
  2586. SendTaskMessage($"询问mes成功:{response}", MessageLevel.Debug);
  2587. string ProductMainSN_Temp = "";
  2588. string[] item = response.Split('\n');
  2589. //获取过度件sn
  2590. string snItem = item.FirstOrDefault(f => f.Contains("sn="));
  2591. if (snItem != null)
  2592. {
  2593. ProductMainSN_Temp = snItem.Replace("sn=", "").Trim();
  2594. SendTaskMessage($"过度件sn:{ProductMainSN_Temp}", MessageLevel.Info);
  2595. }
  2596. (bool isSuccess2, string response2) = await _mesService.StationGetAsync(i, ProductMainSN_Temp, PartSN, 2);//询问mes
  2597. if (isSuccess2)
  2598. {
  2599. LogHelper.WriteLogMes($"第二次询问mes成功");
  2600. SendTaskMessage($"第二次询问mes成功:{response2}", MessageLevel.Debug);
  2601. item = response2.Split('\n');
  2602. //获取产品主sn和wo用于后续上传数据使用
  2603. snItem = item.FirstOrDefault(f => f.Contains("sn="));
  2604. if (snItem != null)
  2605. {
  2606. ProductMainSN[i] = snItem.Replace("sn=", "").Trim();
  2607. if (i == 0)
  2608. {
  2609. _productService.CurrentProductCode = ProductMainSN[i];
  2610. //_eventAggregator.GetEvent<DelaySaveImageNotification>().Publish(true);//通知界面延迟保存图片,等待MES查询结果返回,避免图片和数据不同步
  2611. }
  2612. SendTaskMessage($"产品主sn:{ProductMainSN[i]}", MessageLevel.Info);
  2613. }
  2614. string woItem = item.FirstOrDefault(f => f.Contains("wo="));
  2615. if (woItem != null)
  2616. {
  2617. wo[i] = woItem.Replace("wo=", "").Trim();
  2618. SendTaskMessage($"工单信息:{wo[i]}", MessageLevel.Info);
  2619. }
  2620. string modelItem = item.FirstOrDefault(f => f.Contains("model_num="));
  2621. if (modelItem != null)
  2622. {
  2623. model_num[i] = modelItem.Replace("model_num=", "").Trim();
  2624. SendTaskMessage($"model_num:{model_num[i]}", MessageLevel.Info);
  2625. }
  2626. string colorItem = item.FirstOrDefault(f => f.Contains("color="));
  2627. if (colorItem != null)
  2628. {
  2629. color[i] = colorItem.Replace("color=", "").Trim();
  2630. SendTaskMessage($"color:{color[i]}", MessageLevel.Info);
  2631. }
  2632. string ppidItem = item.FirstOrDefault(f => f.Contains("ppid="));
  2633. if (ppidItem != null)
  2634. {
  2635. ppid[i] = ppidItem.Replace("ppid=", "").Trim();
  2636. SendTaskMessage($"ppid:{ppid[i]}", MessageLevel.Info);
  2637. }
  2638. string revisionItem = item.FirstOrDefault(f => f.Contains("stage="));
  2639. if (revisionItem != null)
  2640. {
  2641. revision[i] = revisionItem.Replace("stage=", "").Trim();
  2642. SendTaskMessage($"stage:{revision[i]}", MessageLevel.Info);
  2643. }
  2644. //if (QRcode != camerasn)
  2645. //{
  2646. // SendTaskMessage($"Camera_SN与相机获取sn不一致", MessageLevel.Error);
  2647. // await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)2);
  2648. // return;
  2649. //}
  2650. //切换产品
  2651. if (i == 0)//i=0时才会切换产品
  2652. {
  2653. var q = await _systemDatabaseService.QueryProductFromMesAsync(model_num[i], color[i]);
  2654. if (q != null && !string.IsNullOrEmpty(q.ProductName))
  2655. {
  2656. ProductList = new ObservableCollection<ProductModel>(_productService.GetAllProducts());
  2657. Guid productId = Guid.Empty;
  2658. foreach (var itt in ProductList)
  2659. {
  2660. if (itt.Name == q.ProductName)
  2661. {
  2662. productId = itt.ID;
  2663. break;
  2664. }
  2665. }
  2666. if (currentProduct.ID != productId)
  2667. {
  2668. //切换产品
  2669. _productService.LoadProduct(productId);
  2670. _productService.SetCurrentProduct(productId);
  2671. await WriteParameterToPlcExAsync();
  2672. SendTaskMessage($"切换产品成功", MessageLevel.Debug);
  2673. }
  2674. }
  2675. }
  2676. //产品发生改变并且供料器发生了变化时,需要通知PLC进行取料更改
  2677. await plc.WriteNodeAsync(addressConfig.Out_ScrewFeederIsChanged.Address, (Int16)1); //不发生改变
  2678. await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)1);
  2679. }
  2680. else
  2681. {
  2682. LogHelper.WriteLogMes($"第二次询问mes失败");
  2683. SendTaskMessage($"第二次询问mes失败:{response2}", MessageLevel.Error);
  2684. //产品发生改变并且供料器发生了变化时,需要通知PLC进行取料更改
  2685. await plc.WriteNodeAsync(addressConfig.Out_ScrewFeederIsChanged.Address, (Int16)1); //不发生改变
  2686. await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)2);
  2687. }
  2688. }
  2689. else
  2690. {
  2691. LogHelper.WriteLogMes($"询问mes失败");
  2692. SendTaskMessage($"询问mes失败:{response}", MessageLevel.Error);
  2693. //产品发生改变并且供料器发生了变化时,需要通知PLC进行取料更改
  2694. await plc.WriteNodeAsync(addressConfig.Out_ScrewFeederIsChanged.Address, (Int16)1); //不发生改变
  2695. await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)2);
  2696. }
  2697. LogHelper.WriteLogMes($"【mes结束QUERY】");
  2698. SendTaskMessage($"mes结束QUERY", MessageLevel.Info);
  2699. }
  2700. else if (stationConfig.EnableMES)
  2701. {
  2702. SendTaskMessage($"收到{i}:{stationConfig.DeviceName}过站检查请求(当站是否生产)(当前不需要双MES验证)...", MessageLevel.Debug);
  2703. LogHelper.WriteLogMes($"【mes开始QUERY】");
  2704. SendTaskMessage($"mes开始QUERY", MessageLevel.Info);
  2705. (bool isSuccess, string response) = await _mesService.StationGetAsync(i, code, PartSN, 2);//询问mes
  2706. if (isSuccess)
  2707. {
  2708. string[] item = response.Split('\n');
  2709. //获取产品主sn和wo用于后续上传数据使用
  2710. string snItem = item.FirstOrDefault(f => f.Contains("sn="));
  2711. if (snItem != null)
  2712. {
  2713. ProductMainSN[i] = snItem.Replace("sn=", "").Trim();
  2714. if (i == 0)
  2715. {
  2716. _productService.CurrentProductCode = ProductMainSN[i];
  2717. //_eventAggregator.GetEvent<DelaySaveImageNotification>().Publish(true);//通知界面延迟保存图片,等待MES查询结果返回,避免图片和数据不同步
  2718. }
  2719. SendTaskMessage($"产品主sn:{ProductMainSN[i]}", MessageLevel.Info);
  2720. }
  2721. string woItem = item.FirstOrDefault(f => f.Contains("wo="));
  2722. if (woItem != null)
  2723. {
  2724. wo[i] = woItem.Replace("wo=", "").Trim();
  2725. SendTaskMessage($"工单信息:{wo[i]}", MessageLevel.Info);
  2726. }
  2727. string modelItem = item.FirstOrDefault(f => f.Contains("model_num="));
  2728. if (modelItem != null)
  2729. {
  2730. model_num[i] = modelItem.Replace("model_num=", "").Trim();
  2731. SendTaskMessage($"model_num:{model_num[i]}", MessageLevel.Info);
  2732. }
  2733. string colorItem = item.FirstOrDefault(f => f.Contains("color="));
  2734. if (colorItem != null)
  2735. {
  2736. color[i] = colorItem.Replace("color=", "").Trim();
  2737. SendTaskMessage($"color:{color[i]}", MessageLevel.Info);
  2738. }
  2739. string ppidItem = item.FirstOrDefault(f => f.Contains("ppid="));
  2740. if (ppidItem != null)
  2741. {
  2742. ppid[i] = ppidItem.Replace("ppid=", "").Trim();
  2743. SendTaskMessage($"ppid:{ppid[i]}", MessageLevel.Info);
  2744. }
  2745. string revisionItem = item.FirstOrDefault(f => f.Contains("stage="));
  2746. if (revisionItem != null)
  2747. {
  2748. revision[i] = revisionItem.Replace("stage=", "").Trim();
  2749. SendTaskMessage($"stage:{revision[i]}", MessageLevel.Info);
  2750. }
  2751. LogHelper.WriteLogMes($"询问mes成功");
  2752. SendTaskMessage($"询问mes成功:{response}", MessageLevel.Debug);
  2753. //切换产品
  2754. if (i == 0)//i=0时才会切换产品
  2755. {
  2756. var q = await _systemDatabaseService.QueryProductFromMesAsync(model_num[i], color[i]);
  2757. if (q != null && !string.IsNullOrEmpty(q.ProductName))
  2758. {
  2759. ProductList = new ObservableCollection<ProductModel>(_productService.GetAllProducts());
  2760. Guid productId = Guid.Empty;
  2761. foreach (var itt in ProductList)
  2762. {
  2763. if (itt.Name == q.ProductName)
  2764. {
  2765. productId = itt.ID;
  2766. break;
  2767. }
  2768. }
  2769. if (currentProduct.ID != productId)
  2770. {
  2771. //切换产品
  2772. _productService.LoadProduct(productId);
  2773. _productService.SetCurrentProduct(productId);
  2774. await WriteParameterToPlcExAsync();
  2775. SendTaskMessage($"切换产品成功", MessageLevel.Debug);
  2776. }
  2777. }
  2778. }
  2779. //产品发生改变并且供料器发生了变化时,需要通知PLC进行取料更改
  2780. await plc.WriteNodeAsync(addressConfig.Out_ScrewFeederIsChanged.Address, (Int16)1); //不发生改变
  2781. await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)1);
  2782. }
  2783. else
  2784. {
  2785. LogHelper.WriteLogMes($"询问mes失败");
  2786. SendTaskMessage($"询问mes失败:{response}", MessageLevel.Error);
  2787. //产品发生改变并且供料器发生了变化时,需要通知PLC进行取料更改
  2788. await plc.WriteNodeAsync(addressConfig.Out_ScrewFeederIsChanged.Address, (Int16)1); //不发生改变
  2789. await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)2);
  2790. }
  2791. LogHelper.WriteLogMes($"【mes结束QUERY】");
  2792. SendTaskMessage($"mes结束QUERY", MessageLevel.Info);
  2793. }
  2794. else
  2795. {
  2796. ProductMainSN[i] = DateTime.Now.ToString();
  2797. if (i == 0)
  2798. {
  2799. _productService.CurrentProductCode = ProductMainSN[i];
  2800. //_eventAggregator.GetEvent<DelaySaveImageNotification>().Publish(true);//通知界面延迟保存图片,等待MES查询结果返回,避免图片和数据不同步
  2801. }
  2802. LogHelper.WriteLogMes($"【未启用mes】");
  2803. SendTaskMessage($"未启用mes", MessageLevel.Info);
  2804. await plc.WriteNodeAsync(addressConfig.Out_ScrewFeederIsChanged.Address, (Int16)1); //不发生改变
  2805. await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)1);
  2806. }
  2807. }
  2808. // 上传本站数据
  2809. else if (state == 2)
  2810. {
  2811. DFC.Clear();//CTQ数据集合
  2812. //获取当前工位的配置
  2813. var stationConfig = _configService.GetDeviceInfo(i);
  2814. SendTaskMessage($"收到{i}:{stationConfig.DeviceName}上传本站数据请求...", MessageLevel.Debug);
  2815. //从服务器中获取当前时间
  2816. var serverTimeResult = await _mesService.GetServerTimeAsync(i, ProductMainSN[i], 2);
  2817. double ct = serverTimeResult.Now.Subtract(_LastMesCheckTime[i]).TotalSeconds;
  2818. if (i == 0)
  2819. {
  2820. CTtime = ct;
  2821. _eventAggregator.GetEvent<DelaySaveImageNotification>().Publish(true);//通知界面延迟保存图片,等待MES查询结果返回,避免图片和数据不同步
  2822. }
  2823. //从PLC获取零件编号
  2824. PartSN = plc.ReadNode<string>(addressConfig.In_PartCode.Address);
  2825. comp = PartSN;
  2826. //读取产品结果
  2827. Int16 resultIndex = plc.ReadNode<Int16>(string.Format(addressConfig.In_Result.Address, i));
  2828. //resultIndex = 1;
  2829. #region 组装结果显示列表信息
  2830. AssemblyRecord assemblyRecord = new AssemblyRecord();
  2831. assemblyRecord.ProductSN = code;
  2832. assemblyRecord.RecipeName = currentProduct.Name;
  2833. assemblyRecord.PartSN = PartSN;
  2834. assemblyRecord.AssemblyDurationSeconds = ct;
  2835. assemblyRecord.AssemblyPressure = currentPressure[i];
  2836. assemblyRecord.DeviceId = i;
  2837. assemblyRecord.DeviceName = stationConfig.DeviceName;
  2838. assemblyRecord.OperatorName = _systemDatabaseService.GetCurrentUser().UserName;
  2839. assemblyRecord.Success = resultIndex == 1 ? true : false;
  2840. assemblyRecord.DownCameraCoord1 = "";
  2841. assemblyRecord.DownCameraCoord2 = "";
  2842. assemblyRecord.DownCameraCoord3 = "";
  2843. assemblyRecord.DownCameraCoord4 = "";
  2844. assemblyRecord.PickMoveCameraCoord = "";
  2845. assemblyRecord.PlaceMoveCameraCoord1 = "";
  2846. assemblyRecord.PlaceMoveCameraCoord2 = "";
  2847. assemblyRecord.PlaceMoveCameraCoord3 = "";
  2848. assemblyRecord.PlaceMoveCameraCoord4 = "";
  2849. assemblyRecord.PlaceActualCoord = "";
  2850. //目前只记录当前站
  2851. if (i == 0)
  2852. {
  2853. await _systemDatabaseService.RecordProductionAsync(new ProductionRecord()
  2854. {
  2855. ProductName = currentProduct.Name,
  2856. ProductCode = code,
  2857. DeviceId = i,
  2858. DeviceName = "",
  2859. Category = resultIndex == 1 ? "OK" : "NG",
  2860. Quantity = 1,
  2861. Remark = $"TotalSeconds:{ct.ToString("F2")}",
  2862. UserName = _systemDatabaseService.GetCurrentUser().UserName,
  2863. });
  2864. assemblyRecord.PlaceMoveCameraCoord1 = $"{UpCameraPutResults[0].X:F3},{UpCameraPutResults[0].Y:F3},{UpCameraPutResults[0].Z:F3}";
  2865. assemblyRecord.PlaceMoveCameraCoord2 = $"{UpCameraPutResults[1].X:F3},{UpCameraPutResults[1].Y:F3},{UpCameraPutResults[1].Z:F3}";
  2866. assemblyRecord.PlaceActualCoord = $"{PlaceActualCoord.X:F3},{PlaceActualCoord.Y:F3},{PlaceActualCoord.Z:F3}";
  2867. App.Current.Dispatcher.Invoke(() =>
  2868. {
  2869. _eventAggregator.GetEvent<AssemblyFinishNotification>().Publish(assemblyRecord);
  2870. });
  2871. }
  2872. await _systemDatabaseService.RecordAssemblyAsync(assemblyRecord);
  2873. #endregion
  2874. LogHelper.WriteLogMes($"【mes开始ADD】");
  2875. SendTaskMessage($"mes开始ADD", MessageLevel.Info);
  2876. Dictionary<string, string> data = new Dictionary<string, string>();
  2877. string press = ""; string presstime = ""; string assypress = "";
  2878. if (i == 0)
  2879. {
  2880. try
  2881. {
  2882. assypress = currentPressure[0].ToString("F2");
  2883. if (currentPressure[0] >= currentProduct.GlobalParamEx.AssemblyPressure.PressureDownLimit && currentPressure[0] <= currentProduct.GlobalParamEx.AssemblyPressure.PressureUpLimit)
  2884. {
  2885. //resultIndex = 1;
  2886. }
  2887. else
  2888. {
  2889. if (!fileresult.Contains("AssyPressure1_File"))
  2890. {
  2891. fileresult.Add($"AssyPressure1_File");
  2892. }
  2893. //resultIndex = 2;
  2894. }
  2895. data = gapDict.Copy();
  2896. data.Add("AssyPressure1", assypress);
  2897. SendTaskMessage($"Gap1:{gapDict["Gap1"]},Gap2:{gapDict["Gap2"]},Gap3:{gapDict["Gap3"]},Gap4:{gapDict["Gap4"]},组装压力:{assypress}", MessageLevel.Info);
  2898. }
  2899. catch (Exception ex)
  2900. {
  2901. }
  2902. #region CSV数据
  2903. bool assyPresRes;
  2904. if (currentPressure[0] >= currentProduct.GlobalParamEx.AssemblyPressure.PressureDownLimit && currentPressure[0] <= currentProduct.GlobalParamEx.AssemblyPressure.PressureUpLimit)
  2905. {
  2906. assyPresRes = true;
  2907. }
  2908. else assyPresRes = false;
  2909. DFC.Clear();
  2910. DFC2.Clear();
  2911. string upLimitValue = "NA", downLimitValue = "NA", midValue = "NA", saveppid = "NA";
  2912. if (stationConfig.SaveCsvPPID) saveppid = ppid[i];
  2913. foreach (var item in gapDict)
  2914. {
  2915. switch (item.Key)
  2916. {
  2917. case "Gap1":
  2918. upLimitValue = currentProduct.GlobalParamEx.Gap1UpLimit.ToString("F3");
  2919. downLimitValue = currentProduct.GlobalParamEx.Gap1DownLimit.ToString("F3");
  2920. midValue = ((currentProduct.GlobalParamEx.Gap1UpLimit + currentProduct.GlobalParamEx.Gap1DownLimit) / 2).ToString("F3");
  2921. if (Convert.ToDouble(item.Value) > Convert.ToDouble(upLimitValue) && Convert.ToDouble(item.Value) < Convert.ToDouble(downLimitValue) && !fileresult.Contains("Gap1_File"))
  2922. {
  2923. fileresult.Add($"Gap1_File");
  2924. }
  2925. break;
  2926. case "Gap2":
  2927. upLimitValue = currentProduct.GlobalParamEx.Gap2UpLimit.ToString("F3");
  2928. downLimitValue = currentProduct.GlobalParamEx.Gap2DownLimit.ToString("F3");
  2929. midValue = ((currentProduct.GlobalParamEx.Gap2UpLimit + currentProduct.GlobalParamEx.Gap2DownLimit) / 2).ToString("F3");
  2930. if (Convert.ToDouble(item.Value) > Convert.ToDouble(upLimitValue) && Convert.ToDouble(item.Value) < Convert.ToDouble(downLimitValue) && !fileresult.Contains("Gap2_File"))
  2931. {
  2932. fileresult.Add($"Gap2_File");
  2933. }
  2934. break;
  2935. case "Gap3":
  2936. upLimitValue = currentProduct.GlobalParamEx.Gap3UpLimit.ToString("F3");
  2937. downLimitValue = currentProduct.GlobalParamEx.Gap3DownLimit.ToString("F3");
  2938. midValue = ((currentProduct.GlobalParamEx.Gap3UpLimit + currentProduct.GlobalParamEx.Gap3DownLimit) / 2).ToString("F3");
  2939. if (Convert.ToDouble(item.Value) > Convert.ToDouble(upLimitValue) && Convert.ToDouble(item.Value) < Convert.ToDouble(downLimitValue) && !fileresult.Contains("Gap3_File"))
  2940. {
  2941. fileresult.Add($"Gap3_File");
  2942. }
  2943. break;
  2944. case "Gap4":
  2945. upLimitValue = currentProduct.GlobalParamEx.Gap4UpLimit.ToString("F3");
  2946. downLimitValue = currentProduct.GlobalParamEx.Gap4DownLimit.ToString("F3");
  2947. midValue = ((currentProduct.GlobalParamEx.Gap4UpLimit + currentProduct.GlobalParamEx.Gap4DownLimit) / 2).ToString("F3");
  2948. if (Convert.ToDouble(item.Value) > Convert.ToDouble(upLimitValue) && Convert.ToDouble(item.Value) < Convert.ToDouble(downLimitValue) && !fileresult.Contains("Gap4_File"))
  2949. {
  2950. fileresult.Add($"Gap4_File");
  2951. }
  2952. break;
  2953. case "Gap5":
  2954. upLimitValue = currentProduct.GlobalParamEx.Gap5UpLimit.ToString("F3");
  2955. downLimitValue = currentProduct.GlobalParamEx.Gap5DownLimit.ToString("F3");
  2956. midValue = ((currentProduct.GlobalParamEx.Gap5UpLimit + currentProduct.GlobalParamEx.Gap5DownLimit) / 2).ToString("F3");
  2957. if (Convert.ToDouble(item.Value) > Convert.ToDouble(upLimitValue) && Convert.ToDouble(item.Value) < Convert.ToDouble(downLimitValue) && !fileresult.Contains("Gap5_File"))
  2958. {
  2959. fileresult.Add($"Gap5_File");
  2960. }
  2961. break;
  2962. case "Gap6":
  2963. upLimitValue = currentProduct.GlobalParamEx.Gap6UpLimit.ToString("F3");
  2964. downLimitValue = currentProduct.GlobalParamEx.Gap6DownLimit.ToString("F3");
  2965. midValue = ((currentProduct.GlobalParamEx.Gap6UpLimit + currentProduct.GlobalParamEx.Gap6DownLimit) / 2).ToString("F3");
  2966. if (Convert.ToDouble(item.Value) > Convert.ToDouble(upLimitValue) && Convert.ToDouble(item.Value) < Convert.ToDouble(downLimitValue) && !fileresult.Contains("Gap6_File"))
  2967. {
  2968. fileresult.Add($"Gap6_File");
  2969. }
  2970. break;
  2971. }
  2972. DFC.Add(new DataFormCtq
  2973. {
  2974. PPID = saveppid,
  2975. PROCESS_NAME = stationConfig.DataInfo.PROCESS_NAME,
  2976. PROCESS_START_TIME = _LastMesCheckTime[i].ToString("yyyy-MM-ddTHH:mm:ss") + "+08:00",
  2977. CTQ_NAME = item.Key,
  2978. CTQ_VALUE = item.Value,
  2979. CTQ_LSL = downLimitValue,
  2980. CTQ_USL = upLimitValue,
  2981. PROCESS_END_TIME = serverTimeResult.Now.ToString("yyyy-MM-ddTHH:mm:ss") + "+08:00",
  2982. SUPPLIER_NAME = stationConfig.DataInfo.SUPPLIER_NAME,
  2983. COMMODITY_TYPE = stationConfig.DataInfo.COMMODITY_TYPE,
  2984. REVISION = stationConfig.DataInfo.REVISION + "_" + revision[i],
  2985. WO = wo[i],
  2986. MFG_LOT = stationConfig.DataInfo.MFG_LOT,
  2987. MFG_ASSY_LINE = stationConfig.DataInfo.MFG_ASSY_LINE,
  2988. PROCESS_OUTCOME = "Pass",
  2989. PROCESS_MACHINE_ID = stationConfig.DataInfo.PROCESS_MACHINE_ID,
  2990. CTQ_UNIT_OF_MEASURE = "mm",
  2991. ERROR_MESSAGE = "NA",
  2992. PD_ATTR_01 = midValue,
  2993. PD_ATTR_02 = ProductMainSN[i],
  2994. PD_ATTR_03 = PartSN,
  2995. PD_ATTR_04 = code,
  2996. PD_ATTR_05 = "NA",
  2997. });
  2998. }
  2999. midValue = ((currentProduct.GlobalParamEx.AssemblyPressure.PressureDownLimit + currentProduct.GlobalParamEx.AssemblyPressure.PressureUpLimit) / 2).ToString("F3");
  3000. DFC.Add(new DataFormCtq
  3001. {
  3002. PPID = saveppid,
  3003. PROCESS_NAME = stationConfig.DataInfo.PROCESS_NAME,
  3004. PROCESS_START_TIME = _LastMesCheckTime[i].ToString("yyyy-MM-ddTHH:mm:ss") + "+08:00",
  3005. CTQ_NAME = "Assy_Pressure",
  3006. CTQ_VALUE = assypress,
  3007. CTQ_LSL = currentProduct.GlobalParamEx.AssemblyPressure.PressureDownLimit.ToString("F3"),
  3008. CTQ_USL = currentProduct.GlobalParamEx.AssemblyPressure.PressureUpLimit.ToString("F3"),
  3009. PROCESS_END_TIME = serverTimeResult.Now.ToString("yyyy-MM-ddTHH:mm:ss") + "+08:00",
  3010. SUPPLIER_NAME = stationConfig.DataInfo.SUPPLIER_NAME,
  3011. COMMODITY_TYPE = stationConfig.DataInfo.COMMODITY_TYPE,
  3012. REVISION = stationConfig.DataInfo.REVISION + "_" + revision[i],
  3013. WO = wo[i],
  3014. MFG_LOT = stationConfig.DataInfo.MFG_LOT,
  3015. MFG_ASSY_LINE = stationConfig.DataInfo.MFG_ASSY_LINE,
  3016. PROCESS_OUTCOME = assyPresRes ? "Pass" : "Fail",
  3017. PROCESS_MACHINE_ID = stationConfig.DataInfo.PROCESS_MACHINE_ID,
  3018. CTQ_UNIT_OF_MEASURE = "kg",
  3019. ERROR_MESSAGE = assyPresRes ? "NA" : "Assy_Pressure OOS",
  3020. PD_ATTR_01 = midValue,
  3021. PD_ATTR_02 = ProductMainSN[i],
  3022. PD_ATTR_03 = PartSN,
  3023. PD_ATTR_04 = code,
  3024. PD_ATTR_05 = "NA",
  3025. });
  3026. try
  3027. {
  3028. //SendTaskMessage($"左1:{DISDict["LeftDis1"]},下1:{DISDict["DownDis1"]},左2:{DISDict["LeftDis2"]},上1:{DISDict["UpDis1"]},右1:{DISDict["RightDis1"]},下2:{DISDict["DownDis2"]},右2:{DISDict["RightDis2"]},上2:{DISDict["UpDis2"]}", MessageLevel.Info);
  3029. if (i == 0)
  3030. {
  3031. DFC2.Add(new DataFormCtq
  3032. {
  3033. PPID = saveppid,
  3034. PROCESS_NAME = ProductMainSN[i],
  3035. PROCESS_START_TIME = _LastMesCheckTime[i].ToString("yyyy-MM-ddTHH:mm:ss") + "+08:00",
  3036. CTQ_NAME = dis[0],//长1
  3037. CTQ_VALUE = dis[1],//宽1
  3038. CTQ_LSL = dis[2],//长2
  3039. CTQ_USL = dis[3],//宽2
  3040. PROCESS_END_TIME = serverTimeResult.Now.ToString("yyyy-MM-ddTHH:mm:ss") + "+08:00",
  3041. SUPPLIER_NAME = stationConfig.DeviceName,
  3042. COMMODITY_TYPE = code,
  3043. REVISION = dis[5],//屏幕长度
  3044. WO = "",//
  3045. MFG_LOT = stationConfig.DataInfo.MFG_LOT,//
  3046. MFG_ASSY_LINE = stationConfig.DataInfo.MFG_ASSY_LINE,
  3047. PROCESS_OUTCOME = "Pass",
  3048. //PROCESS_MACHINE_ID = DISDict["LeftDis1"],//左1
  3049. //CTQ_UNIT_OF_MEASURE = DISDict["DownDis1"],//下1
  3050. //ERROR_MESSAGE = DISDict["LeftDis2"],///左2
  3051. //PD_ATTR_01 = DISDict["UpDis1"],//上1
  3052. //PD_ATTR_02 = DISDict["RightDis1"],//右1
  3053. //PD_ATTR_03 = DISDict["DownDis2"],//下2
  3054. //PD_ATTR_04 = DISDict["RightDis2"],//右2
  3055. //PD_ATTR_05 = DISDict["UpDis2"],//上2
  3056. });
  3057. }
  3058. bool recordResult2 = RecordStationResult2(i, DFC2, ProductMainSN[i], serverTimeResult.Now.Subtract(_LastMesCheckTime[i]).TotalSeconds, _LastMesCheckTime[i], stationConfig.SaveCsvPath);
  3059. if (recordResult2)
  3060. {
  3061. SendTaskMessage("保存外壳屏幕数据成功", MessageLevel.Debug);
  3062. }
  3063. else
  3064. {
  3065. SendTaskMessage("保存外壳屏幕数据失败", MessageLevel.Alarm);
  3066. }
  3067. }
  3068. catch
  3069. {
  3070. SendTaskMessage("保存外壳屏幕数据出错", MessageLevel.Error);
  3071. }
  3072. #endregion
  3073. }
  3074. else if (i == 1)
  3075. {
  3076. try
  3077. {
  3078. press = currentPressure[1].ToString("F2");
  3079. if (currentPressure[1] >= currentProduct.GlobalParamEx.DownPressure.PressureDownLimit && currentPressure[1] <= currentProduct.GlobalParamEx.DownPressure.PressureUpLimit)
  3080. {
  3081. //resultIndex = 1;
  3082. }
  3083. else
  3084. {
  3085. if (!fileresult.Contains("Pressure1_File"))
  3086. {
  3087. fileresult.Add($"Pressure1_File");
  3088. }
  3089. // resultIndex = 2;
  3090. }
  3091. }
  3092. catch (Exception ex)
  3093. {
  3094. }
  3095. presstime = currentProduct.GlobalParamEx.DownPressure.PressureTime.ToString();
  3096. data.Add("Pressure1", press);
  3097. data.Add("PressureTime1", presstime);
  3098. SendTaskMessage($"保压压力:{press},保压时间:{presstime}", MessageLevel.Info);
  3099. #region CSV数据
  3100. bool presRes;
  3101. if (currentPressure[1] >= currentProduct.GlobalParamEx.DownPressure.PressureDownLimit && currentPressure[1] <= currentProduct.GlobalParamEx.DownPressure.PressureUpLimit)
  3102. {
  3103. presRes = true;
  3104. }
  3105. else presRes = false;
  3106. DFC.Clear();
  3107. string midValue = "NA", saveppid = "NA";
  3108. midValue = ((currentProduct.GlobalParamEx.DownPressure.PressureDownLimit + currentProduct.GlobalParamEx.DownPressure.PressureUpLimit) / 2).ToString("F3");
  3109. if (stationConfig.SaveCsvPPID) saveppid = ppid[i];
  3110. DFC.Add(new DataFormCtq
  3111. {
  3112. PPID = saveppid,
  3113. PROCESS_NAME = stationConfig.DataInfo.PROCESS_NAME,
  3114. PROCESS_START_TIME = _LastMesCheckTime[i].ToString("yyyy-MM-ddTHH:mm:ss.fffffff") + "+08:00",
  3115. CTQ_NAME = "Pressure",
  3116. CTQ_VALUE = press,
  3117. CTQ_LSL = currentProduct.GlobalParamEx.DownPressure.PressureDownLimit.ToString("F3"),
  3118. CTQ_USL = currentProduct.GlobalParamEx.DownPressure.PressureUpLimit.ToString("F3"),
  3119. PROCESS_END_TIME = serverTimeResult.Now.ToString("yyyy-MM-ddTHH:mm:ss.fffffff") + "+08:00",
  3120. SUPPLIER_NAME = stationConfig.DataInfo.SUPPLIER_NAME,
  3121. COMMODITY_TYPE = stationConfig.DataInfo.COMMODITY_TYPE,
  3122. REVISION = stationConfig.DataInfo.REVISION + "_" + revision[i],
  3123. WO = wo[i],
  3124. MFG_LOT = stationConfig.DataInfo.MFG_LOT,
  3125. MFG_ASSY_LINE = stationConfig.DataInfo.MFG_ASSY_LINE,
  3126. PROCESS_OUTCOME = presRes ? "Pass" : "Fail",
  3127. PROCESS_MACHINE_ID = stationConfig.DataInfo.PROCESS_MACHINE_ID,
  3128. CTQ_UNIT_OF_MEASURE = "kg",
  3129. ERROR_MESSAGE = presRes ? "NA" : "Pressure OOS",
  3130. PD_ATTR_01 = midValue,
  3131. PD_ATTR_02 = ProductMainSN[i],
  3132. PD_ATTR_03 = "NA",
  3133. PD_ATTR_04 = code,
  3134. PD_ATTR_05 = "NA",
  3135. });
  3136. midValue = ((currentProduct.GlobalParamEx.DownPressure.PressureTime + currentProduct.GlobalParamEx.DownPressure.PressureTime) / 2).ToString("F3");
  3137. DFC.Add(new DataFormCtq
  3138. {
  3139. PPID = saveppid,
  3140. PROCESS_NAME = stationConfig.DataInfo.PROCESS_NAME,
  3141. PROCESS_START_TIME = _LastMesCheckTime[i].ToString("yyyy-MM-ddTHH:mm:ss.fffffff") + "+08:00",
  3142. CTQ_NAME = "Pressure_time",
  3143. CTQ_VALUE = Math.Round(double.Parse(presstime), 3).ToString(),
  3144. CTQ_LSL = currentProduct.GlobalParamEx.DownPressure.PressureTime.ToString("F3"),
  3145. CTQ_USL = currentProduct.GlobalParamEx.DownPressure.PressureTime.ToString("F3"),
  3146. PROCESS_END_TIME = serverTimeResult.Now.ToString("yyyy-MM-ddTHH:mm:ss.fffffff") + "+08:00",
  3147. SUPPLIER_NAME = stationConfig.DataInfo.SUPPLIER_NAME,
  3148. COMMODITY_TYPE = stationConfig.DataInfo.COMMODITY_TYPE,
  3149. REVISION = stationConfig.DataInfo.REVISION + "_" + revision[i],
  3150. WO = stationConfig.DataInfo.WO,
  3151. MFG_LOT = stationConfig.DataInfo.MFG_LOT,
  3152. MFG_ASSY_LINE = stationConfig.DataInfo.MFG_ASSY_LINE,
  3153. PROCESS_OUTCOME = "Pass",
  3154. PROCESS_MACHINE_ID = stationConfig.DataInfo.PROCESS_MACHINE_ID,
  3155. CTQ_UNIT_OF_MEASURE = "s",
  3156. ERROR_MESSAGE = "NA",
  3157. PD_ATTR_01 = midValue,
  3158. PD_ATTR_02 = ProductMainSN[i],
  3159. PD_ATTR_03 = "NA",
  3160. PD_ATTR_04 = code,
  3161. PD_ATTR_05 = "NA",
  3162. });
  3163. #endregion
  3164. }
  3165. //gap-复检gap参数, press-保压站实际压力值, presstime-保压站保压时间, assypress-组装站实际压力值
  3166. (bool isSuccess, string response) = await _mesService.SubmitGetAsync(i, ProductMainSN[i], comp, resultIndex == 1 ? true : false, data, fileresult, _LastMesCheckTime[i], serverTimeResult.Now);
  3167. if (!stationConfig.EnableMES || isSuccess)
  3168. {
  3169. LogHelper.WriteLogMes($"上传mes成功");
  3170. SendTaskMessage($"上传mes成功", MessageLevel.Debug);
  3171. }
  3172. else
  3173. {
  3174. LogHelper.WriteLogMes($"上传mes失败");
  3175. SendTaskMessage($"上传mes失败", MessageLevel.Error);
  3176. }
  3177. LogHelper.WriteLogMes($"【mes结束ADD】");
  3178. SendTaskMessage($"mes结束ADD", MessageLevel.Info);
  3179. await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)3);
  3180. //是否保存当前的产品记录至CSV?
  3181. if (stationConfig.SaveCsv)
  3182. {
  3183. //判断锁付路径字符串是否为空,如果不为空则保存,否则不保存
  3184. if (!string.IsNullOrEmpty(stationConfig.SaveCsvPath))
  3185. {
  3186. bool recordResult = RecordStationResult(i, DFC, ProductMainSN[i], serverTimeResult.Now.Subtract(_LastMesCheckTime[i]).TotalSeconds, _LastMesCheckTime[i], stationConfig.SaveCsvPath);
  3187. if (recordResult)
  3188. {
  3189. SendTaskMessage($"{i}:{stationConfig.DeviceName}产品记录保存成功", MessageLevel.Debug);
  3190. //上传数据
  3191. try
  3192. {
  3193. if (stationConfig.EnableMES)
  3194. {
  3195. DateTime dt = DateTime.Now;
  3196. string filename = $"{ProductMainSN[i]}_{dt.ToString("yyyyMMddHHmmss")}";
  3197. string csvfilename = $"{i}_{stationConfig.DeviceName}_{dt.ToString("yyyy-MM-dd")}";
  3198. var device = _configService.GetDeviceInfo(i);
  3199. var systemConfig = _configService.GetSystemConfiguration();
  3200. var pressureSensorConfig = systemConfig.PressureSensorConfig.FirstOrDefault();
  3201. (bool isSuc, string resp) = await _mesService.SendFtpFileAsync(ProductMainSN[i], filename, device);
  3202. if (isSuc)
  3203. {
  3204. LogHelper.WriteLogMes($"上传数据成功");
  3205. SendTaskMessage($"上传数据成功", MessageLevel.Debug);
  3206. string filePath = $"D:\\image\\Recored\\{dt.ToString("yyyy-MM")}\\{dt.ToString("MM-dd")}\\{currentProduct.Name}\\{ProductMainSN[i]}";
  3207. if (!Directory.Exists(filePath))
  3208. {
  3209. Directory.CreateDirectory(filePath);
  3210. }
  3211. //复制csv
  3212. string source_csv = $"{stationConfig.SaveCsvPath}\\{dt.ToString("yyyy-MM")}\\{i}_{stationConfig.DeviceName}_{dt.ToString("yyyy-MM-dd")}.csv";
  3213. string target_csv = Path.Combine(filePath, Path.GetFileName(source_csv)); ;
  3214. File.Copy(source_csv, target_csv, true);
  3215. //复制log
  3216. string source_log = $"..\\Log\\LogInfo\\{dt.ToString("yyyy-MM")}\\{dt.ToString("yyyy-MM-dd")}.txt";
  3217. string target_log = Path.Combine(filePath, Path.GetFileName(source_log));
  3218. File.Copy(source_log, target_log, true);
  3219. //判断是否保存锁付曲线
  3220. if (pressureSensorConfig.IsSavePressureCurve && i != 0)
  3221. {
  3222. string target_Directory = filePath + "\\压力曲线\\";
  3223. string source_Torque_Image = pressureSensorConfig.PressureCurvePath + $"\\{dt.ToString("yyyy-MM")}\\{dt.ToString("yyyy-MM-dd")}\\{currentProduct.Name}\\{ProductMainSN[i]}";
  3224. var col = Directory.GetFiles(source_Torque_Image);
  3225. try
  3226. {
  3227. Directory.CreateDirectory(target_Directory);
  3228. foreach (var item in col)
  3229. {
  3230. string target_Torque = target_Directory + item.Replace(source_Torque_Image, "");
  3231. File.Copy(item, target_Torque, true);
  3232. SendTaskMessage($"曲线图{i}保存成功", MessageLevel.Debug);
  3233. }
  3234. }
  3235. catch (Exception)
  3236. {
  3237. SendTaskMessage($"扭力曲线图读取失败", MessageLevel.Error);
  3238. }
  3239. }
  3240. CompressionImage(filePath, filename, device.Station, device.FixtureId);
  3241. //上传csv
  3242. bool isAfter20 = dt.Hour >= 20;
  3243. //判断是否是新的一天
  3244. bool isNewDay = dt.Date != _lastCompressDate.Date;
  3245. //当天还没执行过压缩
  3246. if (isAfter20 && isNewDay && !_hasCompressedToday)
  3247. {
  3248. // 执行压缩
  3249. CompressionSingleCsv(source_csv, csvfilename, device.Station, device.FixtureId);
  3250. _lastCompressDate = dt;
  3251. _hasCompressedToday = true;
  3252. LogHelper.WriteLogMes("csv上传数据成功");
  3253. SendTaskMessage($"csv上传数据成功", MessageLevel.Debug);
  3254. }
  3255. // 跨天后重置当天执行标记
  3256. if (!isNewDay && _hasCompressedToday)
  3257. {
  3258. _hasCompressedToday = false;
  3259. }
  3260. }
  3261. else
  3262. {
  3263. LogHelper.WriteLogMes($"上传数据失败");
  3264. SendTaskMessage($"上传数据失败", MessageLevel.Error);
  3265. }
  3266. }
  3267. else
  3268. {
  3269. LogHelper.WriteLogMes($"未启用mes");
  3270. SendTaskMessage($"未启用mes", MessageLevel.Error);
  3271. }
  3272. }
  3273. catch (Exception ex)
  3274. {
  3275. SendTaskMessage($"上传数据时出错:{ex.Message}", MessageLevel.Error);
  3276. LogHelper.WriteLogError($"上传数据时出错", ex);
  3277. }
  3278. }
  3279. else
  3280. {
  3281. SendTaskMessage($"{i}:{stationConfig.DeviceName}产品记录保存失败", MessageLevel.Error);
  3282. }
  3283. }
  3284. else
  3285. {
  3286. SendTaskMessage($"{i}:{stationConfig.DeviceName}工位未配置保存路径,无法保存产品记录", MessageLevel.Error);
  3287. }
  3288. }
  3289. else
  3290. {
  3291. SendTaskMessage($"{i}:{stationConfig.DeviceName}工位未配置保存产品记录", MessageLevel.Debug);
  3292. }
  3293. fileresult.Clear();
  3294. return;
  3295. }
  3296. else
  3297. {
  3298. SendTaskMessage($"{i}过站检查信号:state={state},处理为默认状态", MessageLevel.Alarm);
  3299. await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)0);
  3300. return;
  3301. }
  3302. }
  3303. }
  3304. }
  3305. //开始/停止 组装/锁附
  3306. for (int i = 0; i < 7; i++)
  3307. {
  3308. //开始组装
  3309. if (string.Format(addressConfig.In_WorkStart.Address, i).Contains(tuple.nodeId))
  3310. {
  3311. if (tuple.value is Int16 state)
  3312. {
  3313. if (_LastWorkStart[i] != state)
  3314. {
  3315. _LastWorkStart[i] = state;
  3316. }
  3317. else
  3318. {
  3319. return;
  3320. }
  3321. var code = plc.ReadNode<string>(string.Format(addressConfig.In_Code.Address, i));
  3322. if (state == 1)
  3323. {
  3324. //SendTaskMessage($"收到开始组装信号[{i}]...", MessageLevel.Debug);
  3325. SendTaskMessage($"收到开始保压信号[{i}]...", MessageLevel.Debug);
  3326. // 开始采集压力值
  3327. StartPressureCollection(currentProduct.Name, ProductMainSN[i], i);
  3328. StartCtqTime[i] = DateTime.Now;
  3329. }
  3330. else
  3331. {
  3332. SendTaskMessage($"关闭开始保压信号[{i}]...", MessageLevel.Debug);
  3333. }
  3334. }
  3335. }
  3336. //停止组装
  3337. else if (string.Format(addressConfig.In_WorkDone.Address, i).Contains(tuple.nodeId))
  3338. {
  3339. if (tuple.value is Int16 state)
  3340. {
  3341. if (_LastWorkDone[i] != state)
  3342. {
  3343. _LastWorkDone[i] = state;
  3344. }
  3345. else
  3346. {
  3347. return;
  3348. }
  3349. if (state == 1)
  3350. {
  3351. SendTaskMessage($"收到停止压力信号[{i}]...", MessageLevel.Debug);
  3352. //读取压力值
  3353. //float pressure = plc.ReadNode<float>(addressConfig.In_PressureValue.Address);
  3354. //读取压力值
  3355. //float pressure = plc.ReadNode<float>(string.Format(addressConfig.In_PressureValue.Address, i));
  3356. float pressure = plc.ReadNode<float>(string.Format(addressConfig.In_PressureLastValue.Address, i));
  3357. currentPressure[i] = pressure;
  3358. SendTaskMessage($"[{i}]压力:{pressure}", MessageLevel.Info);
  3359. //await Task.Delay(60);
  3360. await plc.WriteNodeAsync(string.Format(addressConfig.In_WorkDone.Address, i), (Int16)0);
  3361. // 停止采集压力并保存
  3362. await StopAndSavePressureCollectionAsync(currentProduct.Name, ProductMainSN[i], i);
  3363. if (i == 1)
  3364. {
  3365. if (pressure <= currentProduct.GlobalParamEx.DownPressure.PressureDownLimit || pressure >= currentProduct.GlobalParamEx.DownPressure.PressureUpLimit)
  3366. {
  3367. await plc.WriteNodeAsync(string.Format(addressConfig.In_WorkDone.Address, i), (Int16)4);//NG
  3368. }
  3369. else
  3370. {
  3371. await plc.WriteNodeAsync(string.Format(addressConfig.In_WorkDone.Address, i), (Int16)3);//OK
  3372. }
  3373. }
  3374. }
  3375. else
  3376. {
  3377. SendTaskMessage($"停止压力信号[{i}]已关闭", MessageLevel.Alarm);
  3378. }
  3379. }
  3380. }
  3381. }
  3382. }
  3383. }
  3384. catch (Exception ex)
  3385. {
  3386. SendTaskMessage(ex.Message, MessageLevel.Error);
  3387. LogHelper.WriteLogError("PLC命令执行时出错!", ex);
  3388. }
  3389. }
  3390. #endregion
  3391. #region 方法
  3392. private void SendTaskMessage(string msg, MessageLevel level)
  3393. {
  3394. App.Current.Dispatcher.Invoke(() =>
  3395. {
  3396. _eventAggregator.GetEvent<TaskMessageNotification>().Publish(new Models.MessageStruct() { Message = msg, level = level });
  3397. });
  3398. }
  3399. public bool concurrenty = true;
  3400. public void CurrentRunStatus()
  3401. {
  3402. if (!concurrenty)
  3403. {
  3404. return;
  3405. }
  3406. concurrenty = false;
  3407. bool connectStaus = true;
  3408. bool mesStatus = true;
  3409. //bool paraStatus = true;
  3410. try
  3411. {
  3412. var allDevices = _configService.GetAllDeviceInfos();
  3413. for (int i = 0; i < allDevices.Count; i++)
  3414. {
  3415. var stationConfig = allDevices.FirstOrDefault(d => d.DeviceNo == i);
  3416. //var stationConfig = _configService.GetDeviceInfo(i);
  3417. if (!string.IsNullOrEmpty(stationConfig.Station) && stationConfig.Station != "" && !string.IsNullOrEmpty(stationConfig.DeviceName) && stationConfig.DeviceName != "")
  3418. {
  3419. if (!stationConfig.EnableMES || !stationConfig.EnableDoubleMes || !stationConfig.SaveCsv)
  3420. {
  3421. SendTaskMessage("当前MES功能/双验证功能/保存CSV功能未启用,请检查...", MessageLevel.Info);
  3422. mesStatus = false;
  3423. break;
  3424. }
  3425. }
  3426. }
  3427. //var systemConfig = _configService.GetSystemConfiguration();
  3428. //if (!systemConfig.PressureSensorConfig[0].IsSavePressureCurve || !systemConfig.PressureSensorConfig[1].IsSavePressureCurve)
  3429. //{
  3430. // paraStatus = false;
  3431. //}
  3432. foreach (var item in Status)
  3433. {
  3434. if (item.Background == System.Windows.Media.Brushes.Red)
  3435. {
  3436. SendTaskMessage("当前有设备未连接,请检查...", MessageLevel.Info);
  3437. connectStaus = false;
  3438. break;
  3439. }
  3440. }
  3441. }
  3442. catch (Exception)
  3443. {
  3444. connectStaus = false;
  3445. mesStatus = false;
  3446. //paraStatus = false;
  3447. }
  3448. var user = _systemDatabaseService.GetCurrentUser();
  3449. if (connectStaus && mesStatus && _LastAutoRunning && user.userPart == UserPart.Operator)
  3450. {
  3451. RunModelBackground = System.Windows.Media.Brushes.Green;
  3452. ClearModeBackground = System.Windows.Media.Brushes.White;
  3453. ManualModeBackground = System.Windows.Media.Brushes.White;
  3454. SendTaskMessage("当前为生产模式", MessageLevel.Info);
  3455. }
  3456. else
  3457. {
  3458. RunModelBackground = System.Windows.Media.Brushes.White;
  3459. ClearModeBackground = System.Windows.Media.Brushes.White;
  3460. ManualModeBackground = System.Windows.Media.Brushes.Orange;
  3461. SendTaskMessage("当前为调机模式", MessageLevel.Info);
  3462. }
  3463. concurrenty = true;
  3464. }
  3465. #region Pressure collection helpers
  3466. public void InitPressure()
  3467. {
  3468. for (int i = 0; i < _pressureSamples.Length; i++)
  3469. {
  3470. _pressureSamples[i] = new List<(DateTime, float)>();
  3471. }
  3472. for (int i = 0; i < _pressureCts.Length; i++)
  3473. {
  3474. _pressureCts[i] = new CancellationTokenSource();
  3475. }
  3476. for (int i = 0; i < _pressureTimeoutCts.Length; i++)
  3477. {
  3478. _pressureTimeoutCts[i] = new CancellationTokenSource();
  3479. }
  3480. for (int i = 0; i < _pressureLinkedCts.Length; i++)
  3481. {
  3482. _pressureLinkedCts[i] = new CancellationTokenSource();
  3483. }
  3484. }
  3485. /// <summary>
  3486. /// 开始采集压力值
  3487. /// </summary>
  3488. /// <param name="productName">配方名称</param>
  3489. /// <param name="productCode">产品编号</param>
  3490. /// <param name="Index">编号</param>
  3491. private void StartPressureCollection(string productName, string productCode, int Index)
  3492. {
  3493. try
  3494. {
  3495. // cancel existing if any
  3496. //_pressureCts[Index]?.Cancel();
  3497. //_pressureTimeoutCts[Index]?.Cancel();
  3498. //_pressureLinkedCts[Index]?.Dispose();
  3499. _pressureSamples[Index]?.Clear();
  3500. _pressureCts[Index] = new CancellationTokenSource();
  3501. _pressureTimeoutCts[Index] = new CancellationTokenSource();
  3502. // timeout after configured seconds
  3503. _pressureTimeoutCts[Index].CancelAfter(TimeSpan.FromSeconds(_pressureMaxDurationSeconds));
  3504. // link tokens so either stop or timeout cancels
  3505. _pressureLinkedCts[Index] = CancellationTokenSource.CreateLinkedTokenSource(_pressureCts[Index].Token, _pressureTimeoutCts[Index].Token);
  3506. var token = _pressureLinkedCts[Index].Token;
  3507. var addressConfig = _configService.GetPlcAddresses();
  3508. var plc = _plcService.GetPlcByNumber(addressConfig.PlcNo) as OpcUaClientPLC;
  3509. if (plc == null || !plc.IsConnected) return;
  3510. Task.Run(async () =>
  3511. {
  3512. SendTaskMessage($"Pressure collection started for position {Index}", MessageLevel.Debug);
  3513. while (!token.IsCancellationRequested)
  3514. {
  3515. try
  3516. {
  3517. float pressure = plc.ReadNode<float>(string.Format(addressConfig.In_PressureValue.Address, Index));
  3518. lock (_pressureSamples[Index])
  3519. {
  3520. _pressureSamples[Index].Add((DateTime.Now, pressure));
  3521. }
  3522. }
  3523. catch (Exception ex)
  3524. {
  3525. LogHelper.WriteLogError("采集压力值时出错", ex);
  3526. }
  3527. await Task.Delay(_pressureSampleIntervalMs);
  3528. }
  3529. // if cancelled due to timeout
  3530. //if (_pressureTimeoutCts[Index].IsCancellationRequested && !_pressureCts[Index].IsCancellationRequested)
  3531. //{
  3532. // SendTaskMessage("Pressure collection timed out, saving collected data.", MessageLevel.Alarm);
  3533. // await StopAndSavePressureCollectionAsync(productName, productCode, Index);
  3534. //}
  3535. }, token);
  3536. }
  3537. catch (Exception ex)
  3538. {
  3539. LogHelper.WriteLogError("StartPressureCollection error", ex);
  3540. SendTaskMessage($"压力采集时出错:{ex.Message}", MessageLevel.Error);
  3541. }
  3542. }
  3543. /// <summary>
  3544. /// 停止采集并保存压力数据
  3545. /// </summary>
  3546. /// <param name="productName"></param>
  3547. /// <param name="productCode"></param>
  3548. /// <param name="positionIndex"></param>
  3549. /// <returns></returns>
  3550. private async Task StopAndSavePressureCollectionAsync(string productName, string productCode, int Index)
  3551. {
  3552. try
  3553. {
  3554. // cancel sampling
  3555. //_pressureCts[Index]?.Cancel();
  3556. // allow worker to observe cancel
  3557. await Task.Delay(50);
  3558. List<(DateTime Timestamp, float Value)> snapshot;
  3559. lock (_pressureSamples[Index])
  3560. {
  3561. snapshot = new List<(DateTime, float)>(_pressureSamples[Index]);
  3562. }
  3563. if (snapshot.Count == 0)
  3564. {
  3565. SendTaskMessage("No pressure samples collected.", MessageLevel.Info);
  3566. return;
  3567. }
  3568. // save to file under configured path
  3569. try
  3570. {
  3571. //获取系统参数
  3572. var systemConfig = _configService.GetSystemConfiguration();
  3573. if (systemConfig.PressureSensorConfig == null || systemConfig.PressureSensorConfig.Count <= 0)
  3574. {
  3575. SendTaskMessage("当前没有配置任何压力传感器!", MessageLevel.Alarm);
  3576. return;
  3577. }
  3578. //获取当前压力传感器配置
  3579. var pressureSensorConfig = systemConfig.PressureSensorConfig.FirstOrDefault(p => p.SensorId == Index);
  3580. if (pressureSensorConfig == null)
  3581. {
  3582. SendTaskMessage($"当前压力传感器配置不存在!- {Index}", MessageLevel.Alarm);
  3583. return;
  3584. }
  3585. //PressureFinishNotification : Prism.Events.PubSubEvent<(bool IsSuccess, int Index, (DateTime Timestamp, float Value)[] values,string SavePath )>
  3586. string basePath = pressureSensorConfig?.PressureCurvePath ?? Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
  3587. string datePath = DateTime.Now.ToString("yyyy-MM") + "\\" + DateTime.Now.ToString("yyyy-MM-dd") + "\\";
  3588. string dirPath = Path.Combine(basePath, datePath, productName ?? "UnknownProduct", productCode ?? DateTime.Now.ToString("yyyyMMddHHmmssfff"));
  3589. if (!Directory.Exists(dirPath)) Directory.CreateDirectory(dirPath);
  3590. string csvfileName = $"pressure-{productCode}-{Index}-{DateTime.Now.ToString("HHmmss")}.csv";
  3591. string csvPath = Path.Combine(dirPath, csvfileName);
  3592. string pngfileName = $"pressure-{productCode}-{Index}-{DateTime.Now.ToString("HHmmss")}.png";
  3593. string pngPath = Path.Combine(dirPath, pngfileName);
  3594. if (!pressureSensorConfig.IsSavePressureCurve)
  3595. {
  3596. csvPath = string.Empty;
  3597. pngPath = string.Empty;
  3598. }
  3599. App.Current.Dispatcher.Invoke(() =>
  3600. {
  3601. _eventAggregator.GetEvent<PressureFinishNotification>().Publish((true, Index, snapshot.ToArray(), pngPath));
  3602. });
  3603. //判断是否保存锁付曲线
  3604. if (pressureSensorConfig.IsSavePressureCurve)
  3605. {
  3606. if (!string.IsNullOrEmpty(csvPath))
  3607. {
  3608. var sb = new StringBuilder();
  3609. sb.AppendLine("Timestamp,Value");
  3610. foreach (var s in snapshot)
  3611. {
  3612. sb.AppendLine($"{s.Timestamp:O},{s.Value}");
  3613. }
  3614. File.WriteAllText(csvPath, sb.ToString());
  3615. SendTaskMessage($"Pressure samples saved to {csvPath}", MessageLevel.Debug);
  3616. }
  3617. }
  3618. }
  3619. catch (Exception ex)
  3620. {
  3621. LogHelper.WriteLogError("保存压力采样文件时出错", ex);
  3622. SendTaskMessage("保存压力采样文件时出错: " + ex.Message, MessageLevel.Error);
  3623. }
  3624. }
  3625. catch (Exception ex)
  3626. {
  3627. LogHelper.WriteLogError("StopAndSavePressureCollectionAsync error", ex);
  3628. }
  3629. finally
  3630. {
  3631. try
  3632. {
  3633. _pressureLinkedCts[Index]?.Cancel();
  3634. _pressureLinkedCts[Index]?.Dispose();
  3635. _pressureTimeoutCts[Index]?.Dispose();
  3636. _pressureCts[Index]?.Dispose();
  3637. _pressureSamples[Index]?.Clear();
  3638. }
  3639. catch { }
  3640. }
  3641. }
  3642. #endregion
  3643. private async void DoYieldTime(object state)
  3644. {
  3645. try
  3646. {
  3647. //获取当前产品
  3648. var currentproduct = _productService.GetCurrentProduct();
  3649. if (currentproduct == null) return;
  3650. TotalQuantity = await _systemDatabaseService.GetOverallProductionAsync(currentproduct.Name);
  3651. //TotalQuantityToday = await _systemDatabaseService.GetTodayProductionAsync(currentproduct.Name);
  3652. //CurrentUPH = await _systemDatabaseService.GetLastHourProductionAsync(currentproduct.Name);
  3653. ////
  3654. ThrowNumber = await _systemDatabaseService.GetThrowNumberAsync();
  3655. //NgNumber = await _systemDatabaseService.GetNGProductionAsync(currentproduct.Name);
  3656. //Yield = ((double)TotalQuantityToday - (double)NgNumber) / (double)TotalQuantityToday * 100;
  3657. TotalQuantityToday = 0;
  3658. CurrentUPH = 0;
  3659. NgNumber = 0;
  3660. Yield = 0;
  3661. ObservableCollection<ProductModel> ProductList = new ObservableCollection<ProductModel>(_productService.GetAllProducts());
  3662. foreach (var product in ProductList)
  3663. {
  3664. //TotalQuantity += await _systemDatabaseService.GetOverallProductionAsync(product.Name);
  3665. TotalQuantityToday += await _systemDatabaseService.GetTodayProductionAsync(product.Name);
  3666. CurrentUPH += await _systemDatabaseService.GetLastHourProductionAsync(product.Name);
  3667. NgNumber += await _systemDatabaseService.GetNGProductionAsync(product.Name);
  3668. }
  3669. Yield += ((double)TotalQuantityToday - (double)NgNumber) / (double)TotalQuantityToday * 100;
  3670. }
  3671. catch (Exception)
  3672. {
  3673. }
  3674. }
  3675. #region vm启动检测
  3676. /// <summary>
  3677. /// 杀死所有VM有关进程
  3678. /// </summary>
  3679. public void KillVMProcess()
  3680. {
  3681. KillProcess("VisionMasterServerApp");
  3682. KillProcess("VisionMaster");
  3683. KillProcess("VmModuleProxy.exe");
  3684. KillProcess("vServerApp.exe");
  3685. }
  3686. /// <summary>
  3687. /// 杀死指定进程名的进程
  3688. /// </summary>
  3689. /// <param name="strKillName"></param>
  3690. public void KillProcess(string strKillName)
  3691. {
  3692. foreach (System.Diagnostics.Process p in System.Diagnostics.Process.GetProcesses())
  3693. {
  3694. if (p.ProcessName.Contains(strKillName))
  3695. {
  3696. try
  3697. {
  3698. p.Kill();
  3699. p.WaitForExit();
  3700. }
  3701. catch (Exception ex)
  3702. {
  3703. LogHelper.WriteLogError($"杀死进程:{strKillName}时失败!", ex);
  3704. }
  3705. }
  3706. }
  3707. }
  3708. #endregion
  3709. /// <summary>
  3710. /// 记录工位的结果,其他工位要存储的数据项按照键值对集合的形式传入,键为数据项名称,值为数据项的值
  3711. /// </summary>
  3712. /// <param name="stationIndex"></param>
  3713. /// <param name="dfcs"></param>
  3714. /// <param name="productMainSN"></param>
  3715. /// <param name="ct"></param>
  3716. /// <param name="timestamp"></param>
  3717. /// <param name="savePath"></param>
  3718. /// <param name="isFirstEnter"></param>
  3719. /// <returns></returns>
  3720. private bool RecordStationResult(int stationIndex, IEnumerable<DataFormCtq> dfcs, string productMainSN, double ct, DateTime timestamp, string savePath, bool isFirstEnter = true)
  3721. {
  3722. try
  3723. {
  3724. //获取当前工位的配置
  3725. var stationConfig = _configService.GetDeviceInfo(stationIndex);
  3726. if (stationConfig == null) return false;
  3727. //如果保存路径为空,则不保存
  3728. if (string.IsNullOrEmpty(savePath)) return false;
  3729. string fullPath = savePath;
  3730. //如果保存路径是一个文件路径,则直接保存,否则按照工位和日期进行保存
  3731. if (!File.Exists(savePath) || !Path.HasExtension(savePath))
  3732. {
  3733. fullPath = savePath;
  3734. //如果路径不存在,则创建
  3735. // 创建目录
  3736. string directory = Path.GetDirectoryName(fullPath);
  3737. if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
  3738. {
  3739. Directory.CreateDirectory(directory);
  3740. }
  3741. }
  3742. //结果保存至CSV文件,文件名格式:savePath\yyyy-MM\工位编号_工位名称_yyyy-MM-dd.csv
  3743. string datePath = timestamp.ToString("yyyy-MM") + "\\";
  3744. string dirPath = Path.Combine(savePath, datePath);
  3745. if (!Directory.Exists(dirPath)) Directory.CreateDirectory(dirPath);
  3746. string fileName = $"{stationIndex}_{stationConfig.DeviceName}_{timestamp.ToString("yyyy-MM-dd")}.csv";
  3747. fullPath = Path.Combine(dirPath, fileName);
  3748. StringBuilder sb = new StringBuilder();
  3749. if (!File.Exists(fullPath))
  3750. {
  3751. sb.Append("PPID,PROCESS_NAME,PROCESS_START_TIME,CTQ_NAME,CTQ_VALUE,CTQ_LSL,CTQ_USL,PROCESS_END_TIME,SUPPLIER_NAME,COMMODITY_TYPE,REVISION,WO#,MFG_LOT#," +
  3752. "MFG_ASSY_LINE,PROCESS_OUTCOME,PROCESS_MACHINE_ID,CTQ_UNIT_OF_MEASURE,ERROR_MESSAGE,PD_ATTR_01,PD_ATTR_02,PD_ATTR_03,PD_ATTR_04,PD_ATTR_05");
  3753. sb.AppendLine();
  3754. }
  3755. if (dfcs == null || dfcs.Count() == 0)
  3756. {
  3757. return false;
  3758. }
  3759. foreach (var dfc in dfcs)
  3760. {
  3761. sb.Append($"{dfc.PPID},{dfc.PROCESS_NAME},{dfc.PROCESS_START_TIME},{dfc.CTQ_NAME},{dfc.CTQ_VALUE},{dfc.CTQ_LSL},{dfc.CTQ_USL},{dfc.PROCESS_END_TIME},{dfc.SUPPLIER_NAME},{dfc.COMMODITY_TYPE},{dfc.REVISION},{dfc.WO},{dfc.MFG_LOT},{dfc.MFG_ASSY_LINE},{dfc.PROCESS_OUTCOME},{dfc.PROCESS_MACHINE_ID},{dfc.CTQ_UNIT_OF_MEASURE},{dfc.ERROR_MESSAGE},{dfc.PD_ATTR_01},{dfc.PD_ATTR_02},{dfc.PD_ATTR_03},{dfc.PD_ATTR_04},{dfc.PD_ATTR_05}");
  3762. sb.AppendLine();
  3763. }
  3764. // 使用 StreamWriter 以追加模式打开文件
  3765. StreamWriter sw = new StreamWriter(fullPath, true, Encoding.UTF8);
  3766. sw.Write(sb.ToString());
  3767. sw.Close();
  3768. sw.Dispose();
  3769. return true;
  3770. }
  3771. catch (Exception ex)
  3772. {
  3773. if (!isFirstEnter)
  3774. {
  3775. return false;
  3776. }
  3777. LogHelper.WriteLogError("记录其他工位结果时出错!", ex);
  3778. //我希望在这里生成一个临时文件来保存这次的记录,因为如果本地文件被打开时可能会导致无法写入,如果不保存的话可能会丢失这次的记录,所以我想在这里生成一个临时文件来保存这次的记录
  3779. //生成临时文件的路径,格式为savePath\yyyy-MM\工位编号_yyyy-MM-dd_temp\工位编号_yyyy-MM-dd_HH-mm-ss-fff_temp.csv
  3780. string datePath = timestamp.ToString("yyyy-MM") + "\\";
  3781. string tempDirPath = Path.Combine(savePath, datePath, $"{stationIndex}_{timestamp.ToString("yyyy-MM-dd")}_temp");
  3782. string tempFileName = $"{stationIndex}_{timestamp.ToString("yyyy-MM-dd_HH-mm-ss-fff")}_temp.csv";
  3783. RecordStationResult(stationIndex, dfcs, productMainSN, ct, timestamp, tempFileName, false);
  3784. return false;
  3785. }
  3786. }
  3787. private bool RecordStationResult2(int stationIndex, IEnumerable<DataFormCtq> dfcs, string productMainSN, double ct, DateTime timestamp, string savePath, bool isFirstEnter = true)
  3788. {
  3789. try
  3790. {
  3791. //获取当前工位的配置
  3792. DateTime dt = DateTime.Now;
  3793. var stationConfig = _configService.GetDeviceInfo(stationIndex);
  3794. if (stationConfig == null) return false;
  3795. //如果保存路径为空,则不保存
  3796. if (string.IsNullOrEmpty(savePath)) return false;
  3797. //savePath = $"D:\\Data\\{dt.ToString("yyyy-MM")}\\{dt.ToString("yyyy-MM-dd")}\\外壳数据\\{productMainSN}";
  3798. string fullPath = savePath;
  3799. //如果保存路径是一个文件路径,则直接保存,否则按照工位和日期进行保存
  3800. if (!File.Exists(savePath) || !Path.HasExtension(savePath))
  3801. {
  3802. fullPath = savePath;
  3803. //如果路径不存在,则创建
  3804. // 创建目录
  3805. string directory = Path.GetDirectoryName(fullPath);
  3806. if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
  3807. {
  3808. Directory.CreateDirectory(directory);
  3809. }
  3810. }
  3811. string name = "屏幕外壳数据";
  3812. //结果保存至CSV文件,文件名格式:savePath\yyyy-MM\工位编号_工位名称_yyyy-MM-dd.csv
  3813. string datePath = timestamp.ToString("yyyy-MM") + "\\";
  3814. string dirPath = Path.Combine(savePath, datePath);
  3815. if (!Directory.Exists(dirPath)) Directory.CreateDirectory(dirPath);
  3816. string fileName = $"{name}_{timestamp.ToString("yyyy-MM-dd")}.csv";
  3817. fullPath = Path.Combine(dirPath, fileName);
  3818. StringBuilder sb = new StringBuilder();
  3819. if (!File.Exists(fullPath))
  3820. {
  3821. sb.Append("PPID,SN,PROCESS_START_TIME,dis1,dis2,dis3,dis4,PROCESS_END_TIME,Product_Name,CodeSn,dis5,WO#,MFG_LOT#," +
  3822. "MFG_ASSY_LINE,PROCESS_OUTCOME,Left1,Down1,Left2,Up1,Right1,Down2,Right2,Up2");
  3823. sb.AppendLine();
  3824. }
  3825. if (dfcs == null || dfcs.Count() == 0)
  3826. {
  3827. return false;
  3828. }
  3829. foreach (var dfc in dfcs)
  3830. {
  3831. sb.Append($"{dfc.PPID},{dfc.PROCESS_NAME},{dfc.PROCESS_START_TIME},{dfc.CTQ_NAME},{dfc.CTQ_VALUE},{dfc.CTQ_LSL},{dfc.CTQ_USL},{dfc.PROCESS_END_TIME},{dfc.SUPPLIER_NAME},{dfc.COMMODITY_TYPE},{dfc.REVISION},{dfc.WO},{dfc.MFG_LOT},{dfc.MFG_ASSY_LINE},{dfc.PROCESS_OUTCOME},{dfc.PROCESS_MACHINE_ID},{dfc.CTQ_UNIT_OF_MEASURE},{dfc.ERROR_MESSAGE},{dfc.PD_ATTR_01},{dfc.PD_ATTR_02},{dfc.PD_ATTR_03},{dfc.PD_ATTR_04},{dfc.PD_ATTR_05}");
  3832. sb.AppendLine();
  3833. }
  3834. // 使用 StreamWriter 以追加模式打开文件
  3835. StreamWriter sw = new StreamWriter(fullPath, true, Encoding.UTF8);
  3836. sw.Write(sb.ToString());
  3837. sw.Close();
  3838. sw.Dispose();
  3839. return true;
  3840. }
  3841. catch (Exception ex)
  3842. {
  3843. if (!isFirstEnter)
  3844. {
  3845. return false;
  3846. }
  3847. LogHelper.WriteLogError("记录其他工位结果时出错!", ex);
  3848. //我希望在这里生成一个临时文件来保存这次的记录,因为如果本地文件被打开时可能会导致无法写入,如果不保存的话可能会丢失这次的记录,所以我想在这里生成一个临时文件来保存这次的记录
  3849. //生成临时文件的路径,格式为savePath\yyyy-MM\工位编号_yyyy-MM-dd_temp\工位编号_yyyy-MM-dd_HH-mm-ss-fff_temp.csv
  3850. string datePath = timestamp.ToString("yyyy-MM") + "\\";
  3851. string tempDirPath = Path.Combine(savePath, datePath, $"{stationIndex}_{timestamp.ToString("yyyy-MM-dd")}_temp");
  3852. string tempFileName = $"{stationIndex}_{timestamp.ToString("yyyy-MM-dd_HH-mm-ss-fff")}_temp.csv";
  3853. RecordStationResult(stationIndex, dfcs, productMainSN, ct, timestamp, tempFileName, false);
  3854. return false;
  3855. }
  3856. }
  3857. public async Task<string> CheckConsecutiveLowCapacityAsync(string productName, int consecutiveHours, int targetPerHour)
  3858. {
  3859. // 1. 获取你已有的按小时统计的产能数据
  3860. var records = await _systemDatabaseService.GetDailyProductionByHourAndCategoryAsync(productName);
  3861. // 连续计数初始化
  3862. int consecutiveFail = 0;
  3863. int firstFailHour = -1;
  3864. // 2. 遍历 0~23 小时,检查每小时总产能
  3865. for (int hour = 0; hour < 24; hour++)
  3866. {
  3867. // 当前小时总产能 = 该小时所有分类数量相加,没有数据 = 0
  3868. int currentCapacity = 0;
  3869. if (records.TryGetValue(hour, out var categoryDict))
  3870. {
  3871. currentCapacity = categoryDict.Sum(kv => kv.Value);
  3872. }
  3873. // 3. 判断是否不达标
  3874. if (currentCapacity < targetPerHour)
  3875. {
  3876. consecutiveFail++;
  3877. if (firstFailHour == -1)
  3878. firstFailHour = hour;
  3879. if (consecutiveFail >= consecutiveHours)
  3880. {
  3881. int endHour = firstFailHour + consecutiveHours - 1;
  3882. return $"⚠️ 报警:{productName} 自 {firstFailHour:D2}:00 至 {endHour:D2}:59 连续{consecutiveHours}小时产能未达到{targetPerHour},请关注!";
  3883. }
  3884. }
  3885. else
  3886. {
  3887. // 达标,重置连续计数
  3888. consecutiveFail = 0;
  3889. firstFailHour = -1;
  3890. }
  3891. }
  3892. // 无报警
  3893. return string.Empty;
  3894. }
  3895. #region 压缩图片文件夹
  3896. /// <summary>
  3897. /// 压缩文件
  3898. /// </summary>
  3899. /// <param name="sourcePath">原路径</param>
  3900. /// <param name="zipname">压缩后文件的名称</param>
  3901. /// <param name="station">mes中的station</param>
  3902. /// <param name="fixid">mes中的fixtureID</param>
  3903. public async void CompressionImage(string sourcePath, string zipname, string station, string fixid)
  3904. {
  3905. string zipPath = "";
  3906. try
  3907. {
  3908. SendTaskMessage($"开始压缩文件", MessageLevel.Info);
  3909. await Task.Run(() =>
  3910. {
  3911. zipPath = $"Z:\\{station}\\{fixid}\\{DateTime.Now.ToString("yyyyMMdd")}\\{zipname}.zip";//生成的zip文件路径
  3912. string path = $"Z:\\{station}\\{fixid}\\{DateTime.Now.ToString("yyyyMMdd")}";
  3913. if (!Directory.Exists(path))
  3914. {
  3915. Directory.CreateDirectory(path);
  3916. }
  3917. ZipFile.CreateFromDirectory(sourcePath, zipPath);
  3918. });
  3919. }
  3920. catch (Exception ex)
  3921. {
  3922. LogHelper.WriteLogError($"压缩文件时出错", ex);
  3923. }
  3924. }
  3925. public async void CompressionSingleCsv(string csvFilePath, string zipname, string station, string fixid)
  3926. {
  3927. string zipPath = "";
  3928. try
  3929. {
  3930. SendTaskMessage("开始压缩CSV文件", MessageLevel.Info);
  3931. await Task.Run(() =>
  3932. {
  3933. if (!File.Exists(csvFilePath))
  3934. {
  3935. LogHelper.WriteLogMes($"CSV文件不存在,不压缩:{csvFilePath}");
  3936. return;
  3937. }
  3938. zipPath = $"Z:\\{station}\\{fixid}\\{DateTime.Now.ToString("yyyyMMdd")}\\{zipname}.zip";
  3939. string path = $"Z:\\{station}\\{fixid}\\{DateTime.Now.ToString("yyyyMMdd")}";
  3940. if (!Directory.Exists(path))
  3941. {
  3942. Directory.CreateDirectory(path);
  3943. }
  3944. using (FileStream fs = new FileStream(zipPath, FileMode.Create))
  3945. {
  3946. using (ZipArchive archive = new ZipArchive(fs, ZipArchiveMode.Create))
  3947. {
  3948. archive.CreateEntryFromFile(csvFilePath, Path.GetFileName(csvFilePath));
  3949. }
  3950. }
  3951. });
  3952. SendTaskMessage("CSV文件压缩完成", MessageLevel.Debug);
  3953. }
  3954. catch (Exception ex)
  3955. {
  3956. LogHelper.WriteLogError("压缩CSV文件失败", ex);
  3957. }
  3958. }
  3959. #endregion
  3960. #endregion
  3961. }
  3962. }