Management.cs 260 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930
  1. using Cognex.VisionPro.ToolBlock;
  2. using MahApps.Metro.Controls;
  3. using NPOI.OpenXmlFormats.Vml;
  4. using Opc.Ua;
  5. using Org.BouncyCastle.Crypto;
  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.Net.Sockets;
  19. using System.Security.Claims;
  20. using System.Text;
  21. using System.Threading;
  22. using System.Threading.Tasks;
  23. using System.Web;
  24. using System.Web.UI.WebControls;
  25. using System.Windows.Media;
  26. using Team.FFFeederService.Interfaces;
  27. using TeamAAS_VP.Core.PLCs;
  28. using TeamAAS_VP.Core.ScrewDriver;
  29. using TeamAAS_VP.Data;
  30. using TeamAAS_VP.Enums;
  31. using TeamAAS_VP.Events;
  32. using TeamAAS_VP.Interfaces;
  33. using TeamAAS_VP.Models;
  34. using TeamAAS_VP.Models.PLC;
  35. using TeamAAS_VP.Models.Torque;
  36. using TeamAAS_VP.Resources.Languages;
  37. using TeamAAS_VP.Views.Product;
  38. using TouchSocket.Core;
  39. using TouchSocket.SerialPorts;
  40. using TouchSocket.Sockets;
  41. using Timer = System.Threading.Timer;
  42. namespace TeamAAS_VP.Core
  43. {
  44. public class Management : BindableBase
  45. {
  46. #region 字段
  47. IRegionManager _regionManager;
  48. IEventAggregator _eventAggregator;
  49. IContainerProvider _container;
  50. IConfigService _configService;
  51. IRobotService _robotService;
  52. ICameraService _cameraService;
  53. IFeederService _feederService;
  54. IPlcService _plcService;
  55. ILightManagerService _lightManagerService;
  56. IProductService _productService;
  57. ICalibrationService _calibrationService;
  58. IRemoteCommandService _remoteCommandService;
  59. ISystemDatabaseService _systemDatabaseService;
  60. IMesService _mesService;
  61. Timer yieldtimer;
  62. private DateTime StartTime = DateTime.Now;
  63. SerialPortClient client;
  64. // Pressure collection
  65. /// <summary>
  66. /// 压力采集信号控制CTS
  67. /// </summary>
  68. private CancellationTokenSource[] _pressureCts = new CancellationTokenSource[100];
  69. /// <summary>
  70. /// 压力采集超时CTS
  71. /// </summary>
  72. private CancellationTokenSource[] _pressureTimeoutCts = new CancellationTokenSource[100];
  73. /// <summary>
  74. /// 压力采集CTS
  75. /// </summary>
  76. private CancellationTokenSource[] _pressureLinkedCts = new CancellationTokenSource[100];
  77. /// <summary>
  78. /// 压力数据集合
  79. /// </summary>
  80. private List<(DateTime Timestamp, float Value)> _pressureSamples = new List<(DateTime, float)>();
  81. /// <summary>
  82. /// 采样间隔毫秒
  83. /// </summary>
  84. private int _pressureSampleIntervalMs = 50; //采样间隔毫秒
  85. /// <summary>
  86. /// 超时自动停止秒数
  87. /// </summary>
  88. private int _pressureMaxDurationSeconds = 30; //超时自动停止秒数
  89. DateTime[] StartCtqTime = new DateTime[3];//开始采集Ctq数据的时间
  90. /// <summary>
  91. /// 上次执行上传csv的日期
  92. /// </summary>
  93. private DateTime _lastCompressDate = DateTime.MinValue;
  94. /// <summary>
  95. /// 当天是否上传过csv
  96. /// </summary>
  97. private bool _hasCompressedToday = false;
  98. List<LockResult> lockResultsAll = new List<LockResult>();//一个产品的总结果
  99. string productSn = "";//产品sn
  100. DateTime StartScrewTime = DateTime.Now;//开始锁附的时间
  101. #endregion
  102. #region 属性
  103. /// <summary>
  104. /// 当前顶部显示的运行模式
  105. /// </summary>
  106. private RunDisplayMode _CurrentDisplayMode = RunDisplayMode.Unknown;
  107. public RunDisplayMode CurrentDisplayMode
  108. {
  109. get { return _CurrentDisplayMode; }
  110. set { SetProperty(ref _CurrentDisplayMode, value); }
  111. }
  112. private ObservableCollection<ShowRender> _Renders;
  113. /// <summary>
  114. /// 显示界面
  115. /// </summary>
  116. public ObservableCollection<ShowRender> Renders
  117. {
  118. get { return _Renders; }
  119. set { SetProperty(ref _Renders, value); }
  120. }
  121. public BgCommunicate BgCommunicate { get; private set; }
  122. public BgModbusTcpCommunicate BgModbusTcpCommunicate { get; private set; }
  123. private ObservableCollection<StatusInfo> _Status = new ObservableCollection<StatusInfo>();
  124. /// <summary>
  125. /// 状态栏集合
  126. /// </summary>
  127. public ObservableCollection<StatusInfo> Status
  128. {
  129. get { return _Status; }
  130. set { SetProperty(ref _Status, value); }
  131. }
  132. private Int64 _TotalQuantity;
  133. /// <summary>
  134. /// 总产量
  135. /// </summary>
  136. public Int64 TotalQuantity
  137. {
  138. get { return _TotalQuantity; }
  139. set { SetProperty(ref _TotalQuantity, value); }
  140. }
  141. private Int64 _TotalQuantityToday;
  142. /// <summary>
  143. /// 当天的总产量
  144. /// </summary>
  145. public Int64 TotalQuantityToday
  146. {
  147. get { return _TotalQuantityToday; }
  148. set { SetProperty(ref _TotalQuantityToday, value); }
  149. }
  150. private Int64 _CurrentUPH;
  151. /// <summary>
  152. /// 当前的UPH
  153. /// </summary>
  154. public Int64 CurrentUPH
  155. {
  156. get { return _CurrentUPH; }
  157. set { SetProperty(ref _CurrentUPH, value); }
  158. }
  159. private Int64 _ThrowNumber;
  160. /// <summary>
  161. /// 抛料数
  162. /// </summary>
  163. public Int64 ThrowNumber
  164. {
  165. get { return _ThrowNumber; }
  166. set { SetProperty(ref _ThrowNumber, value); }
  167. }
  168. private Int64 _NgNumber;
  169. /// <summary>
  170. /// ng数量
  171. /// </summary>
  172. public Int64 NgNumber
  173. {
  174. get { return _NgNumber; }
  175. set { SetProperty(ref _NgNumber, value); }
  176. }
  177. private double _Yield;
  178. /// <summary>
  179. /// 良率
  180. /// </summary>
  181. public double Yield
  182. {
  183. get { return _Yield; }
  184. set { SetProperty(ref _Yield, value); }
  185. }
  186. private XYD_ScrewDriver _ScrewDriver;
  187. public XYD_ScrewDriver ScrewDriver
  188. {
  189. get { return _ScrewDriver; }
  190. set { SetProperty(ref _ScrewDriver, value); }
  191. }
  192. public Dictionary<string, object> BackupObject { get; set; } = new Dictionary<string, object>();
  193. private string _SignalValue = "0";
  194. /// <summary>
  195. /// 扭力测量仪获取的最大值
  196. /// </summary>
  197. public string SignalValue
  198. {
  199. get { return _SignalValue; }
  200. set { SetProperty(ref _SignalValue, value); }
  201. }
  202. private NumberStatistics _NumStatistics;
  203. /// <summary>
  204. /// 数量(批头、吸嘴、供料器)
  205. /// </summary>
  206. public NumberStatistics NumStatistics
  207. {
  208. get { return _NumStatistics; }
  209. set { SetProperty(ref _NumStatistics, value); }
  210. }
  211. private double _CTtime;
  212. /// <summary>
  213. /// ct
  214. /// </summary>
  215. public double CTtime
  216. {
  217. get { return _CTtime; }
  218. set { SetProperty(ref _CTtime, value); }
  219. }
  220. private bool _TorqueStatus = false;
  221. public bool TorqueStatus
  222. {
  223. get { return _TorqueStatus; }
  224. set { _TorqueStatus = value; }
  225. }
  226. private ObservableCollection<DataFormCtq> _dfc = new ObservableCollection<DataFormCtq>();
  227. /// <summary>
  228. /// CTQ数据集合
  229. /// </summary>
  230. public ObservableCollection<DataFormCtq> DFC
  231. {
  232. get { return _dfc; }
  233. set { SetProperty(ref _dfc, value); }
  234. }
  235. private ObservableCollection<ProductModel> _ProductList;
  236. public ObservableCollection<ProductModel> ProductList
  237. {
  238. get { return _ProductList; }
  239. set { SetProperty(ref _ProductList, value); }
  240. }
  241. #endregion
  242. #region 命令
  243. #endregion
  244. #region 事件
  245. #endregion
  246. public Management(IRegionManager regionManager, IEventAggregator ea, IContainerProvider container, IConfigService configService,
  247. IRobotService robotService, ICameraService cameraService, IFeederService feederService, IPlcService plcService, IProductService productService,
  248. ICalibrationService calibrationService, IRemoteCommandService remoteCommandService, ISystemDatabaseService systemDatabaseService, ILightManagerService lightManagerService,
  249. IMesService mesService)
  250. {
  251. _regionManager = regionManager;
  252. _eventAggregator = ea;
  253. _container = container;
  254. _configService = configService;
  255. _robotService = robotService;
  256. _cameraService = cameraService;
  257. _feederService = feederService;
  258. _plcService = plcService;
  259. _calibrationService = calibrationService;
  260. yieldtimer = new Timer(DoYieldTime, null, 10000, 1000);
  261. Renders = new ObservableCollection<ShowRender>();
  262. _productService = productService;
  263. _remoteCommandService = remoteCommandService;
  264. _systemDatabaseService = systemDatabaseService;
  265. _lightManagerService = lightManagerService;
  266. _mesService = mesService;
  267. _eventAggregator.GetEvent<CurrentStatusNotification>().Subscribe(CurrentRunStatus);
  268. }
  269. #region 读取配置参数
  270. public async Task ReadConfig()
  271. {
  272. await ReadNumber();
  273. }
  274. /// <summary>
  275. /// 读取数量配置
  276. /// </summary>
  277. public async Task ReadNumber()
  278. {
  279. try
  280. {
  281. NumStatistics = new NumberStatistics();
  282. var latestHeadChangeRecord = await _systemDatabaseService.GetLatestHeadChangeRecordAsync();
  283. if (latestHeadChangeRecord != null)
  284. {
  285. NumStatistics.HeaderNum = latestHeadChangeRecord.CurrentLockCount;
  286. }
  287. var latestNozzleChangeRecord = await _systemDatabaseService.GetLatestNozzleChangeRecordAsync();
  288. if (latestNozzleChangeRecord != null)
  289. {
  290. NumStatistics.NozzleNum = latestNozzleChangeRecord.CurrentLockCount;
  291. }
  292. NumStatistics.Feeder1Num = _configService.GetRemainingCountByFeederNumber(1);
  293. NumStatistics.Feeder2Num = _configService.GetRemainingCountByFeederNumber(2);
  294. }
  295. catch (Exception ex)
  296. {
  297. NumStatistics = new NumberStatistics();
  298. LogHelper.WriteLogError("加载数量配置文件时出错!", ex);
  299. }
  300. }
  301. #endregion
  302. #region 初始化硬件模块
  303. /// <summary>
  304. /// 初始化Feeder
  305. /// </summary>
  306. /// <returns></returns>
  307. public async Task<(bool IsSucceed, string Msg)> InitFeeders()
  308. {
  309. var feeders = _configService.GetAllFeeders();
  310. StringBuilder sb = new StringBuilder();
  311. foreach (var item in feeders)
  312. {
  313. _feederService.CreateFeeder(item.Id, item.FeederNo, item.FeederName, item.IP, item.Port, item.FeederBrand);
  314. var feeder = _feederService.GetFeeder(item.Id);
  315. Status.Add(new StatusInfo(item.Id, item.FeederName + $"{Lang.未连接}", new SolidColorBrush(Colors.Red)));
  316. try
  317. {
  318. feeder.FeederConnectionStatusChanged += Management_FeederConnectionStatusChanged;
  319. await feeder.ConnectAsync(); //连接Feeder
  320. //SendTaskMessage($"{item.FeederName}已连接!", MessageLevel.Debug);
  321. }
  322. catch (Exception ex)
  323. {
  324. sb.AppendLine($"{item.FeederName}:{Lang.断开连接}![{ex.Message}]");
  325. SendTaskMessage($"{item.FeederName}{Lang.断开连接}!", MessageLevel.Alarm);
  326. }
  327. }
  328. if (sb.Length == 0)
  329. {
  330. return (true, "");
  331. }
  332. else
  333. {
  334. return (false, sb.ToString());
  335. }
  336. }
  337. /// <summary>
  338. /// 初始化机器人
  339. /// </summary>
  340. /// <returns></returns>
  341. public async Task<(bool IsSucceed, string Msg)> InitRobots()
  342. {
  343. var robots = _configService.GetAllRobots();
  344. StringBuilder sb = new StringBuilder();
  345. foreach (var item in robots)
  346. {
  347. _robotService.CreateRobot(item.Id, item);
  348. try
  349. {
  350. Status.Add(new StatusInfo(item.Id, item.RobotName + $"{Lang.未连接}", new SolidColorBrush(Colors.Red)));
  351. var robot = _robotService.GetRobot(item.Id);
  352. robot.ReceivedEvent += Management_ReceivedEvent;
  353. robot.SendEvent += Management_SendEvent;
  354. robot.ConnectedEvent += Management_ConnectedEvent;
  355. robot.DisconnectedEvent += Management_DisconnectedEvent;
  356. await robot.ConnectAsync(); //连接Robot
  357. SendTaskMessage($"{item.RobotName}{Lang.已连接}!", MessageLevel.Debug);
  358. }
  359. catch (Exception ex)
  360. {
  361. sb.AppendLine($"{item.RobotName}:{Lang.断开连接}![{ex.Message}]");
  362. SendTaskMessage($"{item.RobotName}{Lang.断开连接}!", MessageLevel.Alarm);
  363. }
  364. }
  365. if (sb.Length == 0)
  366. {
  367. return (true, "");
  368. }
  369. else
  370. {
  371. return (false, sb.ToString());
  372. }
  373. }
  374. /// <summary>
  375. /// 初始化PLC
  376. /// </summary>
  377. /// <returns></returns>
  378. public async Task<(bool IsSucceed, string Msg)> InitPlc()
  379. {
  380. var plcs = _configService.GetAllPlcs();
  381. StringBuilder sb = new StringBuilder();
  382. foreach (var item in plcs)
  383. {
  384. try
  385. {
  386. _plcService.CreatePlc(item.Id, item);
  387. var plc = _plcService.GetPlc(item.Id);
  388. Status.Add(new StatusInfo(item.Id, item.Name + $"{Lang.未连接}", new SolidColorBrush(Colors.Red)));
  389. plc.ConnectChangedEvent += Management_PlcConnectChangedEvent;
  390. await plc.ConnectAsync(); //连接PLC
  391. //SendTaskMessage($"{item.Name}{Lang.已连接}!", MessageLevel.Debug);
  392. }
  393. catch (Exception ex)
  394. {
  395. sb.AppendLine($"{item.Name}:{Lang.断开连接}![{ex.Message}]");
  396. SendTaskMessage($"{item.Name}{Lang.断开连接}!", MessageLevel.Alarm);
  397. }
  398. }
  399. try
  400. {
  401. await WriteSystemParameterToPlcAsync(_configService.GetSystemConfiguration());
  402. RegisterPlcMonitorAddress();
  403. }
  404. catch (Exception ex)
  405. {
  406. LogHelper.WriteLogError(Lang.为PLC写入系统参数和订阅变量时出错, ex);
  407. }
  408. if (sb.Length == 0)
  409. {
  410. return (true, "");
  411. }
  412. else
  413. {
  414. return (false, sb.ToString());
  415. }
  416. }
  417. /// <summary>
  418. /// 初始化相机
  419. /// </summary>
  420. /// <returns></returns>
  421. public async Task<(bool IsSucceed, string Msg)> InitCameras()
  422. {
  423. var cameras = _configService.GetAllCameras();
  424. StringBuilder sb = new StringBuilder();
  425. foreach (var item in cameras)
  426. {
  427. try
  428. {
  429. //Renders.Add(new ShowRender() { Id = item.Id, CameraName = item.CameraName });
  430. _cameraService.CreateCamera(item.Id, item);
  431. var camera = _cameraService.GetCamera(item.Id);
  432. Status.Add(new StatusInfo(item.Id, item.CameraName + $"{Lang.未连接}", new SolidColorBrush(Colors.Red)));
  433. camera.CameraConnectChangedEvent += Management_CameraConnectChangedEvent;
  434. try
  435. {
  436. if (await camera.OpenDeviceAsync())
  437. {
  438. SendTaskMessage($"{item.CameraName}{Lang.已连接}!", MessageLevel.Debug);
  439. }
  440. else
  441. {
  442. SendTaskMessage($"{item.CameraName}{Lang.连接失败}!", MessageLevel.Alarm);
  443. sb.AppendLine($"{item.CameraName}:{Lang.连接失败}!");
  444. }
  445. }
  446. catch (Exception ex)
  447. {
  448. SendTaskMessage($"{item.CameraName}{ex.Message}!", MessageLevel.Alarm);
  449. sb.AppendLine($"{item.CameraName}:{ex.Message}!");
  450. }
  451. }
  452. catch (Exception ex)
  453. {
  454. SendTaskMessage($"{item.CameraName}{Lang.连接失败}!", MessageLevel.Alarm);
  455. sb.AppendLine($"{item.CameraName}:{Lang.连接失败}!:{ex.Message}");
  456. }
  457. }
  458. //_container.Resolve<ShowVisionRenderViewModel>().Renders = Renders;
  459. if (sb.Length == 0)
  460. {
  461. return (true, "");
  462. }
  463. else
  464. {
  465. return (false, sb.ToString());
  466. }
  467. }
  468. /// <summary>
  469. /// 初始化光源控制器
  470. /// </summary>
  471. /// <returns></returns>
  472. public async Task<(bool IsSucceed, string Msg)> InitLightControllers()
  473. {
  474. var lightcontrollers = _configService.GetAllLightControllers();
  475. StringBuilder sb = new StringBuilder();
  476. foreach (var item in lightcontrollers)
  477. {
  478. try
  479. {
  480. var lishtCtr = await _lightManagerService.RegisterControllerAsync(item.Id, item);
  481. //var lightcontroller = _cameraService.GetLightController(item.Id);
  482. //await lightcontroller.ConnectAsync(); //连接光源控制器
  483. //SendTaskMessage($"{item.LightControllerName}{Lang.已连接}!", MessageLevel.Debug);
  484. }
  485. catch (Exception ex)
  486. {
  487. //sb.AppendLine($"{item.LightControllerName}:{Lang.断开连接}![{ex.Message}]");
  488. //SendTaskMessage($"{item.LightControllerName}{Lang.断开连接}!", MessageLevel.Alarm);
  489. }
  490. }
  491. if (lightcontrollers.Count > 0)
  492. {
  493. var result = await _lightManagerService.ConnectAllAsync();
  494. if (result)
  495. {
  496. return (true, "");
  497. }
  498. else
  499. {
  500. return (false, "Light Controller Connect Failed!");
  501. }
  502. }
  503. return (true, "");
  504. //if (sb.Length == 0)
  505. //{
  506. // return (true, "");
  507. //}
  508. //else
  509. //{
  510. // return (false, sb.ToString());
  511. //}
  512. }
  513. /// <summary>
  514. /// 初始化后台TCP服务器通讯
  515. /// </summary>
  516. public void InitBgTcp()
  517. {
  518. var config = _configService.GetBgTcp();
  519. try
  520. {
  521. BgCommunicate = new BgCommunicate(config);
  522. BgCommunicate.ConnectedEvent += BgCommunicate_ConnectedEvent;
  523. BgCommunicate.DisconnectedEvent += BgCommunicate_DisconnectedEvent;
  524. BgCommunicate.ReceivedEvent += BgCommunicate_ReceivedEvent;
  525. BgCommunicate.SendEvent += BgCommunicate_SendEvent;
  526. BgCommunicate.StartListening();
  527. SendTaskMessage($"{Lang.已开启监听端口}:{config.Port}", MessageLevel.Debug);
  528. }
  529. catch (Exception ex)
  530. {
  531. SendTaskMessage($"{Lang.未开启监听端口}:{config.Port}", MessageLevel.Error);
  532. LogHelper.WriteLogError(Lang.开启后台服务器通讯时出错, ex);
  533. }
  534. }
  535. /// <summary>
  536. /// 初始化后台ModbusTCP从站
  537. /// </summary>
  538. public void InitModbusTcp()
  539. {
  540. try
  541. {
  542. var config = _configService.GetBgModbusTcp();
  543. BgModbusTcpCommunicate = new BgModbusTcpCommunicate(config);
  544. if (config.IsEnabled)
  545. {
  546. BgModbusTcpCommunicate.Listen();
  547. SendTaskMessage($"Start Modbus Tcp Slave:{BgModbusTcpCommunicate.IP}:{BgModbusTcpCommunicate.Port}:{BgModbusTcpCommunicate.SlaveID}", MessageLevel.Debug);
  548. }
  549. }
  550. catch (Exception ex)
  551. {
  552. SendTaskMessage($"No Start Modbus Tcp Slave:{ex.Message}", MessageLevel.Alarm);
  553. LogHelper.WriteLogError(Lang.开启后台ModbusTCP通讯时出错, ex);
  554. }
  555. }
  556. /// <summary>
  557. /// 初始化后台脚本
  558. /// </summary>
  559. public void InitBackgroundcript()
  560. {
  561. try
  562. {
  563. if (!Directory.Exists(FilePath.BackgroundScriptPath))
  564. {
  565. Directory.CreateDirectory(FilePath.BackgroundScriptPath);
  566. }
  567. try
  568. {
  569. string[] files = Directory.GetFiles(FilePath.BackgroundScriptPath);
  570. List<string> ScriptFilesCollection = new List<string>();
  571. foreach (string file in files)
  572. {
  573. string fileName = Path.GetFileName(file);
  574. string fileExtension = Path.GetExtension(file);
  575. if (fileExtension == ".cs")
  576. {
  577. ScriptFilesCollection.Add(file);
  578. }
  579. }
  580. foreach (var item in ScriptFilesCollection)
  581. {
  582. try
  583. {
  584. string script = FileHelper.ReadFile(item);
  585. SendTaskMessage($"{Lang.执行脚本}:{item}", MessageLevel.Info);
  586. Task.Factory.StartNew(() =>
  587. {
  588. ScriptHelper.ExecuteScriptingAsync(script, _regionManager, _eventAggregator, _container, rst =>
  589. {
  590. if (!rst.Item1)
  591. {
  592. foreach (var err in rst.Item2)
  593. {
  594. SendTaskMessage($"{Lang.后台脚本出错}:{err}", MessageLevel.Error);
  595. }
  596. }
  597. });
  598. });
  599. }
  600. catch (Exception ex)
  601. {
  602. LogHelper.WriteLogError(string.Format(Lang.开启脚本时出错0, item), ex);
  603. }
  604. }
  605. }
  606. catch (Exception ex)
  607. {
  608. LogHelper.WriteLogError(Lang.读取本地脚本文件列表时出错, ex);
  609. }
  610. }
  611. catch (Exception ex)
  612. {
  613. LogHelper.WriteLogError(Lang.开启后台脚本时出错, ex);
  614. }
  615. }
  616. /// <summary>
  617. /// 初始化电批
  618. /// </summary>
  619. /// <returns></returns>
  620. public async Task InitScrewDriver()
  621. {
  622. try
  623. {
  624. var screwDriverConfig = _configService.GetScrewDriverConfig();
  625. ScrewDriver = new XYD_ScrewDriver(screwDriverConfig.IPAdress, screwDriverConfig.Port);
  626. Status.Add(new StatusInfo(ScrewDriver.Id, Lang.电批未连接, new SolidColorBrush(Colors.Red)));
  627. ScrewDriver.ConnectStateChangedEvent += ScrewDriver_ConnectStateChangedEvent;
  628. ScrewDriver.SendTimeout = screwDriverConfig.SendTimeout;
  629. screwDriverConfig.ReceiveTimeout = screwDriverConfig.ReceiveTimeout;
  630. var isConnected = await ScrewDriver.ConnectAsync();
  631. if (isConnected)
  632. {
  633. SendTaskMessage(Lang.电批已连接, MessageLevel.Debug);
  634. }
  635. else
  636. {
  637. SendTaskMessage(Lang.电批连接失败, MessageLevel.Error);
  638. }
  639. }
  640. catch (Exception ex)
  641. {
  642. SendTaskMessage(string.Format(Lang.电批连接失败0, ex.Message), MessageLevel.Alarm);
  643. LogHelper.WriteLogError(Lang.连接电批时出错, ex);
  644. }
  645. }
  646. /// <summary>
  647. /// 初始化扭力测量仪
  648. /// </summary>
  649. /// <returns></returns>
  650. public async Task InitSignal()
  651. {
  652. var torqueConfig = _configService.GetTorqueReaderConfig();
  653. if (torqueConfig != null)
  654. {
  655. try
  656. {
  657. var port = FileHelper.ReadJsonFile<TorqueProduceModel>(FilePath.TorqueReaderConfigurationPath);
  658. if (string.IsNullOrEmpty(port.TorqueMeasureSerialPort))
  659. {
  660. client = new TouchSocket.SerialPorts.SerialPortClient();
  661. client.Connecting = (client1, e) => { return EasyTask.CompletedTask; };//即将连接到端口
  662. client.Connected = (client1, e) => { return EasyTask.CompletedTask; };//成功连接到端口
  663. client.Closing = (client1, e) => { return EasyTask.CompletedTask; };//即将从端口断开连接。此处仅主动断开才有效。
  664. client.Closed = (client1, e) => { return EasyTask.CompletedTask; };//从端口断开连接,当连接不成功时不会触发。
  665. client.Received = async (c, e) =>
  666. {
  667. //await Console.Out.WriteLineAsync(Encoding.UTF8.GetString(e.ByteBlock.Buffer));
  668. await Console.Out.WriteLineAsync(e.ByteBlock.Span.ToString(Encoding.ASCII));
  669. };
  670. client.Received = Sp_DataReceived;
  671. await client.SetupAsync(new TouchSocketConfig()
  672. .SetSerialPortOption(new SerialPortOption()
  673. {
  674. BaudRate = torqueConfig.BaudRate,//波特率
  675. DataBits = torqueConfig.DataBits,//数据位
  676. Parity = torqueConfig.Parity,//校验位
  677. PortName = torqueConfig.PortName,//COM
  678. StopBits = torqueConfig.StopBits//停止位
  679. })
  680. .SetSerialDataHandlingAdapter(() => new PeriodPackageAdapter() { CacheTimeout = TimeSpan.FromMilliseconds(100) })
  681. );
  682. await client.ConnectAsync();
  683. }
  684. else
  685. {
  686. SendTaskMessage("请设置扭力测量仪串口!", MessageLevel.Error);
  687. }
  688. }
  689. catch (Exception ex)
  690. {
  691. LogHelper.WriteLogError("初始化扭力测量仪时出错!", ex);
  692. SendTaskMessage("初始化扭力测量仪时出错!" + ex.Message, MessageLevel.Error);
  693. }
  694. }
  695. else
  696. {
  697. TorqueSerialPortConfig config = new TorqueSerialPortConfig();
  698. FileHelper.WriteJsonFile(config, FilePath.TorqueReaderConfigurationPath);
  699. }
  700. }
  701. #endregion
  702. #region 机器人指令执行
  703. public Dictionary<Guid, CancellationTokenSource> RobotTCPCollection = new Dictionary<Guid, CancellationTokenSource>();
  704. /// <summary>
  705. /// 机器人TCP接收
  706. /// </summary>
  707. /// <param name="arg1"></param>
  708. /// <param name="arg2"></param>
  709. /// <param name="arg3"></param>
  710. private async void Management_ReceivedEvent(Guid robotid, object arg2, TouchSocket.Sockets.ReceivedDataEventArgs e)
  711. {
  712. try
  713. {
  714. if (!RobotTCPCollection.ContainsKey(robotid))
  715. RobotTCPCollection.Add(robotid, new CancellationTokenSource());
  716. else
  717. {
  718. if (RobotTCPCollection[robotid].IsCancellationRequested)
  719. {
  720. RobotTCPCollection[robotid] = new CancellationTokenSource();
  721. }
  722. }
  723. var robot = await _robotService.GetRobotAsync(robotid);
  724. //从服务器收到信息。但是一般byteBlock和requestInfo会根据适配器呈现不同的值。
  725. var buffer = e.ByteBlock.Span.ToString(robot.GetEncoding());
  726. SendTaskMessage($"{robot.Name}Received:{buffer}", MessageLevel.Info);
  727. var _ = _remoteCommandService.ExecuteAsync(buffer, RobotTCPCollection[robotid].Token, async msg =>
  728. {
  729. try
  730. {
  731. if (robot.ConnectType == TCPConnectType.Client)
  732. {
  733. if (robot.IsConnected)
  734. {
  735. await robot.SendAsync($"{msg}");
  736. }
  737. }
  738. else
  739. {
  740. var cient = (ITcpSessionClient)arg2;
  741. if (cient.Online)
  742. {
  743. await robot.SendAsync(cient, $"{msg}");
  744. }
  745. }
  746. }
  747. catch (Exception ex)
  748. {
  749. LogHelper.WriteLogError(Lang.TCP发送数据时出错, ex);
  750. SendTaskMessage(ex.Message, MessageLevel.Alarm);
  751. }
  752. });
  753. }
  754. catch (Exception ex)
  755. {
  756. LogHelper.WriteLogError(Lang.处理机器人接收的命令时出错, ex);
  757. SendTaskMessage(ex.Message, MessageLevel.Alarm);
  758. }
  759. }
  760. #endregion
  761. #region 机器人&Feeder&相机连接状态事件
  762. /// <summary>
  763. /// 机器人发送事件
  764. /// </summary>
  765. /// <param name="arg1"></param>
  766. /// <param name="arg2"></param>
  767. /// <param name="arg3"></param>
  768. /// <exception cref="NotImplementedException"></exception>
  769. private void Management_SendEvent(Guid robotid, object arg2, string arg3)
  770. {
  771. var r = _robotService.GetRobot(robotid);
  772. SendTaskMessage($"{r.Name}Send:{arg3}", MessageLevel.Info);
  773. }
  774. /// <summary>
  775. /// 机器人断开连接时
  776. /// </summary>
  777. /// <param name="arg1"></param>
  778. /// <param name="arg2"></param>
  779. /// <param name="arg3"></param>
  780. /// <exception cref="NotImplementedException"></exception>
  781. private void Management_DisconnectedEvent(Guid robotid, object arg2, ClosedEventArgs arg3)
  782. {
  783. if (RobotTCPCollection.ContainsKey(robotid))
  784. {
  785. RobotTCPCollection[robotid].Cancel();
  786. }
  787. var r = _configService.GetRobot(robotid);
  788. if (r.ConnectType == TCPConnectType.Server)
  789. {
  790. ITcpSessionClient socketClient = arg2 as ITcpSessionClient;
  791. SendTaskMessage($"{r.RobotName}[{socketClient.Id}:{socketClient.IP}]{Lang.断开连接}!", MessageLevel.Error);
  792. var robot = _robotService.GetRobot(robotid);
  793. if (robot.TcpService.Count < 1)
  794. {
  795. var sta = Status.FirstOrDefault(s => s.ID == robotid);
  796. App.Current.Dispatcher.Invoke(new
  797. Action(() =>
  798. {
  799. sta.Message = $"{r.RobotName}{Lang.未连接}";
  800. sta.Background = new SolidColorBrush(Colors.Red);
  801. }));
  802. }
  803. }
  804. else
  805. {
  806. SendTaskMessage($"{r.RobotName}{Lang.断开连接}!", MessageLevel.Error);
  807. var sta = Status.FirstOrDefault(s => s.ID == robotid);
  808. App.Current.Dispatcher.Invoke(new
  809. Action(() =>
  810. {
  811. sta.Message = $"{r.RobotName}{Lang.断开连接}";
  812. sta.Background = new SolidColorBrush(Colors.Red);
  813. }));
  814. }
  815. }
  816. /// <summary>
  817. /// 机器人连接成功时
  818. /// </summary>
  819. /// <param name="arg1"></param>
  820. /// <param name="arg2"></param>
  821. /// <param name="arg3"></param>
  822. /// <exception cref="NotImplementedException"></exception>
  823. private void Management_ConnectedEvent(Guid robotid, object arg2, ConnectedEventArgs arg3)
  824. {
  825. var r = _configService.GetRobot(robotid);
  826. if (r.ConnectType == TCPConnectType.Server)
  827. {
  828. ITcpSessionClient socketClient = arg2 as ITcpSessionClient;
  829. SendTaskMessage($"{r.RobotName}[{socketClient.Id}:{socketClient.IP}]{Lang.已连接}!", MessageLevel.Debug);
  830. var robot = _robotService.GetRobot(robotid);
  831. if (robot.TcpService.Count > 0)
  832. {
  833. var sta = Status.FirstOrDefault(s => s.ID == robotid);
  834. App.Current.Dispatcher.Invoke(new
  835. Action(() =>
  836. {
  837. sta.Message = $"{r.RobotName}{Lang.已连接}";
  838. sta.Background = new SolidColorBrush(Colors.Green);
  839. }));
  840. }
  841. }
  842. else
  843. {
  844. SendTaskMessage($"{r.RobotName}{Lang.已连接}!", MessageLevel.Debug);
  845. var sta = Status.FirstOrDefault(s => s.ID == robotid);
  846. App.Current.Dispatcher.Invoke(new
  847. Action(() =>
  848. {
  849. sta.Message = $"{r.RobotName}{Lang.已连接}";
  850. sta.Background = new SolidColorBrush(Colors.Green);
  851. }));
  852. }
  853. }
  854. /// <summary>
  855. /// Feeder连接状态改变时
  856. /// </summary>
  857. /// <param name="sender"></param>
  858. /// <param name="state"></param>
  859. private void Management_FeederConnectionStatusChanged(object sender, bool state)
  860. {
  861. IFeeder feeder = sender as IFeeder;
  862. var sta = Status.FirstOrDefault(s => s.ID == feeder.Id);
  863. if (state)
  864. {
  865. App.Current.Dispatcher.Invoke(new
  866. Action(() =>
  867. {
  868. sta.Message = $"{feeder.Name}{Lang.已连接}";
  869. sta.Background = new SolidColorBrush(Colors.Green);
  870. }));
  871. SendTaskMessage($"{feeder.Name}{Lang.已连接}!", MessageLevel.Debug);
  872. }
  873. else
  874. {
  875. App.Current.Dispatcher.Invoke(new
  876. Action(() =>
  877. {
  878. sta.Message = $"{feeder.Name}{Lang.未连接}";
  879. sta.Background = new SolidColorBrush(Colors.Red);
  880. }));
  881. SendTaskMessage($"{feeder.Name}{Lang.断开连接}!", MessageLevel.Error);
  882. }
  883. }
  884. /// <summary>
  885. /// 相机连接或者断开时
  886. /// </summary>
  887. /// <param name="id"></param>
  888. /// <param name="state"></param>
  889. private void Management_CameraConnectChangedEvent(Guid id, bool state)
  890. {
  891. var sta = Status.FirstOrDefault(s => s.ID == id);
  892. var camera = _configService.GetCamera(id);
  893. if (sta != null && camera != null)
  894. {
  895. if (state)
  896. {
  897. App.Current.Dispatcher.Invoke(new Action(() =>
  898. {
  899. sta.Message = $"{camera.CameraName}{Lang.已连接}";
  900. sta.Background = new SolidColorBrush(Colors.Green);
  901. }));
  902. }
  903. else
  904. {
  905. App.Current.Dispatcher.Invoke(new Action(() =>
  906. {
  907. sta.Message = $"{camera.CameraName}{Lang.断开连接}";
  908. sta.Background = new SolidColorBrush(Colors.Red);
  909. }));
  910. }
  911. }
  912. }
  913. /// <summary>
  914. /// PLC连接状态改变事件
  915. /// </summary>
  916. /// <param name="plc"></param>
  917. /// <param name="isconnected"></param>
  918. private async void Management_PlcConnectChangedEvent(object sender, bool isconnected)
  919. {
  920. IPlc plc = sender as IPlc;
  921. var sta = Status.FirstOrDefault(s => s.ID == plc.Id);
  922. if (plc.IsConnected)
  923. {
  924. SendTaskMessage($"{plc.Name}{Lang.已连接}!", MessageLevel.Debug);
  925. if (sta == null) return;
  926. App.Current.Dispatcher.Invoke(new
  927. Action(() =>
  928. {
  929. sta.Message = $"{plc.Name}{Lang.已连接}";
  930. sta.Background = new SolidColorBrush(Colors.Green);
  931. }));
  932. try
  933. {
  934. await WriteSystemParameterToPlcAsync(_configService.GetSystemConfiguration());
  935. await WriteParameterToPlcExAsync();
  936. }
  937. catch (Exception ex)
  938. {
  939. LogHelper.WriteLogError("PLC连接成功时,写入系统参数和点位信息时出错!", ex);
  940. }
  941. }
  942. else
  943. {
  944. SendTaskMessage($"{plc.Name}{Lang.断开连接}!", MessageLevel.Error);
  945. if (sta == null) return;
  946. App.Current.Dispatcher.Invoke(new
  947. Action(() =>
  948. {
  949. sta.Message = $"{plc.Name}{Lang.未连接}";
  950. sta.Background = new SolidColorBrush(Colors.Red);
  951. }));
  952. }
  953. }
  954. /// <summary>
  955. /// 电批连接状态改变时
  956. /// </summary>
  957. /// <param name="screwDevice"></param>
  958. /// <param name="state"></param>
  959. private void ScrewDriver_ConnectStateChangedEvent(object screwDevice, bool state)
  960. {
  961. try
  962. {
  963. XYD_ScrewDriver screwDriver = screwDevice as XYD_ScrewDriver;
  964. if (state)
  965. {
  966. SendTaskMessage(Lang.电批已连接, MessageLevel.Debug);
  967. var sta = Status.FirstOrDefault(s => s.ID == screwDriver.Id);
  968. App.Current.Dispatcher.Invoke(new
  969. Action(() =>
  970. {
  971. sta.Message = Lang.电批已连接;
  972. sta.Background = new SolidColorBrush(Colors.Green);
  973. }));
  974. }
  975. else
  976. {
  977. SendTaskMessage(Lang.电批未连接, MessageLevel.Error);
  978. var sta = Status.FirstOrDefault(s => s.ID == screwDriver.Id);
  979. App.Current.Dispatcher.Invoke(new
  980. Action(() =>
  981. {
  982. sta.Message = Lang.电批未连接;
  983. sta.Background = new SolidColorBrush(Colors.Red);
  984. }));
  985. }
  986. }
  987. catch (Exception ex)
  988. {
  989. LogHelper.WriteLogError(Lang.电批连接状态改变时出错了, ex);
  990. }
  991. }
  992. #endregion
  993. #region 后台服务器通讯事件
  994. private void BgCommunicate_SendEvent(ITcpSessionClient client, string msg)
  995. {
  996. SendTaskMessage($"{Lang.服务器}[{client.ServicePort}] Send To {Lang.客户端}[{client.Id}:{client.IP}]:{msg}", MessageLevel.Info);
  997. }
  998. public Dictionary<string, CancellationTokenSource> BgCommunicateollection = new Dictionary<string, CancellationTokenSource>();
  999. private async void BgCommunicate_ReceivedEvent(ITcpSessionClient client, string msg)
  1000. {
  1001. try
  1002. {
  1003. if (!BgCommunicateollection.ContainsKey(client.Id))
  1004. {
  1005. BgCommunicateollection.Add(client.Id, new CancellationTokenSource());
  1006. }
  1007. SendTaskMessage($"{Lang.服务器}[{client.ServicePort}]ReceiveFor{Lang.客户端}[{client.Id}:{client.IP}]:{msg}", MessageLevel.Info);
  1008. var _ = _remoteCommandService.ExecuteAsync(msg, BgCommunicateollection[client.Id].Token, async response =>
  1009. {
  1010. try
  1011. {
  1012. if (!client.Online)
  1013. {
  1014. return;
  1015. }
  1016. await BgCommunicate.SendAsync(client, $"{response}");
  1017. }
  1018. catch (Exception ex)
  1019. {
  1020. LogHelper.WriteLogError(Lang.后台TCP发送数据时出错, ex);
  1021. SendTaskMessage(ex.Message, MessageLevel.Alarm);
  1022. }
  1023. });
  1024. }
  1025. catch (Exception ex)
  1026. {
  1027. LogHelper.WriteLogError(Lang.处理后台服务器接收的命令时出错, ex);
  1028. SendTaskMessage(ex.Message, MessageLevel.Alarm);
  1029. }
  1030. }
  1031. private void BgCommunicate_DisconnectedEvent(ITcpSessionClient client, ClosedEventArgs e)
  1032. {
  1033. SendTaskMessage($"{Lang.服务器}[{client.ServicePort}]{Lang.客户端}[{client.Id}:{client.IP}]{Lang.断开连接}!", MessageLevel.Alarm);
  1034. if (BgCommunicateollection.ContainsKey(client.Id))
  1035. {
  1036. BgCommunicateollection[client.Id].Cancel();
  1037. }
  1038. }
  1039. private void BgCommunicate_ConnectedEvent(ITcpSessionClient client, ConnectedEventArgs e)
  1040. {
  1041. SendTaskMessage($"{Lang.服务器}[{client.ServicePort}]{Lang.客户端}[{client.Id}:{client.IP}]{Lang.连接成功}!", MessageLevel.Debug);
  1042. }
  1043. /// <summary>
  1044. /// 扭力测量仪接收信息
  1045. /// </summary>
  1046. /// <param name="client"></param>
  1047. /// <param name="e"></param>
  1048. /// <returns></returns>
  1049. private async Task Sp_DataReceived(ISerialPortClient client, ReceivedDataEventArgs e)
  1050. {
  1051. try
  1052. {
  1053. string poname = client.MainSerialPort.PortName;
  1054. string data = e.ByteBlock.Span.ToString(Encoding.ASCII);
  1055. await Console.Out.WriteLineAsync(data);
  1056. string response = data.Split('\r')[0];
  1057. SendTaskMessage($"{poname}Receive:{response}", MessageLevel.Info);
  1058. SignalValue = "";
  1059. SignalValue = response;
  1060. }
  1061. catch (Exception ex)
  1062. {
  1063. LogHelper.WriteLogError("扭力测量仪接收信息时出错!", ex);
  1064. SendTaskMessage("扭力测量仪接收信息时出错!" + ex.Message, MessageLevel.Error);
  1065. Thread.Sleep(50);
  1066. }
  1067. }
  1068. #endregion
  1069. #region PLC
  1070. /// <summary>
  1071. /// 将系统参数写入PLC
  1072. /// </summary>
  1073. /// <param name="systemConfiguration"></param>
  1074. /// <returns></returns>
  1075. public async Task WriteSystemParameterToPlcAsync(SystemConfiguration systemConfiguration)
  1076. {
  1077. var addressConfig = _configService.GetPlcAddresses();
  1078. var plc = _plcService.GetPlcByNumber(addressConfig.PlcNo) as OpcUaClientPLC;
  1079. if (plc == null || !plc.IsConnected) return;
  1080. Dictionary<string, object> nodeValues = new Dictionary<string, object>();
  1081. nodeValues.Add(addressConfig.Out_WorkMode.Address, (Int16)systemConfiguration.WorkMode);
  1082. nodeValues.Add(addressConfig.Out_PickPointNum.Address, (Int16)systemConfiguration.PickPointNum);
  1083. nodeValues.Add(addressConfig.Out_DowmCameraNum.Address, (Int16)systemConfiguration.DowmCameraNum);
  1084. nodeValues.Add(addressConfig.Out_UpCameraPickNum.Address, (Int16)systemConfiguration.UpCameraPickNum);
  1085. nodeValues.Add(addressConfig.Out_UpCameraPutNum.Address, (Int16)systemConfiguration.UpCameraPutNum);
  1086. nodeValues.Add(addressConfig.Out_UPCameracheckNum.Address, (Int16)systemConfiguration.UPCameracheckNum);
  1087. nodeValues.Add(addressConfig.Out_FixedCameraPickNum.Address, (Int16)systemConfiguration.FixedCameraPickNum);
  1088. nodeValues.Add(addressConfig.Out_FixedCameraPutNum.Address, (Int16)systemConfiguration.FixedCameraPutNum);
  1089. nodeValues.Add(addressConfig.Out_FixedCameracheckNum.Address, (Int16)systemConfiguration.FixedCameracheckNum);
  1090. nodeValues.Add(addressConfig.Out_ScrewTeachNum.Address, (Int16)systemConfiguration.ScrewTeachNum);
  1091. var res = await plc.WriteNodesAsync(nodeValues);
  1092. }
  1093. /// <summary>
  1094. /// 注册需要监控的PLC地址
  1095. /// </summary>
  1096. public void RegisterPlcMonitorAddress()
  1097. {
  1098. var addressConfig = _configService.GetPlcAddresses();
  1099. var plc = _plcService.GetPlcByNumber(addressConfig.PlcNo) as OpcUaClientPLC;
  1100. if (plc == null || !plc.IsConnected) return;
  1101. //PLC地址数组
  1102. List<string> monitorNodeIds = new List<string>()
  1103. {
  1104. addressConfig.In_DowmCameraExe.Address,
  1105. addressConfig.In_UpCameraPickExe.Address,
  1106. addressConfig.In_UpCameraPutExe.Address,
  1107. addressConfig.In_UPCameracheckExe.Address,
  1108. addressConfig.In_FixedCameraPickExe.Address,
  1109. addressConfig.In_FixedCameraPutExe.Address,
  1110. addressConfig.In_FixedCameracheckExe.Address,
  1111. addressConfig.In_WorkStart.Address,
  1112. addressConfig.In_WorkDone.Address,
  1113. //addressConfig.In_MesCheck.Address,
  1114. addressConfig.In_RequestPosition.Address,
  1115. addressConfig.In_QrCodePhotoRequest.Address,
  1116. addressConfig.In_ScrewTeachExe.Address,
  1117. addressConfig.In_Screw_checkExe.Address,
  1118. addressConfig.In_PickINC.Address,
  1119. addressConfig.In_Throw.Address,
  1120. addressConfig.RunMode.Address,
  1121. addressConfig.AutoRunning.Address,
  1122. addressConfig.ManualMode.Address,
  1123. addressConfig.ClearMode.Address,
  1124. addressConfig.Fault.Address,
  1125. addressConfig.In_FzCameraPixExe.Address,
  1126. };
  1127. var systemconfig = _configService.GetSystemConfiguration();
  1128. for (int i = 0; i < 10; i++)
  1129. {
  1130. if (systemconfig.DoubleSoftMode && i == 1)
  1131. {
  1132. continue;
  1133. }
  1134. monitorNodeIds.Add(string.Format(addressConfig.In_MesCheck.Address, i));
  1135. }
  1136. //订阅PLC地址变化
  1137. //plc.SubscribeNodes("AutoSensor", monitorNodeIds, PlcCommandTrigger, OpcUaSubscriptionMode.CustomPolling);
  1138. plc.SubscribeNodes("AutoSensor", monitorNodeIds, PlcCommandTrigger, OpcUaSubscriptionMode.CustomPolling);
  1139. // 2. 监听重连事件,重连成功后自动重新订阅
  1140. plc.ConnectChangedEvent -= Plc_ConnectChanged; // 防止重复注册
  1141. plc.ConnectChangedEvent += Plc_ConnectChanged;
  1142. // 定义重连处理方法(写在同一个类里即可)
  1143. void Plc_ConnectChanged(object sender, bool isConnected)
  1144. {
  1145. if (isConnected)
  1146. {
  1147. // 重连成功后,重新订阅节点
  1148. plc.SubscribeNodes("AutoSensor", monitorNodeIds, PlcCommandTrigger, OpcUaSubscriptionMode.CustomPolling);
  1149. }
  1150. }
  1151. }
  1152. /// <summary>
  1153. /// 将产品的参数统一写入PLC
  1154. /// </summary>
  1155. /// <returns></returns>
  1156. public async Task WriteParameterToPlcExAsync()
  1157. {
  1158. await WritePositionToPlcAsync();// 写入点位信息给PLC
  1159. //await WriteParameterToPlcAsync();// 写入组装上下限给PLC
  1160. await WriteProductLoadModeToPLC();//把当前产品型号,写入PLC
  1161. }
  1162. /// <summary>
  1163. /// 写入点位信息给PLC
  1164. /// </summary>
  1165. /// <returns></returns>
  1166. public async Task WritePositionToPlcAsync()
  1167. {
  1168. var addressConfig = _configService.GetPlcAddresses();
  1169. var plc = _plcService.GetPlcByNumber(addressConfig.PlcNo) as OpcUaClientPLC;
  1170. if (plc == null || !plc.IsConnected) return;
  1171. var current = _productService.GetCurrentProduct();
  1172. if (current == null) return;
  1173. _LastAutoRunning = plc.ReadNode<bool>(addressConfig.AutoRunning.Address);
  1174. if (current.PickCameraPoints != null && current.PickCameraPoints.Count > 0)
  1175. {
  1176. foreach (var item in current.PickCameraPoints)
  1177. {
  1178. await item.WriteToPlcAddress(addressConfig.Out_PickCamera, plc);
  1179. }
  1180. }
  1181. if (current.PickPoints != null && current.PickPoints.Count > 0)
  1182. {
  1183. foreach (var item in current.PickPoints)
  1184. {
  1185. await item.WriteToPlcAddress(addressConfig.Out_Pick, plc, item.Feeder);
  1186. //await item.WriteToPlcAddress(addressConfig.Out_Pick, plc);
  1187. }
  1188. }
  1189. if (current.SecPosCameraPoints != null && current.SecPosCameraPoints.Count > 0)
  1190. {
  1191. foreach (var item in current.SecPosCameraPoints)
  1192. {
  1193. await item.WriteToPlcAddress(addressConfig.Out_SecPosCamera, plc);
  1194. }
  1195. }
  1196. if (current.ScrewCameraPoints != null && current.ScrewCameraPoints.Count > 0)
  1197. {
  1198. int index = 0;
  1199. foreach (var item in current.ScrewCameraPoints)
  1200. {
  1201. //根据使用的点位进行排序
  1202. if (item.IsUse)
  1203. {
  1204. index++;
  1205. await item.WriteToPlcAddress(addressConfig.Out_ScrewCamera, plc, index);
  1206. }
  1207. }
  1208. }
  1209. if (current.ScrewPoints != null && current.ScrewPoints.Count > 0)
  1210. {
  1211. int index = 0;
  1212. foreach (var item in current.ScrewPoints)
  1213. {
  1214. if (item.IsUse)
  1215. {
  1216. index++;
  1217. await item.WriteToPlcAddressEx(addressConfig.Out_Screw, plc, index);
  1218. }
  1219. }
  1220. await plc.WriteNodeAsync(addressConfig.Out_WorkNum.Address, (Int16)index);
  1221. }
  1222. if (current.CheckCameraPoints != null && current.CheckCameraPoints.Count > 0)
  1223. {
  1224. int index = 0;
  1225. foreach (var item in current.CheckCameraPoints)
  1226. {
  1227. if (item.IsUse)
  1228. {
  1229. index++;
  1230. await item.WriteToPlcAddress(addressConfig.Out_CheckCamera, plc, index);
  1231. }
  1232. }
  1233. }
  1234. if (current.QrCodePoints != null && current.QrCodePoints.Count > 0)
  1235. {
  1236. foreach (var item in current.QrCodePoints)
  1237. {
  1238. await item.WriteToPlcAddress(addressConfig.Out_QrCodeCamera, plc);
  1239. }
  1240. }
  1241. }
  1242. /// <summary>
  1243. /// 将当前产品类型写入PLC
  1244. /// </summary>
  1245. /// <param name="mode"></param>
  1246. public async Task WriteProductLoadModeToPLC(int modeIndex)
  1247. {
  1248. var addressConfig = _configService.GetPlcAddresses();
  1249. var plc = _plcService.GetPlcByNumber(addressConfig.PlcNo) as OpcUaClientPLC;
  1250. if (plc == null || !plc.IsConnected) return;
  1251. await plc.WriteNodeAsync(addressConfig.Out_ProductLoadMode.Address, (Int16)modeIndex);
  1252. }
  1253. /// <summary>
  1254. /// 将当前产品类型写入PLC
  1255. /// </summary>
  1256. /// <param name="mode"></param>
  1257. public async Task WriteProductLoadModeToPLC()
  1258. {
  1259. var addressConfig = _configService.GetPlcAddresses();
  1260. var plc = _plcService.GetPlcByNumber(addressConfig.PlcNo) as OpcUaClientPLC;
  1261. if (plc == null || !plc.IsConnected) return;
  1262. var current = _productService.GetCurrentProduct();
  1263. if (current == null) return;
  1264. var qMes = await _systemDatabaseService.QueryProductFromMesAsync(current.Name);
  1265. if (qMes == null) return;
  1266. await plc.WriteNodeAsync(addressConfig.Out_ProductLoadMode.Address, (Int16)qMes.ProductLoadMode);
  1267. }
  1268. PointF[] DowmCameraResults = new PointF[10];
  1269. PointF[] UpCameraPutResults = new PointF[10];
  1270. bool _LastDowmCameraExe = false;
  1271. bool _LastScrewTeachExe = false;
  1272. bool _LastUpCameraPickExe = false;
  1273. bool _LastScrewFuJianExe = false;
  1274. bool _LastUpCameraPutExe = false;
  1275. bool _LastUPCameracheckExe = false;
  1276. bool _LastFixedCameraPickExe = false;
  1277. bool _LastFixedCameraPutExe = false;
  1278. bool _LastFixedCameracheckExe = false;
  1279. bool _LastFzCameracheckExe = false;
  1280. bool _LastThrowExe = false;
  1281. bool _LastRunModel = false;
  1282. public bool _LastAutoRunning = false;
  1283. bool _LastManualMode = false;
  1284. bool _LastClearMode = false;
  1285. bool _LastFault = false;
  1286. Int16 _LastWorkStart = 0;
  1287. Int16 _LastWorkDone = 0;
  1288. Int16 _LastRequestPosition = 0;
  1289. Int16 _LastQrCodePhotoRequest = 0;
  1290. Int16[] _LastMesCheck = new Int16[10];
  1291. Int16 _LastPickINC = 0;
  1292. DateTime[] _LastMesCheckTime = new DateTime[10];
  1293. Dictionary<string, string> mes_cc = new Dictionary<string, string>();
  1294. Dictionary<string, double> Point = new Dictionary<string, double>();
  1295. string[] ProductMainSN { get; set; } = new string[10];//产品主sn
  1296. string[] model_num { get; set; } = new string[10];//产品model_num
  1297. string[] wo { get; set; } = new string[10];//产品wo
  1298. string[] color { get; set; } = new string[10];//产品color
  1299. string[] ppid { get; set; } = new string[10];//产品ppid
  1300. string[] revision { get; set; } = new string[10];//产品revision
  1301. Dictionary<string, string> dataToMes = new Dictionary<string, string>();//上传mes的数据
  1302. List<string> resultToMes { get; set; } = new List<string>();//上传mes的数据
  1303. /// <summary>
  1304. /// PLC命令触发时
  1305. /// </summary>
  1306. /// <param name="tuple"></param>
  1307. private async void PlcCommandTrigger((string key, string nodeId, object value) tuple)
  1308. {
  1309. try
  1310. {
  1311. if (tuple.key != "AutoSensor")
  1312. {
  1313. SendTaskMessage("PLC指令错误", MessageLevel.Debug);
  1314. return;
  1315. }
  1316. var addressConfig = _configService.GetPlcAddresses();
  1317. var plc = _plcService.GetPlcByNumber(addressConfig.PlcNo) as OpcUaClientPLC;
  1318. if (plc == null || !plc.IsConnected)
  1319. {
  1320. SendTaskMessage("plc为空", MessageLevel.Debug);
  1321. return;
  1322. }
  1323. // 获取当前产品
  1324. var currentProduct = _productService.GetCurrentProduct();
  1325. if (currentProduct == null)
  1326. {
  1327. return;
  1328. }
  1329. ////获取产品编码
  1330. //_productService.CurrentProductCode = plc.ReadNode<string>(string.Format(addressConfig.In_Code.Address, 0));
  1331. ////如果产品编码为空,则以当前的时间戳作为编码
  1332. //if (string.IsNullOrEmpty(_productService.CurrentProductCode))
  1333. //{
  1334. // _productService.CurrentProductCode = DateTime.Now.ToString("yyyyMMddHHmmssfff");
  1335. //}
  1336. // 触发下相机拍照
  1337. if (addressConfig.In_DowmCameraExe.Address.Contains(tuple.nodeId))
  1338. {
  1339. if (tuple.value is bool state)
  1340. {
  1341. if (_LastDowmCameraExe != state)
  1342. {
  1343. _LastDowmCameraExe = state;
  1344. }
  1345. else
  1346. {
  1347. return;
  1348. }
  1349. if (state)
  1350. {
  1351. SendTaskMessage(Lang.触发下相机拍照, MessageLevel.Debug);
  1352. //相机的编号
  1353. Int16 cameraIndex = plc.ReadNode<Int16>(addressConfig.In_DowmCameraNum.Address);
  1354. SendTaskMessage(string.Format(Lang.编号0, cameraIndex), MessageLevel.Info);
  1355. // 获取流程
  1356. var process = _productService.GetCurrentProductProcedureModelById(currentProduct.FixedUpCamera1ProcedureId);
  1357. if (process == null)
  1358. {
  1359. SendTaskMessage(string.Format(Lang.固定向上相机1视觉流程流程ID0, currentProduct.FixedUpCamera1ProcedureId), MessageLevel.Alarm);
  1360. await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)2);
  1361. return;
  1362. }
  1363. // 获取机器人当前坐标
  1364. var robot = _robotService.GetAllRobots().FirstOrDefault();
  1365. if (robot == null)
  1366. {
  1367. SendTaskMessage(Lang.未找到机器人无法执行上相机锁附组装拍照视觉任务, MessageLevel.Alarm);
  1368. await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)2);
  1369. return;
  1370. }
  1371. var currentPosition = robot.GetRobotPos();
  1372. // 执行视觉任务
  1373. var _cts = new CancellationTokenSource();
  1374. ImageParameters imgPara = new ImageParameters();
  1375. imgPara.SNCode = productSn;
  1376. var visionResult = await _remoteCommandService.ExecutePhotoGetSinglePoint(process, null, null, _cts.Token, cameraIndex, cameraIndex, null, imgPara);
  1377. if (visionResult.IsSucceed)
  1378. {
  1379. //披头偏差
  1380. float offsetX = (float)(visionResult.X - currentPosition.X);
  1381. float offsetY = (float)(visionResult.Y - currentPosition.Y);
  1382. DowmCameraResults[cameraIndex] = new PointF(offsetX, offsetY);
  1383. await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)1);
  1384. return;
  1385. }
  1386. else
  1387. {
  1388. await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)2);
  1389. return;
  1390. }
  1391. }
  1392. else
  1393. {
  1394. SendTaskMessage(Lang.触发下相机拍照信号已关闭, MessageLevel.Alarm);
  1395. await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)0);
  1396. }
  1397. }
  1398. }
  1399. // 触发下相机拍照披头
  1400. if (addressConfig.In_ScrewTeachExe.Address.Contains(tuple.nodeId))
  1401. {
  1402. if (tuple.value is bool state)
  1403. {
  1404. if (_LastScrewTeachExe != state)
  1405. {
  1406. _LastScrewTeachExe = state;
  1407. }
  1408. else
  1409. {
  1410. return;
  1411. }
  1412. if (state)
  1413. {
  1414. SendTaskMessage(Lang.触发下相机拍照, MessageLevel.Debug);
  1415. //相机的编号
  1416. Int16 cameraIndex = plc.ReadNode<Int16>(addressConfig.In_DowmCameraNum.Address);
  1417. SendTaskMessage(string.Format(Lang.编号0, cameraIndex), MessageLevel.Info);
  1418. // 获取流程
  1419. var process = _productService.GetCurrentProductProcedureModelById(currentProduct.FixedUpCamera1ProcedureId);
  1420. if (process == null)
  1421. {
  1422. SendTaskMessage(string.Format(Lang.固定向上相机1视觉流程流程ID0, currentProduct.FixedUpCamera1ProcedureId), MessageLevel.Alarm);
  1423. await plc.WriteNodeAsync(addressConfig.Out_ScrewTeachOk.Address, (Int16)2);
  1424. return;
  1425. }
  1426. // 获取机器人当前坐标
  1427. var robot = _robotService.GetAllRobots().FirstOrDefault();
  1428. if (robot == null)
  1429. {
  1430. SendTaskMessage(Lang.未找到机器人无法执行上相机锁附组装拍照视觉任务, MessageLevel.Alarm);
  1431. await plc.WriteNodeAsync(addressConfig.Out_ScrewTeachOk.Address, (Int16)2);
  1432. return;
  1433. }
  1434. var currentPosition = robot.GetRobotPos();
  1435. // 执行视觉任务
  1436. var _cts = new CancellationTokenSource();
  1437. ImageParameters imgPara = new ImageParameters();
  1438. imgPara.SNCode = productSn;
  1439. var visionResult = await _remoteCommandService.ExecutePhotoGetSinglePoint(process, null, null, _cts.Token, cameraIndex, cameraIndex, null, imgPara);
  1440. if (visionResult.IsSucceed)
  1441. {
  1442. //披头偏差
  1443. float offsetX = (float)(visionResult.X - currentPosition.X);
  1444. float offsetY = (float)(visionResult.Y - currentPosition.Y);
  1445. DowmCameraResults[cameraIndex] = new PointF(offsetX, offsetY);
  1446. await plc.WriteNodeAsync(addressConfig.Out_ScrewTeachOk.Address, (Int16)1);
  1447. return;
  1448. }
  1449. else
  1450. {
  1451. await plc.WriteNodeAsync(addressConfig.Out_ScrewTeachOk.Address, (Int16)2);
  1452. return;
  1453. }
  1454. }
  1455. else
  1456. {
  1457. SendTaskMessage(Lang.触发下相机拍照信号已关闭, MessageLevel.Alarm);
  1458. await plc.WriteNodeAsync(addressConfig.Out_ScrewTeachOk.Address, (Int16)0);
  1459. }
  1460. }
  1461. }
  1462. // 触发上相机取料拍照
  1463. else if (addressConfig.In_UpCameraPickExe.Address.Contains(tuple.nodeId))
  1464. {
  1465. if (tuple.value is bool state)
  1466. {
  1467. if (_LastUpCameraPickExe != state)
  1468. {
  1469. _LastUpCameraPickExe = state;
  1470. }
  1471. else
  1472. {
  1473. return;
  1474. }
  1475. if (state)
  1476. {
  1477. SendTaskMessage(Lang.触发上相机取料拍照, MessageLevel.Debug);
  1478. //相机的编号
  1479. //Int16 cameraIndex = plc.ReadNode<Int16>(addressConfig.In_UpCameraPickNum.Address);
  1480. await plc.WriteNodeAsync(addressConfig.Out_UpCameraPickStatus.Address, (Int16)1);
  1481. }
  1482. else
  1483. {
  1484. SendTaskMessage(Lang.触发上相机取料拍照信号已关闭, MessageLevel.Alarm);
  1485. await plc.WriteNodeAsync(addressConfig.Out_UpCameraPickStatus.Address, (Int16)0);
  1486. }
  1487. }
  1488. }
  1489. // 上相机锁附/组装拍照
  1490. else if (addressConfig.In_UpCameraPutExe.Address.Contains(tuple.nodeId))
  1491. {
  1492. if (tuple.value is bool state)
  1493. {
  1494. if (_LastUpCameraPutExe != state)
  1495. {
  1496. _LastUpCameraPutExe = state;
  1497. }
  1498. else
  1499. {
  1500. return;
  1501. }
  1502. if (state)
  1503. {
  1504. SendTaskMessage(Lang.触发上相机锁附组装拍照, MessageLevel.Debug);
  1505. //相机的编号
  1506. Int16 cameraIndex = plc.ReadNode<Int16>(addressConfig.In_UpCameraPutNum.Address);
  1507. SendTaskMessage(string.Format(Lang.编号0, cameraIndex), MessageLevel.Info);
  1508. //视觉流程ID
  1509. Guid procedureId = currentProduct.MoveDownCameraLockPhotoProcedureId;
  1510. List<PlcPoint> plcPoints = new List<PlcPoint>();
  1511. foreach (var item in currentProduct.ScrewCameraPoints)
  1512. {
  1513. if (item.IsUse)
  1514. {
  1515. plcPoints.Add(item);
  1516. }
  1517. }
  1518. int num = plcPoints.Count;
  1519. //获取锁付拍照的点位
  1520. var pos = plcPoints[cameraIndex - 1];
  1521. if (pos != null)
  1522. {
  1523. procedureId = pos.VisionProcessID;
  1524. }
  1525. // 获取流程
  1526. var process = _productService.GetCurrentProductProcedureModelById(procedureId);
  1527. if (process == null)
  1528. {
  1529. SendTaskMessage(string.Format(Lang.未找到上相机锁附组装拍照对应的视觉流程流程ID0, procedureId), MessageLevel.Alarm);
  1530. await plc.WriteNodeAsync(addressConfig.Out_UpCameraPutStatus.Address, (Int16)2);
  1531. return;
  1532. }
  1533. // 获取机器人当前坐标
  1534. var robot = _robotService.GetAllRobots().FirstOrDefault();
  1535. if (robot == null)
  1536. {
  1537. SendTaskMessage(Lang.未找到机器人无法执行上相机锁附组装拍照视觉任务, MessageLevel.Alarm);
  1538. await plc.WriteNodeAsync(addressConfig.Out_UpCameraPutStatus.Address, (Int16)2);
  1539. return;
  1540. }
  1541. var currentPosition = robot.GetRobotPos();
  1542. var systemConfig = _configService.GetSystemConfiguration();
  1543. // 执行视觉任务
  1544. var _cts = new CancellationTokenSource();
  1545. ImageParameters imgPara = new ImageParameters();
  1546. imgPara.SNCode = productSn;
  1547. var visionResult = await _remoteCommandService.ExecutePhotoGetSinglePoint(process, null, new double[] { currentPosition.X, currentPosition.Y, currentPosition.U }, _cts.Token, cameraIndex, num, null, imgPara);
  1548. if (visionResult.IsSucceed)
  1549. {
  1550. //是拍一个锁一个还是拍两个点锁所有
  1551. if (systemConfig.IsBasePartOpenLightKB)
  1552. {
  1553. if (mes_cc.ContainsKey(visionResult.distance.Split(',')[0]))
  1554. {
  1555. mes_cc[visionResult.distance.Split(',')[0]] = visionResult.distance.Split(',')[1];
  1556. }
  1557. else
  1558. {
  1559. mes_cc.Add(visionResult.distance.Split(',')[0], visionResult.distance.Split(',')[1]);
  1560. }
  1561. }
  1562. //拍1打1
  1563. if (systemConfig.WorkMode == 1)
  1564. {
  1565. UpCameraPutResults[1] = new PointF((float)visionResult.X, (float)visionResult.Y);
  1566. }
  1567. else if(systemConfig.WorkMode==2)
  1568. {
  1569. UpCameraPutResults[cameraIndex] = new PointF((float)visionResult.X, (float)visionResult.Y);
  1570. }
  1571. else if(systemConfig.WorkMode==3)
  1572. {
  1573. if (cameraIndex <= 3)
  1574. {
  1575. UpCameraPutResults[1] = new PointF((float)visionResult.X, (float)visionResult.Y);
  1576. }
  1577. else
  1578. {
  1579. UpCameraPutResults[cameraIndex] = new PointF((float)visionResult.X, (float)visionResult.Y);
  1580. }
  1581. }
  1582. if (resultToMes.Contains($"Screw{cameraIndex}_Fail"))
  1583. {
  1584. resultToMes.Remove($"Screw{cameraIndex}_Fail");
  1585. SendTaskMessage($"Screw{cameraIndex}:重复拍照成功", MessageLevel.Debug);
  1586. }
  1587. await plc.WriteNodeAsync(addressConfig.Out_UpCameraPutStatus.Address, (Int16)1);
  1588. }
  1589. else
  1590. {
  1591. if (systemConfig.IsBasePartOpenLightKB)
  1592. {
  1593. if (mes_cc.ContainsKey(visionResult.distance.Split(',')[0]))
  1594. {
  1595. mes_cc[visionResult.distance.Split(',')[0]] = visionResult.distance.Split(',')[1];
  1596. }
  1597. else
  1598. {
  1599. if (mes_cc.ContainsKey(cameraIndex.ToString()))
  1600. {
  1601. mes_cc[cameraIndex.ToString()] = "0";
  1602. }
  1603. else
  1604. {
  1605. mes_cc.Add(cameraIndex.ToString(), "0");
  1606. }
  1607. }
  1608. }
  1609. if (!resultToMes.Contains($"Screw{cameraIndex}_Fail"))
  1610. {
  1611. resultToMes.Add($"Screw{cameraIndex}_Fail");
  1612. SendTaskMessage($"Screw{cameraIndex}:拍照失败", MessageLevel.Error);
  1613. }
  1614. await plc.WriteNodeAsync(addressConfig.Out_UpCameraPutStatus.Address, (Int16)2);
  1615. }
  1616. }
  1617. else
  1618. {
  1619. SendTaskMessage(Lang.触发上相机锁附组装拍照信号已关闭, MessageLevel.Alarm);
  1620. await plc.WriteNodeAsync(addressConfig.Out_UpCameraPutStatus.Address, (Int16)0);
  1621. }
  1622. }
  1623. }
  1624. // 上相机检测拍照
  1625. else if (addressConfig.In_UPCameracheckExe.Address.Contains(tuple.nodeId))
  1626. {
  1627. if (tuple.value is bool state)
  1628. {
  1629. if (_LastUPCameracheckExe != state)
  1630. {
  1631. _LastUPCameracheckExe = state;
  1632. }
  1633. else
  1634. {
  1635. return;
  1636. }
  1637. if (state)
  1638. {
  1639. SendTaskMessage(Lang.触发上相机检测拍照, MessageLevel.Debug);
  1640. //相机的编号
  1641. Int16 cameraIndex = plc.ReadNode<Int16>(addressConfig.In_UPCameracheckNum.Address);
  1642. SendTaskMessage(string.Format(Lang.编号0, cameraIndex), MessageLevel.Info);
  1643. //获取锁付拍照的点位
  1644. List<PlcPoint> plcPoints = new List<PlcPoint>();
  1645. foreach (var item in currentProduct.CheckCameraPoints)
  1646. {
  1647. if (item.IsUse)
  1648. {
  1649. plcPoints.Add(item);
  1650. }
  1651. }
  1652. int num = plcPoints.Count;
  1653. //视觉流程ID
  1654. Guid procedureId = Guid.Empty;
  1655. //获取锁付拍照的点位
  1656. var pos = plcPoints[cameraIndex - 1];
  1657. if (pos != null)
  1658. {
  1659. procedureId = pos.VisionProcessID;
  1660. }
  1661. // 获取流程
  1662. var process = _productService.GetCurrentProductProcedureModelById(procedureId);
  1663. if (process == null)
  1664. {
  1665. SendTaskMessage(string.Format("未找到点位:{0}的视觉流程:{1}", cameraIndex, procedureId), MessageLevel.Alarm);
  1666. await plc.WriteNodeAsync(addressConfig.Out_UPCameracheckStatus.Address, (Int16)2);
  1667. return;
  1668. }
  1669. // 获取机器人当前坐标
  1670. var robot = _robotService.GetAllRobots().FirstOrDefault();
  1671. if (robot == null)
  1672. {
  1673. SendTaskMessage("未找到机器人无法执行 上相机检测拍照视觉任务", MessageLevel.Alarm);
  1674. await plc.WriteNodeAsync(addressConfig.Out_UpCameraPutStatus.Address, (Int16)2);
  1675. return;
  1676. }
  1677. var currentPosition = robot.GetRobotPos();
  1678. // 执行视觉任务
  1679. var _cts = new CancellationTokenSource();
  1680. var visionResult = await _remoteCommandService.ExecutePhotoGetSinglePoint(process, null, new double[] { currentPosition.X, currentPosition.Y, currentPosition.U }, _cts.Token, cameraIndex, num, null, null);
  1681. if (visionResult.IsSucceed)
  1682. {
  1683. await plc.WriteNodeAsync(addressConfig.Out_UPCameracheckStatus.Address, (Int16)1);
  1684. SendTaskMessage($"点位{cameraIndex}检测OK", MessageLevel.Debug);
  1685. }
  1686. else
  1687. {
  1688. await plc.WriteNodeAsync(addressConfig.Out_UPCameracheckStatus.Address, (Int16)2);
  1689. SendTaskMessage($"点位{cameraIndex}检测NG", MessageLevel.Error);
  1690. }
  1691. }
  1692. else
  1693. {
  1694. SendTaskMessage(Lang.触发上相机检测拍照信号已关闭, MessageLevel.Alarm);
  1695. await plc.WriteNodeAsync(addressConfig.Out_UPCameracheckStatus.Address, (Int16)0);
  1696. }
  1697. }
  1698. }
  1699. //下相机拍照螺丝复检
  1700. else if (addressConfig.In_Screw_checkExe.Address.Contains(tuple.nodeId))
  1701. {
  1702. Point.Clear();
  1703. if (tuple.value is bool state)
  1704. {
  1705. if (_LastScrewFuJianExe != state)
  1706. {
  1707. _LastScrewFuJianExe = state;
  1708. }
  1709. else
  1710. {
  1711. return;
  1712. }
  1713. if (state)
  1714. {
  1715. SendTaskMessage(Lang.触发相机拍照螺丝复检, MessageLevel.Debug);
  1716. //相机的编号
  1717. Int16 cameraIndex = plc.ReadNode<Int16>(addressConfig.In_DowmCameraNum.Address);
  1718. SendTaskMessage(string.Format(Lang.编号0, cameraIndex), MessageLevel.Info);
  1719. // 获取流程
  1720. var process = _productService.GetCurrentProductProcedureModelById(currentProduct.FixedUpCameraProcedureId);
  1721. if (process == null)
  1722. {
  1723. SendTaskMessage(string.Format(Lang.固定向上相机视觉流程流程ID, currentProduct.FixedUpCameraProcedureId), MessageLevel.Alarm);
  1724. await plc.WriteNodeAsync(addressConfig.Out_Screw_check_status.Address, (Int16)2);
  1725. return;
  1726. }
  1727. // 获取机器人当前坐标
  1728. var robot = _robotService.GetAllRobots().FirstOrDefault();
  1729. if (robot == null)
  1730. {
  1731. SendTaskMessage(Lang.未找到机器人无法执行上相机锁附组装拍照视觉任务, MessageLevel.Alarm);
  1732. await plc.WriteNodeAsync(addressConfig.Out_Screw_check_status.Address, (Int16)2);
  1733. return;
  1734. }
  1735. var currentPosition = robot.GetRobotPos();
  1736. // 执行视觉任务
  1737. var _cts = new CancellationTokenSource();
  1738. ImageParameters imgPara = new ImageParameters();
  1739. imgPara.SNCode = productSn;
  1740. var visionResult = await _remoteCommandService.ExecutePhotoGetSinglePoint1(process, null, null, _cts.Token, cameraIndex, cameraIndex, null, imgPara);
  1741. //string X, Y, U;
  1742. if (visionResult.IsSucceed)
  1743. {
  1744. //披头偏差
  1745. //float offsetX = (float)(visionResult.X - currentPosition.X);
  1746. //float offsetY = (float)(visionResult.Y - currentPosition.Y);
  1747. string[] keys = { "X", "Y", "U" };
  1748. double[] values = { visionResult.X, visionResult.Y, visionResult.U };
  1749. for (int i = 0; i < keys.Length; i++)
  1750. {
  1751. Point.Add(keys[i], values[i]);
  1752. }
  1753. // 2. 生成CSV文件(替换成你的保存路径)
  1754. string csvPath = @"D:\视觉检测数据\XYU结果.csv";
  1755. // 调用生成方法(appendMode=true 每次拍照新增一行,不会覆盖历史数据)
  1756. ExportDictionaryToCsv(Point, csvPath, appendMode: true);
  1757. //DowmCameraResults[cameraIndex] = new PointF(offsetX, offsetY);
  1758. //ExportDictionaryToCsv1(ProductMainSN[1], visionResult.X, visionResult.Y, visionResult.U);
  1759. await plc.WriteNodeAsync(addressConfig.Out_Screw_check_status.Address, (Int16)1);
  1760. return;
  1761. }
  1762. else
  1763. {
  1764. await plc.WriteNodeAsync(addressConfig.Out_Screw_check_status.Address, (Int16)2);
  1765. return;
  1766. }
  1767. }
  1768. else
  1769. {
  1770. SendTaskMessage(Lang.触发下相机拍照信号已关闭, MessageLevel.Alarm);
  1771. await plc.WriteNodeAsync(addressConfig.Out_Screw_check_status.Address, (Int16)0);
  1772. }
  1773. }
  1774. }
  1775. // 固定相机取料拍照
  1776. else if (addressConfig.In_FixedCameraPickExe.Address.Contains(tuple.nodeId))
  1777. {
  1778. if (tuple.value is bool state)
  1779. {
  1780. if (_LastFixedCameraPickExe != state)
  1781. {
  1782. _LastFixedCameraPickExe = state;
  1783. }
  1784. else
  1785. {
  1786. return;
  1787. }
  1788. if (state)
  1789. {
  1790. SendTaskMessage(Lang.触发固定相机取料拍照, MessageLevel.Debug);
  1791. //相机的编号
  1792. //Int16 cameraIndex = plc.ReadNode<Int16>(addressConfig.In_FixedCameraPickNum.Address);
  1793. await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPickStatus.Address, (Int16)1);
  1794. }
  1795. else
  1796. {
  1797. SendTaskMessage(Lang.触发固定相机取料拍照信号已关闭, MessageLevel.Alarm);
  1798. await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPickStatus.Address, (Int16)0);
  1799. }
  1800. }
  1801. }
  1802. // 固定相机锁附/组装拍照
  1803. else if (addressConfig.In_FixedCameraPutExe.Address.Contains(tuple.nodeId))
  1804. {
  1805. if (tuple.value is bool state)
  1806. {
  1807. if (_LastFixedCameraPutExe != state)
  1808. {
  1809. _LastFixedCameraPutExe = state;
  1810. }
  1811. else
  1812. {
  1813. return;
  1814. }
  1815. if (state)
  1816. {
  1817. SendTaskMessage(Lang.触发固定相机锁附组装拍照, MessageLevel.Debug);
  1818. //相机的编号
  1819. //Int16 cameraIndex = plc.ReadNode<Int16>(addressConfig.In_FixedCameraPutNum.Address);
  1820. await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPutStatus.Address, (Int16)1);
  1821. }
  1822. else
  1823. {
  1824. SendTaskMessage(Lang.触发固定相机锁附组装拍照信号已关闭, MessageLevel.Alarm);
  1825. await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPutStatus.Address, (Int16)0);
  1826. }
  1827. }
  1828. }
  1829. // 固定相机检测拍照
  1830. else if (addressConfig.In_FixedCameracheckExe.Address.Contains(tuple.nodeId))
  1831. {
  1832. if (tuple.value is bool state)
  1833. {
  1834. if (_LastFixedCameracheckExe != state)
  1835. {
  1836. _LastFixedCameracheckExe = state;
  1837. }
  1838. else
  1839. {
  1840. return;
  1841. }
  1842. if (state)
  1843. {
  1844. SendTaskMessage(Lang.触发固定相机检测拍照, MessageLevel.Debug);
  1845. //相机的编号
  1846. //Int16 cameraIndex = plc.ReadNode<Int16>(addressConfig.In_FixedCameracheckNum.Address);
  1847. await plc.WriteNodeAsync(addressConfig.Out_FixedCameracheckStatus.Address, (Int16)1);
  1848. }
  1849. else
  1850. {
  1851. SendTaskMessage(Lang.触发固定相机检测拍照信号已关闭, MessageLevel.Alarm);
  1852. await plc.WriteNodeAsync(addressConfig.Out_FixedCameracheckStatus.Address, (Int16)0);
  1853. }
  1854. }
  1855. }
  1856. // 翻转相机检测拍照
  1857. else if (addressConfig.In_FzCameraPixExe.Address.Contains(tuple.nodeId))
  1858. {
  1859. if (tuple.value is bool state)
  1860. {
  1861. if (_LastFzCameracheckExe != state)
  1862. {
  1863. _LastFzCameracheckExe = state;
  1864. }
  1865. else
  1866. {
  1867. return;
  1868. }
  1869. if (state)
  1870. {
  1871. int i = 5;
  1872. SendTaskMessage("触发翻转相机拍照", MessageLevel.Debug);
  1873. var code = plc.ReadNode<string>(string.Format(addressConfig.In_Code.Address, 1));
  1874. string resultphoto = "";
  1875. //这里缺少从MES获取产品配方的内容,需要补充
  1876. _LastMesCheckTime[5] = (await _mesService.GetServerTimeAsync(5, code, 2)).Now;
  1877. var stationConfig2 = _configService.GetDeviceInfo(0);
  1878. LogHelper.WriteLogMes($"【mes开始QUERY】");
  1879. SendTaskMessage($"mes开始QUERY", MessageLevel.Info);
  1880. //if (stationConfig2.EnableMES)
  1881. //{
  1882. // (bool isSuccess, string response) = await _mesService.StationGetExAsync(code, "", 2);
  1883. // if (isSuccess)
  1884. // {
  1885. // string ProductMainSN_Temp = "";
  1886. // LogHelper.WriteLogMes($"询问mes成功");
  1887. // SendTaskMessage($"询问mes成功:{response}", MessageLevel.Debug);
  1888. // string[] item = response.Split('\n');
  1889. // //获取过度件sn
  1890. // string snItem = item.FirstOrDefault(f => f.Contains("sn="));
  1891. // if (snItem != null)
  1892. // {
  1893. // ProductMainSN_Temp = snItem.Replace("sn=", "").Trim();
  1894. // SendTaskMessage($"过度件sn:{ProductMainSN_Temp}", MessageLevel.Info);
  1895. // }
  1896. // LogHelper.WriteLogMes($"询问mes成功");
  1897. // SendTaskMessage($"询问mes成功:{response}", MessageLevel.Debug);
  1898. // //20260214,螺丝机特别版
  1899. // (bool isSuccess2, string response2) = await _mesService.StationGetAsync(ProductMainSN_Temp, "", 2);//询问mes
  1900. // if (isSuccess2)
  1901. // {
  1902. // LogHelper.WriteLogMes($"询问mes成功");
  1903. // SendTaskMessage($"询问mes成功:{response2}", MessageLevel.Debug);
  1904. // string[] items = response2.Split('\n');
  1905. // //获取产品主sn和wo用于后续上传数据使用
  1906. // string snItem2 = items.FirstOrDefault(f => f.Contains("sn="));
  1907. // if (snItem2 != null)
  1908. // {
  1909. // var codesn = snItem2.Replace("sn=", "").Trim();
  1910. // //_productService.CurrentProductCode = codesn;
  1911. // SendTaskMessage($"产品主sn:{codesn}", MessageLevel.Info);
  1912. // }
  1913. // string woItem = item.FirstOrDefault(f => f.Contains("wo="));
  1914. // if (woItem != null)
  1915. // {
  1916. // wo[i] = woItem.Replace("wo=", "").Trim();
  1917. // SendTaskMessage($"工单信息:{wo[i]}", MessageLevel.Info);
  1918. // }
  1919. // string modelItem = item.FirstOrDefault(f => f.Contains("model_num="));
  1920. // if (modelItem != null)
  1921. // {
  1922. // model_num[i] = modelItem.Replace("model_num=", "").Trim();
  1923. // SendTaskMessage($"model_num:{model_num[i]}", MessageLevel.Info);
  1924. // }
  1925. // string colorItem = item.FirstOrDefault(f => f.Contains("color="));
  1926. // if (colorItem != null)
  1927. // {
  1928. // color[i] = colorItem.Replace("color=", "").Trim();
  1929. // SendTaskMessage($"color:{color[i]}", MessageLevel.Info);
  1930. // }
  1931. // string ppidItem = item.FirstOrDefault(f => f.Contains("ppid="));
  1932. // if (ppidItem != null)
  1933. // {
  1934. // ppid[5] = ppidItem.Replace("ppid=", "").Trim();
  1935. // SendTaskMessage($"ppid:{ppid[5]}", MessageLevel.Info);
  1936. // }
  1937. // string revisionItem = item.FirstOrDefault(f => f.Contains("stage="));
  1938. // if (revisionItem != null)
  1939. // {
  1940. // revision[5] = revisionItem.Replace("stage=", "").Trim();
  1941. // SendTaskMessage($"stage:{revision[5]}", MessageLevel.Info);
  1942. // }
  1943. // LogHelper.WriteLogMes($"第二次询问mes成功");
  1944. // SendTaskMessage($"第二次询问mes成功:{response2}", MessageLevel.Debug);
  1945. // }
  1946. // else
  1947. // {
  1948. // LogHelper.WriteLogMes($"第二次询问mes失败");
  1949. // SendTaskMessage($"第二次询问mes失败:{response2}", MessageLevel.Error);
  1950. // await plc.WriteNodeAsync(addressConfig.Out_FzCameraPickStatus.Address, (Int16)2);
  1951. // return;
  1952. // }
  1953. // }
  1954. // else
  1955. // {
  1956. // LogHelper.WriteLogMes($"询问mes失败");
  1957. // SendTaskMessage($"询问mes失败:{response}", MessageLevel.Error);
  1958. // await plc.WriteNodeAsync(addressConfig.Out_FzCameraPickStatus.Address, (Int16)2);
  1959. // return;
  1960. // }
  1961. //}
  1962. //else
  1963. //{
  1964. // SendTaskMessage($"mes未启用", MessageLevel.Info);
  1965. //}
  1966. Guid FzCameraProcedureId = currentProduct.FzCameraProcedureId;
  1967. var FzCameraProcess = _productService.GetCurrentProductProcedureModelById(FzCameraProcedureId);
  1968. if (FzCameraProcess == null)
  1969. {
  1970. SendTaskMessage($"未找到翻转相机视觉流程,流程ID: {FzCameraProcedureId}", MessageLevel.Alarm);
  1971. await plc.WriteNodeAsync(addressConfig.Out_FzCameraPickStatus.Address, (Int16)2);
  1972. return;
  1973. }
  1974. SendTaskMessage($"开始执行翻转相机视觉流程", MessageLevel.Debug);
  1975. var _cts = new CancellationTokenSource();
  1976. await _remoteCommandService.SetLightBeforePhoto(FzCameraProcess);
  1977. var FzCameraResult = _remoteCommandService.ExecutePhotoGetDistance(FzCameraProcess, null);
  1978. if (FzCameraResult)
  1979. {
  1980. SendTaskMessage("翻转相机拍照成功", MessageLevel.Debug);
  1981. resultphoto = "Pass";
  1982. await plc.WriteNodeAsync(addressConfig.Out_FzCameraPickStatus.Address, (Int16)1);
  1983. }
  1984. else
  1985. {
  1986. SendTaskMessage("翻转相机拍照失败", MessageLevel.Alarm);
  1987. resultphoto = "Fail";
  1988. await plc.WriteNodeAsync(addressConfig.Out_FzCameraPickStatus.Address, (Int16)2);
  1989. }
  1990. if (stationConfig2.SaveCsv)
  1991. {
  1992. List<DataFormCtq> DFC = new List<DataFormCtq>();
  1993. string saveppid = "NA";
  1994. string names = currentProduct.Name;
  1995. if (stationConfig2.SaveCsvPPID) saveppid = ppid[i];
  1996. DateTime servertime = DateTime.Now;
  1997. // Dictionary<string, double> adjacentDiffDict1 = GetAdjacentDiffDict(keyData1);
  1998. //判断锁付路径字符串是否为空,如果不为空则保存,否则不保存
  1999. if (!string.IsNullOrEmpty(stationConfig2.SaveCsvPath))
  2000. {
  2001. DFC.Add(new DataFormCtq
  2002. {
  2003. PPID = saveppid,
  2004. PROCESS_NAME = stationConfig2.DataInfo.PROCESS_NAME,
  2005. PROCESS_START_TIME = _LastMesCheckTime[5].ToString("yyyy-MM-ddTHH:mm:ss.fffffff") + "+08:00",
  2006. CTQ_NAME = "FzCameraPhoto",
  2007. CTQ_VALUE = 0,
  2008. CTQ_LSL = 0,
  2009. CTQ_USL = 0,
  2010. PROCESS_END_TIME = servertime.ToString("yyyy-MM-ddTHH:mm:ss.fffffff") + "+08:00",
  2011. SUPPLIER_NAME = stationConfig2.DataInfo.SUPPLIER_NAME,
  2012. COMMODITY_TYPE = stationConfig2.DataInfo.COMMODITY_TYPE,
  2013. REVISION = stationConfig2.DataInfo.REVISION + "_" + revision[i],
  2014. WO = "wo",//
  2015. MFG_LOT = stationConfig2.DataInfo.MFG_LOT,//
  2016. MFG_ASSY_LINE = stationConfig2.DataInfo.MFG_ASSY_LINE,
  2017. PROCESS_OUTCOME = resultphoto,
  2018. PROCESS_MACHINE_ID = stationConfig2.DataInfo.PROCESS_MACHINE_ID,
  2019. CTQ_UNIT_OF_MEASURE = "mm",
  2020. ERROR_MESSAGE = "",
  2021. PD_ATTR_01 = _productService.CurrentProductCode,//
  2022. PD_ATTR_02 = "",
  2023. PD_ATTR_03 = "",
  2024. PD_ATTR_04 = code,
  2025. PD_ATTR_05 = "",
  2026. });
  2027. bool recordResult = RecordStationResult(0, DFC, _productService.CurrentProductCode, servertime.Subtract(_LastMesCheckTime[5]).TotalSeconds, _LastMesCheckTime[5], stationConfig2.SaveCsvPath);
  2028. if (recordResult)
  2029. {
  2030. SendTaskMessage($"翻转相机拍照产品记录保存成功", MessageLevel.Debug);
  2031. }
  2032. else
  2033. {
  2034. SendTaskMessage($"{stationConfig2.DeviceName}产品记录保存失败", MessageLevel.Error);
  2035. }
  2036. }
  2037. else
  2038. {
  2039. SendTaskMessage($"{stationConfig2.DeviceName}工位未配置保存路径,无法保存产品记录", MessageLevel.Error);
  2040. }
  2041. }
  2042. }
  2043. else
  2044. {
  2045. SendTaskMessage($"触发翻转相机检测拍照信号已关闭", MessageLevel.Alarm);
  2046. await plc.WriteNodeAsync(addressConfig.Out_FzCameraPickStatus.Address, (Int16)0);
  2047. }
  2048. }
  2049. }
  2050. // 开始锁付
  2051. else if (addressConfig.In_WorkStart.Address.Contains(tuple.nodeId))
  2052. {
  2053. if (tuple.value is Int16 state)
  2054. {
  2055. if (_LastWorkStart != state)
  2056. {
  2057. _LastWorkStart = state;
  2058. }
  2059. else
  2060. {
  2061. return;
  2062. }
  2063. var code = plc.ReadNode<string>(string.Format(addressConfig.In_Code.Address, 0));
  2064. if (state == 1)
  2065. {
  2066. SendTaskMessage(Lang.收到开始锁付信号, MessageLevel.Debug);
  2067. StartScrewTime = DateTime.Now;
  2068. //位置编号
  2069. Int16 positionIndex = plc.ReadNode<Int16>(addressConfig.In_ProductNum.Address);
  2070. SendTaskMessage(string.Format(Lang.编号0, positionIndex), MessageLevel.Info);
  2071. // 开始采集压力值,ProductMainSN[i]
  2072. //StartPressureCollection(currentProduct.Name, _productService.CurrentProductCode, 0);
  2073. StartPressureCollection(currentProduct.Name, _productService.CurrentProductCode, 0);
  2074. }
  2075. else
  2076. {
  2077. SendTaskMessage(Lang.开始锁付信号已关闭, MessageLevel.Alarm);
  2078. // 关闭时的处理(如需)可放在这里
  2079. }
  2080. }
  2081. }
  2082. // 停止锁付
  2083. else if (addressConfig.In_WorkDone.Address.Contains(tuple.nodeId))
  2084. {
  2085. if (tuple.value is Int16 state)
  2086. {
  2087. if (_LastWorkDone != state)
  2088. {
  2089. _LastWorkDone = state;
  2090. }
  2091. else
  2092. {
  2093. return;
  2094. }
  2095. if (state == 1 || state == 2)
  2096. {
  2097. if (TorqueStatus)
  2098. {
  2099. }
  2100. SendTaskMessage(Lang.收到停止锁付信号, MessageLevel.Debug);
  2101. //位置编号
  2102. Int16 positionIndex = plc.ReadNode<Int16>(addressConfig.In_ProductNum.Address);
  2103. SendTaskMessage(string.Format(Lang.编号0, positionIndex), MessageLevel.Info);
  2104. //获取当前锁付点
  2105. var screwPoint = currentProduct.ScrewPoints.FirstOrDefault(p => p.Number == positionIndex);
  2106. //获取当前的机器人点位
  2107. var robot = _robotService.GetAllRobots().FirstOrDefault();
  2108. var currentPosition = robot.GetRobotPos();
  2109. //读取压力值
  2110. float pressure = _maxPressure;
  2111. //当前用户名
  2112. string currentUserName = _systemDatabaseService.GetCurrentUser().UserName;
  2113. //吸嘴最后一次的记录
  2114. var nozzleRecord = await _systemDatabaseService.GetLatestNozzleChangeRecordAsync();
  2115. //最后一次的吸嘴编号
  2116. string nozzleNumber = nozzleRecord != null ? nozzleRecord.NozzleBatchNumber : "N/A";
  2117. //披头最后一次的记录
  2118. var headRecord = await _systemDatabaseService.GetLatestHeadChangeRecordAsync();
  2119. //最后一次的披头编号
  2120. string headNumber = headRecord != null ? headRecord.HeadBatchNumber : "N/A";
  2121. await Task.Delay(60);
  2122. //获取电批结果
  2123. bool isSucced = ScrewDriver.GetData();
  2124. LockResult lockResult = new LockResult();
  2125. lockResult.ScrewdriverGetDataSucceed = isSucced;
  2126. lockResult.Number = positionIndex;
  2127. lockResult.LockPassed = ScrewDriver.Success;
  2128. lockResult.ProductName = currentProduct.Name;
  2129. lockResult.ProductNumber = _productService.CurrentProductCode;
  2130. lockResult.LockTurns = (float)ScrewDriver.Truns;
  2131. lockResult.LockTorque = (float)ScrewDriver.TorqueValue;
  2132. lockResult.LockDuration = (float)ScrewDriver.FinalTime;
  2133. lockResult.ScrewdriverProgramNumber = ScrewDriver.ProgramNumber;
  2134. lockResult.ResultDetails = ScrewDriver.ErrorInfo;
  2135. lockResult.Pressure = pressure;
  2136. lockResult.TargetTorqueUpperLimit = ScrewDriver.TargetTorqueUpperLimit;
  2137. lockResult.TargetTorqueLowerLimit = ScrewDriver.TargetTorqueLowerLimit;
  2138. lockResult.TargetTurnsUpperLimit = ScrewDriver.TargetTurnsUpperLimit / 360;
  2139. lockResult.TargetTurnsLowerLimit = ScrewDriver.TargetTurnsLowerLimit / 360;
  2140. lockResult.TargetLockSpeed = ScrewDriver.TargetLockSpeed;
  2141. lockResult.PositionX = currentPosition.X;
  2142. lockResult.PositionY = currentPosition.Y;
  2143. lockResult.StartPositionZ = screwPoint.Z_Position_Start;
  2144. lockResult.EndPositionZ = currentPosition.Z;
  2145. lockResult.LockDescentSpeed = screwPoint.Z_Velocity_Stop;
  2146. lockResult.FeederNumber = screwPoint != null ? screwPoint.Feeder : 0;
  2147. lockResult.NozzleNumber = nozzleNumber;
  2148. lockResult.HeadNumber = headNumber;
  2149. lockResult.OperatorName = currentUserName;
  2150. lockResult.ProductImagePath = "";
  2151. lockResult.WaveDatas = ScrewDriver.WaveDatas;
  2152. //pressure = 0;
  2153. //获取系统参数
  2154. var systemConfig = _configService.GetSystemConfiguration();
  2155. //判断是否保存锁付曲线
  2156. if (systemConfig.IsSaveScrewCurve)
  2157. {
  2158. //锁付曲线的保存路径为:systemConfig.ScrewCurvePath//年月//年月日//配方名//产品SN//产品SN+螺丝索引
  2159. string datePath = DateTime.Now.ToString("yyyy-MM") + "\\" + DateTime.Now.ToString("yyyy-MM-dd") + "\\";
  2160. string dirPath = System.IO.Path.Combine(systemConfig.ScrewCurvePath, datePath, currentProduct.Name, lockResult.ProductNumber);
  2161. if (!Directory.Exists(dirPath))
  2162. {
  2163. Directory.CreateDirectory(dirPath);
  2164. }
  2165. string curveFileName = $"{lockResult.ProductNumber}-{lockResult.Number}.png";
  2166. //锁付CSV保存路径
  2167. string curveFileFullPath = System.IO.Path.Combine(dirPath, $"{lockResult.ProductNumber}-{lockResult.Number}.csv");
  2168. //锁付曲线保存路径
  2169. lockResult.LockCurveImagePath = System.IO.Path.Combine(dirPath, curveFileName);
  2170. var _ = lockResult.SaveToCsvAsync(curveFileFullPath);
  2171. }
  2172. if (lockResult.Number == 1)
  2173. {
  2174. dataToMes.Clear();
  2175. resultToMes.Clear();
  2176. lockResultsAll.Clear();
  2177. }
  2178. //基于当前的锁付结果和产品的规格参数判断锁付结果是否合格
  2179. var stationConfig = _configService.GetDeviceInfo(0);
  2180. bool torqueRes = false, turnRes = false, pressureRes = false;
  2181. bool lastRes = false;//默认不合格,只有当扭力、圈数、压力都在设定的上下限范围内时,才判定为锁付合格
  2182. if (lockResult.LockTorque >= lockResult.TargetTorqueLowerLimit && lockResult.LockTorque <= lockResult.TargetTorqueUpperLimit)
  2183. {
  2184. torqueRes = true;
  2185. }
  2186. if (lockResult.Pressure >= stationConfig.DataInfo.CTQ_LSL_Pressure && lockResult.Pressure <= stationConfig.DataInfo.CTQ_USL_Pressure)
  2187. {
  2188. pressureRes = true;
  2189. }
  2190. if (lockResult.LockTurns >= lockResult.TargetTurnsLowerLimit && lockResult.LockTurns <= lockResult.TargetTurnsUpperLimit)
  2191. {
  2192. turnRes = true;
  2193. }
  2194. if (torqueRes == true && turnRes == true && pressureRes == true)//当扭力、圈数、压力都在设定的上下限范围内时,才判定为锁付合格
  2195. {
  2196. lastRes = true;
  2197. }
  2198. if (dataToMes.Keys.Contains($"Torque{lockResult.Number}"))
  2199. {
  2200. dataToMes.Remove($"Torque{lockResult.Number}");
  2201. }
  2202. if (dataToMes.Keys.Contains($"Turn{lockResult.Number}"))
  2203. {
  2204. dataToMes.Remove($"Turn{lockResult.Number}");
  2205. }
  2206. if (dataToMes.Keys.Contains($"Pressure{lockResult.Number}"))
  2207. {
  2208. dataToMes.Remove($"Pressure{lockResult.Number}");
  2209. }
  2210. lockResult.LockPassed = lastRes;
  2211. dataToMes.Add($"Torque{lockResult.Number}", $"{lockResult.LockTorque:F2}");
  2212. dataToMes.Add($"Turn{lockResult.Number}", $"{lockResult.LockTurns:F2}");
  2213. dataToMes.Add($"Pressure{lockResult.Number}", $"{lockResult.Pressure:F2}");
  2214. //dataToMes.Add($"LastResult{lockResult.Number}", $"{lastRes}");
  2215. if (!lastRes)//如果不合格,则在MES结果中添加"Screw{lockResult.Number}_Fail"
  2216. {
  2217. if (!resultToMes.Contains($"Screw{lockResult.Number}_Fail"))
  2218. {
  2219. resultToMes.Add($"Screw{lockResult.Number}_Fail");
  2220. SendTaskMessage($"Screw{lockResult.Number}:锁附不合格", MessageLevel.Error);
  2221. }
  2222. }
  2223. SendTaskMessage($"螺丝序号:{lockResult.Number},扭力:{lockResult.LockTorque},压力:{lockResult.Pressure},圈数:{lockResult.LockTurns}", MessageLevel.Info);
  2224. _maxPressure = 0;
  2225. lockResultsAll.Add(lockResult);
  2226. App.Current.Dispatcher.Invoke(() =>
  2227. {
  2228. _eventAggregator.GetEvent<LockFinishNotification>().Publish(lockResult);
  2229. });
  2230. //记录锁付结果至数据库
  2231. await _systemDatabaseService.RecordLockResultAsync(lockResult);
  2232. //披头数量+1
  2233. NumStatistics.HeaderNum = await _systemDatabaseService.IncrementLatestHeadLockCountAsync(currentUserName);
  2234. //吸嘴数量+1
  2235. NumStatistics.NozzleNum = await _systemDatabaseService.IncrementLatestNozzleLockCountAsync(currentUserName);
  2236. await plc.WriteNodeAsync(addressConfig.In_WorkDone.Address, (Int16)0);
  2237. // 停止采集压力并保存
  2238. await StopAndSavePressureCollectionAsync(currentProduct.Name, _productService.CurrentProductCode, positionIndex);
  2239. pressure = 0;
  2240. }
  2241. else
  2242. {
  2243. SendTaskMessage(Lang.停止锁付信号已关闭, MessageLevel.Alarm);
  2244. }
  2245. }
  2246. }
  2247. // 多相机坐标合并
  2248. else if (addressConfig.In_RequestPosition.Address.Contains(tuple.nodeId))
  2249. {
  2250. if (tuple.value is Int16 state)
  2251. {
  2252. if (_LastRequestPosition != state)
  2253. {
  2254. _LastRequestPosition = state;
  2255. }
  2256. else
  2257. {
  2258. return;
  2259. }
  2260. if (state == 1)
  2261. {
  2262. SendTaskMessage(Lang.收到多相机坐标合并请求开始合并, MessageLevel.Debug);
  2263. //位置编号
  2264. Int16 positionIndex = plc.ReadNode<Int16>(addressConfig.In_ProductNum.Address);
  2265. //是拍一个锁一个还是拍两个点锁所有
  2266. var systemConfig = _configService.GetSystemConfiguration();
  2267. //拍1打1
  2268. if (systemConfig.WorkMode == 1)
  2269. {
  2270. List<PlcPoint> plcPoints = new List<PlcPoint>();
  2271. foreach (var item in currentProduct.ScrewPoints)
  2272. {
  2273. if (item.IsUse)
  2274. {
  2275. plcPoints.Add(item);
  2276. }
  2277. }
  2278. var TempPoint = plcPoints[positionIndex - 1];
  2279. if (TempPoint == null)
  2280. {
  2281. SendTaskMessage($"点:{positionIndex}不存在", MessageLevel.Info);
  2282. await plc.WriteNodeAsync(addressConfig.Out_CameraDone.Address, (Int16)2);
  2283. return;
  2284. }
  2285. var PositionPoint = TempPoint.Clone();
  2286. PositionPoint.X_Position = UpCameraPutResults[1].X + DowmCameraResults[1].X;
  2287. PositionPoint.Y_Position = UpCameraPutResults[1].Y + DowmCameraResults[1].Y;
  2288. SendTaskMessage($"点:{positionIndex},{PositionPoint.X_Position:F3},{PositionPoint.Y_Position:F4}", MessageLevel.Info);
  2289. if (PositionPoint.X_Position == 0 || PositionPoint.Y_Position == 0)
  2290. {
  2291. SendTaskMessage("拟合位置为0,请检查", MessageLevel.Error);
  2292. await plc.WriteNodeAsync(addressConfig.Out_CameraDone.Address, (Int16)2);
  2293. return;
  2294. }
  2295. if (_configService.GetDeviceInfo().EnableXY)
  2296. {
  2297. //验证当前锁附点位与设置锁附点位的差值是否超限,超限则报警并停止流程
  2298. //目前验证已OK 缺少切换按钮
  2299. try
  2300. {
  2301. var ScrewLimit = currentProduct.GlobalParamListCollection.FirstOrDefault((p) => p.Number == positionIndex - 1);
  2302. //判断实际偏差与设置的点位差数是否偏差过大
  2303. float XOffsetPoint = Math.Abs(PositionPoint.X_Position - (TempPoint.X_Position + TempPoint.X_Offset));
  2304. float YOffsetPoint = Math.Abs(PositionPoint.Y_Position - (TempPoint.Y_Position + TempPoint.Y_Offset));
  2305. if (!(XOffsetPoint >= float.Parse(ScrewLimit.Param1) && XOffsetPoint <= float.Parse(ScrewLimit.Param2)) ||
  2306. !(YOffsetPoint >= float.Parse(ScrewLimit.Param3) && YOffsetPoint <= float.Parse(ScrewLimit.Param4)))
  2307. {
  2308. SendTaskMessage($"当前锁附点位{positionIndex}与设置锁附点位差值超限,X偏差:{XOffsetPoint},Y偏差{YOffsetPoint}", MessageLevel.Error);
  2309. await plc.WriteNodeAsync(addressConfig.Out_CameraDone.Address, (Int16)2);
  2310. return;
  2311. }
  2312. }
  2313. catch (Exception ex)
  2314. {
  2315. SendTaskMessage($"当前锁附点位与设置锁附点位差值未设置:" + ex.Message, MessageLevel.Error);
  2316. await plc.WriteNodeAsync(addressConfig.Out_CameraDone.Address, (Int16)2);
  2317. return;
  2318. }
  2319. }
  2320. await PositionPoint.WriteToPlcAddress(addressConfig.Out_Screw, plc, positionIndex);
  2321. }
  2322. //拍2打所有
  2323. else if (systemConfig.WorkMode == 2)
  2324. {
  2325. //根据UpCameraPutResults[]中的索引1、2,和拍照点对应的Local下的坐标,从创建Local坐标系
  2326. PointF worldP1 = new PointF(UpCameraPutResults[1].X, UpCameraPutResults[1].Y);
  2327. PointF worldP2 = new PointF(UpCameraPutResults[2].X, UpCameraPutResults[2].Y);
  2328. PointF localP1 = new PointF(currentProduct.ScrewCameraLocalPos1.X_Position, currentProduct.ScrewCameraLocalPos1.Y_Position);
  2329. PointF localP2 = new PointF(currentProduct.ScrewCameraLocalPos2.X_Position, currentProduct.ScrewCameraLocalPos2.Y_Position);
  2330. CoordinateTransformer local = new CoordinateTransformer(worldP1, worldP2, localP1, localP2);
  2331. for (int i = 0; i < currentProduct.ScrewPoints.Count; i++)
  2332. {
  2333. var localPoint = new PointF(currentProduct.ScrewPoints[i].X_Position + currentProduct.ScrewPoints[i].X_Offset,
  2334. currentProduct.ScrewPoints[i].Y_Position + currentProduct.ScrewPoints[i].Y_Offset);
  2335. var worldPoint = local.ToOldCoord(localPoint.X, localPoint.Y);
  2336. var point = currentProduct.ScrewPoints[i].Clone();
  2337. //锁付点位在Local中已经加了偏移,这里写回PLC时需要将偏移清零,否则写入PLC端时会重复计算偏移
  2338. point.X_Offset = 0;
  2339. point.Y_Offset = 0;
  2340. point.X_Position = (float)worldPoint[0];
  2341. point.Y_Position = (float)worldPoint[1];
  2342. SendTaskMessage($"点:{i},{point.X_Position:F3},{point.Y_Position:F4}", MessageLevel.Info);
  2343. if (_configService.GetDeviceInfo().ScrewOverrun)
  2344. {
  2345. try
  2346. {
  2347. var ScrewLimit = currentProduct.GlobalParamListCollection.FirstOrDefault((p) => p.Number == positionIndex - 1);
  2348. //判断实际偏差与设置的点位差数是否偏差过大
  2349. float XOffsetPoint = Math.Abs(localPoint.X - (float)worldPoint[0]);
  2350. float YOffsetPoint = Math.Abs(localPoint.Y - (float)worldPoint[1]);
  2351. if ((XOffsetPoint >= float.Parse(ScrewLimit.Param1) && XOffsetPoint <= float.Parse(ScrewLimit.Param2)) ||
  2352. (YOffsetPoint >= float.Parse(ScrewLimit.Param3) && YOffsetPoint <= float.Parse(ScrewLimit.Param4)))
  2353. {
  2354. SendTaskMessage($"当前锁附点位{i}与设置锁附点位差值超限,X偏差:{XOffsetPoint},Y偏差{YOffsetPoint}", MessageLevel.Error);
  2355. await plc.WriteNodeAsync(addressConfig.Out_CameraDone.Address, (Int16)2);
  2356. return;
  2357. }
  2358. }
  2359. catch (Exception ex)
  2360. {
  2361. SendTaskMessage($"当前锁附点位与设置锁附点位差值超限:" + ex.Message, MessageLevel.Error);
  2362. await plc.WriteNodeAsync(addressConfig.Out_CameraDone.Address, (Int16)2);
  2363. }
  2364. }
  2365. await point.WriteToPlcAddress(addressConfig.Out_Screw, plc);
  2366. }
  2367. await plc.WriteNodeAsync(addressConfig.Out_WorkNum.Address, (Int16)currentProduct.ScrewPoints.Count);
  2368. }
  2369. //拍2打所有
  2370. else if (systemConfig.WorkMode == 3)
  2371. {
  2372. if (positionIndex <= 3)
  2373. {
  2374. List<PlcPoint> plcPoints = new List<PlcPoint>();
  2375. foreach (var item in currentProduct.ScrewPoints)
  2376. {
  2377. if (item.IsUse)
  2378. {
  2379. plcPoints.Add(item);
  2380. }
  2381. }
  2382. var point = plcPoints[positionIndex - 1];
  2383. if (point == null)
  2384. {
  2385. SendTaskMessage($"点:{positionIndex}不存在", MessageLevel.Info);
  2386. await plc.WriteNodeAsync(addressConfig.Out_CameraDone.Address, (Int16)2);
  2387. return;
  2388. }
  2389. point = point.Clone();
  2390. point.X_Position = UpCameraPutResults[1].X + DowmCameraResults[1].X;
  2391. point.Y_Position = UpCameraPutResults[1].Y + DowmCameraResults[1].Y;
  2392. SendTaskMessage($"点:{positionIndex},{point.X_Position:F3},{point.Y_Position:F4}", MessageLevel.Info);
  2393. await point.WriteToPlcAddress(addressConfig.Out_Screw, plc, positionIndex);
  2394. }
  2395. else
  2396. {
  2397. //根据UpCameraPutResults[]中的索引1、2,和拍照点对应的Local下的坐标,从创建Local坐标系
  2398. PointF worldP1 = new PointF(UpCameraPutResults[4].X, UpCameraPutResults[4].Y);
  2399. PointF worldP2 = new PointF(UpCameraPutResults[5].X, UpCameraPutResults[5].Y);
  2400. PointF localP1 = new PointF(currentProduct.ScrewCameraLocalPos1.X_Position, currentProduct.ScrewCameraLocalPos1.Y_Position);
  2401. PointF localP2 = new PointF(currentProduct.ScrewCameraLocalPos2.X_Position, currentProduct.ScrewCameraLocalPos2.Y_Position);
  2402. CoordinateTransformer local = new CoordinateTransformer(worldP1, worldP2, localP1, localP2);
  2403. for (int i = 0; i < currentProduct.ScrewPoints.Count; i++)
  2404. {
  2405. var localPoint = new PointF(currentProduct.ScrewPoints[i].X_Position + currentProduct.ScrewPoints[i].X_Offset,
  2406. currentProduct.ScrewPoints[i].Y_Position + currentProduct.ScrewPoints[i].Y_Offset);
  2407. var worldPoint = local.ToOldCoord(localPoint.X, localPoint.Y);
  2408. var point = currentProduct.ScrewPoints[i].Clone();
  2409. //锁付点位在Local中已经加了偏移,这里写回PLC时需要将偏移清零,否则写入PLC端时会重复计算偏移
  2410. point.X_Offset = 0;
  2411. point.Y_Offset = 0;
  2412. point.X_Position = (float)worldPoint[0];
  2413. point.Y_Position = (float)worldPoint[1];
  2414. SendTaskMessage($"点:{i},{point.X_Position:F3},{point.Y_Position:F4}", MessageLevel.Info);
  2415. await point.WriteToPlcAddress(addressConfig.Out_Screw, plc);
  2416. }
  2417. await plc.WriteNodeAsync(addressConfig.Out_WorkNum.Address, (Int16)currentProduct.ScrewPoints.Count);
  2418. }
  2419. }
  2420. await plc.WriteNodeAsync(addressConfig.Out_CameraDone.Address, (Int16)1);
  2421. }
  2422. else
  2423. {
  2424. SendTaskMessage(string.Format(Lang.多相机坐标合并请求已结束或被取消state0, state), MessageLevel.Alarm);
  2425. await plc.WriteNodeAsync(addressConfig.Out_CameraDone.Address, (Int16)0);
  2426. }
  2427. }
  2428. }
  2429. // PLC请求二维码拍照位置
  2430. else if (addressConfig.In_QrCodePhotoRequest.Address.Contains(tuple.nodeId))
  2431. {
  2432. if (tuple.value is Int16 state)
  2433. {
  2434. if (_LastQrCodePhotoRequest != state)
  2435. {
  2436. _LastQrCodePhotoRequest = state;
  2437. }
  2438. else
  2439. {
  2440. return;
  2441. }
  2442. if (state == 1)
  2443. {
  2444. SendTaskMessage("收到PLC二维码拍照位置请求", MessageLevel.Debug);
  2445. if (currentProduct.QrCodePoints == null || currentProduct.QrCodePoints.Count == 0)
  2446. {
  2447. SendTaskMessage("未找到示教后的二维码拍照点位", MessageLevel.Alarm);
  2448. await plc.WriteNodeAsync(addressConfig.Out_QrCodePhotoResault.Address, (Int16)2);
  2449. return;
  2450. }
  2451. var qrpoint = currentProduct.QrCodePoints[0];
  2452. //发送点位
  2453. bool SendSuccess = await qrpoint.WriteToPlcAddress(addressConfig.Out_QrCodeCamera, plc);
  2454. if (SendSuccess)
  2455. {
  2456. SendTaskMessage("二维码拍照点位发送成功", MessageLevel.Alarm);
  2457. await plc.WriteNodeAsync(addressConfig.Out_QrCodePhotoResault.Address, (Int16)1);
  2458. Guid qrCodeProcedureId = currentProduct.QrCodeProcedureId;
  2459. if (qrCodeProcedureId == Guid.Empty)
  2460. {
  2461. // 如果没有配置二维码视觉流程,直接返回成功
  2462. SendTaskMessage("未配置二维码视觉流程,跳过扫码", MessageLevel.Info);
  2463. return;
  2464. }
  2465. }
  2466. else
  2467. {
  2468. SendTaskMessage("二维码拍照点位发送失败", MessageLevel.Alarm);
  2469. await plc.WriteNodeAsync(addressConfig.Out_QrCodePhotoResault.Address, (Int16)2);
  2470. }
  2471. }
  2472. else
  2473. {
  2474. SendTaskMessage("二维码拍照点位发送信号已关闭", MessageLevel.Alarm);
  2475. await plc.WriteNodeAsync(addressConfig.Out_QrCodePhotoResault.Address, (Int16)0);
  2476. }
  2477. }
  2478. }
  2479. // 螺丝供料器螺丝递减
  2480. else if (addressConfig.In_PickINC.Address.Contains(tuple.nodeId))
  2481. {
  2482. if (tuple.value is Int16 state)
  2483. {
  2484. if (_LastPickINC != state)
  2485. {
  2486. _LastPickINC = state;
  2487. }
  2488. else
  2489. {
  2490. return;
  2491. }
  2492. if (state != 0)
  2493. {
  2494. SendTaskMessage($"螺丝供料器...", MessageLevel.Info);
  2495. await plc.WriteNodeAsync(addressConfig.In_PickINC.Address, (Int16)0);
  2496. var feeders = _configService.GetRemainingCountByFeederNumber(state);
  2497. if (state == 1)
  2498. {
  2499. NumStatistics.Feeder1Num = _configService.DecrementScrewCountByFeederNumber(feeders);
  2500. }
  2501. else if (state == 2)
  2502. {
  2503. NumStatistics.Feeder2Num = _configService.DecrementScrewCountByFeederNumber(feeders);
  2504. }
  2505. }
  2506. }
  2507. }
  2508. // 抛料计数
  2509. else if (addressConfig.In_Throw.Address.Contains(tuple.nodeId))
  2510. {
  2511. if (tuple.value is bool state)
  2512. {
  2513. if (_LastThrowExe != state)
  2514. {
  2515. _LastThrowExe = state;
  2516. }
  2517. else
  2518. {
  2519. return;
  2520. }
  2521. if (state)
  2522. {
  2523. SendTaskMessage("收到抛料信号", MessageLevel.Info);
  2524. await _systemDatabaseService.RecordThrowNumberAsync(new ThrowRecord()
  2525. {
  2526. DayTime = DateTime.Now,
  2527. ThrowCount = 1
  2528. });
  2529. }
  2530. }
  2531. }
  2532. // 切换成自动运行模式
  2533. else if (addressConfig.AutoRunning.Address.Contains(tuple.nodeId))
  2534. {
  2535. if (tuple.value is bool state)
  2536. {
  2537. if (_LastAutoRunning != state)
  2538. {
  2539. _LastAutoRunning = state;
  2540. }
  2541. else
  2542. {
  2543. return;
  2544. }
  2545. if (state)
  2546. {
  2547. SendTaskMessage("收到切换成自动模式", MessageLevel.Info);
  2548. }
  2549. else
  2550. {
  2551. SendTaskMessage("收到关闭自动模式", MessageLevel.Info);
  2552. }
  2553. CurrentRunStatus();
  2554. }
  2555. }
  2556. // 切换成维修模式
  2557. else if (addressConfig.ManualMode.Address.Contains(tuple.nodeId))
  2558. {
  2559. if (tuple.value is bool state)
  2560. {
  2561. if (_LastManualMode != state)
  2562. {
  2563. _LastManualMode = state;
  2564. }
  2565. else
  2566. {
  2567. return;
  2568. }
  2569. if (state)
  2570. {
  2571. SendTaskMessage("收到切换成维修模式", MessageLevel.Info);
  2572. }
  2573. else
  2574. {
  2575. SendTaskMessage("收到退出维修模式", MessageLevel.Info);
  2576. }
  2577. CurrentRunStatus();
  2578. }
  2579. }
  2580. // 切换成流线模式
  2581. else if (addressConfig.ClearMode.Address.Contains(tuple.nodeId))
  2582. {
  2583. if (tuple.value is bool state)
  2584. {
  2585. if (_LastClearMode != state)
  2586. {
  2587. _LastClearMode = state;
  2588. }
  2589. else
  2590. {
  2591. return;
  2592. }
  2593. if (state)
  2594. {
  2595. SendTaskMessage("收到切换成流线模式", MessageLevel.Info);
  2596. }
  2597. else
  2598. {
  2599. SendTaskMessage("收到退出流线模式", MessageLevel.Info);
  2600. }
  2601. CurrentRunStatus();
  2602. }
  2603. }
  2604. //plc报警
  2605. else if (addressConfig.Fault.Address.Contains(tuple.nodeId))
  2606. {
  2607. if (tuple.value is bool state)
  2608. {
  2609. if (_LastFault != state)
  2610. {
  2611. _LastFault = state;
  2612. }
  2613. else
  2614. {
  2615. return;
  2616. }
  2617. if (state)
  2618. {
  2619. SendAlarmToUi(plc);
  2620. SendTaskMessage("收到PLC报警", MessageLevel.Error);
  2621. }
  2622. else
  2623. {
  2624. SendTaskMessage("收到PLC消除报警", MessageLevel.Debug);
  2625. }
  2626. }
  2627. }
  2628. // 过站检查
  2629. else
  2630. {
  2631. for (int i = 0; i < 10; i++)
  2632. {
  2633. if (string.Format(addressConfig.In_MesCheck.Address, i).Contains(tuple.nodeId))
  2634. {
  2635. if (tuple.value is Int16 state)
  2636. {
  2637. //var oldState = _LastMesCheck[i];
  2638. //_LastMesCheck[i] = state;
  2639. //SendTaskMessage($"站点{i},收到PLC状态为:{state},旧状态为:{oldState}",MessageLevel.Info);
  2640. //if (oldState == state)
  2641. //{
  2642. // return;
  2643. //}
  2644. if (_LastMesCheck[i] != state)
  2645. {
  2646. _LastMesCheck[i] = state;
  2647. }
  2648. else
  2649. {
  2650. return;
  2651. }
  2652. var systemConfig = _configService.GetSystemConfiguration();
  2653. string code = "";
  2654. //plc启用的模式
  2655. bool models = plc.ReadNode<bool>(addressConfig.Cam_code_mode.Address);
  2656. if (models && state == 1)
  2657. {
  2658. //拍照读码
  2659. SendTaskMessage(Lang.触发上相机锁附组装拍照, MessageLevel.Debug);
  2660. //相机的编号
  2661. Int16 cameraIndex = plc.ReadNode<Int16>(addressConfig.In_UpCameraPutNum.Address);
  2662. SendTaskMessage(string.Format(Lang.编号0, cameraIndex), MessageLevel.Info);
  2663. Guid qrCodeProcedureId = currentProduct.QrCodeProcedureId;
  2664. //视觉流程ID
  2665. var qrCodeProcess = _productService.GetCurrentProductProcedureModelById(qrCodeProcedureId);
  2666. if (qrCodeProcess == null)
  2667. {
  2668. SendTaskMessage($"未找到二维码视觉流程,流程ID: {qrCodeProcedureId}", MessageLevel.Alarm);
  2669. await plc.WriteNodeAsync(addressConfig.Out_QrCodePhotoResault.Address, (Int16)2);
  2670. return;
  2671. }
  2672. SendTaskMessage($"开始执行二维码扫码视觉流程", MessageLevel.Debug);
  2673. var _cts = new CancellationTokenSource();
  2674. await _remoteCommandService.SetLightBeforePhoto(qrCodeProcess);
  2675. var qrCodeResult = _remoteCommandService.ExecutePhoto(i + 1, qrCodeProcess, out CogToolBlockTerminalCollection outputCollection);
  2676. if (qrCodeResult && outputCollection.Contains("QR"))
  2677. {
  2678. code = outputCollection["QR"].Value as string;
  2679. code = code.ToUpper();
  2680. if (!string.IsNullOrEmpty(code) && code.Length > 5)
  2681. {
  2682. if (systemConfig.IsBasePartCodeChooseScrew)//是否基于零件码,判断是不是当站要打的螺丝
  2683. {
  2684. var val = CheckCode(code, systemConfig.ChooseScrewModel);
  2685. if (val)
  2686. {
  2687. SendTaskMessage($"是当站螺丝:{code}", MessageLevel.Info);
  2688. await plc.WriteNodeAsync(addressConfig.Out_QrCodePhotoResault.Address, (Int16)1);
  2689. }
  2690. else
  2691. {
  2692. SendTaskMessage($"不是当站螺丝:{code}", MessageLevel.Info);
  2693. await plc.WriteNodeAsync(addressConfig.Out_QrCodePhotoResault.Address, (Int16)2);
  2694. await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)3);
  2695. return;
  2696. }
  2697. }
  2698. bool _isSuccess = plc.WriteNode<string>(string.Format(addressConfig.In_Code.Address, 0), code);
  2699. if (_isSuccess)
  2700. {
  2701. SendTaskMessage($"二维码扫码成功:{code}", MessageLevel.Info);
  2702. await plc.WriteNodeAsync(addressConfig.Out_QrCodePhotoResault.Address, (Int16)1);
  2703. }
  2704. else
  2705. {
  2706. SendTaskMessage($"二维码扫码成功:{code}", MessageLevel.Info);
  2707. SendTaskMessage($"发送plc二维码失败:{code}", MessageLevel.Error);
  2708. await plc.WriteNodeAsync(addressConfig.Out_QrCodePhotoResault.Address, (Int16)2);
  2709. }
  2710. }
  2711. else
  2712. {
  2713. SendTaskMessage("二维码扫码失败", MessageLevel.Alarm);
  2714. await plc.WriteNodeAsync(addressConfig.Out_QrCodePhotoResault.Address, (Int16)2);
  2715. await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)2);
  2716. return;
  2717. }
  2718. }
  2719. else
  2720. {
  2721. code = "";
  2722. SendTaskMessage("二维码扫码失败", MessageLevel.Alarm);
  2723. await plc.WriteNodeAsync(addressConfig.Out_QrCodePhotoResault.Address, (Int16)2);
  2724. await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)2);
  2725. return;
  2726. }
  2727. }
  2728. else
  2729. {
  2730. code = plc.ReadNode<string>(string.Format(addressConfig.In_Code.Address, i));
  2731. }
  2732. if (!(i == 0 || i == 7))
  2733. {
  2734. return;
  2735. }
  2736. productSn = code;
  2737. SendTaskMessage($"收到plc信号{state}", MessageLevel.Debug);
  2738. //询问mes
  2739. if (state == 1)
  2740. {
  2741. SendTaskMessage($"收到{i}过站检查请求(当站是否生产)...", MessageLevel.Debug);
  2742. //获取过站时间
  2743. _LastMesCheckTime[i] = (await _mesService.GetServerTimeAsync(i, code, 2)).Now;
  2744. if (_configService.GetDeviceInfo().EnableMES)
  2745. {
  2746. LogHelper.WriteLogMes($"【mes开始QUERY】");
  2747. SendTaskMessage($"mes开始QUERY", MessageLevel.Info);
  2748. //dataToMes.Clear();
  2749. if (systemConfig.IsBasePartCodeGetSN)//是否基于零件码,判断是否过站
  2750. {
  2751. SendTaskMessage($"基于零件码,判断是否过站", MessageLevel.Debug);
  2752. (bool isSuccess, string response) = await _mesService.StationGetExAsync(code, "", 2);//基于电池的SN获取产品SN,询问mes
  2753. if (isSuccess)
  2754. {
  2755. string ProductMainSN_Temp = "";
  2756. LogHelper.WriteLogMes($"询问mes成功");
  2757. SendTaskMessage($"询问mes成功:{response}", MessageLevel.Debug);
  2758. string[] item = response.Split('\n');
  2759. //获取过度件sn
  2760. string snItem = item.FirstOrDefault(f => f.Contains("sn="));
  2761. if (snItem != null)
  2762. {
  2763. ProductMainSN_Temp = snItem.Replace("sn=", "").Trim();
  2764. SendTaskMessage($"过度件sn:{ProductMainSN_Temp}", MessageLevel.Info);
  2765. }
  2766. LogHelper.WriteLogMes($"询问mes成功");
  2767. SendTaskMessage($"询问mes成功:{response}", MessageLevel.Debug);
  2768. //20260214,螺丝机特别版
  2769. (bool isSuccess2, string response2) = await _mesService.StationGetAsync(ProductMainSN_Temp, "", 2);//询问mes
  2770. if (isSuccess2)
  2771. {
  2772. LogHelper.WriteLogMes($"询问mes成功");
  2773. SendTaskMessage($"询问mes成功:{response2}", MessageLevel.Debug);
  2774. item = response2.Split('\n');
  2775. snItem = item.FirstOrDefault(f => f.Contains("sn="));
  2776. if (snItem != null)
  2777. {
  2778. ProductMainSN[i] = snItem.Replace("sn=", "").Trim();
  2779. if (i == 0)
  2780. {
  2781. _productService.CurrentProductCode = ProductMainSN[i];
  2782. }
  2783. //_eventAggregator.GetEvent<DelaySaveImageNotification>().Publish(true);//通知界面延迟保存图片,等待MES查询结果返回,避免图片和数据不同步
  2784. SendTaskMessage($"产品主sn:{ProductMainSN[i]}", MessageLevel.Info);
  2785. }
  2786. string woItem = item.FirstOrDefault(f => f.Contains("wo="));
  2787. if (woItem != null)
  2788. {
  2789. wo[i] = woItem.Replace("wo=", "").Trim();
  2790. SendTaskMessage($"工单信息:{wo[i]}", MessageLevel.Info);
  2791. }
  2792. string modelItem = item.FirstOrDefault(f => f.Contains("model_num="));
  2793. if (modelItem != null)
  2794. {
  2795. model_num[i] = modelItem.Replace("model_num=", "").Trim();
  2796. SendTaskMessage($"model_num:{model_num[i]}", MessageLevel.Info);
  2797. }
  2798. string colorItem = item.FirstOrDefault(f => f.Contains("color="));
  2799. if (colorItem != null)
  2800. {
  2801. color[i] = colorItem.Replace("color=", "").Trim();
  2802. SendTaskMessage($"color:{color[i]}", MessageLevel.Info);
  2803. }
  2804. string ppidItem = item.FirstOrDefault(f => f.Contains("ppid="));
  2805. if (ppidItem != null)
  2806. {
  2807. ppid[i] = ppidItem.Replace("ppid=", "").Trim();
  2808. SendTaskMessage($"ppid:{ppid[i]}", MessageLevel.Info);
  2809. }
  2810. string revisionItem = item.FirstOrDefault(f => f.Contains("stage="));
  2811. if (revisionItem != null)
  2812. {
  2813. revision[i] = revisionItem.Replace("stage=", "").Trim();
  2814. SendTaskMessage($"stage:{revision[i]}", MessageLevel.Info);
  2815. }
  2816. LogHelper.WriteLogMes($"第二次询问mes成功");
  2817. SendTaskMessage($"第二次询问mes成功:{response2}", MessageLevel.Debug);
  2818. //切换产品
  2819. if (i == 0)//i=0时才会切换产品
  2820. {
  2821. var q = await _systemDatabaseService.QueryProductFromMesAsync(model_num[i], color[i]);
  2822. if (q != null && !string.IsNullOrEmpty(q.ProductName))
  2823. {
  2824. ProductList = new ObservableCollection<ProductModel>(_productService.GetAllProducts());
  2825. Guid productId = Guid.Empty;
  2826. foreach (var itt in ProductList)
  2827. {
  2828. if (itt.Name == q.ProductName)
  2829. {
  2830. productId = itt.ID;
  2831. break;
  2832. }
  2833. }
  2834. if (currentProduct.ID != productId)
  2835. {
  2836. //切换产品
  2837. _productService.LoadProduct(productId);
  2838. _productService.SetCurrentProduct(productId);
  2839. await WriteParameterToPlcExAsync();
  2840. SendTaskMessage($"切换产品成功", MessageLevel.Debug);
  2841. }
  2842. }
  2843. }
  2844. //产品发生改变并且供料器发生了变化时,需要通知PLC进行取料更改
  2845. await plc.WriteNodeAsync(addressConfig.Out_ScrewFeederIsChanged.Address, (Int16)1); //不发生改变
  2846. await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)1);
  2847. }
  2848. else
  2849. {
  2850. LogHelper.WriteLogMes($"第二次询问mes失败");
  2851. SendTaskMessage($"第二次询问mes失败:{response2}", MessageLevel.Error);
  2852. //产品发生改变并且供料器发生了变化时,需要通知PLC进行取料更改
  2853. await plc.WriteNodeAsync(addressConfig.Out_ScrewFeederIsChanged.Address, (Int16)1); //不发生改变
  2854. await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)3);
  2855. }
  2856. }
  2857. else
  2858. {
  2859. LogHelper.WriteLogMes($"询问mes失败");
  2860. SendTaskMessage($"询问mes失败:{response}", MessageLevel.Error);
  2861. //产品发生改变并且供料器发生了变化时,需要通知PLC进行取料更改
  2862. await plc.WriteNodeAsync(addressConfig.Out_ScrewFeederIsChanged.Address, (Int16)1); //不发生改变
  2863. await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)3);
  2864. }
  2865. }
  2866. else //直接基于RFID询问是否过站
  2867. {
  2868. SendTaskMessage($"基于RFID询问是否过站", MessageLevel.Debug);
  2869. (bool isSuccess, string response) = await _mesService.StationGetExAsync(code, "", 2);
  2870. if (isSuccess)
  2871. {
  2872. string ProductMainSN_Temp = "";
  2873. LogHelper.WriteLogMes($"询问mes成功");
  2874. SendTaskMessage($"询问mes成功:{response}", MessageLevel.Debug);
  2875. string[] item = response.Split('\n');
  2876. //获取过度件sn
  2877. string snItem = item.FirstOrDefault(f => f.Contains("sn="));
  2878. if (snItem != null)
  2879. {
  2880. ProductMainSN_Temp = snItem.Replace("sn=", "").Trim();
  2881. SendTaskMessage($"过度件sn:{ProductMainSN_Temp}", MessageLevel.Info);
  2882. }
  2883. LogHelper.WriteLogMes($"询问mes成功");
  2884. SendTaskMessage($"询问mes成功:{response}", MessageLevel.Debug);
  2885. (bool isSuccess2, string response2) = await _mesService.StationGetAsync(ProductMainSN_Temp, "", 2);//询问mes
  2886. if (isSuccess2)
  2887. {
  2888. item = response2.Split('\n');
  2889. snItem = item.FirstOrDefault(f => f.Contains("sn="));
  2890. if (snItem != null)
  2891. {
  2892. ProductMainSN[i] = snItem.Replace("sn=", "").Trim();
  2893. if (i == 0)
  2894. {
  2895. _productService.CurrentProductCode = ProductMainSN[i];
  2896. }
  2897. SendTaskMessage($"产品主sn:{ProductMainSN[i]}", MessageLevel.Info);
  2898. }
  2899. string woItem = item.FirstOrDefault(f => f.Contains("wo="));
  2900. if (woItem != null)
  2901. {
  2902. wo[i] = woItem.Replace("wo=", "").Trim();
  2903. SendTaskMessage($"工单信息:{wo[i]}", MessageLevel.Info);
  2904. }
  2905. string modelItem = item.FirstOrDefault(f => f.Contains("model_num="));
  2906. if (modelItem != null)
  2907. {
  2908. model_num[i] = modelItem.Replace("model_num=", "").Trim();
  2909. SendTaskMessage($"model_num:{model_num[i]}", MessageLevel.Info);
  2910. }
  2911. string colorItem = item.FirstOrDefault(f => f.Contains("color="));
  2912. if (colorItem != null)
  2913. {
  2914. color[i] = colorItem.Replace("color=", "").Trim();
  2915. SendTaskMessage($"color:{color[i]}", MessageLevel.Info);
  2916. }
  2917. string ppidItem = item.FirstOrDefault(f => f.Contains("ppid="));
  2918. if (ppidItem != null)
  2919. {
  2920. ppid[i] = ppidItem.Replace("ppid=", "").Trim();
  2921. SendTaskMessage($"ppid:{ppid[i]}", MessageLevel.Info);
  2922. }
  2923. string revisionItem = item.FirstOrDefault(f => f.Contains("stage="));
  2924. if (revisionItem != null)
  2925. {
  2926. revision[i] = revisionItem.Replace("stage=", "").Trim();
  2927. SendTaskMessage($"stage:{revision[i]}", MessageLevel.Info);
  2928. }
  2929. LogHelper.WriteLogMes($"第二次询问mes成功");
  2930. SendTaskMessage($"第二次询问mes成功:{response2}", MessageLevel.Debug);
  2931. //切换产品
  2932. if (i == 0)//i=0时才会切换产品
  2933. {
  2934. var q = await _systemDatabaseService.QueryProductFromMesAsync(model_num[i], color[i]);
  2935. if (q != null && !string.IsNullOrEmpty(q.ProductName))
  2936. {
  2937. ProductList = new ObservableCollection<ProductModel>(_productService.GetAllProducts());
  2938. Guid productId = Guid.Empty;
  2939. foreach (var itt in ProductList)
  2940. {
  2941. if (itt.Name == q.ProductName)
  2942. {
  2943. productId = itt.ID;
  2944. break;
  2945. }
  2946. }
  2947. if (currentProduct.ID != productId)
  2948. {
  2949. //切换产品
  2950. _productService.LoadProduct(productId);
  2951. _productService.SetCurrentProduct(productId);
  2952. await WriteParameterToPlcExAsync();
  2953. SendTaskMessage($"切换产品成功", MessageLevel.Debug);
  2954. }
  2955. }
  2956. }
  2957. //产品发生改变并且供料器发生了变化时,需要通知PLC进行取料更改
  2958. await plc.WriteNodeAsync(addressConfig.Out_ScrewFeederIsChanged.Address, (Int16)1); //不发生改变
  2959. await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)1);
  2960. return;
  2961. }
  2962. else
  2963. {
  2964. LogHelper.WriteLogMes($"第二次询问mes失败");
  2965. SendTaskMessage($"第二次询问mes失败:{response2}", MessageLevel.Error);
  2966. //产品发生改变并且供料器发生了变化时,需要通知PLC进行取料更改
  2967. await plc.WriteNodeAsync(addressConfig.Out_ScrewFeederIsChanged.Address, (Int16)1); //不发生改变
  2968. if (models)
  2969. {
  2970. await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)3);
  2971. SendTaskMessage($"发送plc信号3", MessageLevel.Debug);
  2972. return;
  2973. }
  2974. else
  2975. {
  2976. await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)2);
  2977. SendTaskMessage($"发送plc信号2", MessageLevel.Debug);
  2978. return;
  2979. }
  2980. }
  2981. }
  2982. else
  2983. {
  2984. LogHelper.WriteLogMes($"询问mes失败");
  2985. SendTaskMessage($"询问mes失败:{response}", MessageLevel.Error);
  2986. //产品发生改变并且供料器发生了变化时,需要通知PLC进行取料更改
  2987. await plc.WriteNodeAsync(addressConfig.Out_ScrewFeederIsChanged.Address, (Int16)1); //不发生改变
  2988. if (models)
  2989. {
  2990. await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)3);
  2991. SendTaskMessage($"发送plc信号3", MessageLevel.Debug);
  2992. return;
  2993. }
  2994. else
  2995. {
  2996. await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)2);
  2997. SendTaskMessage($"发送plc信号2", MessageLevel.Debug);
  2998. return;
  2999. }
  3000. }
  3001. }
  3002. LogHelper.WriteLogMes($"【mes结束QUERY】");
  3003. SendTaskMessage($"mes结束QUERY", MessageLevel.Info);
  3004. }
  3005. else
  3006. {
  3007. ProductMainSN[i] = DateTime.Now.ToString("yyyyMMddHHmmssfff");
  3008. if (i == 0)
  3009. {
  3010. _productService.CurrentProductCode = ProductMainSN[i];
  3011. }
  3012. LogHelper.WriteLogMes($"【未启用mes】");
  3013. SendTaskMessage($"未启用mes", MessageLevel.Info);
  3014. await plc.WriteNodeAsync(addressConfig.Out_ScrewFeederIsChanged.Address, (Int16)1); //不发生改变
  3015. await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)1);
  3016. SendTaskMessage($"发送plc信号2", MessageLevel.Debug);
  3017. }
  3018. }
  3019. // 上传本站数据
  3020. else if (state == 2)
  3021. {
  3022. DFC.Clear();//CTQ数据集合
  3023. // string productccode = ProductMainSN[i];
  3024. //获取当前工位的配置
  3025. var stationConfig = _configService.GetDeviceInfo(i);
  3026. var sysConfig = _configService.GetSystemConfiguration();
  3027. SendTaskMessage($"收到{i}:{stationConfig.DeviceName}上传本站数据请求...", MessageLevel.Debug);
  3028. //从服务器中获取当前时间
  3029. LogHelper.WriteLogMes($"【从服务器中获取当前时间】");
  3030. SendTaskMessage($"从服务器中获取当前时间", MessageLevel.Info);
  3031. var serverTimeResult = await _mesService.GetServerTimeAsync(i, ProductMainSN[i], 2);
  3032. //这里缺少上传MES的本站数据结果及开始和结束时间,需要补充
  3033. if (i == 0)
  3034. {
  3035. CTtime = serverTimeResult.Now.Subtract(_LastMesCheckTime[i]).TotalSeconds;
  3036. _eventAggregator.GetEvent<DelaySaveImageNotification>().Publish(true);//通知界面延迟保存图片,等待MES查询结果返回,避免图片和数据不同步
  3037. }
  3038. //读取产品结果
  3039. Int16 resultIndex = plc.ReadNode<Int16>(string.Format(addressConfig.In_Result.Address, i));
  3040. if (stationConfig.EnableMES)
  3041. {
  3042. //目前只记录当前站
  3043. if (i == 0)
  3044. await _systemDatabaseService.RecordProductionAsync(new ProductionRecord()
  3045. {
  3046. ProductName = currentProduct.Name,
  3047. ProductCode = ProductMainSN[i],
  3048. DeviceId = i,
  3049. DeviceName = "",
  3050. Category = resultIndex == 1 ? "OK" : "NG",
  3051. Quantity = 1,
  3052. Remark = $"TotalSeconds:{serverTimeResult.Now.Subtract(_LastMesCheckTime[i]).TotalSeconds.ToString("F2")}",
  3053. });
  3054. }
  3055. LogHelper.WriteLogMes($"【mes开始ADD】");
  3056. SendTaskMessage($"mes开始ADD", MessageLevel.Info);
  3057. if (i == 0)
  3058. {
  3059. foreach (var lockResult in lockResultsAll)
  3060. {
  3061. bool torqueRes, turnRes, pressureRes;
  3062. if (lockResult.LockTorque >= lockResult.TargetTorqueLowerLimit && lockResult.LockTorque <= lockResult.TargetTorqueUpperLimit)
  3063. {
  3064. torqueRes = true;
  3065. }
  3066. else torqueRes = false;
  3067. if (lockResult.Pressure >= stationConfig.DataInfo.CTQ_LSL_Pressure && lockResult.Pressure <= stationConfig.DataInfo.CTQ_USL_Pressure)
  3068. {
  3069. pressureRes = true;
  3070. }
  3071. else pressureRes = false;
  3072. if (lockResult.LockTurns >= lockResult.TargetTurnsLowerLimit && lockResult.LockTurns <= lockResult.TargetTurnsUpperLimit)
  3073. {
  3074. turnRes = true;
  3075. }
  3076. else turnRes = false;
  3077. string midValue = "NA", saveppid = "NA";
  3078. if (stationConfig.SaveCsvPPID) saveppid = ppid[i];
  3079. midValue = ((lockResult.TargetTorqueLowerLimit + lockResult.TargetTorqueUpperLimit) / 2).ToString("F3");
  3080. DFC.Add(new DataFormCtq
  3081. {
  3082. PPID = saveppid,
  3083. ScrewNum = lockResult.Number,
  3084. PROCESS_NAME = stationConfig.DataInfo.PROCESS_NAME,
  3085. PROCESS_START_TIME = _LastMesCheckTime[i].ToString("yyyy-MM-ddTHH:mm:ss") + "+08:00",
  3086. CTQ_NAME = "Torque",
  3087. CTQ_VALUE = lockResult.LockTorque,
  3088. CTQ_LSL = lockResult.TargetTorqueLowerLimit,
  3089. CTQ_USL = lockResult.TargetTorqueUpperLimit,
  3090. PROCESS_END_TIME = serverTimeResult.Now.ToString("yyyy-MM-ddTHH:mm:ss") + "+08:00",
  3091. SUPPLIER_NAME = stationConfig.DataInfo.SUPPLIER_NAME,
  3092. COMMODITY_TYPE = stationConfig.DataInfo.COMMODITY_TYPE,
  3093. REVISION = stationConfig.DataInfo.REVISION + "_" + revision[i],
  3094. WO = wo[i],
  3095. MFG_LOT = stationConfig.DataInfo.MFG_LOT,
  3096. MFG_ASSY_LINE = stationConfig.DataInfo.MFG_ASSY_LINE,
  3097. PROCESS_OUTCOME = torqueRes ? "PASS" : "FAIL",
  3098. PROCESS_MACHINE_ID = stationConfig.DataInfo.PROCESS_MACHINE_ID,
  3099. CTQ_UNIT_OF_MEASURE = "kgf.cm",
  3100. ERROR_MESSAGE = torqueRes ? "NA" : "Torque_Spec OOS",
  3101. PD_ATTR_01 = ProductMainSN[i],
  3102. PD_ATTR_02 = midValue,
  3103. PD_ATTR_03 = "NA",
  3104. PD_ATTR_04 = productSn,
  3105. PD_ATTR_05 = "NA",
  3106. });
  3107. midValue = ((lockResult.TargetTurnsLowerLimit + lockResult.TargetTurnsUpperLimit) / 2).ToString("F3");
  3108. DFC.Add(new DataFormCtq
  3109. {
  3110. PPID = saveppid,
  3111. ScrewNum = lockResult.Number,
  3112. PROCESS_NAME = stationConfig.DataInfo.PROCESS_NAME,
  3113. PROCESS_START_TIME = _LastMesCheckTime[i].ToString("yyyy-MM-ddTHH:mm:ss") + "+08:00",
  3114. CTQ_NAME = "Turn",
  3115. CTQ_VALUE = lockResult.LockTurns,
  3116. CTQ_LSL = lockResult.TargetTurnsLowerLimit,
  3117. CTQ_USL = lockResult.TargetTurnsUpperLimit,
  3118. PROCESS_END_TIME = serverTimeResult.Now.ToString("yyyy-MM-ddTHH:mm:ss") + "+08:00",
  3119. SUPPLIER_NAME = stationConfig.DataInfo.SUPPLIER_NAME,
  3120. COMMODITY_TYPE = stationConfig.DataInfo.COMMODITY_TYPE,
  3121. REVISION = stationConfig.DataInfo.REVISION + "_" + revision[i],
  3122. WO = wo[i],
  3123. MFG_LOT = stationConfig.DataInfo.MFG_LOT,
  3124. MFG_ASSY_LINE = stationConfig.DataInfo.MFG_ASSY_LINE,
  3125. PROCESS_OUTCOME = turnRes ? "PASS" : "FAIL",
  3126. PROCESS_MACHINE_ID = stationConfig.DataInfo.PROCESS_MACHINE_ID,
  3127. CTQ_UNIT_OF_MEASURE = "Turn",
  3128. ERROR_MESSAGE = turnRes ? "NA" : "Turn_Spec OOS",
  3129. PD_ATTR_01 = ProductMainSN[i],
  3130. PD_ATTR_02 = midValue,
  3131. PD_ATTR_03 = "NA",
  3132. PD_ATTR_04 = productSn,
  3133. PD_ATTR_05 = "NA",
  3134. });
  3135. midValue = ((stationConfig.DataInfo.CTQ_LSL_Pressure + stationConfig.DataInfo.CTQ_USL_Pressure) / 2).ToString("F3");
  3136. DFC.Add(new DataFormCtq
  3137. {
  3138. PPID = saveppid,
  3139. ScrewNum = lockResult.Number,
  3140. PROCESS_NAME = stationConfig.DataInfo.PROCESS_NAME,
  3141. PROCESS_START_TIME = _LastMesCheckTime[i].ToString("yyyy-MM-ddTHH:mm:ss") + "+08:00",
  3142. CTQ_NAME = "Pressure",
  3143. CTQ_VALUE = lockResult.Pressure,
  3144. CTQ_LSL = stationConfig.DataInfo.CTQ_LSL_Pressure,
  3145. CTQ_USL = stationConfig.DataInfo.CTQ_USL_Pressure,
  3146. PROCESS_END_TIME = serverTimeResult.Now.ToString("yyyy-MM-ddTHH:mm:ss") + "+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 = wo[i],
  3151. MFG_LOT = stationConfig.DataInfo.MFG_LOT,
  3152. MFG_ASSY_LINE = stationConfig.DataInfo.MFG_ASSY_LINE,
  3153. PROCESS_OUTCOME = pressureRes ? "PASS" : "FAIL",
  3154. PROCESS_MACHINE_ID = stationConfig.DataInfo.PROCESS_MACHINE_ID,
  3155. CTQ_UNIT_OF_MEASURE = "kg",
  3156. ERROR_MESSAGE = pressureRes ? "NA" : "Pressure_Spec OOS",
  3157. PD_ATTR_01 = ProductMainSN[i],
  3158. PD_ATTR_02 = midValue,
  3159. PD_ATTR_03 = "NA",
  3160. PD_ATTR_04 = productSn,
  3161. PD_ATTR_05 = "NA",
  3162. });
  3163. }
  3164. if (systemConfig.IsBasePartOpenLightKB)
  3165. {
  3166. string midValue = "999", saveppid = "NA";
  3167. double upvlalue = 0, doenvlaue = 0;
  3168. if (stationConfig.SaveCsvPPID) saveppid = ppid[i];
  3169. bool pressureRes = true;
  3170. foreach (var item in mes_cc)
  3171. {
  3172. switch (item.Key)
  3173. {
  3174. case "1":
  3175. upvlalue = Convert.ToDouble(currentProduct.GlobalParamEx.CC1DownLimit.ToString("F3"));
  3176. doenvlaue = Convert.ToDouble(currentProduct.GlobalParamEx.CC1UpLimit.ToString("F3"));
  3177. midValue = ((currentProduct.GlobalParamEx.CC1DownLimit + currentProduct.GlobalParamEx.CC1UpLimit) / 2).ToString("F3");
  3178. if (Convert.ToDouble(item.Value) < upvlalue || Convert.ToDouble(item.Value) > doenvlaue)
  3179. {
  3180. resultToMes.Add($"CC{item.Key}_Fail");
  3181. SendTaskMessage($"CC{item.Key}:超限", MessageLevel.Error);
  3182. }
  3183. break;
  3184. case "2":
  3185. upvlalue = Convert.ToDouble(currentProduct.GlobalParamEx.CC2DownLimit.ToString("F3"));
  3186. doenvlaue = Convert.ToDouble(currentProduct.GlobalParamEx.CC2UpLimit.ToString("F3"));
  3187. midValue = ((currentProduct.GlobalParamEx.CC2DownLimit + currentProduct.GlobalParamEx.CC2UpLimit) / 2).ToString("F3");
  3188. if (Convert.ToDouble(item.Value) < upvlalue || Convert.ToDouble(item.Value) > doenvlaue)
  3189. {
  3190. resultToMes.Add($"CC{item.Key}_Fail");
  3191. SendTaskMessage($"CC{item.Key}:超限", MessageLevel.Error);
  3192. }
  3193. break;
  3194. }
  3195. dataToMes.Add($"CC{item.Key}", $"{item.Value}");
  3196. DFC.Add(new DataFormCtq
  3197. {
  3198. PPID = saveppid,
  3199. ScrewNum = 1,
  3200. PROCESS_NAME = stationConfig.DataInfo.PROCESS_NAME,
  3201. PROCESS_START_TIME = _LastMesCheckTime[i].ToString("yyyy-MM-ddTHH:mm:ss") + "+08:00",
  3202. CTQ_NAME = $"CC{item.Key}",
  3203. CTQ_VALUE = Convert.ToDouble(item.Value.ToString()),
  3204. CTQ_LSL = doenvlaue,
  3205. CTQ_USL = upvlalue,
  3206. PROCESS_END_TIME = serverTimeResult.Now.ToString("yyyy-MM-ddTHH:mm:ss") + "+08:00",
  3207. SUPPLIER_NAME = stationConfig.DataInfo.SUPPLIER_NAME,
  3208. COMMODITY_TYPE = stationConfig.DataInfo.COMMODITY_TYPE,
  3209. REVISION = stationConfig.DataInfo.REVISION + "_" + revision[i],
  3210. WO = wo[i],
  3211. MFG_LOT = stationConfig.DataInfo.MFG_LOT,
  3212. MFG_ASSY_LINE = stationConfig.DataInfo.MFG_ASSY_LINE,
  3213. PROCESS_OUTCOME = pressureRes ? "PASS" : "FAIL",
  3214. PROCESS_MACHINE_ID = stationConfig.DataInfo.PROCESS_MACHINE_ID,
  3215. CTQ_UNIT_OF_MEASURE = "mm",
  3216. ERROR_MESSAGE = pressureRes ? "NA" : $"CC{item.Key} OOS",
  3217. PD_ATTR_01 = ProductMainSN[i],
  3218. PD_ATTR_02 = midValue,
  3219. PD_ATTR_03 = "NA",
  3220. PD_ATTR_04 = productSn,
  3221. PD_ATTR_05 = "NA",
  3222. });
  3223. }
  3224. }
  3225. }
  3226. //上传mes
  3227. if (stationConfig.EnableMES)
  3228. {
  3229. //测试使用,强制上传mes为pass
  3230. if (sysConfig.IsMesResult)
  3231. {
  3232. resultToMes.Clear();
  3233. }
  3234. (bool isSuccess, string response) = await _mesService.SubmitGetExAsync(ProductMainSN[i], "", dataToMes, resultToMes, _LastMesCheckTime[i], serverTimeResult.Now);
  3235. if (isSuccess)
  3236. {
  3237. LogHelper.WriteLogMes($"上传mes成功");
  3238. SendTaskMessage($"上传mes成功", MessageLevel.Debug);
  3239. }
  3240. else
  3241. {
  3242. LogHelper.WriteLogMes($"上传mes失败");
  3243. SendTaskMessage($"上传mes失败", MessageLevel.Error);
  3244. }
  3245. }
  3246. else
  3247. {
  3248. LogHelper.WriteLogMes($"上传mes成功");
  3249. SendTaskMessage($"上传mes成功", MessageLevel.Debug);
  3250. }
  3251. await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)3);
  3252. if (stationConfig.SaveCsv)
  3253. {
  3254. if (!string.IsNullOrEmpty(stationConfig.SaveCsvPath))
  3255. {
  3256. bool recordResult = false;
  3257. recordResult = RecordStationResult(i, DFC, ProductMainSN[i], serverTimeResult.Now.Subtract(_LastMesCheckTime[i]).TotalSeconds, _LastMesCheckTime[i], stationConfig.SaveCsvPath);
  3258. if (recordResult)
  3259. {
  3260. SendTaskMessage($"{i}:{stationConfig.DeviceName}产品记录保存成功", MessageLevel.Debug);
  3261. //上传数据
  3262. try
  3263. {
  3264. if (stationConfig.EnableMES)
  3265. {
  3266. DateTime dt = DateTime.Now;
  3267. string filename = $"{ProductMainSN[i]}_{dt.ToString("yyyyMMddHHmmss")}";
  3268. var device = _configService.GetDeviceInfo();
  3269. var config = _configService.GetSystemConfiguration();
  3270. (bool isSuc, string resp) = await _mesService.SendFtpFileAsync(ProductMainSN[i], filename);
  3271. if (isSuc)
  3272. {
  3273. LogHelper.WriteLogMes($"上传数据成功");
  3274. SendTaskMessage($"上传数据成功", MessageLevel.Debug);
  3275. string filePath = $"D:\\image\\Recored\\{dt.ToString("yyyy-MM")}\\{dt.ToString("MM-dd")}\\{currentProduct.Name}\\{ProductMainSN[i]}";
  3276. //string source_csv2 = $"{stationConfig.SaveCsvPath}\\{dt.ToString("yyyy-MM")}\\{i}_{stationConfig.DeviceName}_{dt.ToString("yyyy-MM-dd")}";
  3277. if (!Directory.Exists(filePath))
  3278. {
  3279. Directory.CreateDirectory(filePath);
  3280. }
  3281. string csvfilename = $"{i}_{stationConfig.DeviceName}_{dt.ToString("yyyy-MM-dd")}";
  3282. //复制csv
  3283. string source_csv = $"{stationConfig.SaveCsvPath}\\{dt.ToString("yyyy-MM")}\\{i}_{stationConfig.DeviceName}_{dt.ToString("yyyy-MM-dd")}.csv";
  3284. string target_csv = Path.Combine(filePath, Path.GetFileName(source_csv));
  3285. File.Copy(source_csv, target_csv, true);
  3286. //复制log
  3287. string source_log = $"..\\Log\\LogInfo\\{dt.ToString("yyyy-MM")}\\{dt.ToString("yyyy-MM-dd")}.txt";
  3288. string target_log = Path.Combine(filePath, Path.GetFileName(source_log));
  3289. File.Copy(source_log, target_log, true);
  3290. //判断是否保存锁付曲线
  3291. if (systemConfig.IsSaveScrewCurve)
  3292. {
  3293. string target_Directory = filePath + "\\扭力曲线\\";
  3294. string source_Torque_Image = config.ScrewCurvePath + $"\\{dt.ToString("yyyy-MM")}\\{dt.ToString("yyyy-MM-dd")}\\{currentProduct.Name}\\{ProductMainSN[i]}";
  3295. try
  3296. {
  3297. var col = Directory.GetFiles(source_Torque_Image);
  3298. Directory.CreateDirectory(target_Directory);
  3299. foreach (var item in col)
  3300. {
  3301. string target_Torque = target_Directory + item.Replace(source_Torque_Image, "");
  3302. File.Copy(item, target_Torque, true);
  3303. }
  3304. }
  3305. catch (Exception)
  3306. {
  3307. SendTaskMessage($"扭力曲线图读取失败", MessageLevel.Error);
  3308. }
  3309. }
  3310. CompressionImage(filePath, filename, device.Station, device.FixtureId);
  3311. //上传csv
  3312. bool isAfter20 = dt.Hour >= 20;
  3313. //判断是否是新的一天
  3314. bool isNewDay = dt.Date != _lastCompressDate.Date;
  3315. //当天还没执行过压缩
  3316. if (isAfter20 && isNewDay && !_hasCompressedToday)
  3317. {
  3318. // 执行压缩
  3319. CompressionSingleCsv(source_csv, csvfilename, device.Station, device.FixtureId);
  3320. _lastCompressDate = dt;
  3321. _hasCompressedToday = true;
  3322. LogHelper.WriteLogMes("csv上传数据成功");
  3323. SendTaskMessage($"csv上传数据成功", MessageLevel.Debug);
  3324. }
  3325. // 跨天后重置当天执行标记
  3326. if (!isNewDay && _hasCompressedToday)
  3327. {
  3328. _hasCompressedToday = false;
  3329. }
  3330. }
  3331. else
  3332. {
  3333. LogHelper.WriteLogMes($"上传数据失败");
  3334. SendTaskMessage($"上传数据失败", MessageLevel.Error);
  3335. }
  3336. }
  3337. else
  3338. {
  3339. LogHelper.WriteLogMes($"未启用mes");
  3340. SendTaskMessage($"未启用mes", MessageLevel.Error);
  3341. }
  3342. }
  3343. catch (Exception ex)
  3344. {
  3345. SendTaskMessage($"上传数据时出错:{ex.Message}", MessageLevel.Error);
  3346. LogHelper.WriteLogError($"上传数据时出错", ex);
  3347. }
  3348. }
  3349. else
  3350. {
  3351. SendTaskMessage($"{i}:{stationConfig.DeviceName}产品记录保存失败", MessageLevel.Error);
  3352. }
  3353. }
  3354. else
  3355. {
  3356. SendTaskMessage($"{i}:{stationConfig.DeviceName}工位未配置保存路径,无法保存产品记录", MessageLevel.Error);
  3357. }
  3358. }
  3359. else
  3360. {
  3361. SendTaskMessage($"{i}:{stationConfig.DeviceName}工位未配置保存产品记录", MessageLevel.Debug);
  3362. }
  3363. WriteProductLog(lockResultsAll, ProductMainSN[i], CTtime);
  3364. mes_cc.Clear();
  3365. LogHelper.WriteLogMes($"【mes结束ADD】");
  3366. SendTaskMessage($"mes结束ADD", MessageLevel.Info);
  3367. }
  3368. //kb 螺丝站位询问mes 具体需求跟plc对接
  3369. else if (state == 5)
  3370. {
  3371. SendTaskMessage($"收到KB Screw站点{i}过站检查请求(当站是否生产)...", MessageLevel.Debug);
  3372. //获取过站时间
  3373. _LastMesCheckTime[i] = (await _mesService.GetServerTimeAsync(i, code, 2)).Now;
  3374. if (_configService.GetDeviceInfo().EnableMES)
  3375. {
  3376. LogHelper.WriteLogMes($"【mes开始QUERY】");
  3377. SendTaskMessage($"mes开始QUERY", MessageLevel.Info);
  3378. SendTaskMessage($"基于RFID询问是否过站", MessageLevel.Debug);
  3379. (bool isSuccess, string response) = await _mesService.StationGetExAsync(code, "", 2);
  3380. //如果成功的话就代表跟正常mes询问一样,当前站需工作
  3381. if (isSuccess)
  3382. {
  3383. string ProductMainSN_Temp = "";
  3384. LogHelper.WriteLogMes($"询问mes成功");
  3385. SendTaskMessage($"询问mes成功:{response}", MessageLevel.Debug);
  3386. string[] item = response.Split('\n');
  3387. //获取过度件sn
  3388. string snItem = item.FirstOrDefault(f => f.Contains("sn="));
  3389. if (snItem != null)
  3390. {
  3391. ProductMainSN_Temp = snItem.Replace("sn=", "").Trim();
  3392. SendTaskMessage($"过度件sn:{ProductMainSN_Temp}", MessageLevel.Info);
  3393. }
  3394. LogHelper.WriteLogMes($"询问mes成功");
  3395. SendTaskMessage($"询问mes成功:{response}", MessageLevel.Debug);
  3396. (bool isSuccess2, string response2) = await _mesService.StationGetAsync(ProductMainSN_Temp, "", 2);//询问mes
  3397. if (isSuccess2)
  3398. {
  3399. item = response2.Split('\n');
  3400. snItem = item.FirstOrDefault(f => f.Contains("sn="));
  3401. if (snItem != null)
  3402. {
  3403. ProductMainSN[i] = snItem.Replace("sn=", "").Trim();
  3404. if (i == 0)
  3405. {
  3406. _productService.CurrentProductCode = ProductMainSN[i];
  3407. }
  3408. SendTaskMessage($"产品主sn:{ProductMainSN[i]}", MessageLevel.Info);
  3409. }
  3410. string woItem = item.FirstOrDefault(f => f.Contains("wo="));
  3411. if (woItem != null)
  3412. {
  3413. wo[i] = woItem.Replace("wo=", "").Trim();
  3414. SendTaskMessage($"工单信息:{wo[i]}", MessageLevel.Info);
  3415. }
  3416. string modelItem = item.FirstOrDefault(f => f.Contains("model_num="));
  3417. if (modelItem != null)
  3418. {
  3419. model_num[i] = modelItem.Replace("model_num=", "").Trim();
  3420. SendTaskMessage($"model_num:{model_num[i]}", MessageLevel.Info);
  3421. }
  3422. string colorItem = item.FirstOrDefault(f => f.Contains("color="));
  3423. if (colorItem != null)
  3424. {
  3425. color[i] = colorItem.Replace("color=", "").Trim();
  3426. SendTaskMessage($"color:{color[i]}", MessageLevel.Info);
  3427. }
  3428. string ppidItem = item.FirstOrDefault(f => f.Contains("ppid="));
  3429. if (ppidItem != null)
  3430. {
  3431. ppid[i] = ppidItem.Replace("ppid=", "").Trim();
  3432. SendTaskMessage($"ppid:{ppid[i]}", MessageLevel.Info);
  3433. }
  3434. string revisionItem = item.FirstOrDefault(f => f.Contains("stage="));
  3435. if (revisionItem != null)
  3436. {
  3437. revision[i] = revisionItem.Replace("stage=", "").Trim();
  3438. SendTaskMessage($"stage:{revision[i]}", MessageLevel.Info);
  3439. }
  3440. LogHelper.WriteLogMes($"第二次询问mes成功");
  3441. SendTaskMessage($"第二次询问mes成功:{response2}", MessageLevel.Debug);
  3442. //切换产品
  3443. if (i == 0)//i=0时才会切换产品
  3444. {
  3445. var q = await _systemDatabaseService.QueryProductFromMesAsync(model_num[i], color[i]);
  3446. if (q != null && !string.IsNullOrEmpty(q.ProductName))
  3447. {
  3448. ProductList = new ObservableCollection<ProductModel>(_productService.GetAllProducts());
  3449. Guid productId = Guid.Empty;
  3450. foreach (var itt in ProductList)
  3451. {
  3452. if (itt.Name == q.ProductName)
  3453. {
  3454. productId = itt.ID;
  3455. break;
  3456. }
  3457. }
  3458. if (currentProduct.ID != productId)
  3459. {
  3460. //切换产品
  3461. await _productService.LoadProductAsync(productId);
  3462. _productService.SetCurrentProduct(productId);
  3463. await WriteParameterToPlcExAsync();
  3464. SendTaskMessage($"切换产品成功", MessageLevel.Debug);
  3465. }
  3466. }
  3467. }
  3468. //产品发生改变并且供料器发生了变化时,需要通知PLC进行取料更改
  3469. await plc.WriteNodeAsync(addressConfig.Out_ScrewFeederIsChanged.Address, (Int16)1); //不发生改变
  3470. await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)1);
  3471. }
  3472. else
  3473. {
  3474. LogHelper.WriteLogMes($"第二次询问mes失败");
  3475. SendTaskMessage($"第二次询问mes失败:{response2}", MessageLevel.Error);
  3476. //产品发生改变并且供料器发生了变化时,需要通知PLC进行取料更改
  3477. await plc.WriteNodeAsync(addressConfig.Out_ScrewFeederIsChanged.Address, (Int16)1); //不发生改变
  3478. if (models)
  3479. {
  3480. await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)3);
  3481. }
  3482. else
  3483. {
  3484. await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)2);
  3485. }
  3486. }
  3487. }
  3488. else
  3489. {
  3490. LogHelper.WriteLogMes($"询问mes失败");
  3491. SendTaskMessage($"询问mes失败:{response}", MessageLevel.Error);
  3492. //产品发生改变并且供料器发生了变化时,需要通知PLC进行取料更改
  3493. await plc.WriteNodeAsync(addressConfig.Out_ScrewFeederIsChanged.Address, (Int16)1); //不发生改变
  3494. //表示前面的kb screw站点已经打完螺丝并且过站成功到3d aoi,根据plc的需求直接放行
  3495. if (response.Contains("3D AOI"))
  3496. {
  3497. await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)2);
  3498. return;
  3499. }
  3500. //表示kb组装 站点ng 具体写入型号跟plc对接
  3501. else if (response.Contains("C_KB_Link"))
  3502. {
  3503. await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)5);
  3504. return;
  3505. }
  3506. // 后面还有一些判断条件也需跟当前AE对接
  3507. await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)2);
  3508. }
  3509. LogHelper.WriteLogMes($"【mes结束QUERY】");
  3510. SendTaskMessage($"mes结束QUERY", MessageLevel.Info);
  3511. }
  3512. else
  3513. {
  3514. ProductMainSN[i] = DateTime.Now.ToString("yyyyMMddHHmmssfff");
  3515. if (i == 0)
  3516. {
  3517. _productService.CurrentProductCode = ProductMainSN[i];
  3518. }
  3519. LogHelper.WriteLogMes($"【未启用mes】");
  3520. SendTaskMessage($"未启用mes", MessageLevel.Info);
  3521. await plc.WriteNodeAsync(addressConfig.Out_ScrewFeederIsChanged.Address, (Int16)1); //不发生改变
  3522. await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)1);
  3523. }
  3524. }
  3525. else
  3526. {
  3527. SendTaskMessage($"{i}过站检查信号:state={state},处理为默认状态", MessageLevel.Alarm);
  3528. await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)0);
  3529. return;
  3530. }
  3531. // _LastMesCheck[i] = 0;
  3532. }
  3533. }
  3534. }
  3535. }
  3536. }
  3537. catch (Exception ex)
  3538. {
  3539. SendTaskMessage(ex.Message, MessageLevel.Error);
  3540. }
  3541. }
  3542. #endregion
  3543. #region 方法
  3544. public void SendTaskMessage(string msg, MessageLevel level)
  3545. {
  3546. App.Current.Dispatcher.Invoke(() =>
  3547. {
  3548. _eventAggregator.GetEvent<TaskMessageNotification>().Publish(new Models.MessageStruct() { Message = msg, level = level });
  3549. });
  3550. }
  3551. #region 界面状态显示
  3552. // 新增字段
  3553. private readonly object _runStatusLock = new object();
  3554. private RunDisplayMode _lastRunDisplayMode = RunDisplayMode.Unknown;
  3555. /// <summary>
  3556. /// 刷新当前运行状态
  3557. /// </summary>
  3558. public void CurrentRunStatus()
  3559. {
  3560. lock (_runStatusLock)
  3561. {
  3562. RunDisplayMode mode;
  3563. try
  3564. {
  3565. mode = EvaluateRunDisplayMode();
  3566. }
  3567. catch (Exception ex)
  3568. {
  3569. LogHelper.WriteLogError("刷新运行状态时出错", ex);
  3570. mode = RunDisplayMode.Manual;
  3571. }
  3572. ApplyRunDisplayMode(mode);
  3573. }
  3574. }
  3575. /// <summary>
  3576. /// 计算当前应该显示的模式
  3577. /// </summary>
  3578. private RunDisplayMode EvaluateRunDisplayMode()
  3579. {
  3580. // 1. 流线模式优先
  3581. if (_LastClearMode)
  3582. {
  3583. return RunDisplayMode.Clear;
  3584. }
  3585. // 2. 调机模式次优先
  3586. if (_LastManualMode)
  3587. {
  3588. return RunDisplayMode.Manual;
  3589. }
  3590. // 3. 没进自动运行,默认按调机显示
  3591. if (!_LastAutoRunning)
  3592. {
  3593. return RunDisplayMode.Manual;
  3594. }
  3595. // 4. 自动运行时,再检查前置条件
  3596. bool connectStatus = IsAllDeviceConnected();
  3597. bool mesStatus = IsMesReady();
  3598. bool paraStatus = IsParameterReady();
  3599. var curUser = _systemDatabaseService.GetCurrentUser();
  3600. if (connectStatus && mesStatus && paraStatus && curUser.userPart == UserPart.Operator)
  3601. {
  3602. return RunDisplayMode.Auto;
  3603. }
  3604. return RunDisplayMode.Manual;
  3605. }
  3606. /// <summary>
  3607. /// 所有设备是否连接正常
  3608. /// </summary>
  3609. private bool IsAllDeviceConnected()
  3610. {
  3611. List<StatusInfo> statusSnapshot = null;
  3612. App.Current.Dispatcher.Invoke(() =>
  3613. {
  3614. statusSnapshot = Status?.ToList() ?? new List<StatusInfo>();
  3615. });
  3616. if (statusSnapshot.Count == 0)
  3617. {
  3618. return false;
  3619. }
  3620. foreach (var item in statusSnapshot)
  3621. {
  3622. // 注意:这里不要直接 item.Background == Brushes.Red
  3623. if (item?.Background is SolidColorBrush brush)
  3624. {
  3625. bool res = true;
  3626. App.Current.Dispatcher.Invoke(() =>
  3627. {
  3628. if (brush.Color == Colors.Red)
  3629. {
  3630. SendTaskMessage("当前设备存在连接失败,请检查..." + item.Message, MessageLevel.Error);
  3631. res = false;
  3632. }
  3633. });
  3634. if (!res)
  3635. {
  3636. return false;
  3637. }
  3638. }
  3639. }
  3640. return true;
  3641. }
  3642. /// <summary>
  3643. /// MES相关配置是否满足
  3644. /// </summary>
  3645. private bool IsMesReady()
  3646. {
  3647. var allDevices = _configService.GetAllDeviceInfos();
  3648. for (int i = 0; i < ((allDevices.Count >= 8) ? 2 : 1); i++)
  3649. {
  3650. var stationConfig = allDevices.FirstOrDefault(d => d.DeviceNo == (i == 0 ? 0 : 7));
  3651. if (stationConfig == null)
  3652. {
  3653. SendTaskMessage("当前MES未配置,请检查...", MessageLevel.Error);
  3654. return false;
  3655. }
  3656. if (string.IsNullOrEmpty(stationConfig.SaveCsvPath) && i == 0)
  3657. {
  3658. SendTaskMessage("当前CSV路径为空,请检查...", MessageLevel.Error);
  3659. return false;
  3660. }
  3661. if (!stationConfig.EnableMES || !stationConfig.SaveCsv)
  3662. {
  3663. SendTaskMessage("当前未启用MES或未启用CSV保存,请检查...", MessageLevel.Error);
  3664. return false;
  3665. }
  3666. }
  3667. return true;
  3668. }
  3669. /// <summary>
  3670. /// 参数相关配置是否满足
  3671. /// </summary>
  3672. private bool IsParameterReady()
  3673. {
  3674. var systemConfig = _configService.GetSystemConfiguration();
  3675. if (systemConfig == null)
  3676. {
  3677. SendTaskMessage("当前无系统文件,请检查...", MessageLevel.Error);
  3678. return false;
  3679. }
  3680. if (!systemConfig.IsSaveScrewCurve)
  3681. {
  3682. SendTaskMessage("当前保存扭力曲线未勾选,请检查...", MessageLevel.Error);
  3683. return false;
  3684. }
  3685. if (string.IsNullOrEmpty(systemConfig.ScrewCurvePath))
  3686. {
  3687. SendTaskMessage("当前保存扭力曲线为空,请检查...", MessageLevel.Error);
  3688. return false;
  3689. }
  3690. return true;
  3691. }
  3692. /// <summary>
  3693. /// 应用显示模式到UI
  3694. /// </summary>
  3695. private void ApplyRunDisplayMode(RunDisplayMode mode)
  3696. {
  3697. // 模式没变化就不重复刷UI,也不重复打日志
  3698. if (_lastRunDisplayMode == mode)
  3699. {
  3700. return;
  3701. }
  3702. _lastRunDisplayMode = mode;
  3703. App.Current.Dispatcher.Invoke(() =>
  3704. {
  3705. switch (mode)
  3706. {
  3707. case RunDisplayMode.Auto:
  3708. CurrentDisplayMode = mode;
  3709. SendTaskMessage("当前为运行模式", MessageLevel.Debug);
  3710. break;
  3711. case RunDisplayMode.Clear:
  3712. CurrentDisplayMode = mode;
  3713. SendTaskMessage("当前为流线模式", MessageLevel.Info);
  3714. break;
  3715. case RunDisplayMode.Manual:
  3716. default:
  3717. CurrentDisplayMode = mode;
  3718. SendTaskMessage("当前为调机模式", MessageLevel.Info);
  3719. break;
  3720. }
  3721. });
  3722. }
  3723. #endregion
  3724. #region 压力采集
  3725. private float _maxPressure = float.MinValue;
  3726. /// <summary>
  3727. /// 开始采集压力值
  3728. /// </summary>
  3729. /// <param name="productName">配方名称</param>
  3730. /// <param name="productCode">产品编号</param>
  3731. /// <param name="positionIndex">锁付点编号</param>
  3732. private void StartPressureCollection(string productName, string productCode, int positionIndex)
  3733. {
  3734. try
  3735. {
  3736. // cancel existing if any
  3737. //_pressureCts?.Cancel();
  3738. //_pressureTimeoutCts?.Cancel();
  3739. //_pressureLinkedCts?.Dispose();
  3740. //_pressureSamples?.Clear();
  3741. //_pressureTimeoutCts[positionIndex]?.Cancel();
  3742. _pressureSamples?.Clear();
  3743. _pressureCts[positionIndex] = new CancellationTokenSource();
  3744. _pressureTimeoutCts[positionIndex] = new CancellationTokenSource();
  3745. //_pressureCts = new CancellationTokenSource();
  3746. //_pressureTimeoutCts = new CancellationTokenSource();
  3747. // timeout after configured seconds
  3748. _pressureTimeoutCts[positionIndex].CancelAfter(TimeSpan.FromSeconds(_pressureMaxDurationSeconds));
  3749. // link tokens so either stop or timeout cancels
  3750. _pressureLinkedCts[positionIndex] = CancellationTokenSource.CreateLinkedTokenSource(_pressureCts[positionIndex].Token, _pressureTimeoutCts[positionIndex].Token);
  3751. var token = _pressureLinkedCts[positionIndex].Token;
  3752. var addressConfig = _configService.GetPlcAddresses();
  3753. var plc = _plcService.GetPlcByNumber(addressConfig.PlcNo) as OpcUaClientPLC;
  3754. if (plc == null || !plc.IsConnected) return;
  3755. Task.Run(async () =>
  3756. {
  3757. SendTaskMessage($"Pressure collection started for position {positionIndex}", MessageLevel.Debug);
  3758. while (!token.IsCancellationRequested)
  3759. {
  3760. try
  3761. {
  3762. float pressure = plc.ReadNode<float>(addressConfig.In_PressureValue.Address);
  3763. lock (_pressureSamples)
  3764. {
  3765. //SendTaskMessage($"压力采集时出错:{pressure}", MessageLevel.Info);
  3766. _pressureSamples.Add((DateTime.Now, pressure));
  3767. if (pressure > _maxPressure)
  3768. {
  3769. _maxPressure = pressure;
  3770. }
  3771. }
  3772. }
  3773. catch (Exception ex)
  3774. {
  3775. LogHelper.WriteLogError("采集压力值时出错", ex);
  3776. }
  3777. await Task.Delay(_pressureSampleIntervalMs);
  3778. }
  3779. SendTaskMessage($"Pressure {_pressureSamples}", MessageLevel.Debug);
  3780. // if cancelled due to timeout
  3781. //if (_pressureTimeoutCts[positionIndex].IsCancellationRequested && !_pressureCts[positionIndex].IsCancellationRequested)
  3782. //{
  3783. // SendTaskMessage("Pressure collection timed out, saving collected data.", MessageLevel.Alarm);
  3784. // await StopAndSavePressureCollectionAsync(productName, productCode, positionIndex);
  3785. //}
  3786. }, token);
  3787. }
  3788. catch (Exception ex)
  3789. {
  3790. LogHelper.WriteLogError("StartPressureCollection error", ex);
  3791. SendTaskMessage($"压力采集时出错:{ex.Message}", MessageLevel.Error);
  3792. }
  3793. }
  3794. /// <summary>
  3795. /// 停止采集并保存压力数据
  3796. /// </summary>
  3797. /// <param name="productName"></param>
  3798. /// <param name="productCode"></param>
  3799. /// <param name="positionIndex"></param>
  3800. /// <returns></returns>
  3801. private async Task StopAndSavePressureCollectionAsync(string productName, string productCode, int positionIndex)
  3802. {
  3803. try
  3804. {
  3805. // 获取当前产品
  3806. var currentProduct = _productService.GetCurrentProduct();
  3807. if (currentProduct == null)
  3808. {
  3809. return;
  3810. }
  3811. // cancel sampling
  3812. //_pressureCts?.Cancel();
  3813. // allow worker to observe cancel
  3814. //await Task.Delay(50);
  3815. List<(DateTime Timestamp, float Value)> snapshot;
  3816. lock (_pressureSamples)
  3817. {
  3818. snapshot = new List<(DateTime, float)>(_pressureSamples);
  3819. }
  3820. if (snapshot.Count == 0)
  3821. {
  3822. SendTaskMessage("No pressure samples collected.", MessageLevel.Info);
  3823. return;
  3824. }
  3825. // save to file under configured path
  3826. try
  3827. {
  3828. //获取系统参数
  3829. var systemConfig = _configService.GetSystemConfiguration();
  3830. string datePath = DateTime.Now.ToString("yyyy-MM") + "\\" + DateTime.Now.ToString("yyyy-MM-dd") + "\\";
  3831. string dirPath = System.IO.Path.Combine(systemConfig.ScrewCurvePath, datePath, currentProduct.Name, positionIndex.ToString());
  3832. if (!Directory.Exists(dirPath))
  3833. {
  3834. Directory.CreateDirectory(dirPath);
  3835. }
  3836. string fileName = $"pressure-{productCode}-{positionIndex}-{DateTime.Now.ToString("HHmmss")}.csv";
  3837. string fullPath = Path.Combine(dirPath, fileName);
  3838. string pngfileName = $"pressure-{productCode}-{positionIndex}-{DateTime.Now.ToString("HHmmss")}.png";
  3839. string pngPath = Path.Combine(dirPath, pngfileName);
  3840. if (!systemConfig.IsSaveScrewCurve)
  3841. {
  3842. fullPath = string.Empty;
  3843. pngPath = string.Empty;
  3844. }
  3845. App.Current.Dispatcher.Invoke(() =>
  3846. {
  3847. _eventAggregator.GetEvent<PressureFinishNotification>().Publish((true, snapshot.ToArray(), pngPath));
  3848. });
  3849. //判断是否保存锁付曲线
  3850. if (systemConfig.IsSaveScrewCurve)
  3851. {
  3852. if (!string.IsNullOrEmpty(fullPath))
  3853. {
  3854. var sb = new StringBuilder();
  3855. sb.AppendLine("Timestamp,Value");
  3856. foreach (var s in snapshot)
  3857. {
  3858. sb.AppendLine($"{s.Timestamp:O},{s.Value}");
  3859. }
  3860. File.WriteAllText(fullPath, sb.ToString());
  3861. SendTaskMessage($"Pressure samples saved to {fullPath}", MessageLevel.Debug);
  3862. }
  3863. }
  3864. //var sb = new StringBuilder();
  3865. //sb.AppendLine("Timestamp,Value");
  3866. //foreach (var s in snapshot)
  3867. //{
  3868. // sb.AppendLine($"{s.Timestamp:O},{s.Value}");
  3869. //}
  3870. //File.WriteAllText(fullPath, sb.ToString());
  3871. // SendTaskMessage($"Pressure samples saved to {fullPath}", MessageLevel.Debug);
  3872. }
  3873. catch (Exception ex)
  3874. {
  3875. LogHelper.WriteLogError("保存压力采样文件时出错", ex);
  3876. SendTaskMessage("保存压力采样文件时出错: " + ex.Message, MessageLevel.Error);
  3877. }
  3878. }
  3879. catch (Exception ex)
  3880. {
  3881. LogHelper.WriteLogError("StopAndSavePressureCollectionAsync error", ex);
  3882. }
  3883. finally
  3884. {
  3885. try
  3886. {
  3887. _pressureLinkedCts[positionIndex]?.Cancel();
  3888. _pressureLinkedCts[positionIndex]?.Dispose();
  3889. _pressureTimeoutCts[positionIndex]?.Dispose();
  3890. _pressureCts[positionIndex]?.Dispose();
  3891. _pressureSamples?.Clear();
  3892. //_maxPressure = 0;
  3893. }
  3894. catch { }
  3895. }
  3896. }
  3897. #endregion
  3898. #region 产品记录
  3899. private async void DoYieldTime(object state)
  3900. {
  3901. try
  3902. {
  3903. //获取当前产品
  3904. var currentproduct = _productService.GetCurrentProduct();
  3905. if (currentproduct == null) return;
  3906. TotalQuantity = await _systemDatabaseService.GetOverallProductionAsync(currentproduct.Name);
  3907. //TotalQuantityToday = await _systemDatabaseService.GetTodayProductionAsync(currentproduct.Name);
  3908. //CurrentUPH = await _systemDatabaseService.GetLastHourProductionAsync(currentproduct.Name);
  3909. ////
  3910. ThrowNumber = await _systemDatabaseService.GetThrowNumberAsync();
  3911. //NgNumber = await _systemDatabaseService.GetNGProductionAsync(currentproduct.Name);
  3912. //Yield = ((double)TotalQuantityToday - (double)NgNumber) / (double)TotalQuantityToday * 100;
  3913. TotalQuantityToday = 0;
  3914. CurrentUPH = 0;
  3915. NgNumber = 0;
  3916. Yield = 0;
  3917. ObservableCollection<ProductModel> ProductList = new ObservableCollection<ProductModel>(_productService.GetAllProducts());
  3918. foreach (var product in ProductList)
  3919. {
  3920. //TotalQuantity += await _systemDatabaseService.GetOverallProductionAsync(product.Name);
  3921. TotalQuantityToday += await _systemDatabaseService.GetTodayProductionAsync(product.Name);
  3922. CurrentUPH += await _systemDatabaseService.GetLastHourProductionAsync(product.Name);
  3923. NgNumber += await _systemDatabaseService.GetNGProductionAsync(product.Name);
  3924. Yield += ((double)TotalQuantityToday - (double)NgNumber) / (double)TotalQuantityToday * 100;
  3925. }
  3926. }
  3927. catch (Exception)
  3928. {
  3929. }
  3930. }
  3931. #endregion
  3932. #region 字符判定
  3933. /// <summary>
  3934. /// 判断是否符合当站
  3935. /// </summary>
  3936. /// <param name="data"></param>
  3937. /// <param name="checkData"></param>
  3938. /// <returns></returns>
  3939. private bool CheckCode(string data, string checkData)
  3940. {
  3941. try
  3942. {
  3943. if (data.Length != checkData.Length)
  3944. {
  3945. var len = checkData.Length - data.Length;
  3946. LogHelper.WriteLogMes($"判断是否符合当站失败,字符长度差{len}");
  3947. SendTaskMessage($"判断是否符合当站失败,字符长度差{len}", MessageLevel.Error);
  3948. return false;
  3949. }
  3950. int count = checkData.Length;
  3951. for (int i = 0; i < count; i++)
  3952. {
  3953. if (checkData[i] == '*')//不需要检测的部分
  3954. {
  3955. continue;
  3956. }
  3957. if (data[i] != checkData[i])//有值不一致
  3958. {
  3959. LogHelper.WriteLogMes($"判断是否符合当站失败,第{i}个字符,不一致");
  3960. SendTaskMessage($"判断是否符合当站失败,第{i}个字符,不一致", MessageLevel.Error);
  3961. return false;
  3962. }
  3963. }
  3964. return true;
  3965. }
  3966. catch (Exception ex)
  3967. {
  3968. LogHelper.WriteLogMes($"判断是否符合当站失败,{ex}");
  3969. SendTaskMessage($"判断是否符合当站失败,{ex}", MessageLevel.Error);
  3970. return false;
  3971. }
  3972. }
  3973. #endregion
  3974. #region vm启动检测
  3975. /// <summary>
  3976. /// 杀死所有VM有关进程
  3977. /// </summary>
  3978. public void KillVMProcess()
  3979. {
  3980. KillProcess("VisionMasterServerApp");
  3981. KillProcess("VisionMaster");
  3982. KillProcess("VmModuleProxy.exe");
  3983. KillProcess("vServerApp.exe");
  3984. }
  3985. /// <summary>
  3986. /// 杀死指定进程名的进程
  3987. /// </summary>
  3988. /// <param name="strKillName"></param>
  3989. public void KillProcess(string strKillName)
  3990. {
  3991. foreach (System.Diagnostics.Process p in System.Diagnostics.Process.GetProcesses())
  3992. {
  3993. if (p.ProcessName.Contains(strKillName))
  3994. {
  3995. try
  3996. {
  3997. p.Kill();
  3998. p.WaitForExit();
  3999. }
  4000. catch (Exception ex)
  4001. {
  4002. LogHelper.WriteLogError(string.Format(Lang.杀死进程0时失败, strKillName), ex);
  4003. }
  4004. }
  4005. }
  4006. }
  4007. #endregion
  4008. #region 拍螺丝数据保存csv
  4009. /// <summary>
  4010. /// 通用方法:将存储XYU的字典导出为规范CSV文件
  4011. /// </summary>
  4012. /// <param name="dataDict">存储XYU的字典</param>
  4013. /// <param name="savePath">CSV文件保存路径</param>
  4014. /// <param name="appendMode">是否追加模式(true=每次运行新增一行,false=覆盖原有文件)</param>
  4015. /// <param name="decimalPlaces">数值保留的小数位数(默认4位,适配工业视觉精度)</param>
  4016. static void ExportDictionaryToCsv(Dictionary<string, double> dataDict, string savePath, bool appendMode = false, int decimalPlaces = 4)
  4017. {
  4018. // 自动创建目录(避免路径不存在报错)
  4019. string directory = Path.GetDirectoryName(savePath);
  4020. if (!Directory.Exists(directory))
  4021. {
  4022. Directory.CreateDirectory(directory);
  4023. }
  4024. // 构建CSV内容
  4025. StringBuilder csvContent = new StringBuilder();
  4026. // 处理表头:仅当文件不存在/非追加模式时,才写入表头
  4027. if (!appendMode || !File.Exists(savePath))
  4028. {
  4029. csvContent.AppendLine(string.Join(",", dataDict.Keys));
  4030. }
  4031. // 处理数据行:按指定小数位数格式化数值
  4032. List<string> formattedValues = new List<string>();
  4033. foreach (var value in dataDict.Values)
  4034. {
  4035. formattedValues.Add(Math.Round(value, decimalPlaces).ToString());
  4036. }
  4037. csvContent.AppendLine(string.Join(",", formattedValues));
  4038. // 写入文件:UTF-8带BOM编码,Excel打开无乱码
  4039. if (appendMode)
  4040. {
  4041. // 追加模式:在文件末尾新增内容
  4042. File.AppendAllText(savePath, csvContent.ToString(), Encoding.UTF8);
  4043. }
  4044. else
  4045. {
  4046. // 覆盖模式:新建/替换文件
  4047. File.WriteAllText(savePath, csvContent.ToString(), Encoding.UTF8);
  4048. }
  4049. }
  4050. public bool ExportDictionaryToCsv1(string rfid, double x, double y, double u)
  4051. {
  4052. try
  4053. {
  4054. DateTime dt = DateTime.Now;
  4055. if (rfid == null || rfid.Count() == 0)
  4056. {
  4057. rfid = dt.ToString("MM-dd-mm");
  4058. }
  4059. string path = $"D:\\视觉检测数据XYU数据\\{dt.ToString("yyyy-MM")}\\{dt.ToString("yyyy-MM-dd")}\\{rfid}.csv";
  4060. StringBuilder sb = new StringBuilder();
  4061. sb.AppendLine($"SN,X,Y,U");
  4062. sb.AppendLine();
  4063. sb.AppendLine($"{rfid},{x},{y},{u}");
  4064. sb.AppendLine();
  4065. if (!Directory.Exists(Path.GetDirectoryName(path))) //判断文件夹是否
  4066. {
  4067. Directory.CreateDirectory(Path.GetDirectoryName(path)); //创建文件夹
  4068. }
  4069. if (File.Exists(path))
  4070. {
  4071. // 使用 StreamWriter 以追加模式打开文件
  4072. StreamWriter sw = new StreamWriter(path, true, Encoding.UTF8);
  4073. sw.WriteLine(sb.ToString().TrimEnd(','));
  4074. //sw.WriteLine(sb1.ToString().TrimEnd(','));
  4075. sw.Close();
  4076. sw.Dispose();
  4077. }
  4078. else
  4079. {
  4080. StreamWriter sw = new StreamWriter(path, true, Encoding.UTF8);
  4081. sw.WriteLine(sb.ToString().TrimEnd(','));
  4082. sw.Close();
  4083. sw.Dispose();
  4084. }
  4085. return true;
  4086. }
  4087. catch (Exception ex)
  4088. {
  4089. LogHelper.WriteLogError("写入产品记录时出错!", ex);
  4090. return false;
  4091. }
  4092. }
  4093. #endregion
  4094. #region 产品锁付记录进行存储
  4095. /// <summary>
  4096. /// 产品锁付记录进行存储
  4097. /// </summary>
  4098. /// <param name="result">结果</param>
  4099. public void WriteProductLog(List<LockResult> lockResults, string rfid, double ct)
  4100. {
  4101. try
  4102. {
  4103. string result = "";//产品的总结果
  4104. foreach (var item in lockResults)
  4105. {
  4106. result = "OK";
  4107. if (!item.LockPassed)
  4108. {
  4109. result = "NG";
  4110. break;
  4111. }
  4112. }
  4113. DateTime dt = DateTime.Now;
  4114. int number = lockResults.Count;//螺丝数量
  4115. string path = $"D:\\Data\\生产数据\\{dt.ToString("yyyy-MM")}\\{dt.ToString("yyyy-MM-dd")}.csv";
  4116. StringBuilder sb = new StringBuilder();
  4117. //具体的ct需要优化
  4118. sb.Append($"{dt.ToString("yyyy-MM-dd HH:mm:ss")},{lockResults[0].ProductName},{Math.Round(ct, 3)},{rfid},{result},{lockResults[number - 1].NozzleNumber},{lockResults[number - 1].HeadNumber},");
  4119. foreach (var item in lockResults)
  4120. {
  4121. try
  4122. {
  4123. string res = "";
  4124. if (item.LockPassed)
  4125. {
  4126. res = "Pass";
  4127. }
  4128. else res = "Fail";
  4129. sb.Append($"{res},{item.ResultDetails},{item.LockTorque},{item.TargetTorqueUpperLimit},{item.TargetTorqueLowerLimit},{item.LockTurns},{item.TargetTurnsUpperLimit},{item.TargetTurnsLowerLimit},{item.Pressure},{item.PositionX},{item.PositionY},{item.StartPositionZ},{item.EndPositionZ},{item.LockDescentSpeed},");
  4130. }
  4131. catch (Exception)
  4132. {
  4133. sb.Append($",,,,,,,,,,,,,,");
  4134. }
  4135. }
  4136. //========================
  4137. if (!Directory.Exists(Path.GetDirectoryName(path))) //判断文件夹是否
  4138. {
  4139. Directory.CreateDirectory(Path.GetDirectoryName(path)); //创建文件夹
  4140. }
  4141. if (File.Exists(path))
  4142. {
  4143. // 使用 StreamWriter 以追加模式打开文件
  4144. StreamWriter sw = new StreamWriter(path, true, Encoding.UTF8);
  4145. sw.WriteLine(sb.ToString().TrimEnd(','));
  4146. sw.Close();
  4147. sw.Dispose();
  4148. }
  4149. else
  4150. {
  4151. StreamWriter sw = new StreamWriter(path, true, Encoding.UTF8);
  4152. StringBuilder sb1 = new StringBuilder();
  4153. StringBuilder sb_up = new StringBuilder();
  4154. StringBuilder sb_down = new StringBuilder();
  4155. sb1.Append("时间,产品名称,CT,RFID,产品结果,吸嘴数量,披头数量,");
  4156. for (int i = 0; i < number; i++)
  4157. {
  4158. sb1.Append($"螺丝{i + 1}结果,螺丝{i + 1}结果详情,螺丝{i + 1}扭矩,螺丝{i + 1}扭矩上限,螺丝{i + 1}扭矩下限,螺丝{i + 1}圈数,螺丝{i + 1}圈数上限,螺丝{i + 1}圈数下限,螺丝{i + 1}压力值,螺丝{i + 1}锁附X,螺丝{i + 1}锁附Y,螺丝{i + 1}开始位置Z,螺丝{i + 1}结束位置Z,螺丝{i + 1}锁附下降速度,");
  4159. }
  4160. sw.WriteLine(sb1.ToString().TrimEnd(','));
  4161. sw.WriteLine(sb.ToString().TrimEnd(','));
  4162. sw.Close();
  4163. sw.Dispose();
  4164. }
  4165. }
  4166. catch (Exception ex)
  4167. {
  4168. LogHelper.WriteLogError("写入产品记录时出错!", ex);
  4169. }
  4170. lockResultsAll.Clear();
  4171. }
  4172. /// <summary>
  4173. /// 记录当前工位的锁付结果
  4174. /// </summary>
  4175. /// <param name="stationIndex"></param>
  4176. /// <param name="lockResults"></param>
  4177. /// <param name="productMainSN"></param>
  4178. /// <param name="ct"></param>
  4179. /// <param name="timestamp"></param>
  4180. /// <param name="savePath"></param>
  4181. /// <param name="isSingleLine">是否将所有螺丝的锁付结果一行显示,还是每颗螺丝单独一行显示,默认所有颗螺丝单独一行显示</param>
  4182. /// <returns></returns>
  4183. private bool RecordLockResult(int stationIndex, List<LockResult> lockResults, string productMainSN, double ct, DateTime timestamp, string savePath, bool isSingleLine = true, bool isFirstEnter = true)
  4184. {
  4185. try
  4186. {
  4187. //获取当前工位的配置
  4188. var stationConfig = _configService.GetDeviceInfo(stationIndex);
  4189. if (stationConfig == null) return false;
  4190. //如果保存路径为空,则不保存
  4191. if (string.IsNullOrEmpty(savePath)) return false;
  4192. //如果锁付结果列表为空,则不保存
  4193. if (lockResults == null || lockResults.Count == 0) return false;
  4194. string fullPath = savePath;
  4195. //如果保存路径是一个文件路径,则直接保存,否则按照工位和日期进行保存
  4196. if (File.Exists(savePath) || Path.HasExtension(savePath))
  4197. {
  4198. fullPath = savePath;
  4199. //如果路径不存在,则创建
  4200. // 创建目录
  4201. string directory = Path.GetDirectoryName(fullPath);
  4202. if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
  4203. {
  4204. Directory.CreateDirectory(directory);
  4205. }
  4206. }
  4207. else
  4208. {
  4209. //锁付结果保存至CSV文件,文件名格式:savePath\yyyy-MM\工位编号_工位名称_yyyy-MM-dd.csv
  4210. string datePath = timestamp.ToString("yyyy-MM") + "\\";
  4211. string dirPath = Path.Combine(savePath, datePath);
  4212. if (!Directory.Exists(dirPath)) Directory.CreateDirectory(dirPath);
  4213. string fileName = $"{stationIndex}_{stationConfig.DeviceName}_{timestamp.ToString("yyyy-MM-dd")}.csv";
  4214. fullPath = Path.Combine(dirPath, fileName);
  4215. }
  4216. StringBuilder sb = new StringBuilder();
  4217. if (!File.Exists(fullPath))
  4218. {
  4219. if (isSingleLine)
  4220. {
  4221. //将所有螺丝的锁付结果一行显示,列名格式:时间,产品主SN,CT,工位编号,工位名称,配方名称,配方编号,螺丝编号1,锁付结果1,锁付圈数1,锁付扭矩1,锁付时长1,电批程序编号1,结果详情1,压力值1,扭矩上限1,扭矩下限1,圈数上限1,圈数下限1,电批锁付速度1,锁附X1,锁附Y1,开始位置Z1,结束位置Z1,锁附下降速度1,螺丝供料器编号,...(依次类推直到螺丝编号n)
  4222. sb.Append("时间,产品主SN,CT,工位编号,工位名称,配方名称,配方编号,");
  4223. for (int i = 0; i < lockResults.Count; i++)
  4224. {
  4225. sb.Append($"螺丝编号{i + 1},锁付结果{i + 1},锁付圈数{i + 1},锁付扭矩{i + 1},锁付时长{i + 1},电批程序编号{i + 1},结果详情{i + 1},压力值{i + 1}," +
  4226. $"扭矩上限{i + 1},扭矩下限{i + 1},圈数上限{i + 1},圈数下限{i + 1},电批锁付速度{i + 1},锁附X{i + 1},锁附Y{i + 1},开始位置Z{i + 1},结束位置Z{i + 1},锁附下降速度{i + 1},螺丝供料器编号{i + 1},");
  4227. }
  4228. sb.Append("吸嘴编号,披头编号,操作员姓名");
  4229. }
  4230. else
  4231. {
  4232. sb.Append("时间,产品主SN,CT,工位编号,工位名称,配方名称,配方编号,螺丝编号,锁付结果,锁付圈数,锁付扭矩,锁付时长,电批程序编号,结果详情,压力值," +
  4233. "扭矩上限,扭矩下限,圈数上限,圈数下限,电批锁付速度,锁附X,锁附Y,开始位置Z,结束位置Z,锁附下降速度,螺丝供料器编号,吸嘴编号,披头编号,操作员姓名");
  4234. }
  4235. sb.AppendLine();
  4236. }
  4237. if (isSingleLine)
  4238. {
  4239. //将所有螺丝的锁付结果一行显示,列名格式:时间,产品主SN,CT,工位编号,工位名称,配方名称,配方编号,螺丝编号1,锁付结果1,锁付圈数1,锁付扭矩1,锁付时长1,电批程序编号1,结果详情1,压力值1,扭矩上限1,扭矩下限1,圈数上限1,圈数下限1,电批锁付速度1,锁附X1,锁附Y1,开始位置Z1,结束位置Z1,锁附下降速度1,螺丝供料器编号,...(依次类推直到螺丝编号n)
  4240. sb.Append($"{timestamp:yyyy-MM-dd HH:mm:ss},{productMainSN},{Math.Round(ct, 3)},{stationIndex},{stationConfig.DeviceName},{lockResults[0].ProductName},{lockResults[0].ProductNumber},");
  4241. for (int i = 0; i < lockResults.Count; i++)
  4242. {
  4243. var item = lockResults[i];
  4244. sb.Append($"螺丝{i + 1},{item.LockPassed},{item.LockTurns},{item.LockTorque},{item.LockDuration},{item.ScrewdriverProgramNumber}," +
  4245. $"{item.ResultDetails},{item.Pressure},{item.TargetTorqueUpperLimit},{item.TargetTorqueLowerLimit},{item.TargetTurnsUpperLimit},{item.TargetTurnsLowerLimit},{item.TargetLockSpeed}," +
  4246. $"{item.PositionX},{item.PositionY},{item.StartPositionZ},{item.EndPositionZ},{item.LockDescentSpeed},{item.FeederNumber},");
  4247. }
  4248. sb.Append($"{lockResults[lockResults.Count - 1].NozzleNumber},{lockResults[lockResults.Count - 1].HeadNumber},{lockResults[lockResults.Count - 1].OperatorName}");
  4249. sb.AppendLine();
  4250. }
  4251. else
  4252. {
  4253. foreach (var item in lockResults)
  4254. {
  4255. sb.Append($"{timestamp:yyyy-MM-dd HH:mm:ss},{productMainSN},{Math.Round(ct, 3)},{stationIndex},{stationConfig.DeviceName},{item.ProductName},{item.ProductNumber},{item.Number},{item.LockPassed},{item.LockTurns},{item.LockTorque},{item.LockDuration},{item.ScrewdriverProgramNumber}," +
  4256. $"{item.ResultDetails},{item.Pressure},{item.TargetTorqueUpperLimit},{item.TargetTorqueLowerLimit},{item.TargetTurnsUpperLimit},{item.TargetTurnsLowerLimit},{item.TargetLockSpeed}," +
  4257. $"{item.PositionX},{item.PositionY},{item.StartPositionZ},{item.EndPositionZ},{item.LockDescentSpeed},{item.FeederNumber},{item.NozzleNumber},{item.HeadNumber},");
  4258. sb.AppendLine();
  4259. }
  4260. }
  4261. // 使用 StreamWriter 以追加模式打开文件
  4262. StreamWriter sw = new StreamWriter(fullPath, true, Encoding.UTF8);
  4263. sw.Write(sb.ToString());
  4264. sw.Close();
  4265. sw.Dispose();
  4266. return true;
  4267. }
  4268. catch (Exception ex)
  4269. {
  4270. if (!isFirstEnter)
  4271. return false;
  4272. LogHelper.WriteLogError("记录锁付结果时出错!", ex);
  4273. //我希望在这里生成一个临时文件来保存这次的记录,因为如果本地文件被打开时可能会导致无法写入,如果不保存的话可能会丢失这次的记录,所以我想在这里生成一个临时文件来保存这次的记录
  4274. //生成临时文件的路径,格式为savePath\yyyy-MM\工位编号_yyyy-MM-dd_temp\工位编号_yyyy-MM-dd_HH-mm-ss-fff_temp.csv
  4275. string datePath = timestamp.ToString("yyyy-MM") + "\\";
  4276. string tempDirPath = Path.Combine(savePath, datePath, $"{stationIndex}_{timestamp.ToString("yyyy-MM-dd")}_temp");
  4277. string tempFileName = $"{stationIndex}_{timestamp.ToString("yyyy-MM-dd_HH-mm-ss-fff")}_temp.csv";
  4278. RecordLockResult(stationIndex, lockResults, productMainSN, ct, timestamp, tempFileName, isSingleLine, false);
  4279. return false;
  4280. }
  4281. }
  4282. /// <summary>
  4283. /// 记录其他工位的结果,其他工位要存储的数据项按照键值对集合的形式传入,键为数据项名称,值为数据项的值
  4284. /// </summary>
  4285. /// <param name="stationIndex"></param>
  4286. /// <param name="dataItems"></param>
  4287. /// <param name="productMainSN"></param>
  4288. /// <param name="ct"></param>
  4289. /// <param name="timestamp"></param>
  4290. /// <param name="savePath"></param>
  4291. /// <param name="isFirstEnter"></param>
  4292. /// <returns></returns>
  4293. private bool RecordOtherStationResult(int stationIndex, Dictionary<string, string> dataItems, string productMainSN, double ct, DateTime timestamp, string savePath, bool isFirstEnter = true)
  4294. {
  4295. try
  4296. {
  4297. //获取当前工位的配置
  4298. var stationConfig = _configService.GetDeviceInfo(stationIndex);
  4299. if (stationConfig == null) return false;
  4300. // 获取当前产品
  4301. var currentProduct = _productService.GetCurrentProduct();
  4302. if (currentProduct == null)
  4303. {
  4304. return false;
  4305. }
  4306. //如果保存路径为空,则不保存
  4307. if (string.IsNullOrEmpty(savePath)) return false;
  4308. string fullPath = savePath;
  4309. //如果保存路径是一个文件路径,则直接保存,否则按照工位和日期进行保存
  4310. if (File.Exists(savePath) || Path.HasExtension(savePath))
  4311. {
  4312. fullPath = savePath;
  4313. //如果路径不存在,则创建
  4314. // 创建目录
  4315. string directory = Path.GetDirectoryName(fullPath);
  4316. if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
  4317. {
  4318. Directory.CreateDirectory(directory);
  4319. }
  4320. }
  4321. else
  4322. {
  4323. //结果保存至CSV文件,文件名格式:savePath\yyyy-MM\工位编号_工位名称_yyyy-MM-dd.csv
  4324. string datePath = timestamp.ToString("yyyy-MM") + "\\";
  4325. string dirPath = Path.Combine(savePath, datePath);
  4326. if (!Directory.Exists(dirPath)) Directory.CreateDirectory(dirPath);
  4327. string fileName = $"{stationIndex}_{stationConfig.DeviceName}_{timestamp.ToString("yyyy-MM-dd")}.csv";
  4328. fullPath = Path.Combine(dirPath, fileName);
  4329. }
  4330. StringBuilder sb = new StringBuilder();
  4331. if (!File.Exists(fullPath))
  4332. {
  4333. sb.Append("时间,产品主SN,CT,工位编号,工位名称,配方名称,配方编号,");
  4334. foreach (var item in dataItems)
  4335. {
  4336. sb.Append($"{item.Key},");
  4337. }
  4338. sb.AppendLine();
  4339. }
  4340. sb.Append($"{timestamp:yyyy-MM-dd HH:mm:ss},{productMainSN},{Math.Round(ct, 3)},{stationIndex},{stationConfig.DeviceName},{currentProduct.Name},{currentProduct.NumberCode},");
  4341. foreach (var item in dataItems)
  4342. {
  4343. sb.Append($"{item.Value},");
  4344. }
  4345. sb.AppendLine();
  4346. // 使用 StreamWriter 以追加模式打开文件
  4347. StreamWriter sw = new StreamWriter(fullPath, true, Encoding.UTF8);
  4348. sw.Write(sb.ToString());
  4349. sw.Close();
  4350. sw.Dispose();
  4351. return true;
  4352. }
  4353. catch (Exception ex)
  4354. {
  4355. if (!isFirstEnter)
  4356. {
  4357. return false;
  4358. }
  4359. LogHelper.WriteLogError("记录其他工位结果时出错!", ex);
  4360. //我希望在这里生成一个临时文件来保存这次的记录,因为如果本地文件被打开时可能会导致无法写入,如果不保存的话可能会丢失这次的记录,所以我想在这里生成一个临时文件来保存这次的记录
  4361. //生成临时文件的路径,格式为savePath\yyyy-MM\工位编号_yyyy-MM-dd_temp\工位编号_yyyy-MM-dd_HH-mm-ss-fff_temp.csv
  4362. string datePath = timestamp.ToString("yyyy-MM") + "\\";
  4363. string tempDirPath = Path.Combine(savePath, datePath, $"{stationIndex}_{timestamp.ToString("yyyy-MM-dd")}_temp");
  4364. string tempFileName = $"{stationIndex}_{timestamp.ToString("yyyy-MM-dd_HH-mm-ss-fff")}_temp.csv";
  4365. RecordOtherStationResult(stationIndex, dataItems, productMainSN, ct, timestamp, tempFileName, false);
  4366. return false;
  4367. }
  4368. }
  4369. /// <summary>
  4370. /// 记录工位的结果,其他工位要存储的数据项按照键值对集合的形式传入,键为数据项名称,值为数据项的值
  4371. /// </summary>
  4372. /// <param name="stationIndex"></param>
  4373. /// <param name="dfcs"></param>
  4374. /// <param name="productMainSN"></param>
  4375. /// <param name="ct"></param>
  4376. /// <param name="timestamp"></param>
  4377. /// <param name="savePath"></param>
  4378. /// <param name="isFirstEnter"></param>
  4379. /// <returns></returns>
  4380. private bool RecordStationResult(int stationIndex, IEnumerable<DataFormCtq> dfcs, string productMainSN, double ct, DateTime timestamp, string savePath, bool isFirstEnter = true)
  4381. {
  4382. try
  4383. {
  4384. //获取当前工位的配置
  4385. var stationConfig = _configService.GetDeviceInfo(stationIndex);
  4386. if (stationConfig == null) return false;
  4387. //如果保存路径为空,则不保存
  4388. if (string.IsNullOrEmpty(savePath)) return false;
  4389. string fullPath = savePath;
  4390. //如果保存路径是一个文件路径,则直接保存,否则按照工位和日期进行保存
  4391. if (!File.Exists(savePath) || !Path.HasExtension(savePath))
  4392. {
  4393. fullPath = savePath;
  4394. //如果路径不存在,则创建
  4395. // 创建目录
  4396. string directory = Path.GetDirectoryName(fullPath);
  4397. if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
  4398. {
  4399. Directory.CreateDirectory(directory);
  4400. }
  4401. }
  4402. //结果保存至CSV文件,文件名格式:savePath\yyyy-MM\工位编号_工位名称_yyyy-MM-dd.csv
  4403. string datePath = timestamp.ToString("yyyy-MM") + "\\";
  4404. string dirPath = Path.Combine(savePath, datePath);
  4405. if (!Directory.Exists(dirPath)) Directory.CreateDirectory(dirPath);
  4406. string fileName = $"{stationIndex}_{stationConfig.DeviceName}_{timestamp.ToString("yyyy-MM-dd")}.csv";
  4407. fullPath = Path.Combine(dirPath, fileName);
  4408. StringBuilder sb = new StringBuilder();
  4409. if (!File.Exists(fullPath))
  4410. {
  4411. sb.Append("PPID,Screw_Number,PROCESS_NAME,PROCESS_START_TIME,CTQ_NAME,CTQ_VALUE,CTQ_LSL,CTQ_USL,PROCESS_END_TIME,SUPPLIER_NAME,COMMODITY_TYPE,REVISION,WO#,MFG_LOT#," +
  4412. "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");
  4413. sb.AppendLine();
  4414. }
  4415. if (dfcs == null || dfcs.Count() == 0)
  4416. {
  4417. return false;
  4418. }
  4419. foreach (var dfc in dfcs)
  4420. {
  4421. sb.Append($"{dfc.PPID},{dfc.ScrewNum},{dfc.PROCESS_NAME},{dfc.PROCESS_START_TIME},{dfc.CTQ_NAME},{dfc.CTQ_VALUE:F3},{dfc.CTQ_LSL:F3},{dfc.CTQ_USL:F3},{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}");
  4422. sb.AppendLine();
  4423. }
  4424. // 使用 StreamWriter 以追加模式打开文件
  4425. StreamWriter sw = new StreamWriter(fullPath, true, Encoding.UTF8);
  4426. sw.Write(sb.ToString());
  4427. sw.Close();
  4428. sw.Dispose();
  4429. return true;
  4430. }
  4431. catch (Exception ex)
  4432. {
  4433. if (!isFirstEnter)
  4434. {
  4435. return false;
  4436. }
  4437. LogHelper.WriteLogError("记录其他工位结果时出错!", ex);
  4438. //我希望在这里生成一个临时文件来保存这次的记录,因为如果本地文件被打开时可能会导致无法写入,如果不保存的话可能会丢失这次的记录,所以我想在这里生成一个临时文件来保存这次的记录
  4439. //生成临时文件的路径,格式为savePath\yyyy-MM\工位编号_yyyy-MM-dd_temp\工位编号_yyyy-MM-dd_HH-mm-ss-fff_temp.csv
  4440. string datePath = timestamp.ToString("yyyy-MM") + "\\";
  4441. string tempDirPath = Path.Combine(savePath, datePath, $"{stationIndex}_{timestamp.ToString("yyyy-MM-dd")}_temp");
  4442. string tempFileName = $"{stationIndex}_{timestamp.ToString("yyyy-MM-dd_HH-mm-ss-fff")}_temp.csv";
  4443. RecordStationResult(stationIndex, dfcs, productMainSN, ct, timestamp, tempFileName, false);
  4444. return false;
  4445. }
  4446. }
  4447. #endregion
  4448. #region 写log
  4449. TxtFile mesLog = new TxtFile($"D:\\Data\\Mes记录", "");
  4450. public void MesMessage(string msg)
  4451. {
  4452. mesLog.WriteLine("== " + msg + "\r\n");
  4453. }
  4454. TxtFile infoLog = new TxtFile($"D:\\Data\\运行日志", "");
  4455. public void InfoMessage(string msg)
  4456. {
  4457. infoLog.WriteLine("== " + msg + "\r\n");
  4458. }
  4459. #endregion
  4460. #region 压缩图片文件夹
  4461. /// <summary>
  4462. /// 压缩文件
  4463. /// </summary>
  4464. /// <param name="sourcePath">原路径</param>
  4465. /// <param name="zipname">压缩后文件的名称</param>
  4466. /// <param name="station">mes中的station</param>
  4467. /// <param name="fixid">mes中的fixtureID</param>
  4468. public async void CompressionImage(string sourcePath, string zipname, string station, string fixid)
  4469. {
  4470. string zipPath = "";
  4471. try
  4472. {
  4473. SendTaskMessage($"开始压缩文件", MessageLevel.Info);
  4474. await Task.Run(() =>
  4475. {
  4476. zipPath = $"Z:\\{station}\\{fixid}\\{DateTime.Now.ToString("yyyyMMdd")}\\{zipname}.zip";//生成的zip文件路径
  4477. string path = $"Z:\\{station}\\{fixid}\\{DateTime.Now.ToString("yyyyMMdd")}";
  4478. if (!Directory.Exists(path))
  4479. {
  4480. Directory.CreateDirectory(path);
  4481. }
  4482. ZipFile.CreateFromDirectory(sourcePath, zipPath);
  4483. });
  4484. }
  4485. catch (Exception ex)
  4486. {
  4487. LogHelper.WriteLogError($"压缩文件时出错", ex);
  4488. }
  4489. }
  4490. public async void CompressionSingleCsv(string csvFilePath, string zipname, string station, string fixid)
  4491. {
  4492. string zipPath = "";
  4493. try
  4494. {
  4495. SendTaskMessage("开始压缩CSV文件", MessageLevel.Info);
  4496. await Task.Run(() =>
  4497. {
  4498. if (!File.Exists(csvFilePath))
  4499. {
  4500. LogHelper.WriteLogMes($"CSV文件不存在,不压缩:{csvFilePath}");
  4501. return;
  4502. }
  4503. zipPath = $"Z:\\{station}\\{fixid}\\{DateTime.Now.ToString("yyyyMMdd")}\\{zipname}.zip";
  4504. string path = $"Z:\\{station}\\{fixid}\\{DateTime.Now.ToString("yyyyMMdd")}";
  4505. if (!Directory.Exists(path))
  4506. {
  4507. Directory.CreateDirectory(path);
  4508. }
  4509. using (FileStream fs = new FileStream(zipPath, FileMode.Create))
  4510. {
  4511. using (ZipArchive archive = new ZipArchive(fs, ZipArchiveMode.Create))
  4512. {
  4513. archive.CreateEntryFromFile(csvFilePath, Path.GetFileName(csvFilePath));
  4514. }
  4515. }
  4516. });
  4517. SendTaskMessage("CSV文件压缩完成", MessageLevel.Debug);
  4518. }
  4519. catch (Exception ex)
  4520. {
  4521. LogHelper.WriteLogError("压缩CSV文件失败", ex);
  4522. }
  4523. }
  4524. #endregion
  4525. /// <summary>
  4526. /// 后台报警刷新显示(适配PLC结构化变量名Bool点位 OPC版)
  4527. /// 完全沿用你原有循环结构、防抖逻辑、UI报警推送规则
  4528. /// </summary>
  4529. public void SendAlarmToUi(OpcUaClientPLC plc)
  4530. {
  4531. PlcExcelLoad loadData = new PlcExcelLoad();
  4532. loadData.LoadData(6);
  4533. // 增加报警状态缓存,防止循环重复刷屏报警
  4534. Dictionary<string, bool> alarmLastState = new Dictionary<string, bool>();
  4535. foreach (var alarm in loadData.AlaemList)
  4536. {
  4537. alarmLastState.Add(alarm.OpcAddress, true);
  4538. }
  4539. Task.Run(() =>
  4540. {
  4541. while (true)
  4542. {
  4543. try
  4544. {
  4545. Thread.Sleep(50);
  4546. foreach (var alarm in loadData.AlaemList)
  4547. {
  4548. // 调用你封装好的OPC读取Bool方法
  4549. bool plcState = plc.ReadBoolNode(alarm.OpcAddress);
  4550. // 上升沿防抖:只有状态从正常→异常(false)变化时,才推送一次报警
  4551. if (!plcState && alarmLastState[alarm.OpcAddress])
  4552. {
  4553. SendTaskMessage($"PLC报警信息:{alarm.Name}", MessageLevel.Error);
  4554. LogHelper.WriteLogPlcWarn($"PLC报警信息:{alarm.Name}");
  4555. alarmLastState[alarm.OpcAddress] = false;
  4556. }
  4557. // 报警复位:状态恢复正常,重置缓存
  4558. else if (plcState && !alarmLastState[alarm.OpcAddress])
  4559. {
  4560. alarmLastState[alarm.OpcAddress] = true;
  4561. }
  4562. }
  4563. }
  4564. catch
  4565. {
  4566. Thread.Sleep(200);
  4567. }
  4568. }
  4569. });
  4570. }
  4571. #endregion
  4572. }
  4573. }