From 8a5442f4c500080139613d8f844f981f4fa89d51 Mon Sep 17 00:00:00 2001 From: bestlqiang Date: Mon, 20 Jul 2020 17:09:31 +0800 Subject: [PATCH] =?UTF-8?q?=E6=95=B0=E6=8D=AE=E4=B8=8D=E7=A7=BB=E5=8A=A8,?= =?UTF-8?q?=E5=9C=A8=E5=8E=9F=E7=82=B9=E6=94=B6=E9=9B=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- LaiPuLaser/GlobalDefine.h | 16 +- LaiPuLaser/MarkArea.cpp | 9 +- LaiPuLaser/Product.cpp | 174 +++++++++++++++++---- LaiPuLaser/Product.h | 37 ++++- LaiPuLaser/Program_SZ_XL_TrackWorkFlow.cpp | 7 +- LaiPuLaser/WorkCmdMarkArea.cpp | 3 +- LaiPuLaser/hlp/LaiPuLaser.chm | Bin 11728 -> 11730 bytes 7 files changed, 199 insertions(+), 47 deletions(-) diff --git a/LaiPuLaser/GlobalDefine.h b/LaiPuLaser/GlobalDefine.h index 8d3b3a8..9099a7e 100644 --- a/LaiPuLaser/GlobalDefine.h +++ b/LaiPuLaser/GlobalDefine.h @@ -133,6 +133,9 @@ inline bool IsDbEqual(double a,double b) class Dbxy { friend bool operator==(const Dbxy &lhs, const Dbxy &rhs); + friend bool operator!=(const Dbxy &lhs, const Dbxy &rhs); + friend Dbxy operator+(const Dbxy &lhs, const Dbxy &rhs); + friend Dbxy operator-(const Dbxy &lhs, const Dbxy &rhs); public: Dbxy(double _x,double _y):x(_x),y(_y){}; Dbxy() @@ -154,7 +157,18 @@ inline bool operator==(const Dbxy &lhs, const Dbxy &rhs) { return (IsDbEqual(lhs.x,rhs.x) && IsDbEqual(lhs.y,rhs.y)); } - +inline bool operator!=(const Dbxy &lhs, const Dbxy &rhs) +{ + return ((!IsDbEqual(lhs.x, rhs.x)) || (!IsDbEqual(lhs.y, rhs.y))); +} +inline Dbxy operator+(const Dbxy &lhs, const Dbxy &rhs) +{ + return Dbxy((lhs.x+ rhs.x),(lhs.y+ rhs.y)); +} +inline Dbxy operator-(const Dbxy &lhs, const Dbxy &rhs) +{ + return Dbxy((lhs.x - rhs.x), (lhs.y - rhs.y)); +} //抓取点的类型 class CCatchPoint :public Dbxy { diff --git a/LaiPuLaser/MarkArea.cpp b/LaiPuLaser/MarkArea.cpp index 984b957..d87c92a 100644 --- a/LaiPuLaser/MarkArea.cpp +++ b/LaiPuLaser/MarkArea.cpp @@ -240,8 +240,8 @@ void CMarkArea::CollectWorkData(bool bNeedSel,CProduct &Product) { Dbxy AllOffset2; AllOffset2 = gCommonFlowMgr->GetAdjustOffsetAll(); - Offset.x = AllOffset2.x - m_RealBasePt.x; - Offset.y = AllOffset2.y - m_RealBasePt.y; + Offset.x = AllOffset2.x;//-m_RealBasePt.x; + Offset.y = AllOffset2.y;// -m_RealBasePt.y; } if(bNeedSel) @@ -286,6 +286,11 @@ void CMarkArea::CollectWorkData(bool bNeedSel,CProduct &Product) SpecialWorkDataVec.clear(); return; } + + Product.TheoryDataToRealData(WorkDataVec, m_BasePt,Offset); + Product.TheoryDataToRealData(SpecialWorkDataVec, m_BasePt,Offset); + return; + //根据抓取两个mark 来计算拉伸数据 if(gCommonFlowMgr->IsbStretchDataToRealSize()) { diff --git a/LaiPuLaser/Product.cpp b/LaiPuLaser/Product.cpp index e290661..b458b47 100644 --- a/LaiPuLaser/Product.cpp +++ b/LaiPuLaser/Product.cpp @@ -83,8 +83,8 @@ void CProduct::ResetRealMarkPt() m_RealMarkPt1.y = 0; m_RealMarkPt2.x = 0; m_RealMarkPt2.y = 0; - m_RealMarkPt3.x = 0; - m_RealMarkPt3.y = 0; + //m_RealMarkPt3.x = 0; + //m_RealMarkPt3.y = 0; //偏移和旋转都要重设 m_Offset.x = 0;//理论数据映射为平台坐标的偏移X m_Offset.y = 0;//理论数据映射为平台坐标的偏移Y @@ -140,6 +140,9 @@ void CProduct::SetRealMarkPt(Dbxy pt) if(!IsSetRealMarkPt1() || !IsSetRealMarkPt2()) return; + CalAffinePars(); + return; + //计算偏移旋转 CalTheoryToRealPar(); @@ -157,20 +160,98 @@ void CProduct::SetRealMarkPt(Dbxy pt) CalTheoryToRealPar(); } } + +#include +using namespace cv; + +void CProduct::CalAffinePars() +{ + CString logstr; + + o_TheoryMarkPt1 = m_TheoryMarkPt1 - m_BasePt; + logstr.Format("TheoryMarkPt1 Coord(%f,%f)", o_TheoryMarkPt1.x, o_TheoryMarkPt1.y); + gLogMgr->WriteDebugLog(logstr); + + o_TheoryMarkPt2 = m_TheoryMarkPt2 - m_BasePt; + logstr.Format("TheoryMarkPt2 Coord(%f,%f)", o_TheoryMarkPt2.x, o_TheoryMarkPt2.y); + gLogMgr->WriteDebugLog(logstr); + + o_TheoryMarkPt3 = m_TheoryMarkPt3 - m_BasePt; + logstr.Format("TheoryMarkPt3 Coord(%f,%f)", o_TheoryMarkPt3.x, o_TheoryMarkPt3.y); + gLogMgr->WriteDebugLog(logstr); + + o_RealMarkPt1 = m_RealMarkPt1 - m_BasePt; + logstr.Format("RealMarkPt1 Coord(%f,%f)", o_RealMarkPt1.x, o_RealMarkPt1.y); + gLogMgr->WriteDebugLog(logstr); + + o_RealMarkPt2 = m_RealMarkPt2 - m_BasePt; + logstr.Format("RealMarkPt2 Coord(%f,%f)", o_RealMarkPt2.x, o_RealMarkPt2.y); + gLogMgr->WriteDebugLog(logstr); + + o_RealMarkPt3 = m_RealMarkPt3 - m_BasePt; + logstr.Format("RealMarkPt3 Coord(%f,%f)", o_RealMarkPt3.x, o_RealMarkPt3.y); + gLogMgr->WriteDebugLog(logstr); + + double TheoryDis = CalDistance(o_TheoryMarkPt1, o_TheoryMarkPt2); + logstr.Format("[Mark1&2理论间距]: [%f]", TheoryDis); + gLogMgr->WriteDebugLog(logstr); + + double RealDis = CalDistance(o_RealMarkPt1, o_RealMarkPt2); + logstr.Format("[Mark1&2真实间距]: [%f]", RealDis); + gLogMgr->WriteDebugLog(logstr); + + double DisDiff = abs(TheoryDis - RealDis); + logstr.Format(_T("[Mark 间距误差] = [%f]"), DisDiff); + gLogMgr->WriteDebugLog(logstr); + + if (DisDiff > abs(gProductMgr->GetMaxMarkDisDiff())) + { + gTrackWorkFlow1.RadAlamOnOff(true);//报警提示 + ResetRealMarkPt(); + CString LogStr("Mark 间距误差超出范围,定位可能误判!"); + AfxMessageBox(logstr); + gTrackWorkFlow1.RadAlamOnOff(false);//报警提示 + CExceptionMsg Msg; + Msg.SetMsg(CString("")); + throw Msg;//抛出异常 + /* CExceptionMsg Msg; + Msg.SetMsg(LogStr); + throw Msg;*/ + } + + Point2f ThroryCoords[3]; + Point2f RealCoords[3]; + + Mat warp_mat(2, 3, CV_32FC1); + + ThroryCoords[1] = Point2f(o_TheoryMarkPt1.x, o_TheoryMarkPt1.y); + ThroryCoords[2] = Point2f(o_TheoryMarkPt2.x, o_TheoryMarkPt2.y); + ThroryCoords[0] = Point2f(o_TheoryMarkPt3.x, o_TheoryMarkPt3.y); + + RealCoords[1] = Point2f(o_RealMarkPt1.x, o_RealMarkPt1.y); + RealCoords[2] = Point2f(o_RealMarkPt2.x, o_RealMarkPt2.y); + RealCoords[0] = Point2f(o_RealMarkPt3.x, o_RealMarkPt3.y); + + //得放射变换参数矩阵 + warp_mat = getAffineTransform(ThroryCoords, RealCoords); + warp_mat.convertTo(warp_mat, CV_32FC1);//不转化时,默认CV_64FC1 后续计算会错误 + + //取出6个变换参数 + m_p00 = warp_mat.at(0, 0); + m_p01 = warp_mat.at(0, 1); + m_p02 = warp_mat.at(0, 2); + + m_p10 = warp_mat.at(1, 0); + m_p11 = warp_mat.at(1, 1); + m_p12 = warp_mat.at(1, 2); + + +} //计算真实数据的偏移和旋转值 void CProduct::CalTheoryToRealPar() { gLogMgr->WriteDebugLog("func : CalTheoryToRealPar"); - - if (gLogMgr->IsDebuging())//调试时,理论点用作实际点. - { - m_RealMarkPt1 = m_TheoryMarkPt1; - m_RealMarkPt2 = m_TheoryMarkPt2; - m_Offset.x = 0; - m_Offset.y = 0; - m_RotateAng = 0; - return; - } + //以第一个点的来计算相对的偏移 m_Offset.x = m_RealMarkPt1.x - m_TheoryMarkPt1.x; m_Offset.y = m_RealMarkPt1.y - m_TheoryMarkPt1.y; @@ -263,27 +344,6 @@ void CProduct::CalRealStretchPar() //理论数据转换为实际数据 void CProduct::TheoryDataToRealData(vector &vec,Dbxy &Offset) { -#ifdef Create_MarkData_file - CStdioFile sf; - if (!sf.Open("MarkData.txt", CFile::modeCreate | CFile::modeReadWrite | CFile::modeNoTruncate)) - sf.m_hFile = NULL; - CString str; - vector::iterator iter = vec.begin(); - vector::iterator iter_end = vec.end(); - for (; iter != iter_end; iter++) - { - (*iter) = TheoryPtToRealPt((*iter)); - (*iter).x += Offset.x; - (*iter).y += Offset.y; - if (sf.m_hFile != NULL) - { - str.Format("MarkAreaCenter: X=%f;Y=%f \n", (*iter).x, (*iter).y); - sf.SeekToEnd(); - sf.WriteString(str); - } - } - sf.Close(); -#else vector::iterator iter = vec.begin(); vector::iterator iter_end = vec.end(); for(;iter!=iter_end;iter++) @@ -292,7 +352,41 @@ void CProduct::TheoryDataToRealData(vector &vec,Dbxy &Offset) (*iter).x += Offset.x; (*iter).y += Offset.y; } -#endif +} + +//理论数据转换为实际数据 +void CProduct::TheoryDataToRealData(vector &vec, Dbxy &BaseOffset, Dbxy CutAdjust) +{ + vector::iterator iter = vec.begin(); + vector::iterator iter_end = vec.end(); + for (;iter != iter_end;iter++) + { + //(*iter) = TheoryPtToRealPt((*iter)); + //(*iter) = (*iter) - m_BasePt; + auto x = (*iter).x; + auto y = (*iter).y; + double retx = m_p00*x + m_p01*y + m_p02+ CutAdjust.x; + double rety = m_p10*x + m_p11*y + m_p12+ CutAdjust.y; + (*iter).x = retx; + (*iter).y = rety; + + auto temp = BaseOffset - m_BasePt; + + (*iter) = (*iter) - temp; + + /*(*iter).x += CutAdjust.x; + (*iter).y += CutAdjust.y;*/ + } +} +void CProduct::ResetAffinePars() +{ + m_p00 = 1; + m_p01 = 0; + m_p02 = 0; + + m_p10 = 0; + m_p11 = 1; + m_p12 = 0; } //获取实际产品的尺寸比例 Dbxy CProduct::CalRealProductScale() @@ -434,4 +528,16 @@ void CProduct::TheoryDataToRealData(vector> &vec,Dbxy &Offset) } } + +//理论数据转换为实际数据 +void CProduct::TheoryDataToRealData(vector> &vec, Dbxy &BaseOffset, Dbxy CutAdjust) +{ + gLogMgr->WriteDebugLog("CProduct::TheoryDataToRealData"); + vector>::iterator iter = vec.begin(); + vector>::iterator iter_end = vec.end(); + for (;iter != iter_end;iter++) + { + TheoryDataToRealData(*iter, BaseOffset,CutAdjust); + } +} #endif diff --git a/LaiPuLaser/Product.h b/LaiPuLaser/Product.h index 818461c..5c803a5 100644 --- a/LaiPuLaser/Product.h +++ b/LaiPuLaser/Product.h @@ -27,13 +27,18 @@ public: void SetbHasMarkPt3(bool b){m_bHasMarkPt3 = b;}; void SetTheoryMark3Pt(Dbxy pt){m_TheoryMarkPt3 = pt;}; void SetRealMark3Pt(Dbxy pt){m_RealMarkPt3 = pt;}; - Dbxy TheoryPtToRealPt(Dbxy TheoryMarkPt); - void TheoryDataToRealData(vector> &vec,Dbxy &Offset); + Dbxy TheoryPtToRealPt(Dbxy TheoryMarkPt); void StretchDataToRealSize(vector> &vec); - void TheoryDataToRealData(vector &vec,Dbxy &Offset); - void TheoryDataToRealData(vector> &SrcVec,vector> &DecVec,Dbxy &Offset); - void TheoryDataToRealData(vector &SrcVec,vector &DecVec,Dbxy &Offset); - bool IsSetRealMarkPt1(); + + void TheoryDataToRealData(vector> &vec, Dbxy &Offset); + void TheoryDataToRealData(vector &vec, Dbxy &Offset); + void TheoryDataToRealData(vector& vec, Dbxy & BaseOffset, Dbxy CutAdjust); + void ResetAffinePars();//重置仿射变换参数 + void TheoryDataToRealData(vector>& vec, Dbxy & BaseOffset, Dbxy CutAdjust); + + void TheoryDataToRealData(vector> &SrcVec,vector> &DecVec,Dbxy &Offset); + void TheoryDataToRealData(vector &SrcVec,vector &DecVec,Dbxy &Offset); + bool IsSetRealMarkPt1(); bool IsSetRealMarkPt2(); void UseDefualtOffset(); bool IsMarkReady(); @@ -42,12 +47,13 @@ public: void SetbNewAddProduct(bool bNew) { m_bNewAddProduct = bNew; };//设置是否是改造新增的产品 bool IsbNewAddProduct() { return m_bNewAddProduct; };//查询是否是改造新增的产品 private: + void CalAffinePars(); void CalTheoryToRealPar(); Dbxy CalRealProductScale(); void StretchPt(Dbxy &Pt,SObjOperatePar &Par); void CalRealStretchPar(); double CalRealProductScaleExt(Dbxy TheoryMarkPt1,Dbxy TheoryMarkPt2,Dbxy RealMarkPt1,Dbxy RealMarkPt2); -public: +private: Dbxy m_BasePt;//工件的基准坐标点 bool m_bUsed;//是否使用 bool m_bLastOne;//是否为最后一个 @@ -62,6 +68,23 @@ public: Dbxy m_RealMarkPt1;//真实坐标(相对于激光中心点) mm Dbxy m_RealMarkPt2; Dbxy m_RealMarkPt3; +public: + Dbxy o_TheoryMarkPt1;//理论坐标(不移动时) mm + Dbxy o_TheoryMarkPt2; + Dbxy o_TheoryMarkPt3; + //ccd 抓取的实际值------------------------------------ + Dbxy o_RealMarkPt1;//真实坐标(不移动时) mm + Dbxy o_RealMarkPt2; + Dbxy o_RealMarkPt3; + + //仿射参数------------------------------------ + float m_p00=1; + float m_p01=0; + float m_p02=0; + + float m_p10=0; + float m_p11=1; + float m_p12=0; public: //实际偏移旋转结果--------------------------------- Dbxy m_Offset;//理论数据映射为平台坐标的偏移 diff --git a/LaiPuLaser/Program_SZ_XL_TrackWorkFlow.cpp b/LaiPuLaser/Program_SZ_XL_TrackWorkFlow.cpp index 418c9e5..9883848 100644 --- a/LaiPuLaser/Program_SZ_XL_TrackWorkFlow.cpp +++ b/LaiPuLaser/Program_SZ_XL_TrackWorkFlow.cpp @@ -817,8 +817,8 @@ void CTrackWorkFlow::MarkProcess() //设置当前的工作步骤 SetCurTrackWorkStep(_ETrack_Step_Marking); //数据移动回默认的位置 - gProgramCutMgr->MoveObjData(Dbxy(0, 0)); - gMarkAreaMgr->MoveAllAreaToTargetPt(Dbxy(0, 0)); + //gProgramCutMgr->MoveObjData(Dbxy(0, 0)); + //gMarkAreaMgr->MoveAllAreaToTargetPt(Dbxy(0, 0)); //gCurLockTrackType = _ETrackType_NULL;//解锁 @@ -839,6 +839,8 @@ bool CTrackWorkFlow::MarkProcessExt() //gProgram_SZ_XL->SetCurMarkingTrack(m_TrackType); CProduct &Product = gServer->m_RcvProduct; +/* + Dbxy BasePt = Product.GetProductBasePt(); //=======移动obj============== gProgramCutMgr->MoveObjData(BasePt); @@ -847,6 +849,7 @@ bool CTrackWorkFlow::MarkProcessExt() gObjComponentMgr->CalAllObjCenterPt(); Dbxy AllObjCenterPt = gObjComponentMgr->GetAllObjCenterPt(); gMarkAreaMgr->MoveAllAreaToTargetPt(AllObjCenterPt); +*/ bool Ret; //启动切割 diff --git a/LaiPuLaser/WorkCmdMarkArea.cpp b/LaiPuLaser/WorkCmdMarkArea.cpp index 24d7a89..7cf6b68 100644 --- a/LaiPuLaser/WorkCmdMarkArea.cpp +++ b/LaiPuLaser/WorkCmdMarkArea.cpp @@ -57,7 +57,8 @@ bool CWorkCmdMarkArea::Excute() m_MarkArea.SetSelState(true);//选择当前加工的area //设置markarea 内的obj 为已加工状态 - gObjComponentMgr->SetMarkedStateRect(m_MarkArea.GetRect(),m_bSelMark); + // gObjComponentMgr->SetMarkedStateRect(m_MarkArea.GetRect(), m_bSelMark); + gObjComponentMgr->SetMarkedStateRect(m_MarkArea.GetRect(),true); //可能会引起报错? //m_pView->MoveViewCenter(); //m_pView->RefreshView(); diff --git a/LaiPuLaser/hlp/LaiPuLaser.chm b/LaiPuLaser/hlp/LaiPuLaser.chm index b1a86fde0bbd4b60a203b840f6627a4dd6d51142..d28b7f6e20d45fcb1378dee47be1db8909b6483d 100644 GIT binary patch delta 3103 zcma);S5T9Sx`qKMkzNwR3dOKR2?&cSNR=)Mh893N2t;}{fCTxMNJl~oJ%RyLK#CAB zgu3ZcC3K@miS#0fNRe{ZIv3~e?AhPU^W3~M@4T1asQs8d_zud_@B#w^gUjDA{>`V? ziPGs{6ysQ}*8(GxsC4G73t}WcCQU{rYdMk*|0S->PTpMRM)^z5nNEBPth;Ywq%lsJ z*?itydCx<+|7igI4MTdm013{2!UIs=ltpLB?K$<{h0oxrP65kJZZkeAwaqO#2%=ee zWVw`7qPQ47AAWo+uB_ucAb~n0iZd|q=_r;mXtOCzVGnkIxdrmaW(EzyoWn)&6UKJl zV!ro(DU;@!byE1vV;@W@ZhyM4)`CG(QFAIvac>P=V>Yky2I?psWOp$f%@tpTkM&g zn*3})0Hsi^na%5=#GkVCaF!QAP12O{K&7a~Wv-dR*Ybde>*pOBT7nplxgJYm3CQ)Rk1z@IlVkm0rH(1SJpJsZy3Y+QjpEMCzLl zbQch=@SLo3M`D1HI0+KV81M|0*=w$4YSd@=ved zn-m4vbA0dn{hJp!8$-b%VdO{CT|A_AKYtp}G(G;w4W?@cO8MmF=xGyx)a3p)J^rKJ z-WXlpajur++2Nx8V|BFf=dPl0pW<17efD*4k+1JDDZZIG31D;jP5|2%mvu@ z)q)AGfJJ<+kluy}CK7+Fom|Ty35%pZeZ?wwTyi8VWtvDW#}|y}5?p6gg+5L-CUD*U z46_C$9NNXx1bsoImahk|O3|aRXbVW`%*zi?qeaEL{S)paWSU8cdFzz&rwI|WbfshL z*eQ^FDgEHGV54A9tAxtY0kJ`%e0bI|ghLV#?UWYvf>sEH9|Wr4ua@$t4REK}{YDIW zc#CCU@bS9lw3gO6eBi=ne4U@YVQ}tbnKnSOmDoxZHi+#nrt}_5TG#|tBKsu^C7Kz2PnP~%{0L{GeZ$DWx6SMHg@B*VT zD)YESC2NV;5nS~s7cTZ8-$+;Nrin2VH!R0ei=1$cRJEqdd^2)_yi}5`rV`~@yJtb? zFe%OF>|XWL*Whwbx)PA6`MJfxC=c+i&y(*NH5vZ*gvp1FIZ4QnVl0cBX^;zpWUa}( z$0a@umS|Q}ip#O&H2PcYt z53w`Vf)1bz7}vRUw=9XwsLYto5woZY1ZL9c%dU=&J>?JBQ@sIevxMtKn`pXOJLE3a zit}(hKBF4ZLzD*3v-co~+=h;3xU7xs-P9#}u}LUKA&4sB1$DhMC8|y(pMbm^`FIRP zjp~iucc^0-JLw7;MgUrY-{`*v$uB#VR&8L<`HK(w9Hj$g4KCvlpvqe7iQ?dDN)oG>8fW(mtK+!W$&@6aqFukRxXNL1FQlL zCf95FGhSiI!b3%rM(3y`NP2AP?fFWvKxqDuJUl9ScsXx33Q8Ehq3&r-b$Ux+f4ka( z0sl$zu3nV0ah%hM%rYI-1W{yY1k8RuG#MzH4im z5e6Hr{&CLcv>H(vYXUQu0ksQ=>q&kt`KjtOtD{-`nh!I+%1@{Ma_mB5d>FJsAX54LnX) z!8YmYV0_=aK_VKs@7i?%YtCGp$p8U|Wkd(scw7VJrS>Ut<;z-*sGf*L4QWm_N?%w_IF0=pTB!*LI8R zKnoEe5hOdu?eSY_2iP(po@ybUXemaW2P5X$mSj8Fg%5QZqZ*kwYR#Tq26yyH%PoLB z<^^JR(mB$0Kps0+V&7fl^gYy*yj9!vP652Y#`HTd-VH#+r=mlz-wG ztEt)yCAnt>x8zD%B!AD9x2&(sr}y6DLwCT`{gdRAza3&Mfi&CVzNP})5Ln0hYn}UR z1nIb+MEKVb6S@eKlxJ1+JkzT(ZlE$D)bS0?mT##S`JQCHHHsvY(zj^HMiO;v6-goS zZT~`ECl!Bz5#A4ht&De%6fE6)`(=eV`G=ZZMqUtSZSSJ(+1s$hRlh6h-(%{?#IHAm zz##?2k2`=ERWhPLCUCoz7^vKZU5ObZAC0t$;6n;_XP@hR_v_iSXMY-ipL;KOvXacS zvbi$VLXD{D?yuan&eOb4ydUL~YozQ|!&;XSwej*GD-=OJhG-3!7;2tAJ7XzcJ#Yr> z`luuD1Wo-<1re+MU{CqGuhFwtE<9W}~ zhLhF zk>P_b&OZb9(Syl<%o~+gig#jZQDexl zz0#}3*2imxn%Rt(abr2m?P(FG)shhJ=Zl>0Ded_}t&hG`+kdBHai%&Y`P3~1-?WJF zs7V{CvtDHKaq1n~W~tuDaacWcf9I1|#Ena<2Z|j8uhzq1I#3{X_T{7ysettaiay!D z9<-6(Z0lKRad~0*SYU}7L9l1nV6};avn5wW-b_*0YK?fcaxk+BgP&Uk__RK;DYg4` z7kCq|cE;-I79sPV&=U+FFkUv;G2JhX?s+d7BL!8rnLecnNOXd_+O9ir)iSN`nW zmZ(?yHMzLvylgXZYM`V-Yd zXhYp+B2-Dl`6nHfoQ^j3_}?^@Nwi$-UB&0g8jLpVwUx<)(l1#FV9OAX{ip?tdj{ei u`;umAP`>3;ylX3>)X delta 3120 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