Skip to content

Commit

Permalink
Issue #323: Add infrastructure for gCatena.usingLoRaWAN()
Browse files Browse the repository at this point in the history
  • Loading branch information
terrillmoore committed Dec 11, 2021
1 parent f821463 commit 06f3771
Show file tree
Hide file tree
Showing 6 changed files with 443 additions and 14 deletions.
53 changes: 52 additions & 1 deletion src/Catena.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,47 @@ class Catena : public CATENA_H_SUPER_
Catena(const Catena&&) = delete;
Catena& operator=(const Catena&&) = delete;

///
/// \brief Top-level wrapper for LoRaWAN class, for setup() support
///
/// \details
/// In order to support Catena::usingLoRaWAN(), we need a LoRaWAN
/// object that includes a cSetupQueue object.
///
class LoRaWAN : public Super::LoRaWAN
{
public:
/// \brief Constructor is just the default.
LoRaWAN() {}

/// \brief Provide a virtual destructor
virtual ~LoRaWAN() {}

protected:
// for initialization, Catena class can access protected members
friend Catena;

/// \brief Return pointer to setup queue
cSetupQueue *getSetupQueue()
{
return &this->m_setupQueue;
}

///
/// \brief The setup queue element.
///
/// This element is used to add the LoRaWAN object to the
/// list of things to be initialized by Catena::setup().
///
cSetupQueue m_setupQueue;
};

///
/// \brief implementation: set up everything on this particular board.
///
/// \return \c true if setup was successful.
///
virtual bool setup(void) override;
virtual bool setup() override;

virtual uint32_t enable_3v3Boost(bool fRequest) override
{
Expand Down Expand Up @@ -257,7 +292,23 @@ class Catena : public CATENA_H_SUPER_
// virtual bool has_BH1750() const override;
// virtual bool get_PMS7003Request() const override;

///
/// \brief Inform the platform that you are using LoRaWAN on this platform.
///
/// Calling this pulls in an instance of Catena::LoRaWAN, and arranges for
/// it to be initialized by Catena::setup().
///
/// \note This can't be virtual,
/// because that causes the code to get linked in and not optimized away.
///
/// \return
/// \c true if we succeeded in registering the object (or initializing
/// if, if Catena::setup() has already run).
///
bool usingLoRaWAN();

protected:
virtual bool usingObject(cSetupQueue &Object, void *pClientData, cSetupQueue::SetupFn_t *pFn) override;

private:
/// \brief local routine to isolate the flash / download setup.
Expand Down
143 changes: 138 additions & 5 deletions src/CatenaBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -283,15 +283,15 @@ class cPowerControl
/// related power supply whenever an control in the group is
/// requesting power; and will turn off the related power supply
/// whenever no controls are requesting power.
///
///
virtual uint32_t enable(bool fRequest) = 0;

///
/// \brief Get current power request state for a control.
///
/// \return
/// `true` if control is logically requesting power, `false` otherwise.
/// The result is not specified if this->hasControl() is false.
/// The result is not specified if this->hasControl() is false.
///
virtual bool getRequest() const = 0;

Expand Down Expand Up @@ -440,7 +440,7 @@ class cPowerControlGPIO : public cPowerControl
/// different Catena boards. This type of control lets the control
/// of several logical functions be aggregated in a single logical
/// supply.
///
///
class cPowerControlNested : public cPowerControl
{
public:
Expand Down Expand Up @@ -502,6 +502,112 @@ class cPowerControlNested : public cPowerControl
cPowerControl &m_parent;
uint8_t m_count = 0;
};

/****************************************************************************\
|
| Initialization
|
\****************************************************************************/

class Catena; // forward reference

///
/// \brief Queue element for registering initializers for Catena::setup().
///
/// In order to simplify initialization, to promote reuse of sketches,
/// and to centralize code, we have one Catena::setup() routine, which
/// sketches are expected to call. However, there may be differences from
/// platform to platform; and so we allow code to register functions to
/// be called from the setup() handler.
///
/// Each object to be registered should have an associated `cSetupQueue`
/// object. This may be embedded in the object to be registered, or
/// may be separate. These objets are gathered into
/// a list, and processed in FIFO order by Catena::setup().
///
class cSetupQueue
{
public:
///
/// \brief Symbolic type for object-specific setup() functions.
///
/// \param [in] pClientData arbitrary pointer for use by caller,
/// but typically points to the object.
/// \param [in] pCatena points to the Catena object.
///
/// \return Functions of this type must return \c true if
/// initialization was successful, or \c false if
/// initialization failed. Initialization failure is
/// treated as a critical failure by Catena::setup().
///
typedef bool (SetupFn_t)(void *pClientData, Catena *pCatena);

/// \brief Return pointer to successor of this node.
cSetupQueue *getNext() const
{
return this->m_pNext;
}

///
/// \brief Append a node to the list headed by this node.
///
/// \param [in] NodeToAppend is the node to be appended.
///
/// \note
/// To save memory, a singly-linked list is used. To preserve FIFO order
/// of setup calls, the node is appended to the list. The requires an
/// O(n) search for the end of the list. Since this is only called at
/// initialization time, we think this is an acceptable trade-off.
///
void append(cSetupQueue &NodeToAppend)
{
auto pNode = this;

NodeToAppend.m_pNext = nullptr;

while (pNode->m_pNext != nullptr)
pNode = pNode->m_pNext;

pNode->m_pNext = &NodeToAppend;
}

///
/// \brief Initialize a cSetupQueue node.
///
/// \param [in] pClientData is set as the client data to be passed to pSetupFn.
/// \param [in] pSetupFn points to the function to be called to do the
/// object-specific setup. Often this is a lambda.
///
/// \return
/// Reference to the cSetupQueue node.
///
cSetupQueue &init(void *pClientData, SetupFn_t *pSetupFn)
{
this->m_pSetupFn = pSetupFn;
this->m_pUserData = pClientData;
this->m_pNext = nullptr;
return *this;
}

///
/// \brief Call the setup function for this node.
///
bool dispatch(Catena *pCatena) const
{
return this->m_pSetupFn(this->m_pUserData, pCatena);
}

private:
cSetupQueue *m_pNext = nullptr; ///< list thread
SetupFn_t *m_pSetupFn; ///< object setup function for this node.
void *m_pUserData; ///< client data for this node.
};

/****************************************************************************\
|
| CatenaBase
|
\****************************************************************************/

///
/// \brief Base class for all Catena objects
Expand Down Expand Up @@ -1244,8 +1350,8 @@ class CatenaBase
{ return 0; }

/// \} usb

/// \} setup

///
/// \brief return a pointer to string containing the name of this Catena board
///
Expand Down Expand Up @@ -1435,6 +1541,29 @@ class CatenaBase
this->m_pSketchDescription = pDesc;
}

