/**
* Copyright (c) 2010-2017 by the respective copyright holders.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.openhab.binding.modbus.internal;
import static org.mockito.Mockito.*;
import java.net.UnknownHostException;
import java.util.Dictionary;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.junit.Test;
import org.openhab.binding.modbus.ModbusBindingProvider;
import org.openhab.core.library.items.SwitchItem;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.types.UnDefType;
import org.openhab.model.item.binding.BindingConfigParseException;
import org.osgi.service.cm.ConfigurationException;
import net.wimpi.modbus.procimg.SimpleDigitalIn;
import net.wimpi.modbus.procimg.SimpleDigitalOut;
/**
* Tests for erroring queries. Run only against TCP server.
*
*/
public class ErroringQueriesTestCase extends TestCaseSupport {
@SuppressWarnings("serial")
public static class ExpectedFailure extends AssertionError {
}
/**
* Test case for situation where we try to poll too much data.
*
* In this test server has
* - single discrete input
* - single coil inputs
*
* Binding is configured to read
* - two coils
*
* Items are follows
* - first discrete input (Item1) -> no output since query
* should fail
*/
@Test
public void testReadingTooMuch() throws UnknownHostException, ConfigurationException, BindingConfigParseException {
spi.addDigitalIn(new SimpleDigitalIn(true));
spi.addDigitalOut(new SimpleDigitalOut(true));
spi.addDigitalOut(new SimpleDigitalOut(false));
binding = new ModbusBinding();
Dictionary<String, Object> config = newLongPollBindingConfig();
addSlave(config, SLAVE_NAME, ModbusBindingProvider.TYPE_DISCRETE, null, 0, 2);
binding.updated(config);
// Configure items
final ModbusGenericBindingProvider provider = new ModbusGenericBindingProvider();
provider.processBindingConfiguration("test.items", new SwitchItem("Item1"),
String.format("%s:%d", SLAVE_NAME, 0));
binding.setEventPublisher(eventPublisher);
binding.addBindingProvider(provider);
binding.execute();
waitForConnectionsReceived(1);
waitForRequests(1);
verifyNoMoreInteractions(eventPublisher);
}
@Test
public void testConnectionTimeout()
throws UnknownHostException, ConfigurationException, BindingConfigParseException, InterruptedException {
/**
* Test that connection timeout is handled properly
*
* In the test we have non-responding slave (see http://stackoverflow.com/a/904609), and we use connection
* timeout of 300ms
*
* We assert that after 100ms the binding still have not given up on the connection (no UNDEF posted to the
* event bus)
* but after 400ms from the connection, the UNDEF is there.
*/
binding = new ModbusBinding();
Dictionary<String, Object> config = newLongPollBindingConfig();
addSlave(config, ServerType.TCP, "10.255.255.1:9999:0:0:0:1:300", SLAVE_NAME,
ModbusBindingProvider.TYPE_DISCRETE, null, 0, 0, 2);
putSlaveConfigParameter(config, serverType, SLAVE_NAME, "postundefinedonreaderror", "true");
binding.updated(config);
// Configure items
final ModbusGenericBindingProvider provider = new ModbusGenericBindingProvider();
provider.processBindingConfiguration("test.items", new SwitchItem("Item1"),
String.format("%s:%d", SLAVE_NAME, 0));
binding.setEventPublisher(eventPublisher);
binding.addBindingProvider(provider);
final CountDownLatch lock = new CountDownLatch(1);
Thread thread = new Thread() {
@Override
public void run() {
binding.execute();
lock.countDown();
};
};
try {
thread.start();
lock.await(100, TimeUnit.MILLISECONDS);
// After 100ms the binding has not yet given up, i.e. no UNDEF posted to event bus
verifyNoMoreInteractions(eventPublisher);
// After 100ms+300ms+margin 500ms the timeout of 300ms has passed and UNDEF should have been posted
lock.await(100 + 300 + 500, TimeUnit.MILLISECONDS);
verify(eventPublisher).postUpdate("Item1", UnDefType.UNDEF);
verifyNoMoreInteractions(eventPublisher);
} finally {
thread.interrupt();
}
}
@Test
public void testReadingTooMuchWithPostUndef()
throws UnknownHostException, ConfigurationException, BindingConfigParseException {
spi.addDigitalIn(new SimpleDigitalIn(true));
spi.addDigitalOut(new SimpleDigitalOut(true));
spi.addDigitalOut(new SimpleDigitalOut(false));
binding = new ModbusBinding();
Dictionary<String, Object> config = newLongPollBindingConfig();
addSlave(config, SLAVE_NAME, ModbusBindingProvider.TYPE_DISCRETE, null, 0, 2);
putSlaveConfigParameter(config, serverType, SLAVE_NAME, "postundefinedonreaderror", "true");
binding.updated(config);
// Configure items
final ModbusGenericBindingProvider provider = new ModbusGenericBindingProvider();
provider.processBindingConfiguration("test.items", new SwitchItem("Item1"),
String.format("%s:%d", SLAVE_NAME, 0));
binding.setEventPublisher(eventPublisher);
binding.addBindingProvider(provider);
binding.execute();
waitForConnectionsReceived(1);
waitForRequests(1);
verify(eventPublisher).postUpdate("Item1", UnDefType.UNDEF);
verifyNoMoreInteractions(eventPublisher);
}
/**
* Test case for situation where we try to poll too much data.
*
* In this test server has
* - single coil
* - two discrete inputs
*
* Binding is configured to read
* - two coils
* - two discrete inputs
*
* Items are follows
* - first (index=0) coil (Item1) -> no output since coil query should fail
* - index=1 discrete (Item2) should be OK
* - index=2 discrete (Item3) no event transmitted, item readIndex out of bounds. WARN logged
*/
@Test
public void testReadingTooMuchTwoSlaves()
throws UnknownHostException, ConfigurationException, BindingConfigParseException {
spi.addDigitalOut(new SimpleDigitalOut(true));
spi.addDigitalIn(new SimpleDigitalIn(true));
spi.addDigitalIn(new SimpleDigitalIn(true));
spi.addDigitalIn(new SimpleDigitalIn(true));
binding = new ModbusBinding();
Dictionary<String, Object> config = newLongPollBindingConfig();
addSlave(config, SLAVE_NAME, ModbusBindingProvider.TYPE_COIL, null, 0, 2);
addSlave(config, SLAVE2_NAME, ModbusBindingProvider.TYPE_DISCRETE, null, 0, 2);
binding.updated(config);
// Configure items
final ModbusGenericBindingProvider provider = new ModbusGenericBindingProvider();
provider.processBindingConfiguration("test.items", new SwitchItem("Item1"),
String.format("%s:%d", SLAVE_NAME, 0));
provider.processBindingConfiguration("test.items", new SwitchItem("Item2"),
String.format("%s:%d", SLAVE2_NAME, 1));
provider.processBindingConfiguration("test.items", new SwitchItem("Item3"),
String.format("%s:%d", SLAVE2_NAME, 2));
binding.setEventPublisher(eventPublisher);
binding.addBindingProvider(provider);
binding.execute();
// Give the system some time to make the expected connections & requests
waitForConnectionsReceived(2);
waitForRequests(2);
verify(eventPublisher, never()).postCommand(null, null);
verify(eventPublisher, never()).sendCommand(null, null);
verify(eventPublisher).postUpdate("Item2", OnOffType.ON);
verifyNoMoreInteractions(eventPublisher);
}
}