├── .gitignore ├── LICENSE ├── PlcProject.zip ├── README.md ├── Sharp7Example.sln ├── Sharp7Example ├── App.config ├── Program.cs ├── Properties │ └── AssemblyInfo.cs └── Sharp7Example.csproj └── Sharp7Library ├── Properties └── AssemblyInfo.cs ├── Sharp7.cs └── Sharp7Library.csproj /.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 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | *.VC.VC.opendb 85 | 86 | # Visual Studio profiler 87 | *.psess 88 | *.vsp 89 | *.vspx 90 | *.sap 91 | 92 | # TFS 2012 Local Workspace 93 | $tf/ 94 | 95 | # Guidance Automation Toolkit 96 | *.gpState 97 | 98 | # ReSharper is a .NET coding add-in 99 | _ReSharper*/ 100 | *.[Rr]e[Ss]harper 101 | *.DotSettings.user 102 | 103 | # JustCode is a .NET coding add-in 104 | .JustCode 105 | 106 | # TeamCity is a build add-in 107 | _TeamCity* 108 | 109 | # DotCover is a Code Coverage Tool 110 | *.dotCover 111 | 112 | # NCrunch 113 | _NCrunch_* 114 | .*crunch*.local.xml 115 | nCrunchTemp_* 116 | 117 | # MightyMoose 118 | *.mm.* 119 | AutoTest.Net/ 120 | 121 | # Web workbench (sass) 122 | .sass-cache/ 123 | 124 | # Installshield output folder 125 | [Ee]xpress/ 126 | 127 | # DocProject is a documentation generator add-in 128 | DocProject/buildhelp/ 129 | DocProject/Help/*.HxT 130 | DocProject/Help/*.HxC 131 | DocProject/Help/*.hhc 132 | DocProject/Help/*.hhk 133 | DocProject/Help/*.hhp 134 | DocProject/Help/Html2 135 | DocProject/Help/html 136 | 137 | # Click-Once directory 138 | publish/ 139 | 140 | # Publish Web Output 141 | *.[Pp]ublish.xml 142 | *.azurePubxml 143 | # TODO: Comment the next line if you want to checkin your web deploy settings 144 | # but database connection strings (with potential passwords) will be unencrypted 145 | *.pubxml 146 | *.publishproj 147 | 148 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 149 | # checkin your Azure Web App publish settings, but sensitive information contained 150 | # in these scripts will be unencrypted 151 | PublishScripts/ 152 | 153 | # NuGet Packages 154 | *.nupkg 155 | # The packages folder can be ignored because of Package Restore 156 | **/packages/* 157 | # except build/, which is used as an MSBuild target. 158 | !**/packages/build/ 159 | # Uncomment if necessary however generally it will be regenerated when needed 160 | #!**/packages/repositories.config 161 | # NuGet v3's project.json files produces more ignoreable files 162 | *.nuget.props 163 | *.nuget.targets 164 | 165 | # Microsoft Azure Build Output 166 | csx/ 167 | *.build.csdef 168 | 169 | # Microsoft Azure Emulator 170 | ecf/ 171 | rcf/ 172 | 173 | # Windows Store app package directories and files 174 | AppPackages/ 175 | BundleArtifacts/ 176 | Package.StoreAssociation.xml 177 | _pkginfo.txt 178 | 179 | # Visual Studio cache files 180 | # files ending in .cache can be ignored 181 | *.[Cc]ache 182 | # but keep track of directories ending in .cache 183 | !*.[Cc]ache/ 184 | 185 | # Others 186 | ClientBin/ 187 | ~$* 188 | *~ 189 | *.dbmdl 190 | *.dbproj.schemaview 191 | *.pfx 192 | *.publishsettings 193 | node_modules/ 194 | orleans.codegen.cs 195 | 196 | # Since there are multiple workflows, uncomment next line to ignore bower_components 197 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 198 | #bower_components/ 199 | 200 | # RIA/Silverlight projects 201 | Generated_Code/ 202 | 203 | # Backup & report files from converting an old project file 204 | # to a newer Visual Studio version. Backup files are not needed, 205 | # because we have git ;-) 206 | _UpgradeReport_Files/ 207 | Backup*/ 208 | UpgradeLog*.XML 209 | UpgradeLog*.htm 210 | 211 | # SQL Server files 212 | *.mdf 213 | *.ldf 214 | 215 | # Business Intelligence projects 216 | *.rdl.data 217 | *.bim.layout 218 | *.bim_*.settings 219 | 220 | # Microsoft Fakes 221 | FakesAssemblies/ 222 | 223 | # GhostDoc plugin setting file 224 | *.GhostDoc.xml 225 | 226 | # Node.js Tools for Visual Studio 227 | .ntvs_analysis.dat 228 | 229 | # Visual Studio 6 build log 230 | *.plg 231 | 232 | # Visual Studio 6 workspace options file 233 | *.opt 234 | 235 | # Visual Studio LightSwitch build output 236 | **/*.HTMLClient/GeneratedArtifacts 237 | **/*.DesktopClient/GeneratedArtifacts 238 | **/*.DesktopClient/ModelManifest.xml 239 | **/*.Server/GeneratedArtifacts 240 | **/*.Server/ModelManifest.xml 241 | _Pvt_Extensions 242 | 243 | # Paket dependency manager 244 | .paket/paket.exe 245 | paket-files/ 246 | 247 | # FAKE - F# Make 248 | .fake/ 249 | 250 | # JetBrains Rider 251 | .idea/ 252 | *.sln.iml 253 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Michele Cattafesta 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PlcProject.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mesta1/Sharp7-example/7778331a4d49249511fe2bb8e6297ebbeb5310b8/PlcProject.zip -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sharp7-example 2 | 3 | Example on how to communicate to Siemens S7 plc with C# and Sharp7 library. 4 | 5 | This is the sample code used in this article: https://www.mesta-automation.com/how-to-write-a-siemens-s7-plc-driver-with-c-and-sharp7/ 6 | -------------------------------------------------------------------------------- /Sharp7Example.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sharp7Library", "Sharp7Library\Sharp7Library.csproj", "{D1A175B9-9CCA-4718-998C-4C882D37D5DA}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sharp7Example", "Sharp7Example\Sharp7Example.csproj", "{AB52695C-FF24-48F8-85A5-1137AED011C6}" 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 | {D1A175B9-9CCA-4718-998C-4C882D37D5DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {D1A175B9-9CCA-4718-998C-4C882D37D5DA}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {D1A175B9-9CCA-4718-998C-4C882D37D5DA}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {D1A175B9-9CCA-4718-998C-4C882D37D5DA}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {AB52695C-FF24-48F8-85A5-1137AED011C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {AB52695C-FF24-48F8-85A5-1137AED011C6}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {AB52695C-FF24-48F8-85A5-1137AED011C6}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {AB52695C-FF24-48F8-85A5-1137AED011C6}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /Sharp7Example/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Sharp7Example/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Sharp7; 7 | 8 | namespace Sharp7Example 9 | { 10 | class Program 11 | { 12 | static void Main(string[] args) 13 | { 14 | //-------------- Create and connect the client 15 | var client = new S7Client(); 16 | int result = client.ConnectTo("127.0.0.1", 0, 1); 17 | if (result == 0) 18 | { 19 | Console.WriteLine("Connected to 127.0.0.1"); 20 | } 21 | else 22 | { 23 | Console.WriteLine(client.ErrorText(result)); 24 | Console.ReadKey(); 25 | return; 26 | } 27 | 28 | //-------------- Read db1 29 | Console.WriteLine("\n---- Read DB 1"); 30 | 31 | byte[] db1Buffer = new byte[18]; 32 | result = client.DBRead(1, 0, 18, db1Buffer); 33 | if (result != 0) 34 | { 35 | Console.WriteLine("Error: " + client.ErrorText(result)); 36 | } 37 | int db1dbw2 = S7.GetIntAt(db1Buffer, 2); 38 | Console.WriteLine("DB1.DBW2: " + db1dbw2); 39 | 40 | double db1dbd4 = S7.GetRealAt(db1Buffer, 4); 41 | Console.WriteLine("DB1.DBD4: " + db1dbd4); 42 | 43 | double db1dbd8 = S7.GetDIntAt(db1Buffer, 8); 44 | Console.WriteLine("DB1.DBD8: " + db1dbd8); 45 | 46 | double db1dbd12 = S7.GetDWordAt(db1Buffer, 12); 47 | Console.WriteLine("DB1.DBD12: " + db1dbd12); 48 | 49 | double db1dbw16 = S7.GetWordAt(db1Buffer, 16); 50 | Console.WriteLine("DB1.DBD16: " + db1dbw16); 51 | 52 | //-------------- Read DB3 53 | Console.WriteLine("\n---- Read DB 3"); 54 | 55 | byte[] db3Buffer = new byte[18]; 56 | result = client.DBRead(3, 0, 18, db3Buffer); 57 | if (result != 0) 58 | { 59 | Console.WriteLine("Error: " + client.ErrorText(result)); 60 | } 61 | int db3dbw2 = S7.GetIntAt(db3Buffer, 2); 62 | Console.WriteLine("DB3.DBW2: " + db3dbw2); 63 | 64 | double db3dbd4 = S7.GetRealAt(db3Buffer, 4); 65 | Console.WriteLine("DB3.DBD4: " + db3dbd4); 66 | 67 | double db3dbd8 = S7.GetDIntAt(db3Buffer, 8); 68 | Console.WriteLine("DB3.DBD8: " + db3dbd8); 69 | 70 | uint db3dbd12 = S7.GetDWordAt(db3Buffer, 12); 71 | Console.WriteLine("DB3.DBD12: " + db3dbd12); 72 | 73 | ushort db3dbd16 = S7.GetWordAt(db3Buffer, 16); 74 | Console.WriteLine("DB3.DBD16: " + db3dbd16); 75 | 76 | //-------------- Write Db1 77 | Console.WriteLine("\n---- Write BD 1"); 78 | 79 | db1Buffer = new byte[12]; 80 | const int START_INDEX = 4; 81 | S7.SetRealAt(db1Buffer, 4 - START_INDEX, (float)54.36); 82 | S7.SetDIntAt(db1Buffer, 8 - START_INDEX, 555666); 83 | S7.SetDWordAt(db1Buffer, 12 - START_INDEX, 123456); 84 | result = client.DBWrite(1, START_INDEX, db1Buffer.Length, db1Buffer); 85 | if (result != 0) 86 | { 87 | Console.WriteLine("Error: " + client.ErrorText(result)); 88 | } 89 | 90 | //-------------- Read multi vars 91 | var s7MultiVar = new S7MultiVar(client); 92 | byte[] db1 = new byte[18]; 93 | s7MultiVar.Add(S7Consts.S7AreaDB, S7Consts.S7WLByte, 1, 0, db1.Length, ref db1); 94 | byte[] db3 = new byte[18]; 95 | s7MultiVar.Add(S7Consts.S7AreaDB, S7Consts.S7WLByte, 3, 0, db3.Length, ref db3); 96 | result = s7MultiVar.Read(); 97 | if (result != 0) 98 | { 99 | Console.WriteLine("Error on s7MultiVar.Read()"); 100 | } 101 | 102 | db1dbw2 = S7.GetIntAt(db1, 2); 103 | Console.WriteLine("DB1.DBW2.0 = {0}", db1dbw2); 104 | 105 | db1dbd4 = S7.GetRealAt(db1, 4); 106 | Console.WriteLine("DB1.DBW4.0 = {0}", db1dbd4); 107 | 108 | db1dbd8 = S7.GetDIntAt(db1, 8); 109 | Console.WriteLine("DB1.DBW8.0 = {0}", db1dbd8); 110 | 111 | db3dbw2 = S7.GetIntAt(db3, 2); 112 | Console.WriteLine("DB3.DBW2.0 = {0}", db3dbw2); 113 | 114 | db3dbd4 = S7.GetRealAt(db3, 4); 115 | Console.WriteLine("DB3.DBW4.0 = {0}", db3dbd4); 116 | 117 | db3dbd8 = S7.GetDIntAt(db3, 8); 118 | Console.WriteLine("DB3.DBW8.0 = {0}", db3dbd8); 119 | 120 | //-------------- Write multi vars 121 | s7MultiVar = new S7MultiVar(client); 122 | const int DB1_START_INDEX = 2; 123 | db1 = new byte[10]; 124 | S7.SetIntAt(db1, 2 - DB1_START_INDEX, 50); 125 | S7.SetRealAt(db1, 4 - DB1_START_INDEX, (float)36.5); 126 | S7.SetDIntAt(db1, 8 - DB1_START_INDEX, 123456); 127 | s7MultiVar.Add(S7Consts.S7AreaDB, S7Consts.S7WLByte, 1, DB1_START_INDEX, db1.Length, ref db1); 128 | 129 | const int DB3_START_INDEX = 2; 130 | db3 = new byte[10]; 131 | S7.SetIntAt(db3, 2 - DB3_START_INDEX, -50); 132 | S7.SetRealAt(db3, 4 - DB3_START_INDEX, (float)-25.36); 133 | S7.SetDIntAt(db3, 8 - DB3_START_INDEX, -123456); 134 | s7MultiVar.Add(S7Consts.S7AreaDB, S7Consts.S7WLByte, 3, DB3_START_INDEX, db3.Length, ref db3); 135 | result = s7MultiVar.Write(); 136 | if (result != 0) 137 | { 138 | Console.WriteLine("Error on s7MultiVar.Read()"); 139 | } 140 | 141 | //-------------- Disconnect the client 142 | client.Disconnect(); 143 | Console.ReadKey(); 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /Sharp7Example/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Sharp7Example")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Sharp7Example")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("ab52695c-ff24-48f8-85a5-1137aed011c6")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /Sharp7Example/Sharp7Example.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {AB52695C-FF24-48F8-85A5-1137AED011C6} 8 | Exe 9 | Properties 10 | Sharp7Example 11 | Sharp7Example 12 | v4.5.2 13 | 512 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | {d1a175b9-9cca-4718-998c-4c882d37d5da} 55 | Sharp7Library 56 | 57 | 58 | 59 | 66 | -------------------------------------------------------------------------------- /Sharp7Library/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Sharp7Library")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Sharp7Library")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("d1a175b9-9cca-4718-998c-4c882d37d5da")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /Sharp7Library/Sharp7.cs: -------------------------------------------------------------------------------- 1 | /*=============================================================================| 2 | | PROJECT Sharp7 1.0.3 | 3 | |==============================================================================| 4 | | Copyright (C) 2016 Davide Nardella | 5 | | All rights reserved. | 6 | |==============================================================================| 7 | | Sharp7 is free software: you can redistribute it and/or modify | 8 | | it under the terms of the Lesser GNU General Public License as published by | 9 | | the Free Software Foundation, either version 3 of the License, or | 10 | | (at your option) any later version. | 11 | | | 12 | | It means that you can distribute your commercial software which includes | 13 | | Sharp7 without the requirement to distribute the source code of your | 14 | | application and without the requirement that your application be itself | 15 | | distributed under LGPL. | 16 | | | 17 | | Sharp7 is distributed in the hope that it will be useful, | 18 | | but WITHOUT ANY WARRANTY; without even the implied warranty of | 19 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 20 | | Lesser GNU General Public License for more details. | 21 | | | 22 | | You should have received a copy of the GNU General Public License and a | 23 | | copy of Lesser GNU General Public License along with Sharp7. | 24 | | If not, see http://www.gnu.org/licenses/ | 25 | |==============================================================================| 26 | History: 27 | * 1.0.0 2016/10/09 First Release 28 | * 1.0.1 2016/10/22 Added CoreCLR compatibility (CORE_CLR symbol must be 29 | defined in Build options). 30 | Thanks to Dirk-Jan Wassink. 31 | * 1.0.2 2016/11/13 Fixed a bug in CLR compatibility 32 | * 1.0.3 2017/01/25 Fixed a bug in S7.GetIntAt(). Thanks to lupal1 33 | Added S7Timer Read/Write. Thanks to Lukas Palkovic 34 | 35 | */ 36 | using System; 37 | using System.Runtime.InteropServices; 38 | using System.Text; 39 | using System.Threading; 40 | using System.Collections.Generic; 41 | //------------------------------------------------------------------------------ 42 | // If you are compiling for UWP verify that WINDOWS_UWP or NETFX_CORE are 43 | // defined into Project Properties->Build->Conditional compilation symbols 44 | //------------------------------------------------------------------------------ 45 | #if WINDOWS_UWP || NETFX_CORE 46 | using System.Threading.Tasks; 47 | using Windows.Networking; 48 | using Windows.Networking.Sockets; 49 | using Windows.Storage.Streams; 50 | #else // <-- Including MONO 51 | using System.Net.Sockets; 52 | #endif 53 | 54 | namespace Sharp7 55 | { 56 | 57 | #region [Async Sockets UWP(W10,IoT,Phone)/Windows 8/Windows 8 Phone] 58 | #if WINDOWS_UWP || NETFX_CORE 59 | class MsgSocket 60 | { 61 | private DataReader Reader = null; 62 | private DataWriter Writer = null; 63 | private StreamSocket TCPSocket; 64 | 65 | private bool _Connected; 66 | 67 | private int _ReadTimeout = 2000; 68 | private int _WriteTimeout = 2000; 69 | private int _ConnectTimeout = 1000; 70 | 71 | public static int LastError = 0; 72 | 73 | 74 | private void CreateSocket() 75 | { 76 | TCPSocket = new StreamSocket(); 77 | TCPSocket.Control.NoDelay = true; 78 | _Connected = false; 79 | } 80 | 81 | public MsgSocket() 82 | { 83 | } 84 | 85 | public void Close() 86 | { 87 | if (Reader != null) 88 | { 89 | Reader.Dispose(); 90 | Reader = null; 91 | } 92 | if (Writer != null) 93 | { 94 | Writer.Dispose(); 95 | Writer = null; 96 | } 97 | if (TCPSocket != null) 98 | { 99 | TCPSocket.Dispose(); 100 | TCPSocket = null; 101 | } 102 | _Connected = false; 103 | } 104 | 105 | private async Task AsConnect(string Host, string port, CancellationTokenSource cts) 106 | { 107 | HostName ServerHost = new HostName(Host); 108 | try 109 | { 110 | await TCPSocket.ConnectAsync(ServerHost, port).AsTask(cts.Token); 111 | _Connected = true; 112 | } 113 | catch (TaskCanceledException) 114 | { 115 | LastError = S7Consts.errTCPConnectionTimeout; 116 | } 117 | catch 118 | { 119 | LastError = S7Consts.errTCPConnectionFailed; // Maybe unreachable peer 120 | } 121 | } 122 | 123 | public int Connect(string Host, int Port) 124 | { 125 | LastError = 0; 126 | if (!Connected) 127 | { 128 | CreateSocket(); 129 | CancellationTokenSource cts = new CancellationTokenSource(); 130 | try 131 | { 132 | try 133 | { 134 | cts.CancelAfter(_ConnectTimeout); 135 | Task.WaitAny(Task.Run(async () => await AsConnect(Host, Port.ToString(), cts))); 136 | } 137 | catch 138 | { 139 | LastError = S7Consts.errTCPConnectionFailed; 140 | } 141 | } 142 | finally 143 | { 144 | if (cts != null) 145 | { 146 | try 147 | { 148 | cts.Cancel(); 149 | cts.Dispose(); 150 | cts = null; 151 | } 152 | catch { } 153 | } 154 | 155 | } 156 | if (LastError == 0) 157 | { 158 | Reader = new DataReader(TCPSocket.InputStream); 159 | Reader.InputStreamOptions = InputStreamOptions.Partial; 160 | Writer = new DataWriter(TCPSocket.OutputStream); 161 | _Connected = true; 162 | } 163 | else 164 | Close(); 165 | } 166 | return LastError; 167 | } 168 | 169 | private async Task AsReadBuffer(byte[] Buffer, int Size, CancellationTokenSource cts) 170 | { 171 | try 172 | { 173 | await Reader.LoadAsync((uint)Size).AsTask(cts.Token); 174 | Reader.ReadBytes(Buffer); 175 | } 176 | catch 177 | { 178 | LastError = S7Consts.errTCPDataReceive; 179 | } 180 | } 181 | 182 | public int Receive(byte[] Buffer, int Start, int Size) 183 | { 184 | byte[] InBuffer = new byte[Size]; 185 | CancellationTokenSource cts = new CancellationTokenSource(); 186 | LastError = 0; 187 | try 188 | { 189 | try 190 | { 191 | cts.CancelAfter(_ReadTimeout); 192 | Task.WaitAny(Task.Run(async () => await AsReadBuffer(InBuffer, Size, cts))); 193 | } 194 | catch 195 | { 196 | LastError = S7Consts.errTCPDataReceive; 197 | } 198 | } 199 | finally 200 | { 201 | if (cts != null) 202 | { 203 | try 204 | { 205 | cts.Cancel(); 206 | cts.Dispose(); 207 | cts = null; 208 | } 209 | catch { } 210 | } 211 | } 212 | if (LastError == 0) 213 | Array.Copy(InBuffer, 0, Buffer, Start, Size); 214 | else 215 | Close(); 216 | return LastError; 217 | } 218 | 219 | private async Task WriteBuffer(byte[] Buffer, CancellationTokenSource cts) 220 | { 221 | try 222 | { 223 | Writer.WriteBytes(Buffer); 224 | await Writer.StoreAsync().AsTask(cts.Token); 225 | } 226 | catch 227 | { 228 | LastError = S7Consts.errTCPDataSend; 229 | } 230 | } 231 | 232 | public int Send(byte[] Buffer, int Size) 233 | { 234 | byte[] OutBuffer = new byte[Size]; 235 | CancellationTokenSource cts = new CancellationTokenSource(); 236 | Array.Copy(Buffer, 0, OutBuffer, 0, Size); 237 | LastError = 0; 238 | try 239 | { 240 | try 241 | { 242 | cts.CancelAfter(_WriteTimeout); 243 | Task.WaitAny(Task.Run(async () => await WriteBuffer(OutBuffer, cts))); 244 | } 245 | catch 246 | { 247 | LastError = S7Consts.errTCPDataSend; 248 | } 249 | } 250 | finally 251 | { 252 | if (cts != null) 253 | { 254 | try 255 | { 256 | cts.Cancel(); 257 | cts.Dispose(); 258 | cts = null; 259 | } 260 | catch { } 261 | } 262 | } 263 | if (LastError != 0) 264 | Close(); 265 | return LastError; 266 | } 267 | 268 | ~MsgSocket() 269 | { 270 | Close(); 271 | } 272 | 273 | public bool Connected 274 | { 275 | get 276 | { 277 | return (TCPSocket != null) && _Connected; 278 | } 279 | } 280 | 281 | public int ReadTimeout 282 | { 283 | get 284 | { 285 | return _ReadTimeout; 286 | } 287 | set 288 | { 289 | _ReadTimeout = value; 290 | } 291 | } 292 | 293 | public int WriteTimeout 294 | { 295 | get 296 | { 297 | return _WriteTimeout; 298 | } 299 | set 300 | { 301 | _WriteTimeout = value; 302 | } 303 | } 304 | public int ConnectTimeout 305 | { 306 | get 307 | { 308 | return _ConnectTimeout; 309 | } 310 | set 311 | { 312 | _ConnectTimeout = value; 313 | } 314 | } 315 | } 316 | #endif 317 | #endregion 318 | 319 | #region [Sync Sockets Win32/Win64 Desktop Application] 320 | #if !WINDOWS_UWP && !NETFX_CORE 321 | class MsgSocket 322 | { 323 | private Socket TCPSocket; 324 | private int _ReadTimeout = 2000; 325 | private int _WriteTimeout = 2000; 326 | private int _ConnectTimeout = 1000; 327 | public int LastError = 0; 328 | 329 | public MsgSocket() 330 | { 331 | } 332 | 333 | ~MsgSocket() 334 | { 335 | Close(); 336 | } 337 | 338 | public void Close() 339 | { 340 | if (TCPSocket != null) 341 | { 342 | TCPSocket.Dispose(); 343 | TCPSocket = null; 344 | } 345 | } 346 | 347 | private void CreateSocket() 348 | { 349 | TCPSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 350 | TCPSocket.NoDelay = true; 351 | } 352 | 353 | private void TCPPing(string Host, int Port) 354 | { 355 | // To Ping the PLC an Asynchronous socket is used rather then an ICMP packet. 356 | // This allows the use also across Internet and Firewalls (obviously the port must be opened) 357 | LastError = 0; 358 | Socket PingSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 359 | try 360 | { 361 | 362 | #if CORE_CLR 363 | var task = PingSocket.ConnectAsync(Host, Port); 364 | task.Wait(_ConnectTimeout); 365 | bool success = task.IsCompleted; 366 | #else 367 | IAsyncResult result = PingSocket.BeginConnect(Host, Port, null, null); 368 | bool success = result.AsyncWaitHandle.WaitOne(_ConnectTimeout, true); 369 | #endif 370 | if (!success) 371 | { 372 | LastError = S7Consts.errTCPConnectionFailed; 373 | } 374 | } 375 | catch 376 | { 377 | LastError = S7Consts.errTCPConnectionFailed; 378 | }; 379 | #if CORE_CLR 380 | PingSocket.Dispose(); 381 | #else 382 | PingSocket.Close(); 383 | #endif 384 | } 385 | 386 | public int Connect(string Host, int Port) 387 | { 388 | LastError = 0; 389 | if (!Connected) 390 | { 391 | TCPPing(Host, Port); 392 | if (LastError == 0) 393 | try 394 | { 395 | CreateSocket(); 396 | TCPSocket.Connect(Host, Port); 397 | } 398 | catch 399 | { 400 | LastError = S7Consts.errTCPConnectionFailed; 401 | } 402 | } 403 | return LastError; 404 | } 405 | 406 | private int WaitForData(int Size, int Timeout) 407 | { 408 | bool Expired = false; 409 | int SizeAvail; 410 | int Elapsed = Environment.TickCount; 411 | LastError = 0; 412 | try 413 | { 414 | SizeAvail = TCPSocket.Available; 415 | while ((SizeAvail < Size) && (!Expired)) 416 | { 417 | Thread.Sleep(2); 418 | SizeAvail = TCPSocket.Available; 419 | Expired = Environment.TickCount - Elapsed > Timeout; 420 | // If timeout we clean the buffer 421 | if (Expired && (SizeAvail > 0)) 422 | try 423 | { 424 | byte[] Flush = new byte[SizeAvail]; 425 | TCPSocket.Receive(Flush, 0, SizeAvail, SocketFlags.None); 426 | } 427 | catch { } 428 | } 429 | } 430 | catch 431 | { 432 | LastError = S7Consts.errTCPDataReceive; 433 | } 434 | if (Expired) 435 | { 436 | LastError = S7Consts.errTCPDataReceive; 437 | } 438 | return LastError; 439 | } 440 | 441 | public int Receive(byte[] Buffer, int Start, int Size) 442 | { 443 | 444 | int BytesRead = 0; 445 | LastError = WaitForData(Size, _ReadTimeout); 446 | if (LastError == 0) 447 | { 448 | try 449 | { 450 | BytesRead = TCPSocket.Receive(Buffer, Start, Size, SocketFlags.None); 451 | } 452 | catch 453 | { 454 | LastError = S7Consts.errTCPDataReceive; 455 | } 456 | if (BytesRead == 0) // Connection Reset by the peer 457 | { 458 | LastError = S7Consts.errTCPDataReceive; 459 | Close(); 460 | } 461 | } 462 | return LastError; 463 | } 464 | 465 | public int Send(byte[] Buffer, int Size) 466 | { 467 | LastError = 0; 468 | try 469 | { 470 | int BytesSent = TCPSocket.Send(Buffer, Size, SocketFlags.None); 471 | } 472 | catch 473 | { 474 | LastError = S7Consts.errTCPDataSend; 475 | Close(); 476 | } 477 | return LastError; 478 | } 479 | 480 | public bool Connected 481 | { 482 | get 483 | { 484 | return (TCPSocket != null) && (TCPSocket.Connected); 485 | } 486 | } 487 | 488 | public int ReadTimeout 489 | { 490 | get 491 | { 492 | return _ReadTimeout; 493 | } 494 | set 495 | { 496 | _ReadTimeout = value; 497 | } 498 | } 499 | 500 | public int WriteTimeout 501 | { 502 | get 503 | { 504 | return _WriteTimeout; 505 | } 506 | set 507 | { 508 | _WriteTimeout = value; 509 | } 510 | 511 | } 512 | public int ConnectTimeout 513 | { 514 | get 515 | { 516 | return _ConnectTimeout; 517 | } 518 | set 519 | { 520 | _ConnectTimeout = value; 521 | } 522 | } 523 | } 524 | #endif 525 | #endregion 526 | 527 | public static class S7Consts 528 | { 529 | #region [Exported Consts] 530 | // Error codes 531 | //------------------------------------------------------------------------------ 532 | // ERRORS 533 | //------------------------------------------------------------------------------ 534 | public const int errTCPSocketCreation = 0x00000001; 535 | public const int errTCPConnectionTimeout = 0x00000002; 536 | public const int errTCPConnectionFailed = 0x00000003; 537 | public const int errTCPReceiveTimeout = 0x00000004; 538 | public const int errTCPDataReceive = 0x00000005; 539 | public const int errTCPSendTimeout = 0x00000006; 540 | public const int errTCPDataSend = 0x00000007; 541 | public const int errTCPConnectionReset = 0x00000008; 542 | public const int errTCPNotConnected = 0x00000009; 543 | public const int errTCPUnreachableHost = 0x00002751; 544 | 545 | public const int errIsoConnect = 0x00010000; // Connection error 546 | public const int errIsoInvalidPDU = 0x00030000; // Bad format 547 | public const int errIsoInvalidDataSize = 0x00040000; // Bad Datasize passed to send/recv : buffer is invalid 548 | 549 | public const int errCliNegotiatingPDU = 0x00100000; 550 | public const int errCliInvalidParams = 0x00200000; 551 | public const int errCliJobPending = 0x00300000; 552 | public const int errCliTooManyItems = 0x00400000; 553 | public const int errCliInvalidWordLen = 0x00500000; 554 | public const int errCliPartialDataWritten = 0x00600000; 555 | public const int errCliSizeOverPDU = 0x00700000; 556 | public const int errCliInvalidPlcAnswer = 0x00800000; 557 | public const int errCliAddressOutOfRange = 0x00900000; 558 | public const int errCliInvalidTransportSize = 0x00A00000; 559 | public const int errCliWriteDataSizeMismatch = 0x00B00000; 560 | public const int errCliItemNotAvailable = 0x00C00000; 561 | public const int errCliInvalidValue = 0x00D00000; 562 | public const int errCliCannotStartPLC = 0x00E00000; 563 | public const int errCliAlreadyRun = 0x00F00000; 564 | public const int errCliCannotStopPLC = 0x01000000; 565 | public const int errCliCannotCopyRamToRom = 0x01100000; 566 | public const int errCliCannotCompress = 0x01200000; 567 | public const int errCliAlreadyStop = 0x01300000; 568 | public const int errCliFunNotAvailable = 0x01400000; 569 | public const int errCliUploadSequenceFailed = 0x01500000; 570 | public const int errCliInvalidDataSizeRecvd = 0x01600000; 571 | public const int errCliInvalidBlockType = 0x01700000; 572 | public const int errCliInvalidBlockNumber = 0x01800000; 573 | public const int errCliInvalidBlockSize = 0x01900000; 574 | public const int errCliNeedPassword = 0x01D00000; 575 | public const int errCliInvalidPassword = 0x01E00000; 576 | public const int errCliNoPasswordToSetOrClear = 0x01F00000; 577 | public const int errCliJobTimeout = 0x02000000; 578 | public const int errCliPartialDataRead = 0x02100000; 579 | public const int errCliBufferTooSmall = 0x02200000; 580 | public const int errCliFunctionRefused = 0x02300000; 581 | public const int errCliDestroying = 0x02400000; 582 | public const int errCliInvalidParamNumber = 0x02500000; 583 | public const int errCliCannotChangeParam = 0x02600000; 584 | public const int errCliFunctionNotImplemented = 0x02700000; 585 | //------------------------------------------------------------------------------ 586 | // PARAMS LIST FOR COMPATIBILITY WITH Snap7.net.cs 587 | //------------------------------------------------------------------------------ 588 | public const Int32 p_u16_LocalPort = 1; // Not applicable here 589 | public const Int32 p_u16_RemotePort = 2; 590 | public const Int32 p_i32_PingTimeout = 3; 591 | public const Int32 p_i32_SendTimeout = 4; 592 | public const Int32 p_i32_RecvTimeout = 5; 593 | public const Int32 p_i32_WorkInterval = 6; // Not applicable here 594 | public const Int32 p_u16_SrcRef = 7; // Not applicable here 595 | public const Int32 p_u16_DstRef = 8; // Not applicable here 596 | public const Int32 p_u16_SrcTSap = 9; // Not applicable here 597 | public const Int32 p_i32_PDURequest = 10; 598 | public const Int32 p_i32_MaxClients = 11; // Not applicable here 599 | public const Int32 p_i32_BSendTimeout = 12; // Not applicable here 600 | public const Int32 p_i32_BRecvTimeout = 13; // Not applicable here 601 | public const Int32 p_u32_RecoveryTime = 14; // Not applicable here 602 | public const Int32 p_u32_KeepAliveTime = 15; // Not applicable here 603 | // Area ID 604 | public const byte S7AreaPE = 0x81; 605 | public const byte S7AreaPA = 0x82; 606 | public const byte S7AreaMK = 0x83; 607 | public const byte S7AreaDB = 0x84; 608 | public const byte S7AreaCT = 0x1C; 609 | public const byte S7AreaTM = 0x1D; 610 | // Word Length 611 | public const int S7WLBit = 0x01; 612 | public const int S7WLByte = 0x02; 613 | public const int S7WLChar = 0x03; 614 | public const int S7WLWord = 0x04; 615 | public const int S7WLInt = 0x05; 616 | public const int S7WLDWord = 0x06; 617 | public const int S7WLDInt = 0x07; 618 | public const int S7WLReal = 0x08; 619 | public const int S7WLCounter = 0x1C; 620 | public const int S7WLTimer = 0x1D; 621 | // PLC Status 622 | public const int S7CpuStatusUnknown = 0x00; 623 | public const int S7CpuStatusRun = 0x08; 624 | public const int S7CpuStatusStop = 0x04; 625 | 626 | [StructLayout(LayoutKind.Sequential, Pack = 1)] 627 | public struct S7Tag 628 | { 629 | public Int32 Area; 630 | public Int32 DBNumber; 631 | public Int32 Start; 632 | public Int32 Elements; 633 | public Int32 WordLen; 634 | } 635 | #endregion 636 | } 637 | 638 | public class S7Timer 639 | { 640 | #region S7Timer 641 | TimeSpan pt; 642 | TimeSpan et; 643 | bool input = false; 644 | bool q = false; 645 | public S7Timer(byte[] buff, int position) 646 | { 647 | if (position + 12 < buff.Length) 648 | { 649 | return; 650 | } 651 | else 652 | { 653 | SetTimer(new List(buff).GetRange(position, 16).ToArray()); 654 | } 655 | } 656 | 657 | public S7Timer(byte[] buff) 658 | { 659 | SetTimer(buff); 660 | } 661 | 662 | private void SetTimer(byte[] buff) 663 | { 664 | if (buff.Length != 12) 665 | { 666 | this.pt = new TimeSpan(0); 667 | this.et = new TimeSpan(0); 668 | } 669 | else 670 | { 671 | Int32 resPT; 672 | resPT = buff[0]; resPT <<= 8; 673 | resPT += buff[1]; resPT <<= 8; 674 | resPT += buff[2]; resPT <<= 8; 675 | resPT += buff[3]; 676 | this.pt = new TimeSpan(0, 0, 0, 0, resPT); 677 | 678 | Int32 resET; 679 | resET = buff[4]; resET <<= 8; 680 | resET += buff[5]; resET <<= 8; 681 | resET += buff[6]; resET <<= 8; 682 | resET += buff[7]; 683 | this.et = new TimeSpan(0, 0, 0, 0, resET); 684 | 685 | this.input = (buff[8] & 0x01) == 0x01; 686 | this.q = (buff[8] & 0x02) == 0x02; 687 | } 688 | } 689 | public TimeSpan PT 690 | { 691 | get 692 | { 693 | return pt; 694 | } 695 | } 696 | public TimeSpan ET 697 | { 698 | get 699 | { 700 | return et; 701 | } 702 | } 703 | public bool IN 704 | { 705 | get 706 | { 707 | return input; 708 | } 709 | } 710 | public bool Q 711 | { 712 | get 713 | { 714 | return q; 715 | } 716 | } 717 | #endregion 718 | } 719 | 720 | public static class S7 721 | { 722 | #region [Help Functions] 723 | 724 | private static Int64 bias = 621355968000000000; // "decimicros" between 0001-01-01 00:00:00 and 1970-01-01 00:00:00 725 | 726 | private static int BCDtoByte(byte B) 727 | { 728 | return ((B >> 4) * 10) + (B & 0x0F); 729 | } 730 | 731 | private static byte ByteToBCD(int Value) 732 | { 733 | return (byte)(((Value / 10) << 4) | (Value % 10)); 734 | } 735 | 736 | private static byte[] CopyFrom(byte[] Buffer, int Pos, int Size) 737 | { 738 | byte[] Result=new byte[Size]; 739 | Array.Copy(Buffer, Pos, Result, 0, Size); 740 | return Result; 741 | } 742 | 743 | public static int DataSizeByte(int WordLength) 744 | { 745 | switch (WordLength) 746 | { 747 | case S7Consts.S7WLBit: return 1; // S7 sends 1 byte per bit 748 | case S7Consts.S7WLByte: return 1; 749 | case S7Consts.S7WLChar: return 1; 750 | case S7Consts.S7WLWord: return 2; 751 | case S7Consts.S7WLDWord: return 4; 752 | case S7Consts.S7WLInt: return 2; 753 | case S7Consts.S7WLDInt: return 4; 754 | case S7Consts.S7WLReal: return 4; 755 | case S7Consts.S7WLCounter: return 2; 756 | case S7Consts.S7WLTimer: return 2; 757 | default: return 0; 758 | } 759 | } 760 | 761 | #region Get/Set the bit at Pos.Bit 762 | public static bool GetBitAt(byte[] Buffer, int Pos, int Bit) 763 | { 764 | byte[] Mask = {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80}; 765 | if (Bit < 0) Bit = 0; 766 | if (Bit > 7) Bit = 7; 767 | return (Buffer[Pos] & Mask[Bit]) != 0; 768 | } 769 | public static void SetBitAt(ref byte[] Buffer, int Pos, int Bit, bool Value) 770 | { 771 | byte[] Mask = {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80}; 772 | if (Bit < 0) Bit = 0; 773 | if (Bit > 7) Bit = 7; 774 | 775 | if (Value) 776 | Buffer[Pos] = (byte)(Buffer[Pos] | Mask[Bit]); 777 | else 778 | Buffer[Pos] = (byte)(Buffer[Pos] & ~Mask[Bit]); 779 | } 780 | #endregion 781 | 782 | #region Get/Set 8 bit signed value (S7 SInt) -128..127 783 | public static int GetSIntAt(byte[] Buffer, int Pos) 784 | { 785 | int Value = Buffer[Pos]; 786 | if (Value < 128) 787 | return Value; 788 | else 789 | return (int) (Value - 256); 790 | } 791 | public static void SetSIntAt(byte[] Buffer, int Pos, int Value) 792 | { 793 | if (Value < -128) Value = -128; 794 | if (Value > 127) Value = 127; 795 | Buffer[Pos] = (byte)Value; 796 | } 797 | #endregion 798 | 799 | #region Get/Set 16 bit signed value (S7 int) -32768..32767 800 | public static int GetIntAt(byte[] Buffer, int Pos) 801 | { 802 | return (short)((Buffer[Pos] << 8) | Buffer[Pos + 1]); 803 | } 804 | public static void SetIntAt(byte[] Buffer, int Pos, Int16 Value) 805 | { 806 | Buffer[Pos] = (byte)(Value >> 8); 807 | Buffer[Pos + 1] = (byte)(Value & 0x00FF); 808 | } 809 | #endregion 810 | 811 | #region Get/Set 32 bit signed value (S7 DInt) -2147483648..2147483647 812 | public static int GetDIntAt(byte[] Buffer, int Pos) 813 | { 814 | int Result; 815 | Result = Buffer[Pos]; Result <<= 8; 816 | Result += Buffer[Pos + 1]; Result <<= 8; 817 | Result += Buffer[Pos + 2]; Result <<= 8; 818 | Result += Buffer[Pos + 3]; 819 | return Result; 820 | } 821 | public static void SetDIntAt(byte[] Buffer, int Pos, int Value) 822 | { 823 | Buffer[Pos + 3] = (byte)(Value & 0xFF); 824 | Buffer[Pos + 2] = (byte)((Value >> 8) & 0xFF); 825 | Buffer[Pos + 1] = (byte)((Value >> 16) & 0xFF); 826 | Buffer[Pos] = (byte)((Value >> 24) & 0xFF); 827 | } 828 | #endregion 829 | 830 | #region Get/Set 64 bit signed value (S7 LInt) -9223372036854775808..9223372036854775807 831 | public static Int64 GetLIntAt(byte[] Buffer, int Pos) 832 | { 833 | Int64 Result; 834 | Result = Buffer[Pos]; Result <<= 8; 835 | Result += Buffer[Pos + 1]; Result <<= 8; 836 | Result += Buffer[Pos + 2]; Result <<= 8; 837 | Result += Buffer[Pos + 3]; Result <<= 8; 838 | Result += Buffer[Pos + 4]; Result <<= 8; 839 | Result += Buffer[Pos + 5]; Result <<= 8; 840 | Result += Buffer[Pos + 6]; Result <<= 8; 841 | Result += Buffer[Pos + 7]; 842 | return Result; 843 | } 844 | public static void SetLIntAt(byte[] Buffer, int Pos, Int64 Value) 845 | { 846 | Buffer[Pos + 7] = (byte)(Value & 0xFF); 847 | Buffer[Pos + 6] = (byte)((Value >> 8) & 0xFF); 848 | Buffer[Pos + 5] = (byte)((Value >> 16) & 0xFF); 849 | Buffer[Pos + 4] = (byte)((Value >> 24) & 0xFF); 850 | Buffer[Pos + 3] = (byte)((Value >> 32) & 0xFF); 851 | Buffer[Pos + 2] = (byte)((Value >> 40) & 0xFF); 852 | Buffer[Pos + 1] = (byte)((Value >> 48) & 0xFF); 853 | Buffer[Pos] = (byte)((Value >> 56) & 0xFF); 854 | } 855 | #endregion 856 | 857 | #region Get/Set 8 bit unsigned value (S7 USInt) 0..255 858 | public static byte GetUSIntAt(byte[] Buffer, int Pos) 859 | { 860 | return Buffer[Pos]; 861 | } 862 | public static void SetUSIntAt(byte[] Buffer, int Pos, byte Value) 863 | { 864 | Buffer[Pos] = Value; 865 | } 866 | #endregion 867 | 868 | #region Get/Set 16 bit unsigned value (S7 UInt) 0..65535 869 | public static UInt16 GetUIntAt(byte[] Buffer, int Pos) 870 | { 871 | return (UInt16)((Buffer[Pos] << 8) | Buffer[Pos + 1]); 872 | } 873 | public static void SetUIntAt(byte[] Buffer, int Pos, UInt16 Value) 874 | { 875 | Buffer[Pos] = (byte)(Value >> 8); 876 | Buffer[Pos + 1] = (byte)(Value & 0x00FF); 877 | } 878 | #endregion 879 | 880 | #region Get/Set 32 bit unsigned value (S7 UDInt) 0..4294967296 881 | public static UInt32 GetUDIntAt(byte[] Buffer, int Pos) 882 | { 883 | UInt32 Result; 884 | Result = Buffer[Pos]; Result <<= 8; 885 | Result |= Buffer[Pos + 1]; Result <<= 8; 886 | Result |= Buffer[Pos + 2]; Result <<= 8; 887 | Result |= Buffer[Pos + 3]; 888 | return Result; 889 | } 890 | public static void SetUDIntAt(byte[] Buffer, int Pos, UInt32 Value) 891 | { 892 | Buffer[Pos + 3] = (byte)(Value & 0xFF); 893 | Buffer[Pos + 2] = (byte)((Value >> 8) & 0xFF); 894 | Buffer[Pos + 1] = (byte)((Value >> 16) & 0xFF); 895 | Buffer[Pos] = (byte)((Value >> 24) & 0xFF); 896 | } 897 | #endregion 898 | 899 | #region Get/Set 64 bit unsigned value (S7 ULint) 0..18446744073709551616 900 | public static UInt64 GetULIntAt(byte[] Buffer, int Pos) 901 | { 902 | UInt64 Result; 903 | Result = Buffer[Pos]; Result <<= 8; 904 | Result |= Buffer[Pos + 1]; Result <<= 8; 905 | Result |= Buffer[Pos + 2]; Result <<= 8; 906 | Result |= Buffer[Pos + 3]; Result <<= 8; 907 | Result |= Buffer[Pos + 4]; Result <<= 8; 908 | Result |= Buffer[Pos + 5]; Result <<= 8; 909 | Result |= Buffer[Pos + 6]; Result <<= 8; 910 | Result |= Buffer[Pos + 7]; 911 | return Result; 912 | } 913 | public static void SetULintAt(byte[] Buffer, int Pos, UInt64 Value) 914 | { 915 | Buffer[Pos + 7] = (byte)(Value & 0xFF); 916 | Buffer[Pos + 6] = (byte)((Value >> 8) & 0xFF); 917 | Buffer[Pos + 5] = (byte)((Value >> 16) & 0xFF); 918 | Buffer[Pos + 4] = (byte)((Value >> 24) & 0xFF); 919 | Buffer[Pos + 3] = (byte)((Value >> 32) & 0xFF); 920 | Buffer[Pos + 2] = (byte)((Value >> 40) & 0xFF); 921 | Buffer[Pos + 1] = (byte)((Value >> 48) & 0xFF); 922 | Buffer[Pos] = (byte)((Value >> 56) & 0xFF); 923 | } 924 | #endregion 925 | 926 | #region Get/Set 8 bit word (S7 Byte) 16#00..16#FF 927 | public static byte GetByteAt(byte[] Buffer, int Pos) 928 | { 929 | return Buffer[Pos]; 930 | } 931 | public static void SetByteAt(byte[] Buffer, int Pos, byte Value) 932 | { 933 | Buffer[Pos] = Value; 934 | } 935 | #endregion 936 | 937 | #region Get/Set 16 bit word (S7 Word) 16#0000..16#FFFF 938 | public static UInt16 GetWordAt(byte[] Buffer, int Pos) 939 | { 940 | return GetUIntAt(Buffer,Pos); 941 | } 942 | public static void SetWordAt(byte[] Buffer, int Pos, UInt16 Value) 943 | { 944 | SetUIntAt(Buffer, Pos, Value); 945 | } 946 | #endregion 947 | 948 | #region Get/Set 32 bit word (S7 DWord) 16#00000000..16#FFFFFFFF 949 | public static UInt32 GetDWordAt(byte[] Buffer, int Pos) 950 | { 951 | return GetUDIntAt(Buffer, Pos); 952 | } 953 | public static void SetDWordAt(byte[] Buffer, int Pos, UInt32 Value) 954 | { 955 | SetUDIntAt(Buffer, Pos, Value); 956 | } 957 | #endregion 958 | 959 | #region Get/Set 64 bit word (S7 LWord) 16#0000000000000000..16#FFFFFFFFFFFFFFFF 960 | public static UInt64 GetLWordAt(byte[] Buffer, int Pos) 961 | { 962 | return GetULIntAt(Buffer, Pos); 963 | } 964 | public static void SetLWordAt(byte[] Buffer, int Pos, UInt64 Value) 965 | { 966 | SetULintAt(Buffer, Pos, Value); 967 | } 968 | #endregion 969 | 970 | #region Get/Set 32 bit floating point number (S7 Real) (Range of Single) 971 | public static Single GetRealAt(byte[] Buffer, int Pos) 972 | { 973 | UInt32 Value = GetUDIntAt(Buffer, Pos); 974 | byte[] bytes = BitConverter.GetBytes(Value); 975 | return BitConverter.ToSingle(bytes, 0); 976 | } 977 | public static void SetRealAt(byte[] Buffer, int Pos, Single Value) 978 | { 979 | byte[] FloatArray = BitConverter.GetBytes(Value); 980 | Buffer[Pos] = FloatArray[3]; 981 | Buffer[Pos + 1] = FloatArray[2]; 982 | Buffer[Pos + 2] = FloatArray[1]; 983 | Buffer[Pos + 3] = FloatArray[0]; 984 | } 985 | #endregion 986 | 987 | #region Get/Set 64 bit floating point number (S7 LReal) (Range of Double) 988 | public static Double GetLRealAt(byte[] Buffer, int Pos) 989 | { 990 | UInt64 Value = GetULIntAt(Buffer, Pos); 991 | byte[] bytes = BitConverter.GetBytes(Value); 992 | return BitConverter.ToDouble(bytes, 0); 993 | } 994 | public static void SetLRealAt(byte[] Buffer, int Pos, Double Value) 995 | { 996 | byte[] FloatArray = BitConverter.GetBytes(Value); 997 | Buffer[Pos] = FloatArray[7]; 998 | Buffer[Pos + 1] = FloatArray[6]; 999 | Buffer[Pos + 2] = FloatArray[5]; 1000 | Buffer[Pos + 3] = FloatArray[4]; 1001 | Buffer[Pos + 4] = FloatArray[3]; 1002 | Buffer[Pos + 5] = FloatArray[2]; 1003 | Buffer[Pos + 6] = FloatArray[1]; 1004 | Buffer[Pos + 7] = FloatArray[0]; 1005 | } 1006 | #endregion 1007 | 1008 | #region Get/Set DateTime (S7 DATE_AND_TIME) 1009 | public static DateTime GetDateTimeAt(byte[] Buffer, int Pos) 1010 | { 1011 | int Year, Month, Day, Hour, Min, Sec, MSec; 1012 | 1013 | Year = BCDtoByte(Buffer[Pos]); 1014 | if (Year < 90) 1015 | Year += 2000; 1016 | else 1017 | Year += 1900; 1018 | 1019 | Month = BCDtoByte(Buffer[Pos + 1]); 1020 | Day = BCDtoByte(Buffer[Pos + 2]); 1021 | Hour = BCDtoByte(Buffer[Pos + 3]); 1022 | Min = BCDtoByte(Buffer[Pos + 4]); 1023 | Sec = BCDtoByte(Buffer[Pos + 5]); 1024 | MSec = (BCDtoByte(Buffer[Pos + 6]) * 10) + (BCDtoByte(Buffer[Pos + 7]) / 10); 1025 | try 1026 | { 1027 | return new DateTime(Year, Month, Day, Hour, Min, Sec, MSec); 1028 | } 1029 | catch (System.ArgumentOutOfRangeException) 1030 | { 1031 | return new DateTime(0); 1032 | } 1033 | } 1034 | public static void SetDateTimeAt(byte[] Buffer, int Pos, DateTime Value) 1035 | { 1036 | int Year = Value.Year; 1037 | int Month = Value.Month; 1038 | int Day = Value.Day; 1039 | int Hour = Value.Hour; 1040 | int Min = Value.Minute; 1041 | int Sec = Value.Second; 1042 | int Dow = (int)Value.DayOfWeek + 1; 1043 | // MSecH = First two digits of miliseconds 1044 | int MsecH = Value.Millisecond / 10; 1045 | // MSecL = Last digit of miliseconds 1046 | int MsecL = Value.Millisecond % 10; 1047 | if (Year > 1999) 1048 | Year -= 2000; 1049 | 1050 | Buffer[Pos] = ByteToBCD(Year); 1051 | Buffer[Pos + 1] = ByteToBCD(Month); 1052 | Buffer[Pos + 2] = ByteToBCD(Day); 1053 | Buffer[Pos + 3] = ByteToBCD(Hour); 1054 | Buffer[Pos + 4] = ByteToBCD(Min); 1055 | Buffer[Pos + 5] = ByteToBCD(Sec); 1056 | Buffer[Pos + 6] = ByteToBCD(MsecH); 1057 | Buffer[Pos + 7] = ByteToBCD(MsecL * 10 + Dow); 1058 | } 1059 | #endregion 1060 | 1061 | #region Get/Set DATE (S7 DATE) 1062 | public static DateTime GetDateAt(byte[] Buffer, int Pos) 1063 | { 1064 | try 1065 | { 1066 | return new DateTime(1990, 1, 1).AddDays(GetIntAt(Buffer, Pos)); 1067 | } 1068 | catch (System.ArgumentOutOfRangeException) 1069 | { 1070 | return new DateTime(0); 1071 | } 1072 | } 1073 | public static void SetDateAt(byte[] Buffer, int Pos, DateTime Value) 1074 | { 1075 | SetIntAt(Buffer, Pos, (Int16)(Value - new DateTime(1990, 1, 1)).Days); 1076 | } 1077 | 1078 | #endregion 1079 | 1080 | #region Get/Set TOD (S7 TIME_OF_DAY) 1081 | public static DateTime GetTODAt(byte[] Buffer, int Pos) 1082 | { 1083 | try 1084 | { 1085 | return new DateTime(0).AddMilliseconds(S7.GetDIntAt(Buffer, Pos)); 1086 | } 1087 | catch (System.ArgumentOutOfRangeException) 1088 | { 1089 | return new DateTime(0); 1090 | } 1091 | } 1092 | public static void SetTODAt(byte[] Buffer, int Pos, DateTime Value) 1093 | { 1094 | TimeSpan Time = Value.TimeOfDay; 1095 | SetDIntAt(Buffer, Pos, (Int32)Math.Round(Time.TotalMilliseconds)); 1096 | } 1097 | #endregion 1098 | 1099 | #region Get/Set LTOD (S7 1500 LONG TIME_OF_DAY) 1100 | public static DateTime GetLTODAt(byte[] Buffer, int Pos) 1101 | { 1102 | // .NET Tick = 100 ns, S71500 Tick = 1 ns 1103 | try 1104 | { 1105 | return new DateTime(Math.Abs(GetLIntAt(Buffer,Pos)/100)); 1106 | } 1107 | catch (System.ArgumentOutOfRangeException) 1108 | { 1109 | return new DateTime(0); 1110 | } 1111 | } 1112 | public static void SetLTODAt(byte[] Buffer, int Pos, DateTime Value) 1113 | { 1114 | TimeSpan Time = Value.TimeOfDay; 1115 | SetLIntAt(Buffer, Pos, (Int64)Time.Ticks * 100); 1116 | } 1117 | #endregion 1118 | 1119 | #region GET/SET LDT (S7 1500 Long Date and Time) 1120 | public static DateTime GetLDTAt(byte[] Buffer, int Pos) 1121 | { 1122 | try 1123 | { 1124 | return new DateTime((GetLIntAt(Buffer, Pos) / 100) + bias); 1125 | } 1126 | catch (System.ArgumentOutOfRangeException) 1127 | { 1128 | return new DateTime(0); 1129 | } 1130 | } 1131 | public static void SetLDTAt(byte[] Buffer, int Pos, DateTime Value) 1132 | { 1133 | SetLIntAt(Buffer, Pos, (Value.Ticks-bias) * 100); 1134 | } 1135 | #endregion 1136 | 1137 | #region Get/Set DTL (S71200/1500 Date and Time) 1138 | // Thanks to Johan Cardoen for GetDTLAt 1139 | public static DateTime GetDTLAt(byte[] Buffer, int Pos) 1140 | { 1141 | int Year, Month, Day, Hour, Min, Sec, MSec; 1142 | 1143 | Year = Buffer[Pos] * 256 + Buffer[Pos + 1]; 1144 | Month = Buffer[Pos + 2]; 1145 | Day = Buffer[Pos + 3]; 1146 | Hour = Buffer[Pos + 5]; 1147 | Min = Buffer[Pos + 6]; 1148 | Sec = Buffer[Pos + 7]; 1149 | MSec = (int)GetUDIntAt(Buffer, Pos + 8)/1000000; 1150 | 1151 | try 1152 | { 1153 | return new DateTime(Year, Month, Day, Hour, Min, Sec, MSec); 1154 | } 1155 | catch (System.ArgumentOutOfRangeException) 1156 | { 1157 | return new DateTime(0); 1158 | } 1159 | } 1160 | public static void SetDTLAt(byte[] Buffer, int Pos, DateTime Value) 1161 | { 1162 | short Year = (short)Value.Year; 1163 | byte Month = (byte)Value.Month; 1164 | byte Day = (byte)Value.Day; 1165 | byte Hour = (byte)Value.Hour; 1166 | byte Min = (byte)Value.Minute; 1167 | byte Sec = (byte)Value.Second; 1168 | byte Dow = (byte)(Value.DayOfWeek + 1); 1169 | 1170 | Int32 NanoSecs = Value.Millisecond * 1000000; 1171 | 1172 | var bytes_short = BitConverter.GetBytes(Year); 1173 | 1174 | Buffer[Pos] = bytes_short[1]; 1175 | Buffer[Pos + 1] = bytes_short[0]; 1176 | Buffer[Pos + 2] = Month; 1177 | Buffer[Pos + 3] = Day; 1178 | Buffer[Pos + 4] = Dow; 1179 | Buffer[Pos + 5] = Hour; 1180 | Buffer[Pos + 6] = Min; 1181 | Buffer[Pos + 7] = Sec; 1182 | SetDIntAt(Buffer, Pos + 8, NanoSecs); 1183 | } 1184 | 1185 | #endregion 1186 | 1187 | #region Get/Set String (S7 String) 1188 | // Thanks to Pablo Agirre 1189 | public static string GetStringAt(byte[] Buffer, int Pos) 1190 | { 1191 | int size = (int)Buffer[Pos + 1]; 1192 | return Encoding.UTF8.GetString(Buffer, Pos + 2, size); 1193 | } 1194 | public static void SetStringAt(byte[] Buffer, int Pos, int MaxLen, string Value) 1195 | { 1196 | int size = Value.Length; 1197 | Buffer[Pos] = (byte)MaxLen; 1198 | Buffer[Pos + 1] = (byte)size; 1199 | Encoding.UTF8.GetBytes(Value, 0, size, Buffer, Pos + 2); 1200 | } 1201 | #endregion 1202 | 1203 | #region Get/Set Array of char (S7 ARRAY OF CHARS) 1204 | public static string GetCharsAt(byte[] Buffer, int Pos, int Size) 1205 | { 1206 | return Encoding.UTF8.GetString(Buffer, Pos , Size); 1207 | } 1208 | public static void SetCharsAt(byte[] Buffer, int Pos, string Value) 1209 | { 1210 | int MaxLen = Buffer.Length - Pos; 1211 | // Truncs the string if there's no room enough 1212 | if (MaxLen > Value.Length) MaxLen = Value.Length; 1213 | Encoding.UTF8.GetBytes(Value, 0, MaxLen, Buffer, Pos); 1214 | } 1215 | #endregion 1216 | 1217 | #region Get/Set Counter 1218 | public static int GetCounter(ushort Value) 1219 | { 1220 | return BCDtoByte((byte)Value) * 100 + BCDtoByte((byte)(Value >> 8)); 1221 | } 1222 | 1223 | public static int GetCounterAt(ushort[] Buffer, int Index) 1224 | { 1225 | return GetCounter(Buffer[Index]); 1226 | } 1227 | 1228 | public static ushort ToCounter(int Value) 1229 | { 1230 | return (ushort)(ByteToBCD(Value / 100) + (ByteToBCD(Value % 100) << 8)); 1231 | } 1232 | 1233 | public static void SetCounterAt(ushort[] Buffer, int Pos, int Value) 1234 | { 1235 | Buffer[Pos] = ToCounter(Value); 1236 | } 1237 | #endregion 1238 | 1239 | #region Get/Set Timer 1240 | 1241 | public static S7Timer GetS7TimerAt(byte[] Buffer, int Pos) 1242 | { 1243 | return new S7Timer(new List(Buffer).GetRange(Pos, 12).ToArray()); 1244 | } 1245 | 1246 | public static void SetS7TimespanAt(byte[] Buffer, int Pos, TimeSpan Value) 1247 | { 1248 | SetDIntAt(Buffer, Pos, (Int32)Value.TotalMilliseconds); 1249 | } 1250 | 1251 | public static TimeSpan GetS7TimespanAt(byte[] Buffer, int pos) 1252 | { 1253 | if (Buffer.Length < pos + 4) 1254 | { 1255 | return new TimeSpan(); 1256 | } 1257 | 1258 | Int32 a; 1259 | a = Buffer[pos + 0]; a <<= 8; 1260 | a += Buffer[pos + 1]; a <<= 8; 1261 | a += Buffer[pos + 2]; a <<= 8; 1262 | a += Buffer[pos + 3]; 1263 | TimeSpan sp = new TimeSpan(0, 0, 0, 0, a); 1264 | 1265 | return sp; 1266 | } 1267 | 1268 | #endregion 1269 | 1270 | #endregion [Help Functions] 1271 | } 1272 | 1273 | public class S7MultiVar 1274 | { 1275 | #region [MultiRead/Write Helper] 1276 | private S7Client FClient; 1277 | private GCHandle[] Handles = new GCHandle[S7Client.MaxVars]; 1278 | private int Count = 0; 1279 | private S7Client.S7DataItem[] Items = new S7Client.S7DataItem[S7Client.MaxVars]; 1280 | 1281 | 1282 | public int[] Results = new int[S7Client.MaxVars]; 1283 | 1284 | private bool AdjustWordLength(int Area, ref int WordLen, ref int Amount, ref int Start) 1285 | { 1286 | // Calc Word size 1287 | int WordSize = S7.DataSizeByte(WordLen); 1288 | if (WordSize == 0) 1289 | return false; 1290 | 1291 | if (Area == S7Consts.S7AreaCT) 1292 | WordLen = S7Consts.S7WLCounter; 1293 | if (Area == S7Consts.S7AreaTM) 1294 | WordLen = S7Consts.S7WLTimer; 1295 | 1296 | if (WordLen == S7Consts.S7WLBit) 1297 | Amount = 1; // Only 1 bit can be transferred at time 1298 | else 1299 | { 1300 | if ((WordLen != S7Consts.S7WLCounter) && (WordLen != S7Consts.S7WLTimer)) 1301 | { 1302 | Amount = Amount * WordSize; 1303 | Start = Start * 8; 1304 | WordLen = S7Consts.S7WLByte; 1305 | } 1306 | } 1307 | return true; 1308 | } 1309 | 1310 | public S7MultiVar(S7Client Client) 1311 | { 1312 | FClient = Client; 1313 | for (int c = 0; c < S7Client.MaxVars; c++) 1314 | Results[c] = (int)S7Consts.errCliItemNotAvailable; 1315 | } 1316 | ~S7MultiVar() 1317 | { 1318 | Clear(); 1319 | } 1320 | 1321 | public bool Add(S7Consts.S7Tag Tag, ref T[] Buffer, int Offset) 1322 | { 1323 | return Add(Tag.Area, Tag.WordLen, Tag.DBNumber, Tag.Start, Tag.Elements, ref Buffer, Offset); 1324 | } 1325 | 1326 | public bool Add(S7Consts.S7Tag Tag, ref T[] Buffer) 1327 | { 1328 | return Add(Tag.Area, Tag.WordLen, Tag.DBNumber, Tag.Start, Tag.Elements, ref Buffer); 1329 | } 1330 | 1331 | public bool Add(Int32 Area, Int32 WordLen, Int32 DBNumber, Int32 Start, Int32 Amount, ref T[] Buffer) 1332 | { 1333 | return Add(Area, WordLen, DBNumber, Start, Amount, ref Buffer, 0); 1334 | } 1335 | 1336 | public bool Add(Int32 Area, Int32 WordLen, Int32 DBNumber, Int32 Start, Int32 Amount, ref T[] Buffer, int Offset) 1337 | { 1338 | if (Count < S7Client.MaxVars) 1339 | { 1340 | if (AdjustWordLength(Area, ref WordLen, ref Amount, ref Start)) 1341 | { 1342 | Items[Count].Area = Area; 1343 | Items[Count].WordLen = WordLen; 1344 | Items[Count].Result = (int)S7Consts.errCliItemNotAvailable; 1345 | Items[Count].DBNumber = DBNumber; 1346 | Items[Count].Start = Start; 1347 | Items[Count].Amount = Amount; 1348 | GCHandle handle = GCHandle.Alloc(Buffer, GCHandleType.Pinned); 1349 | #if WINDOWS_UWP || NETFX_CORE 1350 | if (IntPtr.Size == 4) 1351 | Items[Count].pData = (IntPtr)(handle.AddrOfPinnedObject().ToInt32() + Offset * Marshal.SizeOf()); 1352 | else 1353 | Items[Count].pData = (IntPtr)(handle.AddrOfPinnedObject().ToInt64() + Offset * Marshal.SizeOf()); 1354 | #else 1355 | if (IntPtr.Size == 4) 1356 | Items[Count].pData = (IntPtr)(handle.AddrOfPinnedObject().ToInt32() + Offset * Marshal.SizeOf(typeof(T))); 1357 | else 1358 | Items[Count].pData = (IntPtr)(handle.AddrOfPinnedObject().ToInt64() + Offset * Marshal.SizeOf(typeof(T))); 1359 | #endif 1360 | Handles[Count] = handle; 1361 | Count++; 1362 | return true; 1363 | } 1364 | else 1365 | return false; 1366 | } 1367 | else 1368 | return false; 1369 | } 1370 | 1371 | public int Read() 1372 | { 1373 | int FunctionResult; 1374 | int GlobalResult = (int)S7Consts.errCliFunctionRefused; 1375 | try 1376 | { 1377 | if (Count > 0) 1378 | { 1379 | FunctionResult = FClient.ReadMultiVars(Items, Count); 1380 | if (FunctionResult == 0) 1381 | for (int c = 0; c < S7Client.MaxVars; c++) 1382 | Results[c] = Items[c].Result; 1383 | GlobalResult = FunctionResult; 1384 | } 1385 | else 1386 | GlobalResult = (int)S7Consts.errCliFunctionRefused; 1387 | } 1388 | finally 1389 | { 1390 | Clear(); // handles are no more needed and MUST be freed 1391 | } 1392 | return GlobalResult; 1393 | } 1394 | 1395 | public int Write() 1396 | { 1397 | int FunctionResult; 1398 | int GlobalResult = (int)S7Consts.errCliFunctionRefused; 1399 | try 1400 | { 1401 | if (Count > 0) 1402 | { 1403 | FunctionResult = FClient.WriteMultiVars(Items, Count); 1404 | if (FunctionResult == 0) 1405 | for (int c = 0; c < S7Client.MaxVars; c++) 1406 | Results[c] = Items[c].Result; 1407 | GlobalResult = FunctionResult; 1408 | } 1409 | else 1410 | GlobalResult = (int)S7Consts.errCliFunctionRefused; 1411 | } 1412 | finally 1413 | { 1414 | Clear(); // handles are no more needed and MUST be freed 1415 | } 1416 | return GlobalResult; 1417 | } 1418 | 1419 | public void Clear() 1420 | { 1421 | for (int c = 0; c < Count; c++) 1422 | { 1423 | if (Handles[c] != null) 1424 | Handles[c].Free(); 1425 | } 1426 | Count = 0; 1427 | } 1428 | #endregion 1429 | } 1430 | 1431 | public class S7Client 1432 | { 1433 | #region [Constants and TypeDefs] 1434 | 1435 | // Block type 1436 | public const int Block_OB = 0x38; 1437 | public const int Block_DB = 0x41; 1438 | public const int Block_SDB = 0x42; 1439 | public const int Block_FC = 0x43; 1440 | public const int Block_SFC = 0x44; 1441 | public const int Block_FB = 0x45; 1442 | public const int Block_SFB = 0x46; 1443 | 1444 | // Sub Block Type 1445 | public const byte SubBlk_OB = 0x08; 1446 | public const byte SubBlk_DB = 0x0A; 1447 | public const byte SubBlk_SDB = 0x0B; 1448 | public const byte SubBlk_FC = 0x0C; 1449 | public const byte SubBlk_SFC = 0x0D; 1450 | public const byte SubBlk_FB = 0x0E; 1451 | public const byte SubBlk_SFB = 0x0F; 1452 | 1453 | // Block languages 1454 | public const byte BlockLangAWL = 0x01; 1455 | public const byte BlockLangKOP = 0x02; 1456 | public const byte BlockLangFUP = 0x03; 1457 | public const byte BlockLangSCL = 0x04; 1458 | public const byte BlockLangDB = 0x05; 1459 | public const byte BlockLangGRAPH = 0x06; 1460 | 1461 | // Max number of vars (multiread/write) 1462 | public static readonly int MaxVars = 20; 1463 | 1464 | // Result transport size 1465 | const byte TS_ResBit = 0x03; 1466 | const byte TS_ResByte = 0x04; 1467 | const byte TS_ResInt = 0x05; 1468 | const byte TS_ResReal = 0x07; 1469 | const byte TS_ResOctet = 0x09; 1470 | 1471 | const ushort Code7Ok = 0x0000; 1472 | const ushort Code7AddressOutOfRange = 0x0005; 1473 | const ushort Code7InvalidTransportSize = 0x0006; 1474 | const ushort Code7WriteDataSizeMismatch = 0x0007; 1475 | const ushort Code7ResItemNotAvailable = 0x000A; 1476 | const ushort Code7ResItemNotAvailable1 = 0xD209; 1477 | const ushort Code7InvalidValue = 0xDC01; 1478 | const ushort Code7NeedPassword = 0xD241; 1479 | const ushort Code7InvalidPassword = 0xD602; 1480 | const ushort Code7NoPasswordToClear = 0xD604; 1481 | const ushort Code7NoPasswordToSet = 0xD605; 1482 | const ushort Code7FunNotAvailable = 0x8104; 1483 | const ushort Code7DataOverPDU = 0x8500; 1484 | 1485 | // Client Connection Type 1486 | public static readonly UInt16 CONNTYPE_PG = 0x01; // Connect to the PLC as a PG 1487 | public static readonly UInt16 CONNTYPE_OP = 0x02; // Connect to the PLC as an OP 1488 | public static readonly UInt16 CONNTYPE_BASIC = 0x03; // Basic connection 1489 | 1490 | public int _LastError = 0; 1491 | 1492 | public struct S7DataItem 1493 | { 1494 | public int Area; 1495 | public int WordLen; 1496 | public int Result; 1497 | public int DBNumber; 1498 | public int Start; 1499 | public int Amount; 1500 | public IntPtr pData; 1501 | } 1502 | 1503 | // Order Code + Version 1504 | public struct S7OrderCode 1505 | { 1506 | public string Code; // such as "6ES7 151-8AB01-0AB0" 1507 | public byte V1; // Version 1st digit 1508 | public byte V2; // Version 2nd digit 1509 | public byte V3; // Version 3th digit 1510 | }; 1511 | 1512 | // CPU Info 1513 | public struct S7CpuInfo 1514 | { 1515 | public string ModuleTypeName; 1516 | public string SerialNumber; 1517 | public string ASName; 1518 | public string Copyright; 1519 | public string ModuleName; 1520 | } 1521 | 1522 | public struct S7CpInfo 1523 | { 1524 | public int MaxPduLength; 1525 | public int MaxConnections; 1526 | public int MaxMpiRate; 1527 | public int MaxBusRate; 1528 | }; 1529 | 1530 | // Block List 1531 | [StructLayout(LayoutKind.Sequential, Pack = 1)] 1532 | public struct S7BlocksList 1533 | { 1534 | public Int32 OBCount; 1535 | public Int32 FBCount; 1536 | public Int32 FCCount; 1537 | public Int32 SFBCount; 1538 | public Int32 SFCCount; 1539 | public Int32 DBCount; 1540 | public Int32 SDBCount; 1541 | }; 1542 | 1543 | // Managed Block Info 1544 | public struct S7BlockInfo 1545 | { 1546 | public int BlkType; 1547 | public int BlkNumber; 1548 | public int BlkLang; 1549 | public int BlkFlags; 1550 | public int MC7Size; // The real size in bytes 1551 | public int LoadSize; 1552 | public int LocalData; 1553 | public int SBBLength; 1554 | public int CheckSum; 1555 | public int Version; 1556 | // Chars info 1557 | public string CodeDate; 1558 | public string IntfDate; 1559 | public string Author; 1560 | public string Family; 1561 | public string Header; 1562 | }; 1563 | 1564 | // See §33.1 of "System Software for S7-300/400 System and Standard Functions" 1565 | // and see SFC51 description too 1566 | [StructLayout(LayoutKind.Sequential, Pack = 1)] 1567 | public struct SZL_HEADER 1568 | { 1569 | public UInt16 LENTHDR; 1570 | public UInt16 N_DR; 1571 | }; 1572 | 1573 | [StructLayout(LayoutKind.Sequential, Pack = 1)] 1574 | public struct S7SZL 1575 | { 1576 | public SZL_HEADER Header; 1577 | [MarshalAs(UnmanagedType.ByValArray)] 1578 | public byte[] Data; 1579 | }; 1580 | 1581 | // SZL List of available SZL IDs : same as SZL but List items are big-endian adjusted 1582 | [StructLayout(LayoutKind.Sequential, Pack = 1)] 1583 | public struct S7SZLList 1584 | { 1585 | public SZL_HEADER Header; 1586 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x2000 - 2)] 1587 | public UInt16[] Data; 1588 | }; 1589 | 1590 | // S7 Protection 1591 | // See §33.19 of "System Software for S7-300/400 System and Standard Functions" 1592 | public struct S7Protection 1593 | { 1594 | public ushort sch_schal; 1595 | public ushort sch_par; 1596 | public ushort sch_rel; 1597 | public ushort bart_sch; 1598 | public ushort anl_sch; 1599 | }; 1600 | 1601 | #endregion 1602 | 1603 | #region [S7 Telegrams] 1604 | 1605 | // ISO Connection Request telegram (contains also ISO Header and COTP Header) 1606 | byte[] ISO_CR = { 1607 | // TPKT (RFC1006 Header) 1608 | 0x03, // RFC 1006 ID (3) 1609 | 0x00, // Reserved, always 0 1610 | 0x00, // High part of packet lenght (entire frame, payload and TPDU included) 1611 | 0x16, // Low part of packet lenght (entire frame, payload and TPDU included) 1612 | // COTP (ISO 8073 Header) 1613 | 0x11, // PDU Size Length 1614 | 0xE0, // CR - Connection Request ID 1615 | 0x00, // Dst Reference HI 1616 | 0x00, // Dst Reference LO 1617 | 0x00, // Src Reference HI 1618 | 0x01, // Src Reference LO 1619 | 0x00, // Class + Options Flags 1620 | 0xC0, // PDU Max Length ID 1621 | 0x01, // PDU Max Length HI 1622 | 0x0A, // PDU Max Length LO 1623 | 0xC1, // Src TSAP Identifier 1624 | 0x02, // Src TSAP Length (2 bytes) 1625 | 0x01, // Src TSAP HI (will be overwritten) 1626 | 0x00, // Src TSAP LO (will be overwritten) 1627 | 0xC2, // Dst TSAP Identifier 1628 | 0x02, // Dst TSAP Length (2 bytes) 1629 | 0x01, // Dst TSAP HI (will be overwritten) 1630 | 0x02 // Dst TSAP LO (will be overwritten) 1631 | }; 1632 | 1633 | // TPKT + ISO COTP Header (Connection Oriented Transport Protocol) 1634 | byte[] TPKT_ISO = { // 7 bytes 1635 | 0x03,0x00, 1636 | 0x00,0x1f, // Telegram Length (Data Size + 31 or 35) 1637 | 0x02,0xf0,0x80 // COTP (see above for info) 1638 | }; 1639 | 1640 | // S7 PDU Negotiation Telegram (contains also ISO Header and COTP Header) 1641 | byte[] S7_PN = { 1642 | 0x03, 0x00, 0x00, 0x19, 1643 | 0x02, 0xf0, 0x80, // TPKT + COTP (see above for info) 1644 | 0x32, 0x01, 0x00, 0x00, 1645 | 0x04, 0x00, 0x00, 0x08, 1646 | 0x00, 0x00, 0xf0, 0x00, 1647 | 0x00, 0x01, 0x00, 0x01, 1648 | 0x00, 0x1e // PDU Length Requested = HI-LO Here Default 480 bytes 1649 | }; 1650 | 1651 | // S7 Read/Write Request Header (contains also ISO Header and COTP Header) 1652 | byte[] S7_RW = { // 31-35 bytes 1653 | 0x03,0x00, 1654 | 0x00,0x1f, // Telegram Length (Data Size + 31 or 35) 1655 | 0x02,0xf0, 0x80, // COTP (see above for info) 1656 | 0x32, // S7 Protocol ID 1657 | 0x01, // Job Type 1658 | 0x00,0x00, // Redundancy identification 1659 | 0x05,0x00, // PDU Reference 1660 | 0x00,0x0e, // Parameters Length 1661 | 0x00,0x00, // Data Length = Size(bytes) + 4 1662 | 0x04, // Function 4 Read Var, 5 Write Var 1663 | 0x01, // Items count 1664 | 0x12, // Var spec. 1665 | 0x0a, // Length of remaining bytes 1666 | 0x10, // Syntax ID 1667 | (byte)S7Consts.S7WLByte, // Transport Size idx=22 1668 | 0x00,0x00, // Num Elements 1669 | 0x00,0x00, // DB Number (if any, else 0) 1670 | 0x84, // Area Type 1671 | 0x00,0x00,0x00, // Area Offset 1672 | // WR area 1673 | 0x00, // Reserved 1674 | 0x04, // Transport size 1675 | 0x00,0x00, // Data Length * 8 (if not bit or timer or counter) 1676 | }; 1677 | private static int Size_RD = 31; // Header Size when Reading 1678 | private static int Size_WR = 35; // Header Size when Writing 1679 | 1680 | // S7 Variable MultiRead Header 1681 | byte[] S7_MRD_HEADER = { 1682 | 0x03,0x00, 1683 | 0x00,0x1f, // Telegram Length 1684 | 0x02,0xf0, 0x80, // COTP (see above for info) 1685 | 0x32, // S7 Protocol ID 1686 | 0x01, // Job Type 1687 | 0x00,0x00, // Redundancy identification 1688 | 0x05,0x00, // PDU Reference 1689 | 0x00,0x0e, // Parameters Length 1690 | 0x00,0x00, // Data Length = Size(bytes) + 4 1691 | 0x04, // Function 4 Read Var, 5 Write Var 1692 | 0x01 // Items count (idx 18) 1693 | }; 1694 | 1695 | // S7 Variable MultiRead Item 1696 | byte[] S7_MRD_ITEM = { 1697 | 0x12, // Var spec. 1698 | 0x0a, // Length of remaining bytes 1699 | 0x10, // Syntax ID 1700 | (byte)S7Consts.S7WLByte, // Transport Size idx=3 1701 | 0x00,0x00, // Num Elements 1702 | 0x00,0x00, // DB Number (if any, else 0) 1703 | 0x84, // Area Type 1704 | 0x00,0x00,0x00 // Area Offset 1705 | }; 1706 | 1707 | // S7 Variable MultiWrite Header 1708 | byte[] S7_MWR_HEADER = { 1709 | 0x03,0x00, 1710 | 0x00,0x1f, // Telegram Length 1711 | 0x02,0xf0, 0x80, // COTP (see above for info) 1712 | 0x32, // S7 Protocol ID 1713 | 0x01, // Job Type 1714 | 0x00,0x00, // Redundancy identification 1715 | 0x05,0x00, // PDU Reference 1716 | 0x00,0x0e, // Parameters Length (idx 13) 1717 | 0x00,0x00, // Data Length = Size(bytes) + 4 (idx 15) 1718 | 0x05, // Function 5 Write Var 1719 | 0x01 // Items count (idx 18) 1720 | }; 1721 | 1722 | // S7 Variable MultiWrite Item (Param) 1723 | byte[] S7_MWR_PARAM = { 1724 | 0x12, // Var spec. 1725 | 0x0a, // Length of remaining bytes 1726 | 0x10, // Syntax ID 1727 | (byte)S7Consts.S7WLByte, // Transport Size idx=3 1728 | 0x00,0x00, // Num Elements 1729 | 0x00,0x00, // DB Number (if any, else 0) 1730 | 0x84, // Area Type 1731 | 0x00,0x00,0x00, // Area Offset 1732 | }; 1733 | 1734 | // SZL First telegram request 1735 | byte[] S7_SZL_FIRST = { 1736 | 0x03, 0x00, 0x00, 0x21, 1737 | 0x02, 0xf0, 0x80, 0x32, 1738 | 0x07, 0x00, 0x00, 1739 | 0x05, 0x00, // Sequence out 1740 | 0x00, 0x08, 0x00, 1741 | 0x08, 0x00, 0x01, 0x12, 1742 | 0x04, 0x11, 0x44, 0x01, 1743 | 0x00, 0xff, 0x09, 0x00, 1744 | 0x04, 1745 | 0x00, 0x00, // ID (29) 1746 | 0x00, 0x00 // Index (31) 1747 | }; 1748 | 1749 | // SZL Next telegram request 1750 | byte[] S7_SZL_NEXT = { 1751 | 0x03, 0x00, 0x00, 0x21, 1752 | 0x02, 0xf0, 0x80, 0x32, 1753 | 0x07, 0x00, 0x00, 0x06, 1754 | 0x00, 0x00, 0x0c, 0x00, 1755 | 0x04, 0x00, 0x01, 0x12, 1756 | 0x08, 0x12, 0x44, 0x01, 1757 | 0x01, // Sequence 1758 | 0x00, 0x00, 0x00, 0x00, 1759 | 0x0a, 0x00, 0x00, 0x00 1760 | }; 1761 | 1762 | // Get Date/Time request 1763 | byte[] S7_GET_DT = { 1764 | 0x03, 0x00, 0x00, 0x1d, 1765 | 0x02, 0xf0, 0x80, 0x32, 1766 | 0x07, 0x00, 0x00, 0x38, 1767 | 0x00, 0x00, 0x08, 0x00, 1768 | 0x04, 0x00, 0x01, 0x12, 1769 | 0x04, 0x11, 0x47, 0x01, 1770 | 0x00, 0x0a, 0x00, 0x00, 1771 | 0x00 1772 | }; 1773 | 1774 | // Set Date/Time command 1775 | byte[] S7_SET_DT = { 1776 | 0x03, 0x00, 0x00, 0x27, 1777 | 0x02, 0xf0, 0x80, 0x32, 1778 | 0x07, 0x00, 0x00, 0x89, 1779 | 0x03, 0x00, 0x08, 0x00, 1780 | 0x0e, 0x00, 0x01, 0x12, 1781 | 0x04, 0x11, 0x47, 0x02, 1782 | 0x00, 0xff, 0x09, 0x00, 1783 | 0x0a, 0x00, 1784 | 0x19, // Hi part of Year (idx=30) 1785 | 0x13, // Lo part of Year 1786 | 0x12, // Month 1787 | 0x06, // Day 1788 | 0x17, // Hour 1789 | 0x37, // Min 1790 | 0x13, // Sec 1791 | 0x00, 0x01 // ms + Day of week 1792 | }; 1793 | 1794 | // S7 Set Session Password 1795 | byte[] S7_SET_PWD = { 1796 | 0x03, 0x00, 0x00, 0x25, 1797 | 0x02, 0xf0, 0x80, 0x32, 1798 | 0x07, 0x00, 0x00, 0x27, 1799 | 0x00, 0x00, 0x08, 0x00, 1800 | 0x0c, 0x00, 0x01, 0x12, 1801 | 0x04, 0x11, 0x45, 0x01, 1802 | 0x00, 0xff, 0x09, 0x00, 1803 | 0x08, 1804 | // 8 Char Encoded Password 1805 | 0x00, 0x00, 0x00, 0x00, 1806 | 0x00, 0x00, 0x00, 0x00 1807 | }; 1808 | 1809 | // S7 Clear Session Password 1810 | byte[] S7_CLR_PWD = { 1811 | 0x03, 0x00, 0x00, 0x1d, 1812 | 0x02, 0xf0, 0x80, 0x32, 1813 | 0x07, 0x00, 0x00, 0x29, 1814 | 0x00, 0x00, 0x08, 0x00, 1815 | 0x04, 0x00, 0x01, 0x12, 1816 | 0x04, 0x11, 0x45, 0x02, 1817 | 0x00, 0x0a, 0x00, 0x00, 1818 | 0x00 1819 | }; 1820 | 1821 | // S7 STOP request 1822 | byte[] S7_STOP = { 1823 | 0x03, 0x00, 0x00, 0x21, 1824 | 0x02, 0xf0, 0x80, 0x32, 1825 | 0x01, 0x00, 0x00, 0x0e, 1826 | 0x00, 0x00, 0x10, 0x00, 1827 | 0x00, 0x29, 0x00, 0x00, 1828 | 0x00, 0x00, 0x00, 0x09, 1829 | 0x50, 0x5f, 0x50, 0x52, 1830 | 0x4f, 0x47, 0x52, 0x41, 1831 | 0x4d 1832 | }; 1833 | 1834 | // S7 HOT Start request 1835 | byte[] S7_HOT_START = { 1836 | 0x03, 0x00, 0x00, 0x25, 1837 | 0x02, 0xf0, 0x80, 0x32, 1838 | 0x01, 0x00, 0x00, 0x0c, 1839 | 0x00, 0x00, 0x14, 0x00, 1840 | 0x00, 0x28, 0x00, 0x00, 1841 | 0x00, 0x00, 0x00, 0x00, 1842 | 0xfd, 0x00, 0x00, 0x09, 1843 | 0x50, 0x5f, 0x50, 0x52, 1844 | 0x4f, 0x47, 0x52, 0x41, 1845 | 0x4d 1846 | }; 1847 | 1848 | // S7 COLD Start request 1849 | byte[] S7_COLD_START = { 1850 | 0x03, 0x00, 0x00, 0x27, 1851 | 0x02, 0xf0, 0x80, 0x32, 1852 | 0x01, 0x00, 0x00, 0x0f, 1853 | 0x00, 0x00, 0x16, 0x00, 1854 | 0x00, 0x28, 0x00, 0x00, 1855 | 0x00, 0x00, 0x00, 0x00, 1856 | 0xfd, 0x00, 0x02, 0x43, 1857 | 0x20, 0x09, 0x50, 0x5f, 1858 | 0x50, 0x52, 0x4f, 0x47, 1859 | 0x52, 0x41, 0x4d 1860 | }; 1861 | const byte pduStart = 0x28; // CPU start 1862 | const byte pduStop = 0x29; // CPU stop 1863 | const byte pduAlreadyStarted = 0x02; // CPU already in run mode 1864 | const byte pduAlreadyStopped = 0x07; // CPU already in stop mode 1865 | 1866 | // S7 Get PLC Status 1867 | byte[] S7_GET_STAT = { 1868 | 0x03, 0x00, 0x00, 0x21, 1869 | 0x02, 0xf0, 0x80, 0x32, 1870 | 0x07, 0x00, 0x00, 0x2c, 1871 | 0x00, 0x00, 0x08, 0x00, 1872 | 0x08, 0x00, 0x01, 0x12, 1873 | 0x04, 0x11, 0x44, 0x01, 1874 | 0x00, 0xff, 0x09, 0x00, 1875 | 0x04, 0x04, 0x24, 0x00, 1876 | 0x00 1877 | }; 1878 | 1879 | // S7 Get Block Info Request Header (contains also ISO Header and COTP Header) 1880 | byte[] S7_BI = { 1881 | 0x03, 0x00, 0x00, 0x25, 1882 | 0x02, 0xf0, 0x80, 0x32, 1883 | 0x07, 0x00, 0x00, 0x05, 1884 | 0x00, 0x00, 0x08, 0x00, 1885 | 0x0c, 0x00, 0x01, 0x12, 1886 | 0x04, 0x11, 0x43, 0x03, 1887 | 0x00, 0xff, 0x09, 0x00, 1888 | 0x08, 0x30, 1889 | 0x41, // Block Type 1890 | 0x30, 0x30, 0x30, 0x30, 0x30, // ASCII Block Number 1891 | 0x41 1892 | }; 1893 | 1894 | #endregion 1895 | 1896 | #region [Internals] 1897 | 1898 | // Defaults 1899 | private static int ISOTCP = 102; // ISOTCP Port 1900 | private static int MinPduSize = 16; 1901 | private static int MinPduSizeToRequest = 240; 1902 | private static int MaxPduSizeToRequest = 960; 1903 | private static int DefaultTimeout = 2000; 1904 | private static int IsoHSize = 7; // TPKT+COTP Header Size 1905 | 1906 | // Properties 1907 | private int _PDULength = 0; 1908 | private int _PduSizeRequested = 480; 1909 | private int _PLCPort = ISOTCP; 1910 | private int _RecvTimeout = DefaultTimeout; 1911 | private int _SendTimeout = DefaultTimeout; 1912 | private int _ConnTimeout = DefaultTimeout; 1913 | 1914 | // Privates 1915 | private string IPAddress; 1916 | private byte LocalTSAP_HI; 1917 | private byte LocalTSAP_LO; 1918 | private byte RemoteTSAP_HI; 1919 | private byte RemoteTSAP_LO; 1920 | private byte LastPDUType; 1921 | private ushort ConnType = CONNTYPE_PG; 1922 | private byte[] PDU = new byte[2048]; 1923 | private MsgSocket Socket = null; 1924 | private int Time_ms = 0; 1925 | 1926 | private void CreateSocket() 1927 | { 1928 | try 1929 | { 1930 | Socket = new MsgSocket(); 1931 | Socket.ConnectTimeout = _ConnTimeout; 1932 | Socket.ReadTimeout = _RecvTimeout; 1933 | Socket.WriteTimeout = _SendTimeout; 1934 | } 1935 | catch 1936 | { 1937 | } 1938 | } 1939 | 1940 | private int TCPConnect() 1941 | { 1942 | if (_LastError==0) 1943 | try 1944 | { 1945 | _LastError=Socket.Connect(IPAddress, _PLCPort); 1946 | } 1947 | catch 1948 | { 1949 | _LastError = S7Consts.errTCPConnectionFailed; 1950 | } 1951 | return _LastError; 1952 | } 1953 | 1954 | private void RecvPacket(byte[] Buffer, int Start, int Size) 1955 | { 1956 | if (Connected) 1957 | _LastError = Socket.Receive(Buffer, Start, Size); 1958 | else 1959 | _LastError = S7Consts.errTCPNotConnected; 1960 | } 1961 | 1962 | private void SendPacket(byte[] Buffer, int Len) 1963 | { 1964 | _LastError = Socket.Send(Buffer, Len); 1965 | } 1966 | 1967 | private void SendPacket(byte[] Buffer) 1968 | { 1969 | if (Connected) 1970 | SendPacket(Buffer, Buffer.Length); 1971 | else 1972 | _LastError = S7Consts.errTCPNotConnected; 1973 | } 1974 | 1975 | private int RecvIsoPacket() 1976 | { 1977 | Boolean Done = false; 1978 | int Size = 0; 1979 | while ((_LastError == 0) && !Done) 1980 | { 1981 | // Get TPKT (4 bytes) 1982 | RecvPacket(PDU, 0, 4); 1983 | if (_LastError == 0) 1984 | { 1985 | Size = S7.GetWordAt(PDU, 2); 1986 | // Check 0 bytes Data Packet (only TPKT+COTP = 7 bytes) 1987 | if (Size == IsoHSize) 1988 | RecvPacket(PDU, 4, 3); // Skip remaining 3 bytes and Done is still false 1989 | else 1990 | { 1991 | if ((Size > _PduSizeRequested + IsoHSize) || (Size < MinPduSize)) 1992 | _LastError = S7Consts.errIsoInvalidPDU; 1993 | else 1994 | Done = true; // a valid Length !=7 && >16 && <247 1995 | } 1996 | } 1997 | } 1998 | if (_LastError == 0) 1999 | { 2000 | RecvPacket(PDU, 4, 3); // Skip remaining 3 COTP bytes 2001 | LastPDUType = PDU[5]; // Stores PDU Type, we need it 2002 | // Receives the S7 Payload 2003 | RecvPacket(PDU, 7, Size - IsoHSize); 2004 | } 2005 | if (_LastError == 0) 2006 | return Size; 2007 | else 2008 | return 0; 2009 | } 2010 | 2011 | private int ISOConnect() 2012 | { 2013 | int Size; 2014 | ISO_CR[16] = LocalTSAP_HI; 2015 | ISO_CR[17] = LocalTSAP_LO; 2016 | ISO_CR[20] = RemoteTSAP_HI; 2017 | ISO_CR[21] = RemoteTSAP_LO; 2018 | 2019 | // Sends the connection request telegram 2020 | SendPacket(ISO_CR); 2021 | if (_LastError == 0) 2022 | { 2023 | // Gets the reply (if any) 2024 | Size = RecvIsoPacket(); 2025 | if (_LastError == 0) 2026 | { 2027 | if (Size == 22) 2028 | { 2029 | if (LastPDUType != (byte)0xD0) // 0xD0 = CC Connection confirm 2030 | _LastError = S7Consts.errIsoConnect; 2031 | } 2032 | else 2033 | _LastError = S7Consts.errIsoInvalidPDU; 2034 | } 2035 | } 2036 | return _LastError; 2037 | } 2038 | 2039 | private int NegotiatePduLength() 2040 | { 2041 | int Length; 2042 | // Set PDU Size Requested 2043 | S7.SetWordAt(S7_PN, 23, (ushort)_PduSizeRequested); 2044 | // Sends the connection request telegram 2045 | SendPacket(S7_PN); 2046 | if (_LastError == 0) 2047 | { 2048 | Length = RecvIsoPacket(); 2049 | if (_LastError == 0) 2050 | { 2051 | // check S7 Error 2052 | if ((Length == 27) && (PDU[17] == 0) && (PDU[18] == 0)) // 20 = size of Negotiate Answer 2053 | { 2054 | // Get PDU Size Negotiated 2055 | _PDULength = S7.GetWordAt(PDU, 25); 2056 | if (_PDULength <= 0) 2057 | _LastError = S7Consts.errCliNegotiatingPDU; 2058 | } 2059 | else 2060 | _LastError = S7Consts.errCliNegotiatingPDU; 2061 | } 2062 | } 2063 | return _LastError; 2064 | } 2065 | 2066 | private int CpuError(ushort Error) 2067 | { 2068 | switch(Error) 2069 | { 2070 | case 0 : return 0; 2071 | case Code7AddressOutOfRange : return S7Consts.errCliAddressOutOfRange; 2072 | case Code7InvalidTransportSize : return S7Consts.errCliInvalidTransportSize; 2073 | case Code7WriteDataSizeMismatch : return S7Consts.errCliWriteDataSizeMismatch; 2074 | case Code7ResItemNotAvailable : 2075 | case Code7ResItemNotAvailable1 : return S7Consts.errCliItemNotAvailable; 2076 | case Code7DataOverPDU : return S7Consts.errCliSizeOverPDU; 2077 | case Code7InvalidValue : return S7Consts.errCliInvalidValue; 2078 | case Code7FunNotAvailable : return S7Consts.errCliFunNotAvailable; 2079 | case Code7NeedPassword : return S7Consts.errCliNeedPassword; 2080 | case Code7InvalidPassword : return S7Consts.errCliInvalidPassword; 2081 | case Code7NoPasswordToSet : 2082 | case Code7NoPasswordToClear : return S7Consts.errCliNoPasswordToSetOrClear; 2083 | default: 2084 | return S7Consts.errCliFunctionRefused; 2085 | }; 2086 | } 2087 | 2088 | #endregion 2089 | 2090 | #region [Class Control] 2091 | 2092 | public S7Client() 2093 | { 2094 | CreateSocket(); 2095 | } 2096 | 2097 | ~S7Client() 2098 | { 2099 | Disconnect(); 2100 | } 2101 | 2102 | public int Connect() 2103 | { 2104 | _LastError = 0; 2105 | Time_ms = 0; 2106 | int Elapsed = Environment.TickCount; 2107 | if (!Connected) 2108 | { 2109 | TCPConnect(); // First stage : TCP Connection 2110 | if (_LastError == 0) 2111 | { 2112 | ISOConnect(); // Second stage : ISOTCP (ISO 8073) Connection 2113 | if (_LastError == 0) 2114 | { 2115 | _LastError = NegotiatePduLength(); // Third stage : S7 PDU negotiation 2116 | } 2117 | } 2118 | } 2119 | if (_LastError != 0) 2120 | Disconnect(); 2121 | else 2122 | Time_ms = Environment.TickCount - Elapsed; 2123 | 2124 | return _LastError; 2125 | } 2126 | 2127 | public int ConnectTo(string Address, int Rack, int Slot) 2128 | { 2129 | UInt16 RemoteTSAP = (UInt16)((ConnType << 8) + (Rack * 0x20) + Slot); 2130 | SetConnectionParams(Address, 0x0100, RemoteTSAP); 2131 | return Connect(); 2132 | } 2133 | 2134 | public int SetConnectionParams(string Address, ushort LocalTSAP, ushort RemoteTSAP) 2135 | { 2136 | int LocTSAP = LocalTSAP & 0x0000FFFF; 2137 | int RemTSAP = RemoteTSAP & 0x0000FFFF; 2138 | IPAddress = Address; 2139 | LocalTSAP_HI = (byte)(LocTSAP >> 8); 2140 | LocalTSAP_LO = (byte)(LocTSAP & 0x00FF); 2141 | RemoteTSAP_HI = (byte)(RemTSAP >> 8); 2142 | RemoteTSAP_LO = (byte)(RemTSAP & 0x00FF); 2143 | return 0; 2144 | } 2145 | 2146 | public int SetConnectionType(ushort ConnectionType) 2147 | { 2148 | ConnType = ConnectionType; 2149 | return 0; 2150 | } 2151 | 2152 | public int Disconnect() 2153 | { 2154 | Socket.Close(); 2155 | return 0; 2156 | } 2157 | 2158 | public int GetParam(Int32 ParamNumber, ref int Value) 2159 | { 2160 | int Result = 0; 2161 | switch (ParamNumber) 2162 | { 2163 | case S7Consts.p_u16_RemotePort: 2164 | { 2165 | Value = PLCPort; 2166 | break; 2167 | } 2168 | case S7Consts.p_i32_PingTimeout: 2169 | { 2170 | Value = ConnTimeout; 2171 | break; 2172 | } 2173 | case S7Consts.p_i32_SendTimeout: 2174 | { 2175 | Value = SendTimeout; 2176 | break; 2177 | } 2178 | case S7Consts.p_i32_RecvTimeout: 2179 | { 2180 | Value = RecvTimeout; 2181 | break; 2182 | } 2183 | case S7Consts.p_i32_PDURequest: 2184 | { 2185 | Value = PduSizeRequested; 2186 | break; 2187 | } 2188 | default: 2189 | { 2190 | Result = S7Consts.errCliInvalidParamNumber; 2191 | break; 2192 | } 2193 | } 2194 | return Result; 2195 | } 2196 | 2197 | // Set Properties for compatibility with Snap7.net.cs 2198 | public int SetParam(Int32 ParamNumber, ref int Value) 2199 | { 2200 | int Result = 0; 2201 | switch(ParamNumber) 2202 | { 2203 | case S7Consts.p_u16_RemotePort: 2204 | { 2205 | PLCPort = Value; 2206 | break; 2207 | } 2208 | case S7Consts.p_i32_PingTimeout: 2209 | { 2210 | ConnTimeout = Value; 2211 | break; 2212 | } 2213 | case S7Consts.p_i32_SendTimeout: 2214 | { 2215 | SendTimeout = Value; 2216 | break; 2217 | } 2218 | case S7Consts.p_i32_RecvTimeout: 2219 | { 2220 | RecvTimeout = Value; 2221 | break; 2222 | } 2223 | case S7Consts.p_i32_PDURequest: 2224 | { 2225 | PduSizeRequested = Value; 2226 | break; 2227 | } 2228 | default: 2229 | { 2230 | Result = S7Consts.errCliInvalidParamNumber; 2231 | break; 2232 | } 2233 | } 2234 | return Result; 2235 | } 2236 | 2237 | public delegate void S7CliCompletion(IntPtr usrPtr, int opCode, int opResult); 2238 | public int SetAsCallBack(S7CliCompletion Completion, IntPtr usrPtr) 2239 | { 2240 | return S7Consts.errCliFunctionNotImplemented; 2241 | } 2242 | 2243 | #endregion 2244 | 2245 | #region [Data I/O main functions] 2246 | 2247 | public int ReadArea(int Area, int DBNumber, int Start, int Amount, int WordLen, byte[] Buffer) 2248 | { 2249 | int BytesRead = 0; 2250 | return ReadArea(Area, DBNumber, Start, Amount, WordLen, Buffer, ref BytesRead); 2251 | } 2252 | 2253 | public int ReadArea(int Area, int DBNumber, int Start, int Amount, int WordLen, byte[] Buffer, ref int BytesRead) 2254 | { 2255 | int Address; 2256 | int NumElements; 2257 | int MaxElements; 2258 | int TotElements; 2259 | int SizeRequested; 2260 | int Length; 2261 | int Offset = 0; 2262 | int WordSize = 1; 2263 | 2264 | _LastError = 0; 2265 | Time_ms = 0; 2266 | int Elapsed = Environment.TickCount; 2267 | // Some adjustment 2268 | if (Area == S7Consts.S7AreaCT) 2269 | WordLen = S7Consts.S7WLCounter; 2270 | if (Area == S7Consts.S7AreaTM) 2271 | WordLen = S7Consts.S7WLTimer; 2272 | 2273 | // Calc Word size 2274 | WordSize = S7.DataSizeByte(WordLen); 2275 | if (WordSize == 0) 2276 | return S7Consts.errCliInvalidWordLen; 2277 | 2278 | if (WordLen == S7Consts.S7WLBit) 2279 | Amount = 1; // Only 1 bit can be transferred at time 2280 | else 2281 | { 2282 | if ((WordLen != S7Consts.S7WLCounter) && (WordLen != S7Consts.S7WLTimer)) 2283 | { 2284 | Amount = Amount * WordSize; 2285 | WordSize = 1; 2286 | WordLen = S7Consts.S7WLByte; 2287 | } 2288 | } 2289 | 2290 | MaxElements = (_PDULength - 18) / WordSize; // 18 = Reply telegram header 2291 | TotElements = Amount; 2292 | 2293 | while ((TotElements > 0) && (_LastError == 0)) 2294 | { 2295 | NumElements = TotElements; 2296 | if (NumElements > MaxElements) 2297 | NumElements = MaxElements; 2298 | 2299 | SizeRequested = NumElements * WordSize; 2300 | 2301 | // Setup the telegram 2302 | Array.Copy(S7_RW, 0, PDU, 0, Size_RD); 2303 | // Set DB Number 2304 | PDU[27] = (byte)Area; 2305 | // Set Area 2306 | if (Area == S7Consts.S7AreaDB) 2307 | S7.SetWordAt(PDU, 25, (ushort)DBNumber); 2308 | 2309 | // Adjusts Start and word length 2310 | if ((WordLen == S7Consts.S7WLBit) || (WordLen == S7Consts.S7WLCounter) || (WordLen == S7Consts.S7WLTimer)) 2311 | { 2312 | Address = Start; 2313 | PDU[22] = (byte)WordLen; 2314 | } 2315 | else 2316 | Address = Start << 3; 2317 | 2318 | // Num elements 2319 | S7.SetWordAt(PDU, 23, (ushort)NumElements); 2320 | 2321 | // Address into the PLC (only 3 bytes) 2322 | PDU[30] = (byte)(Address & 0x0FF); 2323 | Address = Address >> 8; 2324 | PDU[29] = (byte)(Address & 0x0FF); 2325 | Address = Address >> 8; 2326 | PDU[28] = (byte)(Address & 0x0FF); 2327 | 2328 | SendPacket(PDU, Size_RD); 2329 | if (_LastError == 0) 2330 | { 2331 | Length = RecvIsoPacket(); 2332 | if (_LastError == 0) 2333 | { 2334 | if (Length<25) 2335 | _LastError = S7Consts.errIsoInvalidDataSize; 2336 | else 2337 | { 2338 | if (PDU[21] != 0xFF) 2339 | _LastError = CpuError(PDU[21]); 2340 | else 2341 | { 2342 | Array.Copy(PDU, 25, Buffer, Offset, SizeRequested); 2343 | Offset += SizeRequested; 2344 | } 2345 | } 2346 | } 2347 | } 2348 | TotElements -= NumElements; 2349 | Start += NumElements * WordSize; 2350 | } 2351 | 2352 | if (_LastError == 0) 2353 | { 2354 | BytesRead = Offset; 2355 | Time_ms = Environment.TickCount - Elapsed; 2356 | } 2357 | else 2358 | BytesRead = 0; 2359 | return _LastError; 2360 | } 2361 | 2362 | public int WriteArea(int Area, int DBNumber, int Start, int Amount, int WordLen, byte[] Buffer) 2363 | { 2364 | int BytesWritten = 0; 2365 | return WriteArea(Area, DBNumber, Start, Amount, WordLen, Buffer, ref BytesWritten); 2366 | } 2367 | 2368 | public int WriteArea(int Area, int DBNumber, int Start, int Amount, int WordLen, byte[] Buffer, ref int BytesWritten) 2369 | { 2370 | int Address; 2371 | int NumElements; 2372 | int MaxElements; 2373 | int TotElements; 2374 | int DataSize; 2375 | int IsoSize; 2376 | int Length; 2377 | int Offset = 0; 2378 | int WordSize = 1; 2379 | 2380 | _LastError = 0; 2381 | Time_ms = 0; 2382 | int Elapsed = Environment.TickCount; 2383 | // Some adjustment 2384 | if (Area == S7Consts.S7AreaCT) 2385 | WordLen = S7Consts.S7WLCounter; 2386 | if (Area == S7Consts.S7AreaTM) 2387 | WordLen = S7Consts.S7WLTimer; 2388 | 2389 | // Calc Word size 2390 | WordSize = S7.DataSizeByte(WordLen); 2391 | if (WordSize == 0) 2392 | return S7Consts.errCliInvalidWordLen; 2393 | 2394 | if (WordLen == S7Consts.S7WLBit) // Only 1 bit can be transferred at time 2395 | Amount = 1; 2396 | else 2397 | { 2398 | if ((WordLen != S7Consts.S7WLCounter) && (WordLen != S7Consts.S7WLTimer)) 2399 | { 2400 | Amount = Amount * WordSize; 2401 | WordSize = 1; 2402 | WordLen = S7Consts.S7WLByte; 2403 | } 2404 | } 2405 | 2406 | MaxElements = (_PDULength - 35) / WordSize; // 35 = Reply telegram header 2407 | TotElements = Amount; 2408 | 2409 | while ((TotElements > 0) && (_LastError == 0)) 2410 | { 2411 | NumElements = TotElements; 2412 | if (NumElements > MaxElements) 2413 | NumElements = MaxElements; 2414 | 2415 | DataSize = NumElements * WordSize; 2416 | IsoSize = Size_WR + DataSize; 2417 | 2418 | // Setup the telegram 2419 | Array.Copy(S7_RW, 0, PDU, 0, Size_WR); 2420 | // Whole telegram Size 2421 | S7.SetWordAt(PDU, 2, (ushort)IsoSize); 2422 | // Data Length 2423 | Length = DataSize + 4; 2424 | S7.SetWordAt(PDU, 15, (ushort)Length); 2425 | // Function 2426 | PDU[17] = (byte)0x05; 2427 | // Set DB Number 2428 | PDU[27] = (byte)Area; 2429 | if (Area == S7Consts.S7AreaDB) 2430 | S7.SetWordAt(PDU, 25, (ushort)DBNumber); 2431 | 2432 | 2433 | // Adjusts Start and word length 2434 | if ((WordLen == S7Consts.S7WLBit) || (WordLen == S7Consts.S7WLCounter) || (WordLen == S7Consts.S7WLTimer)) 2435 | { 2436 | Address = Start; 2437 | Length = DataSize; 2438 | PDU[22] = (byte)WordLen; 2439 | } 2440 | else 2441 | { 2442 | Address = Start << 3; 2443 | Length = DataSize << 3; 2444 | } 2445 | 2446 | // Num elements 2447 | S7.SetWordAt(PDU, 23, (ushort)NumElements); 2448 | // Address into the PLC 2449 | PDU[30] = (byte)(Address & 0x0FF); 2450 | Address = Address >> 8; 2451 | PDU[29] = (byte)(Address & 0x0FF); 2452 | Address = Address >> 8; 2453 | PDU[28] = (byte)(Address & 0x0FF); 2454 | 2455 | // Transport Size 2456 | switch (WordLen) 2457 | { 2458 | case S7Consts.S7WLBit: 2459 | PDU[32] = TS_ResBit; 2460 | break; 2461 | case S7Consts.S7WLCounter: 2462 | case S7Consts.S7WLTimer: 2463 | PDU[32] = TS_ResOctet; 2464 | break; 2465 | default: 2466 | PDU[32] = TS_ResByte; // byte/word/dword etc. 2467 | break; 2468 | }; 2469 | // Length 2470 | S7.SetWordAt(PDU, 33, (ushort)Length); 2471 | 2472 | // Copies the Data 2473 | Array.Copy(Buffer, Offset, PDU, 35, DataSize); 2474 | 2475 | SendPacket(PDU, IsoSize); 2476 | if (_LastError == 0) 2477 | { 2478 | Length = RecvIsoPacket(); 2479 | if (_LastError == 0) 2480 | { 2481 | if (Length == 22) 2482 | { 2483 | if (PDU[21] != (byte)0xFF) 2484 | _LastError = CpuError(PDU[21]); 2485 | } 2486 | else 2487 | _LastError = S7Consts.errIsoInvalidPDU; 2488 | } 2489 | } 2490 | Offset += DataSize; 2491 | TotElements -= NumElements; 2492 | Start += NumElements * WordSize; 2493 | } 2494 | 2495 | if (_LastError == 0) 2496 | { 2497 | BytesWritten = Offset; 2498 | Time_ms = Environment.TickCount - Elapsed; 2499 | } 2500 | else 2501 | BytesWritten = 0; 2502 | 2503 | return _LastError; 2504 | } 2505 | 2506 | public int ReadMultiVars(S7DataItem[] Items, int ItemsCount) 2507 | { 2508 | int Offset; 2509 | int Length; 2510 | int ItemSize; 2511 | byte[] S7Item = new byte[12]; 2512 | byte[] S7ItemRead = new byte[1024]; 2513 | 2514 | _LastError = 0; 2515 | Time_ms = 0; 2516 | int Elapsed = Environment.TickCount; 2517 | 2518 | // Checks items 2519 | if (ItemsCount > MaxVars) 2520 | return S7Consts.errCliTooManyItems; 2521 | 2522 | // Fills Header 2523 | Array.Copy(S7_MRD_HEADER, 0, PDU, 0, S7_MRD_HEADER.Length); 2524 | S7.SetWordAt(PDU, 13, (ushort)(ItemsCount * S7Item.Length + 2)); 2525 | PDU[18] = (byte)ItemsCount; 2526 | // Fills the Items 2527 | Offset = 19; 2528 | for (int c = 0; c < ItemsCount; c++) 2529 | { 2530 | Array.Copy(S7_MRD_ITEM, S7Item, S7Item.Length); 2531 | S7Item[3] = (byte)Items[c].WordLen; 2532 | S7.SetWordAt(S7Item, 4, (ushort)Items[c].Amount); 2533 | if (Items[c].Area == S7Consts.S7AreaDB) 2534 | S7.SetWordAt(S7Item, 6, (ushort)Items[c].DBNumber); 2535 | S7Item[8] = (byte)Items[c].Area; 2536 | 2537 | // Address into the PLC 2538 | int Address = Items[c].Start; 2539 | S7Item[11] = (byte)(Address & 0x0FF); 2540 | Address = Address >> 8; 2541 | S7Item[10] = (byte)(Address & 0x0FF); 2542 | Address = Address >> 8; 2543 | S7Item[09] = (byte)(Address & 0x0FF); 2544 | 2545 | Array.Copy(S7Item, 0, PDU, Offset, S7Item.Length); 2546 | Offset += S7Item.Length; 2547 | } 2548 | 2549 | if (Offset > _PDULength) 2550 | return S7Consts.errCliSizeOverPDU; 2551 | 2552 | S7.SetWordAt(PDU, 2, (ushort)Offset); // Whole size 2553 | SendPacket(PDU, Offset); 2554 | 2555 | if (_LastError != 0) 2556 | return _LastError; 2557 | // Get Answer 2558 | Length = RecvIsoPacket(); 2559 | if (_LastError != 0) 2560 | return _LastError; 2561 | // Check ISO Length 2562 | if (Length < 22) 2563 | { 2564 | _LastError = S7Consts.errIsoInvalidPDU; // PDU too Small 2565 | return _LastError; 2566 | } 2567 | // Check Global Operation Result 2568 | _LastError = CpuError(S7.GetWordAt(PDU, 17)); 2569 | if (_LastError != 0) 2570 | return _LastError; 2571 | // Get true ItemsCount 2572 | int ItemsRead = S7.GetByteAt(PDU, 20); 2573 | if ((ItemsRead != ItemsCount) || (ItemsRead>MaxVars)) 2574 | { 2575 | _LastError = S7Consts.errCliInvalidPlcAnswer; 2576 | return _LastError; 2577 | } 2578 | // Get Data 2579 | Offset = 21; 2580 | for (int c = 0; c < ItemsCount; c++) 2581 | { 2582 | // Get the Item 2583 | Array.Copy(PDU, Offset, S7ItemRead, 0, Length-Offset); 2584 | if (S7ItemRead[0] == 0xff) 2585 | { 2586 | ItemSize = (int)S7.GetWordAt(S7ItemRead, 2); 2587 | if ((S7ItemRead[1] != TS_ResOctet) && (S7ItemRead[1] != TS_ResReal) && (S7ItemRead[1] != TS_ResBit)) 2588 | ItemSize = ItemSize >> 3; 2589 | Marshal.Copy(S7ItemRead, 4, Items[c].pData, ItemSize); 2590 | Items[c].Result = 0; 2591 | if (ItemSize % 2 != 0) 2592 | ItemSize++; // Odd size are rounded 2593 | Offset = Offset + 4 + ItemSize; 2594 | } 2595 | else 2596 | { 2597 | Items[c].Result = CpuError(S7ItemRead[0]); 2598 | Offset += 4; // Skip the Item header 2599 | } 2600 | } 2601 | Time_ms = Environment.TickCount - Elapsed; 2602 | return _LastError; 2603 | } 2604 | 2605 | public int WriteMultiVars(S7DataItem[] Items, int ItemsCount) 2606 | { 2607 | int Offset; 2608 | int ParLength; 2609 | int DataLength; 2610 | int ItemDataSize; 2611 | byte[] S7ParItem = new byte[S7_MWR_PARAM.Length]; 2612 | byte[] S7DataItem = new byte[1024]; 2613 | 2614 | _LastError = 0; 2615 | Time_ms = 0; 2616 | int Elapsed = Environment.TickCount; 2617 | 2618 | // Checks items 2619 | if (ItemsCount > MaxVars) 2620 | return S7Consts.errCliTooManyItems; 2621 | // Fills Header 2622 | Array.Copy(S7_MWR_HEADER, 0, PDU, 0, S7_MWR_HEADER.Length); 2623 | ParLength = ItemsCount * S7_MWR_PARAM.Length + 2; 2624 | S7.SetWordAt(PDU, 13, (ushort)ParLength); 2625 | PDU[18] = (byte)ItemsCount; 2626 | // Fills Params 2627 | Offset = S7_MWR_HEADER.Length; 2628 | for (int c=0; c> 8; 2639 | S7ParItem[10] = (byte)(Address & 0x0FF); 2640 | Address = Address >> 8; 2641 | S7ParItem[09] = (byte)(Address & 0x0FF); 2642 | Array.Copy(S7ParItem, 0, PDU, Offset, S7ParItem.Length); 2643 | Offset += S7_MWR_PARAM.Length; 2644 | } 2645 | // Fills Data 2646 | DataLength = 0; 2647 | for (int c = 0; c < ItemsCount; c++) 2648 | { 2649 | S7DataItem[0] = 0x00; 2650 | switch (Items[c].WordLen) 2651 | { 2652 | case S7Consts.S7WLBit: 2653 | S7DataItem[1] = TS_ResBit; 2654 | break; 2655 | case S7Consts.S7WLCounter: 2656 | case S7Consts.S7WLTimer: 2657 | S7DataItem[1] = TS_ResOctet; 2658 | break; 2659 | default: 2660 | S7DataItem[1] = TS_ResByte; // byte/word/dword etc. 2661 | break; 2662 | }; 2663 | if ((Items[c].WordLen==S7Consts.S7WLTimer) || (Items[c].WordLen == S7Consts.S7WLCounter)) 2664 | ItemDataSize = Items[c].Amount * 2; 2665 | else 2666 | ItemDataSize = Items[c].Amount; 2667 | 2668 | if ((S7DataItem[1] != TS_ResOctet) && (S7DataItem[1] != TS_ResBit)) 2669 | S7.SetWordAt(S7DataItem, 2, (ushort)(ItemDataSize*8)); 2670 | else 2671 | S7.SetWordAt(S7DataItem, 2, (ushort)ItemDataSize); 2672 | 2673 | Marshal.Copy(Items[c].pData, S7DataItem, 4, ItemDataSize); 2674 | if (ItemDataSize % 2 != 0) 2675 | { 2676 | S7DataItem[ItemDataSize+4] = 0x00; 2677 | ItemDataSize++; 2678 | } 2679 | Array.Copy(S7DataItem, 0, PDU, Offset, ItemDataSize+4); 2680 | Offset = Offset + ItemDataSize + 4; 2681 | DataLength = DataLength + ItemDataSize + 4; 2682 | } 2683 | 2684 | // Checks the size 2685 | if (Offset > _PDULength) 2686 | return S7Consts.errCliSizeOverPDU; 2687 | 2688 | S7.SetWordAt(PDU, 2, (ushort)Offset); // Whole size 2689 | S7.SetWordAt(PDU, 15, (ushort)DataLength); // Whole size 2690 | SendPacket(PDU, Offset); 2691 | 2692 | RecvIsoPacket(); 2693 | if (_LastError==0) 2694 | { 2695 | // Check Global Operation Result 2696 | _LastError = CpuError(S7.GetWordAt(PDU, 17)); 2697 | if (_LastError != 0) 2698 | return _LastError; 2699 | // Get true ItemsCount 2700 | int ItemsWritten = S7.GetByteAt(PDU, 20); 2701 | if ((ItemsWritten != ItemsCount) || (ItemsWritten > MaxVars)) 2702 | { 2703 | _LastError = S7Consts.errCliInvalidPlcAnswer; 2704 | return _LastError; 2705 | } 2706 | 2707 | for (int c=0; c> 8); 2783 | sBuffer[c * 2] = (byte)(Buffer[c] & 0x00FF); 2784 | } 2785 | return WriteArea(S7Consts.S7AreaTM, 0, Start, Amount, S7Consts.S7WLTimer, sBuffer); 2786 | } 2787 | 2788 | public int CTRead(int Start, int Amount, ushort[] Buffer) 2789 | { 2790 | byte[] sBuffer = new byte[Amount * 2]; 2791 | int Result = ReadArea(S7Consts.S7AreaCT, 0, Start, Amount, S7Consts.S7WLCounter, sBuffer); 2792 | if (Result==0) 2793 | { 2794 | for (int c=0; c>8); 2808 | sBuffer[c * 2]= (byte)(Buffer[c] & 0x00FF); 2809 | } 2810 | return WriteArea(S7Consts.S7AreaCT, 0, Start, Amount, S7Consts.S7WLCounter, sBuffer); 2811 | } 2812 | 2813 | #endregion 2814 | 2815 | #region [Directory functions] 2816 | 2817 | public int ListBlocks(ref S7BlocksList List) 2818 | { 2819 | return S7Consts.errCliFunctionNotImplemented; 2820 | } 2821 | 2822 | private string SiemensTimestamp(long EncodedDate) 2823 | { 2824 | DateTime DT = new DateTime(1984, 1, 1).AddSeconds(EncodedDate*86400); 2825 | #if WINDOWS_UWP || NETFX_CORE || CORE_CLR 2826 | return DT.ToString(System.Globalization.DateTimeFormatInfo.CurrentInfo.ShortDatePattern); 2827 | #else 2828 | return DT.ToShortDateString(); 2829 | #endif 2830 | } 2831 | 2832 | public int GetAgBlockInfo(int BlockType, int BlockNum, ref S7BlockInfo Info) 2833 | { 2834 | _LastError = 0; 2835 | Time_ms = 0; 2836 | int Elapsed = Environment.TickCount; 2837 | 2838 | S7_BI[30] = (byte)BlockType; 2839 | // Block Number 2840 | S7_BI[31] = (byte)((BlockNum / 10000) + 0x30); 2841 | BlockNum = BlockNum % 10000; 2842 | S7_BI[32] = (byte)((BlockNum / 1000) + 0x30); 2843 | BlockNum = BlockNum % 1000; 2844 | S7_BI[33] = (byte)((BlockNum / 100) + 0x30); 2845 | BlockNum = BlockNum % 100; 2846 | S7_BI[34] = (byte)((BlockNum / 10) + 0x30); 2847 | BlockNum = BlockNum % 10; 2848 | S7_BI[35] = (byte)((BlockNum / 1) + 0x30); 2849 | 2850 | SendPacket(S7_BI); 2851 | 2852 | if (_LastError == 0) 2853 | { 2854 | int Length = RecvIsoPacket(); 2855 | if (Length > 32) // the minimum expected 2856 | { 2857 | ushort Result = S7.GetWordAt(PDU, 27); 2858 | if (Result == 0) 2859 | { 2860 | Info.BlkFlags= PDU[42]; 2861 | Info.BlkLang = PDU[43]; 2862 | Info.BlkType = PDU[44]; 2863 | Info.BlkNumber = S7.GetWordAt(PDU, 45); 2864 | Info.LoadSize = S7.GetDIntAt(PDU, 47); 2865 | Info.CodeDate = SiemensTimestamp(S7.GetWordAt(PDU, 59)); 2866 | Info.IntfDate = SiemensTimestamp(S7.GetWordAt(PDU, 65)); 2867 | Info.SBBLength = S7.GetWordAt(PDU, 67); 2868 | Info.LocalData = S7.GetWordAt(PDU, 71); 2869 | Info.MC7Size = S7.GetWordAt(PDU, 73); 2870 | Info.Author = S7.GetCharsAt(PDU, 75, 8).Trim(new char[]{(char)0}); 2871 | Info.Family = S7.GetCharsAt(PDU, 83, 8).Trim(new char[]{(char)0}); 2872 | Info.Header = S7.GetCharsAt(PDU, 91, 8).Trim(new char[]{(char)0}); 2873 | Info.Version = PDU[99]; 2874 | Info.CheckSum = S7.GetWordAt(PDU, 101); 2875 | } 2876 | else 2877 | _LastError = CpuError(Result); 2878 | } 2879 | else 2880 | _LastError = S7Consts.errIsoInvalidPDU; 2881 | } 2882 | if (_LastError == 0) 2883 | Time_ms = Environment.TickCount - Elapsed; 2884 | 2885 | return _LastError; 2886 | 2887 | } 2888 | 2889 | public int GetPgBlockInfo(ref S7BlockInfo Info, byte[] Buffer, int Size) 2890 | { 2891 | return S7Consts.errCliFunctionNotImplemented; 2892 | } 2893 | 2894 | public int ListBlocksOfType(int BlockType, ushort[] List, ref int ItemsCount) 2895 | { 2896 | return S7Consts.errCliFunctionNotImplemented; 2897 | } 2898 | 2899 | #endregion 2900 | 2901 | #region [Blocks functions] 2902 | 2903 | public int Upload(int BlockType, int BlockNum, byte[] UsrData, ref int Size) 2904 | { 2905 | return S7Consts.errCliFunctionNotImplemented; 2906 | } 2907 | 2908 | public int FullUpload(int BlockType, int BlockNum, byte[] UsrData, ref int Size) 2909 | { 2910 | return S7Consts.errCliFunctionNotImplemented; 2911 | } 2912 | 2913 | public int Download(int BlockNum, byte[] UsrData, int Size) 2914 | { 2915 | return S7Consts.errCliFunctionNotImplemented; 2916 | } 2917 | 2918 | public int Delete(int BlockType, int BlockNum) 2919 | { 2920 | return S7Consts.errCliFunctionNotImplemented; 2921 | } 2922 | 2923 | public int DBGet(int DBNumber, byte[] UsrData, ref int Size) 2924 | { 2925 | S7BlockInfo BI = new S7BlockInfo(); 2926 | int Elapsed = Environment.TickCount; 2927 | Time_ms = 0; 2928 | 2929 | _LastError = GetAgBlockInfo(Block_DB, DBNumber, ref BI); 2930 | 2931 | if (_LastError==0) 2932 | { 2933 | int DBSize = BI.MC7Size; 2934 | if (DBSize <= UsrData.Length) 2935 | { 2936 | Size = DBSize; 2937 | _LastError = DBRead(DBNumber, 0, DBSize, UsrData); 2938 | if (_LastError == 0) 2939 | Size = DBSize; 2940 | } 2941 | else 2942 | _LastError = S7Consts.errCliBufferTooSmall; 2943 | } 2944 | if (_LastError == 0) 2945 | Time_ms = Environment.TickCount - Elapsed; 2946 | return _LastError; 2947 | } 2948 | 2949 | public int DBFill(int DBNumber, int FillChar) 2950 | { 2951 | S7BlockInfo BI = new S7BlockInfo(); 2952 | int Elapsed = Environment.TickCount; 2953 | Time_ms = 0; 2954 | 2955 | _LastError = GetAgBlockInfo(Block_DB, DBNumber, ref BI); 2956 | 2957 | if (_LastError == 0) 2958 | { 2959 | byte[] Buffer = new byte[BI.MC7Size]; 2960 | for (int c = 0; c < BI.MC7Size; c++) 2961 | Buffer[c] = (byte)FillChar; 2962 | _LastError = DBWrite(DBNumber, 0, BI.MC7Size, Buffer); 2963 | } 2964 | if (_LastError == 0) 2965 | Time_ms = Environment.TickCount - Elapsed; 2966 | return _LastError; 2967 | } 2968 | 2969 | #endregion 2970 | 2971 | #region [Date/Time functions] 2972 | 2973 | public int GetPlcDateTime(ref DateTime DT) 2974 | { 2975 | int Length; 2976 | _LastError = 0; 2977 | Time_ms = 0; 2978 | int Elapsed = Environment.TickCount; 2979 | 2980 | SendPacket(S7_GET_DT); 2981 | if (_LastError == 0) 2982 | { 2983 | Length = RecvIsoPacket(); 2984 | if (Length > 30) // the minimum expected 2985 | { 2986 | if ((S7.GetWordAt(PDU, 27) == 0) && (PDU[29] == 0xFF)) 2987 | { 2988 | DT = S7.GetDateTimeAt(PDU, 35); 2989 | } 2990 | else 2991 | _LastError = S7Consts.errCliInvalidPlcAnswer; 2992 | } 2993 | else 2994 | _LastError = S7Consts.errIsoInvalidPDU; 2995 | } 2996 | 2997 | if(_LastError==0) 2998 | Time_ms = Environment.TickCount - Elapsed; 2999 | 3000 | return _LastError; 3001 | } 3002 | 3003 | public int SetPlcDateTime(DateTime DT) 3004 | { 3005 | int Length; 3006 | _LastError = 0; 3007 | Time_ms = 0; 3008 | int Elapsed = Environment.TickCount; 3009 | 3010 | S7.SetDateTimeAt(S7_SET_DT, 31, DT); 3011 | SendPacket(S7_SET_DT); 3012 | if (_LastError == 0) 3013 | { 3014 | Length = RecvIsoPacket(); 3015 | if (Length > 30) // the minimum expected 3016 | { 3017 | if (S7.GetWordAt(PDU, 27) != 0) 3018 | _LastError = S7Consts.errCliInvalidPlcAnswer; 3019 | } 3020 | else 3021 | _LastError = S7Consts.errIsoInvalidPDU; 3022 | } 3023 | if (_LastError == 0) 3024 | Time_ms = Environment.TickCount - Elapsed; 3025 | 3026 | return _LastError; 3027 | } 3028 | 3029 | public int SetPlcSystemDateTime() 3030 | { 3031 | return SetPlcDateTime(DateTime.Now); 3032 | } 3033 | 3034 | #endregion 3035 | 3036 | #region [System Info functions] 3037 | 3038 | public int GetOrderCode(ref S7OrderCode Info) 3039 | { 3040 | S7SZL SZL = new S7SZL(); 3041 | int Size = 1024; 3042 | SZL.Data = new byte[Size]; 3043 | int Elapsed = Environment.TickCount; 3044 | _LastError = ReadSZL(0x0011, 0x000, ref SZL, ref Size); 3045 | if (_LastError == 0) 3046 | { 3047 | Info.Code = S7.GetCharsAt(SZL.Data, 2, 20); 3048 | Info.V1 = SZL.Data[Size - 3]; 3049 | Info.V2 = SZL.Data[Size - 2]; 3050 | Info.V3 = SZL.Data[Size - 1]; 3051 | } 3052 | if (_LastError == 0) 3053 | Time_ms = Environment.TickCount - Elapsed; 3054 | return _LastError; 3055 | } 3056 | 3057 | public int GetCpuInfo(ref S7CpuInfo Info) 3058 | { 3059 | S7SZL SZL = new S7SZL(); 3060 | int Size = 1024; 3061 | SZL.Data = new byte[Size]; 3062 | int Elapsed = Environment.TickCount; 3063 | _LastError = ReadSZL(0x001C, 0x000, ref SZL, ref Size); 3064 | if (_LastError == 0) 3065 | { 3066 | Info.ModuleTypeName = S7.GetCharsAt(SZL.Data, 172, 32); 3067 | Info.SerialNumber = S7.GetCharsAt(SZL.Data, 138, 24); 3068 | Info.ASName = S7.GetCharsAt(SZL.Data, 2, 24); 3069 | Info.Copyright = S7.GetCharsAt(SZL.Data, 104, 26); 3070 | Info.ModuleName = S7.GetCharsAt(SZL.Data, 36, 24); 3071 | } 3072 | if (_LastError == 0) 3073 | Time_ms = Environment.TickCount - Elapsed; 3074 | return _LastError; 3075 | } 3076 | 3077 | public int GetCpInfo(ref S7CpInfo Info) 3078 | { 3079 | S7SZL SZL = new S7SZL(); 3080 | int Size = 1024; 3081 | SZL.Data = new byte[Size]; 3082 | int Elapsed = Environment.TickCount; 3083 | _LastError = ReadSZL(0x0131, 0x001, ref SZL, ref Size); 3084 | if (_LastError == 0) 3085 | { 3086 | Info.MaxPduLength = S7.GetIntAt(PDU, 2); 3087 | Info.MaxConnections = S7.GetIntAt(PDU, 4); 3088 | Info.MaxMpiRate = S7.GetDIntAt(PDU, 6); 3089 | Info.MaxBusRate = S7.GetDIntAt(PDU, 10); 3090 | } 3091 | if (_LastError == 0) 3092 | Time_ms = Environment.TickCount - Elapsed; 3093 | return _LastError; 3094 | } 3095 | 3096 | public int ReadSZL(int ID, int Index, ref S7SZL SZL, ref int Size) 3097 | { 3098 | int Length; 3099 | int DataSZL; 3100 | int Offset = 0; 3101 | bool Done = false; 3102 | bool First = true; 3103 | byte Seq_in = 0x00; 3104 | ushort Seq_out = 0x0000; 3105 | 3106 | _LastError = 0; 3107 | Time_ms = 0; 3108 | int Elapsed = Environment.TickCount; 3109 | SZL.Header.LENTHDR = 0; 3110 | 3111 | do 3112 | { 3113 | if (First) 3114 | { 3115 | S7.SetWordAt(S7_SZL_FIRST, 11, ++Seq_out); 3116 | S7.SetWordAt(S7_SZL_FIRST, 29, (ushort)ID); 3117 | S7.SetWordAt(S7_SZL_FIRST, 31, (ushort)Index); 3118 | SendPacket(S7_SZL_FIRST); 3119 | } 3120 | else 3121 | { 3122 | S7.SetWordAt(S7_SZL_NEXT, 11, ++Seq_out); 3123 | PDU[24] = (byte)Seq_in; 3124 | SendPacket(S7_SZL_NEXT); 3125 | } 3126 | if (_LastError != 0) 3127 | return _LastError; 3128 | 3129 | Length = RecvIsoPacket(); 3130 | if (_LastError == 0) 3131 | { 3132 | if (First) 3133 | { 3134 | if (Length > 32) // the minimum expected 3135 | { 3136 | if ((S7.GetWordAt(PDU, 27) == 0) && (PDU[29] == (byte)0xFF)) 3137 | { 3138 | // Gets Amount of this slice 3139 | DataSZL = S7.GetWordAt(PDU, 31) - 8; // Skips extra params (ID, Index ...) 3140 | Done = PDU[26] == 0x00; 3141 | Seq_in = (byte)PDU[24]; // Slice sequence 3142 | SZL.Header.LENTHDR = S7.GetWordAt(PDU, 37); 3143 | SZL.Header.N_DR = S7.GetWordAt(PDU, 39); 3144 | Array.Copy(PDU, 41, SZL.Data, Offset, DataSZL); 3145 | // SZL.Copy(PDU, 41, Offset, DataSZL); 3146 | Offset += DataSZL; 3147 | SZL.Header.LENTHDR += SZL.Header.LENTHDR; 3148 | } 3149 | else 3150 | _LastError = S7Consts.errCliInvalidPlcAnswer; 3151 | } 3152 | else 3153 | _LastError = S7Consts.errIsoInvalidPDU; 3154 | } 3155 | else 3156 | { 3157 | if (Length > 32) // the minimum expected 3158 | { 3159 | if ((S7.GetWordAt(PDU, 27) == 0) && (PDU[29] == (byte)0xFF)) 3160 | { 3161 | // Gets Amount of this slice 3162 | DataSZL = S7.GetWordAt(PDU, 31); 3163 | Done = PDU[26] == 0x00; 3164 | Seq_in = (byte)PDU[24]; // Slice sequence 3165 | Array.Copy(PDU, 37, SZL.Data, Offset, DataSZL); 3166 | Offset += DataSZL; 3167 | SZL.Header.LENTHDR += SZL.Header.LENTHDR; 3168 | } 3169 | else 3170 | _LastError = S7Consts.errCliInvalidPlcAnswer; 3171 | } 3172 | else 3173 | _LastError = S7Consts.errIsoInvalidPDU; 3174 | } 3175 | } 3176 | First = false; 3177 | } 3178 | while (!Done && (_LastError == 0)); 3179 | if (_LastError==0) 3180 | { 3181 | Size = SZL.Header.LENTHDR; 3182 | Time_ms = Environment.TickCount - Elapsed; 3183 | } 3184 | return _LastError; 3185 | } 3186 | 3187 | public int ReadSZLList(ref S7SZLList List, ref Int32 ItemsCount) 3188 | { 3189 | return S7Consts.errCliFunctionNotImplemented; 3190 | } 3191 | 3192 | #endregion 3193 | 3194 | #region [Control functions] 3195 | 3196 | public int PlcHotStart() 3197 | { 3198 | _LastError = 0; 3199 | int Elapsed = Environment.TickCount; 3200 | 3201 | SendPacket(S7_HOT_START); 3202 | if (_LastError == 0) 3203 | { 3204 | int Length = RecvIsoPacket(); 3205 | if (Length > 18) // 18 is the minimum expected 3206 | { 3207 | if (PDU[19] != pduStart) 3208 | _LastError = S7Consts.errCliCannotStartPLC; 3209 | else 3210 | { 3211 | if (PDU[20] == pduAlreadyStarted) 3212 | _LastError = S7Consts.errCliAlreadyRun; 3213 | else 3214 | _LastError = S7Consts.errCliCannotStartPLC; 3215 | } 3216 | } 3217 | else 3218 | _LastError = S7Consts.errIsoInvalidPDU; 3219 | } 3220 | if (_LastError == 0) 3221 | Time_ms = Environment.TickCount - Elapsed; 3222 | return _LastError; 3223 | } 3224 | 3225 | public int PlcColdStart() 3226 | { 3227 | _LastError = 0; 3228 | int Elapsed = Environment.TickCount; 3229 | 3230 | SendPacket(S7_COLD_START); 3231 | if (_LastError == 0) 3232 | { 3233 | int Length = RecvIsoPacket(); 3234 | if (Length > 18) // 18 is the minimum expected 3235 | { 3236 | if (PDU[19] != pduStart) 3237 | _LastError = S7Consts.errCliCannotStartPLC; 3238 | else 3239 | { 3240 | if (PDU[20] == pduAlreadyStarted) 3241 | _LastError = S7Consts.errCliAlreadyRun; 3242 | else 3243 | _LastError = S7Consts.errCliCannotStartPLC; 3244 | } 3245 | } 3246 | else 3247 | _LastError = S7Consts.errIsoInvalidPDU; 3248 | } 3249 | if (_LastError == 0) 3250 | Time_ms = Environment.TickCount - Elapsed; 3251 | return _LastError; 3252 | } 3253 | 3254 | public int PlcStop() 3255 | { 3256 | _LastError = 0; 3257 | int Elapsed = Environment.TickCount; 3258 | 3259 | SendPacket(S7_STOP); 3260 | if (_LastError == 0) 3261 | { 3262 | int Length = RecvIsoPacket(); 3263 | if (Length > 18) // 18 is the minimum expected 3264 | { 3265 | if (PDU[19]!=pduStop) 3266 | _LastError = S7Consts.errCliCannotStopPLC; 3267 | else 3268 | { 3269 | if (PDU[20]== pduAlreadyStopped) 3270 | _LastError = S7Consts.errCliAlreadyStop; 3271 | else 3272 | _LastError = S7Consts.errCliCannotStopPLC; 3273 | } 3274 | } 3275 | else 3276 | _LastError = S7Consts.errIsoInvalidPDU; 3277 | } 3278 | if (_LastError == 0) 3279 | Time_ms = Environment.TickCount - Elapsed; 3280 | return _LastError; 3281 | } 3282 | 3283 | public int PlcCopyRamToRom(UInt32 Timeout) 3284 | { 3285 | return S7Consts.errCliFunctionNotImplemented; 3286 | } 3287 | 3288 | public int PlcCompress(UInt32 Timeout) 3289 | { 3290 | return S7Consts.errCliFunctionNotImplemented; 3291 | } 3292 | 3293 | public int PlcGetStatus(ref Int32 Status) 3294 | { 3295 | _LastError = 0; 3296 | int Elapsed = Environment.TickCount; 3297 | 3298 | SendPacket(S7_GET_STAT); 3299 | if (_LastError == 0) 3300 | { 3301 | int Length = RecvIsoPacket(); 3302 | if (Length > 30) // the minimum expected 3303 | { 3304 | ushort Result = S7.GetWordAt(PDU, 27); 3305 | if (Result == 0) 3306 | { 3307 | switch (PDU[44]) 3308 | { 3309 | case S7Consts.S7CpuStatusUnknown: 3310 | case S7Consts.S7CpuStatusRun: 3311 | case S7Consts.S7CpuStatusStop: 3312 | { 3313 | Status = PDU[44]; 3314 | break; 3315 | } 3316 | default: 3317 | { 3318 | // Since RUN status is always 0x08 for all CPUs and CPs, STOP status 3319 | // sometime can be coded as 0x03 (especially for old cpu...) 3320 | Status = S7Consts.S7CpuStatusStop; 3321 | break; 3322 | } 3323 | } 3324 | } 3325 | else 3326 | _LastError = CpuError(Result); 3327 | } 3328 | else 3329 | _LastError = S7Consts.errIsoInvalidPDU; 3330 | } 3331 | if (_LastError == 0) 3332 | Time_ms = Environment.TickCount - Elapsed; 3333 | return _LastError; 3334 | } 3335 | 3336 | #endregion 3337 | 3338 | #region [Security functions] 3339 | public int SetSessionPassword(string Password) 3340 | { 3341 | byte[] pwd = { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 }; 3342 | int Length; 3343 | _LastError = 0; 3344 | int Elapsed = Environment.TickCount; 3345 | // Encodes the Password 3346 | S7.SetCharsAt(pwd, 0, Password); 3347 | pwd[0] = (byte)(pwd[0] ^ 0x55); 3348 | pwd[1] = (byte)(pwd[1] ^ 0x55); 3349 | for (int c = 2; c < 8; c++) 3350 | { 3351 | pwd[c] = (byte)(pwd[c] ^ 0x55 ^ pwd[c - 2]); 3352 | } 3353 | Array.Copy(pwd, 0, S7_SET_PWD, 29, 8); 3354 | // Sends the telegrem 3355 | SendPacket(S7_SET_PWD); 3356 | if (_LastError == 0) 3357 | { 3358 | Length = RecvIsoPacket(); 3359 | if (Length > 32) // the minimum expected 3360 | { 3361 | ushort Result = S7.GetWordAt(PDU, 27); 3362 | if (Result != 0) 3363 | _LastError = CpuError(Result); 3364 | } 3365 | else 3366 | _LastError = S7Consts.errIsoInvalidPDU; 3367 | } 3368 | if (_LastError == 0) 3369 | Time_ms = Environment.TickCount - Elapsed; 3370 | return _LastError; 3371 | } 3372 | 3373 | public int ClearSessionPassword() 3374 | { 3375 | int Length; 3376 | _LastError = 0; 3377 | int Elapsed = Environment.TickCount; 3378 | SendPacket(S7_CLR_PWD); 3379 | if (_LastError == 0) 3380 | { 3381 | Length = RecvIsoPacket(); 3382 | if (Length > 30) // the minimum expected 3383 | { 3384 | ushort Result = S7.GetWordAt(PDU, 27); 3385 | if (Result != 0) 3386 | _LastError = CpuError(Result); 3387 | } 3388 | else 3389 | _LastError = S7Consts.errIsoInvalidPDU; 3390 | } 3391 | return _LastError; 3392 | } 3393 | 3394 | public int GetProtection(ref S7Protection Protection) 3395 | { 3396 | S7Client.S7SZL SZL = new S7Client.S7SZL(); 3397 | int Size = 256; 3398 | SZL.Data = new byte[Size]; 3399 | _LastError = ReadSZL(0x0232, 0x0004, ref SZL, ref Size); 3400 | if (_LastError == 0) 3401 | { 3402 | Protection.sch_schal = S7.GetWordAt(SZL.Data, 2); 3403 | Protection.sch_par = S7.GetWordAt(SZL.Data, 4); 3404 | Protection.sch_rel = S7.GetWordAt(SZL.Data, 6); 3405 | Protection.bart_sch = S7.GetWordAt(SZL.Data, 8); 3406 | Protection.anl_sch = S7.GetWordAt(SZL.Data, 10); 3407 | } 3408 | return _LastError; 3409 | } 3410 | #endregion 3411 | 3412 | #region [Low Level] 3413 | 3414 | public int IsoExchangeBuffer(byte[] Buffer, ref Int32 Size) 3415 | { 3416 | _LastError = 0; 3417 | Time_ms = 0; 3418 | int Elapsed = Environment.TickCount; 3419 | Array.Copy(TPKT_ISO, 0, PDU, 0, TPKT_ISO.Length); 3420 | S7.SetWordAt(PDU, 2, (ushort)(Size + TPKT_ISO.Length)); 3421 | try 3422 | { 3423 | Array.Copy(Buffer, 0, PDU, TPKT_ISO.Length, Size); 3424 | } 3425 | catch 3426 | { 3427 | return S7Consts.errIsoInvalidPDU; 3428 | } 3429 | SendPacket(PDU, TPKT_ISO.Length + Size); 3430 | if (_LastError==0) 3431 | { 3432 | int Length=RecvIsoPacket(); 3433 | if (_LastError==0) 3434 | { 3435 | Array.Copy(PDU, TPKT_ISO.Length, Buffer, 0, Length - TPKT_ISO.Length); 3436 | Size = Length - TPKT_ISO.Length; 3437 | } 3438 | } 3439 | if (_LastError == 0) 3440 | Time_ms = Environment.TickCount - Elapsed; 3441 | else 3442 | Size = 0; 3443 | return _LastError; 3444 | } 3445 | 3446 | #endregion 3447 | 3448 | #region [Async functions (not implemented)] 3449 | 3450 | public int AsReadArea(int Area, int DBNumber, int Start, int Amount, int WordLen, byte[] Buffer) 3451 | { 3452 | return S7Consts.errCliFunctionNotImplemented; 3453 | } 3454 | 3455 | public int AsWriteArea(int Area, int DBNumber, int Start, int Amount, int WordLen, byte[] Buffer) 3456 | { 3457 | return S7Consts.errCliFunctionNotImplemented; 3458 | } 3459 | 3460 | public int AsDBRead(int DBNumber, int Start, int Size, byte[] Buffer) 3461 | { 3462 | return S7Consts.errCliFunctionNotImplemented; 3463 | } 3464 | 3465 | public int AsDBWrite(int DBNumber, int Start, int Size, byte[] Buffer) 3466 | { 3467 | return S7Consts.errCliFunctionNotImplemented; 3468 | } 3469 | 3470 | public int AsMBRead(int Start, int Size, byte[] Buffer) 3471 | { 3472 | return S7Consts.errCliFunctionNotImplemented; 3473 | } 3474 | 3475 | public int AsMBWrite(int Start, int Size, byte[] Buffer) 3476 | { 3477 | return S7Consts.errCliFunctionNotImplemented; 3478 | } 3479 | 3480 | public int AsEBRead(int Start, int Size, byte[] Buffer) 3481 | { 3482 | return S7Consts.errCliFunctionNotImplemented; 3483 | } 3484 | 3485 | public int AsEBWrite(int Start, int Size, byte[] Buffer) 3486 | { 3487 | return S7Consts.errCliFunctionNotImplemented; 3488 | } 3489 | 3490 | public int AsABRead(int Start, int Size, byte[] Buffer) 3491 | { 3492 | return S7Consts.errCliFunctionNotImplemented; 3493 | } 3494 | 3495 | public int AsABWrite(int Start, int Size, byte[] Buffer) 3496 | { 3497 | return S7Consts.errCliFunctionNotImplemented; 3498 | } 3499 | 3500 | public int AsTMRead(int Start, int Amount, ushort[] Buffer) 3501 | { 3502 | return S7Consts.errCliFunctionNotImplemented; 3503 | } 3504 | 3505 | public int AsTMWrite(int Start, int Amount, ushort[] Buffer) 3506 | { 3507 | return S7Consts.errCliFunctionNotImplemented; 3508 | } 3509 | 3510 | public int AsCTRead(int Start, int Amount, ushort[] Buffer) 3511 | { 3512 | return S7Consts.errCliFunctionNotImplemented; 3513 | } 3514 | 3515 | public int AsCTWrite(int Start, int Amount, ushort[] Buffer) 3516 | { 3517 | return S7Consts.errCliFunctionNotImplemented; 3518 | } 3519 | 3520 | public int AsListBlocksOfType(int BlockType, ushort[] List) 3521 | { 3522 | return S7Consts.errCliFunctionNotImplemented; 3523 | } 3524 | 3525 | public int AsReadSZL(int ID, int Index, ref S7SZL Data, ref Int32 Size) 3526 | { 3527 | return S7Consts.errCliFunctionNotImplemented; 3528 | } 3529 | 3530 | public int AsReadSZLList(ref S7SZLList List, ref Int32 ItemsCount) 3531 | { 3532 | return S7Consts.errCliFunctionNotImplemented; 3533 | } 3534 | 3535 | public int AsUpload(int BlockType, int BlockNum, byte[] UsrData, ref int Size) 3536 | { 3537 | return S7Consts.errCliFunctionNotImplemented; 3538 | } 3539 | 3540 | public int AsFullUpload(int BlockType, int BlockNum, byte[] UsrData, ref int Size) 3541 | { 3542 | return S7Consts.errCliFunctionNotImplemented; 3543 | } 3544 | 3545 | public int ASDownload(int BlockNum, byte[] UsrData, int Size) 3546 | { 3547 | return S7Consts.errCliFunctionNotImplemented; 3548 | } 3549 | 3550 | public int AsPlcCopyRamToRom(UInt32 Timeout) 3551 | { 3552 | return S7Consts.errCliFunctionNotImplemented; 3553 | } 3554 | 3555 | public int AsPlcCompress(UInt32 Timeout) 3556 | { 3557 | return S7Consts.errCliFunctionNotImplemented; 3558 | } 3559 | 3560 | public int AsDBGet(int DBNumber, byte[] UsrData, ref int Size) 3561 | { 3562 | return S7Consts.errCliFunctionNotImplemented; 3563 | } 3564 | 3565 | public int AsDBFill(int DBNumber, int FillChar) 3566 | { 3567 | return S7Consts.errCliFunctionNotImplemented; 3568 | } 3569 | 3570 | public bool CheckAsCompletion(ref int opResult) 3571 | { 3572 | opResult = 0; 3573 | return false; 3574 | } 3575 | 3576 | public int WaitAsCompletion(int Timeout) 3577 | { 3578 | return S7Consts.errCliFunctionNotImplemented; 3579 | } 3580 | 3581 | #endregion 3582 | 3583 | #region [Info Functions / Properties] 3584 | 3585 | public string ErrorText(int Error) 3586 | { 3587 | switch (Error) 3588 | { 3589 | case 0: return "OK"; 3590 | case S7Consts.errTCPSocketCreation: return "SYS : Error creating the Socket"; 3591 | case S7Consts.errTCPConnectionTimeout: return "TCP : Connection Timeout"; 3592 | case S7Consts.errTCPConnectionFailed: return "TCP : Connection Error"; 3593 | case S7Consts.errTCPReceiveTimeout: return "TCP : Data receive Timeout"; 3594 | case S7Consts.errTCPDataReceive: return "TCP : Error receiving Data"; 3595 | case S7Consts.errTCPSendTimeout: return "TCP : Data send Timeout"; 3596 | case S7Consts.errTCPDataSend: return "TCP : Error sending Data"; 3597 | case S7Consts.errTCPConnectionReset: return "TCP : Connection reset by the Peer"; 3598 | case S7Consts.errTCPNotConnected: return "CLI : Client not connected"; 3599 | case S7Consts.errTCPUnreachableHost: return "TCP : Unreachable host"; 3600 | case S7Consts.errIsoConnect: return "ISO : Connection Error"; 3601 | case S7Consts.errIsoInvalidPDU: return "ISO : Invalid PDU received"; 3602 | case S7Consts.errIsoInvalidDataSize: return "ISO : Invalid Buffer passed to Send/Receive"; 3603 | case S7Consts.errCliNegotiatingPDU: return "CLI : Error in PDU negotiation"; 3604 | case S7Consts.errCliInvalidParams: return "CLI : invalid param(s) supplied"; 3605 | case S7Consts.errCliJobPending: return "CLI : Job pending"; 3606 | case S7Consts.errCliTooManyItems: return "CLI : too may items (>20) in multi read/write"; 3607 | case S7Consts.errCliInvalidWordLen: return "CLI : invalid WordLength"; 3608 | case S7Consts.errCliPartialDataWritten: return "CLI : Partial data written"; 3609 | case S7Consts.errCliSizeOverPDU: return "CPU : total data exceeds the PDU size"; 3610 | case S7Consts.errCliInvalidPlcAnswer: return "CLI : invalid CPU answer"; 3611 | case S7Consts.errCliAddressOutOfRange: return "CPU : Address out of range"; 3612 | case S7Consts.errCliInvalidTransportSize: return "CPU : Invalid Transport size"; 3613 | case S7Consts.errCliWriteDataSizeMismatch: return "CPU : Data size mismatch"; 3614 | case S7Consts.errCliItemNotAvailable: return "CPU : Item not available"; 3615 | case S7Consts.errCliInvalidValue: return "CPU : Invalid value supplied"; 3616 | case S7Consts.errCliCannotStartPLC: return "CPU : Cannot start PLC"; 3617 | case S7Consts.errCliAlreadyRun: return "CPU : PLC already RUN"; 3618 | case S7Consts.errCliCannotStopPLC: return "CPU : Cannot stop PLC"; 3619 | case S7Consts.errCliCannotCopyRamToRom: return "CPU : Cannot copy RAM to ROM"; 3620 | case S7Consts.errCliCannotCompress: return "CPU : Cannot compress"; 3621 | case S7Consts.errCliAlreadyStop: return "CPU : PLC already STOP"; 3622 | case S7Consts.errCliFunNotAvailable: return "CPU : Function not available"; 3623 | case S7Consts.errCliUploadSequenceFailed: return "CPU : Upload sequence failed"; 3624 | case S7Consts.errCliInvalidDataSizeRecvd: return "CLI : Invalid data size received"; 3625 | case S7Consts.errCliInvalidBlockType: return "CLI : Invalid block type"; 3626 | case S7Consts.errCliInvalidBlockNumber: return "CLI : Invalid block number"; 3627 | case S7Consts.errCliInvalidBlockSize: return "CLI : Invalid block size"; 3628 | case S7Consts.errCliNeedPassword: return "CPU : Function not authorized for current protection level"; 3629 | case S7Consts.errCliInvalidPassword: return "CPU : Invalid password"; 3630 | case S7Consts.errCliNoPasswordToSetOrClear: return "CPU : No password to set or clear"; 3631 | case S7Consts.errCliJobTimeout: return "CLI : Job Timeout"; 3632 | case S7Consts.errCliFunctionRefused: return "CLI : function refused by CPU (Unknown error)"; 3633 | case S7Consts.errCliPartialDataRead: return "CLI : Partial data read"; 3634 | case S7Consts.errCliBufferTooSmall: return "CLI : The buffer supplied is too small to accomplish the operation"; 3635 | case S7Consts.errCliDestroying: return "CLI : Cannot perform (destroying)"; 3636 | case S7Consts.errCliInvalidParamNumber: return "CLI : Invalid Param Number"; 3637 | case S7Consts.errCliCannotChangeParam: return "CLI : Cannot change this param now"; 3638 | case S7Consts.errCliFunctionNotImplemented: return "CLI : Function not implemented"; 3639 | default: return "CLI : Unknown error (0x" + Convert.ToString(Error, 16) + ")"; 3640 | }; 3641 | } 3642 | 3643 | public int LastError() 3644 | { 3645 | return _LastError; 3646 | } 3647 | 3648 | public int RequestedPduLength() 3649 | { 3650 | return _PduSizeRequested; 3651 | } 3652 | 3653 | public int NegotiatedPduLength() 3654 | { 3655 | return _PDULength; 3656 | } 3657 | 3658 | public int ExecTime() 3659 | { 3660 | return Time_ms; 3661 | } 3662 | 3663 | public int ExecutionTime 3664 | { 3665 | get 3666 | { 3667 | return Time_ms; 3668 | } 3669 | } 3670 | 3671 | public int PduSizeNegotiated 3672 | { 3673 | get 3674 | { 3675 | return _PDULength; 3676 | } 3677 | } 3678 | 3679 | public int PduSizeRequested 3680 | { 3681 | get 3682 | { 3683 | return _PduSizeRequested; 3684 | } 3685 | set 3686 | { 3687 | if (value < MinPduSizeToRequest) 3688 | value = MinPduSizeToRequest; 3689 | if (value > MaxPduSizeToRequest) 3690 | value = MaxPduSizeToRequest; 3691 | _PduSizeRequested = value; 3692 | } 3693 | } 3694 | 3695 | public int PLCPort 3696 | { 3697 | get 3698 | { 3699 | return _PLCPort; 3700 | } 3701 | set 3702 | { 3703 | _PLCPort = value; 3704 | } 3705 | } 3706 | 3707 | public int ConnTimeout 3708 | { 3709 | get 3710 | { 3711 | return _ConnTimeout; 3712 | } 3713 | set 3714 | { 3715 | _ConnTimeout = value; 3716 | } 3717 | } 3718 | 3719 | public int RecvTimeout 3720 | { 3721 | get 3722 | { 3723 | return _RecvTimeout; 3724 | } 3725 | set 3726 | { 3727 | _RecvTimeout = value; 3728 | } 3729 | } 3730 | 3731 | public int SendTimeout 3732 | { 3733 | get 3734 | { 3735 | return _SendTimeout; 3736 | } 3737 | set 3738 | { 3739 | _SendTimeout = value; 3740 | } 3741 | } 3742 | 3743 | public bool Connected 3744 | { 3745 | get 3746 | { 3747 | return (Socket != null) && (Socket.Connected); 3748 | } 3749 | } 3750 | 3751 | 3752 | #endregion 3753 | } 3754 | } 3755 | -------------------------------------------------------------------------------- /Sharp7Library/Sharp7Library.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {D1A175B9-9CCA-4718-998C-4C882D37D5DA} 8 | Library 9 | Properties 10 | Sharp7Library 11 | Sharp7Library 12 | v4.5.2 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 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 54 | --------------------------------------------------------------------------------