/*---------------------------------------------------------------------
* File: TIM_bridge.c
*
* External Entity: Time (TIM)
*
* Description:
* This file provides an implementation of the standard Shlaer-Mellor
* timer functionality. Usage is conformed to the bridge interface
* as described in the BridgePoint Action Language Users Guide.
*
* Modify this file to match project design requirements. Simply add
* or remove code. Special comments draw attention to where
* modifications can most easily be made.
*
* Only a subset of the TIM interfaces are provided in this simple
* prototype implementation. Recurring timers and timer expiration
* modification are not supported. Long integers are used to store
* time values thus limiting the duration of timers and the system
* ticker to about 71 minutes. The sample implementation uses
* the localtime, mktime, ftime and time library routines.
*
* For this example implementation to work, TIM_init() must be
* invoked at start-up (perhaps from UserInitializationCallout).
* Also, TIM_tick() must be invoked periodically and as often
* as practical (perhaps from UserBackgroundProcessingCallout).
* The resolution of the timers is driven largely by the frequency
* of invocation of TIM_tick().
*
* Notice:
* (C) Copyright 1999, 2000 ROX Software, Inc.
* All rights reserved.
*-------------------------------------------------------------------*/
#include <sys/timeb.h>
#include <time.h>
#include "TIM_bridge.h"
#include "sys_init.h"
typedef unsigned long ETimer_time_t;
/*---------------------------------------------------------------------
* Timer "Object" Structure Declaration
* [next] is the mechanism used to collect and sequence timers.
* Timer instances are strung together in an active (animate)
* list and an inactive (inanimate) list. The next pointer
* provides the "hole for the beads".
* [expiration] is the system clock time at which this
* timer will pop.
* [t0] (not used) is the system time at installation and
* would be useful with recurring timers.
* [event] is the handle of the event that the timer will
* generate upon expiration.
* [flags] (not used) provides boolean variables useful to
* indicate whether the timer has popped or whether it is
* a recurring timer.
*-------------------------------------------------------------------*/
typedef struct ETimer_s ETimer_t;
struct ETimer_s {
ETimer_t * next;
ETimer_time_t expiration;
OoaEvent_t * event;
};
static ETimer_t swtimers[ SYS_MAX_OOA_TIMERS ]; /* system.clr color */
static ETimer_t * animate = (ETimer_t *) 0, * inanimate = (ETimer_t *) 0;
static ETimer_time_t tinit = 0;
static struct timeb systyme;
static void timer_insert_sorted( ETimer_t * );
static void timer_fire( ETimer_t * const );
static ETimer_time_t ETimer_msec_time( void );
static ETimer_t *timer_start( const ETimer_time_t, OoaEvent_t * const );
static bool timer_cancel( ETimer_t * );
/*=====================================================================
* BridgePoint Primitive:
* <timer_inst_ref_var> = TIM::timer_start(
* microseconds:<integer_var>,
* event_inst:<event_inst_var> )
* This bridge starts up an instance of a one-shot S-M OOA timer.
*===================================================================*/
Escher_Timer_t *
TIM_timer_start(
Escher_OoaEvent_s * ee_event_inst,
const Escher_uSec_t ee_microseconds )
{
/* Insert implementation specific code here. */
return (Escher_Timer_t *) timer_start( ee_microseconds/1000, ee_event_inst );
}
/*=====================================================================
* BridgePoint Primitive:
* <timer_inst_ref_var> = TIM::timer_start_recurring(
* microseconds:<integer_var>,
* event_inst:<event_inst_var> )
* This bridge starts up an instance of a recurring S-M OOA timer.
*===================================================================*/
Escher_Timer_t *
TIM_timer_start_recurring(
Escher_OoaEvent_s * ee_event_inst,
const Escher_uSec_t ee_microseconds )
{
/* Insert implementation specific code here. */
Escher_Timer_t * result = (Escher_Timer_t *) 0;
return result;
}
/*=====================================================================
* BridgePoint Primitive:
* <integer_var> = TIM::timer_remaining_time(
* timer_inst_ref:<timer_inst_ref_var> )
* Return the remaining time of the specified timer.
*===================================================================*/
Escher_uSec_t
TIM_timer_remaining_time(
const Escher_Timer_t * const ee_timer_inst_ref )
{
/* Insert implementation specific code here. */
return ( ee_timer_inst_ref == (Escher_Timer_t *) 0 ) ? 0 :
( 1000 * ((ETimer_t *) ee_timer_inst_ref)->expiration );
}
/*=====================================================================
* BridgePoint Primitive:
* <was_running_flag> = TIM_timer_reset_time(
* microseconds:<integer_var>,
* timer_inst_ref:<timer_inst_ref_var> )
* Try to change expiration of an existing timer.
* If successful, return true.
*===================================================================*/
bool
TIM_timer_reset_time(
const Escher_uSec_t ee_microseconds,
Escher_Timer_t * const ee_timer_inst_ref )
{
/* Insert implementation specific code here. */
return ( false ); /* not implemented */
}
/*=====================================================================
* BridgePoint Primitive:
* <was_running_flag> = TIM_timer_add_time(
* microseconds:<integer_var>,
* timer_inst_ref:<timer_inst_ref_var> )
* Try to add time to an existing timer.
* If successful, return true.
*===================================================================*/
bool
TIM_timer_add_time(
const Escher_uSec_t ee_microseconds,
Escher_Timer_t * const ee_timer_inst_ref )
{
/* Insert implementation specific code here. */
return ( false ); /* not implemented */
}
/*=====================================================================
* BridgePoint Primitive:
* <was_running_flag> = TIM::timer_cancel(
* timer_inst_ref:<timer_inst_ref_var> )
* This attempts to cancel the specified timer.
* Return true if we actually cancelled the timer.
*===================================================================*/
bool
TIM_timer_cancel(
Escher_Timer_t * const ee_timer_inst_ref )
{
/* Insert implementation specific code here. */
return timer_cancel( (ETimer_t *) ee_timer_inst_ref );
}
/*=====================================================================
* BridgePoint Primitive:
* <date_var> = TIM::current_date()
* Return a variable representing the current calendar date.
*===================================================================*/
Escher_Date_t
TIM_current_date()
{
/* Insert implementation specific code here. */
return ( (Escher_Date_t) time( 0 ) );
}
/*=====================================================================
* BridgePoint Primitive:
* <date_var> = TIM::create_date(
* day:<integer_var>,
* hour:<integer_var>,
* minute:<integer_var>,
* month:<integer_var>,
* second:<integer_var>,
* year:<integer_var> )
* Return a variable representing the calendar date as specified
* by the input components.
*===================================================================*/
Escher_Date_t
TIM_create_date(
const int ee_day,
const int ee_hour,
const int ee_minute,
const int ee_month,
const int ee_second,
const int ee_year )
{
/* Insert implementation specific code here. */
struct tm t;
t.tm_mday = ee_day;
t.tm_hour = ee_hour;
t.tm_min = ee_minute;
t.tm_mon = ee_month;
t.tm_sec = ee_second;
t.tm_year = ee_year;
t.tm_year = ee_year;
return ( (Escher_Date_t) mktime( &t ) );
}
/*=====================================================================
* BridgePoint Primitive:
* <integer_var> = TIM::get_second(
* date:<integer_var> )
* Return the year field of the date variable.
*===================================================================*/
int
TIM_get_second(
const Escher_Date_t ee_date )
{
/* Insert implementation specific code here. */
struct tm * tp;
tp = localtime( &ee_date );
return ( tp->tm_sec );
}
/*=====================================================================
* BridgePoint Primitive:
* <integer_var> = TIM::get_minute(
* date:<integer_var> )
* Return the year field of the date variable.
*===================================================================*/
int
TIM_get_minute(
const Escher_Date_t ee_date )
{
/* Insert implementation specific code here. */
struct tm * tp;
tp = localtime( &ee_date );
return ( tp->tm_min );
}
/*=====================================================================
* BridgePoint Primitive:
* <integer_var> = TIM::get_hour(
* date:<integer_var> )
* Return the year field of the date variable.
*===================================================================*/
int
TIM_get_hour(
const Escher_Date_t ee_date )
{
/* Insert implementation specific code here. */
struct tm * tp;
tp = localtime( &ee_date );
return ( tp->tm_hour );
}
/*=====================================================================
* BridgePoint Primitive:
* <integer_var> = TIM::get_day(
* date:<integer_var> )
* Return the year field of the date variable.
*===================================================================*/
int
TIM_get_day(
const Escher_Date_t ee_date )
{
/* Insert implementation specific code here. */
struct tm * tp;
tp = localtime( &ee_date );
return ( tp->tm_mday );
}
/*=====================================================================
* BridgePoint Primitive:
* <integer_var> = TIM::get_month(
* date:<integer_var> )
* Return the year field of the date variable.
*===================================================================*/
int
TIM_get_month(
const Escher_Date_t ee_date )
{
/* Insert implementation specific code here. */
struct tm * tp;
tp = localtime( &ee_date );
return ( tp->tm_mon );
}
/*=====================================================================
* BridgePoint Primitive:
* <integer_var> = TIM::get_year(
* date:<integer_var> )
* Return the year field of the date variable.
*===================================================================*/
int
TIM_get_year(
const Escher_Date_t ee_date )
{
/* Insert implementation specific code here. */
struct tm * tp;
tp = localtime( &ee_date );
return ( tp->tm_year );
}
/*=====================================================================
* BridgePoint Primitive:
* <timestamp_var> = TIM::current_clock()
* This bridge returns a system dependent time value.
*===================================================================*/
Escher_TimeStamp_t
TIM_current_clock()
{
/* Insert implementation specific code here. */
return ( ETimer_msec_time() );
}
/* Routines below are implementation specific. Modify to taste. */
/*---------------------------------------------------------------------
* Get a timer instance from the inanimate list, provide the
* expiration time and insert it into its proper location among
* the currently ticking timers.
*-------------------------------------------------------------------*/
static ETimer_t *timer_start(
const ETimer_time_t duration,
OoaEvent_t * const event
)
{
ETimer_t * t = inanimate;
if ( t == (ETimer_t *) 0 ) return (ETimer_t *) 0;
inanimate = inanimate->next;
t->event = event;
/*-----------------------------------------------------------------*/
/* Calculate the timer expiration time. */
/* Note: Add one to the duration to make sure that delay is */
/* at least as long as duration. */
/*-----------------------------------------------------------------*/
t->expiration = ETimer_msec_time() + duration + 1;
timer_insert_sorted( t );
return ( t );
}
/*---------------------------------------------------------------------
* Insert a timer into the list of ticking timers maintaining
* an order that fires timers chronologically.
*-------------------------------------------------------------------*/
static void timer_insert_sorted(
ETimer_t * t
)
{
ETimer_time_t poptime = t->expiration;
if ( animate == (ETimer_t *) 0 ) { /* empty list */
t->next = (ETimer_t *) 0;
animate = t;
} else if ( poptime <= animate->expiration ) { /* before head */
t->next = animate;
animate = t;
} else { /* find bigger */
ETimer_t * prev = animate;
ETimer_t * cursor;
while ( ( cursor = prev->next ) != (ETimer_t *) 0 ) {
if ( poptime <= cursor->expiration ) {
break;
}
prev = cursor;
}
prev->next = t; /* link in */
t->next = cursor;
}
}
/*---------------------------------------------------------------------
* Insert a timer into the list of ticking timers maintaining
* an order that fires timers chronologically.
*-------------------------------------------------------------------*/
static bool timer_cancel(
ETimer_t * t
)
{
if ( t == (ETimer_t *) 0 ) {
return ( false );
}
if ( animate == (ETimer_t *) 0 ) {
return ( false );
}
/*-----------------------------------------------------------------*/
/* Check to see if the timer has already been reset. This check */
/* is probabilistic. It could have a hole if multitasked. */
/* We need to try to unlink and see if we actually unlinked. */
/* Attempt to remove the timer from the list of running timers. */
/* If we succeed, then we can cancel/delete the timer. If the */
/* timer is not in the list, then there is no point in attempting */
/* to do any more. */
/*-----------------------------------------------------------------*/
if ( t == animate ) {
animate = animate->next;
} else {
ETimer_t * prev = animate;
ETimer_t * cursor;
while ( ( cursor = prev->next ) != t ) { /* find */
if ( cursor == (ETimer_t *) 0 ) {
return ( false );
}
prev = cursor;
}
prev->next = t->next; /* unlink */
}
t->next = inanimate;
inanimate = t;
return ( true );
}
/*---------------------------------------------------------------------
* Generate delayed event to the application.
* Deactivate fired timer.
*-------------------------------------------------------------------*/
static void timer_fire(
ETimer_t * const t
)
{
Escher_SendEvent( t->event );
animate = animate->next; /* Remove from active list. */
t->next = inanimate; /* Connect to inactive list. */
inanimate = t;
}
/*---------------------------------------------------------------------
* This is the low level mechanism for monotonically increasing
* at a constant rate. Substitute code here to read some
* sort of hardware timer.
*-------------------------------------------------------------------*/
static ETimer_time_t ETimer_msec_time()
{
ETimer_time_t t;
ftime( &systyme );
t = ( systyme.time * 1000 ) + systyme.millitm;
return ( t - tinit );
}
/*---------------------------------------------------------------------
* Initialize the tick mechanism and the timer instances.
*-------------------------------------------------------------------*/
void TIM_init()
{
int i;
ftime( &systyme ); /* Initialize the hardware ticker. */
tinit = ( systyme.time * 1000 ) + systyme.millitm;
/*-----------------------------------------------------------------*/
/* Build the collection (linked list) of timers. */
/*-----------------------------------------------------------------*/
for ( i = 0; i < SYS_MAX_OOA_TIMERS; i++ ) {
swtimers[ i ].expiration = 0;
swtimers[ i ].event = (OoaEvent_t *) 0;
swtimers[ i ].next = inanimate;
inanimate = &swtimers[ i ];
}
}
/*---------------------------------------------------------------------
* This is the repetitively invoked timer poller.
* This routine needs to be run periodically.
*-------------------------------------------------------------------*/
void TIM_tick()
{
/*-----------------------------------------------------------------*/
/* Check to see if there are timers in the ticking timers list. */
/*-----------------------------------------------------------------*/
if ( animate != (ETimer_t *) 0 ) {
if ( animate->expiration <= ETimer_msec_time() ) {
timer_fire( animate );
}
}
}