/*
 * Go2OutputChannels.c
 * 
 * Gocator system communication library
 * Copyright (C) 2010 by LMI Technologies Inc.
 * 
 * Licensed under The MIT License.
 * Redistributions of files must retain the above copyright notice.
 */
#include <z/zGo2OutputChannels.h>
#include <z/zGo2System.h>
#include <z/zGo2Api.h>
#include <string.h>

/*
 * Go2Ethernet Class 
 */

GO2_EXPORT(Go2Status) Go2Ethernet_Construct(Go2Ethernet* ethernet, Go2Object system)
{
    Go2Ethernet output = zNIL; 
    Go2Status status = GO2_OK; 

    zCHECK(zCalloc(&output, 1, sizeof(Go2EthernetClass))); 

    if (!zSUCCESS(status = Go2Ethernet_Init(output, system)))
    {
        zCHECK(Go2Object_Destroy(output)); 
        return status; 
    }
    
    *ethernet = output; 

    return GO2_OK; 
}

GO2_EXPORT(Go2Status) Go2Ethernet_Init(Go2Ethernet ethernet, Go2System system)
{
    Go2EthernetClass* obj = zHANDLE_POINTER(ethernet);

    zCHECK(Go2Object_Init(ethernet, GO2_TYPE_ETHERNET)); 

    obj->Release = Go2Ethernet_VRelease; 
    obj->system = system; 

    zCHECK(zList_Construct(&obj->videoOptions, sizeof(z32u), 0)); 
    zCHECK(zList_Construct(&obj->videoSources, sizeof(z32u), 0)); 

    zCHECK(zList_Construct(&obj->rangeOptions, sizeof(z32u), 0)); 
    zCHECK(zList_Construct(&obj->rangeSources, sizeof(z32u), 0)); 

    zCHECK(zList_Construct(&obj->rangeIntensityOptions, sizeof(z32u), 0)); 
    zCHECK(zList_Construct(&obj->rangeIntensitySources, sizeof(z32u), 0)); 

    zCHECK(zList_Construct(&obj->profileOptions, sizeof(z32u), 0)); 
    zCHECK(zList_Construct(&obj->profileSources, sizeof(z32u), 0)); 

    zCHECK(zList_Construct(&obj->rawProfileOptions, sizeof(z32u), 0)); 
    zCHECK(zList_Construct(&obj->rawProfileSources, sizeof(z32u), 0)); 

    zCHECK(zList_Construct(&obj->profileIntensityOptions, sizeof(z32u), 0)); 
    zCHECK(zList_Construct(&obj->profileIntensitySources, sizeof(z32u), 0)); 

    zCHECK(zList_Construct(&obj->rawProfileIntensityOptions, sizeof(z32u), 0)); 
    zCHECK(zList_Construct(&obj->rawProfileIntensitySources, sizeof(z32u), 0));

    zCHECK(zList_Construct(&obj->partOptions, sizeof(z32u), 0)); 
    zCHECK(zList_Construct(&obj->partSources, sizeof(z32u), 0)); 

    zCHECK(zList_Construct(&obj->partIntensityOptions, sizeof(z32u), 0)); 
    zCHECK(zList_Construct(&obj->partIntensitySources, sizeof(z32u), 0)); 

    zCHECK(zList_Construct(&obj->valueOptions, sizeof(z32u), 0)); 
    zCHECK(zList_Construct(&obj->valueSources, sizeof(z32u), 0)); 

    zCHECK(zList_Construct(&obj->decisionOptions, sizeof(z32u), 0)); 
    zCHECK(zList_Construct(&obj->decisionSources, sizeof(z32u), 0)); 

    return GO2_OK; 
}

GO2_EXPORT(Go2Status) Go2Ethernet_VRelease(Go2Ethernet ethernet)
{
    Go2EthernetClass* obj = zHANDLE_POINTER(ethernet);
    
    zCHECK(zList_Destroy(obj->videoOptions)); 
    zCHECK(zList_Destroy(obj->videoSources)); 

    zCHECK(zList_Destroy(obj->rangeOptions)); 
    zCHECK(zList_Destroy(obj->rangeSources)); 

    zCHECK(zList_Destroy(obj->rangeIntensityOptions)); 
    zCHECK(zList_Destroy(obj->rangeIntensitySources)); 

    zCHECK(zList_Destroy(obj->profileOptions)); 
    zCHECK(zList_Destroy(obj->profileSources)); 

    zCHECK(zList_Destroy(obj->rawProfileOptions)); 
    zCHECK(zList_Destroy(obj->rawProfileSources)); 

    zCHECK(zList_Destroy(obj->profileIntensityOptions)); 
    zCHECK(zList_Destroy(obj->profileIntensitySources)); 

    zCHECK(zList_Destroy(obj->rawProfileIntensityOptions)); 
    zCHECK(zList_Destroy(obj->rawProfileIntensitySources)); 

    zCHECK(zList_Destroy(obj->partOptions)); 
    zCHECK(zList_Destroy(obj->partSources)); 

    zCHECK(zList_Destroy(obj->partIntensityOptions)); 
    zCHECK(zList_Destroy(obj->partIntensitySources)); 

    zCHECK(zList_Destroy(obj->valueOptions)); 
    zCHECK(zList_Destroy(obj->valueSources)); 

    zCHECK(zList_Destroy(obj->decisionOptions)); 
    zCHECK(zList_Destroy(obj->decisionSources)); 

    if (!zIS_NULL(obj->asciiCustomDataFormat)) 
    {
        zCHECK(zFree(obj->asciiCustomDataFormat)); 
    }

    zCHECK(Go2Object_VRelease(ethernet));  
        
    return GO2_OK; 
}

GO2_EXPORT(Go2Status) Go2Ethernet_Read(Go2Ethernet ethernet, zXml xml, zXmlItem item)
{
    Go2EthernetClass* obj = zHANDLE_POINTER(ethernet);
    zChar* text = zNULL; 
    z32u textCapacity = GO2_OUTPUT_SOURCE_TEXT_CAPACITY; 

    zEXCEPT_TRY
    {
        zEXCEPT_CHECK(zCalloc(&text, textCapacity, sizeof(zChar))); 

        zEXCEPT_CHECK(zXml_ChildText(xml, item, "VideoOptions", text, textCapacity)); 
        zEXCEPT_CHECK(Go2OptionList_ParseList(text, obj->videoOptions));  

        zEXCEPT_CHECK(zXml_ChildText(xml, item, "Video", text, textCapacity)); 
        zEXCEPT_CHECK(Go2OptionList_ParseList(text, obj->videoSources));  

        zEXCEPT_CHECK(zXml_ChildText(xml, item, "RangeOptions", text, textCapacity)); 
        zEXCEPT_CHECK(Go2OptionList_ParseList(text, obj->rangeOptions));  

        zEXCEPT_CHECK(zXml_ChildText(xml, item, "Range", text, textCapacity)); 
        zEXCEPT_CHECK(Go2OptionList_ParseList(text, obj->rangeSources));  

        zEXCEPT_CHECK(zXml_ChildText(xml, item, "RangeIntensityOptions", text, textCapacity)); 
        zEXCEPT_CHECK(Go2OptionList_ParseList(text, obj->rangeIntensityOptions));  

        zEXCEPT_CHECK(zXml_ChildText(xml, item, "RangeIntensity", text, textCapacity)); 
        zEXCEPT_CHECK(Go2OptionList_ParseList(text, obj->rangeIntensitySources));  

        zEXCEPT_CHECK(zXml_ChildText(xml, item, "ProfileOptions", text, textCapacity)); 
        zEXCEPT_CHECK(Go2OptionList_ParseList(text, obj->profileOptions));  

        zEXCEPT_CHECK(zXml_ChildText(xml, item, "Profile", text, textCapacity)); 
        zEXCEPT_CHECK(Go2OptionList_ParseList(text, obj->profileSources));  

        zEXCEPT_CHECK(zXml_ChildText(xml, item, "RawProfileOptions", text, textCapacity)); 
        zEXCEPT_CHECK(Go2OptionList_ParseList(text, obj->rawProfileOptions));  

        zEXCEPT_CHECK(zXml_ChildText(xml, item, "RawProfile", text, textCapacity)); 
        zEXCEPT_CHECK(Go2OptionList_ParseList(text, obj->rawProfileSources));  

        zEXCEPT_CHECK(zXml_ChildText(xml, item, "ProfileIntensityOptions", text, textCapacity)); 
        zEXCEPT_CHECK(Go2OptionList_ParseList(text, obj->profileIntensityOptions));  

        zEXCEPT_CHECK(zXml_ChildText(xml, item, "ProfileIntensity", text, textCapacity)); 
        zEXCEPT_CHECK(Go2OptionList_ParseList(text, obj->profileIntensitySources));  

        zEXCEPT_CHECK(zXml_ChildText(xml, item, "RawProfileIntensityOptions", text, textCapacity)); 
        zEXCEPT_CHECK(Go2OptionList_ParseList(text, obj->rawProfileIntensityOptions));  

        zEXCEPT_CHECK(zXml_ChildText(xml, item, "RawProfileIntensity", text, textCapacity)); 
        zEXCEPT_CHECK(Go2OptionList_ParseList(text, obj->rawProfileIntensitySources));

        zEXCEPT_CHECK(zXml_ChildText(xml, item, "PartOptions", text, textCapacity)); 
        zEXCEPT_CHECK(Go2OptionList_ParseList(text, obj->partOptions));  

        zEXCEPT_CHECK(zXml_ChildText(xml, item, "Part", text, textCapacity)); 
        zEXCEPT_CHECK(Go2OptionList_ParseList(text, obj->partSources));  

        zEXCEPT_CHECK(zXml_ChildText(xml, item, "PartIntensityOptions", text, textCapacity)); 
        zEXCEPT_CHECK(Go2OptionList_ParseList(text, obj->partIntensityOptions)); 

        zEXCEPT_CHECK(zXml_ChildText(xml, item, "PartIntensity", text, textCapacity)); 
        zEXCEPT_CHECK(Go2OptionList_ParseList(text, obj->partIntensitySources));  

        zEXCEPT_CHECK(zXml_ChildText(xml, item, "ValueOptions", text, textCapacity)); 
        zEXCEPT_CHECK(Go2OptionList_ParseList(text, obj->valueOptions));  

        zEXCEPT_CHECK(zXml_ChildText(xml, item, "Value", text, textCapacity)); 
        zEXCEPT_CHECK(Go2OptionList_ParseList(text, obj->valueSources));  

        zEXCEPT_CHECK(zXml_ChildText(xml, item, "DecisionOptions", text, textCapacity)); 
        zEXCEPT_CHECK(Go2OptionList_ParseList(text, obj->decisionOptions));  

        zEXCEPT_CHECK(zXml_ChildText(xml, item, "Decision", text, textCapacity)); 
        zEXCEPT_CHECK(Go2OptionList_ParseList(text, obj->decisionSources));  

        zEXCEPT_CHECK(zXml_Child32s(xml, item, "Protocol", (z32s*)&obj->protocol));

        zEXCEPT_CHECK(zXml_Child32u(xml, item, "AsciiControlPort", (z32u*)&obj->asciiControlPort));
        zEXCEPT_CHECK(zXml_Child32u(xml, item, "AsciiDataPort", (z32u*)&obj->asciiDataPort));
        zEXCEPT_CHECK(zXml_Child32u(xml, item, "AsciiHealthPort", (z32u*)&obj->asciiHealthPort));
        zEXCEPT_CHECK(zXml_Child32s(xml, item, "AsciiOperation", (z32s*)&obj->asciiOperation));
        zEXCEPT_CHECK(zXml_ChildText(xml, item, "AsciiDelimiter", obj->asciiDelimiter, zCOUNT(obj->asciiDelimiter))); 
        zEXCEPT_CHECK(zXml_ChildText(xml, item, "AsciiTerminator", obj->asciiTerminator, zCOUNT(obj->asciiTerminator))); 
        zEXCEPT_CHECK(zXml_ChildText(xml, item, "AsciiInvalidValue", obj->asciiInvalid, zCOUNT(obj->asciiInvalid))); 
        zEXCEPT_CHECK(zXml_Child32s(xml, item, "AsciiCustomFormatEnabled", &obj->asciiCustomFormatEnabled));

        zCHECK(zFree(obj->asciiCustomDataFormat)); 
        obj->asciiCustomDataFormat = zNULL; 
        zCHECK(zXml_ChildString(xml, item, "AsciiCustomDataFormat", &obj->asciiCustomDataFormat)); 

        zEXCEPT_CHECK(zXml_Child32s(xml, item, "EIPBufferEnabled", &obj->eipBufferEnabled));
        zEXCEPT_CHECK(zXml_Child32s(xml, item, "ModbusBufferEnabled", &obj->modbusBufferEnabled));
    }
    zEXCEPT_CATCH(zNULL) {}
    zEXCEPT_FINALLY
    {
        zEXCEPT_SET(zFree(text)); 
        zEXCEPT_CLOSE(); 
    }

    return GO2_OK; 
}

