├── .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 |
--------------------------------------------------------------------------------