Search in sources :

Example 1 with ModbusFailureCallback

use of org.openhab.core.io.transport.modbus.ModbusFailureCallback in project openhab-addons by openhab.

the class ModbusPollerThingHandlerTest method getPollerFailureCallback.

public ModbusFailureCallback<ModbusReadRequestBlueprint> getPollerFailureCallback(ModbusPollerThingHandler handler) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
    Field callbackField = ModbusPollerThingHandler.class.getDeclaredField("callbackDelegator");
    callbackField.setAccessible(true);
    return (ModbusFailureCallback<ModbusReadRequestBlueprint>) callbackField.get(handler);
}
Also used : Field(java.lang.reflect.Field) ModbusFailureCallback(org.openhab.core.io.transport.modbus.ModbusFailureCallback)

Example 2 with ModbusFailureCallback

use of org.openhab.core.io.transport.modbus.ModbusFailureCallback in project openhab-addons by openhab.

the class ModbusPollerThingHandlerTest method testErrorPassedToChildDataThings.

@Test
public void testErrorPassedToChildDataThings() throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
    PollTask pollTask = Mockito.mock(PollTask.class);
    doReturn(pollTask).when(comms).registerRegularPoll(notNull(), eq(150l), eq(0L), notNull(), notNull());
    Configuration pollerConfig = new Configuration();
    pollerConfig.put("refresh", 150L);
    pollerConfig.put("start", 5);
    pollerConfig.put("length", 13);
    pollerConfig.put("type", "coil");
    poller = createPollerThingBuilder("poller").withConfiguration(pollerConfig).withBridge(endpoint.getUID()).build();
    addThing(poller);
    verifyEndpointBasicInitInteraction();
    assertThat(poller.getStatus(), is(equalTo(ThingStatus.ONLINE)));
    final ArgumentCaptor<ModbusFailureCallback<ModbusReadRequestBlueprint>> callbackCapturer = ArgumentCaptor.forClass((Class) ModbusFailureCallback.class);
    verify(comms).registerRegularPoll(any(), eq(150l), eq(0L), notNull(), callbackCapturer.capture());
    ModbusFailureCallback<ModbusReadRequestBlueprint> readCallback = callbackCapturer.getValue();
    assertNotNull(readCallback);
    ModbusReadRequestBlueprint request = Mockito.mock(ModbusReadRequestBlueprint.class);
    Exception error = Mockito.mock(Exception.class);
    ModbusPollerThingHandler thingHandler = (ModbusPollerThingHandler) poller.getHandler();
    assertNotNull(thingHandler);
    ModbusDataThingHandler child1 = Mockito.mock(ModbusDataThingHandler.class);
    ModbusDataThingHandler child2 = Mockito.mock(ModbusDataThingHandler.class);
    AsyncModbusFailure<ModbusReadRequestBlueprint> result = new AsyncModbusFailure<ModbusReadRequestBlueprint>(request, error);
    // has one data child
    thingHandler.childHandlerInitialized(child1, Mockito.mock(Thing.class));
    readCallback.handle(result);
    verify(child1).handleReadError(result);
    verifyNoMoreInteractions(child1);
    verifyNoMoreInteractions(child2);
    reset(child1);
    // two children (one child initialized)
    thingHandler.childHandlerInitialized(child2, Mockito.mock(Thing.class));
    readCallback.handle(result);
    verify(child1).handleReadError(result);
    verify(child2).handleReadError(result);
    verifyNoMoreInteractions(child1);
    verifyNoMoreInteractions(child2);
    reset(child1);
    reset(child2);
    // one child disposed
    thingHandler.childHandlerDisposed(child1, Mockito.mock(Thing.class));
    readCallback.handle(result);
    verify(child2).handleReadError(result);
    verifyNoMoreInteractions(child1);
    verifyNoMoreInteractions(child2);
}
Also used : ModbusDataThingHandler(org.openhab.binding.modbus.internal.handler.ModbusDataThingHandler) PollTask(org.openhab.core.io.transport.modbus.PollTask) Configuration(org.openhab.core.config.core.Configuration) ModbusFailureCallback(org.openhab.core.io.transport.modbus.ModbusFailureCallback) ModbusReadRequestBlueprint(org.openhab.core.io.transport.modbus.ModbusReadRequestBlueprint) ModbusPollerThingHandler(org.openhab.binding.modbus.handler.ModbusPollerThingHandler) AsyncModbusFailure(org.openhab.core.io.transport.modbus.AsyncModbusFailure) Thing(org.openhab.core.thing.Thing) Test(org.junit.jupiter.api.Test)