///
/// \brief Arrange for object to be set up by Catena::setup().
///
/// \param [in] Object is the initializion queue object.
/// \param [in] pClientData is the context pointer for the setup function
/// \param [in] pFn is the setup function to be registered.
///
/// \details
/// Normally this function should be called before
/// Catena::setup() is invoked; it adds the object
/// to the list of objects to be initialized by
/// Catena::setup(). If called after Catena::setup()
/// has been run, the object is initialized immediately.
///
/// \returns
/// If Catena::setup() has not been called, this function
/// returns `true`. If Catena::setup() failed, this
/// function returns `false`. Otherwise, the object's
/// setup() function is invoked, and its result is
/// returned.
///
virtual bool usingObject(cSetupQueue &Object, void *pClientData, cSetupQueue::SetupFn_t *pFn) = 0;

/// \brief register the well-known system commands
virtual void registerCommands(void);

Expand Down Expand Up @@ -1488,7 +1617,12 @@ class CatenaBase
/// \brief the command processor
McciCatena::cCommandStream m_CommandStream;

/// \brief the pointer to additional objects to be initialized
cSetupQueue *m_pInitializationList = nullptr;

bool m_flashFound = false; ///< set true if flash was probed.
bool m_setupRun = false; ///< set once setup has been run.
bool m_setupResult; ///< memoized result of running setup.

private:
uint32_t m_OperatingFlags; ///< the operating flags
Expand All @@ -1497,7 +1631,6 @@ class CatenaBase
const char * m_pSketchName {nullptr}; ///< the file name of the sketch
const char * m_pSketchDescription {nullptr}; ///< the application description


// internal methods

/// \brief save the platform once it's determined.
Expand Down
7 changes: 7 additions & 0 deletions src/Catena_AppObjects.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,13 @@ namespace McciCatena {
///
extern cBootloaderApi gBootloaderApi;

///
/// \brief the LoRaWAN API object
///
/// This object is only referenced if Catena::usingLoRaWAN() is called (before or after Catena::setup() is called).
///
extern Catena::LoRaWAN gLoRaWAN;

} // end namespace McciCatena

// compute compile-time variables that are used to control things
Expand Down
29 changes: 21 additions & 8 deletions src/lib/Catena_setup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,9 @@ Catena::setup(
void
)
{
if (this->m_setupRun)
return this->m_setupResult;

/// TODO([email protected]) this should really be in begin().
delay(this->enable_3v3Boost(true));
delay(this->enable_i2cVdd(true));
Expand Down Expand Up @@ -205,8 +208,18 @@ Catena::setup(
#endif
}

// always successful
return true;
/* run the list of initializers */
bool fResult = true;

for (auto pObject = this->m_pInitializationList; fResult && pObject != nullptr; pObject = pObject->getNext())
{
fResult = pObject->dispatch(this);
}

this->m_setupResult = fResult;
this->m_setupRun = true;

return this->m_setupResult;
}

#undef FUNCTION
Expand All @@ -233,12 +246,12 @@ bool Catena::setup_banner(void)
if (pSketchDescription != nullptr)
this->SafePrintf("%s\n", pSketchDescription);

this->SafePrintf("Board: %s SYSCLK: %u MHz USB: %sabled\n",
this->CatenaName(),
unsigned(this->GetSystemClockRate() / (1000 * 1000)),
this->get_consoleIsUsb() ? "en" : "dis"
);
this->SafePrintf("Enter 'help' for a list of commands.\n");
this->SafePrintf("Board: %s SYSCLK: %u MHz USB: %sabled\n",
this->CatenaName(),
unsigned(this->GetSystemClockRate() / (1000 * 1000)),
this->get_consoleIsUsb() ? "en" : "dis"
);
this->SafePrintf("Enter 'help' for a list of commands.\n");

putDashes();
putDashes(0); // newline
Expand Down
Loading

0 comments on commit 06f3771

Please sign in to comment.