This project has moved. For the latest updates, please go here.

problem about "initialization sequence"

Developer
Jul 20, 2012 at 5:00 PM

Hello Ritchie: 
I think I have met a problem in latest version of openPDC. this problem indicates that in openPDC, you cannot initialize different adapters simultaneously, but has to initialize InputAdapter before ActionAdapter. 
my case is like below: 
var routingtable = new RoutingTable();

var inputAdapter = new PhasorMeasurementMapper();
routingtable.InputAdapters.Add(inputAdapter);

var actionAdapter = new SomeActionAdapter(); // has specified some InputMeasurementKeys in the configuration file
routingtable.ActionAdapters.Add(actionAdapter);
you know that, when added into AdapterCollectionBase, the adapter will be initialized in thread pool. so "PhasorMeasurementMapper" and "ActionAdapter" will be initialized in a random order
in "PhasorMeasurementMapper :: LoadDeviceMeasurements", it will load MeasurementKeys, andthe SignalID of those MeasurementKey is loaded from "ActiveMeasurement.SignalID" column
in "AdapterBase::ParseInputMeasurementKeys", it will load InputMeasurementKeys from the connection string. And the SignalIds of those InputMeasurementKeys may or may notcopied from "ActiveMeasurement.SignalID" column
see the codes copied from "AdapterBase::ParseInputMeasurementKeys" below: 
foreach (string item in value.Split(';'))
                    {
                        if (!string.IsNullOrWhiteSpace(item))
                        {
                            if (MeasurementKey.TryParse(item, Guid.Empty, out key))
                            {
                                // Attempt to update empty signal ID if available
                                if (dataSourceAvailable && key.SignalID == Guid.Empty)
                                {
                                    if (dataSource.Tables.Contains(measurementTable))
                                    {
                                        DataRow[] filteredRows = dataSource.Tables[measurementTable].Select(string.Format("ID = '{0}'", key.ToString()));

                                        if (filteredRows.Length > 0)
                                            key.SignalID = filteredRows[0]["SignalID"].ToNonNullString(Guid.Empty.ToString()).ConvertToType<Guid>();
                                    }
                                }
if ActionAdapter's Initialize() is invoked before PhasorMeasurementMapper's Initialize(), then "MeasurementKey.TryParse" cannot find existing SignalId in the cache, so it will use a new GUID as the SignalID. then after the function, since "key.SignalID != empty", so it won't copy the SignalID from "ActiveMeasurement.SignalID" column. 
in such situation, for the same "source::pointid", ActionAdapter and PhasorMeasurementMapper will have different SignalID. since RoutingTable calculate routes based on "ActionAdapter's InputMeasurementKey", while the parsing procedure is based on "PhasorMeasurementMapper's m_definedMeasurements", so it will cause the problem that incoming measurements cannot be forwarded to expected adapters.
however, if PhasorMeasurementMapper's Initialize() is invoked before ActionAdapter's Initialize(), it won't have such problem. because after PhasorMeasurementMapper's Initialize(), it has already store "ActiveMeasurement.SignalID" column into cache. when "ActionAdapter::Initialize" is running, it will load SignalID from cache. so the SignalIDs for both ActionAdapter's InputMeasurementKey and PhasorMeasurementMapper's m_definedMeasurements are consistent with each other. 
so if we don't limit the initialization order of different adapters, the behavior of the application is random, sometimes it works, while sometimes it doesn't (because ActionAdapter cannot get data)
my current solution is playing a trick, which initializes PhasorMeasurementMapper first, and waits for some time, and then initialize ActionAdapter, like below: 
var routingtable = new RoutingTable();

var inputAdapter = new PhasorMeasurementMapper();
routingtable.InputAdapters.Add(inputAdapter);
Thread.Sleep(1000);

var actionAdapter = new SomeActionAdapter(); // has specified some InputMeasurementKeys in the configuration file
routingtable.ActionAdapters.Add(actionAdapter);
so would you please give me your opinion about the problem? Do you think it is a bug of openPDC? or do I use the API in a wrong way?
your opinion is very appreciated. 
thank you very much. 

Coordinator
Jul 20, 2012 at 5:07 PM

Hello cheka,

The openPDC defines a startup operation called PhasorDataSourceValidation which calls a function in TVA.PhasorProtocols.CommonPhasorServices called EstablishMeasurementKeyCache which should prevent the race condition.

Thanks,
Stephen 

Developer
Jul 20, 2012 at 5:55 PM

thanks a lot, staphen. 

I have found the function "EstablishDefaultMeasurementKeyCache", it did prevent the race condition. 

 

I have developed on openPDC platform for more than 2 years. in our project, we don't use the "openPDC application" directly, but build our own application based on "openPDC libraries". I regard our application as a "thinner, light" version of openPDC, which removes advanced functions for enterprise, but easy to master for students.

I have a feeling recently that:

now openPDC becomes more and more huge and complex. Although it can provide advanced functions with optimized performance, however, at the same time, different components inside "openPDC source codes" become more and more tightly coupled, and heavily dependent on shared, global variables. 

I feel more and more difficult to just pick the necessary components to create own "thinner, light" version.