/*
    Demo the Real time Kernel for the PK21xx/Rugged Giant or the
    PK22xx/Little Star

    
    Real time kernel timer scheduling calls

    request(unsigned ntask) -- request that a task be run or resumed

    run_every(unsigned ntask, unsigned nticks) -- schedule every nticks ticks

    run_after(unsigned ntask, unsigned long nticks) -- schedule after nticks
                                                        have passed

    run_at( unsigned ntask, int *timer) -- schedule at or after the 48 bit
                                            time pointed to by *timer

    suspend(unsigned nticks) -- call from a task only with nticks <= 255.
                                Blocks running until nticks passed.
                                When nticks changes to zero, task is scheduled
                                again. Other scheduling (every, after at)
                                is updated while task is blocked and resumes
                                after suspension has timed out. If nticks
                                is zero, then task is stopped until an
                                external force requests it starts again,
                                either run (every, after, at) or request.

    int run_timer() -- call from timer interrupt at 10-500 times per second.

*/

#use "default.h"
#use cplc.lib
#use rtk.lib


#if !(BOARD_TYPE==CPLC_BOARD || BOARD_TYPE==L_STAR)
#fatal "This program only runs on a PK21xx/Rugged Giant or a PK22xx/Little Star"
#endif

/***Begin Globals real_time_kernel  */

int task0(void), task1(void), task2(void), task3(void), 
    task4(void), task5(void), backgnd(void);

int (*Ftask[])() =
{
    task0, task1, task2, task3, task4, task5, backgnd
};

char comment_flag;

#define NTASKS 7

#define TASK0 0
#define TASK1 1
#define TASK2 2
#define TASK3 3
#define TASK4 4
#define TASK5 5


#define LK_COLS     20
#define KEYPAD_SIZE 12
#define LK_LINES    2


#define RUNKERNEL 1

/***End Globals real_time_kernel */

/*** ### new restrictions */
/* the main function must be in the root */

root main()
{
    uplc_init();
    vat_init();
    comment_flag = 0;
    run_every(TASK5, 5);     // run task 5 every 5 ticks
    run_every(TASK1, 5);     // run task 1 every 5 ticks
    run_every(TASK4, 5);     // run task 4 every 5 ticks
    backgnd();               // this runs after everything else
}

/* ### new feature */
/* the following line changes the default function class to
   xmem and save the current setting */

goxy(int x, int y)
{
    printf("\x1b=%c%c", x + 32, y + 32);
}

shared float temp;           // temperature in degrees C
shared float settemp;        // temperature setting
shared float volume;         // amount of liquid in vat
shared char heater;          // 1- heater on
shared char intake;          // 1- intake valve open
shared char level;           // 1- water level above switch
shared int station;
shared int xtrain, ytrain;
shared int waiting;
char comment[60];

/* ### all tasks are called indirectly so they must be
   declared as indirect */

indirect task0(void)
{                            // not currently used
}

/* simulate the target system */
/* the outflow rate is 10 units / tick
   the inflow rate is 25 units / tick when intake is open
   the temperature of the intake liquid is 20
   the settemp must be higher
   the capacity of the vessel 1000 units of liquid
   the capacity of the heater is to raise the temperature 1 degree per tick
*/

vat_init(void)
{
    temp = 30.0;
    volume = 1000.0;
    settemp = 56.0;
    station = 0;
}

/*** ### new feature */
/* since task<n> are called indirect by the real-time kernel,
   and they possibly are residing in the XMEM, we have to use
   the "indirect" keyword to make sure indirect calls are
   properly handled */

indirect task1(void)
{
    if (intake)
        temp = 0.985 * temp + 20.0 * 0.015; // dilute with 15 liters at 20C

    if (heater)
        temp = temp + 1.0;

    if (intake)
        volume = volume + 15.0;
    else
        volume = volume - 10.0;

    if (volume > 1000.0)
        level = 0;
    else
        level = 1;

    request(TASK2);          // request the control loop
}

