package tcg.scada.da;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.concurrent.Semaphore;
import org.apache.log4j.Logger;
import tcg.common.LoggerManager;
import tcg.common.util.ExpressionParser;
import tcg.scada.cos.CosDpQualityEnum;
import tcg.scada.cos.CosDpValueStruct;
import tcg.scada.cos.CosDpValueTypeEnum;
import tcg.scada.cos.CosDpValueUnion;
public class xxxDataPoint
{
public int keyId = 0;
public String name = "";
public int wordAddress = 0;
public int bitAddress = 0;
public int byteLength = 0;
//with some protocol, different function code might have overlapping address
//thus, we require this to distinguish possible mix of addressing
public int functionCode = 0;
public EDataPointType type = EDataPointType.TYPE_REAL;
public IEquipment equipment = null;
public DataPointServer dpServer = null;
public ISubsystem subsystem = null;
private static String KEYWORD_START = "{";
private static String KEYWORD_END = "}";
private static Logger logger_ = LoggerManager.getLogger(xxxDataPoint.class.toString());
// expression value for calculated datapoint
private String expression_ = "";
private ExpressionParser expressionParser_ = null;
private CosDpQualityEnum expressionQuality_ = CosDpQualityEnum.QualityGood;
private ArrayList<xxxDataPoint> expressionParams_ = null;
private boolean isEvaluated_ = false; //used to prevent multiple recursive evaluation
// launching condition for control point
private String lccExpression_ = "";
private ExpressionParser lccParser_ = null;
private ArrayList<xxxDataPoint> lccParams_ = null;
// return condition for control point
private String rccExpression_ = "";
private ExpressionParser rccParser_ = null;
private ArrayList<xxxDataPoint> rccParams_ = null;
// expression for engineering conversion
// for linear conversion: y = mx + b
@SuppressWarnings("unused")
private String engExpression_ = "";
@SuppressWarnings("unused")
private ExpressionParser engParser_ = null;
// data type
private EDataPointDataType dataType_ = EDataPointDataType.TYPE_NUMBER;
// output value
private CosDpValueStruct outputValue_ = new CosDpValueStruct();
// source value
private CosDpValueStruct sourceValue_ = new CosDpValueStruct();
// datapoint timestamp. (>< value timestamp)
// this is used mostly to check for updates
private long updateTimestamp_ = 0;
// various datapoint flag
private boolean isOverride_ = false;
private boolean isInhibit_ = false;
private boolean isAlarmInhibit_ = false;
private boolean isAlarmNotAck_ = false;
private boolean isAlarmNotNorm_ = false;
// list of calculated points which use this datapoint as parameter
private ArrayList<xxxDataPoint> relatedDataPoints_ = new ArrayList<xxxDataPoint>();
// flag to prevent infinite recursive calculation
private final Semaphore semaphore_ = new Semaphore(1, true);
// flag to prevent multiple notification over several modification steps
//private boolean isInModification_ = false;
/**
* Default constructor
*/
public xxxDataPoint()
{
// expression quality. default to good quality. this way, we do not need to do
// specific processing for non calculated point i.e. real point or virtual point
expressionQuality_ = CosDpQualityEnum.QualityGood;
}
// public void startModification()
// {
// //start modification mode. in this mode, no notification or synchronization is done
// // until endModification() is called.
// isInModification_ = true;
// }
//
// public void endModification()
// {
// //synchronize the value
// int status1 = synchronizeValue();
//
// //update the quality
// int status2 = updateQuality();
//
// //if either the value/timestamp has changed or quality has changed, send notification
// if (status1 > 0 || status2 > 0)
// {
// // update the dp timestamp
// updateTimestamp_ = Calendar.getInstance().getTimeInMillis();
// // update notify the datapoint server
// if (dpServer != null)
// {
// dpServer.onDataPointChange(this);
// }
// // re-evaluate all related datapoints
// for (int i = 0; i < relatedDataPoints_.size(); i++)
// {
// relatedDataPoints_.get(i).evaluate();
// }
// }
//
// isInModification_ = false;
// }
/**
* Preferred constructor. Set the data type.
*
* @param dataType
* - the datapoint data type
*/
public xxxDataPoint(EDataPointDataType dataType)
{
setDataType(dataType);
// expression quality. default to good quality. this way, we do not need to do
// specific processing for non calculated point i.e. real point or virtual point
expressionQuality_ = CosDpQualityEnum.QualityGood;
}
/**
* Set the data type of this datapoint
*
* @param dataType
* - the data type
*/
public synchronized void setDataType(EDataPointDataType dataType)
{
dataType_ = dataType;
// initialize the value
switch (dataType_)
{
case TYPE_BOOLEAN:
outputValue_.value.boolValue(false);
break;
case TYPE_NUMBER:
outputValue_.value.longValue(0);
break;
case TYPE_UNSIGNED:
outputValue_.value.unsignedValue(0);
break;
case TYPE_DOUBLE:
outputValue_.value.dblValue(0.0);
break;
case TYPE_STRING:
outputValue_.value.charValue("");
break;
case TYPE_BCD:
// internally, BCD type is represented as number
outputValue_.value.longValue(0);
break;
}
outputValue_.timestamp = 0;
outputValue_.quality = CosDpQualityEnum.QualityBad;
// source value
sourceValue_.value = outputValue_.value;
sourceValue_.timestamp = 0;
// source quality. default to good quality.
// only if it is bad quality should this parameter has affect/meaning
sourceValue_.quality = CosDpQualityEnum.QualityGood;
}
// get the datapoint internal data type
public EDataPointDataType getDataType()
{
return dataType_;
}
/**
* Helper function to initialize the datapoint state
*
* @param isInhibit
* - last known inhibit flag
* @param isOverride
* - last known override flag
* @param isAlarmInhibit
* - last known alarm inhibit flag
* @param isAlarmNotAck
* - last known alarm not-ack flag
* @param isAlarmNotNorm
* - last known alarm not-norm flag
*/
protected synchronized void setInitialState(boolean isInhibit, boolean isOverride,
boolean isAlarmInhibit, boolean isAlarmNotAck, boolean isAlarmNotNorm)
{
isInhibit_ = isInhibit;
isOverride_ = isOverride;
isAlarmInhibit_ = isAlarmInhibit;
isAlarmNotAck_ = isAlarmNotAck;
isAlarmNotNorm_ = isAlarmNotNorm;
}
// get the update timestamp
public long getUpdateTimestamp()
{
return updateTimestamp_;
}
// get datapoint value timestamp
public long getTimestamp()
{
return outputValue_.timestamp;
}
// get datapoint quality
public CosDpQualityEnum getQuality()
{
return outputValue_.quality;
}
/**
* Calculate the datapoint quality.
*
* Generally a client should never call this function. This should be triggered
* automatically upon changes on related attributes/parameters.
*
* @return - +1 if there is an updates, 0 if no updates, -1 if there is an error
*/
protected synchronized int updateQuality()
{
int status = update_quality();
if (status > 0)
{
// update the timestamp
updateTimestamp_ = Calendar.getInstance().getTimeInMillis();
// notify the datastore
if (dpServer != null)
{
dpServer.onDataPointChange(this);
}
// because, the quality of parameters affects the quality of a calculated point,
// we need to re-evaluate all related datapoints
for (int i = 0; i < relatedDataPoints_.size(); i++)
{
relatedDataPoints_.get(i).evaluate();
}
}
return status;
}
/**
* Synchronize source value and output value.
*
* Generally a client should never call this function. This should be triggered
* automatically upon changes on related attributes/parameters.
*
* @return - +1 if there is an updates, 0 if no updates, -1 if there is an error
*/
protected synchronized int synchronizeValue()
{
int status = synchronize_value();
if (status > 0)
{
// update the timestamp
updateTimestamp_ = Calendar.getInstance().getTimeInMillis();
// notify the datastore
if (dpServer != null)
{
dpServer.onDataPointChange(this);
}
// re-evaluate related point
for (int i = 0; i < relatedDataPoints_.size(); i++)
{
relatedDataPoints_.get(i).evaluate();
}
}
return status;
}
// get stringified datapoint value
public String getStringifiedValue()
{
return CosDpValueUnion2String(outputValue_.value);
}
// set datapoint value with a stringified value
protected synchronized int setStringifiedValue(String value)
{
// TODO: might not want to use setValue() because setValue() will create an associated event
// whereas this function most probably is used during the initialization
return setValue(String2CosDpValueUnion(outputValue_.value.discriminator(), value));
}
// get datapoint value
public CosDpValueUnion getValue()
{
return outputValue_.value;
}
/**
* Set the datapoint value with a value of another datapoint.
*
* @param value
* - the new data point value
* @return +1 if successful, 0 if the value is already the target value, -1 if the new value is
* invalid
*/
public synchronized int setValue(CosDpValueUnion value)
{
// pass the set-value to underlying subsystem if necessary
if (subsystem != null && subsystem.isSourceSetValue()
&& type == EDataPointType.TYPE_REAL)
{
if (subsystem.setValue(this, value))
{
return +1;
}
return -1;
}
else
{
int status = copy(outputValue_.value, value);
if (status > 0)
{
// update the output value timestamp. if necessary we can override it later
outputValue_.timestamp = Calendar.getInstance().getTimeInMillis();
// update the dp timestamp
updateTimestamp_ = outputValue_.timestamp;
// update notify the datapoint server
if (dpServer != null)
{
dpServer.onDataPointChange(this);
}
// re-evaluate all related datapoints
for (int i = 0; i < relatedDataPoints_.size(); i++)
{
relatedDataPoints_.get(i).evaluate();
}
} //if (status > 0)
return status;
}
}
/**
* Set the datapoint value using the native type of the value.
*
* @param value
* - the new boolean value
* @return +1 if successful, 0 if the value is already the target value, -1 if the new value is
* invalid
*/
public synchronized int setBooleanValue(boolean value)
{
// pass the set-value to underlying subsystem if necessary
if (subsystem != null && subsystem.isSourceSetValue()
&& type == EDataPointType.TYPE_REAL)
{
// convert to associated structure
CosDpValueUnion myValue = new CosDpValueUnion();
myValue.boolValue(value);
// pass to subsystem
if (subsystem.setValue(this, myValue))
{
return +1;
}
return -1;
}
else
{
int status = setBooleanValue(outputValue_.value, value);
if (status > 0)
{
// update the output value timestamp. if necessary we can override it later
outputValue_.timestamp = Calendar.getInstance().getTimeInMillis();
// update the dp timestamp
updateTimestamp_ = outputValue_.timestamp;
// update notify the datapoint server
if (dpServer != null)
{
dpServer.onDataPointChange(this);
}
// re-evaluate all related datapoints
for (int i = 0; i < relatedDataPoints_.size(); i++)
{
relatedDataPoints_.get(i).evaluate();
}
} //if (status > 0)
return status;
}
}
/**
* Set the datapoint value using the native type of the value.
*
* @param value
* - the new corba::long value
* @return +1 if successful, 0 if the value is already the target value, -1 if the new value is
* invalid
*/
public synchronized int setNumberValue(int value)
{
// pass the set-value to underlying subsystem if necessary
if (subsystem != null && subsystem.isSourceSetValue()
&& type == EDataPointType.TYPE_REAL)
{
// convert to associated structure
CosDpValueUnion myValue = new CosDpValueUnion();
myValue.longValue(value);
// pass to subsystem
if (subsystem.setValue(this, myValue))
{
return +1;
}
return -1;
}
else
{
int status = setNumberValue(outputValue_.value, value);
if (status > 0)
{
// update the source value timestamp. if necessary we can override it later
outputValue_.timestamp = Calendar.getInstance().getTimeInMillis();
// update the dp timestamp
updateTimestamp_ = outputValue_.timestamp;
// update notify the datapoint server
if (dpServer != null)
{
dpServer.onDataPointChange(this);
}
// re-evaluate all related datapoints
for (int i = 0; i < relatedDataPoints_.size(); i++)
{
relatedDataPoints_.get(i).evaluate();
}
} //if (status > 0)
return status;
}
}
/**
* Set the datapoint value using the native type of the value.
*
* @param value
* - the new corba::unsigned value
* @return +1 if successful, 0 if the value is already the target value, -1 if the new value is
* invalid
*/
public synchronized int setUnsignedValue(int value)
{
// pass the set-value to underlying subsystem if necessary
if (subsystem != null && subsystem.isSourceSetValue()
&& type == EDataPointType.TYPE_REAL)
{
// convert to associated structure
CosDpValueUnion myValue = new CosDpValueUnion();
myValue.unsignedValue(value);
// pass to subsystem
if (subsystem.setValue(this, myValue))
{
return +1;
}
return -1;
}
else
{
int status = setUnsignedValue(outputValue_.value, value);
if (status > 0)
{
// update the source value timestamp. if necessary we can override it later
outputValue_.timestamp = Calendar.getInstance().getTimeInMillis();
// update the dp timestamp
updateTimestamp_ = outputValue_.timestamp;
// update notify the datapoint server
if (dpServer != null)
{
dpServer.onDataPointChange(this);
}
// re-evaluate all related datapoints
for (int i = 0; i < relatedDataPoints_.size(); i++)
{
relatedDataPoints_.get(i).evaluate();
}
} //if (status > 0)
return status;
}
}
/**
* Set the datapoint value using the native type of the value.
*
* @param value
* - the new double value
* @return +1 if successful, 0 if the value is already the target value, -1 if the new value is
* invalid
*/
public synchronized int setDoubleValue(double value)
{
// pass the set-value to underlying subsystem if necessary
if (subsystem != null && subsystem.isSourceSetValue()
&& type == EDataPointType.TYPE_REAL)
{
// convert to associated structure
CosDpValueUnion myValue = new CosDpValueUnion();
myValue.dblValue(value);
// pass to subsystem
if (subsystem.setValue(this, myValue))
{
return +1;
}
return -1;
}
else
{
int status = setDoubleValue(outputValue_.value, value);
if (status > 0)
{
// update the source value timestamp. if necessary we can override it later
outputValue_.timestamp = Calendar.getInstance().getTimeInMillis();
// update the dp timestamp
updateTimestamp_ = outputValue_.timestamp;
// update notify the datapoint server
if (dpServer != null)
{
dpServer.onDataPointChange(this);
}
// re-evaluate all related datapoints
for (int i = 0; i < relatedDataPoints_.size(); i++)
{
relatedDataPoints_.get(i).evaluate();
}
} //if (status > 0)
return status;
}
}
/**
* Set the datapoint value using the native type of the value.
*
* @param value
* - the new string value
* @return +1 if successful, 0 if the value is already the target value, -1 if the new value is
* invalid
*/
public synchronized int setStringValue(String value)
{
// pass the set-value to underlying subsystem if necessary
if (subsystem != null && subsystem.isSourceSetValue()
&& type == EDataPointType.TYPE_REAL)
{
// convert to associated structure
CosDpValueUnion myValue = new CosDpValueUnion();
myValue.charValue(value);
// pass to subsystem
if (subsystem.setValue(this, myValue))
{
return +1;
}
return -1;
}
else
{
int status = setStringValue(outputValue_.value, value);
if (status > 0)
{
// update the source value timestamp. if necessary we can override it later
outputValue_.timestamp = Calendar.getInstance().getTimeInMillis();
// update the dp timestamp
updateTimestamp_ = outputValue_.timestamp;
// update notify the datapoint server
if (dpServer != null)
{
dpServer.onDataPointChange(this);
}
// re-evaluate all related datapoints
for (int i = 0; i < relatedDataPoints_.size(); i++)
{
relatedDataPoints_.get(i).evaluate();
}
} //if (status > 0)
return status;
}
}
// get source timestamp
public long getSourceTimestamp()
{
return sourceValue_.timestamp;
}
// set source timestamp
public synchronized int setSourceTimestamp(long timestamp)
{
if (sourceValue_.timestamp == timestamp)
{
return 0;
}
// copy the timestamp
sourceValue_.timestamp = timestamp;
//synchronize the timestamp
if (outputValue_.timestamp != sourceValue_.timestamp)
{
outputValue_.timestamp = sourceValue_.timestamp;
// notify the dpServer
if (dpServer != null)
{
dpServer.onDataPointChange(this);
}
}
// update the dp timestamp
updateTimestamp_ = Calendar.getInstance().getTimeInMillis();
// notify the dpServer
if (dpServer != null)
{
dpServer.onDataPointSourceChange(this);
}
return +1;
}
public CosDpQualityEnum getSourceQuality()
{
return sourceValue_.quality;
}
/**
* Set the datapoint source quality
*
* @param quality
* - the new source quality
*/
public synchronized int setSourceQuality(CosDpQualityEnum quality)
{
if (sourceValue_.quality == quality)
{
return 0;
}
// update the source quality
sourceValue_.quality = quality;
// recalculate the datapoint quality
if (update_quality() > 0)
{
//notify dpserver
if (dpServer != null)
{
dpServer.onDataPointChange(this);
}
// because, the quality of parameters affects the quality of a calculated point,
// we need to re-evaluate all related datapoints
for (int i = 0; i < relatedDataPoints_.size(); i++)
{
relatedDataPoints_.get(i).evaluate();
}
}
// update the dp timestamp
updateTimestamp_ = Calendar.getInstance().getTimeInMillis();
// notify the dpServer
if (dpServer != null)
{
dpServer.onDataPointSourceChange(this);
}
return +1;
}
// get stringified datapoint source value
public String getStringifiedSourceValue()
{
return CosDpValueUnion2String(sourceValue_.value);
}
// set datapoint source value with a stringified value
protected synchronized int setStringifiedSourceValue(String value)
{
// TODO: might not want to use setSourceValue() because setSourceValue() will create an
// associated event
// whereas this function most probably is used during the initialization
return setSourceValue(String2CosDpValueUnion(sourceValue_.value.discriminator(), value));
}
// get source value
public CosDpValueUnion getSourceValue()
{
return sourceValue_.value;
}
/**
* Set the datapoint source value, quality and timestamp with a value, quality, and timestamp
* of another datapoint. To ignore the timestamp paremeter, set the timestamp to 0.
*
* @param value
* - the new data point value/quality/timestamp
* @return +1 if successful, 0 if the value is already the target value, -1 if the new value is
* invalid
*/
public synchronized int setSourceValue(CosDpValueStruct value)
{
int retval = -1;
//if there is engineering conversion, do it here
if (engParser_ != null && engParser_.isValid())
{
value.value = perform_eng_conversion(value.value);
}
//update the source value
int status1 = copy(sourceValue_.value, value.value);
if (status1 < 0)
{
//invalid value
return retval;
}
// update the source quality
int status2 = 0;
if (sourceValue_.quality != value.quality)
{
status2 = 1;
sourceValue_.quality = value.quality;
}
//update the source timestamp
int status3 = 0;
if (value.timestamp > 0 && sourceValue_.timestamp != value.timestamp)
{
status3 = 1;
sourceValue_.timestamp = value.timestamp;
}
// any changes?
if (status1 > 0 || status2 > 0 || status3 > 0)
{
retval = 1;
//notify the dpserver
if (dpServer != null)
{
dpServer.onDataPointSourceChange(this);
}
//synchronize the value
status1 = synchronize_value();
//update the quality
status2 = update_quality();
//any changes in output value?
if (status1 > 0 || status2 > 0)
{
//notify dpserver
if (dpServer != null)
{
dpServer.onDataPointChange(this);
}
// re-evaluate all related datapoints
for (int i = 0; i < relatedDataPoints_.size(); i++)
{
relatedDataPoints_.get(i).evaluate();
}
} //if (status1 > 0 || status2 > 0)
//update the dp timestamp
updateTimestamp_ = Calendar.getInstance().getTimeInMillis();
}
else if (status1 == 0 && status2 == 0 && status3 == 0)
{
retval = 0;
}
return retval;
}
/**
* Set the datapoint source value with a value of another datapoint.
*
* @param value
* - the new data point value
* @return +1 if successful, 0 if the value is already the target value, -1 if the new value is
* invalid
*/
protected synchronized int setSourceValue(CosDpValueUnion value)
{
//if there is engineering conversion, do it here
if (engParser_ != null && engParser_.isValid())
{
value = perform_eng_conversion(value);
}
int status = copy(sourceValue_.value, value);
if (status > 0)
{
// update the source value timestamp. if necessary we can override it later
sourceValue_.timestamp = Calendar.getInstance().getTimeInMillis();
// synchronize
if (synchronize_value() > 0)
{
//notify dpserver
if (dpServer != null)
{
dpServer.onDataPointChange(this);
}
// re-evaluate all related datapoints
for (int i = 0; i < relatedDataPoints_.size(); i++)
{
relatedDataPoints_.get(i).evaluate();
}
}
// update the dp timestamp
updateTimestamp_ = sourceValue_.timestamp;
// we still need to notify the dpServer
if (dpServer != null)
{
dpServer.onDataPointSourceChange(this);
}
}
return status;
}
/**
* Set the datapoint source value using the native type of the value.
*
* @param value
* - the new boolean value
* @return +1 if successful, 0 if the value is already the target value, -1 if the new value is
* invalid
*/
protected synchronized int setBooleanSourceValue(boolean value)
{
//if there is engineering conversion, do it here
if (engParser_ != null && engParser_.isValid())
{
value = perform_eng_conversion(value);
}
//copy the value
int status = setBooleanValue(sourceValue_.value, value);
if (status > 0)
{
// update the source value timestamp. if necessary we can override it later
sourceValue_.timestamp = Calendar.getInstance().getTimeInMillis();
// synchronize
if (synchronize_value() > 0)
{
//notify dpserver
if (dpServer != null)
{
dpServer.onDataPointChange(this);
}
// re-evaluate all related datapoints
for (int i = 0; i < relatedDataPoints_.size(); i++)
{
relatedDataPoints_.get(i).evaluate();
}
}
// update the dp timestamp
updateTimestamp_ = sourceValue_.timestamp;
// we still need to notify the dpServer
if (dpServer != null)
{
dpServer.onDataPointSourceChange(this);
}
}
return status;
}
/**
* Set the datapoint source value using the native type of the value.
*
* @param value
* - the new corba::long value
* @return +1 if successful, 0 if the value is already the target value, -1 if the new value is
* invalid
*/
protected synchronized int setNumberSourceValue(int value)
{
//if there is engineering conversion, do it here
if (engParser_ != null && engParser_.isValid())
{
value = perform_eng_conversion(value);
}
//copy the value
int status = setNumberValue(sourceValue_.value, value);
if (status > 0)
{
// update the source value timestamp. if necessary we can override it later
sourceValue_.timestamp = Calendar.getInstance().getTimeInMillis();
// synchronize
if (synchronize_value() > 0)
{
//notify dpserver
if (dpServer != null)
{
dpServer.onDataPointChange(this);
}
// re-evaluate all related datapoints
for (int i = 0; i < relatedDataPoints_.size(); i++)
{
relatedDataPoints_.get(i).evaluate();
}
}
// update the dp timestamp
updateTimestamp_ = sourceValue_.timestamp;
// we still need to notify the dpServer
if (dpServer != null)
{
dpServer.onDataPointSourceChange(this);
}
}
return status;
}
/**
* Set the datapoint source value using the native type of the value.
*
* @param value
* - the new corba::unsigned value
* @return +1 if successful, 0 if the value is already the target value, -1 if the new value is
* invalid
*/
protected synchronized int setUnsignedSourceValue(int value)
{
//if there is engineering conversion, do it here
if (engParser_ != null && engParser_.isValid())
{
value = perform_eng_conversion(value);
}
//copy the value
int status = setUnsignedValue(sourceValue_.value, value);
if (status > 0)
{
// update the source value timestamp. if necessary we can override it later
sourceValue_.timestamp = Calendar.getInstance().getTimeInMillis();
// synchronize
if (synchronize_value() > 0)
{
//notify dpserver
if (dpServer != null)
{
dpServer.onDataPointChange(this);
}
// re-evaluate all related datapoints
for (int i = 0; i < relatedDataPoints_.size(); i++)
{
relatedDataPoints_.get(i).evaluate();
}
}
// update the dp timestamp
updateTimestamp_ = sourceValue_.timestamp;
// we still need to notify the dpServer
if (dpServer != null)
{
dpServer.onDataPointSourceChange(this);
}
}
return status;
}
/**
* Set the datapoint source value using the native type of the value.
*
* @param value
* - the new double value
* @return +1 if successful, 0 if the value is already the target value, -1 if the new value is
* invalid
*/
protected synchronized int setDoubleSourceValue(double value)
{
//if there is engineering conversion, do it here
if (engParser_ != null && engParser_.isValid())
{
value = perform_eng_conversion(value);
}
//copy the value
int status = setDoubleValue(sourceValue_.value, value);
if (status > 0)
{
// update the source value timestamp. if necessary we can override it later
sourceValue_.timestamp = Calendar.getInstance().getTimeInMillis();
// synchronize
if (synchronize_value() > 0)
{
//notify dpserver
if (dpServer != null)
{
dpServer.onDataPointChange(this);
}
// re-evaluate all related datapoints
for (int i = 0; i < relatedDataPoints_.size(); i++)
{
relatedDataPoints_.get(i).evaluate();
}
}
// update the dp timestamp
updateTimestamp_ = sourceValue_.timestamp;
// we still need to notify the dpServer
if (dpServer != null)
{
dpServer.onDataPointSourceChange(this);
}
}
return status;
}
/**
* Set the datapoint source value using the native type of the value.
*
* @param value
* - the new string value
* @return +1 if successful, 0 if the value is already the target value, -1 if the new value is
* invalid
*/
protected synchronized int setStringSourceValue(String value)
{
//if there is engineering conversion, do it here
if (engParser_ != null && engParser_.isValid())
{
value = perform_eng_conversion(value);
}
//copy the value
int status = setStringValue(sourceValue_.value, value);
if (status > 0)
{
// update the source value timestamp. if necessary we can override it later
sourceValue_.timestamp = Calendar.getInstance().getTimeInMillis();
// synchronize
if (synchronize_value() > 0)
{
//notify dpserver
if (dpServer != null)
{
dpServer.onDataPointChange(this);
}
// re-evaluate all related datapoints
for (int i = 0; i < relatedDataPoints_.size(); i++)
{
relatedDataPoints_.get(i).evaluate();
}
}
// update the dp timestamp
updateTimestamp_ = sourceValue_.timestamp;
// we still need to notify the dpServer
if (dpServer != null)
{
dpServer.onDataPointSourceChange(this);
}
}
return status;
}
/**
* Check inhibit state.
*
* @return the inhibit flag
*/
public boolean isInhibit()
{
return isInhibit_;
}
/**
* Set inhibit flag. The same as setInhibit(true)
*/
public synchronized boolean setInhibit()
{
// pass the inhibit to underlying subsystem if necessary
if (subsystem != null && subsystem.isSourceInhibit()
&& type == EDataPointType.TYPE_REAL)
{
return subsystem.setInhibit(this);
}
else
{
// inhibit is local. store it here
setInhibit(true);
return true;
}
}
/**
* Remove inhibit flag. The same as setInhibit(false)
*/
public synchronized boolean removeInhibit()
{
// pass the inhibit to underlying subsystem if necessary
if (subsystem != null && subsystem.isSourceInhibit()
&& type == EDataPointType.TYPE_REAL)
{
return subsystem.removeInhibit(this);
}
else
{
// inhibit is local. store it here
setInhibit(false);
return true;
}
}
/**
* Set/clear inhibit flag.
*
* @param value
* - the new inhibit flag
*/
private synchronized void setInhibit(boolean value)
{
// ignore if already set
if (isInhibit_ == value)
{
return;
}
// set the inhibit flag including the equipment level flag
isInhibit_ = value;
if (equipment != null)
{
equipment.setInhibit(value);
}
// if we remove the inhibit, synchronize the source value to the output value
if (!value && synchronize_value() > 0)
{
//notify dpserver
if (dpServer != null)
{
dpServer.onDataPointChange(this);
}
// re-evaluate all related datapoints
for (int i = 0; i < relatedDataPoints_.size(); i++)
{
relatedDataPoints_.get(i).evaluate();
}
}
// update dp timestamp
updateTimestamp_ = Calendar.getInstance().getTimeInMillis();
// notify the dpServer
if (dpServer != null)
{
dpServer.onDataPointChange(this);
}
}
// check override state
public boolean isOverride()
{
return isOverride_;
}
// set override flag. the same as setOverride(true)
public synchronized boolean setOverride(CosDpValueUnion value)
{
// pass the override to underlying subsystem if necessary
if (subsystem != null && subsystem.isSourceOverride()
&& type == EDataPointType.TYPE_REAL)
{
return subsystem.setOverride(this, value);
}
else
{
// update the data point value
if (setValue(value) < 0)
{
return false;
}
// set the override flag
setOverride(true);
return true;
}
}
// remove override flag. the same as setOverride(false)
public synchronized boolean removeOverride()
{
// pass the override to underlying subsystem if necessary
if (subsystem != null && subsystem.isSourceOverride()
&& type == EDataPointType.TYPE_REAL)
{
return subsystem.removeOverride(this);
}
else
{
setOverride(false);
return true;
}
}
// set/clear override flag
private synchronized void setOverride(boolean value)
{
// ignore if it is already overriden
if (isOverride_ == value)
{
return;
}
// set the override flag including the equipment level flag
isOverride_ = value;
if (equipment != null)
{
equipment.setOverride(value);
}
// if we remove the overrude, synchronize the value
if (!value && synchronize_value() > 0)
{
//notify dpserver
if (dpServer != null)
{
dpServer.onDataPointChange(this);
}
// re-evaluate all related datapoints
for (int i = 0; i < relatedDataPoints_.size(); i++)
{
relatedDataPoints_.get(i).evaluate();
}
}
// update dp timestamp
updateTimestamp_ = Calendar.getInstance().getTimeInMillis();
// notify the dpServer
if (dpServer != null)
{
dpServer.onDataPointChange(this);
}
}
/**
* Check the alarm state. The alarm state is represented by two flags: not-acknowledged and
* not-normalized. On creation of new alarm, both alarm flags are set.
*
* @return true if the datapoint is in alarm, false otherwise
*/
public boolean isAlarm()
{
return (isAlarmNotAck_ || isAlarmNotNorm_);
}
/**
* Set the alarm state. It is the same as setAlarm(true) <br>
* The alarm state is represented by two flags: not-acknowledged and not-normalized. It is
* essentially the same as create a new alarm.
*/
public synchronized void setAlarm()
{
setAlarm(true);
}
/**
* Set/clear the alarm state. <br>
* The alarm state is represented by two flags: not-acknowledged and not-normalized. If status =
* true, it is essentially the same as create a new alarm.
*
* @param status
* - the new alarm state
*/
public synchronized void setAlarm(boolean status)
{
if (status)
{
// if alarm inhibit is set, ignore the creation
if (isAlarmInhibit_) return;
// set the alarm state
// a new alarm has both not-ack and not-norm flag set
isAlarmNotAck_ = true;
isAlarmNotNorm_ = true;
if (equipment != null)
{
equipment.setAlarmNotAck(true);
equipment.setAlarmNotNorm(true);
}
}
else
{
// clear the alarm state
isAlarmNotAck_ = false;
isAlarmNotNorm_ = false;
if (equipment != null)
{
// remove equipment's alarm not ack and alarm not norm is necessary
equipment.setAlarmNotAck(false);
equipment.setAlarmNotNorm(false);
}
}
// update dp timestamp
updateTimestamp_ = Calendar.getInstance().getTimeInMillis();
// notify the dpServer
if (dpServer != null)
{
dpServer.onDataPointChange(this);
}
}
// check the alarm inhibit flag
public boolean isAlarmInhibit()
{
return isAlarmInhibit_;
}
// Set the alarm inhibit flag. The same as setAlarmInhibit(true)
public synchronized void setAlarmInhibit()
{
setAlarmInhibit(true);
}
// Set/clear the alarm inhibit flag
public synchronized void setAlarmInhibit(boolean value)
{
// ignore if it is already set
if (isAlarmInhibit_ == value)
{
return;
}
// set the alarm inhibit
isAlarmInhibit_ = value;
if (value)
{
// if alarm state is set, clear the alarm state
isAlarmNotAck_ = false;
isAlarmNotNorm_ = false;
// update the equipment
if (equipment != null)
{
equipment.setAlarmInhibit(true);
// remove equipment's alarm not ack and alarm not norm is necessary
equipment.setAlarmNotAck(false);
equipment.setAlarmNotNorm(false);
}
}
else
{
if (equipment != null)
{
// remove equipment's alarm inhibit if necessary
equipment.setAlarmInhibit(false);
}
}
// update dp timestamp
updateTimestamp_ = Calendar.getInstance().getTimeInMillis();
// notify the dpServer
if (dpServer != null)
{
dpServer.onDataPointChange(this);
}
}
// Check the alarm not ack flag
public boolean isAlarmNotAck()
{
return isAlarmNotAck_;
}
// Set the alarm not-acknowledged flag. The same as setAlarmNotAck(true)
public synchronized void setAlarmNotAck()
{
setAlarmNotAck(true);
}
// set/clear alarm not acknowledged flag
public synchronized void setAlarmNotAck(boolean value)
{
// ignore if it is already set
if (isAlarmNotAck_ == value)
{
return;
}
isAlarmNotAck_ = value;
if (equipment != null)
{
equipment.setAlarmNotAck(value);
}
// update dp timestamp
updateTimestamp_ = Calendar.getInstance().getTimeInMillis();
// notify the dpServer
if (dpServer != null)
{
dpServer.onDataPointChange(this);
}
}
// check the alarm not normalized flag
public boolean isAlarmNotNorm()
{
return isAlarmNotNorm_;
}
// Set the alarm not-normalized flag. The same as setAlarmNotNorm(true)
public synchronized void setAlarmNotNorm()
{
setAlarmNotAck(true);
}
// set/clear alarm not normalized flag
public synchronized void setAlarmNotNorm(boolean value)
{
// ignore if it is already set
if (isAlarmNotNorm_ == value)
{
// ignore
return;
}
isAlarmNotNorm_ = value;
if (equipment != null)
{
equipment.setAlarmNotNorm(value);
}
// update dp timestamp
updateTimestamp_ = Calendar.getInstance().getTimeInMillis();
// notify the dpServer
if (dpServer != null)
{
dpServer.onDataPointChange(this);
}
}
// create expression parser and parse the expression.
public synchronized void setExpression(String expression)
{
if (expression == null || expression.length() == 0)
{
return;
}
//copy locally
expression_ = expression;
//get the expression type
//first of all get the return type
Class<?> type = int.class; //default is int
switch(outputValue_.value.discriminator().value())
{
case CosDpValueTypeEnum._TypeBoolean:
type = boolean.class;
break;
case CosDpValueTypeEnum._TypeDouble:
type = double.class;
break;
case CosDpValueTypeEnum._TypeNumber:
type = int.class;
break;
case CosDpValueTypeEnum._TypeString:
type = String.class;
break;
case CosDpValueTypeEnum._TypeUnsigned:
type = int.class;
break;
}
//parse the expression. this must be done after all datapoints have been created
//otherwise, invalid/non-existent datapoints will be just marked as 0.
ArrayList<xxxDataPoint> params = new ArrayList<xxxDataPoint>();
ArrayList<String> varnames = new ArrayList<String>();
ArrayList<Class<?>> vartypes = new ArrayList<Class<?>>();
ArrayList<Object> varvalues = new ArrayList<Object>();
//create the parser
String str = parse_expression(expression_, params, varnames, vartypes, varvalues);
expressionParser_ = new ExpressionParser(str, type);
//create all the variables
for (int i=0; i<varnames.size(); i++)
{
expressionParser_.createVariable(varnames.get(i), vartypes.get(i), varvalues.get(i));
}
//for each datapoint params, add to the related dp
xxxDataPoint dp = null;
for (int i=0; i<params.size(); i++)
{
dp = params.get(i);
if (dp != null)
{
dp.addRelatedPoint(this);
}
}
expressionParams_ = params;
}
public synchronized void setEvaluatedStatus(boolean value)
{
isEvaluated_ = value;
}
public boolean isEvaluated()
{
return isEvaluated_;
}
// if it is a calculated point, calculate the expression
public synchronized int evaluate()
{
// ignore non-calculated datapoints
if (type != EDataPointType.TYPE_CALCULATED || expressionParser_ == null)
return -1;
// check for invalid expression
if (!expressionParser_.isValid())
{
//it has been parsed and found invalid. prevent re-parsing
logger_.error("Invalid expression. Check the first thime this happens for error.");
////set quality to bad. should only need to set it first time after parsing.
//expressionQuality_ = CosDpQualityEnum.QualityBad;
return -1;
}
// prevent infinite recursive calls because we call evaluate() of parameter datapoints.
if (!semaphore_.tryAcquire())
{
return 0;
}
int retval = 0;
//this is not a try-catch block but instead try-finally block.
//the purpose is so that the semaphore is automatically released at the end of the block
try
{
// update the value of expression parameters
xxxDataPoint dp = null;
Object value = null;
for (int i=0; i<expressionParams_.size(); i++)
{
dp = expressionParams_.get(i);
if (dp == null)
{
logger_.trace("DP parameter : UNKNOWN");
//this, in theory, should never happens
expressionQuality_ = CosDpQualityEnum.QualityBad;
value = new Integer(0); //default value
}
else
{
logger_.trace("DP parameter : " + dp.name);
//check for bad quality, in which case the expression quality will also
//be set to bad
switch(dp.type)
{
case TYPE_REAL:
if (dp.getQuality() == CosDpQualityEnum.QualityBad)
{
expressionQuality_ = CosDpQualityEnum.QualityBad;
}
break;
case TYPE_VIRTUAL:
if (dp.getQuality() == CosDpQualityEnum.QualityBad)
{
expressionQuality_ = CosDpQualityEnum.QualityBad;
}
break;
case TYPE_CALCULATED:
//try to re-evaluate the value, just in case
//NOTE: this is the reason we need the semaphore.
// otherwise, we might get into infinite recursive calls
if (dp.evaluate() < 0 || dp.getQuality() == CosDpQualityEnum.QualityBad)
{
expressionQuality_ = CosDpQualityEnum.QualityBad;
}
break;
}
//calculate the parameter value
value = CosDpValueUnion2Object(dp.getValue());
}
//set the parameter
expressionParser_.set(i, value);
//verbose
logger_.trace("DP parameter value : " + value.toString());
}
//evaluate the expression value
Object obj = null;
try
{
obj = expressionParser_.evaluate();
}
catch(Exception ex)
{
logger_.warn("Can not evaluate expression. Exception: " + ex.toString());
}
//convert it to CosDpValueUnion
CosDpValueUnion expValue = Object2CosDpValueUnion(outputValue_.value.discriminator(), obj);
// update the output value with the expression value
int status1 = copy(outputValue_.value, expValue);;
// recalculate the datapoint quality
int status2 = update_quality();
// return value
if (status1 > 0 || status2 > 0)
{
//notify dpserver
if (dpServer != null)
{
dpServer.onDataPointChange(this);
}
// re-evaluate all related datapoints
for (int i = 0; i < relatedDataPoints_.size(); i++)
{
relatedDataPoints_.get(i).evaluate();
}
//update the dp timestamp
updateTimestamp_ = Calendar.getInstance().getTimeInMillis();
retval = +1;
}
else if (status1 == 0 || status2 == 0)
{
retval = 0; //no updates or changes
}
else
{
retval = -1; //failure
}
}
finally
{
semaphore_.release();
}
isEvaluated_ = true;
return retval;
}
// create launching condition parser and parse the expression
public synchronized void setLaunchCondition(String expression) throws Exception
{
if (expression == null || expression.length() == 0)
{
return;
}
//copy locally
lccExpression_ = expression;
//get the expression type
//first of all get the return type
Class<?> type = boolean.class;
//parse the expression. this must be done after all datapoints have been created
//otherwise, invalid/non-existent datapoints will be just marked as 0.
ArrayList<xxxDataPoint> params = new ArrayList<xxxDataPoint>();
ArrayList<String> varnames = new ArrayList<String>();
ArrayList<Class<?>> vartypes = new ArrayList<Class<?>>();
ArrayList<Object> varvalues = new ArrayList<Object>();
//create the parser
String str = parse_expression(lccExpression_, params, varnames, vartypes, varvalues);
lccParser_ = new ExpressionParser(str, type);
//create all the variables
for (int i=0; i<varnames.size(); i++)
{
lccParser_.createVariable(varnames.get(i), vartypes.get(i), varvalues.get(i));
}
lccParams_ = params;
}
/**
* Check the launching condition for this control datapoint.
*
* @return true if the launching condition is OK, false otherwise
*/
public boolean checkLaunchCondition() throws Exception
{
if (lccParser_ == null)
{
return false;
}
// check for invalid expression
if (!lccParser_.isValid())
{
//it has been parsed and found invalid. prevent re-parsing
logger_.error("Invalid launching condition expression. "
+ "Check the first thime this happens for error.");
return false;
}
// update the value of expression parameters
xxxDataPoint dp = null;
Object value = null;
for (int i=0; i<lccParams_.size(); i++)
{
dp = lccParams_.get(i);
if (dp == null)
{
logger_.trace("LCC parameter : UNKNOWN");
value = new Integer(0); //default value
}
else
{
logger_.trace("LCC parameter : " + dp.name);
//calculate the parameter value
value = CosDpValueUnion2Object(dp.getValue());
}
//set the parameter
lccParser_.set(i, value);
//verbose
logger_.trace("LCC parameter value : " + value.toString());
}
//evaluate the expression value
Boolean status = null;
try
{
status = (Boolean)lccParser_.evaluate();
}
catch(Exception ex)
{
logger_.warn("Can not evaluate LCC expression. Exception: " + ex.toString());
}
//return value
if (status == null)
{
return false;
}
else
{
return status.booleanValue();
}
}
// create return condition parser and parse the expression
public synchronized void setReturnCondition(String expression)
{
if (expression == null || expression.length() == 0)
{
return;
}
//copy locally
rccExpression_ = expression;
//first of all get the return type
Class<?> type = boolean.class;
//parse the expression. this must be done after all datapoints have been created
//otherwise, invalid/non-existent datapoints will be just marked as 0.
ArrayList<xxxDataPoint> params = new ArrayList<xxxDataPoint>();
ArrayList<String> varnames = new ArrayList<String>();
ArrayList<Class<?>> vartypes = new ArrayList<Class<?>>();
ArrayList<Object> varvalues = new ArrayList<Object>();
//create the parser
String str = parse_expression(rccExpression_, params, varnames, vartypes, varvalues);
rccParser_ = new ExpressionParser(str, type);
//create all the variables
for (int i=0; i<varnames.size(); i++)
{
rccParser_.createVariable(varnames.get(i), vartypes.get(i), varvalues.get(i));
}
rccParams_ = params;
}
/**
* Check the return condition for this control datapoint.
*
* @return true if the return condition is OK, false otherwise
*/
public boolean checkReturnCondition() throws Exception
{
if (rccParser_ == null)
{
return false;
}
// check for invalid expression
if (!rccParser_.isValid())
{
//it has been parsed and found invalid. prevent re-parsing
logger_.error("Invalid return condition expression. "
+ "Check the first thime this happens for error.");
return false;
}
// update the value of expression parameters
xxxDataPoint dp = null;
Object value = null;
for (int i=0; i<rccParams_.size(); i++)
{
dp = rccParams_.get(i);
if (dp == null)
{
logger_.trace("RCC parameter : UNKNOWN");
value = new Integer(0); //default value
}
else
{
logger_.trace("RCC parameter : " + dp.name);
//calculate the parameter value
value = CosDpValueUnion2Object(dp.getValue());
}
//set the parameter
rccParser_.set(i, value);
//verbose
logger_.trace("RCC parameter value : " + value.toString());
}
//evaluate the expression value
Boolean status = null;
try
{
status = (Boolean)rccParser_.evaluate();
}
catch(Exception ex)
{
logger_.warn("Can not evaluate RCC expression. Exception: " + ex.toString());
}
//return value
if (status == null)
{
return false;
}
else
{
return status.booleanValue();
}
}
// create return condition parser and parse the expression
public synchronized void setEngineeringConversion(String expression)
{
if (expression == null || expression.length() == 0)
{
return;
}
//copy locally
engExpression_ = expression;
//first of all get the return type. this will also be the input value's type
Class<?> type = boolean.class;
Object initInputValue = null;
switch(sourceValue_.value.discriminator().value())
{
case CosDpValueTypeEnum._TypeBoolean:
type = boolean.class;
initInputValue = new Boolean(false);
break;
case CosDpValueTypeEnum._TypeNumber:
type = int.class;
initInputValue = new Integer(0);
break;
case CosDpValueTypeEnum._TypeUnsigned:
type = int.class;
initInputValue = new Integer(0);
break;
case CosDpValueTypeEnum._TypeDouble:
type = double.class;
initInputValue = new Double(0);
break;
case CosDpValueTypeEnum._TypeString:
type = String.class;
initInputValue = new String("");
break;
}
//create the parser
rccParser_ = new ExpressionParser(engExpression_, type);
//create the input variable
rccParser_.createVariable("x", type, initInputValue);
}
public synchronized void addRelatedPoint(xxxDataPoint datapoint)
{
if (datapoint == null)
{
return;
}
//check for circular dependecy!
//NOTE: To do this imposes too much overload compares to the benefits.
// Besides, we can only check a direct circular dependency between 2 datapoints
// which is very very rare, can be easily spotted by manual checking and, if anything,
// amount to configuration error.
// So, just do the checking on runtime.
relatedDataPoints_.add(datapoint);
}
protected boolean isRelatedPoint(xxxDataPoint datapoint)
{
for (int i=0; i<relatedDataPoints_.size(); i++)
{
if (datapoint == relatedDataPoints_.get(i))
{
return true;
}
}
return false;
}
/**
* Set a datapoint value with a value of another datapoint. Do a value translation if necessary.
*
* @param value
* - the data point to set the value
* @param newValue
* - the source data point
* @return +1 if successful, 0 if the value is already the target value, -1 if the source value
* is invalid
*/
protected static int copy(CosDpValueUnion value, final CosDpValueUnion newValue)
{
switch (value.discriminator().value())
{
case CosDpValueTypeEnum._TypeBoolean:
{
boolean boolVal = false;
switch (newValue.discriminator().value())
{
case CosDpValueTypeEnum._TypeBoolean:
boolVal = newValue.boolValue();
break;
case CosDpValueTypeEnum._TypeNumber:
boolVal = (newValue.longValue() != 0);
break;
case CosDpValueTypeEnum._TypeUnsigned:
boolVal = (newValue.unsignedValue() != 0);
break;
default:
return -1;
}
// ONLY copy if the new value is really different with the current value
if (boolVal != value.boolValue())
{
value.boolValue(boolVal);
}
else
{
return 0;
}
}
break;
case CosDpValueTypeEnum._TypeNumber:
{
int longVal = 0;
switch (newValue.discriminator().value())
{
case CosDpValueTypeEnum._TypeNumber:
longVal = newValue.longValue();
break;
case CosDpValueTypeEnum._TypeUnsigned:
longVal = newValue.unsignedValue();
break;
default:
return -1;
}
// ONLY copy if the new value is really different with the current value
if (longVal != value.longValue())
{
value.longValue(longVal);
}
else
{
return 0;
}
}
break;
case CosDpValueTypeEnum._TypeUnsigned:
{
int unsignedVal = 0;
switch (newValue.discriminator().value())
{
case CosDpValueTypeEnum._TypeNumber:
unsignedVal = newValue.longValue();
break;
case CosDpValueTypeEnum._TypeUnsigned:
unsignedVal = newValue.unsignedValue();
break;
default:
return -1;
}
// ONLY copy if the new value is really different with the current value
if (unsignedVal != value.unsignedValue())
{
value.unsignedValue(unsignedVal);
}
else
{
return 0;
}
}
break;
case CosDpValueTypeEnum._TypeDouble:
{
double dblVal = 0.0;
switch (newValue.discriminator().value())
{
case CosDpValueTypeEnum._TypeNumber:
dblVal = (double) newValue.longValue();
break;
case CosDpValueTypeEnum._TypeUnsigned:
dblVal = (double) newValue.unsignedValue();
break;
case CosDpValueTypeEnum._TypeDouble:
dblVal = newValue.dblValue();
break;
default:
return -1;
}
// ONLY copy if the new value is really different with the current value
if (dblVal != value.dblValue())
{
value.dblValue(dblVal);
}
else
{
return 0;
}
}
break;
case CosDpValueTypeEnum._TypeString:
{
String strValue;
switch (newValue.discriminator().value())
{
case CosDpValueTypeEnum._TypeBoolean:
strValue = Boolean.toString(newValue.boolValue());
break;
case CosDpValueTypeEnum._TypeNumber:
strValue = Integer.toString(newValue.longValue());
break;
case CosDpValueTypeEnum._TypeUnsigned:
strValue = Integer.toString(newValue.unsignedValue());
break;
case CosDpValueTypeEnum._TypeDouble:
strValue = Double.toString(newValue.dblValue());
break;
case CosDpValueTypeEnum._TypeString:
strValue = newValue.charValue();
break;
default:
return -1;
} // end of switch (srcDpValue._d())
// ONLY copy if the new value is really different with the current value
// must take care truncation!!
if (strValue.length() != value.charValue().length())
{
// (in which case strncmp will give 0 eventhough the string is not actually
// the same!)
value.charValue(strValue);
}
else if (value.charValue().compareTo(strValue) != 0)
{
value.charValue(strValue);
}
else
{
return 0;
}
}
break;
default:
// unknown datatype
return -1;
} // end of switch (dpValue._d())
return 1;
}
/**
* Compare 2 datapoint value
*
* @param value1
* - the first data point
* @param value2
* - the second data point
* @return +1 if first < second or if different boolean value, 0 if first = second, -1 if first
* > second or different data type
*/
protected static int compare(CosDpValueUnion value1, final CosDpValueUnion value2)
{
if (value1.discriminator() != value2.discriminator())
{
return -1;
}
switch (value1.discriminator().value())
{
case CosDpValueTypeEnum._TypeBoolean:
// return 1 if not the same
if (value1.boolValue() != value2.boolValue())
{
return +1;
}
else
{
return 0;
}
case CosDpValueTypeEnum._TypeNumber:
if (value1.longValue() < value2.longValue())
{
return -1;
}
else if (value1.longValue() > value2.longValue())
{
return +1;
}
else
{
return 0;
}
case CosDpValueTypeEnum._TypeUnsigned:
if (value1.unsignedValue() < value2.unsignedValue())
{
return -1;
}
else if (value1.unsignedValue() > value2.unsignedValue())
{
return +1;
}
else
{
return 0;
}
case CosDpValueTypeEnum._TypeDouble:
if (value1.dblValue() < value2.dblValue())
{
return -1;
}
else if (value1.dblValue() > value2.dblValue())
{
return +1;
}
else
{
return 0;
}
case CosDpValueTypeEnum._TypeString:
if (value1.charValue().length() < value2.charValue().length())
{
return -1;
}
else if (value1.charValue().length() > value2.charValue().length())
{
return +1;
}
else
{
return value1.charValue().compareTo(value2.charValue());
}
} // end of switch (dpValue._d())
// if we get here, it is unknown datatype
return -1;
}
/**
* Set a value using the native type of the value.
*
* @param value
* - the datapoint value
* @param boolValue
* - the new boolean value
* @return +1 if successful, 0 if the value is already the target value, -1 if the new value is
* invalid
*/
protected static int setBooleanValue(CosDpValueUnion value, boolean boolValue)
{
// we can do a value translation here. but I think it is not worth the effort.
// instead, just return with error with the value type is not boolean
if (value.discriminator() != CosDpValueTypeEnum.TypeBoolean)
{
return -1;
}
// set the value if and only if the new value is different
if (value.boolValue() == boolValue)
{
return 0;
}
// set the value
value.boolValue(boolValue);
return +1;
}
/**
* Set a value using the native type of the value.
*
* @param value
* - the datapoint value
* @param lngValue
* - the new corba::long value
* @return +1 if successful, 0 if the value is already the target value, -1 if the new value is
* invalid
*/
protected static int setNumberValue(CosDpValueUnion value, int lngValue)
{
// we can do a value translation here. but I think it is not worth the effort.
// instead, just return with error with the value type is not number
if (value.discriminator() != CosDpValueTypeEnum.TypeNumber)
{
return -1;
}
// set the value if and only if the new value is different
if (value.longValue() == lngValue)
{
return 0;
}
// set the value
value.longValue(lngValue);
return +1;
}
/**
* Set a value using the native type of the value.
*
* @param value
* - the datapoint value
* @param unsignedValue
* - the new corba::unsigned value
* @return +1 if successful, 0 if the value is already the target value, -1 if the new value is
* invalid
*/
protected static int setUnsignedValue(CosDpValueUnion value, int unsignedValue)
{
// we can do a value translation here. but I think it is not worth the effort.
// instead, just return with error with the value type is not unsigned
if (value.discriminator() != CosDpValueTypeEnum.TypeUnsigned)
{
return -1;
}
// set the value if and only if the new value is different
if (value.unsignedValue() == unsignedValue)
{
return 0;
}
// set the value
value.unsignedValue(unsignedValue);
return +1;
}
/**
* Set a value using the native type of the value.
*
* @param value
* - the datapoint value
* @param dblValue
* - the new double value
* @return +1 if successful, 0 if the value is already the target value, -1 if the new value is
* invalid
*/
protected static int setDoubleValue(CosDpValueUnion value, double dblValue)
{
// we can do a value translation here. but I think it is not worth the effort.
// instead, just return with error with the value type is not double
if (value.discriminator() != CosDpValueTypeEnum.TypeDouble)
{
return -1;
}
// set the value if and only if the new value is different
if (value.dblValue() == dblValue)
{
return 0;
}
// set the value
value.dblValue(dblValue);
return +1;
}
/**
* Set a value using the native type of the value.
*
* @param value
* - the datapoint value
* @param strValue
* - the new string value
* @return +1 if successful, 0 if the value is already the target value, -1 if the new value is
* invalid
*/
protected static int setStringValue(CosDpValueUnion value, String strValue)
{
// we can do a value translation here. but I think it is not worth the effort.
// instead, just return with error with the value type is not double
if (value.discriminator() != CosDpValueTypeEnum.TypeString)
{
return -1;
}
// validation
if (strValue == null)
{
return -1;
}
// set the value if and only if the new value is different
if (value.charValue().length() == strValue.length()
&& 0 == value.charValue().compareTo(strValue))
{
return 0;
}
// set the value
value.charValue(strValue);
return +1;
}
protected static String CosDpValueUnion2String(CosDpValueUnion value)
{
// validation
if (value == null)
{
return "";
}
switch (value.discriminator().value())
{
case CosDpValueTypeEnum._TypeBoolean:
if (value.boolValue())
{
return "1";
}
else
{
return "0";
}
case CosDpValueTypeEnum._TypeNumber:
return Integer.toString(value.longValue());
case CosDpValueTypeEnum._TypeUnsigned:
return Integer.toString(value.unsignedValue());
case CosDpValueTypeEnum._TypeDouble:
return Double.toString(value.dblValue());
case CosDpValueTypeEnum._TypeString:
return value.charValue();
default:
return "";
}
}
protected static CosDpValueUnion String2CosDpValueUnion(CosDpValueTypeEnum type, String str)
{
// validation is performed as part of the conversion
CosDpValueUnion value = new CosDpValueUnion();
switch (type.value())
{
case CosDpValueTypeEnum._TypeBoolean:
if (str != null && str.compareToIgnoreCase("1") == 0)
{
value.boolValue(true);
}
else
{
value.boolValue(false);
}
break;
case CosDpValueTypeEnum._TypeNumber:
value.longValue(parse_int(str));
break;
case CosDpValueTypeEnum._TypeUnsigned:
value.unsignedValue(parse_int(str));
break;
case CosDpValueTypeEnum._TypeDouble:
value.dblValue(parse_dbl(str));
break;
case CosDpValueTypeEnum._TypeString:
value.charValue(str);
break;
default:
value.longValue(0);
}
return value;
}
protected static Object CosDpValueUnion2Object(CosDpValueUnion value)
{
// validation
if (value == null)
{
return null;
}
switch (value.discriminator().value())
{
case CosDpValueTypeEnum._TypeBoolean:
return new Boolean(value.boolValue());
case CosDpValueTypeEnum._TypeNumber:
return new Integer(value.longValue());
case CosDpValueTypeEnum._TypeUnsigned:
return new Integer(value.unsignedValue());
case CosDpValueTypeEnum._TypeDouble:
return new Double(value.dblValue());
case CosDpValueTypeEnum._TypeString:
return value.charValue();
}
return null;
}
protected static CosDpValueUnion Object2CosDpValueUnion(CosDpValueTypeEnum type, Object obj)
{
// validation is performed as part of the conversion
CosDpValueUnion value = new CosDpValueUnion();
Boolean boolObj = null;
Integer intObj = null;
Double dblObj = null;
String strObj = null;
switch (type.value())
{
case CosDpValueTypeEnum._TypeBoolean:
//cast it
boolObj = null;
try
{
boolObj = (Boolean) obj;
}
catch (Exception ex)
{
//ignore
}
//set the value
if (obj == null)
{
value.boolValue(false); //default value
}
else
{
value.boolValue(boolObj.booleanValue());
}
break;
case CosDpValueTypeEnum._TypeNumber:
//cast it
intObj = null;
try
{
intObj = (Integer) obj;
}
catch (Exception ex)
{
//ignore
}
//set the value
if (obj == null)
{
value.longValue(0); //default value
}
else
{
value.longValue(intObj.intValue());
}
break;
case CosDpValueTypeEnum._TypeUnsigned:
//cast it
intObj = null;
try
{
intObj = (Integer) obj;
}
catch (Exception ex)
{
//ignore
}
//set the value
if (obj == null)
{
value.unsignedValue(0); //default value
}
else
{
value.unsignedValue(intObj.intValue());
}
break;
case CosDpValueTypeEnum._TypeDouble:
//cast it
dblObj = null;
try
{
dblObj = (Double) obj;
}
catch (Exception ex)
{
//ignore
}
//set the value
if (obj == null)
{
value.dblValue(0); //default value
}
else
{
value.dblValue(dblObj.doubleValue());
}
break;
case CosDpValueTypeEnum._TypeString:
//cast it
strObj = null;
try
{
strObj = (String) obj;
}
catch (Exception ex)
{
//ignore
}
//set the value
if (obj == null)
{
value.charValue(""); //default value
}
else
{
value.charValue(strObj);
}
break;
}
return value;
}
private static int parse_int(String str)
{
int value = 0;
try
{
value = Integer.parseInt(str);
}
catch (NumberFormatException nfe)
{
// ignore
}
return value;
}
private static double parse_dbl(String str)
{
double value = 0;
try
{
value = Double.parseDouble(str);
}
catch (NumberFormatException nfe)
{
// ignore
}
return value;
}
// calculate the datapoint quality
// this is protected because generally the client should never call this function.
// this should be triggered automatically upon changes on related attributes/parameters
private int update_quality()
{
CosDpQualityEnum newQuality = CosDpQualityEnum.QualityBad;
// determine the datapoint quality. store the new quality in newQuality
if (sourceValue_.quality == CosDpQualityEnum.QualityBad)
{
// if source quality is bad, always set to bad.
newQuality = CosDpQualityEnum.QualityBad;
}
else
{
// get the subsystem quality, if any
newQuality = CosDpQualityEnum.QualityGood;
if (subsystem != null)
{
newQuality = subsystem.getQuality();
}
else if (type == EDataPointType.TYPE_REAL)
{
// modbus datapoint must have subsystem associated to it. otherwise set to bad
newQuality = CosDpQualityEnum.QualityBad;
}
// if any of the subsystem quality and expression quality is bad, set quality to bad
if (newQuality == CosDpQualityEnum.QualityBad
|| expressionQuality_ == CosDpQualityEnum.QualityBad)
{
newQuality = CosDpQualityEnum.QualityBad;
}
else
{
newQuality = CosDpQualityEnum.QualityGood;
}
}
// set the new quality, only if it is different
if (outputValue_.quality == newQuality)
{
return 0;
}
outputValue_.quality = newQuality;
return 1;
}
// synchronize source value and output value
private int synchronize_value()
{
if (isInhibit() || isOverride() || type != EDataPointType.TYPE_REAL)
{
return -1;
}
// try to copy over the value
int status = copy(outputValue_.value, sourceValue_.value);
// if the value is the same but the timestamp is different
// reset the status. consider it as a successful sync
if (outputValue_.timestamp != sourceValue_.timestamp)
{
status = +1;
// synchronize the timestamp
outputValue_.timestamp = sourceValue_.timestamp;
}
return status;
}
// /**
// * Prepare the expression.
// * <br>
// * This includes parsing the expression and compiling the parser. If the expression contains
// * datapoint name, it is converted into a variable and the datapoint reference inserted into
// * the list of parameters
// * <br>
// * Only when the parsing/compiling is successful, the isValid flag is set to true.
// * (Default value is false.)
// *
// * @param datapoint - reference to parent datapoint
// * @param dpServer - datapoint server where the datapoint is hosted.
// * This will be used to retrieve reference to datapoint in the expression.
// * @throws Exception - when the parsing/compiling fails, exception is thrown. otherwise,
// * the function returns normally.
// */
// public void prepare(DataPoint datapoint, DataPointServer dpServer) throws Exception
// {
// final String KEYWORD_START = "{";
// final String KEYWORD_END = "}";
//
// //first of all get the return type
// Class<?> rettype = int.class; //default is int
// if (datapoint != null)
// {
// switch(datapoint.getValue().discriminator().value())
// {
// case CosDpValueTypeEnum._TypeBoolean:
// rettype = boolean.class;
// break;
// case CosDpValueTypeEnum._TypeDouble:
// rettype = double.class;
// break;
// case CosDpValueTypeEnum._TypeNumber:
// rettype = int.class;
// break;
// case CosDpValueTypeEnum._TypeString:
// rettype = String.class;
// break;
// case CosDpValueTypeEnum._TypeUnsigned:
// rettype = int.class;
// break;
// }
// }
//
// //parse the expression. replace datapoint keyword with simplified name
// if (!parseExpression(dpServer))
// {
// //set the invalid flag to prevent re-parsing next time
// errorString_ = "prepare(): can not parse the expression!";
// isValidExpression_ = false;
// throw new Exception(errorString_);
// }
//
// //create the parser
// try
// {
// parser_ = new ExpressionEvaluator(
// shortExpression_, // expression
// rettype, // expressionType
// varnames_.toArray(new String[0]), // parameterNames
// vartypes_.toArray(new Class<?>[0]) // parameterTypes
// );
// }
// catch(Exception ex)
// {
// //set the invalid flag to prevent re-parsing next time
// errorString_ = "prepare(): " + ex.toString();
// isValidExpression_ = false;
// throw new Exception(errorString_);
// }
//
// isValidExpression_ = true;
// return;
// }
//
private String parse_expression(String expression,
ArrayList<xxxDataPoint> params, ArrayList<String> varnames,
ArrayList<Class<?>> vartypes, ArrayList<Object> varvalues)
{
StringBuffer strExpr = new StringBuffer(expression);
String dpname = "";
String varname = "";
Class<?> vartype = int.class;
Object varvalue = null;
xxxDataPoint datapoint = null;
//prepare list of variables
params.clear();
varnames.clear();
vartypes.clear();
varvalues.clear();
//replace all datapoint name with equivalent variable name
//datapoint name is in the format '{data_point_name}'
int loc1 = strExpr.indexOf(KEYWORD_START, 0);
int loc2 = 0;
while (loc1 >= 0)
{
//reset just in case
varname = "";
//get the end of the datapoint name
loc2 = strExpr.indexOf(KEYWORD_END, loc1);
//if it is unterminated name
if (loc2 < 1)
{
//delete the unterminated name
strExpr.replace(loc1, strExpr.length(), "0");
}
else
{
//find a data point name
dpname = strExpr.substring(loc1+1, loc2).trim();
//parse the datapoint keyword
if (dpServer == null)
{
//delete the datapoint keyword.
strExpr.replace(loc1, loc2+KEYWORD_END.length(), "0");
}
else
{
//get the datapoint reference from dpserver
datapoint = dpServer.getDataPoint(dpname);
if ( datapoint == null )
{
//can not find the handle for the name -> invalid datapoint
//verbose
//__LOG_INFO(logger, "can not get handle!");
//delete the invalid datapoint
strExpr.replace(loc1, loc2+KEYWORD_END.length(), "0");
}
else
{
//can find the handle. replace the keyword with a var name
varname = "var" + params.size();
strExpr.replace(loc1, loc2+KEYWORD_END.length(), varname);
//add the handle into the list of handle for this data point
params.add(datapoint);
//store the var name
varnames.add(varname);
//determine the var type and initial value
vartype = int.class;
varvalue = null;
switch(datapoint.getValue().discriminator().value())
{
case CosDpValueTypeEnum._TypeBoolean:
vartype = boolean.class;
varvalue = new Boolean(datapoint.getValue().boolValue());
break;
case CosDpValueTypeEnum._TypeDouble:
vartype = double.class;
varvalue = new Double(datapoint.getValue().dblValue());
break;
case CosDpValueTypeEnum._TypeNumber:
vartype = int.class;
varvalue = new Integer(datapoint.getValue().longValue());
break;
case CosDpValueTypeEnum._TypeString:
vartype = String.class;
varvalue = new String(datapoint.getValue().charValue());
break;
case CosDpValueTypeEnum._TypeUnsigned:
vartype = int.class;
varvalue = new Integer(datapoint.getValue().unsignedValue());
break;
}
//store the var type and initial value
vartypes.add(vartype);
varvalues.add(varvalue);
}
} //if (dpServer != null)
} //if find KEYWORD_START
//try to find next datapoint name
loc1 = strExpr.indexOf(KEYWORD_END, loc1+varname.length());
} //while (loc1 >= 0)
//store the expression
return strExpr.toString();
}
private CosDpValueUnion perform_eng_conversion(CosDpValueUnion value)
{
switch(value.discriminator().value())
{
case CosDpValueTypeEnum._TypeBoolean:
value.boolValue( perform_eng_conversion(value.boolValue()) );
break;
case CosDpValueTypeEnum._TypeDouble:
value.dblValue( perform_eng_conversion(value.dblValue()) );
break;
case CosDpValueTypeEnum._TypeNumber:
value.longValue( perform_eng_conversion(value.longValue()) );
break;
case CosDpValueTypeEnum._TypeUnsigned:
value.unsignedValue( perform_eng_conversion(value.unsignedValue()) );
break;
case CosDpValueTypeEnum._TypeString:
value.charValue( perform_eng_conversion(value.charValue()) );
break;
}
return value;
}
private boolean perform_eng_conversion(boolean value)
{
// update the input value parameter
Boolean objVal = new Boolean(value);
engParser_.set(0, objVal);
//perform the conversion
try
{
objVal = (Boolean)engParser_.evaluate();
return objVal.booleanValue();
}
catch(Exception ex)
{
logger_.warn("Can not evaluate engineering conversion. Exception: " + ex.toString());
}
//return the original value
return value;
}
private int perform_eng_conversion(int value)
{
// update the input value parameter
Integer objVal = new Integer(value);
engParser_.set(0, objVal);
//perform the conversion
try
{
objVal = (Integer)engParser_.evaluate();
return objVal.intValue();
}
catch(Exception ex)
{
logger_.warn("Can not evaluate engineering conversion. Exception: " + ex.toString());
}
//return the original value
return value;
}
private double perform_eng_conversion(double value)
{
// update the input value parameter
Double objVal = new Double(value);
engParser_.set(0, objVal);
//perform the conversion
try
{
objVal = (Double)engParser_.evaluate();
return objVal.doubleValue();
}
catch(Exception ex)
{
logger_.warn("Can not evaluate engineering conversion. Exception: " + ex.toString());
}
//return the original value
return value;
}
private String perform_eng_conversion(String value)
{
// update the input value parameter
engParser_.set(0, value);
//perform the conversion
try
{
value = (String)engParser_.evaluate();
}
catch(Exception ex)
{
logger_.warn("Can not evaluate engineering conversion. Exception: " + ex.toString());
}
//return the original value
return value;
}
}