GO2_EXPORT(Go2Status) Go2Ethernet_Write(Go2Ethernet ethernet, zXml xml, zXmlItem item)
{
    Go2EthernetClass* obj = zHANDLE_POINTER(ethernet);
    zChar* text = zNULL; 
    z32u textCapacity = GO2_OUTPUT_SOURCE_TEXT_CAPACITY; 

    zEXCEPT_TRY
    {
        zEXCEPT_CHECK(zCalloc(&text, textCapacity, sizeof(zChar))); 

        zEXCEPT_CHECK(Go2OptionList_Format(zList_Data(obj->videoSources), zList_Count(obj->videoSources), text, textCapacity)); 
        zEXCEPT_CHECK(zXml_SetChildText(xml, item, "Video", text)); 

        zEXCEPT_CHECK(Go2OptionList_Format(zList_Data(obj->rangeSources), zList_Count(obj->rangeSources), text, textCapacity)); 
        zEXCEPT_CHECK(zXml_SetChildText(xml, item, "Range", text)); 

        zEXCEPT_CHECK(Go2OptionList_Format(zList_Data(obj->rangeIntensitySources), zList_Count(obj->rangeIntensitySources), text, textCapacity)); 
        zEXCEPT_CHECK(zXml_SetChildText(xml, item, "RangeIntensity", text)); 

        zEXCEPT_CHECK(Go2OptionList_Format(zList_Data(obj->profileSources), zList_Count(obj->profileSources), text, textCapacity)); 
        zEXCEPT_CHECK(zXml_SetChildText(xml, item, "Profile", text)); 

        zEXCEPT_CHECK(Go2OptionList_Format(zList_Data(obj->rawProfileSources), zList_Count(obj->rawProfileSources), text, textCapacity)); 
        zEXCEPT_CHECK(zXml_SetChildText(xml, item, "RawProfile", text)); 

        zEXCEPT_CHECK(Go2OptionList_Format(zList_Data(obj->profileIntensitySources), zList_Count(obj->profileIntensitySources), text, textCapacity)); 
        zEXCEPT_CHECK(zXml_SetChildText(xml, item, "ProfileIntensity", text)); 

        zEXCEPT_CHECK(Go2OptionList_Format(zList_Data(obj->rawProfileIntensitySources), zList_Count(obj->rawProfileIntensitySources), text, textCapacity)); 
        zEXCEPT_CHECK(zXml_SetChildText(xml, item, "RawProfileIntensity", text));

        zEXCEPT_CHECK(Go2OptionList_Format(zList_Data(obj->partSources), zList_Count(obj->partSources), text, textCapacity)); 
        zEXCEPT_CHECK(zXml_SetChildText(xml, item, "Part", text)); 

        zEXCEPT_CHECK(Go2OptionList_Format(zList_Data(obj->partIntensitySources), zList_Count(obj->partIntensitySources), text, textCapacity)); 
        zEXCEPT_CHECK(zXml_SetChildText(xml, item, "PartIntensity", text)); 

        zEXCEPT_CHECK(Go2OptionList_Format(zList_Data(obj->valueSources), zList_Count(obj->valueSources), text, textCapacity)); 
        zEXCEPT_CHECK(zXml_SetChildText(xml, item, "Value", text)); 

        zEXCEPT_CHECK(Go2OptionList_Format(zList_Data(obj->decisionSources), zList_Count(obj->decisionSources), text, textCapacity)); 
        zEXCEPT_CHECK(zXml_SetChildText(xml, item, "Decision", text)); 

        zEXCEPT_CHECK(zXml_SetChild32s(xml, item, "Protocol", obj->protocol)); 

        zEXCEPT_CHECK(zXml_SetChild32u(xml, item, "AsciiControlPort", obj->asciiControlPort));
        zEXCEPT_CHECK(zXml_SetChild32u(xml, item, "AsciiDataPort", obj->asciiDataPort));
        zEXCEPT_CHECK(zXml_SetChild32u(xml, item, "AsciiHealthPort", obj->asciiHealthPort));
        zEXCEPT_CHECK(zXml_SetChild32s(xml, item, "AsciiOperation", obj->asciiOperation));
        zEXCEPT_CHECK(zXml_SetChildText(xml, item, "AsciiDelimiter", obj->asciiDelimiter)); 
        zEXCEPT_CHECK(zXml_SetChildText(xml, item, "AsciiTerminator", obj->asciiTerminator)); 
        zEXCEPT_CHECK(zXml_SetChildText(xml, item, "AsciiInvalidValue", obj->asciiInvalid)); 
        zEXCEPT_CHECK(zXml_SetChild32s(xml, item, "AsciiCustomFormatEnabled", obj->asciiCustomFormatEnabled));
        zEXCEPT_CHECK(zXml_SetChildText(xml, item, "AsciiCustomDataFormat", obj->asciiCustomDataFormat)); 

        zEXCEPT_CHECK(zXml_SetChild32s(xml, item, "EIPBufferEnabled", obj->eipBufferEnabled));
        zEXCEPT_CHECK(zXml_SetChild32s(xml, item, "ModbusBufferEnabled", obj->modbusBufferEnabled));
    }
    zEXCEPT_CATCH(zNULL) {}
    zEXCEPT_FINALLY
    {
        zEXCEPT_SET(zFree(text)); 
        zEXCEPT_CLOSE(); 
    }

    return GO2_OK; 
}

GO2_EXPORT(Go2UInt32) Go2Ethernet_OptionCount(Go2Ethernet ethernet, Go2OutputDataSourceType type)
{
    Go2EthernetClass* obj = zHANDLE_POINTER(ethernet);

    switch (type)
    {
    case GO2_OUTPUT_DATA_SOURCE_TYPE_RANGE:                 return zList_Count(obj->rangeOptions); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_RANGE_INTENSITY:       return zList_Count(obj->rangeIntensityOptions); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_PROFILE:               return zList_Count(obj->profileOptions); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_RAW_PROFILE:           return zList_Count(obj->rawProfileOptions); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_PROFILE_INTENSITY:     return zList_Count(obj->profileIntensityOptions); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_RAW_PROFILE_INTENSITY: return zList_Count(obj->rawProfileIntensityOptions); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_PART:                  return zList_Count(obj->partOptions); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_PART_INTENSITY:        return zList_Count(obj->partIntensityOptions);
    case GO2_OUTPUT_DATA_SOURCE_TYPE_VIDEO:                 return zList_Count(obj->videoOptions); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_VALUE:                 return zList_Count(obj->valueOptions); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_DECISION:              return zList_Count(obj->decisionOptions); 
    default:                                                return 0; 
    }
}

GO2_EXPORT(Go2UInt32) Go2Ethernet_OptionAt(Go2Ethernet ethernet, Go2OutputDataSourceType type, Go2UInt32 index)
{
    Go2EthernetClass* obj = zHANDLE_POINTER(ethernet);
    z32u* option = zNULL; 

    switch (type)
    {
    case GO2_OUTPUT_DATA_SOURCE_TYPE_RANGE:                 option = zList_At(obj->rangeOptions, index);                break;
    case GO2_OUTPUT_DATA_SOURCE_TYPE_RANGE_INTENSITY:       option = zList_At(obj->rangeIntensityOptions, index);       break;
    case GO2_OUTPUT_DATA_SOURCE_TYPE_PROFILE:               option = zList_At(obj->profileOptions, index);              break;
    case GO2_OUTPUT_DATA_SOURCE_TYPE_RAW_PROFILE:           option = zList_At(obj->rawProfileOptions, index);           break;
    case GO2_OUTPUT_DATA_SOURCE_TYPE_PROFILE_INTENSITY:     option = zList_At(obj->profileIntensityOptions, index);     break;
    case GO2_OUTPUT_DATA_SOURCE_TYPE_RAW_PROFILE_INTENSITY: option = zList_At(obj->rawProfileIntensityOptions, index);  break;
    case GO2_OUTPUT_DATA_SOURCE_TYPE_PART:                  option = zList_At(obj->partOptions, index);                 break;
    case GO2_OUTPUT_DATA_SOURCE_TYPE_PART_INTENSITY:        option = zList_At(obj->partIntensityOptions, index);        break;
    case GO2_OUTPUT_DATA_SOURCE_TYPE_VIDEO:                 option = zList_At(obj->videoOptions, index);                break;
    case GO2_OUTPUT_DATA_SOURCE_TYPE_VALUE:                 option = zList_At(obj->valueOptions, index);                break;
    case GO2_OUTPUT_DATA_SOURCE_TYPE_DECISION:              option = zList_At(obj->decisionOptions, index);             break;
    default:                                                option = zNULL;                                             break;
    }

    return !zIS_NULL(option) ? *option : 0; 
}

GO2_EXPORT(Go2UInt32) Go2Ethernet_SourceCount(Go2Ethernet ethernet, Go2OutputDataSourceType type)
{
    Go2EthernetClass* obj = zHANDLE_POINTER(ethernet);

    switch (type)
    {
    case GO2_OUTPUT_DATA_SOURCE_TYPE_RANGE:                 return zList_Count(obj->rangeSources); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_RANGE_INTENSITY:       return zList_Count(obj->rangeIntensitySources); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_PROFILE:               return zList_Count(obj->profileSources); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_RAW_PROFILE:           return zList_Count(obj->rawProfileSources); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_PROFILE_INTENSITY:     return zList_Count(obj->profileIntensitySources); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_RAW_PROFILE_INTENSITY: return zList_Count(obj->rawProfileIntensitySources); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_PART:                  return zList_Count(obj->partSources); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_PART_INTENSITY:        return zList_Count(obj->partIntensitySources); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_VIDEO:                 return zList_Count(obj->videoSources); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_VALUE:                 return zList_Count(obj->valueSources); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_DECISION:              return zList_Count(obj->decisionSources); 
    default:                                                return 0; 
    }
}

