From dfb56687d7a7541a242fdc4ddae74f7300297657 Mon Sep 17 00:00:00 2001 From: iioter <535915157@qq.com> Date: Thu, 18 Jan 2024 14:52:43 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=89=E8=8F=B1=E5=A2=9E=E5=8A=A0=E6=89=B9?= =?UTF-8?q?=E9=87=8F=E8=AF=BB=E5=8F=96=EF=BC=8CReadMultiple=E5=92=8CReadFr?= =?UTF-8?q?omCache=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Drivers/PLC.MelsecMc/DeviceMelsecMc.cs | 160 ++++++++++++++++++ drivers/net6.0/PLC.MelsecMc.dll | Bin 11264 -> 13824 bytes drivers/net6.0/PLC.MelsecMc.pdb | Bin 12052 -> 13268 bytes 3 files changed, 160 insertions(+) diff --git a/Plugins/Drivers/PLC.MelsecMc/DeviceMelsecMc.cs b/Plugins/Drivers/PLC.MelsecMc/DeviceMelsecMc.cs index 0a7d1d2..2da770b 100644 --- a/Plugins/Drivers/PLC.MelsecMc/DeviceMelsecMc.cs +++ b/Plugins/Drivers/PLC.MelsecMc/DeviceMelsecMc.cs @@ -2,6 +2,9 @@ using PluginInterface; using IoTClient.Clients.PLC; using Microsoft.Extensions.Logging; +using DataTypeEnum = IoTClient.Enums.DataTypeEnum; +using System.Text; +using System.Linq; namespace PLC.MelsecMc { @@ -196,6 +199,163 @@ namespace PLC.MelsecMc return ret; } + + private Dictionary _cache = new(); + /// + /// 读 + /// + /// + /// + [Method("批量读PLC标准地址", description: "数据区,开始地址,地址间隔,读取总数")] + public DriverReturnValueModel ReadMultiple(DriverAddressIoArgModel ioArg) + { + var ret = new DriverReturnValueModel { StatusType = VaribaleStatusTypeEnum.Good }; + + if (_plc != null && this.IsConnected) + { + try + { + var addresStrings = ioArg.Address.Split(','); + string block = addresStrings[0]; + ushort start = ushort.Parse(addresStrings[1]); + ushort step = ushort.Parse(addresStrings[2]); + ushort count = ushort.Parse(addresStrings[3]); + + var dataType = DataTypeEnum.UInt16; + + switch (ioArg.ValueType) + { + case PluginInterface.DataTypeEnum.Bool: + dataType = DataTypeEnum.Bool; + break; + case PluginInterface.DataTypeEnum.Bit: + case PluginInterface.DataTypeEnum.UByte: + case PluginInterface.DataTypeEnum.Byte: + dataType = DataTypeEnum.Byte; + break; + case PluginInterface.DataTypeEnum.Uint16: + dataType = DataTypeEnum.UInt16; + break; + case PluginInterface.DataTypeEnum.Int16: + dataType = DataTypeEnum.Int16; + break; + case PluginInterface.DataTypeEnum.Uint32: + dataType = DataTypeEnum.UInt32; + break; + case PluginInterface.DataTypeEnum.Int32: + dataType = DataTypeEnum.Int32; + break; + case PluginInterface.DataTypeEnum.Float: + dataType = DataTypeEnum.Float; + break; + case PluginInterface.DataTypeEnum.Double: + dataType = DataTypeEnum.Double; + break; + case PluginInterface.DataTypeEnum.Uint64: + dataType = DataTypeEnum.UInt64; + break; + case PluginInterface.DataTypeEnum.Int64: + dataType = DataTypeEnum.Int64; + break; + case PluginInterface.DataTypeEnum.AsciiString: + dataType = DataTypeEnum.String; + break; + case PluginInterface.DataTypeEnum.Utf8String: + dataType = DataTypeEnum.String; + break; + default: + ret.StatusType = VaribaleStatusTypeEnum.Bad; + ret.Message = $"读取失败,不支持的类型:{ioArg.ValueType}"; + break; + } + + var batchAddress = new Dictionary(); + for (int i = 0; i < count; i++) + { + batchAddress[$"{block}{start + i * step}"] = dataType; + } + + try + { + var batchResult = _plc.BatchRead(batchAddress, count); + + if (batchResult.IsSucceed) + { + foreach (var value in batchResult.Value) + { + _cache[value.Key] = value.Value; + } + + ret.Value = batchResult.Value.Select(x => x.Value).ToList(); + } + else + { + ret.StatusType = VaribaleStatusTypeEnum.Bad; + foreach (var address in batchAddress) + { + if (_cache.ContainsKey(address.Key)) + _cache.Remove(address.Key); + } + } + } + catch (Exception e) + { + ret.StatusType = VaribaleStatusTypeEnum.Bad; + foreach (var address in batchAddress) + { + if (_cache.ContainsKey(address.Key)) + _cache.Remove(address.Key); + } + } + + } + catch (Exception ex) + { + ret.StatusType = VaribaleStatusTypeEnum.Bad; + ret.Message = $"读取失败,{ex.Message}"; + } + } + else + { + ret.StatusType = VaribaleStatusTypeEnum.Bad; + ret.Message = "连接失败"; + } + + return ret; + } + + /// + /// 从缓存内读 + /// + /// + /// + [Method("从缓存读取", description: "从缓存读取")] + public DriverReturnValueModel ReadFromCache(DriverAddressIoArgModel ioArg) + { + DriverReturnValueModel ret = new(); + try + { + if (_cache.ContainsKey(ioArg.Address) && _cache[ioArg.Address] != null) + { + ret.StatusType = VaribaleStatusTypeEnum.Good; + ret.Value = _cache[ioArg.Address]; + } + else + { + ret.StatusType = VaribaleStatusTypeEnum.Bad; + } + } + catch (Exception ex) + { + ret.StatusType = VaribaleStatusTypeEnum.Bad; + _logger.LogError(ex, $"Device:[{_device}],ReadFromCache(),Error"); + } + + return ret; + } + + + /// /// 写 /// diff --git a/drivers/net6.0/PLC.MelsecMc.dll b/drivers/net6.0/PLC.MelsecMc.dll index 34c0d71603ca9361967983b132aa86dac62ea800..6e571782d09d05f5f660823e93a2ab5bb2a68685 100644 GIT binary patch literal 13824 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 delta 4667 zcmZu!3vdlx>kOy!dNw~>PLIQ~q11SN5Mge&X!Zf5YrBUIM2h}vh zn~>3h#z)*zg{g`ILkCf@4MQJeJEBF#TC~=w+L1<~EsA!kpfc7$+wYvaNqBgfeEa|3 z|DFGw^Pk5qJ-7F)dT3kZi{sY~ko$X|BB3-^MwACaq{y~815d5=PMkw@qa@Z5&C>?L z!So)YbohC-M9ZBpFLqY5DfXQaP$5Y>_p&%Ea1OI6tjHN+k*p=iAb;24kCQEgPp&BqqTxprI0*cnonN+A@hY@h;eU z+X|R+sP0fEJ2iTQWjl>}Y~m=Z2AIm0U!oPnMJ(j((~HtZ6_cHJ^(?geLXQPT*;Kn& z*qLgCuNkE$%lXm}rp$bpEv8#Ojf%5jO zs6>GTl-MrO#>LtA{tC2A#NgA_xm*@Ql}<}sD(Zu^)b>mz#Yiu!qYu7dw(-N_zP z5UznMuXv~_7k^j-LY37mFYjp6X&e(wrolok!o)|OM7Ijb$@bj%l__Y0!e458%`x;g z)(BIr`kJHP-{ntwFBbWFYs=xZVM`vg+Zy)qG1D|@NJtw2F5 zOxf{SWD{iT($3dqwrt9@Y+`67C^^7o;D<^YCTXvM$fh%DSUc3H4Rj`GzNbqBXr?6x zah{*Ey!5ek4nuj2%i_o)o6irJ#@T7_L|SRG0ASgWY@3S%PE zlBNHN1S|;_0^4Jr_KjIN;=PQLN)HDcv1%+%C2Spxk6xm~RF8!=NJDHZa@6BJo=pXG z+O&899W~N9qrcD?;Ik|lt0pmb3R{M?)dJ)^6DL8|79LvjlDOn4ksWwd(4n~!)~%1a`61Kh2b1Rqx3Y0oTk z31Yikuijh~`A=ew?Yq>_xyWU5<5#CYP8?X+dQpa5{`GnZ|U}EDkfMLcKP0dl=n7muv5jgB$y53%1rH(#U8y^&xWAaK$GId;j!!5#;7}i$e1*GO!ZFz^J zm_w-4Leyso>adeM^tj3Jy&~5-3usdz&`0G8XDF;ySg&x2!qrLjXaoxC1ZGmNVzvWw z=r_O!odm|n&mK3+C<`d4T+OkZ$+^`lW(m+oEl>&?P}sTi~gqR`xfsnD$c| zioYMxA5bTIQM7_j?lA9wlcBZJc6P|PpU$YpXXtglhtARw{uJvaRMDYzCW+;;f}Y_BQMx++u6#EAbH;e`tNm z@+m;Gc`2pQ0br000dwe0V3bY(FfOBaL-3YXSbt+pu z4bu0MdLx{}T9XPlsc;iD(NV2giCQ4)(0f(1m%8YAtxwr~O4$eHzqReq^y#}5zlS0e z)c2^id+0XGf_)oBf$cO^-w)-0{(@2-QJ6fUlD?#zR}kKEr7jcgrzfayK*g1Gp1 z7!GcYk>7c?6)7)QRg7@>pml%e)VuGZwpH{$w$LRFs-y+fO?R>Fw4d#yQ?l2YZJU~w zHn(p|Y;13?scdK{rNj>wpGMM@mh*XbiSMSimX7Ay_Rft> zHMcZ0Os{gHIZ@~OoLr|jr$U2r@YS3(%$YxNlG8eI=b&%WPVPJxI_7K-|I8_m{Fs$G zuSMQ*{uFHxa}ygkHa9sndFA7lbu_kh(E8?%hQ$dO?J2`~G%uP~+t$|DNp%}Gb#xB? zJujCzg#|gj$>WO`J*UsFfApCx?4yP6?sJwGl=?P!MToIL&)uoVPG@I9EZM@Xkk0(g zUdu8<6zDl(lgy;&#U}gGyo<#X};Hz1g zK{BP%r=)=io)BTu11v}xreQ(n?|CdR0j0zA@is$k|BDKPw97b`t$dT1nPGD!Gaw4I zIFA%os)19^WQ}6rKagh64e!k0B`FX_7N1c@=5np@rgJb{o;-iZO z%l7dHKEA8Z*;djz%_WDXyQIur_s_oTPn-y^d2egRvbs5`oDv(P9U2Ia<4fiY&MiI0 U2EAopX}n|Fpugf1Hc)2%AHYfybpQYW diff --git a/drivers/net6.0/PLC.MelsecMc.pdb b/drivers/net6.0/PLC.MelsecMc.pdb index ad1bf2881749516e601717dc320c20302faef145..ee109d805bc12e9ff3558fb85ac770768453174f 100644 GIT binary patch delta 1873 zcmbVNYfPJE6h7ze$JehdVTE#MjB>xVI0s=uc45FI7zQerCC(@X8pZ~cOK~)tmKI~8 z!hn57GmXh+%-We{)39i=MYqLde@w*1MWZ9eKN>U5TxK+j`{VO|tpgH&+2ow_Jm6b zl^eWq*w+VsYh)z7D|~z3nW%El9{5-(I{)rvxM_dJdp3)eh=E%mW=J|DACgRzL?(bC zNIS%`bj^|M0Fa|mfCR+6^iy7%99{yEQ{|H6fFwPO$G2T6W4aEpmNPX&jzQk4U=mo| z+7Et%GrjEAqkNZ-=_kdhAc|)ppBeeeFnAU9Bu@gbMTs`}B;N!+3!Vy*+z$Q;aB)1I z489k0YvVix{3Q6HICp@5xGtUwuA+8OZYdiH)8LT5({Y{-J_uX`$w53L9^VJPWgXYR zo$L4waQS-{!iJ7vq6T;p;vR_w<)S`LKLtdg9wVFyt=vw8*8XwrDyf5=bczUWM6J{v-5sIhhjZ zl?o^Hd6(0q(gRlm@F90Ev>)0`N8F?&&~<2yEJEOz?hd_@C;9R(N2BC zzW&2B>GzJ2XJBB$JNa){_j&yN2lp7RN%EA$!GzaOM&xPl z`WL2ddmA;KX%N2F$n4f{I$8ri=uTzH8Tsh7%Xbc3e0=VY(OZ9gnH_%cZP7FPN1M#O zzkP7?`}Xf1UB00dBxLsZ>*wey(ccZv-@kt`WzVJ1`}Vgl+&#W*vwHITQwn6KD=DRh z$=DGydkmA+5;JXv$=Mk*jfTmsiJ3aX+ZMXJ+Xul^M2ep{^?d8`XUF zIqA16S37K-0UXdsRhM_#pV%Bf!M&qXBV`=9#F1+pS>?#j9J$9)!BGcCD>z!u(I+_C z%h4At(N{-Q^hnP3qMf*8-lhNPYT_=v&Rxd`^+|WNS;LFLGvVcPZU<}OG^~qfzShsV zvx9D-Z6c>YBUB0WlUphS6)I?vOa*<17`0NaYB!N8WV33B7${GUG0>KW=)Zi3Di3F@ zAR5()b=*uAVZ|pah{X}S4nnI23NV?D)~aNo!?86paiYfv zCsVcpzx5UpZXGudft^y&~)-o+}b_IXth4`5txtEYF`+7lwFYNGz=I z;%RlMn3syh(k!27N@ul{>G(T5h`$96wOg19g{H=K-6Ax+KBy}~XHpR#v`<~lU84P1X4HgeJuQ(hn^VCRf5R<&>Or4iUaYmWi9A>%@amaEdUj28naT5){;n z_FRv|MEnF*E?HMS;}gCqMYrt8;=ars_4bfMw&?;iM@ef;t0`u3y6 z4bO|$Kg`=wNc8p0N^fBqK-&}C&?c%U?|8b~Q$JsgnoG(^N3k(v%~og(bMYLqr<=) zbrbbxsDD8HE9zUQ|3RZ3jZQS;Xr$3NFVU0w6T5q9sCbZ7_os1?d16V-uz^@ylIb