Java tutorial
/** * Copyright (c) 2010-2015, openHAB.org and others. * * 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 java.util.Collection; import java.util.Dictionary; import java.util.Enumeration; import java.util.HashSet; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.ByteOrder; import net.wimpi.modbus.procimg.InputRegister; import net.wimpi.modbus.util.BitVector; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.StringUtils; import org.openhab.binding.modbus.ModbusBindingProvider; import org.openhab.binding.modbus.internal.ModbusGenericBindingProvider.ModbusBindingConfig; import org.openhab.core.binding.AbstractActiveBinding; import org.openhab.core.binding.BindingProvider; import org.openhab.core.library.items.NumberItem; 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.types.Command; import org.openhab.core.types.State; import org.osgi.service.cm.ConfigurationException; import org.osgi.service.cm.ManagedService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Modbus binding allows to connect to multiple Modbus slaves as TCP master. * This implementation works with coils (boolean values) only. * * @author Dmitry Krasnov * @since 1.1.0 */ public class ModbusBinding extends AbstractActiveBinding<ModbusBindingProvider> implements ManagedService { private static final Logger logger = LoggerFactory.getLogger(ModbusBinding.class); private static final String UDP_PREFIX = "udp"; private static final String TCP_PREFIX = "tcp"; private static final String SERIAL_PREFIX = "serial"; private static final String VALID_COFIG_KEYS = "connection|id|start|length|type|valuetype|rawdatamultiplier|writemultipleregisters"; private static final Pattern EXTRACT_MODBUS_CONFIG_PATTERN = Pattern.compile("^(" + TCP_PREFIX + "|" + UDP_PREFIX + "|" + SERIAL_PREFIX + "|)\\.(.*?)\\.(" + VALID_COFIG_KEYS + ")$"); /** Stores instances of all the slaves defined in cfg file */ private static Map<String, ModbusSlave> modbusSlaves = new ConcurrentHashMap<String, ModbusSlave>(); /** slaves update interval in milliseconds, defaults to 200ms */ public static int pollInterval = 200; public void activate() { } public void deactivate() { } @Override protected long getRefreshInterval() { return pollInterval; } @Override protected String getName() { return "Modbus Polling Service"; } /** * Parses configuration creating Modbus slave instances defined in cfg file * {@inheritDoc} */ protected void internalReceiveCommand(String itemName, Command command) { for (ModbusBindingProvider provider : providers) { if (provider.providesBindingFor(itemName)) { ModbusBindingConfig config = provider.getConfig(itemName); ModbusSlave slave = modbusSlaves.get(config.slaveName); slave.executeCommand(command, config.readRegister, config.writeRegister); } } } /** * Posts update event to OpenHAB bus for "holding" type slaves * @param binding ModbusBinding to get item configuration from BindingProviding * @param registers data received from slave device in the last pollInterval * @param itemName item to update */ protected void internalUpdateItem(String slaveName, InputRegister[] registers, String itemName) { for (ModbusBindingProvider provider : providers) { if (!provider.providesBindingFor(itemName)) { continue; } ModbusBindingConfig config = provider.getConfig(itemName); if (!config.slaveName.equals(slaveName)) { continue; } String slaveValueType = modbusSlaves.get(slaveName).getValueType(); double rawDataMultiplier = modbusSlaves.get(slaveName).getRawDataMultiplier(); State newState = extractStateFromRegisters(registers, config.readRegister, slaveValueType); /* receive data manipulation */ if (config.getItem() instanceof SwitchItem) { newState = newState.equals(DecimalType.ZERO) ? OnOffType.OFF : OnOffType.ON; } if ((rawDataMultiplier != 1) && (config.getItem() instanceof NumberItem)) { double tmpValue = (double) ((DecimalType) newState).doubleValue() * rawDataMultiplier; newState = new DecimalType(String.valueOf(tmpValue)); } State currentState = config.getItemState(); if (!newState.equals(currentState)) { eventPublisher.postUpdate(itemName, newState); } } } private DecimalType extractStateFromRegisters(InputRegister[] registers, int index, String type) { if (type.equals(ModbusBindingProvider.VALUE_TYPE_BIT)) { return new DecimalType((registers[index / 16].toUnsignedShort() >> (index % 16)) & 1); } else if (type.equals(ModbusBindingProvider.VALUE_TYPE_INT8)) { return new DecimalType(registers[index / 2].toBytes()[1 - (index % 2)]); } else if (type.equals(ModbusBindingProvider.VALUE_TYPE_UINT8)) { return new DecimalType((registers[index / 2].toUnsignedShort() >> (8 * (index % 2))) & 0xff); } else if (type.equals(ModbusBindingProvider.VALUE_TYPE_INT16)) { ByteBuffer buff = ByteBuffer.allocate(2); buff.put(registers[index].toBytes()); return new DecimalType(buff.order(ByteOrder.BIG_ENDIAN).getShort(0)); } else if (type.equals(ModbusBindingProvider.VALUE_TYPE_UINT16)) { return new DecimalType(registers[index].toUnsignedShort()); } else if (type.equals(ModbusBindingProvider.VALUE_TYPE_INT32)) { ByteBuffer buff = ByteBuffer.allocate(4); buff.put(registers[index * 2 + 0].toBytes()); buff.put(registers[index * 2 + 1].toBytes()); return new DecimalType(buff.order(ByteOrder.BIG_ENDIAN).getInt(0)); } else if (type.equals(ModbusBindingProvider.VALUE_TYPE_UINT32)) { ByteBuffer buff = ByteBuffer.allocate(8); buff.position(4); buff.put(registers[index * 2 + 0].toBytes()); buff.put(registers[index * 2 + 1].toBytes()); return new DecimalType(buff.order(ByteOrder.BIG_ENDIAN).getLong(0)); } else if (type.equals(ModbusBindingProvider.VALUE_TYPE_FLOAT32)) { ByteBuffer buff = ByteBuffer.allocate(4); buff.put(registers[index * 2 + 0].toBytes()); buff.put(registers[index * 2 + 1].toBytes()); return new DecimalType(buff.order(ByteOrder.BIG_ENDIAN).getFloat(0)); } else { throw new IllegalArgumentException(); } } /** * Posts update event to OpenHAB bus for "coil" type slaves * @param binding ModbusBinding to get item configuration from BindingProviding * @param registers data received from slave device in the last pollInterval * @param item item to update */ protected void internalUpdateItem(String slaveName, BitVector coils, String itemName) { for (ModbusBindingProvider provider : providers) { if (provider.providesBindingFor(itemName)) { ModbusBindingConfig config = provider.getConfig(itemName); if (config.slaveName.equals(slaveName)) { boolean state = coils.getBit(config.readRegister); State currentState = provider.getConfig(itemName).getItemState(); State newState = provider.getConfig(itemName).translateBoolean2State(state); if (!newState.equals(currentState)) { eventPublisher.postUpdate(itemName, newState); } } } } } /** * Returns names of all the items, registered with this binding * @return list of item names */ public Collection<String> getItemNames() { Collection<String> items = null; for (BindingProvider provider : providers) { if (items == null) items = provider.getItemNames(); else items.addAll(provider.getItemNames()); } return items; } /** * updates all slaves from the modbusSlaves */ @Override protected void execute() { Collection<ModbusSlave> slaves = new HashSet<ModbusSlave>(); synchronized (slaves) { slaves.addAll(modbusSlaves.values()); } for (ModbusSlave slave : slaves) { slave.update(this); } } @Override public void updated(Dictionary<String, ?> config) throws ConfigurationException { // remove all known items if configuration changed modbusSlaves.clear(); if (config != null) { Enumeration<String> keys = config.keys(); while (keys.hasMoreElements()) { String key = (String) keys.nextElement(); // the config-key enumeration contains additional keys that we // 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)) { 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_COFIG_KEYS); } continue; } matcher.reset(); matcher.find(); String slave = matcher.group(2); ModbusSlave modbusSlave = modbusSlaves.get(slave); if (modbusSlave == null) { if (matcher.group(1).equals(TCP_PREFIX)) { modbusSlave = new ModbusTcpSlave(slave); } else if (matcher.group(1).equals(UDP_PREFIX)) { modbusSlave = new ModbusUdpSlave(slave); } else if (matcher.group(1).equals(SERIAL_PREFIX)) { modbusSlave = new ModbusSerialSlave(slave); } else { throw new ConfigurationException(slave, "the given slave type '" + slave + "' is unknown"); } logger.debug("modbusSlave '{}' instanciated", slave); modbusSlaves.put(slave, modbusSlave); } String configKey = matcher.group(3); String value = (String) config.get(key); if ("connection".equals(configKey)) { String[] chunks = value.split(":"); if (modbusSlave instanceof ModbusIPSlave) { // expecting: // <devicePort>:<port> ((ModbusIPSlave) modbusSlave).setHost(chunks[0]); if (chunks.length == 2) { ((ModbusIPSlave) modbusSlave).setPort(Integer.valueOf(chunks[1])); } } else if (modbusSlave instanceof ModbusSerialSlave) { // expecting: // <devicePort>[:<baudRate>:<dataBits>:<parity>:<stopBits>:<encoding>] ((ModbusSerialSlave) modbusSlave).setPort(chunks[0]); if (chunks.length >= 2) { ((ModbusSerialSlave) modbusSlave).setBaud(Integer.valueOf(chunks[1])); } if (chunks.length >= 3) { ((ModbusSerialSlave) modbusSlave).setDatabits(Integer.valueOf(chunks[2])); } if (chunks.length >= 4) { ((ModbusSerialSlave) modbusSlave).setParity(chunks[3]); } if (chunks.length >= 5) { ((ModbusSerialSlave) modbusSlave).setStopbits(Double.valueOf(chunks[4])); } if (chunks.length == 6) { ((ModbusSerialSlave) modbusSlave).setEncoding(chunks[5]); } } } 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 { throw new ConfigurationException(configKey, "the given configKey '" + configKey + "' is unknown"); } } logger.debug("config looked good, proceeding with slave-connections"); // connect instances to modbus slaves for (ModbusSlave slave : modbusSlaves.values()) { slave.connect(); } setProperlyConfigured(true); } } }