├── README.md ├── PLCReadWriteDemo ├── App.config ├── packages.config ├── Properties │ └── AssemblyInfo.cs ├── PLCReadWriteDemo.csproj └── Program.cs ├── PLCReadWrite ├── packages.config ├── PLCControl │ ├── DataType.cs │ ├── PLCData.cs │ ├── PLCControl.cs │ ├── PLCControlBase.cs │ └── PLCDataCollection.cs ├── Properties │ └── AssemblyInfo.cs ├── PLCControl.String │ ├── PLCData.cs │ ├── PLCControl.cs │ └── PLCDataCollection.cs ├── PLCReadWrite.csproj └── IPLC.cs ├── PLCReadWrite.sln ├── .gitattributes └── .gitignore /README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CKernal/PLCReadWrite/HEAD/README.md -------------------------------------------------------------------------------- /PLCReadWriteDemo/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /PLCReadWrite/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /PLCReadWriteDemo/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /PLCReadWrite/PLCControl/DataType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace PLCReadWrite.PLCControl 8 | { 9 | public enum DataType 10 | { 11 | /// 12 | /// 布尔型,占1个PLC位地址 13 | /// 14 | BoolAddress, 15 | /// 16 | /// 短整型,占1个PLC字地址 17 | /// 18 | Int16Address, 19 | /// 20 | /// 整型,占2个PLC字地址 21 | /// 22 | Int32Address, 23 | /// 24 | /// 长整型,占4个PLC字地址 25 | /// 26 | Int64Address, 27 | /// 28 | /// 浮点型,占2个PLC字地址 29 | /// 30 | Float32Address, 31 | /// 32 | /// 双精度浮点型,占4个PLC字地址 33 | /// 34 | Double64Address, 35 | /// 36 | /// 字符串型,根据字符串长度的不同占用PLC地址 37 | /// 38 | StringAddress 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /PLCReadWrite/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的一般信息由以下 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("PLCReadWrite")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Microsoft")] 12 | [assembly: AssemblyProduct("PLCReadWrite")] 13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2018")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // 将 ComVisible 设置为 false 会使此程序集中的类型 18 | //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 19 | //请将此类型的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("e3ca1971-c4b2-49dd-9292-deee576f8121")] 24 | 25 | // 程序集的版本信息由下列四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | // 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号 33 | //通过使用 "*",如下所示: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /PLCReadWriteDemo/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的一般信息由以下 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("PLCReadWriteDemo")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Microsoft")] 12 | [assembly: AssemblyProduct("PLCReadWriteDemo")] 13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2018")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // 将 ComVisible 设置为 false 会使此程序集中的类型 18 | //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 19 | //请将此类型的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("e88a8d90-6b7b-4114-ab4b-e06fa609adaf")] 24 | 25 | // 程序集的版本信息由下列四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | // 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号 33 | // 方法是按如下所示使用“*”: : 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /PLCReadWrite.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26228.4 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PLCReadWrite", "PLCReadWrite\PLCReadWrite.csproj", "{E3CA1971-C4B2-49DD-9292-DEEE576F8121}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PLCReadWriteDemo", "PLCReadWriteDemo\PLCReadWriteDemo.csproj", "{E88A8D90-6B7B-4114-AB4B-E06FA609ADAF}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {E3CA1971-C4B2-49DD-9292-DEEE576F8121}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {E3CA1971-C4B2-49DD-9292-DEEE576F8121}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {E3CA1971-C4B2-49DD-9292-DEEE576F8121}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {E3CA1971-C4B2-49DD-9292-DEEE576F8121}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {E88A8D90-6B7B-4114-AB4B-E06FA609ADAF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {E88A8D90-6B7B-4114-AB4B-E06FA609ADAF}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {E88A8D90-6B7B-4114-AB4B-E06FA609ADAF}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {E88A8D90-6B7B-4114-AB4B-E06FA609ADAF}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /PLCReadWrite/PLCControl/PLCData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PLCReadWrite.PLCControl 4 | { 5 | public class PLCData where T : struct 6 | { 7 | private const int TIMEOUT_TICKS = 50000000; 8 | 9 | private T m_data = default(T); 10 | private T m_oldData = default(T); 11 | private DateTime m_lastUpdate; 12 | 13 | public string Name { get; set; } 14 | public uint NameIndex { get; set; } 15 | public string Prefix { get; set; } 16 | public int Addr { get; set; } 17 | public byte Bit { get; set; } 18 | /// 19 | /// 此处有长度分两种情况,一种指PLC的字数据长度,另一种指PLC的位数据长度; 20 | /// 1、PLC的字数据长度,如一个D地址为16位,两个字节,(可对应C#中的数据类型:Int16) 21 | /// 2、PLC的位数据长度,如一个M地址为1位,(可对应C#中的数据类型:Bool) 22 | /// 23 | public int Length { get; set; } 24 | public bool IsBit { get; set; } 25 | 26 | public T Data 27 | { 28 | get { return m_data; } 29 | set 30 | { 31 | m_oldData = m_data; 32 | m_data = value; 33 | m_lastUpdate = DateTime.Now; 34 | } 35 | } 36 | public T OldData 37 | { 38 | get { return m_oldData; } 39 | set { m_oldData = value; } 40 | } 41 | 42 | public bool IsChanged 43 | { 44 | get { return !Data.Equals(OldData); } 45 | } 46 | 47 | public bool Timeout 48 | { 49 | get { return (DateTime.Now.Ticks - m_lastUpdate.Ticks) > TIMEOUT_TICKS; } 50 | } 51 | 52 | public string FullAddress 53 | { 54 | get 55 | { 56 | if (IsBit) 57 | { 58 | return string.Format("{0}{1}.{2}", Prefix, Addr, Bit); 59 | } 60 | return string.Format("{0}{1}", Prefix, Addr); 61 | } 62 | } 63 | 64 | public string FullName 65 | { 66 | get { return string.Format("{0}-{1}", Name, NameIndex); } 67 | } 68 | 69 | public override string ToString() 70 | { 71 | return string.Format("{0}={1}", FullAddress, Data); 72 | } 73 | } 74 | } 75 | 76 | -------------------------------------------------------------------------------- /PLCReadWrite/PLCControl.String/PLCData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PLCReadWrite.PLCControl.String 4 | { 5 | 6 | public class PLCData 7 | { 8 | private const int TIMEOUT_TICKS = 50000000; 9 | 10 | private string m_data = string.Empty; 11 | private string m_oldData = string.Empty; 12 | private DateTime m_lastUpdate; 13 | 14 | public string Name { get; set; } 15 | public uint NameIndex { get; set; } 16 | public string Prefix { get; set; } 17 | public int Addr { get; set; } 18 | public byte Bit { get; set; } 19 | public DataType DataType { get; set; } 20 | /// 21 | /// 此处有长度分两种情况,一种指PLC的字数据长度,另一种指PLC的位数据长度; 22 | /// 1、PLC的字数据长度,如一个D地址为16位,两个字节,(可对应C#中的数据类型:Int16) 23 | /// 2、PLC的位数据长度,如一个M地址为1位,(可对应C#中的数据类型:Bool) 24 | /// 25 | public int Length { get; set; } 26 | public bool IsBit { get; set; } 27 | 28 | public string Data 29 | { 30 | get { return m_data; } 31 | set 32 | { 33 | m_oldData = m_data; 34 | m_data = value; 35 | m_lastUpdate = DateTime.Now; 36 | } 37 | } 38 | 39 | public string OldData 40 | { 41 | get { return m_oldData; } 42 | set { m_oldData = value; } 43 | } 44 | 45 | public bool Timeout 46 | { 47 | get { return (DateTime.Now.Ticks - m_lastUpdate.Ticks) > TIMEOUT_TICKS; } 48 | } 49 | 50 | public bool IsChanged 51 | { 52 | get { return Data != OldData; } 53 | } 54 | 55 | public string FullAddress 56 | { 57 | get 58 | { 59 | if (IsBit) 60 | { 61 | return string.Format("{0}{1}.{2}", Prefix, Addr, Bit); 62 | } 63 | return string.Format("{0}{1}", Prefix, Addr); 64 | } 65 | } 66 | 67 | public string FullName 68 | { 69 | get { return string.Format("{0}-{1}", Name, NameIndex); } 70 | } 71 | 72 | public override string ToString() 73 | { 74 | return string.Format("{0}={1}", FullAddress, Data); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /PLCReadWriteDemo/PLCReadWriteDemo.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {E88A8D90-6B7B-4114-AB4B-E06FA609ADAF} 8 | Exe 9 | PLCReadWriteDemo 10 | PLCReadWriteDemo 11 | v4.5 12 | 512 13 | 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | false 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | false 35 | 36 | 37 | 38 | False 39 | ..\..\HslCommunication\HslCommunication_Net35\bin\Debug\HslCommunication.dll 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | {e3ca1971-c4b2-49dd-9292-deee576f8121} 61 | PLCReadWrite 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /PLCReadWrite/PLCReadWrite.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {E3CA1971-C4B2-49DD-9292-DEEE576F8121} 8 | Library 9 | Properties 10 | PLCReadWrite 11 | PLCReadWrite 12 | v4.5 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | 34 | False 35 | ..\..\HslCommunication\HslCommunication_Net35\bin\Debug\HslCommunication.dll 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /PLCReadWrite/IPLC.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using HslCommunication; 3 | using HslCommunication.Core; 4 | using HslCommunication.Profinet.Melsec; 5 | using HslCommunication.Profinet.Omron; 6 | using System.Net; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | 10 | namespace PLCReadWrite 11 | { 12 | public interface IPLC : HslCommunication.Core.IReadWriteNet 13 | { 14 | /// 15 | /// 获取或设置IP地址 16 | /// 17 | string IpAddress { get; set; } 18 | 19 | /// 20 | /// 获取或设置端口号 21 | /// 22 | int Port { get; set; } 23 | 24 | /// 25 | /// 获得当前PLC的数据变换机制 26 | /// 27 | IByteTransform Transform { get; } 28 | 29 | /// 30 | /// 切换短连接模式到长连接模式,后面的每次请求都共享一个通道 31 | /// 32 | /// 返回连接结果,如果失败的话(IsSuccess为False),包含失败信息 33 | OperateResult ConnectServer(); 34 | 35 | /// 36 | /// 在长连接模式下,断开服务器的连接,并切换到短连接模式 37 | /// 38 | /// 关闭连接,不需要查看IsSuccess属性查看 39 | OperateResult ConnectClose(); 40 | 41 | /// 42 | /// 在读取数据之前可以调用本方法将客户端设置为长连接模式,相当于跳过了ConnectServer的结果验证 43 | /// 44 | void SetPersistentConnection(); 45 | 46 | OperateResult ReadBool(string address); 47 | OperateResult ReadBool(string address, ushort length); 48 | 49 | OperateResult Write(string address, bool value); 50 | OperateResult Write(string address, bool[] values); 51 | 52 | void BeginRead(string address, ushort length, Action> readCallback); 53 | void BeginReadBool(string address, Action> readCallback); 54 | void BeginReadBool(string address, ushort length, Action> readCallback); 55 | void BeginReadInt16(string address, Action> readCallback); 56 | void BeginReadInt16(string address, ushort length, Action> readCallback); 57 | 58 | void BeginWrite(string address, byte[] value, Action writeCallback); 59 | void BeginWrite(string address, bool value, Action writeCallback); 60 | void BeginWrite(string address, bool[] values, Action writeCallback); 61 | void BeginWrite(string address, short value, Action writeCallback); 62 | void BeginWrite(string address, short[] values, Action writeCallback); 63 | } 64 | 65 | public class MelsecPlcA1E : MelsecA1ENet, IPLC 66 | { 67 | public MelsecPlcA1E(string ip, int port) : base(ip, port) 68 | { 69 | ConnectTimeOut = 300; 70 | ReceiveTimeOut = 300; 71 | } 72 | 73 | public IByteTransform Transform 74 | { 75 | get { return base.ByteTransform; } 76 | } 77 | } 78 | 79 | public class MelsecPlcMc : MelsecMcNet, IPLC 80 | { 81 | public MelsecPlcMc(string ip, int port) : base(ip, port) 82 | { 83 | ConnectTimeOut = 300; 84 | ReceiveTimeOut = 300; 85 | } 86 | 87 | public IByteTransform Transform 88 | { 89 | get { return base.ByteTransform; } 90 | } 91 | } 92 | 93 | public class OmronPlcFins : OmronFinsNet, IPLC 94 | { 95 | public OmronPlcFins(string ip, int port) : base(ip, port) 96 | { 97 | ConnectTimeOut = 300; 98 | ReceiveTimeOut = 300; 99 | 100 | /*************************************************************************** 101 | * (SA1) PC网络号,一般为PC IP地址的最后一位(Source node address) 102 | * (DA1) PLC网络号,一般为PLC IP地址的最后一位(destination node address) 103 | * (DA2) PLC单元号,通常为0(Destination unit address) 104 | ***************************************************************************/ 105 | 106 | string localIp = GetLocalIpAddress(); 107 | SA1 = GetIpAddressNode(localIp); 108 | DA1 = GetIpAddressNode(ip); 109 | DA2 = 0x00; 110 | } 111 | 112 | public IByteTransform Transform 113 | { 114 | get { return base.ByteTransform; } 115 | } 116 | 117 | private byte GetIpAddressNode(string ip) 118 | { 119 | IPAddress ipAddress; 120 | if (IPAddress.TryParse(ip, out ipAddress)) 121 | { 122 | byte[] tempByte = ipAddress.GetAddressBytes(); 123 | return tempByte[3]; 124 | } 125 | return default(byte); 126 | } 127 | 128 | private string GetLocalIpAddress() 129 | { 130 | IPAddress localIp = Dns.GetHostAddresses(Dns.GetHostName()) 131 | .Where(ip => ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) 132 | .First(); 133 | 134 | return localIp.ToString(); 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | *.VC.VC.opendb 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | *.nupkg 156 | # The packages folder can be ignored because of Package Restore 157 | **/packages/* 158 | # except build/, which is used as an MSBuild target. 159 | !**/packages/build/ 160 | # Uncomment if necessary however generally it will be regenerated when needed 161 | #!**/packages/repositories.config 162 | # NuGet v3's project.json files produces more ignoreable files 163 | *.nuget.props 164 | *.nuget.targets 165 | 166 | # Microsoft Azure Build Output 167 | csx/ 168 | *.build.csdef 169 | 170 | # Microsoft Azure Emulator 171 | ecf/ 172 | rcf/ 173 | 174 | # Windows Store app package directories and files 175 | AppPackages/ 176 | BundleArtifacts/ 177 | Package.StoreAssociation.xml 178 | _pkginfo.txt 179 | 180 | # Visual Studio cache files 181 | # files ending in .cache can be ignored 182 | *.[Cc]ache 183 | # but keep track of directories ending in .cache 184 | !*.[Cc]ache/ 185 | 186 | # Others 187 | ClientBin/ 188 | ~$* 189 | *~ 190 | *.dbmdl 191 | *.dbproj.schemaview 192 | *.jfm 193 | *.pfx 194 | *.publishsettings 195 | node_modules/ 196 | orleans.codegen.cs 197 | 198 | # Since there are multiple workflows, uncomment next line to ignore bower_components 199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 200 | #bower_components/ 201 | 202 | # RIA/Silverlight projects 203 | Generated_Code/ 204 | 205 | # Backup & report files from converting an old project file 206 | # to a newer Visual Studio version. Backup files are not needed, 207 | # because we have git ;-) 208 | _UpgradeReport_Files/ 209 | Backup*/ 210 | UpgradeLog*.XML 211 | UpgradeLog*.htm 212 | 213 | # SQL Server files 214 | *.mdf 215 | *.ldf 216 | 217 | # Business Intelligence projects 218 | *.rdl.data 219 | *.bim.layout 220 | *.bim_*.settings 221 | 222 | # Microsoft Fakes 223 | FakesAssemblies/ 224 | 225 | # GhostDoc plugin setting file 226 | *.GhostDoc.xml 227 | 228 | # Node.js Tools for Visual Studio 229 | .ntvs_analysis.dat 230 | 231 | # Visual Studio 6 build log 232 | *.plg 233 | 234 | # Visual Studio 6 workspace options file 235 | *.opt 236 | 237 | # Visual Studio LightSwitch build output 238 | **/*.HTMLClient/GeneratedArtifacts 239 | **/*.DesktopClient/GeneratedArtifacts 240 | **/*.DesktopClient/ModelManifest.xml 241 | **/*.Server/GeneratedArtifacts 242 | **/*.Server/ModelManifest.xml 243 | _Pvt_Extensions 244 | 245 | # Paket dependency manager 246 | .paket/paket.exe 247 | paket-files/ 248 | 249 | # FAKE - F# Make 250 | .fake/ 251 | 252 | # JetBrains Rider 253 | .idea/ 254 | *.sln.iml 255 | 256 | # CodeRush 257 | .cr/ 258 | 259 | # Python Tools for Visual Studio (PTVS) 260 | __pycache__/ 261 | *.pyc -------------------------------------------------------------------------------- /PLCReadWrite/PLCControl/PLCControl.cs: -------------------------------------------------------------------------------- 1 | using HslCommunication; 2 | using System; 3 | using System.Collections.Concurrent; 4 | 5 | namespace PLCReadWrite.PLCControl 6 | { 7 | /// 8 | /// PLC读写控制类,提供批量读写方法 9 | /// 10 | public class PLCControl : PLCControlBase 11 | { 12 | private ConcurrentDictionary m_plcDataCollectionDictionary; 13 | 14 | public PLCControl(IPLC plc) : base(plc) 15 | { 16 | m_plcDataCollectionDictionary = new ConcurrentDictionary(); 17 | } 18 | 19 | private bool ReadCollectionBit(ref PLCDataCollection plcDataCollection) where T : struct 20 | { 21 | string startAddr = plcDataCollection.FullStartAddress; 22 | ushort uSize = (ushort)plcDataCollection.DataLength; 23 | 24 | OperateResult read = m_plc.ReadInt16(startAddr, uSize); 25 | IsConnected = read.IsSuccess; 26 | if (IsConnected) 27 | { 28 | byte[] byteData = new byte[uSize * 2]; 29 | for (int index = 0; index < uSize; index++) 30 | { 31 | byte[] tempByte = BitConverter.GetBytes(read.Content[index]); 32 | byteData[index * 2 + 0] = tempByte[0]; 33 | byteData[index * 2 + 1] = tempByte[1]; 34 | } 35 | 36 | System.Collections.BitArray bitArray = new System.Collections.BitArray(byteData); 37 | int sAddr = plcDataCollection.StartAddr; 38 | Type tType = typeof(T); 39 | 40 | foreach (var d in plcDataCollection) 41 | { 42 | int index = ((d.Addr - sAddr) * 16) + d.Bit; 43 | d.Data = (T)(ValueType)bitArray[index]; 44 | } 45 | } 46 | return IsConnected; 47 | } 48 | private bool ReadCollectionNormal(ref PLCDataCollection plcDataCollection) where T : struct 49 | { 50 | string startAddr = plcDataCollection.FullStartAddress; 51 | ushort uSize = (ushort)plcDataCollection.DataLength; 52 | 53 | OperateResult read = m_plc.Read(startAddr, uSize); 54 | IsConnected = read.IsSuccess; 55 | if (IsConnected) 56 | { 57 | int sAddr = plcDataCollection.StartAddr; 58 | DataType dType = plcDataCollection.DataType; 59 | Type tType = typeof(T); 60 | 61 | foreach (var d in plcDataCollection) 62 | { 63 | //根据数据类型为每个PLCData赋值 64 | int index = d.Addr - sAddr; 65 | switch (dType) 66 | { 67 | case DataType.BoolAddress: 68 | d.Data = (T)(ValueType)m_plc.Transform.TransBool(read.Content, index); 69 | break; 70 | case DataType.Int16Address: 71 | d.Data = (T)(ValueType)m_plc.Transform.TransInt16(read.Content, index * 2); 72 | break; 73 | case DataType.Int32Address: 74 | d.Data = (T)(ValueType)m_plc.Transform.TransInt32(read.Content, index * 2); 75 | break; 76 | case DataType.Int64Address: 77 | d.Data = (T)(ValueType)m_plc.Transform.TransInt64(read.Content, index * 2); 78 | break; 79 | case DataType.Float32Address: 80 | d.Data = (T)(ValueType)m_plc.Transform.TransSingle(read.Content, index * 2); 81 | break; 82 | case DataType.Double64Address: 83 | d.Data = (T)(ValueType)m_plc.Transform.TransDouble(read.Content, index * 2); 84 | break; 85 | default: 86 | d.Data = default(T); 87 | break; 88 | } 89 | } 90 | } 91 | 92 | return IsConnected; 93 | } 94 | /// 95 | /// 读取一个PLC数据集 96 | /// 97 | /// 98 | /// 99 | public bool ReadCollection(ref PLCDataCollection plcDataCollection) where T : struct 100 | { 101 | if (plcDataCollection.DataLength <= 0 102 | || plcDataCollection.DataLength > ushort.MaxValue) 103 | { 104 | return false; 105 | } 106 | 107 | if (plcDataCollection.IsBitCollection) 108 | { 109 | return ReadCollectionBit(ref plcDataCollection); 110 | } 111 | return ReadCollectionNormal(ref plcDataCollection); 112 | } 113 | 114 | 115 | #region 内部数据集合操作 116 | /// 117 | /// 获取内部数据集合 118 | /// 119 | /// 120 | /// 121 | public PLCDataCollection GetCollection(int key) where T : struct 122 | { 123 | if (m_plcDataCollectionDictionary.ContainsKey(key)) 124 | { 125 | return (PLCDataCollection)m_plcDataCollectionDictionary[key]; 126 | } 127 | return null; 128 | } 129 | 130 | /// 131 | /// 添加或更新内部数据集合 132 | /// 133 | /// 134 | /// 135 | public void AddCollection(int key, PLCDataCollection collection) where T : struct 136 | { 137 | m_plcDataCollectionDictionary.AddOrUpdate(key, collection, (oldkey, oldvalue) => collection); 138 | } 139 | #endregion 140 | } 141 | 142 | } 143 | -------------------------------------------------------------------------------- /PLCReadWrite/PLCControl.String/PLCControl.cs: -------------------------------------------------------------------------------- 1 | using HslCommunication; 2 | using System; 3 | using System.Collections.Concurrent; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace PLCReadWrite.PLCControl.String 10 | { 11 | /// 12 | /// PLC读写控制类,提供批量读写方法 13 | /// 14 | public class PLCControl : PLCControlBase 15 | { 16 | private ConcurrentDictionary m_plcDataCollectionDictionary; 17 | 18 | public PLCControl(IPLC plc) : base(plc) 19 | { 20 | m_plcDataCollectionDictionary = new ConcurrentDictionary(); 21 | } 22 | 23 | private bool ReadCollectionBit(ref PLCDataCollection plcDataCollection) 24 | { 25 | string startAddr = plcDataCollection.FullStartAddress; 26 | ushort uSize = (ushort)plcDataCollection.DataLength; 27 | 28 | OperateResult read = m_plc.ReadInt16(startAddr, uSize); 29 | IsConnected = read.IsSuccess; 30 | if (IsConnected) 31 | { 32 | SetPLCDataCollection(plcDataCollection, read); 33 | } 34 | return IsConnected; 35 | } 36 | private bool ReadCollectionNormal(ref PLCDataCollection plcDataCollection) 37 | { 38 | string startAddr = plcDataCollection.FullStartAddress; 39 | ushort uSize = (ushort)plcDataCollection.DataLength; 40 | 41 | OperateResult read = m_plc.Read(startAddr, uSize); 42 | IsConnected = read.IsSuccess; 43 | if (IsConnected) 44 | { 45 | SetPLCDataCollection(plcDataCollection, read); 46 | } 47 | 48 | return IsConnected; 49 | } 50 | /// 51 | /// 读取一个PLC数据集 52 | /// 53 | /// 54 | /// 55 | public bool ReadCollection(ref PLCDataCollection plcDataCollection) 56 | { 57 | if (plcDataCollection.DataLength <= 0 58 | || plcDataCollection.DataLength > ushort.MaxValue) 59 | { 60 | return false; 61 | } 62 | 63 | if (plcDataCollection.IsBitCollection) 64 | { 65 | return ReadCollectionBit(ref plcDataCollection); 66 | } 67 | return ReadCollectionNormal(ref plcDataCollection); 68 | } 69 | 70 | private Task ReadCollectionBitAsync(PLCDataCollection plcDataCollection) 71 | { 72 | string startAddr = plcDataCollection.FullStartAddress; 73 | ushort uSize = (ushort)plcDataCollection.DataLength; 74 | 75 | var tcs = new TaskCompletionSource(); 76 | 77 | m_plc.BeginReadInt16(startAddr, uSize, read => 78 | { 79 | if (!read.IsSuccess) 80 | { 81 | tcs.SetResult(false); 82 | return; 83 | } 84 | 85 | SetPLCDataCollection(plcDataCollection, read); 86 | tcs.SetResult(true); 87 | }); 88 | 89 | return tcs.Task; 90 | } 91 | private Task ReadCollectionNormalAsync(PLCDataCollection plcDataCollection) 92 | { 93 | string startAddr = plcDataCollection.FullStartAddress; 94 | ushort uSize = (ushort)plcDataCollection.DataLength; 95 | 96 | var tcs = new TaskCompletionSource(); 97 | 98 | m_plc.BeginRead(startAddr, uSize, read => 99 | { 100 | if (!read.IsSuccess) 101 | { 102 | tcs.SetResult(false); 103 | return; 104 | } 105 | 106 | SetPLCDataCollection(plcDataCollection, read); 107 | tcs.SetResult(true); 108 | }); 109 | 110 | return tcs.Task; 111 | } 112 | 113 | private void SetPLCDataCollection(PLCDataCollection plcDataCollection, OperateResult read) 114 | { 115 | int sAddr = plcDataCollection.StartAddr; 116 | foreach (var d in plcDataCollection) 117 | { 118 | //根据数据类型为每个PLCData赋值 119 | int index = d.Addr - sAddr; 120 | switch (d.DataType) 121 | { 122 | case DataType.BoolAddress: 123 | d.Data = m_plc.Transform.TransBool(read.Content, index).ToString(); 124 | break; 125 | case DataType.Int16Address: 126 | d.Data = m_plc.Transform.TransInt16(read.Content, index * 2).ToString(); 127 | break; 128 | case DataType.Int32Address: 129 | d.Data = m_plc.Transform.TransInt32(read.Content, index * 2).ToString(); 130 | break; 131 | case DataType.Int64Address: 132 | d.Data = m_plc.Transform.TransInt64(read.Content, index * 2).ToString(); 133 | break; 134 | case DataType.Float32Address: 135 | d.Data = m_plc.Transform.TransSingle(read.Content, index * 2).ToString(); 136 | break; 137 | case DataType.Double64Address: 138 | d.Data = m_plc.Transform.TransDouble(read.Content, index * 2).ToString(); 139 | break; 140 | case DataType.StringAddress: 141 | //PLC中一个字地址为2个字节(2Byte),可储存两个ASCII字符,一个中文字符。需要解码的字节数为:(PLC字地址长度*2) 142 | //此处只支持ASCII字符,若想支持中文读取,可使用支持中文的编码如Unicode,PLC端也相应使用Unicode方式写入 143 | //d.Data = Encoding.ASCII.GetString(read.Content, index * 2, d.Length * 2); 144 | d.Data = m_plc.Transform.TransString(read.Content, index * 2, d.Length * 2, Encoding.ASCII).ToString(); 145 | break; 146 | } 147 | } 148 | } 149 | 150 | private void SetPLCDataCollection(PLCDataCollection plcDataCollection, OperateResult read) 151 | { 152 | ushort uSize = (ushort)plcDataCollection.DataLength; 153 | 154 | byte[] byteData = new byte[uSize * 2]; 155 | for (int index = 0; index < uSize; index++) 156 | { 157 | byte[] tempByte = BitConverter.GetBytes(read.Content[index]); 158 | byteData[index * 2 + 0] = tempByte[0]; 159 | byteData[index * 2 + 1] = tempByte[1]; 160 | } 161 | 162 | System.Collections.BitArray bitArray = new System.Collections.BitArray(byteData); 163 | int sAddr = plcDataCollection.StartAddr; 164 | 165 | foreach (var d in plcDataCollection) 166 | { 167 | int index = ((d.Addr - sAddr) * 16) + d.Bit; 168 | d.Data = bitArray[index].ToString(); 169 | } 170 | } 171 | /// 172 | /// 异步读取一个PLC数据集 173 | /// 174 | /// 175 | /// 176 | public Task ReadCollectionAsync(PLCDataCollection plcDataCollection) 177 | { 178 | if (plcDataCollection.IsBitCollection) 179 | { 180 | return ReadCollectionBitAsync(plcDataCollection); 181 | } 182 | return ReadCollectionNormalAsync(plcDataCollection); 183 | } 184 | 185 | #region 内部数据集合操作 186 | /// 187 | /// 获取内部数据集合 188 | /// 189 | /// 190 | /// 191 | public PLCDataCollection GetCollection(int key) 192 | { 193 | if (m_plcDataCollectionDictionary.ContainsKey(key)) 194 | { 195 | return m_plcDataCollectionDictionary[key]; 196 | } 197 | return null; 198 | } 199 | 200 | /// 201 | /// 添加或更新内部数据集合 202 | /// 203 | /// 204 | /// 205 | public void AddCollection(int key, PLCDataCollection collection) 206 | { 207 | m_plcDataCollectionDictionary.AddOrUpdate(key, collection, (oldkey, oldvalue) => collection); 208 | } 209 | #endregion 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /PLCReadWrite/PLCControl/PLCControlBase.cs: -------------------------------------------------------------------------------- 1 | using HslCommunication; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace PLCReadWrite.PLCControl 9 | { 10 | public abstract class PLCControlBase 11 | { 12 | protected IPLC m_plc; 13 | private bool m_isConnected = false; 14 | 15 | /// 16 | /// Plc连接状态发生改变时触发 17 | /// 18 | public event EventHandler OnPlcStatusChanged; 19 | 20 | public PLCControlBase(IPLC plc) 21 | { 22 | m_plc = plc; 23 | } 24 | 25 | /// 26 | /// 获取IP地址 27 | /// 28 | public string IpAddress 29 | { 30 | get { return m_plc.IpAddress; } 31 | } 32 | 33 | /// 34 | /// 获取端口号 35 | /// 36 | public int Port 37 | { 38 | get { return m_plc.Port; } 39 | } 40 | /// 41 | /// 指示Plc连接状态 42 | /// 43 | public bool IsConnected 44 | { 45 | get 46 | { 47 | return m_isConnected; 48 | } 49 | protected set 50 | { 51 | if (value != m_isConnected) 52 | { 53 | if (OnPlcStatusChanged != null) 54 | { 55 | OnPlcStatusChanged.Invoke(this, new PlcStatusArgs(value ? PlcStatus.Connected : PlcStatus.Unconnected)); 56 | } 57 | } 58 | m_isConnected = value; 59 | } 60 | } 61 | 62 | /// 63 | /// 尝试建立连接 64 | /// 65 | /// 66 | public bool Open() 67 | { 68 | try 69 | { 70 | var connect = m_plc.ConnectServer(); 71 | IsConnected = connect.IsSuccess; 72 | return IsConnected; 73 | } 74 | catch (Exception ex) 75 | { 76 | throw ex; 77 | } 78 | } 79 | 80 | /// 81 | /// 关闭连接 82 | /// 83 | public void Close() 84 | { 85 | if (IsConnected) 86 | { 87 | m_plc.ConnectClose(); 88 | IsConnected = false; 89 | } 90 | } 91 | 92 | /// 93 | /// 客户端设置为长连接模式,相当于跳过了ConnectServer的结果验证 94 | /// 95 | public void SetPersistentConnection() 96 | { 97 | m_plc.SetPersistentConnection(); 98 | } 99 | 100 | /// 101 | /// 批量读取PLC位软元件指定地址的Bool数据 102 | /// 103 | /// 104 | /// 105 | /// 106 | /// 107 | public bool ReadBool(string startAddr, ushort uSize, ref bool[] sData) 108 | { 109 | if (uSize == 0) { return false; } 110 | OperateResult read = m_plc.ReadBool(startAddr, uSize); 111 | IsConnected = read.IsSuccess; 112 | if (IsConnected) 113 | { 114 | sData = read.Content; 115 | } 116 | 117 | return IsConnected; 118 | } 119 | /// 120 | /// 批量读取PLC字软元件指定地址的Int16数据 121 | /// 122 | /// 123 | /// 124 | /// 125 | /// 126 | public bool ReadInt16(string startAddr, ushort uSize, ref short[] sData) 127 | { 128 | if (uSize == 0) { return false; } 129 | OperateResult read = m_plc.ReadInt16(startAddr, uSize); 130 | IsConnected = read.IsSuccess; 131 | if (IsConnected) 132 | { 133 | sData = read.Content; 134 | } 135 | 136 | return IsConnected; 137 | } 138 | 139 | public bool WriteInt16(string startAddr, short sData) 140 | { 141 | OperateResult write = m_plc.Write(startAddr, sData); 142 | IsConnected = write.IsSuccess; 143 | return IsConnected; 144 | } 145 | public bool WriteInt16(string startAddr, short[] sData) 146 | { 147 | OperateResult write = m_plc.Write(startAddr, sData); 148 | IsConnected = write.IsSuccess; 149 | return IsConnected; 150 | } 151 | 152 | public bool WriteBool(string startAddr, bool sData) 153 | { 154 | OperateResult write = m_plc.Write(startAddr, sData); 155 | IsConnected = write.IsSuccess; 156 | return IsConnected; 157 | } 158 | public bool WriteBool(string startAddr, bool[] sData) 159 | { 160 | OperateResult write = m_plc.Write(startAddr, sData); 161 | IsConnected = write.IsSuccess; 162 | return IsConnected; 163 | } 164 | 165 | public Task> ReadAsync(string address, ushort length) 166 | { 167 | var tcs = new TaskCompletionSource>(); 168 | m_plc.BeginRead(address, length, read => 169 | { 170 | tcs.SetResult(read); 171 | }); 172 | return tcs.Task; 173 | } 174 | public Task> ReadBoolAsync(string address) 175 | { 176 | var tcs = new TaskCompletionSource>(); 177 | m_plc.BeginReadBool(address, read => 178 | { 179 | tcs.SetResult(read); 180 | }); 181 | return tcs.Task; 182 | } 183 | public Task> ReadBoolAsync(string address, ushort length) 184 | { 185 | var tcs = new TaskCompletionSource>(); 186 | m_plc.BeginReadBool(address, length, read => 187 | { 188 | tcs.SetResult(read); 189 | }); 190 | return tcs.Task; 191 | } 192 | public Task> ReadInt16Async(string address) 193 | { 194 | var tcs = new TaskCompletionSource>(); 195 | m_plc.BeginReadInt16(address, read => 196 | { 197 | tcs.SetResult(read); 198 | }); 199 | return tcs.Task; 200 | } 201 | public Task> ReadInt16Async(string address, ushort length) 202 | { 203 | var tcs = new TaskCompletionSource>(); 204 | m_plc.BeginReadInt16(address, length, read => 205 | { 206 | tcs.SetResult(read); 207 | }); 208 | return tcs.Task; 209 | } 210 | 211 | public Task WriteAsync(string address, byte[] value) 212 | { 213 | var tcs = new TaskCompletionSource(); 214 | m_plc.BeginWrite(address, value, read => 215 | { 216 | tcs.SetResult(read); 217 | }); 218 | return tcs.Task; 219 | } 220 | public Task WriteAsync(string address, bool value) 221 | { 222 | var tcs = new TaskCompletionSource(); 223 | m_plc.BeginWrite(address, value, read => 224 | { 225 | tcs.SetResult(read); 226 | }); 227 | return tcs.Task; 228 | } 229 | public Task WriteAsync(string address, bool[] values) 230 | { 231 | var tcs = new TaskCompletionSource(); 232 | m_plc.BeginWrite(address, values, read => 233 | { 234 | tcs.SetResult(read); 235 | }); 236 | return tcs.Task; 237 | } 238 | public Task WriteAsync(string address, short value) 239 | { 240 | var tcs = new TaskCompletionSource(); 241 | m_plc.BeginWrite(address, value, read => 242 | { 243 | tcs.SetResult(read); 244 | }); 245 | return tcs.Task; 246 | } 247 | public Task WriteAsync(string address, short[] values) 248 | { 249 | var tcs = new TaskCompletionSource(); 250 | m_plc.BeginWrite(address, values, read => 251 | { 252 | tcs.SetResult(read); 253 | }); 254 | return tcs.Task; 255 | } 256 | } 257 | 258 | #region PLC状态改变的事件参数定义 259 | public enum PlcStatus 260 | { 261 | Connected, 262 | Unconnected, 263 | } 264 | public class PlcStatusArgs : EventArgs 265 | { 266 | private PlcStatus plcStatus; 267 | 268 | public PlcStatus Status 269 | { 270 | get { return plcStatus; } 271 | set { plcStatus = value; } 272 | } 273 | 274 | public PlcStatusArgs(PlcStatus status) 275 | { 276 | this.plcStatus = status; 277 | } 278 | } 279 | #endregion 280 | } 281 | -------------------------------------------------------------------------------- /PLCReadWrite/PLCControl/PLCDataCollection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace PLCReadWrite.PLCControl 7 | { 8 | /// 9 | /// PLC数据集合(仅支持同一种地址前缀) 10 | /// 11 | public class PLCDataCollection : ICollection> where T : struct 12 | { 13 | /// 14 | /// 集合内部储存结构 15 | /// 16 | private List> m_plcDataList = new List>(); 17 | 18 | public string Name { get; private set; } 19 | public string Prefix { get; private set; } 20 | public int StartAddr { get; private set; } 21 | public int DataLength { get; private set; } 22 | public bool IsBitCollection { get; private set; } 23 | public DataType DataType { get; private set; } 24 | public byte UnitLength { get; private set; } 25 | public string FullStartAddress 26 | { 27 | get { return string.Format("{0}{1}", Prefix, StartAddr); } 28 | } 29 | public int Count 30 | { 31 | get { return m_plcDataList.Count; } 32 | } 33 | public bool IsReadOnly 34 | { 35 | get { return false; } 36 | } 37 | public PLCData this[int index] 38 | { 39 | get { return m_plcDataList[index]; } 40 | set { m_plcDataList[index] = value; } 41 | } 42 | 43 | public PLCDataCollection(string name) 44 | { 45 | Name = name; 46 | 47 | Type dataType = typeof(T); 48 | switch (dataType.Name) 49 | { 50 | case "Boolean": 51 | DataType = DataType.BoolAddress; 52 | UnitLength = 1; 53 | break; 54 | case "Int16": 55 | DataType = DataType.Int16Address; 56 | UnitLength = 1; 57 | break; 58 | case "Int32": 59 | DataType = DataType.Int32Address; 60 | UnitLength = 2; 61 | break; 62 | case "Int64": 63 | DataType = DataType.Int64Address; 64 | UnitLength = 4; 65 | break; 66 | case "Float32": 67 | DataType = DataType.Float32Address; 68 | UnitLength = 2; 69 | break; 70 | case "Double64": 71 | DataType = DataType.Double64Address; 72 | UnitLength = 4; 73 | break; 74 | default: 75 | throw new Exception("The DataType is not support!"); 76 | } 77 | 78 | } 79 | 80 | 81 | /// 82 | /// 清空数据集合数据 83 | /// 84 | public void ClearData() 85 | { 86 | foreach (var d in m_plcDataList) 87 | { 88 | d.Data = default(T); 89 | d.OldData = default(T); 90 | } 91 | Update(); 92 | } 93 | 94 | /// 95 | /// 向PLC数据集中添加一个Bit地址 96 | /// 97 | /// 98 | /// 99 | /// 100 | /// 101 | private void AddBit(string name, string addr, uint index = 0) 102 | { 103 | if (DataType != DataType.BoolAddress) 104 | { 105 | return; 106 | } 107 | 108 | string[] splits = addr.Substring(1).Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries); 109 | 110 | PLCData plcData = new PLCData() 111 | { 112 | Name = name, 113 | NameIndex = index, 114 | Prefix = addr[0].ToString(), 115 | Addr = int.Parse(splits[0]), 116 | Bit = byte.Parse(splits[1]), 117 | Length = UnitLength, 118 | IsBit = true 119 | }; 120 | Add(plcData); 121 | } 122 | /// 123 | /// 向PLC数据集中添加多个Bit地址,在原基础地址上自动添加count个Bit地址 124 | /// 125 | /// 126 | /// 127 | /// 128 | /// 129 | private void AddBit(string name, string addr, int count) 130 | { 131 | if (DataType != DataType.BoolAddress || count < 0) 132 | { 133 | return; 134 | } 135 | 136 | int baseAddr = 0; 137 | byte basebit = 0; 138 | string[] splits = addr.Substring(1).Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries); 139 | baseAddr = int.Parse(splits[0]); 140 | basebit = byte.Parse(splits[1]); 141 | 142 | for (int i = 0; i < count; i++) 143 | { 144 | int curAddr = baseAddr + i; 145 | string newAddr = string.Format("{0}{1}.{2}", addr[0], curAddr, basebit); 146 | AddBit(name, newAddr, (uint)i); 147 | } 148 | } 149 | /// 150 | /// 向PLC数据集中添加一个地址 151 | /// 152 | /// 153 | /// 154 | /// 155 | /// 156 | public void Add(string name, string addr, uint index = 0) 157 | { 158 | if (addr.Contains('.')) 159 | { 160 | AddBit(name, addr, index); 161 | return; 162 | } 163 | PLCData plcData = new PLCData() 164 | { 165 | Name = name, 166 | NameIndex = index, 167 | Prefix = addr[0].ToString(), 168 | Addr = int.Parse(addr.Substring(1)) 169 | }; 170 | Add(plcData); 171 | } 172 | /// 173 | /// 向PLC数据集中添加多个地址,在原基础地址上自动添加count个地址 174 | /// 175 | /// 176 | /// 177 | /// 178 | /// 179 | public void Add(string name, string addr, int count) 180 | { 181 | if (addr.Contains('.')) 182 | { 183 | AddBit(name, addr, count); 184 | return; 185 | } 186 | 187 | if (count < 0) 188 | { 189 | return; 190 | } 191 | 192 | int baseAddr = 0; 193 | baseAddr = int.Parse(addr.Substring(1)); 194 | 195 | for (int i = 0; i < count; i++) 196 | { 197 | int curAddr = baseAddr + (i * UnitLength); 198 | 199 | string newAddr = string.Format("{0}{1}", addr[0], curAddr); 200 | Add(name, newAddr, (uint)i); 201 | } 202 | } 203 | 204 | /// 205 | /// 更新数据集,添加或移除集合项后调用 206 | /// 207 | public void Update() 208 | { 209 | if (m_plcDataList.Count <= 0) 210 | { 211 | DataLength = 0; 212 | return; 213 | } 214 | 215 | int startAddr = int.MaxValue; 216 | int endAddr = 0; 217 | int endUnitLength = 1; 218 | 219 | foreach (var d in m_plcDataList) 220 | { 221 | d.Length = UnitLength; 222 | if (d.Addr < startAddr) { startAddr = d.Addr; } 223 | if (d.Addr > endAddr) { endAddr = d.Addr; endUnitLength = d.Length; } 224 | } 225 | 226 | StartAddr = startAddr; 227 | DataLength = (endAddr + endUnitLength) - startAddr; 228 | } 229 | 230 | public void ForEach(Action> action) 231 | { 232 | foreach (var item in m_plcDataList) 233 | { 234 | action(item); 235 | } 236 | } 237 | 238 | 239 | public IEnumerator> GetEnumerator() 240 | { 241 | for (int index = 0; index < m_plcDataList.Count; index++) 242 | { 243 | yield return m_plcDataList[index]; 244 | } 245 | } 246 | 247 | IEnumerator IEnumerable.GetEnumerator() 248 | { 249 | return GetEnumerator(); 250 | } 251 | 252 | public void Clear() 253 | { 254 | m_plcDataList.Clear(); 255 | } 256 | public void Add(PLCData item) 257 | { 258 | if (m_plcDataList.Count == 0) 259 | { 260 | this.Prefix = item.Prefix; 261 | this.IsBitCollection = item.IsBit; 262 | } 263 | 264 | if (this.Prefix == item.Prefix 265 | && this.IsBitCollection == item.IsBit) 266 | { 267 | m_plcDataList.Add(item); 268 | } 269 | } 270 | 271 | public bool Contains(PLCData item) 272 | { 273 | return m_plcDataList.Contains(item); 274 | } 275 | 276 | public void CopyTo(PLCData[] array, int arrayIndex) 277 | { 278 | m_plcDataList.CopyTo(array, arrayIndex); 279 | } 280 | 281 | public bool Remove(PLCData item) 282 | { 283 | return m_plcDataList.Remove(item); 284 | } 285 | 286 | } 287 | } 288 | -------------------------------------------------------------------------------- /PLCReadWrite/PLCControl.String/PLCDataCollection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace PLCReadWrite.PLCControl.String 7 | { 8 | /// 9 | /// PLC数据集合(仅支持同一种地址前缀) 10 | /// 11 | public class PLCDataCollection : ICollection 12 | { 13 | /// 14 | /// 集合内部储存结构 15 | /// 16 | private List m_plcDataList = new List(); 17 | 18 | public string Name { get; private set; } 19 | public string Prefix { get; private set; } 20 | public int StartAddr { get; private set; } 21 | public int DataLength { get; private set; } 22 | public bool IsBitCollection { get; private set; } 23 | public string FullStartAddress 24 | { 25 | get { return string.Format("{0}{1}", Prefix, StartAddr); } 26 | } 27 | public int Count 28 | { 29 | get { return m_plcDataList.Count; } 30 | } 31 | public bool IsReadOnly 32 | { 33 | get { return false; } 34 | } 35 | public PLCData this[int index] 36 | { 37 | get { return m_plcDataList[index]; } 38 | set { m_plcDataList[index] = value; } 39 | } 40 | 41 | public PLCDataCollection(string name) 42 | { 43 | Name = name; 44 | } 45 | 46 | /// 47 | /// 清空数据集数据 48 | /// 49 | public void ClearData() 50 | { 51 | foreach (var d in m_plcDataList) 52 | { 53 | d.Data = ""; 54 | d.OldData = ""; 55 | } 56 | } 57 | /// 58 | /// 向PLC数据集中添加一个Bit地址 59 | /// 60 | /// 61 | /// 62 | /// 63 | public void AddBit(string name, string addr, uint index = 0) 64 | { 65 | if (!addr.Contains('.')) 66 | { 67 | return; 68 | } 69 | 70 | string[] splits = addr.Substring(1).Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries); 71 | 72 | PLCData plcData = new PLCData() 73 | { 74 | Name = name, 75 | NameIndex = index, 76 | Prefix = addr[0].ToString(), 77 | Addr = int.Parse(splits[0]), 78 | Bit = byte.Parse(splits[1]), 79 | DataType = DataType.BoolAddress, 80 | Length = 1, 81 | IsBit = true 82 | }; 83 | Add(plcData); 84 | } 85 | /// 86 | /// 向PLC数据集中添加多个Bit地址,在原基础地址上自动添加count个Bit地址 87 | /// 88 | /// 89 | /// 90 | /// 91 | /// 92 | public void AddBit(string name, string addr, int count) 93 | { 94 | if (!addr.Contains('.') || count < 0) 95 | { 96 | return; 97 | } 98 | 99 | int baseAddr = 0; 100 | byte basebit = 0; 101 | string[] splits = addr.Substring(1).Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries); 102 | baseAddr = int.Parse(splits[0]); 103 | basebit = byte.Parse(splits[1]); 104 | 105 | for (int i = 0; i < count; i++) 106 | { 107 | int curAddr = baseAddr + i; 108 | string newAddr = string.Format("{0}{1}.{2}", addr[0], curAddr, basebit); 109 | AddBit(name, newAddr, (uint)i); 110 | } 111 | } 112 | /// 113 | /// 向PLC数据集中添加一个地址 114 | /// 115 | /// 116 | /// 117 | /// 118 | /// 119 | /// 120 | public void Add(string name, string addr, DataType dataType = DataType.Int16Address, uint index = 0) 121 | { 122 | if (dataType == DataType.StringAddress) 123 | { 124 | return; 125 | } 126 | 127 | PLCData plcData = new PLCData() 128 | { 129 | Name = name, 130 | NameIndex = index, 131 | Prefix = addr[0].ToString(), 132 | Addr = int.Parse(addr.Substring(1)), 133 | DataType = dataType, 134 | Length = GetAddressLength(dataType), 135 | }; 136 | Add(plcData); 137 | } 138 | 139 | /// 140 | /// 向PLC数据集中添加多个地址,在原基础地址上自动添加count个地址 141 | /// 142 | /// 143 | /// 144 | /// 145 | /// 146 | /// 147 | public void Add(string name, string addr, DataType dataType, int count) 148 | { 149 | if (count < 0 || dataType == DataType.StringAddress) 150 | { 151 | return; 152 | } 153 | 154 | int baseAddr = 0; 155 | baseAddr = int.Parse(addr.Substring(1)); 156 | 157 | for (int i = 0; i < count; i++) 158 | { 159 | int length = GetAddressLength(dataType); 160 | int curAddr = baseAddr + (i * length); 161 | string newAddr = string.Format("{0}{1}", addr[0], curAddr); 162 | Add(name, newAddr, dataType, (uint)i); 163 | } 164 | } 165 | 166 | /// 167 | /// 向PLC数据集中添加一个字符串地址 168 | /// 169 | /// 170 | /// 171 | /// 172 | /// 173 | public void AddString(string name, string addr, int length, uint index = 0) 174 | { 175 | PLCData plcData = new PLCData() 176 | { 177 | Name = name, 178 | NameIndex = index, 179 | Prefix = addr[0].ToString(), 180 | Addr = int.Parse(addr.Substring(1)), 181 | DataType = DataType.StringAddress, 182 | Length = length, 183 | }; 184 | Add(plcData); 185 | } 186 | 187 | /// 188 | /// 更新数据集,添加或移除集合项后调用 189 | /// 190 | public void Update() 191 | { 192 | int startAddr = int.MaxValue; 193 | int endAddr = 0; 194 | int endUnitLength = 1; 195 | 196 | foreach (var d in m_plcDataList) 197 | { 198 | byte length = GetAddressLength(d.DataType); 199 | if (length != 0) 200 | { 201 | d.Length = length; 202 | } 203 | 204 | if (d.Addr < startAddr) { startAddr = d.Addr; } 205 | if (d.Addr > endAddr) { endAddr = d.Addr; endUnitLength = d.Length; } 206 | } 207 | 208 | this.StartAddr = startAddr; 209 | this.DataLength = (endAddr + endUnitLength) - startAddr; 210 | } 211 | 212 | private byte GetAddressLength(DataType dataType) 213 | { 214 | byte length = 0; 215 | switch (dataType) 216 | { 217 | case DataType.BoolAddress: 218 | length = 1; 219 | break; 220 | case DataType.Int16Address: 221 | length = 1; 222 | break; 223 | case DataType.Int32Address: 224 | length = 2; 225 | break; 226 | case DataType.Int64Address: 227 | length = 4; 228 | break; 229 | case DataType.Float32Address: 230 | length = 2; 231 | break; 232 | case DataType.Double64Address: 233 | length = 4; 234 | break; 235 | case DataType.StringAddress: 236 | //数据类型为字符串时,长度值由外部传入 237 | break; 238 | default: 239 | break; 240 | } 241 | return length; 242 | } 243 | 244 | public IEnumerator GetEnumerator() 245 | { 246 | for (int index = 0; index < m_plcDataList.Count; index++) 247 | { 248 | yield return m_plcDataList[index]; 249 | } 250 | } 251 | 252 | IEnumerator IEnumerable.GetEnumerator() 253 | { 254 | return GetEnumerator(); 255 | } 256 | 257 | public void Clear() 258 | { 259 | m_plcDataList.Clear(); 260 | } 261 | 262 | public void Add(PLCData item) 263 | { 264 | if (m_plcDataList.Count == 0) 265 | { 266 | this.Prefix = item.Prefix; 267 | this.IsBitCollection = item.IsBit; 268 | } 269 | 270 | if (this.Prefix == item.Prefix 271 | && this.IsBitCollection == item.IsBit) 272 | { 273 | m_plcDataList.Add(item); 274 | } 275 | } 276 | 277 | public bool Contains(PLCData item) 278 | { 279 | return m_plcDataList.Contains(item); 280 | } 281 | 282 | public void CopyTo(PLCData[] array, int arrayIndex) 283 | { 284 | m_plcDataList.CopyTo(array, arrayIndex); 285 | } 286 | 287 | public bool Remove(PLCData item) 288 | { 289 | return m_plcDataList.Remove(item); 290 | } 291 | } 292 | 293 | } 294 | -------------------------------------------------------------------------------- /PLCReadWriteDemo/Program.cs: -------------------------------------------------------------------------------- 1 | using HslCommunication; 2 | using HslCommunication.Core; 3 | using PLCReadWrite; 4 | using PLCReadWrite.PLCControl; 5 | using PLCReadWrite.PLCControl.String; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | 12 | namespace PLCReadWriteDemo 13 | { 14 | class Program 15 | { 16 | private static System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); 17 | 18 | private static readonly object locker = new object(); 19 | 20 | private static SimpleHybirdLock simpleHybird = new SimpleHybirdLock(); 21 | 22 | 23 | 24 | static void Main(string[] args) 25 | { 26 | //PLCCollectionTest(); 27 | 28 | StringPLCCollectionTest(); 29 | 30 | Console.ReadKey(); 31 | } 32 | 33 | private static void PLCCollectionTest() 34 | { 35 | var collection = new PLCDataCollection("温度数据集合"); 36 | sw.Restart(); 37 | collection.Add("Slot1", "D100", 10); 38 | collection.Update(); 39 | sw.Stop(); 40 | Console.WriteLine("Elapsed.TotalMilliseconds:{0}", sw.Elapsed.TotalMilliseconds); 41 | 42 | //foreach (var item in collection) 43 | //{ 44 | // Console.WriteLine(item.ToString()); 45 | //} 46 | 47 | Console.WriteLine("*************************************"); 48 | 49 | IPLC plc = new MelsecPlcA1E("192.168.100.1", 5000); 50 | PLCReadWrite.PLCControl.PLCControl plcControl = new PLCReadWrite.PLCControl.PLCControl(plc); 51 | plcControl.SetPersistentConnection(); 52 | plcControl.AddCollection(0, collection); 53 | 54 | sw.Restart(); 55 | var data = plcControl.GetCollection(0); 56 | plcControl.ReadCollection(ref data); 57 | sw.Stop(); 58 | Console.WriteLine("ReadCollection Elapsed.TotalMilliseconds:{0}", sw.Elapsed.TotalMilliseconds); 59 | 60 | //for (int i = 0; i < 20; i++) 61 | //{ 62 | // sw.Restart(); 63 | // plcControl.ReadCollection(ref data); 64 | // sw.Stop(); 65 | // Console.WriteLine("Elapsed.TotalMilliseconds:{0}", sw.Elapsed.TotalMilliseconds); 66 | //} 67 | 68 | //foreach (var item in collection) 69 | //{ 70 | // Console.WriteLine(item.ToString()); 71 | //} 72 | Console.WriteLine("*************************************"); 73 | } 74 | 75 | private static void StringPLCCollectionTest() 76 | { 77 | var collection = new PLCDataCollection("温度数据集合"); 78 | sw.Restart(); 79 | collection.Add("Slot1", "D100", DataType.Int16Address, 64); 80 | collection.Update(); 81 | sw.Stop(); 82 | Console.WriteLine("Elapsed.TotalMilliseconds:{0}", sw.Elapsed.TotalMilliseconds); 83 | 84 | var collection2 = new PLCDataCollection("温度数据集合"); 85 | sw.Restart(); 86 | collection2.Add("Slot1", "D100", DataType.Int16Address, 64); 87 | collection2.Update(); 88 | sw.Stop(); 89 | Console.WriteLine("Elapsed.TotalMilliseconds:{0}", sw.Elapsed.TotalMilliseconds); 90 | 91 | //foreach (var item in collection) 92 | //{ 93 | // Console.WriteLine(item.ToString()); 94 | //} 95 | //sw.Restart(); 96 | //var query = collection.Where(d => d.NameIndex == 999); 97 | //List list = query.ToList(); 98 | //sw.Stop(); 99 | //Console.WriteLine("query.TotalMilliseconds:{0}", sw.Elapsed.TotalMilliseconds); 100 | 101 | //foreach (var item in list) 102 | //{ 103 | // Console.WriteLine(item.ToString()); 104 | //} 105 | 106 | Console.WriteLine("*************************************"); 107 | 108 | IPLC plc = new MelsecPlcA1E("192.168.0.100", 5000); 109 | PLCReadWrite.PLCControl.String.PLCControl plcControl = new PLCReadWrite.PLCControl.String.PLCControl(plc); 110 | plcControl.SetPersistentConnection(); 111 | 112 | IPLC plc2 = new MelsecPlcA1E("192.168.0.101", 5000); 113 | PLCReadWrite.PLCControl.String.PLCControl plcControl2 = new PLCReadWrite.PLCControl.String.PLCControl(plc2); 114 | plcControl2.SetPersistentConnection(); 115 | 116 | //if (plcControl.Open()) 117 | //{ 118 | // Console.WriteLine("Opened!!!!!!!!!!"); 119 | //} 120 | 121 | sw.Restart(); 122 | plcControl.AddCollection(0, collection); 123 | sw.Stop(); 124 | Console.WriteLine("AddCollection Elapsed.TotalMilliseconds:{0}", sw.Elapsed.TotalMilliseconds); 125 | 126 | sw.Restart(); 127 | plcControl2.AddCollection(0, collection); 128 | sw.Stop(); 129 | Console.WriteLine("AddCollection Elapsed.TotalMilliseconds:{0}", sw.Elapsed.TotalMilliseconds); 130 | 131 | //sw.Restart(); 132 | //var data = plcControl.GetCollection(0); 133 | //sw.Stop(); 134 | 135 | 136 | //plcControl.ReadBoolAsync("M100",2).ContinueWith(ret => 137 | //{ 138 | // Console.WriteLine("ReadBoolAsync:{0}", ret.Result.Content[0]); 139 | //}); 140 | 141 | //for (int i = 0; i < 20; i++) 142 | //{ 143 | // sw.Restart(); 144 | // plcControl.ReadCollection(ref collection); 145 | // sw.Stop(); 146 | // Console.WriteLine("Elapsed.TotalMilliseconds:{0}", sw.Elapsed.TotalMilliseconds); 147 | //} 148 | 149 | //for (short i = 0; i < 64; i++) 150 | //{ 151 | // string address = string.Format("D{0}", i + 100); 152 | // var taskWrite = plcControl.WriteAsync(address, i); 153 | // taskWrite.Wait(); 154 | // Console.WriteLine("WriteAsync {0}:{1}", address, taskWrite.Result.IsSuccess); 155 | //} 156 | Console.WriteLine("*************************************"); 157 | 158 | //for (int i = 0; i < 20; i++) 159 | //{ 160 | // sw.Restart(); 161 | // var task = plcControl.ReadCollectionAsync(collection); 162 | // var task2 = plcControl2.ReadCollectionAsync(collection2); 163 | // task.Wait(); 164 | // task2.Wait(); 165 | // sw.Stop(); 166 | // Console.WriteLine("Elapsed.TotalMilliseconds:{0}", sw.Elapsed.TotalMilliseconds); 167 | //} 168 | List plcList = new List(); 169 | 170 | plcList.Add(plcControl); 171 | plcList.Add(plcControl2); 172 | 173 | Task[] taskWrite = new Task[plcList.Count]; 174 | 175 | Task.Factory.StartNew(() => 176 | { 177 | var sw2 = new System.Diagnostics.Stopwatch(); 178 | 179 | for (int i = 0; i < 10; i++) 180 | { 181 | sw2.Restart(); 182 | 183 | 184 | //lock (locker) 185 | { 186 | Console.WriteLine("开始T:{0}", DateTime.Now.ToString("HH:mm:ss.fff")); 187 | var task = plcControl.WriteAsync("D1", 10); 188 | Console.WriteLine("第1个:{0}", DateTime.Now.ToString("HH:mm:ss.fff")); 189 | var task2 = plcControl2.WriteAsync("D1", 10); 190 | Console.WriteLine("第2个:{0}", DateTime.Now.ToString("HH:mm:ss.fff")); 191 | task.Wait(); 192 | task2.Wait(); 193 | //task2.Wait(); 194 | 195 | Console.WriteLine("完成1T:{0} {1} ", DateTime.Now.ToString("HH:mm:ss.fff"), task.Result.IsSuccess); 196 | Console.WriteLine("完成2T:{0} {1} ", DateTime.Now.ToString("HH:mm:ss.fff"), task2.Result.IsSuccess); 197 | } 198 | 199 | sw2.Stop(); 200 | Console.WriteLine("Sw2 Elapsed.TotalMilliseconds:{0}", sw2.Elapsed.TotalMilliseconds); 201 | 202 | 203 | //sw2.Restart(); 204 | 205 | //for (int index = 0; index < plcList.Count; index++) 206 | //{ 207 | // taskWrite[index] = plcList[index].WriteAsync("D1", 10); 208 | //} 209 | 210 | //foreach (var item in taskWrite) 211 | //{ 212 | // item.Wait(); 213 | //} 214 | 215 | //foreach (var item in taskWrite) 216 | //{ 217 | // Console.WriteLine("WriteAsync : {0} {1}", item.Result.Message, i); 218 | //} 219 | 220 | //sw2.Stop(); 221 | //Console.WriteLine("Sw2 Elapsed.TotalMilliseconds:{0}", sw2.Elapsed.TotalMilliseconds); 222 | 223 | //Task[] taskAarry = new Task[plcList.Count]; 224 | //for (int index = 0; index < plcList.Count; index++) 225 | //{ 226 | // taskAarry[index] = plcList[index].ReadCollectionAsync(plcList[index].GetCollection(0)); 227 | //} 228 | 229 | //foreach (var item in taskAarry) 230 | //{ 231 | // Console.WriteLine("ReadCollectionAsync1 : {0} {1}", item.Result, i); 232 | //} 233 | 234 | Console.WriteLine("*************************************"); 235 | } 236 | 237 | }); 238 | 239 | //Task.Factory.StartNew(() => 240 | //{ 241 | // var sw3 = new System.Diagnostics.Stopwatch(); 242 | // sw3.Restart(); 243 | // for (int i = 0; i < 10; i++) 244 | // { 245 | // //lock (locker) 246 | // { 247 | // Console.WriteLine("线程2开始T:{0}", DateTime.Now.ToString("HH:mm:ss.fff")); 248 | // var ret = plcControl.WriteAsync("D99", 8888); 249 | // Console.WriteLine("线程2第1个:{0}", DateTime.Now.ToString("HH:mm:ss.fff")); 250 | // ret.Wait(); 251 | // Console.WriteLine("WriteInt16 : {0} {1} {2}", ret.Result.IsSuccess, i, DateTime.Now.ToString("HH:mm:ss.fff")); 252 | // //if (!ret.Result.IsSuccess) 253 | // //{ 254 | // // Console.WriteLine("WriteInt16 : {0} {1}", ret.Result.IsSuccess, i); 255 | // //} 256 | // } 257 | // } 258 | // sw3.Stop(); 259 | // Console.WriteLine("Sw3 Elapsed.TotalMilliseconds:{0}", sw3.Elapsed.TotalMilliseconds); 260 | //}); 261 | 262 | //foreach (var item in collection) 263 | //{ 264 | // Console.WriteLine(item.ToString()); 265 | //} 266 | 267 | Console.WriteLine("*************************************"); 268 | } 269 | 270 | 271 | /* 272 | read.IsSuccess = true; 273 | read.Content = new byte[] 274 | { 275 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 276 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 277 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 278 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 279 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 280 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 281 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 282 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 283 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 284 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 285 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 286 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 287 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 288 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 289 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 290 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 291 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 292 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 293 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 294 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 295 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 296 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 297 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 298 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 299 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 300 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 301 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 302 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 303 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 304 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 305 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 306 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 307 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 308 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 309 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 310 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 311 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 312 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 313 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 314 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 315 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 316 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 317 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 318 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 319 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 320 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 321 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 322 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 323 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 324 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 325 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 326 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 327 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 328 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 329 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 330 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 331 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 332 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 333 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 334 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 335 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 336 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 337 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 338 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 339 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 340 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 341 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 342 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 343 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 344 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 345 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 346 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 347 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 348 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 349 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 350 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 351 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 352 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 353 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 354 | 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, 355 | }; 356 | */ 357 | } 358 | } 359 | --------------------------------------------------------------------------------