/*******************************************************************************
* Copyright (c) 2011 The Board of Trustees of the Leland Stanford Junior University
* as Operator of the SLAC National Accelerator Laboratory.
* Copyright (c) 2011 Brookhaven National Laboratory.
* EPICS archiver appliance is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*******************************************************************************/
package org.epics.archiverappliance.config;
import java.io.IOException;
import java.io.Serializable;
import java.sql.Timestamp;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import org.apache.log4j.Logger;
import org.epics.archiverappliance.Event;
import org.epics.archiverappliance.StoragePlugin;
import org.epics.archiverappliance.common.BasicContext;
import org.epics.archiverappliance.common.TimeUtils;
import org.epics.archiverappliance.mgmt.policy.PolicyConfig.SamplingMethod;
import org.epics.archiverappliance.utils.ui.JSONEncoder;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
/**
* Somewhat static information about a PV like it's type info, graphic limits, event rates etc.
*
* @author mshankar
*/
public class PVTypeInfo implements Serializable {
private static final long serialVersionUID = 6298175991390616559L;
private static Logger logger = Logger.getLogger(PVTypeInfo.class.getName());
public static final int DEFAULT_BUFFER_INTERVAL = 10;
/**
* The name of the PV
*/
private String pvName;
/**
* The DBRType of the PV
*/
private ArchDBRTypes DBRType;
/**
* Is this a scalar?
*/
private boolean isScalar;
/**
* If waveform, how many elements
*/
private int elementCount;
/**
* Which appliance owns this PV
*/
private String applianceIdentity;
/**
* What's the chunk key that this PV was mapped to?
*/
private String chunkKey;
/**
* IOC where this PV came from last time.
*/
private String hostName;
// Info from the dbr_ctrl structures
/**
* <PV Name>.LOLO; though we get it using the dbr_ctrl structures
*/
private double lowerAlarmLimit;
/**
* <PV Name>.DRVL; though we get it using the dbr_ctrl structures
*/
private double lowerCtrlLimit;
/**
* <PV Name>.LOPR; though we get it using the dbr_ctrl structures
*/
private double lowerDisplayLimit;
/**
* <PV Name>.LOW; though we get it using the dbr_ctrl structures
*/
private double lowerWarningLimit;
/**
* <PV Name>.HIHI; though we get it using the dbr_ctrl structures
*/
private double upperAlarmLimit;
/**
* <PV Name>.DRVH though we get it using the dbr_ctrl structures
*/
private double upperCtrlLimit;
/**
* <PV Name>.HOPR; though we get it using the dbr_ctrl structures
*/
private double upperDisplayLimit;
/**
* <PV Name>.HIGH; though we get it using the dbr_ctrl structures
*/
private double upperWarningLimit;
/**
* <PV Name>.PREC; though we get it using the dbr_ctrl structures
*/
private double precision;
/**
* <PV Name>.EGU; though we get it using the dbr_ctrl structures
*/
private String units;
// Info pertaining to the archiver...
private boolean hasReducedDataSet;
/**
* the average event rate in one minute (event count / sec)
*/
private float computedEventRate;
/**
* the average storage rate in in one minute ( bytes / sec)
*/
private float computedStorageRate;
/**
* the storage size / the total event count if the event count ≠ 0
*/
private int computedBytesPerEvent;
private float userSpecifiedEventRate;
private Timestamp creationTime;
private Timestamp modificationTime;
private boolean paused = false;
private SamplingMethod samplingMethod;
private float samplingPeriod;
private String policyName;
private String[] dataStores = new String[0];
private HashMap<String, String> extraFields = new HashMap<String, String>();
private String controllingPV;
private String[] archiveFields = new String[0];
private boolean usePVAccess = false;
private boolean useDBEProperties = false;
public PVTypeInfo() {
}
public PVTypeInfo(String pvName, ArchDBRTypes dBRType, boolean isScalar, int elementCount) {
super();
this.pvName = pvName;
DBRType = dBRType;
this.isScalar = isScalar;
this.elementCount = elementCount;
}
public PVTypeInfo(String pvName, PVTypeInfo srcTypeInfo) {
this.pvName = pvName;
this.DBRType = srcTypeInfo.DBRType;
this.isScalar = srcTypeInfo.isScalar;
this.elementCount = srcTypeInfo.elementCount;
this.applianceIdentity = srcTypeInfo.applianceIdentity;
this.lowerAlarmLimit = srcTypeInfo.lowerAlarmLimit;
this.lowerCtrlLimit = srcTypeInfo.lowerCtrlLimit;
this.lowerDisplayLimit = srcTypeInfo.lowerDisplayLimit;
this.lowerWarningLimit = srcTypeInfo.lowerWarningLimit;
this.upperAlarmLimit = srcTypeInfo.upperAlarmLimit;
this.upperCtrlLimit = srcTypeInfo.upperCtrlLimit;
this.upperDisplayLimit = srcTypeInfo.upperDisplayLimit;
this.upperWarningLimit = srcTypeInfo.upperWarningLimit;
this.precision = srcTypeInfo.precision;
this.units = srcTypeInfo.units;
this.hasReducedDataSet = srcTypeInfo.hasReducedDataSet;
this.computedEventRate = srcTypeInfo.computedEventRate;
this.computedStorageRate = srcTypeInfo.computedStorageRate;
this.computedBytesPerEvent = srcTypeInfo.computedBytesPerEvent;
this.userSpecifiedEventRate = srcTypeInfo.userSpecifiedEventRate;
this.paused = srcTypeInfo.paused;
this.samplingMethod = srcTypeInfo.samplingMethod;
this.samplingPeriod = srcTypeInfo.samplingPeriod;
this.policyName = srcTypeInfo.policyName;
this.controllingPV = srcTypeInfo.controllingPV;
this.dataStores = Arrays.copyOf(srcTypeInfo.dataStores, srcTypeInfo.dataStores.length);
this.extraFields = new HashMap<String, String>(srcTypeInfo.extraFields);
this.archiveFields = Arrays.copyOf(srcTypeInfo.archiveFields, srcTypeInfo.archiveFields.length);
// Inherit the creation type from the source. This seems to serve us better
this.creationTime = srcTypeInfo.getCreationTime();
this.modificationTime = TimeUtils.now();
}
public String getPvName() {
return pvName;
}
public ArchDBRTypes getDBRType() {
return DBRType;
}
public boolean isScalar() {
return isScalar;
}
public int getElementCount() {
return elementCount;
}
public Double getUpperDisplayLimit() {
return upperDisplayLimit;
}
public Double getLowerDisplayLimit() {
return lowerDisplayLimit;
}
public boolean isHasReducedDataSet() {
return hasReducedDataSet;
}
public float getComputedEventRate() {
return computedEventRate;
}
public float getUserSpecifiedEventRate() {
return userSpecifiedEventRate;
}
public Timestamp getCreationTime() {
return creationTime;
}
public void setCreationTime(Timestamp creationTime) {
this.creationTime = creationTime;
this.modificationTime = creationTime;
}
public Timestamp getModificationTime() {
return modificationTime;
}
public void setModificationTime(Timestamp modificationTime) {
this.modificationTime = modificationTime;
}
public boolean isPaused() {
return paused;
}
public void setPaused(boolean paused) {
this.paused = paused;
}
public void setPvName(String pvName) {
this.pvName = pvName;
}
public void setDBRType(ArchDBRTypes dBRType) {
DBRType = dBRType;
}
public void setScalar(boolean isScalar) {
this.isScalar = isScalar;
}
public void setElementCount(int elementCount) {
this.elementCount = elementCount;
}
public void setUpperDisplayLimit(Double upperDisplayLimit) {
this.upperDisplayLimit = upperDisplayLimit;
}
public void setLowerDisplayLimit(Double lowerDisplayLimit) {
this.lowerDisplayLimit = lowerDisplayLimit;
}
public void setHasReducedDataSet(boolean hasReducedDataSet) {
this.hasReducedDataSet = hasReducedDataSet;
}
public void setComputedEventRate(float computedEventRate) {
this.computedEventRate = computedEventRate;
}
public void setUserSpecifiedEventRate(float userSpecifiedEventRate) {
this.userSpecifiedEventRate = userSpecifiedEventRate;
}
public SamplingMethod getSamplingMethod() {
return samplingMethod;
}
public void setSamplingMethod(SamplingMethod samplingMethod) {
this.samplingMethod = samplingMethod;
}
public float getSamplingPeriod() {
return samplingPeriod;
}
public void setSamplingPeriod(float samplingPeriod) {
this.samplingPeriod = samplingPeriod;
}
public String[] getDataStores() {
return dataStores;
}
public void setDataStores(String[] dataStores) {
this.dataStores = dataStores;
}
public String getApplianceIdentity() {
return applianceIdentity;
}
public void setApplianceIdentity(String applianceIdentity) {
this.applianceIdentity = applianceIdentity;
}
/**
* Parse a string (JSON) representation of the PVTypeInfo object into this object
* @param typeInfoStr
*/
public void parsePolicyRepresentation(String typeInfoStr) {
JSONObject parsedObj = (JSONObject) JSONValue.parse(typeInfoStr);
this.samplingMethod = SamplingMethod.valueOf((String)parsedObj.get("samplingMethod"));
this.samplingPeriod = Float.parseFloat((String)parsedObj.get("samplingPeriod"));
this.policyName = (String) parsedObj.get("policyName");
JSONArray parsedStores = (JSONArray) parsedObj.get("dataStores");
LinkedList<String> parsedStoresList = new LinkedList<String>();
for(Object parsedStore : parsedStores) {
parsedStoresList.add((String)parsedStore);
}
dataStores = parsedStoresList.toArray(new String[0]);
if(logger.isDebugEnabled()) {
try {
JSONEncoder<PVTypeInfo> jsonEncoder = JSONEncoder.getEncoder(PVTypeInfo.class);
JSONObject rep = jsonEncoder.encode(this);
logger.debug("Policy object initialized from string " + rep.toJSONString());
} catch(Exception ex) {
logger.error("Exception marshalling type info for pv " + pvName);
}
}
}
public Double getLowerAlarmLimit() {
return lowerAlarmLimit;
}
public void setLowerAlarmLimit(Double lowerAlarmLimit) {
this.lowerAlarmLimit = lowerAlarmLimit;
}
public Double getLowerCtrlLimit() {
return lowerCtrlLimit;
}
public void setLowerCtrlLimit(Double lowerCtrlLimit) {
this.lowerCtrlLimit = lowerCtrlLimit;
}
public Double getLowerWarningLimit() {
return lowerWarningLimit;
}
public void setLowerWarningLimit(Double lowerWarningLimit) {
this.lowerWarningLimit = lowerWarningLimit;
}
public Double getUpperAlarmLimit() {
return upperAlarmLimit;
}
public void setUpperAlarmLimit(Double upperAlarmLimit) {
this.upperAlarmLimit = upperAlarmLimit;
}
public Double getUpperCtrlLimit() {
return upperCtrlLimit;
}
public void setUpperCtrlLimit(Double upperCtrlLimit) {
this.upperCtrlLimit = upperCtrlLimit;
}
public Double getUpperWarningLimit() {
return upperWarningLimit;
}
public void setUpperWarningLimit(Double upperWarningLimit) {
this.upperWarningLimit = upperWarningLimit;
}
public Double getPrecision() {
return precision;
}
public void setPrecision(Double precision) {
this.precision = precision;
}
public String getUnits() {
return units;
}
public void setUnits(String units) {
this.units = units;
}
public void absorbMetaInfo(MetaInfo metaInfo) {
this.lowerAlarmLimit = metaInfo.getLowerAlarmLimit();
this.lowerCtrlLimit = metaInfo.getLoweCtrlLimit();
this.lowerDisplayLimit = metaInfo.getLowerDisplayLimit();
this.lowerWarningLimit = metaInfo.getLowerWarningLimit();
this.upperAlarmLimit = metaInfo.getUpperAlarmLimit();
this.upperCtrlLimit = metaInfo.getUpperCtrlLimit();
this.upperDisplayLimit = metaInfo.getUpperDisplayLimit();
this.upperWarningLimit = metaInfo.getUpperWarningLimit();
this.precision = metaInfo.getPrecision();
this.units = metaInfo.getUnit();
this.elementCount = metaInfo.getCount();
this.computedEventRate = (float) metaInfo.getEventRate();
this.computedStorageRate = (float) metaInfo.getStorageRate();
if(metaInfo.getEventCount() != 0) {
this.computedBytesPerEvent = (int) (metaInfo.getStorageSize()/metaInfo.getEventCount());
}
HashMap<String, String> otherMetaInfo = metaInfo.getOtherMetaInfo();
for(String extraName : otherMetaInfo.keySet()) {
extraFields.put(extraName, otherMetaInfo.get(extraName).toString());
}
}
public HashMap<String, String> getExtraFields() {
return extraFields;
}
public void setExtraFields(HashMap<String, String> extraFields) {
this.extraFields = extraFields;
}
public boolean hasExtraField(String key) {
if(this.extraFields == null) return false;
return this.extraFields.containsKey(key);
}
public String lookupExtraField(String key) {
if(this.extraFields == null) return null;
return this.extraFields.get(key);
}
public String getPolicyName() {
return policyName;
}
public void setPolicyName(String policyName) {
this.policyName = policyName;
}
public float getComputedStorageRate() {
return computedStorageRate;
}
public void setComputedStorageRate(float computedStorageRate) {
this.computedStorageRate = computedStorageRate;
}
public int getComputedBytesPerEvent() {
return computedBytesPerEvent;
}
public void setComputedBytesPerEvent(int computedBytesPerEvent) {
this.computedBytesPerEvent = computedBytesPerEvent;
}
public String getControllingPV() {
return controllingPV;
}
public void setControllingPV(String controllingPV) {
this.controllingPV = controllingPV;
}
public String[] getArchiveFields() {
return archiveFields;
}
public String obtainArchiveFieldsAsString() {
StringBuilder buf = new StringBuilder();
boolean first = true;
for(String archiveField : archiveFields) {
if(first) { first = false; } else { buf.append(','); }
buf.append(archiveField);
}
return buf.toString();
}
public void setArchiveFields(String[] archiveFields) {
if(archiveFields == null || archiveFields.length == 0) {
this.archiveFields = new String[0];
return;
}
HashSet<String> newFields = new HashSet<String>();
for(String fieldName : archiveFields) {
if(fieldName == null || fieldName.equals("")) continue;
if(fieldName.equals("VAL")) continue;
newFields.add(fieldName);
}
this.archiveFields = newFields.toArray(new String[0]);
}
public void addArchiveField(String fieldName) {
if(fieldName == null || fieldName.equals("")) return;
if(fieldName.equals("VAL")) return;
HashSet<String> newFields = new HashSet<String>();
if(this.archiveFields != null) {
for(String fieldBeingArchived : this.archiveFields) {
newFields.add(fieldBeingArchived);
}
}
newFields.add(fieldName);
this.archiveFields = newFields.toArray(new String[0]);
}
public boolean checkIfFieldAlreadySepcified(String fieldName) {
if(fieldName == null || fieldName.equals("")) return false;
if(fieldName.equals("VAL")) return true;
if(this.archiveFields != null) {
for(String fieldBeingArchived : this.archiveFields) {
if(fieldBeingArchived.equals(fieldName)) {
return true;
}
}
}
return false;
}
/**
* Loop thru the stores outlined in this typeinfo and determine the most recent event for this pv
* @param configService ConfigService
* @throws IOException
* @return Timestamp
*/
public Timestamp determineLastKnownEventFromStores(ConfigService configService) throws IOException {
try(BasicContext context = new BasicContext()) {
for(String storeUrl : this.dataStores) {
try {
StoragePlugin storagePlugin = StoragePluginURLParser.parseStoragePlugin(storeUrl, configService);
Event e = storagePlugin.getLastKnownEvent(context, pvName);
if(e != null) return e.getEventTimeStamp();
} catch(IOException ex) {
logger.error("Exception determining last known timestamp", ex);
}
}
}
return null;
}
/**
* The secondsToBuffer is a system wide property.
* Use this method to get the proper defaults.
* @param configService ConfigService
* @return secondsToBuffer
*/
public static int getSecondsToBuffer(ConfigService configService) {
String secondsToBufferStr = configService.getInstallationProperties().getProperty("org.epics.archiverappliance.config.PVTypeInfo.secondsToBuffer", "10");
int secondsToBuffer = Integer.parseInt(secondsToBufferStr);
if(logger.isDebugEnabled()) logger.debug("Seconds to buffer is " + secondsToBuffer);
return secondsToBuffer;
}
/**
* The archiver appliance stores data in chunks that have a well defined key.
* We record this key in the typeinfo (to accommodate slowly changing key mapping strategies)
* @return chunkKey
*/
public String getChunkKey() {
return chunkKey;
}
public void setChunkKey(String chunkKey) {
this.chunkKey = chunkKey;
}
public boolean keyAlreadyGenerated() {
return this.chunkKey != null;
}
/**
* @return hostName
*/
public String getHostName() {
return hostName;
}
/**
* @param hostName
*/
public void setHostName(String hostName) {
this.hostName = hostName;
}
public boolean isUsePVAccess() {
return usePVAccess;
}
public void setUsePVAccess(boolean usePVAccess) {
this.usePVAccess = usePVAccess;
}
public boolean isUseDBEProperties() {
return useDBEProperties;
}
public void setUseDBEProperties(boolean useDBEProperties) {
this.useDBEProperties = useDBEProperties;
}
}