Package tcg.scada.da

Source Code of tcg.scada.da.CalculatedDataPoint

package tcg.scada.da;

import java.util.ArrayList;
import java.util.Calendar;

import tcg.common.LoggerManager;
import tcg.common.util.ExpressionParser;
import tcg.scada.cos.CosDpQualityEnum;
import tcg.scada.cos.CosDpValueTypeEnum;
import tcg.scada.cos.CosDpValueUnion;

/**
* Implementation of calculated datapoint
*
* <p>
* Expected behaviour for calculated datapoint:
* <ul>
* <li>Source value: expression result</li>
* <li>Source quality: GoodQuality if expression is evaluated successfully</li>
* <li>Source timestamp: timestamp of last expression evaluation</li>
* <li>Output value: expression result if not inhibited/overridden</li>
* <li>Output quality: internal calculation</li>
* <li>Output timestamp: timestamp of last expression evaluation, if not
* inhibited/overridden</li>
* </ul>
* </p>
*
* <p>
* Setting output value/quality/timestamp is not supported for calculated
* datapoint.
* </p>
*
* <p>
* Setting source value/quality/timestamp is also not supported for calculated
* datapoint. To update the value, run evaluate(). This will update the source
* value to the expression result, the source quality to the expression quality
* and the source timestamp to current time.
* </p>
*
* <p>
* <b>On the other hand, inhibit and override IS supported for calculated
* datapoint.</b> Alarm is also supported.
* </p>
*
* <p>
* Deadband and engineering conversion is not supported for virtual datapoint.
* </p>
*
* @author Yoga
*
*/
public class CalculatedDataPoint extends DataPoint
{
  // expression value for calculated datapoint
  protected String expression = "";
  protected ExpressionParser expressionParser = null;
  protected ArrayList<IDataPoint> expressionParams = null;

  // flag to prevent infinite recursive calculation
  // private final Semaphore semaphore_ = new Semaphore(1, true);

  /**
   * Default ctor.
   *
   * @param inType
   *            - the datapoint type
   * @param inDataType
   *            - the internal data type
   */
  public CalculatedDataPoint(CosDpValueTypeEnum inDataType)
  {
    super(inDataType);
    // override the logger
    logger = LoggerManager.getLogger(this.getClass().getName());
    // set the default quality to bad.
    // this way, as soon as the source quality is good, the quality is updated.
    // this in turn will update the datapoint timestamp
    sourceQuality = CosDpQualityEnum.QualityBad;
    outputQuality = CosDpQualityEnum.QualityBad;
  }

  @Override
  public EDataPointType getType()
  {
    return EDataPointType.TYPE_CALCULATED;
  }