GO2_EXPORT(Go2UInt32) Go2Ethernet_SourceAt(Go2Ethernet ethernet, Go2OutputDataSourceType type, Go2UInt32 index)
{
    Go2EthernetClass* obj = zHANDLE_POINTER(ethernet);
    z32u* option = zNULL;

    switch (type)
    {
    case GO2_OUTPUT_DATA_SOURCE_TYPE_RANGE:                 option = zList_At(obj->rangeSources, index);                break;
    case GO2_OUTPUT_DATA_SOURCE_TYPE_RANGE_INTENSITY:       option = zList_At(obj->rangeIntensitySources, index);       break;
    case GO2_OUTPUT_DATA_SOURCE_TYPE_PROFILE:               option = zList_At(obj->profileSources, index);              break;
    case GO2_OUTPUT_DATA_SOURCE_TYPE_RAW_PROFILE:           option = zList_At(obj->rawProfileSources, index);           break;
    case GO2_OUTPUT_DATA_SOURCE_TYPE_PROFILE_INTENSITY:     option = zList_At(obj->profileIntensitySources, index);     break;
    case GO2_OUTPUT_DATA_SOURCE_TYPE_RAW_PROFILE_INTENSITY: option = zList_At(obj->rawProfileIntensitySources, index);  break;
    case GO2_OUTPUT_DATA_SOURCE_TYPE_PART:                  option = zList_At(obj->partSources, index);                 break;
    case GO2_OUTPUT_DATA_SOURCE_TYPE_PART_INTENSITY:        option = zList_At(obj->partIntensitySources, index);        break;
    case GO2_OUTPUT_DATA_SOURCE_TYPE_VIDEO:                 option = zList_At(obj->videoSources, index);                break;
    case GO2_OUTPUT_DATA_SOURCE_TYPE_VALUE:                 option = zList_At(obj->valueSources, index);                break;
    case GO2_OUTPUT_DATA_SOURCE_TYPE_DECISION:              option = zList_At(obj->decisionSources, index);             break;
    default:                                                option = zNULL;                                             break;
    }

    return !zIS_NULL(option) ? *option : 0; 
}

GO2_EXPORT(Go2Status) Go2Ethernet_AddSource(Go2Ethernet ethernet, Go2OutputDataSourceType type, Go2UInt32 sourceId)
{
    Go2EthernetClass* obj = zHANDLE_POINTER(ethernet);

    switch (type)
    {
    case GO2_OUTPUT_DATA_SOURCE_TYPE_RANGE:                 return Go2System_AddOption(obj->system, obj->rangeSources, sourceId, zList_Data(obj->rangeOptions), zList_Count(obj->rangeOptions)); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_RANGE_INTENSITY:       return Go2System_AddOption(obj->system, obj->rangeIntensitySources, sourceId, zList_Data(obj->rangeIntensityOptions), zList_Count(obj->rangeIntensityOptions)); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_PROFILE:               return Go2System_AddOption(obj->system, obj->profileSources, sourceId, zList_Data(obj->profileOptions), zList_Count(obj->profileOptions)); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_RAW_PROFILE:           return Go2System_AddOption(obj->system, obj->rawProfileSources, sourceId, zList_Data(obj->rawProfileOptions), zList_Count(obj->rawProfileOptions)); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_PROFILE_INTENSITY:     return Go2System_AddOption(obj->system, obj->profileIntensitySources, sourceId, zList_Data(obj->profileIntensityOptions), zList_Count(obj->profileIntensityOptions)); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_RAW_PROFILE_INTENSITY: return Go2System_AddOption(obj->system, obj->rawProfileIntensitySources, sourceId, zList_Data(obj->rawProfileIntensityOptions), zList_Count(obj->rawProfileIntensityOptions)); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_PART:                  return Go2System_AddOption(obj->system, obj->partSources, sourceId, zList_Data(obj->partOptions), zList_Count(obj->partOptions)); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_PART_INTENSITY:        return Go2System_AddOption(obj->system, obj->partIntensitySources, sourceId, zList_Data(obj->partIntensityOptions), zList_Count(obj->partIntensityOptions)); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_VIDEO:                 return Go2System_AddOption(obj->system, obj->videoSources, sourceId, zList_Data(obj->videoOptions), zList_Count(obj->videoOptions)); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_VALUE:                 return Go2System_AddOption(obj->system, obj->valueSources, sourceId, zList_Data(obj->valueOptions), zList_Count(obj->valueOptions)); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_DECISION:              return Go2System_AddOption(obj->system, obj->decisionSources, sourceId, zList_Data(obj->decisionOptions), zList_Count(obj->decisionOptions)); 
    default:                                                return zERROR_PARAMETER; 
    }
}

GO2_EXPORT(Go2Status) Go2Ethernet_RemoveSource(Go2Ethernet ethernet, Go2OutputDataSourceType type, Go2UInt32 index)
{
    Go2EthernetClass* obj = zHANDLE_POINTER(ethernet);

    switch (type)
    {
    case GO2_OUTPUT_DATA_SOURCE_TYPE_RANGE:                 return Go2System_RemoveOption(obj->system, obj->rangeSources, index); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_RANGE_INTENSITY:       return Go2System_RemoveOption(obj->system, obj->rangeIntensitySources, index); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_PROFILE:               return Go2System_RemoveOption(obj->system, obj->profileSources, index); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_RAW_PROFILE:           return Go2System_RemoveOption(obj->system, obj->rawProfileSources, index); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_PROFILE_INTENSITY:     return Go2System_RemoveOption(obj->system, obj->profileIntensitySources, index); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_RAW_PROFILE_INTENSITY: return Go2System_RemoveOption(obj->system, obj->rawProfileIntensitySources, index); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_PART:                  return Go2System_RemoveOption(obj->system, obj->partSources, index); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_PART_INTENSITY:        return Go2System_RemoveOption(obj->system, obj->partIntensitySources, index); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_VIDEO:                 return Go2System_RemoveOption(obj->system, obj->videoSources, index); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_VALUE:                 return Go2System_RemoveOption(obj->system, obj->valueSources, index); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_DECISION:              return Go2System_RemoveOption(obj->system, obj->decisionSources, index); 
    default:                                                return zERROR_PARAMETER; 
    }
}

GO2_EXPORT(Go2Status) Go2Ethernet_ClearSources(Go2Ethernet ethernet, Go2OutputDataSourceType type)
{
    Go2EthernetClass* obj = zHANDLE_POINTER(ethernet);

    switch (type)
    {
    case GO2_OUTPUT_DATA_SOURCE_TYPE_RANGE:                 return Go2System_ClearOptions(obj->system, obj->rangeSources); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_RANGE_INTENSITY:       return Go2System_ClearOptions(obj->system, obj->rangeIntensitySources); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_PROFILE:               return Go2System_ClearOptions(obj->system, obj->profileSources); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_RAW_PROFILE:           return Go2System_ClearOptions(obj->system, obj->rawProfileSources); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_PROFILE_INTENSITY:     return Go2System_ClearOptions(obj->system, obj->profileIntensitySources); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_RAW_PROFILE_INTENSITY: return Go2System_ClearOptions(obj->system, obj->rawProfileIntensitySources); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_PART:                  return Go2System_ClearOptions(obj->system, obj->partSources); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_PART_INTENSITY:        return Go2System_ClearOptions(obj->system, obj->partIntensitySources); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_VIDEO:                 return Go2System_ClearOptions(obj->system, obj->videoSources); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_VALUE:                 return Go2System_ClearOptions(obj->system, obj->valueSources); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_DECISION:              return Go2System_ClearOptions(obj->system, obj->decisionSources); 
    default:                                                return zERROR_PARAMETER; 
    }
}

GO2_EXPORT(Go2EthernetProtocol) Go2Ethernet_Protocol(Go2Ethernet ethernet)
{
    Go2EthernetClass* obj = zHANDLE_POINTER(ethernet);
    return obj->protocol; 
}

GO2_EXPORT(Go2Status) Go2Ethernet_SetProtocol(Go2Ethernet ethernet, Go2EthernetProtocol protocol)
{
    Go2EthernetClass* obj = zHANDLE_POINTER(ethernet);
    
    zCHECK(Go2System_CheckConfigurable(obj->system)); 
    obj->protocol = protocol; 
    zCHECK(Go2System_SetModified(obj->system)); 

    return GO2_OK; 
}

GO2_EXPORT(Go2UInt16) Go2Ethernet_AsciiControlPort(Go2Ethernet ethernet)
{
    Go2EthernetClass* obj = zHANDLE_POINTER(ethernet);
    return obj->asciiControlPort; 
}

GO2_EXPORT(Go2Status) Go2Ethernet_SetAsciiControlPort(Go2Ethernet ethernet, Go2UInt16 port)
{
    Go2EthernetClass* obj = zHANDLE_POINTER(ethernet);

    zCHECK(Go2System_CheckConfigurable(obj->system)); 
    zCHECK(Go2Ethernet_CheckPortValidity(port));
    obj->asciiControlPort = port; 
    zCHECK(Go2System_SetModified(obj->system)); 

    return GO2_OK; 
}

GO2_EXPORT(Go2UInt16) Go2Ethernet_AsciiDataPort(Go2Ethernet ethernet)
{
    Go2EthernetClass* obj = zHANDLE_POINTER(ethernet);
    return obj->asciiDataPort; 
}

GO2_EXPORT(Go2Status) Go2Ethernet_SetAsciiDataPort(Go2Ethernet ethernet, Go2UInt16 port)
{
    Go2EthernetClass* obj = zHANDLE_POINTER(ethernet);

    zCHECK(Go2System_CheckConfigurable(obj->system)); 
    zCHECK(Go2Ethernet_CheckPortValidity(port));
    obj->asciiDataPort = port; 
    zCHECK(Go2System_SetModified(obj->system)); 

    return GO2_OK; 
}

GO2_EXPORT(Go2UInt16) Go2Ethernet_AsciiHealthPort(Go2Ethernet ethernet)
{
    Go2EthernetClass* obj = zHANDLE_POINTER(ethernet);
    return obj->asciiHealthPort; 
}

GO2_EXPORT(Go2Status) Go2Ethernet_SetAsciiHealthPort(Go2Ethernet ethernet, Go2UInt16 port)
{
    Go2EthernetClass* obj = zHANDLE_POINTER(ethernet);

    zCHECK(Go2System_CheckConfigurable(obj->system)); 
    zCHECK(Go2Ethernet_CheckPortValidity(port));
    obj->asciiHealthPort = port; 
    zCHECK(Go2System_SetModified(obj->system)); 

    return GO2_OK; 
}

GO2_EXPORT(Go2AsciiOperation) Go2Ethernet_AsciiOperation(Go2Ethernet ethernet)
{
    Go2EthernetClass* obj = zHANDLE_POINTER(ethernet);
    return obj->asciiOperation; 
}

GO2_EXPORT(Go2Status) Go2Ethernet_SetAsciiOperation(Go2Ethernet ethernet, Go2AsciiOperation operation)
{
    Go2EthernetClass* obj = zHANDLE_POINTER(ethernet);

    zCHECK(Go2System_CheckConfigurable(obj->system)); 
    obj->asciiOperation = operation; 
    zCHECK(Go2System_SetModified(obj->system)); 

    return GO2_OK; 
}

