/**
* 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.Arrays;
import java.util.Collection;
import java.util.Dictionary;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.openhab.binding.modbus.ModbusBindingProvider;
import org.openhab.binding.modbus.internal.Transformation.TransformationHelperWrapper;
import org.openhab.core.library.items.NumberItem;
import org.openhab.core.library.items.StringItem;
import org.openhab.core.library.items.SwitchItem;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.transform.TransformationException;
import org.openhab.core.transform.TransformationService;
import org.openhab.model.item.binding.BindingConfigParseException;
import org.osgi.framework.BundleContext;
import org.osgi.service.cm.ConfigurationException;
import net.wimpi.modbus.procimg.SimpleDigitalIn;
import net.wimpi.modbus.procimg.SimpleDigitalOut;
/**
* Tests for items with extended syntax. Run only against TCP server.
*
*/
@RunWith(Parameterized.class)
public class ReadCoilsAndDiscreteExtendedItemConfigurationTestCase extends TestCaseSupport {
private ModbusGenericBindingProvider provider;
private String type;
@Parameters
public static Collection<Object[]> parameters() {
return Arrays.asList(
new Object[][] { { ModbusBindingProvider.TYPE_DISCRETE }, { ModbusBindingProvider.TYPE_COIL } });
}
public ReadCoilsAndDiscreteExtendedItemConfigurationTestCase(String type) {
if (!type.equals(ModbusBindingProvider.TYPE_DISCRETE) && !type.equals(ModbusBindingProvider.TYPE_COIL)) {
throw new IllegalStateException("Test does not support this type");
}
this.type = type;
}
private void addBinary(boolean value) {
if (type.equals(ModbusBindingProvider.TYPE_DISCRETE)) {
spi.addDigitalIn(new SimpleDigitalIn(value));
} else {
spi.addDigitalOut(new SimpleDigitalOut(value));
}
}
private void setBinary(int idx, boolean value) {
if (type.equals(ModbusBindingProvider.TYPE_DISCRETE)) {
spi.setDigitalIn(idx, new SimpleDigitalIn(value));
} else {
spi.setDigitalOut(idx, new SimpleDigitalOut(value));
}
}
@Before
public void initSlaveAndServer() throws Exception {
addBinary(false);
addBinary(true);
binding = new ModbusBinding();
Dictionary<String, Object> config = newLongPollBindingConfig();
addSlave(config, SLAVE_NAME, type, null, 0, 2);
binding.updated(config);
provider = new ModbusGenericBindingProvider();
binding.setEventPublisher(eventPublisher);
binding.addBindingProvider(provider);
}
@Test
public void testSwitchItemSingleReadWithTriggerNoMatch()
throws UnknownHostException, ConfigurationException, BindingConfigParseException {
provider.processBindingConfiguration("test.items", new SwitchItem("Item1"),
String.format("<[%s:0:trigger=ON]", SLAVE_NAME));
binding.execute();
waitForConnectionsReceived(1);
waitForRequests(1);
verifyNoMoreInteractions(eventPublisher);
}
@Test
public void testSwitchItemSingleReadWithTriggerMatch()
throws UnknownHostException, ConfigurationException, BindingConfigParseException {
provider.processBindingConfiguration("test.items", new SwitchItem("Item1"),
String.format("<[%s:0:trigger=OFF]", SLAVE_NAME));
binding.execute();
waitForConnectionsReceived(1);
waitForRequests(1);
verify(eventPublisher).postUpdate("Item1", OnOffType.OFF);
verifyNoMoreInteractions(eventPublisher);
}
@Test
public void testSwitchItemSingleReadWithTriggerMatch2()
throws UnknownHostException, ConfigurationException, BindingConfigParseException {
provider.processBindingConfiguration("test.items", new SwitchItem("Item1"),
String.format("<[%s:1:trigger=ON]", SLAVE_NAME));
binding.execute();
waitForConnectionsReceived(1);
waitForRequests(1);
verify(eventPublisher).postUpdate("Item1", OnOffType.ON);
verifyNoMoreInteractions(eventPublisher);
}
@Test
public void testReadOnlyItemCommandNoEffect()
throws UnknownHostException, ConfigurationException, BindingConfigParseException {
provider.processBindingConfiguration("test.items", new SwitchItem("Item1"),
String.format("<[%s:1:trigger=ON]", SLAVE_NAME));
binding.execute();
waitForConnectionsReceived(1);
waitForRequests(1);
verify(eventPublisher).postUpdate("Item1", OnOffType.ON);
verifyNoMoreInteractions(eventPublisher);
binding.receiveCommand("Item1", OnOffType.OFF);
verifyNoMoreInteractions(eventPublisher);
}
@Test
public void testNumberItemSingleReadRegisterComplextTransformation()
throws UnknownHostException, ConfigurationException, BindingConfigParseException {
provider.processBindingConfiguration("test.items", new NumberItem("Item1"),
String.format("<[%s:1:trigger=*,transformation=MULTIPLY(5)]", SLAVE_NAME));
ModbusBindingConfig config = provider.getConfig("Item1");
// Inject transformation
for (ItemIOConnection itemIOConnection : config.getReadConnections()) {
itemIOConnection.getTransformation().setTransformationHelper(new TransformationHelperWrapper() {
@Override
public TransformationService getTransformationService(BundleContext context,
String transformationServiceName) {
if ("MULTIPLY".equals(transformationServiceName)) {
return new TransformationService() {
@Override
public String transform(String multiplier, String arg) throws TransformationException {
return String.valueOf(Integer.valueOf(multiplier) * Integer.valueOf(arg));
}
};
} else {
throw new AssertionError("unexpected transformation");
}
}
});
}
binding.execute();
waitForConnectionsReceived(1);
waitForRequests(1);
// boolean was converted to number 5 (True -> 1 -> 1*5 = 5)
verify(eventPublisher).postUpdate("Item1", new DecimalType(5));
verifyNoMoreInteractions(eventPublisher);
}
@Test
public void testNumberItemSingleReadRegisterComplextTransformation2()
throws UnknownHostException, ConfigurationException, BindingConfigParseException {
provider.processBindingConfiguration("test.items", new StringItem("Item1"),
String.format("<[%s:1:trigger=*,transformation=STRINGMULTIPLY(5)]", SLAVE_NAME));
ModbusBindingConfig config = provider.getConfig("Item1");
// Inject transformation
for (ItemIOConnection itemIOConnection : config.getReadConnections()) {
itemIOConnection.getTransformation().setTransformationHelper(new TransformationHelperWrapper() {
@Override
public TransformationService getTransformationService(BundleContext context,
String transformationServiceName) {
if ("STRINGMULTIPLY".equals(transformationServiceName)) {
return new TransformationService() {
@Override
public String transform(String multiplier, String arg) throws TransformationException {
return "foob" + String.valueOf(Integer.valueOf(multiplier) * Integer.valueOf(arg));
}
};
} else {
throw new AssertionError("unexpected transformation");
}
}
});
}
binding.execute();
waitForConnectionsReceived(1);
waitForRequests(1);
verify(eventPublisher).postUpdate("Item1", new StringType("foob" + 5));
verifyNoMoreInteractions(eventPublisher);
}
@Test
public void testNumberItemReadTwoDiscreteComplexTransformation3()
throws UnknownHostException, ConfigurationException, BindingConfigParseException {
provider.processBindingConfiguration("test.items", new StringItem("Item1"),
String.format(
"<[%1$s:0:type=STATE,trigger=CHANGED,transformation=STRINGMULTIPLY(5)],<[%1$s:1:type=STATE,trigger=CHANGED,transformation=PLUS(7)]",
SLAVE_NAME));
ModbusBindingConfig config = provider.getConfig("Item1");
// Inject transformation
for (ItemIOConnection itemIOConnection : config.getReadConnections()) {
itemIOConnection.getTransformation().setTransformationHelper(new TransformationHelperWrapper() {
@Override
public TransformationService getTransformationService(BundleContext context,
String transformationServiceName) {
if ("STRINGMULTIPLY".equals(transformationServiceName)) {
return new TransformationService() {
@Override
public String transform(String multiplier, String arg) throws TransformationException {
return "foobar_" + String.valueOf(Integer.valueOf(multiplier) * Integer.valueOf(arg));
}
};
} else if ("PLUS".equals(transformationServiceName)) {
return new TransformationService() {
@Override
public String transform(String offset, String arg) throws TransformationException {
return "foobar_" + String.valueOf(Integer.valueOf(offset) + Integer.valueOf(arg));
}
};
}
else {
throw new AssertionError("unexpected transformation");
}
}
});
}
binding.execute();
waitForConnectionsReceived(1);
waitForRequests(1);
// two events on the first time(the changed-on-poll check is done for each connection separately)
verify(eventPublisher).postUpdate("Item1", new StringType("foobar_0")); // false * 5 = 0
verify(eventPublisher).postUpdate("Item1", new StringType("foobar_8")); // true + 7 = 8
verifyNoMoreInteractions(eventPublisher);
reset(eventPublisher);
binding.execute();
waitForConnectionsReceived(2);
waitForRequests(2);
// no events on the second time, nothing has changed
verifyNoMoreInteractions(eventPublisher);
// modify server data
setBinary(0, true);
reset(eventPublisher);
binding.execute();
waitForConnectionsReceived(3);
waitForRequests(3);
// only the PLUS connection was activated
verify(eventPublisher).postUpdate("Item1", new StringType("foobar_5")); // true * 5 = 5
verifyNoMoreInteractions(eventPublisher);
}
}