/* * Copyright 2011-16 Fraunhofer ISE * * This file is part of OpenMUC. * For more information visit http://www.openmuc.org * * OpenMUC is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenMUC is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenMUC. If not, see <http://www.gnu.org/licenses/>. * */ package org.openmuc.framework.core.datamanager; import java.util.ArrayList; import java.util.List; import org.openmuc.framework.config.ChannelConfig; import org.openmuc.framework.config.DeviceConfig; import org.openmuc.framework.config.IdCollisionException; import org.openmuc.framework.config.ParseException; import org.openmuc.framework.config.ServerMapping; import org.openmuc.framework.data.ValueType; import org.openmuc.framework.dataaccess.ChannelState; import org.openmuc.framework.datalogger.spi.LogChannel; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; public final class ChannelConfigImpl implements ChannelConfig, LogChannel { String id; String channelAddress = null; String description = null; String unit = null; ValueType valueType = null; Integer valueTypeLength = null; Double scalingFactor = null; Double valueOffset = null; Boolean listening = null; Integer samplingInterval = null; Integer samplingTimeOffset = null; String samplingGroup = null; Integer loggingInterval = null; Integer loggingTimeOffset = null; Boolean disabled = null; List<ServerMapping> serverMappings = null; ChannelImpl channel; DeviceConfigImpl deviceParent; ChannelState state; ChannelConfigImpl(String id, DeviceConfigImpl deviceParent) { this.id = id; this.deviceParent = deviceParent; } @Override public String getId() { return id; } @Override public void setId(String id) throws IdCollisionException { if (id == null) { throw new IllegalArgumentException("The channel ID may not be null"); } ChannelConfigImpl.checkIdSyntax(id); if (deviceParent.driverParent.rootConfigParent.channelConfigsById.containsKey(id)) { throw new IdCollisionException("Collision with channel ID:" + id); } deviceParent.channelConfigsById.put(id, deviceParent.channelConfigsById.remove(this.id)); deviceParent.driverParent.rootConfigParent.channelConfigsById.put(id, deviceParent.driverParent.rootConfigParent.channelConfigsById.remove(this.id)); this.id = id; } @Override public String getDescription() { return description; } @Override public void setDescription(String description) { this.description = description; } @Override public String getChannelAddress() { return channelAddress; } @Override public void setChannelAddress(String address) { channelAddress = address; } @Override public String getUnit() { return unit; } @Override public void setUnit(String unit) { this.unit = unit; } @Override public ValueType getValueType() { return valueType; } @Override public void setValueType(ValueType valueType) { this.valueType = valueType; } @Override public Integer getValueTypeLength() { return valueTypeLength; } @Override public void setValueTypeLength(Integer length) { valueTypeLength = length; } @Override public Double getScalingFactor() { return scalingFactor; } @Override public void setScalingFactor(Double factor) { scalingFactor = factor; } @Override public Double getValueOffset() { return valueOffset; } @Override public void setValueOffset(Double offset) { valueOffset = offset; } @Override public Boolean isListening() { return listening; } @Override public void setListening(Boolean listening) { if (samplingInterval != null && listening != null && listening && samplingInterval > 0) { throw new IllegalStateException("Listening may not be enabled while sampling is enabled."); } this.listening = listening; } @Override public Integer getSamplingInterval() { return samplingInterval; } @Override public void setSamplingInterval(Integer samplingInterval) { if (listening != null && samplingInterval != null && listening && samplingInterval > 0) { throw new IllegalStateException("Sampling may not be enabled while listening is enabled."); } this.samplingInterval = samplingInterval; } @Override public Integer getSamplingTimeOffset() { return samplingTimeOffset; } @Override public void setSamplingTimeOffset(Integer samplingTimeOffset) { if (samplingTimeOffset != null && samplingTimeOffset < 0) { throw new IllegalArgumentException("The sampling time offset may not be negative."); } this.samplingTimeOffset = samplingTimeOffset; } @Override public String getSamplingGroup() { return samplingGroup; } @Override public void setSamplingGroup(String group) { samplingGroup = group; } @Override public Integer getLoggingInterval() { return loggingInterval; } @Override public void setLoggingInterval(Integer loggingInterval) { this.loggingInterval = loggingInterval; } @Override public Integer getLoggingTimeOffset() { return loggingTimeOffset; } @Override public void setLoggingTimeOffset(Integer loggingTimeOffset) { if (loggingTimeOffset != null && loggingTimeOffset < 0) { throw new IllegalArgumentException("The logging time offset may not be negative."); } this.loggingTimeOffset = loggingTimeOffset; } @Override public Boolean isDisabled() { return disabled; } @Override public void setDisabled(Boolean disabled) { this.disabled = disabled; } @Override public void delete() { deviceParent.channelConfigsById.remove(id); clear(); } @Override public List<ServerMapping> getServerMappings() { if (serverMappings != null) { return this.serverMappings; } else { return new ArrayList<>(); } } void clear() { deviceParent.driverParent.rootConfigParent.channelConfigsById.remove(id); deviceParent = null; } @Override public DeviceConfig getDevice() { return deviceParent; } static void addChannelFromDomNode(Node channelConfigNode, DeviceConfig parentConfig) throws ParseException { String id = ChannelConfigImpl.getAttributeValue(channelConfigNode, "id"); if (id == null) { throw new ParseException("channel has no id attribute"); } ChannelConfigImpl config; try { config = (ChannelConfigImpl) parentConfig.addChannel(id); } catch (Exception e) { throw new ParseException(e); } NodeList channelChildren = channelConfigNode.getChildNodes(); try { for (int i = 0; i < channelChildren.getLength(); i++) { Node childNode = channelChildren.item(i); String childName = childNode.getNodeName(); if (childName.equals("#text")) { continue; } else if (childName.equals("description")) { config.setDescription(childNode.getTextContent()); } else if (childName.equals("channelAddress")) { config.setChannelAddress(childNode.getTextContent()); } else if (childName.equals("serverMapping")) { NamedNodeMap attributes = childNode.getAttributes(); Node nameAttribute = attributes.getNamedItem("id"); if (nameAttribute != null) { config.addServerMapping( new ServerMapping(nameAttribute.getTextContent(), childNode.getTextContent())); } else { throw new ParseException("No id attribute specified for serverMapping."); } } else if (childName.equals("unit")) { config.setUnit(childNode.getTextContent()); } else if (childName.equals("valueType")) { String valueTypeString = childNode.getTextContent().toUpperCase(); try { config.valueType = ValueType.valueOf(valueTypeString); } catch (IllegalArgumentException e) { throw new ParseException("found unknown channel value type:" + valueTypeString); } if (config.valueType == ValueType.BYTE_ARRAY || config.valueType == ValueType.STRING) { String valueTypeLengthString = getAttributeValue(childNode, "length"); if (valueTypeLengthString == null) { throw new ParseException( "length of " + config.valueType.toString() + " value type was not specified"); } config.valueTypeLength = timeStringToMillis(valueTypeLengthString); } } else if (childName.equals("scalingFactor")) { config.setScalingFactor(Double.parseDouble(childNode.getTextContent())); } else if (childName.equals("valueOffset")) { config.setValueOffset(Double.parseDouble(childNode.getTextContent())); } else if (childName.equals("listening")) { String listeningString = childNode.getTextContent().toLowerCase(); if (listeningString.equals("true")) { config.setListening(true); } else if (listeningString.equals("false")) { config.setListening(false); } else { throw new ParseException("\"listening\" tag contains neither \"true\" nor \"false\""); } } else if (childName.equals("samplingInterval")) { config.setSamplingInterval(timeStringToMillis(childNode.getTextContent())); } else if (childName.equals("samplingTimeOffset")) { config.setSamplingTimeOffset(timeStringToMillis(childNode.getTextContent())); } else if (childName.equals("samplingGroup")) { config.setSamplingGroup(childNode.getTextContent()); } else if (childName.equals("loggingInterval")) { config.setLoggingInterval(timeStringToMillis(childNode.getTextContent())); } else if (childName.equals("loggingTimeOffset")) { config.setLoggingTimeOffset(timeStringToMillis(childNode.getTextContent())); } else if (childName.equals("disabled")) { String disabledString = childNode.getTextContent().toLowerCase(); if (disabledString.equals("true")) { config.setDisabled(true); } else if (disabledString.equals("false")) { config.setDisabled(false); } else { throw new ParseException("\"disabled\" tag contains neither \"true\" nor \"false\""); } } else { throw new ParseException("found unknown tag:" + childName); } } } catch (IllegalArgumentException e) { throw new ParseException(e); } catch (IllegalStateException e) { throw new ParseException(e); } } Element getDomElement(Document document) { Element parentElement = document.createElement("channel"); parentElement.setAttribute("id", id); Element childElement; if (description != null) { childElement = document.createElement("description"); childElement.setTextContent(description); parentElement.appendChild(childElement); } if (channelAddress != null) { childElement = document.createElement("channelAddress"); childElement.setTextContent(channelAddress); parentElement.appendChild(childElement); } if (serverMappings != null) { for (ServerMapping serverMapping : serverMappings) { childElement = document.createElement("serverMapping"); childElement.setAttribute("id", serverMapping.getId()); childElement.setTextContent(serverMapping.getServerAddress()); parentElement.appendChild(childElement); } } if (unit != null) { childElement = document.createElement("unit"); childElement.setTextContent(unit); parentElement.appendChild(childElement); } if (valueType != null) { childElement = document.createElement("valueType"); childElement.setTextContent(valueType.toString()); if (valueTypeLength != null) { if (valueType == ValueType.BYTE_ARRAY || valueType == ValueType.STRING) { childElement.setAttribute("length", valueTypeLength.toString()); } } parentElement.appendChild(childElement); } if (scalingFactor != null) { childElement = document.createElement("scalingFactor"); childElement.setTextContent(Double.toString(scalingFactor)); parentElement.appendChild(childElement); } if (valueOffset != null) { childElement = document.createElement("valueOffset"); childElement.setTextContent(Double.toString(valueOffset)); parentElement.appendChild(childElement); } if (listening != null) { childElement = document.createElement("listening"); if (listening) { childElement.setTextContent("true"); } else { childElement.setTextContent("false"); } parentElement.appendChild(childElement); } if (samplingInterval != null) { childElement = document.createElement("samplingInterval"); childElement.setTextContent(millisToTimeString(samplingInterval)); parentElement.appendChild(childElement); } if (samplingTimeOffset != null) { childElement = document.createElement("samplingTimeOffset"); childElement.setTextContent(millisToTimeString(samplingTimeOffset)); parentElement.appendChild(childElement); } if (samplingGroup != null) { childElement = document.createElement("samplingGroup"); childElement.setTextContent(samplingGroup); parentElement.appendChild(childElement); } if (loggingInterval != null) { childElement = document.createElement("loggingInterval"); childElement.setTextContent(millisToTimeString(loggingInterval)); parentElement.appendChild(childElement); } if (loggingTimeOffset != null) { childElement = document.createElement("loggingTimeOffset"); childElement.setTextContent(millisToTimeString(loggingTimeOffset)); parentElement.appendChild(childElement); } if (disabled != null) { childElement = document.createElement("disabled"); if (disabled) { childElement.setTextContent("true"); } else { childElement.setTextContent("false"); } parentElement.appendChild(childElement); } return parentElement; } ChannelConfigImpl clone(DeviceConfigImpl clonedParentConfig) { ChannelConfigImpl configClone = new ChannelConfigImpl(id, clonedParentConfig); configClone.description = description; configClone.channelAddress = channelAddress; configClone.serverMappings = serverMappings; configClone.unit = unit; configClone.valueType = valueType; configClone.valueTypeLength = valueTypeLength; configClone.scalingFactor = scalingFactor; configClone.valueOffset = valueOffset; configClone.listening = listening; configClone.samplingInterval = samplingInterval; configClone.samplingTimeOffset = samplingTimeOffset; configClone.samplingGroup = samplingGroup; configClone.loggingInterval = loggingInterval; configClone.loggingTimeOffset = loggingTimeOffset; configClone.disabled = disabled; return configClone; } ChannelConfigImpl cloneWithDefaults(DeviceConfigImpl clonedParentConfig) { ChannelConfigImpl configClone = new ChannelConfigImpl(id, clonedParentConfig); if (description == null) { configClone.description = ChannelConfig.DESCRIPTION_DEFAULT; } else { configClone.description = description; } if (channelAddress == null) { configClone.channelAddress = CHANNEL_ADDRESS_DEFAULT; } else { configClone.channelAddress = channelAddress; } if (serverMappings == null) { configClone.serverMappings = new ArrayList<>(); } else { configClone.serverMappings = serverMappings; } if (unit == null) { configClone.unit = ChannelConfig.UNIT_DEFAULT; } else { configClone.unit = unit; } if (valueType == null) { configClone.valueType = ChannelConfig.VALUE_TYPE_DEFAULT; } else { configClone.valueType = valueType; } if (valueTypeLength == null) { if (valueType == ValueType.DOUBLE) { configClone.valueTypeLength = 8; } else if (valueType == ValueType.BYTE_ARRAY) { configClone.valueTypeLength = ChannelConfig.BYTE_ARRAY_SIZE_DEFAULT; } else if (valueType == ValueType.STRING) { configClone.valueTypeLength = ChannelConfig.STRING_SIZE_DEFAULT; } else if (valueType == ValueType.BYTE) { configClone.valueTypeLength = 1; } else if (valueType == ValueType.FLOAT) { configClone.valueTypeLength = 4; } else if (valueType == ValueType.SHORT) { configClone.valueTypeLength = 2; } else if (valueType == ValueType.INTEGER) { configClone.valueTypeLength = 4; } else if (valueType == ValueType.LONG) { configClone.valueTypeLength = 8; } else if (valueType == ValueType.BOOLEAN) { configClone.valueTypeLength = 1; } } else { configClone.valueTypeLength = valueTypeLength; } configClone.scalingFactor = scalingFactor; configClone.valueOffset = valueOffset; if (listening == null) { configClone.listening = ChannelConfig.LISTENING_DEFAULT; } else { configClone.listening = listening; } if (samplingInterval == null) { configClone.samplingInterval = ChannelConfig.SAMPLING_INTERVAL_DEFAULT; } else { configClone.samplingInterval = samplingInterval; } if (samplingTimeOffset == null) { configClone.samplingTimeOffset = ChannelConfig.SAMPLING_TIME_OFFSET_DEFAULT; } else { configClone.samplingTimeOffset = samplingTimeOffset; } if (samplingGroup == null) { configClone.samplingGroup = ChannelConfig.SAMPLING_GROUP_DEFAULT; } else { configClone.samplingGroup = samplingGroup; } if (loggingInterval == null) { configClone.loggingInterval = ChannelConfig.LOGGING_INTERVAL_DEFAULT; } else { configClone.loggingInterval = loggingInterval; } if (loggingTimeOffset == null) { configClone.loggingTimeOffset = ChannelConfig.LOGGING_TIME_OFFSET_DEFAULT; } else { configClone.loggingTimeOffset = loggingTimeOffset; } if (disabled == null) { configClone.disabled = clonedParentConfig.disabled; } else { if (clonedParentConfig.disabled) { configClone.disabled = false; } else { configClone.disabled = disabled; } } return configClone; } static String getAttributeValue(Node element, String attributeName) { NamedNodeMap attributes = element.getAttributes(); Node nameAttribute = attributes.getNamedItem(attributeName); if (nameAttribute == null) { return null; } return nameAttribute.getTextContent(); } static String millisToTimeString(Integer time) { if (time == 0) { return "0"; } if ((time % 1000) == 0) { time /= 1000; if ((time % 60) == 0) { time /= 60; if ((time % 60) == 0) { return time.toString().concat("h"); } return time.toString().concat("m"); } return time.toString().concat("s"); } return time.toString().concat("ms"); } static int timeStringToMillis(String timeString) throws ParseException { try { char lastChar = timeString.charAt(timeString.length() - 1); if (Character.isDigit(lastChar)) { return Integer.parseInt(timeString); } switch (lastChar) { case 's': if (timeString.charAt(timeString.length() - 2) == 'm') { return Integer.parseInt(timeString.substring(0, timeString.length() - 2)); } return Integer.parseInt(timeString.substring(0, timeString.length() - 1)) * 1000; case 'm': return Integer.parseInt(timeString.substring(0, timeString.length() - 1)) * 60000; case 'h': return Integer.parseInt(timeString.substring(0, timeString.length() - 1)) * 3600000; default: throw new ParseException("unknown time string: " + timeString); } } catch (NumberFormatException e) { throw new ParseException(e); } } static void checkIdSyntax(String id) { if (!id.matches("[a-zA-Z0-9_-]+")) { throw new IllegalArgumentException("Invalid ID: \"" + id + "\". An ID may not be the empty string and must contain only ASCII letters, digits, hyphens and underscores."); } } public boolean isSampling() { return !disabled && samplingInterval != null && samplingInterval > 0; } @Override public void addServerMapping(ServerMapping serverMapping) { if (serverMappings == null) { serverMappings = new ArrayList<>(); } serverMappings.add(serverMapping); } @Override public void deleteServerMappings(String id) { if (serverMappings != null) { List<ServerMapping> newMappings = new ArrayList<>(); for (ServerMapping serverMapping : serverMappings) { if (!serverMapping.getId().equals(id)) { newMappings.add(serverMapping); } } serverMappings = newMappings; } } }