GO2_EXPORT(Go2Status) Go2Ethernet_AsciiDelimiter(Go2Ethernet ethernet, Go2Char* delim, Go2UInt32 capacity)
{
    Go2EthernetClass* obj = zHANDLE_POINTER(ethernet);
    strncpy(delim, obj->asciiDelimiter, capacity); 
    delim[capacity-1] = 0; 

    return GO2_OK; 
}

GO2_EXPORT(Go2Status) Go2Ethernet_SetAsciiDelimiter(Go2Ethernet ethernet, const Go2Char* delim)
{
    Go2EthernetClass* obj = zHANDLE_POINTER(ethernet);
    return Go2System_SetText(obj->system, obj->asciiDelimiter, zCOUNT(obj->asciiDelimiter), delim); 
}

GO2_EXPORT(Go2Status) Go2Ethernet_AsciiTerminator(Go2Ethernet ethernet, Go2Char* term, Go2UInt32 capacity)
{
    Go2EthernetClass* obj = zHANDLE_POINTER(ethernet);
    strncpy(term, obj->asciiTerminator, capacity); 
    term[capacity-1] = 0; 

    return GO2_OK; 
}

GO2_EXPORT(Go2Status) Go2Ethernet_SetAsciiTerminator(Go2Ethernet ethernet, const Go2Char* term)
{
    Go2EthernetClass* obj = zHANDLE_POINTER(ethernet);
    return Go2System_SetText(obj->system, obj->asciiTerminator, zCOUNT(obj->asciiTerminator), term); 
}

GO2_EXPORT(Go2Status) Go2Ethernet_AsciiInvalid(Go2Ethernet ethernet, Go2Char* invalid, Go2UInt32 capacity)
{
    Go2EthernetClass* obj = zHANDLE_POINTER(ethernet);
    strncpy(invalid, obj->asciiInvalid, capacity); 
    invalid[capacity-1] = 0; 

    return GO2_OK;  
}

GO2_EXPORT(Go2Status) Go2Ethernet_SetAsciiInvalid(Go2Ethernet ethernet, const Go2Char* invalid)
{
    Go2EthernetClass* obj = zHANDLE_POINTER(ethernet);
    return Go2System_SetText(obj->system, obj->asciiInvalid, zCOUNT(obj->asciiInvalid), invalid);  
}

GO2_EXPORT(Go2Bool) Go2Ethernet_AsciiCustomFormatEnabled(Go2Ethernet ethernet)
{
    Go2EthernetClass* obj = zHANDLE_POINTER(ethernet);
    return obj->asciiCustomFormatEnabled; 
}

GO2_EXPORT(Go2Status) Go2Ethernet_EnableAsciiCustomFormat(Go2Ethernet ethernet, Go2Bool enable)
{
    Go2EthernetClass* obj = zHANDLE_POINTER(ethernet);

    zCHECK(Go2System_CheckConfigurable(obj->system)); 
    obj->asciiCustomFormatEnabled = enable; 
    zCHECK(Go2System_SetModified(obj->system)); 

    return GO2_OK; 
}

GO2_EXPORT(Go2Status) Go2Ethernet_AsciiCustomDataFormat(Go2Ethernet ethernet, Go2Char** format)
{
    Go2EthernetClass* obj = zHANDLE_POINTER(ethernet);
    Go2UInt32 length = (Go2UInt32) strlen(obj->asciiCustomDataFormat); 
    Go2Char* output = zNULL; 

    zCHECK(zCalloc(&output, length+1, sizeof(Go2Char))); 
    strcpy(output, obj->asciiCustomDataFormat); 

    *format = output; 

    return GO2_OK; 
}

GO2_EXPORT(Go2Status) Go2Ethernet_SetAsciiCustomDataFormat(Go2Ethernet ethernet, const Go2Char* format)
{
    Go2EthernetClass* obj = zHANDLE_POINTER(ethernet);

    zCHECK(Go2System_CheckConfigurable(obj->system)); 

    if (!zIS_NULL(format))
    {
        Go2UInt32 length = (Go2UInt32) strlen(format); 

        zCHECK(zFree(obj->asciiCustomDataFormat)); 
        zCHECK(zCalloc(&obj->asciiCustomDataFormat, length + 1, sizeof(Go2Char))); 
        strcpy(obj->asciiCustomDataFormat, format); 
    }
    else
    {
        zCHECK(zFree(obj->asciiCustomDataFormat)); 
        zCHECK(zCalloc(&obj->asciiCustomDataFormat, 1, sizeof(Go2Char))); 
    }

    zCHECK(Go2System_SetModified(obj->system));  

    return GO2_OK;  
}

GO2_EXPORT(Go2Status) Go2Ethernet_CheckPortValidity(Go2UInt16 port)
{
    if (port == GO2_RESERVED_FLASH_POLICY_PORT
        || port == GO2_RESERVED_HTTP_PORT
        || (port >= GO2_RESERVED_RANGE_1_BEGIN && port <= GO2_RESERVED_RANGE_1_END)
        || (port >= GO2_RESERVED_RANGE_2_BEGIN && port <= GO2_RESERVED_RANGE_2_END)
        || (port >= GO2_RESERVED_RANGE_3_BEGIN && port <= GO2_RESERVED_RANGE_3_END))
    {
        return GO2_ERROR;
    }
    
    return GO2_OK;
}


GO2_EXPORT(Go2Bool) Go2Ethernet_EIPBufferEnabled(Go2Ethernet ethernet)
{
    Go2EthernetClass* obj = zHANDLE_POINTER(ethernet);

    return obj->eipBufferEnabled;
}

GO2_EXPORT(Go2Status) Go2Ethernet_EnableEIPBuffer(Go2Ethernet ethernet, Go2Bool enable)
{
    Go2EthernetClass* obj = zHANDLE_POINTER(ethernet);

    zCHECK(Go2System_CheckConfigurable(obj->system)); 

    obj->eipBufferEnabled = enable;

    zCHECK(Go2System_SetModified(obj->system));  

    return GO2_OK;  
}

GO2_EXPORT(Go2Bool) Go2Ethernet_ModbusBufferEnabled(Go2Ethernet ethernet)
{
    Go2EthernetClass* obj = zHANDLE_POINTER(ethernet);

    return obj->modbusBufferEnabled;
}

GO2_EXPORT(Go2Status) Go2Ethernet_EnableModbusBuffer(Go2Ethernet ethernet, Go2Bool enable)
{
    Go2EthernetClass* obj = zHANDLE_POINTER(ethernet);

    zCHECK(Go2System_CheckConfigurable(obj->system)); 

    obj->modbusBufferEnabled = enable;

    zCHECK(Go2System_SetModified(obj->system));  

    return GO2_OK;  
}


/*
 * Go2Serial Class 
 */

GO2_EXPORT(Go2Status) Go2Serial_Construct(Go2Serial* serial, Go2Object system)
{
    Go2Serial output = zNIL; 
    Go2Status status = zOK; 

    zCHECK(zCalloc(&output, 1, sizeof(Go2SerialClass))); 

    if (!zSUCCESS(status = Go2Serial_Init(output, system)))
    {
        zCHECK(Go2Object_Destroy(output)); 
        return status; 
    }
    
    *serial = output; 

    return GO2_OK; 
}

GO2_EXPORT(Go2Status) Go2Serial_Init(Go2Serial serial, Go2System system)
{
    Go2SerialClass* obj = zHANDLE_POINTER(serial);

    zCHECK(Go2Object_Init(serial, GO2_TYPE_SERIAL)); 

    obj->Release = Go2Serial_VRelease; 
    obj->system = system; 

    zCHECK(zList_Construct(&obj->valueOptions, sizeof(z32u), 0)); 
    zCHECK(zList_Construct(&obj->valueSources, sizeof(z32u), 0)); 

    zCHECK(zList_Construct(&obj->decisionOptions, sizeof(z32u), 0)); 
    zCHECK(zList_Construct(&obj->decisionSources, sizeof(z32u), 0)); 

    zCHECK(zList_Construct(&obj->selcomRateOptions, sizeof(z32u), 0)); 
    zCHECK(zList_Construct(&obj->selcomFormatOptions, sizeof(z32u), 0)); 

    return GO2_OK; 
}

GO2_EXPORT(Go2Status) Go2Serial_VRelease(Go2Serial serial)
{
    Go2SerialClass* obj = zHANDLE_POINTER(serial);
    
    zCHECK(zList_Destroy(obj->valueOptions)); 
    zCHECK(zList_Destroy(obj->valueSources)); 

    zCHECK(zList_Destroy(obj->decisionOptions)); 
    zCHECK(zList_Destroy(obj->decisionSources)); 

    zCHECK(zList_Destroy(obj->selcomRateOptions)); 
    zCHECK(zList_Destroy(obj->selcomFormatOptions)); 

    if (!zIS_NULL(obj->asciiCustomDataFormat)) 
    {
        zCHECK(zFree(obj->asciiCustomDataFormat)); 
    }

    zCHECK(Go2Object_VRelease(serial));  
        
    return GO2_OK; 
}

GO2_EXPORT(Go2Status) Go2Serial_Read(Go2Serial serial, zXml xml, zXmlItem item)
{
    Go2SerialClass* obj = zHANDLE_POINTER(serial);
    zChar* text = zNULL; 
    z32u textCapacity = GO2_OUTPUT_SOURCE_TEXT_CAPACITY; 

    zEXCEPT_TRY
    {
        zEXCEPT_CHECK(zCalloc(&text, textCapacity, sizeof(zChar)));   

        zEXCEPT_CHECK(zXml_ChildText(xml, item, "ValueOptions", text, textCapacity)); 
        zEXCEPT_CHECK(Go2OptionList_ParseList(text, obj->valueOptions));  

        zEXCEPT_CHECK(zXml_ChildText(xml, item, "Value", text, textCapacity)); 
        zEXCEPT_CHECK(Go2OptionList_ParseList(text, obj->valueSources));  

        zEXCEPT_CHECK(zXml_ChildText(xml, item, "DecisionOptions", text, textCapacity)); 
        zEXCEPT_CHECK(Go2OptionList_ParseList(text, obj->decisionOptions));  

        zEXCEPT_CHECK(zXml_ChildText(xml, item, "Decision", text, textCapacity)); 
        zEXCEPT_CHECK(Go2OptionList_ParseList(text, obj->decisionSources));  

        zEXCEPT_CHECK(zXml_Child32s(xml, item, "Protocol", (z32s*)&obj->protocol));

        zEXCEPT_CHECK(zXml_ChildText(xml, item, "SelcomRateOptions", text, textCapacity));
        zEXCEPT_CHECK(Go2OptionList_ParseList(text, obj->selcomRateOptions)); 
        zEXCEPT_CHECK(zXml_Child32u(xml, item, "SelcomRate", &obj->selcomRate));
        zEXCEPT_CHECK(zXml_ChildText(xml, item, "SelcomFormatOptions", text, textCapacity));
        zEXCEPT_CHECK(Go2OptionList_ParseList(text, obj->selcomFormatOptions)); 
        zEXCEPT_CHECK(zXml_Child32s(xml, item, "SelcomFormat", (z32s*)&obj->selcomFormat));
        zEXCEPT_CHECK(zXml_Child64f(xml, item, "SelcomDataScaleMin", &obj->selcomDataScaleMin));
        zEXCEPT_CHECK(zXml_Child64f(xml, item, "SelcomDataScaleMax", &obj->selcomDataScaleMax));

        zEXCEPT_CHECK(zXml_ChildText(xml, item, "AsciiDelimiter", obj->asciiDelimiter, zCOUNT(obj->asciiDelimiter))); 
        zEXCEPT_CHECK(zXml_ChildText(xml, item, "AsciiTerminator", obj->asciiTerminator, zCOUNT(obj->asciiTerminator))); 
        zEXCEPT_CHECK(zXml_ChildText(xml, item, "AsciiInvalidValue", obj->asciiInvalid, zCOUNT(obj->asciiInvalid))); 
        zEXCEPT_CHECK(zXml_Child32s(xml, item, "AsciiCustomFormatEnabled", &obj->asciiCustomFormatEnabled));

        zCHECK(zFree(obj->asciiCustomDataFormat)); 
        obj->asciiCustomDataFormat = zNULL; 
        zCHECK(zXml_ChildString(xml, item, "AsciiCustomDataFormat", &obj->asciiCustomDataFormat)); 
    }
    zEXCEPT_CATCH(zNULL) {}
    zEXCEPT_FINALLY
    {
        zEXCEPT_SET(zFree(text)); 
        zEXCEPT_CLOSE(); 
    }

    return GO2_OK; 
}

