protothread/example-codelock.c

415 lines
12 KiB
C
Raw Normal View History

2022-11-22 01:59:34 +00:00
/*
* Copyright (c) 2004-2005, Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the protothreads library.
*
* Author: Adam Dunkels <adam@sics.se>
*
* $Id: example-codelock.c,v 1.5 2005/10/06 07:57:08 adam Exp $
*/
/*
*
* This example shows how to implement a simple code lock. The code
* lock waits for key presses from a numeric keyboard and if the
* correct code is entered, the lock is unlocked. There is a maximum
* time of one second between each key press, and after the correct
* code has been entered, no more keys must be pressed for 0.5 seconds
* before the lock is opened.
*
* This is an example that shows two things:
* - how to implement a code lock key input mechanism, and
* - how to implement a sequential timed routine.
*
* The program consists of two protothreads, one that implements the
* code lock reader and one that implements simulated keyboard input.
*
*
*/
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#include <sys/time.h>
#endif
#include <stdio.h>
#include "pt.h"
/*---------------------------------------------------------------------------*/
/*
* The following definitions are just for the simple timer library
* used in this example. The actual implementation of the functions
* can be found at the end of this file.
*/
struct timer { int start, interval; };
static int timer_expired(struct timer *t);
static void timer_set(struct timer *t, int usecs);
/*---------------------------------------------------------------------------*/
/*
* This example uses two timers: one for the code lock protothread and
* one for the simulated key input protothread.
*/
static struct timer codelock_timer, input_timer;
/*---------------------------------------------------------------------------*/
/*
* This is the code that has to be entered.
*/
static const char code[4] = {'1', '4', '2', '3'};
/*---------------------------------------------------------------------------*/
/*
* This example has two protothread and therefor has two protothread
* control structures of type struct pt. These are initialized with
* PT_INIT() in the main() function below.
*/
static struct pt codelock_pt, input_pt;
/*---------------------------------------------------------------------------*/
/*
* The following code implements a simple key input. Input is made
* with the press_key() function, and the function key_pressed()
* checks if a key has been pressed. The variable "key" holds the
* latest key that was pressed. The variable "key_pressed_flag" is set
* when a key is pressed and cleared when a key press is checked.
*/
static char key, key_pressed_flag;
static void
press_key(char k)
{
printf("--- Key '%c' pressed\n", k);
key = k;
key_pressed_flag = 1;
}
static int
key_pressed(void)
{
if(key_pressed_flag != 0) {
key_pressed_flag = 0;
return 1;
}
return 0;
}
/*---------------------------------------------------------------------------*/
/*
* Declaration of the protothread function implementing the code lock
* logic. The protothread function is declared using the PT_THREAD()
* macro. The function is declared with the "static" keyword since it
* is local to this file. The name of the function is codelock_thread
* and it takes one argument, pt, of the type struct pt.
*
*/
static
PT_THREAD(codelock_thread(struct pt *pt))
{
/* This is a local variable that holds the number of keys that have
* been pressed. Note that it is declared with the "static" keyword
* to make sure that the variable is *not* allocated on the stack.
*/
static int keys;
/*
* Declare the beginning of the protothread.
*/
PT_BEGIN(pt);
/*
* We'll let the protothread loop until the protothread is
* expliticly exited with PT_EXIT().
*/
while(1) {
/*
* We'll be reading key presses until we get the right amount of
* correct keys.
*/
for(keys = 0; keys < sizeof(code); ++keys) {
/*
* If we haven't gotten any keypresses, we'll simply wait for one.
*/
if(keys == 0) {
/*
* The PT_WAIT_UNTIL() function will block until the condition
* key_pressed() is true.
*/
PT_WAIT_UNTIL(pt, key_pressed());
} else {
/*
* If the "key" variable was larger than zero, we have already
* gotten at least one correct key press. If so, we'll not
* only wait for the next key, but we'll also set a timer that
* expires in one second. This gives the person pressing the
* keys one second to press the next key in the code.
*/
timer_set(&codelock_timer, 1000);
/*
* The following statement shows how complex blocking
* conditions can be easily expressed with protothreads and
* the PT_WAIT_UNTIL() function.
*/
PT_WAIT_UNTIL(pt, key_pressed() || timer_expired(&codelock_timer));
/*
* If the timer expired, we should break out of the for() loop
* and start reading keys from the beginning of the while(1)
* loop instead.
*/
if(timer_expired(&codelock_timer)) {
printf("Code lock timer expired.\n");
/*
* Break out from the for() loop and start from the
* beginning of the while(1) loop.
*/
break;
}
}
/*
* Check if the pressed key was correct.
*/
if(key != code[keys]) {
printf("Incorrect key '%c' found\n", key);
/*
* Break out of the for() loop since the key was incorrect.
*/
break;
} else {
printf("Correct key '%c' found\n", key);
}
}
/*
* Check if we have gotten all keys.
*/
if(keys == sizeof(code)) {
printf("Correct code entered, waiting for 500 ms before unlocking.\n");
/*
* Ok, we got the correct code. But to make sure that the code
* was not just a fluke of luck by an intruder, but the correct
* code entered by a person that knows the correct code, we'll
* wait for half a second before opening the lock. If another
* key is pressed during this time, we'll assume that it was a
* fluke of luck that the correct code was entered the first
* time.
*/
timer_set(&codelock_timer, 500);
PT_WAIT_UNTIL(pt, key_pressed() || timer_expired(&codelock_timer));
/*
* If we continued from the PT_WAIT_UNTIL() statement without
* the timer expired, we don't open the lock.
*/
if(!timer_expired(&codelock_timer)) {
printf("Key pressed during final wait, code lock locked again.\n");
} else {
/*
* If the timer expired, we'll open the lock and exit from the
* protothread.
*/
printf("Code lock unlocked.\n");
PT_EXIT(pt);
}
}
}
/*
* Finally, we'll mark the end of the protothread.
*/
PT_END(pt);
}
/*---------------------------------------------------------------------------*/
/*
* This is the second protothread in this example. It implements a
* simulated user pressing the keys. This illustrates how a linear
* sequence of timed instructions can be implemented with
* protothreads.
*/
static
PT_THREAD(input_thread(struct pt *pt))
{
PT_BEGIN(pt);
printf("Waiting 1 second before entering first key.\n");
timer_set(&input_timer, 1000);
PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
press_key('1');
timer_set(&input_timer, 100);
PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
press_key('2');
timer_set(&input_timer, 100);
PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
press_key('3');
timer_set(&input_timer, 2000);
PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
press_key('1');
timer_set(&input_timer, 200);
PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
press_key('4');
timer_set(&input_timer, 200);
PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
press_key('2');
timer_set(&input_timer, 2000);
PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
press_key('3');
timer_set(&input_timer, 200);
PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
press_key('1');
timer_set(&input_timer, 200);
PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
press_key('4');
timer_set(&input_timer, 200);
PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
press_key('2');
timer_set(&input_timer, 100);
PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
press_key('3');
timer_set(&input_timer, 100);
PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
press_key('4');
timer_set(&input_timer, 1500);
PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
press_key('1');
timer_set(&input_timer, 300);
PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
press_key('4');
timer_set(&input_timer, 400);
PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
press_key('2');
timer_set(&input_timer, 500);
PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
press_key('3');
timer_set(&input_timer, 2000);
PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
PT_END(pt);
}
/*---------------------------------------------------------------------------*/
/*
* This is the main function. It initializes the two protothread
* control structures and schedules the two protothreads. The main
* function returns when the protothread the runs the code lock exits.
*/
int
main(void)
{
/*
* Initialize the two protothread control structures.
*/
PT_INIT(&input_pt);
PT_INIT(&codelock_pt);
/*
* Schedule the two protothreads until the codelock_thread() exits.
*/
while(PT_SCHEDULE(codelock_thread(&codelock_pt))) {
PT_SCHEDULE(input_thread(&input_pt));
/*
* When running this example on a multitasking system, we must
* give other processes a chance to run too and therefore we call
* usleep() resp. Sleep() here. On a dedicated embedded system,
* we usually do not need to do this.
*/
#ifdef _WIN32
Sleep(0);
#else
usleep(10);
#endif
}
return 0;
}
/*---------------------------------------------------------------------------*/
/*
* Finally, the implementation of the simple timer library follows.
*/
#ifdef _WIN32
static int clock_time(void)
{ return (int)GetTickCount(); }
#else /* _WIN32 */
static int clock_time(void)
{
struct timeval tv;
struct timezone tz;
gettimeofday(&tv, &tz);
return tv.tv_sec * 1000 + tv.tv_usec / 1000;
}
#endif /* _WIN32 */
static int timer_expired(struct timer *t)
{ return (int)(clock_time() - t->start) >= (int)t->interval; }
static void timer_set(struct timer *t, int interval)
{ t->interval = interval; t->start = clock_time(); }
/*---------------------------------------------------------------------------*/