/*******************************************************************************
* Copyright (c) 2010 Oak Ridge National Laboratory.
* 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.epics.archiverappliance.engine.pv;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import org.apache.log4j.Logger;
import org.epics.archiverappliance.common.POJOEvent;
import org.epics.archiverappliance.common.TimeUtils;
import org.epics.archiverappliance.config.ArchDBRTypes;
import org.epics.archiverappliance.config.ConfigService;
import org.epics.archiverappliance.config.JCA2ArchDBRType;
import org.epics.archiverappliance.config.MetaInfo;
import org.epics.archiverappliance.data.DBRTimeEvent;
import org.epics.archiverappliance.data.ScalarStringSampleValue;
import org.epics.archiverappliance.engine.ArchiveEngine;
import com.cosylab.epics.caj.CAJChannel;
import gov.aps.jca.CAException;
import gov.aps.jca.Channel;
import gov.aps.jca.Channel.ConnectionState;
import gov.aps.jca.Monitor;
import gov.aps.jca.dbr.DBR;
import gov.aps.jca.dbr.DBRType;
import gov.aps.jca.dbr.DBR_CTRL_Double;
import gov.aps.jca.dbr.DBR_CTRL_Int;
import gov.aps.jca.dbr.DBR_LABELS_Enum;
import gov.aps.jca.dbr.DBR_String;
import gov.aps.jca.event.ConnectionEvent;
import gov.aps.jca.event.ConnectionListener;
import gov.aps.jca.event.GetEvent;
import gov.aps.jca.event.GetListener;
import gov.aps.jca.event.MonitorEvent;
import gov.aps.jca.event.MonitorListener;
/**
* EPICS ChannelAccess implementation of the PV interface.
*
* @see PV
* @author Kay Kasemir
* @version Initial version:CSS
* @version 4-Jun-2012, Luofeng Li:added codes to support for the new archiver
*/
public class EPICS_V3_PV implements PV, ControllingPV, ConnectionListener, MonitorListener {
private static final Logger logger = Logger.getLogger(EPICS_V3_PV.class.getName());
/**
* Use plain mode?
* @see #EPICS_V3_PV(String, boolean)
*/
final private boolean plain;
/** Channel name. */
final private String name;
/**the meta info for this pv*/
private MetaInfo totalMetaInfo = new MetaInfo();
/**
* If this pv is a meta field, then the metafield parent PV is where the data for this metafield is stored.
**/
private PV parentPVForMetaField = null;
/**
* If this pv has many meta fields archived, allarchiveFieldsData includes the meta field names and their values.
* allarchiveFieldsData is updated when meta field changes
* if this pv doesn't have meta field archived, this is always null.
*/
private ConcurrentHashMap<String, String> allarchiveFieldsData = null;
/** Runtime fields that are not archived/stored are stored here */
private ConcurrentHashMap<String, String> runTimeFieldsData = new ConcurrentHashMap<String, String>();
/** if this pv has many meta fields archived,changedarchiveFieldsData includes the changed meta values and the field names*/
private ConcurrentHashMap<String, String> changedarchiveFieldsData = null;
/**we save all meta field once every day and lastTimeStampWhenSavingarchiveFields is when we save all last meta fields*/
private Calendar lastTimeStampWhenSavingarchiveFields = null;
/**this pv is meta field or not*/
private boolean isarchiveFieldsField = false;
/** Store the value for this only in the runtime and not into the stores...*/
private boolean isruntimeFieldField = false;
/**
* Establish the monitor using a DBE_PROPERTY mask. DBE_PROPERTY events are also processed a little differently.
*/
private boolean isDBEProperties = false;
private PVConnectionState state = PVConnectionState.Idle;
/**
* Sourced from org/csstudio/platform/libs/epics/EpicsPlugin.java
* @author Original author unknown
* @author Kay Kasemir
* @author Sergei Chevtsov
*/
public enum MonitorMask {
/** Listen to changes in value beyond 'MDEL' threshold or alarm state*/
VALUE(1 | 4),
/** Listen to changes in value beyond 'ADEL' archive limit */
ARCHIVE(2 | 4),
/** Listen to changes in alarm state */
ALARM(4),
/** Listen to changes in property */
PROPERTY(8);
final private int mask;
private MonitorMask(final int mask) {
this.mask = mask;
}
/** @return Mask bits used in underlying CA call */
public int getMask() {
return mask;
}
}
/**configservice used by this pv*/
final private ConfigService configservice;
/** PVListeners of this PV */
final private CopyOnWriteArrayList<PVListener> listeners = new CopyOnWriteArrayList<PVListener>();
/** JCA channel. LOCK <code>this</code> on change. */
private RefCountedChannel channel_ref = null;
/**
* Either <code>null</code>, or the subscription identifier. LOCK
* <code>this</code> on change
*/
private Monitor subscription = null;
private Monitor dbePropertiesSubscription = null;
/**
* isConnected? <code>true</code> if we are currently connected (based on
* the most recent connection callback).
* <p>
* EPICS_V3_PV also runs notifyAll() on <code>this</code> whenever the
* connected flag changes to <code>true</code>.
*/
private volatile boolean connected = false;
/**
* isRunning? <code>true</code> if we want to receive value updates.
*/
private volatile boolean running = false;
/**the DBRTimeEvent constructor for this pv*/
private Constructor<? extends DBRTimeEvent> con;
/**the current DBRTimeEvent*/
private DBRTimeEvent dbrtimeevent;
/**the ArchDBRTypes of this pv*/
private ArchDBRTypes archDBRType = null;
/**
* The JCA command thread that processes actions for this PV.
* This should be inherited from the ArchiveChannel.
*/
private int jcaCommandThreadId;
/**
* The pvs' list who are controlled by this pv to stop or start archiving
**/
private ArrayList<String> controlledPVList = null;
/**
* The current status of all pvs who are controlled by this pv.
* if true, all pvs are archiving.
* else , all pvs are not
**/
private boolean enableAllPV = true;
/**Does this pv have one meta field archived?*/
private boolean hasMetaField = false;
/**
* the ioc host name where this pv is
*/
private String hostName;
@Override
public String getHostName(){
return hostName;
}
/**
*
* @return the status of all pvs controlled by this pv
*/
@Override
public boolean isEnableAllPV() {
return enableAllPV;
}
/**
* @see PV#getArchDBRTypes()
*/
@Override
public ArchDBRTypes getArchDBRTypes() {
return archDBRType;
}
/***
* @see PV#getDBRTimeEvent()
*/
@Override
public DBRTimeEvent getDBRTimeEvent() {
return dbrtimeevent;
}
/***
*get the meta info for this pv
* @return MetaInfo
*/
@Override
public MetaInfo getTotalMetaInfo() {
return totalMetaInfo;
}
/** Listener to the get... for meta data */
private final GetListener meta_get_listener = new GetListener() {
@Override
public void getCompleted(final GetEvent event) { // This runs in a CA
// thread
if (event.getStatus().isSuccessful()) {
state = PVConnectionState.GotMetaData;
final DBR dbr = event.getDBR();
totalMetaInfo.applyBasicInfo(EPICS_V3_PV.this.name, dbr, EPICS_V3_PV.this.configservice);
} else {
logger.error("The meta get listener was not successful for EPICS_V3_PV " + name);
}
PVContext.scheduleCommand(EPICS_V3_PV.this.name, EPICS_V3_PV.this.jcaCommandThreadId, EPICS_V3_PV.this.channel_ref, "getCompleted", new Runnable() {
@Override
public void run() {
subscribe();
}
});
}
};
/**
* @param pvName
* The PV name
*/
@Override
public void addControledPV(String pvName) {
controlledPVList.add(pvName);
}
/**
* Generate an EPICS PV.
*
* @param name
* The PV name.
* @param configservice The config service used by this pv
* @param isControlPV true if this is a pv controlling other pvs
* @param archDBRTypes ArchDBRTypes
* @param jcaCommandThreadId The JCA Command thread.
* @param isDBEProperties
*/
EPICS_V3_PV(final String name, ConfigService configservice, boolean isControlPV, ArchDBRTypes archDBRTypes, int jcaCommandThreadId, boolean isDBEProperties) {
this(name, false, configservice, jcaCommandThreadId);
this.archDBRType = archDBRTypes;
this.isDBEProperties = isDBEProperties;
if(archDBRTypes != null) {
this.con = configservice.getArchiverTypeSystem().getJCADBRConstructor(archDBRType);
}
if (isControlPV) {
this.controlledPVList = new ArrayList<String>();
}
}
/**
* Generate an EPICS PV.
*
* @param name
* The PV name.
* @param configservice The config service used by this pv
* @param jcaCommandThreadId The JCA Command thread
*/
// isControlPV
EPICS_V3_PV(final String name, ConfigService configservice, int jcaCommandThreadId) {
this(name, false, configservice, jcaCommandThreadId);
}
/**
* Generate an EPICS PV.
*
* @param name
* The PV name.
* @param plain
* When <code>true</code>, only the plain value is requested. No
* time etc. Some PVs only work in plain mode, example:
* "record.RTYP".
* @param configservice The config service used by this pv
* @param jcaCommandThreadId The JCA Command thread
*/
private EPICS_V3_PV(final String name, final boolean plain, ConfigService configservice, int jcaCommandThreadId) {
this.name = name;
this.plain = plain;
this.configservice = configservice;
this.jcaCommandThreadId = jcaCommandThreadId;
PVContext.setConfigservice(configservice);
}
/** Use finalize as last resort for cleanup, but give warnings. */
@Override
protected void finalize() throws Throwable {
super.finalize();
if (channel_ref != null) {
stop();
}
}
/** @return Returns the name. */
@Override
public String getName() {
return name;
}
/** {@inheritDoc} */
@Override
public void addListener(final PVListener listener) {
listeners.add(listener);
if (running && isConnected())
listener.pvValueUpdate(this);
}
/** {@inheritDoc} */
@Override
public void removeListener(final PVListener listener) {
listeners.remove(listener);
}
/**
* Try to connect to the PV. OK to call more than once.
*/
private void connect() throws Exception {
logger.debug("pv of"+this.name+" connectting");
PVContext.scheduleCommand(this.name, this.jcaCommandThreadId, this.channel_ref, "connect", new Runnable() {
@Override
public void run() {
//
try {
state = PVConnectionState.Connecting;
// Already attempted a connection?
synchronized (this) {
if (channel_ref == null) {
channel_ref = PVContext.getChannel(name,EPICS_V3_PV.this.jcaCommandThreadId, EPICS_V3_PV.this);
}
fireConnectionRequestMade();
if (channel_ref.getChannel().getConnectionState() == ConnectionState.CONNECTED) {
handleConnected(channel_ref.getChannel());
} else {
}
}
} catch (Exception e) {
logger.error("exception when connecting pv "+name, e);
}
}
});
}
/**
* Disconnect from the PV. OK to call more than once.
*/
private void disconnect() {
// Releasing the _last_ channel will close the context,
// which waits for the JCA Command thread to exit.
// If a connection or update for the channel happens at that time,
// the JCA command thread will send notifications to this PV,
// which had resulted in dead lock:
// This code locked the PV, then tried to join the JCA Command thread.
// JCA Command thread tried to lock the PV, so it could not exit.
// --> Don't lock while calling into the PVContext.
RefCountedChannel channel_ref_copy;
synchronized (this) {
// Never attempted a connection?
if (channel_ref == null)
return;
channel_ref_copy = channel_ref;
channel_ref = null;
connected = false;
}
try {
PVContext.releaseChannel(channel_ref_copy, this);
} catch (final IllegalStateException ile) {
logger.warn("exception when disconnecting pv "+name, ile);
} catch (final Throwable e) {
logger.error("exception when disconnecting pv "+name, e);
}
fireDisconnected();
}
/** Subscribe for value updates. */
private void subscribe() {
synchronized (this) {
// Prevent multiple subscriptions.
if (subscription != null) {
return;
}
// Late callback, channel already closed?
final RefCountedChannel ch_ref = channel_ref;
if (ch_ref == null) {
return;
}
final Channel channel = ch_ref.getChannel();
// final Logger logger = Activator.getLogger();
try {
if(channel.getConnectionState()!=Channel.CONNECTED){
return;
}
//
// the RefCountedChannel should maintain a single
// subscription to the underlying CAJ/JCA channel.
// So even with N PVs for the same channel, it's
// only one subscription on the network instead of
// N subscriptions.
final DBRType type = DBR_Helper.getTimeType(plain, channel.getFieldType());
state = PVConnectionState.Subscribing;
totalMetaInfo.setStartTime(System.currentTimeMillis());
// isnotTimestampDBR
if (this.name.endsWith(".RTYP")) {
subscription = channel.addMonitor(MonitorMask.ARCHIVE.getMask(), this);
} else {
if(this.isDBEProperties && dbePropertiesSubscription == null) {
logger.debug("Adding a DBE_PROPERTIES monitor for " + this.name);
dbePropertiesSubscription = channel.addMonitor(DBR_Helper.getControlType(channel.getFieldType()),
channel.getElementCount(), MonitorMask.PROPERTY.getMask(), new MonitorListener() {
@Override
public void monitorChanged(MonitorEvent monitorEvent) {
logger.debug("Got a DBE_PROPERTIES event for " + name);
processDBEPropertiesMonitorEvent(monitorEvent);
}
});
}
subscription = channel.addMonitor(type,
channel.getElementCount(), MonitorMask.ARCHIVE.getMask(), this);
}
} catch (final Exception ex) {
logger.error("exception when subscribing pv "+name, ex);
}
}
}
/** Unsubscribe from value updates. */
private void unsubscribe() {
Monitor sub_copy;
// Atomic access
synchronized (this) {
sub_copy = subscription;
subscription = null;
}
if (sub_copy == null) {
return;
}
try {
sub_copy.clear();
} catch(IllegalStateException ile) {
logger.warn("Illegal state exception when unsubscribing pv "+ name, ile);
} catch (final Exception ex) {
logger.error("exception when unsubscribing pv "+ name, ex);
}
}
/** {@inheritDoc} */
@Override
public void start() throws Exception {
if (running) {
return;
}
running = true;
connect();
}
/** {@inheritDoc} */
@Override
public boolean isRunning() {
return running;
}
/** {@inheritDoc} */
@Override
public boolean isConnected() {
return connected;
}
/** {@inheritDoc} */
@Override
public String getStateInfo() {
StringBuilder buf = new StringBuilder();
buf.append(state.toString());
if(this.channel_ref != null && this.channel_ref.getChannel() != null && (this.channel_ref.getChannel() instanceof CAJChannel)) {
CAJChannel cajChannel = (CAJChannel)this.channel_ref.getChannel();
int searchTries = cajChannel.getSearchTries();
buf.append(" Searches: " + searchTries);
}
return buf.toString();
}
/** {@inheritDoc} */
@Override
public void stop() {
running = false;
PVContext.scheduleCommand(this.name, this.jcaCommandThreadId, this.channel_ref, "stop", new Runnable() {
@Override
public void run() {
logger.debug("Stopping channel " + EPICS_V3_PV.this.name);
unsubscribe();
disconnect();
}
});
}
/** ConnectionListener interface. */
@Override
public void connectionChanged(final ConnectionEvent ev) {
logger.debug("Connection changed for pv " + this.name);
// This runs in a CA thread
if (ev.isConnected()) { // Transfer to JCACommandThread to avoid
// deadlocks
// The connect event can actually happen 'right
// away'
// when the channel is created, before we even
// get to assign
// the channel_ref. So use the channel from the
// event, not
// the channel_ref which might still be null.
//
// EngineContext.getInstance().getScheduler().execute(new Runnable()
PVContext.scheduleCommand(this.name, this.jcaCommandThreadId, this.channel_ref, "Connection changed connected", new Runnable() {
@Override
public void run() {
handleConnected((Channel) ev.getSource());
}
});
} else {
state = PVConnectionState.Disconnected;
connected = false;
PVContext.scheduleCommand(this.name, this.jcaCommandThreadId, this.channel_ref, "Connection changed disconnected", new Runnable() {
@Override
public void run() {
unsubscribe();
fireDisconnected();
}
});
}
}
/**
* PV is connected. Get meta info, or subscribe right away.
*/
private void handleConnected(final Channel channel) {
try {
if(channel.getConnectionState()!=Channel.CONNECTED){
return;
}
} catch(Exception ex) {
logger.warn("Exception handling connection state change for " + this.name, ex);
return;
}
if (state == PVConnectionState.Connected)
return;
state = PVConnectionState.Connected;
hostName=channel.getHostName();
totalMetaInfo.setHostName(hostName);
for (final PVListener listener : listeners) {
listener.pvConnected(this);
}
// If we're "running", we need to get the meta data and
// then subscribe.
// Otherwise, we're done.
if (!running) {
connected = true;
// meta = null;
synchronized (this) {
this.notifyAll();
}
return;
}
// else: running, get meta data, then subscribe
try {
DBRType type = channel.getFieldType();
if (!(plain || type.isSTRING())) {
state = PVConnectionState.GettingMetadata;
if (type.isDOUBLE() || type.isFLOAT())
type = DBRType.CTRL_DOUBLE;
else if (type.isENUM())
type = DBRType.LABELS_ENUM;
else if (type.isINT())
type = DBRType.CTRL_INT;
else
type = DBRType.CTRL_SHORT;
channel.get(type, 1, meta_get_listener);
return;
}
} catch (final Exception ex) {
logger.error("exception when handleConnect "+name, ex);
return;
}
// Meta info is not requested, not available for this type,
// or there was an error in the get call.
// So reset it, then just move on to the subscription.
// meta = null;
subscribe();
}
/** MonitorListener interface. */
@Override
public void monitorChanged(final MonitorEvent ev) {
// final Logger log = Activator.getLogger();
// This runs in a CA thread.
// Ignore values that arrive after stop()
if (!running) {
return;
}
if (subscription == null) {
return;
}
if (ev.getStatus() == null || !ev.getStatus().isSuccessful()) {
return;
}
if (controlledPVList != null) {
// this pv is control pv.
try {
updateAllControlPVEnablMent(ev);
} catch (Exception e) {
logger.error(
"exception in monitor changed function when updatinng controlled pvs' enablement for " + this.name,
e);
}
return;
}
state = PVConnectionState.GotMonitor;
if (!connected)
connected = true;
try {
try {
DBR dbr = ev.getDBR();
if (dbr == null) {
return;
}
if (this.name.endsWith(".RTYP")) {
String rtypName = (((DBR_String) dbr).getStringValue())[0];
dbrtimeevent = new POJOEvent(ArchDBRTypes.DBR_SCALAR_STRING, TimeUtils.now(), new ScalarStringSampleValue(rtypName), 0, 0);
return;
}
// dbr.printInfo();
ArchDBRTypes generatedDBRType = JCA2ArchDBRType.valueOf(dbr);
if (archDBRType == null) {
archDBRType = generatedDBRType;
con = configservice.getArchiverTypeSystem().getJCADBRConstructor(archDBRType);
} else {
assert(con != null);
if(generatedDBRType != archDBRType) {
logger.warn("The type of PV " + this.name + " has changed from " + archDBRType + " to " + generatedDBRType);
fireDroppedSample(PVListener.DroppedReason.TYPE_CHANGE);
return;
}
}
dbrtimeevent = con.newInstance(dbr);
totalMetaInfo.computeRate(dbrtimeevent);
dbr = null;
} catch (Exception e) {
logger.error(
"exception in monitor changed function when converting DBR to dbrtimeevent for pv " + this.name,
e);
}
updataMetaDataInParentPV(dbrtimeevent);
// if this pv has meta data , handle here
if (hasMetaField) {
// //////////handle the field value when it
// changes//////////////
if (changedarchiveFieldsData.size() > 0) {
logger.debug("Adding changed field for pv " + name + " with " + changedarchiveFieldsData.size());
HashMap<String, String> tempHashMap = new HashMap<String, String>();
tempHashMap.putAll(changedarchiveFieldsData);
// dbrtimeevent.s
dbrtimeevent.setFieldValues(tempHashMap, true);
changedarchiveFieldsData.clear();
}
// //////////////////////////
// ////////////save all the fields once every day//////////////
if (this.lastTimeStampWhenSavingarchiveFields == null) {
if (allarchiveFieldsData.size() != 0) {
saveMetaDataOnceEveryDay();
}
} else {
Calendar currentCalendar = Calendar.getInstance();
currentCalendar.add(Calendar.DAY_OF_MONTH, -1);
if (currentCalendar
.after(lastTimeStampWhenSavingarchiveFields)) {
// Calendar currentCalendar2=Calendar.getInstance();
saveMetaDataOnceEveryDay();
}
}
// //////////////////////////////
}
fireValueUpdate();
} catch (final Exception ex) {
logger.error("exception in monitor changed for pv " + this.name, ex);
}
}
/** Notify all listeners. */
private void fireValueUpdate() {
for (final PVListener listener : listeners) {
listener.pvValueUpdate(this);
}
}
/** Notify all listeners. */
private void fireDisconnected() {
for (final PVListener listener : listeners) {
listener.pvDisconnected(this);
}
}
/** Notify all listeners. */
private void fireConnectionRequestMade() {
for (final PVListener listener : listeners) {
listener.pvConnectionRequestMade(this);
}
}
private void fireDroppedSample(PVListener.DroppedReason reason) {
for (final PVListener listener : listeners) {
listener.pvDroppedSample(this, reason);
}
}
@Override
public String toString() {
return "EPICS_V3_PV '" + name + "'";
}
/***
* if this is a pv control other pvs, when this pv's value changes, it will stop or restart all controlled pvs.
* @param ev
* @throws Exception error when update all controlled pv's archiving status
*/
private void updateAllControlPVEnablMent(MonitorEvent ev) throws Exception {
final DBR dbr = ev.getDBR();
boolean enable = DBR_Helper.decodeBooleanValue(dbr);
ArrayList<String> copyOfControlledPVList = new ArrayList<String>(controlledPVList);
if (enable) {
enableAllPV = true;
for (String pvName : copyOfControlledPVList) {
logger.debug(pvName+" will be resumed");
ArchiveEngine.resumeArchivingPV(pvName, configservice);
}
} else {
enableAllPV = false;
for (String pvName : copyOfControlledPVList) {
logger.debug(pvName+" will be paused");
ArchiveEngine.pauseArchivingPV(pvName, configservice);
}
}
}
@Override
public void setMetaFieldParentPV(PV parentPV, boolean isRuntimeOnly) {
this.parentPVForMetaField = parentPV;
isarchiveFieldsField = true;
this.isruntimeFieldField = isRuntimeOnly;
}
/**
* update the meta field value in the parent pv.
* @param dbrtimeevent DBRTimeEvent
*/
private void updataMetaDataInParentPV(final DBRTimeEvent dbrtimeevent) {
if (isarchiveFieldsField) {
parentPVForMetaField.updataMetaFieldValue(this.name, "" + dbrtimeevent.getSampleValue().toString());
}
}
/**
* @see PV#updataMetaFieldValue
*/
@Override
public void updataMetaFieldValue(String PVname, String fieldValue) {
String[] strs = PVname.split("\\.");
String fieldName = strs[strs.length - 1];
if(isruntimeFieldField) {
logger.debug("Not storing value change for runtime field " + fieldName);
runTimeFieldsData.put(fieldName, fieldValue);
} else {
logger.debug("Storing value change for meta field " + fieldName);
allarchiveFieldsData.put(fieldName, fieldValue);
changedarchiveFieldsData.put(fieldName, fieldValue);
}
}
/***
* @see PV#markPVHasMetafields
*/
@Override
public void markPVHasMetafields(boolean hasMetaField) {
if (hasMetaField) {
allarchiveFieldsData = new ConcurrentHashMap<String, String>();
changedarchiveFieldsData = new ConcurrentHashMap<String, String>();
}
this.hasMetaField = hasMetaField;
}
/**
* save the meta data
*/
private void saveMetaDataOnceEveryDay() {
HashMap<String, String> tempHashMap = new HashMap<String, String>();
tempHashMap.putAll(allarchiveFieldsData);
if(runTimeFieldsData != null && !runTimeFieldsData.isEmpty()) {
// This should store fields like the description at least once every day.
tempHashMap.putAll(runTimeFieldsData);
}
if(this.totalMetaInfo != null) {
if(this.totalMetaInfo.getUnit() != null) {
tempHashMap.put("EGU", this.totalMetaInfo.getUnit());
}
if(this.totalMetaInfo.getPrecision() != 0) {
tempHashMap.put("PREC", Integer.toString(this.totalMetaInfo.getPrecision()));
}
}
// dbrtimeevent.s
dbrtimeevent.setFieldValues(tempHashMap, false);
lastTimeStampWhenSavingarchiveFields = Calendar.getInstance();
}
@Override
public String getLowLevelChannelInfo() {
// Commented out when using JCA. This seems to work in CAJ but not in JCA.
/* if(channel_ref != null) {
ByteArrayOutputStream os = new ByteArrayOutputStream();
PrintStream out = new PrintStream(os);
channel_ref.getChannel().printInfo(out);
out.close();
return os.toString();
}
*/ return null;
}
@Override
public void updateTotalMetaInfo() throws IllegalStateException, CAException {
GetListener getListener = new GetListener() {
@Override
public void getCompleted(final GetEvent event) {
// This runs in a CA thread
if (event.getStatus().isSuccessful()) {
state = PVConnectionState.GotMetaData;
final DBR dbr = event.getDBR();
logger.debug("Updating metadata (EGU/PREC etc) for pv " + EPICS_V3_PV.this.name);
totalMetaInfo.applyBasicInfo(EPICS_V3_PV.this.name, dbr, EPICS_V3_PV.this.configservice);
} else {
logger.error("The meta get listener was not successful for EPICS_V3_PV " + name);
}
}
};
if(channel_ref != null) {
if(channel_ref.getChannel().getConnectionState() == ConnectionState.CONNECTED) {
DBRType type = channel_ref.getChannel().getFieldType();
if (!(plain || type.isSTRING())) {
state = PVConnectionState.GettingMetadata;
if (type.isDOUBLE() || type.isFLOAT())
type = DBRType.CTRL_DOUBLE;
else if (type.isENUM())
type = DBRType.LABELS_ENUM;
else if (type.isINT())
type = DBRType.CTRL_INT;
else
type = DBRType.CTRL_SHORT;
channel_ref.getChannel().get(type, 1, getListener);
}
}
}
}
/**
* Combine the metadata from various sources and return the latest copy.
* @see PV#getLatestMetadata
*/
@Override
public HashMap<String, String> getLatestMetadata() {
HashMap<String, String> retVal = new HashMap<String, String>();
// The totalMetaInfo is updated once every 24hours...
MetaInfo metaInfo = this.getTotalMetaInfo();
if(metaInfo != null) {
metaInfo.addToDict(retVal);
}
// Add the latest value of the fields we are monitoring.
if(allarchiveFieldsData != null) {
retVal.putAll(allarchiveFieldsData);
}
if(runTimeFieldsData != null) {
retVal.putAll(runTimeFieldsData);
}
return retVal;
}
public void setDBEroperties() {
this.isDBEProperties = true;
}
private void addUpdateChangedDBEPropertyMetaField(String fieldName, String value) {
String currentValue = this.allarchiveFieldsData.get(fieldName);
if(currentValue != null && value != null && !currentValue.equals(value)) {
this.changedarchiveFieldsData.put(fieldName, value);
}
this.allarchiveFieldsData.put(fieldName, value);
}
private void processDBEPropertiesMonitorEvent(MonitorEvent monitorEvent) {
DBR dbr = monitorEvent.getDBR();
if (dbr.isLABELS()) {
logger.debug("Updating DBE_PROPERTIES labels for ENUM pv " + name);
final DBR_LABELS_Enum labels = (DBR_LABELS_Enum)dbr;
addUpdateChangedDBEPropertyMetaField("LABELS", String.join(",", labels.getLabels()));
} else if (dbr instanceof DBR_CTRL_Double || dbr instanceof DBR_CTRL_Int) {
logger.debug("Updating DBE_PROPERTIES metafields for DBR_CTRL_Double for pv " + name);
final DBR_CTRL_Double ctrl = (DBR_CTRL_Double)dbr;
// fieldsAvailableFromDBRControl = new String[] {"PREC", "EGU", "HOPR", "LOPR", "HIHI", "HIGH", "LOW", "LOLO", "DRVH", "DRVL" };
addUpdateChangedDBEPropertyMetaField("PREC", Short.toString(ctrl.getPrecision()));
addUpdateChangedDBEPropertyMetaField("EGU", ctrl.getUnits());
addUpdateChangedDBEPropertyMetaField("HOPR", ctrl.getUpperDispLimit().toString());
addUpdateChangedDBEPropertyMetaField("LOPR", ctrl.getLowerDispLimit().toString());
addUpdateChangedDBEPropertyMetaField("HIHI", ctrl.getUpperAlarmLimit().toString());
addUpdateChangedDBEPropertyMetaField("HIGH", ctrl.getUpperWarningLimit().toString());
addUpdateChangedDBEPropertyMetaField("LOW", ctrl.getLowerWarningLimit().toString());
addUpdateChangedDBEPropertyMetaField("LOLO", ctrl.getLowerAlarmLimit().toString());
addUpdateChangedDBEPropertyMetaField("DRVH", ctrl.getUpperCtrlLimit().toString());
addUpdateChangedDBEPropertyMetaField("DRVL", ctrl.getLowerCtrlLimit().toString());
} else {
logger.error("In applyBasicInfo, cannot determine dbr type for " + (dbr != null ? dbr.getClass().getName() : "Null DBR"));
}
}
}