GO2_EXPORT(Go2Status) Go2Serial_Write(Go2Serial serial, zXml xml, zXmlItem item)
{
    Go2SerialClass* obj = zHANDLE_POINTER(serial);
    zChar* text = zNULL; 
    z32u textCapacity = GO2_OUTPUT_SOURCE_TEXT_CAPACITY; 

    zEXCEPT_TRY
    {
        zEXCEPT_CHECK(zCalloc(&text, textCapacity, sizeof(zChar))); 

        zEXCEPT_CHECK(Go2OptionList_Format(zList_Data(obj->valueSources), zList_Count(obj->valueSources), text, textCapacity)); 
        zEXCEPT_CHECK(zXml_SetChildText(xml, item, "Value", text)); 

        zEXCEPT_CHECK(Go2OptionList_Format(zList_Data(obj->decisionSources), zList_Count(obj->decisionSources), text, textCapacity)); 
        zEXCEPT_CHECK(zXml_SetChildText(xml, item, "Decision", text)); 

        zEXCEPT_CHECK(zXml_SetChild32s(xml, item, "Protocol", obj->protocol)); 

        zEXCEPT_CHECK(zXml_SetChild32u(xml, item, "SelcomRate", obj->selcomRate));
        zEXCEPT_CHECK(zXml_SetChild32s(xml, item, "SelcomFormat", obj->selcomFormat));
        zEXCEPT_CHECK(zXml_SetChild64f(xml, item, "SelcomDataScaleMin", obj->selcomDataScaleMin));
        zEXCEPT_CHECK(zXml_SetChild64f(xml, item, "SelcomDataScaleMax", obj->selcomDataScaleMax));

        zEXCEPT_CHECK(zXml_SetChildText(xml, item, "AsciiDelimiter", obj->asciiDelimiter)); 
        zEXCEPT_CHECK(zXml_SetChildText(xml, item, "AsciiTerminator", obj->asciiTerminator)); 
        zEXCEPT_CHECK(zXml_SetChildText(xml, item, "AsciiInvalidValue", obj->asciiInvalid)); 
        zEXCEPT_CHECK(zXml_SetChild32s(xml, item, "AsciiCustomFormatEnabled", obj->asciiCustomFormatEnabled));
        zEXCEPT_CHECK(zXml_SetChildText(xml, item, "AsciiCustomDataFormat", obj->asciiCustomDataFormat)); 

    }
    zEXCEPT_CATCH(zNULL) {}
    zEXCEPT_FINALLY
    {
        zEXCEPT_SET(zFree(text)); 
        zEXCEPT_CLOSE(); 
    }

    return GO2_OK; 
}

GO2_EXPORT(Go2UInt32) Go2Serial_OptionCount(Go2Serial serial, Go2OutputDataSourceType type)
{
    Go2SerialClass* obj = zHANDLE_POINTER(serial);

    switch (type)
    {
    case GO2_OUTPUT_DATA_SOURCE_TYPE_VALUE:              return zList_Count(obj->valueOptions); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_DECISION:           return zList_Count(obj->decisionOptions); 
    default:                                        return 0; 
    }
}

GO2_EXPORT(Go2UInt32) Go2Serial_OptionAt(Go2Serial serial, Go2OutputDataSourceType type, Go2UInt32 index)
{
    Go2SerialClass* obj = zHANDLE_POINTER(serial);
    z32u* option = zNULL; 

    switch (type)
    {
    case GO2_OUTPUT_DATA_SOURCE_TYPE_VALUE:              option = zList_At(obj->valueOptions, index);            break;
    case GO2_OUTPUT_DATA_SOURCE_TYPE_DECISION:           option = zList_At(obj->decisionOptions, index);         break;
    default:                                        option = zNULL;                                         break;
    }

    return !zIS_NULL(option) ? *option : 0; 
}

GO2_EXPORT(Go2UInt32) Go2Serial_SourceCount(Go2Serial serial, Go2OutputDataSourceType type)
{
    Go2SerialClass* obj = zHANDLE_POINTER(serial);

    switch (type)
    {
    case GO2_OUTPUT_DATA_SOURCE_TYPE_VALUE:              return zList_Count(obj->valueSources); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_DECISION:           return zList_Count(obj->decisionSources); 
    default:                                        return 0; 
    }
}

GO2_EXPORT(Go2UInt32) Go2Serial_SourceAt(Go2Serial serial, Go2OutputDataSourceType type, Go2UInt32 index)
{
    Go2SerialClass* obj = zHANDLE_POINTER(serial);
    z32u* option = zNULL; 

    switch (type)
    {
    case GO2_OUTPUT_DATA_SOURCE_TYPE_VALUE:              option = zList_At(obj->valueSources, index);            break;
    case GO2_OUTPUT_DATA_SOURCE_TYPE_DECISION:           option = zList_At(obj->decisionSources, index);         break;
    default:                                        option = zNULL;                                         break;
    }

    return !zIS_NULL(option) ? *option : 0; 
}

GO2_EXPORT(Go2Status) Go2Serial_AddSource(Go2Serial serial, Go2OutputDataSourceType type, Go2UInt32 sourceId)
{
    Go2SerialClass* obj = zHANDLE_POINTER(serial);

    switch (type)
    {
    case GO2_OUTPUT_DATA_SOURCE_TYPE_VALUE:              return Go2System_AddOption(obj->system, obj->valueSources, sourceId, zList_Data(obj->valueOptions), zList_Count(obj->valueOptions)); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_DECISION:           return Go2System_AddOption(obj->system, obj->decisionSources, sourceId, zList_Data(obj->decisionOptions), zList_Count(obj->decisionOptions)); 
    default:                                        return zERROR_PARAMETER; 
    }
}

GO2_EXPORT(Go2Status) Go2Serial_RemoveSource(Go2Serial serial, Go2OutputDataSourceType type, Go2UInt32 index)
{
    Go2SerialClass* obj = zHANDLE_POINTER(serial);

    switch (type)
    {
    case GO2_OUTPUT_DATA_SOURCE_TYPE_VALUE:              return Go2System_RemoveOption(obj->system, obj->valueSources, index); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_DECISION:           return Go2System_RemoveOption(obj->system, obj->decisionSources, index); 
    default:                                        return zERROR_PARAMETER; 
    }
}

GO2_EXPORT(Go2Status) Go2Serial_ClearSources(Go2Serial serial, Go2OutputDataSourceType type)
{
    Go2SerialClass* obj = zHANDLE_POINTER(serial);

    switch (type)
    {
    case GO2_OUTPUT_DATA_SOURCE_TYPE_VALUE:              return Go2System_ClearOptions(obj->system, obj->valueSources); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_DECISION:           return Go2System_ClearOptions(obj->system, obj->decisionSources); 
    default:                                        return zERROR_PARAMETER; 
    }
}

GO2_EXPORT(Go2SerialProtocol) Go2Serial_Protocol(Go2Serial serial)
{
    Go2SerialClass* obj = zHANDLE_POINTER(serial);
    return obj->protocol; 
}

GO2_EXPORT(Go2Status) Go2Serial_SetProtocol(Go2Serial serial, Go2SerialProtocol protocol)
{
    Go2SerialClass* obj = zHANDLE_POINTER(serial);

    zCHECK(Go2System_CheckConfigurable(obj->system)); 
    obj->protocol = protocol; 
    zCHECK(Go2System_SetModified(obj->system)); 

    return GO2_OK; 
}

GO2_EXPORT(Go2SelcomFormat) Go2Serial_SelcomFormatOptionAt(Go2Serial serial, Go2UInt32 index)
{
    Go2SerialClass* obj = zHANDLE_POINTER(serial);
    return *(Go2SelcomFormat*)zList_At(obj->selcomFormatOptions, index); 
}

GO2_EXPORT(Go2UInt32) Go2Serial_SelcomFormatOptionCount(Go2Serial serial)
{
    Go2SerialClass* obj = zHANDLE_POINTER(serial);
    return zList_Count(obj->selcomFormatOptions);
}

GO2_EXPORT(Go2SelcomFormat) Go2Serial_SelcomFormat(Go2Serial serial)
{
    Go2SerialClass* obj = zHANDLE_POINTER(serial);
    return obj->selcomFormat; 
}

GO2_EXPORT(Go2Status) Go2Serial_SetSelcomFormat(Go2Serial serial, Go2SelcomFormat format)
{
    Go2SerialClass* obj = zHANDLE_POINTER(serial);

    return Go2System_SetOption(obj->system, (Go2UInt32*)&obj->selcomFormat, (Go2UInt32)format, zList_Data(obj->selcomFormatOptions), zList_Count(obj->selcomFormatOptions)); 
}

GO2_EXPORT(Go2UInt32) Go2Serial_SelcomRateOptionAt(Go2Serial serial, Go2UInt32 index)
{
    Go2SerialClass* obj = zHANDLE_POINTER(serial);
    return *(Go2UInt32*)zList_At(obj->selcomRateOptions, index); 
}

GO2_EXPORT(Go2UInt32) Go2Serial_SelcomRateOptionCount(Go2Serial serial)
{
    Go2SerialClass* obj = zHANDLE_POINTER(serial);
    return zList_Count(obj->selcomRateOptions);
}

GO2_EXPORT(Go2UInt32) Go2Serial_SelcomRate(Go2Serial serial)
{
    Go2SerialClass* obj = zHANDLE_POINTER(serial);
    return obj->selcomRate; 
}

GO2_EXPORT(Go2Status) Go2Serial_SetSelcomRate(Go2Serial serial, Go2UInt32 rate)
{
    Go2SerialClass* obj = zHANDLE_POINTER(serial);

    return Go2System_SetOption(obj->system, &obj->selcomRate, rate, zList_Data(obj->selcomRateOptions), zList_Count(obj->selcomRateOptions)); 
}

