Skip to content

Commit 98dffb4

Browse files
committed
Add more specific explore methods, and add optional filter operation
1 parent f984d2d commit 98dffb4

File tree

3 files changed

+218
-12
lines changed

3 files changed

+218
-12
lines changed

src/S7CommPlusDriver/Core/ExploreRequest.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ public class ExploreRequest : IS7pSendableObject
3030
public byte ExploreChildsRecursive;
3131
public byte ExploreParents;
3232

33+
public ValueStruct FilterData;
34+
3335
public bool WithIntegrityId = true; // Bei neueren FW (ab 3 oder 4?) immer vorhanden
3436
public UInt32 IntegrityId;
3537

@@ -62,6 +64,21 @@ public int Serialize(Stream buffer)
6264
ret += S7p.EncodeByte(buffer, ExploreChildsRecursive);
6365
ret += S7p.EncodeByte(buffer, 1); // Unbekannt 0 oder 1?
6466
ret += S7p.EncodeByte(buffer, ExploreParents);
67+
68+
if (FilterData != null)
69+
{
70+
ret += S7p.EncodeByte(buffer, 1); // 1 object / value
71+
72+
// TODO / Experimentell:
73+
// Besonderheit hier, oder noch nicht ganz verstanden wie es funktioniert:
74+
// Hier werden bei einer Struct die DatatypeFlags nicht mit in den Stream geschrieben.
75+
// Oder das Byte davor gibt die Flags an? (so wird es aktuell in Wireshark angezeigt)
76+
// Provisorisch wird das eine Byte was (vermutet) die Anzahl der Adressen angab, nicht
77+
// in den Stream geschrieben.
78+
// ret += S7p.EncodeByte(buffer, 0); // 0 address
79+
ret += FilterData.Serialize(buffer);
80+
}
81+
6582
ret += S7p.EncodeByte(buffer, 0); // Number of following Objects / unbekannt
6683

6784
ret += S7p.EncodeUInt32Vlq(buffer, (UInt32)AddressList.Count); // im Wireshark Dissector nur 1 Byte, vermutl. aber ein VLQ

src/S7CommPlusDriver/Core/Ids.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ public static class Ids
3737
public const int ClassTypeInfo = 511;
3838
public const int ObjectOMSTypeInfoContainer = 537;
3939
public const int SystemLimits = 1037;
40+
public const int Filter = 1246;
41+
public const int FilterOperation = 1247;
42+
public const int AddressCount = 1249;
43+
public const int Address = 1250;
44+
public const int FilterValue = 1251;
4045
public const int ObjectQualifier = 1256;
4146
public const int ParentRID = 1257;
4247
public const int CompositionAID = 1258;

src/S7CommPlusDriver/S7CommPlusConnection.cs

Lines changed: 196 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public class S7CommPlusConnection
2929
private MemoryStream m_ReceivedStream;
3030
private bool m_ReceivedNeedMorePdus;
3131
private bool m_NewS7CommPlusReceived;
32-
32+
3333
private UInt32 m_SessionId;
3434
private int m_ReadTimeout = 5000;
3535

@@ -47,7 +47,7 @@ public class S7CommPlusConnection
4747

4848
#region Public Members
4949
public int m_LastError = 0;
50-
50+
5151
#endregion
5252

