/* * This Software is the original work of Daniel TUNG * This Software is submitted in partial fulfillment of the * requirements for the degree of BCSE. * Dept. Computer Science, Monash University 2000 * * Copyright (c) 2000 beetung@cs.monash.edu.au * beetung@geocities.com */ #ifndef CONSUMER_PRODUCER_H #define CONSUMER_PRODUCER_H #include #include "semaphore.h" #include "thread.h" #include "comport.h" #include "timer.h" #include "xwin.h" #include "carop.h" //// //// Consumer, our "base controller loop" for forwarding commands to RS-232 //// ///////////////////////////////// class Consumer : public BaseSema, public ComPort, public Thread { ///////////////////////////////// public: ~Consumer() { cout << "~Consumer() " << endl; } Consumer (MySemaphore& a, int portID =0): BaseSema(a), ComPort(portID), Thread(this,&consumeStart,"Consumer") {cerr << "Consumer::Consumer()" << endl; } private: void *run () { cout << " Consumer :: run() " << endl; return 0;} /// /// a Thread<> would run this function once its operator() is called /// //--------------- void *consumeStart() { //--------------- // cout << " ....ConsumerRead!" << endl; Xwin xMesg("( ) ComPort debug messages"); Thread xThread( &xMesg, &(xMesg.serveEvent), \ "Consumer - xMesg.serveEvent"); int currCount =-1; // count on current command number xMesg.realize_window(); xMesg << "graR graR" ; xThread(); while ( !quit ) { // SemWait(); /* cout << "....Consumer consumed! " << "Executing move : " << moveName[getCommand()] << endl; */ if (!xMesg) { quit = true; cout << "!xMesg breaking .... " << endl; break; } // // all 3 calling methods work the same way, which one is more // illustrative for u? // // ComPort::writeCom(getCommand()); // *this << getCommand(); if ( currCount != CommandCount() ) { xMesg << moveName[getCommand()] ; ComPort::operator<<( getCommand() ) ; currCount = CommandCount(); } else { xMesg << "Consumer :: No Command ... ComPort << CarOp::HALT " ; ComPort::operator<<( CarOp::HALT ) ; } if (xMesg.kbhitArrows) { //// //// At this point, the user has hit the keyboard when the //// mouse cursor is pointing at ComPort Debug window. We //// would like to stop the Xwin::serveEvent(...) thread and //// handle further key press separately. ie: the arrow keys. //// // kill the serveEvent Thread 1st... // xThread.uDie(); // not using it, cos not tested fully // we use a work around, serveStop(); cout << " Going for Manual Drive ... " << endl; xMesg.serveStop(); manualDrive(xMesg); xMesg.serveStart(); //// afterwards, we can start the serveEvent(...) thread again //// or just quit. // xThread(); } /// /// We let the command execute for 0.22sec /// Timer t(0.22); t.block(); } //while (1) return 0; } //--------------- void manualDrive(Xwin &xMesg) { //--------------- int key; int gear =0, dirRight =0; while (1) { key =xMesg.serveKey(); cout << key << endl; switch (key) { case 98: xMesg<<"Up"; gear++; gear %=3; break; case 100: xMesg<<"Left"; dirRight--; if (dirRight < -1) dirRight = -1; break; case 102: xMesg<<"Right"; dirRight++; if (dirRight > 1) dirRight = 1; break; case 104: xMesg<<"Down"; gear--; if (gear < -1) gear =-1; break; default: // if (key == -1) quit = true; break; } if (quit==true) { break; } if (dirRight == 0) if (gear ==2) ComPort::operator<<( CarOp::FF_FORW ) ; else if (gear ==1) ComPort::operator<<( CarOp::FORW ) ; else if (gear ==0) ComPort::operator<<( CarOp::HALT ) ; else if (gear ==-1) ComPort::operator<<( CarOp::BACK ) ; if (dirRight == -1) if (gear ==2) ComPort::operator<<( CarOp::FF_LEFT ) ; else if (gear ==1) ComPort::operator<<( CarOp::F_LEFT ) ; else if (gear ==0) ComPort::operator<<( CarOp::LEFT ) ; else if (gear ==-1) ComPort::operator<<( CarOp::B_LEFT ) ; if (dirRight == 1) if (gear ==2) ComPort::operator<<( CarOp::FF_RIGH ) ; else if (gear ==1) ComPort::operator<<( CarOp::F_RIGH ) ; else if (gear ==0) ComPort::operator<<( CarOp::RIGH ) ; else if (gear ==-1) ComPort::operator<<( CarOp::B_RIGH ) ; } // while (1) } }; //// //// Producer //// ///////////////////////////////// class Producer : public BaseSema { ///////////////////////////////// public: //--------------- Producer(MySemaphore& a) : BaseSema(a) {;} //--------------- ~Producer() { //--------------- cout << "~Producer() " ; /* << "- matching SemExitCounter() " << endl; // not useful on NDA objects, cos ~dtor() is not called // straight away! but, only at end of scope!! // while (SemExitCounter()) SemPost(); // increments finally by 1, so consumer can die happily! */ } /// //--------------- void *run(void) { Produce(); return 0;} void Produce (void) { //--------------- // Timer time(0.44); CommandCount() ++ ; SemPost(); // signal command ready, consumer can consume it now! } /// /// a Thread<> would run this function once its operator() is called /// //--------------- void *run_test() { //--------------- Timer time(0.44); int i=0; int instr[] = { CarOp::LEFT, // left + forward CarOp::RIGH // right + forward }; testCar(); while (1) { i++; cout << " Producer Produced...... ("<< i <<") " << endl; // // This set the move command into a baseClass varialbe, which is // being shared between "consumer" and "producer", need a mutex lock! // setCommand(instr[i%2]); SemPost(); if (quit) return 0; // // sit idle for a while mate // time.reset(); while (!time.isZero()) ; } cout << " Producer completed... terminating " << endl; quit =true; SemPost(); // increments finally by 1, so consumer can die happily! } //--------------------- void testCar (void) { //--------------------- int i,j; i=j=0; // Timer time(0.40); // Timer time(0.44); // best for (left+forward) // Timer time(0.47); // Timer time(0.50); Timer time(0.44); // best for (left+forward) // Timer time(0.22); // for replacing existing one int instr[] = { CarOp::F_LEFT, // left + forward CarOp::F_RIGH, // right + forward CarOp::FF_FORW,// fast forward }; for (i=0;i!=4;i++) { time.reset(); BaseSema::setCommand(instr[i%3]); BaseSema::SemPost(); // let consumer consume it while (!time.isZero()) ; cout << "TIMER Ended " << time << endl; } cout << "END" << endl; } }; #endif // CONSUMER_PRODUCER_H