use of org.apache.plc4x.java.s7.readwrite.field.S7Field in project plc4x by apache.
the class S7ProtocolLogic method serializePlcValue.
private S7VarPayloadDataItem serializePlcValue(S7Field field, PlcValue plcValue) {
try {
DataTransportSize transportSize = field.getDataType().getDataTransportSize();
int stringLength = (field instanceof S7StringField) ? ((S7StringField) field).getStringLength() : 254;
ByteBuffer byteBuffer = null;
for (int i = 0; i < field.getNumberOfElements(); i++) {
final int lengthInBits = DataItem.getLengthInBits(plcValue.getIndex(i), field.getDataType().getDataProtocolId(), stringLength);
final WriteBufferByteBased writeBuffer = new WriteBufferByteBased((int) Math.ceil(((float) lengthInBits) / 8.0f));
DataItem.staticSerialize(writeBuffer, plcValue.getIndex(i), field.getDataType().getDataProtocolId(), stringLength);
// Allocate enough space for all items.
if (byteBuffer == null) {
byteBuffer = ByteBuffer.allocate(writeBuffer.getData().length * field.getNumberOfElements());
}
byteBuffer.put(writeBuffer.getData());
}
if (byteBuffer != null) {
byte[] data = byteBuffer.array();
return new S7VarPayloadDataItem(DataTransportErrorCode.OK, transportSize, data);
}
} catch (SerializationException e) {
logger.warn("Error serializing field item of type: '{}'", field.getDataType().name(), e);
}
return null;
}
use of org.apache.plc4x.java.s7.readwrite.field.S7Field in project plc4x by apache.
the class S7Field method of.
public static S7Field of(String fieldString) {
Matcher matcher;
if ((matcher = DATA_BLOCK_STRING_ADDRESS_PATTERN.matcher(fieldString)).matches()) {
TransportSize dataType = TransportSize.valueOf(matcher.group(DATA_TYPE));
int stringLength = Integer.parseInt(matcher.group(STRING_LENGTH));
MemoryArea memoryArea = MemoryArea.DATA_BLOCKS;
int blockNumber = checkDatablockNumber(Integer.parseInt(matcher.group(BLOCK_NUMBER)));
Short transferSizeCode = getSizeCode(matcher.group(TRANSFER_SIZE_CODE));
int byteOffset = checkByteOffset(Integer.parseInt(matcher.group(BYTE_OFFSET)));
byte bitOffset = 0;
if (matcher.group(BIT_OFFSET) != null) {
bitOffset = Byte.parseByte(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));
}
if ((transferSizeCode != null) && (dataType.getShortName() != transferSizeCode)) {
throw new PlcInvalidFieldException("Transfer size code '" + transferSizeCode + "' doesn't match specified data type '" + dataType.name() + "'");
}
return new S7StringField(dataType, memoryArea, blockNumber, byteOffset, bitOffset, numElements, stringLength);
} else if ((matcher = DATA_BLOCK_STRING_SHORT_PATTERN.matcher(fieldString)).matches()) {
TransportSize dataType = TransportSize.valueOf(matcher.group(DATA_TYPE));
int stringLength = Integer.parseInt(matcher.group(STRING_LENGTH));
MemoryArea memoryArea = MemoryArea.DATA_BLOCKS;
int blockNumber = checkDatablockNumber(Integer.parseInt(matcher.group(BLOCK_NUMBER)));
int byteOffset = checkByteOffset(Integer.parseInt(matcher.group(BYTE_OFFSET)));
byte bitOffset = 0;
int numElements = 1;
if (matcher.group(NUM_ELEMENTS) != null) {
numElements = Integer.parseInt(matcher.group(NUM_ELEMENTS));
}
return new S7StringField(dataType, memoryArea, blockNumber, byteOffset, bitOffset, numElements, stringLength);
} else if ((matcher = DATA_BLOCK_ADDRESS_PATTERN.matcher(fieldString)).matches()) {
TransportSize dataType = TransportSize.valueOf(matcher.group(DATA_TYPE));
MemoryArea memoryArea = MemoryArea.DATA_BLOCKS;
Short transferSizeCode = getSizeCode(matcher.group(TRANSFER_SIZE_CODE));
int blockNumber = checkDatablockNumber(Integer.parseInt(matcher.group(BLOCK_NUMBER)));
int byteOffset = checkByteOffset(Integer.parseInt(matcher.group(BYTE_OFFSET)));
byte bitOffset = 0;
if (matcher.group(BIT_OFFSET) != null) {
bitOffset = Byte.parseByte(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));
}
if ((transferSizeCode != null) && (dataType.getShortName() != 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 if ((matcher = DATA_BLOCK_SHORT_PATTERN.matcher(fieldString)).matches()) {
TransportSize dataType = TransportSize.valueOf(matcher.group(DATA_TYPE));
MemoryArea memoryArea = MemoryArea.DATA_BLOCKS;
int blockNumber = checkDatablockNumber(Integer.parseInt(matcher.group(BLOCK_NUMBER)));
int byteOffset = checkByteOffset(Integer.parseInt(matcher.group(BYTE_OFFSET)));
byte bitOffset = 0;
if (matcher.group(BIT_OFFSET) != null) {
bitOffset = Byte.parseByte(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));
}
return new S7Field(dataType, memoryArea, blockNumber, byteOffset, bitOffset, numElements);
} else if (PLC_PROXY_ADDRESS_PATTERN.matcher(fieldString).matches()) {
try {
byte[] addressData = Hex.decodeHex(fieldString.replaceAll("[-]", ""));
ReadBuffer rb = new ReadBufferByteBased(addressData);
final S7Address s7Address = S7Address.staticParse(rb);
if (s7Address instanceof S7AddressAny) {
S7AddressAny s7AddressAny = (S7AddressAny) s7Address;
if ((s7AddressAny.getTransportSize() != TransportSize.BOOL) && s7AddressAny.getBitAddress() != 0) {
throw new PlcInvalidFieldException("A bit offset other than 0 is only supported for type BOOL");
}
return new S7Field(s7AddressAny.getTransportSize(), s7AddressAny.getArea(), s7AddressAny.getDbNumber(), s7AddressAny.getByteAddress(), s7AddressAny.getBitAddress(), s7AddressAny.getNumberOfElements());
} else {
throw new PlcInvalidFieldException("Unsupported address type.");
}
} catch (ParseException | DecoderException e) {
throw new PlcInvalidFieldException("Unable to parse address: " + fieldString);
}
} else if ((matcher = ADDRESS_PATTERN.matcher(fieldString)).matches()) {
TransportSize dataType = TransportSize.valueOf(matcher.group(DATA_TYPE));
MemoryArea memoryArea = getMemoryAreaForShortName(matcher.group(MEMORY_AREA));
Short transferSizeCode = getSizeCode(matcher.group(TRANSFER_SIZE_CODE));
int byteOffset = checkByteOffset(Integer.parseInt(matcher.group(BYTE_OFFSET)));
byte bitOffset = 0;
if (matcher.group(BIT_OFFSET) != null) {
bitOffset = Byte.parseByte(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));
}
if ((transferSizeCode != null) && (dataType.getShortName() != transferSizeCode)) {
throw new PlcInvalidFieldException("Transfer size code '" + transferSizeCode + "' doesn't match specified data type '" + dataType.name() + "'");
}
if ((dataType != TransportSize.BOOL) && bitOffset != 0) {
throw new PlcInvalidFieldException("A bit offset other than 0 is only supported for type BOOL");
}
return new S7Field(dataType, memoryArea, (short) 0, byteOffset, bitOffset, numElements);
}
throw new PlcInvalidFieldException("Unable to parse address: " + fieldString);
}
use of org.apache.plc4x.java.s7.readwrite.field.S7Field in project plc4x by apache.
the class S7Optimizer method processWriteRequest.
@Override
protected List<PlcRequest> processWriteRequest(PlcWriteRequest writeRequest, DriverContext driverContext) {
S7DriverContext s7DriverContext = (S7DriverContext) driverContext;
List<PlcRequest> processedRequests = new LinkedList<>();
// This calculates the size of the header for the request and response.
int curRequestSize = EMPTY_WRITE_REQUEST_SIZE;
// An empty response has the same size as an empty request.
int curResponseSize = EMPTY_WRITE_RESPONSE_SIZE;
// List of all items in the current request.
LinkedHashMap<String, FieldValueItem> curFields = new LinkedHashMap<>();
for (String fieldName : writeRequest.getFieldNames()) {
S7Field field = (S7Field) writeRequest.getField(fieldName);
PlcValue value = writeRequest.getPlcValue(fieldName);
int writeRequestItemSize = S7_ADDRESS_ANY_SIZE + 4;
if (field.getDataType() == TransportSize.BOOL) {
writeRequestItemSize += Math.ceil((double) field.getNumberOfElements() / 8);
} else {
writeRequestItemSize += (field.getNumberOfElements() * field.getDataType().getSizeInBytes());
}
// If it's an odd number of bytes, add one to make it even
if (writeRequestItemSize % 2 == 1) {
writeRequestItemSize++;
}
int writeResponseItemSize = 4;
// If adding the item would not exceed the sizes, add it to the current request.
if (((curRequestSize + writeRequestItemSize) <= s7DriverContext.getPduSize()) && ((curResponseSize + writeResponseItemSize) <= s7DriverContext.getPduSize())) {
// Increase the current request sizes.
curRequestSize += writeRequestItemSize;
curResponseSize += writeResponseItemSize;
// Add the item.
} else // If adding them would exceed, start a new request.
{
// Create a new PlcWriteRequest containing the current field item.
processedRequests.add(new DefaultPlcWriteRequest(((DefaultPlcWriteRequest) writeRequest).getWriter(), curFields));
// Reset the size and item lists.
curRequestSize = EMPTY_WRITE_REQUEST_SIZE + writeRequestItemSize;
curResponseSize = EMPTY_WRITE_RESPONSE_SIZE + writeResponseItemSize;
curFields = new LinkedHashMap<>();
// Splitting of huge fields not yet implemented, throw an exception instead.
if (((curRequestSize + writeRequestItemSize) > s7DriverContext.getPduSize()) && ((curResponseSize + writeResponseItemSize) > s7DriverContext.getPduSize())) {
throw new PlcRuntimeException("Field size exceeds maximum payload for one item.");
}
}
curFields.put(fieldName, new FieldValueItem(field, value));
}
// Create a new PlcWriteRequest from the remaining field items.
if (!curFields.isEmpty()) {
processedRequests.add(new DefaultPlcWriteRequest(((DefaultPlcWriteRequest) writeRequest).getWriter(), curFields));
}
return processedRequests;
}
use of org.apache.plc4x.java.s7.readwrite.field.S7Field in project plc4x by apache.
the class S7ProtocolLogic method write.
@Override
public CompletableFuture<PlcWriteResponse> write(PlcWriteRequest writeRequest) {
CompletableFuture<PlcWriteResponse> future = new CompletableFuture<>();
DefaultPlcWriteRequest request = (DefaultPlcWriteRequest) writeRequest;
List<S7VarRequestParameterItem> parameterItems = new ArrayList<>(request.getNumberOfFields());
List<S7VarPayloadDataItem> payloadItems = new ArrayList<>(request.getNumberOfFields());
for (String fieldName : request.getFieldNames()) {
final S7Field field = (S7Field) request.getField(fieldName);
final PlcValue plcValue = request.getPlcValue(fieldName);
parameterItems.add(new S7VarRequestParameterItemAddress(encodeS7Address(field)));
payloadItems.add(serializePlcValue(field, plcValue));
}
final int tpduId = tpduGenerator.getAndIncrement();
// If we've reached the max value for a 16 bit transaction identifier, reset back to 1
if (tpduGenerator.get() == 0xFFFF) {
tpduGenerator.set(1);
}
TPKTPacket tpktPacket = new TPKTPacket(new COTPPacketData(null, new S7MessageRequest(tpduId, new S7ParameterWriteVarRequest(parameterItems), new S7PayloadWriteVarRequest(payloadItems, null)), true, (short) tpduId, null));
// Start a new request-transaction (Is ended in the response-handler)
RequestTransactionManager.RequestTransaction transaction = tm.startRequest();
transaction.submit(() -> context.sendRequest(tpktPacket).onTimeout(new TransactionErrorCallback<>(future, transaction)).onError(new TransactionErrorCallback<>(future, transaction)).expectResponse(TPKTPacket.class, REQUEST_TIMEOUT).check(p -> p.getPayload() instanceof COTPPacketData).unwrap(p -> ((COTPPacketData) p.getPayload())).unwrap(COTPPacket::getPayload).check(p -> p.getTpduReference() == tpduId).handle(p -> {
try {
future.complete(((PlcWriteResponse) decodeWriteResponse(p, writeRequest)));
} catch (PlcProtocolException e) {
logger.warn("Error sending 'write' message: '{}'", e.getMessage(), e);
}
// Finish the request-transaction.
transaction.endRequest();
}));
return future;
}
use of org.apache.plc4x.java.s7.readwrite.field.S7Field in project plc4x by apache.
the class S7ProtocolLogic method encodeS7Address.
/**
* Currently we only support the S7 Any type of addresses. This helper simply converts the S7Field
* from PLC4X into S7Address objects.
*
* @param field S7Field instance we need to convert into an S7Address
* @return the S7Address
*/
protected S7Address encodeS7Address(PlcField field) {
if (!(field instanceof S7Field)) {
throw new PlcRuntimeException("Unsupported address type " + field.getClass().getName());
}
S7Field s7Field = (S7Field) field;
TransportSize transportSize = s7Field.getDataType();
int numElements = s7Field.getNumberOfElements();
// As otherwise the S7 will deny them with "Data type not supported" replies.
if ((transportSize == TransportSize.TIME) || /*|| (transportSize == TransportSize.S7_S5TIME)*/
(transportSize == TransportSize.LTIME) || (transportSize == TransportSize.DATE) || (transportSize == TransportSize.TIME_OF_DAY) || (transportSize == TransportSize.DATE_AND_TIME)) {
numElements = numElements * transportSize.getSizeInBytes();
transportSize = TransportSize.BYTE;
}
if (transportSize == TransportSize.STRING) {
transportSize = TransportSize.CHAR;
int stringLength = (s7Field instanceof S7StringField) ? ((S7StringField) s7Field).getStringLength() : 254;
numElements = numElements * (stringLength + 2);
} else if (transportSize == TransportSize.WSTRING) {
transportSize = TransportSize.CHAR;
int stringLength = (s7Field instanceof S7StringField) ? ((S7StringField) s7Field).getStringLength() : 254;
numElements = numElements * (stringLength + 2) * 2;
}
return new S7AddressAny(transportSize, numElements, s7Field.getBlockNumber(), s7Field.getMemoryArea(), s7Field.getByteOffset(), s7Field.getBitOffset());
}
Aggregations