mirror of
https://github.com/S7NetPlus/s7netplus.git
synced 2026-02-17 14:28:25 +08:00
Merge branch 'main' into plc-status
This commit is contained in:
6
.github/workflows/dotnet.yml
vendored
6
.github/workflows/dotnet.yml
vendored
@@ -75,7 +75,7 @@ jobs:
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install Snap7 Linux
|
||||
if: ${{ matrix.os == 'ubuntu-20.04' }}
|
||||
@@ -90,14 +90,14 @@ jobs:
|
||||
brew install snap7
|
||||
|
||||
- name: Setup Dotnet
|
||||
uses: actions/setup-dotnet@v1
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: |
|
||||
6.x
|
||||
7.x
|
||||
|
||||
- name: Nuget Cache
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.nuget/packages
|
||||
# Look to see if there is a cache hit for the corresponding requirements file
|
||||
|
||||
@@ -55,6 +55,7 @@ namespace S7.Net
|
||||
/// See: https://tools.ietf.org/html/rfc905
|
||||
/// </summary>
|
||||
/// <param name="stream">The socket to read from</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used to cancel the asynchronous operation.</param>
|
||||
/// <returns>COTP DPDU instance</returns>
|
||||
public static async Task<TPDU> ReadAsync(Stream stream, CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -89,6 +90,7 @@ namespace S7.Net
|
||||
/// See: https://tools.ietf.org/html/rfc905
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream to read from</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used to cancel the asynchronous operation.</param>
|
||||
/// <returns>Data in TSDU</returns>
|
||||
public static async Task<byte[]> ReadAsync(Stream stream, CancellationToken cancellationToken)
|
||||
{
|
||||
|
||||
@@ -33,8 +33,8 @@ namespace S7.Net
|
||||
/// <summary>
|
||||
/// Creates the header to read bytes from the PLC.
|
||||
/// </summary>
|
||||
/// <param name="stream">The <see cref="System.IO.MemoryStream"/> to write to.</param>
|
||||
/// <param name="amount">The amount of items to read.</param>
|
||||
/// <param name="stream">The stream to write to.</param>
|
||||
/// <param name="amount">The number of items to read.</param>
|
||||
private static void WriteReadHeader(System.IO.MemoryStream stream, int amount = 1)
|
||||
{
|
||||
// Header size 19, 12 bytes per item
|
||||
@@ -99,7 +99,7 @@ namespace S7.Net
|
||||
/// 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="stream">The <see cref="System.IO.MemoryStream"/> to write to.</param>
|
||||
/// <param name="stream">The stream to write the read data request to.</param>
|
||||
/// <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>
|
||||
|
||||
@@ -439,7 +439,6 @@ namespace S7.Net
|
||||
|
||||
/// <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>
|
||||
@@ -518,6 +517,7 @@ namespace S7.Net
|
||||
/// <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="cancellationToken">A cancellation token that can be used to cancel the asynchronous operation.</param>
|
||||
/// <returns>A task that represents the asynchronous write operation.</returns>
|
||||
private async Task WriteBytesWithASingleRequestAsync(DataType dataType, int db, int startByteAdr, ReadOnlyMemory<byte> value, CancellationToken cancellationToken)
|
||||
{
|
||||
|
||||
@@ -289,7 +289,6 @@ namespace S7.Net
|
||||
|
||||
/// <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>
|
||||
|
||||
@@ -15,12 +15,13 @@
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PackageTags>PLC Siemens Communication S7</PackageTags>
|
||||
<Copyright>Derek Heiser 2015</Copyright>
|
||||
<LangVersion>8.0</LangVersion>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>Enable</Nullable>
|
||||
<DebugType>portable</DebugType>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<NoWarn>$(NoWarn);CS1591;NETSDK1138</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(TargetFramework)' == 'net452' Or '$(TargetFramework)' == 'net462' Or '$(TargetFramework)' == 'netstandard2.0' ">
|
||||
|
||||
@@ -39,6 +39,7 @@ namespace S7.Net
|
||||
/// <param name="buffer">the buffer to read into</param>
|
||||
/// <param name="offset">the offset in the buffer to read into</param>
|
||||
/// <param name="count">the amount of bytes to read into the buffer</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used to cancel the asynchronous operation.</param>
|
||||
/// <returns>returns the amount of read bytes</returns>
|
||||
public static async Task<int> ReadExactAsync(this Stream stream, byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||
{
|
||||
|
||||
@@ -29,6 +29,7 @@ namespace S7.Net
|
||||
/// Reads a TPKT from the socket Async
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream to read from</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used to cancel the asynchronous operation.</param>
|
||||
/// <returns>Task TPKT Instace</returns>
|
||||
public static async Task<TPKT> ReadAsync(Stream stream, CancellationToken cancellationToken)
|
||||
{
|
||||
|
||||
@@ -64,7 +64,8 @@ namespace S7.Net.Types
|
||||
numBytes += attribute.ReservedLengthInBytes;
|
||||
break;
|
||||
default:
|
||||
var propertyClass = Activator.CreateInstance(type);
|
||||
var propertyClass = Activator.CreateInstance(type) ??
|
||||
throw new ArgumentException($"Failed to create instance of type {type}.", nameof(type));
|
||||
numBytes = GetClassSize(propertyClass, numBytes, true);
|
||||
break;
|
||||
}
|
||||
@@ -76,6 +77,8 @@ namespace S7.Net.Types
|
||||
/// Gets the size of the class in bytes.
|
||||
/// </summary>
|
||||
/// <param name="instance">An instance of the class</param>
|
||||
/// <param name="numBytes">The offset of the current field.</param>
|
||||
/// <param name="isInnerProperty"><see langword="true" /> if this property belongs to a class being serialized as member of the class requested for serialization; otherwise, <see langword="false" />.</param>
|
||||
/// <returns>the number of bytes</returns>
|
||||
public static double GetClassSize(object instance, double numBytes = 0.0, bool isInnerProperty = false)
|
||||
{
|
||||
@@ -84,8 +87,10 @@ namespace S7.Net.Types
|
||||
{
|
||||
if (property.PropertyType.IsArray)
|
||||
{
|
||||
Type elementType = property.PropertyType.GetElementType();
|
||||
Array array = (Array)property.GetValue(instance, null);
|
||||
Type elementType = property.PropertyType.GetElementType()!;
|
||||
Array array = (Array?) property.GetValue(instance, null) ??
|
||||
throw new ArgumentException($"Property {property.Name} on {instance} must have a non-null value to get it's size.", nameof(instance));
|
||||
|
||||
if (array.Length <= 0)
|
||||
{
|
||||
throw new Exception("Cannot determine size of class, because an array is defined which has no fixed size greater than zero.");
|
||||
@@ -199,7 +204,9 @@ namespace S7.Net.Types
|
||||
numBytes += sData.Length;
|
||||
break;
|
||||
default:
|
||||
var propClass = Activator.CreateInstance(propertyType);
|
||||
var propClass = Activator.CreateInstance(propertyType) ??
|
||||
throw new ArgumentException($"Failed to create instance of type {propertyType}.", nameof(propertyType));
|
||||
|
||||
numBytes = FromBytes(propClass, bytes, numBytes);
|
||||
value = propClass;
|
||||
break;
|
||||
@@ -213,6 +220,8 @@ namespace S7.Net.Types
|
||||
/// </summary>
|
||||
/// <param name="sourceClass">The object to fill in the given array of bytes</param>
|
||||
/// <param name="bytes">The array of bytes</param>
|
||||
/// <param name="numBytes">The offset for the current field.</param>
|
||||
/// <param name="isInnerClass"><see langword="true" /> if this class is the type of a member of the class to be serialized; otherwise, <see langword="false" />.</param>
|
||||
public static double FromBytes(object sourceClass, byte[] bytes, double numBytes = 0, bool isInnerClass = false)
|
||||
{
|
||||
if (bytes == null)
|
||||
@@ -223,9 +232,11 @@ namespace S7.Net.Types
|
||||
{
|
||||
if (property.PropertyType.IsArray)
|
||||
{
|
||||
Array array = (Array)property.GetValue(sourceClass, null);
|
||||
Array array = (Array?) property.GetValue(sourceClass, null) ??
|
||||
throw new ArgumentException($"Property {property.Name} on sourceClass must be an array instance.", nameof(sourceClass));
|
||||
|
||||
IncrementToEven(ref numBytes);
|
||||
Type elementType = property.PropertyType.GetElementType();
|
||||
Type elementType = property.PropertyType.GetElementType()!;
|
||||
for (int i = 0; i < array.Length && numBytes < bytes.Length; i++)
|
||||
{
|
||||
array.SetValue(
|
||||
@@ -320,26 +331,30 @@ namespace S7.Net.Types
|
||||
/// <summary>
|
||||
/// Creates a byte array depending on the struct type.
|
||||
/// </summary>
|
||||
/// <param name="sourceClass">The struct object</param>
|
||||
/// <param name="sourceClass">The struct object.</param>
|
||||
/// <param name="bytes">The target byte array.</param>
|
||||
/// <param name="numBytes">The offset for the current field.</param>
|
||||
/// <returns>A byte array or null if fails.</returns>
|
||||
public static double ToBytes(object sourceClass, byte[] bytes, double numBytes = 0.0)
|
||||
{
|
||||
var properties = GetAccessableProperties(sourceClass.GetType());
|
||||
foreach (var property in properties)
|
||||
{
|
||||
var value = property.GetValue(sourceClass, null) ??
|
||||
throw new ArgumentException($"Property {property.Name} on sourceClass can't be null.", nameof(sourceClass));
|
||||
|
||||
if (property.PropertyType.IsArray)
|
||||
{
|
||||
Array array = (Array)property.GetValue(sourceClass, null);
|
||||
Array array = (Array) value;
|
||||
IncrementToEven(ref numBytes);
|
||||
Type elementType = property.PropertyType.GetElementType();
|
||||
for (int i = 0; i < array.Length && numBytes < bytes.Length; i++)
|
||||
{
|
||||
numBytes = SetBytesFromProperty(array.GetValue(i), property, bytes, numBytes);
|
||||
numBytes = SetBytesFromProperty(array.GetValue(i)!, property, bytes, numBytes);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
numBytes = SetBytesFromProperty(property.GetValue(sourceClass, null), property, bytes, numBytes);
|
||||
numBytes = SetBytesFromProperty(value, property, bytes, numBytes);
|
||||
}
|
||||
}
|
||||
return numBytes;
|
||||
|
||||
@@ -141,7 +141,7 @@ namespace S7.Net.Types
|
||||
/// Converts an array of <see cref="T:System.DateTime"/> values to a byte array.
|
||||
/// </summary>
|
||||
/// <param name="dateTimes">The DateTime values to convert.</param>
|
||||
/// <returns>A byte array containing the S7 date time representations of <paramref name="dateTime"/>.</returns>
|
||||
/// <returns>A byte array containing the S7 date time representations of <paramref name="dateTimes"/>.</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException">Thrown when any value of
|
||||
/// <paramref name="dateTimes"/> is before <see cref="P:SpecMinimumDateTime"/>
|
||||
/// or after <see cref="P:SpecMaximumDateTime"/>.</exception>
|
||||
|
||||
@@ -8,17 +8,17 @@ namespace S7.Net.Types
|
||||
/// An S7 String has a preceeding 2 byte header containing its capacity and length
|
||||
/// </summary>
|
||||
public static class S7String
|
||||
{
|
||||
private static Encoding stringEncoding = Encoding.ASCII;
|
||||
|
||||
{
|
||||
private static Encoding stringEncoding = Encoding.ASCII;
|
||||
|
||||
/// <summary>
|
||||
/// The Encoding used when serializing and deserializing S7String (Encoding.ASCII by default)
|
||||
/// </summary>
|
||||
/// <exception cref="ArgumentNullException">StringEncoding must not be null</exception>
|
||||
public static Encoding StringEncoding
|
||||
{
|
||||
get => stringEncoding;
|
||||
set => stringEncoding = value ?? throw new ArgumentNullException(nameof(StringEncoding));
|
||||
/// <exception cref="ArgumentNullException">StringEncoding must not be null</exception>
|
||||
public static Encoding StringEncoding
|
||||
{
|
||||
get => stringEncoding;
|
||||
set => stringEncoding = value ?? throw new ArgumentNullException(nameof(StringEncoding));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -58,7 +58,7 @@ namespace S7.Net.Types
|
||||
/// <param name="value">The string to convert to byte array.</param>
|
||||
/// <param name="reservedLength">The length (in characters) allocated in PLC for the string.</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)
|
||||
public static byte[] ToByteArray(string? value, int reservedLength)
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace S7.Net.Types
|
||||
/// <param name="value">The string to convert to byte array.</param>
|
||||
/// <param name="reservedLength">The length (in characters) allocated in PLC for the string.</param>
|
||||
/// <returns>A <see cref="T:byte[]" /> containing the string header and string value with a maximum length of <paramref name="reservedLength"/> + 4.</returns>
|
||||
public static byte[] ToByteArray(string value, int reservedLength)
|
||||
public static byte[] ToByteArray(string? value, int reservedLength)
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
|
||||
@@ -12,13 +12,15 @@
|
||||
/// <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)
|
||||
{
|
||||
var length = value?.Length;
|
||||
if (length > reservedLength) length = reservedLength;
|
||||
var bytes = new byte[reservedLength];
|
||||
if (value == null) return bytes;
|
||||
|
||||
if (length == null || length == 0) return bytes;
|
||||
var length = value.Length;
|
||||
if (length == 0) return bytes;
|
||||
|
||||
System.Text.Encoding.ASCII.GetBytes(value, 0, length.Value, bytes, 0);
|
||||
if (length > reservedLength) length = reservedLength;
|
||||
|
||||
System.Text.Encoding.ASCII.GetBytes(value, 0, length, bytes, 0);
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
@@ -98,8 +98,8 @@ namespace S7.Net.Types
|
||||
int bytePos = 0;
|
||||
int bitPos = 0;
|
||||
double numBytes = 0.0;
|
||||
object structValue = Activator.CreateInstance(structType);
|
||||
|
||||
object structValue = Activator.CreateInstance(structType) ??
|
||||
throw new ArgumentException($"Failed to create an instance of the type {structType}.", nameof(structType));
|
||||
|
||||
var infos = structValue.GetType()
|
||||
#if NETSTANDARD1_3
|
||||
@@ -254,6 +254,14 @@ namespace S7.Net.Types
|
||||
|
||||
foreach (var info in infos)
|
||||
{
|
||||
static TValue GetValueOrThrow<TValue>(FieldInfo fi, object structValue) where TValue : struct
|
||||
{
|
||||
var value = fi.GetValue(structValue) as TValue? ??
|
||||
throw new ArgumentException($"Failed to convert value of field {fi.Name} of {structValue} to type {typeof(TValue)}");
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
bytes2 = null;
|
||||
switch (info.FieldType.Name)
|
||||
{
|
||||
@@ -261,7 +269,7 @@ namespace S7.Net.Types
|
||||
// get the value
|
||||
bytePos = (int)Math.Floor(numBytes);
|
||||
bitPos = (int)((numBytes - (double)bytePos) / 0.125);
|
||||
if ((bool)info.GetValue(structValue))
|
||||
if (GetValueOrThrow<bool>(info, structValue))
|
||||
bytes[bytePos] |= (byte)Math.Pow(2, bitPos); // is true
|
||||
else
|
||||
bytes[bytePos] &= (byte)(~(byte)Math.Pow(2, bitPos)); // is false
|
||||
@@ -270,26 +278,26 @@ namespace S7.Net.Types
|
||||
case "Byte":
|
||||
numBytes = (int)Math.Ceiling(numBytes);
|
||||
bytePos = (int)numBytes;
|
||||
bytes[bytePos] = (byte)info.GetValue(structValue);
|
||||
bytes[bytePos] = GetValueOrThrow<byte>(info, structValue);
|
||||
numBytes++;
|
||||
break;
|
||||
case "Int16":
|
||||
bytes2 = Int.ToByteArray((Int16)info.GetValue(structValue));
|
||||
bytes2 = Int.ToByteArray(GetValueOrThrow<short>(info, structValue));
|
||||
break;
|
||||
case "UInt16":
|
||||
bytes2 = Word.ToByteArray((UInt16)info.GetValue(structValue));
|
||||
bytes2 = Word.ToByteArray(GetValueOrThrow<ushort>(info, structValue));
|
||||
break;
|
||||
case "Int32":
|
||||
bytes2 = DInt.ToByteArray((Int32)info.GetValue(structValue));
|
||||
bytes2 = DInt.ToByteArray(GetValueOrThrow<int>(info, structValue));
|
||||
break;
|
||||
case "UInt32":
|
||||
bytes2 = DWord.ToByteArray((UInt32)info.GetValue(structValue));
|
||||
bytes2 = DWord.ToByteArray(GetValueOrThrow<uint>(info, structValue));
|
||||
break;
|
||||
case "Single":
|
||||
bytes2 = Real.ToByteArray((float)info.GetValue(structValue));
|
||||
bytes2 = Real.ToByteArray(GetValueOrThrow<float>(info, structValue));
|
||||
break;
|
||||
case "Double":
|
||||
bytes2 = LReal.ToByteArray((double)info.GetValue(structValue));
|
||||
bytes2 = LReal.ToByteArray(GetValueOrThrow<double>(info, structValue));
|
||||
break;
|
||||
case "String":
|
||||
S7StringAttribute? attribute = info.GetCustomAttributes<S7StringAttribute>().SingleOrDefault();
|
||||
@@ -298,8 +306,8 @@ namespace S7.Net.Types
|
||||
|
||||
bytes2 = attribute.Type switch
|
||||
{
|
||||
S7StringType.S7String => S7String.ToByteArray((string)info.GetValue(structValue), attribute.ReservedLength),
|
||||
S7StringType.S7WString => S7WString.ToByteArray((string)info.GetValue(structValue), attribute.ReservedLength),
|
||||
S7StringType.S7String => S7String.ToByteArray((string?)info.GetValue(structValue), attribute.ReservedLength),
|
||||
S7StringType.S7WString => S7WString.ToByteArray((string?)info.GetValue(structValue), attribute.ReservedLength),
|
||||
_ => throw new ArgumentException("Please use a valid string type for the S7StringAttribute")
|
||||
};
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user