From 5330593256047be8f6aa6b41ad2a35362ab8157e Mon Sep 17 00:00:00 2001 From: SigiRab Date: Fri, 14 Nov 2025 11:15:37 +0100 Subject: [PATCH 01/11] =?UTF-8?q?bitte=20die=20neue=20S7=201200=20G2=20ein?= =?UTF-8?q?f=C3=BCgen,=20diese=20Serie=20kann=20ab=20Tia=20V20=20programmi?= =?UTF-8?q?ert=20werden=20und=20unterst=C3=BCtz=20keine=20Legacy-Verbindun?= =?UTF-8?q?gen=20mehr.=20Der=20Treiber=20funktioniert=20aber=20ansonsten?= =?UTF-8?q?=20einwandfrei.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/S7CommPlusDriver/Legitimation/Legitimation.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/S7CommPlusDriver/Legitimation/Legitimation.cs b/src/S7CommPlusDriver/Legitimation/Legitimation.cs index 41e97c7..5645459 100644 --- a/src/S7CommPlusDriver/Legitimation/Legitimation.cs +++ b/src/S7CommPlusDriver/Legitimation/Legitimation.cs @@ -51,6 +51,10 @@ private int legitimate(ValueStruct serverSession, string password, string userna legacyLegitimation = true; } } + else if (sessionVersionPAOMString.Contains("50-0XB0") && deviceVersion.StartsWith('2')) //New S7-1200 G2 (example: "1;6ES7 212-1HG50-0XB0;V1.0") + { + legacyLegitimation = false; + } else if (deviceVersion.StartsWith("2")) { if (fwVerNo < 403) From 38b00f2bd252c14e692af91b9dde6fc930d1f288 Mon Sep 17 00:00:00 2001 From: Thomas Wiens Date: Thu, 27 Nov 2025 20:07:58 +0100 Subject: [PATCH 02/11] Use double quote for string --- src/S7CommPlusDriver/Legitimation/Legitimation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/S7CommPlusDriver/Legitimation/Legitimation.cs b/src/S7CommPlusDriver/Legitimation/Legitimation.cs index 5645459..8050fd5 100644 --- a/src/S7CommPlusDriver/Legitimation/Legitimation.cs +++ b/src/S7CommPlusDriver/Legitimation/Legitimation.cs @@ -51,7 +51,7 @@ private int legitimate(ValueStruct serverSession, string password, string userna legacyLegitimation = true; } } - else if (sessionVersionPAOMString.Contains("50-0XB0") && deviceVersion.StartsWith('2')) //New S7-1200 G2 (example: "1;6ES7 212-1HG50-0XB0;V1.0") + else if (sessionVersionPAOMString.Contains("50-0XB0") && deviceVersion.StartsWith("2")) //New S7-1200 G2 (example: "1;6ES7 212-1HG50-0XB0;V1.0") { legacyLegitimation = false; } From 9f1864f7a2b879623241d94ce14dd22796dc6929 Mon Sep 17 00:00:00 2001 From: Thomas Wiens Date: Thu, 27 Nov 2025 21:08:51 +0100 Subject: [PATCH 03/11] Alarms: Add method to read the active alarms without notification --- src/S7CommPlusDriver/Alarming/BrowseAlarms.cs | 68 +++++++++++++++++++ src/S7CommPlusDriver/Core/Ids.cs | 2 + 2 files changed, 70 insertions(+) diff --git a/src/S7CommPlusDriver/Alarming/BrowseAlarms.cs b/src/S7CommPlusDriver/Alarming/BrowseAlarms.cs index 6f31c4f..a7f96b5 100644 --- a/src/S7CommPlusDriver/Alarming/BrowseAlarms.cs +++ b/src/S7CommPlusDriver/Alarming/BrowseAlarms.cs @@ -332,6 +332,74 @@ private void GetTexts(byte[] tloa_1, byte[] tloa_2, byte[] tloa_3, byte[] tlsa, } } } + + /// + /// Reads the active program alarms from the Plc (single poll). + /// + /// Call example: + /// CultureInfo ci = new CultureInfo("de-DE"); + /// var alarmList = new List(); + /// conn.GetActiveAlarms(out alarmList, ci.LCID); + /// foreach (var a in alarmList) + /// { + /// Console.WriteLine(a.ToString()); + /// } + /// + /// Contains the alarms, empty if there is no active alarm + /// Language id for retrieving the text entries, use language code e.g. 1031 for german + /// 0 on success + public int GetActiveAlarms(out List alarmList, int languageId) + { + int res; + + alarmList = new List(); + + var exploreReq = new ExploreRequest(ProtocolVersion.V2); + exploreReq.ExploreId = Ids.NativeObjects_theAlarmSubsystem_Rid; + exploreReq.ExploreRequestId = Ids.AlarmSubsystem_itsUpdateRelevantDAI; + exploreReq.ExploreChildsRecursive = 1; + exploreReq.ExploreParents = 0; + + // Add the requestes attributes. + // Request the same attributes we get from an alarm notification, so we can reuse other methods. + exploreReq.AddressList.Add(Ids.DAI_CPUAlarmID); + exploreReq.AddressList.Add(Ids.DAI_AllStatesInfo); + exploreReq.AddressList.Add(Ids.DAI_AlarmDomain); + exploreReq.AddressList.Add(Ids.DAI_Coming); + exploreReq.AddressList.Add(Ids.DAI_Going); + exploreReq.AddressList.Add(Ids.DAI_MessageType); + exploreReq.AddressList.Add(Ids.DAI_HmiInfo); + // Extra ones which we only need for compatibility with notification. + exploreReq.AddressList.Add(Ids.ObjectVariableTypeName); + exploreReq.AddressList.Add(Ids.DAI_SequenceCounter); + exploreReq.AddressList.Add(Ids.DAI_AlarmTexts_Rid); + + res = SendS7plusFunctionObject(exploreReq); + if (res != 0) + { + return res; + } + m_LastError = 0; + WaitForNewS7plusReceived(m_ReadTimeout); + if (m_LastError != 0) + { + return m_LastError; + } + + var exploreRes = ExploreResponse.DeserializeFromPdu(m_ReceivedPDU, true); + res = checkResponseWithIntegrity(exploreReq, exploreRes); + if (res != 0) + { + return res; + } + + foreach (var obj in exploreRes.Objects) + { + alarmList.Add(AlarmsDai.FromNotificationObject(obj, languageId)); + } + + return 0; + } } public class AlarmData diff --git a/src/S7CommPlusDriver/Core/Ids.cs b/src/S7CommPlusDriver/Core/Ids.cs index f543bf7..12b330e 100644 --- a/src/S7CommPlusDriver/Core/Ids.cs +++ b/src/S7CommPlusDriver/Core/Ids.cs @@ -19,6 +19,7 @@ public static class Ids { public const int None = 0; public const int NativeObjects_thePLCProgram_Rid = 3; + public const int NativeObjects_theAlarmSubsystem_Rid = 8; public const int NativeObjects_theCPUexecUnit_Rid = 52; public const int NativeObjects_theIArea_Rid = 80; public const int NativeObjects_theQArea_Rid = 81; @@ -86,6 +87,7 @@ public static class Ids public const int AlarmSubscriptionRef_AlarmDomain = 2659; public const int AlarmSubscriptionRef_itsAlarmSubsystem = 2660; public const int AlarmSubscriptionRef_Class_Rid = 2662; + public const int AlarmSubsystem_itsUpdateRelevantDAI = 2667; public const int DAI_CPUAlarmID = 2670; public const int DAI_AllStatesInfo = 2671; public const int DAI_AlarmDomain = 2672; From ce4c92cc989ea1028e29254a9313b1aa67813806 Mon Sep 17 00:00:00 2001 From: SigiRab Date: Fri, 28 Nov 2025 15:31:44 +0100 Subject: [PATCH 04/11] Add files via upload --- .../ClientApi/PlcTagExtend.cs | 710 ++++++++++++++++++ 1 file changed, 710 insertions(+) create mode 100644 src/S7CommPlusDriver/ClientApi/PlcTagExtend.cs diff --git a/src/S7CommPlusDriver/ClientApi/PlcTagExtend.cs b/src/S7CommPlusDriver/ClientApi/PlcTagExtend.cs new file mode 100644 index 0000000..7bae1bf --- /dev/null +++ b/src/S7CommPlusDriver/ClientApi/PlcTagExtend.cs @@ -0,0 +1,710 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; + +namespace S7CommPlusDriver.ClientApi; + +public class PlcTagBoolArray : PlcTag +{ + private bool[] m_Value; + + public bool[] Value + { + get { return m_Value; } + set { m_Value = value; } + } + + public PlcTagBoolArray(string name, ItemAddress address, uint softdatatype) : base(name, address, softdatatype) + { + m_Value = new bool[1]; + } + + public override void ProcessReadResult(object valueObj, ulong error) + { + LastReadError = error; + if (CheckErrorAndType(error, valueObj, typeof(ValueBoolArray)) == 0) + { + Value = ((ValueBoolArray)valueObj).GetValue(); + + Quality = PlcTagQC.TAG_QUALITY_GOOD; + } + else + { + Quality = PlcTagQC.TAG_QUALITY_BAD; + } + } + + public override PValue GetWriteValue() + { + return new ValueBoolArray(Value); + } + + public override string ToString() + { + var val = new ValueBoolArray(Value); + return ResultString(this, val.ToString()); + } +} + +public class PlcTagByteArray : PlcTag +{ + private byte[] m_Value; + + public byte[] Value + { + get { return m_Value; } + set { m_Value = value; } + } + + public PlcTagByteArray(string name, ItemAddress address, uint softdatatype) : base(name, address, softdatatype) + { + m_Value = new byte[1]; + } + + public override void ProcessReadResult(object valueObj, ulong error) + { + LastReadError = error; + if (CheckErrorAndType(error, valueObj, typeof(ValueByteArray)) == 0) + { + Value = ((ValueByteArray)valueObj).GetValue(); + + Quality = PlcTagQC.TAG_QUALITY_GOOD; + } + else + { + Quality = PlcTagQC.TAG_QUALITY_BAD; + } + } + + public override PValue GetWriteValue() + { + return new ValueByteArray(Value); + } + + public override string ToString() + { + var val = new ValueByteArray(Value); + return ResultString(this, val.ToString()); + } +} + +public class PlcTagWordArray : PlcTag +{ + private ushort[] m_Value; + + public ushort[] Value + { + get { return m_Value; } + set { m_Value = value; } + } + + public PlcTagWordArray(string name, ItemAddress address, uint softdatatype) : base(name, address, softdatatype) + { + m_Value = new ushort[1]; + } + + public override void ProcessReadResult(object valueObj, ulong error) + { + LastReadError = error; + if (CheckErrorAndType(error, valueObj, typeof(ValueWordArray)) == 0) + { + Value = ((ValueWordArray)valueObj).GetValue(); + + Quality = PlcTagQC.TAG_QUALITY_GOOD; + } + else + { + Quality = PlcTagQC.TAG_QUALITY_BAD; + } + } + + public override PValue GetWriteValue() + { + return new ValueWordArray(Value); + } + + public override string ToString() + { + var val = new ValueWordArray(Value); + return ResultString(this, val.ToString()); + } +} + +public class PlcTagIntArray : PlcTag +{ + private short[] m_Value; + + public short[] Value + { + get { return m_Value; } + set { m_Value = value; } + } + + public PlcTagIntArray(string name, ItemAddress address, uint softdatatype) : base(name, address, softdatatype) + { + m_Value = new short[1]; + } + + public override void ProcessReadResult(object valueObj, ulong error) + { + LastReadError = error; + if (CheckErrorAndType(error, valueObj, typeof(ValueIntArray)) == 0) + { + Value = ((ValueIntArray)valueObj).GetValue(); + + Quality = PlcTagQC.TAG_QUALITY_GOOD; + } + else + { + Quality = PlcTagQC.TAG_QUALITY_BAD; + } + } + + public override PValue GetWriteValue() + { + return new ValueIntArray(Value); + } + + public override string ToString() + { + var val = new ValueIntArray(Value); + return ResultString(this, val.ToString()); + } +} + +public class PlcTagDWordArray : PlcTag +{ + private uint[] m_Value; + + public uint[] Value + { + get { return m_Value; } + set { m_Value = value; } + } + + public PlcTagDWordArray(string name, ItemAddress address, uint softdatatype) : base(name, address, softdatatype) + { + m_Value = new uint[1]; + } + + public override void ProcessReadResult(object valueObj, ulong error) + { + LastReadError = error; + if (CheckErrorAndType(error, valueObj, typeof(ValueDWordArray)) == 0) + { + Value = ((ValueDWordArray)valueObj).GetValue(); + + Quality = PlcTagQC.TAG_QUALITY_GOOD; + } + else + { + Quality = PlcTagQC.TAG_QUALITY_BAD; + } + } + + public override PValue GetWriteValue() + { + return new ValueDWordArray(Value); + } + + public override string ToString() + { + var val = new ValueDWordArray(Value); + return ResultString(this, val.ToString()); + } +} + +public class PlcTagDIntArray : PlcTag +{ + private int[] m_Value; + + public int[] Value + { + get { return m_Value; } + set { m_Value = value; } + } + + public PlcTagDIntArray(string name, ItemAddress address, uint softdatatype) : base(name, address, softdatatype) + { + m_Value = new int[1]; + } + + public override void ProcessReadResult(object valueObj, ulong error) + { + LastReadError = error; + if (CheckErrorAndType(error, valueObj, typeof(ValueDIntArray)) == 0) + { + Value = ((ValueDIntArray)valueObj).GetValue(); + + Quality = PlcTagQC.TAG_QUALITY_GOOD; + } + else + { + Quality = PlcTagQC.TAG_QUALITY_BAD; + } + } + + public override PValue GetWriteValue() + { + return new ValueDIntArray(Value); + } + + public override string ToString() + { + var val = new ValueDIntArray(Value); + return ResultString(this, val.ToString()); + } +} + +public class PlcTagRealArray : PlcTag +{ + private float[] m_Value; + + public float[] Value + { + get { return m_Value; } + set { m_Value = value; } + } + + public PlcTagRealArray(string name, ItemAddress address, uint softdatatype) : base(name, address, softdatatype) + { + m_Value = new float[1]; + } + + public override void ProcessReadResult(object valueObj, ulong error) + { + LastReadError = error; + if (CheckErrorAndType(error, valueObj, typeof(ValueRealArray)) == 0) + { + Value = ((ValueRealArray)valueObj).GetValue(); + + Quality = PlcTagQC.TAG_QUALITY_GOOD; + } + else + { + Quality = PlcTagQC.TAG_QUALITY_BAD; + } + } + + public override PValue GetWriteValue() + { + return new ValueRealArray(Value); + } + + public override string ToString() + { + var val = new ValueRealArray(Value); + return ResultString(this, val.ToString()); + } +} + +public class PlcTagUSIntArray : PlcTag +{ + private byte[] m_Value; + + public byte[] Value + { + get { return m_Value; } + set { m_Value = value; } + } + + public PlcTagUSIntArray(string name, ItemAddress address, uint softdatatype) : base(name, address, softdatatype) + { + m_Value = new byte[1]; + } + + public override void ProcessReadResult(object valueObj, ulong error) + { + LastReadError = error; + if (CheckErrorAndType(error, valueObj, typeof(ValueUSIntArray)) == 0) + { + Value = ((ValueUSIntArray)valueObj).GetValue(); + + Quality = PlcTagQC.TAG_QUALITY_GOOD; + } + else + { + Quality = PlcTagQC.TAG_QUALITY_BAD; + } + } + + public override PValue GetWriteValue() + { + return new ValueUSIntArray(Value); + } + + public override string ToString() + { + var val = new ValueUSIntArray(Value); + return ResultString(this, val.ToString()); + } +} + +public class PlcTagUIntArray : PlcTag +{ + private ushort[] m_Value; + + public ushort[] Value + { + get { return m_Value; } + set { m_Value = value; } + } + + public PlcTagUIntArray(string name, ItemAddress address, uint softdatatype) : base(name, address, softdatatype) + { + m_Value = new ushort[1]; + } + + public override void ProcessReadResult(object valueObj, ulong error) + { + LastReadError = error; + if (CheckErrorAndType(error, valueObj, typeof(ValueUIntArray)) == 0) + { + Value = ((ValueUIntArray)valueObj).GetValue(); + + Quality = PlcTagQC.TAG_QUALITY_GOOD; + } + else + { + Quality = PlcTagQC.TAG_QUALITY_BAD; + } + } + + public override PValue GetWriteValue() + { + return new ValueUIntArray(Value); + } + + public override string ToString() + { + var val = new ValueUIntArray(Value); + return ResultString(this, val.ToString()); + } +} + +public class PlcTagUDIntArray : PlcTag +{ + private uint[] m_Value; + + public uint[] Value + { + get { return m_Value; } + set { m_Value = value; } + } + + public PlcTagUDIntArray(string name, ItemAddress address, uint softdatatype) : base(name, address, softdatatype) + { + m_Value = new uint[1]; + } + + public override void ProcessReadResult(object valueObj, ulong error) + { + LastReadError = error; + if (CheckErrorAndType(error, valueObj, typeof(ValueUDIntArray)) == 0) + { + Value = ((ValueUDIntArray)valueObj).GetValue(); + + Quality = PlcTagQC.TAG_QUALITY_GOOD; + } + else + { + Quality = PlcTagQC.TAG_QUALITY_BAD; + } + } + + public override PValue GetWriteValue() + { + return new ValueUDIntArray(Value); + } + + public override string ToString() + { + var val = new ValueUDIntArray(Value); + return ResultString(this, val.ToString()); + } +} + +public class PlcTagSIntArray : PlcTag +{ + private sbyte[] m_Value; + + public sbyte[] Value + { + get { return m_Value; } + set { m_Value = value; } + } + + public PlcTagSIntArray(string name, ItemAddress address, uint softdatatype) : base(name, address, softdatatype) + { + m_Value = new sbyte[1]; + } + + public override void ProcessReadResult(object valueObj, ulong error) + { + LastReadError = error; + if (CheckErrorAndType(error, valueObj, typeof(ValueSIntArray)) == 0) + { + Value = ((ValueSIntArray)valueObj).GetValue(); + + Quality = PlcTagQC.TAG_QUALITY_GOOD; + } + else + { + Quality = PlcTagQC.TAG_QUALITY_BAD; + } + } + + public override PValue GetWriteValue() + { + return new ValueSIntArray(Value); + } + + public override string ToString() + { + var val = new ValueSIntArray(Value); + return ResultString(this, val.ToString()); + } +} + +public class PlcTagDateAndTimeArray : PlcTag +{ + /* BCD coded: + * YYMMDDhhmmssuuuQ + * uuu = milliseconds + * Q = Weekday 1=Su, 2=Mo, 3=Tu, 4=We, 5=Th, 6=Fr, 7=Sa + */ + private DateTime[] m_Value; + + public DateTime[] Value + { + get + { + return m_Value; + } + + set + { + bool dataOk = true; + foreach (var item in value) + { + if (item < new DateTime(1990, 1, 1) && item >= new DateTime(2090, 1, 1)) + { + dataOk = false; + break; + } + } + if (dataOk) + { + m_Value = value; + } + else + { + throw new ArgumentOutOfRangeException("Value", "DateTime must be >= 1990-01-01 and < 2090-01-01"); + } + } + } + + public PlcTagDateAndTimeArray(string name, ItemAddress address, uint softdatatype) : base(name, address, softdatatype) + { + Value = new DateTime[0]; + } + + public override void ProcessReadResult(object valueObj, ulong error) + { + LastReadError = error; + if (CheckErrorAndType(error, valueObj, typeof(ValueUSIntArray)) == 0) + { + List dateTimes = new List(); + var v = ((ValueUSIntArray)valueObj).GetValue(); + int pos = 0; + do + { + int[] ts = new int[8]; + for (int i = 0; i < 7; i++) + { + ts[i] = BcdByteToInt(v[pos + i]); + } + // The left nibble of the last byte contains the LSD of milliseconds, + // the right nibble the weekday (which we don't process here). + ts[7] = v[7] >> 4; + + int year; + if (ts[0] >= 90) + { + year = 1900 + ts[0]; + } + else + { + year = 2000 + ts[0]; + } + var value = new DateTime(year, ts[1], ts[2], ts[3], ts[4], ts[5]); + value = value.AddMilliseconds(ts[6] * 10 + ts[7]); + dateTimes.Add(value); + pos += 8; + } while (pos < v.Length); + Value = dateTimes.ToArray(); + Quality = PlcTagQC.TAG_QUALITY_GOOD; + } + else + { + Quality = PlcTagQC.TAG_QUALITY_BAD; + } + } + + public override PValue GetWriteValue() + { + var byteStrings = new List(); + foreach (var item in Value) + { + int[] ts = new int[8]; + byte[] b = new byte[8]; + if (item.Year < 2000) + { + // 90-99 = 1990-1999 + ts[0] = item.Year - 1900; + } + else + { + // 00-89 = 2000-2089 + ts[0] = item.Year - 2000; + } + ts[1] = item.Month; + ts[2] = item.Day; + ts[3] = item.Hour; + ts[4] = item.Minute; + ts[5] = item.Second; + ts[6] = item.Millisecond / 10; + ts[7] = (item.Millisecond % 10) << 4; // Don't set the weekday + for (int i = 0; i < 7; i++) + { + b[i] = IntToBcdByte(ts[i]); + } + b[7] = (byte)ts[7]; + byteStrings.AddRange(b); + } + return new ValueUSIntArray(byteStrings.ToArray()); + } + + public override string ToString() + { + string s = ""; + for (int i = 0; i < Value.Length; i++) + { + string ts = Value[i].ToString(); + if (Value[i].Millisecond > 0) + { + ts += String.Format(".{0:D03}", Value[i].Millisecond); + } + s += String.Format("{0}", ts); + } + s += ""; + return ResultString(this, s); + } +} + + +public class PlcTagStringArray : PlcTag +{ + private string[] m_Value; + private byte m_MaxLength = 254; + private string m_Encoding = "ISO-8859-1"; + + public string[] Value + { + get + { + return m_Value; + } + + set + { + bool lengthOk = true; + foreach (var item in value) + { + if (item.Length > m_MaxLength) + { + lengthOk = false; + break; + } + } + if (lengthOk) + { + m_Value = value; + } + else + { + throw new ArgumentOutOfRangeException("Value", "String is longer than the allowed max. length of " + m_MaxLength); + } + } + } + + public PlcTagStringArray(string name, ItemAddress address, uint softdatatype, byte maxlength = 254) : base(name, address, softdatatype) + { + m_MaxLength = maxlength; + } + + public override void ProcessReadResult(object valueObj, ulong error) + { + LastReadError = error; + if (CheckErrorAndType(error, valueObj, typeof(ValueUSIntArray)) == 0) + { + List strings = new List(); + var v = ((ValueUSIntArray)valueObj).GetValue(); + int pos = 0; + do + { + int max_len = v[pos]; + int act_len = v[pos + 1]; + // IEC 61131-3 states ISO-646 IRV, with optional extensions like "Latin-1 Supplement". + // Siemens TIA-Portal gives warnings using other than 7 Bit ASCII characters. + // Let the user define his local encoding via SetStringEncoding(). + var str = Encoding.GetEncoding(m_Encoding).GetString(v, pos + 2, act_len); + strings.Add(str); + pos += max_len + 2; + + } while (pos < v.Length); + Value = strings.ToArray(); + Quality = PlcTagQC.TAG_QUALITY_GOOD; + } + else + { + Quality = PlcTagQC.TAG_QUALITY_BAD; + } + } + + public override PValue GetWriteValue() + { + var byteStrings = new List(); + foreach (var item in Value) + { + // Must write the complete array of MaxLength of the string (plus two bytes header). + byte[] sb = Encoding.GetEncoding(m_Encoding).GetBytes(item); + var b = new byte[m_MaxLength + 2]; + b[0] = m_MaxLength; + b[1] = (byte)sb.Length; + for (int i = 0; i < sb.Length; i++) + { + b[i + 2] = sb[i]; + } + byteStrings.AddRange(b); + } + return new ValueUSIntArray(byteStrings.ToArray()); + } + + public void SetStringEncoding(string encoding) + { + m_Encoding = encoding; + } + + public override string ToString() + { + string s = ""; + for (int i = 0; i < Value.Length; i++) + { + s += String.Format("{0}", Value[i]); + } + s += ""; + return ResultString(this, s); + } +} From 9cfa84b34a8499f7af69a063f205f7f2f95ef48d Mon Sep 17 00:00:00 2001 From: SigiRab Date: Fri, 28 Nov 2025 15:39:09 +0100 Subject: [PATCH 05/11] Update PlcTags.cs extended for array-types --- src/S7CommPlusDriver/ClientApi/PlcTags.cs | 30 ++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/S7CommPlusDriver/ClientApi/PlcTags.cs b/src/S7CommPlusDriver/ClientApi/PlcTags.cs index 368b9d9..6a43eb0 100644 --- a/src/S7CommPlusDriver/ClientApi/PlcTags.cs +++ b/src/S7CommPlusDriver/ClientApi/PlcTags.cs @@ -69,25 +69,39 @@ public static int WriteTags(this S7CommPlusConnection conn, IEnumerable return res; } - public static PlcTag TagFactory(string name, ItemAddress address, uint softdatatype) + public static PlcTag TagFactory(string name, ItemAddress address, uint softdatatype, bool Is1Dim = false) { switch (softdatatype) { case Softdatatype.S7COMMP_SOFTDATATYPE_BOOL: + if (Is1Dim) + return new PlcTagBoolArray(name, address, softdatatype); return new PlcTagBool(name, address, softdatatype); case Softdatatype.S7COMMP_SOFTDATATYPE_BYTE: + if (Is1Dim) + return new PlcTagByteArray(name, address, softdatatype); return new PlcTagByte(name, address, softdatatype); case Softdatatype.S7COMMP_SOFTDATATYPE_CHAR: return new PlcTagChar(name, address, softdatatype); case Softdatatype.S7COMMP_SOFTDATATYPE_WORD: + if (Is1Dim) + return new PlcTagWordArray(name, address, softdatatype); return new PlcTagWord(name, address, softdatatype); case Softdatatype.S7COMMP_SOFTDATATYPE_INT: + if (Is1Dim) + return new PlcTagIntArray(name, address, softdatatype); return new PlcTagInt(name, address, softdatatype); case Softdatatype.S7COMMP_SOFTDATATYPE_DWORD: + if (Is1Dim) + return new PlcTagDWordArray(name, address, softdatatype); return new PlcTagDWord(name, address, softdatatype); case Softdatatype.S7COMMP_SOFTDATATYPE_DINT: + if (Is1Dim) + return new PlcTagDIntArray(name, address, softdatatype); return new PlcTagDInt(name, address, softdatatype); case Softdatatype.S7COMMP_SOFTDATATYPE_REAL: + if (Is1Dim) + return new PlcTagRealArray(name, address, softdatatype); return new PlcTagReal(name, address, softdatatype); case Softdatatype.S7COMMP_SOFTDATATYPE_DATE: return new PlcTagDate(name, address, softdatatype); @@ -98,9 +112,13 @@ public static PlcTag TagFactory(string name, ItemAddress address, uint softdatat case Softdatatype.S7COMMP_SOFTDATATYPE_S5TIME: return new PlcTagS5Time(name, address, softdatatype); case Softdatatype.S7COMMP_SOFTDATATYPE_DATEANDTIME: + if (Is1Dim) + return new PlcTagDateAndTimeArray(name, address, softdatatype); return new PlcTagDateAndTime(name, address, softdatatype); case Softdatatype.S7COMMP_SOFTDATATYPE_STRING: + if (Is1Dim) + return new PlcTagStringArray(name, address, softdatatype); return new PlcTagString(name, address, softdatatype); case Softdatatype.S7COMMP_SOFTDATATYPE_POINTER: return new PlcTagPointer(name, address, softdatatype); @@ -118,6 +136,8 @@ public static PlcTag TagFactory(string name, ItemAddress address, uint softdatat return new PlcTagUInt(name, address, softdatatype); case Softdatatype.S7COMMP_SOFTDATATYPE_BBOOL: + if (Is1Dim) + return new PlcTagBoolArray(name, address, softdatatype); return new PlcTagBool(name, address, softdatatype); case Softdatatype.S7COMMP_SOFTDATATYPE_LREAL: @@ -129,12 +149,20 @@ public static PlcTag TagFactory(string name, ItemAddress address, uint softdatat case Softdatatype.S7COMMP_SOFTDATATYPE_LWORD: return new PlcTagLWord(name, address, softdatatype); case Softdatatype.S7COMMP_SOFTDATATYPE_USINT: + if (Is1Dim) + return new PlcTagUSIntArray(name, address, softdatatype); return new PlcTagUSInt(name, address, softdatatype); case Softdatatype.S7COMMP_SOFTDATATYPE_UINT: + if (Is1Dim) + return new PlcTagUIntArray(name, address, softdatatype); return new PlcTagUInt(name, address, softdatatype); case Softdatatype.S7COMMP_SOFTDATATYPE_UDINT: + if (Is1Dim) + return new PlcTagUDIntArray(name, address, softdatatype); return new PlcTagUDInt(name, address, softdatatype); case Softdatatype.S7COMMP_SOFTDATATYPE_SINT: + if (Is1Dim) + return new PlcTagSIntArray(name, address, softdatatype); return new PlcTagSInt(name, address, softdatatype); case Softdatatype.S7COMMP_SOFTDATATYPE_WCHAR: From b9acd44f2abbfb2c684b0349937a5b5d89328521 Mon Sep 17 00:00:00 2001 From: SigiRab Date: Fri, 28 Nov 2025 15:42:26 +0100 Subject: [PATCH 06/11] Update S7CommPlusConnection.cs --- src/S7CommPlusDriver/S7CommPlusConnection.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/S7CommPlusDriver/S7CommPlusConnection.cs b/src/S7CommPlusDriver/S7CommPlusConnection.cs index 2bcb801..8a16b7e 100644 --- a/src/S7CommPlusDriver/S7CommPlusConnection.cs +++ b/src/S7CommPlusDriver/S7CommPlusConnection.cs @@ -1080,9 +1080,17 @@ private PlcTag browsePlcTagBySymbol(uint ti_relid, ref string symbol, VarInfo va if (idx < 0) return null; PVartypeListElement varType = pObj.VartypeList.Elements[idx]; varInfo.AccessSequence += "." + String.Format("{0:X}", varType.LID); + bool is1Dim = false; if (varType.OffsetInfoType.Is1Dim()) { - calcAccessSeqFor1DimArray(ref symbol, varType, varInfo); + if (symbol == "") + { + is1Dim = true; + } + else + { + calcAccessSeqFor1DimArray(ref symbol, varType, varInfo); + } } if (varType.OffsetInfoType.IsMDim()) { @@ -1090,6 +1098,10 @@ private PlcTag browsePlcTagBySymbol(uint ti_relid, ref string symbol, VarInfo va } if (varType.OffsetInfoType.HasRelation()) { + if (symbol.Length <= 0 && varType.Softdatatype == Softdatatype.S7COMMP_SOFTDATATYPE_DTL) + { + return PlcTags.TagFactory(varInfo.Name, new ItemAddress(varInfo.AccessSequence), varType.Softdatatype, is1Dim); + } if (symbol.Length <= 0) { return null; @@ -1102,7 +1114,7 @@ private PlcTag browsePlcTagBySymbol(uint ti_relid, ref string symbol, VarInfo va } else { - return PlcTags.TagFactory(varInfo.Name, new ItemAddress(varInfo.AccessSequence), varType.Softdatatype); + return PlcTags.TagFactory(varInfo.Name, new ItemAddress(varInfo.AccessSequence), varType.Softdatatype, is1Dim); } } From b61c12637e6f0c59681afe29953377af199662cf Mon Sep 17 00:00:00 2001 From: SigiRab Date: Fri, 28 Nov 2025 15:43:12 +0100 Subject: [PATCH 07/11] Update S7CommPlusConnection.cs extended for array-types From b84fb52b4c9d9f66de23b74fa45b2e0ed8e14f98 Mon Sep 17 00:00:00 2001 From: Thomas Wiens Date: Wed, 3 Dec 2025 18:35:44 +0100 Subject: [PATCH 08/11] Move Array-Tag classes into single PlcTags file --- src/S7CommPlusDriver/ClientApi/PlcTag.cs | 709 +++++++++++++++++ .../ClientApi/PlcTagExtend.cs | 710 ------------------ 2 files changed, 709 insertions(+), 710 deletions(-) delete mode 100644 src/S7CommPlusDriver/ClientApi/PlcTagExtend.cs diff --git a/src/S7CommPlusDriver/ClientApi/PlcTag.cs b/src/S7CommPlusDriver/ClientApi/PlcTag.cs index 18cc628..4e83546 100644 --- a/src/S7CommPlusDriver/ClientApi/PlcTag.cs +++ b/src/S7CommPlusDriver/ClientApi/PlcTag.cs @@ -1,5 +1,7 @@ using System; using System.Text; +using System.Collections; +using System.Collections.Generic; namespace S7CommPlusDriver.ClientApi { @@ -1701,4 +1703,711 @@ public override string ToString() return String.Format(fmt, Value.ToString(), ns); } } + + #region Arrays + + public class PlcTagBoolArray : PlcTag + { + private bool[] m_Value; + + public bool[] Value + { + get { return m_Value; } + set { m_Value = value; } + } + + public PlcTagBoolArray(string name, ItemAddress address, uint softdatatype) : base(name, address, softdatatype) + { + m_Value = new bool[1]; + } + + public override void ProcessReadResult(object valueObj, ulong error) + { + LastReadError = error; + if (CheckErrorAndType(error, valueObj, typeof(ValueBoolArray)) == 0) + { + Value = ((ValueBoolArray)valueObj).GetValue(); + + Quality = PlcTagQC.TAG_QUALITY_GOOD; + } + else + { + Quality = PlcTagQC.TAG_QUALITY_BAD; + } + } + + public override PValue GetWriteValue() + { + return new ValueBoolArray(Value); + } + + public override string ToString() + { + var val = new ValueBoolArray(Value); + return ResultString(this, val.ToString()); + } + } + + public class PlcTagByteArray : PlcTag + { + private byte[] m_Value; + + public byte[] Value + { + get { return m_Value; } + set { m_Value = value; } + } + + public PlcTagByteArray(string name, ItemAddress address, uint softdatatype) : base(name, address, softdatatype) + { + m_Value = new byte[1]; + } + + public override void ProcessReadResult(object valueObj, ulong error) + { + LastReadError = error; + if (CheckErrorAndType(error, valueObj, typeof(ValueByteArray)) == 0) + { + Value = ((ValueByteArray)valueObj).GetValue(); + + Quality = PlcTagQC.TAG_QUALITY_GOOD; + } + else + { + Quality = PlcTagQC.TAG_QUALITY_BAD; + } + } + + public override PValue GetWriteValue() + { + return new ValueByteArray(Value); + } + + public override string ToString() + { + var val = new ValueByteArray(Value); + return ResultString(this, val.ToString()); + } + } + + public class PlcTagWordArray : PlcTag + { + private ushort[] m_Value; + + public ushort[] Value + { + get { return m_Value; } + set { m_Value = value; } + } + + public PlcTagWordArray(string name, ItemAddress address, uint softdatatype) : base(name, address, softdatatype) + { + m_Value = new ushort[1]; + } + + public override void ProcessReadResult(object valueObj, ulong error) + { + LastReadError = error; + if (CheckErrorAndType(error, valueObj, typeof(ValueWordArray)) == 0) + { + Value = ((ValueWordArray)valueObj).GetValue(); + + Quality = PlcTagQC.TAG_QUALITY_GOOD; + } + else + { + Quality = PlcTagQC.TAG_QUALITY_BAD; + } + } + + public override PValue GetWriteValue() + { + return new ValueWordArray(Value); + } + + public override string ToString() + { + var val = new ValueWordArray(Value); + return ResultString(this, val.ToString()); + } + } + + public class PlcTagIntArray : PlcTag + { + private short[] m_Value; + + public short[] Value + { + get { return m_Value; } + set { m_Value = value; } + } + + public PlcTagIntArray(string name, ItemAddress address, uint softdatatype) : base(name, address, softdatatype) + { + m_Value = new short[1]; + } + + public override void ProcessReadResult(object valueObj, ulong error) + { + LastReadError = error; + if (CheckErrorAndType(error, valueObj, typeof(ValueIntArray)) == 0) + { + Value = ((ValueIntArray)valueObj).GetValue(); + + Quality = PlcTagQC.TAG_QUALITY_GOOD; + } + else + { + Quality = PlcTagQC.TAG_QUALITY_BAD; + } + } + + public override PValue GetWriteValue() + { + return new ValueIntArray(Value); + } + + public override string ToString() + { + var val = new ValueIntArray(Value); + return ResultString(this, val.ToString()); + } + } + + public class PlcTagDWordArray : PlcTag + { + private uint[] m_Value; + + public uint[] Value + { + get { return m_Value; } + set { m_Value = value; } + } + + public PlcTagDWordArray(string name, ItemAddress address, uint softdatatype) : base(name, address, softdatatype) + { + m_Value = new uint[1]; + } + + public override void ProcessReadResult(object valueObj, ulong error) + { + LastReadError = error; + if (CheckErrorAndType(error, valueObj, typeof(ValueDWordArray)) == 0) + { + Value = ((ValueDWordArray)valueObj).GetValue(); + + Quality = PlcTagQC.TAG_QUALITY_GOOD; + } + else + { + Quality = PlcTagQC.TAG_QUALITY_BAD; + } + } + + public override PValue GetWriteValue() + { + return new ValueDWordArray(Value); + } + + public override string ToString() + { + var val = new ValueDWordArray(Value); + return ResultString(this, val.ToString()); + } + } + + public class PlcTagDIntArray : PlcTag + { + private int[] m_Value; + + public int[] Value + { + get { return m_Value; } + set { m_Value = value; } + } + + public PlcTagDIntArray(string name, ItemAddress address, uint softdatatype) : base(name, address, softdatatype) + { + m_Value = new int[1]; + } + + public override void ProcessReadResult(object valueObj, ulong error) + { + LastReadError = error; + if (CheckErrorAndType(error, valueObj, typeof(ValueDIntArray)) == 0) + { + Value = ((ValueDIntArray)valueObj).GetValue(); + + Quality = PlcTagQC.TAG_QUALITY_GOOD; + } + else + { + Quality = PlcTagQC.TAG_QUALITY_BAD; + } + } + + public override PValue GetWriteValue() + { + return new ValueDIntArray(Value); + } + + public override string ToString() + { + var val = new ValueDIntArray(Value); + return ResultString(this, val.ToString()); + } + } + + public class PlcTagRealArray : PlcTag + { + private float[] m_Value; + + public float[] Value + { + get { return m_Value; } + set { m_Value = value; } + } + + public PlcTagRealArray(string name, ItemAddress address, uint softdatatype) : base(name, address, softdatatype) + { + m_Value = new float[1]; + } + + public override void ProcessReadResult(object valueObj, ulong error) + { + LastReadError = error; + if (CheckErrorAndType(error, valueObj, typeof(ValueRealArray)) == 0) + { + Value = ((ValueRealArray)valueObj).GetValue(); + + Quality = PlcTagQC.TAG_QUALITY_GOOD; + } + else + { + Quality = PlcTagQC.TAG_QUALITY_BAD; + } + } + + public override PValue GetWriteValue() + { + return new ValueRealArray(Value); + } + + public override string ToString() + { + var val = new ValueRealArray(Value); + return ResultString(this, val.ToString()); + } + } + + public class PlcTagUSIntArray : PlcTag + { + private byte[] m_Value; + + public byte[] Value + { + get { return m_Value; } + set { m_Value = value; } + } + + public PlcTagUSIntArray(string name, ItemAddress address, uint softdatatype) : base(name, address, softdatatype) + { + m_Value = new byte[1]; + } + + public override void ProcessReadResult(object valueObj, ulong error) + { + LastReadError = error; + if (CheckErrorAndType(error, valueObj, typeof(ValueUSIntArray)) == 0) + { + Value = ((ValueUSIntArray)valueObj).GetValue(); + + Quality = PlcTagQC.TAG_QUALITY_GOOD; + } + else + { + Quality = PlcTagQC.TAG_QUALITY_BAD; + } + } + + public override PValue GetWriteValue() + { + return new ValueUSIntArray(Value); + } + + public override string ToString() + { + var val = new ValueUSIntArray(Value); + return ResultString(this, val.ToString()); + } + } + + public class PlcTagUIntArray : PlcTag + { + private ushort[] m_Value; + + public ushort[] Value + { + get { return m_Value; } + set { m_Value = value; } + } + + public PlcTagUIntArray(string name, ItemAddress address, uint softdatatype) : base(name, address, softdatatype) + { + m_Value = new ushort[1]; + } + + public override void ProcessReadResult(object valueObj, ulong error) + { + LastReadError = error; + if (CheckErrorAndType(error, valueObj, typeof(ValueUIntArray)) == 0) + { + Value = ((ValueUIntArray)valueObj).GetValue(); + + Quality = PlcTagQC.TAG_QUALITY_GOOD; + } + else + { + Quality = PlcTagQC.TAG_QUALITY_BAD; + } + } + + public override PValue GetWriteValue() + { + return new ValueUIntArray(Value); + } + + public override string ToString() + { + var val = new ValueUIntArray(Value); + return ResultString(this, val.ToString()); + } + } + + public class PlcTagUDIntArray : PlcTag + { + private uint[] m_Value; + + public uint[] Value + { + get { return m_Value; } + set { m_Value = value; } + } + + public PlcTagUDIntArray(string name, ItemAddress address, uint softdatatype) : base(name, address, softdatatype) + { + m_Value = new uint[1]; + } + + public override void ProcessReadResult(object valueObj, ulong error) + { + LastReadError = error; + if (CheckErrorAndType(error, valueObj, typeof(ValueUDIntArray)) == 0) + { + Value = ((ValueUDIntArray)valueObj).GetValue(); + + Quality = PlcTagQC.TAG_QUALITY_GOOD; + } + else + { + Quality = PlcTagQC.TAG_QUALITY_BAD; + } + } + + public override PValue GetWriteValue() + { + return new ValueUDIntArray(Value); + } + + public override string ToString() + { + var val = new ValueUDIntArray(Value); + return ResultString(this, val.ToString()); + } + } + + public class PlcTagSIntArray : PlcTag + { + private sbyte[] m_Value; + + public sbyte[] Value + { + get { return m_Value; } + set { m_Value = value; } + } + + public PlcTagSIntArray(string name, ItemAddress address, uint softdatatype) : base(name, address, softdatatype) + { + m_Value = new sbyte[1]; + } + + public override void ProcessReadResult(object valueObj, ulong error) + { + LastReadError = error; + if (CheckErrorAndType(error, valueObj, typeof(ValueSIntArray)) == 0) + { + Value = ((ValueSIntArray)valueObj).GetValue(); + + Quality = PlcTagQC.TAG_QUALITY_GOOD; + } + else + { + Quality = PlcTagQC.TAG_QUALITY_BAD; + } + } + + public override PValue GetWriteValue() + { + return new ValueSIntArray(Value); + } + + public override string ToString() + { + var val = new ValueSIntArray(Value); + return ResultString(this, val.ToString()); + } + } + + public class PlcTagDateAndTimeArray : PlcTag + { + /* BCD coded: + * YYMMDDhhmmssuuuQ + * uuu = milliseconds + * Q = Weekday 1=Su, 2=Mo, 3=Tu, 4=We, 5=Th, 6=Fr, 7=Sa + */ + private DateTime[] m_Value; + + public DateTime[] Value + { + get + { + return m_Value; + } + + set + { + bool dataOk = true; + foreach (var item in value) + { + if (item < new DateTime(1990, 1, 1) && item >= new DateTime(2090, 1, 1)) + { + dataOk = false; + break; + } + } + if (dataOk) + { + m_Value = value; + } + else + { + throw new ArgumentOutOfRangeException("Value", "DateTime must be >= 1990-01-01 and < 2090-01-01"); + } + } + } + + public PlcTagDateAndTimeArray(string name, ItemAddress address, uint softdatatype) : base(name, address, softdatatype) + { + Value = new DateTime[0]; + } + + public override void ProcessReadResult(object valueObj, ulong error) + { + LastReadError = error; + if (CheckErrorAndType(error, valueObj, typeof(ValueUSIntArray)) == 0) + { + List dateTimes = new List(); + var v = ((ValueUSIntArray)valueObj).GetValue(); + int pos = 0; + do + { + int[] ts = new int[8]; + for (int i = 0; i < 7; i++) + { + ts[i] = BcdByteToInt(v[pos + i]); + } + // The left nibble of the last byte contains the LSD of milliseconds, + // the right nibble the weekday (which we don't process here). + ts[7] = v[7] >> 4; + + int year; + if (ts[0] >= 90) + { + year = 1900 + ts[0]; + } + else + { + year = 2000 + ts[0]; + } + var value = new DateTime(year, ts[1], ts[2], ts[3], ts[4], ts[5]); + value = value.AddMilliseconds(ts[6] * 10 + ts[7]); + dateTimes.Add(value); + pos += 8; + } while (pos < v.Length); + Value = dateTimes.ToArray(); + Quality = PlcTagQC.TAG_QUALITY_GOOD; + } + else + { + Quality = PlcTagQC.TAG_QUALITY_BAD; + } + } + + public override PValue GetWriteValue() + { + var byteStrings = new List(); + foreach (var item in Value) + { + int[] ts = new int[8]; + byte[] b = new byte[8]; + if (item.Year < 2000) + { + // 90-99 = 1990-1999 + ts[0] = item.Year - 1900; + } + else + { + // 00-89 = 2000-2089 + ts[0] = item.Year - 2000; + } + ts[1] = item.Month; + ts[2] = item.Day; + ts[3] = item.Hour; + ts[4] = item.Minute; + ts[5] = item.Second; + ts[6] = item.Millisecond / 10; + ts[7] = (item.Millisecond % 10) << 4; // Don't set the weekday + for (int i = 0; i < 7; i++) + { + b[i] = IntToBcdByte(ts[i]); + } + b[7] = (byte)ts[7]; + byteStrings.AddRange(b); + } + return new ValueUSIntArray(byteStrings.ToArray()); + } + + public override string ToString() + { + string s = ""; + for (int i = 0; i < Value.Length; i++) + { + string ts = Value[i].ToString(); + if (Value[i].Millisecond > 0) + { + ts += String.Format(".{0:D03}", Value[i].Millisecond); + } + s += String.Format("{0}", ts); + } + s += ""; + return ResultString(this, s); + } + } + + + public class PlcTagStringArray : PlcTag + { + private string[] m_Value; + private byte m_MaxLength = 254; + private string m_Encoding = "ISO-8859-1"; + + public string[] Value + { + get + { + return m_Value; + } + + set + { + bool lengthOk = true; + foreach (var item in value) + { + if (item.Length > m_MaxLength) + { + lengthOk = false; + break; + } + } + if (lengthOk) + { + m_Value = value; + } + else + { + throw new ArgumentOutOfRangeException("Value", "String is longer than the allowed max. length of " + m_MaxLength); + } + } + } + + public PlcTagStringArray(string name, ItemAddress address, uint softdatatype, byte maxlength = 254) : base(name, address, softdatatype) + { + m_MaxLength = maxlength; + } + + public override void ProcessReadResult(object valueObj, ulong error) + { + LastReadError = error; + if (CheckErrorAndType(error, valueObj, typeof(ValueUSIntArray)) == 0) + { + List strings = new List(); + var v = ((ValueUSIntArray)valueObj).GetValue(); + int pos = 0; + do + { + int max_len = v[pos]; + int act_len = v[pos + 1]; + // IEC 61131-3 states ISO-646 IRV, with optional extensions like "Latin-1 Supplement". + // Siemens TIA-Portal gives warnings using other than 7 Bit ASCII characters. + // Let the user define his local encoding via SetStringEncoding(). + var str = Encoding.GetEncoding(m_Encoding).GetString(v, pos + 2, act_len); + strings.Add(str); + pos += max_len + 2; + + } while (pos < v.Length); + Value = strings.ToArray(); + Quality = PlcTagQC.TAG_QUALITY_GOOD; + } + else + { + Quality = PlcTagQC.TAG_QUALITY_BAD; + } + } + + public override PValue GetWriteValue() + { + var byteStrings = new List(); + foreach (var item in Value) + { + // Must write the complete array of MaxLength of the string (plus two bytes header). + byte[] sb = Encoding.GetEncoding(m_Encoding).GetBytes(item); + var b = new byte[m_MaxLength + 2]; + b[0] = m_MaxLength; + b[1] = (byte)sb.Length; + for (int i = 0; i < sb.Length; i++) + { + b[i + 2] = sb[i]; + } + byteStrings.AddRange(b); + } + return new ValueUSIntArray(byteStrings.ToArray()); + } + + public void SetStringEncoding(string encoding) + { + m_Encoding = encoding; + } + + public override string ToString() + { + string s = ""; + for (int i = 0; i < Value.Length; i++) + { + s += String.Format("{0}", Value[i]); + } + s += ""; + return ResultString(this, s); + } + } + #endregion } \ No newline at end of file diff --git a/src/S7CommPlusDriver/ClientApi/PlcTagExtend.cs b/src/S7CommPlusDriver/ClientApi/PlcTagExtend.cs deleted file mode 100644 index 7bae1bf..0000000 --- a/src/S7CommPlusDriver/ClientApi/PlcTagExtend.cs +++ /dev/null @@ -1,710 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Text; - -namespace S7CommPlusDriver.ClientApi; - -public class PlcTagBoolArray : PlcTag -{ - private bool[] m_Value; - - public bool[] Value - { - get { return m_Value; } - set { m_Value = value; } - } - - public PlcTagBoolArray(string name, ItemAddress address, uint softdatatype) : base(name, address, softdatatype) - { - m_Value = new bool[1]; - } - - public override void ProcessReadResult(object valueObj, ulong error) - { - LastReadError = error; - if (CheckErrorAndType(error, valueObj, typeof(ValueBoolArray)) == 0) - { - Value = ((ValueBoolArray)valueObj).GetValue(); - - Quality = PlcTagQC.TAG_QUALITY_GOOD; - } - else - { - Quality = PlcTagQC.TAG_QUALITY_BAD; - } - } - - public override PValue GetWriteValue() - { - return new ValueBoolArray(Value); - } - - public override string ToString() - { - var val = new ValueBoolArray(Value); - return ResultString(this, val.ToString()); - } -} - -public class PlcTagByteArray : PlcTag -{ - private byte[] m_Value; - - public byte[] Value - { - get { return m_Value; } - set { m_Value = value; } - } - - public PlcTagByteArray(string name, ItemAddress address, uint softdatatype) : base(name, address, softdatatype) - { - m_Value = new byte[1]; - } - - public override void ProcessReadResult(object valueObj, ulong error) - { - LastReadError = error; - if (CheckErrorAndType(error, valueObj, typeof(ValueByteArray)) == 0) - { - Value = ((ValueByteArray)valueObj).GetValue(); - - Quality = PlcTagQC.TAG_QUALITY_GOOD; - } - else - { - Quality = PlcTagQC.TAG_QUALITY_BAD; - } - } - - public override PValue GetWriteValue() - { - return new ValueByteArray(Value); - } - - public override string ToString() - { - var val = new ValueByteArray(Value); - return ResultString(this, val.ToString()); - } -} - -public class PlcTagWordArray : PlcTag -{ - private ushort[] m_Value; - - public ushort[] Value - { - get { return m_Value; } - set { m_Value = value; } - } - - public PlcTagWordArray(string name, ItemAddress address, uint softdatatype) : base(name, address, softdatatype) - { - m_Value = new ushort[1]; - } - - public override void ProcessReadResult(object valueObj, ulong error) - { - LastReadError = error; - if (CheckErrorAndType(error, valueObj, typeof(ValueWordArray)) == 0) - { - Value = ((ValueWordArray)valueObj).GetValue(); - - Quality = PlcTagQC.TAG_QUALITY_GOOD; - } - else - { - Quality = PlcTagQC.TAG_QUALITY_BAD; - } - } - - public override PValue GetWriteValue() - { - return new ValueWordArray(Value); - } - - public override string ToString() - { - var val = new ValueWordArray(Value); - return ResultString(this, val.ToString()); - } -} - -public class PlcTagIntArray : PlcTag -{ - private short[] m_Value; - - public short[] Value - { - get { return m_Value; } - set { m_Value = value; } - } - - public PlcTagIntArray(string name, ItemAddress address, uint softdatatype) : base(name, address, softdatatype) - { - m_Value = new short[1]; - } - - public override void ProcessReadResult(object valueObj, ulong error) - { - LastReadError = error; - if (CheckErrorAndType(error, valueObj, typeof(ValueIntArray)) == 0) - { - Value = ((ValueIntArray)valueObj).GetValue(); - - Quality = PlcTagQC.TAG_QUALITY_GOOD; - } - else - { - Quality = PlcTagQC.TAG_QUALITY_BAD; - } - } - - public override PValue GetWriteValue() - { - return new ValueIntArray(Value); - } - - public override string ToString() - { - var val = new ValueIntArray(Value); - return ResultString(this, val.ToString()); - } -} - -public class PlcTagDWordArray : PlcTag -{ - private uint[] m_Value; - - public uint[] Value - { - get { return m_Value; } - set { m_Value = value; } - } - - public PlcTagDWordArray(string name, ItemAddress address, uint softdatatype) : base(name, address, softdatatype) - { - m_Value = new uint[1]; - } - - public override void ProcessReadResult(object valueObj, ulong error) - { - LastReadError = error; - if (CheckErrorAndType(error, valueObj, typeof(ValueDWordArray)) == 0) - { - Value = ((ValueDWordArray)valueObj).GetValue(); - - Quality = PlcTagQC.TAG_QUALITY_GOOD; - } - else - { - Quality = PlcTagQC.TAG_QUALITY_BAD; - } - } - - public override PValue GetWriteValue() - { - return new ValueDWordArray(Value); - } - - public override string ToString() - { - var val = new ValueDWordArray(Value); - return ResultString(this, val.ToString()); - } -} - -public class PlcTagDIntArray : PlcTag -{ - private int[] m_Value; - - public int[] Value - { - get { return m_Value; } - set { m_Value = value; } - } - - public PlcTagDIntArray(string name, ItemAddress address, uint softdatatype) : base(name, address, softdatatype) - { - m_Value = new int[1]; - } - - public override void ProcessReadResult(object valueObj, ulong error) - { - LastReadError = error; - if (CheckErrorAndType(error, valueObj, typeof(ValueDIntArray)) == 0) - { - Value = ((ValueDIntArray)valueObj).GetValue(); - - Quality = PlcTagQC.TAG_QUALITY_GOOD; - } - else - { - Quality = PlcTagQC.TAG_QUALITY_BAD; - } - } - - public override PValue GetWriteValue() - { - return new ValueDIntArray(Value); - } - - public override string ToString() - { - var val = new ValueDIntArray(Value); - return ResultString(this, val.ToString()); - } -} - -public class PlcTagRealArray : PlcTag -{ - private float[] m_Value; - - public float[] Value - { - get { return m_Value; } - set { m_Value = value; } - } - - public PlcTagRealArray(string name, ItemAddress address, uint softdatatype) : base(name, address, softdatatype) - { - m_Value = new float[1]; - } - - public override void ProcessReadResult(object valueObj, ulong error) - { - LastReadError = error; - if (CheckErrorAndType(error, valueObj, typeof(ValueRealArray)) == 0) - { - Value = ((ValueRealArray)valueObj).GetValue(); - - Quality = PlcTagQC.TAG_QUALITY_GOOD; - } - else - { - Quality = PlcTagQC.TAG_QUALITY_BAD; - } - } - - public override PValue GetWriteValue() - { - return new ValueRealArray(Value); - } - - public override string ToString() - { - var val = new ValueRealArray(Value); - return ResultString(this, val.ToString()); - } -} - -public class PlcTagUSIntArray : PlcTag -{ - private byte[] m_Value; - - public byte[] Value - { - get { return m_Value; } - set { m_Value = value; } - } - - public PlcTagUSIntArray(string name, ItemAddress address, uint softdatatype) : base(name, address, softdatatype) - { - m_Value = new byte[1]; - } - - public override void ProcessReadResult(object valueObj, ulong error) - { - LastReadError = error; - if (CheckErrorAndType(error, valueObj, typeof(ValueUSIntArray)) == 0) - { - Value = ((ValueUSIntArray)valueObj).GetValue(); - - Quality = PlcTagQC.TAG_QUALITY_GOOD; - } - else - { - Quality = PlcTagQC.TAG_QUALITY_BAD; - } - } - - public override PValue GetWriteValue() - { - return new ValueUSIntArray(Value); - } - - public override string ToString() - { - var val = new ValueUSIntArray(Value); - return ResultString(this, val.ToString()); - } -} - -public class PlcTagUIntArray : PlcTag -{ - private ushort[] m_Value; - - public ushort[] Value - { - get { return m_Value; } - set { m_Value = value; } - } - - public PlcTagUIntArray(string name, ItemAddress address, uint softdatatype) : base(name, address, softdatatype) - { - m_Value = new ushort[1]; - } - - public override void ProcessReadResult(object valueObj, ulong error) - { - LastReadError = error; - if (CheckErrorAndType(error, valueObj, typeof(ValueUIntArray)) == 0) - { - Value = ((ValueUIntArray)valueObj).GetValue(); - - Quality = PlcTagQC.TAG_QUALITY_GOOD; - } - else - { - Quality = PlcTagQC.TAG_QUALITY_BAD; - } - } - - public override PValue GetWriteValue() - { - return new ValueUIntArray(Value); - } - - public override string ToString() - { - var val = new ValueUIntArray(Value); - return ResultString(this, val.ToString()); - } -} - -public class PlcTagUDIntArray : PlcTag -{ - private uint[] m_Value; - - public uint[] Value - { - get { return m_Value; } - set { m_Value = value; } - } - - public PlcTagUDIntArray(string name, ItemAddress address, uint softdatatype) : base(name, address, softdatatype) - { - m_Value = new uint[1]; - } - - public override void ProcessReadResult(object valueObj, ulong error) - { - LastReadError = error; - if (CheckErrorAndType(error, valueObj, typeof(ValueUDIntArray)) == 0) - { - Value = ((ValueUDIntArray)valueObj).GetValue(); - - Quality = PlcTagQC.TAG_QUALITY_GOOD; - } - else - { - Quality = PlcTagQC.TAG_QUALITY_BAD; - } - } - - public override PValue GetWriteValue() - { - return new ValueUDIntArray(Value); - } - - public override string ToString() - { - var val = new ValueUDIntArray(Value); - return ResultString(this, val.ToString()); - } -} - -public class PlcTagSIntArray : PlcTag -{ - private sbyte[] m_Value; - - public sbyte[] Value - { - get { return m_Value; } - set { m_Value = value; } - } - - public PlcTagSIntArray(string name, ItemAddress address, uint softdatatype) : base(name, address, softdatatype) - { - m_Value = new sbyte[1]; - } - - public override void ProcessReadResult(object valueObj, ulong error) - { - LastReadError = error; - if (CheckErrorAndType(error, valueObj, typeof(ValueSIntArray)) == 0) - { - Value = ((ValueSIntArray)valueObj).GetValue(); - - Quality = PlcTagQC.TAG_QUALITY_GOOD; - } - else - { - Quality = PlcTagQC.TAG_QUALITY_BAD; - } - } - - public override PValue GetWriteValue() - { - return new ValueSIntArray(Value); - } - - public override string ToString() - { - var val = new ValueSIntArray(Value); - return ResultString(this, val.ToString()); - } -} - -public class PlcTagDateAndTimeArray : PlcTag -{ - /* BCD coded: - * YYMMDDhhmmssuuuQ - * uuu = milliseconds - * Q = Weekday 1=Su, 2=Mo, 3=Tu, 4=We, 5=Th, 6=Fr, 7=Sa - */ - private DateTime[] m_Value; - - public DateTime[] Value - { - get - { - return m_Value; - } - - set - { - bool dataOk = true; - foreach (var item in value) - { - if (item < new DateTime(1990, 1, 1) && item >= new DateTime(2090, 1, 1)) - { - dataOk = false; - break; - } - } - if (dataOk) - { - m_Value = value; - } - else - { - throw new ArgumentOutOfRangeException("Value", "DateTime must be >= 1990-01-01 and < 2090-01-01"); - } - } - } - - public PlcTagDateAndTimeArray(string name, ItemAddress address, uint softdatatype) : base(name, address, softdatatype) - { - Value = new DateTime[0]; - } - - public override void ProcessReadResult(object valueObj, ulong error) - { - LastReadError = error; - if (CheckErrorAndType(error, valueObj, typeof(ValueUSIntArray)) == 0) - { - List dateTimes = new List(); - var v = ((ValueUSIntArray)valueObj).GetValue(); - int pos = 0; - do - { - int[] ts = new int[8]; - for (int i = 0; i < 7; i++) - { - ts[i] = BcdByteToInt(v[pos + i]); - } - // The left nibble of the last byte contains the LSD of milliseconds, - // the right nibble the weekday (which we don't process here). - ts[7] = v[7] >> 4; - - int year; - if (ts[0] >= 90) - { - year = 1900 + ts[0]; - } - else - { - year = 2000 + ts[0]; - } - var value = new DateTime(year, ts[1], ts[2], ts[3], ts[4], ts[5]); - value = value.AddMilliseconds(ts[6] * 10 + ts[7]); - dateTimes.Add(value); - pos += 8; - } while (pos < v.Length); - Value = dateTimes.ToArray(); - Quality = PlcTagQC.TAG_QUALITY_GOOD; - } - else - { - Quality = PlcTagQC.TAG_QUALITY_BAD; - } - } - - public override PValue GetWriteValue() - { - var byteStrings = new List(); - foreach (var item in Value) - { - int[] ts = new int[8]; - byte[] b = new byte[8]; - if (item.Year < 2000) - { - // 90-99 = 1990-1999 - ts[0] = item.Year - 1900; - } - else - { - // 00-89 = 2000-2089 - ts[0] = item.Year - 2000; - } - ts[1] = item.Month; - ts[2] = item.Day; - ts[3] = item.Hour; - ts[4] = item.Minute; - ts[5] = item.Second; - ts[6] = item.Millisecond / 10; - ts[7] = (item.Millisecond % 10) << 4; // Don't set the weekday - for (int i = 0; i < 7; i++) - { - b[i] = IntToBcdByte(ts[i]); - } - b[7] = (byte)ts[7]; - byteStrings.AddRange(b); - } - return new ValueUSIntArray(byteStrings.ToArray()); - } - - public override string ToString() - { - string s = ""; - for (int i = 0; i < Value.Length; i++) - { - string ts = Value[i].ToString(); - if (Value[i].Millisecond > 0) - { - ts += String.Format(".{0:D03}", Value[i].Millisecond); - } - s += String.Format("{0}", ts); - } - s += ""; - return ResultString(this, s); - } -} - - -public class PlcTagStringArray : PlcTag -{ - private string[] m_Value; - private byte m_MaxLength = 254; - private string m_Encoding = "ISO-8859-1"; - - public string[] Value - { - get - { - return m_Value; - } - - set - { - bool lengthOk = true; - foreach (var item in value) - { - if (item.Length > m_MaxLength) - { - lengthOk = false; - break; - } - } - if (lengthOk) - { - m_Value = value; - } - else - { - throw new ArgumentOutOfRangeException("Value", "String is longer than the allowed max. length of " + m_MaxLength); - } - } - } - - public PlcTagStringArray(string name, ItemAddress address, uint softdatatype, byte maxlength = 254) : base(name, address, softdatatype) - { - m_MaxLength = maxlength; - } - - public override void ProcessReadResult(object valueObj, ulong error) - { - LastReadError = error; - if (CheckErrorAndType(error, valueObj, typeof(ValueUSIntArray)) == 0) - { - List strings = new List(); - var v = ((ValueUSIntArray)valueObj).GetValue(); - int pos = 0; - do - { - int max_len = v[pos]; - int act_len = v[pos + 1]; - // IEC 61131-3 states ISO-646 IRV, with optional extensions like "Latin-1 Supplement". - // Siemens TIA-Portal gives warnings using other than 7 Bit ASCII characters. - // Let the user define his local encoding via SetStringEncoding(). - var str = Encoding.GetEncoding(m_Encoding).GetString(v, pos + 2, act_len); - strings.Add(str); - pos += max_len + 2; - - } while (pos < v.Length); - Value = strings.ToArray(); - Quality = PlcTagQC.TAG_QUALITY_GOOD; - } - else - { - Quality = PlcTagQC.TAG_QUALITY_BAD; - } - } - - public override PValue GetWriteValue() - { - var byteStrings = new List(); - foreach (var item in Value) - { - // Must write the complete array of MaxLength of the string (plus two bytes header). - byte[] sb = Encoding.GetEncoding(m_Encoding).GetBytes(item); - var b = new byte[m_MaxLength + 2]; - b[0] = m_MaxLength; - b[1] = (byte)sb.Length; - for (int i = 0; i < sb.Length; i++) - { - b[i + 2] = sb[i]; - } - byteStrings.AddRange(b); - } - return new ValueUSIntArray(byteStrings.ToArray()); - } - - public void SetStringEncoding(string encoding) - { - m_Encoding = encoding; - } - - public override string ToString() - { - string s = ""; - for (int i = 0; i < Value.Length; i++) - { - s += String.Format("{0}", Value[i]); - } - s += ""; - return ResultString(this, s); - } -} From ef73ef30b2a27d3a45da0a7c63a47e24133b520c Mon Sep 17 00:00:00 2001 From: z Date: Mon, 23 Mar 2026 11:22:01 +0800 Subject: [PATCH 09/11] add function: support for S7-1507S F software controller(min FW:21.9) --- .vs/S7CommPlusDriver/v17/.wsuo | Bin 0 -> 13824 bytes .vs/S7CommPlusDriver/v17/DocumentLayout.json | 97 +++++ .../Legitimation/Legitimation.cs | 55 ++- .../Legitimation/Legitimation.cs.bak | 392 ++++++++++++++++++ 4 files changed, 537 insertions(+), 7 deletions(-) create mode 100644 .vs/S7CommPlusDriver/v17/.wsuo create mode 100644 .vs/S7CommPlusDriver/v17/DocumentLayout.json create mode 100644 src/S7CommPlusDriver/Legitimation/Legitimation.cs.bak diff --git a/.vs/S7CommPlusDriver/v17/.wsuo b/.vs/S7CommPlusDriver/v17/.wsuo new file mode 100644 index 0000000000000000000000000000000000000000..6bfc261eedb41bff1b4e15028361ec8b8400fa38 GIT binary patch literal 13824 zcmeHOO^h5z6|UJ3AUFhK2L}R}B`hJ3)TX_5EpYmLP8wEg)4WY*nD60)J*T}PP=C&BpGRI^t!96 ztLoLO_fxNa=C5}ied`x*KJhOx5Kf5)#NB%ji4PX?YlXP@xDX#fnt6Bc-Mhz6j3as& zrf39KL<_$iF)PlBExg;}23c7-92XDvc>dshYW?WP5C8Fdzx!wH2+JMM3tt4rmUvY> z59}FYXT|IHX=U%{<6@?VpToZQ>%pltWJ7#a(D;dWTWp{QZIJ>R7>f{)cFPTW&54;> zk=?_}74yH2#Ii@#O zkM#8a76lJM>)*Sn|Es`zsQ+sCd$yDIfOdd-`vl@zhd+ck^BAh=KW$N7dhheo$fqsX zn|qjguSbBqJqR@4$xr%uoU^O&pK8rHgL4S&Kj#C=6Z!ucz-Iv`0G|WQ0zMCT67U7U z9N>$9rvP69JPr6V;46S<0M7y>U>@)szyO#43s48x00-a#iZq=ZNS`XCPb0nvI0IM$ zoCTZ%d=qdU@I2rJz_$P|0+sHOJhE_+R9eZf^-VBluuf z@T1?iro6sbTt%v*3zfWQ$M8SDhW6+yF%05gL?2T0;TpWrmhuR9AdB>ocagKdM^@MW z)P7ptYWef4_)MNLETZKWux$co8}F)qZ(f@^JyWY8p>4a)FMF@xBXyhMlH&d~=vEWd z?EwE(^rwwjtUTlW6zBEyllXyUHoNejKxW5m7`s&=ECg#L;KNnb6O4b2{>3QtQ}ng{EyKU2W`Qlt_1ox4J`RKQ!T$Devk!yc4@z^b zuGb%B_>212p95Zk^!51w`LEH>qTf{&Kkc<1e?%t=uiViy$#~7o?VEu*Vis1}z|VxI z$7fj}E$>8l<*om2owr{7>8NdOhMk?X{ld9%>#zLn=*;7fj9-7${tlKu$Zuudq5nU* z|IcxJzX#fZNpxt^`m}?S;HMpD{eBzU3rJQDllH$dqLPA~4+g^|d4p|LNz;e?E#Qne zDb~CG7}tcHYi2R~H{p$O?qA1Rk)APEBppD8nV9yP-uo^}Zy?1r#4hG&dT#4jof`lP zF|IE+@uT-ful5G=R*}2j^DguD8{J$JEBbi=pLMM4*^-CS^h#po9nxD|N3U;BY0oz? zYNVvCsQCSqQqch?$+_u~Aj-+V!=U@vb; zNg+=)=z&p}c<#Y757up|oX!LHQNJU;tCJ(Y+5brbcfazz zyy8ldJG^Z9Jh!omKiNx_$BK0r9~v5B?#SDxpZUoj&cFGOU*9?Qt2Hd-8p6*WyM6ce zi5GwQM)I4Vo%l~Mw7NP6;2L@^4m{IM>XG!)dM53_w56ZAzVwnTHC@+r5-(Cj)w)a5 zg{uoBeNi?poiy4c_4OO~`9FOq>_doqb4&x}JWP7>?p;&Ui&)-6^rfzYx7 zOQv-%k+vBpGBi9-rcoG&u^szyY-?F0*3mi80=9is9_yplgMjV)?VL0G&p>@}ef|yd|KClVM;w()0o=G#oRt+^A4Ibzn2sdd!CkS+N~yZV@51G5ca_GJyB*(YQQ}+tGi|#LocbEUTny~LM*c!&xqX2cTInP zFY>;WKM&7;56^$Muvf?P=EL*f^7H5a$NBFvyrIi#&vfs3cX7|POoN{q+4wLSe0B`}bIrgzAPj@_ z=Qz3ZlK&InG$e5wmXSMrJhNbKF_8bHTZiJeVR7EA)$17rt wFGC8rqT=1SPVWvxzh!?6{V$GxzoqeFhv)y3o&QsBdG43n_y7O^ literal 0 HcmV?d00001 diff --git a/.vs/S7CommPlusDriver/v17/DocumentLayout.json b/.vs/S7CommPlusDriver/v17/DocumentLayout.json new file mode 100644 index 0000000..3b2eef4 --- /dev/null +++ b/.vs/S7CommPlusDriver/v17/DocumentLayout.json @@ -0,0 +1,97 @@ +{ + "Version": 1, + "WorkspaceRootPath": "C:\\Users\\zhangf7.ZF-WORLD\\source\\repos\\S7CommPlusDriver\\", + "Documents": [], + "DocumentGroupContainers": [ + { + "Orientation": 0, + "VerticalTabListWidth": 256, + "DocumentGroups": [ + { + "DockedWidth": 194, + "SelectedChildIndex": -1, + "Children": [ + { + "$type": "Bookmark", + "Name": "ST:0:0:{aa2115a1-9712-457b-9047-dbb71ca2cdd2}" + }, + { + "$type": "Bookmark", + "Name": "ST:132:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}" + }, + { + "$type": "Bookmark", + "Name": "ST:128:0:{1fc202d4-d401-403c-9834-5b218574bb67}" + }, + { + "$type": "Bookmark", + "Name": "ST:135:0:{1fc202d4-d401-403c-9834-5b218574bb67}" + }, + { + "$type": "Bookmark", + "Name": "ST:132:0:{1fc202d4-d401-403c-9834-5b218574bb67}" + }, + { + "$type": "Bookmark", + "Name": "ST:133:0:{1fc202d4-d401-403c-9834-5b218574bb67}" + }, + { + "$type": "Bookmark", + "Name": "ST:134:0:{1fc202d4-d401-403c-9834-5b218574bb67}" + }, + { + "$type": "Bookmark", + "Name": "ST:130:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}" + }, + { + "$type": "Bookmark", + "Name": "ST:137:0:{1fc202d4-d401-403c-9834-5b218574bb67}" + }, + { + "$type": "Bookmark", + "Name": "ST:142:0:{1fc202d4-d401-403c-9834-5b218574bb67}" + }, + { + "$type": "Bookmark", + "Name": "ST:143:0:{1fc202d4-d401-403c-9834-5b218574bb67}" + }, + { + "$type": "Bookmark", + "Name": "ST:0:0:{1c4feeaa-4718-4aa9-859d-94ce25d182ba}" + }, + { + "$type": "Bookmark", + "Name": "ST:0:0:{3ae79031-e1bc-11d0-8f78-00a0c9110057}" + }, + { + "$type": "Bookmark", + "Name": "ST:0:0:{1c64b9c2-e352-428e-a56d-0ace190b99a6}" + }, + { + "$type": "Bookmark", + "Name": "ST:0:0:{de1fc918-f32e-4dd7-a915-1792a051f26b}" + }, + { + "$type": "Bookmark", + "Name": "ST:0:0:{e5c86464-96be-4d7c-9a8b-abcb3bbf5f92}" + } + ] + }, + { + "DockedWidth": 206, + "SelectedChildIndex": -1, + "Children": [ + { + "$type": "Bookmark", + "Name": "ST:928770227:0:{81164725-9a96-4ece-a4cb-440d8fd285e5}" + }, + { + "$type": "Bookmark", + "Name": "ST:254354193:0:{71f361cc-493f-47c0-923f-f2570b6f8618}" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/src/S7CommPlusDriver/Legitimation/Legitimation.cs b/src/S7CommPlusDriver/Legitimation/Legitimation.cs index 8050fd5..7901ced 100644 --- a/src/S7CommPlusDriver/Legitimation/Legitimation.cs +++ b/src/S7CommPlusDriver/Legitimation/Legitimation.cs @@ -23,23 +23,55 @@ public partial class S7CommPlusConnection /// error code (0 = ok) private int legitimate(ValueStruct serverSession, string password, string username = "") { + // S7-1214C (6ES7 214-1AG40-0XB0) 1;6ES7 214-1AG40-0XB0 ;V4.5 + // S7-1510SP (6ES7 510-1DJ01-0AB0) 1;6ES7 510-1DJ01-0AB0;V2.9 + // S7-1507SF (6ES7 672-7FC01-0YA0) 1;6ES7 672-7FC01-0YA0;V21.9 + // Parse device and firmware version + // doc: https://cache.industry.siemens.com/dl/files/068/109769068/att_1329908/v4/109769068_UsingCertificatesWithTIAPortal_DOC_V2_1_en.pdf + // Certificates in the scope of PG/PC and HMI communication + // Starting with TIA Portal V17, PG / PC and HMI communication is secured with TLS, protecting the data exchanged between + // Field PGs and HMIs with SIMATIC CPUs. + // The CPU families that support Secure PG / HMI communication are: + // • S7 - 1500 controllers as of firmware version V2.9. + // • S7 - 1200 controllers as of firmware version V4.5. + // • Software controllers as of firmware version V21.9. + // • SIMATIC Drive controllers as of firmware version V2.9. + // • PLCSim and PLCSim Advanced Version V4.0. + // HMI components that support Secure PG/ HMI communication, as of image version V17, are: + // • Panels or PCs configured with WinCC Basic, Comfort and Advanced. + // • PCs with WinCC RT Professional. + // • WinCC Unified PCs and Comfort Panels. + // In addition, SINAMICS RT SW, as of version V6.1, and STARTDRIVE, as of version V17, support secure communication string sessionVersionPAOMString = ((ValueWString)serverSession.GetStructElement((uint)Ids.LID_SessionVersionSystemPAOMString)).GetValue(); - Regex reVersions = new Regex("^.*;.*[17]\\s?([52]\\d\\d).+;[VS](\\d\\.\\d)$"); + var reVersions = new Regex( + @"^[^;]*;[^;]*[17]\s?(\d{3}).*;[VS](\d{1,2}\.\d+)$", + RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase + ); Match m = reVersions.Match(sessionVersionPAOMString); if (!m.Success) { Console.WriteLine("S7CommPlusConnection - Legitimate: Could not extract firmware version!"); return S7Consts.errCliFirmwareNotSupported; } - string deviceVersion = m.Groups[1].Value; - string firmwareVersion = m.Groups[2].Value; - int fwVerNo = int.Parse(firmwareVersion.Split('.')[0]) * 100; - fwVerNo += int.Parse(firmwareVersion.Split('.')[1]); + string deviceVersion = m.Groups[1].Value; // e.g., "672" + string firmwareVersion = m.Groups[2].Value; // e.g., "21.9" + + // Compute fwVerNo = major*100 + minor (e.g., "21.9" -> 2109) + int fwVerNo; + { + var parts = firmwareVersion.Split('.'); + if (parts.Length < 2 || !int.TryParse(parts[0], out var major) || !int.TryParse(parts[1], out var minor)) + { + Console.WriteLine($"S7CommPlusConnection - Legitimate: Invalid firmware format: {firmwareVersion}"); + return S7Consts.errCliFirmwareNotSupported; + } + fwVerNo = (major * 100) + minor; + } // Check if we have to use legacy legitimation via the firmware version bool legacyLegitimation = false; - if (deviceVersion.StartsWith("5")) + if (deviceVersion.StartsWith("5")) // S7-1500 (5xx) { if (fwVerNo < 209) { @@ -55,7 +87,7 @@ private int legitimate(ValueStruct serverSession, string password, string userna { legacyLegitimation = false; } - else if (deviceVersion.StartsWith("2")) + else if (deviceVersion.StartsWith("2")) // S7-1200 (2xx) { if (fwVerNo < 403) { @@ -67,6 +99,15 @@ private int legitimate(ValueStruct serverSession, string password, string userna legacyLegitimation = true; } } + else if (deviceVersion.StartsWith("6")) // S7-1507S (6xx) + { + if (fwVerNo < 2109) + { + Console.WriteLine("S7CommPlusConnection - Legitimate: Firmware version is not supported!"); + return S7Consts.errCliFirmwareNotSupported; + } + legacyLegitimation = true; + } else { Console.WriteLine("S7CommPlusConnection - Legitimate: Device version is not supported!"); diff --git a/src/S7CommPlusDriver/Legitimation/Legitimation.cs.bak b/src/S7CommPlusDriver/Legitimation/Legitimation.cs.bak new file mode 100644 index 0000000..d04e921 --- /dev/null +++ b/src/S7CommPlusDriver/Legitimation/Legitimation.cs.bak @@ -0,0 +1,392 @@ +using S7CommPlusDriver.Legitimation; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace S7CommPlusDriver { + public partial class S7CommPlusConnection + { + + private byte[] omsSecret; + + /// + /// Legitimation stage of the connect routine + /// + /// Server sesstion information containing the firmware version + /// PLC password + /// PLC username (leave empty for legacy login) + /// error code (0 = ok) + private int legitimate(ValueStruct serverSession, string password, string username = "") + { + // S7-1214C (6ES7 214-1AG40-0XB0) 1;6ES7 214-1AG40-0XB0 ;V4.5 + // S7-1510SP (6ES7 510-1DJ01-0AB0) 1;6ES7 510-1DJ01-0AB0;V2.9 + // S7-1507SF (6ES7 672-7FC01-0YA0) 1;6ES7 672-7FC01-0YA0;V21.9 + + // Parse device and firmware version + // doc: https://cache.industry.siemens.com/dl/files/068/109769068/att_1329908/v4/109769068_UsingCertificatesWithTIAPortal_DOC_V2_1_en.pdf + // Certificates in the scope of PG/PC and HMI communication + // Starting with TIA Portal V17, PG / PC and HMI communication is secured with TLS, protecting the data exchanged between + // Field PGs and HMIs with SIMATIC CPUs. + // The CPU families that support Secure PG / HMI communication are: + // • S7 - 1500 controllers as of firmware version V2.9. + // • S7 - 1200 controllers as of firmware version V4.5. + // • Software controllers as of firmware version V21.9. + // • SIMATIC Drive controllers as of firmware version V2.9. + // • PLCSim and PLCSim Advanced Version V4.0. + // HMI components that support Secure PG/ HMI communication, as of image version V17, are: + // • Panels or PCs configured with WinCC Basic, Comfort and Advanced. + // • PCs with WinCC RT Professional. + // • WinCC Unified PCs and Comfort Panels. + // In addition, SINAMICS RT SW, as of version V6.1, and STARTDRIVE, as of version V17, support secure communication + string sessionVersionPAOMString = ((ValueWString)serverSession.GetStructElement((uint)Ids.LID_SessionVersionSystemPAOMString)).GetValue(); + var reVersions = new Regex( + @"^[^;]*;[^;]*[17]\s?(\d{3}).*;[VS](\d{1,2}\.\d+)$", + RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase + ); + Match m = reVersions.Match(sessionVersionPAOMString); + if (!m.Success) + { + Console.WriteLine("S7CommPlusConnection - Legitimate: Could not extract firmware version!"); + return S7Consts.errCliFirmwareNotSupported; + } + string deviceVersion = m.Groups[1].Value; + string firmwareVersion = m.Groups[2].Value; + + // Compute fwVerNo = major*100 + minor (e.g., "21.9" -> 2109) + int fwVerNo; + { + var parts = firmwareVersion.Split('.'); + if (parts.Length < 2 || !int.TryParse(parts[0], out var major) || !int.TryParse(parts[1], out var minor)) + { + Console.WriteLine($"S7CommPlusConnection - Legitimate: Invalid firmware format: {firmwareVersion}"); + return S7Consts.errCliFirmwareNotSupported; + } + fwVerNo = (major * 100) + minor; + } + + // Check if we have to use legacy legitimation via the firmware version + bool legacyLegitimation = false; + if (deviceVersion.StartsWith("5")) + { + if (fwVerNo < 209) + { + Console.WriteLine("S7CommPlusConnection - Legitimate: Firmware version is not supported!"); + return S7Consts.errCliFirmwareNotSupported; + } + if (fwVerNo < 301) + { + legacyLegitimation = true; + } + } + else if (sessionVersionPAOMString.Contains("50-0XB0") && deviceVersion.StartsWith("2")) //New S7-1200 G2 (example: "1;6ES7 212-1HG50-0XB0;V1.0") + { + legacyLegitimation = false; + } + else if (deviceVersion.StartsWith("2")) + { + if (fwVerNo < 403) + { + Console.WriteLine("S7CommPlusConnection - Legitimate: Firmware version is not supported!"); + return S7Consts.errCliFirmwareNotSupported; + } + if (fwVerNo < 407) + { + legacyLegitimation = true; + } + } + else if (deviceVersion.StartsWith("6")) // S7-1507S (6xx) + { + if (fwVerNo < 2109) + { + Console.WriteLine("S7CommPlusConnection - Legitimate: Firmware version is not supported!"); + return S7Consts.errCliFirmwareNotSupported; + } + legacyLegitimation = true; + } + else + { + Console.WriteLine("S7CommPlusConnection - Legitimate: Device version is not supported!"); + return S7Consts.errCliDeviceNotSupported; + } + + // Get current protection level + var getVarSubstreamedReq = new GetVarSubstreamedRequest(ProtocolVersion.V2); + getVarSubstreamedReq.InObjectId = m_SessionId; + getVarSubstreamedReq.SessionId = m_SessionId; + getVarSubstreamedReq.Address = Ids.EffectiveProtectionLevel; + int res = SendS7plusFunctionObject(getVarSubstreamedReq); + if (res != 0) + { + m_client.Disconnect(); + return res; + } + m_LastError = 0; + WaitForNewS7plusReceived(m_ReadTimeout); + if (m_LastError != 0) + { + m_client.Disconnect(); + return m_LastError; + } + + var getVarSubstreamedRes = GetVarSubstreamedResponse.DeserializeFromPdu(m_ReceivedPDU); + if (getVarSubstreamedRes == null) + { + Console.WriteLine("S7CommPlusConnection - Legitimate: GetVarSubstreamedResponse with Error!"); + m_client.Disconnect(); + return S7Consts.errIsoInvalidPDU; + } + + // Check access level + UInt32 accessLevel = (getVarSubstreamedRes.Value as ValueUDInt).GetValue(); + if (accessLevel > AccessLevel.FullAccess && password != "") + { + // Legitimate + if (legacyLegitimation) + { + return legitimateLegacy(password); + } + else + { + return legitimateNew(password, username); + } + + } + else if (accessLevel > AccessLevel.FullAccess) + { + Console.WriteLine("S7CommPlusConnection - Legitimate: Warning: Access level is not fullaccess but no password set!"); + } + + return 0; + } + + /// + /// Legitimate using the new login method (firmware >= 3.1) + /// + /// PLC password + /// PLC username (leave empy for legacy login) + /// error code (0 = ok) + private int legitimateNew(string password, string username = "") + { + // Get challenge + var getVarSubstreamedReq_challange = new GetVarSubstreamedRequest(ProtocolVersion.V2); + getVarSubstreamedReq_challange.InObjectId = m_SessionId; + getVarSubstreamedReq_challange.SessionId = m_SessionId; + getVarSubstreamedReq_challange.Address = Ids.ServerSessionRequest; + int res = SendS7plusFunctionObject(getVarSubstreamedReq_challange); + if (res != 0) + { + m_client.Disconnect(); + return res; + } + m_LastError = 0; + WaitForNewS7plusReceived(m_ReadTimeout); + if (m_LastError != 0) + { + m_client.Disconnect(); + return m_LastError; + } + + var getVarSubstreamedRes_challenge = GetVarSubstreamedResponse.DeserializeFromPdu(m_ReceivedPDU); + if (getVarSubstreamedRes_challenge == null) + { + Console.WriteLine("S7CommPlusConnection - Legitimate: getVarSubstreamedRes_challenge with Error!"); + m_client.Disconnect(); + return S7Consts.errIsoInvalidPDU; + } + + byte[] challenge = (getVarSubstreamedRes_challenge.Value as ValueUSIntArray).GetValue(); + + // Encrypt challengeResponse + byte[] challengeResponse; + if (omsSecret == null || omsSecret.Length != 32) + { + // Create oms exporter secret + omsSecret = m_client.getOMSExporterSecret(); + } + // Roll key + byte[] key = LegitimationCrypto.sha256(omsSecret); + omsSecret = key; + + // Use the first 16 bytes of the challenge as iv + byte[] iv = new ArraySegment(challenge, 0, 16).ToArray(); + // Encrypt + challengeResponse = LegitimationCrypto.EncryptAesCbc(buildLegitimationPayload(password, username), key, iv); + + // Send challengeResponse + var setVariableReq = new SetVariableRequest(ProtocolVersion.V2); + setVariableReq.InObjectId = m_SessionId; + setVariableReq.SessionId = m_SessionId; + setVariableReq.Address = Ids.Legitimate; + setVariableReq.Value = new ValueBlob(0, challengeResponse); + res = SendS7plusFunctionObject(setVariableReq); + if (res != 0) + { + m_client.Disconnect(); + return res; + } + m_LastError = 0; + WaitForNewS7plusReceived(m_ReadTimeout); + if (m_LastError != 0) + { + m_client.Disconnect(); + return m_LastError; + } + + var setVariableResponse = SetVariableResponse.DeserializeFromPdu(m_ReceivedPDU); + if (setVariableResponse == null) + { + Console.WriteLine("S7CommPlusConnection - Legitimate: setVariableResponse with Error!"); + m_client.Disconnect(); + return S7Consts.errIsoInvalidPDU; + } + // Check if the legitimation attempt was successful + Int16 errorCode = (Int16)setVariableResponse.ReturnValue; + if (errorCode < 0) + { + Console.WriteLine("S7CommPlusConnection - Legitimate: access denied"); + m_client.Disconnect(); + return S7Consts.errCliAccessDenied; + } + + return 0; + } + + /// + /// Builds the legitimation payload from given username and password. + /// If username is empty the payload for a legacy login will be build. + /// If username is not empty the payload for the new login is build. + /// + /// PLC password + /// PLC username (optional) + /// Build payload + private static byte[] buildLegitimationPayload(string password, string username = "") + { + ValueStruct payload = new ValueStruct(Ids.LID_LegitimationPayloadStruct); + if (username != "") + { + // Login with username and password = new login + payload.AddStructElement(Ids.LID_LegitimationPayloadType, new ValueUDInt(LegitimationType.New)); + payload.AddStructElement(Ids.LID_LegitimationPayloadUsername, new ValueBlob(0, Encoding.UTF8.GetBytes(username))); + payload.AddStructElement(Ids.LID_LegitimationPayloadPassword, new ValueBlob(0, Encoding.UTF8.GetBytes(password))); + + } + else + { + // Login with only password = legacy login + // Hash password + byte[] hashedPw; + using (SHA1Managed sha1 = new SHA1Managed()) + { + hashedPw = sha1.ComputeHash(Encoding.UTF8.GetBytes(password)); + } + + payload.AddStructElement(Ids.LID_LegitimationPayloadType, new ValueUDInt(LegitimationType.Legacy)); + payload.AddStructElement(Ids.LID_LegitimationPayloadUsername, new ValueBlob(0, Encoding.UTF8.GetBytes(username))); + payload.AddStructElement(Ids.LID_LegitimationPayloadPassword, new ValueBlob(0, hashedPw)); + } + using (var memStr = new MemoryStream()) + { + payload.Serialize(memStr); + return memStr.ToArray(); + } + } + + /// + /// Legitimate using the old legacy login (firmware version < 3.1) + /// + /// PLC password + /// error code (0 = OK) + private int legitimateLegacy(string password) + { + + // Get challenge + var getVarSubstreamedReq_challange = new GetVarSubstreamedRequest(ProtocolVersion.V2); + getVarSubstreamedReq_challange.InObjectId = m_SessionId; + getVarSubstreamedReq_challange.SessionId = m_SessionId; + getVarSubstreamedReq_challange.Address = Ids.ServerSessionRequest; + int res = SendS7plusFunctionObject(getVarSubstreamedReq_challange); + if (res != 0) + { + m_client.Disconnect(); + return res; + } + m_LastError = 0; + WaitForNewS7plusReceived(m_ReadTimeout); + if (m_LastError != 0) + { + m_client.Disconnect(); + return m_LastError; + } + + var getVarSubstreamedRes_challenge = GetVarSubstreamedResponse.DeserializeFromPdu(m_ReceivedPDU); + if (getVarSubstreamedRes_challenge == null) + { + Console.WriteLine("S7CommPlusConnection - Legitimate: getVarSubstreamedRes_challenge with Error!"); + m_client.Disconnect(); + return S7Consts.errIsoInvalidPDU; + } + + byte[] challenge = (getVarSubstreamedRes_challenge.Value as ValueUSIntArray).GetValue(); + + // Calculate challengeResponse [sha1(password) xor challenge] + byte[] challengeResponse; + using (SHA1Managed sha1 = new SHA1Managed()) + { + challengeResponse = sha1.ComputeHash(Encoding.UTF8.GetBytes(password)); + } + if (challengeResponse.Length != challenge.Length) + { + Console.WriteLine("S7CommPlusConnection - Legitimate: challengeResponse.Length != challenge.Length"); + m_client.Disconnect(); + return S7Consts.errIsoInvalidPDU; + } + for (int i = 0; i < challengeResponse.Length; ++i) + { + challengeResponse[i] = (byte)(challengeResponse[i] ^ challenge[i]); + } + + // Send challengeResponse + var setVariableReq = new SetVariableRequest(ProtocolVersion.V2); + setVariableReq.InObjectId = m_SessionId; + setVariableReq.SessionId = m_SessionId; + setVariableReq.Address = Ids.ServerSessionResponse; + setVariableReq.Value = new ValueUSIntArray(challengeResponse); + res = SendS7plusFunctionObject(setVariableReq); + if (res != 0) + { + m_client.Disconnect(); + return res; + } + m_LastError = 0; + WaitForNewS7plusReceived(m_ReadTimeout); + if (m_LastError != 0) + { + m_client.Disconnect(); + return m_LastError; + } + + var setVariableResponse = SetVariableResponse.DeserializeFromPdu(m_ReceivedPDU); + if (setVariableResponse == null) + { + Console.WriteLine("S7CommPlusConnection - Legitimate: setVariableResponse with Error!"); + m_client.Disconnect(); + return S7Consts.errIsoInvalidPDU; + } + // Check if the legitimation attempt was successful + Int16 errorCode = (Int16)setVariableResponse.ReturnValue; + if (errorCode < 0) + { + Console.WriteLine("S7CommPlusConnection - Legitimate: access denied"); + m_client.Disconnect(); + return S7Consts.errCliAccessDenied; + } + + return 0; + } + } +} From 1a91c494e24ce6fc9e899623df4b7d04cd0f3484 Mon Sep 17 00:00:00 2001 From: z Date: Mon, 23 Mar 2026 12:03:08 +0800 Subject: [PATCH 10/11] Revert "add function: support for S7-1507S F software controller(min FW:21.9)" This reverts commit ef73ef30b2a27d3a45da0a7c63a47e24133b520c. --- .vs/S7CommPlusDriver/v17/.wsuo | Bin 13824 -> 0 bytes .vs/S7CommPlusDriver/v17/DocumentLayout.json | 97 ----- .../Legitimation/Legitimation.cs | 55 +-- .../Legitimation/Legitimation.cs.bak | 392 ------------------ 4 files changed, 7 insertions(+), 537 deletions(-) delete mode 100644 .vs/S7CommPlusDriver/v17/.wsuo delete mode 100644 .vs/S7CommPlusDriver/v17/DocumentLayout.json delete mode 100644 src/S7CommPlusDriver/Legitimation/Legitimation.cs.bak diff --git a/.vs/S7CommPlusDriver/v17/.wsuo b/.vs/S7CommPlusDriver/v17/.wsuo deleted file mode 100644 index 6bfc261eedb41bff1b4e15028361ec8b8400fa38..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13824 zcmeHOO^h5z6|UJ3AUFhK2L}R}B`hJ3)TX_5EpYmLP8wEg)4WY*nD60)J*T}PP=C&BpGRI^t!96 ztLoLO_fxNa=C5}ied`x*KJhOx5Kf5)#NB%ji4PX?YlXP@xDX#fnt6Bc-Mhz6j3as& zrf39KL<_$iF)PlBExg;}23c7-92XDvc>dshYW?WP5C8Fdzx!wH2+JMM3tt4rmUvY> z59}FYXT|IHX=U%{<6@?VpToZQ>%pltWJ7#a(D;dWTWp{QZIJ>R7>f{)cFPTW&54;> zk=?_}74yH2#Ii@#O zkM#8a76lJM>)*Sn|Es`zsQ+sCd$yDIfOdd-`vl@zhd+ck^BAh=KW$N7dhheo$fqsX zn|qjguSbBqJqR@4$xr%uoU^O&pK8rHgL4S&Kj#C=6Z!ucz-Iv`0G|WQ0zMCT67U7U z9N>$9rvP69JPr6V;46S<0M7y>U>@)szyO#43s48x00-a#iZq=ZNS`XCPb0nvI0IM$ zoCTZ%d=qdU@I2rJz_$P|0+sHOJhE_+R9eZf^-VBluuf z@T1?iro6sbTt%v*3zfWQ$M8SDhW6+yF%05gL?2T0;TpWrmhuR9AdB>ocagKdM^@MW z)P7ptYWef4_)MNLETZKWux$co8}F)qZ(f@^JyWY8p>4a)FMF@xBXyhMlH&d~=vEWd z?EwE(^rwwjtUTlW6zBEyllXyUHoNejKxW5m7`s&=ECg#L;KNnb6O4b2{>3QtQ}ng{EyKU2W`Qlt_1ox4J`RKQ!T$Devk!yc4@z^b zuGb%B_>212p95Zk^!51w`LEH>qTf{&Kkc<1e?%t=uiViy$#~7o?VEu*Vis1}z|VxI z$7fj}E$>8l<*om2owr{7>8NdOhMk?X{ld9%>#zLn=*;7fj9-7${tlKu$Zuudq5nU* z|IcxJzX#fZNpxt^`m}?S;HMpD{eBzU3rJQDllH$dqLPA~4+g^|d4p|LNz;e?E#Qne zDb~CG7}tcHYi2R~H{p$O?qA1Rk)APEBppD8nV9yP-uo^}Zy?1r#4hG&dT#4jof`lP zF|IE+@uT-ful5G=R*}2j^DguD8{J$JEBbi=pLMM4*^-CS^h#po9nxD|N3U;BY0oz? zYNVvCsQCSqQqch?$+_u~Aj-+V!=U@vb; zNg+=)=z&p}c<#Y757up|oX!LHQNJU;tCJ(Y+5brbcfazz zyy8ldJG^Z9Jh!omKiNx_$BK0r9~v5B?#SDxpZUoj&cFGOU*9?Qt2Hd-8p6*WyM6ce zi5GwQM)I4Vo%l~Mw7NP6;2L@^4m{IM>XG!)dM53_w56ZAzVwnTHC@+r5-(Cj)w)a5 zg{uoBeNi?poiy4c_4OO~`9FOq>_doqb4&x}JWP7>?p;&Ui&)-6^rfzYx7 zOQv-%k+vBpGBi9-rcoG&u^szyY-?F0*3mi80=9is9_yplgMjV)?VL0G&p>@}ef|yd|KClVM;w()0o=G#oRt+^A4Ibzn2sdd!CkS+N~yZV@51G5ca_GJyB*(YQQ}+tGi|#LocbEUTny~LM*c!&xqX2cTInP zFY>;WKM&7;56^$Muvf?P=EL*f^7H5a$NBFvyrIi#&vfs3cX7|POoN{q+4wLSe0B`}bIrgzAPj@_ z=Qz3ZlK&InG$e5wmXSMrJhNbKF_8bHTZiJeVR7EA)$17rt wFGC8rqT=1SPVWvxzh!?6{V$GxzoqeFhv)y3o&QsBdG43n_y7O^ diff --git a/.vs/S7CommPlusDriver/v17/DocumentLayout.json b/.vs/S7CommPlusDriver/v17/DocumentLayout.json deleted file mode 100644 index 3b2eef4..0000000 --- a/.vs/S7CommPlusDriver/v17/DocumentLayout.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "Version": 1, - "WorkspaceRootPath": "C:\\Users\\zhangf7.ZF-WORLD\\source\\repos\\S7CommPlusDriver\\", - "Documents": [], - "DocumentGroupContainers": [ - { - "Orientation": 0, - "VerticalTabListWidth": 256, - "DocumentGroups": [ - { - "DockedWidth": 194, - "SelectedChildIndex": -1, - "Children": [ - { - "$type": "Bookmark", - "Name": "ST:0:0:{aa2115a1-9712-457b-9047-dbb71ca2cdd2}" - }, - { - "$type": "Bookmark", - "Name": "ST:132:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}" - }, - { - "$type": "Bookmark", - "Name": "ST:128:0:{1fc202d4-d401-403c-9834-5b218574bb67}" - }, - { - "$type": "Bookmark", - "Name": "ST:135:0:{1fc202d4-d401-403c-9834-5b218574bb67}" - }, - { - "$type": "Bookmark", - "Name": "ST:132:0:{1fc202d4-d401-403c-9834-5b218574bb67}" - }, - { - "$type": "Bookmark", - "Name": "ST:133:0:{1fc202d4-d401-403c-9834-5b218574bb67}" - }, - { - "$type": "Bookmark", - "Name": "ST:134:0:{1fc202d4-d401-403c-9834-5b218574bb67}" - }, - { - "$type": "Bookmark", - "Name": "ST:130:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}" - }, - { - "$type": "Bookmark", - "Name": "ST:137:0:{1fc202d4-d401-403c-9834-5b218574bb67}" - }, - { - "$type": "Bookmark", - "Name": "ST:142:0:{1fc202d4-d401-403c-9834-5b218574bb67}" - }, - { - "$type": "Bookmark", - "Name": "ST:143:0:{1fc202d4-d401-403c-9834-5b218574bb67}" - }, - { - "$type": "Bookmark", - "Name": "ST:0:0:{1c4feeaa-4718-4aa9-859d-94ce25d182ba}" - }, - { - "$type": "Bookmark", - "Name": "ST:0:0:{3ae79031-e1bc-11d0-8f78-00a0c9110057}" - }, - { - "$type": "Bookmark", - "Name": "ST:0:0:{1c64b9c2-e352-428e-a56d-0ace190b99a6}" - }, - { - "$type": "Bookmark", - "Name": "ST:0:0:{de1fc918-f32e-4dd7-a915-1792a051f26b}" - }, - { - "$type": "Bookmark", - "Name": "ST:0:0:{e5c86464-96be-4d7c-9a8b-abcb3bbf5f92}" - } - ] - }, - { - "DockedWidth": 206, - "SelectedChildIndex": -1, - "Children": [ - { - "$type": "Bookmark", - "Name": "ST:928770227:0:{81164725-9a96-4ece-a4cb-440d8fd285e5}" - }, - { - "$type": "Bookmark", - "Name": "ST:254354193:0:{71f361cc-493f-47c0-923f-f2570b6f8618}" - } - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/src/S7CommPlusDriver/Legitimation/Legitimation.cs b/src/S7CommPlusDriver/Legitimation/Legitimation.cs index 7901ced..8050fd5 100644 --- a/src/S7CommPlusDriver/Legitimation/Legitimation.cs +++ b/src/S7CommPlusDriver/Legitimation/Legitimation.cs @@ -23,55 +23,23 @@ public partial class S7CommPlusConnection /// error code (0 = ok) private int legitimate(ValueStruct serverSession, string password, string username = "") { - // S7-1214C (6ES7 214-1AG40-0XB0) 1;6ES7 214-1AG40-0XB0 ;V4.5 - // S7-1510SP (6ES7 510-1DJ01-0AB0) 1;6ES7 510-1DJ01-0AB0;V2.9 - // S7-1507SF (6ES7 672-7FC01-0YA0) 1;6ES7 672-7FC01-0YA0;V21.9 - // Parse device and firmware version - // doc: https://cache.industry.siemens.com/dl/files/068/109769068/att_1329908/v4/109769068_UsingCertificatesWithTIAPortal_DOC_V2_1_en.pdf - // Certificates in the scope of PG/PC and HMI communication - // Starting with TIA Portal V17, PG / PC and HMI communication is secured with TLS, protecting the data exchanged between - // Field PGs and HMIs with SIMATIC CPUs. - // The CPU families that support Secure PG / HMI communication are: - // • S7 - 1500 controllers as of firmware version V2.9. - // • S7 - 1200 controllers as of firmware version V4.5. - // • Software controllers as of firmware version V21.9. - // • SIMATIC Drive controllers as of firmware version V2.9. - // • PLCSim and PLCSim Advanced Version V4.0. - // HMI components that support Secure PG/ HMI communication, as of image version V17, are: - // • Panels or PCs configured with WinCC Basic, Comfort and Advanced. - // • PCs with WinCC RT Professional. - // • WinCC Unified PCs and Comfort Panels. - // In addition, SINAMICS RT SW, as of version V6.1, and STARTDRIVE, as of version V17, support secure communication string sessionVersionPAOMString = ((ValueWString)serverSession.GetStructElement((uint)Ids.LID_SessionVersionSystemPAOMString)).GetValue(); - var reVersions = new Regex( - @"^[^;]*;[^;]*[17]\s?(\d{3}).*;[VS](\d{1,2}\.\d+)$", - RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase - ); + Regex reVersions = new Regex("^.*;.*[17]\\s?([52]\\d\\d).+;[VS](\\d\\.\\d)$"); Match m = reVersions.Match(sessionVersionPAOMString); if (!m.Success) { Console.WriteLine("S7CommPlusConnection - Legitimate: Could not extract firmware version!"); return S7Consts.errCliFirmwareNotSupported; } - string deviceVersion = m.Groups[1].Value; // e.g., "672" - string firmwareVersion = m.Groups[2].Value; // e.g., "21.9" - - // Compute fwVerNo = major*100 + minor (e.g., "21.9" -> 2109) - int fwVerNo; - { - var parts = firmwareVersion.Split('.'); - if (parts.Length < 2 || !int.TryParse(parts[0], out var major) || !int.TryParse(parts[1], out var minor)) - { - Console.WriteLine($"S7CommPlusConnection - Legitimate: Invalid firmware format: {firmwareVersion}"); - return S7Consts.errCliFirmwareNotSupported; - } - fwVerNo = (major * 100) + minor; - } + string deviceVersion = m.Groups[1].Value; + string firmwareVersion = m.Groups[2].Value; + int fwVerNo = int.Parse(firmwareVersion.Split('.')[0]) * 100; + fwVerNo += int.Parse(firmwareVersion.Split('.')[1]); // Check if we have to use legacy legitimation via the firmware version bool legacyLegitimation = false; - if (deviceVersion.StartsWith("5")) // S7-1500 (5xx) + if (deviceVersion.StartsWith("5")) { if (fwVerNo < 209) { @@ -87,7 +55,7 @@ private int legitimate(ValueStruct serverSession, string password, string userna { legacyLegitimation = false; } - else if (deviceVersion.StartsWith("2")) // S7-1200 (2xx) + else if (deviceVersion.StartsWith("2")) { if (fwVerNo < 403) { @@ -99,15 +67,6 @@ private int legitimate(ValueStruct serverSession, string password, string userna legacyLegitimation = true; } } - else if (deviceVersion.StartsWith("6")) // S7-1507S (6xx) - { - if (fwVerNo < 2109) - { - Console.WriteLine("S7CommPlusConnection - Legitimate: Firmware version is not supported!"); - return S7Consts.errCliFirmwareNotSupported; - } - legacyLegitimation = true; - } else { Console.WriteLine("S7CommPlusConnection - Legitimate: Device version is not supported!"); diff --git a/src/S7CommPlusDriver/Legitimation/Legitimation.cs.bak b/src/S7CommPlusDriver/Legitimation/Legitimation.cs.bak deleted file mode 100644 index d04e921..0000000 --- a/src/S7CommPlusDriver/Legitimation/Legitimation.cs.bak +++ /dev/null @@ -1,392 +0,0 @@ -using S7CommPlusDriver.Legitimation; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Security.Cryptography; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading.Tasks; - -namespace S7CommPlusDriver { - public partial class S7CommPlusConnection - { - - private byte[] omsSecret; - - /// - /// Legitimation stage of the connect routine - /// - /// Server sesstion information containing the firmware version - /// PLC password - /// PLC username (leave empty for legacy login) - /// error code (0 = ok) - private int legitimate(ValueStruct serverSession, string password, string username = "") - { - // S7-1214C (6ES7 214-1AG40-0XB0) 1;6ES7 214-1AG40-0XB0 ;V4.5 - // S7-1510SP (6ES7 510-1DJ01-0AB0) 1;6ES7 510-1DJ01-0AB0;V2.9 - // S7-1507SF (6ES7 672-7FC01-0YA0) 1;6ES7 672-7FC01-0YA0;V21.9 - - // Parse device and firmware version - // doc: https://cache.industry.siemens.com/dl/files/068/109769068/att_1329908/v4/109769068_UsingCertificatesWithTIAPortal_DOC_V2_1_en.pdf - // Certificates in the scope of PG/PC and HMI communication - // Starting with TIA Portal V17, PG / PC and HMI communication is secured with TLS, protecting the data exchanged between - // Field PGs and HMIs with SIMATIC CPUs. - // The CPU families that support Secure PG / HMI communication are: - // • S7 - 1500 controllers as of firmware version V2.9. - // • S7 - 1200 controllers as of firmware version V4.5. - // • Software controllers as of firmware version V21.9. - // • SIMATIC Drive controllers as of firmware version V2.9. - // • PLCSim and PLCSim Advanced Version V4.0. - // HMI components that support Secure PG/ HMI communication, as of image version V17, are: - // • Panels or PCs configured with WinCC Basic, Comfort and Advanced. - // • PCs with WinCC RT Professional. - // • WinCC Unified PCs and Comfort Panels. - // In addition, SINAMICS RT SW, as of version V6.1, and STARTDRIVE, as of version V17, support secure communication - string sessionVersionPAOMString = ((ValueWString)serverSession.GetStructElement((uint)Ids.LID_SessionVersionSystemPAOMString)).GetValue(); - var reVersions = new Regex( - @"^[^;]*;[^;]*[17]\s?(\d{3}).*;[VS](\d{1,2}\.\d+)$", - RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase - ); - Match m = reVersions.Match(sessionVersionPAOMString); - if (!m.Success) - { - Console.WriteLine("S7CommPlusConnection - Legitimate: Could not extract firmware version!"); - return S7Consts.errCliFirmwareNotSupported; - } - string deviceVersion = m.Groups[1].Value; - string firmwareVersion = m.Groups[2].Value; - - // Compute fwVerNo = major*100 + minor (e.g., "21.9" -> 2109) - int fwVerNo; - { - var parts = firmwareVersion.Split('.'); - if (parts.Length < 2 || !int.TryParse(parts[0], out var major) || !int.TryParse(parts[1], out var minor)) - { - Console.WriteLine($"S7CommPlusConnection - Legitimate: Invalid firmware format: {firmwareVersion}"); - return S7Consts.errCliFirmwareNotSupported; - } - fwVerNo = (major * 100) + minor; - } - - // Check if we have to use legacy legitimation via the firmware version - bool legacyLegitimation = false; - if (deviceVersion.StartsWith("5")) - { - if (fwVerNo < 209) - { - Console.WriteLine("S7CommPlusConnection - Legitimate: Firmware version is not supported!"); - return S7Consts.errCliFirmwareNotSupported; - } - if (fwVerNo < 301) - { - legacyLegitimation = true; - } - } - else if (sessionVersionPAOMString.Contains("50-0XB0") && deviceVersion.StartsWith("2")) //New S7-1200 G2 (example: "1;6ES7 212-1HG50-0XB0;V1.0") - { - legacyLegitimation = false; - } - else if (deviceVersion.StartsWith("2")) - { - if (fwVerNo < 403) - { - Console.WriteLine("S7CommPlusConnection - Legitimate: Firmware version is not supported!"); - return S7Consts.errCliFirmwareNotSupported; - } - if (fwVerNo < 407) - { - legacyLegitimation = true; - } - } - else if (deviceVersion.StartsWith("6")) // S7-1507S (6xx) - { - if (fwVerNo < 2109) - { - Console.WriteLine("S7CommPlusConnection - Legitimate: Firmware version is not supported!"); - return S7Consts.errCliFirmwareNotSupported; - } - legacyLegitimation = true; - } - else - { - Console.WriteLine("S7CommPlusConnection - Legitimate: Device version is not supported!"); - return S7Consts.errCliDeviceNotSupported; - } - - // Get current protection level - var getVarSubstreamedReq = new GetVarSubstreamedRequest(ProtocolVersion.V2); - getVarSubstreamedReq.InObjectId = m_SessionId; - getVarSubstreamedReq.SessionId = m_SessionId; - getVarSubstreamedReq.Address = Ids.EffectiveProtectionLevel; - int res = SendS7plusFunctionObject(getVarSubstreamedReq); - if (res != 0) - { - m_client.Disconnect(); - return res; - } - m_LastError = 0; - WaitForNewS7plusReceived(m_ReadTimeout); - if (m_LastError != 0) - { - m_client.Disconnect(); - return m_LastError; - } - - var getVarSubstreamedRes = GetVarSubstreamedResponse.DeserializeFromPdu(m_ReceivedPDU); - if (getVarSubstreamedRes == null) - { - Console.WriteLine("S7CommPlusConnection - Legitimate: GetVarSubstreamedResponse with Error!"); - m_client.Disconnect(); - return S7Consts.errIsoInvalidPDU; - } - - // Check access level - UInt32 accessLevel = (getVarSubstreamedRes.Value as ValueUDInt).GetValue(); - if (accessLevel > AccessLevel.FullAccess && password != "") - { - // Legitimate - if (legacyLegitimation) - { - return legitimateLegacy(password); - } - else - { - return legitimateNew(password, username); - } - - } - else if (accessLevel > AccessLevel.FullAccess) - { - Console.WriteLine("S7CommPlusConnection - Legitimate: Warning: Access level is not fullaccess but no password set!"); - } - - return 0; - } - - /// - /// Legitimate using the new login method (firmware >= 3.1) - /// - /// PLC password - /// PLC username (leave empy for legacy login) - /// error code (0 = ok) - private int legitimateNew(string password, string username = "") - { - // Get challenge - var getVarSubstreamedReq_challange = new GetVarSubstreamedRequest(ProtocolVersion.V2); - getVarSubstreamedReq_challange.InObjectId = m_SessionId; - getVarSubstreamedReq_challange.SessionId = m_SessionId; - getVarSubstreamedReq_challange.Address = Ids.ServerSessionRequest; - int res = SendS7plusFunctionObject(getVarSubstreamedReq_challange); - if (res != 0) - { - m_client.Disconnect(); - return res; - } - m_LastError = 0; - WaitForNewS7plusReceived(m_ReadTimeout); - if (m_LastError != 0) - { - m_client.Disconnect(); - return m_LastError; - } - - var getVarSubstreamedRes_challenge = GetVarSubstreamedResponse.DeserializeFromPdu(m_ReceivedPDU); - if (getVarSubstreamedRes_challenge == null) - { - Console.WriteLine("S7CommPlusConnection - Legitimate: getVarSubstreamedRes_challenge with Error!"); - m_client.Disconnect(); - return S7Consts.errIsoInvalidPDU; - } - - byte[] challenge = (getVarSubstreamedRes_challenge.Value as ValueUSIntArray).GetValue(); - - // Encrypt challengeResponse - byte[] challengeResponse; - if (omsSecret == null || omsSecret.Length != 32) - { - // Create oms exporter secret - omsSecret = m_client.getOMSExporterSecret(); - } - // Roll key - byte[] key = LegitimationCrypto.sha256(omsSecret); - omsSecret = key; - - // Use the first 16 bytes of the challenge as iv - byte[] iv = new ArraySegment(challenge, 0, 16).ToArray(); - // Encrypt - challengeResponse = LegitimationCrypto.EncryptAesCbc(buildLegitimationPayload(password, username), key, iv); - - // Send challengeResponse - var setVariableReq = new SetVariableRequest(ProtocolVersion.V2); - setVariableReq.InObjectId = m_SessionId; - setVariableReq.SessionId = m_SessionId; - setVariableReq.Address = Ids.Legitimate; - setVariableReq.Value = new ValueBlob(0, challengeResponse); - res = SendS7plusFunctionObject(setVariableReq); - if (res != 0) - { - m_client.Disconnect(); - return res; - } - m_LastError = 0; - WaitForNewS7plusReceived(m_ReadTimeout); - if (m_LastError != 0) - { - m_client.Disconnect(); - return m_LastError; - } - - var setVariableResponse = SetVariableResponse.DeserializeFromPdu(m_ReceivedPDU); - if (setVariableResponse == null) - { - Console.WriteLine("S7CommPlusConnection - Legitimate: setVariableResponse with Error!"); - m_client.Disconnect(); - return S7Consts.errIsoInvalidPDU; - } - // Check if the legitimation attempt was successful - Int16 errorCode = (Int16)setVariableResponse.ReturnValue; - if (errorCode < 0) - { - Console.WriteLine("S7CommPlusConnection - Legitimate: access denied"); - m_client.Disconnect(); - return S7Consts.errCliAccessDenied; - } - - return 0; - } - - /// - /// Builds the legitimation payload from given username and password. - /// If username is empty the payload for a legacy login will be build. - /// If username is not empty the payload for the new login is build. - /// - /// PLC password - /// PLC username (optional) - /// Build payload - private static byte[] buildLegitimationPayload(string password, string username = "") - { - ValueStruct payload = new ValueStruct(Ids.LID_LegitimationPayloadStruct); - if (username != "") - { - // Login with username and password = new login - payload.AddStructElement(Ids.LID_LegitimationPayloadType, new ValueUDInt(LegitimationType.New)); - payload.AddStructElement(Ids.LID_LegitimationPayloadUsername, new ValueBlob(0, Encoding.UTF8.GetBytes(username))); - payload.AddStructElement(Ids.LID_LegitimationPayloadPassword, new ValueBlob(0, Encoding.UTF8.GetBytes(password))); - - } - else - { - // Login with only password = legacy login - // Hash password - byte[] hashedPw; - using (SHA1Managed sha1 = new SHA1Managed()) - { - hashedPw = sha1.ComputeHash(Encoding.UTF8.GetBytes(password)); - } - - payload.AddStructElement(Ids.LID_LegitimationPayloadType, new ValueUDInt(LegitimationType.Legacy)); - payload.AddStructElement(Ids.LID_LegitimationPayloadUsername, new ValueBlob(0, Encoding.UTF8.GetBytes(username))); - payload.AddStructElement(Ids.LID_LegitimationPayloadPassword, new ValueBlob(0, hashedPw)); - } - using (var memStr = new MemoryStream()) - { - payload.Serialize(memStr); - return memStr.ToArray(); - } - } - - /// - /// Legitimate using the old legacy login (firmware version < 3.1) - /// - /// PLC password - /// error code (0 = OK) - private int legitimateLegacy(string password) - { - - // Get challenge - var getVarSubstreamedReq_challange = new GetVarSubstreamedRequest(ProtocolVersion.V2); - getVarSubstreamedReq_challange.InObjectId = m_SessionId; - getVarSubstreamedReq_challange.SessionId = m_SessionId; - getVarSubstreamedReq_challange.Address = Ids.ServerSessionRequest; - int res = SendS7plusFunctionObject(getVarSubstreamedReq_challange); - if (res != 0) - { - m_client.Disconnect(); - return res; - } - m_LastError = 0; - WaitForNewS7plusReceived(m_ReadTimeout); - if (m_LastError != 0) - { - m_client.Disconnect(); - return m_LastError; - } - - var getVarSubstreamedRes_challenge = GetVarSubstreamedResponse.DeserializeFromPdu(m_ReceivedPDU); - if (getVarSubstreamedRes_challenge == null) - { - Console.WriteLine("S7CommPlusConnection - Legitimate: getVarSubstreamedRes_challenge with Error!"); - m_client.Disconnect(); - return S7Consts.errIsoInvalidPDU; - } - - byte[] challenge = (getVarSubstreamedRes_challenge.Value as ValueUSIntArray).GetValue(); - - // Calculate challengeResponse [sha1(password) xor challenge] - byte[] challengeResponse; - using (SHA1Managed sha1 = new SHA1Managed()) - { - challengeResponse = sha1.ComputeHash(Encoding.UTF8.GetBytes(password)); - } - if (challengeResponse.Length != challenge.Length) - { - Console.WriteLine("S7CommPlusConnection - Legitimate: challengeResponse.Length != challenge.Length"); - m_client.Disconnect(); - return S7Consts.errIsoInvalidPDU; - } - for (int i = 0; i < challengeResponse.Length; ++i) - { - challengeResponse[i] = (byte)(challengeResponse[i] ^ challenge[i]); - } - - // Send challengeResponse - var setVariableReq = new SetVariableRequest(ProtocolVersion.V2); - setVariableReq.InObjectId = m_SessionId; - setVariableReq.SessionId = m_SessionId; - setVariableReq.Address = Ids.ServerSessionResponse; - setVariableReq.Value = new ValueUSIntArray(challengeResponse); - res = SendS7plusFunctionObject(setVariableReq); - if (res != 0) - { - m_client.Disconnect(); - return res; - } - m_LastError = 0; - WaitForNewS7plusReceived(m_ReadTimeout); - if (m_LastError != 0) - { - m_client.Disconnect(); - return m_LastError; - } - - var setVariableResponse = SetVariableResponse.DeserializeFromPdu(m_ReceivedPDU); - if (setVariableResponse == null) - { - Console.WriteLine("S7CommPlusConnection - Legitimate: setVariableResponse with Error!"); - m_client.Disconnect(); - return S7Consts.errIsoInvalidPDU; - } - // Check if the legitimation attempt was successful - Int16 errorCode = (Int16)setVariableResponse.ReturnValue; - if (errorCode < 0) - { - Console.WriteLine("S7CommPlusConnection - Legitimate: access denied"); - m_client.Disconnect(); - return S7Consts.errCliAccessDenied; - } - - return 0; - } - } -} From c4eb09925ff6e20c7269b913484a79c39b9a8751 Mon Sep 17 00:00:00 2001 From: z Date: Mon, 23 Mar 2026 12:10:57 +0800 Subject: [PATCH 11/11] support for Software controllers as of firmware version V21.9. --- .../Legitimation/Legitimation.cs | 60 ++++++++++++++++--- 1 file changed, 51 insertions(+), 9 deletions(-) diff --git a/src/S7CommPlusDriver/Legitimation/Legitimation.cs b/src/S7CommPlusDriver/Legitimation/Legitimation.cs index 8050fd5..2597d7c 100644 --- a/src/S7CommPlusDriver/Legitimation/Legitimation.cs +++ b/src/S7CommPlusDriver/Legitimation/Legitimation.cs @@ -8,8 +8,9 @@ using System.Text.RegularExpressions; using System.Threading.Tasks; -namespace S7CommPlusDriver { - public partial class S7CommPlusConnection +namespace S7CommPlusDriver +{ + public partial class S7CommPlusConnection { private byte[] omsSecret; @@ -23,23 +24,55 @@ public partial class S7CommPlusConnection /// error code (0 = ok) private int legitimate(ValueStruct serverSession, string password, string username = "") { + // S7-1214C (6ES7 214-1AG40-0XB0) 1;6ES7 214-1AG40-0XB0 ;V4.5 + // S7-1510SP (6ES7 510-1DJ01-0AB0) 1;6ES7 510-1DJ01-0AB0;V2.9 + // S7-1507SF (6ES7 672-7FC01-0YA0) 1;6ES7 672-7FC01-0YA0;V21.9 + // Parse device and firmware version + // doc: https://cache.industry.siemens.com/dl/files/068/109769068/att_1329908/v4/109769068_UsingCertificatesWithTIAPortal_DOC_V2_1_en.pdf + // Certificates in the scope of PG/PC and HMI communication + // Starting with TIA Portal V17, PG / PC and HMI communication is secured with TLS, protecting the data exchanged between + // Field PGs and HMIs with SIMATIC CPUs. + // The CPU families that support Secure PG / HMI communication are: + // • S7 - 1500 controllers as of firmware version V2.9. + // • S7 - 1200 controllers as of firmware version V4.5. + // • Software controllers as of firmware version V21.9. + // • SIMATIC Drive controllers as of firmware version V2.9. + // • PLCSim and PLCSim Advanced Version V4.0. + // HMI components that support Secure PG/ HMI communication, as of image version V17, are: + // • Panels or PCs configured with WinCC Basic, Comfort and Advanced. + // • PCs with WinCC RT Professional. + // • WinCC Unified PCs and Comfort Panels. + // In addition, SINAMICS RT SW, as of version V6.1, and STARTDRIVE, as of version V17, support secure communication string sessionVersionPAOMString = ((ValueWString)serverSession.GetStructElement((uint)Ids.LID_SessionVersionSystemPAOMString)).GetValue(); - Regex reVersions = new Regex("^.*;.*[17]\\s?([52]\\d\\d).+;[VS](\\d\\.\\d)$"); + var reVersions = new Regex( + @"^[^;]*;[^;]*[17]\s?(\d{3}).*;[VS](\d{1,2}\.\d+)$", + RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase + ); Match m = reVersions.Match(sessionVersionPAOMString); if (!m.Success) { Console.WriteLine("S7CommPlusConnection - Legitimate: Could not extract firmware version!"); return S7Consts.errCliFirmwareNotSupported; } - string deviceVersion = m.Groups[1].Value; - string firmwareVersion = m.Groups[2].Value; - int fwVerNo = int.Parse(firmwareVersion.Split('.')[0]) * 100; - fwVerNo += int.Parse(firmwareVersion.Split('.')[1]); + string deviceVersion = m.Groups[1].Value; // e.g., "672" + string firmwareVersion = m.Groups[2].Value; // e.g., "21.9" + + // Compute fwVerNo = major*100 + minor (e.g., "21.9" -> 2109) + int fwVerNo; + { + var parts = firmwareVersion.Split('.'); + if (parts.Length < 2 || !int.TryParse(parts[0], out var major) || !int.TryParse(parts[1], out var minor)) + { + Console.WriteLine($"S7CommPlusConnection - Legitimate: Invalid firmware format: {firmwareVersion}"); + return S7Consts.errCliFirmwareNotSupported; + } + fwVerNo = (major * 100) + minor; + } // Check if we have to use legacy legitimation via the firmware version bool legacyLegitimation = false; - if (deviceVersion.StartsWith("5")) + if (deviceVersion.StartsWith("5")) // S7-1500 (5xx) { if (fwVerNo < 209) { @@ -55,7 +88,7 @@ private int legitimate(ValueStruct serverSession, string password, string userna { legacyLegitimation = false; } - else if (deviceVersion.StartsWith("2")) + else if (deviceVersion.StartsWith("2")) // S7-1200 (2xx) { if (fwVerNo < 403) { @@ -67,6 +100,15 @@ private int legitimate(ValueStruct serverSession, string password, string userna legacyLegitimation = true; } } + else if (deviceVersion.StartsWith("6")) // S7-1507S (6xx) + { + if (fwVerNo < 2109) + { + Console.WriteLine("S7CommPlusConnection - Legitimate: Firmware version is not supported!"); + return S7Consts.errCliFirmwareNotSupported; + } + legacyLegitimation = true; + } else { Console.WriteLine("S7CommPlusConnection - Legitimate: Device version is not supported!");