| /* |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, |
| software distributed under the License is distributed on an |
| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| KIND, either express or implied. See the License for the |
| specific language governing permissions and limitations |
| under the License. |
| */ |
| package org.apache.plc4x.sandbox.java.dynamic.s7.utils; |
| |
| import org.apache.plc4x.java.api.exceptions.PlcInvalidFieldException; |
| import org.apache.plc4x.java.api.model.PlcField; |
| import org.apache.plc4x.sandbox.java.dynamic.s7.types.MemoryArea; |
| import org.apache.plc4x.sandbox.java.dynamic.s7.types.TransportSize; |
| |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| public class S7Field implements PlcField { |
| |
| //byteOffset theoretically can reach up to 2097151 ... see checkByteOffset() below --> 7digits |
| private static final Pattern ADDRESS_PATTERN = |
| Pattern.compile("^%(?<memoryArea>.)(?<transferSizeCode>[XBWD]?)(?<byteOffset>\\d{1,7})(.(?<bitOffset>[0-7]))?:(?<dataType>[a-zA-Z_]+)(\\[(?<numElements>\\d+)])?"); |
| |
| //blockNumber usually has its max hat around 64000 --> 5digits |
| private static final Pattern DATA_BLOCK_ADDRESS_PATTERN = |
| Pattern.compile("^%DB(?<blockNumber>\\d{1,5}).DB(?<transferSizeCode>[XBWD]?)(?<byteOffset>\\d{1,7})(.(?<bitOffset>[0-7]))?:(?<dataType>[a-zA-Z_]+)(\\[(?<numElements>\\d+)])?"); |
| |
| private static final String DATA_TYPE = "dataType"; |
| private static final String TRANSFER_SIZE_CODE = "transferSizeCode"; |
| private static final String BLOCK_NUMBER = "blockNumber"; |
| private static final String BYTE_OFFSET = "byteOffset"; |
| private static final String BIT_OFFSET = "bitOffset"; |
| private static final String NUM_ELEMENTS = "numElements"; |
| private static final String MEMORY_AREA = "memoryArea"; |
| |
| private final TransportSize dataType; |
| private final MemoryArea memoryArea; |
| private final int blockNumber; |
| private final int byteOffset; |
| private final short bitOffset; |
| private final int numElements; |
| |
| private S7Field(TransportSize dataType, MemoryArea memoryArea, int blockNumber, int byteOffset, short bitOffset, int numElements) { |
| this.dataType = dataType; |
| this.memoryArea = memoryArea; |
| this.blockNumber = blockNumber; |
| this.byteOffset = byteOffset; |
| this.bitOffset = bitOffset; |
| this.numElements = numElements; |
| } |
| |
| public TransportSize getDataType() { |
| return dataType; |
| } |
| |
| public MemoryArea getMemoryArea() { |
| return memoryArea; |
| } |
| |
| public int getBlockNumber() { |
| return blockNumber; |
| } |
| |
| public int getByteOffset() { |
| return byteOffset; |
| } |
| |
| public short getBitOffset() { |
| return bitOffset; |
| } |
| |
| public int getNumElements() { |
| return numElements; |
| } |
| |
| public static boolean matches(String fieldString) { |
| return DATA_BLOCK_ADDRESS_PATTERN.matcher(fieldString).matches() || |
| ADDRESS_PATTERN.matcher(fieldString).matches(); |
| } |
| |
| public static S7Field of(String fieldString) { |
| Matcher matcher = DATA_BLOCK_ADDRESS_PATTERN.matcher(fieldString); |
| if(matcher.matches()) { |
| TransportSize dataType = TransportSize.valueOf(matcher.group(DATA_TYPE)); |
| MemoryArea memoryArea = MemoryArea.DATA_BLOCKS; |
| String transferSizeCode = matcher.group(TRANSFER_SIZE_CODE); |
| |
| int blockNumber = checkDatablockNumber(Integer.parseInt(matcher.group(BLOCK_NUMBER))); |
| |
| int byteOffset = checkByteOffset(Integer.parseInt(matcher.group(BYTE_OFFSET))); |
| |
| short bitOffset = 0; |
| if(matcher.group(BIT_OFFSET) != null) { |
| bitOffset = Short.parseShort(matcher.group(BIT_OFFSET)); |
| } else if(dataType == TransportSize.BOOL) { |
| throw new PlcInvalidFieldException("Expected bit offset for BOOL parameters."); |
| } |
| int numElements = 1; |
| if(matcher.group(NUM_ELEMENTS) != null) { |
| numElements = Integer.parseInt(matcher.group(NUM_ELEMENTS)); |
| } |
| numElements = calcNumberOfElementsForStringTypes(numElements,dataType); |
| if(!transferSizeCode.isEmpty() && !dataType.getSizeCode().equals(transferSizeCode)) { |
| throw new PlcInvalidFieldException("Transfer size code '" + transferSizeCode + |
| "' doesn't match specified data type '" + dataType.name() + "'"); |
| } |
| return new S7Field(dataType, memoryArea, blockNumber, byteOffset, bitOffset, numElements); |
| } else { |
| matcher = ADDRESS_PATTERN.matcher(fieldString); |
| if (matcher.matches()) { |
| TransportSize dataType = TransportSize.valueOf(matcher.group(DATA_TYPE)); |
| MemoryArea memoryArea = MemoryArea.valueOfShortName(matcher.group(MEMORY_AREA)); |
| String transferSizeCode = matcher.group(TRANSFER_SIZE_CODE); |
| |
| int byteOffset = checkByteOffset(Integer.parseInt(matcher.group(BYTE_OFFSET))); |
| |
| short bitOffset = 0; |
| if(matcher.group(BIT_OFFSET) != null) { |
| bitOffset = Short.parseShort(matcher.group(BIT_OFFSET)); |
| } else if(dataType == TransportSize.BOOL) { |
| throw new PlcInvalidFieldException("Expected bit offset for BOOL parameters."); |
| } |
| int numElements = 1; |
| if(matcher.group(NUM_ELEMENTS) != null) { |
| numElements = Integer.parseInt(matcher.group(NUM_ELEMENTS)); |
| } |
| numElements = calcNumberOfElementsForStringTypes(numElements,dataType); |
| if(!transferSizeCode.isEmpty() && !dataType.getSizeCode().equals(transferSizeCode)) { |
| throw new PlcInvalidFieldException("Transfer size code '" + transferSizeCode + |
| "' doesn't match specified data type '" + dataType.name() + "'"); |
| } |
| return new S7Field(dataType, memoryArea, (short) 0, byteOffset, bitOffset, numElements); |
| } |
| } |
| throw new PlcInvalidFieldException("Unable to parse address: " + fieldString); |
| } |
| |
| /** |
| * checks if DatablockNumber of S7Field is in valid range |
| * @param blockNumber given DatablockNumber |
| * @return given blockNumber if Ok, throws PlcInvalidFieldException otherwise |
| */ |
| private static int checkDatablockNumber(int blockNumber){ |
| //ToDo check the value or add reference - limit eventually depending on active S7 --> make a case selection |
| if(blockNumber>64000 || blockNumber<1){ |
| throw new PlcInvalidFieldException("Datablock numbers larger than 64000 or smaller than 1 are not supported."); |
| } |
| return blockNumber; |
| } |
| |
| /** |
| * checks if ByteOffset from S7Field is in valid range |
| * @param byteOffset given byteOffset |
| * @return given byteOffset if Ok, throws PlcInvalidFieldException otherwise |
| */ |
| private static int checkByteOffset(int byteOffset){ |
| //ToDo check the value or add reference |
| if(byteOffset>2097151 || byteOffset<0){ |
| throw new PlcInvalidFieldException("ByteOffset must be smaller than 2097151 and positive."); |
| } |
| return byteOffset; |
| } |
| |
| /** |
| * correct the storage of "array"-like variables like STRING |
| * @param numElements auto-detected numElements (1 if no numElements in brackets has been given, x if a specific number has been given) |
| * @param dataType detected Transport-Size that represents the data-type |
| * @return corrected numElements if nessesary |
| */ |
| private static int calcNumberOfElementsForStringTypes(int numElements,TransportSize dataType){ |
| //if no String nothing has to be done |
| if(!dataType.equals(TransportSize.STRING)){ |
| return numElements; |
| } |
| //on valid String-length add two byte because of S7-representation of Strings |
| if(numElements>1 && numElements<=254){ |
| return numElements+2; |
| } |
| //connection String usage with "STRING" only --> numElements=1 --> enter default value |
| return 256; |
| } |
| |
| } |