Please ensure Javascript is enabled for purposes of website accessibility
Powered by Zoomin Software. For more details please contactZoomin

AVEVA Pipeline Operations for Liquids

Example implementation

  • Last UpdatedApr 24, 2025
  • 5 minute read

The following examples illustrate how to send custom values to the inventory tables using each of the three options described in the previous section.

Option 1: Send one custom value to the Historical table

Use this option if you only need to send one custom value to the Historical table. This example shows how the extendAnomalyInventory function was implemented to return a string containing a single value.

/// <summary>

/// This is the plugin's extension point for our customers to extend the list of fields from Anomaly that pushes to the historical database as part of the LMS Inventory process.

/// </summary>

/// <note> The pipeline record has already been locked with REQ_CVT_RESRW. </note>

/// <example>

/// This functional example uses a single field value as the return string

/// </example>

/// <param name="pPpl"> The pipeline record pointer </param>

/// <param name="anmslot"> The anomaly slot number. </param>

/// <returns> return the extension value as string format </returns>

extern "C" OAS_EXPORT std::string extendAnomalyInventory(pipeline * pPpl, anomalyslot anmslot)

{

    anomaly* pAnom;

    TFlexString anomalyName = "";

    try

    {

        if (!(pAnom = (anomaly*)bt_get_rec(DATABASE_ANOMALY, anmslot)))

        {

            cpu_debug(CPU_DEBUG_ERROR, "extendAnomalyInventory(): Cannot get anomaly record %d in pipeline %s", anmslot, pPpl->name);

            return "";

        }

        anomalyName = pAnom->name;

        return std::to_string(pAnom->voloffset); // returning volume offset as single field value.

    }

    catch (const std::exception& ex)

    {

        cpu_debug(CPU_DEBUG_ERROR, "extendAnomalyInventory(): Exception - Pipeline: %s. Anomaly: %s. Exception message: %s", pPpl->name, anomalyName.Get(), ex.what());

        return "";

    }

}

Option 2: Send multiple custom values to the Historical table

Use this option if you need to send multiple custom values to the Historical table. This example shows how the extendPipeSegmentInventory function was implemented to return a string containing a comma-separated list of key-value pairs. Refer to the Software Development Kit (SDK) for the full example including definitions of supporting types and functions used in this option.

/// <summary>

/// This is the plugin's extension point for our customers to extend the list of fields from Pipe Segment that pushes to the historical database as part of the LMS Inventory process.

/// </summary>

/// <note> The pipeline record has already been locked with REQ_CVT_RESRW. </note>

/// <example>

/// This functional example uses key value pairs data as return string

/// </example>

/// <param name="pPpl"> The pipeline record pointer </param>

/// <param name="pipeslot"> The pipe segment slot </param>

/// <returns> return the extension values as string format </returns>

extern "C" OAS_EXPORT std::string extendPipeSegmentInventory(pipeline * pPpl, pipe_segmentslt pipeslot)

{

    pipe_segment* pPipe;

    listPair extensionList;

    TFlexString pipeSegmentName = "";

    try

    {

        if (!(pPipe = (pipe_segment*)bt_get_rec(DATABASE_PIPE_SEGMENT, pipeslot)))

        {

            cpu_debug(CPU_DEBUG_ERROR, "extendPipeSegmentInventory():Cannot get pipe segment record %d in pipeline %s", pipeslot, pPpl->name);

            return"";

        }

        pipeSegmentName = pPipe->name;

        extensionList.push_back(std::make_pair("capacity", std::to_string(pPipe->capacity)));

        extensionList.push_back(std::make_pair("flowing", std::to_string(pPipe->flowing)));

        extensionList.push_back(std::make_pair("section", std::to_string(pPipe->section)));

        return generateKeyValuePairFormat(extensionList);

    }

    catch (const std::exception& ex)

    {

        cpu_debug(CPU_DEBUG_ERROR, "extendPipeSegmentInventory(): Exception - Pipeline: %s Pipe Segment: %s Exception message: %s", pPpl->name, pipeSegmentName.Get(), ex.what());

        return "";

    }

}

Option 3: Send custom values to an extension table

