package tcg.scada.da;
import java.util.ArrayList;
import java.util.Calendar;
import org.apache.log4j.Logger;
import tcg.common.LoggerManager;
import tcg.scada.cos.CosDpQualityEnum;
import tcg.scada.cos.CosDpValueStruct;
import tcg.scada.cos.CosDpValueTypeEnum;
import tcg.scada.cos.CosDpValueUnion;
/**
* Base class for datapoint implementation.
*
* @author Yoga
*
*/
public abstract class DataPoint implements IDataPoint
{
/**
* Datapoint key id. Predefined based on configuration. Can be sequential
* number.
*/
public int keyId = 0;
/**
* Datapoint name.
*/
public String name = "";
/**
* Source address. Only applicable for REAL datapoint with raw protocol (eq.
* modbus). Normally in words.
*/
protected int address = -1;
/**
* Source bit address. Only applicable for REAL datapoint with raw protocol
* (eq. modbus). Used to get binary value embedded in byte/word data.
*/
protected short bitAddress = -1;
/**
* Source length. Only applicable for REAL datapoint with raw protocol (eq.
* modbus). Use to parse string value. Normally in bytes.
*/
protected short length = 0;
/**
* Datapoint source data type (used for REAL datapoint)
*
* Note: This is the responsibility of the poller to translate from source
* types to supported internal types
*/
protected ESourceDataType sourceType = ESourceDataType.TYPE_INT32;
// source value
protected CosDpValueUnion sourceValue = new CosDpValueUnion();
protected CosDpQualityEnum sourceQuality = CosDpQualityEnum.QualityGood;
protected long sourceTimestamp = 0;
// output value
protected CosDpValueUnion outputValue = new CosDpValueUnion();
protected CosDpQualityEnum outputQuality = CosDpQualityEnum.QualityGood;
protected long outputTimestamp = 0;
// various datapoint flag
protected boolean isOverride = false;
protected boolean isInhibit = false;
protected boolean isAlarmInhibit = false;
protected boolean isAlarmNotAck = false;
protected boolean isAlarmNotNorm = false;
// subsystem
protected ISubsystem subsystem = null;
// equipment
protected IEquipment equipment = null;
// quality datapoint
protected IDataPoint qualityPoint = null;
// datastore. for subscription notification and database persistence
protected IDataStore datastore = null;
/**
* List of calculated points which use this datapoint as parameter
*/
protected DataPointList calculatedPoints = new DataPointList();
/**
* List of meter points which use this datapoint as source
*/
protected DataPointList meterPoints = new DataPointList();
/**
* List of datapoints which use this datapoint as quality point
*/
protected DataPointList childPoints = new DataPointList();
/**
* Internal timestamp. This is distinct from value timestamp because it will
* always be updated with the local current time after each operation
* (whereas value timestamp might use the source timestamp)
*/
protected long updateTimestamp_ = 0;
/**
* Launching condition for control point (temporary storage)
*/
protected String lccExpression = "";
/**
* Return condition for control point (temporary storage)
*/
protected String rccExpression = "";
/**
* Deadband value (+/- from last value).
*/
protected double deadband = 0.0;
/**
* Expression for engineering conversion (temporary storage)
*/
protected String ecExpression = "";
/**
* Expression string for calculated point (temporary storage)
*/
protected String expression = "";
/**
* Evaluated flag for calculated point (temporary flag)
*/
protected boolean hasBeenEvaluated = false;
// logger
protected Logger logger = LoggerManager
.getLogger(this.getClass().getName());
protected boolean isBatchOperation = false;
/**
* Default ctor
*/
public DataPoint()
{
// nothing
}
/**
* Preferred ctor.
*
* @param inDataType
* - the internal data type
*/
public DataPoint(CosDpValueTypeEnum inDataType)
{
// initialize internal value
_setDataType(inDataType);
}
/**
* Set internal data type.
*
* @param inDataType
* - the internal data type
*/
protected void _setDataType(CosDpValueTypeEnum inDataType)
{
// initialize internal value
switch (inDataType.value())
{
case CosDpValueTypeEnum._TypeBoolean:
sourceValue.boolValue(false);
outputValue.boolValue(false);
break;
case CosDpValueTypeEnum._TypeNumber:
sourceValue.longValue(0);
outputValue.longValue(0);
break;
case CosDpValueTypeEnum._TypeUnsigned:
sourceValue.unsignedValue(0);
outputValue.unsignedValue(0);
break;
case CosDpValueTypeEnum._TypeDouble:
sourceValue.dblValue(0.0);
outputValue.dblValue(0.0);
break;
case CosDpValueTypeEnum._TypeString:
sourceValue.charValue("");
outputValue.charValue("");
break;
}
}
/**
* Helper function to initialize the datapoint state. This will set the
* internal states without triggering notification to datastore.
*
* @param inIsInhibit
* - last known inhibit flag
* @param inIsOverride
* - last known override flag
* @param inIsAlarmInhibit
* - last known alarm inhibit flag
* @param inIsAlarmNotAck
* - last known alarm not-ack flag
* @param inIsAlarmNotNorm
* - last known alarm not-norm flag
*/
protected synchronized void _setInitialState(boolean inIsInhibit,
boolean inIsOverride, boolean inIsAlarmInhibit,
boolean inIsAlarmNotAck, boolean inIsAlarmNotNorm)
{
isInhibit = inIsInhibit;
isOverride = inIsOverride;
isAlarmInhibit = inIsAlarmInhibit;
isAlarmNotAck = inIsAlarmNotAck;
isAlarmNotNorm = inIsAlarmNotNorm;
}
/**
* Set the initial value of this datapoint. This will set the source and
* output value without triggering notification to datastore.
*
* <p>
* No deadband check nor engineering conversion is performed. It is just a
* simple set value.
* </p>
*
* <p>
* <b>No value translation is performed.</b> If the caller has no idea of
* the internal data type, then it should probably not use this function at
* all!
* </p>
*
* @param inSourceValue
* - initial source value
* @param inOutputValue
* - initial output value
*/
protected synchronized void _setInitialValue(CosDpValueUnion inSourceValue,
CosDpValueUnion inOutputValue)
{
// validation: InSourceValue
// none. if it is null, just do not set it
// validation: inOuputValue
// none. if it is null, just do not set it
// copy source value
if (inSourceValue != null
&& inSourceValue.discriminator() == sourceValue.discriminator())
{
copy(sourceValue, inSourceValue);
}
// copy output value
if (inOutputValue != null
&& inOutputValue.discriminator() == outputValue.discriminator())
{
copy(outputValue, inOutputValue);
}
}
/**
* Get the current source value in string format. Can be used for database
* persistence.
*
* @return current source value in string.
*/
protected String _getStringifiedSourceValue()
{
return convCosDpValueUnion2String(sourceValue);
}
/**
* Get the current output value in string format. Can be used for database
* persistence.
*
* @return current output value in string.
*/
protected String _getStringifiedValue()
{
return convCosDpValueUnion2String(outputValue);
}
/**
* <p>
* Synchronize between the source value/source timestamp and output
* value/output timestamp. The output quality is also recalculated.
* </p>
*
* @return +1 if output value/output timestamp/output quality is updated, 0
* if none is updated because it is already the expected value, -1
* if the operation fails.
*/
protected synchronized int _synchronize()
{
if (isInhibit || isOverride)
{
return -1;
}
int status1 = 0, status2 = 0, status3 = 0;
// try to copy over the value
status1 = copy(outputValue, sourceValue);
// try to copy over the quality
CosDpQualityEnum newQuality = calculate_quality();
if (outputQuality != newQuality)
{
outputQuality = newQuality;
status2 = +1;
}
// ALTERNATIVE IMPLEMENTATION. DO NOT DELETE!!!
// // only update timestamp and quality if the value/quality has
// changed.
// // NOTE: This is a design decision to protect against cases where
// // timestamp always change even though the value never changed.
// (Ideally,
// // timestamp is only updated when the value has changed)
// if (status1 > 0 || status2 > 0)
// {
// outputTimestamp = sourceTimestamp;
// // update the timestamp
// updateTimestamp_ = Calendar.getInstance().getTimeInMillis();
// return +1;
// }
// update output value timestamp
if (outputTimestamp != sourceTimestamp)
{
outputTimestamp = sourceTimestamp;
status3 = +1;
}
// update the timestamp
int retval = 0;
if (status1 > 0 || status2 > 0 || status3 > 0)
{
retval = +1;
// update the timestamp
updateTimestamp_ = Calendar.getInstance().getTimeInMillis();
// TODO: notify datastore
}
return retval;
}
@Override
public CosDpValueTypeEnum getInternalDataType()
{
return outputValue.discriminator();
}
@Override
public int getKeyId()
{
return keyId;
}
@Override
public String getName()
{
return name;
}
@Override
public int getAddress()
{
return address;
}
@Override
public short getBitAddress()
{
return bitAddress;
}
@Override
public short getLength()
{
return length;
}
@Override
public ESourceDataType getSourceType()
{
return sourceType;
}
@Override
public ISubsystem getSubsystem()
{
return subsystem;
}
@Override
public CosDpQualityEnum getQuality()
{
return outputQuality;
}
@Override
public CosDpQualityEnum getSourceQuality()
{
return sourceQuality;
}
@Override
public long getSourceTimestamp()
{
return sourceTimestamp;
}
@Override
public CosDpValueUnion getSourceValue()
{
return sourceValue;
}
@Override
public long getTimestamp()
{
return outputTimestamp;
}
@Override
public CosDpValueUnion getValue()
{
return outputValue;
}
@Override
public long getUpdateTimestamp()
{
return updateTimestamp_;
}
@Override
public synchronized void setDataStore(IDataStore inDataStore)
{
// validation: inDataStore
if (inDataStore == null)
{
return;
}
datastore = inDataStore;
}
@Override
public synchronized void setEquipment(IEquipment inEquipment)
{
// validation: inEquipment
if (inEquipment == null)
{
return;
}
// copy locally and add to the equipment's list of children
equipment = inEquipment;
equipment.addDataPoint(this);
}
@Override
public synchronized boolean setQualityPoint(IDataPoint inQualityPoint)
{
// validation
if (inQualityPoint == null)
{
return false;
}
// make sure the datapoint is boolean type
if (inQualityPoint.getInternalDataType() != CosDpValueTypeEnum.TypeBoolean)
{
return false;
}
// copy locally and to the point's child
qualityPoint = inQualityPoint;
qualityPoint.addChildPoint(this);
return true;
}
@Override
public synchronized void setSubsystem(ISubsystem inSubsystem)
{
// validation: inSubsystem
if (inSubsystem == null)
{
return;
}
// copy locally and add to subsystem's list of managed datapoints
subsystem = inSubsystem;
subsystem.addDataPoint(this);
}
/**
* <p>
* Set a value with another. Do a value translation if necessary.
* </p>
*
* @param ioValue
* - the data point to set the value
* @param inNewValue
* - 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 ioValue,
final CosDpValueUnion inNewValue)
{
// validation: ioValue
if (ioValue == null)
{
return -1;
}
// validation: inNewValue
if (inNewValue == null)
{
return -1;
}
// copy the value
switch (ioValue.discriminator().value())
{
case CosDpValueTypeEnum._TypeBoolean:
{
boolean boolVal = false;
switch (inNewValue.discriminator().value())
{
case CosDpValueTypeEnum._TypeBoolean:
boolVal = inNewValue.boolValue();
break;
case CosDpValueTypeEnum._TypeNumber:
boolVal = (inNewValue.longValue() != 0);
break;
case CosDpValueTypeEnum._TypeUnsigned:
boolVal = (inNewValue.unsignedValue() != 0);
break;
case CosDpValueTypeEnum._TypeDouble:
boolVal = (inNewValue.dblValue() != 0);
break;
case CosDpValueTypeEnum._TypeString:
boolVal = (inNewValue.charValue() != null && inNewValue
.charValue().length() > 0);
break;
default:
return -1;
}
// ONLY copy if the new value is really different with the
// current
// value
if (boolVal != ioValue.boolValue())
{
ioValue.boolValue(boolVal);
}
else
{
return 0;
}
}
break;
case CosDpValueTypeEnum._TypeNumber:
{
int longVal = 0;
switch (inNewValue.discriminator().value())
{
case CosDpValueTypeEnum._TypeBoolean:
longVal = (inNewValue.boolValue() ? 1 : 0);
break;
case CosDpValueTypeEnum._TypeNumber:
longVal = inNewValue.longValue();
break;
case CosDpValueTypeEnum._TypeUnsigned:
longVal = inNewValue.unsignedValue();
break;
case CosDpValueTypeEnum._TypeDouble:
longVal = (int) inNewValue.dblValue();
break;
case CosDpValueTypeEnum._TypeString:
longVal = parseInt(inNewValue.charValue());
break;
default:
return -1;
}
// ONLY copy if the new value is really different with the
// current
// value
if (longVal != ioValue.longValue())
{
ioValue.longValue(longVal);
}
else
{
return 0;
}
}
break;
case CosDpValueTypeEnum._TypeUnsigned:
{
int unsignedVal = 0;
switch (inNewValue.discriminator().value())
{
case CosDpValueTypeEnum._TypeBoolean:
unsignedVal = (inNewValue.boolValue() ? 1 : 0);
break;
case CosDpValueTypeEnum._TypeNumber:
unsignedVal = inNewValue.longValue();
if (unsignedVal < 0)
{
unsignedVal *= -1;
}
break;
case CosDpValueTypeEnum._TypeUnsigned:
unsignedVal = inNewValue.unsignedValue();
break;
case CosDpValueTypeEnum._TypeDouble:
unsignedVal = (int) inNewValue.dblValue();
if (unsignedVal < 0)
{
unsignedVal *= -1;
}
break;
case CosDpValueTypeEnum._TypeString:
unsignedVal = parseInt(inNewValue.charValue());
if (unsignedVal < 0)
{
unsignedVal *= -1;
}
break;
default:
return -1;
}
// ONLY copy if the new value is really different with the
// current
// value
if (unsignedVal != ioValue.unsignedValue())
{
ioValue.unsignedValue(unsignedVal);
}
else
{
return 0;
}
}
break;
case CosDpValueTypeEnum._TypeDouble:
{
double dblVal = 0.0;
switch (inNewValue.discriminator().value())
{
case CosDpValueTypeEnum._TypeBoolean:
dblVal = (inNewValue.boolValue() ? 1.0 : 0.0);
break;
case CosDpValueTypeEnum._TypeNumber:
dblVal = (double) inNewValue.longValue();
break;
case CosDpValueTypeEnum._TypeUnsigned:
dblVal = (double) inNewValue.unsignedValue();
break;
case CosDpValueTypeEnum._TypeDouble:
dblVal = inNewValue.dblValue();
break;
case CosDpValueTypeEnum._TypeString:
dblVal = parseDbl(inNewValue.charValue());
break;
default:
return -1;
}
// ONLY copy if the new value is really different with the
// current
// value
if (dblVal != ioValue.dblValue())
{
ioValue.dblValue(dblVal);
}
else
{
return 0;
}
}
break;
case CosDpValueTypeEnum._TypeString:
{
String strValue;
switch (inNewValue.discriminator().value())
{
case CosDpValueTypeEnum._TypeBoolean:
strValue = Boolean.toString(inNewValue.boolValue());
break;
case CosDpValueTypeEnum._TypeNumber:
strValue = Integer.toString(inNewValue.longValue());
break;
case CosDpValueTypeEnum._TypeUnsigned:
strValue = Integer.toString(inNewValue.unsignedValue());
break;
case CosDpValueTypeEnum._TypeDouble:
strValue = Double.toString(inNewValue.dblValue());
break;
case CosDpValueTypeEnum._TypeString:
strValue = inNewValue.charValue();
if (strValue == null)
{
strValue = "";
}
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() != ioValue.charValue().length())
{
// (in which case strncmp will give 0 eventhough the string
// is
// not actually
// the same!)
ioValue.charValue(strValue);
}
else if (ioValue.charValue().compareTo(strValue) != 0)
{
ioValue.charValue(strValue);
}
else
{
return 0;
}
}
break;
default:
// unknown datatype
return -1;
} // end of switch (dpValue._d())
return 1;
}
/**
* <p>
* Compare two values
* </p>
*
* @param inValue1
* - the first data point
* @param inValue2
* - 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(final CosDpValueUnion inValue1,
final CosDpValueUnion inValue2)
{
// validation: inValue1
if (inValue1 == null)
{
return -1;
}
// validation: inValue2
if (inValue2 == null)
{
return -1;
}
// don't bother to compare 2 different type
if (inValue1.discriminator() != inValue2.discriminator())
{
return -1;
}
// compare
switch (inValue1.discriminator().value())
{
case CosDpValueTypeEnum._TypeBoolean:
// return 1 if not the same
if (inValue1.boolValue() != inValue2.boolValue())
{
return +1;
}
else
{
return 0;
}
case CosDpValueTypeEnum._TypeNumber:
if (inValue1.longValue() < inValue2.longValue())
{
return -1;
}
else if (inValue1.longValue() > inValue2.longValue())
{
return +1;
}
else
{
return 0;
}
case CosDpValueTypeEnum._TypeUnsigned:
if (inValue1.unsignedValue() < inValue2.unsignedValue())
{
return -1;
}
else if (inValue1.unsignedValue() > inValue2.unsignedValue())
{
return +1;
}
else
{
return 0;
}
case CosDpValueTypeEnum._TypeDouble:
if (inValue1.dblValue() < inValue2.dblValue())
{
return -1;
}
else if (inValue1.dblValue() > inValue2.dblValue())
{
return +1;
}
else
{
return 0;
}
case CosDpValueTypeEnum._TypeString:
{
int status = inValue1.charValue().compareTo(
inValue2.charValue());
if (status < 0)
{
return -1;
}
else if (status > 0)
{
return +1;
}
else
{
return 0;
}
}
} // 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 outValue
* - the datapoint value
* @param inBoolValue
* - 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 outValue,
boolean inBoolValue)
{
// validation: outValue
if (outValue == null)
{
return -1;
}
// validation: inBoolValue
// none
// 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 (outValue.discriminator() != CosDpValueTypeEnum.TypeBoolean)
{
return -1;
}
// set the value if and only if the new value is different
if (outValue.boolValue() == inBoolValue)
{
return 0;
}
// set the value
outValue.boolValue(inBoolValue);
return +1;
}
/**
* Set a value using the native type of the value.
*
* @param outValue
* - the datapoint value
* @param inLngValue
* - 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 outValue, int inLngValue)
{
// validation: outValue
if (outValue == null)
{
return -1;
}
// validation: inNumberValue
// none
// 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 (outValue.discriminator() != CosDpValueTypeEnum.TypeNumber)
{
return -1;
}
// set the value if and only if the new value is different
if (outValue.longValue() == inLngValue)
{
return 0;
}
// set the value
outValue.longValue(inLngValue);
return +1;
}
/**
* Set a value using the native type of the value.
*
* @param outValue
* - the datapoint value
* @param inUnsignedValue
* - 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 outValue,
int inUnsignedValue)
{
// validation: outValue
if (outValue == null)
{
return -1;
}
// validation: inUnsignedValue
// none
// 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 (outValue.discriminator() != CosDpValueTypeEnum.TypeUnsigned)
{
return -1;
}
// set the value if and only if the new value is different
if (outValue.unsignedValue() == inUnsignedValue)
{
return 0;
}
// set the value
outValue.unsignedValue(inUnsignedValue);
return +1;
}
/**
* Set a value using the native type of the value.
*
* @param outValue
* - the datapoint value
* @param inDblValue
* - 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 outValue,
double inDblValue)
{
// validation: outValue
if (outValue == null)
{
return -1;
}
// validation: inDblValue
// none
// 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 (outValue.discriminator() != CosDpValueTypeEnum.TypeDouble)
{
return -1;
}
// set the value if and only if the new value is different
if (outValue.dblValue() == inDblValue)
{
return 0;
}
// set the value
outValue.dblValue(inDblValue);
return +1;
}
/**
* Set a value using the native type of the value.
*
* @param outValue
* - the datapoint value
* @param inStrValue
* - 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 outValue,
String inStrValue)
{
// validation: outValue
if (outValue == null)
{
return -1;
}
// validation: inStrValue
if (inStrValue == null)
{
return -1;
}
// 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 (outValue.discriminator() != CosDpValueTypeEnum.TypeString)
{
return -1;
}
// set the value if and only if the new value is different
if (outValue.charValue().length() == inStrValue.length()
&& 0 == outValue.charValue().compareTo(inStrValue))
{
return 0;
}
// set the value
outValue.charValue(inStrValue);
return +1;
}
/**
* <p>
* Recalculate the output quality.
* </p>
*
* <p>
* Note: The output quality is determined by the following components:
* <ul>
* <li>source quality</li>
* <li>subsystem quality</li>
* <li>quality datapoint's value/quality</li>
* </ul>
* </p>
*
* <p>
* Note: The following rule applies:
* <ul>
* <li>If source quality is <i>BadQuality</i>, set output quality to
* <i>BadQuality</i>.</li>
* <li>If subsystem quality is <i>BadQuality</i>, set output quality to
* <i>BadQuality</i>.</li>
* <li>If quality datapoint's quality is <i>BadQuality</i>, set output
* quality to <i>BadQuality</i>.</li>
* <li>If quality datapoint's value is false (or equivalent), set output
* quality to <i>BadQuality</i>.</li>
* <li>If, and only if, all quality components are <i>GoodQuality</i> and
* quality datapoint's value is true (or equivalent), set output quality to
* <i>GoodQuality</i>.</li>
* </ul>
* </p>
*
* <p>
* Note: Expression quality for calculated point is represented as source
* quality. (The source value is the expression result)
* </p>
*/
private CosDpQualityEnum calculate_quality()
{
// source quality (used as expression quality in calculated point)
if (sourceQuality == CosDpQualityEnum.QualityBad)
{
return CosDpQualityEnum.QualityBad;
}
// subsystem quality
if (subsystem != null
&& subsystem.getQuality() == CosDpQualityEnum.QualityBad)
{
return CosDpQualityEnum.QualityBad;
}
// quality datapoint
if (qualityPoint != null)
{
// quality point's quality
if (qualityPoint.getQuality() == CosDpQualityEnum.QualityBad)
{
return CosDpQualityEnum.QualityBad;
}
// quality point's value
switch (qualityPoint.getInternalDataType().value())
{
case CosDpValueTypeEnum._TypeBoolean:
if (qualityPoint.getValue().boolValue() == false)
{
return CosDpQualityEnum.QualityBad;
}
break;
case CosDpValueTypeEnum._TypeNumber:
if (qualityPoint.getValue().longValue() == 0)
{
return CosDpQualityEnum.QualityBad;
}
break;
case CosDpValueTypeEnum._TypeUnsigned:
if (qualityPoint.getValue().unsignedValue() == 0)
{
return CosDpQualityEnum.QualityBad;
}
break;
case CosDpValueTypeEnum._TypeDouble:
if (qualityPoint.getValue().dblValue() == 0)
{
return CosDpQualityEnum.QualityBad;
}
break;
case CosDpValueTypeEnum._TypeString:
String tmpString = qualityPoint.getValue().charValue();
if (tmpString == null || tmpString.isEmpty())
{
return CosDpQualityEnum.QualityBad;
}
break;
}
}
// everything is good
return CosDpQualityEnum.QualityGood;
}
protected static String convCosDpValueUnion2String(CosDpValueUnion inValue)
{
// validation: value
if (inValue == null)
{
return "";
}
switch (inValue.discriminator().value())
{
case CosDpValueTypeEnum._TypeBoolean:
if (inValue.boolValue())
{
return "1";
}
else
{
return "0";
}
case CosDpValueTypeEnum._TypeNumber:
return Integer.toString(inValue.longValue());
case CosDpValueTypeEnum._TypeUnsigned:
return Integer.toString(inValue.unsignedValue());
case CosDpValueTypeEnum._TypeDouble:
return Double.toString(inValue.dblValue());
case CosDpValueTypeEnum._TypeString:
return inValue.charValue();
default:
return "";
}
}
protected static CosDpValueUnion convString2CosDpValueUnion(
CosDpValueTypeEnum inType, String inStr)
{
// validation: inType
if (inType == null)
{
return null;
}
// validation: inStr
// none. validation is performed as part of the conversion
CosDpValueUnion value = new CosDpValueUnion();
switch (inType.value())
{
case CosDpValueTypeEnum._TypeBoolean:
if (inStr != null && inStr.compareToIgnoreCase("1") == 0)
{
value.boolValue(true);
}
else
{
value.boolValue(false);
}
break;
case CosDpValueTypeEnum._TypeNumber:
value.longValue(parseInt(inStr));
break;
case CosDpValueTypeEnum._TypeUnsigned:
value.unsignedValue(parseInt(inStr));
break;
case CosDpValueTypeEnum._TypeDouble:
value.dblValue(parseDbl(inStr));
break;
case CosDpValueTypeEnum._TypeString:
value.charValue(inStr);
break;
default:
value.longValue(0);
}
return value;
}
protected static Object convCosDpValueUnion2Object(CosDpValueUnion inValue)
{
// validation: value
if (inValue == null)
{
return null;
}
switch (inValue.discriminator().value())
{
case CosDpValueTypeEnum._TypeBoolean:
return Boolean.valueOf(inValue.boolValue());
case CosDpValueTypeEnum._TypeNumber:
return Integer.valueOf(inValue.longValue());
case CosDpValueTypeEnum._TypeUnsigned:
return Integer.valueOf(inValue.unsignedValue());
case CosDpValueTypeEnum._TypeDouble:
return new Double(inValue.dblValue());
case CosDpValueTypeEnum._TypeString:
return inValue.charValue();
}
return null;
}
protected static CosDpValueUnion convObject2CosDpValueUnion(
CosDpValueTypeEnum inType, Object inObj)
{
// validation: type
if (inType == null)
{
return null;
}
// validation: obj
// none. 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 (inType.value())
{
case CosDpValueTypeEnum._TypeBoolean:
// cast it
boolObj = null;
try
{
boolObj = (Boolean) inObj;
}
catch (Exception ex)
{
// ignore
}
// set the value
if (boolObj == null)
{
value.boolValue(false); // default value
}
else
{
value.boolValue(boolObj.booleanValue());
}
break;
case CosDpValueTypeEnum._TypeNumber:
// cast it
intObj = null;
try
{
intObj = (Integer) inObj;
}
catch (Exception ex)
{
// ignore
}
// set the value
if (intObj == null)
{
value.longValue(0); // default value
}
else
{
value.longValue(intObj.intValue());
}
break;
case CosDpValueTypeEnum._TypeUnsigned:
// cast it
intObj = null;
try
{
intObj = (Integer) inObj;
}
catch (Exception ex)
{
// ignore
}
// set the value
if (intObj == null)
{
value.unsignedValue(0); // default value
}
else
{
value.unsignedValue(intObj.intValue());
}
break;
case CosDpValueTypeEnum._TypeDouble:
// cast it
dblObj = null;
try
{
dblObj = (Double) inObj;
}
catch (Exception ex)
{
// ignore
}
// set the value
if (dblObj == null)
{
value.dblValue(0); // default value
}
else
{
value.dblValue(dblObj.doubleValue());
}
break;
case CosDpValueTypeEnum._TypeString:
// cast it
strObj = null;
try
{
strObj = (String) inObj;
}
catch (Exception ex)
{
// ignore
}
// set the value
if (strObj == null)
{
value.charValue(""); // default value
}
else
{
value.charValue(strObj);
}
break;
}
return value;
}
protected static CosDpValueUnion createCosDpValueUnion(
CosDpValueTypeEnum inType)
{
// validation: inType
if (inType == null)
{
return null;
}
CosDpValueUnion value = new CosDpValueUnion();
// initialize internal value
switch (inType.value())
{
case CosDpValueTypeEnum._TypeBoolean:
value.boolValue(false);
break;
case CosDpValueTypeEnum._TypeNumber:
value.longValue(0);
break;
case CosDpValueTypeEnum._TypeUnsigned:
value.unsignedValue(0);
break;
case CosDpValueTypeEnum._TypeDouble:
value.dblValue(0.0);
break;
case CosDpValueTypeEnum._TypeString:
value.charValue("");
break;
}
return value;
}
protected static int parseInt(String inStr)
{
int value = 0;
try
{
value = Integer.parseInt(inStr);
}
catch (NumberFormatException nfe)
{
// ignore
}
catch (NullPointerException npe)
{
// ignore
}
return value;
}
protected static double parseDbl(String inStr)
{
double value = 0;
try
{
value = Double.parseDouble(inStr);
}
catch (NumberFormatException nfe)
{
// ignore
}
catch (NullPointerException npe)
{
// ignore
}
return value;
}
protected static CosDpValueTypeEnum convString2CosDpValueTypeEnum(
String inStr)
{
// validation: inStr
if (inStr == null || inStr.length() == 0)
{
return CosDpValueTypeEnum.TypeNumber;
}
// parse the string
if (inStr.compareToIgnoreCase("BOOL") == 0)
{
return CosDpValueTypeEnum.TypeBoolean;
}
else if (inStr.compareToIgnoreCase("NUMBER") == 0)
{
return CosDpValueTypeEnum.TypeNumber;
}
else if (inStr.compareToIgnoreCase("UNSIGNED") == 0)
{
return CosDpValueTypeEnum.TypeUnsigned;
}
else if (inStr.compareToIgnoreCase("DOUBLE") == 0)
{
return CosDpValueTypeEnum.TypeDouble;
}
else if (inStr.compareToIgnoreCase("STRING") == 0)
{
return CosDpValueTypeEnum.TypeString;
}
// default
return CosDpValueTypeEnum.TypeNumber;
}
/**
* Parse an expression. Look for reference to other datapoint.
*
* <p>
* Note: The resulting parsed expression will have all datapoint references
* replaced with internal variable name, or replace to 0 if the reference
* datapoint is not found in the given datapoint list.
* </p>
*
* @param inDpList
* - list of datapoints to resolve datapoint reference
* @param inExpression
* - the expression to parse
* @param outParams
* - output list to hold list of datapoint reference
* @param outVarNames
* - output list to hold list of variable name for each datapoint
* reference
* @param outVarTypes
* - output list to hold list of variable type for each datapoint
* reference
* @param outVarValues
* - output list to hold list of initial value for each datapoint
* reference
* @return the parsed expression
*/
protected String _parseExpression(DataPointList inDpList,
String inExpression, ArrayList<IDataPoint> outParams,
ArrayList<String> outVarNames, ArrayList<Class<?>> outVarTypes,
ArrayList<Object> outVarValues)
{
StringBuffer strExpr = new StringBuffer(inExpression);
String dpname = "";
String varname = "";
Class<?> vartype = int.class;
Object varvalue = null;
IDataPoint datapoint = null;
// prepare list of variables
outParams.clear();
outVarNames.clear();
outVarTypes.clear();
outVarValues.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 + KEYWORD_END.length(), loc2)
.trim();
// parse the datapoint keyword
if (inDpList == null)
{
// delete the datapoint keyword.
strExpr.replace(loc1, loc2 + KEYWORD_END.length(), "0");
}
else
{
// get the datapoint reference from datapoint list
datapoint = inDpList.getByName(dpname);
if (datapoint == null)
{
// can not find the handle for the name. invalid
// datapoint
logger
.warn("Can not get datapoint component of the expression: "
+ dpname);
// 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" + outParams.size();
strExpr.replace(loc1, loc2 + KEYWORD_END.length(),
varname);
// add the handle into the list of handle for this data
// point
outParams.add(datapoint);
// store the var name
outVarNames.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 = Boolean.valueOf(datapoint.getValue()
.boolValue());
break;
case CosDpValueTypeEnum._TypeDouble:
vartype = double.class;
varvalue = new Double(datapoint.getValue()
.dblValue());
break;
case CosDpValueTypeEnum._TypeNumber:
vartype = int.class;
varvalue = Integer.valueOf(datapoint.getValue()
.longValue());
break;
case CosDpValueTypeEnum._TypeUnsigned:
vartype = int.class;
varvalue = Integer.valueOf(datapoint.getValue()
.unsignedValue());
break;
case CosDpValueTypeEnum._TypeString:
vartype = String.class;
varvalue = datapoint.getValue()
.charValue();
break;
}
// store the var type and initial value
outVarTypes.add(vartype);
outVarValues.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();
}
@Override
public synchronized int setValue(CosDpValueUnion inValue)
{
// default implementation
return -1;
}
@Override
public synchronized int setBooleanValue(boolean inValue)
{
// default implementation
return -1;
}
@Override
public synchronized int setNumberValue(int inValue)
{
// default implementation
return -1;
}
@Override
public synchronized int setUnsignedValue(int inValue)
{
// default implementation
return -1;
}
@Override
public synchronized int setDoubleValue(double inValue)
{
// default implementation
return -1;
}
@Override
public synchronized int setStringValue(String inValue)
{
// default implementation
return -1;
}
@Override
public synchronized int setSourceQuality(CosDpQualityEnum inQuality,
boolean inUpdateTimestamp)
{
// default implementation
return -1;
}
@Override
public synchronized int setSourceTimestamp(long inTimestamp)
{
// default implementation
return -1;
}
@Override
public synchronized int setSourceValue(CosDpValueUnion inValue,
boolean inUpdateTimestamp)
{
// default implementation
return -1;
}
@Override
public synchronized int setBooleanSourceValue(boolean inValue,
boolean inUpdateTimestamp)
{
// default implementation
return -1;
}
@Override
public synchronized int setNumberSourceValue(int inValue,
boolean inUpdateTimestamp)
{
// default implementation
return -1;
}
@Override
public synchronized int setUnsignedSourceValue(int inValue,
boolean inUpdateTimestamp)
{
// default implementation
return -1;
}
@Override
public synchronized int setDoubleSourceValue(double inValue,
boolean inUpdateTimestamp)
{
// default implementation
return -1;
}
@Override
public synchronized int setStringSourceValue(String inValue,
boolean inUpdateTimestamp)
{
// default implementation
return -1;
}
@Override
public synchronized int setSourceValue(CosDpValueStruct inValue)
{
// default implementation
return -1;
}
@Override
public synchronized boolean setInhibit()
{
// validation: already inhibited?
if (isInhibit)
{
return true;
}
// inhibit and override is mutually exclusive. this is a design
// decision.
if (isOverride)
{
return false;
}
// set the local flag
isInhibit = true;
//datapoint event
//iPendingEvent_ |= EVT_STATE_CHANGE;
// set the quality to QualityInhibit
outputQuality = CosDpQualityEnum.QualityInhibit;
//datapoint event
//iPendingEvent_ |= EVT_OUTPUT_QUALITY_CHANGE;
//update the output timestamp
outputTimestamp = Calendar.getInstance().getTimeInMillis();
//datapoint event
//iPendingEvent_ |= EVT_OUTPUT_TIMESTAMP_CHANGE;
// cascade it to equipment
if (equipment != null)
{
equipment.onDataPointInhibitChange(this, true);
}
// re-evaluate all related calculated points, if any
for (int i = 0; i < calculatedPoints.size(); i++)
{
calculatedPoints.get(i).evaluate();
}
// re-calculate all related meter points, if any
for (int i = 0; i < meterPoints.size(); i++)
{
meterPoints.get(i).calculateMeterValue();
}
// re-evaluate all child points' quality, if any
for (int i = 0; i < childPoints.size(); i++)
{
childPoints.get(i).updateQuality();
}
// update timestamp
updateTimestamp_ = Calendar.getInstance().getTimeInMillis();
// TODO: notify datastore
return true;
}
@Override
public synchronized boolean removeInhibit()
{
// validation: never inhibited
if (!isInhibit)
{
return true;
}
// clear the local flag
isInhibit = false;
// synchronize the current value
if (_synchronize() > 0)
{
// re-evaluate all related calculated points, if any
for (int i = 0; i < calculatedPoints.size(); i++)
{
calculatedPoints.get(i).evaluate();
}
// re-calculate all related meter points, if any
for (int i = 0; i < meterPoints.size(); i++)
{
meterPoints.get(i).calculateMeterValue();
}
// re-evaluate all child points' quality, if any
for (int i = 0; i < childPoints.size(); i++)
{
childPoints.get(i).updateQuality();
}
}
// cascade it to equipment
if (equipment != null)
{
equipment.onDataPointInhibitChange(this, false);
}
// update timestamp
updateTimestamp_ = Calendar.getInstance().getTimeInMillis();
// TODO: notify datastore
return true;
}
@Override
public boolean isInhibit()
{
return isInhibit;
}
@Override
public synchronized boolean setOverride(CosDpValueUnion inOverrideValue)
{
// validation
if (inOverrideValue == null)
{
return false;
}
// inhibit and override is mutually exclusive. this is a design
// decision.
if (isInhibit)
{
return false;
}
// attempt to copy the override value to output value
if (copy(outputValue, inOverrideValue) < 0)
{
// can not set, somehow
return false;
}
//datapoint event
//iPendingEvent_ |= EVT_OUTPUT_VALUE_CHANGE;
// set the local flag
isOverride = true;
//datapoint event
//iPendingEvent_ |= EVT_STATE_CHANGE;
// set output quality to QualityOverride
outputQuality = CosDpQualityEnum.QualityOverride;
//datapoint event
//iPendingEvent_ |= EVT_OUTPUT_QUALITY_CHANGE;
// update output timestamp. this is because setOverride() will override
// the output value
outputTimestamp = Calendar.getInstance().getTimeInMillis();
//datapoint event
//iPendingEvent_ |= EVT_OUTPUT_TIMESTAMP_CHANGE;
// cascade it to the equipment
if (equipment != null)
{
equipment.onDataPointOverrideChange(this, true);
}
// re-evaluate all related calculated points, if any
for (int i = 0; i < calculatedPoints.size(); i++)
{
calculatedPoints.get(i).evaluate();
}
// re-calculate all related meter points, if any
for (int i = 0; i < meterPoints.size(); i++)
{
meterPoints.get(i).calculateMeterValue();
}
// re-evaluate all child points' quality, if any
for (int i = 0; i < childPoints.size(); i++)
{
childPoints.get(i).updateQuality();
}
// update datapoint timestamp
updateTimestamp_ = Calendar.getInstance().getTimeInMillis();
// TODO: notify datastore
return true;
}
@Override
public synchronized boolean removeOverride()
{
// validation: never overridden
if (!isOverride)
{
return true;
}
// clear the local flag
isOverride = false;
// synchronize the current value
if (_synchronize() > 0)
{
// re-evaluate all related calculated points, if any
for (int i = 0; i < calculatedPoints.size(); i++)
{
calculatedPoints.get(i).evaluate();
}
// re-calculate all related meter points, if any
for (int i = 0; i < meterPoints.size(); i++)
{
meterPoints.get(i).calculateMeterValue();
}
// re-evaluate all child points' quality, if any
for (int i = 0; i < childPoints.size(); i++)
{
childPoints.get(i).updateQuality();
}
}
// cascade it to equipment
if (equipment != null)
{
equipment.onDataPointOverrideChange(this, false);
}
// update timestamp
updateTimestamp_ = Calendar.getInstance().getTimeInMillis();
// TODO: notify datastore
return true;
}
@Override
public boolean isOverride()
{
return isOverride;
}
@Override
public synchronized boolean setReturnCondition(String inExpression,
DataPointList inDpList)
{
// default implementation
return false;
}
@Override
public synchronized boolean checkReturnCondition()
{
// default implementation
return false;
}
@Override
public synchronized boolean setLaunchCondition(String inExpression,
DataPointList inDpList)
{
// default implementation
return false;
}
@Override
public synchronized boolean checkLaunchCondition()
{
// default implementation
return false;
}
@Override
public synchronized boolean setExpression(String inExpression,
DataPointList inDpList)
{
// default implementation
return false;
}
@Override
public synchronized int evaluate()
{
// default implementation
return 0;
}
@Override
public synchronized boolean setAlarmInhibit()
{
// TODO Auto-generated method stub
return false;
}
@Override
public synchronized boolean removeAlarmInhibit()
{
// TODO Auto-generated method stub
return false;
}
@Override
public boolean isAlarmInhibit()
{
return isAlarmInhibit;
}
@Override
public synchronized boolean raiseAlarm()
{
// TODO Auto-generated method stub
return false;
}
@Override
public synchronized boolean normalizeAlarm()
{
// TODO Auto-generated method stub
return false;
}
@Override
public synchronized boolean acknowledgeAlarm()
{
// TODO Auto-generated method stub
return false;
}
@Override
public boolean isInAlarm()
{
return (isAlarmNotAck | isAlarmNotNorm);
}
@Override
public boolean isAlarmNormalized()
{
return !isAlarmNotNorm;
}
@Override
public boolean isAlarmAcknowledged()
{
return !isAlarmNotAck;
}
@Override
public int setReadingRollOver(int inRollOver)
{
// default implementation
return 0;
}
@Override
public int getReadingRollOver()
{
// default implementation
return 0;
}
@Override
public int setDataPointRollOver(int inRollOver)
{
// default implementation
return 0;
}
@Override
public int getDataPointRollOver()
{
// default implementation
return 0;
}
@Override
public synchronized int adjustMeterValue(int inAdjustment)
{
// default implementation
return 0;
}
@Override
public boolean setMeterSourcePoint(IDataPoint inDataPoint)
{
// default implementation
return false;
}
@Override
public int calculateMeterValue()
{
// default implementation
return -1;
}
@Override
public synchronized boolean setEngineeringConversion(String inExpression)
{
// default implementation
return false;
}
@Override
public synchronized double setDeadBandValue(double inDeadBand)
{
// default implementation
return deadband;
}
@Override
public double getDeadBandValue()
{
return deadband;
}
@Override
public synchronized void startBatchOperation()
{
// just set the flag
isBatchOperation = true;
}
@Override
public synchronized void finishBatchOperation()
{
// just clear the flag
isBatchOperation = false;
}
@Override
public synchronized void addChildPoint(IDataPoint inDataPoint)
{
// validation: inDataPoint
if (inDataPoint == null)
{
return;
}
// check for duplicate entry
if (null != childPoints.getByKeyId(inDataPoint.getKeyId()))
{
return;
}
// add to the list
childPoints.add(inDataPoint);
}
@Override
public synchronized void addMeterPoint(IDataPoint inDataPoint)
{
// validation: inDataPoint
if (inDataPoint == null)
{
return;
}
// check for duplicate entry
if (null != meterPoints.getByKeyId(inDataPoint.getKeyId()))
{
return;
}
// add to the list
meterPoints.add(inDataPoint);
}
@Override
public synchronized void addCalculatedPoint(IDataPoint inDataPoint)
{
// validation: inDataPoint
if (inDataPoint == null)
{
return;
}
// check for duplicate entry
if (null != calculatedPoints.getByKeyId(inDataPoint.getKeyId()))
{
return;
}
// add to the lisinDataPointt
calculatedPoints.add(inDataPoint);
}
@Override
public synchronized int updateQuality()
{
// do not update when it is inhibit or override
if (isInhibit || isOverride)
{
return -1;
}
// update output quality
CosDpQualityEnum newQuality = calculate_quality();
if (outputQuality != newQuality)
{
outputQuality = newQuality;
return +1;
}
return 0;
}
}