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/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/PlcTags.cs b/src/S7CommPlusDriver/ClientApi/PlcTags.cs index cfaa5ea..787cae6 100644 --- a/src/S7CommPlusDriver/ClientApi/PlcTags.cs +++ b/src/S7CommPlusDriver/ClientApi/PlcTags.cs @@ -66,25 +66,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); @@ -95,9 +109,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); @@ -115,6 +133,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: @@ -126,12 +146,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: diff --git a/src/S7CommPlusDriver/Core/Ids.cs b/src/S7CommPlusDriver/Core/Ids.cs index 610ca0d..0bca5fb 100644 --- a/src/S7CommPlusDriver/Core/Ids.cs +++ b/src/S7CommPlusDriver/Core/Ids.cs @@ -1,85 +1,86 @@ -#region License -/****************************************************************************** - * S7CommPlusDriver - * - * Copyright (C) 2023 Thomas Wiens, th.wiens@gmx.de - * - * This file is part of S7CommPlusDriver. - * - * S7CommPlusDriver is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - /****************************************************************************/ -#endregion - -namespace S7CommPlusDriver -{ - public static class Ids - { +#region License +/****************************************************************************** + * S7CommPlusDriver + * + * Copyright (C) 2023 Thomas Wiens, th.wiens@gmx.de + * + * This file is part of S7CommPlusDriver. + * + * S7CommPlusDriver is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + /****************************************************************************/ +#endregion + +namespace S7CommPlusDriver +{ + public static class Ids + { public const int None = 0; - public const int NativeObjects_theASRoot_Rid = 1; + public const int NativeObjects_theASRoot_Rid = 1; public const int NativeObjects_thePLCProgram_Rid = 3; + public const int NativeObjects_theAlarmSubsystem_Rid = 8; public const int NativeObjects_theCPUProxy_Rid = 49; - public const int NativeObjects_theCPUCommon_Rid = 50; - public const int NativeObjects_theCPUexecUnit_Rid = 52; - public const int NativeObjects_theIArea_Rid = 80; - public const int NativeObjects_theQArea_Rid = 81; - public const int NativeObjects_theMArea_Rid = 82; - public const int NativeObjects_theS7Counters_Rid = 83; - public const int NativeObjects_theS7Timers_Rid = 84; - public const int ObjectRoot = 201; - public const int GetNewRIDOnServer = 211; - public const int ObjectVariableTypeParentObject = 229; - public const int ObjectVariableTypeName = 233; - public const int ClassSubscriptions = 255; - public const int ClassServerSessionContainer = 284; - public const int ObjectServerSessionContainer = 285; - public const int ClassServerSession = 287; - public const int ObjectNullServerSession = 288; - public const int ServerSessionClientRID = 300; - public const int ServerSessionRequest = 303; - public const int ServerSessionResponse = 304; - public const int ServerSessionVersion = 306; - public const int LID_SessionVersionSystemPAOMString = 319; - public const int ClassTypeInfo = 511; - public const int ClassOMSTypeInfoContainer = 534; - public const int ObjectOMSTypeInfoContainer = 537; - public const int TextLibraryClassRID = 606; - public const int TextLibraryOffsetArea = 608; - public const int TextLibraryStringArea = 609; - public const int ClassSubscription = 1001; - public const int SubscriptionMissedSendings = 1002; - public const int SubscriptionSubsystemError = 1003; - public const int SubscriptionReferenceTriggerAndTransmitMode = 1005; - public const int SystemLimits = 1037; - public const int SubscriptionRouteMode = 1040; - public const int SubscriptionActive = 1041; - public const int Legitimate = 1846; - public const int SubscriptionReferenceList = 1048; - public const int SubscriptionCycleTime = 1049; - public const int SubscriptionDelayTime = 1050; - public const int SubscriptionDisabled = 1051; - public const int SubscriptionCount = 1052; - public const int SubscriptionCreditLimit = 1053; - public const int SubscriptionTicks = 1054; - public const int FreeItems = 1081; - public const int SubscriptionFunctionClassId = 1082; - public const int Filter = 1246; - public const int FilterOperation = 1247; - public const int AddressCount = 1249; - public const int Address = 1250; - public const int FilterValue = 1251; - public const int ObjectQualifier = 1256; - public const int ParentRID = 1257; - public const int CompositionAID = 1258; - public const int KeyQualifier = 1259; - public const int TI_TComSize = 1502; - public const int EffectiveProtectionLevel = 1842; - public const int ActiveProtectionLevel = 1843; - public const int CPUexecUnit_operatingStateReq = 2167; + public const int NativeObjects_theCPUCommon_Rid = 50; + public const int NativeObjects_theCPUexecUnit_Rid = 52; + public const int NativeObjects_theIArea_Rid = 80; + public const int NativeObjects_theQArea_Rid = 81; + public const int NativeObjects_theMArea_Rid = 82; + public const int NativeObjects_theS7Counters_Rid = 83; + public const int NativeObjects_theS7Timers_Rid = 84; + public const int ObjectRoot = 201; + public const int GetNewRIDOnServer = 211; + public const int ObjectVariableTypeParentObject = 229; + public const int ObjectVariableTypeName = 233; + public const int ClassSubscriptions = 255; + public const int ClassServerSessionContainer = 284; + public const int ObjectServerSessionContainer = 285; + public const int ClassServerSession = 287; + public const int ObjectNullServerSession = 288; + public const int ServerSessionClientRID = 300; + public const int ServerSessionRequest = 303; + public const int ServerSessionResponse = 304; + public const int ServerSessionVersion = 306; + public const int LID_SessionVersionSystemPAOMString = 319; + public const int ClassTypeInfo = 511; + public const int ClassOMSTypeInfoContainer = 534; + public const int ObjectOMSTypeInfoContainer = 537; + public const int TextLibraryClassRID = 606; + public const int TextLibraryOffsetArea = 608; + public const int TextLibraryStringArea = 609; + public const int ClassSubscription = 1001; + public const int SubscriptionMissedSendings = 1002; + public const int SubscriptionSubsystemError = 1003; + public const int SubscriptionReferenceTriggerAndTransmitMode = 1005; + public const int SystemLimits = 1037; + public const int SubscriptionRouteMode = 1040; + public const int SubscriptionActive = 1041; + public const int Legitimate = 1846; + public const int SubscriptionReferenceList = 1048; + public const int SubscriptionCycleTime = 1049; + public const int SubscriptionDelayTime = 1050; + public const int SubscriptionDisabled = 1051; + public const int SubscriptionCount = 1052; + public const int SubscriptionCreditLimit = 1053; + public const int SubscriptionTicks = 1054; + public const int FreeItems = 1081; + public const int SubscriptionFunctionClassId = 1082; + public const int Filter = 1246; + public const int FilterOperation = 1247; + public const int AddressCount = 1249; + public const int Address = 1250; + public const int FilterValue = 1251; + public const int ObjectQualifier = 1256; + public const int ParentRID = 1257; + public const int CompositionAID = 1258; + public const int KeyQualifier = 1259; + public const int TI_TComSize = 1502; + public const int EffectiveProtectionLevel = 1842; + public const int ActiveProtectionLevel = 1843; + public const int CPUexecUnit_operatingStateReq = 2167; public const int ASRoot_ItsFolders = 2468; - public const int PLCProgram_Class_Rid = 2520; + public const int PLCProgram_Class_Rid = 2520; public const int Block_BlockNumber = 2521; public const int Block_AutoNumbering = 2522; public const int Block_BlockLanguage = 2523; @@ -96,13 +97,13 @@ public static class Ids public const int Block_FunctionalSignature = 7589; public const int Block_AdditionalMAC = 7831; public const int Block_FailsafeBlockInfo = 7843; - public const int Block_FailsafeIFRHash = 7955; - public const int ASObjectES_IdentES = 2449; + public const int Block_FailsafeIFRHash = 7955; + public const int ASObjectES_IdentES = 2449; public const int Block_BodyDescription = 2533; - public const int DataInterface_InterfaceDescription = 2544; - public const int DataInterface_LineComments = 2546; - public const int DB_ValueInitial = 2548; - public const int DB_ValueActual = 2550; + public const int DataInterface_InterfaceDescription = 2544; + public const int DataInterface_LineComments = 2546; + public const int DB_ValueInitial = 2548; + public const int DB_ValueActual = 2550; public const int DB_InitialChanged = 2551; public const int DB_Class_Rid = 2574; public const int FB_Class_Rid = 2578; @@ -113,72 +114,73 @@ public static class Ids public const int FunctionalObject_intRefData = 2583; public const int FunctionalObject_NetworkComments = 2584; public const int FunctionalObject_NetworkTitles = 2585; - public const int FunctionalObject_CalleeList = 2586; - public const int FunctionalObject_InterfaceSignature = 2587; - public const int FunctionalObject_DisplayInfo = 2588; - public const int FunctionalObject_DebugInfo = 2589; - public const int FunctionalObject_LocalErrorhandling = 2590; - public const int FunctionalObject_LongConstants = 2591; + public const int FunctionalObject_CalleeList = 2586; + public const int FunctionalObject_InterfaceSignature = 2587; + public const int FunctionalObject_DisplayInfo = 2588; + public const int FunctionalObject_DebugInfo = 2589; + public const int FunctionalObject_LocalErrorhandling = 2590; + public const int FunctionalObject_LongConstants = 2591; public const int FunctionalObject_Class_Rid = 2592; - public const int OB_StartInfoType = 2607; - public const int OB_Class_Rid = 2610; - public const int AlarmSubscriptionRef_AlarmDomain = 2659; - public const int AlarmSubscriptionRef_itsAlarmSubsystem = 2660; - public const int AlarmSubscriptionRef_Class_Rid = 2662; - public const int DAI_CPUAlarmID = 2670; - public const int DAI_AllStatesInfo = 2671; - public const int DAI_AlarmDomain = 2672; - public const int DAI_Coming = 2673; - public const int DAI_Going = 2677; - public const int DAI_Class_Rid = 2681; - public const int DAI_AlarmTexts_Rid = 2715; - public const int AS_CGS_AllStatesInfo = 3474; - public const int AS_CGS_Timestamp = 3475; - public const int AS_CGS_AssociatedValues = 3476; - public const int AS_CGS_AckTimestamp = 3646; - public const int ControllerArea_ValueInitial = 3735; - public const int ControllerArea_ValueActual = 3736; + public const int OB_StartInfoType = 2607; + public const int OB_Class_Rid = 2610; + 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; + public const int DAI_Coming = 2673; + public const int DAI_Going = 2677; + public const int DAI_Class_Rid = 2681; + public const int DAI_AlarmTexts_Rid = 2715; + public const int AS_CGS_AllStatesInfo = 3474; + public const int AS_CGS_Timestamp = 3475; + public const int AS_CGS_AssociatedValues = 3476; + public const int AS_CGS_AckTimestamp = 3646; + public const int ControllerArea_ValueInitial = 3735; + public const int ControllerArea_ValueActual = 3736; public const int ControllerArea_RuntimeModified = 3737; - public const int UDT_Class_Rid = 3932; + public const int UDT_Class_Rid = 3932; public const int DAI_MessageType = 4079; - public const int ConstantsGlobal_Symbolics = 4275; - public const int ASObjectES_Comment = 4288; - public const int AlarmSubscriptionRef_AlarmDomain2 = 7731; - public const int DAI_HmiInfo = 7813; - public const int MultipleSTAI_Class_Rid = 7854; - public const int MultipleSTAI_STAIs = 7859; - public const int DAI_SequenceCounter = 7917; - public const int AlarmSubscriptionRef_AlarmTextLanguages_Rid = 8181; - public const int AlarmSubscriptionRef_SendAlarmTexts_Rid = 8173; - public const int ReturnValue = 40305; - public const int LID_LegitimationPayloadStruct = 40400; - public const int LID_LegitimationPayloadType = 40401; - public const int LID_LegitimationPayloadUsername = 40402; - public const int LID_LegitimationPayloadPassword = 40403; - - public const uint ReleaseMngmtRoot_Rid = 2303328256; - - public const uint Constants = 0x8a110000; - - public const int TI_BOOL = 0x02000000 + 1; - public const int TI_BYTE = 0x02000000 + 2; - public const int TI_CHAR = 0x02000000 + 3; - public const int TI_WORD = 0x02000000 + 4; - public const int TI_INT = 0x02000000 + 5; - public const int TI_DWORD = 0x02000000 + 6; - public const int TI_DINT = 0x02000000 + 7; - public const int TI_REAL = 0x02000000 + 8; - public const int TI_STRING = 0x02000000 + 19; - public const int TI_LREAL = 0x02000000 + 48; - public const int TI_USINT = 0x02000000 + 52; - public const int TI_UINT = 0x02000000 + 53; - public const int TI_UDINT = 0x02000000 + 54; - public const int TI_SINT = 0x02000000 + 55; - public const int TI_WCHAR = 0x02000000 + 61; - public const int TI_WSTRING = 0x02000000 + 62; - public const int TI_STRING_START = 0x020a0000; // Start for String[0] - public const int TI_STRING_END = 0x020affff; // End (String[65535]) - public const int TI_WSTRING_START = 0x020b0000; // Start for WString[0] - public const int TI_WSTRING_END = 0x020bffff; // End (WString[65535]) - } -} + public const int ConstantsGlobal_Symbolics = 4275; + public const int ASObjectES_Comment = 4288; + public const int AlarmSubscriptionRef_AlarmDomain2 = 7731; + public const int DAI_HmiInfo = 7813; + public const int MultipleSTAI_Class_Rid = 7854; + public const int MultipleSTAI_STAIs = 7859; + public const int DAI_SequenceCounter = 7917; + public const int AlarmSubscriptionRef_AlarmTextLanguages_Rid = 8181; + public const int AlarmSubscriptionRef_SendAlarmTexts_Rid = 8173; + public const int ReturnValue = 40305; + public const int LID_LegitimationPayloadStruct = 40400; + public const int LID_LegitimationPayloadType = 40401; + public const int LID_LegitimationPayloadUsername = 40402; + public const int LID_LegitimationPayloadPassword = 40403; + + public const uint ReleaseMngmtRoot_Rid = 2303328256; + + public const uint Constants = 0x8a110000; + + public const int TI_BOOL = 0x02000000 + 1; + public const int TI_BYTE = 0x02000000 + 2; + public const int TI_CHAR = 0x02000000 + 3; + public const int TI_WORD = 0x02000000 + 4; + public const int TI_INT = 0x02000000 + 5; + public const int TI_DWORD = 0x02000000 + 6; + public const int TI_DINT = 0x02000000 + 7; + public const int TI_REAL = 0x02000000 + 8; + public const int TI_STRING = 0x02000000 + 19; + public const int TI_LREAL = 0x02000000 + 48; + public const int TI_USINT = 0x02000000 + 52; + public const int TI_UINT = 0x02000000 + 53; + public const int TI_UDINT = 0x02000000 + 54; + public const int TI_SINT = 0x02000000 + 55; + public const int TI_WCHAR = 0x02000000 + 61; + public const int TI_WSTRING = 0x02000000 + 62; + public const int TI_STRING_START = 0x020a0000; // Start for String[0] + public const int TI_STRING_END = 0x020affff; // End (String[65535]) + public const int TI_WSTRING_START = 0x020b0000; // Start for WString[0] + public const int TI_WSTRING_END = 0x020bffff; // End (WString[65535]) + } +} diff --git a/src/S7CommPlusDriver/Legitimation/Legitimation.cs b/src/S7CommPlusDriver/Legitimation/Legitimation.cs index 41e97c7..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) { @@ -51,7 +84,11 @@ private int legitimate(ValueStruct serverSession, string password, string userna legacyLegitimation = true; } } - else if (deviceVersion.StartsWith("2")) + 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")) // S7-1200 (2xx) { if (fwVerNo < 403) { @@ -63,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!"); diff --git a/src/S7CommPlusDriver/S7CommPlusConnection.cs b/src/S7CommPlusDriver/S7CommPlusConnection.cs index 25737f8..853bfc8 100644 --- a/src/S7CommPlusDriver/S7CommPlusConnection.cs +++ b/src/S7CommPlusDriver/S7CommPlusConnection.cs @@ -1,4 +1,4 @@ -#region License +#region License /****************************************************************************** * S7CommPlusDriver * @@ -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); } }