GO2_EXPORT(Go2Status) Go2Serial_SetSelcomDataScaleMax(Go2Serial serial, Go2Double max)
{
    Go2SerialClass* obj = zHANDLE_POINTER(serial);

    zCHECK(Go2System_CheckConfigurable(obj->system)); 
    obj->selcomDataScaleMax = max; 
    zCHECK(Go2System_SetModified(obj->system)); 

    return GO2_OK; 
}

GO2_EXPORT(Go2Double) Go2Serial_SelcomDataScaleMax(Go2Serial serial)
{
    Go2SerialClass* obj = zHANDLE_POINTER(serial);
    return obj->selcomDataScaleMax; 
}

GO2_EXPORT(Go2Status) Go2Serial_SetSelcomDataScaleMin(Go2Serial serial, Go2Double min)
{
    Go2SerialClass* obj = zHANDLE_POINTER(serial);

    zCHECK(Go2System_CheckConfigurable(obj->system)); 
    obj->selcomDataScaleMin = min; 
    zCHECK(Go2System_SetModified(obj->system)); 

    return GO2_OK; 
}

GO2_EXPORT(Go2Double) Go2Serial_SelcomDataScaleMin(Go2Serial serial)
{
    Go2SerialClass* obj = zHANDLE_POINTER(serial);
    return obj->selcomDataScaleMin; 
}


GO2_EXPORT(Go2Status) Go2Serial_AsciiDelimiter(Go2Serial serial, Go2Char* delim, Go2UInt32 capacity)
{
    Go2SerialClass* obj = zHANDLE_POINTER(serial);
    strncpy(delim, obj->asciiDelimiter, capacity); 
    delim[capacity-1] = 0; 

    return GO2_OK; 
}

GO2_EXPORT(Go2Status) Go2Serial_SetAsciiDelimiter(Go2Serial serial, const Go2Char* delim)
{
    Go2SerialClass* obj = zHANDLE_POINTER(serial);
    return Go2System_SetText(obj->system, obj->asciiDelimiter, zCOUNT(obj->asciiDelimiter), delim); 
}

GO2_EXPORT(Go2Status) Go2Serial_AsciiTerminator(Go2Serial serial, Go2Char* term, Go2UInt32 capacity)
{
    Go2SerialClass* obj = zHANDLE_POINTER(serial);
    strncpy(term, obj->asciiTerminator, capacity); 
    term[capacity-1] = 0; 

    return GO2_OK; 
}

GO2_EXPORT(Go2Status) Go2Serial_SetAsciiTerminator(Go2Serial serial, const Go2Char* term)
{
    Go2SerialClass* obj = zHANDLE_POINTER(serial);
    return Go2System_SetText(obj->system, obj->asciiTerminator, zCOUNT(obj->asciiTerminator), term); 
}

GO2_EXPORT(Go2Status) Go2Serial_AsciiInvalid(Go2Serial serial, Go2Char* invalid, Go2UInt32 capacity)
{
    Go2SerialClass* obj = zHANDLE_POINTER(serial);
    strncpy(invalid, obj->asciiInvalid, capacity); 
    invalid[capacity-1] = 0; 

    return GO2_OK;  
}

GO2_EXPORT(Go2Status) Go2Serial_SetAsciiInvalid(Go2Serial serial, const Go2Char* invalid)
{
    Go2SerialClass* obj = zHANDLE_POINTER(serial);
    return Go2System_SetText(obj->system, obj->asciiInvalid, zCOUNT(obj->asciiInvalid), invalid);  
}

GO2_EXPORT(Go2Bool) Go2Serial_AsciiCustomFormatEnabled(Go2Serial serial)
{
    Go2SerialClass* obj = zHANDLE_POINTER(serial);
    return obj->asciiCustomFormatEnabled; 
}

GO2_EXPORT(Go2Status) Go2Serial_EnableAsciiCustomFormat(Go2Serial serial, Go2Bool enable)
{
    Go2SerialClass* obj = zHANDLE_POINTER(serial);

    zCHECK(Go2System_CheckConfigurable(obj->system)); 
    obj->asciiCustomFormatEnabled = enable; 
    zCHECK(Go2System_SetModified(obj->system)); 

    return GO2_OK; 
}

GO2_EXPORT(Go2Status) Go2Serial_AsciiCustomDataFormat(Go2Serial serial, Go2Char** format)
{
    Go2SerialClass* obj = zHANDLE_POINTER(serial);
    Go2UInt32 length = (Go2UInt32) strlen(obj->asciiCustomDataFormat); 
    Go2Char* output = zNULL; 

    zCHECK(zCalloc(&output, length+1, sizeof(Go2Char))); 
    strcpy(output, obj->asciiCustomDataFormat); 

    *format = output; 

    return GO2_OK; 
}

GO2_EXPORT(Go2Status) Go2Serial_SetAsciiCustomDataFormat(Go2Serial serial, const Go2Char* format)
{
    Go2SerialClass* obj = zHANDLE_POINTER(serial);

    zCHECK(Go2System_CheckConfigurable(obj->system)); 

    if (!zIS_NULL(format))
    {
        Go2UInt32 length = (Go2UInt32) strlen(format); 

        zCHECK(zFree(obj->asciiCustomDataFormat)); 
        zCHECK(zCalloc(&obj->asciiCustomDataFormat, length + 1, sizeof(Go2Char))); 
        strcpy(obj->asciiCustomDataFormat, format); 
    }
    else
    {
        zCHECK(zFree(obj->asciiCustomDataFormat)); 
        zCHECK(zCalloc(&obj->asciiCustomDataFormat, 1, sizeof(Go2Char))); 
    }

    zCHECK(Go2System_SetModified(obj->system));  

    return GO2_OK;  
}


/*
 * Go2Analog Class 
 */

GO2_EXPORT(Go2Status) Go2Analog_Construct(Go2Analog* analog, Go2Object system)
{
    Go2Analog output = zNIL; 
    Go2Status status = zOK; 

    zCHECK(zCalloc(&output, 1, sizeof(Go2AnalogClass))); 

    if (!zSUCCESS(status = Go2Analog_Init(output, system)))
    {
        zCHECK(Go2Object_Destroy(output)); 
        return status; 
    }
    
    *analog = output; 

    return GO2_OK; 
}

GO2_EXPORT(Go2Status) Go2Analog_Init(Go2Analog analog, Go2System system)
{
    Go2AnalogClass* obj = zHANDLE_POINTER(analog);

    zCHECK(Go2Object_Init(analog, GO2_TYPE_ANALOG)); 

    obj->Release = Go2Analog_VRelease; 
    obj->system = system; 

    zCHECK(zList_Construct(&obj->valueOptions, sizeof(z32u), 0)); 
    zCHECK(zList_Construct(&obj->valueSources, sizeof(z32u), 0)); 

    zCHECK(zList_Construct(&obj->decisionOptions, sizeof(z32u), 0)); 
    zCHECK(zList_Construct(&obj->decisionSources, sizeof(z32u), 0)); 

    return GO2_OK; 
}

GO2_EXPORT(Go2Status) Go2Analog_VRelease(Go2Analog analog)
{
    Go2AnalogClass* obj = zHANDLE_POINTER(analog);
    
    zCHECK(zList_Destroy(obj->valueOptions)); 
    zCHECK(zList_Destroy(obj->valueSources)); 

    zCHECK(zList_Destroy(obj->decisionOptions)); 
    zCHECK(zList_Destroy(obj->decisionSources)); 

    zCHECK(Go2Object_VRelease(analog));  
        
    return GO2_OK; 
}

GO2_EXPORT(Go2Status) Go2Analog_Read(Go2Analog analog, zXml xml, zXmlItem item)
{
    Go2AnalogClass* obj = zHANDLE_POINTER(analog);
    zChar* text = zNULL; 
    z32u textCapacity = GO2_OUTPUT_SOURCE_TEXT_CAPACITY; 

    zEXCEPT_TRY
    {
        zEXCEPT_CHECK(zCalloc(&text, textCapacity, sizeof(zChar)));            
        
        zEXCEPT_CHECK(zXml_ChildText(xml, item, "ValueOptions", text, textCapacity)); 
        zEXCEPT_CHECK(Go2OptionList_ParseList(text, obj->valueOptions));  

        zEXCEPT_CHECK(zXml_ChildText(xml, item, "Value", text, textCapacity)); 
        zEXCEPT_CHECK(Go2OptionList_ParseList(text, obj->valueSources));  

        zEXCEPT_CHECK(zXml_ChildText(xml, item, "DecisionOptions", text, textCapacity)); 
        zEXCEPT_CHECK(Go2OptionList_ParseList(text, obj->decisionOptions));  

        zEXCEPT_CHECK(zXml_ChildText(xml, item, "Decision", text, textCapacity)); 
        zEXCEPT_CHECK(Go2OptionList_ParseList(text, obj->decisionSources));  

        zEXCEPT_CHECK(zXml_Child32u(xml, item, "ScheduleEnable", &obj->scheduleEnabled)); 
        zEXCEPT_CHECK(zXml_Child64s(xml, item, "Delay", &obj->delay)); 
        zEXCEPT_CHECK(zXml_Child32u(xml, item, "Event", (z32u*)&obj->event)); 

        zEXCEPT_CHECK(zXml_Child64f(xml, item, "CurrentLimitMin", &obj->currentLimitMin)); 
        zEXCEPT_CHECK(zXml_Child64f(xml, item, "CurrentLimitMax", &obj->currentLimitMax)); 

        zEXCEPT_CHECK(zXml_Child64f(xml, item, "CurrentMin", &obj->currentMin)); 
        zEXCEPT_CHECK(zXml_Child64f(xml, item, "CurrentMax", &obj->currentMax)); 
        zEXCEPT_CHECK(zXml_Child64f(xml, item, "CurrentInvalid", &obj->currentInvalid)); 
        zEXCEPT_CHECK(zXml_Child32s(xml, item, "CurrentInvalidEnable", &obj->currentInvalidEnabled)); 

        zEXCEPT_CHECK(zXml_Child64f(xml, item, "DataScaleMin", &obj->dataScaleMin)); 
        zEXCEPT_CHECK(zXml_Child64f(xml, item, "DataScaleMax", &obj->dataScaleMax)); 

    }
    zEXCEPT_CATCH(zNULL) {}
    zEXCEPT_FINALLY
    {
        zEXCEPT_SET(zFree(text)); 
        zEXCEPT_CLOSE(); 
    }

    return GO2_OK; 
}

