/**
 * @file    GoEthernet.c
 *
 * @internal
 * Copyright (C) 2016-2022 by LMI Technologies Inc.
 * Licensed under the MIT License.
 * Redistributed files must retain the above copyright notice.
 */
#include <GoSdk/Outputs/GoEthernet.h>
#include <GoSdk/GoUtils.h>
#include <GoSdk/GoSensor.h>
#include <stdio.h>

kBeginClassEx(Go, GoEthernet)
    kAddVMethod(GoEthernet, kObject, VRelease)
kEndClassEx()

// Table of the Ethernet Output sources and their configuration XML tag names.
GoValueNamePair g_EthernetOutputSourceTypeMap[] =
{
    { GO_OUTPUT_SOURCE_VIDEO,               "Videos" },
    { GO_OUTPUT_SOURCE_RANGE,               "Ranges" },
    { GO_OUTPUT_SOURCE_PROFILE,             "Profiles" },
    { GO_OUTPUT_SOURCE_SURFACE,             "Surfaces" },
    { GO_OUTPUT_SOURCE_SECTION,             "SurfaceSections" },
    { GO_OUTPUT_SOURCE_RANGE_INTENSITY,     "RangeIntensities" },
    { GO_OUTPUT_SOURCE_PROFILE_INTENSITY,   "ProfileIntensities" },
    { GO_OUTPUT_SOURCE_SURFACE_INTENSITY,   "SurfaceIntensities" },
    { GO_OUTPUT_SOURCE_SECTION_INTENSITY,   "SurfaceSectionsIntensities" },
    { GO_OUTPUT_SOURCE_MEASUREMENT,         "Measurements" },
    { GO_OUTPUT_SOURCE_TRACHEID,            "Tracheids" },
    { GO_OUTPUT_SOURCE_EVENT,               "Events" },
    { GO_OUTPUT_SOURCE_FEATURE,             "Features" },
    { GO_OUTPUT_SOURCE_TOOLDATA,            "ToolDataOutputs" }
};

GoFx(kStatus) GoEthernet_Construct(GoEthernet* ethernet, kObject sensor, kAlloc allocator)
{
    kAlloc alloc = kAlloc_Fallback(allocator);
    kStatus status;

    kCheck(kAlloc_GetObject(alloc, kTypeOf(GoEthernet), ethernet));

    if (!kSuccess(status = GoEthernet_Init(*ethernet, kTypeOf(GoEthernet), sensor, alloc)))
    {
        kAlloc_FreeRef(alloc, ethernet);
    }

    return status;
}

GoFx(kStatus) GoEthernet_Init(GoEthernet ethernet, kType type, kObject sensor, kAlloc alloc)
{
    kObjR(GoEthernet, ethernet);
    kStatus             exception;

    kCheck(kObject_Init(ethernet, type, alloc));
    kZero(obj->xml);
    kZero(obj->xmlItem);
    kZero(obj->videoOptions);
    kZero(obj->videoSources);
    kZero(obj->rangeOptions);
    kZero(obj->rangeSources);
    kZero(obj->rangeIntensityOptions);
    kZero(obj->rangeIntensitySources);
    kZero(obj->profileOptions);
    kZero(obj->profileSources);
    kZero(obj->profileIntensityOptions);
    kZero(obj->profileIntensitySources);
    kZero(obj->surfaceIntensityOptions);
    kZero(obj->surfaceIntensitySources);
    kZero(obj->surfaceOptions);
    kZero(obj->surfaceSources);
    kZero(obj->sectionIntensityOptions);
    kZero(obj->sectionIntensitySources);
    kZero(obj->sectionOptions);
    kZero(obj->sectionSources);
    kZero(obj->measurementOptions);
    kZero(obj->measurementSources);
    kZero(obj->eventOptions);
    kZero(obj->events);
    kZero(obj->tracheidOptions);
    kZero(obj->tracheidSources);
    kZero(obj->featureOptions);
    kZero(obj->featureSources);
    kZero(obj->toolDataOptions);
    kZero(obj->toolDataSources);
    obj->outputUsed = kFALSE;
    obj->protocol = GO_ETHERNET_PROTOCOL_GOCATOR;
    kZero(obj->eip);
    kZero(obj->modbus);
    kZero(obj->ascii);
    kZero(obj->profinet);
    kZero(obj->ptp);

    obj->sensor = sensor;

    // Auto-disconnect is available and enabled by default.
    obj->timeoutEnabled = kTRUE;
    obj->timeoutEnabledAvail = kTRUE;

    obj->timeout = 10.0;

    kTry
    {
        kTest(kArrayList_Construct(&obj->videoOptions, kTypeOf(k32s), 0, alloc));
        kTest(kArrayList_Construct(&obj->videoSources, kTypeOf(k32s), 0, alloc));

        kTest(kArrayList_Construct(&obj->rangeOptions, kTypeOf(k32s), 0, alloc));
        kTest(kArrayList_Construct(&obj->rangeSources, kTypeOf(k32s), 0, alloc));

        kTest(kArrayList_Construct(&obj->rangeIntensityOptions, kTypeOf(k32s), 0, alloc));
        kTest(kArrayList_Construct(&obj->rangeIntensitySources, kTypeOf(k32s), 0, alloc));

        kTest(kArrayList_Construct(&obj->profileOptions, kTypeOf(k32s), 0, alloc));
        kTest(kArrayList_Construct(&obj->profileSources, kTypeOf(k32s), 0, alloc));

        kTest(kArrayList_Construct(&obj->profileIntensityOptions, kTypeOf(k32s), 0, alloc));
        kTest(kArrayList_Construct(&obj->profileIntensitySources, kTypeOf(k32s), 0, alloc));

        kTest(kArrayList_Construct(&obj->surfaceOptions, kTypeOf(k32s), 0, alloc));
        kTest(kArrayList_Construct(&obj->surfaceSources, kTypeOf(k32s), 0, alloc));

        kTest(kArrayList_Construct(&obj->surfaceIntensityOptions, kTypeOf(k32s), 0, alloc));
        kTest(kArrayList_Construct(&obj->surfaceIntensitySources, kTypeOf(k32s), 0, alloc));

        kTest(kArrayList_Construct(&obj->sectionOptions, kTypeOf(GoOutputCompositeSource), 0, alloc));
        kTest(kArrayList_Construct(&obj->sectionSources, kTypeOf(GoOutputCompositeSource), 0, alloc));

        kTest(kArrayList_Construct(&obj->sectionIntensityOptions, kTypeOf(GoOutputCompositeSource), 0, alloc));
        kTest(kArrayList_Construct(&obj->sectionIntensitySources, kTypeOf(GoOutputCompositeSource), 0, alloc));

        kTest(kArrayList_Construct(&obj->measurementOptions, kTypeOf(k32s), 0, alloc));
        kTest(kArrayList_Construct(&obj->measurementSources, kTypeOf(k32s), 0, alloc));

        kTest(kArrayList_Construct(&obj->eventOptions, kTypeOf(k32s), 0, alloc));
        kTest(kArrayList_Construct(&obj->events, kTypeOf(k32s), 0, alloc));

        kTest(kArrayList_Construct(&obj->tracheidOptions, kTypeOf(k32s), 0, alloc));
        kTest(kArrayList_Construct(&obj->tracheidSources, kTypeOf(k32s), 0, alloc));

        kTest(kArrayList_Construct(&obj->featureOptions, kTypeOf(k32s), 0, alloc));
        kTest(kArrayList_Construct(&obj->featureSources, kTypeOf(k32s), 0, alloc));

        kTest(kArrayList_Construct(&obj->toolDataOptions, kTypeOf(k32s), 0, alloc));
        kTest(kArrayList_Construct(&obj->toolDataSources, kTypeOf(k32s), 0, alloc));

        kTest(kString_Construct(&obj->ascii.customFormat, "", alloc));
        kTest(kString_Construct(&obj->ascii.delimiter, "", alloc));
        kTest(kString_Construct(&obj->ascii.invalidValue, "", alloc));
        kTest(kString_Construct(&obj->ascii.terminator, "", alloc));

        kTest(kString_Construct(&obj->profinet.deviceName, "", alloc));
    }
    kCatch(&exception)
    {
        GoEthernet_VRelease(ethernet);
        kEndCatch(exception);
    }

    return kOK;
}

