7070import org .apache .plc4x .java .s7 .events .S7UserEvent ;
7171import org .apache .plc4x .java .s7 .readwrite .utils .S7PlcSubscriptionRequest ;
7272
73+ import javax .xml .crypto .Data ;
74+
7375/**
7476 * The S7 Protocol states that there can not be more then {min(maxAmqCaller, maxAmqCallee} "ongoing" requests.
7577 * So we need to limit those.
@@ -652,9 +654,9 @@ private PlcSubscriptionResponse decodeEventSubscriptionResponse(String strTagNam
652654 }
653655 // This seems to be the case if we're trying to do a subscription on a device that doesn't support that.
654656 else if ((errorClass == 0 ) && (errorCode == (short ) 0x8104 )) {
655- logger .warn ("Got an error response from the PLC. This particular response code usually indicates " +
657+ logger .warn ("Got an error response from the PLC. Error Class: {}, Error Code {}. This particular response code usually indicates " +
656658 "that a given service is not implemented on the PLC. Most probably you tried to subscribe to " +
657- "data on a PLC that doesn't support subsciptions (S7-1200 or S7-1500)" ,
659+ "data on a PLC that doesn't support subscriptions (S7-1200 or S7-1500)" ,
658660 errorClass , errorCode );
659661 for (String tagName : plcSubscriptionRequest .getTagNames ()) {
660662 values .put (tagName , new ResponseItem <>(PlcResponseCode .UNSUPPORTED , null ));
@@ -1287,42 +1289,82 @@ private CompletableFuture<S7Message> performClkSetRequest(DefaultPlcWriteRequest
12871289 private CompletableFuture <S7Message > performVarLengthStringReadRequest (DefaultPlcReadRequest request ) {
12881290 CompletableFuture <S7Message > future = new CompletableFuture <>();
12891291
1290- // Resolve the lengths of all var-length string fields in the request.
1291- CompletableFuture <Map <S7StringVarLengthTag , StringSizes >> stringSizesFuture = getStringSizes (request );
1292- stringSizesFuture .whenComplete ((s7StringVarLengthTagStringSizesMap , throwable ) -> {
1293- if (throwable != null ) {
1294- future .completeExceptionally (new PlcProtocolException ("Error resolving string sizes" , throwable ));
1295- } else {
1296- // Create an alternative list of request items, where all var-length string tags are replaced with
1297- // fixed-length string tags using the string length returned by the previous request.
1298- LinkedHashMap <String , PlcTag > updatedRequestItems = new LinkedHashMap <>(request .getNumberOfTags ());
1299- for (String tagName : request .getTagNames ()) {
1300- PlcTag tag = request .getTag (tagName );
1301- if (tag instanceof S7StringVarLengthTag ) {
1302- S7StringVarLengthTag varLengthTag = (S7StringVarLengthTag ) tag ;
1303- int stringLength = s7StringVarLengthTagStringSizesMap .get (varLengthTag ).getCurLength ();
1304- S7StringFixedLengthTag newTag = new S7StringFixedLengthTag (varLengthTag .getDataType (), varLengthTag .getMemoryArea (),
1305- varLengthTag .getBlockNumber (), varLengthTag .getByteOffset (), varLengthTag .getBitOffset (),
1306- varLengthTag .getNumberOfElements (), stringLength );
1307- updatedRequestItems .put (tagName , newTag );
1308- } else {
1309- updatedRequestItems .put (tagName , tag );
1310- }
1292+ // Replace the var-length string fields with requests to read the
1293+ // length information instead of the string content.
1294+ int numVarLengthStrings = 0 ;
1295+ LinkedHashMap <String , PlcTag > updatedRequestItems = new LinkedHashMap <>(request .getNumberOfTags ());
1296+ for (String tagName : request .getTagNames ()) {
1297+ S7Tag s7tag = (S7Tag ) request .getTag (tagName );
1298+ if (s7tag instanceof S7StringVarLengthTag ) {
1299+ if (s7tag .getDataType () == TransportSize .STRING ) {
1300+ updatedRequestItems .put (tagName , new S7Tag (TransportSize .BYTE , s7tag .getMemoryArea (), s7tag .getBlockNumber (), s7tag .getByteOffset (), s7tag .getBitOffset (), 2 ));
1301+ numVarLengthStrings ++;
1302+ } else if (s7tag .getDataType () == TransportSize .WSTRING ) {
1303+ updatedRequestItems .put (tagName , new S7Tag (TransportSize .BYTE , s7tag .getMemoryArea (), s7tag .getBlockNumber (), s7tag .getByteOffset (), s7tag .getBitOffset (), 4 ));
1304+ numVarLengthStrings ++;
13111305 }
1306+ } else {
1307+ updatedRequestItems .put (tagName , s7tag );
1308+ }
1309+ }
13121310
1313- // Use the normal functionality to execute the read request.
1314- // TODO: Here technically the request object in the response will not match the original request.
1315- CompletableFuture <S7Message > s7MessageCompletableFuture = performOrdinaryReadRequest (new DefaultPlcReadRequest (request .getReader (), updatedRequestItems ));
1316- s7MessageCompletableFuture .whenComplete ((s7Message , throwable1 ) -> {
1317- if (throwable1 != null ) {
1318- future .completeExceptionally (throwable1 );
1319- } else {
1320- future .complete (s7Message );
1311+ CompletableFuture <S7Message > s7MessageCompletableFuture = performOrdinaryReadRequest (new DefaultPlcReadRequest (request .getReader (), updatedRequestItems ));
1312+ int finalNumVarLengthStrings = numVarLengthStrings ;
1313+ s7MessageCompletableFuture .whenComplete ((s7Message , throwable1 ) -> {
1314+ if (throwable1 != null ) {
1315+ future .completeExceptionally (throwable1 );
1316+ return ;
1317+ }
1318+ // Collect the responses for the var-length strings and read them separately.
1319+ LinkedHashMap <String , PlcTag > varLengthStringTags = new LinkedHashMap <>(finalNumVarLengthStrings );
1320+ int curItem = 0 ;
1321+ for (String tagName : request .getTagNames ()) {
1322+ S7Tag s7tag = (S7Tag ) request .getTag (tagName );
1323+ if (s7tag instanceof S7StringVarLengthTag ) {
1324+ S7VarPayloadDataItem s7VarPayloadDataItem = ((S7PayloadReadVarResponse ) s7Message .getPayload ()).getItems ().get (curItem );
1325+ // Simply ignore processing var-lenght strings that are not ok
1326+ if (s7VarPayloadDataItem .getReturnCode () == DataTransportErrorCode .OK ) {
1327+ ReadBuffer rb = new ReadBufferByteBased (s7VarPayloadDataItem .getData ());
1328+ try {
1329+ if (s7tag .getDataType () == TransportSize .STRING ) {
1330+ rb .readShort (8 );
1331+ int stringLength = rb .readShort (8 );
1332+ varLengthStringTags .put (tagName , new S7StringFixedLengthTag (TransportSize .STRING , s7tag .getMemoryArea (), s7tag .getBlockNumber (), s7tag .getByteOffset (), s7tag .getBitOffset (), 1 , stringLength ));
1333+ } else if (s7tag .getDataType () == TransportSize .WSTRING ) {
1334+ rb .readInt (16 );
1335+ int stringLength = rb .readInt (16 );
1336+ varLengthStringTags .put (tagName , new S7StringFixedLengthTag (TransportSize .WSTRING , s7tag .getMemoryArea (), s7tag .getBlockNumber (), s7tag .getByteOffset (), s7tag .getBitOffset (), 1 , stringLength ));
1337+ }
1338+ } catch (Exception e ) {
1339+ logger .warn ("Error parsing string size for tag {}" , tagName , e );
1340+ }
13211341 }
1322- });
1342+ }
1343+ curItem ++;
13231344 }
1345+ // TODO: Technically we would need to let this go through the optimizer in order to split things up.
1346+ // For this we need access to the PlcReader instance of this driver
1347+ CompletableFuture <S7Message > readStringsCompletableFuture = performOrdinaryReadRequest (new DefaultPlcReadRequest (request .getReader (), varLengthStringTags ));
1348+ readStringsCompletableFuture .whenComplete ((s7StringMessage , throwable2 ) -> {
1349+ // Build a new S7Message that replaces the var-length string items of the previous request with the responses of this response.
1350+ int curInitialItem = 0 ;
1351+ int curVarLengthStringItem = 0 ;
1352+ List <S7VarPayloadDataItem > varLengthStringItems = new ArrayList <>(request .getNumberOfTags ());
1353+ for (String tagName : request .getTagNames ()) {
1354+ S7Tag s7tag = (S7Tag ) request .getTag (tagName );
1355+ S7VarPayloadDataItem curResultItem = ((S7PayloadReadVarResponse ) s7Message .getPayload ()).getItems ().get (curInitialItem );
1356+ if (s7tag instanceof S7StringVarLengthTag ) {
1357+ if (curResultItem .getReturnCode () == DataTransportErrorCode .OK ) {
1358+ curResultItem = ((S7PayloadReadVarResponse ) s7StringMessage .getPayload ()).getItems ().get (curVarLengthStringItem );
1359+ curVarLengthStringItem ++;
1360+ }
1361+ }
1362+ varLengthStringItems .add (curResultItem );
1363+ curInitialItem ++;
1364+ }
1365+ future .complete (new S7MessageResponse (s7Message .getTpduReference (), s7Message .getParameter (), new S7PayloadReadVarResponse (varLengthStringItems ), (short ) 0 , (short ) 0 ));
1366+ });
13241367 });
1325-
13261368 return future ;
13271369 }
13281370
@@ -1478,18 +1520,15 @@ private CompletableFuture<S7Message> sendInternal(S7Message request) {
14781520 * This method is called when there is no handler for the message.
14791521 * By default it must correspond to asynchronous events, which if so,
14801522 * must be transferred to the event queue.
1481- *
14821523 * The event's own information is encapsulated in the parameters and payload
14831524 * field. From this it is abstracted to the corresponding event model.
1484- *
14851525 * 01. S7ModeEvent:
14861526 * 02. S7UserEvent:
14871527 * 03. S7SysEvent:
14881528 * 04. S7AlarmEvent
14891529 * 05. S7CyclicEvent:
14901530 * 06. S7CyclicEvent:
1491- *
1492- * TODO: Use mspec to generate types that allow better interpretation of
1531+ * TODO: Use mspec to generate types that allow better interpretation of
14931532 * the code using "instanceof".
14941533 */
14951534 @ Override
@@ -1563,7 +1602,7 @@ protected void decode(ConversationContext<TPKTPacket> context, TPKTPacket msg) t
15631602
15641603 if (cycChangeValueEvents .containsKey (parameterItem .getSequenceNumber ())){
15651604 S7CyclicEvent lastCycEvent = cycChangeValueEvents .get (parameterItem .getSequenceNumber ());
1566- if (cycEvent .equals (lastCycEvent ) == false ) {
1605+ if (! cycEvent .equals (lastCycEvent ) ) {
15671606 cycChangeValueEvents .replace (parameterItem .getSequenceNumber (), cycEvent );
15681607 eventQueue .add (cycEvent );
15691608 }
@@ -1962,25 +2001,29 @@ private S7VarPayloadDataItem serializePlcValue(S7Tag tag, PlcValue plcValue) {
19622001 DataTransportSize transportSize = tag .getDataType ().getDataTransportSize ();
19632002 int stringLength = (tag instanceof S7StringFixedLengthTag ) ? ((S7StringFixedLengthTag ) tag ).getStringLength () : 254 ;
19642003 ByteBuffer byteBuffer = null ;
1965- for (int i = 0 ; i < tag .getNumberOfElements (); i ++) {
1966- int lengthInBits = DataItem .getLengthInBits (plcValue .getIndex (i ), tag .getDataType ().getDataProtocolId (), s7DriverContext .getControllerType (), stringLength );
1967-
1968- // Cap the length of the string with the maximum allowed size.
1969- if (tag .getDataType () == TransportSize .STRING ) {
1970- lengthInBits = Math .min (lengthInBits , (stringLength * 8 ) + 16 );
1971- } else if (tag .getDataType () == TransportSize .WSTRING ) {
1972- lengthInBits = Math .min (lengthInBits , (stringLength * 16 ) + 32 );
1973- } else if (tag .getDataType () == TransportSize .S5TIME ) {
1974- lengthInBits = lengthInBits * 8 ;
1975- }
1976- final WriteBufferByteBased writeBuffer = new WriteBufferByteBased ((int ) Math .ceil (((float ) lengthInBits ) / 8.0f ));
1977- DataItem .staticSerialize (writeBuffer , plcValue .getIndex (i ), tag .getDataType ().getDataProtocolId (), s7DriverContext .getControllerType (), stringLength );
1978- // Allocate enough space for all items.
1979- if (byteBuffer == null ) {
1980- // TODO: This logic will cause problems when reading arrays of strings.
1981- byteBuffer = ByteBuffer .allocate (writeBuffer .getBytes ().length * tag .getNumberOfElements ());
2004+ if ((tag .getDataType () == TransportSize .BYTE ) && (tag .getNumberOfElements () > 1 )) {
2005+ byteBuffer = ByteBuffer .allocate (tag .getNumberOfElements ());
2006+ byteBuffer .put (plcValue .getRaw ());
2007+ } else {
2008+ for (int i = 0 ; i < tag .getNumberOfElements (); i ++) {
2009+ int lengthInBits = DataItem .getLengthInBits (plcValue .getIndex (i ), tag .getDataType ().getDataProtocolId (), s7DriverContext .getControllerType (), stringLength );
2010+ // Cap the length of the string with the maximum allowed size.
2011+ if (tag .getDataType () == TransportSize .STRING ) {
2012+ lengthInBits = Math .min (lengthInBits , (stringLength * 8 ) + 16 );
2013+ } else if (tag .getDataType () == TransportSize .WSTRING ) {
2014+ lengthInBits = Math .min (lengthInBits , (stringLength * 16 ) + 32 );
2015+ } else if (tag .getDataType () == TransportSize .S5TIME ) {
2016+ lengthInBits = lengthInBits * 8 ;
2017+ }
2018+ final WriteBufferByteBased writeBuffer = new WriteBufferByteBased ((int ) Math .ceil (((float ) lengthInBits ) / 8.0f ));
2019+ DataItem .staticSerialize (writeBuffer , plcValue .getIndex (i ), tag .getDataType ().getDataProtocolId (), s7DriverContext .getControllerType (), stringLength );
2020+ // Allocate enough space for all items.
2021+ if (byteBuffer == null ) {
2022+ // TODO: This logic will cause problems when reading arrays of strings.
2023+ byteBuffer = ByteBuffer .allocate (writeBuffer .getBytes ().length * tag .getNumberOfElements ());
2024+ }
2025+ byteBuffer .put (writeBuffer .getBytes ());
19822026 }
1983- byteBuffer .put (writeBuffer .getBytes ());
19842027 }
19852028 if (byteBuffer != null ) {
19862029 byte [] data = byteBuffer .array ();
@@ -2040,7 +2083,8 @@ private PlcResponseCode decodeResponseCode(DataTransportErrorCode dataTransportE
20402083 case OK :
20412084 return PlcResponseCode .OK ;
20422085 case NOT_FOUND :
2043- return PlcResponseCode .NOT_FOUND ;
2086+ // It seems the S7 devices return NOT_FOUND if for example we try to access a DB number
2087+ // which doesn't exist. In other protocols we all map that to invalid address.
20442088 case INVALID_ADDRESS :
20452089 return PlcResponseCode .INVALID_ADDRESS ;
20462090 case DATA_TYPE_NOT_SUPPORTED :
@@ -2309,6 +2353,12 @@ else if (varLengthStringTag.getDataType() == TransportSize.WSTRING) {
23092353 int curItemIndex = 0 ;
23102354 for (S7StringVarLengthTag varLengthStringTag : varLengthStringTags ) {
23112355 S7VarPayloadDataItem s7VarPayloadDataItem = getLengthsResponse .getItems ().get (curItemIndex );
2356+ // Something went wrong.
2357+ // We simply don't save the length information, and then we'll treat it as INVALID_ADDRESS later on
2358+ // in the code.
2359+ if (s7VarPayloadDataItem .getReturnCode () != DataTransportErrorCode .OK ) {
2360+ continue ;
2361+ }
23122362 ReadBufferByteBased readBuffer = new ReadBufferByteBased (s7VarPayloadDataItem .getData ());
23132363 try {
23142364 if (varLengthStringTag .getDataType () == TransportSize .STRING ) {
@@ -2323,7 +2373,7 @@ else if (varLengthStringTag.getDataType() == TransportSize.WSTRING) {
23232373 throw new PlcInvalidTagException ("Only STRING and WSTRING allowed here." );
23242374 }
23252375 } catch (ParseException e ) {
2326- throw new PlcInvalidTagException ("Error reading var-length string actual lengths." );
2376+ throw new PlcInvalidTagException ("Error parsing var-length string actual lengths." );
23272377 }
23282378 }
23292379
0 commit comments