Prev: Cross-platform examples
This application deals with the shared resource problem in active object systems. Showing one of the biggest benefit of using active objects: resource encapsulation. The encapsulation naturally designates the owner of the resource as the ultimate arbiter in resolving any contention and potential conflicts for the resource. The shared application is relatively simple and can be tested only with a couple of LEDs on your target board. Still, Shared contains five (5) concurrent active objects that exchange events via direct event posting mechanism. The application uses four timers, as well as dynamic and static events. On the other hand, this application could be used in either preemptive or cooperative enviroment. Aditionally, the Shared could be used to verify a new RKH port.
The platform-independent Shared source code (in C) is located in the <rkh>/demo/cross/shared/
directory, where <rkh>
stands for the installation directory chosed to install the accompanying software and client.h
, server.h
, shared.h
, client.c
, main.c
and server.c
are the platform-independent files. Thus, the code is actually identical in all Shared versions, such as the Windows version, S08 version, and so on.
Sequence diagram
The sequence diagram in Figure 1 shows the most representative event exchanges among three Clients (cli0, cli1, cli2) and the Server (svr) active objects.
The server shares their resource with clients. A client does not share any of its resources, but requests a server's content or service function. Clients therefore initiate communication sessions with the server which await incoming requests. The server selectively shares its resource; and a client initiates contact with the server in order to make use of the resource. Clients and server exchange messages in a request-response messaging pattern: The client sends a request, and the server returns a response.
As an additional feature, the Clients can be paused for an arbitrary period of time. During this paused period, the Clients don't get permissions to request. After the pause period, the Clients should resume normal operation.
Figure 1 - Sequence diagram of the Shared application
State machines
Figure 2(a) shows the state machine associated with Client active object, which clearly shows the life cycle consisting of states "cli_idle"
, "svr_paused"
, "svr_waiting"
, and "cli_using"
. See Client state-machine implementation section.
Figure 2(a) - Server (svr) state machine
Figure 2(b) shows the state machine associated with the Server active object, with "svr_idle"
, "svr_paused"
, and "svr_busy"
states. See Server state-machine implementation section.
Figure 2b - Client (cli) state machine
- Note
- The notation of UML Statecharts is not purely visual. Any nontrivial state machine requires a large amount of textual information (e.g., the specification of actions and guards). The exact syntax of action and guard expressions isnt defined in the UML specification, so many people use either structured English or, more formally, expressions in an implementation language such as C [Douglass]. In practice, this means that UML Statechart notation depends heavily on the specific programming language.
Client state-machine implementation
The client.c file implements the state machine of Client active object, which illustrates some aspects of implementing state machines with RKH framework. Please correlate this implementation with the state diagram shown above. On the other hand, the header file client.h contains the definitions of object structures related to the state machine. See the following sections:
"client.c" - Client active object implementation
#include "shared.h"
#include "server.h"
#include "client.h"
#include "bsp.h"
#define CLI_REQ_TIME \
(RKH_TNT_T)RKH_TIME_SEC((bsp_rand() % 5) + 2)
#define CLI_USING_TIME \
(RKH_TNT_T)RKH_TIME_SEC((bsp_rand() % 5) + 1)
typedef struct Client Client;
static void client_init(Client *
const me,
RKH_EVT_T *pe);
static void client_req(Client *
const me,
RKH_EVT_T *pe);
static void client_start(Client *
const me,
RKH_EVT_T *pe);
static void client_end(Client *
const me,
RKH_EVT_T *pe);
static void client_delay_req(Client *const me);
static void client_pause(Client *const me);
static void client_resume(Client *const me);
client_delay_req, NULL,
RKH_ROOT, NULL);
RKH_TRREG(TOUT_REQ, NULL, client_req, &client_waiting),
RKH_TRREG(PAUSE, NULL, NULL, &client_paused),
RKH_TRREG(START, NULL, client_start, &client_using),
RKH_TRREG(TOUT_USING, NULL, client_end, &client_idle),
RKH_TRREG(PAUSE, NULL, NULL, &client_paused),
client_pause, client_resume,
RKH_ROOT, NULL);
struct Client
{
};
{
&cli0, &cli1, &cli2, &cli3
};
static void
{
}
static void
{
ReqEvt *e_req;
(void)pe;
bsp_cli_req(e_req->clino);
}
static void
client_start(Client *
const me,
RKH_EVT_T *pe)
{
time = CLI_USING_TIME;
bsp_cli_using(
RKH_CAST(StartEvt, pe)->clino,
}
static void
{
(void)pe;
}
static void
client_pause(Client *const me)
{
}
static void
client_delay_req(Client *const me)
{
time = CLI_REQ_TIME;
}
static void
client_resume(Client *const me)
{
}
#define RKH_SMA_POST_FIFO(me_, e_, sender_)
Invoke the direct event posting facility rkh_sma_post_fifo(). If RKH_CFG_SMA_VFUNCT_EN is set RKH_ENA...
#define RKH_UPCAST(BaseType_, me_)
Convert a pointer to a base-class.
#define RKH_EVT_CAST(_e)
Perform cast to pointer to RKH event structure (RKH_EVT_T*).
#define RKH_ROM_STATIC_EVENT(ev_obj, ev_sig)
This macro declares and initializes the event structure ev_ob with ev_sig signal number and establish...
#define RKH_ALLOC_EVT(et, e, sender_)
This macro dynamically creates a new event of type et with its signal.
#define RKH_CREATE_BASIC_STATE(name, en, ex, parent, prepro)
This macro creates a basic state.
#define RKH_CREATE_TRANS_TABLE(name)
This macro creates a state transition table.
#define RKH_TMR_INIT(t_, e_, th_)
Initializes the previously allocated timer structure RKH_TMR_T.
#define RKH_TMR_ONESHOT(t, sma, itick)
Start a timer as one-shot timer.
rbool_t rkh_tmr_stop(RKH_TMR_T *t)
Stops a running timer.
#define RKH_CFG_FWK_TICK_RATE_HZ
Specify the frequency of the framework tick interrupt (number of ticks in one second)....
#define RKH_TR_FWK_AO(actObj_)
Entry symbol table for active object.
#define RKH_TR_FWK_STATE(actObj_, stateObj_)
Entry symbol table for state object.
RKH framwwork platform - independent interface.
#define RKH_CAST(_type, _obj)
Perform downcast of a reference of a base class to one of its derived classes.
#define RKH_END_TRANS_TABLE
This macro is used to terminate a state transition table. This table have the general structure shown...
#define RKH_DCLR_BASIC_STATE
Declares a previously created state/pseudostate to be used as a global object.
#define RKH_TRREG(evt_, guard_, effect_, target_)
This macro defines a regular state transition.
#define RKH_TRINT(e, g, a)
This macro defines an internal state transition. Internal transitions are simple reactions to events ...
#define RKH_ROOT
This macro indicates the root state of a state machine.
#define RKH_SMA_DEF_PTR(me_)
Declare a opaque pointer pointing to an previously created active object.
#define RKH_GET_PRIO(_ao)
Retrieves the priority number of an registered active object (SMA).
#define RKH_ARRAY_SMA_CREATE(_arr, _num)
Declare and allocate an array of SMAs (a.k.a active objects) derived from RKH_SMA_T.
#define RKH_SMA_CREATE(type, name, prio, ppty, initialState, initialAction, initialEvt)
Declare and allocate a SMA (active object) derived from RKH_SMA_T. Also, initializes and assigns a st...
rui8_t RKH_TNT_T
This data type defines the dynamic range of the time delays measured in clock ticks (maximum number o...
Represents events without parameters.
Describes the SMA (active object in UML).
Defines the data structure used to maintain information that allows the timer-handling facility to up...
Prev: Client state-machine implementation
"client.h" - Client active object specification
#ifndef __CLIENT_H__
#define __CLIENT_H__
#include "shared.h"
#ifdef __cplusplus
extern "C" {
#endif
#define CLI(clino_) RKH_ARRAY_SMA(clis, clino_)
#define CLI0 CLI(0)
#define CLI1 CLI(1)
#define CLI2 CLI(2)
#define CLI3 CLI(3)
#define CLI_STK_SIZE (512 / sizeof(RKH_THREAD_STK_TYPE))
enum
{
CLI_PRIO_0 = 1, CLI_PRIO_1, CLI_PRIO_2, CLI_PRIO_3,
MAX_CLI_PRIO,
NUM_CLIENTS = MAX_CLI_PRIO - 1,
};
#ifdef __cplusplus
}
#endif
#endif
#define RKH_ARRAY_SMA_DCLR(_arr, _num)
Declares a opaque pointer to previously created array of state machine applications SMA (a....
Prev: Client state-machine implementation
Server state-machine implementation
The server.c file implements the state machine of Server active object, which illustrates some aspects of implementing state machines with RKH framework. Please correlate this implementation with the state diagram shown above. On the other hand, the header file svr.h contains the definitions of object structures related to the state machine. See following sections:
- Server active object implementation - "server.c"
- Server active object specification - "server.h"
"server.c" - Server active object implementation
#include "bsp.h"
#include "shared.h"
#include "server.h"
#define MAX_SIZEOF_QREQ (2 * NUM_CLIENTS)
typedef struct Server Server;
static void server_init(Server *
const me,
RKH_EVT_T *pe);
static void server_start(Server *
const me,
RKH_EVT_T *pe);
static void server_end(Server *
const me,
RKH_EVT_T *pe);
static void server_defer(Server *
const me,
RKH_EVT_T *pe);
static void server_terminate(Server *
const me,
RKH_EVT_T *pe);
static void server_pause(Server *const me);
static void server_resume(Server *const me);
RKH_TRREG(REQ, NULL, server_start, &server_busy),
RKH_TRREG(PAUSE, NULL, NULL, &server_paused),
RKH_TRREG(DONE, NULL, server_end, &server_idle),
RKH_TRREG(PAUSE, NULL, NULL, &server_paused),
server_pause, server_resume,
RKH_ROOT, NULL);
struct Server
{
rui32_t ntot;
rui32_t ncr[NUM_CLIENTS];
};
static RKH_EVT_T *queueReqSto[MAX_SIZEOF_QREQ];
static void
{
rInt cn;
rkh_queue_init(&queueReq, (
const void **)queueReqSto, MAX_SIZEOF_QREQ,
CSMA(0));
for (cn = 0; cn < NUM_CLIENTS; ++cn)
}
static void
server_start(Server *
const me,
RKH_EVT_T *pe)
{
StartEvt *e_start;
e_start->clino =
RKH_CAST(ReqEvt, pe)->clino;
bsp_svr_start(e_start->clino);
++
RKH_CAST(Server, me)->ncr[CLI_ID(e_start->clino)];
}
static void
{
ReqEvt *e;
(void)pe;
!= (ReqEvt *)0)
{
bsp_svr_recall(e->clino);
}
bsp_svr_end();
}
static void
server_defer(Server *
const me,
RKH_EVT_T *pe)
{
(void)me;
}
static void
server_terminate(Server *
const me,
RKH_EVT_T *pe)
{
(void)me;
(void)pe;
}
static void
server_pause(Server *const me)
{
bsp_svr_paused(me->ntot, me->ncr);
}
static void
server_resume(Server *const me)
{
ReqEvt *e;
!= (ReqEvt *)0)
{
bsp_svr_recall(e->clino);
}
bsp_svr_resume();
}
void rkh_fwk_exit(void)
Exit the RKH framework.
void rkh_queue_init(RKH_QUEUE_T *q, const void **sstart, RKH_QUENE_T ssize, void *sma)
Initializes the previously allocated queue data structure RKH_QUEUE_T.
#define RKH_TR_FWK_SIG(stateObj_)
Entry symbol table for event signal.
RKH_EVT_T * rkh_sma_recall(RKH_SMA_T *me, RKH_QUEUE_T *q)
Recall a deferred event from a given event queue.
void rkh_sma_defer(RKH_QUEUE_T *q, const RKH_EVT_T *e)
Defer an event to a given separate event queue.
#define RKH_GET_SMA(_prio)
Retrieves the address of an registered active object (SMA) according to its priority.
Defines the data structure used to maintain information about the queue.
Prev: Server state-machine implementation
"server.h" - Server active object specification
#ifndef __SERVER_H__
#define __SERVER_H__
#include "client.h"
#ifdef __cplusplus
extern "C" {
#endif
#define CLI_ID(cp_) ((cp_) - RKH_GET_PRIO(CLI(0)))
#define SVR_STK_SIZE (512 / sizeof(RKH_THREAD_STK_TYPE))
#ifdef __cplusplus
}
#endif
#endif
#define RKH_SMA_DCLR(me_)
This macro declares a opaque pointer to previously created state machine application (SMA aka active ...
Prev: Server state-machine implementation
Signals, events, and active objects
In RKH, signals are typically enumerated constants and events with parameters are structures derived from the RKH_EVT_T base structure. The next listing shows signals and events used in the Shared application (shared.h).
"shared.h" - Signals and events
#ifndef __SHARED_H__
#define __SHARED_H__
#ifdef __cplusplus
extern "C" {
#endif
typedef enum Signals Signals;
enum Signals
{
REQ,
START,
DONE,
TOUT_REQ,
TOUT_USING,
PAUSE,
TERM
};
typedef struct
{
rui8_t clino;
} ReqEvt;
typedef struct
{
rui8_t clino;
} StartEvt;
#ifdef __cplusplus
}
#endif
#endif
Initializing and starting the application
Most of the system initialization and application startup can be written in a platform-independent way.
"main.c" - main() function
#include "bsp.h"
#include "server.h"
#include "client.h"
#define QSTO_SIZE 4
#if RKH_CFGPORT_SMA_QSTO_EN == RKH_ENABLED
static RKH_EVT_T *cli_qsto[NUM_CLIENTS][QSTO_SIZE];
#else
#define svr_qsto
#define cli_qsto
#endif
#if RKH_CFGPORT_SMA_STK_EN == RKH_ENABLED
#else
#define svr_stk
#define cli_stk
#endif
int
main(int argc, char *argv[])
{
rInt cn;
bsp_init(argc, argv);
for (cn = 0; cn < NUM_CLIENTS; ++cn)
{
CLI_STK_SIZE);
}
return 0;
}
#define RKH_SMA_ACTIVATE(me_, qSto_, qStoSize, stkSto_, stkSize_)
Invoke the active object activation function rkh_sma_activate(). If RKH_CFG_SMA_VFUNCT_EN is set RKH_...
#define RKH_TRC_CLOSE()
Close the tracing session.
void rkh_fwk_enter(void)
RKH framework is started.
#define RKH_THREAD_STK_TYPE
Data type to declare thread stack, which is only used when the underlying OS does not internally allo...
Running on various platforms
As said before, the only platform-dependent file is the board support package (BSP) definition. Each of supported platforms defines its own bsp.c
and bsp.h
files, which are contained in the proper directory, for example, the BSP for the Shared application on Windows 32 (Visual Studio 2008) with a simple cooperative scheduler is located in <rkh>/demo/cross/shared/build/80x86/win32_st/vc/
Prev: Cross-platform examples