use of org.openhab.binding.modbus.internal.pooling.ModbusSlaveEndpoint in project openhab1-addons by openhab.
the class ModbusBinding method updated.
@Override
public void updated(Dictionary<String, ?> config) throws ConfigurationException {
try {
// remove all known items if configuration changed
clear();
reconstructConnectionPool();
if (config == null) {
logger.debug("Got null config!");
return;
}
Enumeration<String> keys = config.keys();
Map<String, EndpointPoolConfiguration> slavePoolConfigs = new HashMap<String, EndpointPoolConfiguration>();
Map<ModbusSlaveEndpoint, EndpointPoolConfiguration> endpointPoolConfigs = new HashMap<ModbusSlaveEndpoint, EndpointPoolConfiguration>();
while (keys.hasMoreElements()) {
final String key = keys.nextElement();
final String value = (String) config.get(key);
try {
// don't want to process here ...
if ("service.pid".equals(key)) {
continue;
}
Matcher matcher = EXTRACT_MODBUS_CONFIG_PATTERN.matcher(key);
if (!matcher.matches()) {
if ("poll".equals(key)) {
if (StringUtils.isNotBlank((String) config.get(key))) {
pollInterval = Integer.valueOf((String) config.get(key));
}
} else if ("writemultipleregisters".equals(key)) {
// XXX: ugly to touch base class but kept here for backwards compat
// FIXME: should this be deprecated as introduced as slave specific parameter?
ModbusSlave.setWriteMultipleRegisters(Boolean.valueOf(config.get(key).toString()));
} else {
logger.debug("given modbus-slave-config-key '{}' does not follow the expected pattern or 'serial.<slaveId>.<{}>'", key, VALID_CONFIG_KEYS);
}
continue;
}
matcher.reset();
matcher.find();
String slave = matcher.group(2);
ModbusSlave modbusSlave = modbusSlaves.get(slave);
EndpointPoolConfiguration endpointPoolConfig = slavePoolConfigs.get(slave);
if (modbusSlave == null) {
if (matcher.group(1).equals(TCP_PREFIX)) {
modbusSlave = new ModbusTcpSlave(slave, connectionPool);
} else if (matcher.group(1).equals(UDP_PREFIX)) {
modbusSlave = new ModbusUdpSlave(slave, connectionPool);
} else if (matcher.group(1).equals(SERIAL_PREFIX)) {
modbusSlave = new ModbusSerialSlave(slave, connectionPool);
} else {
throw new ConfigurationException(slave, "the given slave type '" + slave + "' is unknown");
}
endpointPoolConfig = new EndpointPoolConfiguration();
// Do not give up if the connection attempt fails on the first time...
endpointPoolConfig.setConnectMaxTries(Modbus.DEFAULT_RETRIES);
logger.debug("modbusSlave '{}' instanciated", slave);
modbusSlaves.put(slave, modbusSlave);
}
String configKey = matcher.group(3);
if ("connection".equals(configKey)) {
String[] chunks = value.split(":");
Iterator<String> settingIterator = stringArrayIterator(chunks);
if (modbusSlave instanceof ModbusIPSlave) {
((ModbusIPSlave) modbusSlave).setHost(settingIterator.next());
//
// Defaults for endpoint and slave
//
modbusSlave.setRetryDelayMillis(DEFAULT_TCP_INTER_TRANSACTION_DELAY_MILLIS);
endpointPoolConfig.setPassivateBorrowMinMillis(DEFAULT_TCP_INTER_TRANSACTION_DELAY_MILLIS);
//
try {
((ModbusIPSlave) modbusSlave).setPort(Integer.valueOf(settingIterator.next()));
long passivateBorrowMinMillis = Long.parseLong(settingIterator.next());
modbusSlave.setRetryDelayMillis(passivateBorrowMinMillis);
endpointPoolConfig.setPassivateBorrowMinMillis(passivateBorrowMinMillis);
endpointPoolConfig.setReconnectAfterMillis(Integer.parseInt(settingIterator.next()));
// time to wait before trying connect closed connection. Note that
// ModbusSlaveConnectionFactoryImpl makes sure that max{passivateBorrowMinMillis, this
// parameter} is waited between connection attempts
endpointPoolConfig.setInterConnectDelayMillis(Long.parseLong(settingIterator.next()));
endpointPoolConfig.setConnectMaxTries(Integer.parseInt(settingIterator.next()));
endpointPoolConfig.setConnectTimeoutMillis(Integer.parseInt(settingIterator.next()));
} catch (NoSuchElementException e) {
// Some of the optional parameters are missing -- it's ok!
}
if (settingIterator.hasNext()) {
String errMsg = String.format("%s Has too many colon (:) separated connection settings for a tcp/udp modbus slave. " + "Expecting at most 6 parameters: hostname (mandatory) and " + "optionally (in this order) port number, " + "interTransactionDelayMillis, reconnectAfterMillis," + "interConnectDelayMillis, connectMaxTries, connectTimeout.", key);
throw new ConfigurationException(key, errMsg);
}
} else if (modbusSlave instanceof ModbusSerialSlave) {
SerialParameters serialParameters = new SerialParameters();
serialParameters.setPortName(settingIterator.next());
//
// Defaults for endpoint and slave
//
// never "disconnect" (close/open serial
endpointPoolConfig.setReconnectAfterMillis(-1);
// port)
// serial connection between borrows
modbusSlave.setRetryDelayMillis(DEFAULT_SERIAL_INTER_TRANSACTION_DELAY_MILLIS);
endpointPoolConfig.setPassivateBorrowMinMillis(DEFAULT_SERIAL_INTER_TRANSACTION_DELAY_MILLIS);
//
try {
serialParameters.setBaudRate(settingIterator.next());
serialParameters.setDatabits(settingIterator.next());
serialParameters.setParity(settingIterator.next());
serialParameters.setStopbits(settingIterator.next());
serialParameters.setEncoding(settingIterator.next());
// time to wait between connection passive+borrow, i.e. time to wait between
// transactions
long passivateBorrowMinMillis = Long.parseLong(settingIterator.next());
modbusSlave.setRetryDelayMillis(passivateBorrowMinMillis);
endpointPoolConfig.setPassivateBorrowMinMillis(passivateBorrowMinMillis);
serialParameters.setReceiveTimeoutMillis(settingIterator.next());
serialParameters.setFlowControlIn(settingIterator.next());
serialParameters.setFlowControlOut(settingIterator.next());
} catch (NoSuchElementException e) {
// Some of the optional parameters are missing -- it's ok!
}
if (settingIterator.hasNext()) {
String errMsg = String.format("%s Has too many colon (:) separated connection settings for a serial modbus slave. " + "Expecting at most 9 parameters (got %d): devicePort (mandatory), " + "and 0 or more optional parameters (in this order): " + "baudRate, dataBits, parity, stopBits, " + "encoding, interTransactionWaitMillis, " + "receiveTimeoutMillis, flowControlIn, flowControlOut", key, chunks.length);
throw new ConfigurationException(key, errMsg);
}
((ModbusSerialSlave) modbusSlave).setSerialParameters(serialParameters);
}
} else if ("start".equals(configKey)) {
modbusSlave.setStart(Integer.valueOf(value));
} else if ("length".equals(configKey)) {
modbusSlave.setLength(Integer.valueOf(value));
} else if ("id".equals(configKey)) {
modbusSlave.setId(Integer.valueOf(value));
} else if ("type".equals(configKey)) {
if (ArrayUtils.contains(ModbusBindingProvider.SLAVE_DATA_TYPES, value)) {
modbusSlave.setType(value);
} else {
throw new ConfigurationException(configKey, "the given slave type '" + value + "' is invalid");
}
} else if ("valuetype".equals(configKey)) {
if (ArrayUtils.contains(ModbusBindingProvider.VALUE_TYPES, value)) {
modbusSlave.setValueType(value);
} else {
throw new ConfigurationException(configKey, "the given value type '" + value + "' is invalid");
}
} else if ("rawdatamultiplier".equals(configKey)) {
modbusSlave.setRawDataMultiplier(Double.valueOf(value.toString()));
} else if ("updateunchangeditems".equals(configKey)) {
modbusSlave.setUpdateUnchangedItems(Boolean.valueOf(value.toString()));
} else if ("postundefinedonreaderror".equals(configKey)) {
modbusSlave.setPostUndefinedOnReadError(Boolean.valueOf(value.toString()));
} else {
throw new ConfigurationException(configKey, "the given configKey '" + configKey + "' is unknown");
}
modbusSlaves.put(slave, modbusSlave);
slavePoolConfigs.put(slave, endpointPoolConfig);
} catch (Exception e) {
String errMsg = String.format("Exception when parsing configuration parameter %s = %s -- %s %s", key, value, e.getClass().getName(), e.getMessage());
logger.error(errMsg);
throw new ConfigurationException(key, errMsg);
}
}
// Finally, go through each slave definition, and combine the slave pool configurations
for (Entry<String, EndpointPoolConfiguration> slaveEntry : slavePoolConfigs.entrySet()) {
String slave = slaveEntry.getKey();
EndpointPoolConfiguration poolConfiguration = slaveEntry.getValue();
ModbusSlaveEndpoint endpoint = modbusSlaves.get(slave).getEndpoint();
EndpointPoolConfiguration existingPoolConfiguration = endpointPoolConfigs.get(endpoint);
// Do we have two slaves with same endpoint, but different pool configuration parameters? Warn if we do.
if (existingPoolConfiguration != null && !existingPoolConfiguration.equals(poolConfiguration)) {
logger.warn("Slave {} (endpoint {}) has different retry/connection delay " + "(EndpointPoolConfiguration) etc. settings. Replacing {} with {}", slave, endpoint, existingPoolConfiguration, poolConfiguration);
}
endpointPoolConfigs.put(endpoint, poolConfiguration);
}
connectionFactory.applyEndpointPoolConfigs(endpointPoolConfigs);
logger.debug("Parsed the following slave->endpoint configurations: {}. If the endpoint is same, " + "connections are shared between the instances.", slavePoolConfigs);
logger.debug("Parsed the following pool configurations: {}", endpointPoolConfigs);
logger.debug("config looked good");
setProperlyConfigured(true);
} catch (ConfigurationException ce) {
setProperlyConfigured(false);
throw ce;
}
}
use of org.openhab.binding.modbus.internal.pooling.ModbusSlaveEndpoint in project openhab1-addons by openhab.
the class ModbusSlave method executeWriteRequest.
/**
*
* @param request
* @throws ModbusConnectionException when connection cannot be established
* @throws ModbusException ModbusIOException on IO errors, ModbusSlaveException with protocol level exceptions
* @throws ModbusUnexpectedTransactionIdException when response transaction id does not match the request
*/
private void executeWriteRequest(ModbusRequest request) throws ModbusConnectionException, ModbusException, ModbusUnexpectedTransactionIdException {
ModbusSlaveEndpoint endpoint = getEndpoint();
ModbusSlaveConnection connection = null;
try {
connection = getConnection(endpoint);
if (connection == null) {
logger.warn("ModbusSlave ({}): not connected -- aborting request {}", name, request);
throw new ModbusConnectionException(endpoint);
}
transaction.setRequest(request);
try {
transaction.execute();
} catch (Exception e) {
// Note, one could catch ModbusIOException and ModbusSlaveException if more detailed
// exception handling is required. For now, all exceptions are handled the same way with writes.
logger.error("ModbusSlave ({}): error when executing write request ({}): {}", name, request, e.getMessage());
invalidate(endpoint, connection);
// set connection to null such that it is not returned to pool
connection = null;
throw e;
}
ModbusResponse response = transaction.getResponse();
if ((response.getTransactionID() != transaction.getTransactionID()) && !response.isHeadless()) {
logger.warn("ModbusSlave ({}): Transaction id of the response does not match request {}. Endpoint {}. Connection: {}. Ignoring response.", name, request, endpoint, connection);
throw new ModbusUnexpectedTransactionIdException();
}
} finally {
returnConnection(endpoint, connection);
}
}
use of org.openhab.binding.modbus.internal.pooling.ModbusSlaveEndpoint in project openhab1-addons by openhab.
the class ModbusBinding method reconstructConnectionPool.
private static void reconstructConnectionPool() {
connectionFactory = new ModbusSlaveConnectionFactoryImpl();
GenericKeyedObjectPool<ModbusSlaveEndpoint, ModbusSlaveConnection> genericKeyedObjectPool = new GenericKeyedObjectPool<ModbusSlaveEndpoint, ModbusSlaveConnection>(connectionFactory, poolConfig);
genericKeyedObjectPool.setSwallowedExceptionListener(new SwallowedExceptionListener() {
@Override
public void onSwallowException(Exception e) {
logger.error("Connection pool swallowed unexpected exception: {}", e.getMessage());
}
});
connectionPool = genericKeyedObjectPool;
}
use of org.openhab.binding.modbus.internal.pooling.ModbusSlaveEndpoint in project openhab1-addons by openhab.
the class ModbusSlave method getModbusData.
/**
* Executes Modbus transaction that reads data from the device and returns response data
*
* @param request describes what data are requested from the device
* @return response data
* @throws ModbusConnectionException when connection cannot be established
* @throws ModbusException ModbusIOException on IO errors, ModbusSlaveException with protocol level exceptions
* @throws ModbusUnexpectedTransactionIdException when response transaction id does not match the request
*/
private ModbusResponse getModbusData(ModbusRequest request) throws ModbusConnectionException, ModbusException, ModbusUnexpectedTransactionIdException {
ModbusSlaveEndpoint endpoint = getEndpoint();
ModbusSlaveConnection connection = null;
ModbusResponse response = null;
try {
connection = getConnection(endpoint);
if (connection == null) {
logger.warn("ModbusSlave ({}) not connected -- aborting read request {}. Endpoint {}", name, request, endpoint);
throw new ModbusConnectionException(endpoint);
}
request.setUnitID(getId());
transaction.setRequest(request);
try {
transaction.execute();
} catch (ModbusException e) {
logger.error("ModbusSlave ({}): Error getting modbus data for request {}. Error: {}. Endpoint {}. Connection: {}", name, request, e.getMessage(), endpoint, connection);
invalidate(endpoint, connection);
// Invalidated connections should not be returned
connection = null;
throw e;
}
response = transaction.getResponse();
if ((response.getTransactionID() != transaction.getTransactionID()) && !response.isHeadless()) {
logger.warn("ModbusSlave ({}): Transaction id of the response does not match request {}. Endpoint {}. Connection: {}. Ignoring response.", name, request, endpoint, connection);
throw new ModbusUnexpectedTransactionIdException();
}
} finally {
returnConnection(endpoint, connection);
}
return response;
}
use of org.openhab.binding.modbus.internal.pooling.ModbusSlaveEndpoint in project openhab1-addons by openhab.
the class SimultaneousReadWriteTestCase method testPoolBlocks.
@Test
public void testPoolBlocks() throws Exception {
final KeyedObjectPool<ModbusSlaveEndpoint, ModbusSlaveConnection> pool = ModbusBinding.getReconstructedConnectionPoolForTesting();
final ModbusTCPSlaveEndpoint endpoint = new ModbusTCPSlaveEndpoint(localAddress().getHostAddress(), this.tcpModbusPort);
ModbusSlaveConnection borrowObject = pool.borrowObject(endpoint);
Thread thread = new Thread() {
@Override
public void run() {
try {
ModbusSlaveConnection borrowObject2 = pool.borrowObject(endpoint);
pool.returnObject(endpoint, borrowObject2);
} catch (Exception e) {
e.printStackTrace(System.err);
}
}
};
thread.start();
thread.join(500);
if (!thread.isAlive()) {
throw new AssertionError("Thread should still be alive -- blocking since no objects");
} else {
thread.interrupt();
}
pool.returnObject(endpoint, borrowObject);
// Now that object has been returned, borrowing should work again
ModbusSlaveConnection borrowObject2 = pool.borrowObject(endpoint);
pool.returnObject(endpoint, borrowObject2);
}
Aggregations