run-daccs.cmd

client-server thread example

The following example shows that the same DAC channel, named "dac-ch0", can be shared among two threads of the same process.

Please read the output the test produces.

Run-daccs - setup environment to use .\rulbus.conf, run daccs.

Using RULBUS=isa
___

DacCS  1.0 (22 Jan 2004)  Use Rulbus DAC from a client and a server thread.

This program creates a server thread and a client thread.

Server: generate a squarewave on DAC channel 0 [data 1000,3000].
Client: read the data from DAC channel 0 and check its values;
        also copy data read to DAC channel 1.

The server thread is intentionally started somewhat later than
the client thread to show an error report from the latter.

ISA Rulbus Interface at [0x200], using CanIO port I/O

opened Rulbus device 'dac-ch0' at [0:0xD0]
opened Rulbus device 'dac-ch1' at [0:0xD2]

creating thread for server
creating thread for client

Press a key to stop...

client: reading data from   Rulbus device 'dac-ch0' at [0:0xD0]
client: copying data to     Rulbus device 'dac-ch1' at [0:0xD2]
client: dac handle 0 data 1234 not in [1000,3000]
client: dac handle 0 data 1234 not in [1000,3000]
server: write squarewave on Rulbus device 'dac-ch0' at [0:0xD0]

Terminating...
prompt>

Here is the Rulbus configuration file used.

# dac client--server test

rack "top"
{
   address = 0

   rb8510_dac12 "dac-ch0" { address = 0xD0 }
   rb8510_dac12 "dac-ch1" { address = 0xD2 }
}

The C++ source of the program.

/*
 * daccs.cpp - dac client-server.
 *
 * compile: bcc32 -P -q -tWC -tWR -tWM -I..\src .\daccs.cpp ..\bin\rulbus.lib
 */

#include <iostream>                     // for std::cout, std::cerr
#include <conio.h>                      // for kbhit()
#include <windows.h>                    // for DWORD HANDLE, Sleep()

#include "rulbus.h"                     // for rb8510_dac12_open() etc.

namespace ClientServer
{
   const char *title = "DacCS  1.0 (22 Jan 2004)  Use Rulbus DAC from a client and a server thread.\n";

   int           threads ( );
   HANDLE        mkThread( const char *msg, DWORD WINAPI (*ThreadFunc)(LPVOID), int32 *pHandle );
   DWORD  WINAPI client  ( LPVOID arg );
   DWORD  WINAPI server  ( LPVOID arg );
   int           error   (            );

   volatile bool runthread = true;      // true while threads should run

   const   int   NoCode   = 1234;       // DAC 'no' code
   const   int   LoCode   = 1000;       // DAC lower code
   const   int   HiCode   = 3000;       // DAC higher code

   /*
    * Rulbus address tuple for channel 0 and 1
    */

   struct Tuple
   {
      int32 dac0;
      int32 dac1;
   };

   #pragma argsused
   int main( int argc, char *argv[] )
   {
      std::cout << title << std::endl;

      return threads();
   }

   int threads()
   {
      std::cout <<
         "This program creates a server thread and a client thread.\n"
         "\n"
         "Server: generate a squarewave on DAC channel 0 [data " << LoCode << ',' << HiCode << "].\n"
         "Client: read the data from DAC channel 0 and check its values;\n"
         "        also copy data read to DAC channel 1.\n"
         "\n"
         "The server thread is intentionally started somewhat later than\n"
         "the client thread to show an error report from the latter.\n" << std::endl;

      /*
       * report on Rulbus Interface:
       */

      rdl_printRulbusInterface();

      /*
       * open two DAC channels:
       */

      Tuple tuple;

      if ( rb8510_dac12_open( &tuple.dac0, "dac-ch0" ) ||
           rb8510_dac12_open( &tuple.dac1, "dac-ch1" )   )
      {
         return error();
      }

      std::cerr << "opened "; RulbusDevice_print( tuple.dac0 );
      std::cerr << "opened "; RulbusDevice_print( tuple.dac1 );
      std::cerr << std::endl;

      /*
       * initialize DAC outputs to be different from server output:
       */

      rb8510_dac12_setValue( tuple.dac0, NoCode );
      rb8510_dac12_setValue( tuple.dac1, NoCode );

      /*
       * create and resume client and server threads;
       * intentionally resume the server thread somewhat later
       * to notice the error report from the client thread.
       */

      HANDLE thread0 = mkThread( "server", server, reinterpret_cast<LONG *>( &tuple ) );
      HANDLE thread1 = mkThread( "client", client, reinterpret_cast<LONG *>( &tuple ) );

      std::cerr << "\nPress a key to stop...\n" << std::endl;

      ResumeThread( thread1 ); Sleep( 2000 );
      ResumeThread( thread0 );

      /*
       * wait for key pressed; eat character:
       */

      while ( !kbhit() )
        Sleep( 10 );

      (void) getch();

      /*
       *  stop and remove threads and close DACs:
       */

      std::cerr << "\nTerminating..." << std::endl;

      runthread = 0; Sleep( 10 );

      CloseHandle( thread0 );
      CloseHandle( thread1 );

      if ( rb8510_dac12_close( tuple.dac1 ) ||
           rb8510_dac12_close( tuple.dac0 )   )
      {
         return error();
      }

      return EXIT_SUCCESS;
   }

