Search in sources :

Example 1 with ModbusSlaveEndpoint

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;
    }
}
Also used : HashMap(java.util.HashMap) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) ModbusSlaveEndpoint(org.openhab.binding.modbus.internal.pooling.ModbusSlaveEndpoint) Matcher(java.util.regex.Matcher) ConfigurationException(org.osgi.service.cm.ConfigurationException) NoSuchElementException(java.util.NoSuchElementException) SerialParameters(net.wimpi.modbus.util.SerialParameters) EndpointPoolConfiguration(org.openhab.binding.modbus.internal.pooling.EndpointPoolConfiguration) ConfigurationException(org.osgi.service.cm.ConfigurationException) NoSuchElementException(java.util.NoSuchElementException)

Example 2 with ModbusSlaveEndpoint

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);
    }
}
Also used : ModbusSlaveEndpoint(org.openhab.binding.modbus.internal.pooling.ModbusSlaveEndpoint) ModbusResponse(net.wimpi.modbus.msg.ModbusResponse) ModbusSlaveConnection(net.wimpi.modbus.net.ModbusSlaveConnection) ModbusException(net.wimpi.modbus.ModbusException)

Example 3 with ModbusSlaveEndpoint

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;
}
Also used : GenericKeyedObjectPool(org.apache.commons.pool2.impl.GenericKeyedObjectPool) ModbusSlaveEndpoint(org.openhab.binding.modbus.internal.pooling.ModbusSlaveEndpoint) SwallowedExceptionListener(org.apache.commons.pool2.SwallowedExceptionListener) ModbusSlaveConnection(net.wimpi.modbus.net.ModbusSlaveConnection) ModbusSlaveConnectionFactoryImpl(org.openhab.binding.modbus.internal.pooling.ModbusSlaveConnectionFactoryImpl) ConfigurationException(org.osgi.service.cm.ConfigurationException) NoSuchElementException(java.util.NoSuchElementException)

Example 4 with ModbusSlaveEndpoint

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;
}
Also used : ModbusSlaveEndpoint(org.openhab.binding.modbus.internal.pooling.ModbusSlaveEndpoint) ModbusResponse(net.wimpi.modbus.msg.ModbusResponse) ModbusSlaveConnection(net.wimpi.modbus.net.ModbusSlaveConnection) ModbusException(net.wimpi.modbus.ModbusException)

Example 5 with ModbusSlaveEndpoint

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);
}
Also used : ModbusSlaveEndpoint(org.openhab.binding.modbus.internal.pooling.ModbusSlaveEndpoint) ModbusTCPSlaveEndpoint(org.openhab.binding.modbus.internal.pooling.ModbusTCPSlaveEndpoint) ModbusSlaveConnection(net.wimpi.modbus.net.ModbusSlaveConnection) BindingConfigParseException(org.openhab.model.item.binding.BindingConfigParseException) Test(org.junit.Test)

Aggregations

ModbusSlaveEndpoint (org.openhab.binding.modbus.internal.pooling.ModbusSlaveEndpoint)5 ModbusSlaveConnection (net.wimpi.modbus.net.ModbusSlaveConnection)4 NoSuchElementException (java.util.NoSuchElementException)2 ModbusException (net.wimpi.modbus.ModbusException)2 ModbusResponse (net.wimpi.modbus.msg.ModbusResponse)2 ConfigurationException (org.osgi.service.cm.ConfigurationException)2 HashMap (java.util.HashMap)1 ConcurrentHashMap (java.util.concurrent.ConcurrentHashMap)1 Matcher (java.util.regex.Matcher)1 SerialParameters (net.wimpi.modbus.util.SerialParameters)1 SwallowedExceptionListener (org.apache.commons.pool2.SwallowedExceptionListener)1 GenericKeyedObjectPool (org.apache.commons.pool2.impl.GenericKeyedObjectPool)1 Test (org.junit.Test)1 EndpointPoolConfiguration (org.openhab.binding.modbus.internal.pooling.EndpointPoolConfiguration)1 ModbusSlaveConnectionFactoryImpl (org.openhab.binding.modbus.internal.pooling.ModbusSlaveConnectionFactoryImpl)1 ModbusTCPSlaveEndpoint (org.openhab.binding.modbus.internal.pooling.ModbusTCPSlaveEndpoint)1 BindingConfigParseException (org.openhab.model.item.binding.BindingConfigParseException)1