GoFx(kStatus) GoEthernet_VRelease(GoEthernet ethernet)
{
    kObj(GoEthernet, ethernet);

    kCheck(kDisposeRef(&obj->videoOptions));
    kCheck(kDisposeRef(&obj->videoSources));

    kCheck(kDisposeRef(&obj->rangeOptions));
    kCheck(kDisposeRef(&obj->rangeSources));

    kCheck(kDisposeRef(&obj->rangeIntensityOptions));
    kCheck(kDisposeRef(&obj->rangeIntensitySources));

    kCheck(kDisposeRef(&obj->profileOptions));
    kCheck(kDisposeRef(&obj->profileSources));

    kCheck(kDisposeRef(&obj->profileIntensityOptions));
    kCheck(kDisposeRef(&obj->profileIntensitySources));

    kCheck(kDisposeRef(&obj->surfaceOptions));
    kCheck(kDisposeRef(&obj->surfaceSources));

    kCheck(kDisposeRef(&obj->surfaceIntensityOptions));
    kCheck(kDisposeRef(&obj->surfaceIntensitySources));

    kCheck(kDisposeRef(&obj->sectionOptions));
    kCheck(kDisposeRef(&obj->sectionSources));

    kCheck(kDisposeRef(&obj->sectionIntensityOptions));
    kCheck(kDisposeRef(&obj->sectionIntensitySources));

    kCheck(kDisposeRef(&obj->measurementOptions));
    kCheck(kDisposeRef(&obj->measurementSources));

    kCheck(kDisposeRef(&obj->eventOptions));
    kCheck(kDisposeRef(&obj->events));

    kCheck(kDisposeRef(&obj->tracheidOptions));
    kCheck(kDisposeRef(&obj->tracheidSources));

    kCheck(kDisposeRef(&obj->featureOptions));
    kCheck(kDisposeRef(&obj->featureSources));

    kCheck(kDisposeRef(&obj->toolDataOptions));
    kCheck(kDisposeRef(&obj->toolDataSources));

    kCheck(kDestroyRef(&obj->ascii.customFormat));
    kCheck(kDestroyRef(&obj->ascii.delimiter));
    kCheck(kDestroyRef(&obj->ascii.invalidValue));
    kCheck(kDestroyRef(&obj->ascii.terminator));

    kCheck(kDestroyRef(&obj->profinet.deviceName));

    return kObject_VRelease(ethernet);
}

GoFx(kStatus) GoEthernet_ParseOutputList(GoOutputSource outputSource, kChar* text, kArrayList outputList)
{
    if (outputSource == GO_OUTPUT_SOURCE_EVENT)
    {
        // Events are of type GoEventType which is a signed integer.
        kCheck(GoOptionList_ParseList32s(text, outputList));
    }
    else if ((outputSource == GO_OUTPUT_SOURCE_SECTION) ||
             (outputSource == GO_OUTPUT_SOURCE_SECTION_INTENSITY))
    {
        // Not typical ints -> actually of the form "ID-DataSource".
        kCheck(GoEthernet_ParseCompositeOptionList(text, outputList));
    }
    else
    {
        kCheck(GoOptionList_ParseList32u(text, outputList));
    }

    return kOK;
}

GoFx(kStatus) GoEthernet_ParseOutputOptionList(GoEthernet ethernet, GoOutputSource outputSource, kChar* text)
{
    kArrayList outputList;

    outputList = GoEthernet_OptionList(ethernet, outputSource);
    kCheckState(!kIsNull(outputList));

    kCheck(GoEthernet_ParseOutputList(outputSource, text, outputList));

    return kOK;
}

GoFx(kStatus) GoEthernet_ParseOutputSourceList(GoEthernet ethernet, GoOutputSource outputSource, kChar* text)
{
    kArrayList outputList;

    outputList = GoEthernet_SourceList(ethernet, outputSource);
    kCheckState(!kIsNull(outputList));

    kCheck(GoEthernet_ParseOutputList(outputSource, text, outputList));

    return kOK;
}

GoFx(kStatus) GoEthernet_ReadAllOutputConfig(GoEthernet ethernet)
{
    kObj(GoEthernet, ethernet);
    kChar*              text = kNULL;
    kSize               textCapacity = GO_OUTPUT_SOURCE_TEXT_CAPACITY;
    kXmlItem            tempItem = kNULL;
    kSize               i;
    kSize               numMapEntry = kCountOf(g_EthernetOutputSourceTypeMap);
    GoValueNamePair*    tableEntry;

    kTry
    {
        kTest(kObject_GetMemZero(ethernet, sizeof(kChar) * textCapacity, &text));

        for (i = 0; i < numMapEntry; i++)
        {
            tableEntry = &g_EthernetOutputSourceTypeMap[i];

            tempItem = kXml_Child(obj->xml, obj->xmlItem, tableEntry->name);

            if (!kIsNull(tempItem))
            {
                kTest(kXml_AttrText(obj->xml, tempItem, "options", text, textCapacity));
                kTest(GoEthernet_ParseOutputOptionList(ethernet, (GoOutputSource) tableEntry->value, text));

                kTest(kXml_ItemText(obj->xml, tempItem, text, textCapacity));
                kTest(GoEthernet_ParseOutputSourceList(ethernet, (GoOutputSource) tableEntry->value, text));
            }
        }
    }
    kFinally
    {
        kObject_FreeMem(ethernet, text);
        kEndFinally();
    }

    return kOK;
}

