/**
* Copyright (C) 2010-14 diirt developers. See COPYRIGHT.TXT
* All rights reserved. Use is subject to license terms. See LICENSE.TXT
*/
package org.diirt.datasource.loc;
import org.diirt.datasource.ChannelWriteCallback;
import org.diirt.datasource.ChannelHandlerReadSubscription;
import org.diirt.datasource.MultiplexedChannelHandler;
import org.diirt.datasource.ChannelHandlerWriteSubscription;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import static org.diirt.vtype.ValueFactory.*;
import org.diirt.vtype.VDouble;
import org.diirt.vtype.VDoubleArray;
import org.diirt.vtype.VEnum;
import org.diirt.vtype.VString;
import org.diirt.vtype.VStringArray;
import org.diirt.vtype.VTable;
import org.diirt.vtype.VType;
import org.diirt.vtype.ValueFactory;
/**
* Implementation for channels of a {@link LocalDataSource}.
*
* @author carcassi
*/
class LocalChannelHandler extends MultiplexedChannelHandler<Object, Object> {
private static Logger log = Logger.getLogger(LocalChannelHandler.class.getName());
LocalChannelHandler(String channelName) {
super(channelName);
}
@Override
public void connect() {
processConnection(new Object());
}
@Override
public void disconnect() {
initialArguments = null;
type = null;
processConnection(null);
}
@Override
protected synchronized void addReader(ChannelHandlerReadSubscription subscription) {
// Override for test visibility purposes
super.addReader(subscription);
}
@Override
protected synchronized void addWriter(ChannelHandlerWriteSubscription subscription) {
// Override for test visibility purposes
super.addWriter(subscription);
}
@Override
protected synchronized void removeReader(ChannelHandlerReadSubscription subscription) {
// Override for test visibility purposes
super.removeReader(subscription);
}
@Override
protected synchronized void removeWrite(ChannelHandlerWriteSubscription subscription) {
// Override for test visibility purposes
super.removeWrite(subscription);
}
private Object checkValue(Object value) {
if (type != null && !type.isInstance(value)) {
throw new IllegalArgumentException("Value " + value + " is not of type " + type.getSimpleName());
}
return value;
}
@Override
public void write(Object newValue, ChannelWriteCallback callback) {
try {
if (VEnum.class.equals(type)) {
// Handle enum writes
int newIndex = -1;
VEnum firstEnum = (VEnum) initialValue;
List<String> labels = firstEnum.getLabels();
if (newValue instanceof Number) {
newIndex = ((Number) newValue).intValue();
}else if (newValue instanceof String) {
newIndex = labels.indexOf((String) newValue);
// Only if the String is not in the labels, try and
// parse a number.
if (newIndex == -1) {
String value = (String) newValue;
try {
newIndex = Double.valueOf(value).intValue();
} catch (NumberFormatException ex) {
}
}
} else {
throw new IllegalArgumentException("Value" + newValue + " can not be accepted by VEnum.");
}
newValue = ValueFactory.newVEnum(newIndex, firstEnum.getLabels(), alarmNone(), timeNow());
} else if (VString.class.equals(type) && newValue instanceof String) {
newValue = ValueFactory.newVString((String)newValue, alarmNone(), timeNow());
} else {
// If the string can be parsed to a number, do it
if (newValue instanceof String) {
String value = (String) newValue;
try {
newValue = Double.valueOf(value);
} catch (NumberFormatException ex) {
// Not a double - continue
}
}
// If new value is not a VType, try to convert it
if (!(newValue instanceof VType)) {
newValue = checkValue(ValueFactory.toVTypeChecked(newValue));
}
}
processMessage(newValue);
callback.channelWritten(null);
} catch (Exception ex) {
callback.channelWritten(ex);
}
}
@Override
protected boolean isWriteConnected(Object payload) {
return isConnected(payload);
}
private Object initialArguments;
private Object initialValue;
private Class<?> type;
synchronized void setInitialValue(Object value) {
if (initialArguments != null && !initialArguments.equals(value)) {
String message = "Different initialization for local channel " + getChannelName() + ": " + value + " but was " + initialArguments;
log.log(Level.WARNING, message);
throw new RuntimeException(message);
}
initialArguments = value;
if (getLastMessagePayload() == null) {
if (VEnum.class.equals(type)) {
List<?> args = (List<?>) initialArguments;
// TODO error message if not Number
int index = ((Number) args.get(0)).intValue();
List<String> labels = (List<String>) args.get(1);
initialValue = ValueFactory.newVEnum(index, labels, alarmNone(), timeNow());
} else {
initialValue = checkValue(ValueFactory.toVTypeChecked(value));
}
processMessage(initialValue);
}
}
synchronized void setType(String typeName) {
if (typeName == null) {
return;
}
Class<?> newType = null;
if ("VDouble".equals(typeName)) {
newType = VDouble.class;
}
if ("VString".equals(typeName)) {
newType = VString.class;
}
if ("VDoubleArray".equals(typeName)) {
newType = VDoubleArray.class;
}
if ("VStringArray".equals(typeName)) {
newType = VStringArray.class;
}
if ("VTable".equals(typeName)) {
newType = VTable.class;
}
if ("VEnum".equals(typeName)) {
newType = VEnum.class;
}
if (newType == null) {
throw new IllegalArgumentException("Type " + typeName + " for channel " + getChannelName() + " is not supported by local datasource.");
}
if (type != null && !type.equals(newType)) {
throw new IllegalArgumentException("Type mismatch for channel " + getChannelName() + ": " + typeName + " but was " + type.getSimpleName());
}
type = newType;
}
@Override
public synchronized Map<String, Object> getProperties() {
Map<String, Object> properties = new HashMap<>();
properties.put("Name", getChannelName());
properties.put("Type", type);
properties.put("Initial Value", initialArguments);
return properties;
}
}