Files
handleaiuinew/env/LMI/include/GoSdk/Internal/GoAccelSensorPortAlloc.c
T
2024-05-18 23:53:26 +08:00

1006 lines
37 KiB
C

/**
* @file GoAccelSensorPortAlloc.c
*
* This calls implements the sensor port allocator functionality for
* accelerated sensors.
* The ports allocated for an accelerated sensors are:
* - control channel port
* - upgrade channel port
* - health channel port
* - private data channel port
* - public data channel port
* - web/http server port
*
* SUPPORT FOR LEGACY BEHAVIOUR
* Legacy accelerator application uses the default sensor ports for the
* accelerated sensor (eg. 3190 for control port etc).
* This port allocator class will allocate the same ports to accelerated sensors
* if:
* - the port range starts from the default control port of 3190
* - only one sensor is ever accelerated in the system.
* - note that accelerating two sensors and then decelerating one of the
* sensors is NOT the same as accelerating only one sensor in the system,
* but instad is treated as accelerating more than one sensor.
*
* SUPPORT FOR MORE THAN ONE ACCELERATED SENSOR
* A. If more than one sensor is accelerated, and the port range starting number
* is the default control port (3190), then the second and subsequent sensors
* are assigned in consecutive order after the set of legacy port values, to
* maximize the use of the port range.
* If the allocator wraps around to the legacy port values, it can pick an
* an available port from within the legacy port range.
*
* B. If more than one sensor is accelerated, and the port range starting number
* is NOT the default control port (3190), then the legacy port numbers are not
* allocated to the first sensor. The allocated ports are chosen in consecutive
* order to maximize the use of the port range.
*
* @internal
* Copyright (C) 2016 by LMI Technologies Inc.
* Licensed under the MIT License.
* Redistributed files must retain the above copyright notice.
*/
#include <GoSdk/Internal/GoAccelSensorPortAlloc.h>
#include <kApi/Io/kSocket.h>
//#include <kApi/Utils/kUtils.h>
//#include <kApi/Data/kImage.h>
//#include <kApi/Io/kDat6Serializer.h>
// This definition needed only because of SDKNET wrapper definitions.
kBeginValueEx(Go, GoAccelSensorPortAllocPorts)
kEndValueEx()
kBeginClassEx(Go, GoAccelSensorPortAlloc)
kAddVMethod(GoAccelSensorPortAlloc, kObject, VRelease)
kEndClassEx()
GoFx(kStatus) GoAccelSensorPortAlloc_Construct(GoAccelSensorPortAlloc* portAlloc, kAlloc allocator)
{
kAlloc alloc = kAlloc_Fallback(allocator);
kStatus status;
kCheck(kAlloc_GetObject(alloc, kTypeOf(GoAccelSensorPortAlloc), portAlloc));
if (!kSuccess(status = GoAccelSensorPortAlloc_Init(*portAlloc, kTypeOf(GoAccelSensorPortAlloc), alloc)))
{
kAlloc_FreeRef(alloc, portAlloc);
}
return status;
}
GoFx(kStatus) GoAccelSensorPortAlloc_Init(GoAccelSensorPortAlloc portAlloc, kType type, kAlloc alloc)
{
kObjR(GoAccelSensorPortAlloc, portAlloc);
kCheck(kObject_Init(portAlloc, type, alloc));
kZero(obj->portRange);
kZero(obj->allocatedPorts);
// Make sure the default port range limits has been coded correctly.
kAssert((GO_ACCEL_SENSOR_PORT_ALLOC_PORT_RANGE_DEFAULT_MAX - GO_ACCEL_SENSOR_PORT_ALLOC_PORT_RANGE_DEFAULT_MIN + 1) >= GO_ACCEL_SENSOR_PORT_ALLOC_MIN_PORT_NUMBERS);
obj->portRange.minPort = GO_ACCEL_SENSOR_PORT_ALLOC_PORT_RANGE_DEFAULT_MIN;
obj->portRange.maxPort = GO_ACCEL_SENSOR_PORT_ALLOC_PORT_RANGE_DEFAULT_MAX;
obj->portRange.startPort = obj->portRange.minPort;
kCheck(GoAccelSensorPortAlloc_InitAllocatedPortBlock(portAlloc, alloc));
return kOK;
}
GoFx(kStatus) GoAccelSensorPortAlloc_VRelease(GoAccelSensorPortAlloc portAlloc)
{
kObj(GoAccelSensorPortAlloc, portAlloc);
kCheck(GoAccelSensorPortAlloc_ReleaseAllocatedPortBlock(portAlloc));
kCheck(kObject_VRelease(portAlloc));
return kOK;
}
GoFx(kStatus) GoAccelSensorPortAlloc_InitAllocatedPortBlock(GoAccelSensorPortAlloc portAlloc, kAlloc alloc)
{
kObj(GoAccelSensorPortAlloc, portAlloc);
kCheck(kLock_Construct(&obj->allocatedPorts.lock, alloc));
kCheck(kMap_Construct(&obj->allocatedPorts.ports, kTypeOf(k16u), kTypeOf(k16u), 0, alloc));
return kOK;
}
GoFx(kStatus) GoAccelSensorPortAlloc_ReleaseAllocatedPortBlock(GoAccelSensorPortAlloc portAlloc)
{
kObj(GoAccelSensorPortAlloc, portAlloc);
kCheck(kDestroyRef(&obj->allocatedPorts.ports));
kCheck(kDestroyRef(&obj->allocatedPorts.lock));
return kOK;
}
GoFx(kBool) GoAccelSensorPortAlloc_IsAutoPortSelect(GoAccelSensorPortAlloc portAlloc, GoAccelSensorPortAllocPorts* sensorPorts)
{
kObj(GoAccelSensorPortAlloc, portAlloc);
kBool autoSelect = kTRUE;
k16u tempSum;
// All the port numbers should be set to zero (kIP_PORT_ANY) for auto port selection.
tempSum =
sensorPorts->controlPort | sensorPorts->healthPort | sensorPorts->upgradePort |
sensorPorts->privateDataPort | sensorPorts->publicDataPort | sensorPorts->webPort;
if (tempSum != 0)
{
autoSelect = kFALSE;
}
return autoSelect;
}
GoFx(kBool) GoAccelSensorPortAlloc_PortInRange(GoAccelSensorPortAlloc portAlloc, k16u portValue)
{
kObj(GoAccelSensorPortAlloc, portAlloc);
kBool isValid = kFALSE;
if ((portValue >= obj->portRange.minPort) &&
(portValue <= obj->portRange.maxPort))
{
isValid = kTRUE;
}
return isValid;
}
GoFx(kBool) GoAccelSensorPortAlloc_ValidatePortInRange(GoAccelSensorPortAlloc portAlloc, GoAccelSensorPortAllocPorts* sensorPorts)
{
kObj(GoAccelSensorPortAlloc, portAlloc);
kBool isValid = kFALSE;
if (GoAccelSensorPortAlloc_PortInRange(portAlloc, sensorPorts->controlPort) &&
GoAccelSensorPortAlloc_PortInRange(portAlloc, sensorPorts->upgradePort) &&
GoAccelSensorPortAlloc_PortInRange(portAlloc, sensorPorts->healthPort) &&
GoAccelSensorPortAlloc_PortInRange(portAlloc, sensorPorts->privateDataPort) &&
GoAccelSensorPortAlloc_PortInRange(portAlloc, sensorPorts->publicDataPort) &&
GoAccelSensorPortAlloc_PortInRange(portAlloc, sensorPorts->webPort))
{
isValid = kTRUE;
}
return isValid;
}
// This function makes getting the starting port number look easier.
GoFx(k16u) GoAccelSensorPortAlloc_GetStartPort(GoAccelSensorPortAlloc portAlloc)
{
kObj(GoAccelSensorPortAlloc, portAlloc);
return obj->portRange.startPort;
}
// This function makes setting the starting port number look easier.
GoFx(void) GoAccelSensorPortAlloc_SetStartPort(GoAccelSensorPortAlloc portAlloc, k16u port)
{
kObj(GoAccelSensorPortAlloc, portAlloc);
obj->portRange.startPort = port;
}
// This function takes the port number input and replaces it with the next port
// number. The function has to take into account:
// - wrap around within the configured port range.
// - detect if already gone through the entire range.
// The function does not take into account whether the port is in use or not.
// This function returns an error if the next available port is the same as
// the starting port, indicating the port range has been exhausted.
GoFx(kStatus) GoAccelSensorPortAlloc_NextPort(GoAccelSensorPortAlloc portAlloc, k16u* port)
{
kObj(GoAccelSensorPortAlloc, portAlloc);
k16u nextPort;
nextPort = *port;
// Code should also handle last port number that is outside the port range.
if (nextPort >= obj->portRange.maxPort)
{
// Wrap around scenario.
nextPort = obj->portRange.minPort;
}
else
{
nextPort++;
}
// Check if gone through the entire port range already (ie. run out of
// ports to select). Bail out because no more ports.
// The starting port number remains unchanged so no need to update it.
kCheckTrue(nextPort != GoAccelSensorPortAlloc_GetStartPort(portAlloc), kERROR_FULL);
*port = nextPort;
return kOK;
}
// This function calculates the next starting port number, given the last
// port that was used/allocated.
GoFx(kStatus) GoAccelSensorPortAlloc_SetNextStartPort(GoAccelSensorPortAlloc portAlloc, k16u lastUsedPort)
{
kObj(GoAccelSensorPortAlloc, portAlloc);
kStatus exception;
k16u nextPort;
nextPort = lastUsedPort;
kTry
{
// This call can fail if the next port is the same as the
// starting port.
// This could happen if there are only enough ports in the port range
// for one sensor, and all the ports were just allocated to a
// sensor. So the next starting port becomes the same as
// the original starting port.
// In that case, the starting port number remains unchanged.
kTest(GoAccelSensorPortAlloc_NextPort(portAlloc, &nextPort));
GoAccelSensorPortAlloc_SetStartPort(portAlloc, nextPort);
}
kCatch(&exception)
{
// The next port is the same as the starting port number.
// It is an error if trying to find a free port, but is not
// an error if enough free ports were found for a sensor.
kLogf("%s: status %d - next port in port range (%u-%u) is same as current "
"start point, last used port %u.",
__FUNCTION__,
exception,
obj->portRange.minPort,
obj->portRange.maxPort,
lastUsedPort);
kEndCatch(exception);
}
return kOK;
}
// This function attempts to create a socket with the specified port to determine
// if the port is being used or not.
GoFx(kBool) GoAccelSensorPortAlloc_SocketIsFree(GoAccelSensorPortAlloc portAlloc, kIpAddress interfaceIp, kSocketType socketType, k16u port)
{
kObj(GoAccelSensorPortAlloc, portAlloc);
kSocket socket = kNULL;
kBool isFree = kFALSE;
// Create a socket and bind it to the specified port.
// Afterwards, release the socket (and therefore port), by closing the
// socket.
if (kSuccess(kSocket_Construct(&socket, kIP_VERSION_4, socketType, kObject_Alloc(portAlloc))))
{
if (kSuccess(kSocket_Bind(socket, interfaceIp, (k32u) port)))
{
isFree = kTRUE;
}
}
kDestroyRef(&socket);
return isFree;
}
// This function checks if the specified port is already allocated, or if
// it is currently in use (ie. currently bound to a socket).
// If neither, then the port is available for allocation, and hopefully remains free
// until the port is bound by the acceleration code.
// NOTE: this function assumes the port number are within the valid port
// range, and so does not explicitly check for that.
GoFx(kBool) GoAccelSensorPortAlloc_PortIsFree(GoAccelSensorPortAlloc portAlloc, k16u port, kBool doAllocationCheck)
{
kObj(GoAccelSensorPortAlloc, portAlloc);
kBool isFree = kFALSE;
if (!GoAccelSensorPortAlloc_ReservedPort(portAlloc, port))
{
// Skip socket check if allocation check is enabled and the port is
// found to be allocated already => port is not free.
if (!(doAllocationCheck && GoAccelSensorPortAlloc_PortIsAllocated(portAlloc, port)))
{
// Test if port is free by attempting to create a socket using
// that port.
// Only TCP and UDP sockets are checked because these are the most
// commonly used sockets by Gocator software and other external
// applications on an accelerator platform.
// IP address used for the socket is 0.0.0.0 as that is the most common
// address used by Gocator software.
if (GoAccelSensorPortAlloc_SocketIsFree(portAlloc, kIpAddress_AnyV4(), kSOCKET_TYPE_UDP, port) &&
GoAccelSensorPortAlloc_SocketIsFree(portAlloc, kIpAddress_AnyV4(), kSOCKET_TYPE_TCP, port))
{
isFree = kTRUE;
}
}
}
return isFree;
}
// This function selects ports compatible with the legacy port values used
// when accelerating one sensor.
// Because the legacy web/http server port is so much larger than the sensor
// channel ports, the logic here gets a bit complicated when the configured
// port range and selecting the next starting port is taken into account.
GoFx(kStatus) GoAccelSensorPortAlloc_SelectLegacyPorts(GoAccelSensorPortAlloc portAlloc, GoAccelSensorPortAllocPorts* sensorPorts)
{
kObj(GoAccelSensorPortAlloc, portAlloc);
k16u webPort;
k16u lastPort; // Last free port chosen.
// Choose legacy values for sensor channel ports (3190, 3192, 3194, 3195, 3196).
// For legacy web port, use 8080 if possible.
kCheckTrue(GoAccelSensorPortAlloc_PortIsFree(portAlloc, GO_SDK_RESERVED_PORT_SENSOR_CONTROL, kTRUE), kERROR_NETWORK);
kCheckTrue(GoAccelSensorPortAlloc_PortIsFree(portAlloc, GO_SDK_RESERVED_PORT_SENSOR_UPGRADE, kTRUE), kERROR_NETWORK);
kCheckTrue(GoAccelSensorPortAlloc_PortIsFree(portAlloc, GO_SDK_RESERVED_PORT_SENSOR_HEALTH, kTRUE), kERROR_NETWORK);
kCheckTrue(GoAccelSensorPortAlloc_PortIsFree(portAlloc, GO_SDK_RESERVED_PORT_SENSOR_PRIVATE_DATA, kTRUE), kERROR_NETWORK);
kCheckTrue(GoAccelSensorPortAlloc_PortIsFree(portAlloc, GO_SDK_RESERVED_PORT_SENSOR_PUBLIC_DATA, kTRUE), kERROR_NETWORK);
// If port range does not include the legacy accelerated sensor HTTP server
// (which is far away from the last sensor channel port value),
// then choose a free port from within the legacy
// port range. In this case, choose the value after the public data port
// in case port numbers lower that public data port is needed for future
// sensor channels.
// The minimum port numbers in a port range already allows for this scenario.
if (GoAccelSensorPortAlloc_PortInRange(portAlloc, GO_SDK_RESERVED_PORT_DEFAULT_ACCELERATED_SENSOR_HTTP_SERVER))
{
webPort = GO_SDK_RESERVED_PORT_DEFAULT_ACCELERATED_SENSOR_HTTP_SERVER;
// The default port is so big using it as the last port would result in
// a large gap of unused ports. So set the last port to the public data
// port to avoid large gaps of ports.
lastPort = GO_SDK_RESERVED_PORT_SENSOR_PUBLIC_DATA;
}
else
{
// Use a port number outside the range of legacy sensor channel ports.
webPort = GO_SDK_RESERVED_PORT_SENSOR_PUBLIC_DATA + 1;
// The web port was set to the largest port number in the legacy port
// range, so use this as the last port.
lastPort = webPort;
}
kCheckTrue(GoAccelSensorPortAlloc_PortIsFree(portAlloc, webPort, kTRUE), kERROR_NETWORK);
// Ports are free so update worker message param with the port values.
sensorPorts->controlPort = GO_SDK_RESERVED_PORT_SENSOR_CONTROL;
sensorPorts->upgradePort = GO_SDK_RESERVED_PORT_SENSOR_UPGRADE;
sensorPorts->healthPort = GO_SDK_RESERVED_PORT_SENSOR_HEALTH;
sensorPorts->privateDataPort = GO_SDK_RESERVED_PORT_SENSOR_PRIVATE_DATA;
sensorPorts->publicDataPort = GO_SDK_RESERVED_PORT_SENSOR_PUBLIC_DATA;
sensorPorts->webPort = webPort;
// Because all the sensor ports were allocated, adjust the start port to
// next available port.
// Can ignore errors from this call because the error just means the starting
// port number has wrapped around. Since the legacy ports are
// available, this error should not halt the acceleration processing.
(void) GoAccelSensorPortAlloc_SetNextStartPort(portAlloc, lastPort);
return kOK;
}
// This function checks if the specified port is a port reserved for use by
// Gocator software. It does not check if the port is used by Firesync software.
// Note that the port might not be in use yet.
//
// TODO:
// - the list of ports can be read from a file, instead of being hard coded.
GoFx(kBool) GoAccelSensorPortAlloc_ReservedPort(GoAccelSensorPortAlloc portAlloc, k16u port)
{
kObj(GoAccelSensorPortAlloc, portAlloc);
kBool reserved = kFALSE;
// Port number is within the configured port range. Check against
// specific ports used by Gocator software.
switch (port)
{
case GO_SDK_RESERVED_PORT_DISCOVERY_PROTOCOL:
case GO_SDK_RESERVED_PORT_REMOTE_PROCEDURE_CALL:
case GO_SDK_RESERVED_PORT_DEFAULT_ETHERNET_ASCII_SERVER:
case GO_SDK_RESERVED_PORT_DEFAULT_ETHERNET_IP_EXPLICIT_SERVER:
reserved = kTRUE;
break;
// Default is kFALSE;
}
return reserved;
}
GoFx(kStatus) GoAccelSensorPortAlloc_FindOneFreePort(GoAccelSensorPortAlloc portAlloc, k16u* port)
{
kObj(GoAccelSensorPortAlloc, portAlloc);
k16u nextPort = *port;
// REMOVE
kLogf("%s: check if port %u is free", __FUNCTION__, nextPort);
// This loop runs until the port number wraps around back to the
// starting port number, or a free port is found.
while (!GoAccelSensorPortAlloc_PortIsFree(portAlloc, nextPort, kTRUE))
{
// This condition exits the loop if there are insufficient ports.
kCheck(GoAccelSensorPortAlloc_NextPort(portAlloc, &nextPort));
}
*port = nextPort;
// REMOVE
kLogf("%s: found free port %u", __FUNCTION__, nextPort);
return kOK;
}
GoFx(kStatus) GoAccelSensorPortAlloc_FindOneAutoSelectPort(GoAccelSensorPortAlloc portAlloc, kBool incrementPort, k16u* nextPort, k16u* targetPort)
{
kObj(GoAccelSensorPortAlloc, portAlloc);
kStatus exception;
// REMOVE
kLogf("%s: increment %u, nextPort %u", __FUNCTION__, incrementPort, *nextPort);
kTry
{
if (incrementPort)
{
kTest(GoAccelSensorPortAlloc_NextPort(portAlloc, nextPort));
}
kTest(GoAccelSensorPortAlloc_FindOneFreePort(portAlloc, nextPort));
*targetPort = *nextPort;
}
kCatch(&exception)
{
kLogf("%s: error %d finding a free auto select port starting from port %u",
__FUNCTION__,
exception,
*nextPort);
kEndCatch(exception);
}
return kOK;
}
// This function selects ports by automatically finding available ports.
GoFx(kStatus) GoAccelSensorPortAlloc_SelectAutoSelectPorts(GoAccelSensorPortAlloc portAlloc, k16u nextPort, GoAccelSensorPortAllocPorts* sensorPorts)
{
kObj(GoAccelSensorPortAlloc, portAlloc);
kStatus exception;
k16u controlPort = 0;
k16u upgradePort = 0;
k16u healthPort = 0;
k16u privateDataPort = 0;
k16u publicDataPort = 0;
k16u webPort = 0;
kTry
{
kTest(GoAccelSensorPortAlloc_FindOneAutoSelectPort(portAlloc, kFALSE, &nextPort, &controlPort));
kTest(GoAccelSensorPortAlloc_FindOneAutoSelectPort(portAlloc, kTRUE, &nextPort, &upgradePort));
kTest(GoAccelSensorPortAlloc_FindOneAutoSelectPort(portAlloc, kTRUE, &nextPort, &healthPort));
kTest(GoAccelSensorPortAlloc_FindOneAutoSelectPort(portAlloc, kTRUE, &nextPort, &privateDataPort));
kTest(GoAccelSensorPortAlloc_FindOneAutoSelectPort(portAlloc, kTRUE, &nextPort, &publicDataPort));
kTest(GoAccelSensorPortAlloc_FindOneAutoSelectPort(portAlloc, kTRUE, &nextPort, &webPort));
sensorPorts->controlPort = controlPort;
sensorPorts->upgradePort = upgradePort;
sensorPorts->healthPort = healthPort;
sensorPorts->privateDataPort = privateDataPort;
sensorPorts->publicDataPort = publicDataPort;
sensorPorts->webPort = webPort;
// Got all the ports needed to accelerate the sensor so
// advance the start port to the next port number.
// If next starting port is equal to current starting port, it is not
// an error because enough ports were found to accelerate the requested
// sensor. So ignore the return code.
(void) GoAccelSensorPortAlloc_SetNextStartPort(portAlloc, nextPort);
}
kCatch(&exception)
{
kLogf(
"%s: error %d auto selecting ports: ctl %u, upg %u, "
"health %u, private data %u, public data %u, web %u",
__FUNCTION__,
exception,
controlPort,
upgradePort,
healthPort,
privateDataPort,
publicDataPort,
webPort);
kEndCatch(exception);
}
return kOK;
}
// This function tries to find free ports that can be used by the accelerated
// virtual sensor to use.
// NOTES:
// 1. For legacy behaviour, if the starting port is 3190, then try to select
// these ports for sensor use:
// - 3190 - control port
// - 3192 - upgrade port
// - 3194 - health port
// - 3195 - private data port
// - 3196 - public data port
// - 8080 or 3197 (depending on port range) - web port.
// 2. If starting port number is not 3190, then the ports will be picked in
// sequential order (eg. 4000 - 4005) as long as the port is free. If a
// port is not free, try the next sequential port number.
// 3. For each sensor, the starting port number is always the previous starting
// port number plus 1:
// 4. Known ports used by Gocator software are treated as reserved ports
// and are not available for allocation.
GoFx(kStatus) GoAccelSensorPortAlloc_SelectPort(GoAccelSensorPortAlloc portAlloc, k32u sensorId, GoAccelSensorPortAllocPorts* sensorPorts)
{
kObj(GoAccelSensorPortAlloc, portAlloc);
kBool quit = kFALSE;
kStatus exception;
k16u nextPort;
nextPort = GoAccelSensorPortAlloc_GetStartPort(portAlloc);
kLogf("%s: starting port number %u", __FUNCTION__, nextPort);
kTry
{
do
{
if (nextPort == GO_SDK_RESERVED_PORT_SENSOR_CONTROL)
{
// To support legacy SDK applications that accelerate only
// one sensor, give the first accelerated sensor ports starting
// from the default control port, if the default control port
// is within the configured port range.
if (kSuccess(GoAccelSensorPortAlloc_SelectLegacyPorts(portAlloc, sensorPorts)))
{
quit = kTRUE;
}
else
{
kLogf(
"%s: legacy accelerator ports unavailable for sensor %u",
__FUNCTION__,
sensorId);
// The legacy ports are unavailable. This can happen if
// the starting port number has wrapped around because
// all ports in the range has been allocated.
// Try to find other ports by setting the next port number
// beyond the last legacy port number.
// If no more ports available in the port range, then that is
// an error.
nextPort = GO_SDK_RESERVED_PORT_SENSOR_CONTROL + GO_ACCEL_SENSOR_PORT_ALLOC_MIN_PORT_NUMBERS - 1;
kTest(GoAccelSensorPortAlloc_NextPort(portAlloc, &nextPort));
}
}
else
{
kLogf("%s: selecting ports for sensor %u, next port %u",
__FUNCTION__,
sensorId,
nextPort);
// Automatically select ports for sensor. This can fail if there
// are insufficient ports in the entire port range.
kTest(GoAccelSensorPortAlloc_SelectAutoSelectPorts(portAlloc, nextPort, sensorPorts));
quit = kTRUE;
}
} while (!quit);
}
kCatch(&exception);
{
kLogf("%s: error %d allocating ports for sensor %u",
__FUNCTION__,
exception,
sensorId);
kEndCatch(exception);
}
return kOK;
}
GoFx(kStatus) GoAccelSensorPortAlloc_SetPortRange(GoAccelSensorPortAlloc portAlloc, k16u startPort, k16u endPort)
{
kObj(GoAccelSensorPortAlloc, portAlloc);
kStatus exception;
kTry
{
kTestArgs(startPort < endPort);
kTestArgs((endPort - startPort + 1) >= GO_ACCEL_SENSOR_PORT_ALLOC_MIN_PORT_NUMBERS);
kTestArgs(startPort >= GO_ACCEL_SENSOR_PORT_ALLOC_PORT_RANGE_DEFAULT_MIN);
kTestArgs(endPort <= GO_ACCEL_SENSOR_PORT_ALLOC_PORT_RANGE_DEFAULT_MAX);
obj->portRange.minPort = startPort;
obj->portRange.maxPort = endPort;
// Reset the starting port number.
GoAccelSensorPortAlloc_SetStartPort(portAlloc, startPort);
}
kCatch(&exception)
{
kLogf("%s: error %d in new port range %u-%u",
__FUNCTION__,
exception,
startPort,
endPort);
kEndCatch(exception);
}
return kOK;
}
// This function returns the currently configured port range for use in assigning
// ports to accelerated sensors.
GoFx(kStatus) GoAccelSensorPortAlloc_GetPortRange(GoAccelSensorPortAlloc portAlloc, k16u* startPort, k16u* endPort)
{
kObj(GoAccelSensorPortAlloc, portAlloc);
if (kIsNull(startPort) || kIsNull(endPort))
{
kLogf("%s: error - received null arguments for ports", __FUNCTION__);
return kERROR_PARAMETER;
}
*startPort = obj->portRange.minPort;
*endPort = obj->portRange.maxPort;
return kOK;
}
// This function returns the hard limits on the min/max values for the port range,
// and the minimum number ports the range must support.
// These limits are NOT configurable.
GoFx(kStatus) GoAccelSensorPortAlloc_GetPortRangeLimits(GoAccelSensorPortAlloc portAlloc, k16u* startLimit, k16u* endLimit, k16u* minNumPorts)
{
kObj(GoAccelSensorPortAlloc, portAlloc);
if (kIsNull(startLimit) || kIsNull(endLimit) || kIsNull(minNumPorts))
{
kLogf("%s: error - received null arguments for port range limits", __FUNCTION__);
return kERROR_PARAMETER;
}
*startLimit = GO_ACCEL_SENSOR_PORT_ALLOC_PORT_RANGE_DEFAULT_MIN;
*endLimit = GO_ACCEL_SENSOR_PORT_ALLOC_PORT_RANGE_DEFAULT_MAX;
*minNumPorts = GO_ACCEL_SENSOR_PORT_ALLOC_MIN_PORT_NUMBERS;
return kOK;
}
// This function checks if the user provided/selected ports are free or not.
GoFx(kBool) GoAccelSensorPortAlloc_UserSelectedPortsFree(GoAccelSensorPortAlloc portAlloc, GoAccelSensorPortAllocPorts* sensorPorts, kBool doAllocationCheck)
{
kObj(GoAccelSensorPortAlloc, portAlloc);
kStatus exception;
kBool portsFree = kFALSE;
k16u portToCheck = 0;
kTry
{
portToCheck = sensorPorts->controlPort;
kTestTrue(GoAccelSensorPortAlloc_PortIsFree(portAlloc, portToCheck, doAllocationCheck), kERROR_NETWORK);
portToCheck = sensorPorts->upgradePort;
kTestTrue(GoAccelSensorPortAlloc_PortIsFree(portAlloc, portToCheck, doAllocationCheck), kERROR_NETWORK);
portToCheck = sensorPorts->healthPort;
kTestTrue(GoAccelSensorPortAlloc_PortIsFree(portAlloc, portToCheck, doAllocationCheck), kERROR_NETWORK);
portToCheck = sensorPorts->privateDataPort;
kTestTrue(GoAccelSensorPortAlloc_PortIsFree(portAlloc, portToCheck, doAllocationCheck), kERROR_NETWORK);
portToCheck = sensorPorts->publicDataPort;
kTestTrue(GoAccelSensorPortAlloc_PortIsFree(portAlloc, portToCheck, doAllocationCheck), kERROR_NETWORK);
portToCheck = sensorPorts->webPort;
kTestTrue(GoAccelSensorPortAlloc_PortIsFree(portAlloc, portToCheck, doAllocationCheck), kERROR_NETWORK);
portsFree = kTRUE;
}
kCatch(&exception)
{
kLogf("%s: error %d port %u is in use",
__FUNCTION__,
exception,
portToCheck);
// Want to return a boolean instead of status, so force the catch block
// to not exit.
kEndCatch(kOK);
}
return portsFree;
}
// This function receives a list of potential ports for an accelerated
// sensor from the client and does one of the following:
// - if caller has provided specific ports, then the ports are validated
// to ensure they are available.
// - if caller has not provided specific ports, then ports will be allocated
// for the sensor from a set of available ports, if sufficient ports
// are available.
// The set of ports allocated are then stored to ensure no other sensor
// can use that set.
// This is an external API, so must run within a lock to ensure thread safety.
GoFx(kStatus) GoAccelSensorPortAlloc_AllocatePorts(GoAccelSensorPortAlloc portAlloc, k32u sensorId, GoAccelSensorPortAllocPorts* sensorPorts)
{
kObj(GoAccelSensorPortAlloc, portAlloc);
kStatus exception;
kCheck(kLock_Enter(obj->allocatedPorts.lock));
kTry
{
// Check the port numbers provided by the client.
if (GoAccelSensorPortAlloc_IsAutoPortSelect(portAlloc, sensorPorts))
{
kLogf("%s: client requested auto port selection", __FUNCTION__);
// Check if ports can be allocated for sensor.
kTest(GoAccelSensorPortAlloc_SelectPort(portAlloc, sensorId, sensorPorts));
}
else
{
kLogf("%s: checking client provided ports", __FUNCTION__);
// Check if client provided ports are valid or not.
kTestArgs(GoAccelSensorPortAlloc_ValidatePortInRange(portAlloc, sensorPorts));
kTestArgs(GoAccelSensorPortAlloc_UserSelectedPortsFree(portAlloc, sensorPorts, kTRUE));
}
// Ports automatically selected or provided by the caller are free.
// Reserve them in the port allocator.
kTest(GoAccelSensorPortAlloc_StoreAllocatedPorts(portAlloc, sensorId, sensorPorts));
}
kCatchEx(&exception)
{
kLogf("%s: error %d allocating ports for sensor %u",
__FUNCTION__,
exception,
sensorId);
kEndCatchEx(exception);
}
kFinallyEx
{
kLock_Exit(obj->allocatedPorts.lock);
kEndFinallyEx();
}
return kOK;
}
// This function is used to allocate ports recovered/restored from configuration.
// These ports should have originally passed all the checks in
// GoAccelSensorPortAlloc_AllocatePorts(), so when these ports are restored,
// only very basic checks are made on the assumption these were valid ports.
GoFx(kStatus) GoAccelSensorPortAlloc_AllocateRestoredPorts(GoAccelSensorPortAlloc portAlloc, k32u sensorId, GoAccelSensorPortAllocPorts* sensorPorts)
{
kObj(GoAccelSensorPortAlloc, portAlloc);
kStatus exception;
kCheck(kLock_Enter(obj->allocatedPorts.lock));
kTry
{
kLogf("%s: checking restored ports", __FUNCTION__);
// This check should never fail for restored ports. If it does,
// that means the configured ports were probably corrupted or modified
// from their original valid values.
kTestArgs(GoAccelSensorPortAlloc_ValidatePortInRange(portAlloc, sensorPorts));
// Still need to reserve restored ports in the port allocator.
kTest(GoAccelSensorPortAlloc_StoreAllocatedPorts(portAlloc, sensorId, sensorPorts));
}
kCatchEx(&exception)
{
kLogf("%s: error %d allocating restored ports for sensor %u",
__FUNCTION__,
exception,
sensorId);
kEndCatchEx(exception);
}
kFinallyEx
{
kLock_Exit(obj->allocatedPorts.lock);
kEndFinallyEx();
}
return kOK;
}
// This function checks to see if a port has been allocated by this port allocator.
GoFx(kBool) GoAccelSensorPortAlloc_PortIsAllocated(GoAccelSensorPortAlloc portAlloc, k16u port)
{
kObj(GoAccelSensorPortAlloc, portAlloc);
k16u tempPort = port;
kBool isAllocated;
isAllocated = kMap_HasT(obj->allocatedPorts.ports, &tempPort);
return isAllocated;
}
// This function removes the set of specified ports from the set of ports
// allocated by this port allocator.
// This is an external API, so must run within a lock to ensure thread safety.
GoFx(kStatus) GoAccelSensorPortAlloc_RemoveAllocatedPorts(GoAccelSensorPortAlloc portAlloc, k32u sensorId, GoAccelSensorPortAllocPorts* sensorPorts)
{
kObj(GoAccelSensorPortAlloc, portAlloc);
kLogf("%s: removing allocated ports for sensor %u: "
"ctl %u, upg %u, health %u, private %u, public %u, web %u",
__FUNCTION__,
sensorId,
sensorPorts->controlPort,
sensorPorts->upgradePort,
sensorPorts->healthPort,
sensorPorts->privateDataPort,
sensorPorts->publicDataPort,
sensorPorts->webPort);
kCheck(kLock_Enter(obj->allocatedPorts.lock));
// Ignore errors to maximum number of ports cleared.
kMap_Discard(obj->allocatedPorts.ports, &sensorPorts->controlPort);
kMap_Discard(obj->allocatedPorts.ports, &sensorPorts->upgradePort);
kMap_Discard(obj->allocatedPorts.ports, &sensorPorts->healthPort);
kMap_Discard(obj->allocatedPorts.ports, &sensorPorts->privateDataPort);
kMap_Discard(obj->allocatedPorts.ports, &sensorPorts->publicDataPort);
kMap_Discard(obj->allocatedPorts.ports, &sensorPorts->webPort);
if (kMap_Count(obj->allocatedPorts.ports) == 0)
{
kLogf("%s: setting start port to port range start: %u",
__FUNCTION__,
obj->portRange.minPort);
// Reset the starting port number when no ports are allocated.
GoAccelSensorPortAlloc_SetStartPort(portAlloc, obj->portRange.minPort);
}
kLock_Exit(obj->allocatedPorts.lock);
return kOK;
}
// This function stores the specified ports for a sensor into the set of
// ports allocated by this port allocator.
GoFx(kStatus) GoAccelSensorPortAlloc_StoreAllocatedPorts(GoAccelSensorPortAlloc portAlloc, k32u sensorId, GoAccelSensorPortAllocPorts* sensorPorts)
{
kObj(GoAccelSensorPortAlloc, portAlloc);
kStatus exception;
k16u portAddCount = 0; // Used to clean up added ports.
kLogf("%s: sensor %u ports: "
"ctl %u, upg %u, health %u, private %u, public %u, web %u",
__FUNCTION__,
sensorId,
sensorPorts->controlPort,
sensorPorts->upgradePort,
sensorPorts->healthPort,
sensorPorts->privateDataPort,
sensorPorts->publicDataPort,
sensorPorts->webPort);
kTry
{
// If port is already allocated, print a log but continue trying to
// add the other ports.
kTest(kMap_AddT(obj->allocatedPorts.ports, &sensorPorts->controlPort, &sensorPorts->controlPort));
portAddCount++;
kTest(kMap_AddT(obj->allocatedPorts.ports, &sensorPorts->upgradePort, &sensorPorts->upgradePort));
portAddCount++;
kTest(kMap_AddT(obj->allocatedPorts.ports, &sensorPorts->healthPort, &sensorPorts->healthPort));
portAddCount++;
kTest(kMap_AddT(obj->allocatedPorts.ports, &sensorPorts->privateDataPort, &sensorPorts->privateDataPort));
portAddCount++;
kTest(kMap_AddT(obj->allocatedPorts.ports, &sensorPorts->publicDataPort, &sensorPorts->publicDataPort));
portAddCount++;
kTest(kMap_AddT(obj->allocatedPorts.ports, &sensorPorts->webPort, &sensorPorts->webPort));
}
kCatch(&exception)
{
// Should never allocate the same port more than once!
kLogf("%s: error %d adding allocated ports, count %u, sensor %u: "
"ctl %u, upg %u, health %u, private %u, public %u, web %u",
__FUNCTION__,
exception,
portAddCount,
sensorId,
sensorPorts->controlPort,
sensorPorts->upgradePort,
sensorPorts->healthPort,
sensorPorts->privateDataPort,
sensorPorts->publicDataPort,
sensorPorts->webPort);
// Don't know which port failed, so try to remove all the input ports
// that were added. Ignore error return status.
if (portAddCount == 5)
{
kMap_Discard(obj->allocatedPorts.ports, &sensorPorts->publicDataPort);
portAddCount--;
}
if (portAddCount == 4)
{
kMap_Discard(obj->allocatedPorts.ports, &sensorPorts->privateDataPort);
portAddCount--;
}
if (portAddCount == 3)
{
kMap_Discard(obj->allocatedPorts.ports, &sensorPorts->healthPort);
portAddCount--;
}
if (portAddCount == 2)
{
kMap_Discard(obj->allocatedPorts.ports, &sensorPorts->upgradePort);
portAddCount--;
}
if (portAddCount == 1)
{
kMap_Discard(obj->allocatedPorts.ports, &sensorPorts->controlPort);
portAddCount--;
}
kEndCatch(exception);
}
return kOK;
}
// This function removes all ports allocated by this port allocated.
// Used for unit testing.
// This is an external API, so must run within a lock to ensure thread safety.
GoFx(kStatus) GoAccelSensorPortAlloc_PurgeAllAllocatedPorts(GoAccelSensorPortAlloc portAlloc)
{
kObj(GoAccelSensorPortAlloc, portAlloc);
kStatus exception;
kLogf("%s: purge all allocated ports", __FUNCTION__);
kCheck(kLock_Enter(obj->allocatedPorts.lock));
kTry
{
kTest(kMap_Purge(obj->allocatedPorts.ports));
// Reset the starting port number when no ports are allocated.
GoAccelSensorPortAlloc_SetStartPort(portAlloc, obj->portRange.minPort);
}
kCatchEx(&exception)
{
kLogf("%s: error %d purging allocated ports", __FUNCTION__, exception);
kEndCatchEx(exception);
}
kFinallyEx
{
kLock_Exit(obj->allocatedPorts.lock);
kEndFinallyEx();
}
return kOK;
}