Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Representing heirarchical enumeration

I have a set of enumeration values (fault codes to be precise). The code is a 16 bit unsigned integer. I am looking for a data structure that could represent such an enumeration. A similar question has been asked here: What's the best C# pattern for implementing a hierarchy with an enum?. But this hierarchy is deeper.


Sample enumeration values

Current = 0x2000,
Current_DeviceInputSide = 0x2100,
ShortToEarth = 0x2120,
ShortToEarthInPhase1 = 0x2121,
ShortToEarthInPhase2 = 0x2122,
ShortToEarthInPhase3 = 0x2123


Use case
When the user provides a code then the UI has to display the equivalent meaning of the code with the hierarchy.
For example, if the user provides a value 0x2121 then the UI has to display Short to earth in phase 1 in the current at device input side. The best way to represent this is by using a hierarchical notation: Current : DeviceInputSide : ShortToEarth : ShortToEarthInPhase1.


Competing approaches
I have three competing approaches to represent the enumeration:

  1. Create an enumeration at each level of the hierarchy. Then use a controller class to resolve the name.
  2. Store the enumeration values in an xml and use LINQ to generate the meaning of the code.
  3. Store the enumeration values in an xml. During the application startup. Create a singleton instance to retrieve the meaning. The instance contains a dictionary populated with the values from the xml.


Approach 1
The enumerations:

enum WarnCodes
{
    None= 0x000,
    Current = 0x2000
}

enum WarnCodes_Current
{
    DeviceInputSide = 0x2100,
    DeviceOutputSide = 0x2200
}

enum WarnCodes_Current_DeviceInputSide
{
    ShortToEarth = 0x2120,
    ShortCircuit = 0x2130
}

enum WarnCodes_Current_DeviceInputSide_ShortToEarth 
{
    InPhase1 = 0x2121,
    InPhase2 = 0x2122
}

The controller:

public string GetMeaning(int code)
{
    int bitMask = 0xF000;
    int maskedCode = bitMask & code;
    StringBuilder meaning = new StringBuilder();

    switch (maskedCode)
    {
        case WarnCodes.Current:
            meaning.Append("Current : ");
            bitMask = 0xFF00;
            maskedCode = bitMask & code;
            switch (maskedCode)
            {
                case WarnCodes_Current.DeviceInputSide:
                    meaning.Append("Current : Device Input Side :");
                    ...
                    break;
            }

            break;

            ...
    }
}


Approach 2
The xml to store the enumeration values looks like this

<WarnCodes>
  <code hex="2000" meaning="Current">
    <code hex="2100" meaning="Current, Device Input side">
      <code hex="2120" meaning="Short to Earth">
        <code hex="2121" meaning="Short to earth in Phase L1"/>
        <code hex="2122" meaning="Short to earth in Phase L2"/>
      </code>
    </code>
  </code>
</WarnCodes>
And the method used to query the codes is:
XElement rootElement = XElement.Load(settingsFilePath);
public string GetHierarchicalMeaning(int code)
{
    XElement rootElement = XElement.Load(warnCodesFilePath);

    List<string> meanings = new List();
    StringBuilder stringBuilder = new StringBuilder();
    IEnumerable<XElement> elements;

    elements = from el in rootElement.Descendants("code")
               where (string)el.Attribute("hex") == code.ToString("X")
               select el;

    XElement element = elements.First();

    while (element.Parent != null)
    {
        meanings.Add(element.Attribute("meaning").Value);
        element = element.Parent;
    }

    meanings.Reverse();

    foreach (string meaning in meanings)
    {
        stringBuilder.AppendFormat("{0} : ", meaning);
    }

    return stringBuilder.ToString().Trim().TrimEnd(':').Trim();
}


Approach 3
The xml to store the enumeration values is same as in Approach 2. The dictionary is populated from the xml by GetChildren().

private Dictionary<int, WarnCodeValue> warnCodesDictionary;

public void Initialize()
{
    XElement rootElement = XElement.Load(settingsFilePath);
    warnCodesDictionary = GetChildren(rootElement);
}

private Dictionary<int, WarnCodeValue> GetChildren(XElement element)
{
    if (element.Descendants().Count() > 0)
    {
        Dictionary<int, WarnCodeValue> childNodeDictionary = new Dictionary();

        foreach (XElement childElement in element.Elements())
        {
            int hex = Convert.ToInt32(childElement.Attribute("hex").Value, 16);
            string meaning = childElement.Attribute("meaning").Value;

            Dictionary<int, WarnCodeValue> dictionary = GetChildren(childElement);
            WarnCodeValue warnCodeValue;
            if (dictionary == null)
            {
                warnCodeValue = new WarnCodeValue() {Meaning = meaning};
            }
            else
            {
                warnCodeValue = new WarnCodeValue() {Meaning = meaning, ChildNodes = dictionary};
            }

            childNodeDictionary.Add(hex, warnCodeValue);
        }

        return childNodeDictionary;
    }

    return null;
}

The meanings are retrieved using GetHierarchicalMeaning():

public string GetHierarchicalMeaning(int code)
{
    StringBuilder stringBuilder = new StringBuilder();

    int firstLevel = code & 0xF000;
    int secondLevel = code & 0xFF00;
    int thirdLevel = code & 0xFFF0;

    if(warnCodesDictionary.ContainsKey(firstLevel))
    {
        stringBuilder.AppendFormat("{0} : ", warnCodesDictionary[firstLevel].Meaning);
        if (warnCodesDictionary[firstLevel].ChildNodes != null && 
            warnCodesDictionary[firstLevel].ChildNodes.ContainsKey(secondLevel))
        {
            stringBuilder.AppendFormat("{0} : ", warnCodesDictionary[firstLevel].ChildNodes[secondLevel].Meaning);

            if (warnCodesDictionary[firstLevel].ChildNodes[secondLevel].ChildNodes != null &&
                warnCodesDictionary[firstLevel].ChildNodes[secondLevel].ChildNodes.ContainsKey(thirdLevel))
            {
                stringBuilder.AppendFormat("{0} : ", 
                    warnCodesDictionary[firstLevel].ChildNodes[secondLevel].ChildNodes[thirdLevel].Meaning);

                if (warnCodesDictionary[firstLevel].ChildNodes[secondLevel].ChildNodes[thirdLevel].ChildNodes != null &&
                    warnCodesDictionary[firstLevel].ChildNodes[secondLevel].ChildNodes[thirdLevel].ChildNodes.ContainsKey(code))
                {
                    stringBuilder.AppendFormat("{0} : ", 
                        warnCodesDictionary[firstLevel].ChildNodes[secondLevel].ChildNodes[thirdLevel].ChildNodes[code].Meaning);
                }
            }
        }
    }
}

The WarnCodeValue class:

class WarnCodeValue
{
    public string Meaning
    { get; set; }

    public Dictionary<int, WarnCodeValue> ChildNodes { get; set; }
}


Questions

  1. Which of the above 3 approaches is better from a performance point of view?
  2. Are there any other approaches for representing the enumeration?
  3. Any improvements to the code?
like image 863
Devendra D. Chavan Avatar asked Feb 18 '11 12:02

Devendra D. Chavan


1 Answers

Consider using classes instead of enums, you then use a singleton for each value and can use the type system to build a tree, including virtual methods to produce error txt etc. (This can sometimes be a good option, but can also lead you into lots of problems if it does not fit well)

like image 135
Ian Ringrose Avatar answered Oct 19 '22 09:10

Ian Ringrose