GoFx(kStatus) GoEthernet_Read(GoEthernet ethernet, kXml xml, kXmlItem item)
{
    kObj(GoEthernet, ethernet);
    kXmlItem tempItem = kNULL;

    obj->xml = xml;
    obj->xmlItem = item;

    kTry
    {
        tempItem = kXml_Child(xml, item, "TimeoutEnabled");
        if (!kIsNull(tempItem))
        {
            kTest(kXml_ItemBool(xml, tempItem, &obj->timeoutEnabled));
            // Set default value to true to match legacy behaviour, if
            // the configuration file does not have this attribute.
            kTest(GoConfig_ReadAttrBoolOptional(xml, tempItem, "used", kTRUE, &obj->timeoutEnabledAvail));

            kTest(kXml_Child64f(xml, item, "Timeout", &obj->timeout));
        }

        kTest(GoConfig_ReadAttrBoolOptional(xml, item, "used", kTRUE, &obj->outputUsed));;
        kTest(kXml_Child32s(xml, item, "Protocol", (k32s*)&obj->protocol));

        kTest(GoEthernet_ReadAscii(ethernet, xml, item));
        kTest(GoEthernet_ReadEip(ethernet, xml, item));
        kTest(GoEthernet_ReadModbus(ethernet, xml, item));
        kTest(GoEthernet_ReadProfinet(ethernet, xml, item));
        kTest(GoEthernet_ReadPtp(ethernet, xml, item));
        kTest(GoEthernet_ReadAllOutputConfig(ethernet));
    }
    kFinally
    {
        kEndFinally();
    }

    return kOK;
}

GoFx(kStatus) GoEthernet_WriteAllOutputConfig(GoEthernet ethernet, kXml xml, kXmlItem item)
{
    kChar*              text = kNULL;
    kSize               textCapacity = GO_OUTPUT_SOURCE_TEXT_CAPACITY;
    GoValueNamePair*    tableEntry;
    kSize               i;
    kSize               numMapEntry = kCountOf(g_EthernetOutputSourceTypeMap);
    kArrayList          sourceList;

    kTry
    {
        kTest(kObject_GetMemZero(ethernet, sizeof(kChar) * textCapacity, &text));

        for (i = 0; i < numMapEntry; i++)
        {
            tableEntry = &g_EthernetOutputSourceTypeMap[i];
            sourceList = GoEthernet_SourceList(ethernet, (GoOutputSource) tableEntry->value);
            kTestState(!kIsNull(sourceList));

            if (((GoOutputSource) tableEntry->value == GO_OUTPUT_SOURCE_SECTION) ||
                ((GoOutputSource) tableEntry->value == GO_OUTPUT_SOURCE_SECTION_INTENSITY))
            {
                kTest(GoOptionList_FormatOutputCompositeSource(kArrayList_DataT(sourceList, const GoOutputCompositeSource), kArrayList_Count(sourceList), text, textCapacity));
            }
            else
            {
                kTest(GoOptionList_Format32u(kArrayList_DataT(sourceList, const k32u), kArrayList_Count(sourceList), text, textCapacity));
            }
            kTest(kXml_SetChildText(xml, item, tableEntry->name, text));
        }
    }
    kFinally
    {
        kObject_FreeMem(ethernet, text);
        kEndFinally();
    }

    return kOK;
}

GoFx(kStatus) GoEthernet_Write(GoEthernet ethernet, kXml xml, kXmlItem item)
{
    kObj(GoEthernet, ethernet);
    kXmlItem            tempItem = kNULL;

    kTry
    {
        kTest(kXml_SetChild32s(xml, item, "Protocol", obj->protocol));

        // Only need to send the TimeoutEnabled value to the sensor.
        // The "used" attribute is only sent from sensor to client, never
        // from client to sensor.
        kTest(kXml_SetChildBool(xml, item, "TimeoutEnabled", obj->timeoutEnabled));

        kTest(kXml_SetChild64f(xml, item, "Timeout", obj->timeout));

        kTest(GoEthernet_WriteAscii(ethernet, xml, item));
        kTest(GoEthernet_WriteEip(ethernet, xml, item));
        kTest(GoEthernet_WriteModbus(ethernet, xml, item));

        // Client never changes the Profinet parameters since it is
        // read-only for the client. Therefore don't send the
        // Profinet parameters back to the sensor.

        kTest(GoEthernet_WriteAllOutputConfig(ethernet, xml, item));
    }
    kFinally
    {
        kEndFinally();
    }

    // Forwards Compatibility.
    kCheck(GoUtils_XmlMerge(obj->xml, obj->xmlItem, xml, item));

    return kOK;
}

GoFx(kStatus) GoEthernet_ReadAscii(GoEthernet ethernet, kXml xml, kXmlItem item)
{
    kObj(GoEthernet, ethernet);
    kXmlItem tempItem = kNULL;

    //ASCII config
    tempItem = kXml_Child(xml, item, "Ascii");
    if (!kIsNull(tempItem))
    {
        kCheck(kXml_Child32u(xml, tempItem, "Operation", (k32u*) &obj->ascii.operation));
        kCheck(kXml_Child32u(xml, tempItem, "ControlPort", &obj->ascii.controlPort));
        kCheck(kXml_Child32u(xml, tempItem, "DataPort", &obj->ascii.dataPort));
        kCheck(kXml_Child32u(xml, tempItem, "HealthPort", &obj->ascii.healthPort));
        kCheck(kXml_ChildString(xml, tempItem, "Delimiter", obj->ascii.delimiter));
        kCheck(kXml_ChildString(xml, tempItem, "Terminator", obj->ascii.terminator));
        kCheck(kXml_ChildString(xml, tempItem, "InvalidValue", obj->ascii.invalidValue));
        kCheck(kXml_ChildString(xml, tempItem, "CustomDataFormat", obj->ascii.customFormat));
        kCheck(kXml_ChildBool(xml, tempItem, "CustomFormatEnabled", &obj->ascii.customFormatEnabled));

        if (kXml_ChildExists(xml, tempItem, "StandardFormatMode"))
        {
            kCheck(kXml_Child32s(xml, tempItem, "StandardFormatMode", &obj->ascii.standardFormatMode));
        }
    }
    return kOK;
}

