Hierarchical Custom Controller Browsing
Browsing Custom Controller in a Hierarchy
About this task
Hierarchical browsing can be developed using the Collector Toolkit. Hierarchical browsing enables you to browse custom collectors in a hierarchical manner if your server supports hierarchical organization of tags in a tree structure.
To browse for Custom Collector tags in a hierarchy:
Procedure
Developing Hierarchical Browsing using Collector Toolkit
The following callback is used to develop hierarchical browsing using the Collector Toolkit:
ihCollectorToolkitGetTagsHierarchical(wchar_t* BrowsePosition, wchar_t* NodeFilter,
ihTKHierarchicalBrowseResponse* Response);
The following are the parameters in this callback:
Name | Description |
---|---|
BrowsePosition | The place where the current node is selected. |
NodeFilter | The delimiter using which hierarchical node differentiation is made |
Response structure | Contains the number of nodes/leaves for a particular node. |
To define the hierarchical tree format as illustrated in the Add Multiple Tags from Collector figure above, Historian provides the following information in three responses, provided as a sample here. Use the information in the following three responses to browse the tags and add the required tags.
As a first response Historian returns:
ihInterfaceGetTagsHierarchical()
The following are the parameter values:
BrowsePosition "ihHierarchicalBrOwSeRoOt"
NodeFilter: "" (By default, this uses a ???/???)
Response = NULL
NodeCount 1 int
NodeNames "_Status" wchar_t *
FullNodeNames "/_Status" wchar_t *
ihInterfaceGetTags()
BrowsePosition ???ihHierarchicalBrOwSeRoOt wchar_t*
Second response:
ihInterfaceGetTagsHierarchical()
BrowsePosition "/_Status"
NodeNames "DA_Server"
FullNodeNames "/_Status/DA_Server"
ihInterfaceGetTags()
BrowsePosition ???/_Status???
Third response:
ihInterfaceGetTagsHierarchical()
BrowsePosition "/_Status/DA_Server"
BrowsePosition "/_Status/DA_Server"
Collector Initialization Callbacks
The following are the callbacks used for a collector when it is initialized:
ihCollectorToolkitPreInitialize
ihCollectorToolkitInitialize
ihCollectorToolkitInitializeCompleted
When a collector is shutting down, ihCollectorToolkitShutdown
method is called and the collector performs all the necessary steps before it is completely shut down.
Example
The following sample program helps you understand the ihCollectorToolkitPreInitialize
function.
Historian expects Custom Collector Pre-Initialization information from the user. For example, custom collector can browse tags, collector type and so on.
/// </summary>
/// <param name="PreCfg">Collector Pre - Configuration information structure</param>
/// <param name="Cfg">Collector Configuration information structure</param>
void RandomValueSimulator::ihCollectorToolkitPreInitialize(ihInterfaceTKPreCfgInfo *PreCfg, ihInterfaceTKCfgInfo *Cfg)
{
// Initializes General1-5 values, here General1,2 were initialized with 1000, 60.
Cfg->CustomProp1 = TKStrdup(_T("1000"));
Cfg->CustomProp2 = TKStrdup(_T("60"));
// Historian follows representation of the collectors in the form of ComputerName_CollectorName, Tags in the form of ComputerName.TagName.
// In the following section custom collector is trying to get the computer name.
CString ComputerName, IP;
TKGetHostNameAndIP(ComputerName, IP);
if (ComputerName.GetLength() >; 0)
{
ComputerName.Append(_T("."));
wcscpy_s(Cfg->;DefaultTagPrefix, ComputerName.GetBuffer());
}
RandPreCfg = *PreCfg;
RandPreCfg.CanSendOPCQuality = TRUE; // to send OPC Quality
RandPreCfg.InterfaceType = ihTKCustom; // interface type
RandPreCfg.MultipleInstancesAllowed = FALSE;
RandPreCfg.MinimumInterval = RandMinimumInterval;
RandPreCfg.MaxTagsPerRead = MaxTagsPerGroup;
RandPreCfg.CanReadASync = TRUE;// to read tags Asynchronously
RandPreCfg.CanBrowseSource = TRUE;// you can browse the collector
RandPreCfg.CanSourceTimestamp = TRUE;// collector sends data containing source time stamp or server time stamp
RandPreCfg.ForceInputScaling = FALSE;
RandPreCfg.NeedMsgPump = FALSE;
RandPreCfg.ForcedScaleLO = 0.0; // Low engineering unit
RandPreCfg.ForcedScaleHI = (float) RAND_MAX;// High engineering unit
RandPreCfg.DoesReloadMode = FALSE;
RandPreCfg.DoesLagTimes = FALSE;
RandPreCfg.CanBrowseHierarchical = TRUE;
*PreCfg = RandPreCfg;
}
For the ihCollectorToolkitInitialize
function:
Collector Initialization
/// </summary>
/// <param name="Cfg">Collector Configuration information</param>
/// <param name="PreCfg">Collector Pre-Configuration Information</param>
/// <param name="ErrorMsg">Error Message while initializing</param>
/// <param name="ErrorMsgSize">Size of Error Message</param>
/// <param name="RegKeyName">modification required registry keys, if any </param>
/// <param name="Callbacks">Callbacks of all the asynchronous methods</param>
/// <param name="DoDebug">debug param</param>
/// <returns>status of the method call</returns>
int RandomValueSimulator::ihCollectorToolkitInitialize(ihInterfaceTKCfgInfo *Cfg,
ihInterfaceTKPreCfgInfo *PreCfg, wchar_t *ErrorMsg, int ErrorMsgSize, wchar_t *RegKeyName, ihCollectorToolkitCallback *Callbacks, int DoDebug)
{
// updates configuration information to collector
ihCollectorToolkitPropertyUpdate(Cfg);
TKFree(TagPrefix);
// Historian follows representation of Tags in the form of ComputerName.TagName.
TagPrefix = TKStrdup(Cfg->DefaultTagPrefix);
Cfg->DoOnFly = 1;
// Initializes error msg to ""(NULL)
ErrorMsg = TKStrdup(_T(""));
srand((unsigned) time(NULL));
RandCfg = *Cfg;
g_Callbacks = Callbacks;
return(TRUE);
}
Polled Tag Callbacks
For polled tags we use different callback functions at various stages.
- Step 1
- At collector start up,
ihCollectorToolkitPolledInit
andihCollectorToolkitPolledInitCompleted
callbacks are triggered and the details of all the Polled tags for the given collector is returned. - Step 2
- If a polled tag is added,
ihCollectorToolkitPolledAddTag
method is called and the details of the tag is entered in the collector tag list. - Step 3
- If a polled tag is deleted,
ihCollectorToolkitPolledDeleteTag
method is called and the tag details are removed from the collector tag list.
Example
The following sample helps you understand the given callback functions.
The collectors maintains the polled tag details in the local Cache as:
map<int, ihInterfaceTKPolledTagInfo> TagIdToTagInfoMap;
This map is filled using this sample example:
/// Polled tags initialization started
/// </summary>
/// <returns>TRUE/FALSE</returns>
int RandomValueSimulator::ihCollectorToolkitPolledInit(void)
{
TagIdToTagInfoMap.clear();
return CCollectorDelegator::ihCollectorToolkitPolledInit();
}
??
---------------------------------------------------------------------
??
/// <summary>
/// Historian updating the source saying that, polled tags initialization successful.
Are there any initialization from source for polled tags?
/// <summary>
/// <returns>TRUE/FALSE</returns>
int RandomValueSimulator::ihCollectorToolkitPolledInitCompleted(void)
{
return CCollectorDelegator::ihCollectorToolkitPolledInitCompleted();
}
??
---------------------------------------------------------------------
??
/// <summary>
/// Adds polled tag to the historian from source
/// </summary>
/// <param name="PolledTag">ihInterfaceTKPolledTagInfo instance</param>
/// <param name="IsCollectorStarting">collector status</param>
/// <returns>TRUE/FALSE</returns>
int RandomValueSimulator::ihCollectorToolkitPolledAddTag(ihInterfaceTKPolledTagInfo
*PolledTag, int IsCollectorStarting)
{
TagIdToTagInfoMap.insert(std::pair<int, ihInterfaceTKPolledTagInfo>
(PolledTag-<TagId, *PolledTag));
return CCollectorDelegator::ihCollectorToolkitPolledAddTag(PolledTag,
IsCollectorStarting);
}
??
-----------------------------------------------------------------------
??
/// <summary>
/// Deleted polled tag from Historian from Client Tools/non web admin/external tools
/// <summary>
/// <param name="tagId">Tag Identification</param>
/// <returns>TRUE/FALSE</returns>
int RandomValueSimulator::ihCollectorToolkitPolledDeleteTag(int tagId)
{
TagIdToTagInfoMap.erase(TagIdToTagInfoMap.find(tagId));
return CCollectorDelegator::ihCollectorToolkitPolledDeleteTag(tagId);
}
Unsolicited Tags Callbacks
For unsolicited tags we use different callback functions at various stages.
- Step 1
- At collector start up,
ihCollectorToolkitASyncInit
andihCollectorToolkitASyncInitCompleted
callbacks are triggered and the details of all the unsolicited tags for the given collector is returned. - Step 2
- If an unsolicited tag is added,
ihCollectorToolkitASyncAddTag
method is called and the details of the tag is entered in the collector tag list. - Step 3
- If an unsolicited tag is deleted,
ihCollectorToolkitASyncDeleteTag
method is called and the tag details are removed from the collector tag list.
Example
The following sample helps you understand the given callback functions. The below sample code creates a thread for simulating the Unsolicited tag's collector behavior.
//This structure is the list of Unsolicited Tags.
struct AsyncTagList : public CList<ihInterfaceTKASyncTagInfo*, ihInterfaceTKASyncTagInfo*>
{
virtual ~AsyncTagList()
{
FreeAll();
}
void AddTag(ihInterfaceTKASyncTagInfo* tag)
{
ihInterfaceTKASyncTagInfo* info = new ihInterfaceTKASyncTagInfo;//need to allocate in delegator
memcpy(info, tag, sizeof(ihInterfaceTKASyncTagInfo));
AddTail(info);
}
void FreeAll()
{
while (!IsEmpty())
delete RemoveHead();
}
};
AsyncTagList g_AsyncTags;
??
----------------------------------------------------------------------------
??
/// <summary>
/// Initializes unsolicited tags, by creating dedicated thread
/// <summary>
/// <returns></returns>
int ??????RandomValueSimulator::ihCollectorToolkitASyncInit(void)
{
??????????????CSingleLock lock(&g_Sync, TRUE);
??????????????g_AsyncTags.FreeAll();
??????????????if (!g_AsyncThread)
????????????????????????????g_AsyncThread = AfxBeginThread(TKAsyncReadFunc, this);
??????????????return TRUE;
}
??
----------------------------------------------------------------------------
??
/// <summary>
/// unsolicited tags initialization completed
/// <summary>
/// <returns>TRUE/FALSE</returns>
int ??????RandomValueSimulator::ihCollectorToolkitASyncInitCompleted(void)
{
??????????????return TRUE;
}
??
------------------------------------------------------------------------------
??
/// <summary>
/// Custom Collector Initialization completed and is ready to read data from Source for unsolicited tag
/// <summary>
/// <returns>TRUE/FALSE</returns>
int ??????RandomValueSimulator::ihCollectorToolkitASyncStartReading(void)
{
??????????????InterlockedExchange(&g_DoAsyncRead, TRUE);
??????????????return TRUE;
}
??
------------------------------------------------------------------------------
??
/// <summary>
/// Adds unsolicited tag to the historian
/// <summary>
/// <param name="ASyncTag">ihInterfaceTKAsyncTagInfo pointer</param>
/// <param name="IsCollectorStarting">Current Status of the Collector</param>
/// <returns>TRUE/FALSE</returns>
int ??????RandomValueSimulator::ihCollectorToolkitASyncAddTag(ihInterfaceTKASyncTagInfo *ASyncTag, int IsCollectorStarting)
{
??????????????CSingleLock lock(&g_Sync, TRUE);
??????????????g_AsyncTags.AddTag(ASyncTag);
??????????????return TRUE;
}
??
-------------------------------------------------------------------------------
??
/// <summary>
/// From Clients tools/non-web admin/custom tools, if user deletes a tag, historian updates custom collector saying that tag got deleted.
/// So that, custom collector stops collecting data from source for that tag
/// <summary>
/// <param name="tagId">Tag Identifier</param>
/// <returns>TRUE/FALSE</returns>
int ??????RandomValueSimulator::ihCollectorToolkitASyncDeleteTag(int tagId)
{
??????????????CSingleLock lock(&g_Sync, TRUE);
??????????????POSITION pos = g_AsyncTags.GetHeadPosition();
??????????????while (pos)
??????????????{
????????????????????????????ihInterfaceTKASyncTagInfo * tagInfo = g_AsyncTags.GetAt(pos);
????????????????????????????if (tagInfo->TagId == tagId)
????????????????????????????{
??????????????????????????????????????????g_AsyncTags.RemoveAt(pos);
??????????????????????????????????????????return TRUE;
????????????????????????????} ??????????????
????????????????????????????g_AsyncTags.GetNext(pos);
??????????????}
??????????????return TRUE;
}
??
--------------------------------------------------------------------------------
??
/// <summary>
/// This method called by historian, if it needs to perform any calculations on source data. This method is only usefull in "Calculation collector" way of collection for unsolicited tags
/// <summary>
/// <param name="StartTime"> start time for reload</param>
/// <param name="EndTime"> end time for reload</param>
/// <returns>TRUE/FALSE</returns>
int RandomValueSimulator::ihCollectorToolkitASyncReload(ihTKTimeStruct *StartTime, ihTKTimeStruct *EndTime)
{
??????????????return CCollectorDelegator::ihCollectorToolkitASyncReload(StartTime, EndTime);
}
??
-------------------------------------------------------------------------------
??
/// <summary>
/// Unsolicited dedicated thread corresponding method. Here all unsolicited tags get data from source(usually, way should be source notification to historian) and sends to historian
/// <summary>
/// <param name="param"> Collector instance </param>
UINT RandomValueSimulator::TKAsyncReadFunc(void* param)
{
??????????????POSITION pos = NULL;
??????????????RandomValueSimulator* pColl = (RandomValueSimulator*) param;
??????????????while (TRUE)
??????????????{
????????????????????????????if (g_DoAsyncRead)
????????????????????????????{
??????????????????????????????????????????CSingleLock lock(&g_Sync, TRUE);
??????????????????????????????????????????if (!g_AsyncTags.IsEmpty())
??????????????????????????????????????????{
??????????????????????????????????????????????????????// gets each unsolicited tag into ihInterfaceTKAsyncTagInfo object
??????????????????????????????????????????????????????if (!pos)
????????????????????????????????????????????????????????????????????pos = g_AsyncTags.GetHeadPosition();
??????????????????????????????????????????????????????ihInterfaceTKASyncTagInfo* tag = g_AsyncTags.GetNext(pos);
??????????????????????????????????????????????????????int numTags = 1;
??????????????????????????????????????????????????????ihInterfaceTKDataInfo data;
??????????????????????????????????????????????????????memset(&data, 0, sizeof(ihInterfaceTKDataInfo));
??????????????????????????????????????????????????????data.Tag = pColl->TKStrdup(tag->Tag);
??????????????????????????????????????????????????????data.DataProp.ValueDataType = tag->DataType; ????????????????????????????????????????????????
??????????????????????????????????????????????????????data.DataProp.TimeStamp = pColl->TKGetSystemTime();
??????????????????????????????????????????????????????// gets data for selected tag
??????????????????????????????????????????????????????pColl->ihCollectorToolkitGetData(0, 0, 0, numTags, NULL, &data);
??????????????????????????????????????????????????????unsigned long collectionTime = (unsigned long)time(0);
??????????????????????????????????????????????????????int tagId = tag->TagId;
????????????????????????????????????????????????????????ihInterfaceTKASyncData asyncData;
??????????????????????????????????????????????????????memset(&asyncData, 0, sizeof(ihInterfaceTKASyncData));
??????????????????????????????????????????????????????asyncData.NumValues = numTags;
??????????????????????????????????????????????????????asyncData.TagIds = &tagId;
??????????????????????????????????????????????????????asyncData.Values = &data.DataProp;
??????????????????????????????????????????????????????asyncData.CollectionTimes = &collectionTime;
??????????????????????????????????????????????????????// sends to historian
??????????????????????????????????????????????????????pColl->ihCollectorToolkitDataCallback(&asyncData);
??????????????????????????????????????????}
??????????????????????????????????????????else
??????????????????????????????????????????{
??????????????????????????????????????????????????????pos = NULL;
??????????????????????????????????????????}
????????????????????????????}
????????????????????????????else
????????????????????????????{
??????????????????????????????????????????pos = NULL;
????????????????????????????}
????????????????????????????long sleepTimeInMs = 500 + (rand() % 46) * 100; // sleep from 0.5 to 5 seconds
????????????????????????????Sleep(sleepTimeInMs);
??????????????}
??????????????return 0;
}