/******************************************************************************* * 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.engine.metadata; import java.io.UnsupportedEncodingException; import java.util.Enumeration; import java.util.Hashtable; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import org.apache.log4j.Logger; import org.epics.archiverappliance.config.ConfigService; import org.epics.archiverappliance.config.MetaInfo; import org.epics.archiverappliance.config.PVNames; import org.epics.archiverappliance.data.DBRTimeEvent; import org.epics.archiverappliance.data.SampleValue; import org.epics.archiverappliance.data.ScalarStringSampleValue; import org.epics.archiverappliance.data.ScalarValue; import org.epics.archiverappliance.data.VectorStringSampleValue; import org.epics.archiverappliance.data.VectorValue; import org.epics.archiverappliance.engine.pv.PV; import org.epics.archiverappliance.engine.pv.PVFactory; import org.epics.archiverappliance.engine.pv.PVListener; /** * this class is used to create channel for pv and compute the meta info for one pv. * @author Luofeng Li * */ public class MetaGet implements Runnable { private static ConcurrentHashMap<String, MetaGet> metaGets = new ConcurrentHashMap<String, MetaGet>(); private String pvName; private String metadatafields[]; private boolean usePVAccess = false; private MetaCompletedListener metaListener; private Hashtable<String, PV> pvList = new Hashtable<String, PV>(); final private ConfigService configservice; private static final Logger logger = Logger.getLogger(MetaGet.class.getName()); private boolean isScheduled = false; public MetaGet(String pvName, ConfigService configservice, String metadatafields[], boolean usePVAccess, MetaCompletedListener metaListener) { this.pvName = pvName; this.usePVAccess = usePVAccess; this.metadatafields = metadatafields; this.metaListener = metaListener; this.configservice = configservice; metaGets.put(pvName, this); } /** * create channel of pv and its meta field * @throws Exception error when creating channel for pv and its meta field */ public void initpv() throws Exception { try { int jcaCommandThreadId = configservice.getEngineContext().assignJCACommandThread(pvName, null); PV pv = PVFactory.createPV(pvName, configservice, jcaCommandThreadId, usePVAccess); pv.addListener(new PVListener() { @Override public void pvValueUpdate(PV pv) { } @Override public void pvDisconnected(PV pv) { } @Override public void pvConnected(PV pv) { if (!isScheduled) { logger.debug("Starting the timer to measure event and storage rates for about 60 seconds for pv " + MetaGet.this.pvName); ScheduledThreadPoolExecutor scheduler = configservice.getEngineContext().getScheduler(); scheduler.schedule(MetaGet.this, 60, TimeUnit.SECONDS); isScheduled = true; } } @Override public void pvConnectionRequestMade(PV pv) { } @Override public void pvDroppedSample(PV pv, DroppedReason reason) { } }); pvList.put("main", pv); pv.start(); if(this.usePVAccess) { logger.debug("Skipping getting meta fields for a PVAccess PV " + this.pvName); } else { PV pv2 = PVFactory.createPV(PVNames.normalizePVNameWithField(pvName, "NAME"), configservice, jcaCommandThreadId, usePVAccess); pvList.put("NAME", pv2); pv2.start(); PV pv3 = PVFactory.createPV(PVNames.normalizePVNameWithField(pvName, "NAME$"), configservice, jcaCommandThreadId, usePVAccess); pvList.put("NAME$", pv3); pv3.start(); if (metadatafields != null) { for (int i = 0; i < metadatafields.length; i++) { String metaField = metadatafields[i]; // We return the fields of the src PV even if we are archiving a field... PV pvTemp = PVFactory.createPV(PVNames.normalizePVNameWithField(pvName, metaField), configservice, jcaCommandThreadId, usePVAccess); pvTemp.start(); pvList.put(metaField, pvTemp); } } } } catch (Exception e) { throw (e); } } @Override public void run() { logger.debug("Finished the timer to measure event and storage rates for about 60 seconds for pv " + MetaGet.this.pvName); try { PV pvMain = pvList.get("main"); MetaInfo mainMeta = pvMain.getTotalMetaInfo(); if(this.usePVAccess) { logger.debug(this.pvName + " is a PVAccess PV; so we are pretty much done with the metaget"); } else { // Per Dirk Zimoch, we first check the NAME$. // If that exists, we use it. If not, we use the NAME PV pv_NameDollar = pvList.get("NAME$"); DBRTimeEvent nameDollarValue = pv_NameDollar.getDBRTimeEvent(); if (nameDollarValue != null && nameDollarValue.getSampleValue() != null) { logger.debug("Using the NAME$ value as the NAME for pv " + pvName); SampleValue sampleValue = nameDollarValue.getSampleValue(); parseAliasInfo(sampleValue, mainMeta); } else { logger.debug("Using the NAME value as the NAME for pv " + pvName); PV pv_Name = pvList.get("NAME"); DBRTimeEvent nameValue = pv_Name.getDBRTimeEvent(); if (nameValue != null && nameValue.getSampleValue() != null) { SampleValue sampleValue = nameValue.getSampleValue(); parseAliasInfo(sampleValue, mainMeta); } else { logger.warn("Either we probably did not have time to determine .NAME for " + MetaGet.this.pvName + " or the field does not exist"); } } Enumeration<String> fieldNameList = pvList.keys(); while (fieldNameList.hasMoreElements()) { String fieldName = fieldNameList.nextElement(); if (fieldName.equals("main") || fieldName.equals("NAME") || fieldName.equals("NAME$")) { // These have already been processed; so do nothing. } else { if (fieldName.endsWith("RTYP")) { if(pvList.get(fieldName) != null && pvList.get(fieldName).getDBRTimeEvent() != null && pvList.get(fieldName).getDBRTimeEvent().getSampleValue() != null) { String rtyp = pvList.get(fieldName).getDBRTimeEvent().getSampleValue().toString(); mainMeta.addOtherMetaInfo(fieldName, rtyp); logger.info("The RTYP for the PV " + MetaGet.this.pvName + " is " + rtyp); } else { logger.debug("Something about RTYP is null for PV " + MetaGet.this.pvName); } } else { DBRTimeEvent valueTemp = pvList.get(fieldName).getDBRTimeEvent(); if (valueTemp != null) { SampleValue tempvalue = valueTemp.getSampleValue(); parseOtherInfo(tempvalue, mainMeta, fieldName); } else { logger.warn("Either we probably did not have time to determine " + fieldName + " for " + MetaGet.this.pvName + " or the field does not exist"); } } } pvList.get(fieldName).stop(); } } // Make sure we have at least the DBR type here. if(mainMeta.getArchDBRTypes() == null) { logger.error("Cannot determine DBR type for pv " + MetaGet.this.pvName); } metaListener.completed(mainMeta); metaGets.remove(pvName); } catch (Exception ex) { logger.error("Exception when schecule MetaGet " + pvName, ex); } } /** * parse the sample value and save the meta info in it. * @param tempvalue the sample value * @param mainMeta the MetaInfo object for this pv. */ private void parseAliasInfo(SampleValue tempvalue, MetaInfo mainMeta) { if (tempvalue instanceof ScalarValue<?>) { // We have a number for a NAME???? logger.error("We have a number as the NAME field for pv " + pvName); mainMeta.setAliasName(PVNames.transferField(pvName, "" + ((ScalarValue<?>) tempvalue).getValue().doubleValue())); } else if (tempvalue instanceof ScalarStringSampleValue) { String tempName = PVNames.transferField(pvName, ((ScalarStringSampleValue) tempvalue).toString()); mainMeta.setAliasName(tempName); mainMeta.addOtherMetaInfo("NAME", tempName); } else if (tempvalue instanceof VectorValue<?>) { VectorValue<?> vectorValue = (VectorValue<?>) tempvalue; int elementCount = vectorValue.getElementCount(); byte[] namebuf = new byte[elementCount]; String nameDollar = null; for(int i = 0; i < elementCount; i++) { byte byteValue = (byte) vectorValue.getValue(i).byteValue(); if(byteValue == 0) { try { nameDollar = new String(namebuf, 0, i, "UTF-8"); } catch (UnsupportedEncodingException e) { logger.fatal(e.getMessage(), e); } break; } namebuf[i] = byteValue; } if(nameDollar != null) { nameDollar = PVNames.transferField(pvName, nameDollar); mainMeta.setAliasName(nameDollar); mainMeta.addOtherMetaInfo("NAME", nameDollar); } else { logger.error("We got a NAME$ value but could not use it for some reason for PV " + pvName); } } else if (tempvalue instanceof VectorStringSampleValue) { // We have an array of strings? for a NAME???? String tempName = PVNames.transferField(pvName, ((VectorStringSampleValue) tempvalue).toString()); if (!pvName.equals(tempName)) mainMeta.setAliasName(tempName); } } /** * parse the other meta info from the sample value * @param tempvalue sample value * @param mainMeta the MetaInfo object for this pv * @param fieldName the info name to be parsed */ private void parseOtherInfo(SampleValue tempvalue, MetaInfo mainMeta, String fieldName) { logger.debug("In MetaGet, processing field " + fieldName); if(fieldName.equals("SCAN")) { int enumIndex = ((ScalarValue<?>) tempvalue).getValue().intValue(); String[] labels = pvList.get(fieldName).getTotalMetaInfo().getLabel(); if(labels != null && enumIndex < labels.length) { String scanValue = labels[enumIndex]; logger.debug("Looked up scan value enum name and it is " + scanValue); mainMeta.addOtherMetaInfo("SCAN", scanValue); return; } else { logger.warn("SCAN does not seem to be a valid label"); mainMeta.addOtherMetaInfo("SCAN", Integer.toString(enumIndex)); } } if (tempvalue instanceof ScalarValue<?>) { mainMeta.addOtherMetaInfo(fieldName, new Double( ((ScalarValue<?>) tempvalue).getValue().doubleValue())); } else if (tempvalue instanceof ScalarStringSampleValue) { mainMeta.addOtherMetaInfo(fieldName, ((ScalarStringSampleValue) tempvalue).toString()); } else if (tempvalue instanceof VectorValue<?>) { mainMeta.addOtherMetaInfo(fieldName, new Double( ((VectorValue<?>) tempvalue).getValue().doubleValue())); } else if (tempvalue instanceof VectorStringSampleValue) { mainMeta.addOtherMetaInfo(fieldName, ((VectorStringSampleValue) tempvalue).toString()); } } public static boolean abortMetaGet(String pvName) { MetaGet metaGet = metaGets.get(pvName); if(metaGet != null) { metaGets.remove(pvName); for(PV pv : metaGet.pvList.values()) { pv.stop(); } return true; } return false; } public static int getPendingMetaGetsSize() { return metaGets.size(); } public boolean isUsePVAccess() { return usePVAccess; } }