Developer guide on how to develop algorithmic blocks. Only custom data processing blocks can be built at this point.
Figure 1
An algorithm usually has 3 types of blocks, i.e
The first (Block 1 & 2 figure 1) and third (Block 4 figure 1) blocks are mandatory without which an algorithm won’t work. Currently the apis are only available to build Data processing/logic blocks.
Once user requests for an algorithm’s output, the request is passed on from the result block to all the way to the datasource blocks (nested algorithms are also supported, in that case the request goes all the way till the nested algorithms datasource blocks.). In response to the request, the data flows from the datasource blocks to the result blocks. The data flows as streams instead of bulk operation i.e. the records are fetched from history only on demand record by record, this is in turn keeps the memory foot print low even while processing large data sets. In case of blocks requiring data to be processed in bulk, caching mechanism can be built into the blocks, but should be done with proper care as it carries risk of affecting the available memory space. Also care should be taken while caching the instances returned by the algorithm. See AnalyticValue
All custom data processing blocks in the Niagara Analytics Framework should extend
BAlgorithmBlock or BOutputBlock. The properties that needs to be used as in/out for these blocks should be of type BBlockPin
BBiMathBlock extends BOutputBlock
BBiMathBlock
@NiagaraType
@NiagaraProperty(name = "in1", type = "BBlockPin", defaultValue = "new BBlockPin()", flags = Flags.SUMMARY)
@NiagaraProperty(name = "in2", type = "BBlockPin", defaultValue = "new BBlockPin()", flags = Flags.SUMMARY)
public class BBiMathBlock
extends BOutputBlock
{
//
// Returns a trend of with the function applied to each row.
//
@Override
public AnalyticTrend getTrend(AnalyticContext cx)
{
return new MyTrend(this,cx);
}
//
// Performs the function directly on the inputs.
//
@Override
public AnalyticValue getValue(AnalyticContext cx)
{
return eval(getInputValue(0,cx),getInputValue(1,cx));
}
//
// Performs the function for both getValue and getTrend requests.
//
private AnalyticValue eval(AnalyticValue in1, AnalyticValue in2)
{
AnalyticValue ret = in1;
double result = in1.toNumeric() + in2.toNumeric();
int sts = in1.getStatus() | in2.getStatus();
if (Double.isNaN(result) || Double.isInfinite(result))
sts = sts | STATUS_NULL;
if (ret instanceof AnalyticNumeric)
((AnalyticNumeric)ret).setValue(result);
else
ret.setValue(BDouble.make(result));
ret.setStatus(sts);
return ret;
}
//
// Calls eval for each interval.
//
private class MyTrend extends BlockTrend
{
public MyTrend(AlgorithmBlock block, AnalyticContext cx)
{
super(block,cx);
}
protected AnalyticValue getNext()
{
if (!advance()) return null;
return eval(getValue(0),getValue(1));
}
}
}