/**
* Copyright (c) 2010-2016, 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.fatekplc.internal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.openhab.binding.fatekplc.FatekPLCBindingProvider;
import org.openhab.binding.fatekplc.items.FatekPLCItem;
import org.openhab.core.binding.AbstractActiveBinding;
import org.openhab.core.types.Command;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedService;
import org.simplify4u.jfatek.io.FatekIOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Binding implementation for Fatek PLC.
*
* @author Slawomir Jaranowski
* @since 1.9.0
*/
public class FatekPLCBinding extends AbstractActiveBinding<FatekPLCBindingProvider> implements ManagedService {
/**
* Main logger.
*/
private static final Logger logger = LoggerFactory.getLogger(FatekPLCBinding.class);
/**
* Globals config keys name
*/
private static final List<String> GLOBAL_CONFIG_NAMES = Arrays.asList("refresh", "service.pid");
/**
* the refresh interval which is used to poll values from the Fatek PLC server (optional, defaults to 60000ms)
*/
private long refreshInterval = 60000;
/**
* Map of slave device.
*/
private Map<String, FatekPLCSlave> slaves = new HashMap<>();
public FatekPLCBinding() {
}
/**
* {@inheritDoc}
*/
@Override
protected long getRefreshInterval() {
return refreshInterval;
}
/**
* {@inheritDoc}
*/
@Override
protected String getName() {
return "Fatek PLC Refresh Service";
}
/**
* Called by the SCR to deactivate the component when either the configuration is removed or mandatory references
* are no longer satisfied or the component has simply been stopped.
*
* @param reason
* Reason code for the deactivation:<br>
* <ul>
* <li>0 Unspecified
* <li>1 The component was disabled
* <li>2 A reference became unsatisfied
* <li>3 A configuration was changed
* <li>4 A configuration was deleted
* <li>5 The component was disposed
* <li>6 The bundle was stopped
* </ul>
*/
public void deactivate(final int reason) {
setProperlyConfigured(false);
logger.debug("deactivate executed reason={}", reason);
}
@Override
public void updated(Dictionary<String, ?> properties) throws ConfigurationException {
logger.debug("update start");
try {
// stop and clear current slaves
for (FatekPLCSlave slave : slaves.values()) {
try {
slave.disconnect();
} catch (FatekIOException e) {
logger.warn("diconnect", e);
}
}
slaves.clear();
// global configurations
refreshInterval = getLongProperty(properties, "refresh", refreshInterval);
// slaves configurations
Enumeration<String> keys = properties.keys();
while (keys.hasMoreElements()) {
String key = keys.nextElement();
// skip global configs item
if (GLOBAL_CONFIG_NAMES.contains(key)) {
continue;
}
String keyItems[] = key.split("\\.");
if (keyItems.length != 2) {
throw new ConfigurationException(key, "incorrect format, should be slave.property");
}
FatekPLCSlave slave = slaves.get(keyItems[0]);
if (slave == null) {
slave = new FatekPLCSlave(keyItems[0], eventPublisher);
slaves.put(slave.getName(), slave);
}
slave.configure(keyItems[1], properties.get(key));
}
} catch (ConfigurationException e) {
logger.error("", e);
throw e;
}
setProperlyConfigured(true);
logger.info("Configuration has been updated, refresh={}", refreshInterval);
}
/**
* Parse long from property with default.
*
* @param properties
* properties map
* @param key
* given key
* @param defaultVale
* @return long value for given key
*/
private long getLongProperty(Dictionary<String, ?> properties, String key, long defaultVale) {
String value = (String) properties.get(key);
long ret;
try {
ret = Long.parseLong(value);
} catch (NumberFormatException e) {
ret = defaultVale;
logger.warn("Property {} parse exception {}", key, e.getMessage());
}
return ret;
}
/**
* {@inheritDoc}
*/
@Override
protected void execute() {
Map<String, List<FatekPLCItem>> toUpdate = new HashMap<>();
// collect items by slave PLC
for (FatekPLCBindingProvider provider : providers) {
for (String itemName : provider.getItemNames()) {
FatekPLCItem item = provider.geFatektItem(itemName);
if (item.isToRefresh()) {
List<FatekPLCItem> itemsList = toUpdate.get(item.getSlaveName());
if (itemsList == null) {
itemsList = new ArrayList<>();
toUpdate.put(item.getSlaveName(), itemsList);
}
itemsList.add(item);
}
}
}
logger.debug("toUpdate={}", toUpdate);
// process list to update
for (String slaveName : toUpdate.keySet()) {
FatekPLCSlave slave = slaves.get(slaveName);
List<FatekPLCItem> items = toUpdate.get(slaveName);
if (slave != null) {
try {
slave.updateItems(items);
} catch (Exception e) {
logger.error("update items error", e);
}
} else {
logger.warn("Unknown slave: {} to process update: {}", slaveName, items);
}
}
}
/**
* {@inheritDoc}
*/
@Override
protected void internalReceiveCommand(String itemName, Command command) {
logger.debug("internalReceiveCommand({},{}) is called!", itemName, command);
for (FatekPLCBindingProvider provider : providers) {
FatekPLCItem item = provider.geFatektItem(itemName);
if (item != null) {
FatekPLCSlave slave = slaves.get(item.getSlaveName());
if (slave != null) {
try {
slave.command(item, command);
} catch (Exception e) {
logger.error(String.format("internalReceiveCommand(%s, %s)", itemName, command), e);
}
}
}
}
}
protected void addBindingProvider(FatekPLCBindingProvider bindingProvider) {
super.addBindingProvider(bindingProvider);
}
protected void removeBindingProvider(FatekPLCBindingProvider bindingProvider) {
super.removeBindingProvider(bindingProvider);
}
}