Example 3 with ModbusFailureCallback

use of org.openhab.core.io.transport.modbus.ModbusFailureCallback in project openhab-core by openhab.

the class ModbusManagerImpl method executeOperation.

/**
 * Execute operation using a retry mechanism.
 *
 * This is a helper function for executing read and write operations and handling the exceptions in a common way.
 *
 * With some connection types, the connection is reseted (disconnected), and new connection is received from the
 * pool. This means that potentially other operations queuing for the connection can be executed in-between.
 *
 * With some other connection types, the operation is retried without resetting the connection type.
 *
 * @param task
 * @param oneOffTask
 * @param operation
 */
private <R, C extends ModbusResultCallback, F extends ModbusFailureCallback<R>, T extends TaskWithEndpoint<R, C, F>> void executeOperation(T task, boolean oneOffTask, ModbusOperation<T> operation) {
    AggregateStopWatch timer = new AggregateStopWatch();
    timer.total.resume();
    String operationId = timer.operationId;
    ModbusSlaveConnectionFactoryImpl connectionFactory = this.connectionFactory;
    if (connectionFactory == null) {
        // deactivated manager
        logger.trace("Deactivated manager - aborting operation.");
        return;
    }
    logTaskQueueInfo();
    R request = task.getRequest();
    ModbusSlaveEndpoint endpoint = task.getEndpoint();
    F failureCallback = task.getFailureCallback();
    int maxTries = task.getMaxTries();
    AtomicReference<@Nullable Exception> lastError = new AtomicReference<>();
    long retryDelay = getEndpointPoolConfiguration(endpoint).getInterTransactionDelayMillis();
    if (maxTries <= 0) {
        throw new IllegalArgumentException("maxTries should be positive");
    }
    Optional<ModbusSlaveConnection> connection = Optional.empty();
    try {
        logger.trace("Starting new operation with task {}. Trying to get connection [operation ID {}]", task, operationId);
        connection = getConnection(timer, oneOffTask, task);
        logger.trace("Operation with task {}. Got a connection {} [operation ID {}]", task, connection.isPresent() ? "successfully" : "which was unconnected (connection issue)", operationId);
        if (!connection.isPresent()) {
            // Could not acquire connection, time to abort
            // Error logged already, error callback called as well
            logger.trace("Initial connection was not successful, aborting. [operation ID {}]", operationId);
            return;
        }
        if (scheduledThreadPoolExecutor == null) {
            logger.debug("Manager has been shut down, aborting proecssing request {} [operation ID {}]", request, operationId);
            return;
        }
        int tryIndex = 0;
        /**
         * last execution is tracked such that the endpoint is not spammed on retry. First retry can be executed
         * right away since getConnection ensures enough time has passed since last transaction. More precisely,
         * ModbusSlaveConnectionFactoryImpl sleeps on activate() (i.e. before returning connection).
         */
        @Nullable Long lastTryMillis = null;
        while (tryIndex < maxTries) {
            logger.trace("Try {} out of {} [operation ID {}]", tryIndex + 1, maxTries, operationId);
            if (!connection.isPresent()) {
                // Connection was likely reseted with previous try, and connection was not successfully
                // re-established. Error has been logged, time to abort.
                logger.trace("Try {} out of {}. Connection was not successful, aborting. [operation ID {}]", tryIndex + 1, maxTries, operationId);
                return;
            }
            if (Thread.interrupted()) {
                logger.warn("Thread interrupted. Aborting operation [operation ID {}]", operationId);
                return;
            }
            // Check poll task is still registered (this is all asynchronous)
            if (!oneOffTask && task instanceof PollTask) {
                verifyTaskIsRegistered((PollTask) task);
            }
            // Let's ensure that enough time is between the retries
            logger.trace("Ensuring that enough time passes before retrying again. Sleeping if necessary [operation ID {}]", operationId);
            long slept = ModbusSlaveConnectionFactoryImpl.waitAtleast(lastTryMillis, retryDelay);
            logger.trace("Sleep ended, slept {} [operation ID {}]", slept, operationId);
            boolean willRetry = false;
            try {
                tryIndex++;
                willRetry = tryIndex < maxTries;
                operation.accept(timer, task, connection.get());
                lastError.set(null);
                break;
            } catch (IOException e) {
                lastError.set(new ModbusSlaveIOExceptionImpl(e));
                // broken pipe on write)
                if (willRetry) {
                    logger.warn("Try {} out of {} failed when executing request ({}). Will try again soon. Error was I/O error, so resetting the connection. Error details: {} {} [operation ID {}]", tryIndex, maxTries, request, e.getClass().getName(), e.getMessage(), operationId);
                } else {
                    logger.error("Last try {} failed when executing request ({}). Aborting. Error was I/O error, so resetting the connection. Error details: {} {} [operation ID {}]", tryIndex, request, e.getClass().getName(), e.getMessage(), operationId);
                }
                if (endpoint instanceof ModbusSerialSlaveEndpoint) {
                    // Workaround for https://github.com/openhab/openhab-core/issues/1842
                    // Avoid disconnect/re-connect serial interfaces
                    logger.debug("Skipping invalidation of serial connection to workaround openhab-core#1842.");
                } else {
                    // Invalidate connection, and empty (so that new connection is acquired before new retry)
                    timer.connection.timeConsumer(c -> invalidate(endpoint, c), connection);
                    connection = Optional.empty();
                }
                continue;
            } catch (ModbusIOException e) {
                lastError.set(new ModbusSlaveIOExceptionImpl(e));
                // broken pipe on write)
                if (willRetry) {
                    logger.warn("Try {} out of {} failed when executing request ({}). Will try again soon. Error was I/O error, so resetting the connection. Error details: {} {} [operation ID {}]", tryIndex, maxTries, request, e.getClass().getName(), e.getMessage(), operationId);
                } else {
                    logger.error("Last try {} failed when executing request ({}). Aborting. Error was I/O error, so resetting the connection. Error details: {} {} [operation ID {}]", tryIndex, request, e.getClass().getName(), e.getMessage(), operationId);
                }
                if (endpoint instanceof ModbusSerialSlaveEndpoint) {
                    // Workaround for https://github.com/openhab/openhab-core/issues/1842
                    // Avoid disconnect/re-connect serial interfaces
                    logger.debug("Skipping invalidation of serial connection to workaround openhab-core#1842.");
                } else {
                    // Invalidate connection, and empty (so that new connection is acquired before new retry)
                    timer.connection.timeConsumer(c -> invalidate(endpoint, c), connection);
                    connection = Optional.empty();
                }
                continue;
            } catch (ModbusSlaveException e) {
                lastError.set(new ModbusSlaveErrorResponseExceptionImpl(e));
                // Slave returned explicit error response, no reason to re-establish new connection
                if (willRetry) {
                    logger.warn("Try {} out of {} failed when executing request ({}). Will try again soon. Error was: {} {} [operation ID {}]", tryIndex, maxTries, request, e.getClass().getName(), e.getMessage(), operationId);
                } else {
                    logger.error("Last try {} failed when executing request ({}). Aborting. Error was: {} {} [operation ID {}]", tryIndex, request, e.getClass().getName(), e.getMessage(), operationId);
                }
                continue;
            } catch (ModbusUnexpectedTransactionIdException | ModbusUnexpectedResponseFunctionCodeException | ModbusUnexpectedResponseSizeException e) {
                lastError.set(e);
                // transaction error details already logged
                if (willRetry) {
                    logger.warn("Try {} out of {} failed when executing request ({}). Will try again soon. The response did not match the request. Resetting the connection. Error details: {} {} [operation ID {}]", tryIndex, maxTries, request, e.getClass().getName(), e.getMessage(), operationId);
                } else {
                    logger.error("Last try {} failed when executing request ({}). Aborting. The response did not match the request. Resetting the connection. Error details: {} {} [operation ID {}]", tryIndex, request, e.getClass().getName(), e.getMessage(), operationId);
                }
                if (endpoint instanceof ModbusSerialSlaveEndpoint) {
                    // Workaround for https://github.com/openhab/openhab-core/issues/1842
                    // Avoid disconnect/re-connect serial interfaces
                    logger.debug("Skipping invalidation of serial connection to workaround openhab-core#1842.");
                } else {
                    // Invalidate connection, and empty (so that new connection is acquired before new retry)
                    timer.connection.timeConsumer(c -> invalidate(endpoint, c), connection);
                    connection = Optional.empty();
                }
                continue;
            } catch (ModbusException e) {
                lastError.set(e);
                // Some other (unexpected) exception occurred
                if (willRetry) {
                    logger.warn("Try {} out of {} failed when executing request ({}). Will try again soon. Error was unexpected error, so resetting the connection. Error details: {} {} [operation ID {}]", tryIndex, maxTries, request, e.getClass().getName(), e.getMessage(), operationId, e);
                } else {
                    logger.error("Last try {} failed when executing request ({}). Aborting. Error was unexpected error, so resetting the connection. Error details: {} {} [operation ID {}]", tryIndex, request, e.getClass().getName(), e.getMessage(), operationId, e);
                }
                // Invalidate connection, and empty (so that new connection is acquired before new retry)
                timer.connection.timeConsumer(c -> invalidate(endpoint, c), connection);
                connection = Optional.empty();
                continue;
            } finally {
                lastTryMillis = System.currentTimeMillis();
                // Try to re-establish connection.
                if (willRetry && !connection.isPresent()) {
                    connection = getConnection(timer, oneOffTask, task);
                }
            }
        }
        Exception exception = lastError.get();
        if (exception != null) {
            // All retries failed with some error
            timer.callback.timeRunnable(() -> {
                invokeCallbackWithError(request, failureCallback, exception);
            });
        }
    } catch (PollTaskUnregistered e) {
        logger.warn("Poll task was unregistered -- not executing/proceeding with the poll: {} [operation ID {}]", e.getMessage(), operationId);
        return;
    } catch (InterruptedException e) {
        logger.warn("Poll task was canceled -- not executing/proceeding with the poll: {} [operation ID {}]", e.getMessage(), operationId);
        if (endpoint instanceof ModbusSerialSlaveEndpoint) {
            // Workaround for https://github.com/openhab/openhab-core/issues/1842
            // Avoid disconnect/re-connect serial interfaces
            logger.debug("Skipping invalidation of serial connection to workaround openhab-core#1842.");
        } else {
            // Invalidate connection, and empty (so that new connection is acquired before new retry)
            timer.connection.timeConsumer(c -> invalidate(endpoint, c), connection);
            connection = Optional.empty();
        }
    } finally {
        timer.connection.timeConsumer(c -> returnConnection(endpoint, c), connection);
        logger.trace("Connection was returned to the pool, ending operation [operation ID {}]", operationId);
        timer.suspendAllRunning();
        logger.debug("Modbus operation ended, timing info: {} [operation ID {}]", timer, operationId);
    }
}
Also used : ScheduledFuture(java.util.concurrent.ScheduledFuture) ModbusTransaction(net.wimpi.modbus.io.ModbusTransaction) LoggerFactory(org.slf4j.LoggerFactory) ModbusSlaveException(net.wimpi.modbus.ModbusSlaveException) Future(java.util.concurrent.Future) Nullable(org.eclipse.jdt.annotation.Nullable) Map(java.util.Map) ModbusCommunicationInterface(org.openhab.core.io.transport.modbus.ModbusCommunicationInterface) ModbusResponse(net.wimpi.modbus.msg.ModbusResponse) NonNullByDefault(org.eclipse.jdt.annotation.NonNullByDefault) ModbusReadCallback(org.openhab.core.io.transport.modbus.ModbusReadCallback) ModbusUDPSlaveEndpoint(org.openhab.core.io.transport.modbus.endpoint.ModbusUDPSlaveEndpoint) ModbusWriteCallback(org.openhab.core.io.transport.modbus.ModbusWriteCallback) Deactivate(org.osgi.service.component.annotations.Deactivate) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) ModbusSlaveConnectionFactoryImpl(org.openhab.core.io.transport.modbus.internal.pooling.ModbusSlaveConnectionFactoryImpl) Set(java.util.Set) Objects(java.util.Objects) TaskWithEndpoint(org.openhab.core.io.transport.modbus.TaskWithEndpoint) SwallowedExceptionListener(org.apache.commons.pool2.SwallowedExceptionListener) AsyncModbusWriteResult(org.openhab.core.io.transport.modbus.AsyncModbusWriteResult) ModbusRequest(net.wimpi.modbus.msg.ModbusRequest) Optional(java.util.Optional) NonNull(org.eclipse.jdt.annotation.NonNull) ModbusIOException(net.wimpi.modbus.ModbusIOException) ModbusReadRequestBlueprint(org.openhab.core.io.transport.modbus.ModbusReadRequestBlueprint) ThreadPoolExecutor(java.util.concurrent.ThreadPoolExecutor) AsyncModbusFailure(org.openhab.core.io.transport.modbus.AsyncModbusFailure) ModbusWriteRequestBlueprint(org.openhab.core.io.transport.modbus.ModbusWriteRequestBlueprint) IIOException(javax.imageio.IIOException) AtomicReference(java.util.concurrent.atomic.AtomicReference) Function(java.util.function.Function) Component(org.osgi.service.component.annotations.Component) ModbusTCPSlaveEndpoint(org.openhab.core.io.transport.modbus.endpoint.ModbusTCPSlaveEndpoint) ScheduledExecutorService(java.util.concurrent.ScheduledExecutorService) ModbusFailureCallback(org.openhab.core.io.transport.modbus.ModbusFailureCallback) ModbusUnexpectedResponseFunctionCodeException(org.openhab.core.io.transport.modbus.exception.ModbusUnexpectedResponseFunctionCodeException) ModbusSerialSlaveEndpoint(org.openhab.core.io.transport.modbus.endpoint.ModbusSerialSlaveEndpoint) Activate(org.osgi.service.component.annotations.Activate) LinkedList(java.util.LinkedList) Modbus(net.wimpi.modbus.Modbus) ModbusConnectionException(org.openhab.core.io.transport.modbus.exception.ModbusConnectionException) ThreadPoolManager(org.openhab.core.common.ThreadPoolManager) EndpointPoolConfiguration(org.openhab.core.io.transport.modbus.endpoint.EndpointPoolConfiguration) Logger(org.slf4j.Logger) GenericKeyedObjectPool(org.apache.commons.pool2.impl.GenericKeyedObjectPool) WriteTask(org.openhab.core.io.transport.modbus.WriteTask) ModbusResultCallback(org.openhab.core.io.transport.modbus.ModbusResultCallback) PollTask(org.openhab.core.io.transport.modbus.PollTask) IOException(java.io.IOException) ModbusUnexpectedTransactionIdException(org.openhab.core.io.transport.modbus.exception.ModbusUnexpectedTransactionIdException) TimeUnit(java.util.concurrent.TimeUnit) KeyedObjectPool(org.apache.commons.pool2.KeyedObjectPool) ModbusSlaveEndpointVisitor(org.openhab.core.io.transport.modbus.endpoint.ModbusSlaveEndpointVisitor) ModbusUnexpectedResponseSizeException(org.openhab.core.io.transport.modbus.exception.ModbusUnexpectedResponseSizeException) ModbusSlaveEndpoint(org.openhab.core.io.transport.modbus.endpoint.ModbusSlaveEndpoint) ModbusException(net.wimpi.modbus.ModbusException) ModbusManager(org.openhab.core.io.transport.modbus.ModbusManager) ModbusSlaveConnection(net.wimpi.modbus.net.ModbusSlaveConnection) ModbusSlaveException(net.wimpi.modbus.ModbusSlaveException) ModbusException(net.wimpi.modbus.ModbusException) ModbusSerialSlaveEndpoint(org.openhab.core.io.transport.modbus.endpoint.ModbusSerialSlaveEndpoint) PollTask(org.openhab.core.io.transport.modbus.PollTask) ModbusUnexpectedTransactionIdException(org.openhab.core.io.transport.modbus.exception.ModbusUnexpectedTransactionIdException) ModbusSlaveConnection(net.wimpi.modbus.net.ModbusSlaveConnection) ModbusSlaveEndpoint(org.openhab.core.io.transport.modbus.endpoint.ModbusSlaveEndpoint) AtomicReference(java.util.concurrent.atomic.AtomicReference) ModbusIOException(net.wimpi.modbus.ModbusIOException) IIOException(javax.imageio.IIOException) IOException(java.io.IOException) ModbusUDPSlaveEndpoint(org.openhab.core.io.transport.modbus.endpoint.ModbusUDPSlaveEndpoint) TaskWithEndpoint(org.openhab.core.io.transport.modbus.TaskWithEndpoint) ModbusReadRequestBlueprint(org.openhab.core.io.transport.modbus.ModbusReadRequestBlueprint) ModbusWriteRequestBlueprint(org.openhab.core.io.transport.modbus.ModbusWriteRequestBlueprint) ModbusTCPSlaveEndpoint(org.openhab.core.io.transport.modbus.endpoint.ModbusTCPSlaveEndpoint) ModbusSerialSlaveEndpoint(org.openhab.core.io.transport.modbus.endpoint.ModbusSerialSlaveEndpoint) ModbusSlaveEndpoint(org.openhab.core.io.transport.modbus.endpoint.ModbusSlaveEndpoint) ModbusSlaveException(net.wimpi.modbus.ModbusSlaveException) ModbusIOException(net.wimpi.modbus.ModbusIOException) IIOException(javax.imageio.IIOException) ModbusUnexpectedResponseFunctionCodeException(org.openhab.core.io.transport.modbus.exception.ModbusUnexpectedResponseFunctionCodeException) ModbusConnectionException(org.openhab.core.io.transport.modbus.exception.ModbusConnectionException) IOException(java.io.IOException) ModbusUnexpectedTransactionIdException(org.openhab.core.io.transport.modbus.exception.ModbusUnexpectedTransactionIdException) ModbusUnexpectedResponseSizeException(org.openhab.core.io.transport.modbus.exception.ModbusUnexpectedResponseSizeException) ModbusException(net.wimpi.modbus.ModbusException) ModbusIOException(net.wimpi.modbus.ModbusIOException) ModbusUnexpectedResponseFunctionCodeException(org.openhab.core.io.transport.modbus.exception.ModbusUnexpectedResponseFunctionCodeException) ModbusUnexpectedResponseSizeException(org.openhab.core.io.transport.modbus.exception.ModbusUnexpectedResponseSizeException) ModbusSlaveConnectionFactoryImpl(org.openhab.core.io.transport.modbus.internal.pooling.ModbusSlaveConnectionFactoryImpl) Nullable(org.eclipse.jdt.annotation.Nullable)

Aggregations

ModbusFailureCallback (org.openhab.core.io.transport.modbus.ModbusFailureCallback)3 AsyncModbusFailure (org.openhab.core.io.transport.modbus.AsyncModbusFailure)2 ModbusReadRequestBlueprint (org.openhab.core.io.transport.modbus.ModbusReadRequestBlueprint)2 PollTask (org.openhab.core.io.transport.modbus.PollTask)2 IOException (java.io.IOException)1 Field (java.lang.reflect.Field)1 LinkedList (java.util.LinkedList)1 Map (java.util.Map)1 Objects (java.util.Objects)1 Optional (java.util.Optional)1 Set (java.util.Set)1 ConcurrentHashMap (java.util.concurrent.ConcurrentHashMap)1 Future (java.util.concurrent.Future)1 ScheduledExecutorService (java.util.concurrent.ScheduledExecutorService)1 ScheduledFuture (java.util.concurrent.ScheduledFuture)1 ThreadPoolExecutor (java.util.concurrent.ThreadPoolExecutor)1 TimeUnit (java.util.concurrent.TimeUnit)1 AtomicReference (java.util.concurrent.atomic.AtomicReference)1 Function (java.util.function.Function)1 IIOException (javax.imageio.IIOException)1