GoFx(kStatus) GoEthernet_WriteAscii(GoEthernet ethernet, kXml xml, kXmlItem item)
{
    kObj(GoEthernet, ethernet);
    kXmlItem tempItem = kNULL;

    //ASCII config
    kCheck(kXml_AddItem(xml, item, "Ascii", &tempItem));
    kCheck(kXml_SetChild32s(xml, tempItem, "Operation", obj->ascii.operation));
    kCheck(kXml_SetChild32u(xml, tempItem, "ControlPort", obj->ascii.controlPort));
    kCheck(kXml_SetChild32u(xml, tempItem, "DataPort", obj->ascii.dataPort));
    kCheck(kXml_SetChild32u(xml, tempItem, "HealthPort", obj->ascii.healthPort));
    kCheck(kXml_SetChildText(xml, tempItem, "Delimiter", kString_Chars(obj->ascii.delimiter)));
    kCheck(kXml_SetChildText(xml, tempItem, "Terminator", kString_Chars(obj->ascii.terminator)));
    kCheck(kXml_SetChildText(xml, tempItem, "InvalidValue", kString_Chars(obj->ascii.invalidValue)));
    kCheck(kXml_SetChildText(xml, tempItem, "CustomDataFormat", kString_Chars(obj->ascii.customFormat)));
    kCheck(kXml_SetChildBool(xml, tempItem, "CustomFormatEnabled", obj->ascii.customFormatEnabled));
    kCheck(kXml_SetChild32s(xml, tempItem, "StandardFormatMode", obj->ascii.standardFormatMode));

    return kOK;
}

GoFx(kStatus) GoEthernet_ReadEip(GoEthernet ethernet, kXml xml, kXmlItem item)
{
    kObj(GoEthernet, ethernet);
    kXmlItem tempItem = kNULL;

    tempItem = kXml_Child(xml, item, "EIP");
    if (!kIsNull(tempItem))
    {
        kCheck(kXml_ChildBool(xml, tempItem, "BufferEnabled", &obj->eip.bufferEnabled));
        kCheck(GoConfig_Read32sOptional(xml, tempItem, "EndianOutputType", kFALSE, &obj->eip.endianOutputType));
        kCheck(GoConfig_ReadBoolOptional(xml, tempItem, "ImplicitOutputEnabled", kFALSE, &obj->eip.implicitOutputEnabled));
        kCheck(GoConfig_Read32sOptional(xml, tempItem, "ImplicitTriggerOverride", kFALSE, &obj->eip.implicitTriggerOverride));
    }
    return kOK;
}

GoFx(kStatus) GoEthernet_WriteEip(GoEthernet ethernet, kXml xml, kXmlItem item)
{
    kObj(GoEthernet, ethernet);
    kXmlItem tempItem = kNULL;

    kCheck(kXml_AddItem(xml, item, "EIP", &tempItem));
    kCheck(kXml_SetChildBool(xml, tempItem, "BufferEnabled", obj->eip.bufferEnabled));
    kCheck(kXml_SetChild32s(xml, tempItem, "EndianOutputType", obj->eip.endianOutputType));
    kCheck(kXml_SetChildBool(xml, tempItem, "ImplicitOutputEnabled", obj->eip.implicitOutputEnabled));
    kCheck(kXml_SetChild32s(xml, tempItem, "ImplicitTriggerOverride", obj->eip.implicitTriggerOverride));

    return kOK;
}

GoFx(kStatus) GoEthernet_ReadModbus(GoEthernet ethernet, kXml xml, kXmlItem item)
{
    kObj(GoEthernet, ethernet);
    kXmlItem tempItem = kNULL;

    tempItem = kXml_Child(xml, item, "Modbus");
    if (!kIsNull(tempItem))
    {
        kCheck(kXml_ChildBool(xml, tempItem, "BufferEnabled", &obj->modbus.bufferEnabled));
    }

    return kOK;
}

GoFx(kStatus) GoEthernet_WriteModbus(GoEthernet ethernet, kXml xml, kXmlItem item)
{
    kObj(GoEthernet, ethernet);
    kXmlItem tempItem = kNULL;

    kCheck(kXml_AddItem(xml, item, "Modbus", &tempItem));
    kCheck(kXml_SetChildBool(xml, tempItem, "BufferEnabled", obj->modbus.bufferEnabled));

    return kOK;
}

GoFx(kStatus) GoEthernet_ReadProfinet(GoEthernet ethernet, kXml xml, kXmlItem item)
{
    kObj(GoEthernet, ethernet);
    kXmlItem tempItem = kNULL;
    kString  tempString = kNULL;
    kStatus  exception;

    tempItem = kXml_Child(xml, item, "Profinet");
    if (!kIsNull(tempItem))
    {
        kTry
        {
            kTest(kString_Construct(&tempString, "", kObject_Alloc(ethernet)));

            kTest(kXml_ChildString(xml, tempItem, "IpAddress", tempString));
            kTest(kIpAddress_Parse(&obj->profinet.ipAddress, kString_Chars(tempString)));

            kTest(kXml_ChildString(xml, tempItem, "SubnetMask", tempString));
            kTest(kIpAddress_Parse(&obj->profinet.subnetMask, kString_Chars(tempString)));

            kTest(kXml_ChildString(xml, tempItem, "Gateway", tempString));
            kTest(kIpAddress_Parse(&obj->profinet.gateway, kString_Chars(tempString)));

            kTest(kXml_ChildString(xml, tempItem, "DeviceName", obj->profinet.deviceName));
        }
        kCatchEx(&exception)
        {
            kLogf("%s: error %d reading Profinet configuration", __FUNCTION__, exception);

            kEndCatchEx(exception);
        }
        kFinallyEx
        {
            kCheck(kObject_Destroy(tempString));

            kEndFinallyEx();
        }
    }

    return kOK;
}

GoFx(kStatus) GoEthernet_ReadPtp(GoEthernet ethernet, kXml xml, kXmlItem item)
{
    kObj(GoEthernet, ethernet);
    kXmlItem tempItem = kNULL;
    kString  tempString = kNULL;
    kStatus  exception;

    tempItem = kXml_Child(xml, item, "Ptp");
    if (!kIsNull(tempItem))
    {
        kTry
        {
            kTest(kString_Construct(&tempString, "", kObject_Alloc(ethernet)));
        }
        kCatchEx(&exception)
        {
            kLogf("%s: error %d reading Ptp configuration", __FUNCTION__, exception);

            kEndCatchEx(exception);
        }
        kFinallyEx
        {
            kCheck(kObject_Destroy(tempString));

            kEndFinallyEx();
        }
    }

    return kOK;
}

GoFx(kStatus) GoEthernet_ParseHelperComposite(const kChar* text, kSize length, GoOutputCompositeSource* value)
{
    const kChar* readIt = text;
    const kChar* separator = kNULL;

    if (length == 0)
    {
        return kERROR_PARAMETER;
    }

    if (!kIsNull(separator = strchr(readIt, GO_UTILS_STREAM_ID_SEPARATOR)))
    {
        if (kSuccess(GoOptionList_ParseHelper32s(readIt, separator - readIt, &value->id)))
        {
            readIt = separator + 1;
            if (kSuccess(GoOptionList_ParseHelper32s(readIt, separator - readIt, &value->dataSource)))
            {
                return kOK;
            }
        }
    }

    return kERROR_CONFLICT;
}