/* regulate the temperature and level in vessel */
indirect task2(void)
{
    intake = level;          // if level low, enable intake valve
    heater = temp < settemp; // if temp low, turn on heater
}

indirect task3(void)
{                            // not used

}

indirect task4(void)
{
    static k;

    while (comment_flag) 
        suspend(5);          // if display not updated

    strcpy(comment, "Begin at Sacramento, Ca.");
    comment_flag = 1;

    xtrain = 30;
    ytrain = 1;              // at Sacramento station
    suspend(60);             // wait in Sacramento station
    while (comment_flag) 
        suspend(5);          // wait for display update
    strcpy(comment, "Leave Sacramento");
    comment_flag = 1;

                             // Sacto to Davis
    for (k = 31; k < 48; k++)
    {
        xtrain = k;
        suspend(15);
    }
    while (comment_flag) 
        suspend(5);
    strcpy(comment, "Stop at Davis");
    comment_flag = 1;
    suspend(40);             // wait at Davis station
    while (comment_flag) 
        suspend(5);
    strcpy(comment, "Leave Davis");
    comment_flag = 1;

    for (k = 48; k < 63; k++)
    {
        xtrain = k;
        suspend(8);
    }
    while (comment_flag) 
        suspend(5);
    strcpy(comment, "Stop at Oakland");
    comment_flag = 1;
    suspend(60);             // wait at Oakland station
    while (comment_flag) 
        suspend(5);
    strcpy(comment, "Leave Oakland");
    comment_flag = 1;

    for (k = 1; k < 4; k++)
    {
        ytrain = k;
        suspend(20);
    }
    while (comment_flag) 
        suspend(5);
    strcpy(comment, "Arrive at San Jose");
    comment_flag = 1;
    suspend(60);             // wait at San Jose Stations
}

indirect task5(void)
{
    runwatch();
}

indirect backgnd(void)
{
    int k;
    static int intakeold, heaterold, xtold, ytold;
    static float tempold, volumeold, setold;

    k     = 0;
    xtold = 70;
    ytold = 2;
    setold= 0.;
    goxy(0, 0);
    printf("Real-time kernel demo");

    goxy(0, 1);
    printf("temp=");
    goxy(0, 2);
    printf("volume=");
    goxy(30, 0);
    printf("Sacramento");
    goxy(45, 0);
    printf("Davis");
    goxy(60, 0);
    printf("Oakland");
    goxy(60, 4);
    printf("San Jose");
    goxy(25, 4);
    printf("F4 (DOS only)/ arrows to change ");

    while (1)
    {
        if (xtold != xtrain || ytold != ytrain)
        {
            goxy(xtold, ytold);
            printf(" ");
            xtold = xtrain;
            ytold = ytrain;
            goxy(xtold, ytold);
            printf("T");
        }

        if (setold != settemp)
        {
            goxy(0, 4);
            printf("set temperature=%5.2f", setold = settemp);
        }

        if (temp != tempold)
        {
            goxy(8, 1);
            printf("%8.2f", temp);
            tempold = temp;
        }

        if (volume != volumeold)
        {
            goxy(8, 2);
            printf("%8.2f", volumeold = volume);
        }

        if (intake != intakeold || heaterold != heater)
        {
            goxy(0, 3);
            printf("intake=%d  heater=%d", intake, heater);
            heaterold = heater;
            intakeold = intake;
        }

        if (comment_flag)
        {
            goxy(30, 2);
            printf("                        ");
            goxy(30, 2);
            printf("%s", comment);
            comment_flag = 0;
        }

        if (kbhit())
        {
            k = getchar();
            if (k == 0xc8 || k == 0x38) 
                settemp = settemp + 1.0;
            if (k == 0x32 || k == 0xd0) 
                settemp = settemp - 1.0;
        }
    }
}

/*** ### new feature */
/* the following line resume the original function code default to
   the one before the previous #memmap push directive */

/****************************************End of DEMO_RTX.C */
