/** * Copyright (c) 2010-2016 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.wago.internal; import java.io.InputStream; import java.net.InetAddress; import java.net.URL; import java.net.URLConnection; import java.net.UnknownHostException; import java.util.Collection; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.openhab.binding.wago.internal.WagoGenericBindingProvider.WagoBindingConfig; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.IncreaseDecreaseType; import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.OpenClosedType; import org.openhab.core.library.types.UpDownType; import org.openhab.core.types.Command; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import net.wimpi.modbus.io.ModbusTCPTransaction; import net.wimpi.modbus.msg.ModbusRequest; import net.wimpi.modbus.msg.ReadCoilsRequest; import net.wimpi.modbus.msg.ReadCoilsResponse; import net.wimpi.modbus.msg.ReadMultipleRegistersRequest; import net.wimpi.modbus.msg.ReadMultipleRegistersResponse; import net.wimpi.modbus.msg.WriteCoilRequest; import net.wimpi.modbus.msg.WriteMultipleRegistersRequest; import net.wimpi.modbus.net.TCPMasterConnection; import net.wimpi.modbus.procimg.Register; import net.wimpi.modbus.procimg.SimpleRegister; /** * This class represents the wago-field-bus-coupler with all of its modules and * configurations. * * @author Kaltofen * @since 1.7.0 */ public class FBCoupler { private static final Logger logger = LoggerFactory.getLogger(FBCoupler.class); final int DIStart = 0; // Start of the input-coils final int DOStart = 512; // Start of the output-coils final int IRStart = 0; // Start of the input-registers final int ORStart = 512; // Start of the output-registers String name; TCPMasterConnection connection; ModbusTCPTransaction transaction; String ip; int modbusPort = 502; int ftpPort = 21; String username = "user"; String password = "user"; Module modules[]; public void setIp(String ip) { this.ip = ip; } public void setModbus(int port) { modbusPort = port; } public void setFTP(int port) { ftpPort = port; } public void setUsername(String username) { this.username = username; } public void setPassword(String password) { this.password = password; } public void setup() { try { URL url = new URL( "ftp://" + username + ":" + password + "@" + ip + ":" + ftpPort + "/etc/EA-config.xml;type=i"); URLConnection urlc = url.openConnection(); InputStream is = urlc.getInputStream(); DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); Document doc = dBuilder.parse(is); doc.getDocumentElement().normalize(); NodeList moduleList = doc.getElementsByTagName("Module"); modules = new Module[moduleList.getLength()]; for (int i = 0; i < moduleList.getLength(); i++) { Node moduleXML = moduleList.item(i); NamedNodeMap attributes = moduleXML.getAttributes(); String article = attributes.getNamedItem("ARTIKELNR").getNodeValue(); String type = attributes.getNamedItem("MODULETYPE").getNodeValue(); int channelcount = Integer.parseInt(attributes.getNamedItem("CHANNELCOUNT").getNodeValue()); // int DIOffset = DIStart; int DOOffset = DOStart; // int IROffset = IRStart; int OROffset = ORStart; if (type.equals("DO")) { modules[i] = new DOModule(article, DOOffset, channelcount); DOOffset += channelcount; } else if (type.equals("COMPLEX")) { if (article.equals("750-511/000-000")) { // PWM-Module modules[i] = new PWMModule(article, OROffset); OROffset += 4; // 4 registers for 2 outputs } } } } catch (Exception e) { logger.warn("wago-coupler setup failed."); } } public boolean connect() { try { if (connection == null) { connection = new TCPMasterConnection(InetAddress.getByName(ip)); } } catch (UnknownHostException e) { logger.warn("unable to connect to wago-coupler."); return false; } if (!connection.isConnected()) { try { connection.setPort(modbusPort); connection.connect(); if (transaction == null) { transaction = new ModbusTCPTransaction(); } transaction.setConnection(connection); transaction.setReconnecting(false); } catch (Exception e) { logger.warn("unable to connect to wago-coupler: " + e.getMessage()); return false; } } return true; } public FBCoupler(String name) { this.name = name; } public void executeCommand(Command command, WagoBindingConfig conf) { Module module = modules[conf.module]; if (module != null) { module.executeCommand(command, conf.channel); } else { logger.warn("module " + module + " wasn't correctly initialized."); } } public void update(WagoBinding binding) { int i = 0; for (Module module : modules) { if (module != null) { module.update(binding, name, i); } i++; } } class Module { String article; String type; int offset; int chancount; public String getArticle() { return article; } public String getType() { return type; } public int getChannelcount() { return chancount; } public void update(WagoBinding binding, String couplerName, int module) { } public void executeCommand(Command command, int channel) { } Module(String article, String type, int offset, int chancount) { this.article = article; this.type = type; this.offset = offset; this.chancount = chancount; } } class DOModule extends Module { boolean state[]; DOModule(String article, int offset, int chancount) { super(article, "DO", offset, chancount); state = new boolean[chancount]; } @Override public void update(WagoBinding binding, String couplerName, int module) { if (!connect()) { logger.warn("coupler not connected."); return; } ModbusRequest request = new ReadCoilsRequest(offset, chancount); transaction.setRequest(request); try { transaction.execute(); } catch (Exception e) { logger.debug("update of channels failed: " + e.getMessage()); return; } ReadCoilsResponse response = (ReadCoilsResponse) transaction.getResponse(); if ((response.getTransactionID() != transaction.getTransactionID()) && !response.isHeadless()) { logger.debug("update of channels failed: invalid response."); return; } for (int i = 0; i < chancount; i++) { state[i] = response.getCoils().getBit(i); } Collection<String> itemNames = binding.getItemNames(); for (String itemName : itemNames) { binding.updateItem(itemName, couplerName, module, state); } } private boolean translateCommand2Boolean(Command command) { if (command.equals(OnOffType.ON)) { return true; } if (command.equals(OnOffType.OFF)) { return false; } if (command.equals(OpenClosedType.OPEN)) { return true; } if (command.equals(OpenClosedType.CLOSED)) { return false; } throw new IllegalArgumentException("command not supported"); } @Override public void executeCommand(Command command, int channel) { if (!connect()) { logger.warn("coupler not connected."); return; } try { if (translateCommand2Boolean(command)) { switchON(channel); } else { switchOFF(channel); } } catch (IllegalArgumentException e) { e.printStackTrace(); } } private void setCoil(int channel, boolean state) { ModbusRequest request = new WriteCoilRequest(offset + channel, state); transaction.setRequest(request); try { transaction.execute(); } catch (Exception e) { logger.debug("can't set channel " + channel + " of digital output."); return; } } private void switchON(int channel) { if (state[channel] != true) { setCoil(channel, true); state[channel] = true; logger.debug("switching channel " + channel + " on."); } } private void switchOFF(int channel) { if (state[channel] != false) { setCoil(channel, false); state[channel] = false; logger.debug("switching channel " + channel + " off."); } } public boolean getState(int channel) { return state[channel]; } } class PWMModule extends Module { int values[]; public PWMModule(String article, int offset) { super(article, "COMPLEX", offset, 2); values = new int[2]; } @Override public void update(WagoBinding binding, String couplerName, int module) { if (!connect()) { logger.warn("coupler not connected."); return; } ModbusRequest request = new ReadMultipleRegistersRequest(offset, 4); transaction.setRequest(request); try { transaction.execute(); } catch (Exception e) { logger.debug("update of channels failed: " + e.getMessage()); return; } ReadMultipleRegistersResponse response = (ReadMultipleRegistersResponse) transaction.getResponse(); if ((response.getTransactionID() != transaction.getTransactionID()) && !response.isHeadless()) { logger.debug("update of channels failed: invalid response."); return; } values[0] = response.getRegister(1).getValue() >> 4; values[1] = response.getRegister(3).getValue() >> 4; Collection<String> itemNames = binding.getItemNames(); for (String itemName : itemNames) { binding.updateItemPWM(itemName, couplerName, module, values); } } @Override public void executeCommand(Command command, int channel) { if (!connect()) { logger.warn("coupler not connected."); return; } if (command instanceof IncreaseDecreaseType || command instanceof UpDownType) { int value = (int) ((float) values[channel] / 1023 * 100); if (command.equals(command.equals(IncreaseDecreaseType.INCREASE)) || command.equals(UpDownType.UP)) { value += 1; } else if (command.equals(IncreaseDecreaseType.DECREASE) || command.equals(UpDownType.DOWN)) { value -= 1; } value = (int) ((float) value / 100 * 1023); if (value > 1023) { value = 1023; } else if (value < 0) { value = 0; } setValue(channel, value); } else if (command instanceof OnOffType) { if (command.equals(OnOffType.ON)) { setValue(channel, 1023); } else if (command.equals(OnOffType.OFF)) { setValue(channel, 0); } } else if (command instanceof DecimalType) { DecimalType percentage = (DecimalType) command; int value = (int) ((float) percentage.intValue() / 100 * 1023); setValue(channel, value); } } public void setValue(int channel, int value) { values[channel] = value; Register reg[] = new SimpleRegister[2]; reg[0] = new SimpleRegister(0); // Must be set to 0 reg[1] = new SimpleRegister(value << 4);// << 20); // 4 + 16 = 20 ModbusRequest request = new WriteMultipleRegistersRequest(offset + channel * 2, reg); transaction.setRequest(request); try { transaction.execute(); } catch (Exception e) { logger.debug("can't set channel " + channel + " of PWM module."); return; } } public int getValue(int channel) { return values[channel]; } } }