mirror of
https://github.com/S7NetPlus/s7netplus.git
synced 2026-02-17 22:38:27 +08:00
Merge pull request #102 from mbalous/master
Non breaking changes. Code styling, comments, etc...
This commit is contained in:
106
S7.Net/PLC.cs
106
S7.Net/PLC.cs
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
@@ -17,31 +18,32 @@ namespace S7.Net
|
||||
public class Plc : IDisposable
|
||||
{
|
||||
private const int CONNECTION_TIMED_OUT_ERROR_CODE = 10060;
|
||||
|
||||
private Socket _mSocket; //TCP connection to device
|
||||
|
||||
//TCP connection to device
|
||||
private Socket _mSocket;
|
||||
|
||||
/// <summary>
|
||||
/// Ip address of the plc
|
||||
/// IP address of the PLC
|
||||
/// </summary>
|
||||
public string IP { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Cpu type of the plc
|
||||
/// CPU type of the PLC
|
||||
/// </summary>
|
||||
public CpuType CPU { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Rack of the plc
|
||||
/// Rack of the PLC
|
||||
/// </summary>
|
||||
public Int16 Rack { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Slot of the CPU of the plc
|
||||
/// Slot of the CPU of the PLC
|
||||
/// </summary>
|
||||
public Int16 Slot { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if a connection to the plc can be established
|
||||
/// Returns true if a connection to the PLC can be established
|
||||
/// </summary>
|
||||
public bool IsAvailable
|
||||
{
|
||||
@@ -61,7 +63,7 @@ namespace S7.Net
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the socket is connected and polls the other peer (the plc) to see if it's connected.
|
||||
/// 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>
|
||||
@@ -100,15 +102,21 @@ namespace S7.Net
|
||||
/// 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.
|
||||
/// <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>
|
||||
public Plc(CpuType cpu, string ip, Int16 rack, Int16 slot)
|
||||
{
|
||||
IP = ip;
|
||||
if (!Enum.IsDefined(typeof(CpuType), cpu))
|
||||
throw new InvalidEnumArgumentException(nameof(cpu), (int) cpu, typeof(CpuType));
|
||||
|
||||
if (string.IsNullOrEmpty(ip))
|
||||
throw new ArgumentException("IP address must valid.", nameof(ip));
|
||||
|
||||
CPU = cpu;
|
||||
IP = ip;
|
||||
Rack = rack;
|
||||
Slot = slot;
|
||||
}
|
||||
@@ -146,7 +154,8 @@ namespace S7.Net
|
||||
}
|
||||
|
||||
/// <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.
|
||||
/// Open a <see cref="Socket"/> and connects to the PLC, sending all the corrected package
|
||||
/// and returning if the connection was successful (<see cref="ErrorCode.NoError"/>) of it was wrong.
|
||||
/// </summary>
|
||||
/// <returns>Returns ErrorCode.NoError if the connection was successful, otherwise check the ErrorCode</returns>
|
||||
public ErrorCode Open()
|
||||
@@ -237,7 +246,7 @@ namespace S7.Net
|
||||
catch(Exception exc)
|
||||
{
|
||||
LastErrorCode = ErrorCode.ConnectionError;
|
||||
LastErrorString = "Couldn't establish the connection to " + IP + ".\nMessage: " + exc.Message;
|
||||
LastErrorString = string.Format("Couldn't establish the connection to {0}.\nMessage: {1}", IP, exc.Message);
|
||||
return ErrorCode.ConnectionError;
|
||||
}
|
||||
|
||||
@@ -245,7 +254,7 @@ namespace S7.Net
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disonnects from the plc and close the socket
|
||||
/// Disonnects from the PLC and close the socket
|
||||
/// </summary>
|
||||
public void Close()
|
||||
{
|
||||
@@ -268,8 +277,10 @@ namespace S7.Net
|
||||
{
|
||||
int cntBytes = dataItems.Sum(dataItem => VarTypeToByteLength(dataItem.VarType, dataItem.Count));
|
||||
|
||||
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
|
||||
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
|
||||
{
|
||||
@@ -287,7 +298,8 @@ namespace S7.Net
|
||||
|
||||
byte[] bReceive = new byte[512];
|
||||
int numReceived = _mSocket.Receive(bReceive, 512, SocketFlags.None);
|
||||
if (bReceive[21] != 0xff) throw new Exception(ErrorCode.WrongNumberReceivedBytes.ToString());
|
||||
if (bReceive[21] != 0xff)
|
||||
throw new Exception(ErrorCode.WrongNumberReceivedBytes.ToString());
|
||||
|
||||
int offset = 25;
|
||||
foreach (var dataItem in dataItems)
|
||||
@@ -365,7 +377,7 @@ namespace S7.Net
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a single variable from the plc, takes in input strings like "DB1.DBX0.0", "DB20.DBD200", "MB20", "T45", etc.
|
||||
/// 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>
|
||||
@@ -487,7 +499,8 @@ namespace S7.Net
|
||||
}
|
||||
|
||||
string txt2 = txt.Substring(1);
|
||||
if (txt2.IndexOf(".") == -1) throw new Exception();
|
||||
if (txt2.IndexOf(".") == -1)
|
||||
throw new Exception();
|
||||
|
||||
mByte = int.Parse(txt2.Substring(0, txt2.IndexOf(".")));
|
||||
mBit = int.Parse(txt2.Substring(txt2.IndexOf(".") + 1));
|
||||
@@ -535,7 +548,7 @@ namespace S7.Net
|
||||
}
|
||||
|
||||
/// <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.
|
||||
/// 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>
|
||||
@@ -559,28 +572,28 @@ namespace S7.Net
|
||||
}
|
||||
|
||||
/// <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.
|
||||
/// 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>
|
||||
/// <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.
|
||||
/// 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>
|
||||
/// <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();
|
||||
@@ -688,7 +701,7 @@ namespace S7.Net
|
||||
{
|
||||
var intValue = (int) value;
|
||||
if (intValue < 0 || intValue > 7)
|
||||
throw new Exception(string.Format("Addressing Error: You can only reference bitwise locations 0-7. Address {0} is invalid", bitAdr));
|
||||
throw new ArgumentOutOfRangeException(string.Format("Addressing Error: You can only reference bitwise locations 0-7. Address {0} is invalid", bitAdr), nameof(bitAdr));
|
||||
|
||||
bitValue = intValue == 1;
|
||||
}
|
||||
@@ -749,11 +762,11 @@ 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 LastErrorCode or LastErrorString.
|
||||
/// 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>
|
||||
/// <param name="value">Value to be written to the PLC</param>
|
||||
/// <returns>NoError if it was successful, or the error is specified</returns>
|
||||
public ErrorCode Write(string variable, object value)
|
||||
{
|
||||
@@ -911,17 +924,17 @@ namespace S7.Net
|
||||
catch(Exception exc)
|
||||
{
|
||||
LastErrorCode = ErrorCode.WrongVarFormat;
|
||||
LastErrorString = "The variable'" + variable + "' could not be parsed. Please check the syntax and try again.\nException: " + exc.Message;
|
||||
LastErrorString = string.Format("The variable'{0}' could not be parsed. Please check the syntax and try again.\nException: {1}", variable, exc.Message);
|
||||
return LastErrorCode;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a C# struct to a DB in the plc
|
||||
/// 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>
|
||||
/// <param name="startByteAdr">Start bytes on the PLC</param>
|
||||
/// <returns>NoError if it was successful, or the error is specified</returns>
|
||||
public ErrorCode WriteStruct(object structValue, int db, int startByteAdr = 0)
|
||||
{
|
||||
@@ -931,11 +944,11 @@ namespace S7.Net
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a C# class to a DB in the plc
|
||||
/// 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>
|
||||
/// <param name="startByteAdr">Start bytes on the PLC</param>
|
||||
/// <returns>NoError if it was successful, or the error is specified</returns>
|
||||
public ErrorCode WriteClass(object classValue, int db, int startByteAdr = 0)
|
||||
{
|
||||
@@ -945,7 +958,7 @@ namespace S7.Net
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the LastErrorCode to NoError and LastErrorString to String.Empty
|
||||
/// Sets the <see cref="LastErrorCode"/> to <see cref="ErrorCode.NoError"/> and <see cref="LastErrorString"/> to <see cref="string.Empty"/>.
|
||||
/// </summary>
|
||||
public void ClearLastError()
|
||||
{
|
||||
@@ -954,7 +967,7 @@ namespace S7.Net
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the header to read bytes from the plc
|
||||
/// Creates the header to read bytes from the PLC
|
||||
/// </summary>
|
||||
/// <param name="amount"></param>
|
||||
/// <returns></returns>
|
||||
@@ -976,7 +989,7 @@ namespace S7.Net
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the bytes-package to request data from the plc. You have to specify the memory type (dataType),
|
||||
/// 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>
|
||||
@@ -1059,7 +1072,7 @@ namespace S7.Net
|
||||
}
|
||||
|
||||
/// <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.
|
||||
/// 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>
|
||||
@@ -1139,7 +1152,7 @@ namespace S7.Net
|
||||
package.Add(Types.Word.ToByteArray((ushort)varCount));
|
||||
package.Add(Types.Word.ToByteArray((ushort)(db)));
|
||||
package.Add((byte)dataType);
|
||||
var overflow = (int)(startByteAdr * 8 / 0xffffU); // handles words with address bigger than 8191
|
||||
int overflow = (int)(startByteAdr * 8 / 0xffffU); // handles words with address bigger than 8191
|
||||
package.Add((byte)overflow);
|
||||
package.Add(Types.Word.ToByteArray((ushort)(startByteAdr * 8 + bitAdr)));
|
||||
package.Add(new byte[] { 0, 0x03 }); //ending 0x03 is used for writing a sinlge bit
|
||||
@@ -1176,7 +1189,8 @@ namespace S7.Net
|
||||
/// <returns></returns>
|
||||
private object ParseBytes(VarType varType, byte[] bytes, int varCount, byte bitAdr = 0)
|
||||
{
|
||||
if (bytes == null) return null;
|
||||
if (bytes == null)
|
||||
return null;
|
||||
|
||||
switch (varType)
|
||||
{
|
||||
@@ -1228,23 +1242,27 @@ namespace S7.Net
|
||||
return Types.Counter.ToArray(bytes);
|
||||
case VarType.Bit:
|
||||
if (varCount == 1)
|
||||
{
|
||||
if (bitAdr > 7)
|
||||
return null;
|
||||
else
|
||||
return Types.Bit.FromByte(bytes[0], bitAdr);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Types.Bit.ToBitArray(bytes);
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a S7 variable type (Bool, Word, DWord, etc.), it returns how many bytes to read.
|
||||
/// 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></returns>
|
||||
/// <returns>Byte lenght of variable</returns>
|
||||
private int VarTypeToByteLength(VarType varType, int varCount = 1)
|
||||
{
|
||||
switch (varType)
|
||||
@@ -1274,7 +1292,7 @@ namespace S7.Net
|
||||
#region IDisposable members
|
||||
|
||||
/// <summary>
|
||||
/// Releases all resources, disonnects from the plc and closes the socket
|
||||
/// Releases all resources, disonnects from the PLC and closes the <see cref="Socket"/>
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user