GoFx(kStatus) GoEthernet_ParseCompositeOptionList(const kChar* text, kArrayList list)
{
    const kChar* readIt = text;
    const kChar* separator = kNULL;
    GoOutputCompositeSource option;

    kCheck(kArrayList_Clear(list));
    kCheck(kArrayList_Allocate(list, kTypeOf(GoOutputCompositeSource), 0));

    while (!kIsNull(separator = strchr(readIt, ',')))
    {
        if (kSuccess(GoEthernet_ParseHelperComposite(readIt, separator - readIt, &option)))
        {
            kCheck(kArrayList_AddT(list, &option));
        }
        readIt = separator + 1;
    }

    if (kSuccess(GoEthernet_ParseHelperComposite(readIt, strlen(readIt), &option)))
    {
        kCheck(kArrayList_AddT(list, &option));
    }

    return kOK;
}

GoFx(kBool) GoEthernet_CheckCompositeSource(const GoOutputCompositeSource* optionList, kSize count, GoOutputCompositeSource value)
{
    kSize i;
    for (i = 0; i < count; ++i)
    {
        if (optionList[i].id == value.id && optionList[i].dataSource == value.dataSource)
        {
            return kTRUE;
        }
    }

    return kFALSE;
}

GoFx(kArrayList) GoEthernet_OptionList(GoEthernet ethernet, GoOutputSource type)
{
    kObj(GoEthernet, ethernet);
    kArrayList list = kNULL;

    switch (type)
    {
        case GO_OUTPUT_SOURCE_VIDEO:                           list = obj->videoOptions;            break;
        case GO_OUTPUT_SOURCE_RANGE:                           list = obj->rangeOptions;            break;
        case GO_OUTPUT_SOURCE_RANGE_INTENSITY:                 list = obj->rangeIntensityOptions;   break;
        case GO_OUTPUT_SOURCE_PROFILE:                         list = obj->profileOptions;          break;
        case GO_OUTPUT_SOURCE_PROFILE_INTENSITY:               list = obj->profileIntensityOptions; break;
        case GO_OUTPUT_SOURCE_SURFACE:                         list = obj->surfaceOptions;          break;
        case GO_OUTPUT_SOURCE_SURFACE_INTENSITY:               list = obj->surfaceIntensityOptions; break;
        case GO_OUTPUT_SOURCE_SECTION:                         list = obj->sectionOptions;          break;
        case GO_OUTPUT_SOURCE_SECTION_INTENSITY:               list = obj->sectionIntensityOptions; break;
        case GO_OUTPUT_SOURCE_MEASUREMENT:                     list = obj->measurementOptions;      break;
        case GO_OUTPUT_SOURCE_TRACHEID:                        list = obj->tracheidOptions;         break;
        case GO_OUTPUT_SOURCE_EVENT:                           list = obj->eventOptions;            break;
        case GO_OUTPUT_SOURCE_FEATURE:                         list = obj->featureOptions;          break;
        case GO_OUTPUT_SOURCE_TOOLDATA:                        list = obj->toolDataOptions;         break;
    }

    return list;
}

GoFx(kArrayList) GoEthernet_SourceList(GoEthernet ethernet, GoOutputSource type)
{
    kObj(GoEthernet, ethernet);
    kArrayList list = kNULL;

    switch (type)
    {
        case GO_OUTPUT_SOURCE_VIDEO:                         list = obj->videoSources;              break;
        case GO_OUTPUT_SOURCE_RANGE:                         list = obj->rangeSources;              break;
        case GO_OUTPUT_SOURCE_RANGE_INTENSITY:               list = obj->rangeIntensitySources;     break;
        case GO_OUTPUT_SOURCE_PROFILE:                       list = obj->profileSources;            break;
        case GO_OUTPUT_SOURCE_PROFILE_INTENSITY:             list = obj->profileIntensitySources;   break;
        case GO_OUTPUT_SOURCE_SURFACE:                       list = obj->surfaceSources;            break;
        case GO_OUTPUT_SOURCE_SURFACE_INTENSITY:             list = obj->surfaceIntensitySources;   break;
        case GO_OUTPUT_SOURCE_SECTION:                       list = obj->sectionSources;            break;
        case GO_OUTPUT_SOURCE_SECTION_INTENSITY:             list = obj->sectionIntensitySources;   break;
        case GO_OUTPUT_SOURCE_MEASUREMENT:                   list = obj->measurementSources;        break;
        case GO_OUTPUT_SOURCE_TRACHEID:                      list = obj->tracheidSources;           break;
        case GO_OUTPUT_SOURCE_EVENT:                         list = obj->events;                    break;
        case GO_OUTPUT_SOURCE_FEATURE:                       list = obj->featureSources;            break;
        case GO_OUTPUT_SOURCE_TOOLDATA:                      list = obj->toolDataSources;           break;
    }

    return list;
}

GoFx(kSize) GoEthernet_OptionCount(GoEthernet ethernet, GoOutputSource type)
{
    kObj(GoEthernet, ethernet);
    kArrayList list;

    GoSensor_SyncConfig(obj->sensor);

    list = GoEthernet_OptionList(ethernet, type);

    if (kIsNull(list))
    {
        return 0;
    }

    return kArrayList_Count(list);
}

GoFx(k32u) GoEthernet_OptionAt(GoEthernet ethernet, GoOutputSource type, kSize index)
{
    kObj(GoEthernet, ethernet);
    kArrayList list;

    GoSensor_SyncConfig(obj->sensor);

    list = GoEthernet_OptionList(ethernet, type);

    if (kIsNull(list))
    {
        return 0;
    }

    kAssert(index < kArrayList_Count(list));

    return kArrayList_AsT(list, index, k32u);
}

GoFx(kSize) GoEthernet_SourceCount(GoEthernet ethernet, GoOutputSource type)
{
    kObj(GoEthernet, ethernet);
    kArrayList list;

    GoSensor_SyncConfig(obj->sensor);

    list = GoEthernet_SourceList(ethernet, type);

    if (kIsNull(list))
    {
        return 0;
    }

    return kArrayList_Count(list);
}

GoFx(k32u) GoEthernet_SourceAt(GoEthernet ethernet, GoOutputSource type, kSize index)
{
    kObj(GoEthernet, ethernet);
    kArrayList list;

    GoSensor_SyncConfig(obj->sensor);

    list = GoEthernet_SourceList(ethernet, type);

    if (kIsNull(list))
    {
        return 0;
    }

    kAssert(index < kArrayList_Count(list));

    return kArrayList_AsT(list, index, k32u);
}

GoFx(kStatus) GoEthernet_AddSource(GoEthernet ethernet, GoOutputSource type, k32s sourceId)
{
    kObj(GoEthernet, ethernet);
    kArrayList list = kNULL;
    kArrayList optionList = kNULL;

    kCheck(GoSensor_SyncConfig(obj->sensor));


    // Section and Section Intensity:
    //  - GoEthernet_RemoveCompositeSource must be used for sections.
     if ((type != GO_OUTPUT_SOURCE_SECTION) &&
            (type != GO_OUTPUT_SOURCE_SECTION_INTENSITY))
    {
        list = GoEthernet_SourceList(ethernet, type);
        optionList = GoEthernet_OptionList(ethernet, type);
    }

    if (kIsNull(list) || kIsNull(optionList))
    {
        return kERROR_PARAMETER;
    }

    kCheckArgs(GoOptionList_Check32s(kArrayList_DataT(optionList, const k32s), kArrayList_Count(optionList), sourceId));
    kCheck(kArrayList_AddT(list, &sourceId));
    kCheck(GoSensor_SetConfigModified(obj->sensor));

    return kOK;
}