GO2_EXPORT(Go2Status) Go2Analog_Write(Go2Analog analog, zXml xml, zXmlItem item)
{
    Go2AnalogClass* obj = zHANDLE_POINTER(analog);
    zChar* text = zNULL; 
    z32u textCapacity = GO2_OUTPUT_SOURCE_TEXT_CAPACITY; 

    zEXCEPT_TRY
    {
        zEXCEPT_CHECK(zCalloc(&text, textCapacity, sizeof(zChar))); 

        zEXCEPT_CHECK(Go2OptionList_Format(zList_Data(obj->valueSources), zList_Count(obj->valueSources), text, textCapacity)); 
        zEXCEPT_CHECK(zXml_SetChildText(xml, item, "Value", text)); 

        zEXCEPT_CHECK(Go2OptionList_Format(zList_Data(obj->decisionSources), zList_Count(obj->decisionSources), text, textCapacity)); 
        zEXCEPT_CHECK(zXml_SetChildText(xml, item, "Decision", text)); 

        zEXCEPT_CHECK(zXml_SetChild32u(xml, item, "ScheduleEnable", obj->scheduleEnabled)); 
        zEXCEPT_CHECK(zXml_SetChild64s(xml, item, "Delay", obj->delay)); 
        zEXCEPT_CHECK(zXml_SetChild32u(xml, item, "Event", obj->event)); 

        zEXCEPT_CHECK(zXml_SetChild64f(xml, item, "CurrentMin", obj->currentMin)); 
        zEXCEPT_CHECK(zXml_SetChild64f(xml, item, "CurrentMax", obj->currentMax)); 
        zEXCEPT_CHECK(zXml_SetChild64f(xml, item, "CurrentInvalid", obj->currentInvalid)); 
        zEXCEPT_CHECK(zXml_SetChild64f(xml, item, "CurrentInvalidEnable", obj->currentInvalidEnabled)); 

        zEXCEPT_CHECK(zXml_SetChild64f(xml, item, "DataScaleMin", obj->dataScaleMin)); 
        zEXCEPT_CHECK(zXml_SetChild64f(xml, item, "DataScaleMax", obj->dataScaleMax)); 
    }
    zEXCEPT_CATCH(zNULL) {}
    zEXCEPT_FINALLY
    {
        zEXCEPT_SET(zFree(text)); 
        zEXCEPT_CLOSE(); 
    }

    return GO2_OK; 
}

GO2_EXPORT(Go2Status) Go2Analog_SetEvent(Go2Analog analog, Go2AnalogEvent event)
{
    Go2AnalogClass* obj = zHANDLE_POINTER(analog);
    
    zCHECK(Go2System_CheckConfigurable(obj->system)); 
    obj->event = event; 
    zCHECK(Go2System_SetModified(obj->system)); 

    return GO2_OK; 
}

GO2_EXPORT(Go2AnalogEvent) Go2Analog_Event(Go2Analog analog)
{
    Go2AnalogClass* obj = zHANDLE_POINTER(analog);
    return obj->event; 
}

GO2_EXPORT(Go2UInt32) Go2Analog_OptionCount(Go2Analog analog, Go2OutputDataSourceType type)
{
    Go2AnalogClass* obj = zHANDLE_POINTER(analog);

    switch (type)
    {
    case GO2_OUTPUT_DATA_SOURCE_TYPE_VALUE:              return zList_Count(obj->valueOptions); 
    case GO2_OUTPUT_DATA_SOURCE_TYPE_DECISION:           return zList_Count(obj->decisionOptions); 
    default:                                        return 0; 
    }
}

GO2_EXPORT(Go2UInt32) Go2Analog_OptionAt(Go2Analog analog, Go2OutputDataSourceType type, Go2UInt32 index)
{
    Go2AnalogClass* obj = zHANDLE_POINTER(analog);
    z32u* option = zNULL; 

    switch (type)
    {
    case GO2_OUTPUT_DATA_SOURCE_TYPE_VALUE:              option = zList_At(obj->valueOptions, index);            break;
    case GO2_OUTPUT_DATA_SOURCE_TYPE_DECISION:           option = zList_At(obj->decisionOptions, index);         break;
    default:                                        option = zNULL;                                         break;
    }

    return !zIS_NULL(option) ? *option : 0; 
}

GO2_EXPORT(Go2Status) Go2Analog_SetSource(Go2Analog analog, Go2OutputDataSourceType type, Go2UInt32 source)
{
    Go2AnalogClass* obj = zHANDLE_POINTER(analog);

    zCHECK(zList_Clear(obj->valueSources)); 
    zCHECK(zList_Clear(obj->decisionSources)); 

    switch(type)
    {
        case GO2_OUTPUT_DATA_SOURCE_TYPE_VALUE: return Go2System_AddOption(obj->system, obj->valueSources, source, zList_Data(obj->valueOptions), zList_Count(obj->valueOptions)); break;
        case GO2_OUTPUT_DATA_SOURCE_TYPE_DECISION: return Go2System_AddOption(obj->system, obj->decisionSources, source, zList_Data(obj->decisionOptions), zList_Count(obj->decisionOptions)); break;
        case GO2_OUTPUT_DATA_SOURCE_TYPE_NONE: Go2System_SetModified(obj->system); break;        
        default: break; 
    }

    return GO2_OK; 
}

GO2_EXPORT(Go2OutputDataSourceType) Go2Analog_SourceType(Go2Analog analog)
{
    Go2AnalogClass* obj = zHANDLE_POINTER(analog);

    if (zList_Count(obj->valueSources) > 0)
    {
        return GO2_OUTPUT_DATA_SOURCE_TYPE_VALUE;
    }
    else if (zList_Count(obj->decisionSources) > 0)
    {
        return GO2_OUTPUT_DATA_SOURCE_TYPE_DECISION; 
    }
    else
    {
        return GO2_OUTPUT_DATA_SOURCE_TYPE_NONE; 
    }
}

GO2_EXPORT(Go2UInt32) Go2Analog_Source(Go2Analog analog)
{
    Go2AnalogClass* obj = zHANDLE_POINTER(analog);
    Go2UInt32 source = 0; 

    if (zList_Count(obj->valueSources) > 0)
    {
        zCHECK(zList_Item(obj->valueSources, 0, &source)); 
    }
    else if (zList_Count(obj->decisionSources) > 0)
    {
        zCHECK(zList_Item(obj->decisionSources, 0, &source)); 
    }

    return source; 
}

GO2_EXPORT(Go2Double) Go2Analog_CurrentLimitMin(Go2Analog analog)
{
    Go2AnalogClass* obj = zHANDLE_POINTER(analog);
    return obj->currentLimitMin; 
}

GO2_EXPORT(Go2Double) Go2Analog_CurrentLimitMax(Go2Analog analog)
{
    Go2AnalogClass* obj = zHANDLE_POINTER(analog);
    return obj->currentLimitMax; 
}

GO2_EXPORT(Go2Status) Go2Analog_SetCurrentMin(Go2Analog analog, Go2Double min)
{
    Go2AnalogClass* obj = zHANDLE_POINTER(analog);
    return Go2System_Set64f(obj->system, &obj->currentMin, min, obj->currentLimitMin, obj->currentLimitMax); 
}

GO2_EXPORT(Go2Double) Go2Analog_CurrentMin(Go2Analog analog)
{
    Go2AnalogClass* obj = zHANDLE_POINTER(analog);
    return obj->currentMin; 
}

GO2_EXPORT(Go2Status) Go2Analog_SetCurrentMax(Go2Analog analog, Go2Double max)
{
    Go2AnalogClass* obj = zHANDLE_POINTER(analog);
    return Go2System_Set64f(obj->system, &obj->currentMax, max, obj->currentLimitMin, obj->currentLimitMax); 
}

GO2_EXPORT(Go2Double) Go2Analog_CurrentMax(Go2Analog analog)
{
    Go2AnalogClass* obj = zHANDLE_POINTER(analog);
    return obj->currentMax; 
}

GO2_EXPORT(Go2Status) Go2Analog_EnableCurrentInvalid(Go2Analog analog, Go2Bool enable)
{
    Go2AnalogClass* obj = zHANDLE_POINTER(analog);
    
    zCHECK(Go2System_CheckConfigurable(obj->system)); 
    obj->currentInvalidEnabled = enable; 
    zCHECK(Go2System_SetModified(obj->system)); 

    return GO2_OK; 
}

GO2_EXPORT(Go2Bool) Go2Analog_CurrentInvalidEnabled(Go2Analog analog)
{
    Go2AnalogClass* obj = zHANDLE_POINTER(analog);
    return obj->currentInvalidEnabled; 
}

GO2_EXPORT(Go2Status) Go2Analog_SetCurrentInvalid(Go2Analog analog, Go2Double invalid)
{
    Go2AnalogClass* obj = zHANDLE_POINTER(analog);
    return Go2System_Set64f(obj->system, &obj->currentInvalid, invalid, obj->currentLimitMin, obj->currentLimitMax); 
}

GO2_EXPORT(Go2Double) Go2Analog_CurrentInvalid(Go2Analog analog)
{
    Go2AnalogClass* obj = zHANDLE_POINTER(analog);
    return obj->currentInvalid; 
}

GO2_EXPORT(Go2Status) Go2Analog_SetDataScaleMin(Go2Analog analog, Go2Double min)
{
    Go2AnalogClass* obj = zHANDLE_POINTER(analog);
    return Go2System_Set64f(obj->system, &obj->dataScaleMin, min, -z64F_MAX, z64F_MAX); 
}

GO2_EXPORT(Go2Double) Go2Analog_DataScaleMin(Go2Analog analog)
{
    Go2AnalogClass* obj = zHANDLE_POINTER(analog);
    return obj->dataScaleMin; 
}

GO2_EXPORT(Go2Status) Go2Analog_SetDataScaleMax(Go2Analog analog, Go2Double max)
{
    Go2AnalogClass* obj = zHANDLE_POINTER(analog);
    return Go2System_Set64f(obj->system, &obj->dataScaleMax, max, -z64F_MAX, z64F_MAX); 
}

GO2_EXPORT(Go2Double) Go2Analog_DataScaleMax(Go2Analog analog)
{
    Go2AnalogClass* obj = zHANDLE_POINTER(analog);
    return obj->dataScaleMax; 
}

GO2_EXPORT(Go2Status) Go2Analog_SetDelay(Go2Analog analog, Go2Int64 delay)
{
    Go2AnalogClass* obj = zHANDLE_POINTER(analog);
    
    zCHECK(Go2System_CheckConfigurable(obj->system)); 
    obj->delay = delay; 
    zCHECK(Go2System_SetModified(obj->system)); 

    return GO2_OK; 
}

GO2_EXPORT(Go2Int64) Go2Analog_Delay(Go2Analog analog)
{
    Go2AnalogClass* obj = zHANDLE_POINTER(analog);
    return obj->delay; 
}

GO2_EXPORT(Go2Status) Go2Analog_EnableSchedule(Go2Analog analog, Go2Bool enabled )
{
    Go2AnalogClass* obj = zHANDLE_POINTER(analog);
    
    zCHECK(Go2System_CheckConfigurable(obj->system)); 
    obj->scheduleEnabled = enabled; 
    zCHECK(Go2System_SetModified(obj->system)); 

    return GO2_OK; 
}

GO2_EXPORT(Go2Bool) Go2Analog_ScheduleEnabled(Go2Analog analog)
{
    Go2AnalogClass* obj = zHANDLE_POINTER(analog);
    return obj->scheduleEnabled; 
}

/*
 * Go2Digital Class 
 */

GO2_EXPORT(Go2Status) Go2Digital_Construct(Go2Digital* digital, Go2UInt32 id, Go2Object system)
{
    Go2Digital output = zNIL; 
    Go2Status status = zOK; 

    zCHECK(zCalloc(&output, 1, sizeof(Go2DigitalClass))); 

    if (!zSUCCESS(status = Go2Digital_Init(output, id, system)))
    {
        zCHECK(Go2Object_Destroy(output)); 
        return status; 
    }
    
    *digital = output; 

    return GO2_OK; 
}

