FeeServer implemented in C++

0.9

This is a C++ implementation of the FeeServer.

Contents

It's a complete re-implementation of the original C FeeServer (see Links).

The focus of this implementation is to make the code simple and clear, and provide a simple interface for customisation of the FeeServer.

Server

The basic object of the FeeServer is the class FeeServer::Main. A user should always define an object of this type. Alternatively, one can derive a user class from this class for doing more stuff (code from test_server::cc).

  FeeServer::Main m(name, detector, dns);

Channels

The basic FeeServer::Main class provides the 3 DIM channels

where name is the name of the server as set in the constructor of FeeServer::Main.

The command channel provides a way for clients to send commands to the server. The command contains a header (see FeeServer::Header) followed by binary large object (BLOB) of data (see FeeServer::Command). The header contains a few pieces of information for the server to use. The meaning of the binary data depends entirely on the command send and the implementation of the server.

The acknowledge channel provides the feed back channel from the command handling. After a command is processed, this channel is updated. The channel contains a header (see FeeServer::Header) followed by binary large object (BLOB) of data (see FeeServer::Acknowledge). The meaning of the BLOB depends entirely on the command sent and the implementation of the server. The header will have the same identifier field as the processed command, and contain a status code.

The message channel provides various messages about the operations of the server (see FeeServer::Message). The type of messages that are send is configurable.

  if (debug) {
    unsigned int mask = m.GetLogLevel();
    std::cout << "Log mask: 0x" << std::hex << mask << " -> ";
    mask |= FeeServer::Message::Debug;
    std::cout << "0x" << mask << std::dec << std::endl;
    m.SetLogLevel(mask);
  }

Proccess in the server

The server runs several process in its process.

Monitoring

A separate thread is running the monitoring (see FeeServer::MonitorThread). Each time a registered service is updated, a corresponding DIM service will be updated. The frequency of forces updates can be configured.

Message watch dog

A separate thread is making sure that messages are send to the clients (see FeeServer::MessageWatchdog) and that the channel isn't flooded.

Issue thread

Handling of control engine commands is done in a separate thread to allow the server to continue monitoring services while handling commmands (see FeeServer::IssueThread).

Server commands

As it stands, the FeeServer::Main object has no commands what so ever. However,the abstract class FeeServer::ServerCommand provides an interface for defining server commands. A number of commands is defined in the library (see Server commands).

You can simple create objects of the server command type, and then add them to the server using the member function FeeServer::Main::AddCommand.

  // m.AddCommand(new UpdateServer(m));
  m.AddCommand(new FeeServer::RestartServer(m));
  // m.AddCommand(new FeeServer::RebootMachine(m));
  // m.AddCommand(new FeeServer::ShutdownMachine(m));
  m.AddCommand(new FeeServer::ExitServer(m));
  m.AddCommand(new FeeServer::SetDeadband(m));
  m.AddCommand(new FeeServer::GetDeadband(m));
  m.AddCommand(new FeeServer::SetIssueTimeout(m));
  m.AddCommand(new FeeServer::GetIssueTimeout(m));
  m.AddCommand(new FeeServer::SetUpdateRate(m));
  m.AddCommand(new FeeServer::GetUpdateRate(m));
  m.AddCommand(new FeeServer::SetLogLevel(m));
  m.AddCommand(new FeeServer::GetLogLevel(m));

Control Engine

To control actual hardware or other stuff in the server machine, one needs to define a Control Engine. The library provides the abstract class FeeServer::ControlEngine that the user should derive from and implement.

namespace Examples 
{
  /** A test control engine 
      @ingroup tests */
  class TestControlEngine : public FeeServer::ControlEngine 
  {
  public:
    /** Constructor 
        @param m Reference to FeeServer */
    TestControlEngine(FeeServer::Main& m) 
      : FeeServer::ControlEngine(m), 
        _int_service("INT"), 
        _float_service("FLOAT")
    {
      m.Publish(_int_service);
      m.Publish(_float_service);
    }

The derived class should do periodic stuff in the FeeServer::ControlEngine::Start member function.

    /** Start the thread */
    void Start() 
    {
      usleep(5);
      Ready();

      sleep(1);
      while (true) {
        TestCancel();
        int   ival = int(10 * float(rand())/RAND_MAX);
        float fval = int(10 * float(rand())/RAND_MAX) / 10.;
        _int_service   = ival;
        _float_service = fval;
        usleep(100000);
      }
    } //End-Start
Note:
It's crucial that the member function FeeServer::ControlEngine::Ready is called from this member function. The main object waits for the control engine to signal that it is ready, and the control engine should wait until the FeeServer::Main object is ready. The FeeServer::ControlEngine::Ready does both of this.
Commands for the control engine should be handled in the FeeServer::ControlEngine::Issue member function.

    /** Handle commands send to the control engine 
        @param idata Input data 
        @param isize # of valid bytes in @a idata 
        @param odata On output, the result. 
        @param osize On output, the # of valid bytes in @a odata 
        @return Status code */
    short Issue(const std::vector<unsigned char>&  idata, size_t  /*isize*/,
                std::vector<unsigned char>& odata, size_t& osize) 
    {
      switch (idata[0]) {
      case 1: 
        osize = _int_service.Size();
        if (odata.size() < osize) odata.resize(osize);
        memcpy(&(odata[0]), &(_int_service.Value()), osize);
        break;
      case 2: 
        osize = _float_service.Size();
        if (odata.size() < osize) odata.resize(osize);
        memcpy(&(odata[0]), &(_float_service.Value()), osize);
        break;
      case 3:
        osize = 0;
        std::cout << "Services to monitor: " << std::endl;
        for (FeeServer::MonitorThread::ItemMap::iterator i = 
               _main.MonThread().Items().begin(); 
             i != _main.MonThread().Items().end(); i++) 
          std::cout << "\t" << i->first << "\t" << i->second->Name() 
                    << std::endl;
        break;
      default:
        osize = 0;
      }
      return 0;
    } //End-Issue

The member function FeeServer::ControlEngine::CleanUp is called when the server exists.

    /** Clean-up member function */
    void CleanUp() {}

Services

The control engine can define a number of services by declaring objects of sub-classes of the abstract class FeeServer::ServiceBase. The services should be added to some FeeServer::ServiceHandler on which a periodic call to FeeServer::ServiceHandler::UpdateAll is done.

The library provides a class template (see FeeServer::ServiceT) for defining types, as well as some specialisation for common types (FeeServer::IntService, FeeServer::FloatService).

    // An integer service 
    FeeServer::IntService _int_service;
    // A floating point service 
    FeeServer::FloatService _float_service;
  };
} //End-Example

The control engine should then be registered with the FeeServer::Main object, and we can execute the server.

  Examples::TestControlEngine ce(m);
  m.SetCtrlEngine(ce);

  std::cout << "Running fee server " << std::endl;
  return m.Run();

Simple client

The class FeeServer::Client provides a simple client. For more elaborate stuff, one could extract this client (in source files fee_client.hh and fee_client.cc) and the packet definitions (in source file fee_packet.hh) from this library and use it in some client code. The client provides a number of member functions to handle messages, acknowledges, and as well as missing services, and to send commands to the server (see test_client::cc).

Links


Generated on Thu Jun 26 17:02:21 2008 for FeeServer++ 0.9 by  doxygen 1.5.6