GoFx(kStatus) GoEthernet_RemoveSource(GoEthernet ethernet, GoOutputSource type, kSize index)
{
    kObj(GoEthernet, ethernet);
    kArrayList list = kNULL;

    kCheck(GoSensor_SyncConfig(obj->sensor));

    // Section and Section Intensity:
    //  - GoEthernet_RemoveCompositeSource must be used for sections.
    // Event:
    //  - Use the event specific APIs to add and remove events.
    if ((type != GO_OUTPUT_SOURCE_EVENT)   &&
        (type != GO_OUTPUT_SOURCE_SECTION) &&
        (type != GO_OUTPUT_SOURCE_SECTION_INTENSITY))
    {
        list = GoEthernet_SourceList(ethernet, type);
    }

    if (kIsNull(list))
    {
        return kERROR_PARAMETER;
    }

    kCheck(kArrayList_Discard(list, index));
    kCheck(GoSensor_SetConfigModified(obj->sensor));

    return kOK;
}

GoFx(kSize) GoEthernet_CompositeOptionCount(GoEthernet ethernet, GoOutputSource type)
{
    kObj(GoEthernet, ethernet);
    kArrayList list = kNULL;

    GoSensor_SyncConfig(obj->sensor);

    if (type == GO_OUTPUT_SOURCE_SECTION ||
        type == GO_OUTPUT_SOURCE_SECTION_INTENSITY)
    {
        list = GoEthernet_OptionList(ethernet, type);
    }

    if (kIsNull(list))
    {
        return 0;
    }

    return kArrayList_Count(list);
}

GoFx(GoOutputCompositeSource) GoEthernet_CompositeOptionAt(GoEthernet ethernet, GoOutputSource type, kSize index)
{
    kObj(GoEthernet, ethernet);
    kArrayList list = kNULL;
    GoOutputCompositeSource failedSearch;

    failedSearch.id = -1;
    failedSearch.dataSource = GO_DATA_SOURCE_NONE;

    GoSensor_SyncConfig(obj->sensor);

    if (type == GO_OUTPUT_SOURCE_SECTION ||
        type == GO_OUTPUT_SOURCE_SECTION_INTENSITY)
    {
        list = GoEthernet_OptionList(ethernet, type);
    }

    if (kIsNull(list))
    {
        return failedSearch;
    }

    kAssert(index < kArrayList_Count(list));

    return kArrayList_AsT(list, index, GoOutputCompositeSource);
}

GoFx(kSize) GoEthernet_CompositeSourceCount(GoEthernet ethernet, GoOutputSource type)
{
    kObj(GoEthernet, ethernet);
    kArrayList list = kNULL;

    GoSensor_SyncConfig(obj->sensor);

    if (type == GO_OUTPUT_SOURCE_SECTION ||
        type == GO_OUTPUT_SOURCE_SECTION_INTENSITY)
    {
        list = GoEthernet_OptionList(ethernet, type);
    }

    if (kIsNull(list))
    {
        return 0;
    }

    return kArrayList_Count(list);
}

GoFx(GoOutputCompositeSource) GoEthernet_CompositeSourceAt(GoEthernet ethernet, GoOutputSource type, kSize index)
{
    kObj(GoEthernet, ethernet);
    kArrayList list = kNULL;
    GoOutputCompositeSource failedSearch;

    failedSearch.id = -1;
    failedSearch.dataSource = GO_DATA_SOURCE_NONE;

    GoSensor_SyncConfig(obj->sensor);

    if (type == GO_OUTPUT_SOURCE_SECTION ||
        type == GO_OUTPUT_SOURCE_SECTION_INTENSITY)
    {
        list = GoEthernet_OptionList(ethernet, type);
    }

    if (kIsNull(list))
    {
        return failedSearch;
    }

    kAssert(index < kArrayList_Count(list));

    return kArrayList_AsT(list, index, GoOutputCompositeSource);
}

GoFx(kStatus) GoEthernet_AddCompositeSource(GoEthernet ethernet, GoOutputSource type, GoOutputCompositeSource option)
{
    kObj(GoEthernet, ethernet);
    kArrayList list = kNULL;
    kArrayList optionList = kNULL;

    kCheck(GoSensor_SyncConfig(obj->sensor));

    if (type == GO_OUTPUT_SOURCE_SECTION ||
        type == GO_OUTPUT_SOURCE_SECTION_INTENSITY)
    {
        list = GoEthernet_SourceList(ethernet, type);
        optionList = GoEthernet_OptionList(ethernet, type);
    }

    if (kIsNull(list) || kIsNull(optionList))
    {
        return kERROR_PARAMETER;
    }

    kCheck(GoEthernet_CheckCompositeSource(kArrayList_DataT(optionList, const GoOutputCompositeSource), kArrayList_Count(optionList), option));
    kCheck(kArrayList_AddT(list, &option));
    kCheck(GoSensor_SetConfigModified(obj->sensor));

    return kOK;
}

GoFx(kStatus) GoEthernet_RemoveCompositeSource(GoEthernet ethernet, GoOutputSource type, kSize index)
{
    kObj(GoEthernet, ethernet);
    kArrayList list = kNULL;

    kCheck(GoSensor_SyncConfig(obj->sensor));

    if (type == GO_OUTPUT_SOURCE_SECTION ||
        type == GO_OUTPUT_SOURCE_SECTION_INTENSITY)
    {
        list = GoEthernet_SourceList(ethernet, type);
    }

    if (kIsNull(list))
    {
        return kERROR_PARAMETER;
    }

    kCheck(kArrayList_Discard(list, index));
    kCheck(GoSensor_SetConfigModified(obj->sensor));

    return kOK;
}


GoFx(kStatus) GoEthernet_ClearAllSources(GoEthernet ethernet)
{
    kObj(GoEthernet, ethernet);
    kSize               i;
    kSize               numMapEntry = kCountOf(g_EthernetOutputSourceTypeMap);

    kCheck(GoSensor_SyncConfig(obj->sensor));

    for (i = 0; i < numMapEntry; i++)
    {
        kCheck(GoEthernet_ClearSources(ethernet, (GoOutputSource) g_EthernetOutputSourceTypeMap[i].value));
    }

    return kOK;
}

GoFx(kStatus) GoEthernet_ClearSources(GoEthernet ethernet, GoOutputSource type)
{
    kObj(GoEthernet, ethernet);
    kArrayList list;

    list = GoEthernet_SourceList(ethernet, type);
    if (kIsNull(list))
    {
        return kERROR_PARAMETER;
    }

    kCheck(kArrayList_Clear(list));
    kCheck(GoSensor_SetConfigModified(obj->sensor));

    return kOK;
}