  @Override
  public synchronized boolean setExpression(String inExpression,
      DataPointList inDpList)
  {
    // validation: inExpression
    if (inExpression == null || inExpression.isEmpty())
    {
      return false;
    }

    // validation: inDpList
    // none

    // get the expression type
    // first of all get the return type
    Class<?> type = int.class; // default is int
    switch (outputValue.discriminator().value())
    {
      case CosDpValueTypeEnum._TypeBoolean:
        type = boolean.class;
        break;
      case CosDpValueTypeEnum._TypeNumber:
        type = int.class;
        break;
      case CosDpValueTypeEnum._TypeUnsigned:
        type = int.class;
        break;
      case CosDpValueTypeEnum._TypeDouble:
        type = double.class;
        break;
      case CosDpValueTypeEnum._TypeString:
        type = String.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<IDataPoint> params = new ArrayList<IDataPoint>();
    ArrayList<String> varnames = new ArrayList<String>();
    ArrayList<Class<?>> vartypes = new ArrayList<Class<?>>();
    ArrayList<Object> varvalues = new ArrayList<Object>();

    // parse the expression
    String parsedExpression = _parseExpression(inDpList, inExpression,
        params, varnames, vartypes, varvalues);

    // create the parser
    expressionParser = new ExpressionParser(parsedExpression, type);

    // create all the variables
    for (int i = 0; i < varnames.size(); i++)
    {
      expressionParser.createVariable(varnames.get(i), vartypes.get(i),
          varvalues.get(i));
    }

    // copy the list of datapoint component
    expressionParams = params;

    // try to evaluate
    boolean status = true;
    try
    {
      Object result = expressionParser.evaluate();
      if (result == null)
      {
        status = false;
      }
    }
    catch (Exception ex)
    {
      logger
          .error("Can not set datapoint expression: "
              + ex.getMessage());
      status = false;
    }

    // if not valid, set the source quality to bad and just quit
    if (!status)
    {
      sourceQuality = CosDpQualityEnum.QualityBad;
      sourceTimestamp = Calendar.getInstance().getTimeInMillis();
      // synchronize
      _synchronize();
      // update the timestamp
      updateTimestamp_ = Calendar.getInstance().getTimeInMillis();
      // return
      return false;
    }

    // for each datapoint param, add this datapoint as child point
    IDataPoint datapoint = null;
    for (int i = 0; i < expressionParams.size(); i++)
    {
      datapoint = expressionParams.get(i);
      if (datapoint != null)
      {
        datapoint.addCalculatedPoint(this);
      }
    }

    // update source quality and timestamp
    sourceQuality = CosDpQualityEnum.QualityGood;
    sourceTimestamp = Calendar.getInstance().getTimeInMillis();

    // synchronize
    _synchronize();

    // update the timestamp
    updateTimestamp_ = Calendar.getInstance().getTimeInMillis();

    return true;
  }

  @Override
  public synchronized int evaluate()
  {
    // validation: expression is never set
    if (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 time this happens for error.");
      return -1;
    }

    //TODO: prevent for infinite recursive relationship

    int retval = 0;
    IDataPoint datapoint = null;
    Object value = null;

    CosDpQualityEnum expressionQuality = CosDpQualityEnum.QualityGood;
    CosDpValueUnion expressionValue = null;

    // update the value of expression parameters
    for (int i = 0; i < expressionParams.size(); i++)
    {
      datapoint = expressionParams.get(i);
      if (datapoint == null)
      {
        logger.trace("DP parameter : UNKNOWN");
        // this, in theory, should never happens
        expressionQuality = CosDpQualityEnum.QualityBad;
        // default value
        value = Integer.valueOf(0);
      }
      else
      {
        logger.trace("DP parameter : " + datapoint.getName());

        // check for bad quality, in which case the expression quality
        // will also be set to bad
        // Note: Anything that is not QualityGood is considered bad quality.
        //       This includes QualityInhibit and QualityOverride
        if (datapoint.getQuality() != CosDpQualityEnum.QualityGood)
        {
          expressionQuality = CosDpQualityEnum.QualityBad;
        }

        // calculate the parameter value
        value = convCosDpValueUnion2Object(datapoint.getValue());
      }

      // set the parameter with the new value
      expressionParser.set(i, value);

      // verbose
      logger.trace("DP parameter value : " + value.toString());
    }

    // evaluate the expression value
    try
    {
      value = expressionParser.evaluate();
      if (value == null)
      {
        logger.warn("Can not evaluate expression. Null result");
        expressionQuality = CosDpQualityEnum.QualityBad;
      }
    }
    catch (Exception ex)
    {
      logger.warn("Can not evaluate expression. Exception: "
          + ex.toString());
      expressionQuality = CosDpQualityEnum.QualityBad;
    }

    // convert it to CosDpValueUnion
    expressionValue = convObject2CosDpValueUnion(sourceValue
        .discriminator(), value);

    // update the source value with the expression value
    int status1 = copy(sourceValue, expressionValue);
    ;

    // update the source quality with the expression quality
    int status2 = 0;
    if (sourceQuality != expressionQuality)
    {
      sourceQuality = expressionQuality;
      status2 = +1;
    }

    // return value
    if (status1 > 0 || status2 > 0)
    {
      retval = +1;
    }
    else if (status1 == 0 || status2 == 0)
    {
      retval = 0; // no updates or changes
    }
    else
    {
      retval = -1; // failure
    }

    // post-processing
    if (retval == 1)
    {
      // update the source timestamp
      sourceTimestamp = Calendar.getInstance().getTimeInMillis();

      // update the update timestamp
      updateTimestamp_ = sourceTimestamp;

      // synchronize the source and output value/quality/timestamp
      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();
        }
      }

      // TODO: notify datastore
      if (datastore != null)
      {
        // dpServer.onDataPointChange(this);
      }
    } // if (retval == 1)

    return retval;
  }
}
TOP

Related Classes of tcg.scada.da.CalculatedDataPoint

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.