   HANDLE mkThread( const char *msg, DWORD WINAPI (*ThreadFunc)(LPVOID), int32 *pTuple )
   {
      std::cerr << "creating thread for " << msg << std::endl;

      DWORD id;

      return CreateThread(
         NULL,             // pointer to thread security attributes
         0,                // initial thread stack size, in bytes
         ThreadFunc,       // pointer to thread function
         pTuple,           // argument for new thread
         CREATE_SUSPENDED, // creation flags
         &id               // pointer to returned thread identifier
      );
   }

   DWORD WINAPI server( LPVOID arg )
   {
      int32 dac0 = reinterpret_cast<Tuple *>(arg)->dac0;

      std::cerr << "server: write squarewave on "; RulbusDevice_print( dac0 );

      while ( runthread )
      {
         rb8510_dac12_setValue( dac0, LoCode ); Sleep( 0 );
         rb8510_dac12_setValue( dac0, HiCode ); Sleep( 0 );
      }

      return 0;
   }

   DWORD WINAPI client( LPVOID arg )
   {
      int32 dac0 = reinterpret_cast<Tuple *>(arg)->dac0;
      int32 dac1 = reinterpret_cast<Tuple *>(arg)->dac1;

      std::cerr << "client: reading data from   "; RulbusDevice_print( dac0 );
      std::cerr << "client: copying data to     "; RulbusDevice_print( dac1 );

      while ( runthread )
      {
         int32 n;

         if ( rb8510_dac12_getValue( dac0, &n ) ||
              rb8510_dac12_setValue( dac1,  n )    )
         {
            return error();
         }

         if ( n != LoCode && n!= HiCode )
         {
            std::cerr << "client: dac handle " << dac0 << " data " << n << " not in [" << LoCode << ',' << HiCode << "]" << std::endl;
            Sleep  ( 1000 );
         }
         else
         {
            // do nothing
            // std::cerr << n << " "; Sleep(300);
         }

         Sleep(0);
      }

      return 0;
   }

   int error()
   {
      const int len = 100; char  msg[len];

      rdl_getLastError( msg, len );
      std::cerr << msg << std::endl;

      return EXIT_FAILURE;
   }

} // namespace ClientServer

/*
 * End of file
 */

This is the batch command file used to invoke the program.

@echo off
rem
rem run-daccs.bat - setup environment to use .\run-daccs.conf, run daccs.
rem

cls
echo Run-daccs - setup environment to use .\run-daccs.conf, run daccs.
echo.

set RULBUS.ORG=%RULBUS%
set RULBUS_CONFIG_FILE.ORG=%RULBUS_CONFIG_FILE%

:set RULBUS=epp,0x378;nocheck
set RULBUS=isa

echo Using RULBUS=%RULBUS%
echo ___
echo.

set RULBUS_CONFIG_FILE=run-daccs.conf

copy ..\bin\rulbus.dll . >nul

daccs

rem
rem restore environment:
rem

set RULBUS=%RULBUS.ORG%
set RULBUS.ORG=

set RULBUS_CONFIG_FILE=%RULBUS_CONFIG_FILE.ORG%
set RULBUS_CONFIG_FILE.ORG=

rem
rem end of file
rem


Generated on Wed Apr 6 08:59:18 2005 for Rulbus Device Library for Microsoft Windows by doxygen 1.4.0