GoFx(k64f) GoEthernet_Timeout(GoEthernet ethernet)
{
    kObj(GoEthernet, ethernet);

    GoSensor_SyncConfig(obj->sensor);

    return obj->timeout;
}

GoFx(kStatus) GoEthernet_SetTimeout(GoEthernet ethernet, k64f value)
{
    kObj(GoEthernet, ethernet);

    kCheckArgs(value >= 0.0);

    obj->timeout = value;

    kCheck(GoSensor_SetConfigModified(obj->sensor));

    return kOK;
}

GoFx(kBool) GoEthernet_TimeoutEnabled(GoEthernet ethernet)
{
    kObj(GoEthernet, ethernet);

    GoSensor_SyncConfig(obj->sensor);

    return obj->timeoutEnabled;
}

GoFx(kStatus) GoEthernet_EnableTimeout(GoEthernet ethernet, kBool value)
{
    kObj(GoEthernet, ethernet);

    obj->timeoutEnabled = value;

    kCheck(GoSensor_SetConfigModified(obj->sensor));

    return kOK;
}

GoFx(kBool) GoEthernet_TimeoutEnabledIsAvailable(GoEthernet ethernet)
{
    kObj(GoEthernet, ethernet);

    GoSensor_SyncConfig(obj->sensor);

    return obj->timeoutEnabledAvail;
}

GoFx(GoEthernetProtocol) GoEthernet_Protocol(GoEthernet ethernet)
{
    kObj(GoEthernet, ethernet);

    GoSensor_SyncConfig(obj->sensor);

    return obj->protocol;
}

GoFx(kStatus) GoEthernet_SetProtocol(GoEthernet ethernet, GoEthernetProtocol protocol)
{
    kObj(GoEthernet, ethernet);

    obj->protocol = protocol;
    kCheck(GoSensor_SetConfigModified(obj->sensor));

    return kOK;
}

GoFx(GoAsciiOperation) GoEthernet_AsciiOperation(GoEthernet ethernet)
{
    kObj(GoEthernet, ethernet);

    GoSensor_SyncConfig(obj->sensor);

    return obj->ascii.operation;
}

GoFx(kStatus) GoEthernet_SetAsciiOperation(GoEthernet ethernet, GoAsciiOperation mode)
{
    kObj(GoEthernet, ethernet);

    obj->ascii.operation = mode;
    kCheck(GoSensor_SetConfigModified(obj->sensor));

    return kOK;
}

GoFx(k32u) GoEthernet_AsciiControlPort(GoEthernet ethernet)
{
    kObj(GoEthernet, ethernet);

    GoSensor_SyncConfig(obj->sensor);

    return obj->ascii.controlPort;
}

GoFx(kStatus) GoEthernet_SetAsciiControlPort(GoEthernet ethernet, k32u port)
{
    kObj(GoEthernet, ethernet);

    obj->ascii.controlPort = port;
    kCheck(GoSensor_SetConfigModified(obj->sensor));

    return kOK;
}

GoFx(k32u) GoEthernet_AsciiHealthPort(GoEthernet ethernet)
{
    kObj(GoEthernet, ethernet);

    GoSensor_SyncConfig(obj->sensor);

    return obj->ascii.healthPort;
}

GoFx(kStatus) GoEthernet_SetAsciiHealthPort(GoEthernet ethernet, k32u port)
{
    kObj(GoEthernet, ethernet);

    obj->ascii.healthPort = port;
    kCheck(GoSensor_SetConfigModified(obj->sensor));

    return kOK;
}

GoFx(k32u) GoEthernet_AsciiDataPort(GoEthernet ethernet)
{
    kObj(GoEthernet, ethernet);

    GoSensor_SyncConfig(obj->sensor);

    return obj->ascii.dataPort;
}

GoFx(kStatus) GoEthernet_SetAsciiDataPort(GoEthernet ethernet, k32u port)
{
    kObj(GoEthernet, ethernet);

    obj->ascii.dataPort = port;
    kCheck(GoSensor_SetConfigModified(obj->sensor));

    return kOK;
}

GoFx(kChar*) GoEthernet_AsciiDelimiter(GoEthernet ethernet)
{
    kObj(GoEthernet, ethernet);

    GoSensor_SyncConfig(obj->sensor);

    return kString_Chars(obj->ascii.delimiter);
}

GoFx(kStatus) GoEthernet_SetAsciiDelimiter(GoEthernet ethernet, const kChar* string)
{
    kObj(GoEthernet, ethernet);

    kCheck(kString_Set(obj->ascii.delimiter, string));
    kCheck(GoSensor_SetConfigModified(obj->sensor));

    return kOK;
}

GoFx(kChar*) GoEthernet_AsciiTerminator(GoEthernet ethernet)
{
    kObj(GoEthernet, ethernet);

    GoSensor_SyncConfig(obj->sensor);

    return kString_Chars(obj->ascii.terminator);
}

GoFx(kStatus) GoEthernet_SetAsciiTerminator(GoEthernet ethernet, const kChar* string)
{
    kObj(GoEthernet, ethernet);

    kCheck(kString_Set(obj->ascii.terminator, string));
    kCheck(GoSensor_SetConfigModified(obj->sensor));

    return kOK;
}

GoFx(kChar*) GoEthernet_AsciiInvalidValue(GoEthernet ethernet)
{
    kObj(GoEthernet, ethernet);

    GoSensor_SyncConfig(obj->sensor);

    return kString_Chars(obj->ascii.invalidValue);
}

GoFx(kStatus) GoEthernet_SetAsciiInvalidValue(GoEthernet ethernet, const kChar* string)
{
    kObj(GoEthernet, ethernet);

    kCheck(kString_Set(obj->ascii.invalidValue, string));
    kCheck(GoSensor_SetConfigModified(obj->sensor));

    return kOK;
}

GoFx(kChar*) GoEthernet_AsciiCustomDataFormat(GoEthernet ethernet)
{
    kObj(GoEthernet, ethernet);

    GoSensor_SyncConfig(obj->sensor);

    return kString_Chars(obj->ascii.customFormat);
}

GoFx(kStatus) GoEthernet_SetAsciiCustomDataFormat(GoEthernet ethernet, const kChar* string)
{
    kObj(GoEthernet, ethernet);

    kCheck(kString_Set(obj->ascii.customFormat, string));
    kCheck(GoSensor_SetConfigModified(obj->sensor));

    return kOK;
}

GoFx(kStatus) GoEthernet_EnableAsciiCustomFormat(GoEthernet ethernet, kBool enabled)
{
    kObj(GoEthernet, ethernet);

    obj->ascii.customFormatEnabled = enabled;
    kCheck(GoSensor_SetConfigModified(obj->sensor));

    return kOK;
}