5353
#region Private Methods
@@ -90,12 +90,13 @@ private void WaitForNewS7plusReceived(int Timeout)
9090
Thread.Sleep(2);
9191
Expired = Environment.TickCount - Elapsed > Timeout;
9292
}
93-
93+
9494
if (Expired)
9595
{
9696
Console.WriteLine("S7CommPlusConnection - WaitForNewS7plusReceived: FEHLER: Timeout!");
9797
m_LastError = S7Consts.errTCPDataReceive;
98-
} else
98+
}
99+
else
99100
{
100101
// Console.WriteLine("S7CommPlusConnection - WaitForNewS7plusReceived: ...neue S7CommPlusPDU vollständig empfangen. Zeit: " + (Environment.TickCount - Elapsed) + " ms.");
101102
}
@@ -339,7 +340,7 @@ private int checkResponseWithIntegrity(object responseObject, UInt16 requestSequ
339340
// Hier kann ein Overflow vorkommen, ist aber erlaubt und Ergebnis wird akzeptiert.
340341
UInt32 reqIntegCheck = (UInt32)requestSequenceNumber + requestIntegrity;
341342
if (responseIntegrity != reqIntegCheck)
342-
{
343+
{
343344
Console.WriteLine(String.Format("checkResponseWithIntegrity: FEHLER! Integrity der Response ({0}) passt nicht zum Request ({1})", responseIntegrity, reqIntegCheck));
344345
// Vorerst nicht als Fehler zurückgeben
345346
}
@@ -377,7 +378,7 @@ public int Connect(string address)
377378
return res;
378379

379380
#region Schritt 1: Unverschlüsselt InitSSL Request / Response
380-
381+
381382
// Ab jetzt den Thread starten
382383
InitSslRequest sslrequest = new InitSslRequest(ProtocolVersion.V1, GetNextSequenceNumber(), 0);
383384
res = SendS7plusFunctionObject(sslrequest);
@@ -454,7 +455,7 @@ public int Connect(string address)
454455
#endregion
455456

456457
#region Schritt 4: SetMultiVariablesRequest / Response
457-
458+
458459
SetMultiVariablesRequest setMultiVariablesRequest = new SetMultiVariablesRequest(ProtocolVersion.V2);
459460
setMultiVariablesRequest.SetSessionSetupData(m_SessionId, serverSession);
460461
setMultiVariablesRequest.SequenceNumber = GetNextSequenceNumber();
@@ -539,7 +540,7 @@ public int ReadValues(List<ItemAddress> addresslist, out List<object> values, ou
539540
getMultiVariablesRequest.AddressList.Clear();
540541
count_perChunk = 0;
541542
while (count_perChunk < m_MaxTagsPerReadRequestLimit && (chunk_startIndex + count_perChunk) < addresslist.Count)
542-
{
543+
{
543544
getMultiVariablesRequest.AddressList.Add(addresslist[chunk_startIndex + count_perChunk]);
544545
count_perChunk++;
545546
}
@@ -580,7 +581,7 @@ public int ReadValues(List<ItemAddress> addresslist, out List<object> values, ou
580581

581582
foreach (var ev in getMultiVariablesResponse.ErrorValues)
582583
{
583-
errors[chunk_startIndex+ (int)ev.Key - 1] = ev.Value;
584+
errors[chunk_startIndex + (int)ev.Key - 1] = ev.Value;
584585
}
585586

586587
chunk_startIndex += count_perChunk;
@@ -733,7 +734,7 @@ public int Browse(out List<VarInfo> varInfoList)
733734
#endregion
734735

735736
#region Alle Datenbausteine auswerten die anschließend gebrowst werden müssen
736-
737+
737738
List<PObject> objList = exploreRes.ResponseObject.GetObjects();
738739

739740
foreach (var ob in objList)
@@ -869,7 +870,7 @@ public int Browse(out List<VarInfo> varInfoList)
869870
else
870871
{
871872
List<PObject> objs = exploreResponse.ResponseObject.GetObjectsByClassId(Ids.ClassTypeInfo);
872-
873+
873874
vars.SetTypeInfoContainerObjects(objs);
874875
vars.BuildTree();
875876
vars.BuildFlatList();
@@ -897,6 +898,189 @@ public class BrowseData
897898
public UInt32 db_block_ti_relid; // Type-Info RID des Datenbausteins
898899
public List<BrowseEntry> variables = new List<BrowseEntry>(); // Variablen des Datenbausteins
899900
};
900-
#endregion
901+
902+
903+
public class DatablockInfo
904+
{
905+
public string db_name; // Name of the datablock
906+
public UInt32 db_number; // Number of the datablock
907+
public UInt32 db_block_relid; // RID of the datablock
908+
public UInt32 db_block_ti_relid; // Type-Info RID of the datablock
909+
};
910+
911+
public int GetListOfDatablocks(out List<DatablockInfo> dbInfoList)
912+
{
913+
int res;
914+
915+
dbInfoList = new List<DatablockInfo>();
916+
917+
var exploreReq = new ExploreRequest(ProtocolVersion.V2);
918+
exploreReq.SessionId = m_SessionId;
919+
exploreReq.SequenceNumber = GetNextSequenceNumber();
920+
exploreReq.ExploreId = Ids.NativeObjects_thePLCProgram_Rid;
921+
exploreReq.ExploreRequestId = Ids.None;
922+
exploreReq.ExploreChildsRecursive = 1;
923+
exploreReq.ExploreParents = 0;
924+
exploreReq.IntegrityId = GetNextIntegrityId();
925+
926+
// Add the attributes we need in the response
927+
exploreReq.AddressList.Add(Ids.ObjectVariableTypeName);
928+
929+
// Set filter on Id for Datablock Class RID. With this filter, we only
930+
// get informations from datablocks, and not other blocks we don't need here.
931+
var filter = new ValueStruct(Ids.Filter);
932+
filter.AddStructElement(Ids.FilterOperation, new ValueDInt(8)); // 8 = InstanceIOf
933+
filter.AddStructElement(Ids.AddressCount, new ValueUDInt(0));
934+
uint[] faddress = new uint[32]; // Unknown, possible dependant on FilterOperation
935+
filter.AddStructElement(Ids.Address, new ValueUDIntArray(faddress));
936+
filter.AddStructElement(Ids.FilterValue, new ValueRID(Ids.DB_Class_Rid));
937+
938+
exploreReq.FilterData = filter;
939+
940+
res = SendS7plusFunctionObject(exploreReq);
941+
if (res != 0)
942+
{
943+
return res;
944+
}
945+
m_LastError = 0;
946+
WaitForNewS7plusReceived(m_ReadTimeout);
947+
if (m_LastError != 0)
948+
{
949+
return m_LastError;
950+
}
951+
952+
var exploreRes = ExploreResponse.DeserializeFromPdu(m_ReceivedStream, true);
953+
res = checkResponseWithIntegrity(exploreRes,
954+
exploreReq.SequenceNumber,
955+
exploreRes.SequenceNumber,
956+
exploreReq.IntegrityId,
957+
exploreRes.IntegrityId);
958+
if (res != 0)
959+
{
960+
return res;
961+
}
962+
963+
// Get the datablock information we want further informations from.
964+
var objList = exploreRes.ResponseObject.GetObjects();
965+
966+
foreach (var ob in objList)
967+
{
968+
// May be this check can be removed, if setting the filter to the DB_Class_Rid is working 100%.
969+
switch (ob.ClassId)
970+
{
971+
case Ids.DB_Class_Rid:
972+
UInt32 relid = ob.RelationId;
973+
UInt32 area = (relid >> 16);
974+
UInt32 num = relid & 0xffff;
975+
if (area == 0x8a0e)
976+
{
977+
var name = (ValueWString)(ob.GetAttribute(Ids.ObjectVariableTypeName));
978+
DatablockInfo data = new DatablockInfo();
979+
data.db_block_relid = relid;
980+
data.db_name = name.GetValue();
981+
data.db_number = num;
982+
dbInfoList.Add(data);
983+
}
984+
break;
985+
}
986+
}
987+
988+
// Get the TypeInfo RID to RelId from the first response
989+
990+
// With LID=1 we get the RID back. With this number we can explore further
991+
// informations of this datablock.
992+
// This is neccessary, because informations about instance DBs (e.g. TON) you
993+
// don't get by the RID of the DB, instead of exploring the TON Type RID.
994+
var readlist = new List<ItemAddress>();
995+
var values = new List<object>();
996+
var errors = new List<UInt64>();
997+
998+
foreach (var data in dbInfoList)
999+
{
1000+
if (data.db_number > 0)
1001+
{
1002+
// Insert the address
1003+
var adr1 = new ItemAddress();
1004+
adr1.AccessArea = data.db_block_relid;
1005+
adr1.AccessSubArea = Ids.DB_ValueActual;
1006+
adr1.LID.Add(1);
1007+
readlist.Add(adr1);
1008+
}
1009+
}
1010+
res = ReadValues(readlist, out values, out errors);
1011+
if (res != 0)
1012+
{
1013+
return res;
1014+
}
1015+
1016+
// Insert response data into the list
1017+
for (int i = 0; i < values.Count; i++)
1018+
{
1019+
if (errors[i] == 0)
1020+
{
1021+
var rid = (ValueRID)values[i];
1022+
var data = dbInfoList[i];
1023+
data.db_block_ti_relid = rid.GetValue();
1024+
dbInfoList[i] = data;
1025+
}
1026+
else
1027+
{
1028+
// On error, set relid=0, which is then removed in the next step
1029+
// Should we report this for the user?
1030+
var data = dbInfoList[i];
1031+
data.db_block_ti_relid = 0;
1032+
dbInfoList[i] = data;
1033+
}
1034+
}
1035+
1036+
// Remove elements with db_block_ti_relid == 0.
1037+
// This can occur on datablocks which are only in load memory, and can't be explored.
1038+
dbInfoList.RemoveAll(item => item.db_block_ti_relid == 0);
1039+
1040+
return 0;
1041+
}
1042+
1043+
1044+
public int GetTypeInformation(uint exploreId, out List<PObject> objList)
1045+
{
1046+
int res;
1047+
objList = new List<PObject>();
1048+
1049+
var exploreReq = new ExploreRequest(ProtocolVersion.V2);
1050+
exploreReq.SessionId = m_SessionId;
1051+
exploreReq.SequenceNumber = GetNextSequenceNumber();
1052+
exploreReq.IntegrityId = GetNextIntegrityId();
1053+
exploreReq.ExploreId = exploreId;
1054+
exploreReq.ExploreRequestId = Ids.None;
1055+
exploreReq.ExploreChildsRecursive = 1;
1056+
exploreReq.ExploreParents = 0;
1057+
1058+
res = SendS7plusFunctionObject(exploreReq);
1059+
if (res != 0)
1060+
{
1061+
return res;
1062+
}
1063+
m_LastError = 0;
1064+
WaitForNewS7plusReceived(m_ReadTimeout);
1065+
if (m_LastError != 0)
1066+
{
1067+
return m_LastError;
1068+
}
1069+
1070+
var exploreRes = ExploreResponse.DeserializeFromPdu(m_ReceivedStream, true);
1071+
res = checkResponseWithIntegrity(exploreRes,
1072+
exploreReq.SequenceNumber,
1073+
exploreRes.SequenceNumber,
1074+
exploreReq.IntegrityId,
1075+
exploreRes.IntegrityId);
1076+
if (res != 0)
1077+
{
1078+
return res;
1079+
}
1080+
objList.Add(exploreRes.ResponseObject);
1081+
1082+
return 0;
1083+
}
9011084
}
1085+
#endregion
9021086
}

0 commit comments

Comments
 (0)