Files
2024-05-18 23:53:26 +08:00

557 lines
19 KiB
C

/**
* @file GoDiscoveryExtInfo.c
*
* @internal
* Copyright (C) 2016-2021 by LMI Technologies Inc.
* Licensed under the MIT License.
* Redistributed files must retain the above copyright notice.
*/
#include <GoSdk/Messages/GoDiscoveryExtInfo.h>
kBeginValueEx(Go, GoDiscoveryProperty)
kAddField(GoDiscoveryProperty, kText256, name)
kAddField(GoDiscoveryProperty, kText256, value)
kEndValueEx()
/*
* GoDiscoveryExtInfo
*/
kBeginClassEx(Go, GoDiscoveryExtInfo)
//virtual methods
kAddVMethod(GoDiscoveryExtInfo, kObject, VInitClone)
kAddVMethod(GoDiscoveryExtInfo, kObject, VRelease)
kAddVMethod(GoDiscoveryExtInfo, kObject, VSize)
kEndClassEx()
GoFx(kStatus) GoDiscoveryExtInfo_Construct(GoDiscoveryExtInfo* msg, kAlloc allocator)
{
kAlloc alloc = kAlloc_Fallback(allocator);
kStatus status;
kCheck(kAlloc_GetObject(alloc, kTypeOf(GoDiscoveryExtInfo), msg));
if (!kSuccess(status = GoDiscoveryExtInfo_Init(*msg, kTypeOf(GoDiscoveryExtInfo), alloc)))
{
kAlloc_FreeRef(alloc, msg);
}
return status;
}
GoFx(kStatus) GoDiscoveryExtInfo_Init(GoDiscoveryExtInfo msg, kType type, kAlloc alloc)
{
kObjR(GoDiscoveryExtInfo, msg);
kCheck(kObject_Init(msg, type, alloc));
obj->id = 0;
kZero(obj->address);
kZero(obj->ports);
kZero(obj->version);
obj->uptime = 0;
kZero(obj->properties);
obj->opMode = 0;
obj->accelSensorIpAddress = kIpAddress_AnyV4();
return kOK;
}
GoFx(kStatus) GoDiscoveryExtInfo_VInitClone(GoDiscoveryExtInfo msg, GoDiscoveryExtInfo source, kAlloc alloc)
{
kObjR(GoDiscoveryExtInfo, msg);
kObjN(GoDiscoveryExtInfo, srcObj, source);
kStatus status;
kCheck(GoDiscoveryExtInfo_Init(msg, kObject_Type(source), alloc));
kTry
{
obj->id = srcObj->id;
obj->version = srcObj->version;
obj->uptime = srcObj->uptime;
kMemCopy(&(obj->address), &(srcObj->address), sizeof(GoAddressInfo));
kMemCopy(&(obj->ports), &(srcObj->ports), sizeof(GoPortInfo));
kTest(kObject_Clone(&obj->properties, srcObj->properties, alloc));
}
kCatch(&status)
{
GoDiscoveryExtInfo_VRelease(msg);
kEndCatch(status);
}
return kOK;
}
GoFx(kStatus) GoDiscoveryExtInfo_AllocateProperties(GoDiscoveryExtInfo msg, kSize count)
{
kObj(GoDiscoveryExtInfo, msg);
kCheck(kDisposeRef(&obj->properties));
kCheck(kArrayList_Construct(&obj->properties, kTypeOf(GoDiscoveryProperty), count, kObject_Alloc(msg)));
kCheck(kArrayList_AddCount(obj->properties, count));
return kOK;
}
GoFx(kStatus) GoDiscoveryExtInfo_VRelease(GoDiscoveryExtInfo msg)
{
kObj(GoDiscoveryExtInfo, msg);
kCheck(kDisposeRef(&obj->properties));
return kObject_VRelease(msg);
}
GoFx(kSize) GoDiscoveryExtInfo_VSize(GoDiscoveryExtInfo msg)
{
kObj(GoDiscoveryExtInfo, msg);
return sizeof(GoDiscoveryExtInfoClass) + (kIsNull(obj->properties) ? 0 : kObject_Size(obj->properties));
}
GoFx(k32u) GoDiscoveryExtInfo_SubnetPrefixToMask(k32u prefix)
{
k32u mask = 0;
k32s lower = 32 - (k32s)prefix;
k32s i = 0;
for (i = 31; i >= lower; --i)
{
mask |= (1 << i);
}
return mask;
}
GoFx(kStatus) GoDiscoveryExtInfo_Read(GoDiscoveryExtInfo msg, kSerializer serializer, kAlloc alloc)
{
kObj(GoDiscoveryExtInfo, msg);
k64s responseId;
k32u prefixLength;
k64s signature;
kByte useDhcp;
kByte tempByte;
k64s status;
k16u attrSize;
k8u propertyCount;
kBool accelInfoInProperty = kFALSE;
kCheck(GoDiscoveryExtInfo_Init(msg, kTypeOf(GoDiscoveryExtInfo), alloc));
kTry
{
// GOC-12805: changed writing the header as 32-bit data to 64-bit data
// for consistency with the other replies.
// kSerializer always writes data out in little endian format even for
// big endian architecture, so changing to 64-bit writes should not
// cause any backwards compatibiity issues with older SDK or older
// sensor software.
//
// The size field is included in the message size count stored in the size field.
kTest(kSerializer_BeginRead(serializer, kTypeOf(k64u), kTRUE));
kTest(kSerializer_Read64s(serializer, &responseId));
// The 5.1 and newer SDK sends both a GET ADDRESS and GET INFO
// discovery request. A 5.1 and newer sensor firmware version
// supports both requests and therefore sends back both replies.
// In the caller to this function, a reply is assumed to be a GET ADDRESS
// reply and if it is not, then the reply is parsed as a GET
// INFO reply.
// If the order were reversed, then it is valid that the
// response id is not an GET INFO REPLY so
// it is not a real error but an expected scenario.
if (responseId != GO_DISCOVERY_GET_INFO_REPLY)
{
// Flush the remaining data in the byte stream following the first
// three 32-bit integers that were already read.
// Also free allocated serializer memory.
kTest(kSerializer_EndRead(serializer));
kThrow(kERROR_COMMAND);
}
kTest(kSerializer_Read64s(serializer, &status));
kTest(status);
kTest(kSerializer_Read64s(serializer, &signature));
kTest(signature == GO_DISOVERY_SIGNATURE);
// This block of code must not change or else the change will break
// backwards compatibility with older sensor software.
{
kTest(kSerializer_Read16u(serializer, &attrSize));
// The number of bytes of data up to the properties is expected to never
// change across SDK versions.
// This can never change for forwards and backwards compatibility
// between SDK and sensor code.
kTest(attrSize == GO_DISCOVERY_GET_INFO_REPLY_ATTR_SIZE);
kTest(kSerializer_Read32u(serializer, &obj->id));
kTest(kSerializer_Read32u(serializer, &obj->version));
kTest(kSerializer_Read64u(serializer, &obj->uptime));
kTest(kSerializer_ReadByte(serializer, &useDhcp));
obj->address.useDhcp = (kBool) useDhcp;
kTest(kSerializer_ReadByte(serializer, &tempByte));
obj->address.address.version = (kIpVersion) tempByte;
// The size of this block of code assumes an IP address is sent
// as 16 bytes from the sensor.
kAssert(kCountOf(obj->address.address.address) == 16);
kTest(kSerializer_ReadByteArray(serializer, obj->address.address.address, kCountOf(obj->address.address.address)));
kTest(kSerializer_Read32u(serializer, &prefixLength));
obj->address.mask = kIpAddress_FromHost32u(GoDiscoveryExtInfo_SubnetPrefixToMask(prefixLength));
kTest(kSerializer_ReadByte(serializer, &tempByte));
obj->address.gateway.version = (kIpVersion) tempByte;
// Size of IP address has been verified above to be 16 bytes.
kTest(kSerializer_ReadByteArray(serializer, obj->address.gateway.address, kCountOf(obj->address.gateway.address)));
kTest(kSerializer_Read16u(serializer, &obj->ports.controlPort));
kTest(kSerializer_Read16u(serializer, &obj->ports.upgradePort));
kTest(kSerializer_Read16u(serializer, &obj->ports.healthPort));
kTest(kSerializer_Read16u(serializer, &obj->ports.dataPort));
kTest(kSerializer_Read16u(serializer, &obj->ports.webPort));
}
kTest(kSerializer_Read8u(serializer, &propertyCount));
if (propertyCount > 0)
{
kTest(GoDiscoveryExtInfo_ReadProperties(msg, serializer, propertyCount));
}
// Acceleration info was added to 5.3 sensor software.
kTest(GoDiscoveryExtInfo_ExtractAccelSensorInfoFromProperty(msg, &accelInfoInProperty));
// If the acceleration info is found in the properties, then skip the rest of the data
// in the data stream. Otherwise, try to find the acceleration info in
// the message data that come after the properties.
if (!accelInfoInProperty)
{
// Sensor software is pre-5.3.
// Check if SDK is talking to an sensor software version before 5.1 that does
// not have the extra accelerated sensor data following the properties.
if (!kSerializer_ReadCompleted(serializer))
{
// Sensor software is 5.1 or 5.2.
kTest(GoDiscoveryExtInfo_ExtractAccelSensorInfoFromMsg(msg, serializer));
}
else
{
// Older sensor firmware (pre-5.1) doesn't support this field, so initialize
// this field to a sensible value.
obj->opMode = GO_DISCOVERY_OP_MODE_NOT_AVAILABLE;
// Initialize IP version to valid value even though the address may
// not be meaningful. This prevents errors if code tries to convert the
// meaningless address to a string.
obj->accelSensorIpAddress.version = (kIpVersion) kIP_VERSION_4;
}
}
}
kFinally
{
// Automatically flush any unread data.
// TODO: Older 5.0 code's discovery protocol sends the incorrect size
// of the byte stream, which causes the EndRead() call to sometimes fail
// because it looks like too much data was read.
// Until that older code is no longer supported, suppress error
// checking the return code of EndRead().
kSerializer_EndRead(serializer);
kEndFinally();
}
return kOK;
}
GoFx(k32u) GoDiscoveryExtInfo_Id(GoDiscoveryExtInfo msg)
{
return GoDiscoveryExtInfo_Content_(msg)->id;
}
GoFx(GoAddressInfo) GoDiscoveryExtInfo_Address(GoDiscoveryExtInfo msg)
{
return GoDiscoveryExtInfo_Content_(msg)->address;
}
GoFx(GoPortInfo) GoDiscoveryExtInfo_Ports(GoDiscoveryExtInfo msg)
{
return GoDiscoveryExtInfo_Content_(msg)->ports;
}
GoFx(kVersion) GoDiscoveryExtInfo_Version(GoDiscoveryExtInfo msg)
{
return GoDiscoveryExtInfo_Content_(msg)->version;
}
GoFx(k64u) GoDiscoveryExtInfo_UpTime(GoDiscoveryExtInfo msg)
{
return GoDiscoveryExtInfo_Content_(msg)->uptime;
}
GoFx(kSize) GoDiscoveryExtInfo_PropertyCount(GoDiscoveryExtInfo msg)
{
return kArrayList_Count(GoDiscoveryExtInfo_Content_(msg)->properties);
}
GoFx(const GoDiscoveryProperty*) GoDiscoveryExtInfo_PropertyAt(GoDiscoveryExtInfo msg, kSize index)
{
if (index >= kArrayList_Count(GoDiscoveryExtInfo_Content_(msg)->properties))
{
return kNULL;
}
return kArrayList_AtT(GoDiscoveryExtInfo_Content_(msg)->properties, index, GoDiscoveryProperty);
}
GoFx(GoDiscoveryOpMode) GoDiscoveryExtInfo_OpMode(GoDiscoveryExtInfo msg)
{
return GoDiscoveryExtInfo_Content_(msg)->opMode;
}
GoFx(kIpAddress) GoDiscoveryExtInfo_AccelSensorIpAddress(GoDiscoveryExtInfo msg)
{
return GoDiscoveryExtInfo_Content_(msg)->accelSensorIpAddress;
}
// DEPRECATED
GoFx(kStatus) GoDiscoveryExtInfo_CompleteModel(GoDiscoveryExtInfo msg, kChar* model, kSize capacity)
{
// Complete Model is deprecated (since it was ambiguous), provide the original behavior
// which was to simply return partNumber.
return GoDiscoveryExtInfo_PartNumber(msg, model, capacity);
}
// Acceleration information was put as part of the extended information
// after the property list in 5.1 and 5.2 sensor software.
GoFx(kStatus) GoDiscoveryExtInfo_ExtractAccelSensorInfoFromMsg(GoDiscoveryExtInfo msg, kSerializer serializer)
{
kObj(GoDiscoveryExtInfo, msg);
kByte ipVersion;
// Get opmode and host ip address even if controller is not an accelerator host.
// The IP address must be read from stream otherwise it will be read and
// interpreted as other data.
kCheck(kSerializer_Read8u(serializer, &obj->opMode));
kCheck(kSerializer_ReadByte(serializer, &ipVersion));
obj->accelSensorIpAddress.version = (kIpVersion) ipVersion;
// The size of an IP address sent by sensor is expected to be 16 bytes and
// can never change because of backwards and forwards compatibility between
// SDK and sensor code.
kAssert(kCountOf(obj->accelSensorIpAddress.address) == 16);
kCheck(kSerializer_ReadByteArray(serializer, obj->accelSensorIpAddress.address, kCountOf(obj->accelSensorIpAddress.address)));
return kOK;
}
// This function is used to work with sensor software 5.3 and newer where the
// accelerated sensor information is stored in the properties.
// The information is also still available in the data stream that follows the
// properties, when it was implemented in 5.1 and 5.2 sensor software,
// but the data following the properties is being obsoleted from 5.3 onwards, so
// don't use it unless the SDK is interacting with a 5.1/5.2 sensor software.
// The flag 'foundAccelInfo' indicates whether the properties has
GoFx(kStatus) GoDiscoveryExtInfo_ExtractAccelSensorInfoFromProperty(GoDiscoveryExtInfo msg, kBool* foundAccelInfo)
{
kObj(GoDiscoveryExtInfo, msg);
kSize propCount;
kSize i;
const GoDiscoveryProperty* propertyPtr;
kByte ipVersion = 0;
// Initialize return value.
*foundAccelInfo = kFALSE;
propCount = GoDiscoveryExtInfo_PropertyCount(msg);
for (i = 0; i < propCount; i++)
{
propertyPtr = GoDiscoveryExtInfo_PropertyAt(msg, i);
if (kStrnEquals(propertyPtr->name, "OpMode", kCountOf(propertyPtr->name)))
{
kCheck(k8u_Parse(&obj->opMode, propertyPtr->value));
}
else if (kStrnEquals(propertyPtr->name, "AccelSensorIpVersion", kCountOf(propertyPtr->name)))
{
kCheck(k8u_Parse(&ipVersion, propertyPtr->value));
}
else if (kStrnEquals(propertyPtr->name, "AccelSensorIp", kCountOf(propertyPtr->name)))
{
// Check that the accelerated sensor IP address is sane.
if (ipVersion == kIP_VERSION_4)
{
kCheck(kIpAddress_Parse(&obj->accelSensorIpAddress, propertyPtr->value));
*foundAccelInfo = kTRUE;
}
}
}
return kOK;
}
GoFx(kStatus) GoDiscoveryExtInfo_ReadProperties(GoDiscoveryExtInfo msg, kSerializer serializer, k8u propertyCount)
{
kObj(GoDiscoveryExtInfo, msg);
k8u i;
kStatus exception = kOK;
k8u nameLength;
k8u valueLength;
GoDiscoveryProperty* property = kNULL;
kTry
{
kTest(GoDiscoveryExtInfo_AllocateProperties(msg, propertyCount));
for (i = 0; i < propertyCount; i++)
{
property = kArrayList_AtT(obj->properties, i, GoDiscoveryProperty);
kTest(kSerializer_Read8u(serializer, &nameLength));
kTest(kSerializer_ReadCharArray(serializer, property->name, nameLength));
property->name[nameLength] = 0;
kTest(kSerializer_Read8u(serializer, &valueLength));
kTest(kSerializer_ReadCharArray(serializer, property->value, valueLength));
property->value[valueLength] = 0;
}
}
kCatch(&exception)
{
kEndCatch(exception);
}
return kOK;
}
GoFx(kStatus) GoDiscoveryExtInfo_PartNumber(GoDiscoveryExtInfo msg, kChar* partNumber, kSize capacity)
{
kObj(GoDiscoveryExtInfo, msg);
kSize foundIndex;
const GoDiscoveryProperty* propertyPtr;
// PartNumber may not exist in very old sensors (see GOC-12999) and is filled
// within the ExtendedId as "FullModel".
// Only return blank if NEITHER "FullModel" nor "PartNumber" are present.
if ((foundIndex = GoDiscoveryExtInfo_FindProperty(msg, "FullModel")) == kSIZE_NULL &&
(foundIndex = GoDiscoveryExtInfo_FindProperty(msg, "PartNumber")) == kSIZE_NULL)
{
kCheck(kStrCopy(partNumber, capacity, ""));
}
else
{
propertyPtr = GoDiscoveryExtInfo_PropertyAt(msg, foundIndex);
kCheck(kStrCopy(partNumber, capacity, propertyPtr->value));
}
return kOK;
}
GoFx(kStatus) GoDiscoveryExtInfo_FillModelNumber(GoDiscoveryExtInfo msg, kChar* fallBackModelNumber, kSize capacity)
{
kObj(GoDiscoveryExtInfo, msg);
kSize foundIndex;
const GoDiscoveryProperty* propertyPtr;
// Model holds the number XXXX after the Gocator_XXXX and has been filled in as part of
// ExtendedId since at least 4.1.x.x (~2014) and has been part of discovery info as far
// back as GOC-3533,3576 (~2015-08). This will be present even if the underlying
// Id.xml is schemaVersion="1" and lacks ModelNumber itself.
if ((foundIndex = GoDiscoveryExtInfo_FindProperty(msg, "Model")) == kSIZE_NULL)
{
kCheck(kStrCopy(fallBackModelNumber, capacity, ""));
}
else
{
propertyPtr = GoDiscoveryExtInfo_PropertyAt(msg, foundIndex);
kCheck(kStrCopy(fallBackModelNumber, capacity, propertyPtr->value));
}
return kOK;
}
GoFx(kStatus) GoDiscoveryExtInfo_ModelNumber(GoDiscoveryExtInfo msg, kChar* modelNumber, kSize capacity)
{
kObj(GoDiscoveryExtInfo, msg);
kSize foundIndex;
const GoDiscoveryProperty* propertyPtr;
if ((foundIndex = GoDiscoveryExtInfo_FindProperty(msg, "ModelNumber")) == kSIZE_NULL)
{
// ModelNumber is part of Id.xml schemaVersion="2" and a gap left unfilled
// by ExtendedId for sensors with Id.xml schemaVersion="1".
// If not found -- fall back to the "Model" field using a fill function.
kCheck(GoDiscoveryExtInfo_FillModelNumber(msg, modelNumber, capacity));
}
else
{
propertyPtr = GoDiscoveryExtInfo_PropertyAt(msg, foundIndex);
kCheck(kStrCopy(modelNumber, capacity, propertyPtr->value));
}
return kOK;
}
GoFx(kStatus) GoDiscoveryExtInfo_ModelDisplayName(GoDiscoveryExtInfo msg, kChar* modelDisplayName, kSize capacity)
{
kObj(GoDiscoveryExtInfo, msg);
kSize foundIndex;
const GoDiscoveryProperty* propertyPtr;
if ((foundIndex = GoDiscoveryExtInfo_FindProperty(msg, "ModelDisplayName")) == kSIZE_NULL)
{
// If not found in pre-5.3 versions -- fall back to modelNumber.
kCheck(GoDiscoveryExtInfo_ModelNumber(msg, modelDisplayName, capacity));
}
else
{
propertyPtr = GoDiscoveryExtInfo_PropertyAt(msg, foundIndex);
kCheck(kStrCopy(modelDisplayName, capacity, propertyPtr->value));
}
return kOK;
}
// For internal use only to retrieve the LMI model number of the sensor.
GoFx(kStatus) GoDiscoveryExtInfo_Family(GoDiscoveryExtInfo msg, kChar* family, kSize capacity)
{
kObj(GoDiscoveryExtInfo, msg);
kSize foundIndex;
const GoDiscoveryProperty* propertyPtr;
// Family has been filled in as part of ExtendedId since at least 4.1.x.x (~2014).
// ExtendedId has been part of discovery info as far back as GOC-3533,3576 (~2015-08).
// So if Family doesn't exist -- the upstream caller will need to seek other
// non-discovery based information sources (sucn as GetSystemInfo()).
if ((foundIndex = GoDiscoveryExtInfo_FindProperty(msg, "Family")) == kSIZE_NULL)
{
kCheck(kStrCopy(family, capacity, ""));
}
else
{
propertyPtr = GoDiscoveryExtInfo_PropertyAt(msg, foundIndex);
kCheck(kStrCopy(family, capacity, propertyPtr->value));
}
return kOK;
}
GoFx(kSize) GoDiscoveryExtInfo_FindProperty(GoDiscoveryExtInfo msg, const kChar *name)
{
kSize propCount;
kSize i;
const GoDiscoveryProperty* propertyPtr;
propCount = GoDiscoveryExtInfo_PropertyCount(msg);
for (i = 0; i < propCount; i++)
{
propertyPtr = GoDiscoveryExtInfo_PropertyAt(msg, i);
if (kStrnEquals(propertyPtr->name, name, kCountOf(propertyPtr->name)))
{
return i;
}
}
return kSIZE_NULL;
}