fsfw/doc/README-localpools.md

6.0 KiB

Local Data Pools Developer Information

The local data pools can be used to store data like sensor values so they can be used by other software objects like controllers as well. If a class should have a local pool which can be used by other software objects as well, following steps have to be performed:

  1. Create a LocalDataPoolManager member class
  2. Implement the HasLocalDataPoolIF

The local data pool manager is also able to process housekeeping service requests in form of messages, generate periodic housekeeping packet, generate notification and snapshots of changed variables and datasets and process notifications and snapshots coming from other objects. Two important framework classes already perform the two steps shown above so the steps required are altered slightly.

Storing and Accessing pool data

The pool manager is responsible for thread-safe access of the pool data, but the actual access to the pool data is done via proxy classes like pool variable classes or dataset classes. Generally, a user will create a dataset class which in turn groups all cohesive pool variables.

The user can then use this set class to read the variables and commit changed variables back into the pool. The general approach is that a user will create a header containing the set class. For example, the following code shows an implementation to access data from a Gyroscope:

class GyroPrimaryDataset: public StaticLocalDataSet<3 * sizeof(float)> {
public:
    /**
     * Constructor for data users
     * @param gyroId
     */
    GyroPrimaryDataset(object_id_t gyroId):
            StaticLocalDataSet(sid_t(gyroId, gyrodefs::GYRO_DATA_SET_ID)) {
        setAllVariablesReadOnly();
    }

    lp_var_t<float> angVelocityX = lp_var_t<float>(sid.objectId,
            gyrodefs::ANGULAR_VELOCITY_X, this);
    lp_var_t<float> angVelocityY = lp_var_t<float>(sid.objectId,
            gyrodefs::ANGULAR_VELOCITY_Y, this);
    lp_var_t<float> angVelocityZ = lp_var_t<float>(sid.objectId,
            gyrodefs::ANGULAR_VELOCITY_Z, this);
private:

    friend class GyroHandler;
    /**
     * Constructor for data creator
     * @param hkOwner
     */
    GyroPrimaryDataset(HasLocalDataPoolIF* hkOwner):
            StaticLocalDataSet(hkOwner, gyrodefs::GYRO_DATA_SET_ID) {}
};

There is a constructor for users which sets all variables to read-only and there is the constructor for the GyroHandler data creator. Both the atittude controller and the GyroHandler can now use the same class definition to access the pool variables with read and commit semantics in a thread-safe way. Generally, each class requiring access will have the set class as a member class. The data creator will also be generally a DeviceHandlerBase subclass and some additional steps are necessary to expose the set for housekeeping purposes.

Using the local data pools in a DeviceHandlerBase subclass

It is very common to store data generated by devices like a sensor into a pool which can then be used by other objects. Therefore, the DeviceHandlerBase already has a local pool. Using the aforementioned example, our GyroHandler will now have the set class as a member:

class GyroHandler: ... {

public:
	...
private:
	...
	GyroPrimaryDataset gyroData;
	...
};

The constructor used for the creators expects the owner class as a parameter, so we initialize the object in the GyroHandler constructor like this:

GyroHandler::GyroHandler(object_id_t objectId, object_id_t comIF,
        CookieIF *comCookie, uint8_t switchId):
        DeviceHandlerBase(objectId, comIF, comCookie), switchId(switchId),
		gyroData(this) {}

We need to assign the set to a reply ID used in the DeviceHandlerBase. The combination of the GyroHandler object ID and the reply ID will be the 64-bit structure ID sid_t and is used to globally identify the set, for example when requesting housekeeping data or generating update messages. We need to assign our custom in some way so that the local pool manager can access the custom data sets as well. By default, the getDataSetHandle will take care of this tasks. The default implementation for a DeviceHandlerBase subclass will use the internal command map to retrieve a handle to a dataset from a given reply ID. Therefore, we assign the set in the fillCommandAndReplyMap function:

void GyroHandler::fillCommandAndReplyMap() {
	...
	this->insertInCommandAndReplyMap(gyrodefs::GYRO_DATA, 3, &gyroData);
	...
}

Now, we need to create the actual pool entries as well, using the initializeLocalDataPool function. Here, we also immediately subscribe for periodic housekeeping packets with an interval of 4 seconds. They are still disabled in this example and can be enabled with a housekeeping service command.

ReturnValue_t GyroHandler::initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
		LocalDataPoolManager &poolManager) {
	localDataPoolMap.emplace(gyrodefs::ANGULAR_VELOCITY_X,
			new PoolEntry<float>({0.0}));
	localDataPoolMap.emplace(gyrodefs::ANGULAR_VELOCITY_Y,
			new PoolEntry<float>({0.0}));
	localDataPoolMap.emplace(gyrodefs::ANGULAR_VELOCITY_Z,
			new PoolEntry<float>({0.0}));
	localDataPoolMap.emplace(gyrodefs::GENERAL_CONFIG_REG42,
			new PoolEntry<uint8_t>({0}));
	localDataPoolMap.emplace(gyrodefs::RANGE_CONFIG_REG43,
			new PoolEntry<uint8_t>({0}));

	poolManager.subscribeForPeriodicPacket(gyroData.getSid(), false, 4.0, false);
	return HasReturnvaluesIF::RETURN_OK;
}

Now, we have received some sensor data and converted them into the right format, we can write it into the pool like this, using a guard class to ensure the set is commited back in any case:

PoolReadGuard readHelper(&gyroData);
if(readHelper.getReadResult() == HasReturnvaluesIF::RETURN_OK) {
	if(not gyroData.isValid()) {
		gyroData.setValidity(true, true);
	}

	gyroData.angVelocityX = angularVelocityX;
	gyroData.angVelocityY = angularVelocityY;
	gyroData.angVelocityZ = angularVelocityZ;
}

Using the local data pools in a ExtendedControllerBase subclass

Coming soon