102 Commits

Author SHA1 Message Date
Michael Croes
fd17bfa03b Merge branch 'release/0.3' 2018-07-11 22:51:36 +02:00
Michael Croes
8dad14955e Merge pull request #166 from mycroes/fix-string-to-byte-array
Fix string to byte array
2018-07-11 22:46:44 +02:00
Michael Croes
96efb9d56a Add tests for String.ToByteArray(...) 2018-07-11 22:07:34 +02:00
Michael Croes
09d323925a Add length to String.ToByteArray(...) 2018-07-11 22:07:08 +02:00
Michael Croes
bf4550655e Add support for creating DataItem from string (#149)
* Refactor PLCAddress

- Change fields into properties
- Apply PascalCase naming to all properties
- Make Parse public static with out parameters

* Support creating DataItem from string

* Rename PlcAddress.Address to StartByte
2018-07-11 10:21:49 +02:00
Raphael Schlameuß
214a7a73c8 Read many bytes 1500 (develop branch) (#160)
* ReadBytesWithSingleRequest cannot read >491 Bytes on S7-1500
2018-07-11 10:15:08 +02:00
Michael Croes
a5d3c70373 Add SourceLink.Copy.PdbFiles to add missing .pdb files (#163) 2018-07-11 09:53:33 +02:00
Michael Croes
2204ab360c Fix write stringex (#162)
* Add StringEx.ToByteArray(...)

* Add Serialization.SerializeDataItem(DataItem)

Supports StringEx VarType or offloads to SerializeValue method.

* Use SerializeDataItem in S7WriteMultiple

* Assume string length without header in StringEx.ToByteArray

VarTypeToByteLength already assumed that StringEx declared count for
the number of characters without the header, this now matches that
behavior.

* Add unit tests for StringEx conversions

* Fix incorrect value passed to Encoding.GetBytes

The length must actually be within string limits.
2018-07-11 09:47:43 +02:00
Raphael Schlameuß
a1b69a5c5a Merge pull request #161 from mycroes/throw-on-error
Replace LastErrorCode and LastErrorString with exceptions
2018-07-11 09:26:27 +02:00
Michael Croes
1538de148b Replace LastErrorCode and LastErrorString with exceptions 2018-07-09 20:07:47 +02:00
Michael Croes
ff7e13cd49 Merge pull request #158 from mycroes/feature/ci
Add AppVeyor and GitVersion configuration
2018-07-09 19:57:06 +02:00
Michael Croes
c651380647 Add AppVeyor and GitVersion configuration 2018-07-08 21:10:05 +02:00
Michael Croes
0298371bfc Remove accidental .nuget leftover 2018-07-07 10:19:44 +02:00
Michael Croes
ff1e5fdc26 Merge pull request #155 from mycroes/release-preparations
Release preparations
2018-07-06 22:32:10 +02:00
Michael Croes
bd50709ad7 Remove .nuget/
Not sure why it was there in the first place.
2018-07-06 22:24:07 +02:00
Michael Croes
cafca8e28e Remove nuget-pack files
Obsoleted by SDK style project format.
2018-07-06 22:24:07 +02:00
Michael Croes
ab11cc8e3e Add SourceLink support 2018-07-06 22:24:07 +02:00
Michael Croes
9740224966 Add NuGet tags to S7.Net.csproj 2018-07-06 22:24:07 +02:00
Michael Croes
db50a62aad Merge pull request #152 from rapha-dev/invalid-tpkt-length
Fixes invalid TPKT length in request header
2018-07-04 23:15:58 +02:00
Raphael
2f3bbddaef Fixes invalid TPKT length in request header 2018-07-04 11:47:13 +02:00
Michael Croes
4ab73d0e3a Merge pull request #143 from rapha-dev/datatype-float
Add support for datatype float (Single) and obsolete double (Double) …
2018-07-03 21:54:22 +02:00
Raphael
932433ad69 Merge branch 'master' of https://github.com/rapha-dev/s7netplus into datatype-float 2018-07-03 08:44:51 +02:00
Raphael
d37c388d20 Fixed obsolete message 2018-07-03 08:41:54 +02:00
Michael Croes
c13fb970d0 Merge pull request #142 from mycroes/net-standard-1-3
Support for NetStandard 1.3
2018-07-02 23:00:19 +02:00
Michael Croes
28a1225265 Change S7.Net project type in S7.sln 2018-07-02 22:59:16 +02:00
Michael Croes
fc62bb79d4 Update supported frameworks and compile section 2018-07-02 20:27:29 +02:00
Raphael
b8b144d7ae Add support for datatype float (Single) and obsolete double (Double) usage 2018-07-02 10:20:02 +02:00
Michael Croes
98228924ea Simplify info declaration in Struct field loops 2018-06-30 22:19:44 +02:00
Michael Croes
a1f4e44c48 R#: Cleanup usings 2018-06-30 22:19:44 +02:00
Michael Croes
2df9d0b0bf Remove obsolete UWP / S7.Net.Core files 2018-06-30 22:19:44 +02:00
Michael Croes
7821b6b6f6 Add NetStandard 1.3 support
Supersedes UWP support since UWP 10.0 supports up to NetStandard 1.4.
2018-06-30 22:19:44 +02:00
Michael Croes
e516675a70 Replace InvalidEnumArgumentException usage
InvalidEnumArgumentException is not supported in NetStandard 1.3.
2018-06-30 22:19:44 +02:00
Michael Croes
710ab2e026 Provide a buffer to MemoryStream
NetStandard 1.3 doesn't expose .GetBuffer(), this removes the need for
having it. The support for messages that span TPKT's is quite nasty, but
probably S7 PLC's will never even use a message spanning multiple TPKT's.
2018-06-30 22:17:48 +02:00
Michael Croes
534ecb2546 Merge pull request #123 from deinok/master
Port to NetStandard
2018-06-28 22:07:09 +02:00
Raul Hidalgo Caballero
66fe6750b2 Merge branch 'master' into master 2018-06-28 10:40:11 +02:00
Michael Croes
85e1abfdb0 Merge pull request #138 from mycroes/connection-request
Connection request test and cleanup
2018-06-27 22:28:47 +02:00
Michael Croes
3409e52fef Add message for ReadWriteBytesMany assertion 2018-06-27 21:51:37 +02:00
Michael Croes
514dde365e Cleanup TSAP parameters
- Remove duplicate parameter code / length (already in initial array)
- Remove separate branch for S7-400 (same as S7-300/1200)
- Change all values to 2-character hex
2018-06-27 21:26:44 +02:00
Michael Croes
0f151e4947 Add tests for ConnectionRequest 2018-06-27 21:24:23 +02:00
Michael Croes
5f220cd31f Fix default namespace in S7.Net.UnitTest 2018-06-27 21:09:11 +02:00
Michael Croes
6569e5e169 Extract ConnectionRequest from PLCHelpers 2018-06-27 21:07:41 +02:00
Michael Croes
4bca9d8c19 Merge pull request #130 from mycroes/fix-communication-setup-pdu-size
Fix the PDU size in communication setup
2018-06-26 22:18:44 +02:00
Michael Croes
229558d586 Merge pull request #129 from mycroes/remove-obsolete-on-sync
Remove obsolete attribute and comments
2018-06-26 22:18:10 +02:00
Raul Hidalgo Caballero
daf0f8e0d6 Merge branch 'master' into master 2018-06-21 21:24:01 +02:00
Michael Croes
d9abebe550 Fix the PDU size in communication setup
[7, 80] resulted in 1872; [7, 128] (or [0x07, 0x80] in hex) results in
the 1920 as specified in comments.
2018-06-21 20:49:05 +02:00
Michael Croes
2083ab1501 Remove obsolete attribute and comments
References #124.
2018-06-21 20:28:22 +02:00
Michele Cattafesta
003d775228 Merge pull request #127 from mycroes/write-short-array-fix
Write short array fix
2018-06-21 13:32:44 +01:00
Michele Cattafesta
e1c62c899b Merge pull request #121 from mycroes/master
Add multiple write support
2018-06-21 13:31:39 +01:00
Michael Croes
5a82313eb1 Change order of byte assignment for uniformity
Order is now same as ToByteArray(Int16[] value), where the order is
important due to the index increment in the same line.
2018-06-19 21:26:03 +02:00
Michael Croes
8d64bd89fc Fix byte order when serializing short[] / Int16[]
Values should be written as big-endian, unfortunately the ordering for
short values in an array was little-endian.
2018-06-19 21:24:28 +02:00
Michael Croes
299918e293 Add Plc.Write/WriteAsync(params DataItem[] dataItems) 2018-06-15 21:50:41 +02:00
Michael Croes
740a47ab43 Add initial WriteMultiple protocol 2018-06-15 21:39:38 +02:00
Michael Croes
38d089e117 Add boolean support to GetPackage 2018-06-15 21:39:38 +02:00
Michael Croes
aaab24a4c2 Add and move serialization helpers
Added (Get|Set)WordAt, SetAddressAt and moved GetPackage method.
2018-06-15 21:39:38 +02:00
Michele Cattafesta
0721b1a84a Merge pull request #125 from rapha-dev/master
Fixes for Exception naming ReadData
2018-06-10 17:13:15 +01:00
Raphael
1b22badea1 Fixed ErrorCode.ReadData for specific methods 2018-06-08 10:26:52 +02:00
Raphael
ddfedaa17e Typo in Exception string 2018-06-08 10:22:42 +02:00
Raul Hidalgo Caballero
2d24adc874 Port to NetStandard 2018-06-05 17:33:42 +02:00
Michele Cattafesta
44bf1366e4 Merge pull request #111 from thoj/tcpclient
RFC/WIP: Async API
2018-05-21 20:23:07 +01:00
Thomas Jäger
64e485c54a Revert "Use socket instead of TcpClient and stream."
Revert back to using Stream/TcpClient. High performance stuff is moved
to highperformancesockets branch. I think this is interesting, but i also
feel that this is premature optimization. I doubth that this will be a
performance bottleneck ofr the forseeable future.

This reverts commit 1b34716a30.
2018-05-18 08:52:19 +02:00
Thomas Jäger
219c1cc71a Revert "Use high performance wrapper"
This reverts commit a3277133af.
2018-05-18 08:43:44 +02:00
Thomas Jäger
a3277133af Use high performance wrapper 2018-05-16 16:29:11 +02:00
Thomas Jäger
00e22ee214 Incorporate fixes from #117 (moved to helpers)
fix array naming.
2018-05-16 15:50:00 +02:00
Thomas Jäger
8ac96162f9 Merge remote-tracking branch 'upstream/master' into tcpclient 2018-05-16 11:33:46 +02:00
Thomas Jäger
1b34716a30 Use socket instead of TcpClient and stream.
Async implemented with wrapper.
2018-05-16 11:24:21 +02:00
Thomas Jäger
b8b890977e Fix structured comments 2018-05-14 14:19:33 +02:00
Michele Cattafesta
013ff5fd92 Merge pull request #117 from rapha-dev/master
Fixed bug in ReadMultipleVars on VarType.Bit
2018-05-12 23:03:57 +01:00
Raphael
642cf8169e Fixed bug for reading VarType.Bit and VarType.Byte on odd number of bytes in ReadMultipleVars() 2018-05-07 11:47:52 +02:00
Raphael
f6e370b162 Merge remote-tracking branch 'upstream/master' 2018-05-07 11:44:58 +02:00
Raphael
cbaa8921df import killnine master 2018-05-07 09:47:20 +02:00
Michele Cattafesta
b3458a8304 unit tests 2018-05-05 23:24:06 +01:00
Michele Cattafesta
a824344a4c Merge pull request #110 from Buchter/ImproveClassSizeCalculation
Fixed bug regarding size calculation of small S7 Structs
2018-05-05 23:00:15 +01:00
Michele Cattafesta
587e496497 Merge pull request #108 from thoj/master
Take 2: Use TPKT/COTP for reading responses from PLS
2018-05-05 22:09:13 +01:00
Raphael
0d1bc472c8 Added BitAdr to DataItem and fixed bug in ReadMultipleVars on VarType.Bit 2018-04-27 16:00:41 +02:00
Thomas Jäger
e6f1114bc1 Add COTP.ReadTSDU test 2018-04-19 15:39:41 +02:00
Thomas Jäger
84aee0671a Only return used bytes 2018-04-19 15:39:03 +02:00
Thomas Jäger
74af1c0da7 Change project to allow testing internal classes.
Add protocol tests
Simplify async tests
2018-04-19 15:08:52 +02:00
Thomas Jäger
c80b0dd55d Add signing key to S7.Net.UnitTest for testing internal classes 2018-04-19 15:07:06 +02:00
Thomas Jäger
eb0d6a3429 Better TPKT error handling 2018-04-19 15:06:10 +02:00
Thomas Jäger
f53a3bd320 Use TcpClient and implemnt async methods
Note: This keeps the old methods to be backward compatible.
Note: Unforntunatly a lot of whitespace fixes, refactoring and other trivial stuff is
included. It was to hard to split of in a seperate commit.

Note: Async methods does not use exactly the same structure/signature as the
existing methods. "Out" parameters like ReadClass and ReadStruct instead
returns the struct in tuple. Async methods also rely on exceptions
instead of ErrorCodes to communicate exception states to calling client.

* Use TcpClient and use Async methods (ReadAsync/WriteAsync)
* Implemnt async methods for all existing methods
* Implemnt existing methods using tcpclient.
* Split Plc.cs in more files. (Common, Async, Sync, Helpers)
* Mark old methods as Obsolete
* Split tests in two files
* Implement Async tests
2018-04-19 13:13:08 +02:00
Buchter
4b04ed74a1 Fixed bug regarding size calculation of small S7 Structs
There was an Error when you had Structs conaining less than 8 Bits. The size calculation in this case returned 0 and the Plc.ReadClass() method throwed an excpetion. Structs in Step7 within da DataBlock always starts with adresses that can by devided by two. The extended code ensures the correct size even if there are a couple of structs in a DataBlock containing only a few bits.
2018-04-19 12:09:30 +02:00
Thomas Jäger
50b026d7a5 Read TPKT/COTP packets / Read MaxPDU size from PLC
Read responses from the PLS using classes for TPKT and COPT. This
makes the communication more robust. It will now handle empty COTP
packets that SoftPLS and WinAC based PLCs send out. I use RFC names for
functions and classes.

Change logic to use COTP and S7Comm reponse codes instead of
relying on packet sizes.

Read Max PDU size from connection setup. Ref #21
Change logic to use MaxPDUSize when reading istead of hardcoded limit.

I tried using MaxPDUSize when writing data but this failed when packet size is
over 256 on snap7. So i decided to drop changes to write size.
I have done some tests against WinAC cpu and it seems to handle bigger pdu's
when writing if negotiated in the connection setup. This might just be a SNAP7 bug.

Fix MaxPDUSize for readbytes

Remove debug line

Simplify byte copy. Remove unessesarry buffer
2018-04-19 00:34:11 +02:00
Thomas Jäger
f740ba0078 Fix writing double to PLC 2018-04-19 00:34:11 +02:00
Thomas Jäger
1b4faf21d7 Code Lint: Standard dispose Pattern. /// to // comment 2018-04-19 00:34:11 +02:00
Thomas Jäger
3a18d13805 Add test for Read/Write Double and PDUSize Test 2018-04-19 00:34:10 +02:00
Michele Cattafesta
9fd515280a Revert "Merge pull request #107 from thoj/master"
This reverts commit d17fdf8efb, reversing
changes made to bfeacee08f.
2018-04-11 20:22:24 +01:00
Michele Cattafesta
d17fdf8efb Merge pull request #107 from thoj/master
Fix communication with WinAC/SoftPLC CPUs and Fix write double
2018-04-11 19:28:41 +01:00
Thomas Jäger
8a3db22629 Read TPKT/COTP packets / Read MaxPDU size from PLC
Read responses from the PLS using classes for TPKT and COPT. This
makes the communication more robust. It will now handle empty COTP
packets that SoftPLS and WinAC based PLCs send out. I use RFC names for
functions and classes.

Change logic to use COTP and S7Comm reponse codes instead of
relying on packet sizes.

Read Max PDU size from connection setup. Ref #21
Change logic to use MaxPDU from cpu limit instead of hardcoded limit.

Remove var count limit.
2018-04-10 15:26:04 +02:00
Thomas Jäger
6470f8d076 Document bSend1 data packet 2018-04-10 15:25:20 +02:00
Thomas Jäger
723b0ffd42 Fix writing double to PLC 2018-04-10 15:23:46 +02:00
Michele Cattafesta
bfeacee08f Merge pull request #102 from mbalous/master
Non breaking changes. Code styling, comments, etc...
2018-04-08 21:12:22 +01:00
Michele Cattafesta
b6b53078f9 Merge pull request #101 from tomsoftware/master
fix data type converting for num-types
2018-04-08 21:08:27 +01:00
mbalous
a99ea469ce Correct exceptions. Constructor parameter checking. 2018-03-21 22:40:25 +01:00
mbalous
dd71e1bf0b Checking constructor parameters. 2018-03-21 22:32:23 +01:00
mbalous
530072b70f Improved XML function comments. 2018-03-21 22:18:37 +01:00
Thomas Ze
117ad5cd1b fix variable length bug for StringEx 2018-03-20 22:37:44 +01:00
Thomas Ze
7cedec5909 - fix data type converting
- add new string string data type
2018-03-18 21:26:22 +01:00
Michele Cattafesta
f6a2e11045 0.1.8 2018-02-05 20:07:11 +00:00
Michele Cattafesta
8005304827 Merge pull request #97 from GS770/master
Added Bit to Types
2017-12-14 23:00:19 +00:00
shen.jz
ef5e060948 Added Bit to Types
get bool or BitArray from byte array
2017-12-14 10:38:10 +08:00
Michele Cattafesta
3178d2aa09 Merge pull request #96 from GS770/master
Get bit of read bytes
2017-12-13 08:57:52 +00:00
shen.jz
6a2bc708a9 Get bit of read bytes 2017-12-12 15:56:18 +08:00
70 changed files with 4246 additions and 5478 deletions

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<solution>
<add key="disableSourceControlIntegration" value="true" />
</solution>
</configuration>

Binary file not shown.

View File

@@ -1,144 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">$(MSBuildProjectDirectory)\..\</SolutionDir>
<!-- Enable the restore command to run before builds -->
<RestorePackages Condition=" '$(RestorePackages)' == '' ">false</RestorePackages>
<!-- Property that enables building a package from a project -->
<BuildPackage Condition=" '$(BuildPackage)' == '' ">false</BuildPackage>
<!-- Determines if package restore consent is required to restore packages -->
<RequireRestoreConsent Condition=" '$(RequireRestoreConsent)' != 'false' ">true</RequireRestoreConsent>
<!-- Download NuGet.exe if it does not already exist -->
<DownloadNuGetExe Condition=" '$(DownloadNuGetExe)' == '' ">false</DownloadNuGetExe>
</PropertyGroup>
<ItemGroup Condition=" '$(PackageSources)' == '' ">
<!-- Package sources used to restore packages. By default, registered sources under %APPDATA%\NuGet\NuGet.Config will be used -->
<!-- The official NuGet package source (https://www.nuget.org/api/v2/) will be excluded if package sources are specified and it does not appear in the list -->
<!--
<PackageSource Include="https://www.nuget.org/api/v2/" />
<PackageSource Include="https://my-nuget-source/nuget/" />
-->
</ItemGroup>
<PropertyGroup Condition=" '$(OS)' == 'Windows_NT'">
<!-- Windows specific commands -->
<NuGetToolsPath>$([System.IO.Path]::Combine($(SolutionDir), ".nuget"))</NuGetToolsPath>
</PropertyGroup>
<PropertyGroup Condition=" '$(OS)' != 'Windows_NT'">
<!-- We need to launch nuget.exe with the mono command if we're not on windows -->
<NuGetToolsPath>$(SolutionDir).nuget</NuGetToolsPath>
</PropertyGroup>
<PropertyGroup>
<PackagesProjectConfig Condition=" '$(OS)' == 'Windows_NT'">$(MSBuildProjectDirectory)\packages.$(MSBuildProjectName.Replace(' ', '_')).config</PackagesProjectConfig>
<PackagesProjectConfig Condition=" '$(OS)' != 'Windows_NT'">$(MSBuildProjectDirectory)\packages.$(MSBuildProjectName).config</PackagesProjectConfig>
</PropertyGroup>
<PropertyGroup>
<PackagesConfig Condition="Exists('$(MSBuildProjectDirectory)\packages.config')">$(MSBuildProjectDirectory)\packages.config</PackagesConfig>
<PackagesConfig Condition="Exists('$(PackagesProjectConfig)')">$(PackagesProjectConfig)</PackagesConfig>
</PropertyGroup>
<PropertyGroup>
<!-- NuGet command -->
<NuGetExePath Condition=" '$(NuGetExePath)' == '' ">$(NuGetToolsPath)\NuGet.exe</NuGetExePath>
<PackageSources Condition=" $(PackageSources) == '' ">@(PackageSource)</PackageSources>
<NuGetCommand Condition=" '$(OS)' == 'Windows_NT'">"$(NuGetExePath)"</NuGetCommand>
<NuGetCommand Condition=" '$(OS)' != 'Windows_NT' ">mono --runtime=v4.0.30319 "$(NuGetExePath)"</NuGetCommand>
<PackageOutputDir Condition="$(PackageOutputDir) == ''">$(TargetDir.Trim('\\'))</PackageOutputDir>
<RequireConsentSwitch Condition=" $(RequireRestoreConsent) == 'true' ">-RequireConsent</RequireConsentSwitch>
<NonInteractiveSwitch Condition=" '$(VisualStudioVersion)' != '' AND '$(OS)' == 'Windows_NT' ">-NonInteractive</NonInteractiveSwitch>
<PaddedSolutionDir Condition=" '$(OS)' == 'Windows_NT'">"$(SolutionDir) "</PaddedSolutionDir>
<PaddedSolutionDir Condition=" '$(OS)' != 'Windows_NT' ">"$(SolutionDir)"</PaddedSolutionDir>
<!-- Commands -->
<RestoreCommand>$(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir)</RestoreCommand>
<BuildCommand>$(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols</BuildCommand>
<!-- We need to ensure packages are restored prior to assembly resolve -->
<BuildDependsOn Condition="$(RestorePackages) == 'true'">
RestorePackages;
$(BuildDependsOn);
</BuildDependsOn>
<!-- Make the build depend on restore packages -->
<BuildDependsOn Condition="$(BuildPackage) == 'true'">
$(BuildDependsOn);
BuildPackage;
</BuildDependsOn>
</PropertyGroup>
<Target Name="CheckPrerequisites">
<!-- Raise an error if we're unable to locate nuget.exe -->
<Error Condition="'$(DownloadNuGetExe)' != 'true' AND !Exists('$(NuGetExePath)')" Text="Unable to locate '$(NuGetExePath)'" />
<!--
Take advantage of MsBuild's build dependency tracking to make sure that we only ever download nuget.exe once.
This effectively acts as a lock that makes sure that the download operation will only happen once and all
parallel builds will have to wait for it to complete.
-->
<MsBuild Targets="_DownloadNuGet" Projects="$(MSBuildThisFileFullPath)" Properties="Configuration=NOT_IMPORTANT;DownloadNuGetExe=$(DownloadNuGetExe)" />
</Target>
<Target Name="_DownloadNuGet">
<DownloadNuGet OutputFilename="$(NuGetExePath)" Condition=" '$(DownloadNuGetExe)' == 'true' AND !Exists('$(NuGetExePath)')" />
</Target>
<Target Name="RestorePackages" DependsOnTargets="CheckPrerequisites">
<Exec Command="$(RestoreCommand)"
Condition="'$(OS)' != 'Windows_NT' And Exists('$(PackagesConfig)')" />
<Exec Command="$(RestoreCommand)"
LogStandardErrorAsError="true"
Condition="'$(OS)' == 'Windows_NT' And Exists('$(PackagesConfig)')" />
</Target>
<Target Name="BuildPackage" DependsOnTargets="CheckPrerequisites">
<Exec Command="$(BuildCommand)"
Condition=" '$(OS)' != 'Windows_NT' " />
<Exec Command="$(BuildCommand)"
LogStandardErrorAsError="true"
Condition=" '$(OS)' == 'Windows_NT' " />
</Target>
<UsingTask TaskName="DownloadNuGet" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
<ParameterGroup>
<OutputFilename ParameterType="System.String" Required="true" />
</ParameterGroup>
<Task>
<Reference Include="System.Core" />
<Using Namespace="System" />
<Using Namespace="System.IO" />
<Using Namespace="System.Net" />
<Using Namespace="Microsoft.Build.Framework" />
<Using Namespace="Microsoft.Build.Utilities" />
<Code Type="Fragment" Language="cs">
<![CDATA[
try {
OutputFilename = Path.GetFullPath(OutputFilename);
Log.LogMessage("Downloading latest version of NuGet.exe...");
WebClient webClient = new WebClient();
webClient.DownloadFile("https://www.nuget.org/nuget.exe", OutputFilename);
return true;
}
catch (Exception ex) {
Log.LogErrorFromException(ex);
return false;
}
]]>
</Code>
</Task>
</UsingTask>
</Project>

17
GitVersion.yml Normal file
View File

@@ -0,0 +1,17 @@
assembly-informational-format: '{NuGetVersion}'
mode: ContinuousDeployment
branches:
master:
tag: rc
increment: Minor
features?[/-]:
tag: rc-{BranchName}
increment: Minor
(pull|pull\-requests|pr)[/-]:
tag: rc-pr-{BranchName}
increment: Minor
hotfix(es)?[/-]:
tag: rc
increment: Patch
dev(elop)?(ment)?$:
tag: b

View File

@@ -1,12 +0,0 @@
@echo off
set /p version="Version: "
msbuild S7.Net\S7.Net.csproj /P:Configuration=Release
rmdir /S /Q nuget-pack\lib
xcopy S7.Net\bin\Release\S7.Net.dll nuget-pack\lib\net35\ /Y
xcopy S7.Net\bin\Release\S7.Net.xml nuget-pack\lib\net35\ /Y
xcopy S7.Net\bin\Release\S7.Net.dll nuget-pack\lib\net40\ /Y
xcopy S7.Net\bin\Release\S7.Net.xml nuget-pack\lib\net40\ /Y
xcopy S7.Net\bin\Release\S7.Net.dll nuget-pack\lib\net45\ /Y
xcopy S7.Net\bin\Release\S7.Net.xml nuget-pack\lib\net45\ /Y
.nuget\nuget pack nuget-pack\S7.Net.nuspec -Version %version%
pause

View File

@@ -18,12 +18,13 @@ S7.Net Plus has a [User Manual](https://github.com/killnine/s7netplus/blob/maste
+ Compatible S7 PLC (S7-200, S7-300, S7-400, S7-1200, S7-1500)
## Target framework
+ .NET Framework 3.5 or higher
+ Universal Windows Application (.Net Core) - see S7.UniversalWindowsApp.sln
## Supported frameworks
+ .NET Framework 4.5.2 and higher
+ .NET Standard 1.3 (.NET Core 1.0, UWP 10.0, Xamarin, ...)
+ .NET Standard 2.0 (.NET Core 2.0, .NET Framework 4.6.1)
## Compile
You need at least Visual Studio 2015 (you can download the Community Edition for free).
You need at least Visual Studio 2017 (you can download the Community Edition for free).
## Nuget

View File

@@ -1,29 +0,0 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("S7.Net.Core")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Microsoft")]
[assembly: AssemblyProduct("S7.Net.Core")]
[assembly: AssemblyCopyright("Copyright © Microsoft 2016")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: ComVisible(false)]

View File

@@ -1,33 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
This file contains Runtime Directives, specifications about types your application accesses
through reflection and other dynamic code patterns. Runtime Directives are used to control the
.NET Native optimizer and ensure that it does not remove code accessed by your library. If your
library does not do any reflection, then you generally do not need to edit this file. However,
if your library reflects over types, especially types passed to it or derived from its types,
then you should write Runtime Directives.
The most common use of reflection in libraries is to discover information about types passed
to the library. Runtime Directives have three ways to express requirements on types passed to
your library.
1. Parameter, GenericParameter, TypeParameter, TypeEnumerableParameter
Use these directives to reflect over types passed as a parameter.
2. SubTypes
Use a SubTypes directive to reflect over types derived from another type.
3. AttributeImplies
Use an AttributeImplies directive to indicate that your library needs to reflect over
types or methods decorated with an attribute.
For more information on writing Runtime Directives for libraries, please visit
http://go.microsoft.com/fwlink/?LinkID=391919
-->
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
<Library Name="S7.Net.Core">
<!-- add directives for your library here -->
</Library>
</Directives>

View File

@@ -1,176 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{CBFF80E8-3D3D-4656-A27C-A65EA5774536}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>S7.Net.Core</RootNamespace>
<AssemblyName>S7.Net.Core</AssemblyName>
<DefaultLanguage>en-US</DefaultLanguage>
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
<TargetPlatformVersion>10.0.10586.0</TargetPlatformVersion>
<TargetPlatformMinVersion>10.0.10240.0</TargetPlatformMinVersion>
<MinimumVisualStudioVersion>14</MinimumVisualStudioVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>ARM</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>ARM</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<PlatformTarget>x86</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<NoWarn>;2008</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<PlatformTarget>x86</PlatformTarget>
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<Optimize>true</Optimize>
<NoWarn>;2008</NoWarn>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM'">
<PlatformTarget>ARM</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\ARM\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<NoWarn>;2008</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>ARM</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM'">
<PlatformTarget>ARM</PlatformTarget>
<OutputPath>bin\ARM\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<Optimize>true</Optimize>
<NoWarn>;2008</NoWarn>
<DebugType>pdbonly</DebugType>
<PlatformTarget>ARM</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<PlatformTarget>x64</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<NoWarn>;2008</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<PlatformTarget>x64</PlatformTarget>
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<Optimize>true</Optimize>
<NoWarn>;2008</NoWarn>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<ItemGroup>
<!-- A reference to the entire .Net Framework and Windows SDK are automatically included -->
<None Include="project.json" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\S7.Net\Conversion.cs">
<Link>Conversion.cs</Link>
</Compile>
<Compile Include="..\S7.Net\Enums.cs">
<Link>Enums.cs</Link>
</Compile>
<Compile Include="..\S7.Net\PLC.cs">
<Link>PLC.cs</Link>
</Compile>
<Compile Include="..\S7.Net\Types\Boolean.cs">
<Link>Types\Boolean.cs</Link>
</Compile>
<Compile Include="..\S7.Net\Types\Byte.cs">
<Link>Types\Byte.cs</Link>
</Compile>
<Compile Include="..\S7.Net\Types\ByteArray.cs">
<Link>Types\ByteArray.cs</Link>
</Compile>
<Compile Include="..\S7.Net\Types\Class.cs">
<Link>Types\Class.cs</Link>
</Compile>
<Compile Include="..\S7.Net\Types\Counter.cs">
<Link>Types\Counter.cs</Link>
</Compile>
<Compile Include="..\S7.Net\Types\DataItem.cs">
<Link>Types\DataItem.cs</Link>
</Compile>
<Compile Include="..\S7.Net\Types\DInt.cs">
<Link>Types\DInt.cs</Link>
</Compile>
<Compile Include="..\S7.Net\Types\Double.cs">
<Link>Types\Double.cs</Link>
</Compile>
<Compile Include="..\S7.Net\Types\DWord.cs">
<Link>Types\DWord.cs</Link>
</Compile>
<Compile Include="..\S7.Net\Types\Int.cs">
<Link>Types\Int.cs</Link>
</Compile>
<Compile Include="..\S7.Net\Types\String.cs">
<Link>Types\String.cs</Link>
</Compile>
<Compile Include="..\S7.Net\Types\Struct.cs">
<Link>Types\Struct.cs</Link>
</Compile>
<Compile Include="..\S7.Net\Types\Timer.cs">
<Link>Types\Timer.cs</Link>
</Compile>
<Compile Include="..\S7.Net\Types\Word.cs">
<Link>Types\Word.cs</Link>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SocketClient.cs" />
<EmbeddedResource Include="Properties\S7.Net.Core.rd.xml" />
</ItemGroup>
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '14.0' ">
<VisualStudioVersion>14.0</VisualStudioVersion>
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@@ -1,21 +0,0 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<RestoreSuccess Condition=" '$(RestoreSuccess)' == '' ">True</RestoreSuccess>
<RestoreTool Condition=" '$(RestoreTool)' == '' ">NuGet</RestoreTool>
<ProjectAssetsFile Condition=" '$(ProjectAssetsFile)' == '' ">C:\Users\shade\Documents\GitHub\s7netplus\S7.Net.Core\project.lock.json</ProjectAssetsFile>
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\shade\.nuget\packages\</NuGetPackageFolders>
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">ProjectJson</NuGetProjectStyle>
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">4.3.1</NuGetToolVersion>
</PropertyGroup>
<PropertyGroup>
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
</PropertyGroup>
<ImportGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<Import Project="$(NuGetPackageRoot)microsoft.net.native.sharedlibrary-x86\1.7.0\build\Microsoft.Net.Native.SharedLibrary-x86.props" Condition="Exists('$(NuGetPackageRoot)microsoft.net.native.sharedlibrary-x86\1.7.0\build\Microsoft.Net.Native.SharedLibrary-x86.props')" />
<Import Project="$(NuGetPackageRoot)microsoft.net.native.sharedlibrary-x64\1.7.0\build\Microsoft.Net.Native.SharedLibrary-x64.props" Condition="Exists('$(NuGetPackageRoot)microsoft.net.native.sharedlibrary-x64\1.7.0\build\Microsoft.Net.Native.SharedLibrary-x64.props')" />
<Import Project="$(NuGetPackageRoot)microsoft.net.native.sharedlibrary-arm\1.7.0\build\Microsoft.Net.Native.SharedLibrary-arm.props" Condition="Exists('$(NuGetPackageRoot)microsoft.net.native.sharedlibrary-arm\1.7.0\build\Microsoft.Net.Native.SharedLibrary-arm.props')" />
<Import Project="$(NuGetPackageRoot)microsoft.net.native.compiler\1.7.0\build\Microsoft.Net.Native.Compiler.props" Condition="Exists('$(NuGetPackageRoot)microsoft.net.native.compiler\1.7.0\build\Microsoft.Net.Native.Compiler.props')" />
</ImportGroup>
</Project>

View File

@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
</PropertyGroup>
<ImportGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<Import Project="$(NuGetPackageRoot)microsoft.net.native.sharedlibrary-x86\1.7.0\build\Microsoft.Net.Native.SharedLibrary-x86.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.net.native.sharedlibrary-x86\1.7.0\build\Microsoft.Net.Native.SharedLibrary-x86.targets')" />
<Import Project="$(NuGetPackageRoot)microsoft.net.native.sharedlibrary-x64\1.7.0\build\Microsoft.Net.Native.SharedLibrary-x64.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.net.native.sharedlibrary-x64\1.7.0\build\Microsoft.Net.Native.SharedLibrary-x64.targets')" />
<Import Project="$(NuGetPackageRoot)microsoft.net.native.sharedlibrary-arm\1.7.0\build\Microsoft.Net.Native.SharedLibrary-arm.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.net.native.sharedlibrary-arm\1.7.0\build\Microsoft.Net.Native.SharedLibrary-arm.targets')" />
<Import Project="$(NuGetPackageRoot)microsoft.net.native.compiler\1.7.0\build\Microsoft.Net.Native.Compiler.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.net.native.compiler\1.7.0\build\Microsoft.Net.Native.Compiler.targets')" />
</ImportGroup>
</Project>

View File

@@ -1,481 +0,0 @@
using System;
using System.Net.Sockets;
using System.Threading;
using System.Net;
namespace S7.Net
{
/// <summary>
/// This class encapsulate System.Net.Sockets.Socket class of .Net core, so we can use the same methods of the standard Socket class inside the S7.Net sources.
/// </summary>
internal class Socket
{
public bool Connected
{
get
{
if (_socket == null)
return false;
return _socket.Connected;
}
}
public SocketError LastSocketError { get; private set; }
public Socket(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType)
{
_socket = new System.Net.Sockets.Socket(addressFamily, socketType, protocolType);
}
public void Connect(IPEndPoint server)
{
if (Connected)
return;
LastSocketError = SocketError.NotConnected;
var socketEventArg = new SocketAsyncEventArgs();
socketEventArg.RemoteEndPoint = server;
var completedEvent = new EventHandler<SocketAsyncEventArgs>(delegate (object s, SocketAsyncEventArgs e)
{
LastSocketError = e.SocketError;
_clientDone.Set();
});
socketEventArg.Completed += completedEvent;
_clientDone.Reset();
LastSocketError = SocketError.TimedOut;
_socket.ConnectAsync(socketEventArg);
_clientDone.WaitOne(TIMEOUT_MILLISECONDS);
socketEventArg.Completed -= completedEvent;
}
public int Send(byte[] buffer, int size, SocketFlags socketFlag)
{
var response = 0;
if (_socket != null)
{
var socketEventArg = new SocketAsyncEventArgs();
socketEventArg.RemoteEndPoint = _socket.RemoteEndPoint;
socketEventArg.UserToken = null;
var completedEvent = new EventHandler<SocketAsyncEventArgs>(delegate (object s, SocketAsyncEventArgs e)
{
LastSocketError = e.SocketError;
if (e.SocketError == SocketError.Success)
response = e.BytesTransferred;
_clientDone.Set();
});
socketEventArg.Completed += completedEvent;
socketEventArg.SetBuffer(buffer, 0, size);
_clientDone.Reset();
LastSocketError = SocketError.TimedOut;
_socket.SendAsync(socketEventArg);
_clientDone.WaitOne(_sendTimeout);
socketEventArg.Completed -= completedEvent;
}
else
{
LastSocketError = SocketError.NotInitialized;
}
return response;
}
public int Receive(byte[] buffer, int size, SocketFlags socketFlag)
{
var response = 0;
if (_socket != null)
{
var socketEventArg = new SocketAsyncEventArgs();
socketEventArg.RemoteEndPoint = _socket.RemoteEndPoint;
socketEventArg.SetBuffer(buffer, 0, size);
var completedEvent = new EventHandler<SocketAsyncEventArgs>(delegate (object s, SocketAsyncEventArgs e)
{
LastSocketError = e.SocketError;
if (e.SocketError == SocketError.Success)
response = e.BytesTransferred;
_clientDone.Set();
});
socketEventArg.Completed += completedEvent;
_clientDone.Reset();
LastSocketError = SocketError.TimedOut;
_socket.ReceiveAsync(socketEventArg);
_clientDone.WaitOne(_receiveTimeout);
socketEventArg.Completed -= completedEvent;
}
else
{
LastSocketError = SocketError.NotInitialized;
}
return response;
}
public void Shutdown(SocketShutdown how)
{
_socket.Shutdown(how);
}
public void Close()
{
if (_socket != null)
{
_socket.Dispose();
_socket = null;
}
}
//
// Summary:
// Sets the specified System.Net.Sockets.Socket option to the specified integer
// value.
//
// Parameters:
// optionLevel:
// One of the System.Net.Sockets.SocketOptionLevel values.
//
// optionName:
// One of the System.Net.Sockets.SocketOptionName values.
//
// optionValue:
// A value of the option.
//
// Exceptions:
// T:System.Net.Sockets.SocketException:
// An error occurred when attempting to access the socket. See the Remarks section
// for more information.
//
// T:System.ObjectDisposedException:
// The System.Net.Sockets.Socket has been closed.
public void SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, int optionValue)
{
switch (optionName)
{
case SocketOptionName.ReceiveTimeout:
_receiveTimeout = optionValue;
break;
case SocketOptionName.SendTimeout:
_sendTimeout = optionValue;
break;
default:
throw new NotImplementedException("SetSocketOption option not implemented");
}
}
private System.Net.Sockets.Socket _socket = null;
private int _receiveTimeout = TIMEOUT_MILLISECONDS;
private int _sendTimeout = TIMEOUT_MILLISECONDS;
private readonly static ManualResetEvent _clientDone =
new ManualResetEvent(false);
private const int TIMEOUT_MILLISECONDS = 1000;
}
//
// Summary:
// Specifies socket send and receive behaviors.
[Flags]
public enum SocketFlags
{
//
// Summary:
// Use no flags for this call.
None = 0,
////
//// Summary:
//// Process out-of-band data.
//OutOfBand = 1,
////
//// Summary:
//// Peek at the incoming message.
//Peek = 2,
////
//// Summary:
//// Send without using routing tables.
//DontRoute = 4,
////
//// Summary:
//// Provides a standard value for the number of WSABUF structures that are used to
//// send and receive data.
//MaxIOVectorLength = 16,
////
//// Summary:
//// The message was too large to fit into the specified buffer and was truncated.
//Truncated = 256,
////
//// Summary:
//// Indicates that the control data did not fit into an internal 64-KB buffer and
//// was truncated.
//ControlDataTruncated = 512,
////
//// Summary:
//// Indicates a broadcast packet.
//Broadcast = 1024,
////
//// Summary:
//// Indicates a multicast packet.
//Multicast = 2048,
////
//// Summary:
//// Partial send or receive for message.
//Partial = 32768
}
//
// Summary:
// Defines socket option levels for the System.Net.Sockets.Socket.SetSocketOption(System.Net.Sockets.SocketOptionLevel,System.Net.Sockets.SocketOptionName,System.Int32)
// and System.Net.Sockets.Socket.GetSocketOption(System.Net.Sockets.SocketOptionLevel,System.Net.Sockets.SocketOptionName)
// methods.
public enum SocketOptionLevel
{
//
// Summary:
// System.Net.Sockets.Socket options apply only to IP sockets.
IP = 0,
//
// Summary:
// System.Net.Sockets.Socket options apply only to TCP sockets.
Tcp = 6,
//
// Summary:
// System.Net.Sockets.Socket options apply only to UDP sockets.
Udp = 17,
//
// Summary:
// System.Net.Sockets.Socket options apply only to IPv6 sockets.
IPv6 = 41,
//
// Summary:
// System.Net.Sockets.Socket options apply to all sockets.
Socket = 65535
}
//
// Summary:
// Defines configuration option names.
public enum SocketOptionName
{
//
// Summary:
// Close the socket gracefully without lingering.
DontLinger = -129,
//
// Summary:
// Enables a socket to be bound for exclusive access.
ExclusiveAddressUse = -5,
//
// Summary:
// Record debugging information.
Debug = 1,
//
// Summary:
// Specifies the IP options to be inserted into outgoing datagrams.
IPOptions = 1,
//
// Summary:
// Disables the Nagle algorithm for send coalescing.
NoDelay = 1,
//
// Summary:
// Send UDP datagrams with checksum set to zero.
NoChecksum = 1,
//
// Summary:
// The socket is listening.
AcceptConnection = 2,
//
// Summary:
// Indicates that the application provides the IP header for outgoing datagrams.
HeaderIncluded = 2,
//
// Summary:
// Use urgent data as defined in RFC-1222. This option can be set only once; after
// it is set, it cannot be turned off.
BsdUrgent = 2,
//
// Summary:
// Use expedited data as defined in RFC-1222. This option can be set only once;
// after it is set, it cannot be turned off.
Expedited = 2,
//
// Summary:
// Change the IP header type of the service field.
TypeOfService = 3,
//
// Summary:
// Allows the socket to be bound to an address that is already in use.
ReuseAddress = 4,
//
// Summary:
// Set the IP header Time-to-Live field.
IpTimeToLive = 4,
//
// Summary:
// Use keep-alives.
KeepAlive = 8,
//
// Summary:
// Set the interface for outgoing multicast packets.
MulticastInterface = 9,
//
// Summary:
// An IP multicast Time to Live.
MulticastTimeToLive = 10,
//
// Summary:
// An IP multicast loopback.
MulticastLoopback = 11,
//
// Summary:
// Add an IP group membership.
AddMembership = 12,
//
// Summary:
// Drop an IP group membership.
DropMembership = 13,
//
// Summary:
// Do not fragment IP datagrams.
DontFragment = 14,
//
// Summary:
// Join a source group.
AddSourceMembership = 15,
//
// Summary:
// Do not route; send the packet directly to the interface addresses.
DontRoute = 16,
//
// Summary:
// Drop a source group.
DropSourceMembership = 16,
//
// Summary:
// Block data from a source.
BlockSource = 17,
//
// Summary:
// Unblock a previously blocked source.
UnblockSource = 18,
//
// Summary:
// Return information about received packets.
PacketInformation = 19,
//
// Summary:
// Set or get the UDP checksum coverage.
ChecksumCoverage = 20,
//
// Summary:
// Specifies the maximum number of router hops for an Internet Protocol version
// 6 (IPv6) packet. This is similar to Time to Live (TTL) for Internet Protocol
// version 4.
HopLimit = 21,
//
// Summary:
// Permit sending broadcast messages on the socket.
Broadcast = 32,
//
// Summary:
// Bypass hardware when possible.
UseLoopback = 64,
//
// Summary:
// Linger on close if unsent data is present.
Linger = 128,
//
// Summary:
// Receives out-of-band data in the normal data stream.
OutOfBandInline = 256,
//
// Summary:
// Specifies the total per-socket buffer space reserved for sends. This is unrelated
// to the maximum message size or the size of a TCP window.
SendBuffer = 4097,
//
// Summary:
// Specifies the total per-socket buffer space reserved for receives. This is unrelated
// to the maximum message size or the size of a TCP window.
ReceiveBuffer = 4098,
//
// Summary:
// Specifies the low water mark for Overload:System.Net.Sockets.Socket.Send operations.
SendLowWater = 4099,
//
// Summary:
// Specifies the low water mark for Overload:System.Net.Sockets.Socket.Receive operations.
ReceiveLowWater = 4100,
//
// Summary:
// Send a time-out. This option applies only to synchronous methods; it has no effect
// on asynchronous methods such as the System.Net.Sockets.Socket.BeginSend(System.Byte[],System.Int32,System.Int32,System.Net.Sockets.SocketFlags,System.AsyncCallback,System.Object)
// method.
SendTimeout = 4101,
//
// Summary:
// Receive a time-out. This option applies only to synchronous methods; it has no
// effect on asynchronous methods such as the System.Net.Sockets.Socket.BeginSend(System.Byte[],System.Int32,System.Int32,System.Net.Sockets.SocketFlags,System.AsyncCallback,System.Object)
// method.
ReceiveTimeout = 4102,
//
// Summary:
// Get the error status and clear.
Error = 4103,
//
// Summary:
// Get the socket type.
Type = 4104,
//
// Summary:
// Updates an accepted socket's properties by using those of an existing socket.
// This is equivalent to using the Winsock2 SO_UPDATE_ACCEPT_CONTEXT socket option
// and is supported only on connection-oriented sockets.
UpdateAcceptContext = 28683,
//
// Summary:
// Updates a connected socket's properties by using those of an existing socket.
// This is equivalent to using the Winsock2 SO_UPDATE_CONNECT_CONTEXT socket option
// and is supported only on connection-oriented sockets.
UpdateConnectContext = 28688,
//
// Summary:
// Not supported; will throw a System.Net.Sockets.SocketException if used.
MaxConnections = int.MaxValue
}
}

View File

@@ -1,16 +0,0 @@
{
"dependencies": {
"Microsoft.NETCore.UniversalWindowsPlatform": "5.4.0"
},
"frameworks": {
"uap10.0.10240": {}
},
"runtimes": {
"win10-arm": {},
"win10-arm-aot": {},
"win10-x86": {},
"win10-x86-aot": {},
"win10-x64": {},
"win10-x64-aot": {}
}
}

View File

@@ -0,0 +1,82 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using S7.Net.Protocol;
namespace S7.Net.UnitTest
{
[TestClass]
public class ConnectionRequestTest
{
[TestMethod]
public void Test_ConnectionRequest_S7_200()
{
CollectionAssert.AreEqual(MakeConnectionRequest(16, 0, 16, 0),
ConnectionRequest.GetCOTPConnectionRequest(CpuType.S7200, 0, 0));
}
[TestMethod]
public void Test_ConnectionRequest_S7_300()
{
CollectionAssert.AreEqual(MakeConnectionRequest(1, 0, 3, 0),
ConnectionRequest.GetCOTPConnectionRequest(CpuType.S7300, 0, 0));
CollectionAssert.AreEqual(MakeConnectionRequest(1, 0, 3, 1),
ConnectionRequest.GetCOTPConnectionRequest(CpuType.S7300, 0, 1));
CollectionAssert.AreEqual(MakeConnectionRequest(1, 0, 3, 33),
ConnectionRequest.GetCOTPConnectionRequest(CpuType.S7300, 1, 1));
}
[TestMethod]
public void Test_ConnectionRequest_S7_400()
{
CollectionAssert.AreEqual(MakeConnectionRequest(1, 0, 3, 0),
ConnectionRequest.GetCOTPConnectionRequest(CpuType.S7400, 0, 0));
CollectionAssert.AreEqual(MakeConnectionRequest(1, 0, 3, 1),
ConnectionRequest.GetCOTPConnectionRequest(CpuType.S7400, 0, 1));
CollectionAssert.AreEqual(MakeConnectionRequest(1, 0, 3, 33),
ConnectionRequest.GetCOTPConnectionRequest(CpuType.S7400, 1, 1));
}
[TestMethod]
public void Test_ConnectionRequest_S7_1200()
{
CollectionAssert.AreEqual(MakeConnectionRequest(1, 0, 3, 0),
ConnectionRequest.GetCOTPConnectionRequest(CpuType.S71200, 0, 0));
CollectionAssert.AreEqual(MakeConnectionRequest(1, 0, 3, 1),
ConnectionRequest.GetCOTPConnectionRequest(CpuType.S71200, 0, 1));
CollectionAssert.AreEqual(MakeConnectionRequest(1, 0, 3, 33),
ConnectionRequest.GetCOTPConnectionRequest(CpuType.S71200, 1, 1));
}
[TestMethod]
public void Test_ConnectionRequest_S7_1500()
{
CollectionAssert.AreEqual(MakeConnectionRequest(0x10, 0x2, 3, 0),
ConnectionRequest.GetCOTPConnectionRequest(CpuType.S71500, 0, 0));
CollectionAssert.AreEqual(MakeConnectionRequest(0x10, 0x2, 3, 1),
ConnectionRequest.GetCOTPConnectionRequest(CpuType.S71500, 0, 1));
CollectionAssert.AreEqual(MakeConnectionRequest(0x10, 0x2, 3, 33),
ConnectionRequest.GetCOTPConnectionRequest(CpuType.S71500, 1, 1));
}
private static byte[] MakeConnectionRequest(byte sourceTsap1, byte sourceTsap2, byte destTsap1, byte destTsap2)
{
return new byte[]
{
3, 0, 0, 22, //TPKT
17, //COTP Header Length
224, //Connect Request
0, 0, //Destination Reference
0, 46, //Source Reference
0, //Flags
193, //Parameter Code (src-tasp)
2, //Parameter Length
sourceTsap1, sourceTsap2, //Source TASP
194, //Parameter Code (dst-tasp)
2, //Parameter Length
destTsap1, destTsap2, //Destination TASP
192, //Parameter Code (tpdu-size)
1, //Parameter Length
11 //TPDU Size (2^11 = 2048)
};
}
}
}

View File

@@ -35,16 +35,21 @@ namespace S7.Net.UnitTest.Helpers
/// <summary>
/// DB1.DBD4
/// </summary>
public double RealVariable { get; set; }
public double RealVariableDouble { get; set; }
/// <summary>
/// DB1.DBD8
/// </summary>
public int DIntVariable { get; set; }
public float RealVariableFloat { get; set; }
/// <summary>
/// DB1.DBD12
/// </summary>
public int DIntVariable { get; set; }
/// <summary>
/// DB1.DBD16
/// </summary>
public ushort DWordVariable { get; set; }
}
}

View File

@@ -15,10 +15,12 @@ namespace S7.UnitTest.Helpers
public ushort[] UShorts { get; set; } = new ushort[2];
public int[] Ints { get; set; } = new int[2];
public double[] Doubles { get; set; } = new double[2];
public float[] Singles { get; set; } = new float[2];
public short Short { get; set; }
public ushort UShort { get; set; }
public int Int { get; set; }
public double Double { get; set; }
public float Single { get; set; }
}
}

View File

@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace S7.UnitTest.Helpers
{
class TestSmallClass
{
public bool Bool1 { get; set; }
}
}

View File

@@ -35,16 +35,21 @@ namespace S7.Net.UnitTest.Helpers
/// <summary>
/// DB1.DBD4
/// </summary>
public double RealVariable;
public double RealVariableDouble;
/// <summary>
/// DB1.DBD8
/// </summary>
public int DIntVariable;
public float RealVariableFloat;
/// <summary>
/// DB1.DBD12
/// </summary>
public int DIntVariable;
/// <summary>
/// DB1.DBD16
/// </summary>
public ushort DWordVariable;
}
}

View File

@@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using S7.Net;
using System.IO;
using System.Threading.Tasks;
namespace S7.Net.UnitTest
{
[TestClass]
public class ProtocolUnitTest
{
[TestMethod]
public void TPKT_Read()
{
var m = new MemoryStream(StringToByteArray("0300002902f0803203000000010002001400000401ff0400807710000100000103000000033f8ccccd"));
var t = TPKT.Read(m);
Assert.AreEqual(0x03, t.Version);
Assert.AreEqual(0x29, t.Length);
m.Position = 0;
t = TPKT.ReadAsync(m).Result;
Assert.AreEqual(0x03, t.Version);
Assert.AreEqual(0x29, t.Length);
}
[TestMethod]
[ExpectedException(typeof(TPKTInvalidException))]
public void TPKT_ReadShort()
{
var m = new MemoryStream(StringToByteArray("0300002902f0803203000000010002001400000401ff040080"));
var t = TPKT.Read(m);
}
[TestMethod]
[ExpectedException(typeof(TPKTInvalidException))]
public async Task TPKT_ReadShortAsync()
{
var m = new MemoryStream(StringToByteArray("0300002902f0803203000000010002001400000401ff040080"));
var t = await TPKT.ReadAsync(m);
}
[TestMethod]
public void COTP_ReadTSDU()
{
var expected = StringToByteArray("320700000400000800080001120411440100ff09000400000000");
var m = new MemoryStream(StringToByteArray("0300000702f0000300000702f0000300002102f080320700000400000800080001120411440100ff09000400000000"));
var t = COTP.TSDU.Read(m);
Assert.IsTrue(expected.SequenceEqual(t));
m.Position = 0;
t = COTP.TSDU.ReadAsync(m).Result;
Assert.IsTrue(expected.SequenceEqual(t));
}
private static byte[] StringToByteArray(string hex)
{
return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
}
}
}

View File

@@ -6,9 +6,9 @@
<ProjectGuid>{303CCED6-9ABC-4899-A509-743341AAA804}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>S7.UnitTest</RootNamespace>
<RootNamespace>S7.Net.UnitTest</RootNamespace>
<AssemblyName>S7Net.UnitTest</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
@@ -16,6 +16,7 @@
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
<IsCodedUITest>False</IsCodedUITest>
<TestProjectType>UnitTest</TestProjectType>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@@ -25,6 +26,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
@@ -33,8 +35,18 @@
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup>
<SignAssembly>true</SignAssembly>
</PropertyGroup>
<PropertyGroup>
<AssemblyOriginatorKeyFile>S7.Net.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.ServiceProcess" />
<Reference Include="System.Windows.Forms" />
@@ -45,14 +57,12 @@
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
</ItemGroup>
</When>
<Otherwise>
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework" />
</ItemGroup>
</Otherwise>
<Otherwise />
</Choose>
<ItemGroup>
<Compile Include="ConnectionRequestTest.cs" />
<Compile Include="ConvertersUnitTest.cs" />
<Compile Include="ProtocolTests.cs" />
<Compile Include="Helpers\ConsoleManager.cs" />
<Compile Include="Helpers\NativeMethods.cs" />
<Compile Include="Helpers\S7TestServer.cs" />
@@ -60,12 +70,16 @@
<Compile Include="Helpers\TestClassWithCustomType.cs" />
<Compile Include="Helpers\TestClassWithPrivateSetters.cs" />
<Compile Include="Helpers\TestLongClass.cs" />
<Compile Include="S7NetTestsAsync.cs" />
<Compile Include="Helpers\TestSmallClass.cs" />
<Compile Include="Snap7\snap7.net.cs" />
<Compile Include="Helpers\TestClass.cs" />
<Compile Include="Helpers\TestStruct.cs" />
<Compile Include="S7NetTests.cs" />
<Compile Include="S7NetTestsSync.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Helpers\TestLongStruct.cs" />
<Compile Include="TypeTests\StringExTests.cs" />
<Compile Include="TypeTests\StringTests.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="snap7.dll">
@@ -78,6 +92,9 @@
<Name>S7.Net</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="S7.Net.snk" />
</ItemGroup>
<Choose>
<When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'">
<ItemGroup>

BIN
S7.Net.UnitTest/S7.Net.snk Normal file

Binary file not shown.

View File

@@ -0,0 +1,870 @@
#region Using
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using S7.Net;
using S7.Net.UnitTest.Helpers;
using S7.Net.UnitTest;
using System.ServiceProcess;
using S7.Net.Types;
using S7.UnitTest.Helpers;
using System.Threading.Tasks;
#endregion
/**
* About the tests:
* ---------------------------------------------------------------------------
* The tests were written to show how to use this library to read and write
* different types of values, how to box and unbox values and of course to
* address some of the bugs of the library.
* These tests are not meant to cover 100% the code, but to check that once a
* variable is written, it stores the correct value.
* ----------------------------------------------------------------------------
* The plc used for the tests is the S7 "server" provided by Snap7 opensource
* library, that you can get for free here:http://snap7.sourceforge.net/
* The implementation of the server will not be discussed here, but there are
* some issues with the interop that cause the server, and unit test, to fail
* under some circumstances, like "click on Run all tests" too much.
* This doesn't mean that S7.Net has bugs, but that the implementation of the
* server has problems.
*
*/
//Tests for Async Methods
namespace S7.Net.UnitTest
{
public partial class S7NetTests
{
#region Tests
[TestMethod]
public async Task Test_Async_Connection()
{
if (plc.IsConnected == false)
{
await plc.OpenAsync();
}
}
/// <summary>
/// Read/Write a single Int16 or UInt16 with a single request.
/// Test that writing a UInt16 (ushort) and reading it gives the correct value.
/// Test also that writing a Int16 (short) and reading it gives the correct value.
/// </summary>
[TestMethod]
public async Task Test_Async_WriteAndReadInt16Variable()
{
Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor.");
// To write a ushort i don't need any cast, only unboxing must be done
ushort val = 40000;
await plc.WriteAsync("DB1.DBW0", val);
ushort result = (ushort)await plc.ReadAsync("DB1.DBW0");
Assert.AreEqual(val, result, "A ushort goes from 0 to 64512");
// To write a short i need to convert it to UShort, then i need to reconvert the readed value to get
// the negative sign back
// Depending if i'm writing on a DWORD or on a DEC, i will see ushort or short value in the plc
short value = -100;
Assert.IsTrue(plc.IsConnected, "After connecting, IsConnected must be set to true");
await plc.WriteAsync("DB1.DBW0", value.ConvertToUshort());
short result2 = ((ushort)await plc.ReadAsync("DB1.DBW0")).ConvertToShort();
Assert.AreEqual(value, result2, "A short goes from -32767 to 32766");
}
/// <summary>
/// Read/Write a single Int32 or UInt32 with a single request.
/// Test that writing a UInt32 (uint) and reading it gives the correct value.
/// Test also that writing a Int32 (int) and reading it gives the correct value.
/// </summary>
[TestMethod]
public async Task Test_Async_WriteAndReadInt32Variable()
{
Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor.");
// To write a uint I don't need any cast, only unboxing must be done
int val = 1000;
await plc.WriteAsync("DB1.DBD40", val);
int result = ((uint)await plc.ReadAsync("DB1.DBD40")).ConvertToInt();
Assert.AreEqual(val, result);
// To write a int I need to convert it to uint, then I need to reconvert the readed value to get
// the negative sign back
// Depending if I'm writing on a DBD or on a LONG, I will see uint or int value in the plc
int value = -60000;
await plc.WriteAsync("DB1.DBD60", value);
int result2 = ((uint)await plc.ReadAsync("DB1.DBD60")).ConvertToInt();
Assert.AreEqual(value, result2);
}
/// <summary>
/// Read/Write a single REAL with a single request.
/// Test that writing a double and reading it gives the correct value.
/// </summary>
[TestMethod]
public async Task Test_Async_WriteAndReadRealVariables()
{
Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor.");
// Reading and writing a double is quite complicated, because it needs to be converted to DWord before the write,
// then reconvert to double after the read.
double val = 35.68729;
await plc.WriteAsync("DB1.DBD40", val.ConvertToUInt());
double result = ((uint)await plc.ReadAsync("DB1.DBD40")).ConvertToDouble();
Assert.AreEqual(val, Math.Round(result, 5)); // float lose precision, so i need to round it
// Reading and writing a float is quite complicated, because it needs to be converted to DWord before the write,
// then reconvert to float after the read. Float values can contain only 7 digits, so no precision is lost.
float val2 = 1234567;
await plc.WriteAsync("DB1.DBD40", val2.ConvertToUInt());
float result2 = ((uint)await plc.ReadAsync("DB1.DBD40")).ConvertToFloat();
Assert.AreEqual(val2, result2);
float val3 = 12.34567f;
await plc.WriteAsync("DB1.DBD40", val3.ConvertToUInt());
float result3 = ((uint)await plc.ReadAsync("DB1.DBD40")).ConvertToFloat();
Assert.AreEqual(val3, result3);
}
/// <summary>
/// Read/Write a class that has the same properties of a DB with the same field in the same order
/// </summary>
[TestMethod]
public async Task Test_Async_ReadAndWriteClass()
{
Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor.");
TestClass tc = new TestClass
{
BitVariable00 = true,
BitVariable10 = true,
DIntVariable = -100000,
IntVariable = -15000,
RealVariableDouble = -154.789,
RealVariableFloat = -154.789f,
DWordVariable = 850
};
await plc.WriteClassAsync(tc, DB2);
TestClass tc2 = new TestClass();
// Values that are read from a class are stored inside the class itself, that is passed by reference
await plc.ReadClassAsync(tc2, DB2);
Assert.AreEqual(tc.BitVariable00, tc2.BitVariable00);
Assert.AreEqual(tc.BitVariable10, tc2.BitVariable10);
Assert.AreEqual(tc.DIntVariable, tc2.DIntVariable);
Assert.AreEqual(tc.IntVariable, tc2.IntVariable);
Assert.AreEqual(tc.RealVariableDouble, Math.Round(tc2.RealVariableDouble, 3));
Assert.AreEqual(tc.RealVariableFloat, tc2.RealVariableFloat);
Assert.AreEqual(tc.DWordVariable, tc2.DWordVariable);
}
/// <summary>
/// Read/Write a struct that has the same properties of a DB with the same field in the same order
/// </summary>
[TestMethod]
public async Task Test_Async_ReadAndWriteStruct()
{
Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor.");
TestStruct tc = new TestStruct
{
BitVariable00 = true,
BitVariable10 = true,
DIntVariable = -100000,
IntVariable = -15000,
RealVariableDouble = -154.789,
RealVariableFloat = -154.789f,
DWordVariable = 850
};
plc.WriteStruct(tc, DB2);
// Values that are read from a struct are stored in a new struct, returned by the funcion ReadStruct
TestStruct tc2 = (TestStruct)await plc.ReadStructAsync(typeof(TestStruct), DB2);
Assert.AreEqual(tc.BitVariable00, tc2.BitVariable00);
Assert.AreEqual(tc.BitVariable10, tc2.BitVariable10);
Assert.AreEqual(tc.DIntVariable, tc2.DIntVariable);
Assert.AreEqual(tc.IntVariable, tc2.IntVariable);
Assert.AreEqual(tc.RealVariableDouble, Math.Round(tc2.RealVariableDouble, 3));
Assert.AreEqual(tc.RealVariableFloat, tc2.RealVariableFloat);
Assert.AreEqual(tc.DWordVariable, tc2.DWordVariable);
}
/// <summary>
/// Read/Write a struct that has the same properties of a DB with the same field in the same order
/// </summary>
[TestMethod]
public async Task Test_Async_ReadAndWriteLongStruct()
{
Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor.");
TestLongStruct tc = new TestLongStruct
{
IntVariable0 = 0,
IntVariable1 = 1,
IntVariable10 = 10,
IntVariable11 = 11,
IntVariable20 = 20,
IntVariable21 = 21,
IntVariable30 = 30,
IntVariable31 = 31,
IntVariable40 = 40,
IntVariable41 = 41,
IntVariable50 = 50,
IntVariable51 = 51,
IntVariable60 = 60,
IntVariable61 = 61,
IntVariable70 = 70,
IntVariable71 = 71,
IntVariable80 = 80,
IntVariable81 = 81,
IntVariable90 = 90,
IntVariable91 = 91,
IntVariable100 = 100,
IntVariable101 = 101,
IntVariable110 = 200,
IntVariable111 = 201
};
plc.WriteStruct(tc, DB2);
// Values that are read from a struct are stored in a new struct, returned by the funcion ReadStruct
TestLongStruct tc2 = (TestLongStruct)await plc.ReadStructAsync(typeof(TestLongStruct), DB2);
Assert.AreEqual(tc.IntVariable0, tc2.IntVariable0);
Assert.AreEqual(tc.IntVariable1, tc2.IntVariable1);
Assert.AreEqual(tc.IntVariable10, tc2.IntVariable10);
Assert.AreEqual(tc.IntVariable11, tc2.IntVariable11);
Assert.AreEqual(tc.IntVariable20, tc2.IntVariable20);
Assert.AreEqual(tc.IntVariable21, tc2.IntVariable21);
Assert.AreEqual(tc.IntVariable30, tc2.IntVariable30);
Assert.AreEqual(tc.IntVariable31, tc2.IntVariable31);
Assert.AreEqual(tc.IntVariable40, tc2.IntVariable40);
Assert.AreEqual(tc.IntVariable41, tc2.IntVariable41);
Assert.AreEqual(tc.IntVariable50, tc2.IntVariable50);
Assert.AreEqual(tc.IntVariable51, tc2.IntVariable51);
Assert.AreEqual(tc.IntVariable60, tc2.IntVariable60);
Assert.AreEqual(tc.IntVariable61, tc2.IntVariable61);
Assert.AreEqual(tc.IntVariable70, tc2.IntVariable70);
Assert.AreEqual(tc.IntVariable71, tc2.IntVariable71);
Assert.AreEqual(tc.IntVariable80, tc2.IntVariable80);
Assert.AreEqual(tc.IntVariable81, tc2.IntVariable81);
Assert.AreEqual(tc.IntVariable90, tc2.IntVariable90);
Assert.AreEqual(tc.IntVariable91, tc2.IntVariable91);
Assert.AreEqual(tc.IntVariable100, tc2.IntVariable100);
Assert.AreEqual(tc.IntVariable101, tc2.IntVariable101);
Assert.AreEqual(tc.IntVariable110, tc2.IntVariable110);
Assert.AreEqual(tc.IntVariable111, tc2.IntVariable111);
}
/// <summary>
/// Read/Write a class that has the same properties of a DB with the same field in the same order
/// </summary>
[TestMethod]
public async Task Test_Async_ReadAndWriteLongClass()
{
Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor.");
TestLongClass tc = new TestLongClass
{
IntVariable0 = 0,
IntVariable1 = 1,
IntVariable10 = 10,
IntVariable11 = 11,
IntVariable20 = 20,
IntVariable21 = 21,
IntVariable30 = 30,
IntVariable31 = 31,
IntVariable40 = 40,
IntVariable41 = 41,
IntVariable50 = 50,
IntVariable51 = 51,
IntVariable60 = 60,
IntVariable61 = 61,
IntVariable70 = 70,
IntVariable71 = 71,
IntVariable80 = 80,
IntVariable81 = 81,
IntVariable90 = 90,
IntVariable91 = 91,
IntVariable100 = 100,
IntVariable101 = 101,
IntVariable110 = 200,
IntVariable111 = 201
};
await plc.WriteClassAsync(tc, DB2);
// Values that are read from a struct are stored in a new struct, returned by the funcion ReadStruct
TestLongClass tc2 = new TestLongClass();
await plc.ReadClassAsync(tc2, DB2);
Assert.AreEqual(tc.IntVariable0, tc2.IntVariable0);
Assert.AreEqual(tc.IntVariable1, tc2.IntVariable1);
Assert.AreEqual(tc.IntVariable10, tc2.IntVariable10);
Assert.AreEqual(tc.IntVariable11, tc2.IntVariable11);
Assert.AreEqual(tc.IntVariable20, tc2.IntVariable20);
Assert.AreEqual(tc.IntVariable21, tc2.IntVariable21);
Assert.AreEqual(tc.IntVariable30, tc2.IntVariable30);
Assert.AreEqual(tc.IntVariable31, tc2.IntVariable31);
Assert.AreEqual(tc.IntVariable40, tc2.IntVariable40);
Assert.AreEqual(tc.IntVariable41, tc2.IntVariable41);
Assert.AreEqual(tc.IntVariable50, tc2.IntVariable50);
Assert.AreEqual(tc.IntVariable51, tc2.IntVariable51);
Assert.AreEqual(tc.IntVariable60, tc2.IntVariable60);
Assert.AreEqual(tc.IntVariable61, tc2.IntVariable61);
Assert.AreEqual(tc.IntVariable70, tc2.IntVariable70);
Assert.AreEqual(tc.IntVariable71, tc2.IntVariable71);
Assert.AreEqual(tc.IntVariable80, tc2.IntVariable80);
Assert.AreEqual(tc.IntVariable81, tc2.IntVariable81);
Assert.AreEqual(tc.IntVariable90, tc2.IntVariable90);
Assert.AreEqual(tc.IntVariable91, tc2.IntVariable91);
Assert.AreEqual(tc.IntVariable100, tc2.IntVariable100);
Assert.AreEqual(tc.IntVariable101, tc2.IntVariable101);
Assert.AreEqual(tc.IntVariable110, tc2.IntVariable110);
Assert.AreEqual(tc.IntVariable111, tc2.IntVariable111);
}
/// <summary>
/// Tests that a read and a write on addresses bigger than 8192 are executed correctly
/// </summary>
[TestMethod]
public async Task Test_Async_WriteAndReadInt16VariableAddress8192()
{
Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor.");
// To write a ushort i don't need any cast, only unboxing must be done
ushort val = 8192;
await plc.WriteAsync("DB2.DBW8192", val);
ushort result = (ushort)await plc.ReadAsync("DB2.DBW8192");
Assert.AreEqual(val, result, "A ushort goes from 0 to 64512");
// To write a short i need to convert it to UShort, then i need to reconvert the readed value to get
// the negative sign back
// Depending if i'm writing on a DWORD or on a DEC, i will see ushort or short value in the plc
short value = -8192;
Assert.IsTrue(plc.IsConnected, "After connecting, IsConnected must be set to true");
await plc.WriteAsync("DB2.DBW8192", value.ConvertToUshort());
short result2 = ((ushort)await plc.ReadAsync("DB2.DBW8192")).ConvertToShort();
Assert.AreEqual(value, result2, "A short goes from -32767 to 32766");
}
/// <summary>
/// Tests that a read and a write on addresses bigger than 8192 are executed correctly
/// </summary>
[TestMethod]
public async Task Test_Async_WriteAndReadInt16VariableAddress16384()
{
Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor.");
// To write a ushort i don't need any cast, only unboxing must be done
ushort val = 16384;
await plc.WriteAsync("DB2.DBW16384", val);
ushort result = (ushort)await plc.ReadAsync("DB2.DBW16384");
Assert.AreEqual(val, result, "A ushort goes from 0 to 64512");
// To write a short i need to convert it to UShort, then i need to reconvert the readed value to get
// the negative sign back
// Depending if i'm writing on a DWORD or on a DEC, i will see ushort or short value in the plc
short value = -16384;
Assert.IsTrue(plc.IsConnected, "After connecting, IsConnected must be set to true");
await plc.WriteAsync("DB2.DBW16384", value.ConvertToUshort());
short result2 = ((ushort)await plc.ReadAsync("DB2.DBW16384")).ConvertToShort();
Assert.AreEqual(value, result2, "A short goes from -32767 to 32766");
}
[TestMethod]
public async Task Test_Async_ReadMultipleBytes()
{
Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor.");
bool val = true;
await plc.WriteAsync("DB2.DBX0.5", val);
bool result = (bool)await plc.ReadAsync("DB2.DBX0.5");
Assert.AreEqual(val, result);
ushort val1 = 16384;
await plc.WriteAsync("DB2.DBW16384", val1);
ushort result1 = (ushort)await plc.ReadAsync("DB2.DBW16384");
Assert.AreEqual(val1, result1, "A ushort goes from 0 to 64512");
bool val2 = true;
await plc.WriteAsync("DB2.DBX8192.7", val2);
bool result2 = (bool)await plc.ReadAsync("DB2.DBX8192.7");
Assert.AreEqual(val2, result2);
ushort val3 = 129;
await plc.WriteAsync("DB2.DBW16", val3);
ushort result3 = (ushort)await plc.ReadAsync("DB2.DBW16");
Assert.AreEqual(val3, result3, "A ushort goes from 0 to 64512");
byte[] val4 = new byte[] { 0x12, 0x34 };
await plc.WriteAsync("DB2.DBB2048", val4[0]);
await plc.WriteAsync("DB2.DBB2049", val4[1]);
byte result4b0 = (byte)await plc.ReadAsync("DB2.DBB2048");
byte result4b1 = (byte)await plc.ReadAsync("DB2.DBB2049");
Assert.AreEqual(val4[0], result4b0);
Assert.AreEqual(val4[1], result4b1);
bool val6 = true;
await plc.WriteAsync("DB2.DBX16384.6", val6);
bool result6 = (bool)await plc.ReadAsync("DB2.DBX16384.6");
Assert.AreEqual(val6, result6);
var dataItems = new List<DataItem>()
{
new DataItem
{
Count = 1,
DataType = DataType.DataBlock,
DB = 2,
StartByteAdr = 0,
BitAdr = 5,
VarType = VarType.Bit
}
,new DataItem
{
Count = 1,
DataType = DataType.DataBlock,
DB = 2,
StartByteAdr = 16384,
VarType = VarType.Word
},
new DataItem
{
Count = 1,
DataType = DataType.DataBlock,
DB = 2,
StartByteAdr = 8192,
BitAdr = 7,
VarType = VarType.Bit
},
new DataItem
{
Count = 1,
DataType = DataType.DataBlock,
DB = 2,
StartByteAdr = 16,
VarType = VarType.Word
},
// single byte
new DataItem
{
Count = 1,
DataType = DataType.DataBlock,
DB = 2,
StartByteAdr = 2048,
VarType = VarType.Byte
},
// multiple bytes
new DataItem
{
Count = 2,
DataType = DataType.DataBlock,
DB = 2,
StartByteAdr = 2048,
VarType = VarType.Byte
},
new DataItem
{
Count = 1,
DataType = DataType.DataBlock,
DB = 2,
StartByteAdr = 16384,
BitAdr = 6,
VarType = VarType.Bit
},
};
var dataItemsRes = await plc.ReadMultipleVarsAsync(dataItems);
Assert.AreEqual(val, dataItemsRes[0].Value);
Assert.AreEqual(val1, dataItemsRes[1].Value);
Assert.AreEqual(val2, dataItemsRes[2].Value);
Assert.AreEqual(val3, dataItemsRes[3].Value);
Assert.AreEqual(val4[0], dataItemsRes[4].Value);
Assert.AreEqual(val4[0], ((byte[])dataItemsRes[5].Value)[0]); //dataItem[5].Value should be byte[2]
Assert.AreEqual(val4[1], ((byte[])dataItemsRes[5].Value)[1]);
Assert.AreEqual(val6, dataItemsRes[6].Value);
}
/// <summary>
/// Tests that a read and a write on addresses bigger than 8192 are executed correctly
/// </summary>
[TestMethod]
public async Task Test_Async_WriteAndReadBooleanVariable()
{
Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor.");
// tests when writing true/false
await plc.WriteAsync("DB1.DBX0.0", false);
var boolVariable = (bool)await plc.ReadAsync("DB1.DBX0.0");
Assert.IsFalse(boolVariable);
await plc.WriteAsync("DB1.DBX0.0", true);
boolVariable = (bool)await plc.ReadAsync("DB1.DBX0.0");
Assert.IsTrue(boolVariable);
// tests when writing 0/1
await plc.WriteAsync("DB1.DBX0.0", 0);
boolVariable = (bool)await plc.ReadAsync("DB1.DBX0.0");
Assert.IsFalse(boolVariable);
await plc.WriteAsync("DB1.DBX0.0", 1);
boolVariable = (bool)await plc.ReadAsync("DB1.DBX0.0");
Assert.IsTrue(boolVariable);
await plc.WriteAsync("DB1.DBX0.7", 1);
boolVariable = (bool)await plc.ReadAsync("DB1.DBX0.7");
Assert.IsTrue(boolVariable);
await plc.WriteAsync("DB1.DBX0.7", 0);
boolVariable = (bool)await plc.ReadAsync("DB1.DBX0.7");
Assert.IsFalse(boolVariable);
await plc.WriteAsync("DB1.DBX658.0", 1);
boolVariable = (bool)await plc.ReadAsync("DB1.DBX658.0");
Assert.IsTrue(boolVariable);
await plc.WriteAsync("DB1.DBX658.7", 1);
boolVariable = (bool)await plc.ReadAsync("DB1.DBX658.7");
Assert.IsTrue(boolVariable);
await plc.WriteAsync("DB2.DBX9658.0", 1);
boolVariable = (bool)await plc.ReadAsync("DB2.DBX9658.0");
Assert.IsTrue(boolVariable);
}
[TestMethod]
public async Task Test_Async_ReadClassIgnoresNonPublicSetters()
{
Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor.");
TestClassWithPrivateSetters tc = new TestClassWithPrivateSetters
{
BitVariable00 = true,
BitVariable10 = true,
DIntVariable = -100000,
IntVariable = -15000,
RealVariableDouble = -154.789,
RealVariableFloat = -154.789f,
DWordVariable = 850
};
await plc.WriteClassAsync(tc, DB2);
TestClassWithPrivateSetters tc2 = new TestClassWithPrivateSetters();
// Values that are read from a class are stored inside the class itself, that is passed by reference
var res = await plc.ReadClassAsync(tc2, DB2);
tc = (TestClassWithPrivateSetters)res.Item2;
Assert.AreEqual(tc.BitVariable00, tc2.BitVariable00);
Assert.AreEqual(tc.BitVariable10, tc2.BitVariable10);
Assert.AreEqual(tc.DIntVariable, tc2.DIntVariable);
Assert.AreEqual(tc.IntVariable, tc2.IntVariable);
Assert.AreEqual(tc.RealVariableDouble, tc2.RealVariableDouble, 0.1);
Assert.AreEqual(tc.RealVariableFloat, tc2.RealVariableFloat);
Assert.AreEqual(tc.DWordVariable, tc2.DWordVariable);
Assert.AreEqual(TestClassWithPrivateSetters.PRIVATE_SETTER_VALUE, tc2.PrivateSetterProperty);
Assert.AreEqual(TestClassWithPrivateSetters.PROTECTED_SETTER_VALUE, tc2.ProtectedSetterProperty);
Assert.AreEqual(TestClassWithPrivateSetters.INTERNAL_SETTER_VALUE, tc2.InternalSetterProperty);
Assert.AreEqual(TestClassWithPrivateSetters.JUST_A_GETTER_VALUE, tc2.JustAGetterProperty);
}
[TestMethod]
[ExpectedException(typeof(NullReferenceException))]
public async Task Test_Async_ReadBytesReturnsNullIfPlcIsNotConnected()
{
using (var notConnectedPlc = new Plc(CpuType.S7300, "255.255.255.255", 0, 0))
{
Assert.IsFalse(notConnectedPlc.IsConnected);
TestClass tc = new TestClass();
var res = await notConnectedPlc.ReadClassAsync(tc, DB2);
Assert.Fail();
}
}
[TestMethod]
public async Task Test_Async_ReadClassWithGenericReturnsSameResultAsReadClassWithoutGeneric()
{
Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor.");
TestClass tc = new TestClass
{
BitVariable00 = true,
BitVariable10 = true,
DIntVariable = -100000,
IntVariable = -15000,
RealVariableDouble = -154.789,
RealVariableFloat = -154.789f,
DWordVariable = 850
};
await plc.WriteClassAsync(tc, DB2);
// Values that are read from a class are stored inside the class itself, that is passed by reference
TestClass tc2 = new TestClass();
var res = await plc.ReadClassAsync(tc2, DB2);
tc2 = (TestClass)res.Item2;
TestClass tc2Generic = await plc.ReadClassAsync<TestClass>(DB2);
Assert.AreEqual(tc2.BitVariable00, tc2Generic.BitVariable00);
Assert.AreEqual(tc2.BitVariable10, tc2Generic.BitVariable10);
Assert.AreEqual(tc2.DIntVariable, tc2Generic.DIntVariable);
Assert.AreEqual(tc2.IntVariable, tc2Generic.IntVariable);
Assert.AreEqual(Math.Round(tc2.RealVariableDouble, 3), Math.Round(tc2Generic.RealVariableDouble, 3));
Assert.AreEqual(tc2.RealVariableFloat, tc2Generic.RealVariableFloat);
Assert.AreEqual(tc2.DWordVariable, tc2Generic.DWordVariable);
}
[TestMethod]
[ExpectedException(typeof(NullReferenceException))]
public async Task Test_Async_ReadClassWithGenericReturnsNullIfPlcIsNotConnected()
{
using (var notConnectedPlc = new Plc(CpuType.S7300, "255.255.255.255", 0, 0))
{
Assert.IsFalse(notConnectedPlc.IsConnected, "Before executing this test, the plc must be connected. Check constructor.");
TestClass tc = await notConnectedPlc.ReadClassAsync<TestClass>(DB2);
}
}
[TestMethod]
public async Task Test_Async_ReadClassWithGenericAndClassFactoryReturnsSameResultAsReadClassWithoutGeneric()
{
Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor.");
TestClass tc = new TestClass
{
BitVariable00 = true,
BitVariable10 = true,
DIntVariable = -100000,
IntVariable = -15000,
RealVariableDouble = -154.789,
RealVariableFloat = -154.789f,
DWordVariable = 850
};
await plc.WriteClassAsync(tc, DB2);
// Values that are read from a class are stored inside the class itself, that is passed by reference
TestClass tc2Generic = await plc.ReadClassAsync<TestClass>(DB2);
TestClass tc2GenericWithClassFactory = await plc.ReadClassAsync(() => new TestClass(), DB2);
Assert.AreEqual(tc2Generic.BitVariable00, tc2GenericWithClassFactory.BitVariable00);
Assert.AreEqual(tc2Generic.BitVariable10, tc2GenericWithClassFactory.BitVariable10);
Assert.AreEqual(tc2Generic.DIntVariable, tc2GenericWithClassFactory.DIntVariable);
Assert.AreEqual(Math.Round(tc2Generic.RealVariableDouble, 3), Math.Round(tc2GenericWithClassFactory.RealVariableDouble, 3));
Assert.AreEqual(tc2Generic.RealVariableFloat, tc2GenericWithClassFactory.RealVariableFloat);
Assert.AreEqual(tc2Generic.DWordVariable, tc2GenericWithClassFactory.DWordVariable);
}
[TestMethod]
[ExpectedException(typeof(NullReferenceException))]
public async Task Test_Async_ReadClassWithGenericAndClassFactoryThrowsExceptionPlcIsNotConnected()
{
using (var notConnectedPlc = new Plc(CpuType.S7300, "255.255.255.255", 0, 0))
{
Assert.IsFalse(notConnectedPlc.IsConnected);
TestClass tc = await notConnectedPlc.ReadClassAsync(() => new TestClass(), DB2);
}
}
[TestMethod]
[ExpectedException(typeof(NullReferenceException))]
public async Task Test_Async_ReadStructThrowsExceptionPlcIsNotConnected()
{
using (var notConnectedPlc = new Plc(CpuType.S7300, "255.255.255.255", 0, 0))
{
Assert.IsFalse(notConnectedPlc.IsConnected);
object tsObj = await notConnectedPlc.ReadStructAsync(typeof(TestStruct), DB2);
}
}
[TestMethod]
public async Task Test_Async_ReadStructWithGenericReturnsSameResultAsReadStructWithoutGeneric()
{
Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor.");
TestStruct ts = new TestStruct
{
BitVariable00 = true,
BitVariable10 = true,
DIntVariable = -100000,
IntVariable = -15000,
RealVariableDouble = -154.789,
RealVariableFloat = -154.789f,
DWordVariable = 850
};
plc.WriteStruct(ts, DB2);
// Values that are read from a struct are stored in a new struct, returned by the funcion ReadStruct
TestStruct ts2 = (TestStruct)await plc.ReadStructAsync(typeof(TestStruct), DB2);
var test = await plc.ReadStructAsync<TestStruct>(DB2);
TestStruct ts2Generic = test.Value;
Assert.AreEqual(ts2.BitVariable00, ts2Generic.BitVariable00);
Assert.AreEqual(ts2.BitVariable10, ts2Generic.BitVariable10);
Assert.AreEqual(ts2.DIntVariable, ts2Generic.DIntVariable);
Assert.AreEqual(ts2.IntVariable, ts2Generic.IntVariable);
Assert.AreEqual(Math.Round(ts2.RealVariableDouble, 3), Math.Round(ts2Generic.RealVariableDouble, 3));
Assert.AreEqual(ts2.RealVariableFloat, ts2Generic.RealVariableFloat);
Assert.AreEqual(ts2.DWordVariable, ts2Generic.DWordVariable);
}
[TestMethod]
[ExpectedException(typeof(NullReferenceException))]
public async Task Test_Async_ReadStructWithGenericThrowsExceptionIfPlcIsNotConnected()
{
using (var notConnectedPlc = new Plc(CpuType.S7300, "255.255.255.255", 0, 0))
{
Assert.IsFalse(notConnectedPlc.IsConnected);
object tsObj = await notConnectedPlc.ReadStructAsync<TestStruct>(DB2);
}
}
/// <summary>
/// Tests that the method ReadClass returns the number of bytes read from the plc
/// </summary>
[TestMethod]
public async Task Test_Async_ReadClassReturnsNumberOfReadBytesFromThePlc()
{
Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor.");
TestClass tc = new TestClass
{
BitVariable00 = true,
BitVariable10 = true,
DIntVariable = -100000,
IntVariable = -15000,
RealVariableDouble = -154.789,
RealVariableFloat = -154.789f,
DWordVariable = 850
};
plc.WriteClass(tc, DB2);
int expectedReadBytes = Types.Class.GetClassSize(tc);
TestClass tc2 = new TestClass();
// Values that are read from a class are stored inside the class itself, that is passed by reference
var res = await plc.ReadClassAsync(tc2, DB2);
int actualReadBytes = res.Item1;
Assert.AreEqual(expectedReadBytes, actualReadBytes);
}
[TestMethod]
public async Task Test_Async_ReadClassWithArray()
{
Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor.");
TestClassWithArrays tc = new TestClassWithArrays
{
Bool = true
};
tc.BoolValues[1] = true;
tc.Int = int.MinValue;
tc.Ints[0] = int.MinValue;
tc.Ints[1] = int.MaxValue;
tc.Short = short.MinValue;
tc.Shorts[0] = short.MinValue;
tc.Shorts[1] = short.MaxValue;
tc.Double = float.MinValue;
tc.Doubles[0] = float.MinValue + 1;
tc.Doubles[1] = float.MaxValue;
tc.UShort = ushort.MinValue + 1;
tc.UShorts[0] = ushort.MinValue + 1;
tc.UShorts[1] = ushort.MaxValue;
plc.WriteClass(tc, DB2);
TestClassWithArrays tc2 = await plc.ReadClassAsync<TestClassWithArrays>(DB2);
Assert.AreEqual(tc.Bool, tc2.Bool);
Assert.AreEqual(tc.BoolValues[0], tc2.BoolValues[0]);
Assert.AreEqual(tc.BoolValues[1], tc2.BoolValues[1]);
Assert.AreEqual(tc.Int, tc2.Int);
Assert.AreEqual(tc.Ints[0], tc2.Ints[0]);
Assert.AreEqual(tc.Ints[1], tc.Ints[1]);
Assert.AreEqual(tc.Short, tc2.Short);
Assert.AreEqual(tc.Shorts[0], tc2.Shorts[0]);
Assert.AreEqual(tc.Shorts[1], tc2.Shorts[1]);
Assert.AreEqual(tc.Double, tc2.Double);
Assert.AreEqual(tc.Doubles[0], tc2.Doubles[0]);
Assert.AreEqual(tc.Doubles[1], tc2.Doubles[1]);
Assert.AreEqual(tc.UShort, tc2.UShort);
Assert.AreEqual(tc.UShorts[0], tc2.UShorts[0]);
Assert.AreEqual(tc.UShorts[1], tc2.UShorts[1]);
}
[TestMethod]
public async Task Test_Async_ReadClassWithArrayAndCustomType()
{
Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor.");
TestClassWithCustomType tc = new TestClassWithCustomType
{
Int = int.MinValue,
CustomType = new CustomType()
};
tc.CustomType.Bools[1] = true;
tc.CustomTypes[0] = new CustomType();
tc.CustomTypes[1] = new CustomType();
tc.CustomTypes[0].Bools[0] = true;
tc.CustomTypes[1].Bools[1] = true;
plc.WriteClass(tc, DB2);
TestClassWithCustomType tc2 = await plc.ReadClassAsync<TestClassWithCustomType>(DB2);
Assert.AreEqual(tc.Int, tc2.Int);
Assert.AreEqual(tc.CustomType.Bools[0], tc2.CustomType.Bools[0]);
Assert.AreEqual(tc.CustomType.Bools[1], tc2.CustomType.Bools[1]);
Assert.AreEqual(tc.CustomTypes[0].Bools[0], tc2.CustomTypes[0].Bools[0]);
Assert.AreEqual(tc.CustomTypes[0].Bools[1], tc2.CustomTypes[0].Bools[1]);
Assert.AreEqual(tc.CustomTypes[1].Bools[0], tc2.CustomTypes[1].Bools[0]);
Assert.AreEqual(tc.CustomTypes[1].Bools[1], tc2.CustomTypes[1].Bools[1]);
}
[TestMethod]
public async Task Test_Async_ReadWriteDouble()
{
double test_value = 55.66;
await plc.WriteAsync("DB1.DBD0", test_value);
var helper = await plc.ReadAsync("DB1.DBD0");
double test_value2 = Conversion.ConvertToDouble((uint)helper);
Assert.AreEqual(test_value, test_value2, 0.01, "Compare Write/Read"); //Need delta here because S7 only has 32 bit reals
}
[TestMethod]
public async Task Test_Async_ReadWriteSingle()
{
float test_value = 55.6632f;
await plc.WriteAsync("DB1.DBD0", test_value);
var helper = await plc.ReadAsync("DB1.DBD0");
float test_value2 = Conversion.ConvertToFloat((uint)helper);
Assert.AreEqual(test_value, test_value2, "Compare Write/Read"); //No delta, datatype matches
}
[TestMethod]
public async Task Test_Async_ReadWriteBytesMany()
{
Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor.");
var count = 2000;
var dataItems = new List<byte>();
for (int i = 0; i < count; i++)
{
dataItems.Add((byte)(i % 256));
}
await plc.WriteBytesAsync(DataType.DataBlock, 2, 0, dataItems.ToArray());
var res = await plc.ReadBytesAsync(DataType.DataBlock, 2, 0, count);
for (int x = 0; x < count; x++)
{
Assert.AreEqual(x % 256, res[x], string.Format("Bit {0} failed", x));
}
}
#endregion
}
}

View File

@@ -30,10 +30,13 @@ using S7.UnitTest.Helpers;
* server has problems.
*
*/
//This file contains tests for the synchronous methods
#pragma warning disable CS0618
namespace S7.Net.UnitTest
{
[TestClass]
public class S7NetTests
public partial class S7NetTests : IDisposable
{
#region Constants
const int DB2 = 2;
@@ -78,8 +81,14 @@ namespace S7.Net.UnitTest
{
if (plc.IsConnected == false)
{
var error = plc.Open();
Assert.AreEqual(ErrorCode.NoError, error, "If you have s7 installed you must close s7oiehsx64 service.");
try
{
plc.Open();
}
catch (Exception e)
{
throw new Exception("If you have s7 installed you must close s7oiehsx64 service.", e);
}
}
}
@@ -145,10 +154,22 @@ namespace S7.Net.UnitTest
// Reading and writing a double is quite complicated, because it needs to be converted to DWord before the write,
// then reconvert to double after the read.
double val = 35.687;
double val = 35.68729;
plc.Write("DB1.DBD40", val.ConvertToUInt());
double result = ((uint)plc.Read("DB1.DBD40")).ConvertToDouble();
Assert.AreEqual(val, Math.Round(result, 3)); // float lose precision, so i need to round it
Assert.AreEqual(val, Math.Round(result, 5)); // float lose precision, so i need to round it
// Reading and writing a float is quite complicated, because it needs to be converted to DWord before the write,
// then reconvert to float after the read. Float values can contain only 7 digits, so no precision is lost.
float val2 = 1234567;
plc.Write("DB1.DBD40", val2.ConvertToUInt());
float result2 = ((uint)plc.Read("DB1.DBD40")).ConvertToFloat();
Assert.AreEqual(val2, result2);
float val3 = 12.34567f;
plc.Write("DB1.DBD40", val3.ConvertToUInt());
float result3 = ((uint)plc.Read("DB1.DBD40")).ConvertToFloat();
Assert.AreEqual(val3, result3);
}
/// <summary>
@@ -164,7 +185,8 @@ namespace S7.Net.UnitTest
tc.BitVariable10 = true;
tc.DIntVariable = -100000;
tc.IntVariable = -15000;
tc.RealVariable = -154.789;
tc.RealVariableDouble = -154.789;
tc.RealVariableFloat = -154.789f;
tc.DWordVariable = 850;
plc.WriteClass(tc, DB2);
TestClass tc2 = new TestClass();
@@ -174,7 +196,8 @@ namespace S7.Net.UnitTest
Assert.AreEqual(tc.BitVariable10, tc2.BitVariable10);
Assert.AreEqual(tc.DIntVariable, tc2.DIntVariable);
Assert.AreEqual(tc.IntVariable, tc2.IntVariable);
Assert.AreEqual(tc.RealVariable, Math.Round(tc2.RealVariable, 3));
Assert.AreEqual(Math.Round(tc.RealVariableDouble, 3), Math.Round(tc2.RealVariableDouble, 3));
Assert.AreEqual(tc.RealVariableFloat, tc2.RealVariableFloat);
Assert.AreEqual(tc.DWordVariable, tc2.DWordVariable);
}
@@ -191,7 +214,8 @@ namespace S7.Net.UnitTest
tc.BitVariable10 = true;
tc.DIntVariable = -100000;
tc.IntVariable = -15000;
tc.RealVariable = -154.789;
tc.RealVariableDouble = -154.789;
tc.RealVariableFloat = -154.789f;
tc.DWordVariable = 850;
plc.WriteStruct(tc, DB2);
// Values that are read from a struct are stored in a new struct, returned by the funcion ReadStruct
@@ -200,7 +224,8 @@ namespace S7.Net.UnitTest
Assert.AreEqual(tc.BitVariable10, tc2.BitVariable10);
Assert.AreEqual(tc.DIntVariable, tc2.DIntVariable);
Assert.AreEqual(tc.IntVariable, tc2.IntVariable);
Assert.AreEqual(tc.RealVariable, Math.Round(tc2.RealVariable, 3));
Assert.AreEqual(tc.RealVariableDouble, Math.Round(tc2.RealVariableDouble, 3));
Assert.AreEqual(tc.RealVariableFloat, tc2.RealVariableFloat);
Assert.AreEqual(tc.DWordVariable, tc2.DWordVariable);
}
@@ -238,30 +263,28 @@ namespace S7.Net.UnitTest
tc.IntVariable110 = 200;
tc.IntVariable111 = 201;
plc.WriteStruct(tc, DB2);
Assert.AreEqual(ErrorCode.NoError, plc.LastErrorCode);
// Values that are read from a struct are stored in a new struct, returned by the funcion ReadStruct
TestLongStruct tc2 = (TestLongStruct)plc.ReadStruct(typeof(TestLongStruct), DB2);
Assert.AreEqual(ErrorCode.NoError, plc.LastErrorCode);
Assert.AreEqual( tc.IntVariable0, tc2.IntVariable0 );
Assert.AreEqual( tc.IntVariable1, tc2.IntVariable1 );
Assert.AreEqual( tc.IntVariable10, tc2.IntVariable10);
Assert.AreEqual( tc.IntVariable11, tc2.IntVariable11);
Assert.AreEqual( tc.IntVariable20, tc2.IntVariable20);
Assert.AreEqual( tc.IntVariable21, tc2.IntVariable21);
Assert.AreEqual( tc.IntVariable30, tc2.IntVariable30);
Assert.AreEqual( tc.IntVariable31, tc2.IntVariable31);
Assert.AreEqual( tc.IntVariable40, tc2.IntVariable40);
Assert.AreEqual( tc.IntVariable41, tc2.IntVariable41);
Assert.AreEqual( tc.IntVariable50, tc2.IntVariable50);
Assert.AreEqual( tc.IntVariable51, tc2.IntVariable51);
Assert.AreEqual( tc.IntVariable60, tc2.IntVariable60);
Assert.AreEqual( tc.IntVariable61, tc2.IntVariable61);
Assert.AreEqual( tc.IntVariable70, tc2.IntVariable70);
Assert.AreEqual( tc.IntVariable71, tc2.IntVariable71);
Assert.AreEqual( tc.IntVariable80, tc2.IntVariable80);
Assert.AreEqual( tc.IntVariable81, tc2.IntVariable81);
Assert.AreEqual( tc.IntVariable90, tc2.IntVariable90);
Assert.AreEqual(tc.IntVariable91, tc2.IntVariable91);
Assert.AreEqual(tc.IntVariable0, tc2.IntVariable0);
Assert.AreEqual(tc.IntVariable1, tc2.IntVariable1);
Assert.AreEqual(tc.IntVariable10, tc2.IntVariable10);
Assert.AreEqual(tc.IntVariable11, tc2.IntVariable11);
Assert.AreEqual(tc.IntVariable20, tc2.IntVariable20);
Assert.AreEqual(tc.IntVariable21, tc2.IntVariable21);
Assert.AreEqual(tc.IntVariable30, tc2.IntVariable30);
Assert.AreEqual(tc.IntVariable31, tc2.IntVariable31);
Assert.AreEqual(tc.IntVariable40, tc2.IntVariable40);
Assert.AreEqual(tc.IntVariable41, tc2.IntVariable41);
Assert.AreEqual(tc.IntVariable50, tc2.IntVariable50);
Assert.AreEqual(tc.IntVariable51, tc2.IntVariable51);
Assert.AreEqual(tc.IntVariable60, tc2.IntVariable60);
Assert.AreEqual(tc.IntVariable61, tc2.IntVariable61);
Assert.AreEqual(tc.IntVariable70, tc2.IntVariable70);
Assert.AreEqual(tc.IntVariable71, tc2.IntVariable71);
Assert.AreEqual(tc.IntVariable80, tc2.IntVariable80);
Assert.AreEqual(tc.IntVariable81, tc2.IntVariable81);
Assert.AreEqual(tc.IntVariable90, tc2.IntVariable90);
Assert.AreEqual(tc.IntVariable91, tc2.IntVariable91);
Assert.AreEqual(tc.IntVariable100, tc2.IntVariable100);
Assert.AreEqual(tc.IntVariable101, tc2.IntVariable101);
Assert.AreEqual(tc.IntVariable110, tc2.IntVariable110);
@@ -302,11 +325,9 @@ namespace S7.Net.UnitTest
tc.IntVariable110 = 200;
tc.IntVariable111 = 201;
plc.WriteClass(tc, DB2);
Assert.AreEqual(ErrorCode.NoError, plc.LastErrorCode);
// Values that are read from a struct are stored in a new struct, returned by the funcion ReadStruct
TestLongClass tc2 = new TestLongClass();
plc.ReadClass(tc2, DB2);
Assert.AreEqual(ErrorCode.NoError, plc.LastErrorCode);
Assert.AreEqual(tc.IntVariable0, tc2.IntVariable0);
Assert.AreEqual(tc.IntVariable1, tc2.IntVariable1);
Assert.AreEqual(tc.IntVariable10, tc2.IntVariable10);
@@ -386,19 +407,51 @@ namespace S7.Net.UnitTest
{
Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor.");
ushort val = 16384;
plc.Write("DB2.DBW16384", val);
ushort result = (ushort)plc.Read("DB2.DBW16384");
Assert.AreEqual(val, result, "A ushort goes from 0 to 64512");
bool val = true;
plc.Write("DB2.DBX0.5", val);
bool result = (bool)plc.Read("DB2.DBX0.5");
Assert.AreEqual(val, result);
ushort val2 = 129;
plc.Write("DB2.DBW16", val2);
ushort result2 = (ushort)plc.Read("DB2.DBW16");
Assert.AreEqual(val2, result2, "A ushort goes from 0 to 64512");
ushort val1 = 16384;
plc.Write("DB2.DBW16384", val1);
ushort result1 = (ushort)plc.Read("DB2.DBW16384");
Assert.AreEqual(val1, result1, "A ushort goes from 0 to 64512");
bool val2 = true;
plc.Write("DB2.DBX8192.7", val2);
bool result2 = (bool)plc.Read("DB2.DBX8192.7");
Assert.AreEqual(val2, result2);
ushort val3 = 129;
plc.Write("DB2.DBW16", val3);
ushort result3 = (ushort)plc.Read("DB2.DBW16");
Assert.AreEqual(val3, result3, "A ushort goes from 0 to 64512");
byte[] val4 = new byte[] { 0x12, 0x34 };
plc.Write("DB2.DBB2048", val4[0]);
plc.Write("DB2.DBB2049", val4[1]);
byte result4b0 = (byte)plc.Read("DB2.DBB2048");
byte result4b1 = (byte)plc.Read("DB2.DBB2049");
Assert.AreEqual(val4[0], result4b0);
Assert.AreEqual(val4[1], result4b1);
bool val6 = true;
plc.Write("DB2.DBX16384.6", val6);
bool result6 = (bool)plc.Read("DB2.DBX16384.6");
Assert.AreEqual(val6, result6);
var dataItems = new List<DataItem>()
{
new DataItem
{
Count = 1,
DataType = DataType.DataBlock,
DB = 2,
StartByteAdr = 0,
BitAdr = 5,
VarType = VarType.Bit
}
,new DataItem
{
Count = 1,
DataType = DataType.DataBlock,
@@ -407,19 +460,61 @@ namespace S7.Net.UnitTest
VarType = VarType.Word
},
new DataItem
{
Count = 1,
DataType = DataType.DataBlock,
DB = 2,
StartByteAdr = 8192,
BitAdr = 7,
VarType = VarType.Bit
},
new DataItem
{
Count = 1,
DataType = DataType.DataBlock,
DB = 2,
StartByteAdr = 16,
VarType = VarType.Word
}
},
// single byte
new DataItem
{
Count = 1,
DataType = DataType.DataBlock,
DB = 2,
StartByteAdr = 2048,
VarType = VarType.Byte
},
// multiple bytes
new DataItem
{
Count = 2,
DataType = DataType.DataBlock,
DB = 2,
StartByteAdr = 2048,
VarType = VarType.Byte
},
new DataItem
{
Count = 1,
DataType = DataType.DataBlock,
DB = 2,
StartByteAdr = 16384,
BitAdr = 6,
VarType = VarType.Bit
},
};
plc.ReadMultipleVars(dataItems);
Assert.AreEqual(dataItems[0].Value, val);
Assert.AreEqual(dataItems[1].Value, val2);
Assert.AreEqual(dataItems[1].Value, val1);
Assert.AreEqual(dataItems[2].Value, val2);
Assert.AreEqual(dataItems[3].Value, val3);
Assert.AreEqual(dataItems[4].Value, val4[0]);
Assert.AreEqual(((byte[])dataItems[5].Value)[0], val4[0]); //dataItem[5].Value should be byte[2]
Assert.AreEqual(((byte[])dataItems[5].Value)[1], val4[1]);
Assert.AreEqual(dataItems[6].Value, val6);
}
/// <summary>
@@ -479,7 +574,8 @@ namespace S7.Net.UnitTest
tc.BitVariable10 = true;
tc.DIntVariable = -100000;
tc.IntVariable = -15000;
tc.RealVariable = -154.789;
tc.RealVariableDouble = -154.789;
tc.RealVariableFloat = -154.789f;
tc.DWordVariable = 850;
plc.WriteClass(tc, DB2);
@@ -491,7 +587,8 @@ namespace S7.Net.UnitTest
Assert.AreEqual(tc.BitVariable10, tc2.BitVariable10);
Assert.AreEqual(tc.DIntVariable, tc2.DIntVariable);
Assert.AreEqual(tc.IntVariable, tc2.IntVariable);
Assert.AreEqual(tc.RealVariable, Math.Round(tc2.RealVariable, 3));
Assert.AreEqual(Math.Round(tc.RealVariableDouble, 3), Math.Round(tc2.RealVariableDouble, 3));
Assert.AreEqual(tc.RealVariableFloat, tc2.RealVariableFloat);
Assert.AreEqual(tc.DWordVariable, tc2.DWordVariable);
Assert.AreEqual(TestClassWithPrivateSetters.PRIVATE_SETTER_VALUE, tc2.PrivateSetterProperty);
@@ -501,19 +598,14 @@ namespace S7.Net.UnitTest
}
[TestMethod]
public void T13_ReadBytesReturnsEmptyArrayIfPlcIsNotConnected()
[TestMethod, ExpectedException(typeof(PlcException))]
public void T13_ReadBytesThrowsIfPlcIsNotConnected()
{
using (var notConnectedPlc = new Plc(CpuType.S7300, "255.255.255.255", 0, 0))
{
Assert.IsFalse(notConnectedPlc.IsConnected);
int expectedReadBytes = 0; // 0 bytes, because no connection was established
TestClass tc = new TestClass();
int actualReadBytes = notConnectedPlc.ReadClass(tc, DB2);
Assert.AreEqual(expectedReadBytes, actualReadBytes);
}
}
@@ -527,7 +619,8 @@ namespace S7.Net.UnitTest
tc.BitVariable10 = true;
tc.DIntVariable = -100000;
tc.IntVariable = -15000;
tc.RealVariable = -154.789;
tc.RealVariableDouble = -154.789;
tc.RealVariableFloat = -154.789f;
tc.DWordVariable = 850;
plc.WriteClass(tc, DB2);
@@ -541,12 +634,13 @@ namespace S7.Net.UnitTest
Assert.AreEqual(tc2.BitVariable10, tc2Generic.BitVariable10);
Assert.AreEqual(tc2.DIntVariable, tc2Generic.DIntVariable);
Assert.AreEqual(tc2.IntVariable, tc2Generic.IntVariable);
Assert.AreEqual(Math.Round(tc2.RealVariable, 3), Math.Round(tc2Generic.RealVariable, 3));
Assert.AreEqual(Math.Round(tc2.RealVariableDouble, 3), Math.Round(tc2Generic.RealVariableDouble, 3));
Assert.AreEqual(tc2.RealVariableFloat, tc2Generic.RealVariableFloat);
Assert.AreEqual(tc2.DWordVariable, tc2Generic.DWordVariable);
}
[TestMethod]
public void T15_ReadClassWithGenericReturnsNullIfPlcIsNotConnected()
[TestMethod, ExpectedException(typeof(PlcException))]
public void T15_ReadClassWithGenericThrowsIfPlcIsNotConnected()
{
using (var notConnectedPlc = new Plc(CpuType.S7300, "255.255.255.255", 0, 0))
{
@@ -568,7 +662,8 @@ namespace S7.Net.UnitTest
tc.BitVariable10 = true;
tc.DIntVariable = -100000;
tc.IntVariable = -15000;
tc.RealVariable = -154.789;
tc.RealVariableDouble = -154.789;
tc.RealVariableFloat = -154.789f;
tc.DWordVariable = 850;
plc.WriteClass(tc, DB2);
@@ -581,12 +676,13 @@ namespace S7.Net.UnitTest
Assert.AreEqual(tc2Generic.BitVariable10, tc2GenericWithClassFactory.BitVariable10);
Assert.AreEqual(tc2Generic.DIntVariable, tc2GenericWithClassFactory.DIntVariable);
Assert.AreEqual(tc2Generic.IntVariable, tc2GenericWithClassFactory.IntVariable);
Assert.AreEqual(Math.Round(tc2Generic.RealVariable, 3), Math.Round(tc2GenericWithClassFactory.RealVariable, 3));
Assert.AreEqual(Math.Round(tc2Generic.RealVariableDouble, 3), Math.Round(tc2GenericWithClassFactory.RealVariableDouble, 3));
Assert.AreEqual(tc2Generic.RealVariableFloat, tc2GenericWithClassFactory.RealVariableFloat);
Assert.AreEqual(tc2Generic.DWordVariable, tc2GenericWithClassFactory.DWordVariable);
}
[TestMethod]
public void T17_ReadClassWithGenericAndClassFactoryReturnsNullIfPlcIsNotConnected()
[TestMethod, ExpectedException(typeof(PlcException))]
public void T17_ReadClassWithGenericAndClassFactoryThrowsIfPlcIsNotConnected()
{
using (var notConnectedPlc = new Plc(CpuType.S7300, "255.255.255.255", 0, 0))
{
@@ -598,8 +694,8 @@ namespace S7.Net.UnitTest
}
}
[TestMethod]
public void T18_ReadStructReturnsNullIfPlcIsNotConnected()
[TestMethod, ExpectedException(typeof(PlcException))]
public void T18_ReadStructThrowsIfPlcIsNotConnected()
{
using (var notConnectedPlc = new Plc(CpuType.S7300, "255.255.255.255", 0, 0))
{
@@ -621,7 +717,8 @@ namespace S7.Net.UnitTest
ts.BitVariable10 = true;
ts.DIntVariable = -100000;
ts.IntVariable = -15000;
ts.RealVariable = -154.789;
ts.RealVariableDouble = -154.789;
ts.RealVariableFloat = -154.789f;
ts.DWordVariable = 850;
plc.WriteStruct(ts, DB2);
@@ -634,12 +731,13 @@ namespace S7.Net.UnitTest
Assert.AreEqual(ts2.BitVariable10, ts2Generic.BitVariable10);
Assert.AreEqual(ts2.DIntVariable, ts2Generic.DIntVariable);
Assert.AreEqual(ts2.IntVariable, ts2Generic.IntVariable);
Assert.AreEqual(Math.Round(ts2.RealVariable, 3), Math.Round(ts2Generic.RealVariable, 3));
Assert.AreEqual(Math.Round(ts2.RealVariableDouble, 3), Math.Round(ts2Generic.RealVariableDouble, 3));
Assert.AreEqual(ts2.RealVariableFloat, ts2Generic.RealVariableFloat);
Assert.AreEqual(ts2.DWordVariable, ts2Generic.DWordVariable);
}
[TestMethod]
public void T20_ReadStructWithGenericReturnsNullIfPlcIsNotConnected()
[TestMethod, ExpectedException(typeof(PlcException))]
public void T20_ReadStructThrowsIfPlcIsNotConnected()
{
using (var notConnectedPlc = new Plc(CpuType.S7300, "255.255.255.255", 0, 0))
{
@@ -664,7 +762,8 @@ namespace S7.Net.UnitTest
tc.BitVariable10 = true;
tc.DIntVariable = -100000;
tc.IntVariable = -15000;
tc.RealVariable = -154.789;
tc.RealVariableDouble = -154.789;
tc.RealVariableFloat = -154.789f;
tc.DWordVariable = 850;
plc.WriteClass(tc, DB2);
@@ -676,7 +775,7 @@ namespace S7.Net.UnitTest
Assert.AreEqual(expectedReadBytes, actualReadBytes);
}
[TestMethod]
public void T22_ReadClassWithArray()
{
@@ -694,6 +793,9 @@ namespace S7.Net.UnitTest
tc.Double = float.MinValue;
tc.Doubles[0] = float.MinValue + 1;
tc.Doubles[1] = float.MaxValue;
tc.Single = float.MinValue;
tc.Singles[0] = float.MinValue + 1;
tc.Singles[1] = float.MaxValue;
tc.UShort = ushort.MinValue + 1;
tc.UShorts[0] = ushort.MinValue + 1;
tc.UShorts[1] = ushort.MaxValue;
@@ -717,6 +819,10 @@ namespace S7.Net.UnitTest
Assert.AreEqual(tc.Doubles[0], tc2.Doubles[0]);
Assert.AreEqual(tc.Doubles[1], tc2.Doubles[1]);
Assert.AreEqual(tc.Single, tc2.Single);
Assert.AreEqual(tc.Singles[0], tc2.Singles[0]);
Assert.AreEqual(tc.Singles[1], tc2.Singles[1]);
Assert.AreEqual(tc.UShort, tc2.UShort);
Assert.AreEqual(tc.UShorts[0], tc2.UShorts[0]);
Assert.AreEqual(tc.UShorts[1], tc2.UShorts[1]);
@@ -769,25 +875,129 @@ namespace S7.Net.UnitTest
Assert.IsTrue(reachablePlc.IsAvailable);
}
#endregion
#region Private methods
private static void ShutDownServiceS7oiehsx64()
[TestMethod]
public void T26_ReadWriteDouble()
{
try
double test_value = 55.66;
plc.Write("DB1.DBD0", test_value);
var helper = plc.Read("DB1.DBD0");
double test_value2 = Conversion.ConvertToDouble((uint)helper);
Assert.AreEqual(test_value, test_value2, 0.01, "Compare Write/Read"); //Need delta here because S7 only has 32 bit reals
}
[TestMethod]
public void T27_ReadWriteBytesMany()
{
Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor.");
var count = 2000;
var dataItems = new List<byte>();
for (int i = 0; i < count; i++)
{
ServiceController sc = new ServiceController("s7oiehsx64");
switch (sc.Status)
{
case ServiceControllerStatus.Running:
sc.Stop();
break;
}
dataItems.Add((byte)(i % 256));
}
catch { } // service not found
plc.WriteBytes(DataType.DataBlock, 2, 0, dataItems.ToArray());
var res = plc.ReadBytes(DataType.DataBlock, 2, 0, count);
for (int x = 0; x < count; x++)
{
Assert.AreEqual(x % 256, res[x], $"Mismatch at offset {x}, expected {x % 256}, actual {res[x]}.");
}
}
[TestMethod]
public void T28_ReadClass_DoesntCrash_When_ReadingLessThan1Byte()
{
Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor.");
var tc = new TestSmallClass
{
Bool1 = true
};
plc.WriteClass(tc, DB2);
var tc2 = plc.ReadClass<TestSmallClass>(DB2);
Assert.AreEqual(tc.Bool1, tc2.Bool1);
}
[TestMethod, ExpectedException(typeof(PlcException))]
public void T29_Read_Write_ThrowsWhenPlcIsNotReachable()
{
// leave plc Open
S7TestServer.Stop();
double test_value = 55.66;
plc.Write("DB1.DBD0", test_value);
var helper = plc.Read("DB1.DBD0");
Assert.AreEqual(helper, null, "Value in Read.");
}
[TestMethod]
public void T30_ReadWriteSingle()
{
float test_value = 55.6632f;
plc.Write("DB1.DBD0", test_value);
var helper = plc.Read("DB1.DBD0");
float test_value2 = Conversion.ConvertToFloat((uint)helper);
Assert.AreEqual(test_value, test_value2, "Compare Write/Read"); //No delta, datatype matches
}
#endregion
#region Private methods
private static void ShutDownServiceS7oiehsx64()
{
ServiceController[] services = ServiceController.GetServices();
var service = services.FirstOrDefault(s => s.ServiceName == "s7oiehsx64");
if (service != null)
{
if (service.Status == ServiceControllerStatus.Running)
{
service.Stop();
}
}
}
#region IDisposable Support
private bool disposedValue = false; // To detect redundant calls
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
plc.Close();
}
// TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
// TODO: set large fields to null.
disposedValue = true;
}
}
// TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
// ~S7NetTests() {
// // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
// Dispose(false);
// }
// This code added to correctly implement the disposable pattern.
public void Dispose()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(true);
// TODO: uncomment the following line if the finalizer is overridden above.
// GC.SuppressFinalize(this);
}
#endregion
#endregion
}
}

View File

@@ -0,0 +1,115 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using S7.Net.Types;
namespace S7.Net.UnitTest.TypeTests
{
[TestClass]
public class StringExTests
{
[TestMethod]
public void ReadEmptyStringWithZeroByteLength()
{
AssertFromByteArrayEquals("", 0, 0);
}
[TestMethod]
public void ReadEmptyStringWithOneByteLength()
{
AssertFromByteArrayEquals("", 1, 0, 0);
}
[TestMethod]
public void ReadEmptyStringWithOneByteGarbage()
{
AssertFromByteArrayEquals("", 1, 0, (byte) 'a');
}
[TestMethod]
public void ReadA()
{
AssertFromByteArrayEquals("A", 1, 1, (byte) 'A');
}
[TestMethod]
public void ReadAbc()
{
AssertFromByteArrayEquals("Abc", 1, 3, (byte) 'A', (byte) 'b', (byte) 'c');
}
[TestMethod]
public void WriteNullWithReservedLengthZero()
{
AssertToByteArrayEquals(null, 0, 0, 0);
}
[TestMethod]
public void WriteEmptyStringWithReservedLengthZero()
{
AssertToByteArrayEquals("", 0, 0, 0);
}
[TestMethod]
public void WriteAWithReservedLengthZero()
{
AssertToByteArrayEquals("A", 0, 0, 0);
}
[TestMethod]
public void WriteNullWithReservedLengthOne()
{
AssertToByteArrayEquals(null, 1, 1, 0);
}
[TestMethod]
public void WriteEmptyStringWithReservedLengthOne()
{
AssertToByteArrayEquals("", 1, 1, 0);
}
[TestMethod]
public void WriteAWithReservedLengthOne()
{
AssertToByteArrayEquals("A", 1, 1, 1, (byte) 'A');
}
[TestMethod]
public void WriteAWithReservedLengthTwo()
{
AssertToByteArrayEquals("A", 2, 2, 1, (byte) 'A');
}
[TestMethod]
public void WriteAbcWithReservedLengthOne()
{
AssertToByteArrayEquals("Abc", 1, 1, 1, (byte) 'A');
}
[TestMethod]
public void WriteAbcWithReservedLengthTwo()
{
AssertToByteArrayEquals("Abc", 2, 2, 2, (byte) 'A', (byte) 'b');
}
[TestMethod]
public void WriteAbcWithReservedLengthThree()
{
AssertToByteArrayEquals("Abc", 3, 3, 3, (byte) 'A', (byte) 'b', (byte) 'c');
}
[TestMethod]
public void WriteAbcWithReservedLengthFour()
{
AssertToByteArrayEquals("Abc", 4, 4, 3, (byte) 'A', (byte) 'b', (byte) 'c');
}
private static void AssertFromByteArrayEquals(string expected, params byte[] bytes)
{
Assert.AreEqual(expected, StringEx.FromByteArray(bytes));
}
private static void AssertToByteArrayEquals(string value, int reservedLength, params byte[] expected)
{
CollectionAssert.AreEqual(expected, StringEx.ToByteArray(value, reservedLength));
}
}
}

View File

@@ -0,0 +1,85 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using S7.Net.Types;
namespace S7.Net.UnitTest.TypeTests
{
[TestClass]
public class StringTests
{
[TestMethod]
public void WriteNullWIthReservedLengthZero()
{
AssertToByteArrayEquals(null, 0);
}
[TestMethod]
public void WriteEmptyStringWithReservedLengthZero()
{
AssertToByteArrayEquals("", 0);
}
[TestMethod]
public void WriteAWithReservedLengthZero()
{
AssertToByteArrayEquals("A", 0);
}
[TestMethod]
public void WriteNullWithReservedLengthOne()
{
AssertToByteArrayEquals(null, 1, 0);
}
[TestMethod]
public void WriteEmptyStringWithReservedLengthOne()
{
AssertToByteArrayEquals("", 1, 0);
}
[TestMethod]
public void WriteAWithReservedLengthOne()
{
AssertToByteArrayEquals("A", 1, (byte) 'A');
}
[TestMethod]
public void WriteAWithReservedLengthTwo()
{
AssertToByteArrayEquals("A", 2, (byte) 'A', 0);
}
[TestMethod]
public void WriteAbcWithReservedLengthOne()
{
AssertToByteArrayEquals("Abc", 1, (byte) 'A');
}
[TestMethod]
public void WriteAbcWithReservedLengthTwo()
{
AssertToByteArrayEquals("Abc", 2, (byte) 'A', (byte) 'b');
}
[TestMethod]
public void WriteAbcWithReservedLengthThree()
{
AssertToByteArrayEquals("Abc", 3, (byte) 'A', (byte) 'b', (byte) 'c');
}
[TestMethod]
public void WriteAbcWithReservedLengthFour()
{
AssertToByteArrayEquals("Abc", 4, (byte) 'A', (byte) 'b', (byte) 'c', 0);
}
private static void AssertFromByteArrayEquals(string expected, params byte[] bytes)
{
Assert.AreEqual(expected, String.FromByteArray(bytes));
}
private static void AssertToByteArrayEquals(string value, int reservedLength, params byte[] expected)
{
CollectionAssert.AreEqual(expected, String.ToByteArray(value, reservedLength));
}
}
}

143
S7.Net/COTP.cs Normal file
View File

@@ -0,0 +1,143 @@
using System;
using System.IO;
using System.Threading.Tasks;
using System.Linq;
namespace S7.Net
{
/// <summary>
/// COTP Protocol functions and types
/// </summary>
internal class COTP
{
/// <summary>
/// Describes a COTP TPDU (Transport protocol data unit)
/// </summary>
public class TPDU
{
public byte HeaderLength;
public byte PDUType;
public int TPDUNumber;
public byte[] Data;
public bool LastDataUnit;
public TPDU(TPKT tPKT)
{
var br = new BinaryReader(new MemoryStream(tPKT.Data));
HeaderLength = br.ReadByte();
if (HeaderLength >= 2)
{
PDUType = br.ReadByte();
if (PDUType == 0xf0) //DT Data
{
var flags = br.ReadByte();
TPDUNumber = flags & 0x7F;
LastDataUnit = (flags & 0x80) > 0;
Data = br.ReadBytes(tPKT.Length - HeaderLength - 4); //4 = TPKT Size
return;
}
//TODO: Handle other PDUTypes
}
Data = new byte[0];
}
/// <summary>
/// Reads COTP TPDU (Transport protocol data unit) from the network stream
/// See: https://tools.ietf.org/html/rfc905
/// </summary>
/// <param name="stream">The socket to read from</param>
/// <returns>COTP DPDU instance</returns>
public static TPDU Read(Stream stream)
{
var tpkt = TPKT.Read(stream);
if (tpkt.Length > 0) return new TPDU(tpkt);
return null;
}
/// <summary>
/// Reads COTP TPDU (Transport protocol data unit) from the network stream
/// See: https://tools.ietf.org/html/rfc905
/// </summary>
/// <param name="stream">The socket to read from</param>
/// <returns>COTP DPDU instance</returns>
public static async Task<TPDU> ReadAsync(Stream stream)
{
var tpkt = await TPKT.ReadAsync(stream);
if (tpkt.Length > 0) return new TPDU(tpkt);
return null;
}
public override string ToString()
{
return string.Format("Length: {0} PDUType: {1} TPDUNumber: {2} Last: {3} Segment Data: {4}",
HeaderLength,
PDUType,
TPDUNumber,
LastDataUnit,
BitConverter.ToString(Data)
);
}
}
/// <summary>
/// Describes a COTP TSDU (Transport service data unit). One TSDU consist of 1 ore more TPDUs
/// </summary>
public class TSDU
{
/// <summary>
/// Reads the full COTP TSDU (Transport service data unit)
/// See: https://tools.ietf.org/html/rfc905
/// </summary>
/// <param name="stream">The stream to read from</param>
/// <returns>Data in TSDU</returns>
public static byte[] Read(Stream stream)
{
var segment = TPDU.Read(stream);
if (segment == null) return null;
var buffer = new byte[segment.Data.Length];
var output = new MemoryStream(buffer);
output.Write(segment.Data, 0, segment.Data.Length);
while (!segment.LastDataUnit)
{
segment = TPDU.Read(stream);
Array.Resize(ref buffer, buffer.Length + segment.Data.Length);
var lastPosition = output.Position;
output = new MemoryStream(buffer);
output.Write(segment.Data, (int) lastPosition, segment.Data.Length);
}
return buffer.Take((int)output.Position).ToArray();
}
/// <summary>
/// Reads the full COTP TSDU (Transport service data unit)
/// See: https://tools.ietf.org/html/rfc905
/// </summary>
/// <param name="stream">The stream to read from</param>
/// <returns>Data in TSDU</returns>
public static async Task<byte[]> ReadAsync(Stream stream)
{
var segment = await TPDU.ReadAsync(stream);
if (segment == null) return null;
var buffer = new byte[segment.Data.Length];
var output = new MemoryStream(buffer);
output.Write(segment.Data, 0, segment.Data.Length);
while (!segment.LastDataUnit)
{
segment = await TPDU.ReadAsync(stream);
Array.Resize(ref buffer, buffer.Length + segment.Data.Length);
var lastPosition = output.Position;
output = new MemoryStream(buffer);
output.Write(segment.Data, (int) lastPosition, segment.Data.Length);
}
return buffer.Take((int)output.Position).ToArray();
}
}
}
}

View File

@@ -0,0 +1,19 @@
using System.Net.Sockets;
namespace S7.Net
{
public static class TcpClientMixins
{
#if NETSTANDARD1_3
public static void Close(this TcpClient tcpClient)
{
tcpClient.Dispose();
}
public static void Connect(this TcpClient tcpClient, string host, int port)
{
tcpClient.ConnectAsync(host, port).GetAwaiter().GetResult();
}
#endif
}
}

View File

@@ -15,15 +15,11 @@ namespace S7.Net
/// <returns></returns>
public static int BinStringToInt32(this string txt)
{
int cnt = 0;
int ret = 0;
for (cnt = txt.Length - 1; cnt >= 0; cnt += -1)
for (int i = 0; i < txt.Length; i++)
{
if (int.Parse(txt.Substring(cnt, 1)) == 1)
{
ret += (int)(Math.Pow(2, (txt.Length - 1 - cnt)));
}
ret = (ret << 1) | ((txt[i] == '1') ? 1 : 0);
}
return ret;
}
@@ -35,20 +31,7 @@ namespace S7.Net
/// <returns></returns>
public static byte? BinStringToByte(this string txt)
{
int cnt = 0;
int ret = 0;
if (txt.Length == 8)
{
for (cnt = 7; cnt >= 0; cnt += -1)
{
if (int.Parse(txt.Substring(cnt, 1)) == 1)
{
ret += (int)(Math.Pow(2, (txt.Length - 1 - cnt)));
}
}
return (byte)ret;
}
if (txt.Length == 8) return (byte)BinStringToInt32(txt);
return null;
}
@@ -147,7 +130,7 @@ namespace S7.Net
}
return txt;
}
catch
catch
{
return "";
}
@@ -221,23 +204,49 @@ namespace S7.Net
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static UInt32 ConvertToUInt(this double input)
[Obsolete("Double support is obsolete. Use ConvertToUInt(float) instead.")]
public static UInt32 ConvertToUInt(this double input)
{
uint output;
output = S7.Net.Types.DWord.FromByteArray(S7.Net.Types.Double.ToByteArray(input));
return output;
}
/// <summary>
/// Converts from float to DWord (DBD)
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static UInt32 ConvertToUInt(this float input)
{
uint output;
output = S7.Net.Types.DWord.FromByteArray(S7.Net.Types.Single.ToByteArray(input));
return output;
}
/// <summary>
/// Converts from DWord (DBD) to double
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[Obsolete("Double support is obsolete. Use ConvertToFloat(uint) instead.")]
public static double ConvertToDouble(this uint input)
{
double output;
output = S7.Net.Types.Double.FromByteArray(S7.Net.Types.DWord.ToByteArray(input));
return output;
}
/// <summary>
/// Converts from DWord (DBD) to float
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static float ConvertToFloat(this uint input)
{
float output;
output = S7.Net.Types.Single.FromByteArray(S7.Net.Types.DWord.ToByteArray(input));
return output;
}
}
}

View File

@@ -163,6 +163,11 @@
/// </summary>
String,
/// <summary>
/// String variable type (variable)
/// </summary>
StringEx,
/// <summary>
/// Timer variable type
/// </summary>

File diff suppressed because it is too large Load Diff

197
S7.Net/PLCAddress.cs Normal file
View File

@@ -0,0 +1,197 @@
namespace S7.Net
{
internal class PLCAddress
{
private DataType dataType;
private int dbNumber;
private int startByte;
private int bitNumber;
private VarType varType;
public DataType DataType
{
get => dataType;
set => dataType = value;
}
public int DbNumber
{
get => dbNumber;
set => dbNumber = value;
}
public int StartByte
{
get => startByte;
set => startByte = value;
}
public int BitNumber
{
get => bitNumber;
set => bitNumber = value;
}
public VarType VarType
{
get => varType;
set => varType = value;
}
public PLCAddress(string address)
{
Parse(address, out dataType, out dbNumber, out varType, out startByte, out bitNumber);
}
public static void Parse(string input, out DataType dataType, out int dbNumber, out VarType varType, out int address, out int bitNumber)
{
bitNumber = -1;
dbNumber = 0;
switch (input.Substring(0, 2))
{
case "DB":
string[] strings = input.Split(new char[] { '.' });
if (strings.Length < 2)
throw new InvalidAddressException("To few periods for DB address");
dataType = DataType.DataBlock;
dbNumber = int.Parse(strings[0].Substring(2));
address = int.Parse(strings[1].Substring(3));
string dbType = strings[1].Substring(0, 3);
switch (dbType)
{
case "DBB":
varType = VarType.Byte;
return;
case "DBW":
varType = VarType.Word;
return;
case "DBD":
varType = VarType.DWord;
return;
case "DBX":
bitNumber = int.Parse(strings[2]);
if (bitNumber > 7)
throw new InvalidAddressException("Bit can only be 0-7");
varType = VarType.Bit;
return;
default:
throw new InvalidAddressException();
}
case "EB":
// Input byte
dataType = DataType.Input;
dbNumber = 0;
address = int.Parse(input.Substring(2));
varType = VarType.Byte;
return;
case "EW":
// Input word
dataType = DataType.Input;
dbNumber = 0;
address = int.Parse(input.Substring(2));
varType = VarType.Word;
return;
case "ED":
// Input double-word
dataType = DataType.Input;
dbNumber = 0;
address = int.Parse(input.Substring(2));
varType = VarType.DWord;
return;
case "AB":
// Output byte
dataType = DataType.Output;
dbNumber = 0;
address = int.Parse(input.Substring(2));
varType = VarType.Byte;
return;
case "AW":
// Output word
dataType = DataType.Output;
dbNumber = 0;
address = int.Parse(input.Substring(2));
varType = VarType.Word;
return;
case "AD":
// Output double-word
dataType = DataType.Output;
dbNumber = 0;
address = int.Parse(input.Substring(2));
varType = VarType.DWord;
return;
case "MB":
// Memory byte
dataType = DataType.Memory;
dbNumber = 0;
address = int.Parse(input.Substring(2));
varType = VarType.Byte;
return;
case "MW":
// Memory word
dataType = DataType.Memory;
dbNumber = 0;
address = int.Parse(input.Substring(2));
varType = VarType.Word;
return;
case "MD":
// Memory double-word
dataType = DataType.Memory;
dbNumber = 0;
address = int.Parse(input.Substring(2));
varType = VarType.DWord;
return;
default:
switch (input.Substring(0, 1))
{
case "E":
case "I":
// Input
dataType = DataType.Input;
varType = VarType.Bit;
break;
case "A":
case "O":
// Output
dataType = DataType.Output;
varType = VarType.Bit;
break;
case "M":
// Memory
dataType = DataType.Memory;
varType = VarType.Byte;
break;
case "T":
// Timer
dataType = DataType.Timer;
dbNumber = 0;
address = int.Parse(input.Substring(1));
varType = VarType.Timer;
return;
case "Z":
case "C":
// Counter
dataType = DataType.Timer;
dbNumber = 0;
address = int.Parse(input.Substring(1));
varType = VarType.Counter;
return;
default:
throw new InvalidAddressException(string.Format("{0} is not a valid address", input.Substring(0, 1)));
}
string txt2 = input.Substring(1);
if (txt2.IndexOf(".") == -1)
throw new InvalidAddressException("To few periods for DB address");
address = int.Parse(txt2.Substring(0, txt2.IndexOf(".")));
bitNumber = int.Parse(txt2.Substring(txt2.IndexOf(".") + 1));
if (bitNumber > 7)
throw new InvalidAddressException("Bit can only be 0-7");
return;
}
}
}
}

92
S7.Net/PLCExceptions.cs Normal file
View File

@@ -0,0 +1,92 @@
using System;
#if NET_FULL
using System.Runtime.Serialization;
#endif
namespace S7.Net
{
internal class WrongNumberOfBytesException : Exception
{
public WrongNumberOfBytesException() : base()
{
}
public WrongNumberOfBytesException(string message) : base(message)
{
}
public WrongNumberOfBytesException(string message, Exception innerException) : base(message, innerException)
{
}
#if NET_FULL
protected WrongNumberOfBytesException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
#endif
}
internal class InvalidAddressException : Exception
{
public InvalidAddressException() : base ()
{
}
public InvalidAddressException(string message) : base(message)
{
}
public InvalidAddressException(string message, Exception innerException) : base(message, innerException)
{
}
#if NET_FULL
protected InvalidAddressException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
#endif
}
internal class InvalidVariableTypeException : Exception
{
public InvalidVariableTypeException() : base()
{
}
public InvalidVariableTypeException(string message) : base(message)
{
}
public InvalidVariableTypeException(string message, Exception innerException) : base(message, innerException)
{
}
#if NET_FULL
protected InvalidVariableTypeException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
#endif
}
internal class TPKTInvalidException : Exception
{
public TPKTInvalidException() : base()
{
}
public TPKTInvalidException(string message) : base(message)
{
}
public TPKTInvalidException(string message, Exception innerException) : base(message, innerException)
{
}
#if NET_FULL
protected TPKTInvalidException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
#endif
}
}

222
S7.Net/PLCHelpers.cs Normal file
View File

@@ -0,0 +1,222 @@
using S7.Net.Types;
using System;
using System.Collections.Generic;
using System.Linq;
namespace S7.Net
{
public partial class Plc
{
/// <summary>
/// Creates the header to read bytes from the PLC
/// </summary>
/// <param name="amount"></param>
/// <returns></returns>
private ByteArray ReadHeaderPackage(int amount = 1)
{
//header size = 19 bytes
var package = new Types.ByteArray(19);
package.Add(new byte[] { 0x03, 0x00 });
//complete package size
package.Add(Types.Int.ToByteArray((short)(19 + (12 * amount))));
package.Add(new byte[] { 0x02, 0xf0, 0x80, 0x32, 0x01, 0x00, 0x00, 0x00, 0x00 });
//data part size
package.Add(Types.Word.ToByteArray((ushort)(2 + (amount * 12))));
package.Add(new byte[] { 0x00, 0x00, 0x04 });
//amount of requests
package.Add((byte)amount);
return package;
}
/// <summary>
/// Create the bytes-package to request data from the PLC. You have to specify the memory type (dataType),
/// the address of the memory, the address of the byte and the bytes count.
/// </summary>
/// <param name="dataType">MemoryType (DB, Timer, Counter, etc.)</param>
/// <param name="db">Address of the memory to be read</param>
/// <param name="startByteAdr">Start address of the byte</param>
/// <param name="count">Number of bytes to be read</param>
/// <returns></returns>
private ByteArray CreateReadDataRequestPackage(DataType dataType, int db, int startByteAdr, int count = 1)
{
//single data req = 12
var package = new Types.ByteArray(12);
package.Add(new byte[] { 0x12, 0x0a, 0x10 });
switch (dataType)
{
case DataType.Timer:
case DataType.Counter:
package.Add((byte)dataType);
break;
default:
package.Add(0x02);
break;
}
package.Add(Word.ToByteArray((ushort)(count)));
package.Add(Word.ToByteArray((ushort)(db)));
package.Add((byte)dataType);
var overflow = (int)(startByteAdr * 8 / 0xffffU); // handles words with address bigger than 8191
package.Add((byte)overflow);
switch (dataType)
{
case DataType.Timer:
case DataType.Counter:
package.Add(Types.Word.ToByteArray((ushort)(startByteAdr)));
break;
default:
package.Add(Types.Word.ToByteArray((ushort)((startByteAdr) * 8)));
break;
}
return package;
}
/// <summary>
/// Given a S7 variable type (Bool, Word, DWord, etc.), it converts the bytes in the appropriate C# format.
/// </summary>
/// <param name="varType"></param>
/// <param name="bytes"></param>
/// <param name="varCount"></param>
/// <param name="bitAdr"></param>
/// <returns></returns>
private object ParseBytes(VarType varType, byte[] bytes, int varCount, byte bitAdr = 0)
{
if (bytes == null || bytes.Length == 0)
return null;
switch (varType)
{
case VarType.Byte:
if (varCount == 1)
return bytes[0];
else
return bytes;
case VarType.Word:
if (varCount == 1)
return Word.FromByteArray(bytes);
else
return Word.ToArray(bytes);
case VarType.Int:
if (varCount == 1)
return Int.FromByteArray(bytes);
else
return Int.ToArray(bytes);
case VarType.DWord:
if (varCount == 1)
return DWord.FromByteArray(bytes);
else
return DWord.ToArray(bytes);
case VarType.DInt:
if (varCount == 1)
return DInt.FromByteArray(bytes);
else
return DInt.ToArray(bytes);
case VarType.Real:
if (varCount == 1)
return Types.Single.FromByteArray(bytes);
else
return Types.Single.ToArray(bytes);
case VarType.String:
return Types.String.FromByteArray(bytes);
case VarType.StringEx:
return StringEx.FromByteArray(bytes);
case VarType.Timer:
if (varCount == 1)
return Timer.FromByteArray(bytes);
else
return Timer.ToArray(bytes);
case VarType.Counter:
if (varCount == 1)
return Counter.FromByteArray(bytes);
else
return Counter.ToArray(bytes);
case VarType.Bit:
if (varCount == 1)
{
if (bitAdr > 7)
return null;
else
return Bit.FromByte(bytes[0], bitAdr);
}
else
{
return Bit.ToBitArray(bytes);
}
default:
return null;
}
}
/// <summary>
/// Given a S7 <see cref="VarType"/> (Bool, Word, DWord, etc.), it returns how many bytes to read.
/// </summary>
/// <param name="varType"></param>
/// <param name="varCount"></param>
/// <returns>Byte lenght of variable</returns>
private int VarTypeToByteLength(VarType varType, int varCount = 1)
{
switch (varType)
{
case VarType.Bit:
return varCount; //TODO
case VarType.Byte:
return (varCount < 1) ? 1 : varCount;
case VarType.String:
return varCount;
case VarType.StringEx:
return varCount + 2;
case VarType.Word:
case VarType.Timer:
case VarType.Int:
case VarType.Counter:
return varCount * 2;
case VarType.DWord:
case VarType.DInt:
case VarType.Real:
return varCount * 4;
default:
return 0;
}
}
private byte[] GetS7ConnectionSetup()
{
return new byte[] { 3, 0, 0, 25, 2, 240, 128, 50, 1, 0, 0, 255, 255, 0, 8, 0, 0, 240, 0, 0, 3, 0, 3,
7, 128 //Try 1920 PDU Size. Same as libnodave.
};
}
private void ParseDataIntoDataItems(byte[] s7data, List<DataItem> dataItems)
{
int offset = 14;
foreach (var dataItem in dataItems)
{
// check for Return Code = Success
if (s7data[offset] != 0xff)
throw new PlcException(ErrorCode.WrongNumberReceivedBytes);
// to Data bytes
offset += 4;
int byteCnt = VarTypeToByteLength(dataItem.VarType, dataItem.Count);
dataItem.Value = ParseBytes(
dataItem.VarType,
s7data.Skip(offset).Take(byteCnt).ToArray(),
dataItem.Count,
dataItem.BitAdr
);
// next Item
offset += byteCnt;
// Fill byte in response when bytecount is odd
if (dataItem.Count % 2 != 0 && (dataItem.VarType == VarType.Byte || dataItem.VarType == VarType.Bit))
offset++;
}
}
}
}

513
S7.Net/PlcAsynchronous.cs Normal file
View File

@@ -0,0 +1,513 @@
using S7.Net.Types;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Threading.Tasks;
using S7.Net.Protocol;
namespace S7.Net
{
/// <summary>
/// Creates an instance of S7.Net driver
/// </summary>
public partial class Plc
{
/// <summary>
/// Connects to the PLC and performs a COTP ConnectionRequest and S7 CommunicationSetup.
/// </summary>
/// <returns>A task that represents the asynchronous open operation.</returns>
public async Task OpenAsync()
{
await ConnectAsync();
await stream.WriteAsync(ConnectionRequest.GetCOTPConnectionRequest(CPU, Rack, Slot), 0, 22);
var response = await COTP.TPDU.ReadAsync(stream);
if (response.PDUType != 0xd0) //Connect Confirm
{
throw new WrongNumberOfBytesException("Waiting for COTP connect confirm");
}
await stream.WriteAsync(GetS7ConnectionSetup(), 0, 25);
var s7data = await COTP.TSDU.ReadAsync(stream);
if (s7data == null || s7data[1] != 0x03) //Check for S7 Ack Data
{
throw new WrongNumberOfBytesException("Waiting for S7 connection setup");
}
MaxPDUSize = (short)(s7data[18] * 256 + s7data[19]);
}
private async Task ConnectAsync()
{
tcpClient = new TcpClient();
await tcpClient.ConnectAsync(IP, 102);
stream = tcpClient.GetStream();
}
/// <summary>
/// Reads a number of bytes from a DB starting from a specified index. This handles more than 200 bytes with multiple requests.
/// If the read was not successful, check LastErrorCode or LastErrorString.
/// </summary>
/// <param name="dataType">Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output.</param>
/// <param name="db">Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc.</param>
/// <param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
/// <param name="count">Byte count, if you want to read 120 bytes, set this to 120.</param>
/// <returns>Returns the bytes in an array</returns>
public async Task<byte[]> ReadBytesAsync(DataType dataType, int db, int startByteAdr, int count)
{
List<byte> resultBytes = new List<byte>();
int index = startByteAdr;
while (count > 0)
{
//This works up to MaxPDUSize-1 on SNAP7. But not MaxPDUSize-0.
var maxToRead = (int)Math.Min(count, MaxPDUSize - 18);
byte[] bytes = await ReadBytesWithSingleRequestAsync(dataType, db, index, maxToRead);
if (bytes == null)
return resultBytes.ToArray();
resultBytes.AddRange(bytes);
count -= maxToRead;
index += maxToRead;
}
return resultBytes.ToArray();
}
/// <summary>
/// Read and decode a certain number of bytes of the "VarType" provided.
/// This can be used to read multiple consecutive variables of the same type (Word, DWord, Int, etc).
/// If the read was not successful, check LastErrorCode or LastErrorString.
/// </summary>
/// <param name="dataType">Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output.</param>
/// <param name="db">Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc.</param>
/// <param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
/// <param name="varType">Type of the variable/s that you are reading</param>
/// <param name="bitAdr">Address of bit. If you want to read DB1.DBX200.6, set 6 to this parameter.</param>
/// <param name="varCount"></param>
public async Task<object> ReadAsync(DataType dataType, int db, int startByteAdr, VarType varType, int varCount, byte bitAdr = 0)
{
int cntBytes = VarTypeToByteLength(varType, varCount);
byte[] bytes = await ReadBytesAsync(dataType, db, startByteAdr, cntBytes);
return ParseBytes(varType, bytes, varCount, bitAdr);
}
/// <summary>
/// Reads a single variable from the PLC, takes in input strings like "DB1.DBX0.0", "DB20.DBD200", "MB20", "T45", etc.
/// If the read was not successful, check LastErrorCode or LastErrorString.
/// </summary>
/// <param name="variable">Input strings like "DB1.DBX0.0", "DB20.DBD200", "MB20", "T45", etc.</param>
/// <returns>Returns an object that contains the value. This object must be cast accordingly.</returns>
public async Task<object> ReadAsync(string variable)
{
var adr = new PLCAddress(variable);
return await ReadAsync(adr.DataType, adr.DbNumber, adr.StartByte, adr.VarType, 1, (byte)adr.BitNumber);
}
/// <summary>
/// Reads all the bytes needed to fill a struct in C#, starting from a certain address, and return an object that can be casted to the struct.
/// </summary>
/// <param name="structType">Type of the struct to be readed (es.: TypeOf(MyStruct)).</param>
/// <param name="db">Address of the DB.</param>
/// <param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
/// <returns>Returns a struct that must be cast.</returns>
public async Task<object> ReadStructAsync(Type structType, int db, int startByteAdr = 0)
{
int numBytes = Types.Struct.GetStructSize(structType);
// now read the package
var resultBytes = await ReadBytesAsync(DataType.DataBlock, db, startByteAdr, numBytes);
// and decode it
return Types.Struct.FromBytes(structType, resultBytes);
}
/// <summary>
/// Reads all the bytes needed to fill a struct in C#, starting from a certain address, and returns the struct or null if nothing was read.
/// </summary>
/// <typeparam name="T">The struct type</typeparam>
/// <param name="db">Address of the DB.</param>
/// <param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
/// <returns>Returns a nulable struct. If nothing was read null will be returned.</returns>
public async Task<T?> ReadStructAsync<T>(int db, int startByteAdr = 0) where T : struct
{
return await ReadStructAsync(typeof(T), db, startByteAdr) as T?;
}
/// <summary>
/// Reads all the bytes needed to fill a class in C#, starting from a certain address, and set all the properties values to the value that are read from the PLC.
/// This reads only properties, it doesn't read private variable or public variable without {get;set;} specified.
/// </summary>
/// <param name="sourceClass">Instance of the class that will store the values</param>
/// <param name="db">Index of the DB; es.: 1 is for DB1</param>
/// <param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
/// <returns>The number of read bytes</returns>
public async Task<Tuple<int, object>> ReadClassAsync(object sourceClass, int db, int startByteAdr = 0)
{
int numBytes = Class.GetClassSize(sourceClass);
if (numBytes <= 0)
{
throw new Exception("The size of the class is less than 1 byte and therefore cannot be read");
}
// now read the package
var resultBytes = await ReadBytesAsync(DataType.DataBlock, db, startByteAdr, numBytes);
// and decode it
Class.FromBytes(sourceClass, resultBytes);
return new Tuple<int, object>(resultBytes.Length, sourceClass);
}
/// <summary>
/// Reads all the bytes needed to fill a class in C#, starting from a certain address, and set all the properties values to the value that are read from the PLC.
/// This reads only properties, it doesn't read private variable or public variable without {get;set;} specified. To instantiate the class defined by the generic
/// type, the class needs a default constructor.
/// </summary>
/// <typeparam name="T">The class that will be instantiated. Requires a default constructor</typeparam>
/// <param name="db">Index of the DB; es.: 1 is for DB1</param>
/// <param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
/// <returns>An instance of the class with the values read from the PLC. If no data has been read, null will be returned</returns>
public async Task<T> ReadClassAsync<T>(int db, int startByteAdr = 0) where T : class
{
return await ReadClassAsync(() => Activator.CreateInstance<T>(), db, startByteAdr);
}
/// <summary>
/// Reads all the bytes needed to fill a class in C#, starting from a certain address, and set all the properties values to the value that are read from the PLC.
/// This reads only properties, it doesn't read private variable or public variable without {get;set;} specified.
/// </summary>
/// <typeparam name="T">The class that will be instantiated</typeparam>
/// <param name="classFactory">Function to instantiate the class</param>
/// <param name="db">Index of the DB; es.: 1 is for DB1</param>
/// <param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
/// <returns>An instance of the class with the values read from the PLC. If no data has been read, null will be returned</returns>
public async Task<T> ReadClassAsync<T>(Func<T> classFactory, int db, int startByteAdr = 0) where T : class
{
var instance = classFactory();
var res = await ReadClassAsync(instance, db, startByteAdr);
int readBytes = res.Item1;
if (readBytes <= 0)
{
return null;
}
return (T)res.Item2;
}
/// <summary>
/// Reads multiple vars in a single request.
/// You have to create and pass a list of DataItems and you obtain in response the same list with the values.
/// Values are stored in the property "Value" of the dataItem and are already converted.
/// If you don't want the conversion, just create a dataItem of bytes.
/// DataItems must not be more than 20 (protocol restriction) and bytes must not be more than 200 + 22 of header (protocol restriction).
/// </summary>
/// <param name="dataItems">List of dataitems that contains the list of variables that must be read. Maximum 20 dataitems are accepted.</param>
public async Task<List<DataItem>> ReadMultipleVarsAsync(List<DataItem> dataItems)
{
int cntBytes = dataItems.Sum(dataItem => VarTypeToByteLength(dataItem.VarType, dataItem.Count));
//TODO: Figure out how to use MaxPDUSize here
//Snap7 seems to choke on PDU sizes above 256 even if snap7
//replies with bigger PDU size in connection setup.
if (dataItems.Count > 20)
throw new Exception("Too many vars requested");
if (cntBytes > 222)
throw new Exception("Too many bytes requested"); // TODO: proper TDU check + split in multiple requests
try
{
// first create the header
int packageSize = 19 + (dataItems.Count * 12);
ByteArray package = new ByteArray(packageSize);
package.Add(ReadHeaderPackage(dataItems.Count));
// package.Add(0x02); // datenart
foreach (var dataItem in dataItems)
{
package.Add(CreateReadDataRequestPackage(dataItem.DataType, dataItem.DB, dataItem.StartByteAdr, VarTypeToByteLength(dataItem.VarType, dataItem.Count)));
}
await stream.WriteAsync(package.Array, 0, package.Array.Length);
var s7data = await COTP.TSDU.ReadAsync(stream); //TODO use Async
if (s7data == null || s7data[14] != 0xff)
throw new PlcException(ErrorCode.WrongNumberReceivedBytes);
ParseDataIntoDataItems(s7data, dataItems);
}
catch (SocketException socketException)
{
throw new PlcException(ErrorCode.ReadData, socketException);
}
catch (Exception exc)
{
throw new PlcException(ErrorCode.ReadData, exc);
}
return dataItems;
}
/// <summary>
/// Write a number of bytes from a DB starting from a specified index. This handles more than 200 bytes with multiple requests.
/// If the write was not successful, check LastErrorCode or LastErrorString.
/// </summary>
/// <param name="dataType">Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output.</param>
/// <param name="db">Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc.</param>
/// <param name="startByteAdr">Start byte address. If you want to write DB1.DBW200, this is 200.</param>
/// <param name="value">Bytes to write. If more than 200, multiple requests will be made.</param>
/// <returns>A task that represents the asynchronous write operation.</returns>
public async Task WriteBytesAsync(DataType dataType, int db, int startByteAdr, byte[] value)
{
int localIndex = 0;
int count = value.Length;
while (count > 0)
{
//TODO: Figure out how to use MaxPDUSize here
//Snap7 seems to choke on PDU sizes above 256 even if snap7
//replies with bigger PDU size in connection setup.
var maxToWrite = (int)Math.Min(count, 200);
await WriteBytesWithASingleRequestAsync(dataType, db, startByteAdr + localIndex, value.Skip(localIndex).Take(maxToWrite).ToArray());
count -= maxToWrite;
localIndex += maxToWrite;
}
}
/// <summary>
/// Write a single bit from a DB with the specified index.
/// </summary>
/// <param name="dataType">Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output.</param>
/// <param name="db">Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc.</param>
/// <param name="startByteAdr">Start byte address. If you want to write DB1.DBW200, this is 200.</param>
/// <param name="bitAdr">The address of the bit. (0-7)</param>
/// <param name="value">Bytes to write. If more than 200, multiple requests will be made.</param>
/// <returns>A task that represents the asynchronous write operation.</returns>
public async Task WriteBitAsync(DataType dataType, int db, int startByteAdr, int bitAdr, bool value)
{
if (bitAdr < 0 || bitAdr > 7)
throw new InvalidAddressException(string.Format("Addressing Error: You can only reference bitwise locations 0-7. Address {0} is invalid", bitAdr));
await WriteBitWithASingleRequestAsync(dataType, db, startByteAdr, bitAdr, value);
}
/// <summary>
/// Write a single bit from a DB with the specified index.
/// </summary>
/// <param name="dataType">Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output.</param>
/// <param name="db">Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc.</param>
/// <param name="startByteAdr">Start byte address. If you want to write DB1.DBW200, this is 200.</param>
/// <param name="bitAdr">The address of the bit. (0-7)</param>
/// <param name="value">Bytes to write. If more than 200, multiple requests will be made.</param>
/// <returns>A task that represents the asynchronous write operation.</returns>
public async Task WriteBitAsync(DataType dataType, int db, int startByteAdr, int bitAdr, int value)
{
if (value < 0 || value > 1)
throw new ArgumentException("Value must be 0 or 1", nameof(value));
await WriteBitAsync(dataType, db, startByteAdr, bitAdr, value == 1);
}
/// <summary>
/// Takes in input an object and tries to parse it to an array of values. This can be used to write many data, all of the same type.
/// You must specify the memory area type, memory are address, byte start address and bytes count.
/// If the read was not successful, check LastErrorCode or LastErrorString.
/// </summary>
/// <param name="dataType">Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output.</param>
/// <param name="db">Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc.</param>
/// <param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
/// <param name="value">Bytes to write. The lenght of this parameter can't be higher than 200. If you need more, use recursion.</param>
/// <param name="bitAdr">The address of the bit. (0-7)</param>
/// <returns>A task that represents the asynchronous write operation.</returns>
public async Task WriteAsync(DataType dataType, int db, int startByteAdr, object value, int bitAdr = -1)
{
if (bitAdr != -1)
{
//Must be writing a bit value as bitAdr is specified
if (value is bool)
{
await WriteBitAsync(dataType, db, startByteAdr, bitAdr, (bool) value);
}
else if (value is int intValue)
{
if (intValue < 0 || intValue > 7)
throw new ArgumentOutOfRangeException(
string.Format(
"Addressing Error: You can only reference bitwise locations 0-7. Address {0} is invalid",
bitAdr), nameof(bitAdr));
await WriteBitAsync(dataType, db, startByteAdr, bitAdr, intValue == 1);
}
else throw new ArgumentException("Value must be a bool or an int to write a bit", nameof(value));
}
else await WriteBytesAsync(dataType, db, startByteAdr, Serialization.SerializeValue(value));
}
/// <summary>
/// Writes a single variable from the PLC, takes in input strings like "DB1.DBX0.0", "DB20.DBD200", "MB20", "T45", etc.
/// If the write was not successful, check <see cref="LastErrorCode"/> or <see cref="LastErrorString"/>.
/// </summary>
/// <param name="variable">Input strings like "DB1.DBX0.0", "DB20.DBD200", "MB20", "T45", etc.</param>
/// <param name="value">Value to be written to the PLC</param>
/// <returns>A task that represents the asynchronous write operation.</returns>
public async Task WriteAsync(string variable, object value)
{
var adr = new PLCAddress(variable);
await WriteAsync(adr.DataType, adr.DbNumber, adr.StartByte, value, adr.BitNumber);
}
/// <summary>
/// Writes a C# struct to a DB in the PLC
/// </summary>
/// <param name="structValue">The struct to be written</param>
/// <param name="db">Db address</param>
/// <param name="startByteAdr">Start bytes on the PLC</param>
/// <returns>A task that represents the asynchronous write operation.</returns>
public async Task WriteStructAsync(object structValue, int db, int startByteAdr = 0)
{
var bytes = Struct.ToBytes(structValue).ToList();
await WriteBytesAsync(DataType.DataBlock, db, startByteAdr, bytes.ToArray());
}
/// <summary>
/// Writes a C# class to a DB in the PLC
/// </summary>
/// <param name="classValue">The class to be written</param>
/// <param name="db">Db address</param>
/// <param name="startByteAdr">Start bytes on the PLC</param>
/// <returns>A task that represents the asynchronous write operation.</returns>
public async Task WriteClassAsync(object classValue, int db, int startByteAdr = 0)
{
var bytes = Types.Class.ToBytes(classValue).ToList();
await WriteBytesAsync(DataType.DataBlock, db, startByteAdr, bytes.ToArray());
}
private async Task<byte[]> ReadBytesWithSingleRequestAsync(DataType dataType, int db, int startByteAdr, int count)
{
byte[] bytes = new byte[count];
// first create the header
int packageSize = 31;
ByteArray package = new ByteArray(packageSize);
package.Add(ReadHeaderPackage());
// package.Add(0x02); // datenart
package.Add(CreateReadDataRequestPackage(dataType, db, startByteAdr, count));
await stream.WriteAsync(package.Array, 0, package.Array.Length);
var s7data = await COTP.TSDU.ReadAsync(stream);
if (s7data == null || s7data[14] != 0xff)
throw new PlcException(ErrorCode.WrongNumberReceivedBytes);
for (int cnt = 0; cnt < count; cnt++)
bytes[cnt] = s7data[cnt + 18];
return bytes;
}
/// <summary>
/// Write DataItem(s) to the PLC. Throws an exception if the response is invalid
/// or when the PLC reports errors for item(s) written.
/// </summary>
/// <param name="dataItems">The DataItem(s) to write to the PLC.</param>
/// <returns>Task that completes when response from PLC is parsed.</returns>
public async Task WriteAsync(params DataItem[] dataItems)
{
var message = new ByteArray();
var length = S7WriteMultiple.CreateRequest(message, dataItems);
await stream.WriteAsync(message.Array, 0, length).ConfigureAwait(false);
var response = await COTP.TSDU.ReadAsync(stream).ConfigureAwait(false);
S7WriteMultiple.ParseResponse(response, response.Length, dataItems);
}
/// <summary>
/// Writes up to 200 bytes to the PLC. You must specify the memory area type, memory are address, byte start address and bytes count.
/// </summary>
/// <param name="dataType">Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output.</param>
/// <param name="db">Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc.</param>
/// <param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
/// <param name="value">Bytes to write. The lenght of this parameter can't be higher than 200. If you need more, use recursion.</param>
/// <returns>A task that represents the asynchronous write operation.</returns>
private async Task WriteBytesWithASingleRequestAsync(DataType dataType, int db, int startByteAdr, byte[] value)
{
byte[] bReceive = new byte[513];
int varCount = 0;
try
{
varCount = value.Length;
// first create the header
int packageSize = 35 + value.Length;
ByteArray package = new ByteArray(packageSize);
package.Add(new byte[] { 3, 0, 0 });
package.Add((byte)packageSize);
package.Add(new byte[] { 2, 0xf0, 0x80, 0x32, 1, 0, 0 });
package.Add(Word.ToByteArray((ushort)(varCount - 1)));
package.Add(new byte[] { 0, 0x0e });
package.Add(Word.ToByteArray((ushort)(varCount + 4)));
package.Add(new byte[] { 0x05, 0x01, 0x12, 0x0a, 0x10, 0x02 });
package.Add(Word.ToByteArray((ushort)varCount));
package.Add(Word.ToByteArray((ushort)(db)));
package.Add((byte)dataType);
var overflow = (int)(startByteAdr * 8 / 0xffffU); // handles words with address bigger than 8191
package.Add((byte)overflow);
package.Add(Word.ToByteArray((ushort)(startByteAdr * 8)));
package.Add(new byte[] { 0, 4 });
package.Add(Word.ToByteArray((ushort)(varCount * 8)));
// now join the header and the data
package.Add(value);
await stream.WriteAsync(package.Array, 0, package.Array.Length);
var s7data = await COTP.TSDU.ReadAsync(stream);
if (s7data == null || s7data[14] != 0xff)
{
throw new PlcException(ErrorCode.WrongNumberReceivedBytes);
}
}
catch (Exception exc)
{
throw new PlcException(ErrorCode.WriteData, exc);
}
}
private async Task WriteBitWithASingleRequestAsync(DataType dataType, int db, int startByteAdr, int bitAdr, bool bitValue)
{
byte[] bReceive = new byte[513];
int varCount = 0;
try
{
var value = new[] {bitValue ? (byte) 1 : (byte) 0};
varCount = value.Length;
// first create the header
int packageSize = 35 + value.Length;
ByteArray package = new Types.ByteArray(packageSize);
package.Add(new byte[] { 3, 0, 0 });
package.Add((byte)packageSize);
package.Add(new byte[] { 2, 0xf0, 0x80, 0x32, 1, 0, 0 });
package.Add(Word.ToByteArray((ushort)(varCount - 1)));
package.Add(new byte[] { 0, 0x0e });
package.Add(Word.ToByteArray((ushort)(varCount + 4)));
package.Add(new byte[] { 0x05, 0x01, 0x12, 0x0a, 0x10, 0x01 }); //ending 0x01 is used for writing a sinlge bit
package.Add(Word.ToByteArray((ushort)varCount));
package.Add(Word.ToByteArray((ushort)(db)));
package.Add((byte)dataType);
int overflow = (int)(startByteAdr * 8 / 0xffffU); // handles words with address bigger than 8191
package.Add((byte)overflow);
package.Add(Word.ToByteArray((ushort)(startByteAdr * 8 + bitAdr)));
package.Add(new byte[] { 0, 0x03 }); //ending 0x03 is used for writing a sinlge bit
package.Add(Word.ToByteArray((ushort)(varCount)));
// now join the header and the data
package.Add(value);
await stream.WriteAsync(package.Array, 0, package.Array.Length);
var s7data = await COTP.TSDU.ReadAsync(stream);
if (s7data == null || s7data[14] != 0xff)
throw new PlcException(ErrorCode.WrongNumberReceivedBytes);
}
catch (Exception exc)
{
throw new PlcException(ErrorCode.WriteData, exc);
}
}
}
}

39
S7.Net/PlcException.cs Normal file
View File

@@ -0,0 +1,39 @@
using System;
namespace S7.Net
{
#if NET_FULL
[Serializable]
#endif
public class PlcException : Exception
{
public ErrorCode ErrorCode { get; }
public PlcException(ErrorCode errorCode) : this(errorCode, $"PLC communication failed with error '{errorCode}'.")
{
}
public PlcException(ErrorCode errorCode, Exception innerException) : this(errorCode, innerException.Message,
innerException)
{
}
public PlcException(ErrorCode errorCode, string message) : base(message)
{
ErrorCode = errorCode;
}
public PlcException(ErrorCode errorCode, string message, Exception inner) : base(message, inner)
{
ErrorCode = errorCode;
}
#if NET_FULL
protected PlcException(System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context) : base(info, context)
{
ErrorCode = (ErrorCode) info.GetInt32(nameof(ErrorCode));
}
#endif
}
}

512
S7.Net/PlcSynchronous.cs Normal file
View File

@@ -0,0 +1,512 @@
using S7.Net.Types;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using S7.Net.Protocol;
//Implement synchronous methods here
namespace S7.Net
{
public partial class Plc
{
/// <summary>
/// Connects to the PLC and performs a COTP ConnectionRequest and S7 CommunicationSetup.
/// </summary>
public void Open()
{
Connect();
try
{
stream.Write(ConnectionRequest.GetCOTPConnectionRequest(CPU, Rack, Slot), 0, 22);
var response = COTP.TPDU.Read(stream);
if (response.PDUType != 0xd0) //Connect Confirm
{
throw new WrongNumberOfBytesException("Waiting for COTP connect confirm");
}
stream.Write(GetS7ConnectionSetup(), 0, 25);
var s7data = COTP.TSDU.Read(stream);
if (s7data == null || s7data[1] != 0x03) //Check for S7 Ack Data
{
throw new WrongNumberOfBytesException("Waiting for S7 connection setup");
}
MaxPDUSize = (short)(s7data[18] * 256 + s7data[19]);
}
catch (Exception exc)
{
throw new PlcException(ErrorCode.ConnectionError,
$"Couldn't establish the connection to {IP}.\nMessage: {exc.Message}", exc);
}
}
private void Connect()
{
try
{
tcpClient = new TcpClient();
tcpClient.Connect(IP, 102);
stream = tcpClient.GetStream();
}
catch (SocketException sex)
{
// see https://msdn.microsoft.com/en-us/library/windows/desktop/ms740668(v=vs.85).aspx
throw new PlcException(
sex.SocketErrorCode == SocketError.TimedOut
? ErrorCode.IPAddressNotAvailable
: ErrorCode.ConnectionError, sex);
}
catch (Exception ex)
{
throw new PlcException(ErrorCode.ConnectionError, ex);
}
}
/// <summary>
/// Reads a number of bytes from a DB starting from a specified index. This handles more than 200 bytes with multiple requests.
/// If the read was not successful, check LastErrorCode or LastErrorString.
/// </summary>
/// <param name="dataType">Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output.</param>
/// <param name="db">Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc.</param>
/// <param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
/// <param name="count">Byte count, if you want to read 120 bytes, set this to 120.</param>
/// <returns>Returns the bytes in an array</returns>
public byte[] ReadBytes(DataType dataType, int db, int startByteAdr, int count)
{
List<byte> resultBytes = new List<byte>();
int index = startByteAdr;
while (count > 0)
{
//This works up to MaxPDUSize-1 on SNAP7. But not MaxPDUSize-0.
var maxToRead = (int)Math.Min(count, MaxPDUSize - 18);
byte[] bytes = ReadBytesWithSingleRequest(dataType, db, index, maxToRead);
if (bytes == null)
return resultBytes.ToArray();
resultBytes.AddRange(bytes);
count -= maxToRead;
index += maxToRead;
}
return resultBytes.ToArray();
}
/// <summary>
/// Read and decode a certain number of bytes of the "VarType" provided.
/// This can be used to read multiple consecutive variables of the same type (Word, DWord, Int, etc).
/// If the read was not successful, check LastErrorCode or LastErrorString.
/// </summary>
/// <param name="dataType">Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output.</param>
/// <param name="db">Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc.</param>
/// <param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
/// <param name="varType">Type of the variable/s that you are reading</param>
/// <param name="bitAdr">Address of bit. If you want to read DB1.DBX200.6, set 6 to this parameter.</param>
/// <param name="varCount"></param>
public object Read(DataType dataType, int db, int startByteAdr, VarType varType, int varCount, byte bitAdr = 0)
{
int cntBytes = VarTypeToByteLength(varType, varCount);
byte[] bytes = ReadBytes(dataType, db, startByteAdr, cntBytes);
return ParseBytes(varType, bytes, varCount, bitAdr);
}
/// <summary>
/// Reads a single variable from the PLC, takes in input strings like "DB1.DBX0.0", "DB20.DBD200", "MB20", "T45", etc.
/// If the read was not successful, check LastErrorCode or LastErrorString.
/// </summary>
/// <param name="variable">Input strings like "DB1.DBX0.0", "DB20.DBD200", "MB20", "T45", etc.</param>
/// <returns>Returns an object that contains the value. This object must be cast accordingly.</returns>
public object Read(string variable)
{
var adr = new PLCAddress(variable);
return Read(adr.DataType, adr.DbNumber, adr.StartByte, adr.VarType, 1, (byte)adr.BitNumber);
}
/// <summary>
/// Reads all the bytes needed to fill a struct in C#, starting from a certain address, and return an object that can be casted to the struct.
/// </summary>
/// <param name="structType">Type of the struct to be readed (es.: TypeOf(MyStruct)).</param>
/// <param name="db">Address of the DB.</param>
/// <param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
/// <returns>Returns a struct that must be cast.</returns>
public object ReadStruct(Type structType, int db, int startByteAdr = 0)
{
int numBytes = Struct.GetStructSize(structType);
// now read the package
var resultBytes = ReadBytes(DataType.DataBlock, db, startByteAdr, numBytes);
// and decode it
return Struct.FromBytes(structType, resultBytes);
}
/// <summary>
/// Reads all the bytes needed to fill a struct in C#, starting from a certain address, and returns the struct or null if nothing was read.
/// </summary>
/// <typeparam name="T">The struct type</typeparam>
/// <param name="db">Address of the DB.</param>
/// <param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
/// <returns>Returns a nullable struct. If nothing was read null will be returned.</returns>
public T? ReadStruct<T>(int db, int startByteAdr = 0) where T : struct
{
return ReadStruct(typeof(T), db, startByteAdr) as T?;
}
/// <summary>
/// Reads all the bytes needed to fill a class in C#, starting from a certain address, and set all the properties values to the value that are read from the PLC.
/// This reads only properties, it doesn't read private variable or public variable without {get;set;} specified.
/// </summary>
/// <param name="sourceClass">Instance of the class that will store the values</param>
/// <param name="db">Index of the DB; es.: 1 is for DB1</param>
/// <param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
/// <returns>The number of read bytes</returns>
public int ReadClass(object sourceClass, int db, int startByteAdr = 0)
{
int numBytes = Class.GetClassSize(sourceClass);
if (numBytes <= 0)
{
throw new Exception("The size of the class is less than 1 byte and therefore cannot be read");
}
// now read the package
var resultBytes = ReadBytes(DataType.DataBlock, db, startByteAdr, numBytes);
// and decode it
Class.FromBytes(sourceClass, resultBytes);
return resultBytes.Length;
}
/// <summary>
/// Reads all the bytes needed to fill a class in C#, starting from a certain address, and set all the properties values to the value that are read from the PLC.
/// This reads only properties, it doesn't read private variable or public variable without {get;set;} specified. To instantiate the class defined by the generic
/// type, the class needs a default constructor.
/// </summary>
/// <typeparam name="T">The class that will be instantiated. Requires a default constructor</typeparam>
/// <param name="db">Index of the DB; es.: 1 is for DB1</param>
/// <param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
/// <returns>An instance of the class with the values read from the PLC. If no data has been read, null will be returned</returns>
public T ReadClass<T>(int db, int startByteAdr = 0) where T : class
{
return ReadClass(() => Activator.CreateInstance<T>(), db, startByteAdr);
}
/// <summary>
/// Reads all the bytes needed to fill a class in C#, starting from a certain address, and set all the properties values to the value that are read from the PLC.
/// This reads only properties, it doesn't read private variable or public variable without {get;set;} specified.
/// </summary>
/// <typeparam name="T">The class that will be instantiated</typeparam>
/// <param name="classFactory">Function to instantiate the class</param>
/// <param name="db">Index of the DB; es.: 1 is for DB1</param>
/// <param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
/// <returns>An instance of the class with the values read from the PLC. If no data has been read, null will be returned</returns>
public T ReadClass<T>(Func<T> classFactory, int db, int startByteAdr = 0) where T : class
{
var instance = classFactory();
int readBytes = ReadClass(instance, db, startByteAdr);
if (readBytes <= 0)
{
return null;
}
return instance;
}
/// <summary>
/// Write a number of bytes from a DB starting from a specified index. This handles more than 200 bytes with multiple requests.
/// If the write was not successful, check LastErrorCode or LastErrorString.
/// </summary>
/// <param name="dataType">Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output.</param>
/// <param name="db">Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc.</param>
/// <param name="startByteAdr">Start byte address. If you want to write DB1.DBW200, this is 200.</param>
/// <param name="value">Bytes to write. If more than 200, multiple requests will be made.</param>
public void WriteBytes(DataType dataType, int db, int startByteAdr, byte[] value)
{
int localIndex = 0;
int count = value.Length;
while (count > 0)
{
//TODO: Figure out how to use MaxPDUSize here
//Snap7 seems to choke on PDU sizes above 256 even if snap7
//replies with bigger PDU size in connection setup.
var maxToWrite = Math.Min(count, 200);
WriteBytesWithASingleRequest(dataType, db, startByteAdr + localIndex, value.Skip(localIndex).Take(maxToWrite).ToArray());
count -= maxToWrite;
localIndex += maxToWrite;
}
}
/// <summary>
/// Write a single bit from a DB with the specified index.
/// </summary>
/// <param name="dataType">Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output.</param>
/// <param name="db">Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc.</param>
/// <param name="startByteAdr">Start byte address. If you want to write DB1.DBW200, this is 200.</param>
/// <param name="bitAdr">The address of the bit. (0-7)</param>
/// <param name="value">Bytes to write. If more than 200, multiple requests will be made.</param>
public void WriteBit(DataType dataType, int db, int startByteAdr, int bitAdr, bool value)
{
if (bitAdr < 0 || bitAdr > 7)
throw new InvalidAddressException(string.Format("Addressing Error: You can only reference bitwise locations 0-7. Address {0} is invalid", bitAdr));
WriteBitWithASingleRequest(dataType, db, startByteAdr, bitAdr, value);
}
/// <summary>
/// Write a single bit to a DB with the specified index.
/// </summary>
/// <param name="dataType">Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output.</param>
/// <param name="db">Address of the memory area (if you want to write DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc.</param>
/// <param name="startByteAdr">Start byte address. If you want to write DB1.DBW200, this is 200.</param>
/// <param name="bitAdr">The address of the bit. (0-7)</param>
/// <param name="value">Value to write (0 or 1).</param>
public void WriteBit(DataType dataType, int db, int startByteAdr, int bitAdr, int value)
{
if (value < 0 || value > 1)
throw new ArgumentException("Value must be 0 or 1", nameof(value));
WriteBit(dataType, db, startByteAdr, bitAdr, value == 1);
}
/// <summary>
/// Takes in input an object and tries to parse it to an array of values. This can be used to write many data, all of the same type.
/// You must specify the memory area type, memory are address, byte start address and bytes count.
/// If the read was not successful, check LastErrorCode or LastErrorString.
/// </summary>
/// <param name="dataType">Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output.</param>
/// <param name="db">Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc.</param>
/// <param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
/// <param name="value">Bytes to write. The lenght of this parameter can't be higher than 200. If you need more, use recursion.</param>
/// <param name="bitAdr">The address of the bit. (0-7)</param>
public void Write(DataType dataType, int db, int startByteAdr, object value, int bitAdr = -1)
{
if (bitAdr != -1)
{
//Must be writing a bit value as bitAdr is specified
if (value is bool)
{
WriteBit(dataType, db, startByteAdr, bitAdr, (bool) value);
}
else if (value is int intValue)
{
if (intValue < 0 || intValue > 7)
throw new ArgumentOutOfRangeException(
string.Format(
"Addressing Error: You can only reference bitwise locations 0-7. Address {0} is invalid",
bitAdr), nameof(bitAdr));
WriteBit(dataType, db, startByteAdr, bitAdr, intValue == 1);
}
else
throw new ArgumentException("Value must be a bool or an int to write a bit", nameof(value));
}
else WriteBytes(dataType, db, startByteAdr, Serialization.SerializeValue(value));
}
/// <summary>
/// Writes a single variable from the PLC, takes in input strings like "DB1.DBX0.0", "DB20.DBD200", "MB20", "T45", etc.
/// If the write was not successful, check <see cref="LastErrorCode"/> or <see cref="LastErrorString"/>.
/// </summary>
/// <param name="variable">Input strings like "DB1.DBX0.0", "DB20.DBD200", "MB20", "T45", etc.</param>
/// <param name="value">Value to be written to the PLC</param>
public void Write(string variable, object value)
{
var adr = new PLCAddress(variable);
Write(adr.DataType, adr.DbNumber, adr.StartByte, value, adr.BitNumber);
}
/// <summary>
/// Writes a C# struct to a DB in the PLC
/// </summary>
/// <param name="structValue">The struct to be written</param>
/// <param name="db">Db address</param>
/// <param name="startByteAdr">Start bytes on the PLC</param>
public void WriteStruct(object structValue, int db, int startByteAdr = 0)
{
WriteStructAsync(structValue, db, startByteAdr).GetAwaiter().GetResult();
}
/// <summary>
/// Writes a C# class to a DB in the PLC
/// </summary>
/// <param name="classValue">The class to be written</param>
/// <param name="db">Db address</param>
/// <param name="startByteAdr">Start bytes on the PLC</param>
public void WriteClass(object classValue, int db, int startByteAdr = 0)
{
WriteClassAsync(classValue, db, startByteAdr).GetAwaiter().GetResult();
}
private byte[] ReadBytesWithSingleRequest(DataType dataType, int db, int startByteAdr, int count)
{
byte[] bytes = new byte[count];
try
{
// first create the header
int packageSize = 31;
ByteArray package = new ByteArray(packageSize);
package.Add(ReadHeaderPackage());
// package.Add(0x02); // datenart
package.Add(CreateReadDataRequestPackage(dataType, db, startByteAdr, count));
stream.Write(package.Array, 0, package.Array.Length);
var s7data = COTP.TSDU.Read(stream);
if (s7data == null || s7data[14] != 0xff)
throw new PlcException(ErrorCode.WrongNumberReceivedBytes);
for (int cnt = 0; cnt < count; cnt++)
bytes[cnt] = s7data[cnt + 18];
return bytes;
}
catch (Exception exc)
{
throw new PlcException(ErrorCode.ReadData, exc);
}
}
/// <summary>
/// Write DataItem(s) to the PLC. Throws an exception if the response is invalid
/// or when the PLC reports errors for item(s) written.
/// </summary>
/// <param name="dataItems">The DataItem(s) to write to the PLC.</param>
public void Write(params DataItem[] dataItems)
{
var message = new ByteArray();
var length = S7WriteMultiple.CreateRequest(message, dataItems);
stream.Write(message.Array, 0, length);
var response = COTP.TSDU.Read(stream);
S7WriteMultiple.ParseResponse(response, response.Length, dataItems);
}
private void WriteBytesWithASingleRequest(DataType dataType, int db, int startByteAdr, byte[] value)
{
int varCount = 0;
try
{
varCount = value.Length;
// first create the header
int packageSize = 35 + value.Length;
ByteArray package = new ByteArray(packageSize);
package.Add(new byte[] { 3, 0, 0 });
package.Add((byte)packageSize);
package.Add(new byte[] { 2, 0xf0, 0x80, 0x32, 1, 0, 0 });
package.Add(Word.ToByteArray((ushort)(varCount - 1)));
package.Add(new byte[] { 0, 0x0e });
package.Add(Word.ToByteArray((ushort)(varCount + 4)));
package.Add(new byte[] { 0x05, 0x01, 0x12, 0x0a, 0x10, 0x02 });
package.Add(Word.ToByteArray((ushort)varCount));
package.Add(Word.ToByteArray((ushort)(db)));
package.Add((byte)dataType);
var overflow = (int)(startByteAdr * 8 / 0xffffU); // handles words with address bigger than 8191
package.Add((byte)overflow);
package.Add(Word.ToByteArray((ushort)(startByteAdr * 8)));
package.Add(new byte[] { 0, 4 });
package.Add(Word.ToByteArray((ushort)(varCount * 8)));
// now join the header and the data
package.Add(value);
stream.Write(package.Array, 0, package.Array.Length);
var s7data = COTP.TSDU.Read(stream);
if (s7data == null || s7data[14] != 0xff)
{
throw new PlcException(ErrorCode.WrongNumberReceivedBytes);
}
}
catch (Exception exc)
{
throw new PlcException(ErrorCode.WriteData, exc);
}
}
private void WriteBitWithASingleRequest(DataType dataType, int db, int startByteAdr, int bitAdr, bool bitValue)
{
int varCount = 0;
try
{
var value = new[] {bitValue ? (byte) 1 : (byte) 0};
varCount = value.Length;
// first create the header
int packageSize = 35 + value.Length;
ByteArray package = new ByteArray(packageSize);
package.Add(new byte[] { 3, 0, 0 });
package.Add((byte)packageSize);
package.Add(new byte[] { 2, 0xf0, 0x80, 0x32, 1, 0, 0 });
package.Add(Word.ToByteArray((ushort)(varCount - 1)));
package.Add(new byte[] { 0, 0x0e });
package.Add(Word.ToByteArray((ushort)(varCount + 4)));
package.Add(new byte[] { 0x05, 0x01, 0x12, 0x0a, 0x10, 0x01 }); //ending 0x01 is used for writing a sinlge bit
package.Add(Word.ToByteArray((ushort)varCount));
package.Add(Word.ToByteArray((ushort)(db)));
package.Add((byte)dataType);
int overflow = (int)(startByteAdr * 8 / 0xffffU); // handles words with address bigger than 8191
package.Add((byte)overflow);
package.Add(Word.ToByteArray((ushort)(startByteAdr * 8 + bitAdr)));
package.Add(new byte[] { 0, 0x03 }); //ending 0x03 is used for writing a sinlge bit
package.Add(Word.ToByteArray((ushort)(varCount)));
// now join the header and the data
package.Add(value);
stream.Write(package.Array, 0, package.Array.Length);
var s7data = COTP.TSDU.Read(stream);
if (s7data == null || s7data[14] != 0xff)
throw new PlcException(ErrorCode.WrongNumberReceivedBytes);
}
catch (Exception exc)
{
throw new PlcException(ErrorCode.WriteData, exc);
}
}
/// <summary>
/// Reads multiple vars in a single request.
/// You have to create and pass a list of DataItems and you obtain in response the same list with the values.
/// Values are stored in the property "Value" of the dataItem and are already converted.
/// If you don't want the conversion, just create a dataItem of bytes.
/// DataItems must not be more than 20 (protocol restriction) and bytes must not be more than 200 + 22 of header (protocol restriction).
/// </summary>
/// <param name="dataItems">List of dataitems that contains the list of variables that must be read. Maximum 20 dataitems are accepted.</param>
public void ReadMultipleVars(List<DataItem> dataItems)
{
int cntBytes = dataItems.Sum(dataItem => VarTypeToByteLength(dataItem.VarType, dataItem.Count));
//TODO: Figure out how to use MaxPDUSize here
//Snap7 seems to choke on PDU sizes above 256 even if snap7
//replies with bigger PDU size in connection setup.
if (dataItems.Count > 20)
throw new Exception("Too many vars requested");
if (cntBytes > 222)
throw new Exception("Too many bytes requested"); // TODO: proper TDU check + split in multiple requests
try
{
// first create the header
int packageSize = 19 + (dataItems.Count * 12);
ByteArray package = new ByteArray(packageSize);
package.Add(ReadHeaderPackage(dataItems.Count));
// package.Add(0x02); // datenart
foreach (var dataItem in dataItems)
{
package.Add(CreateReadDataRequestPackage(dataItem.DataType, dataItem.DB, dataItem.StartByteAdr, VarTypeToByteLength(dataItem.VarType, dataItem.Count)));
}
stream.Write(package.Array, 0, package.Array.Length);
var s7data = COTP.TSDU.Read(stream); //TODO use Async
if (s7data == null || s7data[14] != 0xff)
throw new PlcException(ErrorCode.WrongNumberReceivedBytes);
ParseDataIntoDataItems(s7data, dataItems);
}
catch (Exception exc)
{
throw new PlcException(ErrorCode.ReadData, exc);
}
}
}
}

View File

@@ -1,36 +1,3 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
// Allgemeine Informationen über eine Assembly werden über die folgenden
// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
// die mit einer Assembly verknüpft sind.
[assembly: AssemblyTitle("S7.Net")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Microsoft")]
[assembly: AssemblyProduct("S7.Net")]
[assembly: AssemblyCopyright("Copyright © Microsoft 2009")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar
// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von
// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest.
[assembly: ComVisible(false)]
// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
[assembly: Guid("1c01e753-a660-4c35-a681-c6f6a7deee83")]
// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
//
// Hauptversion
// Nebenversion
// Buildnummer
// Revision
//
// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern
// übernehmen, indem Sie "*" eingeben:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: InternalsVisibleTo("S7Net.UnitTest, PublicKey=00240000048000009400000006020000002400005253413100040000010001002d1032db55f60d64bf90ea1cc2247b5a8b9b6168a07bcd464a07ce2e425d027ff9409a64ba0e3f37718e14c50cf964d0d921e5ae8b8d74bd8a82431794f897cebf0ee668feb2ccd030153611b2808fcb7785c5e5136a98e0ec23de3c1ed385d2026c26e4bed5805ff9db7e0544f59b1f19d369d43403a624586795926e38c48d")]

View File

@@ -0,0 +1,61 @@
using System;
namespace S7.Net.Protocol
{
internal static class ConnectionRequest
{
public static byte[] GetCOTPConnectionRequest(CpuType cpu, Int16 rack, Int16 slot)
{
byte[] bSend1 = {
3, 0, 0, 22, //TPKT
17, //COTP Header Length
224, //Connect Request
0, 0, //Destination Reference
0, 46, //Source Reference
0, //Flags
193, //Parameter Code (src-tasp)
2, //Parameter Length
1, 0, //Source TASP
194, //Parameter Code (dst-tasp)
2, //Parameter Length
3, 0, //Destination TASP
192, //Parameter Code (tpdu-size)
1, //Parameter Length
11 //TPDU Size (2^11 = 2048)
};
switch (cpu)
{
case CpuType.S7200:
//S7200: Chr(193) & Chr(2) & Chr(16) & Chr(0) 'Eigener Tsap
bSend1[13] = 0x10;
bSend1[14] = 0x00;
//S7200: Chr(194) & Chr(2) & Chr(16) & Chr(0) 'Fremder Tsap
bSend1[17] = 0x10;
bSend1[18] = 0x00;
break;
case CpuType.S71200:
case CpuType.S7300:
case CpuType.S7400:
//S7300: Chr(193) & Chr(2) & Chr(1) & Chr(0) 'Eigener Tsap
bSend1[13] = 0x01;
bSend1[14] = 0x00;
//S7300: Chr(194) & Chr(2) & Chr(3) & Chr(2) 'Fremder Tsap
bSend1[17] = 0x03;
bSend1[18] = (byte) ((rack << 5) | (int) slot);
break;
case CpuType.S71500:
// Eigener Tsap
bSend1[13] = 0x10;
bSend1[14] = 0x02;
// Fredmer Tsap
bSend1[17] = 0x03;
bSend1[18] = (byte) ((rack << 5) | (int) slot);
break;
default:
throw new Exception("Wrong CPU Type Secified");
}
return bSend1;
}
}
}

View File

@@ -0,0 +1,147 @@
using System;
using System.Collections.Generic;
using S7.Net.Types;
namespace S7.Net.Protocol
{
internal static class S7WriteMultiple
{
public static int CreateRequest(ByteArray message, DataItem[] dataItems)
{
message.Add(Header.Template);
message[Header.Offsets.ParameterCount] = (byte) dataItems.Length;
var paramSize = dataItems.Length * Parameter.Template.Length;
Serialization.SetWordAt(message, Header.Offsets.ParameterSize,
(ushort) (2 + paramSize));
var paramOffset = Header.Template.Length;
var dataOffset = paramOffset + paramSize;
var data = new ByteArray();
foreach (var item in dataItems)
{
message.Add(Parameter.Template);
var value = Serialization.SerializeDataItem(item);
var wordLen = item.Value is bool ? 1 : 2;
message[paramOffset + Parameter.Offsets.WordLength] = (byte) wordLen;
Serialization.SetWordAt(message, paramOffset + Parameter.Offsets.Amount, (ushort) value.Length);
Serialization.SetWordAt(message, paramOffset + Parameter.Offsets.DbNumber, (ushort) item.DB);
message[paramOffset + Parameter.Offsets.Area] = (byte) item.DataType;
Serialization.SetAddressAt(message, paramOffset + Parameter.Offsets.Address, item.StartByteAdr, item.BitAdr);
paramOffset += Parameter.Template.Length;
data.Add(0x00);
if (item.Value is bool b)
{
data.Add(0x03);
data.AddWord(1);
data.Add(b ? (byte) 1 : (byte) 0);
data.Add(0);
}
else
{
var len = value.Length;
data.Add(0x04);
data.AddWord((ushort) (len << 3));
data.Add(value);
if ((len & 0b1) == 1)
{
data.Add(0);
}
}
}
message.Add(data.Array);
Serialization.SetWordAt(message, Header.Offsets.MessageLength, (ushort) message.Length);
Serialization.SetWordAt(message, Header.Offsets.DataLength, (ushort) (message.Length - paramOffset));
return message.Length;
}
public static void ParseResponse(byte[] message, int length, DataItem[] dataItems)
{
if (length < 12) throw new Exception("Not enough data received to parse write response.");
var messageError = Serialization.GetWordAt(message, 10);
if (messageError != 0)
throw new Exception($"Write failed with error {messageError}.");
if (length < 14 + dataItems.Length)
throw new Exception("Not enough data received to parse individual item responses.");
IList<byte> itemResults = new ArraySegment<byte>(message, 14, dataItems.Length);
List<Exception> errors = null;
for (int i = 0; i < dataItems.Length; i++)
{
var result = itemResults[i];
if (result == 0xff) continue;
if (errors == null) errors = new List<Exception>();
errors.Add(new Exception($"Write of dataItem {dataItems[i]} failed with error code {result}."));
}
if (errors != null)
throw new AggregateException(
$"Write failed for {errors.Count} items. See the innerExceptions for details.", errors);
}
private static class Header
{
public static byte[] Template { get; } =
{
0x03, 0x00, 0x00, 0x00, // TPKT
0x02, 0xf0, 0x80, // ISO DT
0x32, // S7 protocol ID
0x01, // JobRequest
0x00, 0x00, // Reserved
0x05, 0x00, // PDU reference
0x00, 0x0e, // Parameters length
0x00, 0x00, // Data length
0x05, // Function: Write var
0x00, // Number of items to write
};
public static class Offsets
{
public const int MessageLength = 2;
public const int ParameterSize = 13;
public const int DataLength = 15;
public const int ParameterCount = 18;
}
}
private static class Parameter
{
public static byte[] Template { get; } =
{
0x12, // Spec
0x0a, // Length of remaining bytes
0x10, // Addressing mode
0x02, // Transport size
0x00, 0x00, // Number of elements
0x00, 0x00, // DB number
0x84, // Area type
0x00, 0x00, 0x00 // Area offset
};
public static class Offsets
{
public const int WordLength = 3;
public const int Amount = 4;
public const int DbNumber = 6;
public const int Area = 8;
public const int Address = 9;
}
}
}
}

View File

@@ -0,0 +1,84 @@
using System;
using System.Collections.Generic;
using S7.Net.Types;
namespace S7.Net.Protocol
{
internal static class Serialization
{
public static ushort GetWordAt(IList<byte> buf, int index)
{
return (ushort)((buf[index] << 8) + buf[index]);
}
public static byte[] SerializeDataItem(DataItem dataItem)
{
if (dataItem.Value is string s)
return dataItem.VarType == VarType.StringEx
? StringEx.ToByteArray(s, dataItem.Count)
: Types.String.ToByteArray(s, dataItem.Count);
return SerializeValue(dataItem.Value);
}
public static byte[] SerializeValue(object value)
{
switch (value.GetType().Name)
{
case "Boolean":
return new[] { (byte)((bool)value ? 1 : 0) };
case "Byte":
return Types.Byte.ToByteArray((byte)value);
case "Int16":
return Types.Int.ToByteArray((Int16)value);
case "UInt16":
return Types.Word.ToByteArray((UInt16)value);
case "Int32":
return Types.DInt.ToByteArray((Int32)value);
case "UInt32":
return Types.DWord.ToByteArray((UInt32)value);
case "Double":
return Types.Double.ToByteArray((double)value);
case "Single":
return Types.Single.ToByteArray((float)value);
case "Byte[]":
return (byte[])value;
case "Int16[]":
return Types.Int.ToByteArray((Int16[])value);
case "UInt16[]":
return Types.Word.ToByteArray((UInt16[])value);
case "Int32[]":
return Types.DInt.ToByteArray((Int32[])value);
case "UInt32[]":
return Types.DWord.ToByteArray((UInt32[])value);
case "Double[]":
return Types.Double.ToByteArray((double[])value);
case "Single[]":
return Types.Single.ToByteArray((float[])value);
case "String":
// Hack: This is backwards compatible with the old code, but functionally it's broken
// if the consumer does not pay attention to string length.
var stringVal = (string) value;
return Types.String.ToByteArray(stringVal, stringVal.Length);
default:
throw new InvalidVariableTypeException();
}
}
public static void SetAddressAt(ByteArray buffer, int index, int startByte, byte bitNumber)
{
var start = startByte * 8 + bitNumber;
buffer[index + 2] = (byte)start;
start = start >> 8;
buffer[index + 1] = (byte)start;
start = start >> 8;
buffer[index] = (byte)start;
}
public static void SetWordAt(ByteArray buffer, int index, ushort value)
{
buffer[index] = (byte)(value >> 8);
buffer[index + 1] = (byte)value;
}
}
}

View File

@@ -1,122 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{BFD484F9-3F04-42A2-BF2A-60A189A25DCF}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>S7.Net</RootNamespace>
<AssemblyName>S7.Net</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<FileUpgradeFlags>
</FileUpgradeFlags>
<UpgradeBackupLocation>
</UpgradeBackupLocation>
<OldToolsVersion>3.5</OldToolsVersion>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
<DocumentationFile>bin\Debug\S7.Net.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
<DocumentationFile>bin\Release\S7.Net.xml</DocumentationFile>
</PropertyGroup>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net452;netstandard2.0;netstandard1.3</TargetFrameworks>
<SignAssembly>true</SignAssembly>
</PropertyGroup>
<PropertyGroup>
<AssemblyOriginatorKeyFile>Properties\S7.Net.snk</AssemblyOriginatorKeyFile>
<InternalsVisibleTo>S7.Net.UnitTest</InternalsVisibleTo>
<PackageId>S7netplus</PackageId>
<Title>S7.Net Plus</Title>
<Description>A continuation of Juergen1969's Siemens communication library.</Description>
<Authors>Derek Heiser;Michele Cattafesta;Michael Croes;Raphael Schlameuß</Authors>
<PackageProjectUrl>https://github.com/killnine/s7netplus</PackageProjectUrl>
<RepositoryUrl>https://github.com/killnine/s7netplus</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>PLC Siemens Communication S7</PackageTags>
<Copyright>Derek Heiser 2015</Copyright>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'net452' Or '$(TargetFramework)' == 'netstandard2.0' ">
<DefineConstants>NET_FULL</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Xml.Linq">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Data.DataSetExtensions">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-beta-62925-02" PrivateAssets="All" />
<PackageReference Include="SourceLink.Copy.PdbFiles" Version="2.8.3" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<Compile Include="Conversion.cs" />
<Compile Include="Enums.cs" />
<Compile Include="PLC.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Types\Boolean.cs" />
<Compile Include="Types\Byte.cs" />
<Compile Include="Types\ByteArray.cs" />
<Compile Include="Types\Class.cs" />
<Compile Include="Types\Counter.cs" />
<Compile Include="Types\DataItem.cs" />
<Compile Include="Types\DInt.cs" />
<Compile Include="Types\Double.cs" />
<Compile Include="Types\DWord.cs" />
<Compile Include="Types\Int.cs" />
<Compile Include="Types\String.cs" />
<Compile Include="Types\Struct.cs" />
<Compile Include="Types\Timer.cs" />
<Compile Include="Types\Word.cs" />
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
<Install>false</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>true</Install>
</BootstrapperPackage>
</ItemGroup>
<ItemGroup>
<None Include="Properties\S7.Net.snk" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>

78
S7.Net/TPKT.cs Normal file
View File

@@ -0,0 +1,78 @@
using System;
using System.IO;
using System.Threading.Tasks;
namespace S7.Net
{
/// <summary>
/// Describes a TPKT Packet
/// </summary>
internal class TPKT
{
public byte Version;
public byte Reserved1;
public int Length;
public byte[] Data;
/// <summary>
/// Reads a TPKT from the socket
/// </summary>
/// <param name="stream">The stream to read from</param>
/// <returns>TPKT Instance</returns>
public static TPKT Read(Stream stream)
{
var buf = new byte[4];
int len = stream.Read(buf, 0, 4);
if (len < 4) throw new TPKTInvalidException("TPKT is incomplete / invalid");
var pkt = new TPKT
{
Version = buf[0],
Reserved1 = buf[1],
Length = buf[2] * 256 + buf[3] //BigEndian
};
if (pkt.Length > 0)
{
pkt.Data = new byte[pkt.Length - 4];
len = stream.Read(pkt.Data, 0, pkt.Length - 4);
if (len < pkt.Length - 4)
throw new TPKTInvalidException("TPKT is incomplete / invalid");
}
return pkt;
}
/// <summary>
/// Reads a TPKT from the socket Async
/// </summary>
/// <param name="stream">The stream to read from</param>
/// <returns>Task TPKT Instace</returns>
public static async Task<TPKT> ReadAsync(Stream stream)
{
var buf = new byte[4];
int len = await stream.ReadAsync(buf, 0, 4);
if (len < 4) throw new TPKTInvalidException("TPKT is incomplete / invalid");
var pkt = new TPKT
{
Version = buf[0],
Reserved1 = buf[1],
Length = buf[2] * 256 + buf[3] //BigEndian
};
if (pkt.Length > 0)
{
pkt.Data = new byte[pkt.Length - 4];
len = await stream.ReadAsync(pkt.Data, 0, pkt.Length - 4);
if (len < pkt.Length - 4) throw new TPKTInvalidException("TPKT is incomplete / invalid");
}
return pkt;
}
public override string ToString()
{
return string.Format("Version: {0} Length: {1} Data: {2}",
Version,
Length,
BitConverter.ToString(Data)
);
}
}
}

27
S7.Net/Types/Bit.cs Normal file
View File

@@ -0,0 +1,27 @@
using System.Collections;
namespace S7.Net.Types
{
/// <summary>
/// Contains the conversion methods to convert Bit from S7 plc to C#.
/// </summary>
public static class Bit
{
/// <summary>
/// Converts a Bit to bool
/// </summary>
public static bool FromByte(byte v, byte bitAdr)
{
return (((int)v & (1 << bitAdr)) != 0);
}
/// <summary>
/// Converts an array of bytes to a BitArray
/// </summary>
public static BitArray ToBitArray(byte[] bytes)
{
BitArray bitArr = new BitArray(bytes);
return bitArr;
}
}
}

View File

@@ -1,6 +1,4 @@
using System;
namespace S7.Net.Types
namespace S7.Net.Types
{
/// <summary>
/// Contains the methods to read, set and reset bits inside bytes
@@ -12,10 +10,7 @@ namespace S7.Net.Types
/// </summary>
public static bool GetValue(byte value, int bit)
{
if ((value & (int)Math.Pow(2, bit)) != 0)
return true;
else
return false;
return (((int)value & (1 << bit)) != 0);
}
/// <summary>
@@ -23,7 +18,7 @@ namespace S7.Net.Types
/// </summary>
public static byte SetBit(byte value, int bit)
{
return (byte)(value | (byte)Math.Pow(2, bit));
return (byte)((value | (1 << bit)) & 0xFF);
}
/// <summary>
@@ -31,7 +26,7 @@ namespace S7.Net.Types
/// </summary>
public static byte ClearBit(byte value, int bit)
{
return (byte)(value & (byte)(~(byte)Math.Pow(2, bit)));
return (byte)((value | (~(1 << bit))) & 0xFF);
}
}

View File

@@ -12,8 +12,7 @@ namespace S7.Net.Types
/// </summary>
public static byte[] ToByteArray(byte value)
{
byte[] bytes = new byte[] { value};
return bytes;
return new byte[] { value }; ;
}
/// <summary>

View File

@@ -6,11 +6,19 @@ namespace S7.Net.Types
{
List<byte> list = new List<byte>();
public byte[] array
public byte this[int index]
{
get => list[index];
set => list[index] = value;
}
public byte[] Array
{
get { return list.ToArray(); }
}
public int Length => list.Count;
public ByteArray()
{
list = new List<byte>();
@@ -31,6 +39,12 @@ namespace S7.Net.Types
list.Add(item);
}
public void AddWord(ushort value)
{
list.Add((byte) (value >> 8));
list.Add((byte) value);
}
public void Add(byte[] items)
{
list.AddRange(items);
@@ -38,7 +52,7 @@ namespace S7.Net.Types
public void Add(ByteArray byteArray)
{
list.AddRange(byteArray.array);
list.AddRange(byteArray.Array);
}
}
}

View File

@@ -13,8 +13,8 @@ namespace S7.Net.Types
private static IEnumerable<PropertyInfo> GetAccessableProperties(Type classType)
{
return classType
#if NETFX_CORE
.GetProperties().Where(p => p.GetSetMethod() != null);
#if NETSTANDARD1_3
.GetTypeInfo().DeclaredProperties.Where(p => p.SetMethod != null);
#else
.GetProperties(
BindingFlags.SetProperty |
@@ -52,7 +52,7 @@ namespace S7.Net.Types
numBytes++;
numBytes += 4;
break;
case "Float":
case "Single":
case "Double":
numBytes = Math.Ceiling(numBytes);
if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0)
@@ -99,6 +99,10 @@ namespace S7.Net.Types
numBytes = GetIncreasedNumberOfBytes(numBytes, property.PropertyType);
}
}
// enlarge numBytes to next even number because S7-Structs in a DB always will be resized to an even byte count
numBytes = Math.Ceiling(numBytes);
if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0)
numBytes++;
return (int)numBytes;
}
@@ -177,6 +181,19 @@ namespace S7.Net.Types
bytes[(int)numBytes + 3] });
numBytes += 4;
break;
case "Single":
numBytes = Math.Ceiling(numBytes);
if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0)
numBytes++;
// hier auswerten
value = Single.FromByteArray(
new byte[] {
bytes[(int)numBytes],
bytes[(int)numBytes + 1],
bytes[(int)numBytes + 2],
bytes[(int)numBytes + 3] });
numBytes += 4;
break;
default:
var propClass = Activator.CreateInstance(propertyType);
var buffer = new byte[GetClassSize(propClass)];
@@ -272,6 +289,9 @@ namespace S7.Net.Types
case "Double":
bytes2 = Double.ToByteArray((double)propertyValue);
break;
case "Single":
bytes2 = Single.ToByteArray((float)propertyValue);
break;
default:
bytes2 = ToBytes(propertyValue);
break;

View File

@@ -18,16 +18,9 @@ namespace S7.Net.Types
}
// bytes[0] -> HighByte
// bytes[1] -> LowByte
return FromBytes(bytes[1], bytes[0]);
return (UInt16)((bytes[0] << 8) | bytes[1]);
}
/// <summary>
/// Converts a Counter (2 bytes) to ushort (UInt16)
/// </summary>
public static UInt16 FromBytes(byte LoVal, byte HiVal)
{
return (UInt16)(HiVal * 256 + LoVal);
}
/// <summary>
/// Converts a ushort (UInt16) to word (2 bytes)
@@ -35,16 +28,10 @@ namespace S7.Net.Types
public static byte[] ToByteArray(UInt16 value)
{
byte[] bytes = new byte[2];
int x = 2;
long valLong = (long)((UInt16)value);
for (int cnt = 0; cnt < x; cnt++)
{
Int64 x1 = (Int64)Math.Pow(256, (cnt));
Int64 x3 = (Int64)(valLong / x1);
bytes[x - cnt - 1] = (byte)(x3 & 255);
valLong -= bytes[x - cnt - 1] * x1;
}
bytes[0] = (byte)((value << 8) & 0xFF);
bytes[1] = (byte)((value) & 0xFF);
return bytes;
}
@@ -56,7 +43,7 @@ namespace S7.Net.Types
ByteArray arr = new ByteArray();
foreach (UInt16 val in value)
arr.Add(ToByteArray(val));
return arr.array;
return arr.Array;
}
/// <summary>

View File

@@ -16,16 +16,9 @@ namespace S7.Net.Types
{
throw new ArgumentException("Wrong number of bytes. Bytes array must contain 4 bytes.");
}
return FromBytes(bytes[3], bytes[2], bytes[1], bytes[0]);
return bytes[0] << 24 | bytes[1] << 16 | bytes[2] << 8 | bytes[3];
}
/// <summary>
/// Converts a S7 DInt (4 bytes) to int (Int32)
/// </summary>
public static Int32 FromBytes(byte v1, byte v2, byte v3, byte v4)
{
return (Int32)(v1 + v2 * Math.Pow(2, 8) + v3 * Math.Pow(2, 16) + v4 * Math.Pow(2, 24));
}
/// <summary>
/// Converts a int (Int32) to S7 DInt (4 bytes)
@@ -33,16 +26,12 @@ namespace S7.Net.Types
public static byte[] ToByteArray(Int32 value)
{
byte[] bytes = new byte[4];
int x = 4;
long valLong = (long)((Int32)value);
for (int cnt = 0; cnt < x; cnt++)
{
Int64 x1 = (Int64)Math.Pow(256, (cnt));
Int64 x3 = (Int64)(valLong / x1);
bytes[x - cnt - 1] = (byte)(x3 & 255);
valLong -= bytes[x - cnt - 1] * x1;
}
bytes[0] = (byte)((value >> 24) & 0xFF);
bytes[1] = (byte)((value >> 16) & 0xFF);
bytes[2] = (byte)((value >> 8) & 0xFF);
bytes[3] = (byte)((value) & 0xFF);
return bytes;
}
@@ -54,7 +43,7 @@ namespace S7.Net.Types
ByteArray arr = new ByteArray();
foreach (Int32 val in value)
arr.Add(ToByteArray(val));
return arr.array;
return arr.Array;
}
/// <summary>
@@ -71,18 +60,6 @@ namespace S7.Net.Types
return values;
}
/// <summary>
/// Converts from C# long (Int64) to C# int (Int32)
/// </summary>
public static Int32 CDWord(Int64 value)
{
if (value > Int32.MaxValue)
{
value -= (long)Int32.MaxValue + 1;
value = (long)Int32.MaxValue + 1 - value;
value *= -1;
}
return (int)value;
}
}
}

View File

@@ -12,36 +12,39 @@ namespace S7.Net.Types
/// </summary>
public static UInt32 FromByteArray(byte[] bytes)
{
return FromBytes(bytes[3], bytes[2], bytes[1], bytes[0]);
return (UInt32)(bytes[0] << 24 | bytes[1] << 16 | bytes[2] << 8 | bytes[3]);
}
/// <summary>
/// Converts a S7 DWord (4 bytes) to uint (UInt32)
/// Converts 4 bytes to DWord (UInt32)
/// </summary>
public static UInt32 FromBytes(byte v1, byte v2, byte v3, byte v4)
public static UInt32 FromBytes(byte b1, byte b2, byte b3, byte b4)
{
return (UInt32)(v1 + v2 * Math.Pow(2, 8) + v3 * Math.Pow(2, 16) + v4 * Math.Pow(2, 24));
return (UInt32)((b4 << 24) | (b3 << 16) | (b2 << 8) | b1);
}
/// <summary>
/// Converts a uint (UInt32) to S7 DWord (4 bytes)
/// </summary>
public static byte[] ToByteArray(UInt32 value)
{
byte[] bytes = new byte[4];
int x = 4;
long valLong = (long)((UInt32)value);
for (int cnt = 0; cnt < x; cnt++)
{
Int64 x1 = (Int64)Math.Pow(256, (cnt));
Int64 x3 = (Int64)(valLong / x1);
bytes[x - cnt - 1] = (byte)(x3 & 255);
valLong -= bytes[x - cnt - 1] * x1;
}
bytes[0] = (byte)((value >> 24) & 0xFF);
bytes[1] = (byte)((value >> 16) & 0xFF);
bytes[2] = (byte)((value >> 8) & 0xFF);
bytes[3] = (byte)((value) & 0xFF);
return bytes;
}
/// <summary>
/// Converts an array of uint (UInt32) to an array of S7 DWord (4 bytes)
/// </summary>
@@ -50,7 +53,7 @@ namespace S7.Net.Types
ByteArray arr = new ByteArray();
foreach (UInt32 val in value)
arr.Add(ToByteArray(val));
return arr.array;
return arr.Array;
}
/// <summary>

View File

@@ -1,4 +1,6 @@
namespace S7.Net.Types
using System;
namespace S7.Net.Types
{
/// <summary>
/// Create an instance of a memory block that can be read by using ReadMultipleVars
@@ -25,6 +27,11 @@
/// </summary>
public int StartByteAdr { get; set; }
/// <summary>
/// Addess of bit to read from StartByteAdr
/// </summary>
public byte BitAdr { get; set; }
/// <summary>
/// Number of variables to read
/// </summary>
@@ -43,5 +50,42 @@
VarType = VarType.Byte;
Count = 1;
}
/// <summary>
/// Create an instance of <see cref="DataItem"/> from the supplied address.
/// </summary>
/// <param name="address">The address to create the DataItem for.</param>
/// <returns>A new <see cref="DataItem"/> instance with properties parsed from <paramref name="address"/>.</returns>
/// <remarks>The <see cref="Count" /> property is not parsed from the address.</remarks>
public static DataItem FromAddress(string address)
{
PLCAddress.Parse(address, out var dataType, out var dbNumber, out var varType, out var startByte,
out var bitNumber);
return new DataItem
{
DataType = dataType,
DB = dbNumber,
VarType = varType,
StartByteAdr = startByte,
BitAdr = (byte) bitNumber
};
}
/// <summary>
/// Create an instance of <see cref="DataItem"/> from the supplied address and value.
/// </summary>
/// <param name="address">The address to create the DataItem for.</param>
/// <param name="value">The value to be applied to the DataItem.</param>
/// <returns>A new <see cref="DataItem"/> instance with properties parsed from <paramref name="address"/> and the supplied value set.</returns>
public static DataItem FromAddressAndValue<T>(string address, T value)
{
var dataItem = FromAddress(address);
dataItem.Value = value;
if (typeof(T).IsArray) dataItem.Count = ((Array) dataItem.Value).Length;
return dataItem;
}
}
}

View File

@@ -16,38 +16,15 @@ namespace S7.Net.Types
{
throw new ArgumentException("Wrong number of bytes. Bytes array must contain 4 bytes.");
}
byte v1 = bytes[0];
byte v2 = bytes[1];
byte v3 = bytes[2];
byte v4 = bytes[3];
if ((int)v1 + v2 + v3 + v4 == 0)
// sps uses bigending so we have to reverse if platform needs
if (BitConverter.IsLittleEndian)
{
return 0.0;
// create deep copy of the array and reverse
bytes = new byte[] { bytes[3], bytes[2], bytes[1], bytes[0] };
}
else
{
// nun String bilden
string txt = ValToBinString(v1) + ValToBinString(v2) + ValToBinString(v3) + ValToBinString(v4);
// erstmal das Vorzeichen
int vz = int.Parse(txt.Substring(0, 1));
int exd = Conversion.BinStringToInt32(txt.Substring(1, 8));
string ma = txt.Substring(9, 23);
double mantisse = 1;
double faktor = 1.0;
//das ist die Anzahl der restlichen bit's
for (int cnt = 0; cnt <= 22; cnt++)
{
faktor = faktor / 2.0;
//entspricht 2^-y
if (ma.Substring(cnt, 1) == "1")
{
mantisse = mantisse + faktor;
}
}
return Math.Pow((-1), vz) * Math.Pow(2, (exd - 127)) * mantisse;
}
return BitConverter.ToSingle(bytes, 0);
}
/// <summary>
@@ -76,48 +53,13 @@ namespace S7.Net.Types
/// </summary>
public static byte[] ToByteArray(double value)
{
double wert = (double)value;
string binString = "";
byte[] bytes = new byte[4];
if (wert != 0f)
{
if (wert < 0)
{
wert *= -1;
binString = "1";
}
else
{
binString = "0";
}
int exponent = (int)Math.Floor((double)Math.Log(wert) / Math.Log(2.0));
wert = wert / (Math.Pow(2, exponent)) - 1;
byte[] bytes = BitConverter.GetBytes((float)(value));
binString += ValToBinString((byte)(exponent + 127));
for (int cnt = 1; cnt <= 23; cnt++)
{
if (!(wert - System.Math.Pow(2, -cnt) < 0))
{
wert = wert - System.Math.Pow(2, -cnt);
binString += "1";
}
else
binString += "0";
}
bytes[0] = (byte)BinStringToByte(binString.Substring(0, 8));
bytes[1] = (byte)BinStringToByte(binString.Substring(8, 8));
bytes[2] = (byte)BinStringToByte(binString.Substring(16, 8));
bytes[3] = (byte)BinStringToByte(binString.Substring(24, 8));
}
else
{
bytes[0] = 0;
bytes[1] = 0;
bytes[2] = 0;
bytes[3] = 0;
}
return bytes;
// sps uses bigending so we have to check if platform is same
if (!BitConverter.IsLittleEndian) return bytes;
// create deep copy of the array and reverse
return new byte[] { bytes[3], bytes[2], bytes[1], bytes[0] };
}
/// <summary>
@@ -128,7 +70,7 @@ namespace S7.Net.Types
ByteArray arr = new ByteArray();
foreach (double val in value)
arr.Add(ToByteArray(val));
return arr.array;
return arr.Array;
}
/// <summary>
@@ -145,38 +87,5 @@ namespace S7.Net.Types
return values;
}
private static string ValToBinString(byte value)
{
string txt = "";
for (int cnt = 7; cnt >= 0; cnt += -1)
{
if ((value & (byte)Math.Pow(2, cnt)) > 0)
txt += "1";
else
txt += "0";
}
return txt;
}
private static byte? BinStringToByte(string txt)
{
int cnt = 0;
int ret = 0;
if (txt.Length == 8)
{
for (cnt = 7; cnt >= 0; cnt += -1)
{
if (int.Parse(txt.Substring(cnt, 1)) == 1)
{
ret += (int)(Math.Pow(2, (txt.Length - 1 - cnt)));
}
}
return (byte)ret;
}
return null;
}
}
}

View File

@@ -10,7 +10,7 @@ namespace S7.Net.Types
/// <summary>
/// Converts a S7 Int (2 bytes) to short (Int16)
/// </summary>
public static Int16 FromByteArray(byte[] bytes)
public static short FromByteArray(byte[] bytes)
{
if (bytes.Length != 2)
{
@@ -18,16 +18,9 @@ namespace S7.Net.Types
}
// bytes[0] -> HighByte
// bytes[1] -> LowByte
return FromBytes(bytes[1], bytes[0]);
return (short)((int)(bytes[1]) | ((int)(bytes[0]) << 8));
}
/// <summary>
/// Converts a S7 Int (2 bytes) to short (Int16)
/// </summary>
public static Int16 FromBytes(byte LoVal, byte HiVal)
{
return (Int16)(HiVal * 256 + LoVal);
}
/// <summary>
/// Converts a short (Int16) to a S7 Int byte array (2 bytes)
@@ -35,16 +28,10 @@ namespace S7.Net.Types
public static byte[] ToByteArray(Int16 value)
{
byte[] bytes = new byte[2];
int x = 2;
long valLong = (long)((Int16)value);
for (int cnt = 0; cnt < x; cnt++)
{
Int64 x1 = (Int64)Math.Pow(256, (cnt));
Int64 x3 = (Int64)(valLong / x1);
bytes[x - cnt - 1] = (byte)(x3 & 255);
valLong -= bytes[x - cnt - 1] * x1;
}
bytes[0] = (byte) (value >> 8 & 0xFF);
bytes[1] = (byte)(value & 0xFF);
return bytes;
}
@@ -53,10 +40,15 @@ namespace S7.Net.Types
/// </summary>
public static byte[] ToByteArray(Int16[] value)
{
ByteArray arr = new ByteArray();
foreach (Int16 val in value)
arr.Add(ToByteArray(val));
return arr.array;
byte[] bytes = new byte[value.Length * 2];
int bytesPos = 0;
for(int i=0; i< value.Length; i++)
{
bytes[bytesPos++] = (byte)((value[i] >> 8) & 0xFF);
bytes[bytesPos++] = (byte) (value[i] & 0xFF);
}
return bytes;
}
/// <summary>
@@ -64,10 +56,12 @@ namespace S7.Net.Types
/// </summary>
public static Int16[] ToArray(byte[] bytes)
{
Int16[] values = new Int16[bytes.Length / 2];
int shortsCount = bytes.Length / 2;
Int16[] values = new Int16[shortsCount];
int counter = 0;
for (int cnt = 0; cnt < bytes.Length / 2; cnt++)
for (int cnt = 0; cnt < shortsCount; cnt++)
values[cnt] = FromByteArray(new byte[] { bytes[counter++], bytes[counter++] });
return values;

91
S7.Net/Types/Single.cs Normal file
View File

@@ -0,0 +1,91 @@
using System;
namespace S7.Net.Types
{
/// <summary>
/// Contains the conversion methods to convert Real from S7 plc to C# float.
/// </summary>
public static class Single
{
/// <summary>
/// Converts a S7 Real (4 bytes) to float
/// </summary>
public static float FromByteArray(byte[] bytes)
{
if (bytes.Length != 4)
{
throw new ArgumentException("Wrong number of bytes. Bytes array must contain 4 bytes.");
}
// sps uses bigending so we have to reverse if platform needs
if (BitConverter.IsLittleEndian)
{
// create deep copy of the array and reverse
bytes = new byte[] { bytes[3], bytes[2], bytes[1], bytes[0] };
}
return BitConverter.ToSingle(bytes, 0);
}
/// <summary>
/// Converts a S7 DInt to float
/// </summary>
public static float FromDWord(Int32 value)
{
byte[] b = DInt.ToByteArray(value);
float d = FromByteArray(b);
return d;
}
/// <summary>
/// Converts a S7 DWord to float
/// </summary>
public static float FromDWord(UInt32 value)
{
byte[] b = DWord.ToByteArray(value);
float d = FromByteArray(b);
return d;
}
/// <summary>
/// Converts a double to S7 Real (4 bytes)
/// </summary>
public static byte[] ToByteArray(float value)
{
byte[] bytes = BitConverter.GetBytes((float)(value));
// sps uses bigending so we have to check if platform is same
if (!BitConverter.IsLittleEndian) return bytes;
// create deep copy of the array and reverse
return new byte[] { bytes[3], bytes[2], bytes[1], bytes[0] };
}
/// <summary>
/// Converts an array of float to an array of bytes
/// </summary>
public static byte[] ToByteArray(float[] value)
{
ByteArray arr = new ByteArray();
foreach (float val in value)
arr.Add(ToByteArray(val));
return arr.Array;
}
/// <summary>
/// Converts an array of S7 Real to an array of float
/// </summary>
public static float[] ToArray(byte[] bytes)
{
float[] values = new float[bytes.Length / 4];
int counter = 0;
for (int cnt = 0; cnt < bytes.Length / 4; cnt++)
values[cnt] = FromByteArray(new byte[] { bytes[counter++], bytes[counter++], bytes[counter++], bytes[counter++] });
return values;
}
}
}

View File

@@ -3,18 +3,23 @@
/// <summary>
/// Contains the methods to convert from S7 strings to C# strings
/// </summary>
public static class String
public class String
{
/// <summary>
/// Converts a string to S7 bytes
/// Converts a string to <paramref name="reservedLength"/> of bytes, padded with 0-bytes if required.
/// </summary>
public static byte[] ToByteArray(string value)
/// <param name="value">The string to write to the PLC.</param>
/// <param name="reservedLength">The amount of bytes reserved for the <paramref name="value"/> in the PLC.</param>
public static byte[] ToByteArray(string value, int reservedLength)
{
string txt = (string)value;
char[] ca = txt.ToCharArray();
byte[] bytes = new byte[txt.Length];
for (int cnt = 0; cnt <= ca.Length - 1; cnt++)
bytes[cnt] = (byte)Asc(ca[cnt].ToString());
var length = value?.Length;
if (length > reservedLength) length = reservedLength;
var bytes = new byte[reservedLength];
if (length == null || length == 0) return bytes;
System.Text.Encoding.ASCII.GetBytes(value, 0, length.Value, bytes, 0);
return bytes;
}
@@ -27,13 +32,6 @@
{
return System.Text.Encoding.ASCII.GetString(bytes);
}
private static int Asc(string s)
{
byte[] b = System.Text.Encoding.ASCII.GetBytes(s);
if (b.Length > 0)
return b[0];
return 0;
}
}
}

50
S7.Net/Types/StringEx.cs Normal file
View File

@@ -0,0 +1,50 @@
using System;
using System.Text;
namespace S7.Net.Types
{
/// <summary>
/// Contains the methods to convert from S7 strings to C# strings
/// there are two kinds how strings a send. This one is with a pre of two bytes
/// they contain the length of the string
/// </summary>
public static class StringEx
{
/// <summary>
/// Converts S7 bytes to a string
/// </summary>
/// <param name="bytes"></param>
/// <returns></returns>
public static string FromByteArray(byte[] bytes)
{
if (bytes.Length < 2) return "";
int size = bytes[0];
int length = bytes[1];
return System.Text.Encoding.ASCII.GetString(bytes, 2, length);
}
/// <summary>
/// Converts a <see cref="T:string"/> to S7 string with 2-byte header.
/// </summary>
/// <param name="value">The string to convert to byte array.</param>
/// <param name="reservedLength">The length (in bytes) allocated in PLC for string excluding header.</param>
/// <returns>A <see cref="T:byte[]" /> containing the string header and string value with a maximum length of <paramref name="reservedLength"/> + 2.</returns>
public static byte[] ToByteArray(string value, int reservedLength)
{
if (reservedLength > byte.MaxValue) throw new ArgumentException($"The maximum string length supported is {byte.MaxValue}.");
var length = value?.Length;
if (length > reservedLength) length = reservedLength;
var bytes = new byte[(length ?? 0) + 2];
bytes[0] = (byte) reservedLength;
if (value == null) return bytes;
bytes[1] = (byte) Encoding.ASCII.GetBytes(value, 0, length.Value, bytes, 2);
return bytes;
}
}
}

View File

@@ -1,6 +1,4 @@
using System;
using System.Linq;
using System.Globalization;
using System.Reflection;
namespace S7.Net.Types
@@ -19,8 +17,14 @@ namespace S7.Net.Types
{
double numBytes = 0.0;
System.Reflection.FieldInfo[] infos = structType.GetFields();
foreach (System.Reflection.FieldInfo info in infos)
var infos = structType
#if NETSTANDARD1_3
.GetTypeInfo().DeclaredFields;
#else
.GetFields();
#endif
foreach (var info in infos)
{
switch (info.FieldType.Name)
{
@@ -45,7 +49,7 @@ namespace S7.Net.Types
numBytes++;
numBytes += 4;
break;
case "Float":
case "Single":
case "Double":
numBytes = Math.Ceiling(numBytes);
if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0)
@@ -80,8 +84,15 @@ namespace S7.Net.Types
double numBytes = 0.0;
object structValue = Activator.CreateInstance(structType);
System.Reflection.FieldInfo[] infos = structValue.GetType().GetFields();
foreach (System.Reflection.FieldInfo info in infos)
var infos = structValue.GetType()
#if NETSTANDARD1_3
.GetTypeInfo().DeclaredFields;
#else
.GetFields();
#endif
foreach (var info in infos)
{
switch (info.FieldType.Name)
{
@@ -126,8 +137,8 @@ namespace S7.Net.Types
uint sourceUInt = DWord.FromBytes(bytes[(int)numBytes + 3],
bytes[(int)numBytes + 2],
bytes[(int)numBytes + 1],
bytes[(int)numBytes + 0]);
info.SetValue(structValue, sourceUInt.ConvertToInt());
bytes[(int)numBytes + 0]);
info.SetValue(structValue, sourceUInt.ConvertToInt());
numBytes += 4;
break;
case "UInt32":
@@ -152,6 +163,17 @@ namespace S7.Net.Types
bytes[(int)numBytes + 3] }));
numBytes += 4;
break;
case "Single":
numBytes = Math.Ceiling(numBytes);
if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0)
numBytes++;
// hier auswerten
info.SetValue(structValue, Single.FromByteArray(new byte[] { bytes[(int)numBytes],
bytes[(int)numBytes + 1],
bytes[(int)numBytes + 2],
bytes[(int)numBytes + 3] }));
numBytes += 4;
break;
default:
var buffer = new byte[GetStructSize(info.FieldType)];
if (buffer.Length == 0)
@@ -182,8 +204,14 @@ namespace S7.Net.Types
int bitPos = 0;
double numBytes = 0.0;
System.Reflection.FieldInfo[] infos = type.GetFields();
foreach (System.Reflection.FieldInfo info in infos)
var infos = type
#if NETSTANDARD1_3
.GetTypeInfo().DeclaredFields;
#else
.GetFields();
#endif
foreach (var info in infos)
{
bytes2 = null;
switch (info.FieldType.Name)
@@ -219,6 +247,9 @@ namespace S7.Net.Types
case "Double":
bytes2 = Double.ToByteArray((double)info.GetValue(structValue));
break;
case "Single":
bytes2 = Single.ToByteArray((float)info.GetValue(structValue));
break;
}
if (bytes2 != null)
{
@@ -227,7 +258,7 @@ namespace S7.Net.Types
if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0)
numBytes++;
bytePos = (int)numBytes;
for (int bCnt=0; bCnt<bytes2.Length; bCnt++)
for (int bCnt = 0; bCnt < bytes2.Length; bCnt++)
bytes[bytePos + bCnt] = bytes2[bCnt];
numBytes += bytes2.Length;
}
@@ -235,6 +266,6 @@ namespace S7.Net.Types
return bytes;
}
}
}

View File

@@ -13,26 +13,30 @@ namespace S7.Net.Types
public static double FromByteArray(byte[] bytes)
{
double wert = 0;
Int16 value = (Int16)Types.Word.FromBytes(bytes[1], bytes[0]);
string txt = Conversion.ValToBinString(value);
wert = Conversion.BinStringToInt32(txt.Substring(4, 4)) * 100.0;
wert += Conversion.BinStringToInt32(txt.Substring(8, 4)) * 10.0;
wert += Conversion.BinStringToInt32(txt.Substring(12, 4));
switch (txt.Substring(2, 2))
wert = ((bytes[0]) & 0x0F) * 100.0;
wert += ((bytes[1] >> 4) & 0x0F) * 10.0;
wert += ((bytes[1]) & 0x0F) * 1.0;
// this value is not used... may for a nother exponation
//int unknown = (bytes[0] >> 6) & 0x03;
switch ((bytes[0] >> 4) & 0x03)
{
case "00":
case 0:
wert *= 0.01;
break;
case "01":
case 1:
wert *= 0.1;
break;
case "10":
case 2:
wert *= 1.0;
break;
case "11":
case 3:
wert *= 10.0;
break;
}
return wert;
}
@@ -42,16 +46,9 @@ namespace S7.Net.Types
public static byte[] ToByteArray(UInt16 value)
{
byte[] bytes = new byte[2];
int x = 2;
long valLong = (long)((UInt16)value);
for (int cnt = 0; cnt < x; cnt++)
{
Int64 x1 = (Int64)Math.Pow(256, (cnt));
bytes[1] = (byte)((int)value & 0xFF);
bytes[0] = (byte)((int)value >> 8 & 0xFF);
Int64 x3 = (Int64)(valLong / x1);
bytes[x - cnt - 1] = (byte)(x3 & 255);
valLong -= bytes[x - cnt - 1] * x1;
}
return bytes;
}
@@ -63,7 +60,7 @@ namespace S7.Net.Types
ByteArray arr = new ByteArray();
foreach (UInt16 val in value)
arr.Add(ToByteArray(val));
return arr.array;
return arr.Array;
}
/// <summary>

View File

@@ -16,35 +16,30 @@ namespace S7.Net.Types
{
throw new ArgumentException("Wrong number of bytes. Bytes array must contain 2 bytes.");
}
// bytes[0] -> HighByte
// bytes[1] -> LowByte
return FromBytes(bytes[1], bytes[0]);
return (UInt16)((bytes[0] << 8) | bytes[1]);
}
/// <summary>
/// Converts a word (2 bytes) to ushort (UInt16)
/// Converts 2 bytes to ushort (UInt16)
/// </summary>
public static UInt16 FromBytes(byte LoVal, byte HiVal)
public static UInt16 FromBytes(byte b1, byte b2)
{
return (UInt16) (HiVal*256 + LoVal);
return (UInt16)((b2 << 8) | b1);
}
/// <summary>
/// Converts a ushort (UInt16) to word (2 bytes)
/// </summary>
public static byte[] ToByteArray(UInt16 value)
{
byte[] bytes = new byte[2];
int x = 2;
long valLong = (long) ((UInt16) value);
for (int cnt = 0; cnt < x; cnt++)
{
Int64 x1 = (Int64) Math.Pow(256, (cnt));
Int64 x3 = (Int64) (valLong/x1);
bytes[x - cnt - 1] = (byte) (x3 & 255);
valLong -= bytes[x - cnt - 1]*x1;
}
bytes[1] = (byte)(value & 0xFF);
bytes[0] = (byte)((value>>8) & 0xFF);
return bytes;
}
@@ -56,7 +51,7 @@ namespace S7.Net.Types
ByteArray arr = new ByteArray();
foreach (UInt16 val in value)
arr.Add(ToByteArray(val));
return arr.array;
return arr.Array;
}
/// <summary>

View File

@@ -1,45 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.24720.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7A8252C3-E6AE-435A-809D-4413C06E0711}"
ProjectSection(SolutionItems) = preProject
README.md = README.md
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "S7.Net.Core", "S7.Net.Core\S7.Net.Core.csproj", "{CBFF80E8-3D3D-4656-A27C-A65EA5774536}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|ARM = Debug|ARM
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|ARM = Release|ARM
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{CBFF80E8-3D3D-4656-A27C-A65EA5774536}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CBFF80E8-3D3D-4656-A27C-A65EA5774536}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CBFF80E8-3D3D-4656-A27C-A65EA5774536}.Debug|ARM.ActiveCfg = Debug|ARM
{CBFF80E8-3D3D-4656-A27C-A65EA5774536}.Debug|ARM.Build.0 = Debug|ARM
{CBFF80E8-3D3D-4656-A27C-A65EA5774536}.Debug|x64.ActiveCfg = Debug|x64
{CBFF80E8-3D3D-4656-A27C-A65EA5774536}.Debug|x64.Build.0 = Debug|x64
{CBFF80E8-3D3D-4656-A27C-A65EA5774536}.Debug|x86.ActiveCfg = Debug|x86
{CBFF80E8-3D3D-4656-A27C-A65EA5774536}.Debug|x86.Build.0 = Debug|x86
{CBFF80E8-3D3D-4656-A27C-A65EA5774536}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CBFF80E8-3D3D-4656-A27C-A65EA5774536}.Release|Any CPU.Build.0 = Release|Any CPU
{CBFF80E8-3D3D-4656-A27C-A65EA5774536}.Release|ARM.ActiveCfg = Release|ARM
{CBFF80E8-3D3D-4656-A27C-A65EA5774536}.Release|ARM.Build.0 = Release|ARM
{CBFF80E8-3D3D-4656-A27C-A65EA5774536}.Release|x64.ActiveCfg = Release|x64
{CBFF80E8-3D3D-4656-A27C-A65EA5774536}.Release|x64.Build.0 = Release|x64
{CBFF80E8-3D3D-4656-A27C-A65EA5774536}.Release|x86.ActiveCfg = Release|x86
{CBFF80E8-3D3D-4656-A27C-A65EA5774536}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

9
S7.sln
View File

@@ -1,7 +1,9 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2012
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "S7.Net", "S7.Net\S7.Net.csproj", "{BFD484F9-3F04-42A2-BF2A-60A189A25DCF}"
# Visual Studio 15
VisualStudioVersion = 15.0.27703.2026
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "S7.Net", "S7.Net\S7.Net.csproj", "{BFD484F9-3F04-42A2-BF2A-60A189A25DCF}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7A8252C3-E6AE-435A-809D-4413C06E0711}"
ProjectSection(SolutionItems) = preProject
@@ -28,4 +30,7 @@ Global
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {F33D8F72-56A0-47E4-B37A-497E01977AD4}
EndGlobalSection
EndGlobal

13
appveyor.yml Normal file
View File

@@ -0,0 +1,13 @@
image: Visual Studio 2017
configuration: Release
install:
- choco install gitversion.portable -y
before_build:
- cmd: gitversion /l console /output buildserver /b %APPVEYOR_REPO_BRANCH%
- nuget restore
build_script:
msbuild /nologo /v:m /p:AssemblyVersion=%GitVersion_AssemblySemVer% /p:FileVersion=%GitVersion_MajorMinorPatch% /p:InformationalVersion=%GitVersion_InformationalVersion% /p:Configuration=%CONFIGURATION% S7.sln
after_build:
- dotnet pack S7.Net -c %CONFIGURATION% /p:Version=%GitVersion_NuGetVersion% --no-build -o ..\artifacts
artifacts:
- path: artifacts\*.*

View File

@@ -1 +0,0 @@
+lib/

View File

@@ -1,17 +0,0 @@
<?xml version="1.0"?>
<package>
<metadata>
<id>S7netplus</id>
<version>$version$</version>
<title>S7.Net Plus</title>
<authors>Derek Heiser</authors>
<owners>Derek Heiser</owners>
<projectUrl>https://github.com/killnine/s7netplus</projectUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>A continuation of Juergen1969's Siemens communication library.</description>
<tags>PLC Siemens Communication S7</tags>
<language>en-US</language>
<copyright>Derek Heiser 2015</copyright>
<dependencies></dependencies>
</metadata>
</package>

Binary file not shown.

View File

@@ -1,917 +0,0 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>S7.Net</name>
</assembly>
<members>
<member name="T:S7.Net.Conversion">
<summary>
Conversion methods to convert from Siemens numeric format to C# and back
</summary>
</member>
<member name="M:S7.Net.Conversion.BinStringToInt32(System.String)">
<summary>
Converts a binary string to Int32 value
</summary>
<param name="txt"></param>
<returns></returns>
</member>
<member name="M:S7.Net.Conversion.BinStringToByte(System.String)">
<summary>
Converts a binary string to a byte. Can return null.
</summary>
<param name="txt"></param>
<returns></returns>
</member>
<member name="M:S7.Net.Conversion.ValToBinString(System.Object)">
<summary>
Converts the value to a binary string
</summary>
<param name="value"></param>
<returns></returns>
</member>
<member name="M:S7.Net.Conversion.SelectBit(System.Byte,System.Int32)">
<summary>
Helper to get a bit value given a byte and the bit index.
Example: DB1.DBX0.5 -> var bytes = ReadBytes(DB1.DBW0); bool bit = bytes[0].SelectBit(5);
</summary>
<param name="data"></param>
<param name="bitPosition"></param>
<returns></returns>
</member>
<member name="M:S7.Net.Conversion.ConvertToShort(System.UInt16)">
<summary>
Converts from ushort value to short value; it's used to retrieve negative values from words
</summary>
<param name="input"></param>
<returns></returns>
</member>
<member name="M:S7.Net.Conversion.ConvertToUshort(System.Int16)">
<summary>
Converts from short value to ushort value; it's used to pass negative values to DWs
</summary>
<param name="input"></param>
<returns></returns>
</member>
<member name="M:S7.Net.Conversion.ConvertToInt(System.UInt32)">
<summary>
Converts from UInt32 value to Int32 value; it's used to retrieve negative values from DBDs
</summary>
<param name="input"></param>
<returns></returns>
</member>
<member name="M:S7.Net.Conversion.ConvertToUInt(System.Int32)">
<summary>
Converts from Int32 value to UInt32 value; it's used to pass negative values to DBDs
</summary>
<param name="input"></param>
<returns></returns>
</member>
<member name="M:S7.Net.Conversion.ConvertToUInt(System.Double)">
<summary>
Converts from double to DWord (DBD)
</summary>
<param name="input"></param>
<returns></returns>
</member>
<member name="M:S7.Net.Conversion.ConvertToDouble(System.UInt32)">
<summary>
Converts from DWord (DBD) to double
</summary>
<param name="input"></param>
<returns></returns>
</member>
<member name="T:S7.Net.CpuType">
<summary>
Types of S7 cpu supported by the library
</summary>
</member>
<member name="F:S7.Net.CpuType.S7200">
<summary>
S7 200 cpu type
</summary>
</member>
<member name="F:S7.Net.CpuType.S7300">
<summary>
S7 300 cpu type
</summary>
</member>
<member name="F:S7.Net.CpuType.S7400">
<summary>
S7 400 cpu type
</summary>
</member>
<member name="F:S7.Net.CpuType.S71200">
<summary>
S7 1200 cpu type
</summary>
</member>
<member name="F:S7.Net.CpuType.S71500">
<summary>
S7 1500 cpu type
</summary>
</member>
<member name="T:S7.Net.ErrorCode">
<summary>
Types of error code that can be set after a function is called
</summary>
</member>
<member name="F:S7.Net.ErrorCode.NoError">
<summary>
The function has been executed correctly
</summary>
</member>
<member name="F:S7.Net.ErrorCode.WrongCPU_Type">
<summary>
Wrong type of CPU error
</summary>
</member>
<member name="F:S7.Net.ErrorCode.ConnectionError">
<summary>
Connection error
</summary>
</member>
<member name="F:S7.Net.ErrorCode.IPAddressNotAvailable">
<summary>
Ip address not available
</summary>
</member>
<member name="F:S7.Net.ErrorCode.WrongVarFormat">
<summary>
Wrong format of the variable
</summary>
</member>
<member name="F:S7.Net.ErrorCode.WrongNumberReceivedBytes">
<summary>
Wrong number of received bytes
</summary>
</member>
<member name="F:S7.Net.ErrorCode.SendData">
<summary>
Error on send data
</summary>
</member>
<member name="F:S7.Net.ErrorCode.ReadData">
<summary>
Error on read data
</summary>
</member>
<member name="F:S7.Net.ErrorCode.WriteData">
<summary>
Error on write data
</summary>
</member>
<member name="T:S7.Net.DataType">
<summary>
Types of memory area that can be read
</summary>
</member>
<member name="F:S7.Net.DataType.Input">
<summary>
Input area memory
</summary>
</member>
<member name="F:S7.Net.DataType.Output">
<summary>
Output area memory
</summary>
</member>
<member name="F:S7.Net.DataType.Memory">
<summary>
Merkers area memory (M0, M0.0, ...)
</summary>
</member>
<member name="F:S7.Net.DataType.DataBlock">
<summary>
DB area memory (DB1, DB2, ...)
</summary>
</member>
<member name="F:S7.Net.DataType.Timer">
<summary>
Timer area memory(T1, T2, ...)
</summary>
</member>
<member name="F:S7.Net.DataType.Counter">
<summary>
Counter area memory (C1, C2, ...)
</summary>
</member>
<member name="T:S7.Net.VarType">
<summary>
Types
</summary>
</member>
<member name="F:S7.Net.VarType.Bit">
<summary>
S7 Bit variable type (bool)
</summary>
</member>
<member name="F:S7.Net.VarType.Byte">
<summary>
S7 Byte variable type (8 bits)
</summary>
</member>
<member name="F:S7.Net.VarType.Word">
<summary>
S7 Word variable type (16 bits, 2 bytes)
</summary>
</member>
<member name="F:S7.Net.VarType.DWord">
<summary>
S7 DWord variable type (32 bits, 4 bytes)
</summary>
</member>
<member name="F:S7.Net.VarType.Int">
<summary>
S7 Int variable type (16 bits, 2 bytes)
</summary>
</member>
<member name="F:S7.Net.VarType.DInt">
<summary>
DInt variable type (32 bits, 4 bytes)
</summary>
</member>
<member name="F:S7.Net.VarType.Real">
<summary>
Real variable type (32 bits, 4 bytes)
</summary>
</member>
<member name="F:S7.Net.VarType.String">
<summary>
String variable type (variable)
</summary>
</member>
<member name="F:S7.Net.VarType.Timer">
<summary>
Timer variable type
</summary>
</member>
<member name="F:S7.Net.VarType.Counter">
<summary>
Counter variable type
</summary>
</member>
<member name="T:S7.Net.Plc">
<summary>
Creates an instance of S7.Net driver
</summary>
</member>
<member name="P:S7.Net.Plc.IP">
<summary>
Ip address of the plc
</summary>
</member>
<member name="P:S7.Net.Plc.CPU">
<summary>
Cpu type of the plc
</summary>
</member>
<member name="P:S7.Net.Plc.Rack">
<summary>
Rack of the plc
</summary>
</member>
<member name="P:S7.Net.Plc.Slot">
<summary>
Slot of the CPU of the plc
</summary>
</member>
<member name="P:S7.Net.Plc.IsAvailable">
<summary>
Returns true if a connection to the plc can be established
</summary>
</member>
<member name="P:S7.Net.Plc.IsConnected">
<summary>
Checks if the socket is connected and polls the other peer (the plc) to see if it's connected.
This is the variable that you should continously check to see if the communication is working
See also: http://stackoverflow.com/questions/2661764/how-to-check-if-a-socket-is-connected-disconnected-in-c
</summary>
</member>
<member name="P:S7.Net.Plc.LastErrorString">
<summary>
Contains the last error registered when executing a function
</summary>
</member>
<member name="P:S7.Net.Plc.LastErrorCode">
<summary>
Contains the last error code registered when executing a function
</summary>
</member>
<member name="M:S7.Net.Plc.#ctor(S7.Net.CpuType,System.String,System.Int16,System.Int16)">
<summary>
Creates a PLC object with all the parameters needed for connections.
For S7-1200 and S7-1500, the default is rack = 0 and slot = 0.
You need slot > 0 if you are connecting to external ethernet card (CP).
For S7-300 and S7-400 the default is rack = 0 and slot = 2.
</summary>
<param name="cpu">CpuType of the plc (select from the enum)</param>
<param name="ip">Ip address of the plc</param>
<param name="rack">rack of the plc, usually it's 0, but check in the hardware configuration of Step7 or TIA portal</param>
<param name="slot">slot of the CPU of the plc, usually it's 2 for S7300-S7400, 0 for S7-1200 and S7-1500.
If you use an external ethernet card, this must be set accordingly.</param>
</member>
<member name="M:S7.Net.Plc.Open">
<summary>
Open a socket and connects to the plc, sending all the corrected package and returning if the connection was successful (ErroreCode.NoError) of it was wrong.
</summary>
<returns>Returns ErrorCode.NoError if the connection was successful, otherwise check the ErrorCode</returns>
</member>
<member name="M:S7.Net.Plc.Close">
<summary>
Disonnects from the plc and close the socket
</summary>
</member>
<member name="M:S7.Net.Plc.ReadMultipleVars(System.Collections.Generic.List{S7.Net.Types.DataItem})">
<summary>
Reads multiple vars in a single request.
You have to create and pass a list of DataItems and you obtain in response the same list with the values.
Values are stored in the property "Value" of the dataItem and are already converted.
If you don't want the conversion, just create a dataItem of bytes.
DataItems must not be more than 20 (protocol restriction) and bytes must not be more than 200 + 22 of header (protocol restriction).
</summary>
<param name="dataItems">List of dataitems that contains the list of variables that must be read. Maximum 20 dataitems are accepted.</param>
</member>
<member name="M:S7.Net.Plc.ReadBytes(S7.Net.DataType,System.Int32,System.Int32,System.Int32)">
<summary>
Reads a number of bytes from a DB starting from a specified index. This handles more than 200 bytes with multiple requests.
If the read was not successful, check LastErrorCode or LastErrorString.
</summary>
<param name="dataType">Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output.</param>
<param name="db">Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc.</param>
<param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
<param name="count">Byte count, if you want to read 120 bytes, set this to 120.</param>
<returns>Returns the bytes in an array</returns>
</member>
<member name="M:S7.Net.Plc.Read(S7.Net.DataType,System.Int32,System.Int32,S7.Net.VarType,System.Int32)">
<summary>
Read and decode a certain number of bytes of the "VarType" provided.
This can be used to read multiple consecutive variables of the same type (Word, DWord, Int, etc).
If the read was not successful, check LastErrorCode or LastErrorString.
</summary>
<param name="dataType">Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output.</param>
<param name="db">Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc.</param>
<param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
<param name="varType">Type of the variable/s that you are reading</param>
<param name="varCount"></param>
</member>
<member name="M:S7.Net.Plc.Read(System.String)">
<summary>
Reads a single variable from the plc, takes in input strings like "DB1.DBX0.0", "DB20.DBD200", "MB20", "T45", etc.
If the read was not successful, check LastErrorCode or LastErrorString.
</summary>
<param name="variable">Input strings like "DB1.DBX0.0", "DB20.DBD200", "MB20", "T45", etc.</param>
<returns>Returns an object that contains the value. This object must be cast accordingly.</returns>
</member>
<member name="M:S7.Net.Plc.ReadStruct(System.Type,System.Int32,System.Int32)">
<summary>
Reads all the bytes needed to fill a struct in C#, starting from a certain address, and return an object that can be casted to the struct.
</summary>
<param name="structType">Type of the struct to be readed (es.: TypeOf(MyStruct)).</param>
<param name="db">Address of the DB.</param>
<param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
<returns>Returns a struct that must be cast.</returns>
</member>
<member name="M:S7.Net.Plc.ReadStruct``1(System.Int32,System.Int32)">
<summary>
Reads all the bytes needed to fill a struct in C#, starting from a certain address, and returns the struct or null if nothing was read.
</summary>
<typeparam name="T">The struct type</typeparam>
<param name="db">Address of the DB.</param>
<param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
<returns>Returns a nulable struct. If nothing was read null will be returned.</returns>
</member>
<member name="M:S7.Net.Plc.ReadClass(System.Object,System.Int32,System.Int32)">
<summary>
Reads all the bytes needed to fill a class in C#, starting from a certain address, and set all the properties values to the value that are read from the plc.
This reads only properties, it doesn't read private variable or public variable without {get;set;} specified.
</summary>
<param name="sourceClass">Instance of the class that will store the values</param>
<param name="db">Index of the DB; es.: 1 is for DB1</param>
<param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
<returns>The number of read bytes</returns>
</member>
<member name="M:S7.Net.Plc.ReadClass``1(System.Int32,System.Int32)">
<summary>
Reads all the bytes needed to fill a class in C#, starting from a certain address, and set all the properties values to the value that are read from the plc.
This reads only properties, it doesn't read private variable or public variable without {get;set;} specified. To instantiate the class defined by the generic
type, the class needs a default constructor.
</summary>
<typeparam name="T">The class that will be instantiated. Requires a default constructor</typeparam>
<param name="db">Index of the DB; es.: 1 is for DB1</param>
<param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
<returns>An instance of the class with the values read from the plc. If no data has been read, null will be returned</returns>
</member>
<member name="M:S7.Net.Plc.ReadClass``1(System.Func{``0},System.Int32,System.Int32)">
<summary>
Reads all the bytes needed to fill a class in C#, starting from a certain address, and set all the properties values to the value that are read from the plc.
This reads only properties, it doesn't read private variable or public variable without {get;set;} specified.
</summary>
<typeparam name="T">The class that will be instantiated</typeparam>
<param name="classFactory">Function to instantiate the class</param>
<param name="db">Index of the DB; es.: 1 is for DB1</param>
<param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
<returns>An instance of the class with the values read from the plc. If no data has been read, null will be returned</returns>
</member>
<member name="M:S7.Net.Plc.WriteBytes(S7.Net.DataType,System.Int32,System.Int32,System.Byte[])">
<summary>
Write a number of bytes from a DB starting from a specified index. This handles more than 200 bytes with multiple requests.
If the write was not successful, check LastErrorCode or LastErrorString.
</summary>
<param name="dataType">Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output.</param>
<param name="db">Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc.</param>
<param name="startByteAdr">Start byte address. If you want to write DB1.DBW200, this is 200.</param>
<param name="value">Bytes to write. If more than 200, multiple requests will be made.</param>
<returns>NoError if it was successful, or the error is specified</returns>
</member>
<member name="M:S7.Net.Plc.WriteBit(S7.Net.DataType,System.Int32,System.Int32,System.Int32,System.Boolean)">
<summary>
Write a single bit from a DB with the specified index.
</summary>
<param name="dataType">Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output.</param>
<param name="db">Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc.</param>
<param name="startByteAdr">Start byte address. If you want to write DB1.DBW200, this is 200.</param>
<param name="bitAdr">The address of the bit. (0-7)</param>
<param name="value">Bytes to write. If more than 200, multiple requests will be made.</param>
<returns>NoError if it was successful, or the error is specified</returns>
</member>
<member name="M:S7.Net.Plc.WriteBit(S7.Net.DataType,System.Int32,System.Int32,System.Int32,System.Int32)">
<summary>
Write a single bit from a DB with the specified index.
</summary>
<param name="dataType">Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output.</param>
<param name="db">Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc.</param>
<param name="startByteAdr">Start byte address. If you want to write DB1.DBW200, this is 200.</param>
<param name="bitAdr">The address of the bit. (0-7)</param>
<param name="value">Bytes to write. If more than 200, multiple requests will be made.</param>
<returns>NoError if it was successful, or the error is specified</returns>
</member>
<member name="M:S7.Net.Plc.Write(S7.Net.DataType,System.Int32,System.Int32,System.Object,System.Int32)">
<summary>
Takes in input an object and tries to parse it to an array of values. This can be used to write many data, all of the same type.
You must specify the memory area type, memory are address, byte start address and bytes count.
If the read was not successful, check LastErrorCode or LastErrorString.
</summary>
<param name="dataType">Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output.</param>
<param name="db">Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc.</param>
<param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
<param name="value">Bytes to write. The lenght of this parameter can't be higher than 200. If you need more, use recursion.</param>
<param name="bitAdr">The address of the bit. (0-7)</param>
<returns>NoError if it was successful, or the error is specified</returns>
</member>
<member name="M:S7.Net.Plc.Write(System.String,System.Object)">
<summary>
Writes a single variable from the plc, takes in input strings like "DB1.DBX0.0", "DB20.DBD200", "MB20", "T45", etc.
If the write was not successful, check LastErrorCode or LastErrorString.
</summary>
<param name="variable">Input strings like "DB1.DBX0.0", "DB20.DBD200", "MB20", "T45", etc.</param>
<param name="value">Value to be written to the plc</param>
<returns>NoError if it was successful, or the error is specified</returns>
</member>
<member name="M:S7.Net.Plc.WriteStruct(System.Object,System.Int32,System.Int32)">
<summary>
Writes a C# struct to a DB in the plc
</summary>
<param name="structValue">The struct to be written</param>
<param name="db">Db address</param>
<param name="startByteAdr">Start bytes on the plc</param>
<returns>NoError if it was successful, or the error is specified</returns>
</member>
<member name="M:S7.Net.Plc.WriteClass(System.Object,System.Int32,System.Int32)">
<summary>
Writes a C# class to a DB in the plc
</summary>
<param name="classValue">The class to be written</param>
<param name="db">Db address</param>
<param name="startByteAdr">Start bytes on the plc</param>
<returns>NoError if it was successful, or the error is specified</returns>
</member>
<member name="M:S7.Net.Plc.ClearLastError">
<summary>
Sets the LastErrorCode to NoError and LastErrorString to String.Empty
</summary>
</member>
<member name="M:S7.Net.Plc.ReadHeaderPackage(System.Int32)">
<summary>
Creates the header to read bytes from the plc
</summary>
<param name="amount"></param>
<returns></returns>
</member>
<member name="M:S7.Net.Plc.CreateReadDataRequestPackage(S7.Net.DataType,System.Int32,System.Int32,System.Int32)">
<summary>
Create the bytes-package to request data from the plc. You have to specify the memory type (dataType),
the address of the memory, the address of the byte and the bytes count.
</summary>
<param name="dataType">MemoryType (DB, Timer, Counter, etc.)</param>
<param name="db">Address of the memory to be read</param>
<param name="startByteAdr">Start address of the byte</param>
<param name="count">Number of bytes to be read</param>
<returns></returns>
</member>
<member name="M:S7.Net.Plc.WriteBytesWithASingleRequest(S7.Net.DataType,System.Int32,System.Int32,System.Byte[])">
<summary>
Writes up to 200 bytes to the plc and returns NoError if successful. You must specify the memory area type, memory are address, byte start address and bytes count.
If the write was not successful, check LastErrorCode or LastErrorString.
</summary>
<param name="dataType">Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output.</param>
<param name="db">Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc.</param>
<param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
<param name="value">Bytes to write. The lenght of this parameter can't be higher than 200. If you need more, use recursion.</param>
<returns>NoError if it was successful, or the error is specified</returns>
</member>
<member name="M:S7.Net.Plc.ParseBytes(S7.Net.VarType,System.Byte[],System.Int32)">
<summary>
Given a S7 variable type (Bool, Word, DWord, etc.), it converts the bytes in the appropriate C# format.
</summary>
<param name="varType"></param>
<param name="bytes"></param>
<param name="varCount"></param>
<returns></returns>
</member>
<member name="M:S7.Net.Plc.VarTypeToByteLength(S7.Net.VarType,System.Int32)">
<summary>
Given a S7 variable type (Bool, Word, DWord, etc.), it returns how many bytes to read.
</summary>
<param name="varType"></param>
<param name="varCount"></param>
<returns></returns>
</member>
<member name="M:S7.Net.Plc.Dispose">
<summary>
Releases all resources, disonnects from the plc and closes the socket
</summary>
</member>
<member name="T:S7.Net.Types.Boolean">
<summary>
Contains the methods to read, set and reset bits inside bytes
</summary>
</member>
<member name="M:S7.Net.Types.Boolean.GetValue(System.Byte,System.Int32)">
<summary>
Returns the value of a bit in a bit, given the address of the bit
</summary>
</member>
<member name="M:S7.Net.Types.Boolean.SetBit(System.Byte,System.Int32)">
<summary>
Sets the value of a bit to 1 (true), given the address of the bit
</summary>
</member>
<member name="M:S7.Net.Types.Boolean.ClearBit(System.Byte,System.Int32)">
<summary>
Resets the value of a bit to 0 (false), given the address of the bit
</summary>
</member>
<member name="T:S7.Net.Types.Byte">
<summary>
Contains the methods to convert from bytes to byte arrays
</summary>
</member>
<member name="M:S7.Net.Types.Byte.ToByteArray(System.Byte)">
<summary>
Converts a byte to byte array
</summary>
</member>
<member name="M:S7.Net.Types.Byte.FromByteArray(System.Byte[])">
<summary>
Converts a byte array to byte
</summary>
<param name="bytes"></param>
<returns></returns>
</member>
<member name="T:S7.Net.Types.Class">
<summary>
Contains the methods to convert a C# class to S7 data types
</summary>
</member>
<member name="M:S7.Net.Types.Class.GetClassSize(System.Object)">
<summary>
Gets the size of the class in bytes.
</summary>
<param name="instance">An instance of the class</param>
<returns>the number of bytes</returns>
</member>
<member name="M:S7.Net.Types.Class.FromBytes(System.Object,System.Byte[])">
<summary>
Sets the object's values with the given array of bytes
</summary>
<param name="sourceClass">The object to fill in the given array of bytes</param>
<param name="bytes">The array of bytes</param>
</member>
<member name="M:S7.Net.Types.Class.ToBytes(System.Object)">
<summary>
Creates a byte array depending on the struct type.
</summary>
<param name="sourceClass">The struct object</param>
<returns>A byte array or null if fails.</returns>
</member>
<member name="T:S7.Net.Types.Counter">
<summary>
Contains the conversion methods to convert Counter from S7 plc to C# ushort (UInt16).
</summary>
</member>
<member name="M:S7.Net.Types.Counter.FromByteArray(System.Byte[])">
<summary>
Converts a Counter (2 bytes) to ushort (UInt16)
</summary>
</member>
<member name="M:S7.Net.Types.Counter.FromBytes(System.Byte,System.Byte)">
<summary>
Converts a Counter (2 bytes) to ushort (UInt16)
</summary>
</member>
<member name="M:S7.Net.Types.Counter.ToByteArray(System.UInt16)">
<summary>
Converts a ushort (UInt16) to word (2 bytes)
</summary>
</member>
<member name="M:S7.Net.Types.Counter.ToByteArray(System.UInt16[])">
<summary>
Converts an array of ushort (UInt16) to an array of bytes
</summary>
</member>
<member name="M:S7.Net.Types.Counter.ToArray(System.Byte[])">
<summary>
Converts an array of bytes to an array of ushort
</summary>
</member>
<member name="T:S7.Net.Types.DataItem">
<summary>
Create an instance of a memory block that can be read by using ReadMultipleVars
</summary>
</member>
<member name="P:S7.Net.Types.DataItem.DataType">
<summary>
Memory area to read
</summary>
</member>
<member name="P:S7.Net.Types.DataItem.VarType">
<summary>
Type of data to be read (default is bytes)
</summary>
</member>
<member name="P:S7.Net.Types.DataItem.DB">
<summary>
Address of memory area to read (example: for DB1 this value is 1, for T45 this value is 45)
</summary>
</member>
<member name="P:S7.Net.Types.DataItem.StartByteAdr">
<summary>
Address of the first byte to read
</summary>
</member>
<member name="P:S7.Net.Types.DataItem.Count">
<summary>
Number of variables to read
</summary>
</member>
<member name="P:S7.Net.Types.DataItem.Value">
<summary>
Contains the value of the memory area after the read has been executed
</summary>
</member>
<member name="M:S7.Net.Types.DataItem.#ctor">
<summary>
Create an instance of DataItem
</summary>
</member>
<member name="T:S7.Net.Types.DInt">
<summary>
Contains the conversion methods to convert DInt from S7 plc to C# int (Int32).
</summary>
</member>
<member name="M:S7.Net.Types.DInt.FromByteArray(System.Byte[])">
<summary>
Converts a S7 DInt (4 bytes) to int (Int32)
</summary>
</member>
<member name="M:S7.Net.Types.DInt.FromBytes(System.Byte,System.Byte,System.Byte,System.Byte)">
<summary>
Converts a S7 DInt (4 bytes) to int (Int32)
</summary>
</member>
<member name="M:S7.Net.Types.DInt.ToByteArray(System.Int32)">
<summary>
Converts a int (Int32) to S7 DInt (4 bytes)
</summary>
</member>
<member name="M:S7.Net.Types.DInt.ToByteArray(System.Int32[])">
<summary>
Converts an array of int (Int32) to an array of bytes
</summary>
</member>
<member name="M:S7.Net.Types.DInt.ToArray(System.Byte[])">
<summary>
Converts an array of S7 DInt to an array of int (Int32)
</summary>
</member>
<member name="M:S7.Net.Types.DInt.CDWord(System.Int64)">
<summary>
Converts from C# long (Int64) to C# int (Int32)
</summary>
</member>
<member name="T:S7.Net.Types.Double">
<summary>
Contains the conversion methods to convert Real from S7 plc to C# double.
</summary>
</member>
<member name="M:S7.Net.Types.Double.FromByteArray(System.Byte[])">
<summary>
Converts a S7 Real (4 bytes) to double
</summary>
</member>
<member name="M:S7.Net.Types.Double.FromDWord(System.Int32)">
<summary>
Converts a S7 DInt to double
</summary>
</member>
<member name="M:S7.Net.Types.Double.FromDWord(System.UInt32)">
<summary>
Converts a S7 DWord to double
</summary>
</member>
<member name="M:S7.Net.Types.Double.ToByteArray(System.Double)">
<summary>
Converts a double to S7 Real (4 bytes)
</summary>
</member>
<member name="M:S7.Net.Types.Double.ToByteArray(System.Double[])">
<summary>
Converts an array of double to an array of bytes
</summary>
</member>
<member name="M:S7.Net.Types.Double.ToArray(System.Byte[])">
<summary>
Converts an array of S7 Real to an array of double
</summary>
</member>
<member name="T:S7.Net.Types.DWord">
<summary>
Contains the conversion methods to convert DWord from S7 plc to C#.
</summary>
</member>
<member name="M:S7.Net.Types.DWord.FromByteArray(System.Byte[])">
<summary>
Converts a S7 DWord (4 bytes) to uint (UInt32)
</summary>
</member>
<member name="M:S7.Net.Types.DWord.FromBytes(System.Byte,System.Byte,System.Byte,System.Byte)">
<summary>
Converts a S7 DWord (4 bytes) to uint (UInt32)
</summary>
</member>
<member name="M:S7.Net.Types.DWord.ToByteArray(System.UInt32)">
<summary>
Converts a uint (UInt32) to S7 DWord (4 bytes)
</summary>
</member>
<member name="M:S7.Net.Types.DWord.ToByteArray(System.UInt32[])">
<summary>
Converts an array of uint (UInt32) to an array of S7 DWord (4 bytes)
</summary>
</member>
<member name="M:S7.Net.Types.DWord.ToArray(System.Byte[])">
<summary>
Converts an array of S7 DWord to an array of uint (UInt32)
</summary>
</member>
<member name="T:S7.Net.Types.Int">
<summary>
Contains the conversion methods to convert Int from S7 plc to C#.
</summary>
</member>
<member name="M:S7.Net.Types.Int.FromByteArray(System.Byte[])">
<summary>
Converts a S7 Int (2 bytes) to short (Int16)
</summary>
</member>
<member name="M:S7.Net.Types.Int.FromBytes(System.Byte,System.Byte)">
<summary>
Converts a S7 Int (2 bytes) to short (Int16)
</summary>
</member>
<member name="M:S7.Net.Types.Int.ToByteArray(System.Int16)">
<summary>
Converts a short (Int16) to a S7 Int byte array (2 bytes)
</summary>
</member>
<member name="M:S7.Net.Types.Int.ToByteArray(System.Int16[])">
<summary>
Converts an array of short (Int16) to a S7 Int byte array (2 bytes)
</summary>
</member>
<member name="M:S7.Net.Types.Int.ToArray(System.Byte[])">
<summary>
Converts an array of S7 Int to an array of short (Int16)
</summary>
</member>
<member name="M:S7.Net.Types.Int.CWord(System.Int32)">
<summary>
Converts a C# int value to a C# short value, to be used as word.
</summary>
<param name="value"></param>
<returns></returns>
</member>
<member name="T:S7.Net.Types.String">
<summary>
Contains the methods to convert from S7 strings to C# strings
</summary>
</member>
<member name="M:S7.Net.Types.String.ToByteArray(System.String)">
<summary>
Converts a string to S7 bytes
</summary>
</member>
<member name="M:S7.Net.Types.String.FromByteArray(System.Byte[])">
<summary>
Converts S7 bytes to a string
</summary>
<param name="bytes"></param>
<returns></returns>
</member>
<member name="T:S7.Net.Types.Struct">
<summary>
Contains the method to convert a C# struct to S7 data types
</summary>
</member>
<member name="M:S7.Net.Types.Struct.GetStructSize(System.Type)">
<summary>
Gets the size of the struct in bytes.
</summary>
<param name="structType">the type of the struct</param>
<returns>the number of bytes</returns>
</member>
<member name="M:S7.Net.Types.Struct.FromBytes(System.Type,System.Byte[])">
<summary>
Creates a struct of a specified type by an array of bytes.
</summary>
<param name="structType">The struct type</param>
<param name="bytes">The array of bytes</param>
<returns>The object depending on the struct type or null if fails(array-length != struct-length</returns>
</member>
<member name="M:S7.Net.Types.Struct.ToBytes(System.Object)">
<summary>
Creates a byte array depending on the struct type.
</summary>
<param name="structValue">The struct object</param>
<returns>A byte array or null if fails.</returns>
</member>
<member name="T:S7.Net.Types.Timer">
<summary>
Converts the Timer data type to C# data type
</summary>
</member>
<member name="M:S7.Net.Types.Timer.FromByteArray(System.Byte[])">
<summary>
Converts the timer bytes to a double
</summary>
</member>
<member name="M:S7.Net.Types.Timer.ToByteArray(System.UInt16)">
<summary>
Converts a ushort (UInt16) to an array of bytes formatted as time
</summary>
</member>
<member name="M:S7.Net.Types.Timer.ToByteArray(System.UInt16[])">
<summary>
Converts an array of ushorts (Uint16) to an array of bytes formatted as time
</summary>
</member>
<member name="M:S7.Net.Types.Timer.ToArray(System.Byte[])">
<summary>
Converts an array of bytes formatted as time to an array of doubles
</summary>
<param name="bytes"></param>
<returns></returns>
</member>
<member name="T:S7.Net.Types.Word">
<summary>
Contains the conversion methods to convert Words from S7 plc to C#.
</summary>
</member>
<member name="M:S7.Net.Types.Word.FromByteArray(System.Byte[])">
<summary>
Converts a word (2 bytes) to ushort (UInt16)
</summary>
</member>
<member name="M:S7.Net.Types.Word.FromBytes(System.Byte,System.Byte)">
<summary>
Converts a word (2 bytes) to ushort (UInt16)
</summary>
</member>
<member name="M:S7.Net.Types.Word.ToByteArray(System.UInt16)">
<summary>
Converts a ushort (UInt16) to word (2 bytes)
</summary>
</member>
<member name="M:S7.Net.Types.Word.ToByteArray(System.UInt16[])">
<summary>
Converts an array of ushort (UInt16) to an array of bytes
</summary>
</member>
<member name="M:S7.Net.Types.Word.ToArray(System.Byte[])">
<summary>
Converts an array of bytes to an array of ushort
</summary>
</member>
</members>
</doc>

Binary file not shown.

View File

@@ -1,917 +0,0 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>S7.Net</name>
</assembly>
<members>
<member name="T:S7.Net.Conversion">
<summary>
Conversion methods to convert from Siemens numeric format to C# and back
</summary>
</member>
<member name="M:S7.Net.Conversion.BinStringToInt32(System.String)">
<summary>
Converts a binary string to Int32 value
</summary>
<param name="txt"></param>
<returns></returns>
</member>
<member name="M:S7.Net.Conversion.BinStringToByte(System.String)">
<summary>
Converts a binary string to a byte. Can return null.
</summary>
<param name="txt"></param>
<returns></returns>
</member>
<member name="M:S7.Net.Conversion.ValToBinString(System.Object)">
<summary>
Converts the value to a binary string
</summary>
<param name="value"></param>
<returns></returns>
</member>
<member name="M:S7.Net.Conversion.SelectBit(System.Byte,System.Int32)">
<summary>
Helper to get a bit value given a byte and the bit index.
Example: DB1.DBX0.5 -> var bytes = ReadBytes(DB1.DBW0); bool bit = bytes[0].SelectBit(5);
</summary>
<param name="data"></param>
<param name="bitPosition"></param>
<returns></returns>
</member>
<member name="M:S7.Net.Conversion.ConvertToShort(System.UInt16)">
<summary>
Converts from ushort value to short value; it's used to retrieve negative values from words
</summary>
<param name="input"></param>
<returns></returns>
</member>
<member name="M:S7.Net.Conversion.ConvertToUshort(System.Int16)">
<summary>
Converts from short value to ushort value; it's used to pass negative values to DWs
</summary>
<param name="input"></param>
<returns></returns>
</member>
<member name="M:S7.Net.Conversion.ConvertToInt(System.UInt32)">
<summary>
Converts from UInt32 value to Int32 value; it's used to retrieve negative values from DBDs
</summary>
<param name="input"></param>
<returns></returns>
</member>
<member name="M:S7.Net.Conversion.ConvertToUInt(System.Int32)">
<summary>
Converts from Int32 value to UInt32 value; it's used to pass negative values to DBDs
</summary>
<param name="input"></param>
<returns></returns>
</member>
<member name="M:S7.Net.Conversion.ConvertToUInt(System.Double)">
<summary>
Converts from double to DWord (DBD)
</summary>
<param name="input"></param>
<returns></returns>
</member>
<member name="M:S7.Net.Conversion.ConvertToDouble(System.UInt32)">
<summary>
Converts from DWord (DBD) to double
</summary>
<param name="input"></param>
<returns></returns>
</member>
<member name="T:S7.Net.CpuType">
<summary>
Types of S7 cpu supported by the library
</summary>
</member>
<member name="F:S7.Net.CpuType.S7200">
<summary>
S7 200 cpu type
</summary>
</member>
<member name="F:S7.Net.CpuType.S7300">
<summary>
S7 300 cpu type
</summary>
</member>
<member name="F:S7.Net.CpuType.S7400">
<summary>
S7 400 cpu type
</summary>
</member>
<member name="F:S7.Net.CpuType.S71200">
<summary>
S7 1200 cpu type
</summary>
</member>
<member name="F:S7.Net.CpuType.S71500">
<summary>
S7 1500 cpu type
</summary>
</member>
<member name="T:S7.Net.ErrorCode">
<summary>
Types of error code that can be set after a function is called
</summary>
</member>
<member name="F:S7.Net.ErrorCode.NoError">
<summary>
The function has been executed correctly
</summary>
</member>
<member name="F:S7.Net.ErrorCode.WrongCPU_Type">
<summary>
Wrong type of CPU error
</summary>
</member>
<member name="F:S7.Net.ErrorCode.ConnectionError">
<summary>
Connection error
</summary>
</member>
<member name="F:S7.Net.ErrorCode.IPAddressNotAvailable">
<summary>
Ip address not available
</summary>
</member>
<member name="F:S7.Net.ErrorCode.WrongVarFormat">
<summary>
Wrong format of the variable
</summary>
</member>
<member name="F:S7.Net.ErrorCode.WrongNumberReceivedBytes">
<summary>
Wrong number of received bytes
</summary>
</member>
<member name="F:S7.Net.ErrorCode.SendData">
<summary>
Error on send data
</summary>
</member>
<member name="F:S7.Net.ErrorCode.ReadData">
<summary>
Error on read data
</summary>
</member>
<member name="F:S7.Net.ErrorCode.WriteData">
<summary>
Error on write data
</summary>
</member>
<member name="T:S7.Net.DataType">
<summary>
Types of memory area that can be read
</summary>
</member>
<member name="F:S7.Net.DataType.Input">
<summary>
Input area memory
</summary>
</member>
<member name="F:S7.Net.DataType.Output">
<summary>
Output area memory
</summary>
</member>
<member name="F:S7.Net.DataType.Memory">
<summary>
Merkers area memory (M0, M0.0, ...)
</summary>
</member>
<member name="F:S7.Net.DataType.DataBlock">
<summary>
DB area memory (DB1, DB2, ...)
</summary>
</member>
<member name="F:S7.Net.DataType.Timer">
<summary>
Timer area memory(T1, T2, ...)
</summary>
</member>
<member name="F:S7.Net.DataType.Counter">
<summary>
Counter area memory (C1, C2, ...)
</summary>
</member>
<member name="T:S7.Net.VarType">
<summary>
Types
</summary>
</member>
<member name="F:S7.Net.VarType.Bit">
<summary>
S7 Bit variable type (bool)
</summary>
</member>
<member name="F:S7.Net.VarType.Byte">
<summary>
S7 Byte variable type (8 bits)
</summary>
</member>
<member name="F:S7.Net.VarType.Word">
<summary>
S7 Word variable type (16 bits, 2 bytes)
</summary>
</member>
<member name="F:S7.Net.VarType.DWord">
<summary>
S7 DWord variable type (32 bits, 4 bytes)
</summary>
</member>
<member name="F:S7.Net.VarType.Int">
<summary>
S7 Int variable type (16 bits, 2 bytes)
</summary>
</member>
<member name="F:S7.Net.VarType.DInt">
<summary>
DInt variable type (32 bits, 4 bytes)
</summary>
</member>
<member name="F:S7.Net.VarType.Real">
<summary>
Real variable type (32 bits, 4 bytes)
</summary>
</member>
<member name="F:S7.Net.VarType.String">
<summary>
String variable type (variable)
</summary>
</member>
<member name="F:S7.Net.VarType.Timer">
<summary>
Timer variable type
</summary>
</member>
<member name="F:S7.Net.VarType.Counter">
<summary>
Counter variable type
</summary>
</member>
<member name="T:S7.Net.Plc">
<summary>
Creates an instance of S7.Net driver
</summary>
</member>
<member name="P:S7.Net.Plc.IP">
<summary>
Ip address of the plc
</summary>
</member>
<member name="P:S7.Net.Plc.CPU">
<summary>
Cpu type of the plc
</summary>
</member>
<member name="P:S7.Net.Plc.Rack">
<summary>
Rack of the plc
</summary>
</member>
<member name="P:S7.Net.Plc.Slot">
<summary>
Slot of the CPU of the plc
</summary>
</member>
<member name="P:S7.Net.Plc.IsAvailable">
<summary>
Returns true if a connection to the plc can be established
</summary>
</member>
<member name="P:S7.Net.Plc.IsConnected">
<summary>
Checks if the socket is connected and polls the other peer (the plc) to see if it's connected.
This is the variable that you should continously check to see if the communication is working
See also: http://stackoverflow.com/questions/2661764/how-to-check-if-a-socket-is-connected-disconnected-in-c
</summary>
</member>
<member name="P:S7.Net.Plc.LastErrorString">
<summary>
Contains the last error registered when executing a function
</summary>
</member>
<member name="P:S7.Net.Plc.LastErrorCode">
<summary>
Contains the last error code registered when executing a function
</summary>
</member>
<member name="M:S7.Net.Plc.#ctor(S7.Net.CpuType,System.String,System.Int16,System.Int16)">
<summary>
Creates a PLC object with all the parameters needed for connections.
For S7-1200 and S7-1500, the default is rack = 0 and slot = 0.
You need slot > 0 if you are connecting to external ethernet card (CP).
For S7-300 and S7-400 the default is rack = 0 and slot = 2.
</summary>
<param name="cpu">CpuType of the plc (select from the enum)</param>
<param name="ip">Ip address of the plc</param>
<param name="rack">rack of the plc, usually it's 0, but check in the hardware configuration of Step7 or TIA portal</param>
<param name="slot">slot of the CPU of the plc, usually it's 2 for S7300-S7400, 0 for S7-1200 and S7-1500.
If you use an external ethernet card, this must be set accordingly.</param>
</member>
<member name="M:S7.Net.Plc.Open">
<summary>
Open a socket and connects to the plc, sending all the corrected package and returning if the connection was successful (ErroreCode.NoError) of it was wrong.
</summary>
<returns>Returns ErrorCode.NoError if the connection was successful, otherwise check the ErrorCode</returns>
</member>
<member name="M:S7.Net.Plc.Close">
<summary>
Disonnects from the plc and close the socket
</summary>
</member>
<member name="M:S7.Net.Plc.ReadMultipleVars(System.Collections.Generic.List{S7.Net.Types.DataItem})">
<summary>
Reads multiple vars in a single request.
You have to create and pass a list of DataItems and you obtain in response the same list with the values.
Values are stored in the property "Value" of the dataItem and are already converted.
If you don't want the conversion, just create a dataItem of bytes.
DataItems must not be more than 20 (protocol restriction) and bytes must not be more than 200 + 22 of header (protocol restriction).
</summary>
<param name="dataItems">List of dataitems that contains the list of variables that must be read. Maximum 20 dataitems are accepted.</param>
</member>
<member name="M:S7.Net.Plc.ReadBytes(S7.Net.DataType,System.Int32,System.Int32,System.Int32)">
<summary>
Reads a number of bytes from a DB starting from a specified index. This handles more than 200 bytes with multiple requests.
If the read was not successful, check LastErrorCode or LastErrorString.
</summary>
<param name="dataType">Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output.</param>
<param name="db">Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc.</param>
<param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
<param name="count">Byte count, if you want to read 120 bytes, set this to 120.</param>
<returns>Returns the bytes in an array</returns>
</member>
<member name="M:S7.Net.Plc.Read(S7.Net.DataType,System.Int32,System.Int32,S7.Net.VarType,System.Int32)">
<summary>
Read and decode a certain number of bytes of the "VarType" provided.
This can be used to read multiple consecutive variables of the same type (Word, DWord, Int, etc).
If the read was not successful, check LastErrorCode or LastErrorString.
</summary>
<param name="dataType">Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output.</param>
<param name="db">Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc.</param>
<param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
<param name="varType">Type of the variable/s that you are reading</param>
<param name="varCount"></param>
</member>
<member name="M:S7.Net.Plc.Read(System.String)">
<summary>
Reads a single variable from the plc, takes in input strings like "DB1.DBX0.0", "DB20.DBD200", "MB20", "T45", etc.
If the read was not successful, check LastErrorCode or LastErrorString.
</summary>
<param name="variable">Input strings like "DB1.DBX0.0", "DB20.DBD200", "MB20", "T45", etc.</param>
<returns>Returns an object that contains the value. This object must be cast accordingly.</returns>
</member>
<member name="M:S7.Net.Plc.ReadStruct(System.Type,System.Int32,System.Int32)">
<summary>
Reads all the bytes needed to fill a struct in C#, starting from a certain address, and return an object that can be casted to the struct.
</summary>
<param name="structType">Type of the struct to be readed (es.: TypeOf(MyStruct)).</param>
<param name="db">Address of the DB.</param>
<param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
<returns>Returns a struct that must be cast.</returns>
</member>
<member name="M:S7.Net.Plc.ReadStruct``1(System.Int32,System.Int32)">
<summary>
Reads all the bytes needed to fill a struct in C#, starting from a certain address, and returns the struct or null if nothing was read.
</summary>
<typeparam name="T">The struct type</typeparam>
<param name="db">Address of the DB.</param>
<param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
<returns>Returns a nulable struct. If nothing was read null will be returned.</returns>
</member>
<member name="M:S7.Net.Plc.ReadClass(System.Object,System.Int32,System.Int32)">
<summary>
Reads all the bytes needed to fill a class in C#, starting from a certain address, and set all the properties values to the value that are read from the plc.
This reads only properties, it doesn't read private variable or public variable without {get;set;} specified.
</summary>
<param name="sourceClass">Instance of the class that will store the values</param>
<param name="db">Index of the DB; es.: 1 is for DB1</param>
<param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
<returns>The number of read bytes</returns>
</member>
<member name="M:S7.Net.Plc.ReadClass``1(System.Int32,System.Int32)">
<summary>
Reads all the bytes needed to fill a class in C#, starting from a certain address, and set all the properties values to the value that are read from the plc.
This reads only properties, it doesn't read private variable or public variable without {get;set;} specified. To instantiate the class defined by the generic
type, the class needs a default constructor.
</summary>
<typeparam name="T">The class that will be instantiated. Requires a default constructor</typeparam>
<param name="db">Index of the DB; es.: 1 is for DB1</param>
<param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
<returns>An instance of the class with the values read from the plc. If no data has been read, null will be returned</returns>
</member>
<member name="M:S7.Net.Plc.ReadClass``1(System.Func{``0},System.Int32,System.Int32)">
<summary>
Reads all the bytes needed to fill a class in C#, starting from a certain address, and set all the properties values to the value that are read from the plc.
This reads only properties, it doesn't read private variable or public variable without {get;set;} specified.
</summary>
<typeparam name="T">The class that will be instantiated</typeparam>
<param name="classFactory">Function to instantiate the class</param>
<param name="db">Index of the DB; es.: 1 is for DB1</param>
<param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
<returns>An instance of the class with the values read from the plc. If no data has been read, null will be returned</returns>
</member>
<member name="M:S7.Net.Plc.WriteBytes(S7.Net.DataType,System.Int32,System.Int32,System.Byte[])">
<summary>
Write a number of bytes from a DB starting from a specified index. This handles more than 200 bytes with multiple requests.
If the write was not successful, check LastErrorCode or LastErrorString.
</summary>
<param name="dataType">Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output.</param>
<param name="db">Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc.</param>
<param name="startByteAdr">Start byte address. If you want to write DB1.DBW200, this is 200.</param>
<param name="value">Bytes to write. If more than 200, multiple requests will be made.</param>
<returns>NoError if it was successful, or the error is specified</returns>
</member>
<member name="M:S7.Net.Plc.WriteBit(S7.Net.DataType,System.Int32,System.Int32,System.Int32,System.Boolean)">
<summary>
Write a single bit from a DB with the specified index.
</summary>
<param name="dataType">Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output.</param>
<param name="db">Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc.</param>
<param name="startByteAdr">Start byte address. If you want to write DB1.DBW200, this is 200.</param>
<param name="bitAdr">The address of the bit. (0-7)</param>
<param name="value">Bytes to write. If more than 200, multiple requests will be made.</param>
<returns>NoError if it was successful, or the error is specified</returns>
</member>
<member name="M:S7.Net.Plc.WriteBit(S7.Net.DataType,System.Int32,System.Int32,System.Int32,System.Int32)">
<summary>
Write a single bit from a DB with the specified index.
</summary>
<param name="dataType">Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output.</param>
<param name="db">Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc.</param>
<param name="startByteAdr">Start byte address. If you want to write DB1.DBW200, this is 200.</param>
<param name="bitAdr">The address of the bit. (0-7)</param>
<param name="value">Bytes to write. If more than 200, multiple requests will be made.</param>
<returns>NoError if it was successful, or the error is specified</returns>
</member>
<member name="M:S7.Net.Plc.Write(S7.Net.DataType,System.Int32,System.Int32,System.Object,System.Int32)">
<summary>
Takes in input an object and tries to parse it to an array of values. This can be used to write many data, all of the same type.
You must specify the memory area type, memory are address, byte start address and bytes count.
If the read was not successful, check LastErrorCode or LastErrorString.
</summary>
<param name="dataType">Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output.</param>
<param name="db">Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc.</param>
<param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
<param name="value">Bytes to write. The lenght of this parameter can't be higher than 200. If you need more, use recursion.</param>
<param name="bitAdr">The address of the bit. (0-7)</param>
<returns>NoError if it was successful, or the error is specified</returns>
</member>
<member name="M:S7.Net.Plc.Write(System.String,System.Object)">
<summary>
Writes a single variable from the plc, takes in input strings like "DB1.DBX0.0", "DB20.DBD200", "MB20", "T45", etc.
If the write was not successful, check LastErrorCode or LastErrorString.
</summary>
<param name="variable">Input strings like "DB1.DBX0.0", "DB20.DBD200", "MB20", "T45", etc.</param>
<param name="value">Value to be written to the plc</param>
<returns>NoError if it was successful, or the error is specified</returns>
</member>
<member name="M:S7.Net.Plc.WriteStruct(System.Object,System.Int32,System.Int32)">
<summary>
Writes a C# struct to a DB in the plc
</summary>
<param name="structValue">The struct to be written</param>
<param name="db">Db address</param>
<param name="startByteAdr">Start bytes on the plc</param>
<returns>NoError if it was successful, or the error is specified</returns>
</member>
<member name="M:S7.Net.Plc.WriteClass(System.Object,System.Int32,System.Int32)">
<summary>
Writes a C# class to a DB in the plc
</summary>
<param name="classValue">The class to be written</param>
<param name="db">Db address</param>
<param name="startByteAdr">Start bytes on the plc</param>
<returns>NoError if it was successful, or the error is specified</returns>
</member>
<member name="M:S7.Net.Plc.ClearLastError">
<summary>
Sets the LastErrorCode to NoError and LastErrorString to String.Empty
</summary>
</member>
<member name="M:S7.Net.Plc.ReadHeaderPackage(System.Int32)">
<summary>
Creates the header to read bytes from the plc
</summary>
<param name="amount"></param>
<returns></returns>
</member>
<member name="M:S7.Net.Plc.CreateReadDataRequestPackage(S7.Net.DataType,System.Int32,System.Int32,System.Int32)">
<summary>
Create the bytes-package to request data from the plc. You have to specify the memory type (dataType),
the address of the memory, the address of the byte and the bytes count.
</summary>
<param name="dataType">MemoryType (DB, Timer, Counter, etc.)</param>
<param name="db">Address of the memory to be read</param>
<param name="startByteAdr">Start address of the byte</param>
<param name="count">Number of bytes to be read</param>
<returns></returns>
</member>
<member name="M:S7.Net.Plc.WriteBytesWithASingleRequest(S7.Net.DataType,System.Int32,System.Int32,System.Byte[])">
<summary>
Writes up to 200 bytes to the plc and returns NoError if successful. You must specify the memory area type, memory are address, byte start address and bytes count.
If the write was not successful, check LastErrorCode or LastErrorString.
</summary>
<param name="dataType">Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output.</param>
<param name="db">Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc.</param>
<param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
<param name="value">Bytes to write. The lenght of this parameter can't be higher than 200. If you need more, use recursion.</param>
<returns>NoError if it was successful, or the error is specified</returns>
</member>
<member name="M:S7.Net.Plc.ParseBytes(S7.Net.VarType,System.Byte[],System.Int32)">
<summary>
Given a S7 variable type (Bool, Word, DWord, etc.), it converts the bytes in the appropriate C# format.
</summary>
<param name="varType"></param>
<param name="bytes"></param>
<param name="varCount"></param>
<returns></returns>
</member>
<member name="M:S7.Net.Plc.VarTypeToByteLength(S7.Net.VarType,System.Int32)">
<summary>
Given a S7 variable type (Bool, Word, DWord, etc.), it returns how many bytes to read.
</summary>
<param name="varType"></param>
<param name="varCount"></param>
<returns></returns>
</member>
<member name="M:S7.Net.Plc.Dispose">
<summary>
Releases all resources, disonnects from the plc and closes the socket
</summary>
</member>
<member name="T:S7.Net.Types.Boolean">
<summary>
Contains the methods to read, set and reset bits inside bytes
</summary>
</member>
<member name="M:S7.Net.Types.Boolean.GetValue(System.Byte,System.Int32)">
<summary>
Returns the value of a bit in a bit, given the address of the bit
</summary>
</member>
<member name="M:S7.Net.Types.Boolean.SetBit(System.Byte,System.Int32)">
<summary>
Sets the value of a bit to 1 (true), given the address of the bit
</summary>
</member>
<member name="M:S7.Net.Types.Boolean.ClearBit(System.Byte,System.Int32)">
<summary>
Resets the value of a bit to 0 (false), given the address of the bit
</summary>
</member>
<member name="T:S7.Net.Types.Byte">
<summary>
Contains the methods to convert from bytes to byte arrays
</summary>
</member>
<member name="M:S7.Net.Types.Byte.ToByteArray(System.Byte)">
<summary>
Converts a byte to byte array
</summary>
</member>
<member name="M:S7.Net.Types.Byte.FromByteArray(System.Byte[])">
<summary>
Converts a byte array to byte
</summary>
<param name="bytes"></param>
<returns></returns>
</member>
<member name="T:S7.Net.Types.Class">
<summary>
Contains the methods to convert a C# class to S7 data types
</summary>
</member>
<member name="M:S7.Net.Types.Class.GetClassSize(System.Object)">
<summary>
Gets the size of the class in bytes.
</summary>
<param name="instance">An instance of the class</param>
<returns>the number of bytes</returns>
</member>
<member name="M:S7.Net.Types.Class.FromBytes(System.Object,System.Byte[])">
<summary>
Sets the object's values with the given array of bytes
</summary>
<param name="sourceClass">The object to fill in the given array of bytes</param>
<param name="bytes">The array of bytes</param>
</member>
<member name="M:S7.Net.Types.Class.ToBytes(System.Object)">
<summary>
Creates a byte array depending on the struct type.
</summary>
<param name="sourceClass">The struct object</param>
<returns>A byte array or null if fails.</returns>
</member>
<member name="T:S7.Net.Types.Counter">
<summary>
Contains the conversion methods to convert Counter from S7 plc to C# ushort (UInt16).
</summary>
</member>
<member name="M:S7.Net.Types.Counter.FromByteArray(System.Byte[])">
<summary>
Converts a Counter (2 bytes) to ushort (UInt16)
</summary>
</member>
<member name="M:S7.Net.Types.Counter.FromBytes(System.Byte,System.Byte)">
<summary>
Converts a Counter (2 bytes) to ushort (UInt16)
</summary>
</member>
<member name="M:S7.Net.Types.Counter.ToByteArray(System.UInt16)">
<summary>
Converts a ushort (UInt16) to word (2 bytes)
</summary>
</member>
<member name="M:S7.Net.Types.Counter.ToByteArray(System.UInt16[])">
<summary>
Converts an array of ushort (UInt16) to an array of bytes
</summary>
</member>
<member name="M:S7.Net.Types.Counter.ToArray(System.Byte[])">
<summary>
Converts an array of bytes to an array of ushort
</summary>
</member>
<member name="T:S7.Net.Types.DataItem">
<summary>
Create an instance of a memory block that can be read by using ReadMultipleVars
</summary>
</member>
<member name="P:S7.Net.Types.DataItem.DataType">
<summary>
Memory area to read
</summary>
</member>
<member name="P:S7.Net.Types.DataItem.VarType">
<summary>
Type of data to be read (default is bytes)
</summary>
</member>
<member name="P:S7.Net.Types.DataItem.DB">
<summary>
Address of memory area to read (example: for DB1 this value is 1, for T45 this value is 45)
</summary>
</member>
<member name="P:S7.Net.Types.DataItem.StartByteAdr">
<summary>
Address of the first byte to read
</summary>
</member>
<member name="P:S7.Net.Types.DataItem.Count">
<summary>
Number of variables to read
</summary>
</member>
<member name="P:S7.Net.Types.DataItem.Value">
<summary>
Contains the value of the memory area after the read has been executed
</summary>
</member>
<member name="M:S7.Net.Types.DataItem.#ctor">
<summary>
Create an instance of DataItem
</summary>
</member>
<member name="T:S7.Net.Types.DInt">
<summary>
Contains the conversion methods to convert DInt from S7 plc to C# int (Int32).
</summary>
</member>
<member name="M:S7.Net.Types.DInt.FromByteArray(System.Byte[])">
<summary>
Converts a S7 DInt (4 bytes) to int (Int32)
</summary>
</member>
<member name="M:S7.Net.Types.DInt.FromBytes(System.Byte,System.Byte,System.Byte,System.Byte)">
<summary>
Converts a S7 DInt (4 bytes) to int (Int32)
</summary>
</member>
<member name="M:S7.Net.Types.DInt.ToByteArray(System.Int32)">
<summary>
Converts a int (Int32) to S7 DInt (4 bytes)
</summary>
</member>
<member name="M:S7.Net.Types.DInt.ToByteArray(System.Int32[])">
<summary>
Converts an array of int (Int32) to an array of bytes
</summary>
</member>
<member name="M:S7.Net.Types.DInt.ToArray(System.Byte[])">
<summary>
Converts an array of S7 DInt to an array of int (Int32)
</summary>
</member>
<member name="M:S7.Net.Types.DInt.CDWord(System.Int64)">
<summary>
Converts from C# long (Int64) to C# int (Int32)
</summary>
</member>
<member name="T:S7.Net.Types.Double">
<summary>
Contains the conversion methods to convert Real from S7 plc to C# double.
</summary>
</member>
<member name="M:S7.Net.Types.Double.FromByteArray(System.Byte[])">
<summary>
Converts a S7 Real (4 bytes) to double
</summary>
</member>
<member name="M:S7.Net.Types.Double.FromDWord(System.Int32)">
<summary>
Converts a S7 DInt to double
</summary>
</member>
<member name="M:S7.Net.Types.Double.FromDWord(System.UInt32)">
<summary>
Converts a S7 DWord to double
</summary>
</member>
<member name="M:S7.Net.Types.Double.ToByteArray(System.Double)">
<summary>
Converts a double to S7 Real (4 bytes)
</summary>
</member>
<member name="M:S7.Net.Types.Double.ToByteArray(System.Double[])">
<summary>
Converts an array of double to an array of bytes
</summary>
</member>
<member name="M:S7.Net.Types.Double.ToArray(System.Byte[])">
<summary>
Converts an array of S7 Real to an array of double
</summary>
</member>
<member name="T:S7.Net.Types.DWord">
<summary>
Contains the conversion methods to convert DWord from S7 plc to C#.
</summary>
</member>
<member name="M:S7.Net.Types.DWord.FromByteArray(System.Byte[])">
<summary>
Converts a S7 DWord (4 bytes) to uint (UInt32)
</summary>
</member>
<member name="M:S7.Net.Types.DWord.FromBytes(System.Byte,System.Byte,System.Byte,System.Byte)">
<summary>
Converts a S7 DWord (4 bytes) to uint (UInt32)
</summary>
</member>
<member name="M:S7.Net.Types.DWord.ToByteArray(System.UInt32)">
<summary>
Converts a uint (UInt32) to S7 DWord (4 bytes)
</summary>
</member>
<member name="M:S7.Net.Types.DWord.ToByteArray(System.UInt32[])">
<summary>
Converts an array of uint (UInt32) to an array of S7 DWord (4 bytes)
</summary>
</member>
<member name="M:S7.Net.Types.DWord.ToArray(System.Byte[])">
<summary>
Converts an array of S7 DWord to an array of uint (UInt32)
</summary>
</member>
<member name="T:S7.Net.Types.Int">
<summary>
Contains the conversion methods to convert Int from S7 plc to C#.
</summary>
</member>
<member name="M:S7.Net.Types.Int.FromByteArray(System.Byte[])">
<summary>
Converts a S7 Int (2 bytes) to short (Int16)
</summary>
</member>
<member name="M:S7.Net.Types.Int.FromBytes(System.Byte,System.Byte)">
<summary>
Converts a S7 Int (2 bytes) to short (Int16)
</summary>
</member>
<member name="M:S7.Net.Types.Int.ToByteArray(System.Int16)">
<summary>
Converts a short (Int16) to a S7 Int byte array (2 bytes)
</summary>
</member>
<member name="M:S7.Net.Types.Int.ToByteArray(System.Int16[])">
<summary>
Converts an array of short (Int16) to a S7 Int byte array (2 bytes)
</summary>
</member>
<member name="M:S7.Net.Types.Int.ToArray(System.Byte[])">
<summary>
Converts an array of S7 Int to an array of short (Int16)
</summary>
</member>
<member name="M:S7.Net.Types.Int.CWord(System.Int32)">
<summary>
Converts a C# int value to a C# short value, to be used as word.
</summary>
<param name="value"></param>
<returns></returns>
</member>
<member name="T:S7.Net.Types.String">
<summary>
Contains the methods to convert from S7 strings to C# strings
</summary>
</member>
<member name="M:S7.Net.Types.String.ToByteArray(System.String)">
<summary>
Converts a string to S7 bytes
</summary>
</member>
<member name="M:S7.Net.Types.String.FromByteArray(System.Byte[])">
<summary>
Converts S7 bytes to a string
</summary>
<param name="bytes"></param>
<returns></returns>
</member>
<member name="T:S7.Net.Types.Struct">
<summary>
Contains the method to convert a C# struct to S7 data types
</summary>
</member>
<member name="M:S7.Net.Types.Struct.GetStructSize(System.Type)">
<summary>
Gets the size of the struct in bytes.
</summary>
<param name="structType">the type of the struct</param>
<returns>the number of bytes</returns>
</member>
<member name="M:S7.Net.Types.Struct.FromBytes(System.Type,System.Byte[])">
<summary>
Creates a struct of a specified type by an array of bytes.
</summary>
<param name="structType">The struct type</param>
<param name="bytes">The array of bytes</param>
<returns>The object depending on the struct type or null if fails(array-length != struct-length</returns>
</member>
<member name="M:S7.Net.Types.Struct.ToBytes(System.Object)">
<summary>
Creates a byte array depending on the struct type.
</summary>
<param name="structValue">The struct object</param>
<returns>A byte array or null if fails.</returns>
</member>
<member name="T:S7.Net.Types.Timer">
<summary>
Converts the Timer data type to C# data type
</summary>
</member>
<member name="M:S7.Net.Types.Timer.FromByteArray(System.Byte[])">
<summary>
Converts the timer bytes to a double
</summary>
</member>
<member name="M:S7.Net.Types.Timer.ToByteArray(System.UInt16)">
<summary>
Converts a ushort (UInt16) to an array of bytes formatted as time
</summary>
</member>
<member name="M:S7.Net.Types.Timer.ToByteArray(System.UInt16[])">
<summary>
Converts an array of ushorts (Uint16) to an array of bytes formatted as time
</summary>
</member>
<member name="M:S7.Net.Types.Timer.ToArray(System.Byte[])">
<summary>
Converts an array of bytes formatted as time to an array of doubles
</summary>
<param name="bytes"></param>
<returns></returns>
</member>
<member name="T:S7.Net.Types.Word">
<summary>
Contains the conversion methods to convert Words from S7 plc to C#.
</summary>
</member>
<member name="M:S7.Net.Types.Word.FromByteArray(System.Byte[])">
<summary>
Converts a word (2 bytes) to ushort (UInt16)
</summary>
</member>
<member name="M:S7.Net.Types.Word.FromBytes(System.Byte,System.Byte)">
<summary>
Converts a word (2 bytes) to ushort (UInt16)
</summary>
</member>
<member name="M:S7.Net.Types.Word.ToByteArray(System.UInt16)">
<summary>
Converts a ushort (UInt16) to word (2 bytes)
</summary>
</member>
<member name="M:S7.Net.Types.Word.ToByteArray(System.UInt16[])">
<summary>
Converts an array of ushort (UInt16) to an array of bytes
</summary>
</member>
<member name="M:S7.Net.Types.Word.ToArray(System.Byte[])">
<summary>
Converts an array of bytes to an array of ushort
</summary>
</member>
</members>
</doc>

Binary file not shown.

View File

@@ -1,917 +0,0 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>S7.Net</name>
</assembly>
<members>
<member name="T:S7.Net.Conversion">
<summary>
Conversion methods to convert from Siemens numeric format to C# and back
</summary>
</member>
<member name="M:S7.Net.Conversion.BinStringToInt32(System.String)">
<summary>
Converts a binary string to Int32 value
</summary>
<param name="txt"></param>
<returns></returns>
</member>
<member name="M:S7.Net.Conversion.BinStringToByte(System.String)">
<summary>
Converts a binary string to a byte. Can return null.
</summary>
<param name="txt"></param>
<returns></returns>
</member>
<member name="M:S7.Net.Conversion.ValToBinString(System.Object)">
<summary>
Converts the value to a binary string
</summary>
<param name="value"></param>
<returns></returns>
</member>
<member name="M:S7.Net.Conversion.SelectBit(System.Byte,System.Int32)">
<summary>
Helper to get a bit value given a byte and the bit index.
Example: DB1.DBX0.5 -> var bytes = ReadBytes(DB1.DBW0); bool bit = bytes[0].SelectBit(5);
</summary>
<param name="data"></param>
<param name="bitPosition"></param>
<returns></returns>
</member>
<member name="M:S7.Net.Conversion.ConvertToShort(System.UInt16)">
<summary>
Converts from ushort value to short value; it's used to retrieve negative values from words
</summary>
<param name="input"></param>
<returns></returns>
</member>
<member name="M:S7.Net.Conversion.ConvertToUshort(System.Int16)">
<summary>
Converts from short value to ushort value; it's used to pass negative values to DWs
</summary>
<param name="input"></param>
<returns></returns>
</member>
<member name="M:S7.Net.Conversion.ConvertToInt(System.UInt32)">
<summary>
Converts from UInt32 value to Int32 value; it's used to retrieve negative values from DBDs
</summary>
<param name="input"></param>
<returns></returns>
</member>
<member name="M:S7.Net.Conversion.ConvertToUInt(System.Int32)">
<summary>
Converts from Int32 value to UInt32 value; it's used to pass negative values to DBDs
</summary>
<param name="input"></param>
<returns></returns>
</member>
<member name="M:S7.Net.Conversion.ConvertToUInt(System.Double)">
<summary>
Converts from double to DWord (DBD)
</summary>
<param name="input"></param>
<returns></returns>
</member>
<member name="M:S7.Net.Conversion.ConvertToDouble(System.UInt32)">
<summary>
Converts from DWord (DBD) to double
</summary>
<param name="input"></param>
<returns></returns>
</member>
<member name="T:S7.Net.CpuType">
<summary>
Types of S7 cpu supported by the library
</summary>
</member>
<member name="F:S7.Net.CpuType.S7200">
<summary>
S7 200 cpu type
</summary>
</member>
<member name="F:S7.Net.CpuType.S7300">
<summary>
S7 300 cpu type
</summary>
</member>
<member name="F:S7.Net.CpuType.S7400">
<summary>
S7 400 cpu type
</summary>
</member>
<member name="F:S7.Net.CpuType.S71200">
<summary>
S7 1200 cpu type
</summary>
</member>
<member name="F:S7.Net.CpuType.S71500">
<summary>
S7 1500 cpu type
</summary>
</member>
<member name="T:S7.Net.ErrorCode">
<summary>
Types of error code that can be set after a function is called
</summary>
</member>
<member name="F:S7.Net.ErrorCode.NoError">
<summary>
The function has been executed correctly
</summary>
</member>
<member name="F:S7.Net.ErrorCode.WrongCPU_Type">
<summary>
Wrong type of CPU error
</summary>
</member>
<member name="F:S7.Net.ErrorCode.ConnectionError">
<summary>
Connection error
</summary>
</member>
<member name="F:S7.Net.ErrorCode.IPAddressNotAvailable">
<summary>
Ip address not available
</summary>
</member>
<member name="F:S7.Net.ErrorCode.WrongVarFormat">
<summary>
Wrong format of the variable
</summary>
</member>
<member name="F:S7.Net.ErrorCode.WrongNumberReceivedBytes">
<summary>
Wrong number of received bytes
</summary>
</member>
<member name="F:S7.Net.ErrorCode.SendData">
<summary>
Error on send data
</summary>
</member>
<member name="F:S7.Net.ErrorCode.ReadData">
<summary>
Error on read data
</summary>
</member>
<member name="F:S7.Net.ErrorCode.WriteData">
<summary>
Error on write data
</summary>
</member>
<member name="T:S7.Net.DataType">
<summary>
Types of memory area that can be read
</summary>
</member>
<member name="F:S7.Net.DataType.Input">
<summary>
Input area memory
</summary>
</member>
<member name="F:S7.Net.DataType.Output">
<summary>
Output area memory
</summary>
</member>
<member name="F:S7.Net.DataType.Memory">
<summary>
Merkers area memory (M0, M0.0, ...)
</summary>
</member>
<member name="F:S7.Net.DataType.DataBlock">
<summary>
DB area memory (DB1, DB2, ...)
</summary>
</member>
<member name="F:S7.Net.DataType.Timer">
<summary>
Timer area memory(T1, T2, ...)
</summary>
</member>
<member name="F:S7.Net.DataType.Counter">
<summary>
Counter area memory (C1, C2, ...)
</summary>
</member>
<member name="T:S7.Net.VarType">
<summary>
Types
</summary>
</member>
<member name="F:S7.Net.VarType.Bit">
<summary>
S7 Bit variable type (bool)
</summary>
</member>
<member name="F:S7.Net.VarType.Byte">
<summary>
S7 Byte variable type (8 bits)
</summary>
</member>
<member name="F:S7.Net.VarType.Word">
<summary>
S7 Word variable type (16 bits, 2 bytes)
</summary>
</member>
<member name="F:S7.Net.VarType.DWord">
<summary>
S7 DWord variable type (32 bits, 4 bytes)
</summary>
</member>
<member name="F:S7.Net.VarType.Int">
<summary>
S7 Int variable type (16 bits, 2 bytes)
</summary>
</member>
<member name="F:S7.Net.VarType.DInt">
<summary>
DInt variable type (32 bits, 4 bytes)
</summary>
</member>
<member name="F:S7.Net.VarType.Real">
<summary>
Real variable type (32 bits, 4 bytes)
</summary>
</member>
<member name="F:S7.Net.VarType.String">
<summary>
String variable type (variable)
</summary>
</member>
<member name="F:S7.Net.VarType.Timer">
<summary>
Timer variable type
</summary>
</member>
<member name="F:S7.Net.VarType.Counter">
<summary>
Counter variable type
</summary>
</member>
<member name="T:S7.Net.Plc">
<summary>
Creates an instance of S7.Net driver
</summary>
</member>
<member name="P:S7.Net.Plc.IP">
<summary>
Ip address of the plc
</summary>
</member>
<member name="P:S7.Net.Plc.CPU">
<summary>
Cpu type of the plc
</summary>
</member>
<member name="P:S7.Net.Plc.Rack">
<summary>
Rack of the plc
</summary>
</member>
<member name="P:S7.Net.Plc.Slot">
<summary>
Slot of the CPU of the plc
</summary>
</member>
<member name="P:S7.Net.Plc.IsAvailable">
<summary>
Returns true if a connection to the plc can be established
</summary>
</member>
<member name="P:S7.Net.Plc.IsConnected">
<summary>
Checks if the socket is connected and polls the other peer (the plc) to see if it's connected.
This is the variable that you should continously check to see if the communication is working
See also: http://stackoverflow.com/questions/2661764/how-to-check-if-a-socket-is-connected-disconnected-in-c
</summary>
</member>
<member name="P:S7.Net.Plc.LastErrorString">
<summary>
Contains the last error registered when executing a function
</summary>
</member>
<member name="P:S7.Net.Plc.LastErrorCode">
<summary>
Contains the last error code registered when executing a function
</summary>
</member>
<member name="M:S7.Net.Plc.#ctor(S7.Net.CpuType,System.String,System.Int16,System.Int16)">
<summary>
Creates a PLC object with all the parameters needed for connections.
For S7-1200 and S7-1500, the default is rack = 0 and slot = 0.
You need slot > 0 if you are connecting to external ethernet card (CP).
For S7-300 and S7-400 the default is rack = 0 and slot = 2.
</summary>
<param name="cpu">CpuType of the plc (select from the enum)</param>
<param name="ip">Ip address of the plc</param>
<param name="rack">rack of the plc, usually it's 0, but check in the hardware configuration of Step7 or TIA portal</param>
<param name="slot">slot of the CPU of the plc, usually it's 2 for S7300-S7400, 0 for S7-1200 and S7-1500.
If you use an external ethernet card, this must be set accordingly.</param>
</member>
<member name="M:S7.Net.Plc.Open">
<summary>
Open a socket and connects to the plc, sending all the corrected package and returning if the connection was successful (ErroreCode.NoError) of it was wrong.
</summary>
<returns>Returns ErrorCode.NoError if the connection was successful, otherwise check the ErrorCode</returns>
</member>
<member name="M:S7.Net.Plc.Close">
<summary>
Disonnects from the plc and close the socket
</summary>
</member>
<member name="M:S7.Net.Plc.ReadMultipleVars(System.Collections.Generic.List{S7.Net.Types.DataItem})">
<summary>
Reads multiple vars in a single request.
You have to create and pass a list of DataItems and you obtain in response the same list with the values.
Values are stored in the property "Value" of the dataItem and are already converted.
If you don't want the conversion, just create a dataItem of bytes.
DataItems must not be more than 20 (protocol restriction) and bytes must not be more than 200 + 22 of header (protocol restriction).
</summary>
<param name="dataItems">List of dataitems that contains the list of variables that must be read. Maximum 20 dataitems are accepted.</param>
</member>
<member name="M:S7.Net.Plc.ReadBytes(S7.Net.DataType,System.Int32,System.Int32,System.Int32)">
<summary>
Reads a number of bytes from a DB starting from a specified index. This handles more than 200 bytes with multiple requests.
If the read was not successful, check LastErrorCode or LastErrorString.
</summary>
<param name="dataType">Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output.</param>
<param name="db">Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc.</param>
<param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
<param name="count">Byte count, if you want to read 120 bytes, set this to 120.</param>
<returns>Returns the bytes in an array</returns>
</member>
<member name="M:S7.Net.Plc.Read(S7.Net.DataType,System.Int32,System.Int32,S7.Net.VarType,System.Int32)">
<summary>
Read and decode a certain number of bytes of the "VarType" provided.
This can be used to read multiple consecutive variables of the same type (Word, DWord, Int, etc).
If the read was not successful, check LastErrorCode or LastErrorString.
</summary>
<param name="dataType">Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output.</param>
<param name="db">Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc.</param>
<param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
<param name="varType">Type of the variable/s that you are reading</param>
<param name="varCount"></param>
</member>
<member name="M:S7.Net.Plc.Read(System.String)">
<summary>
Reads a single variable from the plc, takes in input strings like "DB1.DBX0.0", "DB20.DBD200", "MB20", "T45", etc.
If the read was not successful, check LastErrorCode or LastErrorString.
</summary>
<param name="variable">Input strings like "DB1.DBX0.0", "DB20.DBD200", "MB20", "T45", etc.</param>
<returns>Returns an object that contains the value. This object must be cast accordingly.</returns>
</member>
<member name="M:S7.Net.Plc.ReadStruct(System.Type,System.Int32,System.Int32)">
<summary>
Reads all the bytes needed to fill a struct in C#, starting from a certain address, and return an object that can be casted to the struct.
</summary>
<param name="structType">Type of the struct to be readed (es.: TypeOf(MyStruct)).</param>
<param name="db">Address of the DB.</param>
<param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
<returns>Returns a struct that must be cast.</returns>
</member>
<member name="M:S7.Net.Plc.ReadStruct``1(System.Int32,System.Int32)">
<summary>
Reads all the bytes needed to fill a struct in C#, starting from a certain address, and returns the struct or null if nothing was read.
</summary>
<typeparam name="T">The struct type</typeparam>
<param name="db">Address of the DB.</param>
<param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
<returns>Returns a nulable struct. If nothing was read null will be returned.</returns>
</member>
<member name="M:S7.Net.Plc.ReadClass(System.Object,System.Int32,System.Int32)">
<summary>
Reads all the bytes needed to fill a class in C#, starting from a certain address, and set all the properties values to the value that are read from the plc.
This reads only properties, it doesn't read private variable or public variable without {get;set;} specified.
</summary>
<param name="sourceClass">Instance of the class that will store the values</param>
<param name="db">Index of the DB; es.: 1 is for DB1</param>
<param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
<returns>The number of read bytes</returns>
</member>
<member name="M:S7.Net.Plc.ReadClass``1(System.Int32,System.Int32)">
<summary>
Reads all the bytes needed to fill a class in C#, starting from a certain address, and set all the properties values to the value that are read from the plc.
This reads only properties, it doesn't read private variable or public variable without {get;set;} specified. To instantiate the class defined by the generic
type, the class needs a default constructor.
</summary>
<typeparam name="T">The class that will be instantiated. Requires a default constructor</typeparam>
<param name="db">Index of the DB; es.: 1 is for DB1</param>
<param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
<returns>An instance of the class with the values read from the plc. If no data has been read, null will be returned</returns>
</member>
<member name="M:S7.Net.Plc.ReadClass``1(System.Func{``0},System.Int32,System.Int32)">
<summary>
Reads all the bytes needed to fill a class in C#, starting from a certain address, and set all the properties values to the value that are read from the plc.
This reads only properties, it doesn't read private variable or public variable without {get;set;} specified.
</summary>
<typeparam name="T">The class that will be instantiated</typeparam>
<param name="classFactory">Function to instantiate the class</param>
<param name="db">Index of the DB; es.: 1 is for DB1</param>
<param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
<returns>An instance of the class with the values read from the plc. If no data has been read, null will be returned</returns>
</member>
<member name="M:S7.Net.Plc.WriteBytes(S7.Net.DataType,System.Int32,System.Int32,System.Byte[])">
<summary>
Write a number of bytes from a DB starting from a specified index. This handles more than 200 bytes with multiple requests.
If the write was not successful, check LastErrorCode or LastErrorString.
</summary>
<param name="dataType">Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output.</param>
<param name="db">Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc.</param>
<param name="startByteAdr">Start byte address. If you want to write DB1.DBW200, this is 200.</param>
<param name="value">Bytes to write. If more than 200, multiple requests will be made.</param>
<returns>NoError if it was successful, or the error is specified</returns>
</member>
<member name="M:S7.Net.Plc.WriteBit(S7.Net.DataType,System.Int32,System.Int32,System.Int32,System.Boolean)">
<summary>
Write a single bit from a DB with the specified index.
</summary>
<param name="dataType">Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output.</param>
<param name="db">Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc.</param>
<param name="startByteAdr">Start byte address. If you want to write DB1.DBW200, this is 200.</param>
<param name="bitAdr">The address of the bit. (0-7)</param>
<param name="value">Bytes to write. If more than 200, multiple requests will be made.</param>
<returns>NoError if it was successful, or the error is specified</returns>
</member>
<member name="M:S7.Net.Plc.WriteBit(S7.Net.DataType,System.Int32,System.Int32,System.Int32,System.Int32)">
<summary>
Write a single bit from a DB with the specified index.
</summary>
<param name="dataType">Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output.</param>
<param name="db">Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc.</param>
<param name="startByteAdr">Start byte address. If you want to write DB1.DBW200, this is 200.</param>
<param name="bitAdr">The address of the bit. (0-7)</param>
<param name="value">Bytes to write. If more than 200, multiple requests will be made.</param>
<returns>NoError if it was successful, or the error is specified</returns>
</member>
<member name="M:S7.Net.Plc.Write(S7.Net.DataType,System.Int32,System.Int32,System.Object,System.Int32)">
<summary>
Takes in input an object and tries to parse it to an array of values. This can be used to write many data, all of the same type.
You must specify the memory area type, memory are address, byte start address and bytes count.
If the read was not successful, check LastErrorCode or LastErrorString.
</summary>
<param name="dataType">Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output.</param>
<param name="db">Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc.</param>
<param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
<param name="value">Bytes to write. The lenght of this parameter can't be higher than 200. If you need more, use recursion.</param>
<param name="bitAdr">The address of the bit. (0-7)</param>
<returns>NoError if it was successful, or the error is specified</returns>
</member>
<member name="M:S7.Net.Plc.Write(System.String,System.Object)">
<summary>
Writes a single variable from the plc, takes in input strings like "DB1.DBX0.0", "DB20.DBD200", "MB20", "T45", etc.
If the write was not successful, check LastErrorCode or LastErrorString.
</summary>
<param name="variable">Input strings like "DB1.DBX0.0", "DB20.DBD200", "MB20", "T45", etc.</param>
<param name="value">Value to be written to the plc</param>
<returns>NoError if it was successful, or the error is specified</returns>
</member>
<member name="M:S7.Net.Plc.WriteStruct(System.Object,System.Int32,System.Int32)">
<summary>
Writes a C# struct to a DB in the plc
</summary>
<param name="structValue">The struct to be written</param>
<param name="db">Db address</param>
<param name="startByteAdr">Start bytes on the plc</param>
<returns>NoError if it was successful, or the error is specified</returns>
</member>
<member name="M:S7.Net.Plc.WriteClass(System.Object,System.Int32,System.Int32)">
<summary>
Writes a C# class to a DB in the plc
</summary>
<param name="classValue">The class to be written</param>
<param name="db">Db address</param>
<param name="startByteAdr">Start bytes on the plc</param>
<returns>NoError if it was successful, or the error is specified</returns>
</member>
<member name="M:S7.Net.Plc.ClearLastError">
<summary>
Sets the LastErrorCode to NoError and LastErrorString to String.Empty
</summary>
</member>
<member name="M:S7.Net.Plc.ReadHeaderPackage(System.Int32)">
<summary>
Creates the header to read bytes from the plc
</summary>
<param name="amount"></param>
<returns></returns>
</member>
<member name="M:S7.Net.Plc.CreateReadDataRequestPackage(S7.Net.DataType,System.Int32,System.Int32,System.Int32)">
<summary>
Create the bytes-package to request data from the plc. You have to specify the memory type (dataType),
the address of the memory, the address of the byte and the bytes count.
</summary>
<param name="dataType">MemoryType (DB, Timer, Counter, etc.)</param>
<param name="db">Address of the memory to be read</param>
<param name="startByteAdr">Start address of the byte</param>
<param name="count">Number of bytes to be read</param>
<returns></returns>
</member>
<member name="M:S7.Net.Plc.WriteBytesWithASingleRequest(S7.Net.DataType,System.Int32,System.Int32,System.Byte[])">
<summary>
Writes up to 200 bytes to the plc and returns NoError if successful. You must specify the memory area type, memory are address, byte start address and bytes count.
If the write was not successful, check LastErrorCode or LastErrorString.
</summary>
<param name="dataType">Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output.</param>
<param name="db">Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc.</param>
<param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
<param name="value">Bytes to write. The lenght of this parameter can't be higher than 200. If you need more, use recursion.</param>
<returns>NoError if it was successful, or the error is specified</returns>
</member>
<member name="M:S7.Net.Plc.ParseBytes(S7.Net.VarType,System.Byte[],System.Int32)">
<summary>
Given a S7 variable type (Bool, Word, DWord, etc.), it converts the bytes in the appropriate C# format.
</summary>
<param name="varType"></param>
<param name="bytes"></param>
<param name="varCount"></param>
<returns></returns>
</member>
<member name="M:S7.Net.Plc.VarTypeToByteLength(S7.Net.VarType,System.Int32)">
<summary>
Given a S7 variable type (Bool, Word, DWord, etc.), it returns how many bytes to read.
</summary>
<param name="varType"></param>
<param name="varCount"></param>
<returns></returns>
</member>
<member name="M:S7.Net.Plc.Dispose">
<summary>
Releases all resources, disonnects from the plc and closes the socket
</summary>
</member>
<member name="T:S7.Net.Types.Boolean">
<summary>
Contains the methods to read, set and reset bits inside bytes
</summary>
</member>
<member name="M:S7.Net.Types.Boolean.GetValue(System.Byte,System.Int32)">
<summary>
Returns the value of a bit in a bit, given the address of the bit
</summary>
</member>
<member name="M:S7.Net.Types.Boolean.SetBit(System.Byte,System.Int32)">
<summary>
Sets the value of a bit to 1 (true), given the address of the bit
</summary>
</member>
<member name="M:S7.Net.Types.Boolean.ClearBit(System.Byte,System.Int32)">
<summary>
Resets the value of a bit to 0 (false), given the address of the bit
</summary>
</member>
<member name="T:S7.Net.Types.Byte">
<summary>
Contains the methods to convert from bytes to byte arrays
</summary>
</member>
<member name="M:S7.Net.Types.Byte.ToByteArray(System.Byte)">
<summary>
Converts a byte to byte array
</summary>
</member>
<member name="M:S7.Net.Types.Byte.FromByteArray(System.Byte[])">
<summary>
Converts a byte array to byte
</summary>
<param name="bytes"></param>
<returns></returns>
</member>
<member name="T:S7.Net.Types.Class">
<summary>
Contains the methods to convert a C# class to S7 data types
</summary>
</member>
<member name="M:S7.Net.Types.Class.GetClassSize(System.Object)">
<summary>
Gets the size of the class in bytes.
</summary>
<param name="instance">An instance of the class</param>
<returns>the number of bytes</returns>
</member>
<member name="M:S7.Net.Types.Class.FromBytes(System.Object,System.Byte[])">
<summary>
Sets the object's values with the given array of bytes
</summary>
<param name="sourceClass">The object to fill in the given array of bytes</param>
<param name="bytes">The array of bytes</param>
</member>
<member name="M:S7.Net.Types.Class.ToBytes(System.Object)">
<summary>
Creates a byte array depending on the struct type.
</summary>
<param name="sourceClass">The struct object</param>
<returns>A byte array or null if fails.</returns>
</member>
<member name="T:S7.Net.Types.Counter">
<summary>
Contains the conversion methods to convert Counter from S7 plc to C# ushort (UInt16).
</summary>
</member>
<member name="M:S7.Net.Types.Counter.FromByteArray(System.Byte[])">
<summary>
Converts a Counter (2 bytes) to ushort (UInt16)
</summary>
</member>
<member name="M:S7.Net.Types.Counter.FromBytes(System.Byte,System.Byte)">
<summary>
Converts a Counter (2 bytes) to ushort (UInt16)
</summary>
</member>
<member name="M:S7.Net.Types.Counter.ToByteArray(System.UInt16)">
<summary>
Converts a ushort (UInt16) to word (2 bytes)
</summary>
</member>
<member name="M:S7.Net.Types.Counter.ToByteArray(System.UInt16[])">
<summary>
Converts an array of ushort (UInt16) to an array of bytes
</summary>
</member>
<member name="M:S7.Net.Types.Counter.ToArray(System.Byte[])">
<summary>
Converts an array of bytes to an array of ushort
</summary>
</member>
<member name="T:S7.Net.Types.DataItem">
<summary>
Create an instance of a memory block that can be read by using ReadMultipleVars
</summary>
</member>
<member name="P:S7.Net.Types.DataItem.DataType">
<summary>
Memory area to read
</summary>
</member>
<member name="P:S7.Net.Types.DataItem.VarType">
<summary>
Type of data to be read (default is bytes)
</summary>
</member>
<member name="P:S7.Net.Types.DataItem.DB">
<summary>
Address of memory area to read (example: for DB1 this value is 1, for T45 this value is 45)
</summary>
</member>
<member name="P:S7.Net.Types.DataItem.StartByteAdr">
<summary>
Address of the first byte to read
</summary>
</member>
<member name="P:S7.Net.Types.DataItem.Count">
<summary>
Number of variables to read
</summary>
</member>
<member name="P:S7.Net.Types.DataItem.Value">
<summary>
Contains the value of the memory area after the read has been executed
</summary>
</member>
<member name="M:S7.Net.Types.DataItem.#ctor">
<summary>
Create an instance of DataItem
</summary>
</member>
<member name="T:S7.Net.Types.DInt">
<summary>
Contains the conversion methods to convert DInt from S7 plc to C# int (Int32).
</summary>
</member>
<member name="M:S7.Net.Types.DInt.FromByteArray(System.Byte[])">
<summary>
Converts a S7 DInt (4 bytes) to int (Int32)
</summary>
</member>
<member name="M:S7.Net.Types.DInt.FromBytes(System.Byte,System.Byte,System.Byte,System.Byte)">
<summary>
Converts a S7 DInt (4 bytes) to int (Int32)
</summary>
</member>
<member name="M:S7.Net.Types.DInt.ToByteArray(System.Int32)">
<summary>
Converts a int (Int32) to S7 DInt (4 bytes)
</summary>
</member>
<member name="M:S7.Net.Types.DInt.ToByteArray(System.Int32[])">
<summary>
Converts an array of int (Int32) to an array of bytes
</summary>
</member>
<member name="M:S7.Net.Types.DInt.ToArray(System.Byte[])">
<summary>
Converts an array of S7 DInt to an array of int (Int32)
</summary>
</member>
<member name="M:S7.Net.Types.DInt.CDWord(System.Int64)">
<summary>
Converts from C# long (Int64) to C# int (Int32)
</summary>
</member>
<member name="T:S7.Net.Types.Double">
<summary>
Contains the conversion methods to convert Real from S7 plc to C# double.
</summary>
</member>
<member name="M:S7.Net.Types.Double.FromByteArray(System.Byte[])">
<summary>
Converts a S7 Real (4 bytes) to double
</summary>
</member>
<member name="M:S7.Net.Types.Double.FromDWord(System.Int32)">
<summary>
Converts a S7 DInt to double
</summary>
</member>
<member name="M:S7.Net.Types.Double.FromDWord(System.UInt32)">
<summary>
Converts a S7 DWord to double
</summary>
</member>
<member name="M:S7.Net.Types.Double.ToByteArray(System.Double)">
<summary>
Converts a double to S7 Real (4 bytes)
</summary>
</member>
<member name="M:S7.Net.Types.Double.ToByteArray(System.Double[])">
<summary>
Converts an array of double to an array of bytes
</summary>
</member>
<member name="M:S7.Net.Types.Double.ToArray(System.Byte[])">
<summary>
Converts an array of S7 Real to an array of double
</summary>
</member>
<member name="T:S7.Net.Types.DWord">
<summary>
Contains the conversion methods to convert DWord from S7 plc to C#.
</summary>
</member>
<member name="M:S7.Net.Types.DWord.FromByteArray(System.Byte[])">
<summary>
Converts a S7 DWord (4 bytes) to uint (UInt32)
</summary>
</member>
<member name="M:S7.Net.Types.DWord.FromBytes(System.Byte,System.Byte,System.Byte,System.Byte)">
<summary>
Converts a S7 DWord (4 bytes) to uint (UInt32)
</summary>
</member>
<member name="M:S7.Net.Types.DWord.ToByteArray(System.UInt32)">
<summary>
Converts a uint (UInt32) to S7 DWord (4 bytes)
</summary>
</member>
<member name="M:S7.Net.Types.DWord.ToByteArray(System.UInt32[])">
<summary>
Converts an array of uint (UInt32) to an array of S7 DWord (4 bytes)
</summary>
</member>
<member name="M:S7.Net.Types.DWord.ToArray(System.Byte[])">
<summary>
Converts an array of S7 DWord to an array of uint (UInt32)
</summary>
</member>
<member name="T:S7.Net.Types.Int">
<summary>
Contains the conversion methods to convert Int from S7 plc to C#.
</summary>
</member>
<member name="M:S7.Net.Types.Int.FromByteArray(System.Byte[])">
<summary>
Converts a S7 Int (2 bytes) to short (Int16)
</summary>
</member>
<member name="M:S7.Net.Types.Int.FromBytes(System.Byte,System.Byte)">
<summary>
Converts a S7 Int (2 bytes) to short (Int16)
</summary>
</member>
<member name="M:S7.Net.Types.Int.ToByteArray(System.Int16)">
<summary>
Converts a short (Int16) to a S7 Int byte array (2 bytes)
</summary>
</member>
<member name="M:S7.Net.Types.Int.ToByteArray(System.Int16[])">
<summary>
Converts an array of short (Int16) to a S7 Int byte array (2 bytes)
</summary>
</member>
<member name="M:S7.Net.Types.Int.ToArray(System.Byte[])">
<summary>
Converts an array of S7 Int to an array of short (Int16)
</summary>
</member>
<member name="M:S7.Net.Types.Int.CWord(System.Int32)">
<summary>
Converts a C# int value to a C# short value, to be used as word.
</summary>
<param name="value"></param>
<returns></returns>
</member>
<member name="T:S7.Net.Types.String">
<summary>
Contains the methods to convert from S7 strings to C# strings
</summary>
</member>
<member name="M:S7.Net.Types.String.ToByteArray(System.String)">
<summary>
Converts a string to S7 bytes
</summary>
</member>
<member name="M:S7.Net.Types.String.FromByteArray(System.Byte[])">
<summary>
Converts S7 bytes to a string
</summary>
<param name="bytes"></param>
<returns></returns>
</member>
<member name="T:S7.Net.Types.Struct">
<summary>
Contains the method to convert a C# struct to S7 data types
</summary>
</member>
<member name="M:S7.Net.Types.Struct.GetStructSize(System.Type)">
<summary>
Gets the size of the struct in bytes.
</summary>
<param name="structType">the type of the struct</param>
<returns>the number of bytes</returns>
</member>
<member name="M:S7.Net.Types.Struct.FromBytes(System.Type,System.Byte[])">
<summary>
Creates a struct of a specified type by an array of bytes.
</summary>
<param name="structType">The struct type</param>
<param name="bytes">The array of bytes</param>
<returns>The object depending on the struct type or null if fails(array-length != struct-length</returns>
</member>
<member name="M:S7.Net.Types.Struct.ToBytes(System.Object)">
<summary>
Creates a byte array depending on the struct type.
</summary>
<param name="structValue">The struct object</param>
<returns>A byte array or null if fails.</returns>
</member>
<member name="T:S7.Net.Types.Timer">
<summary>
Converts the Timer data type to C# data type
</summary>
</member>
<member name="M:S7.Net.Types.Timer.FromByteArray(System.Byte[])">
<summary>
Converts the timer bytes to a double
</summary>
</member>
<member name="M:S7.Net.Types.Timer.ToByteArray(System.UInt16)">
<summary>
Converts a ushort (UInt16) to an array of bytes formatted as time
</summary>
</member>
<member name="M:S7.Net.Types.Timer.ToByteArray(System.UInt16[])">
<summary>
Converts an array of ushorts (Uint16) to an array of bytes formatted as time
</summary>
</member>
<member name="M:S7.Net.Types.Timer.ToArray(System.Byte[])">
<summary>
Converts an array of bytes formatted as time to an array of doubles
</summary>
<param name="bytes"></param>
<returns></returns>
</member>
<member name="T:S7.Net.Types.Word">
<summary>
Contains the conversion methods to convert Words from S7 plc to C#.
</summary>
</member>
<member name="M:S7.Net.Types.Word.FromByteArray(System.Byte[])">
<summary>
Converts a word (2 bytes) to ushort (UInt16)
</summary>
</member>
<member name="M:S7.Net.Types.Word.FromBytes(System.Byte,System.Byte)">
<summary>
Converts a word (2 bytes) to ushort (UInt16)
</summary>
</member>
<member name="M:S7.Net.Types.Word.ToByteArray(System.UInt16)">
<summary>
Converts a ushort (UInt16) to word (2 bytes)
</summary>
</member>
<member name="M:S7.Net.Types.Word.ToByteArray(System.UInt16[])">
<summary>
Converts an array of ushort (UInt16) to an array of bytes
</summary>
</member>
<member name="M:S7.Net.Types.Word.ToArray(System.Byte[])">
<summary>
Converts an array of bytes to an array of ushort
</summary>
</member>
</members>
</doc>