Management.cs 209 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204
  1. using Cognex.VisionPro.ToolBlock;
  2. using Cognex.VisionPro;
  3. using MathNet.Numerics.LinearAlgebra;
  4. using MathNet.Numerics.RootFinding;
  5. using NPOI.SS.Formula.Functions;
  6. using NPOI.Util;
  7. using OpenCvSharp;
  8. using OpenCvSharp.Flann;
  9. using Prism.Events;
  10. using Prism.Ioc;
  11. using Prism.Mvvm;
  12. using Prism.Regions;
  13. using Prism.Services.Dialogs;
  14. using System;
  15. using System.Collections.Generic;
  16. using System.Collections.ObjectModel;
  17. using System.IO;
  18. using System.Linq;
  19. using System.Net.Sockets;
  20. using System.Security.Cryptography;
  21. using System.Text;
  22. using System.Threading;
  23. using System.Threading.Tasks;
  24. using System.Windows;
  25. using System.Windows.Documents;
  26. using System.Windows.Interop;
  27. using System.Windows.Media;
  28. using System.Windows.Media.Media3D;
  29. using Team.FFFeederService;
  30. using Team.FFFeederService.Interfaces;
  31. using TeamAAS_VP.Data;
  32. using TeamAAS_VP.Enums;
  33. using TeamAAS_VP.Events;
  34. using TeamAAS_VP.Models;
  35. using TeamAAS_VP.ViewModels.Home;
  36. using TeamAAS_VP;
  37. using TouchSocket.Core;
  38. using TouchSocket.Sockets;
  39. using static MaterialDesignThemes.Wpf.Theme.ToolBar;
  40. using System.ComponentModel;
  41. using System.Collections;
  42. using System.Reflection.Metadata;
  43. using TeamAAS_VP.Resources.Languages;
  44. using TeamAAS_VP.Core.PLCs;
  45. using TeamAAS_VP.Interfaces;
  46. using Opc.Ua;
  47. using TeamAAS_VP.Core.Robots;
  48. using TeamAAS_VP.Core.RFID;
  49. using TeamAAS_VP.Controls;
  50. using TeamAAS_VP.Core.Sfis;
  51. namespace TeamAAS_VP.Core
  52. {
  53. public class Management : BindableBase
  54. {
  55. /// <summary>EthernetIP 底栏状态项固定 Id(与设备 Id 无关)</summary>
  56. public static readonly Guid EthernetIpStatusId = new Guid("E4E10000-0001-4000-8000-000000000001");
  57. #region 字段
  58. IRegionManager _regionManager;
  59. IEventAggregator _eventAggregator;
  60. IContainerProvider _container;
  61. System.Threading.Timer cTtimer;
  62. System.Threading.Timer yieldtimer;
  63. private bool IsStart = false;
  64. private DateTime StartTime = DateTime.Now;
  65. #endregion
  66. #region 属性
  67. /// <summary>
  68. /// 当前用户
  69. /// </summary>
  70. public User CurrentUser { get; set; } = new User() { UserName = "操作员", UserPassword = "", IsRemember = false, userPart = Enums.UserPart.Operator, CreateTime = DateTime.Now };
  71. /// <summary>
  72. /// 设备参数
  73. /// </summary>
  74. public DeviceConfiguration DeviceConfig { get; set; } = new DeviceConfiguration();
  75. private Language _CurrentLanguage = Language.ChineseSimplified;
  76. /// <summary>
  77. /// 当前软件使用的语言
  78. /// </summary>
  79. public Language CurrentLanguage
  80. {
  81. get { return _CurrentLanguage; }
  82. set { SetProperty(ref _CurrentLanguage, value); }
  83. }
  84. private ObservableCollection<ShowRender> _Renders;
  85. /// <summary>
  86. /// 显示界面
  87. /// </summary>
  88. public ObservableCollection<ShowRender> Renders
  89. {
  90. get { return _Renders; }
  91. set { SetProperty(ref _Renders, value); }
  92. }
  93. private Dictionary<Guid, Thread> _plcTrigger = new Dictionary<Guid, Thread>();
  94. /// <summary>
  95. /// 当前plc触发监控
  96. /// </summary>
  97. public Dictionary<Guid, Thread> PlcTrigger { get => _plcTrigger; set => _plcTrigger = value; }
  98. private ObservableCollection<ProductModel> _Products;
  99. /// <summary>
  100. /// 产品列表
  101. /// </summary>
  102. public ObservableCollection<ProductModel> Products
  103. {
  104. get { return _Products; }
  105. set { SetProperty(ref _Products, value); }
  106. }
  107. private ProductModel _CurrentProduct;
  108. /// <summary>
  109. /// 当前产品
  110. /// </summary>
  111. public ProductModel CurrentProduct
  112. {
  113. get { return _CurrentProduct; }
  114. set { SetProperty(ref _CurrentProduct, value); }
  115. }
  116. public FeederService FeederService { get; set; }
  117. public RobotService RobotService { get; set; }
  118. public CameraService CameraService { get; set; }
  119. public PlcService PlcService { get; set; }
  120. public BgCommunicate BgCommunicate { get; private set; }
  121. public BgModbusTcpCommunicate BgModbusTcpCommunicate { get; private set; }
  122. private CancellationTokenSource _handshakeCts;
  123. private Task _handshakeTask;
  124. private CancellationTokenSource _heartbeatCts;
  125. private Task _heartbeatTask;
  126. private CancellationTokenSource _sfisSignalCts;
  127. private Task _sfisSignalTask;
  128. private SfisWorkflowService _sfisWorkflow;
  129. private RFIDTagData _lastRfidTag;
  130. private readonly object _sync = new object();
  131. public SIG350RFIDClient BgEipCommunicate { get; private set; }
  132. private CancellationTokenSource _mcReconnectCts;
  133. private Task _mcReconnectTask;
  134. private readonly object _mcReconnectSync = new object();
  135. private DateTime _mcLastReconnectAttempt = DateTime.MinValue;
  136. private const int McReconnectIntervalMs = 3000;
  137. private Action<string> _mcPlcLogHandler;
  138. private volatile bool _mcMonitorLoopsStarted;
  139. public ModbusTcpMasterCommunicate ModbusTcpMasterCommunicate { get; private set; }
  140. /// <summary>
  141. /// Modbus TCP 主站配置(连接电批等设备)
  142. /// </summary>
  143. public ModbusTcpMasterConfig ModbusTcpMasterConfig { get; set; } = new ModbusTcpMasterConfig();
  144. private ObservableCollection<StatusInfo> _Status = new ObservableCollection<StatusInfo>();
  145. /// <summary>
  146. /// 状态栏集合
  147. /// </summary>
  148. public ObservableCollection<StatusInfo> Status
  149. {
  150. get { return _Status; }
  151. set { SetProperty(ref _Status, value); }
  152. }
  153. private TimeSpan _CT;
  154. public TimeSpan CT
  155. {
  156. get { return _CT; }
  157. set { SetProperty(ref _CT, value); }
  158. }
  159. private Int64 _TotalQuantity;
  160. /// <summary>
  161. /// 总产量
  162. /// </summary>
  163. public Int64 TotalQuantity
  164. {
  165. get { return _TotalQuantity; }
  166. set { SetProperty(ref _TotalQuantity, value); }
  167. }
  168. private Int64 _TotalQuantityToday;
  169. /// <summary>
  170. /// 当天的总产量
  171. /// </summary>
  172. public Int64 TotalQuantityToday
  173. {
  174. get { return _TotalQuantityToday; }
  175. set { SetProperty(ref _TotalQuantityToday, value); }
  176. }
  177. private Int64 _CurrentUPH;
  178. /// <summary>
  179. /// 当前的UPH
  180. /// </summary>
  181. public Int64 CurrentUPH
  182. {
  183. get { return _CurrentUPH; }
  184. set { SetProperty(ref _CurrentUPH, value); }
  185. }
  186. public Dictionary<string, object> BackupObject { get; set; } = new Dictionary<string, object>();
  187. #endregion
  188. #region 命令
  189. #endregion
  190. #region 事件
  191. #endregion
  192. public Management(IRegionManager regionManager, IEventAggregator ea, IContainerProvider container)
  193. {
  194. _regionManager = regionManager;
  195. _eventAggregator = ea;
  196. _container = container;
  197. cTtimer = new Timer(DoCycleTime, null, 5000, 100);
  198. yieldtimer = new Timer(DoYieldTime, null, 10000, 1000);
  199. Renders = new ObservableCollection<ShowRender>();
  200. }
  201. #region 配置参数读写操作
  202. /// <summary>
  203. /// 读取配置参数
  204. /// </summary>
  205. public void ReadConfig()
  206. {
  207. ReadCamerasDeviceConfig();
  208. ReadRobotsDeviceConfig();
  209. ReadFeedersDeviceConfig();
  210. ReadPlcDeviceConfig();
  211. ReadBgTcpIpDeviceConfig();
  212. ReadBgModbusTcpDeviceConfig();
  213. ReadModbusTcpMasterDeviceConfig();
  214. ReadBgEthernetIPDeviceConfig();
  215. ReadBgSfisDeviceConfig();
  216. }
  217. /// <summary>
  218. /// 读取相机设备配置参数
  219. /// </summary>
  220. public void ReadCamerasDeviceConfig()
  221. {
  222. try
  223. {
  224. if (File.Exists(FilePath.CamerasConfigurationPath))
  225. {
  226. DeviceConfig.Cameras = FileHelper.ReadJsonFile<ObservableCollection<CameraInfo>>(FilePath.CamerasConfigurationPath);
  227. }
  228. else
  229. {
  230. DeviceConfig.Cameras = new ObservableCollection<CameraInfo>();
  231. }
  232. }
  233. catch (Exception ex)
  234. {
  235. DeviceConfig.Robots = new ObservableCollection<RobotInfo>();
  236. LogHelper.WriteLogError("加载相机配置文件时出错!", ex);
  237. }
  238. }
  239. /// <summary>
  240. /// 保存相机设备配置参数
  241. /// </summary>
  242. public void SaveCamerasDeviceConfig()
  243. {
  244. try
  245. {
  246. FileHelper.WriteJsonFile(DeviceConfig.Cameras, FilePath.CamerasConfigurationPath);
  247. }
  248. catch (Exception ex)
  249. {
  250. LogHelper.WriteLogError("保存相机配置参数至文件时出错!", ex);
  251. }
  252. }
  253. /// <summary>
  254. /// 读取机器人设备配置参数
  255. /// </summary>
  256. public void ReadRobotsDeviceConfig()
  257. {
  258. try
  259. {
  260. if (File.Exists(FilePath.RobotsConfigurationPath))
  261. {
  262. DeviceConfig.Robots = FileHelper.ReadJsonFile<ObservableCollection<RobotInfo>>(FilePath.RobotsConfigurationPath);
  263. }
  264. else
  265. {
  266. DeviceConfig.Robots = new ObservableCollection<RobotInfo>();
  267. }
  268. }
  269. catch (Exception ex)
  270. {
  271. DeviceConfig.Robots = new ObservableCollection<RobotInfo>();
  272. LogHelper.WriteLogError("加载机器人配置文件时出错!", ex);
  273. }
  274. }
  275. /// <summary>
  276. /// 保存机器人设备配置参数
  277. /// </summary>
  278. public void SaveRobotsDeviceConfig()
  279. {
  280. try
  281. {
  282. FileHelper.WriteJsonFile(DeviceConfig.Robots, FilePath.RobotsConfigurationPath);
  283. }
  284. catch (Exception ex)
  285. {
  286. LogHelper.WriteLogError("保存机器人配置参数至文件时出错!", ex);
  287. }
  288. }
  289. /// <summary>
  290. /// 读取Feeder设备配置参数
  291. /// </summary>
  292. public void ReadFeedersDeviceConfig()
  293. {
  294. try
  295. {
  296. if (File.Exists(FilePath.FeedersConfigurationPath))
  297. {
  298. DeviceConfig.Feeders = FileHelper.ReadJsonFile<ObservableCollection<FeederInfo>>(FilePath.FeedersConfigurationPath);
  299. }
  300. else
  301. {
  302. DeviceConfig.Feeders = new ObservableCollection<FeederInfo>();
  303. }
  304. }
  305. catch (Exception ex)
  306. {
  307. DeviceConfig.Feeders = new ObservableCollection<FeederInfo>();
  308. LogHelper.WriteLogError("加载Feeder配置文件时出错!", ex);
  309. }
  310. }
  311. /// <summary>
  312. /// 保存Feeder设备配置参数
  313. /// </summary>
  314. public void SaveFeederDeviceConfig()
  315. {
  316. try
  317. {
  318. FileHelper.WriteJsonFile(DeviceConfig.Feeders, FilePath.FeedersConfigurationPath);
  319. }
  320. catch (Exception ex)
  321. {
  322. LogHelper.WriteLogError("保存Feeder配置参数至文件时出错!", ex);
  323. }
  324. }
  325. /// <summary>
  326. /// 读取PLC设备配置参数
  327. /// </summary>
  328. public void ReadPlcDeviceConfig()
  329. {
  330. try
  331. {
  332. if (File.Exists(FilePath.PlcConfigurationPath))
  333. {
  334. DeviceConfig.Plcs = FileHelper.ReadJsonFile<ObservableCollection<PlcInfo>>(FilePath.PlcConfigurationPath);
  335. }
  336. else
  337. {
  338. DeviceConfig.Plcs = new ObservableCollection<PlcInfo>();
  339. }
  340. }
  341. catch (Exception ex)
  342. {
  343. DeviceConfig.Plcs = new ObservableCollection<PlcInfo>();
  344. LogHelper.WriteLogError("加载PLC配置文件时出错!", ex);
  345. }
  346. TryMigrateLegacyMcConfig();
  347. }
  348. private void TryMigrateLegacyMcConfig()
  349. {
  350. if (DeviceConfig.Plcs.Any(p => p.CommunicationType == CommunicationType.Mitsubishi_MC))
  351. return;
  352. try
  353. {
  354. if (!File.Exists(FilePath.BgMCToPcCommunicatePath))
  355. return;
  356. var legacy = FileHelper.ReadJsonFile<MCToPCInfo>(FilePath.BgMCToPcCommunicatePath);
  357. if (legacy == null)
  358. return;
  359. var mcPlc = new PlcInfo
  360. {
  361. Id = Guid.NewGuid(),
  362. Name = "Mitsubishi MC",
  363. CommunicationType = CommunicationType.Mitsubishi_MC
  364. };
  365. mcPlc.ApplyMcInfo(legacy);
  366. DeviceConfig.Plcs.Add(mcPlc);
  367. SavePlcDeviceConfig();
  368. }
  369. catch (Exception ex)
  370. {
  371. LogHelper.WriteLogError("迁移旧版 MC 配置至 PLC 列表时出错!", ex);
  372. }
  373. }
  374. /// <summary>
  375. /// 保存PLC设备配置参数
  376. /// </summary>
  377. public void SavePlcDeviceConfig()
  378. {
  379. try
  380. {
  381. FileHelper.WriteJsonFile(DeviceConfig.Plcs, FilePath.PlcConfigurationPath);
  382. }
  383. catch (Exception ex)
  384. {
  385. LogHelper.WriteLogError("保存PLC配置参数至文件时出错!", ex);
  386. }
  387. }
  388. /// <summary>
  389. /// 读取语言配置参数
  390. /// </summary>
  391. public void ReadLanguageDeviceConfig()
  392. {
  393. try
  394. {
  395. if (File.Exists(FilePath.LanguageConfigurationPath))
  396. {
  397. CurrentLanguage = FileHelper.ReadJsonFile<Language>(FilePath.LanguageConfigurationPath);
  398. }
  399. else
  400. {
  401. CurrentLanguage = Language.ChineseSimplified;
  402. }
  403. }
  404. catch (Exception ex)
  405. {
  406. CurrentLanguage = Language.ChineseSimplified;
  407. LogHelper.WriteLogError("加载Language配置文件时出错!", ex);
  408. }
  409. }
  410. /// <summary>
  411. /// 保存语言配置参数
  412. /// </summary>
  413. public void SaveLanguageDeviceConfig()
  414. {
  415. try
  416. {
  417. FileHelper.WriteJsonFile(CurrentLanguage, FilePath.LanguageConfigurationPath);
  418. }
  419. catch (Exception ex)
  420. {
  421. LogHelper.WriteLogError("保存Language配置参数至文件时出错!", ex);
  422. }
  423. }
  424. /// <summary>
  425. /// 读取后台TCPIP通讯配置参数
  426. /// </summary>
  427. public void ReadBgTcpIpDeviceConfig()
  428. {
  429. try
  430. {
  431. if (File.Exists(FilePath.BgTcpIpConfigurationPath))
  432. {
  433. DeviceConfig.BgCommunicate = FileHelper.ReadJsonFile<BgTcpIP>(FilePath.BgTcpIpConfigurationPath);
  434. }
  435. else
  436. {
  437. DeviceConfig.BgCommunicate = new BgTcpIP();
  438. }
  439. }
  440. catch (Exception ex)
  441. {
  442. DeviceConfig.BgCommunicate = new BgTcpIP();
  443. LogHelper.WriteLogError("加载后台TCPIP通讯配置文件时出错!", ex);
  444. }
  445. }
  446. /// <summary>
  447. /// 保存后台TCPIP通讯配置参数
  448. /// </summary>
  449. public void SaveBgTcpIpDeviceConfig()
  450. {
  451. try
  452. {
  453. FileHelper.WriteJsonFile(DeviceConfig.BgCommunicate, FilePath.BgTcpIpConfigurationPath);
  454. }
  455. catch (Exception ex)
  456. {
  457. LogHelper.WriteLogError("保存后台TCPIP通讯配置参数至文件时出错!", ex);
  458. }
  459. }
  460. /// <summary>
  461. /// 读取后台ModbusTcp通讯配置参数
  462. /// </summary>
  463. public void ReadBgModbusTcpDeviceConfig()
  464. {
  465. try
  466. {
  467. if (File.Exists(FilePath.BgModbusTcpConfigurationPath))
  468. {
  469. DeviceConfig.BgModbusCommunicate = FileHelper.ReadJsonFile<BgModbusTcp>(FilePath.BgModbusTcpConfigurationPath);
  470. }
  471. else
  472. {
  473. DeviceConfig.BgModbusCommunicate = new BgModbusTcp();
  474. }
  475. }
  476. catch (Exception ex)
  477. {
  478. DeviceConfig.BgModbusCommunicate = new BgModbusTcp();
  479. LogHelper.WriteLogError("加载后台Modbus TCP通讯配置文件时出错!", ex);
  480. }
  481. }
  482. /// <summary>
  483. /// 保存后台ModbusTcp通讯配置参数
  484. /// </summary>
  485. public void SaveBgModbusTcpDeviceConfig()
  486. {
  487. try
  488. {
  489. FileHelper.WriteJsonFile(DeviceConfig.BgModbusCommunicate, FilePath.BgModbusTcpConfigurationPath);
  490. }
  491. catch (Exception ex)
  492. {
  493. LogHelper.WriteLogError("保存后台ModbusTcp通讯配置参数至文件时出错!", ex);
  494. }
  495. }
  496. /// <summary>
  497. /// 读取后台EthernetIP通讯配置参数
  498. /// </summary>
  499. public void ReadBgEthernetIPDeviceConfig()
  500. {
  501. try
  502. {
  503. if (File.Exists(FilePath.BgEthernetIPCommunicatePath))
  504. {
  505. DeviceConfig.BgEthernetIPCommunicate = FileHelper.ReadJsonFile<BgEthernetIP>(FilePath.BgEthernetIPCommunicatePath);
  506. }
  507. else
  508. {
  509. DeviceConfig.BgEthernetIPCommunicate = new BgEthernetIP();
  510. FileHelper.WriteJsonFile(DeviceConfig.BgEthernetIPCommunicate, FilePath.BgEthernetIPCommunicatePath);
  511. }
  512. }
  513. catch (Exception ex)
  514. {
  515. DeviceConfig.BgEthernetIPCommunicate = new BgEthernetIP();
  516. LogHelper.WriteLogError("加载后台Eip通讯配置文件时出错!", ex);
  517. }
  518. }
  519. public void ReadBgSfisDeviceConfig()
  520. {
  521. try
  522. {
  523. if (File.Exists(FilePath.BgSfisCommunicatePath))
  524. {
  525. DeviceConfig.BgSfisCommunicate = FileHelper.ReadJsonFile<BgSfisConfig>(FilePath.BgSfisCommunicatePath);
  526. }
  527. else
  528. {
  529. DeviceConfig.BgSfisCommunicate = new BgSfisConfig();
  530. FileHelper.WriteJsonFile(DeviceConfig.BgSfisCommunicate, FilePath.BgSfisCommunicatePath);
  531. }
  532. }
  533. catch (Exception ex)
  534. {
  535. DeviceConfig.BgSfisCommunicate = new BgSfisConfig();
  536. LogHelper.WriteLogError("加载 SFIS 配置文件时出错!", ex);
  537. }
  538. }
  539. /// <summary>
  540. /// 保存EIP配置
  541. /// </summary>
  542. public void SaveBgEthernetIPDeviceConfig()
  543. {
  544. try
  545. {
  546. FileHelper.WriteJsonFile(DeviceConfig.BgEthernetIPCommunicate, FilePath.BgEthernetIPCommunicatePath);
  547. }
  548. catch (Exception ex)
  549. {
  550. LogHelper.WriteLogError("保存后台Eip通讯配置参数至文件时出错!", ex);
  551. }
  552. }
  553. public void SaveBgSfisDeviceConfig()
  554. {
  555. try
  556. {
  557. FileHelper.WriteJsonFile(DeviceConfig.BgSfisCommunicate, FilePath.BgSfisCommunicatePath);
  558. }
  559. catch (Exception ex)
  560. {
  561. LogHelper.WriteLogError("保存 SFIS 配置参数至文件时出错!", ex);
  562. }
  563. }
  564. #endregion
  565. #region 初始化硬件模块
  566. /// <summary>
  567. /// 初始化视觉
  568. /// </summary>
  569. public Tuple<bool, string> InitVision()
  570. {
  571. try
  572. {
  573. return new Tuple<bool, string>(true, "");
  574. }
  575. catch (Exception ex)
  576. {
  577. LogHelper.WriteLogError("初始化VM视觉方式时出错!", ex);
  578. return new Tuple<bool, string>(false, ex.Message);
  579. }
  580. }
  581. /// <summary>
  582. /// 初始化Feeder
  583. /// </summary>
  584. /// <returns></returns>
  585. public async Task<Tuple<bool, string>> InitFeeders()
  586. {
  587. FeederService = new FeederService();
  588. StringBuilder sb = new StringBuilder();
  589. foreach (var item in DeviceConfig.Feeders)
  590. {
  591. FeederService.CreateFeeder(item.Id, item.IP, item.Port, item.FeederBrand);
  592. Status.Add(new StatusInfo(item.Id, item.FeederName + $"{Lang.未连接}", new SolidColorBrush(Colors.Red)));
  593. try
  594. {
  595. FeederService.GetFeeder(item.Id).FeederConnectedEvent += Management_FeederConnectedEvent;
  596. FeederService.GetFeeder(item.Id).FeederDisconnectedEvent += Management_FeederDisconnectedEvent;
  597. await FeederService.GetFeeder(item.Id).ConnectAsync(); //连接Feeder
  598. //SendTaskMessage($"{item.FeederName}已连接!", MessageLevel.Debug);
  599. }
  600. catch (Exception ex)
  601. {
  602. sb.AppendLine($"{item.FeederName}:{Lang.断开连接}![{ex.Message}]");
  603. SendTaskMessage($"{item.FeederName}{Lang.断开连接}!", MessageLevel.Alarm);
  604. }
  605. }
  606. if (sb.Length == 0)
  607. {
  608. return new Tuple<bool, string>(true, "");
  609. }
  610. else
  611. {
  612. return new Tuple<bool, string>(false, sb.ToString());
  613. }
  614. }
  615. /// <summary>
  616. /// 初始化机器人
  617. /// </summary>
  618. /// <returns></returns>
  619. public async Task<Tuple<bool, string>> InitRobots()
  620. {
  621. RobotService = new RobotService();
  622. StringBuilder sb = new StringBuilder();
  623. foreach (var item in DeviceConfig.Robots)
  624. {
  625. RobotService.CreateRobot(item.Id, item);
  626. if (item.RobotBrand == RobotBrand.XYZ_Platform || item.RobotBrand == RobotBrand.XYZU_Platform)
  627. {
  628. RobotService.GetRobot(item.Id).TrggerChangeEvent += Management_TrggerChangeEvent;
  629. continue;
  630. }
  631. try
  632. {
  633. Status.Add(new StatusInfo(item.Id, item.RobotName + $"{Lang.未连接}", new SolidColorBrush(Colors.Red)));
  634. RobotService.GetRobot(item.Id).ReceivedEvent += Management_ReceivedEvent;
  635. RobotService.GetRobot(item.Id).SendEvent += Management_SendEvent;
  636. RobotService.GetRobot(item.Id).ConnectedEvent += Management_ConnectedEvent;
  637. RobotService.GetRobot(item.Id).DisconnectedEvent += Management_DisconnectedEvent;
  638. await RobotService.GetRobot(item.Id).ConnectAsync(); //连接Robot
  639. SendTaskMessage($"{item.RobotName}{Lang.已连接}!", MessageLevel.Debug);
  640. }
  641. catch (Exception ex)
  642. {
  643. sb.AppendLine($"{item.RobotName}:{Lang.断开连接}![{ex.Message}]");
  644. SendTaskMessage($"{item.RobotName}{Lang.断开连接}!", MessageLevel.Alarm);
  645. }
  646. }
  647. if (sb.Length == 0)
  648. {
  649. return new Tuple<bool, string>(true, "");
  650. }
  651. else
  652. {
  653. return new Tuple<bool, string>(false, sb.ToString());
  654. }
  655. }
  656. /// <summary>
  657. /// 初始化PLC
  658. /// </summary>
  659. /// <returns></returns>
  660. public async Task<Tuple<bool, string>> InitPlc()
  661. {
  662. PlcService = new PlcService();
  663. StringBuilder sb = new StringBuilder();
  664. foreach (var item in DeviceConfig.Plcs)
  665. {
  666. try
  667. {
  668. if (!PlcService.CreatePlc(item.Id, item))
  669. {
  670. sb.AppendLine($"{item.Name}: 不支持的 PLC 通讯类型 [{item.CommunicationType}]");
  671. continue;
  672. }
  673. var plc = PlcService.GetPlc(item.Id);
  674. if (plc == null)
  675. {
  676. sb.AppendLine($"{item.Name}: 创建 PLC 失败");
  677. continue;
  678. }
  679. Status.Add(new StatusInfo(item.Id, item.Name + $"{Lang.未连接}", new SolidColorBrush(Colors.Red)));
  680. plc.ConnectChangedEvent += Management_PlcConnectChangedEvent;
  681. await plc.ConnectAsync();
  682. }
  683. catch (Exception ex)
  684. {
  685. sb.AppendLine($"{item.Name}:{Lang.断开连接}![{ex.Message}]");
  686. SendTaskMessage($"{item.Name}{Lang.断开连接}!", MessageLevel.Alarm);
  687. }
  688. }
  689. if (sb.Length == 0)
  690. {
  691. return new Tuple<bool, string>(true, "");
  692. }
  693. else
  694. {
  695. return new Tuple<bool, string>(false, sb.ToString());
  696. }
  697. }
  698. /// <summary>
  699. /// 初始化相机
  700. /// </summary>
  701. /// <returns></returns>
  702. public Task<Tuple<bool, string>> InitCameras()
  703. {
  704. CameraService = new CameraService();
  705. StringBuilder sb = new StringBuilder();
  706. foreach (var item in DeviceConfig.Cameras)
  707. {
  708. try
  709. {
  710. Renders.Add(new ShowRender() { Id = item.Id, CameraName = item.CameraName });
  711. CameraService.CreateCamera(item.Id, item);
  712. Status.Add(new StatusInfo(item.Id, item.CameraName + $"{Lang.未连接}", new SolidColorBrush(Colors.Red)));
  713. CameraService.GetCamera(item.Id).CameraConnectChangedEvent += Management_CameraConnectChangedEvent;
  714. if (CameraService.GetCamera(item.Id).OpenDevice())
  715. {
  716. SendTaskMessage($"{item.CameraName}{Lang.已连接}!", MessageLevel.Debug);
  717. }
  718. else
  719. {
  720. SendTaskMessage($"{item.CameraName}{Lang.连接失败}!", MessageLevel.Alarm);
  721. sb.AppendLine($"{item.CameraName}:{Lang.连接失败}!");
  722. }
  723. }
  724. catch (Exception ex)
  725. {
  726. SendTaskMessage($"{item.CameraName}{Lang.连接失败}!", MessageLevel.Alarm);
  727. sb.AppendLine($"{item.CameraName}:{Lang.连接失败}!:{ex.Message}");
  728. }
  729. }
  730. _container.Resolve<ShowVisionRenderViewModel>().Renders = Renders;
  731. if (sb.Length == 0)
  732. {
  733. return Task.FromResult(new Tuple<bool, string>(true, ""));
  734. }
  735. else
  736. {
  737. return Task.FromResult(new Tuple<bool, string>(false, sb.ToString()));
  738. }
  739. }
  740. /// <summary>
  741. /// 初始化后台TCP服务器通讯
  742. /// </summary>
  743. public void InitBgTcp()
  744. {
  745. try
  746. {
  747. BgCommunicate = new BgCommunicate(DeviceConfig.BgCommunicate);
  748. BgCommunicate.ConnectedEvent += BgCommunicate_ConnectedEvent;
  749. BgCommunicate.DisconnectedEvent += BgCommunicate_DisconnectedEvent;
  750. BgCommunicate.ReceivedEvent += BgCommunicate_ReceivedEvent;
  751. BgCommunicate.SendEvent += BgCommunicate_SendEvent;
  752. BgCommunicate.StartListening();
  753. SendTaskMessage($"{Lang.已开启监听端口}:{DeviceConfig.BgCommunicate.Port}", MessageLevel.Debug);
  754. }
  755. catch (Exception ex)
  756. {
  757. SendTaskMessage($"{Lang.未开启监听端口}:{DeviceConfig.BgCommunicate.Port}", MessageLevel.Error);
  758. LogHelper.WriteLogError("开启后台服务器通讯时出错!", ex);
  759. }
  760. }
  761. /// <summary>
  762. /// 初始化后台ModbusTCP从站
  763. /// </summary>
  764. public void InitModbusTcp()
  765. {
  766. try
  767. {
  768. BgModbusTcpCommunicate = new BgModbusTcpCommunicate(DeviceConfig.BgModbusCommunicate);
  769. if (DeviceConfig.BgModbusCommunicate.IsEnabled)
  770. {
  771. BgModbusTcpCommunicate.Listen();
  772. SendTaskMessage($"Start Modbus Tcp Slave:{BgModbusTcpCommunicate.IP}:{BgModbusTcpCommunicate.Port}:{BgModbusTcpCommunicate.SlaveID}", MessageLevel.Debug);
  773. }
  774. }
  775. catch (Exception ex)
  776. {
  777. SendTaskMessage($"No Start Modbus Tcp Slave:{ex.Message}", MessageLevel.Alarm);
  778. LogHelper.WriteLogError("开启后台Modbus TCP通讯时出错!", ex);
  779. }
  780. }
  781. /// <summary>
  782. /// 读取 Modbus TCP 主站配置
  783. /// </summary>
  784. public void ReadModbusTcpMasterDeviceConfig()
  785. {
  786. try
  787. {
  788. if (File.Exists(FilePath.ModbusTcpMasterConfigurationPath))
  789. {
  790. ModbusTcpMasterConfig = FileHelper.ReadJsonFile<ModbusTcpMasterConfig>(FilePath.ModbusTcpMasterConfigurationPath);
  791. }
  792. else
  793. {
  794. ModbusTcpMasterConfig = new ModbusTcpMasterConfig();
  795. }
  796. }
  797. catch (Exception ex)
  798. {
  799. ModbusTcpMasterConfig = new ModbusTcpMasterConfig();
  800. LogHelper.WriteLogError("加载 Modbus TCP 主站配置文件时出错!", ex);
  801. }
  802. }
  803. /// <summary>
  804. /// 保存 Modbus TCP 主站配置
  805. /// </summary>
  806. public void SaveModbusTcpMasterDeviceConfig()
  807. {
  808. try
  809. {
  810. FileHelper.WriteJsonFile(ModbusTcpMasterConfig, FilePath.ModbusTcpMasterConfigurationPath);
  811. }
  812. catch (Exception ex)
  813. {
  814. LogHelper.WriteLogError("保存 Modbus TCP 主站配置时出错!", ex);
  815. }
  816. }
  817. /// <summary>
  818. /// 初始化 Modbus TCP 主站(轮询电批/扭矩控制器)
  819. /// </summary>
  820. public void InitModbusTcpMaster()
  821. {
  822. try
  823. {
  824. ModbusTcpMasterCommunicate?.Stop();
  825. ModbusTcpMasterCommunicate = new ModbusTcpMasterCommunicate(ModbusTcpMasterConfig, _eventAggregator);
  826. if (!ModbusTcpMasterConfig.IsEnabled)
  827. {
  828. return;
  829. }
  830. ModbusTcpMasterCommunicate.Start();
  831. SendTaskMessage($"Start Modbus Tcp Master:{ModbusTcpMasterConfig.IP}:{ModbusTcpMasterConfig.Port}:{ModbusTcpMasterConfig.SlaveID}", MessageLevel.Debug);
  832. }
  833. catch (Exception ex)
  834. {
  835. SendTaskMessage($"No Start Modbus Tcp Master:{ex.Message}", MessageLevel.Alarm);
  836. LogHelper.WriteLogError("开启 Modbus TCP 主站通讯时出错!", ex);
  837. }
  838. }
  839. /// <summary>
  840. /// 初始化 EthernetIP
  841. /// </summary>
  842. public void InitEthernetIP()
  843. {
  844. try
  845. {
  846. EnsureEthernetIpStatusEntry();
  847. DisposeRfidClient();
  848. BgEipCommunicate = new SIG350RFIDClient(DeviceConfig.BgEthernetIPCommunicate);
  849. BgEipCommunicate.BeforeStartRfidReading = ApplyEthernetIpRfidConfiguration;
  850. BgEipCommunicate.OnLog += BgEipCommunicate_OnLog;
  851. BgEipCommunicate.OnTagDataReceived += RfidClient_OnTagDataReceived;
  852. BgEipCommunicate.OnConnectReceive += Management_EipConnectChangedEvent;
  853. if (!DeviceConfig.BgEthernetIPCommunicate.IsEnabled)
  854. {
  855. UpdateEthernetIpStatus(false);
  856. return;
  857. }
  858. if (BgEipCommunicate.Connect())
  859. {
  860. UpdateEthernetIpStatus(true);
  861. SendTaskMessage($"EthernetIP:{DeviceConfig.BgEthernetIPCommunicate.IP}", MessageLevel.Debug);
  862. StartEthernetIpRfidIO();
  863. }
  864. else
  865. {
  866. UpdateEthernetIpStatus(false);
  867. // Connect 失败时会触发 OnConnectReceive(false),内部自动重连
  868. }
  869. }
  870. catch (Exception ex)
  871. {
  872. UpdateEthernetIpStatus(false);
  873. SendTaskMessage($"EthernetIP err:{ex.Message}", MessageLevel.Alarm);
  874. LogHelper.WriteLogError("开启 EthernetIP通讯时出错!", ex);
  875. }
  876. }
  877. private void EnsureEthernetIpStatusEntry()
  878. {
  879. if (Status.Any(s => s.ID == EthernetIpStatusId))
  880. return;
  881. App.Current.Dispatcher.Invoke(() =>
  882. {
  883. if (!Status.Any(s => s.ID == EthernetIpStatusId))
  884. {
  885. Status.Add(new StatusInfo(
  886. EthernetIpStatusId,
  887. $"{Lang.EthernetIP}{Lang.未连接}",
  888. new SolidColorBrush(Colors.Red)));
  889. }
  890. });
  891. }
  892. private void UpdateEthernetIpStatus(bool isConnected)
  893. {
  894. var sta = Status.FirstOrDefault(s => s.ID == EthernetIpStatusId);
  895. if (sta == null)
  896. return;
  897. App.Current.Dispatcher.Invoke(() =>
  898. {
  899. if (isConnected)
  900. {
  901. sta.Message = $"{Lang.EthernetIP}{Lang.已连接}";
  902. sta.Background = new SolidColorBrush(Colors.Green);
  903. }
  904. else
  905. {
  906. sta.Message = $"{Lang.EthernetIP}{Lang.未连接}";
  907. sta.Background = new SolidColorBrush(Colors.Red);
  908. }
  909. });
  910. }
  911. private void Management_EipConnectChangedEvent(bool isConnected)
  912. {
  913. if (isConnected)
  914. {
  915. SendTaskMessage($"{Lang.EthernetIP}{Lang.已连接}!", MessageLevel.Debug);
  916. TryStartHandshakeAfterEipReady();
  917. }
  918. else
  919. {
  920. SendTaskMessage($"{Lang.EthernetIP}{Lang.断开连接}!", MessageLevel.Error);
  921. }
  922. UpdateEthernetIpStatus(isConnected);
  923. }
  924. /// <summary>配置 RFID 端口(1..N)并固定为 UID 读模式。</summary>
  925. private void ApplyEthernetIpRfidConfiguration()
  926. {
  927. if (BgEipCommunicate == null || !BgEipCommunicate.IsConnected)
  928. return;
  929. var eipConfig = DeviceConfig.BgEthernetIPCommunicate;
  930. var ports = eipConfig.GetEnabledPortNumbers();
  931. BgEipCommunicate.ConfigureRFIDPorts(ports, PortMode.IOLAutostart);
  932. BgEipCommunicate.ConfigureReadOptions(eipConfig.ToRfh5xxReadOptions());
  933. SendTaskMessage($"RFID 已配置端口 1–{eipConfig.RfidPortCount},读 UID", MessageLevel.Debug);
  934. }
  935. private void StartEthernetIpRfidIO()
  936. {
  937. if (BgEipCommunicate == null || !BgEipCommunicate.IsConnected)
  938. return;
  939. try
  940. {
  941. ApplyEthernetIpRfidConfiguration();
  942. }
  943. catch (Exception ex)
  944. {
  945. SendTaskMessage("RFID 端口配置失败: " + ex.Message, MessageLevel.Alarm);
  946. LogHelper.WriteLogError("RFID 端口配置失败", ex);
  947. }
  948. BgEipCommunicate.StartRFIDReading(DeviceConfig.BgEthernetIPCommunicate.CycleTime);
  949. TryStartHandshakeAfterEipReady();
  950. }
  951. /// <summary>EIP 就绪后尝试启动 Ready 握手;MC 未连接则跳过,由 MC 重连循环补启。</summary>
  952. private void TryStartHandshakeAfterEipReady()
  953. {
  954. var mcPlc = GetMcPlcInfo();
  955. if (mcPlc == null)
  956. return;
  957. var client = GetMcPlcClient();
  958. if (client == null || !client.IsConnected)
  959. return;
  960. TryStartHandshakeLoop(mcPlc);
  961. }
  962. /// <summary>
  963. /// 初始化三菱 MC PLC:自动连接、心跳、Ready 握手、SFIS 信号监听
  964. /// </summary>
  965. public void InitMcCommunicate()
  966. {
  967. var mcPlc = GetMcPlcInfo();
  968. if (mcPlc == null)
  969. {
  970. SendTaskMessage("未配置三菱 MC PLC,跳过 MC 通讯初始化", MessageLevel.Debug);
  971. return;
  972. }
  973. try
  974. {
  975. StopMcCommunicateLoops();
  976. var mcWrapper = PlcService?.GetPlc(mcPlc.Id) as MitsubishiMcPLC;
  977. if (mcWrapper == null)
  978. {
  979. SendTaskMessage("MC PLC 未通过 PlcService 创建,请检查 PLC 配置", MessageLevel.Alarm);
  980. return;
  981. }
  982. var client = mcWrapper.Client;
  983. if (_mcPlcLogHandler == null)
  984. _mcPlcLogHandler = msg => SendTaskMessage($"PLC {msg}", MessageLevel.Info);
  985. client.OnLog -= _mcPlcLogHandler;
  986. client.OnLog += _mcPlcLogHandler;
  987. client.ConnectionLost -= McPlcConnectionLost;
  988. client.ConnectionLost += McPlcConnectionLost;
  989. if (!client.IsConnected)
  990. {
  991. mcWrapper.Connect();
  992. }
  993. if (!client.IsConnected)
  994. {
  995. SendTaskMessage("PLC 连接失败,将自动重连", MessageLevel.Alarm);
  996. StartMcReconnectLoop();
  997. return;
  998. }
  999. SendTaskMessage("PLC TCP 连接成功,正在测试 MC 读写...", MessageLevel.Info);
  1000. string testDev = string.IsNullOrWhiteSpace(mcPlc.PlcDataStartDevice) ? "D110" : mcPlc.PlcDataStartDevice.Trim();
  1001. if (testDev == "0")
  1002. testDev = "D110";
  1003. if (!client.TestCommunication(testDev))
  1004. {
  1005. SendTaskMessage("MC 通讯测试失败,请检查:二进制3E、端口、允许RUN中写入", MessageLevel.Alarm);
  1006. }
  1007. else
  1008. {
  1009. SendTaskMessage("连接成功,MC 字通讯正常", MessageLevel.Info);
  1010. }
  1011. string bitTestDev = ResolveBitTestDevice(mcPlc);
  1012. if (bitTestDev != null)
  1013. {
  1014. if (!client.TestBitCommunication(bitTestDev))
  1015. SendTaskMessage("MC 位通讯测试失败,请检查 OK/NG 位地址及 RUN 中写入", MessageLevel.Alarm);
  1016. }
  1017. else
  1018. {
  1019. SendTaskMessage("MC 位通讯测试跳过:请配置读OK或读NG位地址", MessageLevel.Debug);
  1020. }
  1021. client.ResetHeartbeatState();
  1022. EnsureMcCommunicateLoopsRunning(mcPlc, client);
  1023. StartMcReconnectLoop();
  1024. }
  1025. catch (Exception ex)
  1026. {
  1027. SendTaskMessage("PLC 连接失败: " + ex.Message, MessageLevel.Alarm);
  1028. LogHelper.WriteLogError("PLC 连接失败", ex);
  1029. StopMcCommunicateLoops();
  1030. }
  1031. }
  1032. private void McPlcConnectionLost()
  1033. {
  1034. _mcMonitorLoopsStarted = false;
  1035. SendTaskMessage("MC PLC 连接中断", MessageLevel.Alarm);
  1036. }
  1037. /// <summary>MC 连接可用时确保心跳 / SFIS / Ready 握手循环已启动。</summary>
  1038. private void EnsureMcCommunicateLoopsRunning(PlcInfo mcPlc, MitsubishiPLC client)
  1039. {
  1040. if (mcPlc == null || client == null || !client.IsConnected)
  1041. return;
  1042. client.ResetHeartbeatState();
  1043. StartHeartbeatLoop(mcPlc);
  1044. StartSfisSignalLoop();
  1045. TryStartHandshakeLoop(mcPlc);
  1046. _mcMonitorLoopsStarted = true;
  1047. }
  1048. private void TryStartHandshakeLoop(PlcInfo mcPlc)
  1049. {
  1050. if (mcPlc == null || !mcPlc.PlcForwardEnabled || !CanStartHandshake(mcPlc))
  1051. return;
  1052. StartHandshakeLoop();
  1053. }
  1054. private PlcInfo GetMcPlcInfo()
  1055. {
  1056. return DeviceConfig?.Plcs?.FirstOrDefault(p => p.CommunicationType == CommunicationType.Mitsubishi_MC);
  1057. }
  1058. private MitsubishiPLC GetMcPlcClient()
  1059. {
  1060. var mcInfo = GetMcPlcInfo();
  1061. if (mcInfo == null || PlcService == null)
  1062. return null;
  1063. return (PlcService.GetPlc(mcInfo.Id) as MitsubishiMcPLC)?.Client;
  1064. }
  1065. private bool CanStartHandshake(PlcInfo mcPlc)
  1066. {
  1067. if (BgEipCommunicate == null || !BgEipCommunicate.IsConnected || !BgEipCommunicate.IsIOModeActive)
  1068. return false;
  1069. var mapping = mcPlc.ToPlcMapping();
  1070. return MitsubishiPLC.IsDeviceEnabled(mapping.ReadyBitDevice)
  1071. && MitsubishiPLC.IsDeviceEnabled(mapping.PortDevice)
  1072. && MitsubishiPLC.IsDeviceEnabled(mapping.DataStartDevice);
  1073. }
  1074. private string ResolveBitTestDevice(PlcInfo mcPlc)
  1075. {
  1076. if (mcPlc == null) return null;
  1077. if (MitsubishiPLC.IsDeviceEnabled(mcPlc.PlcOkBitDevice))
  1078. return mcPlc.PlcOkBitDevice.Trim();
  1079. if (MitsubishiPLC.IsDeviceEnabled(mcPlc.PlcNgBitDevice))
  1080. return mcPlc.PlcNgBitDevice.Trim();
  1081. return null;
  1082. }
  1083. private void StopMcCommunicateLoops()
  1084. {
  1085. try
  1086. {
  1087. _mcMonitorLoopsStarted = false;
  1088. StopMcReconnectLoop();
  1089. StopHandshakeLoop();
  1090. StopHeartbeatLoop();
  1091. StopSfisSignalLoop();
  1092. ResetSfisWorkflow();
  1093. }
  1094. catch { /* ignore */ }
  1095. }
  1096. private void StartMcReconnectLoop()
  1097. {
  1098. StopMcReconnectLoop();
  1099. _mcReconnectCts = new CancellationTokenSource();
  1100. var token = _mcReconnectCts.Token;
  1101. _mcReconnectTask = Task.Run(() => McReconnectLoop(token), token);
  1102. }
  1103. private void StopMcReconnectLoop()
  1104. {
  1105. try
  1106. {
  1107. if (_mcReconnectCts != null)
  1108. {
  1109. _mcReconnectCts.Cancel();
  1110. try
  1111. {
  1112. if (_mcReconnectTask != null)
  1113. _mcReconnectTask.Wait(2000);
  1114. }
  1115. catch (AggregateException) { /* ignore */ }
  1116. _mcReconnectCts.Dispose();
  1117. }
  1118. }
  1119. catch { /* ignore */ }
  1120. finally
  1121. {
  1122. _mcReconnectCts = null;
  1123. _mcReconnectTask = null;
  1124. }
  1125. }
  1126. private void McReconnectLoop(CancellationToken token)
  1127. {
  1128. int attempt = 0;
  1129. while (!token.IsCancellationRequested)
  1130. {
  1131. try
  1132. {
  1133. var mcPlc = GetMcPlcInfo();
  1134. if (mcPlc == null)
  1135. {
  1136. Thread.Sleep(1000);
  1137. continue;
  1138. }
  1139. var mcWrapper = PlcService?.GetPlc(mcPlc.Id) as MitsubishiMcPLC;
  1140. var client = mcWrapper?.Client;
  1141. if (client == null)
  1142. {
  1143. Thread.Sleep(1000);
  1144. continue;
  1145. }
  1146. if (client.IsConnected)
  1147. {
  1148. if (!_mcMonitorLoopsStarted)
  1149. EnsureMcCommunicateLoopsRunning(mcPlc, client);
  1150. attempt = 0;
  1151. Thread.Sleep(500);
  1152. continue;
  1153. }
  1154. if ((DateTime.UtcNow - _mcLastReconnectAttempt).TotalMilliseconds < McReconnectIntervalMs)
  1155. {
  1156. Thread.Sleep(500);
  1157. continue;
  1158. }
  1159. _mcLastReconnectAttempt = DateTime.UtcNow;
  1160. attempt++;
  1161. SendTaskMessage($"MC PLC 连接断开,正在重连(第 {attempt} 次)...", MessageLevel.Info);
  1162. bool connected;
  1163. lock (_mcReconnectSync)
  1164. {
  1165. if (client.IsConnected)
  1166. {
  1167. connected = true;
  1168. }
  1169. else
  1170. {
  1171. mcWrapper.Connect();
  1172. connected = client.IsConnected;
  1173. }
  1174. }
  1175. if (connected)
  1176. {
  1177. EnsureMcCommunicateLoopsRunning(mcPlc, client);
  1178. SendTaskMessage("MC PLC 重连成功", MessageLevel.Info);
  1179. attempt = 0;
  1180. }
  1181. else
  1182. {
  1183. SendTaskMessage("MC PLC 重连失败,将继续重试", MessageLevel.Alarm);
  1184. }
  1185. }
  1186. catch (Exception ex)
  1187. {
  1188. if (token.IsCancellationRequested)
  1189. break;
  1190. LogHelper.WriteLogError("MC 重连循环异常", ex);
  1191. Thread.Sleep(McReconnectIntervalMs);
  1192. }
  1193. }
  1194. }
  1195. private void StartHeartbeatLoop(PlcInfo mcPlc)
  1196. {
  1197. StopHeartbeatLoop();
  1198. if (mcPlc == null) return;
  1199. string device = mcPlc.PlcHeartbeatDevice;
  1200. if (!MitsubishiPLC.IsDeviceEnabled(device))
  1201. return;
  1202. PlcHeartbeatKind kind;
  1203. try
  1204. {
  1205. kind = MitsubishiPLC.ResolveHeartbeatKind(device);
  1206. }
  1207. catch (Exception ex)
  1208. {
  1209. SendTaskMessage("PLC 心跳地址无效", MessageLevel.Alarm);
  1210. LogHelper.WriteLogError("PLC 心跳地址无效", ex);
  1211. return;
  1212. }
  1213. if (kind == PlcHeartbeatKind.Disabled)
  1214. return;
  1215. int intervalMs = Math.Max(200, mcPlc.PlcHeartbeatIntervalMs);
  1216. _heartbeatCts = new CancellationTokenSource();
  1217. var token = _heartbeatCts.Token;
  1218. _heartbeatTask = Task.Run(() => HeartbeatLoop(token, device.Trim(), intervalMs), token);
  1219. SendTaskMessage("PLC 已启动心跳 " + device.Trim()
  1220. + (kind == PlcHeartbeatKind.Bit ? "(位翻转)" : "(字递增)")
  1221. + " 间隔=" + intervalMs + "ms", MessageLevel.Info);
  1222. }
  1223. private void StopHeartbeatLoop()
  1224. {
  1225. try
  1226. {
  1227. if (_heartbeatCts != null)
  1228. {
  1229. _heartbeatCts.Cancel();
  1230. try
  1231. {
  1232. if (_heartbeatTask != null)
  1233. _heartbeatTask.Wait(2000);
  1234. }
  1235. catch (AggregateException) { /* ignore */ }
  1236. _heartbeatCts.Dispose();
  1237. }
  1238. }
  1239. catch { /* ignore */ }
  1240. finally
  1241. {
  1242. _heartbeatCts = null;
  1243. _heartbeatTask = null;
  1244. }
  1245. }
  1246. private void HeartbeatLoop(CancellationToken token, string device, int intervalMs)
  1247. {
  1248. while (!token.IsCancellationRequested)
  1249. {
  1250. try
  1251. {
  1252. MitsubishiPLC plc;
  1253. lock (_sync)
  1254. plc = GetMcPlcClient();
  1255. if (plc == null || !plc.IsConnected)
  1256. {
  1257. Thread.Sleep(100);
  1258. continue;
  1259. }
  1260. plc.PulseHeartbeat(device);
  1261. }
  1262. catch (Exception ex)
  1263. {
  1264. if (!token.IsCancellationRequested)
  1265. {
  1266. SendTaskMessage("心跳异常: " + ex.Message, MessageLevel.Alarm);
  1267. LogHelper.WriteLogError("心跳异常", ex);
  1268. }
  1269. }
  1270. Thread.Sleep(intervalMs);
  1271. }
  1272. }
  1273. private void StartHandshakeLoop()
  1274. {
  1275. StopHandshakeLoop();
  1276. _handshakeCts = new CancellationTokenSource();
  1277. var token = _handshakeCts.Token;
  1278. _handshakeTask = Task.Run(() => HandshakeLoop(token), token);
  1279. SendTaskMessage("已启动 Ready 握手循环", MessageLevel.Info);
  1280. }
  1281. private void HandshakeLoop(CancellationToken token)
  1282. {
  1283. bool lastReady = false;
  1284. while (!token.IsCancellationRequested)
  1285. {
  1286. try
  1287. {
  1288. MitsubishiPLC plc;
  1289. SIG350RFIDClient rfid;
  1290. lock (_sync)
  1291. {
  1292. plc = GetMcPlcClient();
  1293. rfid = BgEipCommunicate;
  1294. }
  1295. if (plc == null || !plc.IsConnected || rfid == null || !rfid.IsConnected || !rfid.IsIOModeActive)
  1296. {
  1297. Thread.Sleep(50);
  1298. continue;
  1299. }
  1300. PlcRfidMapping mapping = SnapshotMapping();
  1301. if (!MitsubishiPLC.IsDeviceEnabled(mapping.ReadyBitDevice))
  1302. {
  1303. Thread.Sleep(100);
  1304. continue;
  1305. }
  1306. bool ready = plc.ReadReadyBit(mapping);
  1307. if (ready && !lastReady)
  1308. {
  1309. SendTaskMessage("PLC 检测到 Ready=ON,开始读卡", MessageLevel.Info);
  1310. ProcessReadyRequest(plc, rfid, mapping, token);
  1311. while (!token.IsCancellationRequested)
  1312. {
  1313. if (!plc.IsConnected)
  1314. break;
  1315. if (!plc.ReadReadyBit(mapping))
  1316. break;
  1317. Thread.Sleep(20);
  1318. }
  1319. if (!token.IsCancellationRequested && plc.IsConnected)
  1320. {
  1321. plc.ClearReceiveBit(mapping);
  1322. SendTaskMessage("PLC Ready=OFF,已清除 Receive", MessageLevel.Info);
  1323. }
  1324. lastReady = false;
  1325. continue;
  1326. }
  1327. lastReady = ready;
  1328. Thread.Sleep(20);
  1329. }
  1330. catch (Exception ex)
  1331. {
  1332. if (token.IsCancellationRequested)
  1333. break;
  1334. SendTaskMessage("PLC 握手异常", MessageLevel.Alarm);
  1335. LogHelper.WriteLogError("PLC 握手异常", ex);
  1336. Thread.Sleep(100);
  1337. lastReady = false;
  1338. }
  1339. }
  1340. }
  1341. private PlcRfidMapping SnapshotMapping()
  1342. {
  1343. lock (_sync)
  1344. {
  1345. var mcPlc = GetMcPlcInfo();
  1346. return mcPlc?.ToPlcMapping() ?? new PlcRfidMapping();
  1347. }
  1348. }
  1349. private void ProcessReadyRequest(MitsubishiPLC plc, SIG350RFIDClient rfid, PlcRfidMapping mapping, CancellationToken token)
  1350. {
  1351. int port;
  1352. try
  1353. {
  1354. port = plc.ReadRequestPort(mapping);
  1355. }
  1356. catch (Exception ex)
  1357. {
  1358. SendTaskMessage("PLC 读端口号失败", MessageLevel.Alarm);
  1359. LogHelper.WriteLogError("PLC 读端口号失败", ex);
  1360. try { plc.WriteReadFailure(mapping); } catch { /* ignore */ }
  1361. return;
  1362. }
  1363. SendTaskMessage("PLC 请求端口 " + port, MessageLevel.Info);
  1364. if (port < 1 || port > 8)
  1365. {
  1366. SendTaskMessage("端口号无效: " + port, MessageLevel.Alarm);
  1367. try { plc.WriteReadFailure(mapping); } catch { /* ignore */ }
  1368. return;
  1369. }
  1370. if (token.IsCancellationRequested)
  1371. return;
  1372. RFIDTagData tag = null;
  1373. try
  1374. {
  1375. int timeout = mapping.ReadTimeoutMs > 0 ? mapping.ReadTimeoutMs : 2000;
  1376. var readOpts = DeviceConfig.BgEthernetIPCommunicate.ToRfh5xxReadOptions();
  1377. tag = rfid.ReadRFIDTag(port, timeout, readOpts);
  1378. }
  1379. catch (Exception ex)
  1380. {
  1381. SendTaskMessage("RFID 读卡异常", MessageLevel.Alarm);
  1382. LogHelper.WriteLogError("RFID 读卡异常", ex);
  1383. }
  1384. if (tag != null && tag.Data != null && tag.Data.Length > 0)
  1385. {
  1386. lock (_sync)
  1387. _lastRfidTag = CloneTag(tag);
  1388. try
  1389. {
  1390. plc.WriteReadSuccess(mapping, tag);
  1391. }
  1392. catch (Exception ex)
  1393. {
  1394. SendTaskMessage("RFID 写 OK/数据失败", MessageLevel.Alarm);
  1395. LogHelper.WriteLogError("RFID 写 OK/数据失败", ex);
  1396. }
  1397. }
  1398. else
  1399. {
  1400. SendTaskMessage("RFID 端口 " + port + " 未读到标签", MessageLevel.Alarm);
  1401. try { plc.WriteReadFailure(mapping); }
  1402. catch (Exception ex)
  1403. {
  1404. SendTaskMessage("RFID 写 NG 失败", MessageLevel.Alarm);
  1405. LogHelper.WriteLogError("RFID 写 NG 失败", ex);
  1406. }
  1407. }
  1408. }
  1409. private bool SnapshotSfisEnabled()
  1410. {
  1411. lock (_sync)
  1412. return DeviceConfig.BgSfisCommunicate?.IsEnabled == true;
  1413. }
  1414. private SfisConfig SnapshotSfisConfig()
  1415. {
  1416. lock (_sync)
  1417. return DeviceConfig.BgSfisCommunicate?.ToSfisConfig() ?? new SfisConfig();
  1418. }
  1419. private PlcSfisFeedback SnapshotSfisPlcFeedback()
  1420. {
  1421. lock (_sync)
  1422. return DeviceConfig.BgSfisCommunicate?.ToPlcSfisFeedback() ?? new PlcSfisFeedback();
  1423. }
  1424. private SfisWorkflowService CreateSfisWorkflow()
  1425. {
  1426. var workflow = new SfisWorkflowService(SnapshotSfisConfig());
  1427. workflow.OnLog += msg => SendTaskMessage("SFIS " + msg, MessageLevel.Info);
  1428. return workflow;
  1429. }
  1430. private SfisWorkflowService GetOrCreateSfisWorkflow()
  1431. {
  1432. lock (_sync)
  1433. {
  1434. if (_sfisWorkflow != null)
  1435. return _sfisWorkflow;
  1436. }
  1437. var workflow = CreateSfisWorkflow();
  1438. lock (_sync)
  1439. {
  1440. if (_sfisWorkflow == null)
  1441. _sfisWorkflow = workflow;
  1442. else
  1443. {
  1444. try { workflow.Dispose(); }
  1445. catch { /* ignore */ }
  1446. }
  1447. return _sfisWorkflow;
  1448. }
  1449. }
  1450. private void ResetSfisWorkflow()
  1451. {
  1452. lock (_sync)
  1453. {
  1454. if (_sfisWorkflow != null)
  1455. {
  1456. try { _sfisWorkflow.Dispose(); }
  1457. catch { /* ignore */ }
  1458. _sfisWorkflow = null;
  1459. }
  1460. }
  1461. }
  1462. private void StartSfisSignalLoop()
  1463. {
  1464. StopSfisSignalLoop();
  1465. var sfis = DeviceConfig.BgSfisCommunicate;
  1466. if (sfis == null || !sfis.IsEnabled)
  1467. return;
  1468. bool passReady = sfis.PassEnabled && MitsubishiPLC.IsDeviceEnabled(sfis.PlcSfisPassSignalDevice);
  1469. bool uploadReady = sfis.UploadEnabled && MitsubishiPLC.IsDeviceEnabled(sfis.PlcSfisUploadSignalDevice);
  1470. if (!passReady && !uploadReady)
  1471. return;
  1472. _sfisSignalCts = new CancellationTokenSource();
  1473. var token = _sfisSignalCts.Token;
  1474. _sfisSignalTask = Task.Run(() => SfisSignalLoop(token), token);
  1475. SendTaskMessage("SFIS 已启动信号监听"
  1476. + (passReady ? " 过站=" + sfis.PlcSfisPassSignalDevice.Trim() : string.Empty)
  1477. + (uploadReady ? " 上传=" + sfis.PlcSfisUploadSignalDevice.Trim() : string.Empty), MessageLevel.Info);
  1478. }
  1479. private void StopSfisSignalLoop()
  1480. {
  1481. try
  1482. {
  1483. if (_sfisSignalCts != null)
  1484. {
  1485. _sfisSignalCts.Cancel();
  1486. try
  1487. {
  1488. if (_sfisSignalTask != null)
  1489. _sfisSignalTask.Wait(2000);
  1490. }
  1491. catch (AggregateException) { /* ignore */ }
  1492. _sfisSignalCts.Dispose();
  1493. }
  1494. }
  1495. catch { /* ignore */ }
  1496. finally
  1497. {
  1498. _sfisSignalCts = null;
  1499. _sfisSignalTask = null;
  1500. }
  1501. }
  1502. private void SfisSignalLoop(CancellationToken token)
  1503. {
  1504. bool lastPass = false;
  1505. bool lastUpload = false;
  1506. while (!token.IsCancellationRequested)
  1507. {
  1508. try
  1509. {
  1510. if (!SnapshotSfisEnabled())
  1511. {
  1512. lastPass = false;
  1513. lastUpload = false;
  1514. Thread.Sleep(200);
  1515. continue;
  1516. }
  1517. MitsubishiPLC plc;
  1518. lock (_sync)
  1519. plc = GetMcPlcClient();
  1520. if (plc == null || !plc.IsConnected)
  1521. {
  1522. Thread.Sleep(100);
  1523. continue;
  1524. }
  1525. var sfis = DeviceConfig.BgSfisCommunicate;
  1526. bool passEnabled = sfis.PassEnabled;
  1527. bool uploadEnabled = sfis.UploadEnabled;
  1528. string passDev = sfis.PlcSfisPassSignalDevice;
  1529. string uploadDev = sfis.PlcSfisUploadSignalDevice;
  1530. bool passOn = passEnabled && MitsubishiPLC.IsDeviceEnabled(passDev) && plc.ReadBit(passDev);
  1531. bool uploadOn = uploadEnabled && MitsubishiPLC.IsDeviceEnabled(uploadDev) && plc.ReadBit(uploadDev);
  1532. if (passEnabled && passOn && !lastPass)
  1533. {
  1534. SendTaskMessage("SFIS 过站信号上升沿 " + passDev.Trim(), MessageLevel.Info);
  1535. RunSfisOnSignal(plc, runPass: true, runUpload: false);
  1536. }
  1537. else if (passEnabled && !passOn && lastPass)
  1538. {
  1539. try
  1540. {
  1541. plc.ClearSfisReceiveBit(SnapshotSfisPlcFeedback(), isPassStation: true);
  1542. SendTaskMessage("过站信号 OFF,已清除 Receive", MessageLevel.Info);
  1543. }
  1544. catch (Exception ex)
  1545. {
  1546. SendTaskMessage("清除过站 Receive 失败: " + ex.Message, MessageLevel.Alarm);
  1547. }
  1548. }
  1549. if (uploadEnabled && uploadOn && !lastUpload)
  1550. {
  1551. SendTaskMessage("SFIS 上传信号上升沿 " + uploadDev.Trim(), MessageLevel.Info);
  1552. RunSfisOnSignal(plc, runPass: false, runUpload: true);
  1553. }
  1554. else if (uploadEnabled && !uploadOn && lastUpload)
  1555. {
  1556. try
  1557. {
  1558. plc.ClearSfisReceiveBit(SnapshotSfisPlcFeedback(), isPassStation: false);
  1559. SendTaskMessage("上传信号 OFF,已清除 Receive", MessageLevel.Info);
  1560. }
  1561. catch (Exception ex)
  1562. {
  1563. SendTaskMessage("清除上传 Receive 失败: " + ex.Message, MessageLevel.Alarm);
  1564. }
  1565. }
  1566. lastPass = passOn;
  1567. lastUpload = uploadOn;
  1568. Thread.Sleep(50);
  1569. }
  1570. catch (Exception ex)
  1571. {
  1572. if (token.IsCancellationRequested)
  1573. break;
  1574. SendTaskMessage("SFIS 信号监听异常: " + ex.Message, MessageLevel.Alarm);
  1575. Thread.Sleep(200);
  1576. lastPass = false;
  1577. lastUpload = false;
  1578. }
  1579. }
  1580. }
  1581. private struct SfisRunPlan
  1582. {
  1583. public bool RunPass;
  1584. public bool RunUpload;
  1585. }
  1586. private void RunSfisOnSignal(MitsubishiPLC plc, bool runPass, bool runUpload)
  1587. {
  1588. bool isPassStation = runPass;
  1589. PlcSfisFeedback feedback = SnapshotSfisPlcFeedback();
  1590. bool success = false;
  1591. try
  1592. {
  1593. RFIDTagData tag = TryLoadTagForSfis(plc);
  1594. if (tag == null)
  1595. {
  1596. SendTaskMessage("SFIS 无可用 RFID 数据,写 PLC NG", MessageLevel.Alarm);
  1597. WriteSfisPlcResult(plc, feedback, isPassStation, false);
  1598. return;
  1599. }
  1600. success = TryRunSfisWorkflow(tag, new SfisRunPlan { RunPass = runPass, RunUpload = runUpload });
  1601. }
  1602. catch (Exception ex)
  1603. {
  1604. SendTaskMessage("SFIS 流程异常: " + ex.Message, MessageLevel.Alarm);
  1605. success = false;
  1606. }
  1607. WriteSfisPlcResult(plc, feedback, isPassStation, success);
  1608. }
  1609. private void WriteSfisPlcResult(MitsubishiPLC plc, PlcSfisFeedback feedback, bool isPassStation, bool success)
  1610. {
  1611. try
  1612. {
  1613. plc.WriteSfisResult(feedback, isPassStation, success);
  1614. }
  1615. catch (Exception ex)
  1616. {
  1617. SendTaskMessage((isPassStation ? "过站" : "上传") + " 回写失败: " + ex.Message, MessageLevel.Alarm);
  1618. }
  1619. }
  1620. private RFIDTagData TryLoadTagForSfis(MitsubishiPLC plc)
  1621. {
  1622. PlcRfidMapping mapping = SnapshotMapping();
  1623. var readOpts = DeviceConfig.BgEthernetIPCommunicate.ToRfh5xxReadOptions();
  1624. if (MitsubishiPLC.IsDeviceEnabled(mapping.DataStartDevice))
  1625. {
  1626. try
  1627. {
  1628. byte[] data = plc.ReadDeviceBytes(mapping.DataStartDevice, mapping.MaxDataBytes);
  1629. data = TrimTrailingZeros(data);
  1630. if (data.Length > 0)
  1631. {
  1632. int port = 0;
  1633. if (MitsubishiPLC.IsDeviceEnabled(mapping.PortDevice))
  1634. port = plc.ReadRequestPort(mapping);
  1635. return new RFIDTagData
  1636. {
  1637. Port = port,
  1638. Data = data,
  1639. Timestamp = DateTime.Now,
  1640. PayloadKind = readOpts.Kind
  1641. };
  1642. }
  1643. }
  1644. catch (Exception ex)
  1645. {
  1646. SendTaskMessage("SFIS 从 PLC 读取 RFID 数据失败: " + ex.Message, MessageLevel.Alarm);
  1647. }
  1648. }
  1649. lock (_sync)
  1650. {
  1651. if (_lastRfidTag != null && _lastRfidTag.Data != null && _lastRfidTag.Data.Length > 0)
  1652. return CloneTag(_lastRfidTag);
  1653. }
  1654. return null;
  1655. }
  1656. private static RFIDTagData CloneTag(RFIDTagData tag)
  1657. {
  1658. return new RFIDTagData
  1659. {
  1660. Port = tag.Port,
  1661. Data = (byte[])tag.Data.Clone(),
  1662. Timestamp = tag.Timestamp,
  1663. Quality = tag.Quality,
  1664. PayloadKind = tag.PayloadKind
  1665. };
  1666. }
  1667. private static byte[] TrimTrailingZeros(byte[] data)
  1668. {
  1669. if (data == null || data.Length == 0)
  1670. return new byte[0];
  1671. int len = data.Length;
  1672. while (len > 0 && data[len - 1] == 0)
  1673. len--;
  1674. if (len == data.Length)
  1675. return data;
  1676. var trimmed = new byte[len];
  1677. Array.Copy(data, trimmed, len);
  1678. return trimmed;
  1679. }
  1680. private bool TryRunSfisWorkflow(RFIDTagData tag, SfisRunPlan plan)
  1681. {
  1682. try
  1683. {
  1684. var config = SnapshotSfisConfig();
  1685. var context = SfisRfidMapper.FromRfidTag(tag, config);
  1686. var result = GetOrCreateSfisWorkflow().ProcessUnit(context, plan.RunPass, plan.RunUpload);
  1687. PrintSfisResult(result);
  1688. return result.Success;
  1689. }
  1690. catch (ArgumentException ex)
  1691. {
  1692. SendTaskMessage("SFIS 数据无效: " + ex.Message, MessageLevel.Alarm);
  1693. return false;
  1694. }
  1695. catch (Exception ex)
  1696. {
  1697. SendTaskMessage("SFIS 流程异常: " + ex.Message, MessageLevel.Alarm);
  1698. return false;
  1699. }
  1700. }
  1701. private void PrintSfisResult(SfisWorkflowResult result)
  1702. {
  1703. if (result == null)
  1704. return;
  1705. SendTaskMessage(result.Success
  1706. ? "SFIS 流程成功 ISN=" + result.Isn
  1707. : "SFIS 流程失败 ISN=" + result.Isn + " => " + result.ErrorMessage, MessageLevel.Info);
  1708. if (result.Steps == null)
  1709. return;
  1710. foreach (var step in result.Steps)
  1711. {
  1712. string msg = step.Response != null ? step.Response.Message : string.Empty;
  1713. int code = step.Response != null ? step.Response.ReturnCode : -1;
  1714. SendTaskMessage("SFIS " + step.StepName + ": P_RET=" + code + " " + msg, MessageLevel.Debug);
  1715. }
  1716. }
  1717. private void BgEipCommunicate_OnLog(string obj)
  1718. {
  1719. SendTaskMessage(obj, MessageLevel.Info);
  1720. }
  1721. /// <summary>
  1722. /// 初始化后台脚本
  1723. /// </summary>
  1724. public void InitBackgroundcript()
  1725. {
  1726. try
  1727. {
  1728. if (!Directory.Exists(FilePath.BackgroundScriptPath))
  1729. {
  1730. Directory.CreateDirectory(FilePath.BackgroundScriptPath);
  1731. }
  1732. try
  1733. {
  1734. string[] files = Directory.GetFiles(FilePath.BackgroundScriptPath);
  1735. List<string> ScriptFilesCollection = new List<string>();
  1736. foreach (string file in files)
  1737. {
  1738. string fileName = Path.GetFileName(file);
  1739. string fileExtension = Path.GetExtension(file);
  1740. if (fileExtension == ".cs")
  1741. {
  1742. ScriptFilesCollection.Add(file);
  1743. }
  1744. }
  1745. foreach (var item in ScriptFilesCollection)
  1746. {
  1747. try
  1748. {
  1749. string script = FileHelper.ReadFile(item);
  1750. SendTaskMessage($"{Lang.执行脚本}:{item}", MessageLevel.Info);
  1751. Task.Factory.StartNew(() =>
  1752. {
  1753. ScriptHelper.ExecuteScriptingAsync(script, _regionManager, _eventAggregator, _container, rst =>
  1754. {
  1755. if (!rst.Item1)
  1756. {
  1757. foreach (var err in rst.Item2)
  1758. {
  1759. SendTaskMessage($"{Lang.后台脚本出错}:{err}", MessageLevel.Error);
  1760. }
  1761. }
  1762. });
  1763. });
  1764. }
  1765. catch (Exception ex)
  1766. {
  1767. LogHelper.WriteLogError($"开启脚本时出错:{item}", ex);
  1768. }
  1769. }
  1770. }
  1771. catch (Exception ex)
  1772. {
  1773. LogHelper.WriteLogError("读取本地脚本文件列表时出错", ex);
  1774. }
  1775. }
  1776. catch (Exception ex)
  1777. {
  1778. LogHelper.WriteLogError("开启后台脚本时出错!", ex);
  1779. }
  1780. }
  1781. #endregion
  1782. #region 产品操作
  1783. /// <summary>
  1784. /// 加载所有产品
  1785. /// </summary>
  1786. public void LoadAllProducts()
  1787. {
  1788. Products = new ObservableCollection<ProductModel>();
  1789. try
  1790. {
  1791. if (File.Exists(FilePath.ProductsPath + "//ProductList.cfg"))
  1792. {
  1793. Dictionary<Guid, string> list = FileHelper.ReadJsonFile<Dictionary<Guid, string>>(FilePath.ProductsPath + "//ProductList.cfg");
  1794. foreach (var item in list)
  1795. {
  1796. Products.Add(FileHelper.ReadJsonFile<ProductModel>(FilePath.ProductsPath + "//" + item.Value + "//" + item.Value + ".cfg"));
  1797. //for (int i = 0; i < Products[Products.Count - 1].CameraProcedures.Count; i++)
  1798. //{
  1799. // for (int j = 0; j < Products[Products.Count - 1].CameraProcedures[i].ProcedureModels.Count; j++)
  1800. // {
  1801. // string prcPath = FilePath.ProductsPath + "//" + item.Value + "//" + Products[Products.Count - 1].CameraProcedures[i].Camera + "//" + Products[Products.Count - 1].CameraProcedures[i].ProcedureModels[j].Name + ".prc";
  1802. // Products[Products.Count - 1].CameraProcedures[i].ProcedureModels[j].Procedure = VmProcedure.Load(prcPath);
  1803. // }
  1804. //}
  1805. }
  1806. SendTaskMessage($"{Lang.所有产品加载完成}!", MessageLevel.Debug);
  1807. }
  1808. else
  1809. {
  1810. SendTaskMessage($"{Lang.无产品}!", MessageLevel.Alarm);
  1811. }
  1812. }
  1813. catch (Exception ex)
  1814. {
  1815. SendTaskMessage(ex.Message, MessageLevel.Error);
  1816. LogHelper.WriteLogError("加载所有产品时出错!", ex);
  1817. }
  1818. }
  1819. /// <summary>
  1820. /// 加载产品
  1821. /// </summary>
  1822. public void LoadProducts(ProductModel product)
  1823. {
  1824. var prd = Products.FirstOrDefault(x => x.ID == product.ID);
  1825. if (prd == null)
  1826. {
  1827. return;
  1828. }
  1829. try
  1830. {
  1831. //清除上一个产品的相机拍照数据
  1832. feederCurentBlobArea = new Dictionary<Guid, Dictionary<Guid, double>>();
  1833. LastPointsForFeeder = new Dictionary<Guid, List<Vector<double>>>();
  1834. PointCollection = new Dictionary<Guid, Dictionary<string, Queue<string>>>();
  1835. for (int i = 0; i < prd.CameraProcedures.Count; i++)
  1836. {
  1837. for (int j = 0; j < prd.CameraProcedures[i].ProcedureModels.Count; j++)
  1838. {
  1839. string prcPath = FilePath.ProductsPath + "//" + prd.Name + "//" + prd.CameraProcedures[i].Camera + "//" + prd.CameraProcedures[i].ProcedureModels[j].Name + ".vpp";
  1840. prd.CameraProcedures[i].ProcedureModels[j].ToolBlock = CogSerializer.LoadObjectFromFile(prcPath) as CogToolBlock;
  1841. }
  1842. }
  1843. //设置Feeder的光源亮度及Feeder的振动参数
  1844. prd.IsLoad = true;
  1845. foreach (var item in Products)
  1846. {
  1847. if (item.ID != prd.ID)
  1848. {
  1849. item.IsLoad = false;
  1850. }
  1851. }
  1852. CurrentProduct = prd;
  1853. Dictionary<Guid, List<NodeId>> keyValuePairs = new Dictionary<Guid, List<NodeId>>();
  1854. Dictionary<Guid, ObservableCollection<ProcedureOutputOpcAdr>> keyValuePairsT = new Dictionary<Guid, ObservableCollection<ProcedureOutputOpcAdr>>();
  1855. foreach (var item in prd.CameraProcedures)
  1856. {
  1857. foreach (var itemT in item.ProcedureModels)
  1858. {
  1859. IRobot robot = RobotService.GetRobot(itemT.RobotId);
  1860. if (robot != null && robot.Brand == RobotBrand.XYZ_Platform)
  1861. {
  1862. if (keyValuePairs.ContainsKey(itemT.Id))
  1863. {
  1864. keyValuePairs[itemT.Id].Add(itemT.TriggerCommand);
  1865. }
  1866. else
  1867. {
  1868. keyValuePairs.Add(itemT.Id, new List<NodeId> { itemT.TriggerCommand });
  1869. }
  1870. if (!keyValuePairsT.ContainsKey(itemT.Id))
  1871. {
  1872. keyValuePairsT.Add(itemT.Id, itemT.ProcedureOutputsOpcAdrs);
  1873. }
  1874. }
  1875. }
  1876. }
  1877. Guid[] Threadkeys = PlcTrigger.Keys.ToArray();
  1878. Thread[] ThreadValues = PlcTrigger.Values.ToArray();
  1879. for (int i = 0; i < PlcTrigger.Count; i++)
  1880. {
  1881. var procedure = prd.CameraProcedures.FirstOrDefault(p => p.ProcedureModels?.FirstOrDefault(f => f.Id == Threadkeys[i]) != null);
  1882. Guid guid = procedure.ProcedureModels.FirstOrDefault(f => f.Id == Threadkeys[i]).RobotId;
  1883. XYZ_Platform robot = RobotService.GetRobot(guid) as XYZ_Platform;
  1884. robot.IsTrigger = false;
  1885. ThreadValues[i].Abort();
  1886. }
  1887. PlcTrigger.Clear();
  1888. Guid[] keys = keyValuePairs.Keys.ToArray();
  1889. List<NodeId>[] values = keyValuePairs.Values.ToArray();
  1890. for (int i = 0; i < keyValuePairs?.Count; i++)
  1891. {
  1892. var procedure = prd.CameraProcedures.FirstOrDefault(p =>
  1893. p.ProcedureModels?.FirstOrDefault(f => f.Id == keys[i]) != null);
  1894. Guid guid = procedure.ProcedureModels.FirstOrDefault(f => f.Id == keys[i]).RobotId;
  1895. XYZ_Platform robot = RobotService.GetRobot(guid) as XYZ_Platform;
  1896. robot.IsTrigger = false;
  1897. if (keyValuePairsT.ContainsKey(keys[i]))
  1898. {
  1899. List<ProcedureOutputOpcAdr> ProcedureOutputOpcAdrs = new List<ProcedureOutputOpcAdr>();
  1900. var saas = keyValuePairsT[keys[i]];
  1901. for (int j = 0; j < keyValuePairsT[keys[i]]?.Count; j++)
  1902. {
  1903. ProcedureOutputOpcAdrs.Add(keyValuePairsT[keys[i]][j]);
  1904. }
  1905. robot.IsTrigger = true;
  1906. Thread thread = robot.CreateThreadToTrigger(values[i].ToArray(), ProcedureOutputOpcAdrs);
  1907. PlcTrigger.Add(keys[i], thread);
  1908. }
  1909. }
  1910. DatabaseHelper.UpdateProductDatabase(product);
  1911. SendTaskMessage(Lang.产品加载完成.Replace("{0}", CurrentProduct.Name), MessageLevel.Debug);
  1912. App.Current.Dispatcher.Invoke(() =>
  1913. {
  1914. _eventAggregator.GetEvent<ProductChangedNotification>().Publish(CurrentProduct);
  1915. _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = Lang.产品加载完成.Replace("{0}", CurrentProduct.Name), Duration = 1 });
  1916. });
  1917. }
  1918. catch (Exception ex)
  1919. {
  1920. SendTaskMessage(ex.Message, MessageLevel.Error);
  1921. LogHelper.WriteLogError($"加载产品[{prd.Name}]时出错!", ex);
  1922. }
  1923. }
  1924. /// <summary>
  1925. /// 保存产品
  1926. /// </summary>
  1927. /// <param name="product"></param>
  1928. public void SaveProducts(ProductModel product)
  1929. {
  1930. try
  1931. {
  1932. product.DateTime = DateTime.Now;
  1933. var prod = Products.FirstOrDefault(item => item.ID == product.ID);
  1934. if (prod != null)
  1935. {
  1936. prod = product;
  1937. }
  1938. else
  1939. {
  1940. product.IsCreate = false;
  1941. Products.Add(product);
  1942. }
  1943. Dictionary<Guid, string> list = new Dictionary<Guid, string>();
  1944. foreach (var item in Products)
  1945. {
  1946. list.Add(item.ID, item.Name);
  1947. }
  1948. FileHelper.WriteJsonFile(list, FilePath.ProductsPath + "//ProductList.cfg");
  1949. FileHelper.WriteJsonFile(product, FilePath.ProductsPath + "//" + product.Name + "//" + product.Name + ".cfg");
  1950. foreach (var item in product.CameraProcedures)
  1951. {
  1952. foreach (var item1 in item.ProcedureModels)
  1953. {
  1954. string prcPath = FilePath.ProductsPath + "//" + product.Name + "//" + item.Camera + "//" + item1.Name + ".vpp";
  1955. if (!Directory.Exists(Path.GetDirectoryName(prcPath)))
  1956. {
  1957. Directory.CreateDirectory(Path.GetDirectoryName(prcPath));
  1958. }
  1959. if (item1.ToolBlock != null)
  1960. {
  1961. CogSerializer.SaveObjectToFile(item1.ToolBlock, prcPath);
  1962. //item1.ToolBlock= CogSerializer.LoadObjectFromFile(FilePath.SimpleProcedurePath) as CogToolBlock;
  1963. }
  1964. else
  1965. {
  1966. if (File.Exists(prcPath))
  1967. {
  1968. item1.ToolBlock = CogSerializer.LoadObjectFromFile(prcPath) as CogToolBlock;
  1969. }
  1970. else
  1971. {
  1972. item1.ToolBlock = CogSerializer.LoadObjectFromFile(FilePath.SimpleProcedurePath) as CogToolBlock;
  1973. }
  1974. }
  1975. }
  1976. }
  1977. DatabaseHelper.UpdateProductDatabase(product);
  1978. _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = Lang.保存完成, Duration = 1 });
  1979. }
  1980. catch (Exception ex)
  1981. {
  1982. SendTaskMessage(ex.Message, MessageLevel.Error);
  1983. _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = ex.Message, Duration = 1 });
  1984. LogHelper.WriteLogError("保存产品时出错!", ex);
  1985. }
  1986. }
  1987. /// <summary>
  1988. /// 保存产品
  1989. /// </summary>
  1990. public void SaveProducts()
  1991. {
  1992. Dictionary<Guid, string> list = new Dictionary<Guid, string>();
  1993. foreach (var item in Products)
  1994. {
  1995. list.Add(item.ID, item.Name);
  1996. }
  1997. FileHelper.WriteJsonFile(list, FilePath.ProductsPath + "//ProductList.cfg");
  1998. _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = Lang.保存完成, Duration = 1 });
  1999. }
  2000. /// <summary>
  2001. /// 保存产品模板
  2002. /// </summary>
  2003. /// <param name="product"></param>
  2004. public void SaveProductTemplate(ProductModel product)
  2005. {
  2006. try
  2007. {
  2008. product.DateTime = DateTime.Now;
  2009. var prod = Products.FirstOrDefault(item => item.ID == product.ID);
  2010. if (prod != null)
  2011. {
  2012. prod = product;
  2013. }
  2014. else
  2015. {
  2016. product.IsCreate = false;
  2017. }
  2018. FileHelper.WriteJsonFile(product, FilePath.ProductTemplatePath + "//" + product.Name + "//" + product.Name + ".cfg");
  2019. foreach (var item in product.CameraProcedures)
  2020. {
  2021. foreach (var item1 in item.ProcedureModels)
  2022. {
  2023. string prcPath = FilePath.ProductTemplatePath + "//" + product.Name + "//" + item.Camera + "//" + item1.Name + ".vpp";
  2024. if (!Directory.Exists(Path.GetDirectoryName(prcPath)))
  2025. {
  2026. Directory.CreateDirectory(Path.GetDirectoryName(prcPath));
  2027. }
  2028. if (item1.ToolBlock != null)
  2029. {
  2030. CogSerializer.SaveObjectToFile(item1.ToolBlock, prcPath);
  2031. //item1.ToolBlock= CogSerializer.LoadObjectFromFile(FilePath.SimpleProcedurePath) as CogToolBlock;
  2032. }
  2033. else
  2034. {
  2035. if (File.Exists(prcPath))
  2036. {
  2037. item1.ToolBlock = CogSerializer.LoadObjectFromFile(prcPath) as CogToolBlock;
  2038. }
  2039. else
  2040. {
  2041. item1.ToolBlock = CogSerializer.LoadObjectFromFile(FilePath.SimpleProcedurePath) as CogToolBlock;
  2042. }
  2043. }
  2044. }
  2045. }
  2046. DatabaseHelper.UpdateProductDatabase(product);
  2047. _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = Lang.保存完成, Duration = 1 });
  2048. }
  2049. catch (Exception ex)
  2050. {
  2051. SendTaskMessage(ex.Message, MessageLevel.Error);
  2052. _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = ex.Message, Duration = 1 });
  2053. LogHelper.WriteLogError("保存产品模板时出错!", ex);
  2054. }
  2055. }
  2056. /// <summary>
  2057. /// 复制产品
  2058. /// </summary>
  2059. /// <param name="product"></param>
  2060. /// <param name="newName"></param>
  2061. public ProductModel CopyProduct(ProductModel product, string newName)
  2062. {
  2063. var p = product.Clone();
  2064. p.Name = newName;
  2065. p.ID = Guid.NewGuid();
  2066. p.DateTime = DateTime.Now;
  2067. for (int i = 0; i < p.CameraProcedures.Count; i++)
  2068. {
  2069. for (int j = 0; j < p.CameraProcedures[i].ProcedureModels.Count; j++)
  2070. {
  2071. string sorPath = FilePath.ProductsPath + "//" + ((ProductModel)product).Name + "//" + p.CameraProcedures[i].Camera + "//" + ((ProductModel)product).CameraProcedures[i].ProcedureModels[j].Name + ".vpp";
  2072. string procname = p.CameraProcedures[i].ProcedureModels[j].Name.Split('-')[2];
  2073. procname = $"{p.Name}-{p.CameraProcedures[i].Camera}-{procname}";
  2074. p.CameraProcedures[i].ProcedureModels[j].Name = procname;
  2075. string prcPath = FilePath.ProductsPath + "//" + p.Name + "//" + p.CameraProcedures[i].Camera + "//" + p.CameraProcedures[i].ProcedureModels[j].Name + ".vpp";
  2076. if (!Directory.Exists(Path.GetDirectoryName(prcPath)))
  2077. {
  2078. Directory.CreateDirectory(Path.GetDirectoryName(prcPath));
  2079. }
  2080. if (File.Exists(sorPath))
  2081. {
  2082. File.Copy(sorPath, prcPath, true);
  2083. if (p.CameraProcedures[i].ProcedureModels[j].ToolBlock == null)
  2084. {
  2085. p.CameraProcedures[i].ProcedureModels[j].ToolBlock = CogSerializer.LoadObjectFromFile(prcPath) as CogToolBlock;
  2086. }
  2087. }
  2088. }
  2089. }
  2090. Products.Add(p);
  2091. SaveProducts(p);
  2092. return p;
  2093. }
  2094. /// <summary>
  2095. /// 重新命名产品
  2096. /// </summary>
  2097. /// <param name="product"></param>
  2098. /// <param name="newName"></param>
  2099. public void RenameProduct(ProductModel product, string newName)
  2100. {
  2101. string oldname = product.Name;
  2102. //更改产品名称
  2103. product.Name = newName;
  2104. //遍历所有的相机流程,更改相机流程的名称
  2105. for (int i = 0; i < product.CameraProcedures.Count; i++)
  2106. {
  2107. for (int j = 0; j < product.CameraProcedures[i].ProcedureModels.Count; j++)
  2108. {
  2109. //旧路径
  2110. string sorPath = FilePath.ProductsPath + "//" + oldname + "//" + product.CameraProcedures[0].Camera + "//" + product.CameraProcedures[i].ProcedureModels[j].Name + ".vpp";
  2111. string procname = product.CameraProcedures[i].ProcedureModels[j].Name.Split('-')[2];
  2112. procname = $"{product.Name}-{product.CameraProcedures[i].Camera}-{procname}";
  2113. product.CameraProcedures[i].ProcedureModels[j].Name = procname;
  2114. //新路径
  2115. string prcPath = FilePath.ProductsPath + "//" + product.Name + "//" + product.CameraProcedures[0].Camera + "//" + product.CameraProcedures[i].ProcedureModels[j].Name + ".vpp";
  2116. if (!Directory.Exists(Path.GetDirectoryName(prcPath)))
  2117. {
  2118. Directory.CreateDirectory(Path.GetDirectoryName(prcPath));
  2119. }
  2120. if (File.Exists(sorPath))
  2121. {
  2122. File.Copy(sorPath, prcPath, true);
  2123. File.Delete(sorPath);
  2124. }
  2125. }
  2126. string prcPath1 = FilePath.ProductsPath + "//" + oldname + "//" + product.CameraProcedures[0].Camera;
  2127. if (Directory.Exists(prcPath1))
  2128. {
  2129. Directory.Delete(prcPath1);
  2130. }
  2131. }
  2132. //更改产品数据库
  2133. string src = FilePath.ProductsPath + "//" + oldname + "//" + oldname + ".db";
  2134. string dsc = FilePath.ProductsPath + "//" + product.Name + "//" + product.Name + ".db";
  2135. if (File.Exists(src))
  2136. {
  2137. File.Copy(src, dsc, true);
  2138. File.Delete(src);
  2139. }
  2140. //更改数据库中有关的产品名称
  2141. DatabaseHelper.RevampProductName(product, oldname);
  2142. SaveProducts(product);
  2143. //删除旧产品路径
  2144. string sorPath1 = FilePath.ProductsPath + "//" + oldname + "//" + oldname + ".cfg";
  2145. if (File.Exists(sorPath1))
  2146. {
  2147. File.Delete(sorPath1);
  2148. }
  2149. sorPath1 = FilePath.ProductsPath + "//" + oldname;
  2150. if (Directory.Exists(sorPath1))
  2151. {
  2152. Directory.Delete(sorPath1);
  2153. }
  2154. }
  2155. /// <summary>
  2156. /// 移除产品
  2157. /// </summary>
  2158. /// <param name="product"></param>
  2159. public void RemoveProduct(ProductModel product)
  2160. {
  2161. var prod = Products.FirstOrDefault(p => p.ID == product.ID);
  2162. if (prod != null)
  2163. {
  2164. Products.Remove(prod);
  2165. SaveProducts();
  2166. }
  2167. }
  2168. #endregion
  2169. #region 相机校准操作
  2170. /// <summary>
  2171. /// 加载所有校准
  2172. /// </summary>
  2173. public void LoadAllCalibration()
  2174. {
  2175. try
  2176. {
  2177. if (File.Exists(FilePath.CalibrationPath + "//CalibrationList.cfg"))
  2178. {
  2179. DeviceConfig.Calibrations = new ObservableCollection<CalibrationInfo>();
  2180. Dictionary<Guid, string> list = FileHelper.ReadJsonFile<Dictionary<Guid, string>>(FilePath.CalibrationPath + "//CalibrationList.cfg");
  2181. foreach (var item in list)
  2182. {
  2183. DeviceConfig.Calibrations.Add(FileHelper.ReadJsonFile<CalibrationInfo>(FilePath.CalibrationPath + "//" + item.Value + "//" + item.Value + ".cfg"));
  2184. }
  2185. SendTaskMessage(Lang.校准加载完成, MessageLevel.Debug);
  2186. }
  2187. else
  2188. {
  2189. SendTaskMessage(Lang.无校准, MessageLevel.Alarm);
  2190. }
  2191. }
  2192. catch (Exception ex)
  2193. {
  2194. DeviceConfig.Calibrations = new ObservableCollection<CalibrationInfo>();
  2195. LogHelper.WriteLogError("加载校准配置文件时出错!", ex);
  2196. }
  2197. }
  2198. /// <summary>
  2199. /// 保存校准
  2200. /// </summary>
  2201. /// <param name="calibration"></param>
  2202. public void SaveCalibration(CalibrationInfo calibration)
  2203. {
  2204. try
  2205. {
  2206. calibration.DateTime = DateTime.Now;
  2207. var calib = DeviceConfig.Calibrations.FirstOrDefault(item => item.Id == calibration.Id);
  2208. if (calib != null)
  2209. {
  2210. calib = calibration;
  2211. }
  2212. else
  2213. {
  2214. calibration.IsCreate = false;
  2215. DeviceConfig.Calibrations.Add(calibration);
  2216. }
  2217. Dictionary<Guid, string> list = new Dictionary<Guid, string>();
  2218. foreach (var item in DeviceConfig.Calibrations)
  2219. {
  2220. list.Add(item.Id, item.Name);
  2221. }
  2222. FileHelper.WriteJsonFile(list, FilePath.CalibrationPath + "//CalibrationList.cfg");
  2223. FileHelper.WriteJsonFile(calibration, FilePath.CalibrationPath + "//" + calibration.Name + "//" + calibration.Name + ".cfg");
  2224. if (calibration.ToolBlock != null)
  2225. {
  2226. string prcPath = FilePath.CalibrationPath + "//" + calibration.Name + "//" + calibration.Name + ".vpp";
  2227. if (!Directory.Exists(Path.GetDirectoryName(prcPath)))
  2228. {
  2229. Directory.CreateDirectory(Path.GetDirectoryName(prcPath));
  2230. }
  2231. CogSerializer.SaveObjectToFile(calibration.ToolBlock, prcPath);
  2232. }
  2233. _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = Lang.保存完成, Duration = 1 });
  2234. }
  2235. catch (Exception ex)
  2236. {
  2237. SendTaskMessage(ex.Message, MessageLevel.Error);
  2238. _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = ex.Message, Duration = 1 });
  2239. LogHelper.WriteLogError("保存校准时出错!", ex);
  2240. }
  2241. }
  2242. /// <summary>
  2243. /// 移除校准
  2244. /// </summary>
  2245. /// <param name=""></param>
  2246. public void RemoveCalibration(CalibrationInfo calibration)
  2247. {
  2248. var calib = DeviceConfig.Calibrations.FirstOrDefault(p => p.Id == calibration.Id);
  2249. if (calib != null)
  2250. {
  2251. DeviceConfig.Calibrations.Remove(calib);
  2252. Dictionary<Guid, string> list = new Dictionary<Guid, string>();
  2253. foreach (var item in DeviceConfig.Calibrations)
  2254. {
  2255. list.Add(item.Id, item.Name);
  2256. }
  2257. FileHelper.WriteJsonFile(list, FilePath.CalibrationPath + "//CalibrationList.cfg");
  2258. _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = Lang.保存完成, Duration = 1 });
  2259. }
  2260. }
  2261. #endregion
  2262. #region Feeder清料动作
  2263. /// <summary>
  2264. /// 加载Feeder清料动作
  2265. /// </summary>
  2266. public Dictionary<Guid, ObservableCollection<FeederClearWork>> LoadFeedersClearTask()
  2267. {
  2268. Dictionary<Guid, ObservableCollection<FeederClearWork>> FeedersClearWork = new Dictionary<Guid, ObservableCollection<FeederClearWork>>();
  2269. try
  2270. {
  2271. if (File.Exists(FilePath.FeedersClearTaskPath))
  2272. {
  2273. FeedersClearWork = FileHelper.ReadJsonFile<Dictionary<Guid, ObservableCollection<FeederClearWork>>>(FilePath.FeedersClearTaskPath);
  2274. }
  2275. return FeedersClearWork;
  2276. }
  2277. catch (Exception ex)
  2278. {
  2279. LogHelper.WriteLogError("加载Feeder清料动作配置文件时出错!", ex);
  2280. return FeedersClearWork;
  2281. }
  2282. }
  2283. /// <summary>
  2284. /// 保存Feeder清料动作
  2285. /// </summary>
  2286. public void SaveFeedersClearTask(Dictionary<Guid, ObservableCollection<FeederClearWork>> FeedersClearWork)
  2287. {
  2288. FileHelper.WriteJsonFile(FeedersClearWork, FilePath.FeedersClearTaskPath);
  2289. }
  2290. /// <summary>
  2291. /// 执行Feeder清料
  2292. /// </summary>
  2293. /// <param name="FeederId"></param>
  2294. /// <param name="feederClearWork"></param>
  2295. /// <returns></returns>
  2296. public async Task ExecuteFeederClear(Guid FeederId, FeederClearWork feederClearWork, CancellationToken cancellationToken)
  2297. {
  2298. IVoiceCoilMotorFeeder Feeder = FeederService.GetFeeder(FeederId);
  2299. if (!Feeder.IsConnected)
  2300. {
  2301. return;
  2302. }
  2303. for (int i = 0; i < feederClearWork.ClearCount; i++)
  2304. {
  2305. //1.Feeder振动之前,打开挡板
  2306. await Feeder.OutputAsync((int)feederClearWork.BeforeOutput + 1, 0);
  2307. await Task.Delay(200);
  2308. //2.执行配置的Feeder振动任务
  2309. for (int j = 0; j < feederClearWork.VibrationCount; j++)
  2310. {
  2311. await ExecuteFeederWorks(Feeder, feederClearWork.FeederWorks, true);
  2312. //如果料仓停止了,则打开料仓运动
  2313. if (!await Feeder.GetOutputStatusAsync())
  2314. {
  2315. await Feeder.OutputAsync((int)feederClearWork.BeforeOutput + 1, 0);
  2316. }
  2317. if (cancellationToken != null && cancellationToken.IsCancellationRequested)
  2318. {
  2319. await Feeder.StopOutputAsync();
  2320. return;
  2321. }
  2322. }
  2323. if (cancellationToken != null && cancellationToken.IsCancellationRequested)
  2324. {
  2325. await Feeder.StopOutputAsync();
  2326. return;
  2327. }
  2328. //3.Feeder振动之后,关闭挡板
  2329. await Feeder.StopOutputAsync();
  2330. await Task.Delay(300);
  2331. }
  2332. //3.Feeder振动之后,关闭挡板
  2333. await Feeder.StopOutputAsync();
  2334. await Task.Delay(300);
  2335. }
  2336. #endregion
  2337. #region 机器人指令执行
  2338. public Dictionary<Guid, CancellationTokenSource> RobotTCPCollection = new Dictionary<Guid, CancellationTokenSource>();
  2339. /// <summary>
  2340. /// 机器人TCP接收
  2341. /// </summary>
  2342. /// <param name="arg1"></param>
  2343. /// <param name="arg2"></param>
  2344. /// <param name="arg3"></param>
  2345. private async void Management_ReceivedEvent(Guid robotid, object arg2, TouchSocket.Sockets.ReceivedDataEventArgs e)
  2346. {
  2347. try
  2348. {
  2349. if (!RobotTCPCollection.ContainsKey(robotid))
  2350. RobotTCPCollection.Add(robotid, new CancellationTokenSource());
  2351. else
  2352. {
  2353. if (RobotTCPCollection[robotid].IsCancellationRequested)
  2354. {
  2355. RobotTCPCollection[robotid] = new CancellationTokenSource();
  2356. }
  2357. }
  2358. var robot = await RobotService.GetRobotAsync(robotid);
  2359. var r = DeviceConfig.Robots.FirstOrDefault(ro => ro.Id == robotid);
  2360. //从服务器收到信息。但是一般byteBlock和requestInfo会根据适配器呈现不同的值。
  2361. var buffer = e.ByteBlock.Span.ToString(robot.GetEncoding());
  2362. SendTaskMessage($"{r.RobotName}Received:{buffer}", MessageLevel.Info);
  2363. var _ = Task.Run(async () =>
  2364. {
  2365. try
  2366. {
  2367. await ExecuteRemoteCommand(buffer, async result =>
  2368. {
  2369. if (result.Item1)
  2370. {
  2371. try
  2372. {
  2373. if (result.Item2.Length > 1)
  2374. {
  2375. foreach (var item in result.Item2)
  2376. {
  2377. if (robot.ConnectType == TCPConnectType.Client)
  2378. {
  2379. if (robot.IsConnected)
  2380. {
  2381. await robot.SendAsync($"{item}");
  2382. }
  2383. }
  2384. else
  2385. {
  2386. var cient = (ITcpSessionClient)arg2;
  2387. if (cient.Online)
  2388. {
  2389. await robot.SendAsync(cient, $"{item}");
  2390. }
  2391. }
  2392. await Task.Delay(10);
  2393. }
  2394. }
  2395. else
  2396. {
  2397. if (robot.ConnectType == TCPConnectType.Client)
  2398. {
  2399. if (robot.IsConnected)
  2400. {
  2401. await robot.SendAsync($"{result.Item2[0]}");
  2402. }
  2403. }
  2404. else
  2405. {
  2406. var cient = (ITcpSessionClient)arg2;
  2407. if (cient.Online)
  2408. {
  2409. await robot.SendAsync(cient, $"{result.Item2[0]}");
  2410. }
  2411. }
  2412. }
  2413. }
  2414. catch (Exception ex)
  2415. {
  2416. LogHelper.WriteLogError("TCP发送数据时出错!", ex);
  2417. SendTaskMessage(ex.Message, MessageLevel.Alarm);
  2418. }
  2419. }
  2420. }, RobotTCPCollection[robotid].Token);
  2421. }
  2422. catch (Exception ex)
  2423. {
  2424. LogHelper.WriteLogError("处理机器人接收的命令时出错!", ex);
  2425. SendTaskMessage(ex.Message, MessageLevel.Alarm);
  2426. }
  2427. });
  2428. }
  2429. catch (Exception ex)
  2430. {
  2431. LogHelper.WriteLogError("处理机器人接收的命令时出错!", ex);
  2432. SendTaskMessage(ex.Message, MessageLevel.Alarm);
  2433. }
  2434. }
  2435. /// <summary>
  2436. /// 机器人XY模组执行命令
  2437. /// </summary>
  2438. /// <param name="arg1"></param>
  2439. /// <param name="arg2"></param>
  2440. /// <exception cref="NotImplementedException"></exception>
  2441. private async void Management_TrggerChangeEvent(Guid arg1, object arg2, List<ProcedureOutputOpcAdr> arg3)
  2442. {
  2443. if (!RobotTCPCollection.ContainsKey(arg1))
  2444. RobotTCPCollection.Add(arg1, new CancellationTokenSource());
  2445. else
  2446. {
  2447. if (RobotTCPCollection[arg1].IsCancellationRequested)
  2448. {
  2449. RobotTCPCollection[arg1] = new CancellationTokenSource();
  2450. }
  2451. }
  2452. var robot = await RobotService.GetRobotAsync(arg1) as XYZ_Platform;
  2453. var r = DeviceConfig.Robots.FirstOrDefault(ro => ro.Id == arg1);
  2454. SendTaskMessage($"{r.RobotName}Received:{arg2}", MessageLevel.Info);
  2455. var _ = Task.Run(async () =>
  2456. {
  2457. try
  2458. {
  2459. await ExecuteRemoteCommand(arg2.ToString(), async result =>
  2460. {
  2461. if (result.Item1)
  2462. {
  2463. try
  2464. {
  2465. string[] adrs = new string[arg3.Count];
  2466. object[] values = new object[arg3.Count];
  2467. values[0] = false;
  2468. values[1] = false;
  2469. string[] results = result.Item2[0].Split(',');
  2470. for (int i = 0; i < arg3.Count; i++)
  2471. {
  2472. if (i == 0)
  2473. {
  2474. if (results[1] == "OK")
  2475. {
  2476. adrs[i] = arg3[i].Name;
  2477. values[i] = true;
  2478. adrs[i + 1] = arg3[i + 1].Name;
  2479. values[i + 1] = false;
  2480. }
  2481. else
  2482. {
  2483. adrs[i] = arg3[i].Name;
  2484. values[i] = false;
  2485. adrs[i + 1] = arg3[i + 1].Name;
  2486. values[i + 1] = true;
  2487. }
  2488. i += 1;
  2489. continue;
  2490. }
  2491. if (i < arg3.Count - 1)
  2492. {
  2493. adrs[i] = arg3[i].Name;
  2494. if (i < results.Length)
  2495. {
  2496. float res = 0f;
  2497. if (float.TryParse(results[i], out res))
  2498. {
  2499. values[i] = results[i];
  2500. }
  2501. else
  2502. {
  2503. values[i] = res;
  2504. }
  2505. }
  2506. else
  2507. {
  2508. values[i] = 0f;
  2509. }
  2510. }
  2511. }
  2512. adrs[adrs.Length - 1] = arg3[arg3.Count - 1].Name;
  2513. values[values.Length - 1] = true;
  2514. robot.Plc.OpcUaClient.WriteNodes(adrs, values);
  2515. await Task.Delay(10);
  2516. }
  2517. catch (Exception ex)
  2518. {
  2519. LogHelper.WriteLogError("回复plc发送数据时出错!", ex);
  2520. SendTaskMessage(ex.Message, MessageLevel.Alarm);
  2521. }
  2522. }
  2523. }, RobotTCPCollection[arg1].Token);
  2524. }
  2525. catch (Exception ex)
  2526. {
  2527. LogHelper.WriteLogError("处理XYZ机器人接收的命令时出错!", ex);
  2528. SendTaskMessage(ex.Message, MessageLevel.Alarm);
  2529. }
  2530. });
  2531. }
  2532. #region 处理命令
  2533. /// <summary>
  2534. /// 执行命令
  2535. /// </summary>
  2536. /// <param name="command"></param>
  2537. public async Task ExecuteRemoteCommand(string command, Action<Tuple<bool, string[]>> callback, CancellationToken cancellationToken)
  2538. {
  2539. string[] commandItem = command.Split(',');
  2540. if (CurrentProduct == null)
  2541. {
  2542. if (commandItem[0] == "GetProductName")
  2543. {
  2544. callback.Invoke(new Tuple<bool, string[]>(true, new string[] { $"{commandItem[0]},null" }));
  2545. return;
  2546. }
  2547. }
  2548. ProcedureModel procedure = null;
  2549. foreach (var cameraProcedures in CurrentProduct.CameraProcedures)
  2550. {
  2551. procedure = cameraProcedures.ProcedureModels.FirstOrDefault(p => p.TriggerCommand == commandItem[0]);
  2552. if (procedure != null) break;
  2553. }
  2554. if (procedure != null)
  2555. {
  2556. //相机拍照
  2557. await RunProcedure(procedure, commandItem, rst =>
  2558. {
  2559. if (rst.Item1)
  2560. {
  2561. if (rst.Item2.Length > 1)
  2562. {
  2563. List<string> buffers = new List<string>();
  2564. foreach (var item in rst.Item2)
  2565. {
  2566. buffers.Add($"{commandItem[0]},{item}");
  2567. }
  2568. callback.Invoke(new Tuple<bool, string[]>(rst.Item1, buffers.ToArray()));
  2569. }
  2570. else
  2571. {
  2572. callback.Invoke(new Tuple<bool, string[]>(rst.Item1, new string[] { $"{commandItem[0]},{rst.Item2[0]}" }));
  2573. }
  2574. }
  2575. else
  2576. {
  2577. callback.Invoke(rst);
  2578. }
  2579. }, cancellationToken);
  2580. return;
  2581. }
  2582. switch (commandItem[0])
  2583. {
  2584. case "LoadProduct":
  2585. await LoadProduct(commandItem[1], rst =>
  2586. {
  2587. callback.Invoke(new Tuple<bool, string[]>(rst.Item1, new string[] { $"{commandItem[0]},{rst.Item2}" }));
  2588. });
  2589. break;
  2590. case "GetProductName":
  2591. await GetCurrentProductName(rst =>
  2592. {
  2593. callback.Invoke(new Tuple<bool, string[]>(rst.Item1, new string[] { $"{commandItem[0]},{rst.Item2}" }));
  2594. });
  2595. break;
  2596. case "GetProductID":
  2597. await GetCurrentProductID(rst =>
  2598. {
  2599. callback.Invoke(new Tuple<bool, string[]>(rst.Item1, new string[] { $"{commandItem[0]},{rst.Item2}" }));
  2600. });
  2601. break;
  2602. case "GetSpeed":
  2603. await GetCurrentProductSpeed(rst =>
  2604. {
  2605. callback.Invoke(new Tuple<bool, string[]>(rst.Item1, new string[] { $"{commandItem[0]},{rst.Item2}" }));
  2606. });
  2607. break;
  2608. case "GetAllRobotPoints":
  2609. await GetAllRobotPoints(rst =>
  2610. {
  2611. callback.Invoke(new Tuple<bool, string[]>(rst.Item1, new string[] { $"{commandItem[0]},{rst.Item2}" }));
  2612. });
  2613. break;
  2614. case "GetRobotPointsCount":
  2615. await GetRobotPointsCount(rst =>
  2616. {
  2617. callback.Invoke(new Tuple<bool, string[]>(rst.Item1, new string[] { $"{commandItem[0]},{rst.Item2}" }));
  2618. });
  2619. break;
  2620. case "GetRobotPoint":
  2621. await GetRobotPoint(int.Parse(commandItem[1]), rst =>
  2622. {
  2623. callback.Invoke(new Tuple<bool, string[]>(rst.Item1, new string[] { $"{commandItem[0]},{rst.Item2}" }));
  2624. });
  2625. break;
  2626. case "GetPalletsCount":
  2627. await GetPalletsCount(rst =>
  2628. {
  2629. callback.Invoke(new Tuple<bool, string[]>(rst.Item1, new string[] { $"{commandItem[0]},{rst.Item2}" }));
  2630. });
  2631. break;
  2632. case "GetAllPallets":
  2633. await GetAllPallet(rst =>
  2634. {
  2635. callback.Invoke(new Tuple<bool, string[]>(rst.Item1, new string[] { $"{commandItem[0]},{rst.Item2}" }));
  2636. });
  2637. break;
  2638. case "GetPallet":
  2639. await GetPallet(commandItem[1], rst =>
  2640. {
  2641. callback.Invoke(new Tuple<bool, string[]>(rst.Item1, new string[] { $"{commandItem[0]},{rst.Item2}" }));
  2642. });
  2643. break;
  2644. case "GetPalletPoint":
  2645. await GetPalletPoint(commandItem[1], int.Parse(commandItem[2]), rst =>
  2646. {
  2647. callback.Invoke(new Tuple<bool, string[]>(rst.Item1, new string[] { $"{commandItem[0]},{rst.Item2}" }));
  2648. });
  2649. break;
  2650. case "GetToolCount":
  2651. await GetToolCount(commandItem[1], rst =>
  2652. {
  2653. callback.Invoke(new Tuple<bool, string[]>(rst.Item1, new string[] { $"{commandItem[0]},{rst.Item2}" }));
  2654. });
  2655. break;
  2656. case "GetTool":
  2657. await GetTool(commandItem[1], int.Parse(commandItem[2]), rst =>
  2658. {
  2659. callback.Invoke(new Tuple<bool, string[]>(rst.Item1, new string[] { $"{commandItem[0]},{rst.Item2}" }));
  2660. });
  2661. break;
  2662. case "GetProcedureParam":
  2663. await GetProcedureParam(commandItem[1], rst =>
  2664. {
  2665. callback.Invoke(new Tuple<bool, string[]>(rst.Item1, new string[] { $"{commandItem[0]},{rst.Item2}" }));
  2666. });
  2667. break;
  2668. case "CycleTime":
  2669. ExecuteCycleTime(commandItem[1], rst =>
  2670. {
  2671. callback.Invoke(new Tuple<bool, string[]>(rst.Item1, new string[] { $"{commandItem[0]},{rst.Item2}" }));
  2672. });
  2673. break;
  2674. case "Record":
  2675. AddProductionRecords(commandItem[1], rst =>
  2676. {
  2677. callback.Invoke(new Tuple<bool, string[]>(rst.Item1, new string[] { $"{commandItem[0]},{rst.Item2}" }));
  2678. });
  2679. break;
  2680. case "Err":
  2681. SendTaskMessage(commandItem[1], MessageLevel.Error);
  2682. DatabaseHelper.AppendAlarm(commandItem[1]);
  2683. break;
  2684. case "Info":
  2685. SendTaskMessage(commandItem[1], MessageLevel.Info);
  2686. break;
  2687. case "GetGlobalParam":
  2688. if (commandItem.Length == 2)
  2689. {
  2690. GetGlobalParam(commandItem[1], -1, rst =>
  2691. {
  2692. callback.Invoke(new Tuple<bool, string[]>(rst.Item1, new string[] { $"{commandItem[0]},{rst.Item2}" }));
  2693. });
  2694. }
  2695. else if (commandItem.Length == 3)
  2696. {
  2697. GetGlobalParam(commandItem[1], int.Parse(commandItem[2]), rst =>
  2698. {
  2699. callback.Invoke(new Tuple<bool, string[]>(rst.Item1, new string[] { $"{commandItem[0]},{rst.Item2}" }));
  2700. });
  2701. }
  2702. break;
  2703. }
  2704. }
  2705. /// <summary>
  2706. /// 运行流程
  2707. /// </summary>
  2708. /// <param name="procedure">流程对象</param>
  2709. /// <param name="items">触发命令</param>
  2710. /// <param name="callback">回调函数</param>
  2711. /// <param name="cancellationToken">取消任务</param>
  2712. /// <returns></returns>
  2713. public async Task RunProcedure(ProcedureModel procedure, string[] items, Action<Tuple<bool, string[]>> callback, CancellationToken cancellationToken)
  2714. {
  2715. string path = FilePath.ProductsPath + "//" + CurrentProduct.Name + "//" + procedure.CameraName + "//" + procedure.Name + ".cs";
  2716. string script = "";
  2717. if (File.Exists(path))
  2718. {
  2719. try
  2720. {
  2721. script = FileHelper.ReadFile(path);
  2722. SendTaskMessage(Lang.执行视觉处理脚本, MessageLevel.Debug);
  2723. var rst1 = await ScriptHelper.ExecuteVisionScriptingAsync(script, _regionManager, _eventAggregator, _container, procedure, items, rst =>
  2724. {
  2725. callback.Invoke(rst);
  2726. });
  2727. if (!rst1.Item1)
  2728. {
  2729. SendTaskMessage(Lang.视觉处理脚本出错, MessageLevel.Error);
  2730. callback.Invoke(new Tuple<bool, string[]>(false, new string[] { "ScriptError" }));
  2731. foreach (var item in rst1.Item2)
  2732. {
  2733. SendTaskMessage(item, MessageLevel.Error);
  2734. }
  2735. }
  2736. }
  2737. catch (Exception ex)
  2738. {
  2739. SendTaskMessage(ex.Message, MessageLevel.Error);
  2740. LogHelper.WriteLogError("执行视觉处理脚本时出错!", ex);
  2741. callback.Invoke(new Tuple<bool, string[]>(false, new string[] { "ScriptError" }));
  2742. }
  2743. }
  2744. else
  2745. {
  2746. //正常执行模块,每次拍照,会返回所有的结果数据
  2747. IVoiceCoilMotorFeeder Feeder = null;
  2748. if (procedure.FeederId != Guid.Empty)
  2749. {
  2750. Feeder = this.FeederService.GetFeeder(procedure.FeederId);
  2751. await RunProcedureFeeder(Feeder, procedure, items, rst =>
  2752. {
  2753. callback.Invoke(rst);
  2754. }, cancellationToken);
  2755. }
  2756. else
  2757. {
  2758. RunProcedureOther(procedure, items, rst =>
  2759. {
  2760. callback.Invoke(rst);
  2761. }, cancellationToken);
  2762. }
  2763. }
  2764. }
  2765. /// <summary>
  2766. /// Feeder当前Blob的面积,产品-流程-面积
  2767. /// </summary>
  2768. public Dictionary<Guid, Dictionary<Guid, double>> feederCurentBlobArea = new Dictionary<Guid, Dictionary<Guid, double>>();
  2769. /// <summary>
  2770. /// 最后一次feeder拍照出来的点位,流程-点数据
  2771. /// </summary>
  2772. public Dictionary<Guid, List<Vector<double>>> LastPointsForFeeder = new Dictionary<Guid, List<Vector<double>>>();
  2773. /// <summary>
  2774. /// 每次拍照得到的点位数据集合,每个流程中包含多个结果输出项,每个结果项中包含多个点位,流程-输出集合-点位队列
  2775. /// </summary>
  2776. public Dictionary<Guid, Dictionary<string, Queue<string>>> PointCollection = new Dictionary<Guid, Dictionary<string, Queue<string>>>();
  2777. /// <summary>
  2778. /// Feeder相机引导模式
  2779. /// </summary>
  2780. /// <param name="Feeder">feeder</param>
  2781. /// <param name="procedure">流程对象</param>
  2782. /// <param name="items">触发命令</param>
  2783. /// <param name="callback">回调函数</param>
  2784. /// <returns></returns>
  2785. public async Task RunProcedureFeeder(IVoiceCoilMotorFeeder Feeder, ProcedureModel procedure, string[] items, Action<Tuple<bool, string[]>> callback, CancellationToken cancellationToken)
  2786. {
  2787. //是否检测上次的点位,相同点位排除
  2788. bool IsCheckLastPoints = false;
  2789. bool IsSingleResultModel = false;
  2790. int InputVibration = 0;
  2791. int GetPosCount = 1;
  2792. Dictionary<string, string> InputTerminal = null; //触发命令传入的输入终端
  2793. if (items.Length >= 2 && string.Equals(items[1], "GetPos"))
  2794. {
  2795. //Camera1,GetPos,PosCount,CheckLastPoints
  2796. if (items.Length >= 4 && string.Equals(items[3], "CheckLastPoints"))
  2797. {
  2798. IsCheckLastPoints = true;
  2799. GetPosCount = int.Parse(items[2]);
  2800. }
  2801. ////Camera1,GetPos,CheckLastPoints
  2802. else if (items.Length >= 3 && string.Equals(items[2], "CheckLastPoints"))
  2803. {
  2804. IsCheckLastPoints = true;
  2805. GetPosCount = 1;
  2806. }
  2807. else if (items.Length >= 3 && int.TryParse(items[2], out GetPosCount))
  2808. {
  2809. }
  2810. else
  2811. {
  2812. GetPosCount = 1;
  2813. }
  2814. if (GetPosCount <= 0)
  2815. {
  2816. GetPosCount = 1;
  2817. }
  2818. //进入一次拍照后,,点位数据保存在软件里面,机器人每次跟软件获取一个或者两个点位
  2819. ////获取点位数据
  2820. if (!PointCollection.ContainsKey(procedure.Id))
  2821. {
  2822. PointCollection.Add(procedure.Id, new Dictionary<string, Queue<string>>());
  2823. }
  2824. IsSingleResultModel = true;
  2825. }
  2826. else if (items.Length >= 2 && string.Equals(items[1], "ClearPos"))
  2827. {
  2828. //进入一次拍照后,,点位数据保存在软件里面,机器人每次跟软件获取一个或者两个点位
  2829. ////清除所有点位数据
  2830. if (!PointCollection.ContainsKey(procedure.Id))
  2831. {
  2832. PointCollection.Add(procedure.Id, new Dictionary<string, Queue<string>>());
  2833. }
  2834. PointCollection[procedure.Id].Clear();
  2835. IsSingleResultModel = true;
  2836. callback.Invoke(new Tuple<bool, string[]>(true, new string[] { "ClearPos" }));
  2837. return;
  2838. }
  2839. else if (items.Length >= 2 && string.Equals(items[1], "CheckLastPoints"))
  2840. {
  2841. IsCheckLastPoints = true;
  2842. }
  2843. else if (items.Length >= 2 && string.Equals(items[1], "Vibration"))
  2844. {
  2845. //先振后拍
  2846. InputVibration = 1;
  2847. }
  2848. else if (items.Length >= 2 && string.Equals(items[1], "NoVibration"))
  2849. {
  2850. //先拍再决定是否要不要振
  2851. InputVibration = 2;
  2852. }
  2853. else if (items.Length >= 2 && string.Equals(items[1], "SetVisionToolInputs"))
  2854. {
  2855. InputTerminal = new Dictionary<string, string>();
  2856. //触发命令传入的输入终端
  2857. for (int i = 2; i < items.Length; i += 2)
  2858. {
  2859. if (!string.IsNullOrEmpty(items[i]) && items.Length >= i + 2)
  2860. {
  2861. InputTerminal.Add(items[i].Trim(), items[i + 1].Trim());
  2862. }
  2863. }
  2864. if (InputTerminal.Count <= 0)
  2865. {
  2866. InputTerminal = null;
  2867. }
  2868. }
  2869. //如果还没有结果项目,则触发拍照,获取结果
  2870. if (IsSingleResultModel && PointCollection[procedure.Id].Count > 0)
  2871. {
  2872. //遍历所有的结果项
  2873. foreach (var item in PointCollection[procedure.Id])
  2874. {
  2875. //如果结果的数量大于等于可获取的点位数量,则直接返回点位数据
  2876. if (item.Value != null && item.Value.Count >= GetPosCount)
  2877. {
  2878. List<string> poslist = new List<string>();
  2879. for (int i = 0; i < GetPosCount; i++)
  2880. {
  2881. poslist.Add(item.Value.Dequeue());
  2882. }
  2883. callback.Invoke(new Tuple<bool, string[]>(true, new string[] { $"OK,{item.Key},{string.Join(",", poslist)}" }));
  2884. SendTaskMessage($"{item.Key}{Lang.剩余点位数量}:{item.Value.Count}", MessageLevel.Debug);
  2885. return;
  2886. }
  2887. }
  2888. }
  2889. if (IsSingleResultModel)
  2890. {
  2891. PointCollection[procedure.Id].Clear();
  2892. }
  2893. await ExecuteFeederForPhotoBefore(Feeder, procedure, InputVibration, cancellationToken);
  2894. if (cancellationToken != null && cancellationToken.IsCancellationRequested)
  2895. {
  2896. await Feeder.CloseLightAsync();
  2897. return;
  2898. }
  2899. DateTime nowtime = DateTime.Now;
  2900. //多次拍照
  2901. for (int i = 0; i < procedure.FeederFailLimit; i++)
  2902. {
  2903. if (cancellationToken != null && cancellationToken.IsCancellationRequested)
  2904. {
  2905. await Feeder.CloseLightAsync();
  2906. return;
  2907. }
  2908. SendTaskMessage(Lang.开始执行视觉流程.Replace("{0}", $"{i + 1}").Replace("{1}", procedure.Name), MessageLevel.Info);
  2909. string photoresult = ExecutePhoto(procedure, InputTerminal, out CogToolBlockTerminalCollection outputCollection);
  2910. if (!string.IsNullOrEmpty(photoresult))
  2911. {
  2912. //多次拍照,如果拍照次数到达上限,直接返回结果
  2913. if (i == procedure.FeederFailLimit - 1)
  2914. {
  2915. callback.Invoke(new Tuple<bool, string[]>(true, new string[] { $"NG,{photoresult}" }));
  2916. return;
  2917. }
  2918. else
  2919. {
  2920. continue;
  2921. }
  2922. }
  2923. var BlobTotalArea = (double)outputCollection["BlobTotalArea"].Value;
  2924. //记录当前的blob面积
  2925. Dictionary<Guid, double> tuple;
  2926. if (feederCurentBlobArea.TryGetValue(CurrentProduct.ID, out tuple))
  2927. {
  2928. //如流程当前已经存在此流程的记录
  2929. if (tuple.ContainsKey(procedure.Id))
  2930. {
  2931. tuple[procedure.Id] = BlobTotalArea;
  2932. }
  2933. else
  2934. {
  2935. tuple.Add(procedure.Id, BlobTotalArea);
  2936. }
  2937. }
  2938. else
  2939. {
  2940. feederCurentBlobArea.Add(CurrentProduct.ID, new Dictionary<Guid, double>());
  2941. feederCurentBlobArea[CurrentProduct.ID].Add(procedure.Id, BlobTotalArea);
  2942. }
  2943. if (!outputCollection.Contains("Found"))
  2944. {
  2945. callback.Invoke(new Tuple<bool, string[]>(true, new string[] { "NG,NoOut[Found]" }));
  2946. return;
  2947. }
  2948. bool Found = (bool)(outputCollection["Found"].Value);
  2949. //拍照OK
  2950. if (Found)
  2951. {
  2952. SendTaskMessage(Lang.拍照OK, MessageLevel.Debug);
  2953. if (procedure.ProcedureOutputs == null)
  2954. {
  2955. SendTaskMessage(Lang.视觉流程未配置输出项, MessageLevel.Alarm);
  2956. callback.Invoke(new Tuple<bool, string[]>(true, new string[] { "NG,NotOutputs" }));
  2957. return;
  2958. }
  2959. var ResultCollection = GetVisionOutput(procedure, outputCollection, items);
  2960. if (!ResultCollection.Item1)
  2961. {
  2962. callback.Invoke(new Tuple<bool, string[]>(true, new string[] { ResultCollection.Item2 }));
  2963. return;
  2964. }
  2965. List<string> SendResults = new List<string>();
  2966. //将本次点位结果记录至最后一次
  2967. if (!LastPointsForFeeder.ContainsKey(Feeder.Id))
  2968. {
  2969. LastPointsForFeeder.Add(Feeder.Id, new List<Vector<double>>());
  2970. }
  2971. int PosNum = 0;
  2972. bool ishavepoint = false;
  2973. List<Vector<double>> pointBuffer = new List<Vector<double>>();
  2974. foreach (var result in ResultCollection.Item3)
  2975. {
  2976. //是点位数据时
  2977. if (result.Value.Item1)
  2978. {
  2979. ishavepoint = true;
  2980. //值不为空时
  2981. if (result.Value.Item2 != null)
  2982. {
  2983. List<string> points = new List<string>();
  2984. foreach (var pos in (List<Vector<double>>)(result.Value.Item2))
  2985. {
  2986. pointBuffer.Add(pos);
  2987. //判断是否有重复的点位,防止机器人重复取同一个物料,陷入死循环
  2988. if (IsCheckLastPoints)
  2989. {
  2990. if (LastPointsForFeeder.ContainsKey(Feeder.Id))
  2991. {
  2992. var repeated = LastPointsForFeeder[Feeder.Id].FirstOrDefault(p => Math.Abs(p[0] - pos[0]) < 1 && Math.Abs(p[1] - pos[1]) < 1);
  2993. if (repeated != null) continue;
  2994. }
  2995. }
  2996. if (IsSingleResultModel)
  2997. {
  2998. PosNum++;
  2999. //如果当前是软件点位队列存储模式,则,将所有数据存储在队列中
  3000. if (!PointCollection[procedure.Id].ContainsKey(result.Key))
  3001. {
  3002. PointCollection[procedure.Id].Add(result.Key, new Queue<string>());
  3003. }
  3004. PointCollection[procedure.Id][result.Key].Enqueue($"{pos[0]:F3},{pos[1]:F3},{pos[2]:F3}");
  3005. }
  3006. else
  3007. {
  3008. //根据当前点位数量决定是否继续输出点位。
  3009. PosNum++;
  3010. if (PosNum >= procedure.OutputPointCountMax)
  3011. {
  3012. points.Add($"{pos[0]:F3},{pos[1]:F3},{pos[2]:F3}");
  3013. break;
  3014. }
  3015. else
  3016. {
  3017. points.Add($"{pos[0]:F3},{pos[1]:F3},{pos[2]:F3}");
  3018. }
  3019. }
  3020. }
  3021. if (IsSingleResultModel)
  3022. {
  3023. //如果当前是软件点位队列存储模式,则,将所有数据存储在队列中
  3024. if (!PointCollection[procedure.Id].ContainsKey(result.Key))
  3025. {
  3026. PointCollection[procedure.Id].Add(result.Key, new Queue<string>());
  3027. }
  3028. SendTaskMessage($"{result.Key}{Lang.可用的点位个数}:{PointCollection[procedure.Id][result.Key].Count}", MessageLevel.Debug);
  3029. }
  3030. else
  3031. {
  3032. if (points.Count == 0)
  3033. {
  3034. SendResults.Add($"0");
  3035. break;
  3036. }
  3037. else
  3038. {
  3039. SendResults.Add($"{points.Count},{string.Join(",", points)}");
  3040. }
  3041. }
  3042. }
  3043. else
  3044. {
  3045. SendResults.Add($"0");
  3046. }
  3047. }
  3048. //其他输出数据时
  3049. else
  3050. {
  3051. if (result.Value.Item2 != null)
  3052. {
  3053. SendResults.Add((string)result.Value.Item2);
  3054. }
  3055. else
  3056. {
  3057. SendResults.Add($"null");
  3058. }
  3059. }
  3060. }
  3061. //将本次点位结果记录至最后一次
  3062. LastPointsForFeeder[Feeder.Id] = pointBuffer;
  3063. if (ishavepoint && PosNum <= 0)
  3064. {
  3065. //存在点位输出项,但是点位数量为0
  3066. await ExecuteFeederForPhotoLater(Feeder, procedure, BlobTotalArea, cancellationToken);
  3067. if (cancellationToken != null && cancellationToken.IsCancellationRequested)
  3068. {
  3069. await Feeder.CloseLightAsync();
  3070. return;
  3071. }
  3072. continue;
  3073. }
  3074. if (Feeder != null)
  3075. {
  3076. await Feeder.CloseLightAsync();
  3077. }
  3078. if (IsSingleResultModel)
  3079. {
  3080. //遍历所有的结果项
  3081. foreach (var item in PointCollection[procedure.Id])
  3082. {
  3083. //如果结果的数量大于等于可获取的点位数量,则直接返回点位数据
  3084. if (item.Value != null && item.Value.Count >= GetPosCount)
  3085. {
  3086. List<string> poslist = new List<string>();
  3087. for (int j = 0; j < GetPosCount; j++)
  3088. {
  3089. poslist.Add(item.Value.Dequeue());
  3090. }
  3091. callback.Invoke(new Tuple<bool, string[]>(true, new string[] { $"OK,{item.Key},{string.Join(",", poslist)}" }));
  3092. SendTaskMessage($"{item.Key}{Lang.剩余点位数量}:{item.Value.Count}", MessageLevel.Debug);
  3093. return;
  3094. }
  3095. }
  3096. }
  3097. else
  3098. {
  3099. callback.Invoke(new Tuple<bool, string[]>(true, new string[] { $"OK,{string.Join(";", SendResults)}" }));
  3100. }
  3101. return;
  3102. }
  3103. //拍照NG
  3104. else
  3105. {
  3106. SendTaskMessage(Lang.拍照NG, MessageLevel.Error);
  3107. await ExecuteFeederForPhotoLater(Feeder, procedure, BlobTotalArea, cancellationToken);
  3108. continue;
  3109. }
  3110. }
  3111. SendTaskMessage(Lang.多次拍照失败, MessageLevel.Alarm);
  3112. callback.Invoke(new Tuple<bool, string[]>(true, new string[] { "NG,NoFind" }));
  3113. }
  3114. /// <summary>
  3115. /// 相机拍照,其他模式
  3116. /// </summary>
  3117. /// <param name="procedure"></param>
  3118. /// <param name="items"></param>
  3119. /// <param name="callback"></param>
  3120. public void RunProcedureOther(ProcedureModel procedure, string[] items, Action<Tuple<bool, string[]>> callback, CancellationToken cancellationToken)
  3121. {
  3122. DateTime nowtime = DateTime.Now;
  3123. Dictionary<string, string> InputTerminal = null; //触发命令传入的输入终端
  3124. if (items.Length >= 2 && string.Equals(items[1], "SetVisionToolInputs"))
  3125. {
  3126. InputTerminal = new Dictionary<string, string>();
  3127. //触发命令传入的输入终端
  3128. for (int i = 2; i < items.Length; i += 2)
  3129. {
  3130. if (!string.IsNullOrEmpty(items[i]) && items.Length >= i + 2)
  3131. {
  3132. InputTerminal.Add(items[i].Trim(), items[i + 1].Trim());
  3133. }
  3134. }
  3135. if (InputTerminal.Count <= 0)
  3136. {
  3137. InputTerminal = null;
  3138. }
  3139. }
  3140. for (int i = 0; i < procedure.FeederFailLimit; i++)
  3141. {
  3142. if (cancellationToken != null && cancellationToken.IsCancellationRequested)
  3143. {
  3144. return;
  3145. }
  3146. SendTaskMessage(Lang.开始执行视觉流程.Replace("{0}", $"{i + 1}").Replace("{1}", procedure.Name), MessageLevel.Info);
  3147. string photoresult = ExecutePhoto(procedure, InputTerminal, out CogToolBlockTerminalCollection outputCollection);
  3148. if (!string.IsNullOrEmpty(photoresult))
  3149. {
  3150. //多次拍照,如果拍照次数到达上限,直接返回结果
  3151. if (i == procedure.FeederFailLimit - 1)
  3152. {
  3153. callback.Invoke(new Tuple<bool, string[]>(true, new string[] { $"NG,{photoresult}" }));
  3154. return;
  3155. }
  3156. else
  3157. {
  3158. continue;
  3159. }
  3160. }
  3161. if (!outputCollection.Contains("Found"))
  3162. {
  3163. callback.Invoke(new Tuple<bool, string[]>(true, new string[] { "NG,NoOut[Found]" }));
  3164. return;
  3165. }
  3166. bool Found = (bool)(outputCollection["Found"].Value);
  3167. //拍照OK
  3168. if (Found)
  3169. {
  3170. SendTaskMessage(Lang.拍照OK, MessageLevel.Debug);
  3171. if (procedure.ProcedureOutputs == null)
  3172. {
  3173. SendTaskMessage(Lang.视觉流程未配置输出项, MessageLevel.Alarm);
  3174. callback.Invoke(new Tuple<bool, string[]>(true, new string[] { "NG,NotOutputs" }));
  3175. return;
  3176. }
  3177. var ResultCollection = GetVisionOutput(procedure, outputCollection, items);
  3178. if (!ResultCollection.Item1)
  3179. {
  3180. callback.Invoke(new Tuple<bool, string[]>(true, new string[] { ResultCollection.Item2 }));
  3181. return;
  3182. }
  3183. List<string> SendResults = new List<string>();
  3184. int PosNum = 0;
  3185. bool ishavepoint = false;
  3186. foreach (var result in ResultCollection.Item3)
  3187. {
  3188. //是点位数据时
  3189. if (result.Value.Item1)
  3190. {
  3191. ishavepoint = true;
  3192. //值不为空时
  3193. if (result.Value.Item2 != null)
  3194. {
  3195. List<string> points = new List<string>();
  3196. foreach (var pos in (List<Vector<double>>)(result.Value.Item2))
  3197. {
  3198. //根据当前点位数量决定是否继续输出点位。
  3199. PosNum++;
  3200. if (PosNum >= procedure.OutputPointCountMax)
  3201. {
  3202. points.Add($"{pos[0]:F3},{pos[1]:F3},{pos[2]:F3}");
  3203. break;
  3204. }
  3205. else
  3206. {
  3207. points.Add($"{pos[0]:F3},{pos[1]:F3},{pos[2]:F3}");
  3208. }
  3209. }
  3210. if (points.Count == 0)
  3211. {
  3212. SendResults.Add($"0");
  3213. break;
  3214. }
  3215. else
  3216. {
  3217. SendResults.Add($"{points.Count},{string.Join(",", points)}");
  3218. }
  3219. }
  3220. else
  3221. {
  3222. SendResults.Add($"0");
  3223. }
  3224. }
  3225. //其他输出数据时
  3226. else
  3227. {
  3228. if (result.Value.Item2 != null)
  3229. {
  3230. SendResults.Add((string)result.Value.Item2);
  3231. }
  3232. else
  3233. {
  3234. SendResults.Add($"null");
  3235. }
  3236. }
  3237. }
  3238. if (ishavepoint && PosNum <= 0)
  3239. {
  3240. continue;
  3241. }
  3242. callback.Invoke(new Tuple<bool, string[]>(true, new string[] { $"OK,{string.Join(";", SendResults)}" }));
  3243. return;
  3244. }
  3245. //拍照NG
  3246. else
  3247. {
  3248. SendTaskMessage(Lang.拍照NG, MessageLevel.Error);
  3249. continue;
  3250. }
  3251. }
  3252. SendTaskMessage(Lang.多次拍照失败, MessageLevel.Alarm);
  3253. callback.Invoke(new Tuple<bool, string[]>(true, new string[] { "NG,NoFind" }));
  3254. }
  3255. #region 相机拍照
  3256. /// <summary>
  3257. /// 执行相机拍照
  3258. /// </summary>
  3259. /// <param name="procedure"></param>
  3260. /// <param name="outputCollection"></param>
  3261. /// <returns></returns>
  3262. public string ExecutePhoto(ProcedureModel procedure, Dictionary<string, string> InputTerminal, out CogToolBlockTerminalCollection outputCollection)
  3263. {
  3264. try
  3265. {
  3266. //采集图像
  3267. var camera = CameraService.GetCamera(procedure.CameraId);
  3268. bool succed = camera.SetExposureTime(procedure.ExposureTime);
  3269. if (!succed)
  3270. {
  3271. SendTaskMessage($"相机曝光设置失败!", MessageLevel.Error);
  3272. }
  3273. succed = camera.SetGain(procedure.Gain);
  3274. if (!succed)
  3275. {
  3276. SendTaskMessage($"相机增益设置失败!", MessageLevel.Error);
  3277. }
  3278. DateTime nowtime = DateTime.Now;
  3279. LogHelper.WriteLogInfo("开始采集图像");
  3280. var image = camera.Grab();
  3281. if (image == null)
  3282. {
  3283. SendTaskMessage(Lang.图像采集失败, MessageLevel.Error);
  3284. outputCollection = null;
  3285. return "CameraError";
  3286. }
  3287. procedure.ToolBlock.Inputs["InputImage"].Value = image;
  3288. LogHelper.WriteLogInfo($"采集图像完成;用时:{(DateTime.Now - nowtime).Milliseconds}ms");
  3289. if (InputTerminal != null)
  3290. {
  3291. LogHelper.WriteLogInfo($"为ToolBlock传入输入终端");
  3292. foreach (var item in InputTerminal)
  3293. {
  3294. if (procedure.ToolBlock.Inputs.Contains(item.Key))
  3295. {
  3296. LogHelper.WriteLogInfo($"传入[{item.Key}]={item.Value}");
  3297. procedure.ToolBlock.Inputs[item.Key].Value = item.Value;
  3298. }
  3299. else
  3300. {
  3301. LogHelper.WriteLogInfo($"创建并传入[{item.Key}]={item.Value}");
  3302. procedure.ToolBlock.Inputs.Add(new CogToolBlockTerminal(item.Key, item.Value));
  3303. }
  3304. }
  3305. }
  3306. //运行视觉工具
  3307. LogHelper.WriteLogInfo("开始运行视觉工具");
  3308. procedure.ToolBlock.Run();
  3309. if (procedure.ToolBlock.RunStatus.Result == CogToolResultConstants.Accept)
  3310. {
  3311. SendTaskMessage(Lang.视觉流程执行耗时.Replace("{0}", procedure.Name).Replace("{1}", $"{procedure.ToolBlock.RunStatus.ProcessingTime:F1}"), MessageLevel.Info);
  3312. outputCollection = procedure.ToolBlock.Outputs;
  3313. ICogRecord record = null;
  3314. foreach (CogToolBlockTerminal item in outputCollection)
  3315. {
  3316. if (item.Value is ICogRecord)
  3317. record = item.Value as ICogRecord;
  3318. }
  3319. _eventAggregator.GetEvent<RenderUpdateNotification>().Publish(new ShowRender()
  3320. {
  3321. CameraName = procedure.CameraName,
  3322. Id = procedure.CameraId,
  3323. Image = (ICogImage)procedure.ToolBlock.Inputs["InputImage"].Value,
  3324. Graphic = outputCollection["Graphic"].Value as CogGraphicCollection,
  3325. Record = record,
  3326. Result = (bool)(outputCollection["Found"].Value),
  3327. IsShow = true,
  3328. IsSaveImage = procedure.IsSaveImage,
  3329. SaveImageModel = procedure.SaveImageModel,
  3330. SaveImagePathModel = procedure.SaveImagePathModel,
  3331. SavePath = procedure.SavePath,
  3332. IsCompress = procedure.IsCompress,
  3333. });
  3334. DatabaseHelper.AddCameraRecords(CurrentProduct.Name, procedure.Name, outputCollection);
  3335. return string.Empty;
  3336. }
  3337. else
  3338. {
  3339. SendTaskMessage(Lang.视觉流程执行出错耗时.Replace("{0}", procedure.Name).Replace("{1}", $"{procedure.ToolBlock.RunStatus.ProcessingTime:F1}"), MessageLevel.Error);
  3340. SendTaskMessage(procedure.ToolBlock.RunStatus.Message, MessageLevel.Error);
  3341. outputCollection = null;
  3342. _eventAggregator.GetEvent<RenderUpdateNotification>().Publish(new ShowRender()
  3343. {
  3344. CameraName = procedure.CameraName,
  3345. Id = procedure.CameraId,
  3346. Image = (ICogImage)procedure.ToolBlock.Inputs["InputImage"].Value,
  3347. Graphic = new CogGraphicCollection(),
  3348. Result = false,
  3349. IsShow = true,
  3350. IsSaveImage = procedure.IsSaveImage,
  3351. SaveImageModel = procedure.SaveImageModel,
  3352. SaveImagePathModel = procedure.SaveImagePathModel,
  3353. SavePath = procedure.SavePath
  3354. });
  3355. DatabaseHelper.AddCameraRecords(CurrentProduct.Name, procedure.Name, outputCollection);
  3356. return "ToolBlockError";
  3357. }
  3358. }
  3359. catch (Exception ex)
  3360. {
  3361. SendTaskMessage(ex.Message, MessageLevel.Error);
  3362. LogHelper.WriteLogError("执行相机取图并执行视觉工具组时出错!", ex);
  3363. outputCollection = null;
  3364. return "Error";
  3365. }
  3366. }
  3367. /// <summary>
  3368. /// 校准转换
  3369. /// </summary>
  3370. /// <param name="calib"></param>
  3371. /// <param name="x"></param>
  3372. /// <param name="y"></param>
  3373. /// <param name="angle"></param>
  3374. /// <returns></returns>
  3375. public Vector<double> TransformPoint(CalibrationInfo calib, double x, double y, double angle, string[] items, out string message, RobotBrand robotBrand = RobotBrand.Default)
  3376. {
  3377. //得到校准矩阵
  3378. Matrix<double> matrix = Matrix<double>.Build.DenseOfArray(calib.AffineTransformationMaterial);
  3379. if (calib.CameraMount == CameraMount.FixedDown || calib.CameraMount == CameraMount.MobileJ2 || calib.CameraMount == CameraMount.MobileJ4)
  3380. {
  3381. angle *= -1;
  3382. }
  3383. //将像素坐标转换成机器人坐标
  3384. var rpos = matrix * Vector<double>.Build.Dense(new double[] { x, y, 1 });
  3385. if (calib.CameraMount == Enums.CameraMount.MobileJ4)
  3386. {
  3387. if (items.Length < 4)
  3388. {
  3389. SendTaskMessage(Lang.J4移动相机校准转换条件错误, MessageLevel.Alarm);
  3390. message = "NG,DataError";
  3391. return null;
  3392. }
  3393. //像素直接转换成mm时的点位
  3394. double Robot_X, Robot_Y;
  3395. Robot_X = rpos[0];
  3396. Robot_Y = rpos[1];
  3397. //机器人当前TOOL 0下的坐标
  3398. double curpos_x = 0, curpos_y = 0, curpos_u = 0;
  3399. curpos_x = double.Parse(items[1]);
  3400. curpos_y = double.Parse(items[2]);
  3401. curpos_u = double.Parse(items[3]);
  3402. //得到旋转矩阵
  3403. double angle1 = Math.PI * (curpos_u - calib.MarkPoint.U) / 180;
  3404. // 创建T矩阵
  3405. Matrix<double> rotationMatrix = Matrix<double>.Build.DenseOfArray(new double[,]
  3406. {
  3407. { Math.Cos(angle1), -Math.Sin(angle1) },
  3408. { Math.Sin(angle1), Math.Cos(angle1) }
  3409. });
  3410. Vector<double> p3 = Vector<double>.Build.Dense(new double[] { Robot_X - calib.CalibPoints[0].Robot.X, Robot_Y - calib.CalibPoints[0].Robot.Y });
  3411. Vector<double> phere = Vector<double>.Build.Dense(new double[] { curpos_x, curpos_y });
  3412. var cc = rotationMatrix * p3 + phere;
  3413. rpos = Vector<double>.Build.Dense(new double[] { cc[0], cc[1] });
  3414. }
  3415. else if (calib.CameraMount == Enums.CameraMount.MobileDown_XYPlatform)
  3416. {
  3417. if (items.Length < 4)
  3418. {
  3419. SendTaskMessage(Lang.移动相机校准转换条件错误, MessageLevel.Alarm);
  3420. message = "NG,DataError";
  3421. return null;
  3422. }
  3423. //像素直接转换成mm时的点位
  3424. double Robot_X, Robot_Y;
  3425. Robot_X = rpos[0];
  3426. Robot_Y = rpos[1];
  3427. //模组当前TOOL 0下的坐标(相当于模组吸嘴坐标)
  3428. double curpos_x = 0, curpos_y = 0, curpos_u = 0;
  3429. curpos_x = double.Parse(items[1]);
  3430. curpos_y = double.Parse(items[2]);
  3431. curpos_u = double.Parse(items[3]);
  3432. //得到旋转矩阵
  3433. //double angle1 = Math.PI * (curpos_u - calib.MarkPoint.U) / 180;
  3434. double angle1 = 0;
  3435. // 创建T矩阵
  3436. Matrix<double> rotationMatrix = Matrix<double>.Build.DenseOfArray(new double[,]
  3437. {
  3438. { Math.Cos(angle1), -Math.Sin(angle1) },
  3439. { Math.Sin(angle1), Math.Cos(angle1) }
  3440. });
  3441. Vector<double> p3 = Vector<double>.Build.Dense(new double[] { Robot_X - calib.CalibPoints[0].Robot.X, Robot_Y - calib.CalibPoints[0].Robot.Y });
  3442. Vector<double> phere = Vector<double>.Build.Dense(new double[] { curpos_x, curpos_y });
  3443. var cc = rotationMatrix * p3 + phere;
  3444. rpos = Vector<double>.Build.Dense(new double[] { cc[0], cc[1] });//Mark点在tool0下的的坐标
  3445. }
  3446. else
  3447. {
  3448. //当需要直接转换工具坐标时
  3449. if (items.Length == 4 && double.TryParse(items[1], out double curposX) && double.TryParse(items[2], out double curposY) && double.TryParse(items[3], out double curposU))
  3450. {
  3451. //根据机器人当前的点位计算工具坐标
  3452. ToolCoord tool = new ToolCoord();
  3453. if (robotBrand == RobotBrand.Schneider)
  3454. {
  3455. curposU *= -1;
  3456. }
  3457. tool.ComputeTool(curposX, curposY, curposU, rpos[0], rpos[1]);
  3458. rpos = Vector<double>.Build.Dense(new double[] { tool.X, tool.Y });
  3459. }
  3460. }
  3461. message = string.Empty;
  3462. return Vector<double>.Build.Dense(new double[] { rpos[0], rpos[1], angle });
  3463. }
  3464. /// <summary>
  3465. /// 获取需要输出项
  3466. /// </summary>
  3467. /// <param name="procedure"></param>
  3468. /// <param name="outputCollection"></param>
  3469. /// <param name="IsCheckLastPoints"></param>
  3470. /// <returns>是否成功,错误返回内容,<项的名称,<是否是点位数据,值>></returns>
  3471. public Tuple<bool, string, Dictionary<string, Tuple<bool, object>>> GetVisionOutput(ProcedureModel procedure, CogToolBlockTerminalCollection outputCollection, string[] items)
  3472. {
  3473. RobotBrand robotBrand = RobotBrand.Default;
  3474. var robot = DeviceConfig.Robots.FirstOrDefault(r => r.Id == procedure.RobotId);
  3475. if (robot != null)
  3476. {
  3477. robotBrand = robot.RobotBrand;
  3478. }
  3479. List<string> SendResults = new List<string>();
  3480. //<输出项的名称,<是否点位,每项输出集合值>>
  3481. Dictionary<string, Tuple<bool, object>> ResultCollection = new Dictionary<string, Tuple<bool, object>>();
  3482. foreach (var output in procedure.ProcedureOutputs) //遍历配置的所有需要输出项
  3483. {
  3484. //根据输出类型进行区分输出
  3485. if (output.ValueType == typeof(string))
  3486. {
  3487. //需要点位转换时,进行点位提取
  3488. if (output.IsPointTran)
  3489. {
  3490. if (outputCollection[output.Name].Value == null || string.IsNullOrEmpty(outputCollection[output.Name].Value.ToString()))
  3491. {
  3492. //如果需要输出的项的值为空,即点位数据为空,则输出点位的个数为0
  3493. ResultCollection.Add(output.Name, new Tuple<bool, object>(true, null));
  3494. continue;
  3495. }
  3496. string[] strpoints = outputCollection[output.Name].Value.ToString().Split(";");
  3497. List<Vector<double>> currentPointsForFeeder = new List<Vector<double>>();
  3498. int index = 0;
  3499. //获取相机校准
  3500. var calib = DeviceConfig.Calibrations.FirstOrDefault(cal => cal.Id == procedure.CalibrationId);
  3501. if (calib == null)
  3502. {
  3503. SendTaskMessage(Lang.J4移动相机校准转换条件错误1, MessageLevel.Alarm);
  3504. return new Tuple<bool, string, Dictionary<string, Tuple<bool, object>>>(false, "NG,NotCalibrations", null);
  3505. }
  3506. List<string> SendDtat = new List<string>();
  3507. List<string> points = new List<string>();
  3508. //遍历所有的输出点位
  3509. foreach (var pos in strpoints)
  3510. {
  3511. //转换点位-像素转换成机器人绝对坐标
  3512. var rpos = TransformPoint(calib, double.Parse(pos.Split(',')[0]), double.Parse(pos.Split(',')[1]), double.Parse(pos.Split(',')[2]), items, out string message, robotBrand);
  3513. if (rpos == null)
  3514. {
  3515. return new Tuple<bool, string, Dictionary<string, Tuple<bool, object>>>(false, message, null);
  3516. }
  3517. currentPointsForFeeder.Add(rpos);
  3518. }
  3519. ResultCollection[output.Name] = new Tuple<bool, object>(true, currentPointsForFeeder);
  3520. }
  3521. //直接输出字符串
  3522. else
  3523. {
  3524. if (outputCollection[output.Name].Value == null || string.IsNullOrEmpty(outputCollection[output.Name].Value.ToString()))
  3525. {
  3526. ResultCollection.Add(output.Name, new Tuple<bool, object>(false, null));
  3527. continue;
  3528. }
  3529. ResultCollection[output.Name] = new Tuple<bool, object>(false, outputCollection[output.Name].Value.ToString());
  3530. }
  3531. }
  3532. else
  3533. {
  3534. if (outputCollection[output.Name].Value == null)
  3535. {
  3536. ResultCollection.Add(output.Name, new Tuple<bool, object>(false, null));
  3537. }
  3538. else
  3539. {
  3540. ResultCollection[output.Name] = new Tuple<bool, object>(false, outputCollection[output.Name].Value.ToString());
  3541. }
  3542. }
  3543. }
  3544. return new Tuple<bool, string, Dictionary<string, Tuple<bool, object>>>(true, string.Empty, ResultCollection);
  3545. }
  3546. /// <summary>
  3547. /// Feeder拍照前,执行Feeder
  3548. /// </summary>
  3549. /// <param name="Feeder"></param>
  3550. /// <param name="procedure"></param>
  3551. /// <returns></returns>
  3552. public async Task ExecuteFeederForPhotoBefore(IVoiceCoilMotorFeeder Feeder, ProcedureModel procedure, int CompulsoryVibration, CancellationToken cancellationToken)
  3553. {
  3554. if (Feeder.FeederBrand == FeederBrand.AFAG)
  3555. {
  3556. await Feeder.SetLightValueAsync((ushort)procedure.LightValue);
  3557. }
  3558. else
  3559. {
  3560. //Feeder光源亮度设置
  3561. if (procedure.LightValue > 3)
  3562. {
  3563. var light = await Feeder.GetLightBrightnessAsync();
  3564. if (procedure.LightValue != light)
  3565. {
  3566. await Feeder.SetLightValueAsync((ushort)procedure.LightValue);
  3567. }
  3568. //打开光源
  3569. await Feeder.OpenLightAsync();
  3570. }
  3571. }
  3572. if (CompulsoryVibration == 2)
  3573. {
  3574. return;
  3575. }
  3576. //先振后拍时
  3577. if (procedure.VibrationModel == VibrationModel.VibrationPhoto || CompulsoryVibration == 1)
  3578. {
  3579. if (procedure.FeederWorks != null && procedure.FeederWorks.FeederWorkList != null)
  3580. {
  3581. bool flog = false;
  3582. Dictionary<Guid, double> tuple;
  3583. if (feederCurentBlobArea.TryGetValue(CurrentProduct.ID, out tuple))
  3584. {
  3585. //首先判断是否是当前流程
  3586. if (tuple.TryGetValue(procedure.Id, out double minArea))
  3587. {
  3588. //如果上次的Blob面积小于设定的最小值,则触发供料
  3589. if (minArea <= procedure.MinArea)
  3590. {
  3591. flog = true;
  3592. SendTaskMessage(Lang.执行料仓供料, MessageLevel.Info);
  3593. await ExecuteFeederStockWorks(Feeder, procedure.FeederStockWorks, cancellationToken);
  3594. await Task.Delay(procedure.WaitFeederStop);
  3595. SendTaskMessage(Lang.料仓供料结束, MessageLevel.Info);
  3596. }
  3597. }
  3598. }
  3599. if (!flog)
  3600. {
  3601. //执行配置的Feeder振动任务
  3602. await ExecuteFeederWorks(Feeder, procedure.FeederWorks, cancellationToken, false);
  3603. }
  3604. await Task.Delay(procedure.WaitFeederStop);
  3605. }
  3606. }
  3607. else
  3608. {
  3609. if (procedure.LightValue > 3)
  3610. {
  3611. await Task.Delay(procedure.WaitPhoto);
  3612. }
  3613. Dictionary<Guid, double> tuple;
  3614. if (feederCurentBlobArea.TryGetValue(CurrentProduct.ID, out tuple))
  3615. {
  3616. //首先判断是否是当前流程
  3617. if (tuple.TryGetValue(procedure.Id, out double minArea))
  3618. {
  3619. //如果上次的Blob面积小于设定的最小值,则触发供料
  3620. if (minArea <= procedure.MinArea)
  3621. {
  3622. SendTaskMessage(Lang.执行料仓供料, MessageLevel.Info);
  3623. await ExecuteFeederStockWorks(Feeder, procedure.FeederStockWorks, cancellationToken);
  3624. await Task.Delay(procedure.WaitFeederStop);
  3625. SendTaskMessage(Lang.料仓供料结束, MessageLevel.Info);
  3626. //执行配置的Feeder振动任务
  3627. /*foreach (var feederwork in procedure.FeederWorks.FeederWorkList)
  3628. {
  3629. if (feederwork.Function == VibrationFunction.Delay)
  3630. {
  3631. await Task.Delay(feederwork.Duration);
  3632. }
  3633. else
  3634. {
  3635. await Feeder.StartAction((int)feederwork.Function, feederwork.Duration);
  3636. await Task.Delay(feederwork.Duration);
  3637. }
  3638. }*/
  3639. await Task.Delay(procedure.WaitFeederStop);
  3640. }
  3641. }
  3642. }
  3643. }
  3644. }
  3645. /// <summary>
  3646. /// Feeder拍照后,执行Feeder
  3647. /// </summary>
  3648. /// <param name="Feeder"></param>
  3649. /// <param name="procedure"></param>
  3650. /// <param name="curentBlobArea"></param>
  3651. /// <returns></returns>
  3652. public async Task ExecuteFeederForPhotoLater(IVoiceCoilMotorFeeder Feeder, ProcedureModel procedure, double curentBlobArea, CancellationToken cancellationToken)
  3653. {
  3654. //根据斑点面积决定是否料仓供料
  3655. if (curentBlobArea <= procedure.MinArea)
  3656. {
  3657. SendTaskMessage(Lang.执行料仓供料, MessageLevel.Info);
  3658. await ExecuteFeederStockWorks(Feeder, procedure.FeederStockWorks, cancellationToken);
  3659. await Task.Delay(procedure.WaitFeederStop);
  3660. SendTaskMessage(Lang.料仓供料结束, MessageLevel.Info);
  3661. }
  3662. else
  3663. {
  3664. if (procedure.FeederWorks != null && procedure.FeederWorks.FeederWorkList != null)
  3665. {
  3666. SendTaskMessage(Lang.执行Feeder振动, MessageLevel.Info);
  3667. //执行配置的Feeder振动任务
  3668. await ExecuteFeederWorks(Feeder, procedure.FeederWorks, cancellationToken);
  3669. await Task.Delay(procedure.WaitFeederStop);
  3670. SendTaskMessage(Lang.Feeder振动结束, MessageLevel.Info);
  3671. }
  3672. }
  3673. }
  3674. #endregion
  3675. /// <summary>
  3676. /// 加载产品
  3677. /// </summary>
  3678. /// <param name="name"></param>
  3679. /// <param name="callback"></param>
  3680. /// <returns></returns>
  3681. private Task LoadProduct(string name, Action<Tuple<bool, string>> callback)
  3682. {
  3683. return Task.Run(() =>
  3684. {
  3685. ProductModel product = null;
  3686. product = Products.FirstOrDefault(p => p.Name == name);
  3687. if (product == null)
  3688. {
  3689. if (int.TryParse(name, out int ProductNumber))
  3690. {
  3691. product = Products.FirstOrDefault(p => p.NumberCode == ProductNumber);
  3692. }
  3693. else
  3694. {
  3695. callback.Invoke(new Tuple<bool, string>(true, "NG,NoProduct"));
  3696. return;
  3697. }
  3698. }
  3699. LoadProducts(product);
  3700. callback.Invoke(new Tuple<bool, string>(true, "OK"));
  3701. });
  3702. }
  3703. /// <summary>
  3704. /// 获取当前产品名称
  3705. /// </summary>
  3706. /// <param name="callback"></param>
  3707. /// <returns></returns>
  3708. private Task GetCurrentProductName(Action<Tuple<bool, string>> callback)
  3709. {
  3710. return Task.Run(() =>
  3711. {
  3712. if (CurrentProduct == null)
  3713. {
  3714. callback.Invoke(new Tuple<bool, string>(true, "null"));
  3715. return;
  3716. }
  3717. callback.Invoke(new Tuple<bool, string>(true, CurrentProduct.Name));
  3718. });
  3719. }
  3720. /// <summary>
  3721. /// 获取当前产品ID
  3722. /// </summary>
  3723. /// <param name="callback"></param>
  3724. /// <returns></returns>
  3725. private Task GetCurrentProductID(Action<Tuple<bool, string>> callback)
  3726. {
  3727. return Task.Run(() =>
  3728. {
  3729. if (CurrentProduct == null)
  3730. {
  3731. callback.Invoke(new Tuple<bool, string>(true, "null"));
  3732. return;
  3733. }
  3734. callback.Invoke(new Tuple<bool, string>(true, CurrentProduct.ID.ToString()));
  3735. });
  3736. }
  3737. /// <summary>
  3738. /// 获取速度
  3739. /// </summary>
  3740. /// <param name="callback"></param>
  3741. /// <returns></returns>
  3742. private Task GetCurrentProductSpeed(Action<Tuple<bool, string>> callback)
  3743. {
  3744. return Task.Run(() =>
  3745. {
  3746. if (CurrentProduct == null)
  3747. {
  3748. return;
  3749. }
  3750. int power = CurrentProduct.Power ? 1 : 0;
  3751. int IsUserUpCamera = CurrentProduct.IsUserUpCamera ? 1 : 0;
  3752. callback.Invoke(new Tuple<bool, string>(true, $"{CurrentProduct.Speed},{CurrentProduct.Accel},{CurrentProduct.Speeds},{CurrentProduct.Accels},{power},{IsUserUpCamera}"));
  3753. });
  3754. }
  3755. /// <summary>
  3756. /// 获取所有的机器人点位
  3757. /// </summary>
  3758. /// <param name="callback"></param>
  3759. /// <returns></returns>
  3760. private Task GetAllRobotPoints(Action<Tuple<bool, string>> callback)
  3761. {
  3762. return Task.Run(() =>
  3763. {
  3764. if (CurrentProduct == null || CurrentProduct.RobotPoint == null)
  3765. {
  3766. return;
  3767. }
  3768. StringBuilder sb = new StringBuilder();
  3769. foreach (var item in CurrentProduct.RobotPoint.Points)
  3770. {
  3771. sb.Append($"{item.Number},{item.X.ToString("F3")},{item.Y.ToString("F3")},{item.Z.ToString("F3")},{item.U.ToString("F3")},{item.V.ToString("F3")},{item.W.ToString("F3")},{(int)item.Hand},{item.Local},{item.Tool},{item.RobotId.ToString()},");
  3772. }
  3773. callback.Invoke(new Tuple<bool, string>(true, $"{sb.ToString().TrimEnd(',')}"));
  3774. });
  3775. }
  3776. /// <summary>
  3777. /// 获取机器人点位数量
  3778. /// </summary>
  3779. /// <param name="callback"></param>
  3780. /// <returns></returns>
  3781. private Task GetRobotPointsCount(Action<Tuple<bool, string>> callback)
  3782. {
  3783. return Task.Run(() =>
  3784. {
  3785. if (CurrentProduct == null || CurrentProduct.RobotPoint == null)
  3786. {
  3787. return;
  3788. }
  3789. if (CurrentProduct.RobotPoint.Points == null)
  3790. {
  3791. callback.Invoke(new Tuple<bool, string>(true, $"0"));
  3792. }
  3793. else
  3794. {
  3795. callback.Invoke(new Tuple<bool, string>(true, $"{CurrentProduct.RobotPoint.Points.Count}"));
  3796. }
  3797. });
  3798. }
  3799. /// <summary>
  3800. /// 获取指定编号机器人点位
  3801. /// </summary>
  3802. /// <param name="callback"></param>
  3803. /// <returns></returns>
  3804. private Task GetRobotPoint(int number, Action<Tuple<bool, string>> callback)
  3805. {
  3806. return Task.Run(() =>
  3807. {
  3808. if (CurrentProduct == null || CurrentProduct.RobotPoint == null)
  3809. {
  3810. return;
  3811. }
  3812. var pos = CurrentProduct.RobotPoint.Points.FirstOrDefault(p => p.Number == number);
  3813. if (pos == null)
  3814. {
  3815. callback.Invoke(new Tuple<bool, string>(true, $"null"));
  3816. }
  3817. else
  3818. {
  3819. callback.Invoke(new Tuple<bool, string>(true, $"{pos.Number},{pos.X.ToString("F3")},{pos.Y.ToString("F3")},{pos.Z.ToString("F3")},{pos.U.ToString("F3")},{pos.V.ToString("F3")},{pos.W.ToString("F3")},{(int)pos.Hand},{pos.Local},{pos.Tool},{pos.RobotId.ToString()}"));
  3820. }
  3821. });
  3822. }
  3823. /// <summary>
  3824. /// 获取托盘数量
  3825. /// </summary>
  3826. /// <param name="callback"></param>
  3827. /// <returns></returns>
  3828. private Task GetPalletsCount(Action<Tuple<bool, string>> callback)
  3829. {
  3830. return Task.Run(() =>
  3831. {
  3832. if (CurrentProduct == null || CurrentProduct.RobotPoint == null)
  3833. {
  3834. return;
  3835. }
  3836. if (CurrentProduct.RobotPoint.Pallets == null)
  3837. {
  3838. callback.Invoke(new Tuple<bool, string>(true, $"0"));
  3839. }
  3840. else
  3841. {
  3842. callback.Invoke(new Tuple<bool, string>(true, $"{CurrentProduct.RobotPoint.Pallets.Count}"));
  3843. }
  3844. });
  3845. }
  3846. /// <summary>
  3847. /// 获取所有的托盘
  3848. /// </summary>
  3849. /// <param name="callback"></param>
  3850. /// <returns></returns>
  3851. private Task GetAllPallet(Action<Tuple<bool, string>> callback)
  3852. {
  3853. return Task.Run(() =>
  3854. {
  3855. if (CurrentProduct == null || CurrentProduct.RobotPoint == null)
  3856. {
  3857. return;
  3858. }
  3859. StringBuilder sb = new StringBuilder();
  3860. foreach (var item in CurrentProduct.RobotPoint.Pallets)
  3861. {
  3862. sb.Append($"{item.Name},{item.Row},{item.Column},{(int)item.Arrangement},{item.P0.X.ToString("F3")},{item.P0.Y.ToString("F3")},{item.P0.Z.ToString("F3")},{item.P0.U.ToString("F3")},{item.P0.V.ToString("F3")},{item.P0.W.ToString("F3")},{(int)item.P0.Hand},{item.P0.Local},{item.P0.Tool}," +
  3863. $"{item.P1.X.ToString("F3")},{item.P1.Y.ToString("F3")},{item.P1.Z.ToString("F3")},{item.P1.U.ToString("F3")},{item.P1.V.ToString("F3")},{item.P1.W.ToString("F3")},{(int)item.P1.Hand},{item.P1.Local},{item.P1.Tool}," +
  3864. $"{item.P2.X.ToString("F3")},{item.P2.Y.ToString("F3")},{item.P2.Z.ToString("F3")},{item.P2.U.ToString("F3")},{item.P2.V.ToString("F3")},{item.P2.W.ToString("F3")},{(int)item.P2.Hand},{item.P0.Local},{item.P2.Tool},");
  3865. }
  3866. callback.Invoke(new Tuple<bool, string>(true, $"{sb.ToString().TrimEnd(',')}"));
  3867. });
  3868. }
  3869. /// <summary>
  3870. /// 获取指定名称的托盘
  3871. /// </summary>
  3872. /// <param name="callback"></param>
  3873. /// <returns></returns>
  3874. private Task GetPallet(string name, Action<Tuple<bool, string>> callback)
  3875. {
  3876. return Task.Run(() =>
  3877. {
  3878. if (CurrentProduct == null || CurrentProduct.RobotPoint == null)
  3879. {
  3880. return;
  3881. }
  3882. var pallet = CurrentProduct.RobotPoint.Pallets.FirstOrDefault(p => p.Name == name);
  3883. if (pallet == null)
  3884. {
  3885. callback.Invoke(new Tuple<bool, string>(true, $"null"));
  3886. }
  3887. else
  3888. {
  3889. callback.Invoke(new Tuple<bool, string>(true, $"{pallet.Name},{pallet.Row},{pallet.Column},{(int)pallet.Arrangement},{pallet.P0.X.ToString("F3")},{pallet.P0.Y.ToString("F3")},{pallet.P0.Z.ToString("F3")},{pallet.P0.U.ToString("F3")},{pallet.P0.V.ToString("F3")},{pallet.P0.W.ToString("F3")},{(int)pallet.P0.Hand},{pallet.P0.Local},{pallet.P0.Tool}," +
  3890. $"{pallet.P1.X.ToString("F3")},{pallet.P1.Y.ToString("F3")},{pallet.P1.Z.ToString("F3")},{pallet.P1.U.ToString("F3")},{pallet.P1.V.ToString("F3")},{pallet.P1.W.ToString("F3")},{(int)pallet.P1.Hand},{pallet.P1.Local},{pallet.P1.Tool}," +
  3891. $"{pallet.P2.X.ToString("F3")},{pallet.P2.Y.ToString("F3")},{pallet.P2.Z.ToString("F3")},{pallet.P2.U.ToString("F3")},{pallet.P2.V.ToString("F3")},{pallet.P2.W.ToString("F3")},{(int)pallet.P2.Hand},{pallet.P0.Local},{pallet.P2.Tool}"));
  3892. }
  3893. });
  3894. }
  3895. /// <summary>
  3896. /// 获取夹具末端执行器数量
  3897. /// </summary>
  3898. /// <param name="callback"></param>
  3899. /// <returns></returns>
  3900. private Task GetToolCount(string command, Action<Tuple<bool, string>> callback)
  3901. {
  3902. return Task.Run(() =>
  3903. {
  3904. if (CurrentProduct == null || CurrentProduct.CameraProcedures == null)
  3905. {
  3906. return;
  3907. }
  3908. ProcedureModel procedure = null;
  3909. foreach (var cameraProcedures in CurrentProduct.CameraProcedures)
  3910. {
  3911. procedure = cameraProcedures.ProcedureModels.FirstOrDefault(p => p.TriggerCommand == command);
  3912. if (procedure != null) break;
  3913. }
  3914. if (procedure == null || procedure.ToolInfo == null)
  3915. {
  3916. callback.Invoke(new Tuple<bool, string>(true, $"0"));
  3917. return;
  3918. }
  3919. callback.Invoke(new Tuple<bool, string>(true, $"{procedure.ToolInfo.Count}"));
  3920. });
  3921. }
  3922. /// <summary>
  3923. /// 获取指定编号的夹具末端执行器参数
  3924. /// </summary>
  3925. /// <param name="callback"></param>
  3926. /// <returns></returns>
  3927. private Task GetTool(string command, int index, Action<Tuple<bool, string>> callback)
  3928. {
  3929. return Task.Run(() =>
  3930. {
  3931. if (CurrentProduct == null || CurrentProduct.CameraProcedures == null)
  3932. {
  3933. return;
  3934. }
  3935. ProcedureModel procedure = null;
  3936. foreach (var cameraProcedures in CurrentProduct.CameraProcedures)
  3937. {
  3938. procedure = cameraProcedures.ProcedureModels.FirstOrDefault(p => p.TriggerCommand == command);
  3939. if (procedure != null) break;
  3940. }
  3941. if (procedure == null || procedure.ToolInfo == null)
  3942. {
  3943. callback.Invoke(new Tuple<bool, string>(true, $"null"));
  3944. return;
  3945. }
  3946. var tool = procedure.ToolInfo[index];
  3947. callback.Invoke(new Tuple<bool, string>(true, $"{tool.Tool.Number},{tool.Tool.X.ToString("F3")},{tool.Tool.Y.ToString("F3")},{tool.OffsetX.ToString("F3")},{tool.OffsetY.ToString("F3")},{tool.OffsetZ.ToString("F3")},{tool.OffsetU.ToString("F3")}"));
  3948. });
  3949. }
  3950. /// <summary>
  3951. /// 获取流程参数
  3952. /// </summary>
  3953. /// <param name="callback"></param>
  3954. /// <returns></returns>
  3955. private Task GetProcedureParam(string command, Action<Tuple<bool, string>> callback)
  3956. {
  3957. return Task.Run(() =>
  3958. {
  3959. if (CurrentProduct == null || CurrentProduct.RobotPoint == null)
  3960. {
  3961. return;
  3962. }
  3963. ProcedureModel procedure = null;
  3964. foreach (var cameraProcedures in CurrentProduct.CameraProcedures)
  3965. {
  3966. procedure = cameraProcedures.ProcedureModels.FirstOrDefault(p => p.TriggerCommand == command);
  3967. if (procedure != null) break;
  3968. }
  3969. if (procedure == null)
  3970. {
  3971. callback.Invoke(new Tuple<bool, string>(true, $"null"));
  3972. return;
  3973. }
  3974. callback.Invoke(new Tuple<bool, string>(true, $"{procedure.WaitSuction},{procedure.WaitBlow},{procedure.WaitPhoto}"));
  3975. });
  3976. }
  3977. /// <summary>
  3978. /// 获取指定名称托盘的指定编号的点坐标
  3979. /// </summary>
  3980. /// <param name="callback"></param>
  3981. /// <returns></returns>
  3982. private Task GetPalletPoint(string name, int index, Action<Tuple<bool, string>> callback)
  3983. {
  3984. return Task.Run(() =>
  3985. {
  3986. if (CurrentProduct == null || CurrentProduct.RobotPoint == null)
  3987. {
  3988. return;
  3989. }
  3990. var pallet = CurrentProduct.RobotPoint.Pallets.FirstOrDefault(p => p.Name == name);
  3991. if (pallet == null)
  3992. {
  3993. callback.Invoke(new Tuple<bool, string>(true, $"null"));
  3994. }
  3995. else
  3996. {
  3997. PalletTool palletTool = new PalletTool(pallet);
  3998. StringBuilder sb = new StringBuilder();
  3999. callback.Invoke(new Tuple<bool, string>(true, $"{pallet.Name},{palletTool.Points.Count()},{index},{palletTool.Points[index].X.ToString("F3")},{palletTool.Points[index].Y.ToString("F3")},{palletTool.Points[index].Z.ToString("F3")}," +
  4000. $"{palletTool.Points[index].U.ToString("F3")},{palletTool.Points[index].V.ToString("F3")},{palletTool.Points[index].W.ToString("F3")},{(int)(palletTool.Points[index].Hand)},{palletTool.Points[index].Local},{palletTool.Points[index].Tool}"));
  4001. }
  4002. });
  4003. }
  4004. /// <summary>
  4005. /// 执行Feeder振动集合
  4006. /// </summary>
  4007. /// <param name="feeder"></param>
  4008. /// <param name="works"></param>
  4009. /// <returns></returns>
  4010. public async Task ExecuteFeederWorks(IVoiceCoilMotorFeeder feeder, FeederWorks works, CancellationToken cancellationToken, bool isUpdateParame = true)
  4011. {
  4012. //执行配置的Feeder振动任务
  4013. if (feeder.FeederBrand == FeederBrand.AFAG)
  4014. {
  4015. foreach (var feederwork in works.FeederWorkList)
  4016. {
  4017. if (feederwork.ConfigIndex < 0)
  4018. {
  4019. continue;
  4020. }
  4021. if (feederwork.ConfigIndex == 0)
  4022. {
  4023. await Task.Delay(feederwork.Duration);
  4024. }
  4025. else
  4026. {
  4027. ((AfagFeeder)feeder).LoadConfiguration(feederwork.ConfigIndex);
  4028. ((AfagFeeder)feeder).SetIntensity(feederwork.Intensity);
  4029. ((AfagFeeder)feeder).SetDirection(feederwork.Direction);
  4030. await Task.Delay(feederwork.Duration);
  4031. ((AfagFeeder)feeder).SetIntensity(0);
  4032. }
  4033. if (cancellationToken != null && cancellationToken.IsCancellationRequested)
  4034. {
  4035. return;
  4036. }
  4037. }
  4038. }
  4039. else
  4040. {
  4041. foreach (var feederwork in works.FeederWorkList)
  4042. {
  4043. if (feederwork.Function == VibrationFunction.Delay)
  4044. {
  4045. await Task.Delay(feederwork.Duration);
  4046. }
  4047. else
  4048. {
  4049. if (feederwork.SingleAction != null && isUpdateParame)
  4050. {
  4051. await feeder.SetShakeParametersAsync((int)feederwork.Function, feederwork.SingleAction);
  4052. }
  4053. await feeder.StartAction((int)feederwork.Function, feederwork.Duration);
  4054. await Task.Delay(feederwork.Duration);
  4055. }
  4056. if (cancellationToken != null && cancellationToken.IsCancellationRequested)
  4057. {
  4058. return;
  4059. }
  4060. }
  4061. }
  4062. }
  4063. /// <summary>
  4064. /// 执行Feeder振动集合
  4065. /// </summary>
  4066. /// <param name="feeder"></param>
  4067. /// <param name="works"></param>
  4068. /// <returns></returns>
  4069. public async Task ExecuteFeederWorks(IVoiceCoilMotorFeeder feeder, FeederWorks works, bool isUpdateParame = true)
  4070. {
  4071. //执行配置的Feeder振动任务
  4072. if (feeder.FeederBrand == FeederBrand.AFAG)
  4073. {
  4074. foreach (var feederwork in works.FeederWorkList)
  4075. {
  4076. if (feederwork.ConfigIndex < 0)
  4077. {
  4078. continue;
  4079. }
  4080. if (feederwork.ConfigIndex == 0)
  4081. {
  4082. await Task.Delay(feederwork.Duration);
  4083. }
  4084. else
  4085. {
  4086. ((AfagFeeder)feeder).LoadConfiguration(feederwork.ConfigIndex);
  4087. ((AfagFeeder)feeder).SetIntensity(feederwork.Intensity);
  4088. ((AfagFeeder)feeder).SetDirection(feederwork.Direction);
  4089. await Task.Delay(feederwork.Duration);
  4090. ((AfagFeeder)feeder).SetIntensity(0);
  4091. }
  4092. }
  4093. }
  4094. else
  4095. {
  4096. foreach (var feederwork in works.FeederWorkList)
  4097. {
  4098. if (feederwork.Function == VibrationFunction.Delay)
  4099. {
  4100. await Task.Delay(feederwork.Duration);
  4101. }
  4102. else
  4103. {
  4104. if (feederwork.SingleAction != null && isUpdateParame)
  4105. {
  4106. await feeder.SetShakeParametersAsync((int)feederwork.Function, feederwork.SingleAction);
  4107. }
  4108. await feeder.StartAction((int)feederwork.Function, feederwork.Duration);
  4109. await Task.Delay(feederwork.Duration);
  4110. }
  4111. }
  4112. }
  4113. }
  4114. /// <summary>
  4115. /// 执行Feeder料仓供料及振动集合
  4116. /// </summary>
  4117. /// <param name="feeder"></param>
  4118. /// <param name="works"></param>
  4119. /// <returns></returns>
  4120. public async Task ExecuteFeederStockWorks(IVoiceCoilMotorFeeder feeder, FeederStockWorks works, bool isUpdateParame = true)
  4121. {
  4122. List<Task> tasks = new List<Task>();
  4123. if (feeder.FeederBrand == FeederBrand.AFAG)
  4124. {
  4125. tasks.Add(Task.Run(() =>
  4126. {
  4127. if (works != null && works.FeederStockWorkList != null)
  4128. {
  4129. //执行配置的料仓振动任务
  4130. foreach (var stockwork in works.FeederStockWorkList)
  4131. {
  4132. if (stockwork.RegisterIndex == 0)
  4133. {
  4134. Task.Delay(stockwork.Duration).Wait();
  4135. }
  4136. else if (stockwork.RegisterIndex == 1)
  4137. {
  4138. if (BgModbusTcpCommunicate != null)
  4139. {
  4140. BgModbusTcpCommunicate.Slave.DataStore.HoldingRegisters[stockwork.Address] = 1;
  4141. Thread.Sleep(stockwork.Duration);
  4142. BgModbusTcpCommunicate.Slave.DataStore.HoldingRegisters[stockwork.Address] = 0;
  4143. }
  4144. }
  4145. }
  4146. }
  4147. }));
  4148. tasks.Add(Task.Run(() =>
  4149. {
  4150. if (works != null && works.FeederWorkList != null)
  4151. {
  4152. //执行配置的Feeder振动任务
  4153. foreach (var feederwork in works.FeederWorkList)
  4154. {
  4155. if (feederwork.ConfigIndex < 0)
  4156. {
  4157. continue;
  4158. }
  4159. if (feederwork.ConfigIndex == 0)
  4160. {
  4161. Thread.Sleep(feederwork.Duration);
  4162. }
  4163. else
  4164. {
  4165. ((AfagFeeder)feeder).LoadConfiguration(feederwork.ConfigIndex);
  4166. ((AfagFeeder)feeder).SetIntensity(feederwork.Intensity);
  4167. ((AfagFeeder)feeder).SetDirection(feederwork.Direction);
  4168. Thread.Sleep(feederwork.Duration);
  4169. ((AfagFeeder)feeder).SetIntensity(0);
  4170. }
  4171. }
  4172. }
  4173. }));
  4174. }
  4175. else
  4176. {
  4177. tasks.Add(Task.Run(() =>
  4178. {
  4179. if (works != null && works.FeederStockWorkList != null)
  4180. {
  4181. //执行配置的料仓振动任务
  4182. foreach (var stockwork in works.FeederStockWorkList)
  4183. {
  4184. if (stockwork.Function == VibrationFunction.Delay)
  4185. {
  4186. Task.Delay(stockwork.Duration).Wait();
  4187. }
  4188. else
  4189. {
  4190. feeder.OutputAsync(((int)stockwork.Function) + 1, stockwork.Duration).Wait();
  4191. Thread.Sleep(stockwork.Duration);
  4192. }
  4193. }
  4194. }
  4195. }));
  4196. tasks.Add(Task.Run(() =>
  4197. {
  4198. if (works != null && works.FeederWorkList != null)
  4199. {
  4200. //执行配置的Feeder振动任务
  4201. foreach (var feederwork in works.FeederWorkList)
  4202. {
  4203. if (feederwork.Function == VibrationFunction.Delay)
  4204. {
  4205. Task.Delay(feederwork.Duration).Wait();
  4206. }
  4207. else
  4208. {
  4209. if (feederwork.SingleAction != null && isUpdateParame)
  4210. {
  4211. feeder.SetShakeParameters((int)feederwork.Function, feederwork.SingleAction);
  4212. }
  4213. feeder.StartAction((int)feederwork.Function, feederwork.Duration).Wait();
  4214. Thread.Sleep(feederwork.Duration);
  4215. }
  4216. }
  4217. }
  4218. }));
  4219. }
  4220. await Task.WhenAll(tasks);
  4221. }
  4222. /// <summary>
  4223. /// 执行Feeder料仓供料及振动集合
  4224. /// </summary>
  4225. /// <param name="feeder"></param>
  4226. /// <param name="works"></param>
  4227. /// <returns></returns>
  4228. public async Task ExecuteFeederStockWorks(IVoiceCoilMotorFeeder feeder, FeederStockWorks works, CancellationToken cancellationToken, bool isUpdateParame = true)
  4229. {
  4230. List<Task> tasks = new List<Task>();
  4231. if (feeder.FeederBrand == FeederBrand.AFAG)
  4232. {
  4233. tasks.Add(Task.Run(() =>
  4234. {
  4235. if (works != null && works.FeederStockWorkList != null)
  4236. {
  4237. //执行配置的料仓振动任务
  4238. foreach (var stockwork in works.FeederStockWorkList)
  4239. {
  4240. if (stockwork.RegisterIndex == 0)
  4241. {
  4242. LogHelper.WriteLogDebug($"开启延时:{stockwork.Duration}ms");
  4243. Thread.Sleep(stockwork.Duration);
  4244. LogHelper.WriteLogDebug($"延时结束");
  4245. }
  4246. else if (stockwork.RegisterIndex == 1)
  4247. {
  4248. if (BgModbusTcpCommunicate != null)
  4249. {
  4250. LogHelper.WriteLogDebug($"料仓开始震动:{stockwork.Duration}ms");
  4251. BgModbusTcpCommunicate.Slave.DataStore.HoldingRegisters[stockwork.Address] = 1;
  4252. Thread.Sleep(stockwork.Duration);
  4253. BgModbusTcpCommunicate.Slave.DataStore.HoldingRegisters[stockwork.Address] = 0;
  4254. LogHelper.WriteLogDebug($"料仓停止震动:{stockwork.Duration}ms");
  4255. }
  4256. }
  4257. if (cancellationToken != null && cancellationToken.IsCancellationRequested)
  4258. {
  4259. return;
  4260. }
  4261. }
  4262. }
  4263. }));
  4264. tasks.Add(Task.Run(() =>
  4265. {
  4266. if (works != null && works.FeederWorkList != null)
  4267. {
  4268. //执行配置的Feeder振动任务
  4269. foreach (var feederwork in works.FeederWorkList)
  4270. {
  4271. if (feederwork.ConfigIndex < 0)
  4272. {
  4273. continue;
  4274. }
  4275. if (feederwork.ConfigIndex == 0)
  4276. {
  4277. Thread.Sleep(feederwork.Duration);
  4278. }
  4279. else
  4280. {
  4281. ((AfagFeeder)feeder).LoadConfiguration(feederwork.ConfigIndex);
  4282. ((AfagFeeder)feeder).SetIntensity(feederwork.Intensity);
  4283. ((AfagFeeder)feeder).SetDirection(feederwork.Direction);
  4284. Thread.Sleep(feederwork.Duration);
  4285. ((AfagFeeder)feeder).SetIntensity(0);
  4286. }
  4287. if (cancellationToken != null && cancellationToken.IsCancellationRequested)
  4288. {
  4289. return;
  4290. }
  4291. }
  4292. }
  4293. }));
  4294. }
  4295. else
  4296. {
  4297. tasks.Add(Task.Run(() =>
  4298. {
  4299. if (works != null && works.FeederStockWorkList != null)
  4300. {
  4301. //执行配置的料仓振动任务
  4302. foreach (var stockwork in works.FeederStockWorkList)
  4303. {
  4304. if (stockwork.Function == VibrationFunction.Delay)
  4305. {
  4306. Task.Delay(stockwork.Duration).Wait();
  4307. }
  4308. else
  4309. {
  4310. feeder.OutputAsync(((int)stockwork.Function) + 1, stockwork.Duration).Wait();
  4311. Thread.Sleep(stockwork.Duration);
  4312. }
  4313. if (cancellationToken != null && cancellationToken.IsCancellationRequested)
  4314. {
  4315. return;
  4316. }
  4317. }
  4318. }
  4319. }));
  4320. tasks.Add(Task.Run(() =>
  4321. {
  4322. if (works != null && works.FeederWorkList != null)
  4323. {
  4324. //执行配置的Feeder振动任务
  4325. foreach (var feederwork in works.FeederWorkList)
  4326. {
  4327. if (feederwork.Function == VibrationFunction.Delay)
  4328. {
  4329. Task.Delay(feederwork.Duration).Wait();
  4330. }
  4331. else
  4332. {
  4333. if (feederwork.SingleAction != null && isUpdateParame)
  4334. {
  4335. feeder.SetShakeParameters((int)feederwork.Function, feederwork.SingleAction);
  4336. }
  4337. feeder.StartAction((int)feederwork.Function, feederwork.Duration).Wait();
  4338. Thread.Sleep(feederwork.Duration);
  4339. }
  4340. if (cancellationToken != null && cancellationToken.IsCancellationRequested)
  4341. {
  4342. return;
  4343. }
  4344. }
  4345. }
  4346. }));
  4347. }
  4348. await Task.WhenAll(tasks);
  4349. }
  4350. /// <summary>
  4351. /// 执行计时器
  4352. /// </summary>
  4353. /// <param name="state"></param>
  4354. /// <param name="callback"></param>
  4355. public void ExecuteCycleTime(string state, Action<Tuple<bool, string>> callback)
  4356. {
  4357. if (int.Parse(state) == 1)
  4358. {
  4359. IsStart = true;
  4360. StartTime = DateTime.Now;
  4361. callback.Invoke(new Tuple<bool, string>(true, $"1"));
  4362. }
  4363. else
  4364. {
  4365. IsStart = false;
  4366. callback.Invoke(new Tuple<bool, string>(true, $"0"));
  4367. }
  4368. }
  4369. /// <summary>
  4370. /// 生产计数
  4371. /// </summary>
  4372. /// <param name="_Content"></param>
  4373. /// <param name="callback"></param>
  4374. public void AddProductionRecords(string _Content, Action<Tuple<bool, string>> callback)
  4375. {
  4376. DatabaseHelper.AddProductProductionRecords(CurrentProduct.Name, _Content);
  4377. callback.Invoke(new Tuple<bool, string>(true, _Content));
  4378. }
  4379. /// <summary>
  4380. /// 获取产品的全局参数
  4381. /// </summary>
  4382. /// <param name="ParamName"></param>
  4383. /// <param name="index"></param>
  4384. /// <param name="callback"></param>
  4385. public void GetGlobalParam(string ParamName, int index, Action<Tuple<bool, string>> callback)
  4386. {
  4387. if (CurrentProduct == null || CurrentProduct.RobotPoint == null)
  4388. {
  4389. return;
  4390. }
  4391. var pos = CurrentProduct.GlobalParamListCollection.FirstOrDefault(p => p.Name == ParamName);
  4392. if (pos == null)
  4393. {
  4394. callback.Invoke(new Tuple<bool, string>(true, $"null"));
  4395. }
  4396. else
  4397. {
  4398. switch (index)
  4399. {
  4400. case 1:
  4401. callback.Invoke(new Tuple<bool, string>(true, $"{pos.Name},{pos.Param1}"));
  4402. break;
  4403. case 2:
  4404. callback.Invoke(new Tuple<bool, string>(true, $"{pos.Name},{pos.Param2}"));
  4405. break;
  4406. case 3:
  4407. callback.Invoke(new Tuple<bool, string>(true, $"{pos.Name},{pos.Param3}"));
  4408. break;
  4409. case 4:
  4410. callback.Invoke(new Tuple<bool, string>(true, $"{pos.Name},{pos.Param4}"));
  4411. break;
  4412. default:
  4413. callback.Invoke(new Tuple<bool, string>(true, $"{pos.Name},{pos.Param1},{pos.Param2},{pos.Param3},{pos.Param4}"));
  4414. break;
  4415. }
  4416. }
  4417. }
  4418. #endregion
  4419. #endregion
  4420. #region 机器人&Feeder&相机连接状态事件
  4421. /// <summary>
  4422. /// 机器人发送事件
  4423. /// </summary>
  4424. /// <param name="arg1"></param>
  4425. /// <param name="arg2"></param>
  4426. /// <param name="arg3"></param>
  4427. /// <exception cref="NotImplementedException"></exception>
  4428. private void Management_SendEvent(Guid robotid, object arg2, string arg3)
  4429. {
  4430. var r = DeviceConfig.Robots.FirstOrDefault(ro => ro.Id == robotid);
  4431. SendTaskMessage($"{r.RobotName}Send:{arg3}", MessageLevel.Info);
  4432. }
  4433. /// <summary>
  4434. /// 机器人断开连接时
  4435. /// </summary>
  4436. /// <param name="arg1"></param>
  4437. /// <param name="arg2"></param>
  4438. /// <param name="arg3"></param>
  4439. /// <exception cref="NotImplementedException"></exception>
  4440. private void Management_DisconnectedEvent(Guid robotid, object arg2, ClosedEventArgs arg3)
  4441. {
  4442. if (RobotTCPCollection.ContainsKey(robotid))
  4443. {
  4444. RobotTCPCollection[robotid].Cancel();
  4445. }
  4446. var r = DeviceConfig.Robots.FirstOrDefault(ro => ro.Id == robotid);
  4447. if (r.ConnectType == TCPConnectType.Server)
  4448. {
  4449. ITcpSessionClient socketClient = arg2 as ITcpSessionClient;
  4450. SendTaskMessage($"{r.RobotName}[{socketClient.Id}:{socketClient.IP}]{Lang.断开连接}!", MessageLevel.Error);
  4451. var robot = this.RobotService.GetRobot(robotid);
  4452. if (robot.TcpService.Count < 1)
  4453. {
  4454. var sta = Status.FirstOrDefault(s => s.ID == robotid);
  4455. App.Current.Dispatcher.Invoke(new
  4456. Action(() =>
  4457. {
  4458. sta.Message = $"{r.RobotName}{Lang.未连接}";
  4459. sta.Background = new SolidColorBrush(Colors.Red);
  4460. }));
  4461. }
  4462. }
  4463. else
  4464. {
  4465. SendTaskMessage($"{r.RobotName}{Lang.断开连接}!", MessageLevel.Error);
  4466. var sta = Status.FirstOrDefault(s => s.ID == robotid);
  4467. App.Current.Dispatcher.Invoke(new
  4468. Action(() =>
  4469. {
  4470. sta.Message = $"{r.RobotName}{Lang.断开连接}";
  4471. sta.Background = new SolidColorBrush(Colors.Red);
  4472. }));
  4473. }
  4474. }
  4475. /// <summary>
  4476. /// 机器人连接成功时
  4477. /// </summary>
  4478. /// <param name="arg1"></param>
  4479. /// <param name="arg2"></param>
  4480. /// <param name="arg3"></param>
  4481. /// <exception cref="NotImplementedException"></exception>
  4482. private void Management_ConnectedEvent(Guid robotid, object arg2, ConnectedEventArgs arg3)
  4483. {
  4484. var r = DeviceConfig.Robots.FirstOrDefault(ro => ro.Id == robotid);
  4485. if (r.ConnectType == TCPConnectType.Server)
  4486. {
  4487. ITcpSessionClient socketClient = arg2 as ITcpSessionClient;
  4488. SendTaskMessage($"{r.RobotName}[{socketClient.Id}:{socketClient.IP}]{Lang.已连接}!", MessageLevel.Debug);
  4489. var robot = this.RobotService.GetRobot(robotid);
  4490. if (robot.TcpService.Count > 0)
  4491. {
  4492. var sta = Status.FirstOrDefault(s => s.ID == robotid);
  4493. App.Current.Dispatcher.Invoke(new
  4494. Action(() =>
  4495. {
  4496. sta.Message = $"{r.RobotName}{Lang.已连接}";
  4497. sta.Background = new SolidColorBrush(Colors.Green);
  4498. }));
  4499. }
  4500. }
  4501. else
  4502. {
  4503. SendTaskMessage($"{r.RobotName}{Lang.已连接}!", MessageLevel.Debug);
  4504. var sta = Status.FirstOrDefault(s => s.ID == robotid);
  4505. App.Current.Dispatcher.Invoke(new
  4506. Action(() =>
  4507. {
  4508. sta.Message = $"{r.RobotName}{Lang.已连接}";
  4509. sta.Background = new SolidColorBrush(Colors.Green);
  4510. }));
  4511. }
  4512. }
  4513. /// <summary>
  4514. /// Feeder断开连接时
  4515. /// </summary>
  4516. /// <param name="arg1"></param>
  4517. /// <param name="arg2"></param>
  4518. /// <param name="arg3"></param>
  4519. /// <exception cref="NotImplementedException"></exception>
  4520. private void Management_FeederDisconnectedEvent(Guid feederid, ITcpClient arg2, ClosedEventArgs arg3)
  4521. {
  4522. var f = DeviceConfig.Feeders.FirstOrDefault(ro => ro.Id == feederid);
  4523. var sta = Status.FirstOrDefault(s => s.ID == feederid);
  4524. App.Current.Dispatcher.Invoke(new
  4525. Action(() =>
  4526. {
  4527. sta.Message = $"{f.FeederName}{Lang.未连接}";
  4528. sta.Background = new SolidColorBrush(Colors.Red);
  4529. }));
  4530. SendTaskMessage($"{f.FeederName}{Lang.断开连接}!", MessageLevel.Error);
  4531. }
  4532. /// <summary>
  4533. /// Feeder连接成功时
  4534. /// </summary>
  4535. /// <param name="arg1"></param>
  4536. /// <param name="arg2"></param>
  4537. /// <param name="arg3"></param>
  4538. /// <exception cref="NotImplementedException"></exception>
  4539. private void Management_FeederConnectedEvent(Guid feederid, ITcpClient arg2, ConnectedEventArgs arg3)
  4540. {
  4541. var f = DeviceConfig.Feeders.FirstOrDefault(ro => ro.Id == feederid);
  4542. var sta = Status.FirstOrDefault(s => s.ID == feederid);
  4543. App.Current.Dispatcher.Invoke(new
  4544. Action(() =>
  4545. {
  4546. sta.Message = $"{f.FeederName}{Lang.已连接}";
  4547. sta.Background = new SolidColorBrush(Colors.Green);
  4548. }));
  4549. SendTaskMessage($"{f.FeederName}{Lang.已连接}!", MessageLevel.Debug);
  4550. }
  4551. /// <summary>
  4552. /// 相机连接或者断开时
  4553. /// </summary>
  4554. /// <param name="id"></param>
  4555. /// <param name="state"></param>
  4556. private void Management_CameraConnectChangedEvent(Guid id, bool state)
  4557. {
  4558. var sta = Status.FirstOrDefault(s => s.ID == id);
  4559. var camera = DeviceConfig.Cameras.FirstOrDefault(c => c.Id == id);
  4560. if (sta != null && camera != null)
  4561. {
  4562. if (state)
  4563. {
  4564. App.Current.Dispatcher.Invoke(new Action(() =>
  4565. {
  4566. sta.Message = $"{camera.CameraName}{Lang.已连接}";
  4567. sta.Background = new SolidColorBrush(Colors.Green);
  4568. }));
  4569. }
  4570. else
  4571. {
  4572. App.Current.Dispatcher.Invoke(new Action(() =>
  4573. {
  4574. sta.Message = $"{camera.CameraName}{Lang.断开连接}";
  4575. sta.Background = new SolidColorBrush(Colors.Red);
  4576. }));
  4577. }
  4578. }
  4579. }
  4580. /// <summary>
  4581. /// PLC连接状态改变事件
  4582. /// </summary>
  4583. /// <param name="plc"></param>
  4584. /// <param name="isconnected"></param>
  4585. private void Management_PlcConnectChangedEvent(object sender, bool isconnected)
  4586. {
  4587. IPlc plc = sender as IPlc;
  4588. var sta = Status.FirstOrDefault(s => s.ID == plc.Id);
  4589. if (plc.IsConnected)
  4590. {
  4591. SendTaskMessage($"{plc.Name}{Lang.已连接}!", MessageLevel.Debug);
  4592. if (sta == null) return;
  4593. App.Current.Dispatcher.Invoke(new
  4594. Action(() =>
  4595. {
  4596. sta.Message = $"{plc.Name}{Lang.已连接}";
  4597. sta.Background = new SolidColorBrush(Colors.Green);
  4598. }));
  4599. }
  4600. else
  4601. {
  4602. SendTaskMessage($"{plc.Name}{Lang.断开连接}!", MessageLevel.Error);
  4603. if (sta == null) return;
  4604. App.Current.Dispatcher.Invoke(new
  4605. Action(() =>
  4606. {
  4607. sta.Message = $"{plc.Name}{Lang.未连接}";
  4608. sta.Background = new SolidColorBrush(Colors.Red);
  4609. }));
  4610. }
  4611. }
  4612. #endregion
  4613. #region 后台服务器通讯事件
  4614. private void BgCommunicate_SendEvent(ITcpSessionClient client, string msg)
  4615. {
  4616. SendTaskMessage($"{Lang.服务器}[{client.ServicePort}] Send To {Lang.客户端}[{client.Id}:{client.IP}]:{msg}", MessageLevel.Info);
  4617. }
  4618. public Dictionary<string, CancellationTokenSource> BgCommunicateollection = new Dictionary<string, CancellationTokenSource>();
  4619. private async void BgCommunicate_ReceivedEvent(ITcpSessionClient client, string msg)
  4620. {
  4621. try
  4622. {
  4623. if (!BgCommunicateollection.ContainsKey(client.Id))
  4624. {
  4625. BgCommunicateollection.Add(client.Id, new CancellationTokenSource());
  4626. }
  4627. SendTaskMessage($"{Lang.服务器}[{client.ServicePort}]ReceiveFor{Lang.客户端}[{client.Id}:{client.IP}]:{msg}", MessageLevel.Info);
  4628. Task.Run(async () =>
  4629. {
  4630. try
  4631. {
  4632. await ExecuteRemoteCommand(msg, async result =>
  4633. {
  4634. try
  4635. {
  4636. if (!client.Online)
  4637. {
  4638. return;
  4639. }
  4640. if (result.Item1)
  4641. {
  4642. if (result.Item2.Length > 1)
  4643. {
  4644. foreach (var item in result.Item2)
  4645. {
  4646. await BgCommunicate.SendAsync(client, $"{item}");
  4647. await Task.Delay(10);
  4648. }
  4649. }
  4650. else
  4651. {
  4652. await BgCommunicate.SendAsync(client, $"{result.Item2[0]}");
  4653. }
  4654. }
  4655. }
  4656. catch (Exception ex)
  4657. {
  4658. LogHelper.WriteLogError("后台TCP发送数据时出错!", ex);
  4659. SendTaskMessage(ex.Message, MessageLevel.Alarm);
  4660. }
  4661. }, BgCommunicateollection[client.Id].Token);
  4662. }
  4663. catch (Exception ex)
  4664. {
  4665. LogHelper.WriteLogError("处理后台服务器接收的命令时出错!", ex);
  4666. SendTaskMessage(ex.Message, MessageLevel.Alarm);
  4667. }
  4668. });
  4669. }
  4670. catch (Exception ex)
  4671. {
  4672. LogHelper.WriteLogError("处理后台服务器接收的命令时出错!", ex);
  4673. SendTaskMessage(ex.Message, MessageLevel.Alarm);
  4674. }
  4675. }
  4676. private void BgCommunicate_DisconnectedEvent(ITcpSessionClient client, ClosedEventArgs e)
  4677. {
  4678. SendTaskMessage($"{Lang.服务器}[{client.ServicePort}]{Lang.客户端}[{client.Id}:{client.IP}]{Lang.断开连接}!", MessageLevel.Alarm);
  4679. if (BgCommunicateollection.ContainsKey(client.Id))
  4680. {
  4681. BgCommunicateollection[client.Id].Cancel();
  4682. }
  4683. }
  4684. private void BgCommunicate_ConnectedEvent(ITcpSessionClient client, ConnectedEventArgs e)
  4685. {
  4686. SendTaskMessage($"{Lang.服务器}[{client.ServicePort}]{Lang.客户端}[{client.Id}:{client.IP}]{Lang.连接成功}!", MessageLevel.Debug);
  4687. }
  4688. #endregion
  4689. #region Eip
  4690. private void DisposeRfidClient()
  4691. {
  4692. try
  4693. {
  4694. if (BgEipCommunicate != null)
  4695. {
  4696. BgEipCommunicate.OnTagDataReceived -= RfidClient_OnTagDataReceived;
  4697. BgEipCommunicate.OnConnectReceive -= Management_EipConnectChangedEvent;
  4698. BgEipCommunicate.Dispose();
  4699. }
  4700. UpdateEthernetIpStatus(false);
  4701. }
  4702. catch { /* ignore */ }
  4703. finally
  4704. {
  4705. BgEipCommunicate = null;
  4706. }
  4707. }
  4708. private void RfidClient_OnTagDataReceived(object sender, RFIDTagDataEventArgs e)
  4709. {
  4710. if (e == null || e.Tags == null || e.Tags.Count == 0)
  4711. return;
  4712. foreach (var tag in e.Tags)
  4713. {
  4714. lock (_sync)
  4715. _lastRfidTag = CloneTag(tag);
  4716. SendTaskMessage("RFID 端口" + tag.Port + " Hex=" + tag.ToHexString(), MessageLevel.Debug);
  4717. }
  4718. }
  4719. private void StopHandshakeLoop()
  4720. {
  4721. try
  4722. {
  4723. if (_handshakeCts != null)
  4724. {
  4725. _handshakeCts.Cancel();
  4726. try
  4727. {
  4728. if (_handshakeTask != null)
  4729. _handshakeTask.Wait(2000);
  4730. }
  4731. catch (AggregateException) { /* ignore */ }
  4732. _handshakeCts.Dispose();
  4733. }
  4734. }
  4735. catch { /* ignore */ }
  4736. finally
  4737. {
  4738. _handshakeCts = null;
  4739. _handshakeTask = null;
  4740. }
  4741. }
  4742. #endregion
  4743. #region 方法
  4744. private void SendTaskMessage(string msg, MessageLevel level)
  4745. {
  4746. _eventAggregator.GetEvent<TaskMessageNotification>().Publish(new Models.MessageStruct() { Message = msg, level = level });
  4747. }
  4748. private void DoCycleTime(object state)
  4749. {
  4750. if (IsStart)
  4751. {
  4752. App.Current.Dispatcher.Invoke(new Action(() =>
  4753. {
  4754. CT = DateTime.Now - StartTime;
  4755. }));
  4756. }
  4757. }
  4758. private void DoYieldTime(object state)
  4759. {
  4760. if (CurrentProduct == null) return;
  4761. TotalQuantity = DatabaseHelper.GetTotalQuantity(CurrentProduct.Name);
  4762. TotalQuantityToday = DatabaseHelper.GetTotalQuantityToday(CurrentProduct.Name);
  4763. CurrentUPH = DatabaseHelper.GetUPH(CurrentProduct.Name);
  4764. }
  4765. #region vm启动检测
  4766. /// <summary>
  4767. /// 杀死所有VM有关进程
  4768. /// </summary>
  4769. public void KillVMProcess()
  4770. {
  4771. KillProcess("VisionMasterServerApp");
  4772. KillProcess("VisionMaster");
  4773. KillProcess("VmModuleProxy.exe");
  4774. KillProcess("vServerApp.exe");
  4775. }
  4776. /// <summary>
  4777. /// 杀死指定进程名的进程
  4778. /// </summary>
  4779. /// <param name="strKillName"></param>
  4780. public void KillProcess(string strKillName)
  4781. {
  4782. foreach (System.Diagnostics.Process p in System.Diagnostics.Process.GetProcesses())
  4783. {
  4784. if (p.ProcessName.Contains(strKillName))
  4785. {
  4786. try
  4787. {
  4788. p.Kill();
  4789. p.WaitForExit();
  4790. }
  4791. catch (Exception ex)
  4792. {
  4793. LogHelper.WriteLogError($"杀死进程:{strKillName}时失败!", ex);
  4794. }
  4795. }
  4796. }
  4797. }
  4798. #endregion
  4799. #endregion
  4800. }
  4801. }