From 17ea3127a79f7c0d575cb857cd9197de741ff5ac Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Sat, 10 Oct 2020 17:04:43 +0200 Subject: [PATCH 1/8] minor form improvements, include guards --- action/ActionHelper.cpp | 15 +++++---- action/ActionHelper.h | 74 ++++++++++++++++++++++++++--------------- 2 files changed, 56 insertions(+), 33 deletions(-) diff --git a/action/ActionHelper.cpp b/action/ActionHelper.cpp index 361f7dc3..ab986c6c 100644 --- a/action/ActionHelper.cpp +++ b/action/ActionHelper.cpp @@ -3,8 +3,9 @@ #include "../ipc/MessageQueueSenderIF.h" #include "../objectmanager/ObjectManagerIF.h" -ActionHelper::ActionHelper(HasActionsIF* setOwner, MessageQueueIF* useThisQueue) : - owner(setOwner), queueToUse(useThisQueue), ipcStore(nullptr) { +ActionHelper::ActionHelper(HasActionsIF* setOwner, + MessageQueueIF* useThisQueue) : + owner(setOwner), queueToUse(useThisQueue) { } ActionHelper::~ActionHelper() { @@ -33,13 +34,15 @@ ReturnValue_t ActionHelper::initialize(MessageQueueIF* queueToUse_) { return HasReturnvaluesIF::RETURN_OK; } -void ActionHelper::step(uint8_t step, MessageQueueId_t reportTo, ActionId_t commandId, ReturnValue_t result) { +void ActionHelper::step(uint8_t step, MessageQueueId_t reportTo, + ActionId_t commandId, ReturnValue_t result) { CommandMessage reply; ActionMessage::setStepReply(&reply, commandId, step + STEP_OFFSET, result); queueToUse->sendMessage(reportTo, &reply); } -void ActionHelper::finish(MessageQueueId_t reportTo, ActionId_t commandId, ReturnValue_t result) { +void ActionHelper::finish(MessageQueueId_t reportTo, ActionId_t commandId, + ReturnValue_t result) { CommandMessage reply; ActionMessage::setCompletionReply(&reply, commandId, result); queueToUse->sendMessage(reportTo, &reply); @@ -49,8 +52,8 @@ void ActionHelper::setQueueToUse(MessageQueueIF* queue) { queueToUse = queue; } -void ActionHelper::prepareExecution(MessageQueueId_t commandedBy, ActionId_t actionId, - store_address_t dataAddress) { +void ActionHelper::prepareExecution(MessageQueueId_t commandedBy, + ActionId_t actionId, store_address_t dataAddress) { const uint8_t* dataPtr = NULL; size_t size = 0; ReturnValue_t result = ipcStore->getData(dataAddress, &dataPtr, &size); diff --git a/action/ActionHelper.h b/action/ActionHelper.h index bbc6d114..a20f286a 100644 --- a/action/ActionHelper.h +++ b/action/ActionHelper.h @@ -1,15 +1,18 @@ -#ifndef ACTIONHELPER_H_ -#define ACTIONHELPER_H_ +#ifndef FSFW_ACTION_ACTIONHELPER_H_ +#define FSFW_ACTION_ACTIONHELPER_H_ #include "ActionMessage.h" #include "../serialize/SerializeIF.h" #include "../ipc/MessageQueueIF.h" /** - * \brief Action Helper is a helper class which handles action messages + * @brief Action Helper is a helper class which handles action messages * - * Components which use the HasActionIF this helper can be used to handle the action messages. - * It does handle step messages as well as other answers to action calls. It uses the executeAction function - * of its owner as callback. The call of the initialize function is mandatory and it needs a valid messageQueueIF pointer! + * Components which use the HasActionIF this helper can be used to handle + * the action messages. + * It does handle step messages as well as other answers to action calls. + * It uses the executeAction function of its owner as callback. + * The call of the initialize function is mandatory and needs a + * valid MessageQueueIF pointer! */ class HasActionsIF; @@ -18,7 +21,8 @@ public: /** * Constructor of the action helper * @param setOwner Pointer to the owner of the interface - * @param useThisQueue messageQueue to be used, can be set during initialize function as well. + * @param useThisQueue messageQueue to be used, can be set during + * initialize function as well. */ ActionHelper(HasActionsIF* setOwner, MessageQueueIF* useThisQueue); @@ -26,28 +30,35 @@ public: /** * Function to be called from the owner with a new command message * - * If the message is a valid action message the helper will use the executeAction function from HasActionsIF. - * If the message is invalid or the callback fails a message reply will be send to the sender of the message automatically. + * If the message is a valid action message the helper will use the + * executeAction function from HasActionsIF. + * If the message is invalid or the callback fails a message reply will be + * send to the sender of the message automatically. * * @param command Pointer to a command message received by the owner - * @return HasReturnvaluesIF::RETURN_OK if the message is a action message, CommandMessage::UNKNOW_COMMAND if this message ID is unkown + * @return HasReturnvaluesIF::RETURN_OK if the message is a action message, + * CommandMessage::UNKNOW_COMMAND if this message ID is unkown */ ReturnValue_t handleActionMessage(CommandMessage* command); /** - * Helper initialize function. Must be called before use of any other helper function - * @param queueToUse_ Pointer to the messageQueue to be used, optional if queue was set in constructor + * Helper initialize function. Must be called before use of any other + * helper function + * @param queueToUse_ Pointer to the messageQueue to be used, optional + * if queue was set in constructor * @return Returns RETURN_OK if successful */ ReturnValue_t initialize(MessageQueueIF* queueToUse_ = nullptr); /** - * Function to be called from the owner to send a step message. Success or failure will be determined by the result value. + * Function to be called from the owner to send a step message. + * Success or failure will be determined by the result value. * * @param step Number of steps already done * @param reportTo The messageQueueId to report the step message to * @param commandId ID of the executed command * @param result Result of the execution */ - void step(uint8_t step, MessageQueueId_t reportTo, ActionId_t commandId, ReturnValue_t result = HasReturnvaluesIF::RETURN_OK); + void step(uint8_t step, MessageQueueId_t reportTo, ActionId_t commandId, + ReturnValue_t result = HasReturnvaluesIF::RETURN_OK); /** * Function to be called by the owner to send a action completion message * @@ -55,39 +66,48 @@ public: * @param commandId ID of the executed command * @param result Result of the execution */ - void finish(MessageQueueId_t reportTo, ActionId_t commandId, ReturnValue_t result = HasReturnvaluesIF::RETURN_OK); + void finish(MessageQueueId_t reportTo, ActionId_t commandId, + ReturnValue_t result = HasReturnvaluesIF::RETURN_OK); /** * Function to be called by the owner if an action does report data * - * @param reportTo MessageQueueId_t to report the action completion message to + * @param reportTo MessageQueueId_t to report the action completion + * message to * @param replyId ID of the executed command * @param data Pointer to the data * @return Returns RETURN_OK if successful, otherwise failure code */ - ReturnValue_t reportData(MessageQueueId_t reportTo, ActionId_t replyId, SerializeIF* data, bool hideSender = false); + ReturnValue_t reportData(MessageQueueId_t reportTo, ActionId_t replyId, + SerializeIF* data, bool hideSender = false); /** - * Function to setup the MessageQueueIF* of the helper. Can be used to set the messageQueueIF* if - * message queue is unavailable at construction and initialize but must be setup before first call of other functions. + * Function to setup the MessageQueueIF* of the helper. Can be used to + * set the MessageQueueIF* if message queue is unavailable at construction + * and initialize but must be setup before first call of other functions. * @param queue Queue to be used by the helper */ void setQueueToUse(MessageQueueIF *queue); protected: - static const uint8_t STEP_OFFSET = 1;//!< Increase of value of this per step + //!< Increase of value of this per step + static const uint8_t STEP_OFFSET = 1; HasActionsIF* owner;//!< Pointer to the owner - MessageQueueIF* queueToUse;//!< Queue to be used as response sender, has to be set with - StorageManagerIF* ipcStore;//!< Pointer to an IPC Store, initialized during construction or initialize(MessageQueueIF* queueToUse_) or with setQueueToUse(MessageQueueIF *queue) + //! Queue to be used as response sender, has to be set in ctor or with + //! setQueueToUse + MessageQueueIF* queueToUse; + //! Pointer to an IPC Store, initialized during construction or + StorageManagerIF* ipcStore = nullptr; + /** - *Internal function called by handleActionMessage(CommandMessage* command) - * + * Internal function called by handleActionMessage * @param commandedBy MessageQueueID of Commander * @param actionId ID of action to be done * @param dataAddress Address of additional data in IPC Store */ - virtual void prepareExecution(MessageQueueId_t commandedBy, ActionId_t actionId, store_address_t dataAddress); + virtual void prepareExecution(MessageQueueId_t commandedBy, + ActionId_t actionId, store_address_t dataAddress); /** - * + * @brief Default implementation is empty. */ virtual void resetHelper(); }; -#endif /* ACTIONHELPER_H_ */ +#endif /* FSFW_ACTION_ACTIONHELPER_H_ */ From 829be0f08298c8aea79eccfff9403a2c23e381c8 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Mon, 12 Oct 2020 16:58:04 +0200 Subject: [PATCH 2/8] doc correction, action helper new helper function --- action/ActionHelper.cpp | 56 +++++++++++++++++++++++++++++++++++------ action/ActionHelper.h | 15 +++++++++-- action/HasActionsIF.h | 7 +++--- 3 files changed, 65 insertions(+), 13 deletions(-) diff --git a/action/ActionHelper.cpp b/action/ActionHelper.cpp index ab986c6c..8122885b 100644 --- a/action/ActionHelper.cpp +++ b/action/ActionHelper.cpp @@ -89,22 +89,28 @@ ReturnValue_t ActionHelper::reportData(MessageQueueId_t reportTo, if (result != HasReturnvaluesIF::RETURN_OK) { return result; } - result = data->serialize(&dataPtr, &size, maxSize, SerializeIF::Endianness::BIG); + result = data->serialize(&dataPtr, &size, maxSize, + SerializeIF::Endianness::BIG); if (result != HasReturnvaluesIF::RETURN_OK) { ipcStore->deleteData(storeAddress); return result; } - //We don't need to report the objectId, as we receive REQUESTED data before the completion success message. - //True aperiodic replies need to be reported with another dedicated message. + // We don't need to report the objectId, as we receive REQUESTED data + // before the completion success message. + // True aperiodic replies need to be reported with + // another dedicated message. ActionMessage::setDataReply(&reply, replyId, storeAddress); - //TODO Service Implementation sucks at the moment - if (hideSender){ + // TODO: Service Implementation sucks at the moment + // TODO: why does it suck and why would someone need to hide the sender? + if (hideSender) { result = MessageQueueSenderIF::sendMessage(reportTo, &reply); - } else { + } + else { result = queueToUse->sendMessage(reportTo, &reply); } - if ( result != HasReturnvaluesIF::RETURN_OK){ + + if (result != HasReturnvaluesIF::RETURN_OK){ ipcStore->deleteData(storeAddress); } return result; @@ -112,3 +118,39 @@ ReturnValue_t ActionHelper::reportData(MessageQueueId_t reportTo, void ActionHelper::resetHelper() { } + +ReturnValue_t ActionHelper::reportData(MessageQueueId_t reportTo, + ActionId_t replyId, const uint8_t *data, size_t dataSize, + bool hideSender) { + CommandMessage reply; + store_address_t storeAddress; + ReturnValue_t result = ipcStore->addData(&storeAddress, data, dataSize); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + + if (result != HasReturnvaluesIF::RETURN_OK) { + ipcStore->deleteData(storeAddress); + return result; + } + + // We don't need to report the objectId, as we receive REQUESTED data + // before the completion success message. + // True aperiodic replies need to be reported with + // another dedicated message. + ActionMessage::setDataReply(&reply, replyId, storeAddress); + + // TODO: Service Implementation sucks at the moment + // TODO: why does it suck and why would someone need to hide the sender? + if (hideSender) { + result = MessageQueueSenderIF::sendMessage(reportTo, &reply); + } + else { + result = queueToUse->sendMessage(reportTo, &reply); + } + + if (result != HasReturnvaluesIF::RETURN_OK){ + ipcStore->deleteData(storeAddress); + } + return result; +} diff --git a/action/ActionHelper.h b/action/ActionHelper.h index a20f286a..17ca3ebd 100644 --- a/action/ActionHelper.h +++ b/action/ActionHelper.h @@ -69,8 +69,8 @@ public: void finish(MessageQueueId_t reportTo, ActionId_t commandId, ReturnValue_t result = HasReturnvaluesIF::RETURN_OK); /** - * Function to be called by the owner if an action does report data - * + * Function to be called by the owner if an action does report data. + * Takes a SerializeIF* pointer and serializes it into the IPC store. * @param reportTo MessageQueueId_t to report the action completion * message to * @param replyId ID of the executed command @@ -79,6 +79,17 @@ public: */ ReturnValue_t reportData(MessageQueueId_t reportTo, ActionId_t replyId, SerializeIF* data, bool hideSender = false); + /** + * Function to be called by the owner if an action does report data. + * Takes the raw data and writes it into the IPC store. + * @param reportTo MessageQueueId_t to report the action completion + * message to + * @param replyId ID of the executed command + * @param data Pointer to the data + * @return Returns RETURN_OK if successful, otherwise failure code + */ + ReturnValue_t reportData(MessageQueueId_t reportTo, ActionId_t replyId, + const uint8_t* data, size_t dataSize, bool hideSender = false); /** * Function to setup the MessageQueueIF* of the helper. Can be used to * set the MessageQueueIF* if message queue is unavailable at construction diff --git a/action/HasActionsIF.h b/action/HasActionsIF.h index 886d0837..a26ed588 100644 --- a/action/HasActionsIF.h +++ b/action/HasActionsIF.h @@ -47,10 +47,9 @@ public: virtual MessageQueueId_t getCommandQueue() const = 0; /** * Execute or initialize the execution of a certain function. - * Returning #EXECUTION_FINISHED or a failure code, nothing else needs to - * be done. When needing more steps, return RETURN_OK and issue steps and - * completion manually. - * One "step failed" or completion report must be issued! + * When used in conjunction with the ActionHelper class, returning + * a return code which is not equal to RETURN_OK will trigger a step reply + * with step 0. */ virtual ReturnValue_t executeAction(ActionId_t actionId, MessageQueueId_t commandedBy, const uint8_t* data, size_t size) = 0; From af18f6c94cb3ff60439a7589e55cba477b55018c Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Thu, 29 Oct 2020 13:07:08 +0100 Subject: [PATCH 3/8] todos removed --- action/ActionHelper.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/action/ActionHelper.cpp b/action/ActionHelper.cpp index 8122885b..7c1c0d04 100644 --- a/action/ActionHelper.cpp +++ b/action/ActionHelper.cpp @@ -140,8 +140,8 @@ ReturnValue_t ActionHelper::reportData(MessageQueueId_t reportTo, // another dedicated message. ActionMessage::setDataReply(&reply, replyId, storeAddress); - // TODO: Service Implementation sucks at the moment - // TODO: why does it suck and why would someone need to hide the sender? + // If the sender needs to be hidden, for example to handle packet + // as unrequested reply, this will be done here. if (hideSender) { result = MessageQueueSenderIF::sendMessage(reportTo, &reply); } From 3c1415a4bd5637ea8e9cbac531b347d5705e014e Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Thu, 29 Oct 2020 13:08:09 +0100 Subject: [PATCH 4/8] todos removed2 --- action/ActionHelper.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/action/ActionHelper.cpp b/action/ActionHelper.cpp index 7c1c0d04..fdbd2e3b 100644 --- a/action/ActionHelper.cpp +++ b/action/ActionHelper.cpp @@ -101,8 +101,8 @@ ReturnValue_t ActionHelper::reportData(MessageQueueId_t reportTo, // another dedicated message. ActionMessage::setDataReply(&reply, replyId, storeAddress); - // TODO: Service Implementation sucks at the moment - // TODO: why does it suck and why would someone need to hide the sender? + // If the sender needs to be hidden, for example to handle packet + // as unrequested reply, this will be done here. if (hideSender) { result = MessageQueueSenderIF::sendMessage(reportTo, &reply); } From edb2d3848ddac5184eeea547987e17cfe436daeb Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Thu, 29 Oct 2020 13:27:41 +0100 Subject: [PATCH 5/8] some improverments for simple helper --- action/SimpleActionHelper.cpp | 4 ++-- action/SimpleActionHelper.h | 17 ++++++++++------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/action/SimpleActionHelper.cpp b/action/SimpleActionHelper.cpp index d79a3c97..1ab4f476 100644 --- a/action/SimpleActionHelper.cpp +++ b/action/SimpleActionHelper.cpp @@ -1,9 +1,9 @@ #include "HasActionsIF.h" #include "SimpleActionHelper.h" + SimpleActionHelper::SimpleActionHelper(HasActionsIF* setOwner, MessageQueueIF* useThisQueue) : - ActionHelper(setOwner, useThisQueue), isExecuting(false), lastCommander( - 0), lastAction(0), stepCount(0) { + ActionHelper(setOwner, useThisQueue), isExecuting(false) { } SimpleActionHelper::~SimpleActionHelper() { diff --git a/action/SimpleActionHelper.h b/action/SimpleActionHelper.h index 1329b5fb..d401cdc1 100644 --- a/action/SimpleActionHelper.h +++ b/action/SimpleActionHelper.h @@ -1,8 +1,10 @@ -#ifndef SIMPLEACTIONHELPER_H_ -#define SIMPLEACTIONHELPER_H_ +#ifndef FSFW_ACTION_SIMPLEACTIONHELPER_H_ +#define FSFW_ACTION_SIMPLEACTIONHELPER_H_ #include "ActionHelper.h" +/** + */ class SimpleActionHelper: public ActionHelper { public: SimpleActionHelper(HasActionsIF* setOwner, MessageQueueIF* useThisQueue); @@ -12,13 +14,14 @@ public: ReturnValue_t reportData(SerializeIF* data); protected: - void prepareExecution(MessageQueueId_t commandedBy, ActionId_t actionId, store_address_t dataAddress); - virtual void resetHelper(); + void prepareExecution(MessageQueueId_t commandedBy, ActionId_t actionId, + store_address_t dataAddress); + virtual void resetHelper(); private: bool isExecuting; - MessageQueueId_t lastCommander; - ActionId_t lastAction; - uint8_t stepCount; + MessageQueueId_t lastCommander = MessageQueueIF::NO_QUEUE; + ActionId_t lastAction = 0; + uint8_t stepCount = 0; }; #endif /* SIMPLEACTIONHELPER_H_ */ From 4557a2eb361abf6de9fc1ca49908ae4421504fcb Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Thu, 29 Oct 2020 13:34:53 +0100 Subject: [PATCH 6/8] better comments --- action/ActionHelper.h | 5 +++-- action/SimpleActionHelper.cpp | 3 ++- action/SimpleActionHelper.h | 3 +++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/action/ActionHelper.h b/action/ActionHelper.h index 17ca3ebd..a91722f3 100644 --- a/action/ActionHelper.h +++ b/action/ActionHelper.h @@ -57,8 +57,9 @@ public: * @param commandId ID of the executed command * @param result Result of the execution */ - void step(uint8_t step, MessageQueueId_t reportTo, ActionId_t commandId, - ReturnValue_t result = HasReturnvaluesIF::RETURN_OK); + void step(uint8_t step, MessageQueueId_t reportTo, + ActionId_t commandId, + ReturnValue_t result = HasReturnvaluesIF::RETURN_OK); /** * Function to be called by the owner to send a action completion message * diff --git a/action/SimpleActionHelper.cpp b/action/SimpleActionHelper.cpp index 1ab4f476..af57736c 100644 --- a/action/SimpleActionHelper.cpp +++ b/action/SimpleActionHelper.cpp @@ -10,7 +10,8 @@ SimpleActionHelper::~SimpleActionHelper() { } void SimpleActionHelper::step(ReturnValue_t result) { - //STEP_OFFESET is subtracted to compensate for adding offset in base method, which is not necessary here. + // STEP_OFFESET is subtracted to compensate for adding offset in base + // method, which is not necessary here. ActionHelper::step(stepCount - STEP_OFFSET, lastCommander, lastAction, result); if (result != HasReturnvaluesIF::RETURN_OK) { diff --git a/action/SimpleActionHelper.h b/action/SimpleActionHelper.h index d401cdc1..1f35d9fd 100644 --- a/action/SimpleActionHelper.h +++ b/action/SimpleActionHelper.h @@ -4,6 +4,9 @@ #include "ActionHelper.h" /** + * @brief This is an action helper which is only able to service one action + * at a time but remembers last commander and last action which + * simplifies usage */ class SimpleActionHelper: public ActionHelper { public: From 352296d20031f0df68e249cb3eec112ce5606bc8 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Thu, 29 Oct 2020 13:52:52 +0100 Subject: [PATCH 7/8] action helper update --- action/ActionHelper.cpp | 5 +++++ action/HasActionsIF.h | 9 ++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/action/ActionHelper.cpp b/action/ActionHelper.cpp index fdbd2e3b..0d3baa88 100644 --- a/action/ActionHelper.cpp +++ b/action/ActionHelper.cpp @@ -65,6 +65,11 @@ void ActionHelper::prepareExecution(MessageQueueId_t commandedBy, } result = owner->executeAction(actionId, commandedBy, dataPtr, size); ipcStore->deleteData(dataAddress); + if(result == HasActionsIF::EXECUTION_FINISHED) { + CommandMessage reply; + ActionMessage::setCompletionReply(&reply, actionId, result); + queueToUse->sendMessage(commandedBy, &reply); + } if (result != HasReturnvaluesIF::RETURN_OK) { CommandMessage reply; ActionMessage::setStepReply(&reply, actionId, 0, result); diff --git a/action/HasActionsIF.h b/action/HasActionsIF.h index a26ed588..ad9f4c61 100644 --- a/action/HasActionsIF.h +++ b/action/HasActionsIF.h @@ -47,9 +47,12 @@ public: virtual MessageQueueId_t getCommandQueue() const = 0; /** * Execute or initialize the execution of a certain function. - * When used in conjunction with the ActionHelper class, returning - * a return code which is not equal to RETURN_OK will trigger a step reply - * with step 0. + * The ActionHelpers will execute this function and behave differently + * depending on the returnvalue. + * + * @return + * -@c EXECUTION_FINISHED Finish reply will be generated + * -@c Not RETURN_OK Step failure reply will be generated */ virtual ReturnValue_t executeAction(ActionId_t actionId, MessageQueueId_t commandedBy, const uint8_t* data, size_t size) = 0; From 4229e256d1012320cedcb788790e74e9fdbef796 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Thu, 29 Oct 2020 13:55:49 +0100 Subject: [PATCH 8/8] include guard fix --- action/HasActionsIF.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/action/HasActionsIF.h b/action/HasActionsIF.h index ad9f4c61..690f0369 100644 --- a/action/HasActionsIF.h +++ b/action/HasActionsIF.h @@ -1,11 +1,12 @@ -#ifndef FRAMEWORK_ACTION_HASACTIONSIF_H_ -#define FRAMEWORK_ACTION_HASACTIONSIF_H_ +#ifndef FSFW_ACTION_HASACTIONSIF_H_ +#define FSFW_ACTION_HASACTIONSIF_H_ #include "ActionHelper.h" #include "ActionMessage.h" #include "SimpleActionHelper.h" #include "../returnvalues/HasReturnvaluesIF.h" #include "../ipc/MessageQueueIF.h" + /** * @brief * Interface for component which uses actions @@ -59,4 +60,4 @@ public: }; -#endif /* FRAMEWORK_ACTION_HASACTIONSIF_H_ */ +#endif /* FSFW_ACTION_HASACTIONSIF_H_ */