GoFx(kBool) GoEthernet_AsciiCustomFormatEnabled(GoEthernet ethernet)
{
    kObj(GoEthernet, ethernet);

    GoSensor_SyncConfig(obj->sensor);

    return obj->ascii.customFormatEnabled;
}

GoFx(kStatus) GoEthernet_SetAsciiStandardFormat(GoEthernet ethernet, GoAsciiStandardFormatMode mode)
{
    kObj(GoEthernet, ethernet);

    obj->ascii.standardFormatMode = mode;
    kCheck(GoSensor_SetConfigModified(obj->sensor));

    return kOK;
}

GoFx(GoAsciiStandardFormatMode) GoEthernet_AsciiStandardFormat(GoEthernet ethernet)
{
    kObj(GoEthernet, ethernet);

    GoSensor_SyncConfig(obj->sensor);

    return obj->ascii.standardFormatMode;
}

GoFx(kStatus) GoEthernet_SetEIPBufferingEnabled(GoEthernet ethernet, kBool enabled)
{
    kObj(GoEthernet, ethernet);

    obj->eip.bufferEnabled = enabled;
    kCheck(GoSensor_SetConfigModified(obj->sensor));

    return kOK;
}

GoFx(kBool) GoEthernet_EIPBufferingEnabled(GoEthernet ethernet)
{
    kObj(GoEthernet, ethernet);

    GoSensor_SyncConfig(obj->sensor);

    return obj->eip.bufferEnabled;
}

GoFx(kStatus) GoEthernet_SetEIPImplicitOutputEnabled(GoEthernet ethernet, kBool enabled)
{
    kObj(GoEthernet, ethernet);

    obj->eip.implicitOutputEnabled = enabled;
    kCheck(GoSensor_SetConfigModified(obj->sensor));

    return kOK;
}

GoFx(kBool) GoEthernet_EIPImplicitOutputEnabled(GoEthernet ethernet)
{
    kObj(GoEthernet, ethernet);

    GoSensor_SyncConfig(obj->sensor);

    return obj->eip.implicitOutputEnabled;
}

GoFx(kStatus) GoEthernet_SetEIPEndianOutputType(GoEthernet ethernet, GoEndianType type)
{
    kObj(GoEthernet, ethernet);

    obj->eip.endianOutputType = type;
    kCheck(GoSensor_SetConfigModified(obj->sensor));

    return kOK;
}

GoFx(GoEndianType) GoEthernet_EIPEndianOutputType(GoEthernet ethernet)
{
    kObj(GoEthernet, ethernet);

    GoSensor_SyncConfig(obj->sensor);

    return obj->eip.endianOutputType;
}

GoFx(kStatus) GoEthernet_SetEIPImplicitTriggerOverride(GoEthernet ethernet, GoImplicitTriggerOverride value)
{
    kObj(GoEthernet, ethernet);

    obj->eip.implicitTriggerOverride = value;
    kCheck(GoSensor_SetConfigModified(obj->sensor));

    return kOK;
}

GoFx(GoImplicitTriggerOverride) GoEthernet_EIPImplicitTriggerOverride(GoEthernet ethernet)
{
    kObj(GoEthernet, ethernet);

    GoSensor_SyncConfig(obj->sensor);

    return obj->eip.implicitTriggerOverride;
}


GoFx(kStatus) GoEthernet_SetModbusBufferingEnabled(GoEthernet ethernet, kBool enabled)
{
    kObj(GoEthernet, ethernet);

    obj->modbus.bufferEnabled = enabled;
    kCheck(GoSensor_SetConfigModified(obj->sensor));

    return kOK;
}

GoFx(kBool) GoEthernet_ModbusBufferingEnabled(GoEthernet ethernet)
{
    kObj(GoEthernet, ethernet);

    GoSensor_SyncConfig(obj->sensor);

    return obj->modbus.bufferEnabled;
}

GoFx(const kIpAddress*) GoEthernet_ProfinetIpAddress(GoEthernet ethernet)
{
    kObj(GoEthernet, ethernet);

    GoSensor_SyncConfig(obj->sensor);

    return (const kIpAddress*) &obj->profinet.ipAddress;
}

GoFx(const kIpAddress*) GoEthernet_ProfinetSubnetMask(GoEthernet ethernet)
{
    kObj(GoEthernet, ethernet);

    GoSensor_SyncConfig(obj->sensor);

    return (const kIpAddress*) &obj->profinet.subnetMask;
}

GoFx(const kIpAddress*) GoEthernet_ProfinetGateway(GoEthernet ethernet)
{
    kObj(GoEthernet, ethernet);

    GoSensor_SyncConfig(obj->sensor);

    return (const kIpAddress*) &obj->profinet.gateway;
}

GoFx(const kChar*) GoEthernet_ProfinetDeviceName(GoEthernet ethernet)
{
    kObj(GoEthernet, ethernet);

    GoSensor_SyncConfig(obj->sensor);

    return (const kChar*) kString_Chars(obj->profinet.deviceName);
}

GoFx(kSize) GoEthernet_EventOptionCount(GoEthernet ethernet)
{
    kObj(GoEthernet, ethernet);

    return kArrayList_Count(obj->eventOptions);
}

GoFx(GoEventType) GoEthernet_EventOptionAt(GoEthernet ethernet, kSize index)
{
    kObj(GoEthernet, ethernet);

    kAssert(index < kArrayList_Count(obj->eventOptions));

    return *kArrayList_AtT(obj->eventOptions, index, GoEventType);
}

GoFx(kSize) GoEthernet_EventCount(GoEthernet ethernet)
{
    kObj(GoEthernet, ethernet);

    return kArrayList_Count(obj->events);
}

GoFx(GoEventType) GoEthernet_EventAt(GoEthernet ethernet, kSize index)
{
    kObj(GoEthernet, ethernet);

    kAssert(index < kArrayList_Count(obj->events));

    return *kArrayList_AtT(obj->events, index, GoEventType);
}

GoFx(kStatus) GoEthernet_AddEvent(GoEthernet ethernet, GoEventType type)
{
    kObj(GoEthernet, ethernet);

    kCheck(kArrayList_AddT(obj->events, &type));
    kCheck(GoSensor_SetConfigModified(obj->sensor));

    return kOK;
}

GoFx(kStatus) GoEthernet_RemoveEvent(GoEthernet ethernet, kSize index)
{
    kObj(GoEthernet, ethernet);

    kAssert(index < kArrayList_Count(obj->eventOptions));
    kCheck(kArrayList_Discard(obj->eventOptions, index));
    kCheck(GoSensor_SetConfigModified(obj->sensor));

    return kOK;
}

GoFx(kStatus) GoEthernet_ClearEvents(GoEthernet ethernet)
{
    kObj(GoEthernet, ethernet);

    kCheck(kArrayList_Clear(obj->events));
    kCheck(GoSensor_SetConfigModified(obj->sensor));

    return kOK;
}