Use this option if you want to send custom values to an extension table as opposed to storing the values in the Extension field within the baseline table. This example shows how the extendStationInventory function was implemented to send the custom values to a custom table in the Historical database and return the generated unique ID to be stored in the Extension field within the baseline table. This unique ID can then be used by SQL queries as a foreign key to join the baseline table with the custom table. Refer to the SDK for the full example including definitions of supporting types and functions used in this option.

Note: If you use this option, additional steps are required to ensure Datapump can replicate the custom table. For more information, refer to the AVEVA Enterprise SCADA Historical Administrator Guide documentation's "Configure a New Table for Datapump" topic.

/// <summary>

/// This is the plugin's extension point for our customers to extend the list of fields from Station that pushes to the historical database as part of the LMS Inventory process.

/// </summary>

/// <note> The pipeline record has already been locked with REQ_CVT_RESRW. </note>

/// <example>

/// This functional example uses custom historical table filling and returning a foreign key as return string

/// </example>

/// <param name="pPpl"> The pipeline record pointer </param>

/// <param name="curnode"> The current node slot number. </param>

/// <param name="lastpipe"> The last pipe segment slot from current node </param>

/// <param name="nextpipe"> The next pipe segment slot from current node</param>

/// <returns> return the extension values as string format </returns>

extern "C" OAS_EXPORT std::string extendStationInventory(pipeline * pPpl, nodeslot curnode, pipe_segmentslt lastpipe, pipe_segmentslt nextpipe)

{

node* pNode;

pipe_segment* pLastPipe = NULL, * pNextPipe = NULL;

double dns_linepost = 0.0, ups_distance_to_temp = 0.0;

TFlexString nodeName = "";

TFlexString extensionID = "";

try

{

if (!(pNode = (node*)bt_get_rec(DATABASE_NODE, curnode)))

{

cpu_debug(CPU_DEBUG_ERROR, "extendStationInventory(): Cannot get node record %d in pipeline %s", curnode, pPpl->name);

return "";

}

nodeName = pNode->name;

if (lastpipe > 0)

{

if (!(pLastPipe = (pipe_segment*)bt_get_rec(DATABASE_PIPE_SEGMENT, lastpipe)))

{

cpu_debug(CPU_DEBUG_ERROR, "extendStationInventory(): Cannot get pipe segment record %d in pipeline %s", lastpipe, pPpl->name);

}

else

{

dns_linepost = pLastPipe->dns_linepost;

}

}

if (nextpipe > 0)

{

if (!(pNextPipe = (pipe_segment*)bt_get_rec(DATABASE_PIPE_SEGMENT, nextpipe)))

{

cpu_debug(CPU_DEBUG_ERROR, "extendStationInventory(): Cannot get pipe segment record %d in pipeline %s", nextpipe, pPpl->name);

}

else

{

ups_distance_to_temp = pNextPipe->ups_tdist;

}

}

//The code that follows shows how to send a unique ID as a GUID string as foreign key to the baseline station inventory table

//and then send additional fields with the same unique ID as a primary key to a custom historical table that is located in a custom database.

//This allows the historical tables to be queried while joining both tables using the unique key.

//However, it needs a custom stored procedure like "writepplStationInventoryToCustomTable" to write data to custom historical table first in order to use

the

//Aveva provided API component "app_SpoolTransaction" to transmit the extra data to a custom table.

//Note: Users must additionally configure datapump to replicate the custom table among other history servers as needed; please refer to the Historical guide.

TFlexString buffer = "execute CustomDB..writepplStationInventoryToCustomTable ";

buffer.SetLocale("C");

std::string stdGuid = generateGUID();

TFlexString flexGuid = TFlexString(stdGuid.c_str());

buffer

<< "'" << flexGuid << "', "

<< pNode->station << ", "

<< dns_linepost << ", "

<< ups_distance_to_temp;

if (!app_SpoolTransaction("Historical", buffer.Get(), NULL, OAS_FALSE, OAS_FALSE, OAS_FALSE))

{

cpu_debug(CPU_DEBUG_ERROR, "extendStationInventory(): Could not spool command to Historical service: '%s'", buffer.Get());

}

return stdGuid;

}

catch (const std::exception& ex)

{

cpu_debug(CPU_DEBUG_ERROR, "extendStationInventory(): Exception - Pipeline: %s. Node: %s. Exception message: %s", pPpl->name, nodeName.Get(), ex.what());

return "";

}