GO2_EXPORT(Go2Status) Go2Digital_Init(Go2Digital digital, Go2UInt32 id, Go2System system)
{
    Go2DigitalClass* obj = zHANDLE_POINTER(digital);

    zCHECK(Go2Object_Init(digital, GO2_TYPE_DIGITAL)); 

    obj->Release = Go2Digital_VRelease; 
    obj->system = system; 
    obj->id = id; 

    zCHECK(zList_Construct(&obj->decisionOptions, sizeof(z32u), 0)); 
    zCHECK(zList_Construct(&obj->decisionSources, sizeof(z32u), 0)); 

    return GO2_OK; 
}

GO2_EXPORT(Go2Status) Go2Digital_VRelease(Go2Digital digital)
{
    Go2DigitalClass* obj = zHANDLE_POINTER(digital);
    
    zCHECK(zList_Destroy(obj->decisionOptions)); 
    zCHECK(zList_Destroy(obj->decisionSources)); 

    zCHECK(Go2Object_VRelease(digital));  
        
    return GO2_OK; 
}

GO2_EXPORT(Go2Status) Go2Digital_Read(Go2Digital digital, zXml xml, zXmlItem item)
{
    Go2DigitalClass* obj = zHANDLE_POINTER(digital);
    zChar* text = zNULL; 
    z32u textCapacity = GO2_OUTPUT_SOURCE_TEXT_CAPACITY; 

    zEXCEPT_TRY
    {
        zEXCEPT_CHECK(zXml_Attr32u(xml, item, "id", &obj->id)); 

        zEXCEPT_CHECK(zCalloc(&text, textCapacity, sizeof(zChar))); 
            
        zEXCEPT_CHECK(zXml_ChildText(xml, item, "DecisionOptions", text, textCapacity)); 
        zEXCEPT_CHECK(Go2OptionList_ParseList(text, obj->decisionOptions));  

        zEXCEPT_CHECK(zXml_ChildText(xml, item, "Decision", text, textCapacity)); 
        zEXCEPT_CHECK(Go2OptionList_ParseList(text, obj->decisionSources));  

        zEXCEPT_CHECK(zXml_Child32s(xml, item, "PassMode", (z32s*)&obj->passMode)); 

        zEXCEPT_CHECK(zXml_Child32u(xml, item, "ScheduleEnable", &obj->scheduleEnabled)); 
        zEXCEPT_CHECK(zXml_Child64s(xml, item, "Delay", &obj->delay)); 
        zEXCEPT_CHECK(zXml_Child32u(xml, item, "Event", (z32u*)&obj->event)); 
        zEXCEPT_CHECK(zXml_Child32u(xml, item, "SignalType", (z32u*)&obj->signalType)); 

        zEXCEPT_CHECK(zXml_Child32u(xml, item, "PulseWidthMin", &obj->pulseWidthLimitMin)); 
        zEXCEPT_CHECK(zXml_Child32u(xml, item, "PulseWidthMax", &obj->pulseWidthLimitMax)); 
        zEXCEPT_CHECK(zXml_Child32u(xml, item, "PulseWidth", &obj->pulseWidth)); 
    }
    zEXCEPT_CATCH(zNULL) {}
    zEXCEPT_FINALLY
    {
        zEXCEPT_SET(zFree(text)); 
        zEXCEPT_CLOSE(); 
    }

    return GO2_OK; 
}

GO2_EXPORT(Go2Status) Go2Digital_Write(Go2Digital digital, zXml xml, zXmlItem item)
{
    Go2DigitalClass* obj = zHANDLE_POINTER(digital);
    zChar* text = zNULL; 
    z32u textCapacity = GO2_OUTPUT_SOURCE_TEXT_CAPACITY; 

    zEXCEPT_TRY
    {
        zEXCEPT_CHECK(zXml_SetAttr32u(xml, item, "id", obj->id)); 

        zEXCEPT_CHECK(zCalloc(&text, textCapacity, sizeof(zChar)));  

        zEXCEPT_CHECK(Go2OptionList_Format(zList_Data(obj->decisionSources), zList_Count(obj->decisionSources), text, textCapacity)); 
        zEXCEPT_CHECK(zXml_SetChildText(xml, item, "Decision", text)); 
       
        zEXCEPT_CHECK(zXml_SetChild32s(xml, item, "PassMode", obj->passMode)); 
        zEXCEPT_CHECK(zXml_SetChild32u(xml, item, "PulseWidth", obj->pulseWidth)); 

        zEXCEPT_CHECK(zXml_SetChild32u(xml, item, "ScheduleEnable", obj->scheduleEnabled)); 
        zEXCEPT_CHECK(zXml_SetChild64s(xml, item, "Delay", obj->delay)); 
        zEXCEPT_CHECK(zXml_SetChild32u(xml, item, "SignalType", obj->signalType)); 
        zEXCEPT_CHECK(zXml_SetChild32u(xml, item, "Event", obj->event)); 
    }
    zEXCEPT_CATCH(zNULL) {}
    zEXCEPT_FINALLY
    {
        zEXCEPT_SET(zFree(text)); 
        zEXCEPT_CLOSE(); 
    }

    return GO2_OK; 
}

GO2_EXPORT(Go2UInt32) Go2Digital_Id(Go2Digital digital)
{
    Go2DigitalClass* obj = zHANDLE_POINTER(digital);
    return obj->id;
}

GO2_EXPORT(Go2Status) Go2Digital_SetEvent(Go2Digital digital, Go2DigitalEvent event)
{
    Go2DigitalClass* obj = zHANDLE_POINTER(digital);
    
    zCHECK(Go2System_CheckConfigurable(obj->system)); 
    obj->event = event; 
    zCHECK(Go2System_SetModified(obj->system)); 

    return GO2_OK; 
}

GO2_EXPORT(Go2DigitalEvent) Go2Digital_Event(Go2Digital digital)
{
    Go2DigitalClass* obj = zHANDLE_POINTER(digital);
    return obj->event; 
}

GO2_EXPORT(Go2UInt32) Go2Digital_OptionCount(Go2Digital digital)
{
    Go2DigitalClass* obj = zHANDLE_POINTER(digital);
    return zList_Count(obj->decisionOptions); 
}

GO2_EXPORT(Go2UInt32) Go2Digital_OptionAt(Go2Digital digital, Go2UInt32 index)
{
    Go2DigitalClass* obj = zHANDLE_POINTER(digital);
    z32u option; 

    return zSUCCESS(zList_Item(obj->decisionOptions, index, &option)) ? option : z32U_NULL; 
}

GO2_EXPORT(Go2UInt32) Go2Digital_SourceCount(Go2Digital digital)
{
    Go2DigitalClass* obj = zHANDLE_POINTER(digital);
    return zList_Count(obj->decisionSources); 
}

GO2_EXPORT(Go2UInt32) Go2Digital_SourceAt(Go2Digital digital, Go2UInt32 index)
{
    Go2DigitalClass* obj = zHANDLE_POINTER(digital);
    z32u source; 

    return zSUCCESS(zList_Item(obj->decisionSources, index, &source)) ? source : z32U_NULL; 
}

GO2_EXPORT(Go2Status) Go2Digital_AddSource(Go2Digital digital, Go2UInt32 sourceId)
{
    Go2DigitalClass* obj = zHANDLE_POINTER(digital);    
    return Go2System_AddOption(obj->system, obj->decisionSources, sourceId, zList_Data(obj->decisionOptions), zList_Count(obj->decisionOptions)); 
}

GO2_EXPORT(Go2Status) Go2Digital_RemoveSource(Go2Digital digital, Go2UInt32 index)
{
    Go2DigitalClass* obj = zHANDLE_POINTER(digital);
    return Go2System_RemoveOption(obj->system, obj->decisionSources, index); 
}

GO2_EXPORT(Go2Status) Go2Digital_ClearSources(Go2Digital digital)
{
    Go2DigitalClass* obj = zHANDLE_POINTER(digital);
    return Go2System_ClearOptions(obj->system, obj->decisionSources); 
}

GO2_EXPORT(Go2Status) Go2Digital_SetPassMode(Go2Digital digital, Go2DigitalPassMode mode)
{
    Go2DigitalClass* obj = zHANDLE_POINTER(digital);
    
    zCHECK(Go2System_CheckConfigurable(obj->system)); 
    obj->passMode = mode; 
    zCHECK(Go2System_SetModified(obj->system)); 

    return GO2_OK; 
}

GO2_EXPORT(Go2DigitalPassMode) Go2Digital_PassMode(Go2Digital digital)
{
    Go2DigitalClass* obj = zHANDLE_POINTER(digital);
    return obj->passMode; 
}

GO2_EXPORT(Go2UInt32) Go2Digital_PulseWidthLimitMin(Go2Digital digital)
{
    Go2DigitalClass* obj = zHANDLE_POINTER(digital);
    return obj->pulseWidthLimitMin; 
}

GO2_EXPORT(Go2UInt32) Go2Digital_PulseWidthLimitMax(Go2Digital digital)
{
    Go2DigitalClass* obj = zHANDLE_POINTER(digital);
    return obj->pulseWidthLimitMax; 
}

GO2_EXPORT(Go2Status) Go2Digital_SetPulseWidth(Go2Digital digital, Go2UInt32 width)
{
    Go2DigitalClass* obj = zHANDLE_POINTER(digital);
    return Go2System_Set32u(obj->system, &obj->pulseWidth, width, obj->pulseWidthLimitMin, obj->pulseWidthLimitMax); 
}

GO2_EXPORT(Go2UInt32) Go2Digital_PulseWidth(Go2Digital digital)
{
    Go2DigitalClass* obj = zHANDLE_POINTER(digital);
    return obj->pulseWidth; 
}

GO2_EXPORT(Go2Status) Go2Digital_SetSignalType(Go2Digital digital, Go2DigitalSignalType signalType)
{
    Go2DigitalClass* obj = zHANDLE_POINTER(digital);
    
    zCHECK(Go2System_CheckConfigurable(obj->system)); 
    obj->signalType = signalType; 
    zCHECK(Go2System_SetModified(obj->system)); 

    return GO2_OK; 
}

GO2_EXPORT(Go2DigitalSignalType) Go2Digital_SignalType(Go2Digital digital)
{
    Go2DigitalClass* obj = zHANDLE_POINTER(digital);
    return obj->signalType; 
}

GO2_EXPORT(Go2Status) Go2Digital_SetDelay(Go2Digital digital, Go2Int64 delay)
{
    Go2DigitalClass* obj = zHANDLE_POINTER(digital);
    
    zCHECK(Go2System_CheckConfigurable(obj->system)); 
    obj->delay = delay; 
    zCHECK(Go2System_SetModified(obj->system)); 

    return GO2_OK; 
}

GO2_EXPORT(Go2Int64) Go2Digital_Delay(Go2Digital digital)
{
    Go2DigitalClass* obj = zHANDLE_POINTER(digital);
    return obj->delay; 
}


GO2_EXPORT(Go2Status) Go2Digital_EnableSchedule(Go2Digital digital, Go2Bool enabled )
{
    Go2DigitalClass* obj = zHANDLE_POINTER(digital);
    
    zCHECK(Go2System_CheckConfigurable(obj->system)); 
    obj->scheduleEnabled = enabled; 
    zCHECK(Go2System_SetModified(obj->system)); 

    return GO2_OK; 
}

GO2_EXPORT(Go2Bool) Go2Digital_ScheduleEnabled(Go2Digital digital)
{
    Go2DigitalClass* obj = zHANDLE_POINTER(digital);
    return obj->scheduleEnabled; 
}
