Extending the System

Home

Writing Controller Drivers

Controller drivers are interface codes between Kinoko and the UNIX device driver. By implementing accessing of device drivers through interfaces set to the standard of Kinoko, the details of each device drivers are hidden from Kinoko and a unified interface is provided to other parts of Kinoko (such as module drivers).

CAMAC Controller Driver

CAMAC controller driver is an interface code to CAMAC controller's UNIX device driver. In Kinoko, as a common interface to all CAMAC controllers, a abstract class TRoomCamacController class is defined. Controller drivers to each device inherit this class and are made by implementing methods from this class.

TRoomCamacController class is defined by kinoko/src/kernel/lib-common/room/RoomCamacAccess.hh.

class TRoomCamacController: public TRoomController {
  public:
    TRoomCamacController(void);
    virtual ~TRoomCamacController();
    virtual TRoomCamacController* Clone(void) = 0;
  public:
    virtual void Open(void) throw(THardwareException) = 0;
    virtual void Close(void);
    virtual int Transact(int StationNumber, int Function, int Address, int &Data, int &Q, int &X) throw(THardwareException) = 0;
    virtual int Initialize(void) throw(THardwareException) = 0;
    virtual int Clear(void) throw(THardwareException) = 0;
    virtual int SetInhibition(void) throw(THardwareException) = 0;
    virtual int ReleaseInhibition(void) throw(THardwareException) = 0;
    virtual int EnableInterruptNotification(void) throw(THardwareException) = 0;
    virtual int DisableInterruptNotification(void) throw(THardwareException) = 0;
    virtual bool IsSignalOnInterruptAvailable(void);
    virtual int ReadLam(void) throw(THardwareException) = 0;
    virtual int WaitLam(int TimeOut_sec) throw(THardwareException) = 0;
};
Almost all methods are pure virtual functions and all of these must be implemented by child classes. Methods that are not virtual will be overridden if needed.

The following list explains each method.

TRoomCamacController* Clone(void)
Creates an instance of itself and returns the pointer to it.

void Open(void) throw(THardwareException)
Open. Called only once before any other method is called. Normally used to open drivers.

void Close(void)
Close. Called only once at the end of all processes. Normally used to close drivers.

int Transact(int StationNumber, int Function, int Address, int &Data, int &Q, int &X) throw(THardwareException)
Executes CAMAC action once according to the argument passed. If it is a READ action, returns the data value in the argument "Data." Q and X responses are returned in the cooresponding arguments Q and X.

int Initialize(void) throw(THardwareException)
Issues Z (Initialize).

int Clear(void) throw(THardwareException)
Issues C (Clear).

int SetInhibition(void) throw(THardwareException)
Sets I (Inhibit).

int ReleaseInhibition(void) throw(THardwareException)
Releases I (Inhibit).

int EnableInterruptNotification(void) throw(THardwareException)
Enables interrupts to controller devices.

int DisableInterruptNotification(void) throw(THardwareException)
Disables interrupts to controller devices.

bool IsSignalOnInterruptAvailable(void)
Returns "true" if the device driver has a function that can be interrupted to issues signals.

int ReadLam(void) throw(THardwareException)
Returns LAM pattern as a bit sequence. If the driver does not have this function, exceptions can be thrown.

int WaitLam(int TimeOut_sec) throw(THardwareException)
Waits for LAM with TimeOut_sec indicated in the argument as an upper limit of waiting time. If there is a LAM signal, the value of the LAM signal pattern in bit array is returned. If timed out, returns a negative value.

Controller drivers are made with file names "controller-vender name_device name.hh/cc". Place this in the ROOM directory (kinoko/src/kernel/lib-common/room) and edit Makefile.in and Makefile so it can be compiled.

Add the following bolded line before all: in Makefile.in/Makefile (in the following example, the vender name is CamacInternational, and the device name is Camac2000).

# add the following two lines
DEVOBJS += controller-CamacInternational_Camac2000.o
HAS_DEVICE=yes

all: $(ARC) $(DEVOBJS) $(BINS)
	@if [ $(HAS_DEVICE) = yes ]; then \
		cd samples; $(MAKE); \
	fi

To register the controller driver object we just made to the system, TRoomCamacControllerCreator defined in RoomDeviceFactory.hh is used. This is a little bit tricky, but create the static Creator object inside the .cc file, and pass the controller driver name and the controller driver instance as the contructor argument. By doing this, the controller driver instance is automatically created when the driver code is loaded and will be registered to the system.

/* controller-CamacInternational_Camac2000.cc */

#include "RoomDeviceFactory.hh"
#include "controller-CamacInternational_Camac2000.hh"

static TRoomCamacControllerCreator Creator(
    "CamacInternational_Camac2000", 
    new TCamacController_CamacInternational_Camac2000()
);

//...

VME Controller Driver

The VME controller driver is very similar to the CAMAC controller driver but is a bit more complicated to accomodate all the various functions VME modules have.

Since the VME module has many parameters related to accessing such as base address and access mode, it is common to open a device driver for each module and set access modes and map memory. Therefore VME controller drivers will be opened repeatedly for each modules connected.

By system call "open()", the VME controller driver in Kinoko will create a VmeAccessProperty object and return it. Information such as device descriptor of the driver and access mode parameters are recorded in the VmeAccessProperty object so that VME access after "open()" will be done with VmeAccessProperty called. In general, the information inside VmeAccessProperty is used only by the VME controller driver.

The following shows the structure of VmeAccessProperty.

class TRoomVmeAccessProperty {
  public:
    off_t VmeAddress;                    // VME base address of the module
    TRoomVmeTransferMode TransferMode;   // transfer mode (address
  width, data word length)
    int DeviceDescriptor;                // device descriptor returned
  by calling open()
    caddr_t MappedAddress;               // address mapped by mmap()
    size_t MapSize;                      // map size passed to mmap()
    off_t MapOffset;                     // offset of the address passed to mmap() from the base address
    int DmaDeviceDescriptor;             // device descriptor when
  open() is called for DMA transfer
    int InterruptNumber;                 // interrupt IRQ (or
  interrupt number)
    int InterruptVector;                 // interrupt vector
    int InterruptSignalId;               // signal ID of the signal
  issued by interrupt
    void* DeviceSpecificData;            // data space the controller
  driver can use freely
};
The meaning of each field is the follwoing.
off_t VmeAddress
Module's VME offset address. Normally set by jumpers of the module.

TRoomVmeTransferMode TransferMode
enum values that show the address width and data word length of the transfer mode. The following values are defined.
enum TRoomVmeTransferMode {
    VmeTransferMode_A16D16, 
    VmeTransferMode_A16D32, 
    VmeTransferMode_A24D16, 
    VmeTransferMode_A24D32, 
    VmeTransferMode_A32D16, 
    VmeTransferMode_A32D32,
    VmeTransferMode_Unknown
};

int DeviceDescriptor
device descriptor returned by system call open().

caddr_t MappedAddress
address of the process in the address space set by system call mmap().

size_t MapSize
map size passed to system call mmap().

off_t MapOffset
offset from the base address (VmeAddress) of the address in VME address space passed to system call mmap().

int DmaDeviceDescriptor
device descriptor of another device entry opened for DMA transfer.

int InterruptNumber
interrupt number when interrupts are used. Generally the IRQ number. For certain devices, it refers to different numbers such as entry number of an interrupt table.

int InterruptVector
interrupt vector.

int InterruptSignalId
signal ID issued if the interrupt issues signal IDs. A signal ID is the value defined in <signal.h>.

void* DeviceSpecificData
free space for the controller driver.
The creating of a VME controller driver is done similarly to the CAMAC case by inheriting the abstract class TRoomVmeController and overriding necessary methods. The TRoomVmeController class is defined in RoomVmeAccess.hh.
class TRoomVmeController: public TRoomController {
  public:
    TRoomVmeController(void);
    virtual ~TRoomVmeController();
    virtual TRoomVmeController* Clone(void) = 0;
  public:
    virtual TRoomVmeAccessProperty* Open(TRoomVmeTransferMode TransferMode) throw(THardwareException);
    virtual void Close(TRoomVmeAccessProperty* Property);
    virtual long Read(TRoomVmeAccessProperty* Property, void* Buffer, int Size) throw(THardwareException);
    virtual long Write(TRoomVmeAccessProperty* Property, const void* Buffer, int Size) throw(THardwareException);
    virtual off_t Seek(TRoomVmeAccessProperty* Property, off_t OffsetAddress) throw(THardwareException);
    virtual caddr_t Map(TRoomVmeAccessProperty* Property, off_t VmeAddress, size_t MapSize, off_t MapOffset) throw(THardwareException);
    virtual void Unmap(TRoomVmeAccessProperty* Property) throw(THardwareException);
    virtual void RegisterInterruptNotification(TRoomVmeAccessProperty* Property, int SignalId = 0) throw(THardwareException) = 0;
    virtual void UnregisterInterruptNotification(TRoomVmeAccessProperty* Property) throw(THardwareException) = 0;
    virtual void EnableInterruptNotification(TRoomVmeAccessProperty* Property) throw(THardwareException) = 0;
    virtual void DisableInterruptNotification(TRoomVmeAccessProperty* Property) throw(THardwareException) = 0;
    virtual bool WaitForInterruptNotification(TRoomVmeAccessProperty* Property, unsigned TimeOut_sec) throw(THardwareException) = 0;
    virtual long DmaRead(TRoomVmeAccessProperty* Property, void* Buffer, int Size) throw(THardwareException);
    virtual long DmaWrite(TRoomVmeAccessProperty* Property, const void* Buffer, int Size) throw(THardwareException);
    virtual off_t DmaSeek(TRoomVmeAccessProperty* Property, off_t OffsetAddress) throw(THardwareException);
  public:
    virtual bool IsSignalOnInterruptAvailable(void);
  protected:
    virtual const char* DeviceFileName(TRoomVmeTransferMode TransferMode) = 0;
    virtual const char* DmaDeviceFileName(TRoomVmeTransferMode TransferMode) = 0;
    virtual void DmaOpen(TRoomVmeAccessProperty* Property) throw(THardwareException);
    virtual void DmaClose(TRoomVmeAccessProperty* Property);
};
The following list explains each method. Unlike CAMAC, some common methods such as Open() / Close() / Read() / Write( ) / Seek() / Map() / Unmap() are already implemented and do not need to be overridden in child classes unless there is a special need to (other virtual functions need to be implemented in child classes).
TRoomVmeController* Clone(void)
Creates an instance of itself and returns the pointer to it.

const char* DeviceFileName(TRoomVmeTransferMode TransferMode)
returns the UNIX device driver entry name (file name of /dev) that corresponds to the transfer mode specified in the argument.

const char* DmaDeviceFileName(TRoomVmeTransferMode TransferMode)
returns the UNIX device driver entry name (file name of /dev) that corresponds to the transfer mode specified in the argument when transfering with DMA transfer. Even if the device driver has the same entry with PIO and DMA, it returns that entry name. If DMA is not supported, then PIO entry name is returned.

TRoomVmeAccessProperty* Open(TRoomVmeTransferMode TransferMode) throw(THardwareException)
opens the detice driver for PIO mode and creats an AccessProperty object and returns it. Normally there is no need to override.

void Close(TRoomVmeAccessProperty* Property)
releases resources and closes the device driver. Normally there is no need to override.

long Read(TRoomVmeAccessProperty* Property, void* Buffer, int Size) throw(THardwareException)
readout data from the device. Normally there is no need to override.

long Write(TRoomVmeAccessProperty* Property, const void* Buffer, int Size) throw(THardwareException)
write in data into the device. Normally there is no need to override.

off_t Seek(TRoomVmeAccessProperty* Property, off_t OffsetAddress) throw(THardwareException)
move the file pointer for Read()/Write()(lseek()). Normally there is no need to override.

caddr_t Map(TRoomVmeAccessProperty* Property, off_t VmeAddress, size_t MapSize, off_t MapOffset) throw(THardwareException)
maps (mmap()) the VME address space indicated in the argument to the process address space and returns the address in process address space. Normally there is no need to override.

void Unmap(TRoomVmeAccessProperty* Property) throw(THardwareException)
unmaps the area mapped by Map(). Normally there is no need to override.

void RegisterInterruptNotification(TRoomVmeAccessProperty* Property, int SignalId = 0) throw(THardwareException)
registers interrupts and prepares for receiving them. If the argument SignalId is not 0, signal is issued on interrupt.

void UnregisterInterruptNotification(TRoomVmeAccessProperty* Property) throw(THardwareException)
unregisters a registered interrupt and releace resource.

void EnableInterruptNotification(TRoomVmeAccessProperty* Property) throw(THardwareException)
enables interrupts.

void DisableInterruptNotification(TRoomVmeAccessProperty* Property) throw(THardwareException)
disables interrupts.

bool WaitForInterruptNotification(TRoomVmeAccessProperty* Property, unsigned TimeOut_sec) throw(THardwareException)
waits for an interrupt with the time indicated in argument TimeOut_sec as the maximum waiting time. If there is an interrupt, return ture. If it timed out, return false.

void DmaOpen(TRoomVmeAccessProperty* Property) throw(THardwareException)
opens device driver for DMA mode. Normally there is no need to overload.

void DmaClose(TRoomVmeAccessProperty* Property)
closes the device driver opened for DMA. Normally there is no need to overload.

long DmaRead(TRoomVmeAccessProperty* Property, void* Buffer, int Size) throw(THardwareException)
reads out data from the device in DMA mode. Normally there is no need to overload.

long DmaWrite(TRoomVmeAccessProperty* Property, const void* Buffer, int Size) throw(THardwareException)
writes data in to the device in DMA mode. Normally there is no need to overload.

off_t DmaSeek(TRoomVmeAccessProperty* Property, off_t OffsetAddress) throw(THardwareException)
moves file pointers for DmaRead()/DmaWrite() (lseek()). Normally there is no need to overload.

bool IsSignalOnInterruptAvailable(void)
returns true if the device driver has a function that can issue a signal on interrupt.
Like the CAMAC controller driver, VME controller driver files need to be named such as "controller-vender name_device name.hh/cc". Place this in the ROOM directory (kinoko/src/kernel/lib-common/room) and edit Makefile.in and Makefile so it can be compiled.
# add the following two lines
DEVOBJS += controller-VmeInternational_Vme2000.o
HAS_DEVICE=yes

all: $(ARC) $(DEVOBJS) $(BINS)
	@if [ $(HAS_DEVICE) = yes ]; then \
		cd samples; $(MAKE); \
	fi

Again, use TRoomVmeControllerCreator to register the controller driver object to the system, just like the CAMAC case.

/* controller-VmeInternational_Vme2000.cc */

#include "RoomDeviceFactory.hh"
#include "controller-VmeInternational_Vme2000.hh"

static TRoomVmeControllerCreator Creator(
    "VmeInternational_Vme2000", 
    new TVmeController_VmeInternational_Vme2000()
);

//...

Writing Module Drivers

Module drivers are interfaces between Kinoko and data acquiring modules such as ADC and I/O register. As a common interface to all modules, an abstract class TRoomModule is defined, and within this class many controls that are common to most modules such as Read() and WaitData() are declared. A module driver corresponding to each module implements an interface with Kinoko by overriding necessary controls from this class.

Classes for VME and CAMAC modules, TRoomVmeModule and TRoomCamacModule, are generated from the TRoomModule class. These posess pointers from VME and CAMAC controller drivers, and through these classes basic controls to access VME and CAMAC are provided. Module drivers that correspond to a specific module implement interfaces using these basic controls.

The class TRoomModule is defined in src/kernel/lib-common/room/RoomModule.hh. The following is an excerpt from the definition.
class TRoomModule: public TRoomServiceRequester {
  protected:
    TRoomModule(const std::string& ModuleType, const std::string& ModelName);
  public:
    virtual ~TRoomModule();

    // control related //
    virtual bool Probe(void) throw(THardwareException);
    virtual int Initialize(int InitialState = 0) throw(THardwareException);
    virtual int Finalize(int FinalState = 0) throw(THardwareException);
    virtual int Enable(int Address = -1) throw(THardwareException);
    virtual int Disable(int Address = -1) throw(THardwareException);
    virtual int IsEnabled(int Address = -1) throw(THardwareException);
    virtual int IsBusy(int Address = -1) throw(THardwareException);
    virtual int Clear(int Address = -1) throw(THardwareException);

    // data input/output related //
    /* signal input/output */
    virtual int Read(int Address, int &Data) throw(THardwareException);
    virtual int Write(int Address, int Data) throw(THardwareException);
    /* sequential input/output */
    virtual int SequentialRead(int Address, int Data[], int MaxSize) throw(THardwareException);
    virtual int SequentialWrite(int Address, int Data[], int Size) throw(THardwareException);
    virtual int NextNumberOfDataElements(int Address = -1) throw(THardwareException);
    /* block input/output */
    virtual int BlockRead(int Address, void *Data, int MaxSize) throw(THardwareException);
    virtual int BlockWrite(int Address, const void *Data, int Size) throw(THardwareException);
    virtual int NextDataBlockSize(int Address = -1) throw(THardwareException);

    // interrupt processing related //
    virtual int EnableInterrupt(int SignalId = 0) throw(THardwareException);
    virtual int DisableInterrupt(void) throw(THardwareException);
    virtual int ClearInterrupt(void) throw(THardwareException);

    // introspection related //
    virtual int NumberOfChannels(void) throw(THardwareException);
    virtual int AddressBitLength(void);
    virtual int DataBitLength(void);

    // others //
    virtual int ReadRegister(int Address, int& Data) throw(THardwareException);
    virtual int WriteRegister(int Address, int Data) throw(THardwareException);
    virtual bool HasData(int Address = -1) throw(THardwareException);
    virtual bool WaitData(unsigned TimeOut_sec = 1) throw(THardwareException);
};
The meaning of each method is explained below. Be aware that only the ones necessary need to be overridden in the module driver. If a control that is not overridden is called, a default behavier is executed if there is one and if not, THardwareException is thrown.
= = = = = [Control Related] = = = = =

bool Probe(void) throw(THardwareException)
returns true if the module is connected and working properly. Usually checks Identification register. If the module does not have this feature, true is returned unconditionally (unless it is overridden).

int Initialize(int InitialState = 0) throw(THardwareException)
initializes the module. Nothing occurs if it is not overridden. happen.

int Finalize(int FinalState = 0) throw(THardwareException)
prepares the module for finalizing. Nothing occurs if it is not overridden.

int Enable(int Address = -1) throw(THardwareException)
enables the indicated channel (if a negative number is indicated, all channels are enabled). For starting FADC or starting scalar counting.

int Disable(int Address = -1) throw(THardwareException)
disables the indicated channel (if a negative number is indicated, all channels are disabled). For stopping scalar counting and such.

int IsEnabled(int Address = -1) throw(THardwareException)
returns 1 if the indicated channel is enabled (or if all channels are enabled for negative numbers), and 0 if not.

int IsBusy(int Address = -1) throw(THardwareException)
returns 1 if the indicated channel is busy (or if all channels are busy for negative numbers), and 0 otherwise.

int Clear(int Address = -1) throw(THardwareException)
clears the indicated channel (or all channels for negative numbers).

= = = = = [Data Input/Output Related] = = = = =

[Signal Input/Output]

int Read(int Address, int &Data) throw(THardwareException)
reads out one word of data from the indicated address (channel). Returns 1 if successfully read out, and 0 if the data does not exist. In FADC and multihit TDC, the next dataword in that specific channel is returned to the argument everytime this method is called, and returns 0 when all data words are read. In case of error, THardwareException is thrown.

int Write(int Address, int Data) throw(THardwareException)
writes one word of data in the indicated address (channel). Returns 1 if finished successfully and throws THardwareException when an error occurs.

[Sequential Input/Output]

int SequentialRead(int Address, int Data[], int MaxSize) throw(THardwareException)
reads out data from the specified address (channel) while there is data with the number of data limited with MaxSize and records to Data[]. Returns the number of dataword read out. If this method is not overridden, an implementation that loops Read() is used.

int SequentialWrite(int Address, int Data[], int Size) throw(THardwareException)
data word array passed to Data and Size is written in to the indicated address (channel) and the number of datawords is returned. If this method is not overridden, an implementation that loops Write() is used.

int NextNumberOfDataElements(int Address = -1) throw(THardwareException)
returns the number of datawords (or its' maximum) that will be readout by the next SequencialRead(). For FADC's number of words and multihit TDC's maximum hits. This value obtained is used for securing buffer for the next SequentialRead() and argument MaxSize. Be aware that the actual number of words readout will be returned when SequencialRead() is called.

[Block Input/Output]

int BlockRead(int Address, void *Data, int MaxSize) throw(THardwareException)
reads out datablocks from the indicated address (channel) with MaxSize as its' maximum, records them in data area indicated in Data, and returns the size of data readout. The interpretation of the argument Address depends on the module. Sometimes it will mean channel and other times it might mean the offset from the base address. In many cases, argument Address is simply ignored.

int BlockWrite(int Address, const void *Data, int Size) throw(THardwareException)
writes the datablock indicated by Data and Size to the specified address (channel) and returns the size it wrote in. Depending on the specs of the module, the argument Address relies on the module itself (in many cases, simply ignored).

int NextDataBlockSize(int Address = -1) throw(THardwareException)
returns the datablock size (or its' upper limit) of the next datablock that will be readout after calling BlockRead(). This value is used for securing buffer for the next calling of BlockRead() and to specify MaxSize. Be aware that the actual readout size will be returned when BlockRead() is called.

= = = = = [Interrupt Processing Related] = = = = =

int EnableInterrupt(int SignalId = 0) throw(THardwareException)
enables interrupts. If the SignalId is not 0, a signal is issued upon interrupt.

int DisableInterrupt(void) throw(THardwareException)
disables interrupts.

int ClearInterrupt(void) throw(THardwareException)
clears interrupts.

= = = = = [Introspection Related] = = = = =

int NumberOfChannels(void) throw(THardwareException)
returns the number of channels a module has.

int AddressBitLength(void)
returns the number of bits required to specify any address (channel) used in Read()/SequencialRead(). If there are 16 channels, this method will return 4. This value is used to determine data format.

int DataBitLength(void)
returns the number of bits of the dataword returned by calling Read()/SequencialRead(). This value is used to determine dataformat.

= = = = = [Others] = = = = =

int ReadRegister(int Address, int& Data) throw(THardwareException)
reads value from the address indicated in argument Address and stores to argument Data.

int WriteRegister(int Address, int Data) throw(THardwareException)
writes the value in the argument Data to the address indicated in argument Address.

bool HasData(int Address = -1) throw(THardwareException)
returns true if the indicated address (channel) has data that can be readout immediately.

bool WaitData(unsigned TimeOut_sec = 1) throw(THardwareException)
waits for data with time passed in the argument as maximum waiting time. If data arrives, returns ture. If not, returns false.

CAMAC Module Driver

The TRoomCamacModule class is the basis of making a CAMAC module driver and is defined in RoomCamacAccess.hh. This class is generated from an abstract class TRoomModule that has a set module interface, posesses pointers from CAMAC controller driver, and has basic controls implemented that allows access to CAMAC. Further more, a default implementation is provided for some module interfaces with similar behavier for most CAMAC modules (of course, overriding for each module driver is allowed).

Some of the TRoomCamacModule class definitions are shown below.

class TRoomCamacModule: public TRoomModule {
  public:
    TRoomCamacModule(const std::string& ModuleType, const std::string& ModelName);
    virtual ~TRoomCamacModule();
    virtual TRoomCamacModule* Clone(void) = 0;
  public:
    // methods that provide access to CAMAC devices //
    int Transact(int Function, int Address, int &Data, int &Q, int &X);
    int Transact(int Function, int Address, int &Data) throw(THardwareException);
    int Transact(int Function, int Address) throw(THardwareException);
    int EnableLam(void) throw(THardwareException);
    int DisableLam(void) throw(THardwareException);
    int ClearLam(void) throw(THardwareException);
    bool WaitLam(int TimeOut_sec) throw(THardwareException);
    bool IsRequestingLam(void);
  public:
    // standard implementations for module driver interfaces //
    virtual int Read(int Address, int& Data) throw(THardwareException);
    virtual int Write(int Address, int Data) throw(THardwareException);
    virtual int ReadRegister(int Address, int& Data) throw(THardwareException);
    virtual int WriteRegister(int Address, int Data) throw(THardwareException);
    virtual int Clear(int Address = -1) throw(THardwareException);
    virtual bool HasData(int Address = -1) throw(THardwareException);
    virtual bool WaitData(unsigned TimeOut_sec = 1) throw(THardwareException);
    virtual int EnableInterrupt(int SignalId = 0) throw(THardwareException);
    virtual int DisableInterrupt(void) throw(THardwareException);
    virtual int ClearInterrupt(void) throw(THardwareException);
    virtual int AddressBitLength(void);
    virtual int DataBitLength(void);
  protected:
    // common constants used throughout CAMAC //
    enum TCamacFunctionTable {
	fnRead = 0,
	fnTestLam = 8,
	fnClear = 9,
	fnClearLam = 10,
	fnWrite = 16,
	fnDisable = 24,
	fnEnable = 26,
	_NumberOfCamacFunctions
    };
    enum TCamacAddressTable {
	adAny = 0,
	_NumberOfCamacAddresses
    };
};
A constructor and the Clone() method are two implementations that is necessary in module drivers. The TRoomCamacModule constructor takes the module type and model number as its string arguments. The Clone() method creates one instance of itself and returns it.

The following is an example of Rinei(REPIC)'s 16ch 12bit cs ADC (RPC-022). As a general rule, classes are named T{module type}_{vendor name}_{model number}, and files are named module-{vendor name}_{model number}.hh/cc.

// module-Rinei_RPC022.hh //

#include "RoomCamacAccess.hh"

class TCamacQADC_Rinei_RPC022: public TRoomCamacModule {
  public:
    TCamacQADC_Rinei_RPC022(void);
    virtual ~TCamacQADC_Rinei_RPC022();
    virtual TRoomCamacModule* Clone(void);
    // implementations of other interfaces //
    // ...
};
// module-Rinei_RPC022.cc //

#include "RoomCamacAccess.hh"
#include "module-Rinei_RPC022.hh"

TCamacQADC_Rinei_RPC022::TCamacQADC_Rinei_RPC022(void)
 : TRoomCamacModule("CamacQADC", "RPC022")
{
}

TCamacQADC_Rinei_RPC022::~TCamacQADC_Rinei_RPC022()
{
}

TRoomCamacModule* TCamacQADC_Rinei_RPC022::Clone(void)
{
    return new TCamacQADC_Rinei_RPC022();
}

// implementations of other interfaces //
// ...
The following methods are methods that TRoomCamacModule provides for each module driver to access CAMAC devices. Module drivers use these methods to implement interfaces such as Read().
int Transact(int Function, int Address, int &Data, int &Q, int &X)
executes a CAMAC cycle with the function and address passed as arguments. If it is a write-in cycle the value passed as argument Data is used as data value, and if it s a readout cycle the data value is set to the value in argument Data. The Q and X responses are returned to the argument Q and X respectively. Return value is the Q response.

int Transact(int Function, int Address, int &Data) throw(THardwareException)
executes a CAMAC cycle with the function and address passed as arguments. If it is a write-in cycle the value passed as argument Data is used as data value, and if it s a readout cycle the data value is set to the value in argument Data. Q response is returned as the return value. If there is no X response, exception THardwareException is thrown.

int Transact(int Function, int Address) throw(THardwareException)
executes a CAMAC cycle without the involvement of data with function and address passed as arguments. Q response is returned as the return value. If there is no X response, exception THardwareException is thrown. This is the simplified version of the above Transact() method.

int EnableLam(void) throw(THardwareException)
sends F26 and enables LAM.

int DisableLam(void) throw(THardwareException)
sends F24 and disables LAM.

int ClearLam(void) throw(THardwareException)
sends F10 and clears LAM.

bool WaitLam(int TimeOut_sec) throw(THardwareException)
waits for a LAM signal with the time passed as argument a maximum waiting time. If there is a LAM signal, returns true. If timed out or terminated with other reasons, returns false.

bool IsRequestingLam(void)
sends F8 and sees if there is a LAM signal.
Interfaces with common module behaviers have TRoomCamacAccess implemented. The following methods are already implemented methods and their default behavier. If these default behaviers do not suit what the user wants, they can be overridden in each module driver.
int Read(int Address, int& Data) throw(THardwareException);
reads data with F0 and returns Q response.

int Write(int Address, int Data) throw(THardwareException);
writes data with F16 and returns Q response.

int ReadRegister(int Address, int& Data) throw(THardwareException);
same as Read().

int WriteRegister(int Address, int Data) throw(THardwareException);
same as Write().

int Clear(int Address = -1) throw(THardwareException);
clears data with F9. If the Address argument is negative, it is set to 0 (in many modules, F9 ignores addresses).

bool HasData(int Address = -1) throw(THardwareException);
calls IsRequestingLam().

bool WaitData(unsigned TimeOut_sec = 1) throw(THardwareException);
calls WaitLam().

int EnableInterrupt(int SignalId = 0) throw(THardwareException);
calls EnableLam().

int DisableInterrupt(void) throw(THardwareException);
calls DisableLam().

int ClearInterrupt(void) throw(THardwareException);
calls ClearLam().

int AddressBitLength(void);
returns 8. This value is the address width of the CAMAC cycle.

int DataBitLength(void);
returns 24. This value is the data word length of the CAMAC cycle.

These default implementations work for almost all CAMAC modules. Therefore, there is no need (in the case of CAMAC) to implement these interfaces individually.

To register the module driver to the system, TRoomCamacModuleCreater defined in RoomDeviceFactory.hh is used. This is a little bit tricky, but create the static Creator object inside the .cc file, and pass the module driver name and the module driver instance as the contructor argument. By doing this, the module driver instance is automatically created when the driver code is loaded and will be registered to the system.

/* module-Rinei_RPC022.cc */

#include "RoomCamacAccess.hh"
#include "RoomDeviceFactory.hh"
#include "module-Rinei_RPC022.hh"

static TRoomCamacModuleCreator Creator(
    "Rinei_RPC022", 
    new TCamacModule_Rinei_RPC022()
);

//...

Place the created module driver's .hh/.cc file in kinoko/src/kernel/lib-common/room and recompile Kinoko. Now this can be used through Kinoko. There is no need to edit Makefile.

VME Module Driver

The TRoomVmeModule class is the basis of making a VME module driver and defined in RoomCamacAccess.hh. This class is generated from an abstract class TRoomModule that has a set module interface, posesses pointers from VME controller driver, and has basic controls implemented that allows access to VME. Further more, a default implementation is provided for some module interfaces with similar behavier for most VME modules (of course, overriding for each module driver is allowed).

Some of the TRoomVmeModule class definitions are shown below.

class TRoomVmeModule: public TRoomModule {
  public:
    TRoomVmeModule(
        const std::string& ModuleType, const std::string& ModelName, 
        TRoomVmeTransferMode TransferMode, size_t MapSize = 0, off_t MapOffset = 0
    );
    virtual ~TRoomVmeModule();
    virtual TRoomVmeModule* Clone(void) = 0;
  public:
    //  methods that provide access to VME devices //
    inline volatile Word* WordPointer(off_t OffsetAddress = 0);
    inline volatile Word& WordAt(off_t OffsetAddress);
    inline volatile DoubleWord* DoubleWordPointer(off_t OffsetAddress = 0);
    inline volatile DoubleWord& DoubleWordAt(off_t OffsetAddress);
    long PioRead(off_t Address, void* Buffer, int Size) throw(THardwareException);
    long PioWrite(off_t Address, const void* Buffer, int Size) throw(THardwareException);
    long DmaRead(off_t Address, void* Buffer, int Size) throw(THardwareException);
    long DmaWrite(off_t Address, const void* Buffer, int Size) throw(THardwareException);
  public:
    // standard implementations for module driver interfaces //
    virtual int BlockRead(int Address, void* Data, int MaxSize) throw(THardwareException)
    virtual int BlockWrite(int Address, const void* Data, int Size) throw(THardwareException)
    virtual int ReadRegister(int Address, int& Data) throw(THardwareException)
    virtual int WriteRegister(int Address, int Data) throw(THardwareException)
    virtual int EnableInterrupt(int SignalId = 0) throw(THardwareException)
    virtual int DisableInterrupt(void) throw(THardwareException)
    virtual int ClearInterrupt(void) throw(THardwareException)
    virtual bool WaitData(unsigned TimeOut_sec) throw(THardwareException)
};
A constructor and the Clone() method are two implementations that is necessary in module drivers. The TRoomVmeModule constructor takes the module type and model number as its string arguments, and also a couple of parameters regarding transfer mode and memory map as the rest of the arguments. The Clone() method creates one instance of itself and returns it.

The following is an example of Renei (REPIC)'s Flash ADC (RPV-160). This module's transfer mode is address 32bit data 16bit and the group of registers that is to be accessed is located in range 0x31000 (size 0x1000) from the offset 0x30000.

// module-Rinei_RPV160.hh //

#include "RoomVmeAccess.hh"

class TVmeFADC_Rinei_RPV160: public TRoomVmeModule {
  public:
    TVmeFADC_Rinei_RPV160(void);
    virtual ~TVmeFADC_Rinei_RPV160();
    virtual TRoomVmeModule* Clone(void);
    // implementations of other interfaces //
    // ...
};
// module-Rinei_RPV160.cc //

#include "RoomVmeAccess.hh"
#include "module-Rinei_RPV160.hh"

static const TVmeTransferMode TransferMode = VmeTransferMode_A32D16;
static const off_t MapOffset = 0x30000;
static const size_t MapSize = 0x1000;

TVmeFADC_Rinei_RPV160::TVmeFADC_Rinei_RPV160(void)
: TRoomVmeModule("VmeFADC", "Rinei_RPV160", TransferMode, MapSize, MapOffset)
{
}

TVmeFADC_Rinei_RPV160::~TVmeFADC_Rinei_RPV160()
{
}

TRoomVmeModule* TVmeFADC_Rinei_RPV160::Clone(void)
{
    return new TVmeFADC_Rinei_RPV160();
}

// implementations of other interfaces //
// ...
The following methods are methods that TRoomCamacModule provides for each module driver to access VME devices. Module drivers use these methods to implement interfaces such as Read().
volatile Word* WordPointer(off_t OffsetAddress = 0)
returns a pointer to the word (16bit) that is located at the offset passed as the argument from the beginning of the memory map area.

volatile Word& WordAt(off_t OffsetAddress)
returns a reference to the word (16bit) that is located at the offset passed as the argument from the beginning of the memory map area.

volatile DoubleWord* DoubleWordPointer(off_t OffsetAddress = 0)
returns a pointer to the double word (32bit) that is located at the offset passed as the argument from the beginning of the memory map area.

volatile DoubleWord& DoubleWordAt(off_t OffsetAddress)
returns a reference to the double word (32bit) that is located at the offset passed as the argument from the beginning of the memory map area.

long PioRead(off_t Address, void* Buffer, int Size) throw(THardwareException)
reads data of the indicated Size from the indicated Address (offset from base address) both passed as arguments by PIO (Programmed I/O), stores it in the area passed as the Buffer argument, and returns the actual readout size.

long PioWrite(off_t Address, const void* Buffer, int Size) throw(THardwareException)
writes the data indicated by arguments Buffer and Size using PIO (Programmed I/O) and returns the actual size written in.

long DmaRead(off_t Address, void* Buffer, int Size) throw(THardwareException)
reads data of the indicated Size from the indicated Address (offset from base address) both passed as arguments using DMA (Direct Memory Access), stores it in the area passed as the Buffer argument, and returns the actual readout size.

long DmaWrite(off_t Address, const void* Buffer, int Size) throw(THardwareException)
writes the data indicated by arguments Buffer and Size using DMA (Direct Memory Access) and returns the actual size written in.

Interfaces with common module behaviers have TRoomVmeAccess implemented. The following methods are already implemented methods and their default behavier. If these default behaviers do not suit what the user wants, they can be overridden in each module driver.
int BlockRead(int Address, void* Data, int MaxSize) throw(THardwareException)
calls DmaRead(Address, Data, MaxSize).

int BlockWrite(int Address, const void* Data, int Size) throw(THardwareException)
calls DmaWrite(Address, Data, Size).

int ReadRegister(int Address, int& Data) throw(THardwareException)
calls PioRead(Address, Data, {word size set by transfer mode}).

int WriteRegister(int Address, int Data) throw(THardwareException)
calls PioWrite(Address, Data, {word size set by transfer mode}).

int EnableInterrupt(int SignalId = 0) throw(THardwareException)
sets the settings for VME controller interrupts and enables them. Normally an interrupt related register of the module needs to be set so a user needs to override this method and add other processes.

int DisableInterrupt(void) throw(THardwareException)
sets interrupts to the VME controller disabled. Normally an interrupt related register of the module needs to be set so a user needs to override this method and add other processes.

int ClearInterrupt(void) throw(THardwareException)
does not do anything.Normally an interrupt related register of the module needs to be set so a user needs to override this method and add other processes.

bool WaitData(unsigned TimeOut_sec) throw(THardwareException)
calls EnableInterrupt() and waits for interrupts using the controller's WaitForInterrupt().

Unlike CAMAC, these methods are not particularly useful with its default behaviers. Be especially aware that there are no Read()/Write() that allow I/O one word at a time. The interrupt related methods do not manipulate the module at all. Also, the Address arguments in BlockRead()/BlockWrite() discribe offsets from the base address as their default, but in most cases this does not hold much meaning for proper behavier. Further more, ReadRegister()/WriteRegister() use PioRead()/PioWrite() but if memory mapping is being used, accessing the mapped region with a pointer is far more efficient.

To register the module driver to the system, TRoomVmeModuleCreater defined in RoomDeviceFactory.hh is used. This is a little bit tricky, but create the static Creator object inside the .cc file, and pass the module driver name and the module driver instance as the contructor argument. By doing this, the module driver instance is automatically created when the driver code is loaded and will be registered to the system.

/* module-Rinei_RPV160.cc */

#include "RoomVmeAccess.hh"
#include "RoomDeviceFactory.hh"
#include "module-Rinei_RPV160.hh"

static TRoomVmeModuleCreator Creator(
    "Rinei_RPV160", 
    new TCamacModule_Rinei_RPV160()
);

//...

Place the created module driver's .hh/.cc file in kinoko/src/kernel/lib-common/room and recompile Kinoko. Now this can be used through Kinoko. There is no need to edit Makefile.

MiscControl Interface

Many functions of many modules can be used through the interfaces set by TRoomModule. However, sometimes interfaces that are not in TRoomModule will be in need. THe MiscControl interface allows special module functions not included in standard the interface of TRoomModule to be used through Kinoko.

In MiscControl, the command name is given as a string, and information such as parameters and data are passed back and forth as integer . The following are methods used in MiscControl.

int MiscControlIdOf(const std::string& CommandName) throw(THardwareException)
returns a unique ID (non-negative integer) corresponding to the command name passed in the argument. If the command name is invalid, a negative value is returned.

int MiscControl(int ControlId, int* ArgumentList = 0, int NumberOfArguments = 0) throw (THardwareException)
executes the control expressed by the ID passed as an argument and returns 1 if successfuly executed and 0 if not. Parameters are passed through "ArgumentList." If values need to be returned, directly insert them in the elements of the ArgumentList. If not, then do NOT make changes to the ArgumentList values.

An example of MiscControl is shown below. Here a CAMAC output register KC3471 (Kaizu-seisakusyo) is used with commands outputLevel and outputPulse.
class TCamacOutputRegister_Kaizu_KC3471: public TRoomCamacModule {
  public:
    virtual int MiscControlIdOf(const std::string& CommandName) throw (THardwareException);
    virtual int MiscControl(int ControlId, int* ArgumentList = 0, int NumberOfArguments = 0) throw (THardwareException);
  public:
    // define Control ID 
    enum TControlId {
	ControlId_OutputLevel,
	ControlId_OutputPulse,
	_NumberOfControls
    };
};
// convert from command name to  Control ID //
int TCamacOutputRegister_Kaizu_KC3471::MiscControlIdOf(const std::string& CommandName) throw (THardwareException)
{
    int ControlId = -1;

    if (CommandName == "outputLevel") {
	ControlId = ControlId_OutputLevel;
    }
    else if (CommandName == "outputPulse") {
	ControlId = ControlId_OutputPulse;
    }
    else {
	throw THardwareException(
	    _ModelName, "unknown command: " + CommandName
	);
    }

    return ControlId;
}

// execute command //
int TCamacOutputRegister_Kaizu_KC3471::MiscControl(int ControlId, int* ArgumentList, int NumberOfArguments) throw (THardwareException)
{
    int Result = 0;

    if (ControlId == ControlId_OutputLevel) {
	if (NumberOfArguments < 1) {
	    throw THardwareException(
		"TCamacOutputRegister_Kaizu_KC3471::OutputLevel(int Data)",
		"too few argument[s]"
	    );
	}

        // intermediary variables are used in order not to change values in ArgumentList
	int Data = ArgumentList[0];
	Transact(fnOutputLevel, adOutput, Data);

	Result = 1;
    }
    else if (ControlId == ControlId_OutputPulse) {
	if (NumberOfArguments < 1) {
	    throw THardwareException(
		"TCamacOutputRegister_Kaizu_KC3471::OutputPulse(int Data)",
		"too few argument[s]"
	    );
	}

	int Data = ArgumentList[0];
	Transact(fnOutputPulse, adOutput, Data);

	Result = 1;
    }

    return Result;
}

Service Requester

The service requester is an interface that transmits module requests such as readout request to the system. Modules that actively request readout (modules that request interrupts, etc) must have this interface implemented. Interfaces regulated by TRoomModule contained ones that process readout requests such as WaitData() and HasData(), but the service requester has a more sophisticated structure that allows processing such as processing readout request (service request) from multiple modules at the same time in the most effective way.

The service requester interface is abstract class TRoomServiceRequester and defined in RoomServiceRequester.hh. This class is also the parent class of the standard module interface describing abstract class TRoomModule.

class TRoomServiceRequester {
  public:
    TRoomServiceRequester(void);
    virtual ~TRoomServiceRequester();

    // Basic Interface //
    virtual bool WaitForServiceRequest(int TimeOut_sec) throw(THardwareException);
    virtual int RequestingServiceNumber(void) throw(THardwareException);

    // Polling Interface //
    virtual void EnableServiceRequest(void) throw(THardwareException);
    virtual void DisableServiceRequest(void) throw(THardwareException);
    virtual void ClearServiceRequest(void) throw(THardwareException);
    virtual bool IsRequestingService(void) throw(THardwareException);

    // Signal on Service Request //
    virtual bool IsSignalOnServiceRequestAvailable(void);
    virtual void EnableSignalOnServiceRequest(int SignalId) throw(THardwareException);
    virtual void DisableSignalOnServiceRequest(void) throw(THardwareException);
};
The explanation of each method is the following.
bool WaitForServiceRequest(int TimeOut_sec) throw(THardwareException)
waits for service request with maximum wait time passed as an argument. If there is a service request, returns true. If not, or if for some reason it is interrupted, returns false.

int RequestingServiceNumber(void) throw(THardwareException)
returns the parameter value if the service request has a parameter.

void EnableServiceRequest(void) throw(THardwareException)
enables service request.

void DisableServiceRequest(void) throw(THardwareException)
disables service request.

void ClearServiceRequest(void) throw(THardwareException)
clears service request.

bool IsRequestingService(void) throw(THardwareException)
returns true if service is requested now and false if not.

bool IsSignalOnServiceRequestAvailable(void)
returns true if there is a function that issues signals on service request and false if not.

void EnableSignalOnServiceRequest(int SignalId) throw(THardwareException)
sets it so that a signal is issued on service request.

void DisableSignalOnServiceRequest(void) throw(THardwareException)
disables the setting that issues a signal on service request.

Some of these service requesters are implemented with common behaviers in TRoomCamacModule and TRoomVmeModule.

Service Requesters Implemented in TRoomCamacModule

bool WaitForServiceRequest(int TimeOut_sec) throw(THardwareException)
calls WaitData(TimeOut_sec). WaitData() calls WaitLam().

int RequestingServiceNumber(void) throw(THardwareException)
returns 0.

void EnableServiceRequest(void) throw(THardwareException)
calls EnableLam().

void DisableServiceRequest(void) throw(THardwareException)
calls DisableLam().

void ClearServiceRequest(void) throw(THardwareException)
calls ClearLam().

bool IsRequestingService(void) throw(THardwareException)
calls HasData().

bool IsSignalOnServiceRequestAvailable(void)
calls control driver's IsSignalOnInterruptAvailable().

void EnableSignalOnServiceRequest(int SignalId) throw(THardwareException)
does not do anyting.

void DisableSignalOnServiceRequest(void) throw(THardwareException)
does not do anything.
Most CAMAC modules will suffice with these default implementations.

Service Requesters Implemented In TRoomVmeModule

bool WaitForServiceRequest(int TimeOut_sec) throw(THardwareException)
calls WaitData(TimeOut_sec). WaitData() calls EnableInterrupt() and waits for an interrupt.

int RequestingServiceNumber(void) throw(THardwareException)
returns 0.

void EnableServiceRequest(void) throw(THardwareException)
does not do anything. Most likely needs to be overridden and implemented.

void DisableServiceRequest(void) throw(THardwareException)
does not do anything. Most likely needs to be overridden and implemented.

void ClearServiceRequest(void) throw(THardwareException)
throws exception THardwareException. Needs to be overridden and implemented.

bool IsRequestingService(void) throw(THardwareException)
cals HasData(). Since HasData() is not implemented in TRoomVmeModule, at least HasData() has to be overridden and implemented.

bool IsSignalOnServiceRequestAvailable(void)
calls controller driver's IsSignalOnInterruptAvailable().

void EnableSignalOnServiceRequest(int SignalId) throw(THardwareException)
calls EnableInterrupt(SignalId).

void DisableSignalOnServiceRequest(void) throw(THardwareException)
calls DisableInterrupt().
Once again, unlike the CAMAC case, the standard implementations described here are not so complete. EnableServiceRequest() / DisableServiceRequest() / ClearServiceRequest() and many more methods need to be implemented compatible to the individual module.

Making Custom Components

When a user needs to implement functional capabilities that are not encompassed with the current Kinoko component system and scripting, such as when an experiment specific online data analysis needs to be scripted in C++ directly or when an interface to a specific external library or data storage system is in need, users will have to create a custom made component. For controller components that just generate specific component events, simply generate a component from the abstract component class in KinokoComponents and add and register necessary methods. On the other hand, to create data stream components that does original analysis of the datastream, it is most convenient to use the analysis framework Kinoko provides so without worrying about implementations of components a user can create them with just implementing the data analysis process parts. Even components that involve mutual interaction of the datastream and component events can be implemented with relatively few steps by inheriting the appropriate class.

Here as examples of simple custom components, we will create the following.

alarm clock
a component that issues an alarm event everytime a certain interval of time passes.

event counter
a component that counts the number of events in the datastream and issues an alarm event when the total reaches a certain number.

dataflow meter
a component that measures the amount of data flow and attaches that amount as a new data, then passes it down the stream.

Example 1) alarm clock

This is a component that issues an alarm event everytime a certain interval of time passes. The interface declaration of this component is the following.
component KinokoAlarmClock {
    emits ring();
    accepts setInterval(int interval_sec);
    accepts start();
    accepts stop();
    accepts quit();
}
The complete source code of this component can be found at kinoko/local/tutorials/Component (KinokoAlarmClock.hh/cc etc).

This component simply acquires the current time, calculates the time interval that passed, and issues an alarm event when the interval exceeds a set value. To implement this, a component event slot that sets a specific time and controls the start/stop of an action needs to be implemented as well as a component event source that issues an alarm event and the calculations needed to calculate the time interval.

The steps that need to be taken to implement this simple component are the following.

  1. inherit the abstract component frame class and create an AlarmClock class
  2. add event slot and event source declarations to the component descriptor
  3. script the process that will be executed upon receiving of the event
  4. script the normal process (component task)
  5. wrap the component object with the component process object and pass the control through main()
From here, we explain each step in order.

1) Defining Custom Component Class

Create file KinokoAlarmClockCom.hh and define class TKinokoAlarmClockCom within it.

 13: class TKinokoAlarmClockCom: public TKinokoSystemComponent {
 14:   public:
 15:     TKinokoAlarmClockCom(void);
 16:     virtual ~TKinokoAlarmClockCom();
 17:     virtual void BuildDescriptor(TKcomComponentDescriptor& Descriptor);
 18:     virtual int ProcessEvent(int EventId, TKcomEvent& Event);
 19:     virtual int DoTransaction(void) throw(TKcomException);
 20:   protected:
 21:     int ProcessSetIntervalEvent(TKcomEvent& Event);
 22:     int ProcessStartEvent(TKcomEvent& Event);
 23:     int ProcessStopEvent(TKcomEvent& Event);
 24:     void EmitRingEvent(void);
 25:   protected:
 26:     enum TEventId {
 27:         EventId_SetInterval = TKinokoSystemComponent::_NumberOfEvents,
 28:         EventId_Start,
 29:         EventId_Stop,
 30:         EventId_Ring,
 31:         _NumberOfEvents
 32:     };
 33:   protected:
 34:     bool _IsRunning;
 35:     long _StartTime, _Interval;
 36: };
Methods on lines 17-19 are declared in the component frame class TKcomComponent and has to be overridden in each component implementation class. Methos on lines 21-24 are supplements used internally. All events are distinguished by unique event IDs. In order to add an original event, an event ID must be given. This is done on lines 26-32 by the enum constant. Member variables on lines 34-35 are used internally.

Now we will create file KinokoAlarmClockCom.cc and implement methods in class TKinokoAlarmClockCom. First methods are the constructor and destructor. The component frame class takes component type names as its argument. This is usually the same name as the component class name. The initialization of member variable is done in the constructor as well.

 12: TKinokoAlarmClockCom::TKinokoAlarmClockCom(void)
 13: : TKinokoSystemComponent("KinokoAlarmClock")
 14: {
 15:     _IsRunning = false;
 16:     _Interval = 0;
 17:     _StartTime = 0;
 18: }
 19: 
 20: TKinokoAlarmClockCom::~TKinokoAlarmClockCom()
 21: {
 22: }

2) Building The Component Descriptor

The component descriptor scripts the component's external interface (component definition). Structure a descriptor object by overriding method BuildDescriptor(). If event slots and properties are written in the descriptor, they will be reflected in the component interface declaration (try comparing it with the interface declaration above).

 24: void TKinokoAlarmClockCom::BuildDescriptor(TKcomComponentDescriptor& Descriptor)
 25: {
 26:     TKinokoSystemComponent::BuildDescriptor(Descriptor);
 27: 
 28:     TKcomEventDeclaration SetIntervalEvent("setInterval");
 29:     SetIntervalEvent.AddArgument(TKcomPropertyDeclaration(
 30:         "interval_sec", TKcomPropertyDeclaration::Type_Int
 31:     ));
 32:     Descriptor.RegisterEventSlot(EventId_SetInterval, SetIntervalEvent);
 33: 
 34:     TKcomEventDeclaration StartEvent("start");
 35:     Descriptor.RegisterEventSlot(EventId_Start, StartEvent);
 36: 
 37:     TKcomEventDeclaration StopEvent("stop");
 38:     Descriptor.RegisterEventSlot(EventId_Stop, StopEvent);
 39: 
 40:     TKcomEventDeclaration RingEvent("ring");
 41:     Descriptor.RegisterEventSource(EventId_Ring, RingEvent);
 42: }
First call the same method in the parent class and take in the interfaces inherited from the parent component. Then create an original event descriptor object and register it in the component descriptor. The event descriptor only holds signiture information of the event, so when registering in the component descriptor, pass the corresponding event ID as well. The descriptor registering method's argument is passed by copying so passing the object to the event object for a moment is not a problem.

3) Implementing Event Processing Functions

When the component receives an event, it will call method ProcessEvent(). The arguments passed are event ID of the event slot and the event object which holds event arguments. Here we will assign it to an appropriate processing function simply by the event ID. If the event is processed successfully, return 1. If not, return 0.

 44: int TKinokoAlarmClockCom::ProcessEvent(int EventId, TKcomEvent& Event)
 45: {
 46:     int Result = 0;
 47: 
 48:     switch (EventId) {
 49:       case EventId_SetInterval:
 50:         Result = ProcessSetIntervalEvent(Event);
 51:         break;
 52: 
 53:       case EventId_Start:
 54:         Result = ProcessStartEvent(Event);
 55:         break;
 56: 
 57:       case EventId_Stop:
 58:         Result = ProcessStopEvent(Event);
 59:         break;
 60: 
 61:       default:
 62:         Result = TKinokoSystemComponent::ProcessEvent(EventId, Event);
 63:     }
 64: 
 65:     return Result;
 66: }
Each event processing functions are implemented as the following.
 81: int TKinokoAlarmClockCom::ProcessSetIntervalEvent(TKcomEvent& Event)
 82: {
 83:     int Result = 0;
 84:     if (istrstream(Event.ArgumentList()[0].c_str()) >> _Interval) {
 85:         Result = 1;
 86:     }
 87: 
 88:     return Result;
 89: }
 90: 
 91: int TKinokoAlarmClockCom::ProcessStartEvent(TKcomEvent& Event)
 92: {
 93:     _StartTime = TMushDateTime().AsLong();
 94:     _IsRunning = true;
 95: 
 96:     return 1;
 97: }
 98: 
 99: int TKinokoAlarmClockCom::ProcessStopEvent(TKcomEvent& Event)
100: {
101:     _IsRunning = false;
102: 
103:     return 1;
104: }
In ProcessSetIntervalEvent() the first argument is read and set as member variable _Interval. As shown in this example, the TKcomEvent class returns a list of event arguments by ArgumentList() method. The type ArgumentList() returns is a vector of strings (vector<string>&). When event start is received, current time is obtained and active flag (_IsRunning) is set.

4) Implementing Component Tasks

The component calls method DoTransaction() repeatedly while it is running. Tasks executed normally by the component are scripted here (component tasks). In this example, while in active mode, current time is obtained, elapsed time is calculated, and alarm events are issued if this elapsed time exceeds the pre-set time interval.

 68: int TKinokoAlarmClockCom::DoTransaction(void) throw(TKcomException)
 69: {
 70:     if (_IsRunning && (_Interval > 0)) {
 71:         int CurrentTime = TMushDateTime().AsLong();
 72:         if ((CurrentTime - _StartTime) > _Interval) {
 73:             _StartTime = CurrentTime;
 74:             EmitRingEvent();
 75:         }
 76:     }
 77: 
 78:     return TKinokoSystemComponent::DoTransaction();
 79: }

If there is nothing to do in the component task or if the component task can be completed in a very short period of time and has no need of being called repeatedly, call the same method in the parent class. This method in the component framework releases the CPU for a certain amount of time and sleeps the process (order in ms). If this is not done, CPU usage percentage is increased largely for unneeded processes (If there aren't any similar processes, it will take up close to 100%).

Method EmitRingEvent() that issues alarm events are implemented as the following.

106: void TKinokoAlarmClockCom::EmitRingEvent(void)
107: {
108:     TKcomEvent Event;
109:     EmitEventOneWay(EventId_Ring, Event);
110: }
This event does not take event arguments so it throws the event object created as it is (only EventId is used). If it is an event that takes arguments, then an obtain the argument list with the event object's ArgumentList() method and set the values there.

The issuing of an event is done by EmitEvent() method or EmitEventOneWay() method. The EmitEvent() method transmits the event and stops run untill it is processed. EmitEventOneWay() starts processing right away after throwing the event without waiting for a response. If there is no need for synchronization between components, use EmitEventOneWay().

5) Generating Component Processes

Finally, an instance of the custom component class is created, registered in component process object, and connected to the function main(). Create file KinokoAlarmClock-kcom.cc and write in the following. Here the file name has to be "component type name-kcom.cc".

  6: #include <iostream.h>
  7: #include "KcomProcess.hh"
  8: #include "KinokoAlarmClockCom.hh"
  9: 
 10: 
 11: int main(int argc, char** argv)
 12: {
 13:     TMushArgumentList ArgumentList(argc, argv);
 14:     
 15:     TKcomComponent* Component = new TKinokoAlarmClockCom();
 16:     TKcomProcess* ComProcess = new TKcomProcess(Component);
 17:     
 18:     try {
 19:         ComProcess->Start(ArgumentList);
 20:     }
 21:     catch (TKcomException &e) {
 22:         cerr << "ERROR: " << argv[0] << ": " << e << endl;
 23:     }
 24: 
 25:     delete ComProcess;
 26:     delete Component;
 27: 
 28:     return 0;
 29: }
Here an instance to the component class generated is created, the instnace of the compnent process class is wrapped, and compnent process object is allowd to start executing. This format is the same with all component implementation.

Example 2) Event Counter

This is a component that counts the number of events in the datastream and issues an alarm event when the total reaches a certain number. The compnent's interface declaration is the following (the part that is common to stream sink compnent is omitted).
component KinokoEventCounter {
    // parts handed over from KinokoStreamSinkComponent omitted
    emits alarm();
    accepts setNumberOfEvents(long number_of_events);
}
The complete source code of this component can be found at kinoko/local/tutorials/Component (KinokoEventCounter.hh/cc etc).

This component is a component that issues an event using the number of events that comes down the datastream rather than using time measurements like in Example 1). It is almost the same as the alarm clock in Example 1 except for the fact that it accesses the datastream inside the compnent task. Here compnents, methods, and objects that were not involved in the first example are explained explicitly.

Components that access the datastream inherit and implement one of the following classes depending on the connection format to the stream.

TKinokoStreamSourceComponent
component that becomes the source of data to the datastream

TKinokoStreamPipeComponent
compnent that receives data from the stream, processes it if required, and passes it down the datastream again

TKinokoStreamSinkComponent
component that simple receives data in the datastream

In these classes, the following methods are declared as virtual functions in order to override them in the generated class and to implement them.

void Construct(void)
called when the component shifts from StreamReady state to SystemReady state. Construction and sending out internal objects and data descriptors are done here.

void Destruct(void)
called when the component shifts from SystemReady state to StreamReady state. Destruction and cleaning up internal objects are done here. Sometimes Construct() is called again after this method is called so a user must make sure there are no contradictions.

int ProcessData(void)
called repeatedly from the data processing loop. If necessary data is received from the stream (other than Source), processed, and sent out (other than Sink).

void OnStart(void)
called when the component shifts from SystemReady state to Running state. If there is no need to then this method does not have to be implemented.

void OnStop(void)
called when the component shifts from Running state to SystemReady state. If there is no need to then this method does not have to be implemented.
Further more, there are the following objects that the framework class provies so they can be used in the generated class.
TKinokoInputStream* _InputDataStream;
datastream object on the side of receiving data

TKinokoOutputStream* _OutputDataStream;
datastream object on the side of sending out data

TKinokoStreamCommandProcessor* _StreamCommandProcessor;
object that processes the command received from the datastream and tells the component

TKinokoLogger* _Logger;
object imported from logger component. Used to record logs.

Now we will actually use these to create an event counter component. The event counter simple reads the data from the stream and does not write anything out so we inherit the stream sink component and implement it.

 15: class TKinokoEventCounterCom: public TKinokoStreamSinkComponent {
 16:   public:
 17:     TKinokoEventCounterCom(void);
 18:     virtual ~TKinokoEventCounterCom();
 19:     virtual void BuildDescriptor(TKcomComponentDescriptor& Descriptor);
 20:     virtual int ProcessEvent(int EventId, TKcomEvent& Event);
 21:   protected:
 22:     virtual int ProcessSetNumberOfEventsEvent(TKcomEvent& Event);
 23:     virtual void EmitAlarmEvent(void);
 24:     virtual void Construct(void) throw(TKinokoException);
 25:     virtual void Destruct(void) throw(TKinokoException);
 26:     virtual int ProcessData(void)throw(TKinokoException);
 27:   protected:
 28:     enum TEventId {
 29:         EventId_SetNumberOfEvents = TKinokoStreamSinkComponent::_NumberOfEvents,
 30:         EventId_Alarm,
 31:         _NumberOfEvents
 32:     };
 33:   protected:
 34:     int _EventCount;
 35:     int _MaxNumberOfEvents;
 36:     TKinokoDataStreamScanner* _StreamScanner;
 37: };
The constructor and destructor on lines 17-18 and methods on lines 24-26 are explained here. For other parts, please refer to the explanations in Example 1) alarm clock.

First the constructor and the destructor.

 10: TKinokoEventCounterCom::TKinokoEventCounterCom(void)
 11: : TKinokoStreamSinkComponent("KinokoEventCounter")
 12: {
 13:     _MaxNumberOfEvents = 0;
 14:     _StreamScanner = new TKinokoDataStreamScanner();
 15: }
 16: 
 17: TKinokoEventCounterCom::~TKinokoEventCounterCom()
 18: {
 19:     delete _StreamScanner;
 20: }
The parent class constructor takes arguments as component type names. StreamScanner is an object that pulls out information such as packet type from the data package (void pointer) received from the datastream.

Construct() method normally constructs internal objects and initializes but here it simply resets the event counter. Destruct() does not need to do anything.

 79: void TKinokoEventCounterCom::Construct(void) throw(TKinokoException)
 80: {
 81:     _EventCount = 0;
 82: }
 83: 
 84: void TKinokoEventCounterCom::Destruct(void) throw(TKinokoException)
 85: {
 86: }

ProcessData() method reads data from the datastream and processes. First it reads the data from the stream and corrects the byte orders (if necessary). _InputDataStream's NextEntry() method waits for the data to arrive in the stream and returns the data size, but returns 0 if the data waiting is terminated for some reason or if the datastream reaches the end.

 88: int TKinokoEventCounterCom::ProcessData(void)throw(TKinokoException)
 89: {
 90:     static const int Trailer_Event = TKinokoDataStreamScanner::Trailer_Event;
 91: 
 92:     void* Data;
 93:     int DataSize = _InputDataStream->NextEntry(Data);
 94:     if (DataSize <= 0) {
 95:         return 0;
 96:     }
 97:     _StreamScanner->CorrectByteOrder(Data, DataSize);
 98:

Next we set how to process special packets (data descriptor or commands) when they are received. In this example, we do not need a detailed explanation (meaning, structure, etc) of the data we receive so descripter packets are simply discarded. For command packets, we hand it over to the _StreamCommandProcessor object the parent class provides for processing.

 99:     if (_StreamScanner->IsDataDescriptorPacket(Data)) {
100:         ;
101:     }
102:     else if (_StreamScanner->IsCommandPacket(Data)) {
103:         int CommandValue = _StreamScanner->CommandValueOf(Data);
104:         _StreamCommandProcessor->ProcessCommand(CommandValue);
105:     }

If an event trailer packet is received, the event counter is incremented and checked if it has reached the event number set as the maximum. If it has reached that number, it is recorded in the log and an alarm event is issued.

106:     else if (
107:         _StreamScanner->IsTrailerPacket(Data) &&
108:         (_StreamScanner->TrailerValueOf(Data) == Trailer_Event)
109:     ){
110:         _EventCount++;
111: 
112:         if (_EventCount % _MaxNumberOfEvents == 0) {
113:             _Logger->WriteNotice(
114:                 ComponentName(), "event counts reached the maximum value"
115:             );
116:             EmitAlarmEvent();
117:         }
118:     }     
119: 

Finally, data packets held in the stream are released.

120:     _InputDataStream->Flush(Data);
121: 
122:     return 1;
123: }

The component is now complete. As in example 1, this component can be used in the script after wrapping it with the component process object.

Example 3) Dataflow Meter

This is a component that measures the amount of dataflow and attaches that amount as a new piece of data, then passes it down the stream. The data descriptor that this component generates is as the following.
datasource "flow_meter"<1>
{
    section "data_flow"<1>: tagged {
        field "event_rate": int-32bit;
        field "data_flow": int-32bit;
    }
}
The complete source code of this component can be found at kinoko/local/tutorials/Component (KinokoDataFlowMeter.hh/cc etc).

This component simply reads data from the datastream, performs simple calculations to it, and writes it out in the datastream and does not issue or receive any original component events. Component like this that does not handle component events directly can be created easily by using the already implemented component class KinokoDataProcessorCom and registering its own DataProcessor object to it.

The component created here is a pipe type component so the base analysis framework class used is the TKinokoDataProcessor class. This framework class and its parent classes TKinokoDataSender and TKinokoDataReceiver have the following methods declared as virtual functions to be overriden in the generated class and their content implemented.

void BuildDataSource(TKinokoDataSource* DataSource)
called on system construction. DataSource objects that correspond to data that is outputted is constructed here. This method has to be implemented.

void OnConstruct(void)
called on system construction. Necessary initializations such as constructing internal objects are done here. Implementation not necessary if there is no need.

void OnDestruct(void)
called on system destruction. Necessary termination processes such as destructing internal objects are done here. Implementation not necessary if there is no need.

void OnReceivePacket(void* DataPacket, long PacketSize)
called when a packet is received from the stream with the packet as its argument. Called for all packets regardless of the packet type received. Does not have to be implemented if there is no need.

void OnReceiveDataPacket(void* DataPacket, long PacketSize)
called when a data packet is received from the stream with the data packet as its argument. Implementation not necessary there is no need to.

void OnReceiveEventTrailerPacket(void* DataPacket, long PacketSize)
called when an event trailer packet is received from the stream with the event trailer packet as its argument. Implementation not necessary there is no need to.
Be careful that OnReceivePacket() is called even if OnReceiveDataPacket() or other equivalent methods are implemented.

Also the following methods that are used in the generated class are provided by these framework classes.

void SendDataDestructorPacket(void)
generates a data descriptor packet and sends it out to the output stream.

void SendRunBeginPacket(void)
generates a RunBegin packet and sends it out to the output stream.

void SendRunEndPacket(void)
generates a RunEnd packet and sends it out to the output stream.

void SendEventTrailerPacket(void)
generates an EventTrailer packet and sends it out to the output stream.

void SendPacket(void* Packet, long PacketSize)
sends the packet passed as the argument to the output stream.

A couple more objects are provided that can be used in the generated class.

TKinokoInputStream* _InputStream
a datastream object that is on the side of receiving data.

TKinokoDataStreamScanner* _InputStreamScanner
an object that extracts information out from the data packet received.

TKinokoDataDescriptor* _InputDataDescriptor
a data descriptor object of data that comes from the input datastream.

TKinokoOutputStream* _OutputStream
a datastream object that is on the side of sending out data.

TKinokoDataStreamFormatter* _OutputStreamFormatter
an objects that writes in information to the packet being sent out.

TKinokoDataDescriptor* _InputDataDescriptor
a data descriptor object of data that will be sent out to the output datastream.

TKinokoDataSource* _OutputDataSource
a datasource defining object of data that will be sent out to the output datastream.
Using these methods and objects, we will now implement the DataFlowMeter class. The following is the class definition of the DataFlowMeter class (write in file KinokoDataFlowMeter.hh).
 14: class TKinokoDataFlowMeter: public TKinokoDataProcessor {
 15:   public:
 16:     TKinokoDataFlowMeter(void);
 17:     virtual ~TKinokoDataFlowMeter();
 18:   protected:
 19:     virtual void BuildDataSource(TKinokoDataSource* DataSource);
 20:     virtual void OnConstruct(void) throw(TKinokoException);
 21:     virtual void OnReceivePacket(void* Packet, long PacketSize) throw(TKinokoException);
 22:     virtual void OnReceiveDataPacket(void* Packet, long PacketSize) throw(TKinokoException);
 23:     virtual void OnReceiveEventTrailerPacket(void* Packet, long PacketSize) throw(TKinokoException);
 24:   protected:
 25:     virtual void SendReportPacket(int EventRate, int DataFlow) throw(TKinokoException);
 26:   protected:
 27:     TKinokoTaggedDataSection* _DataSection;
 28:     TKinokoTaggedDataSectionFormatter* _Formatter;
 29:     long _EventCount, _DataAmount;
 30:     int _EventRateIndex, _DataFlowIndex;
 31:     int _LastReportTime, _ReportInterval;
 32: };
The SendReportPacket() method on line 25 is a method that is used internally to send dataflow data generated in this component to the stream. _DataSection on line 27 is an object that has the structure of the dataflow data section to be sent out scripted, and _Formatter on line 28 is an utility object for writing in data into the data stream packet.

Create file KinokoDataFlowMeter.cc and implement the following methods inside.

The constructor and destructor. There is nothing that needs to be done here in this example.

 13: TKinokoDataFlowMeter::TKinokoDataFlowMeter(void)
 14: {
 15: }
 16: 
 17: TKinokoDataFlowMeter::~TKinokoDataFlowMeter()
 18: {
 19: }

BuildDataSource()

 21: void TKinokoDataFlowMeter::BuildDataSource(TKinokoDataSource* DataSource)
 22: {
 23:     _DataSection = new TKinokoTaggedDataSection(DataSource, "data_flow");
 24: 
 25:     int DataWidth = 32;
 26:     _EventRateIndex = _DataSection->AddField("event_rate", DataWidth);
 27:     _DataFlowIndex = _DataSection->AddField("data_flow", DataWidth);
 28: 
 29:     DataSource->AddDataSection(_DataSection);
 30:     _Formatter = _DataSection->Formatter();
 31: }
Because the section type of the data generated here is Tagged, object TKinokoTaggedDataSection is created. The data source object's pointer and section name has to be passed to the constructor as in the examle.

Next, necessary fields are added to the generated section. TKinokoTaggedDataSection's AddField() method takes field name and data width as arguments, adds fields to the data section, and returns the index of the field. This index necessary in order to access the field later so it is saved in the member variable.

After building the data section object, tegister it to the data source object and the building of data sources is done. Later when recording the data values to the data packet of the section, that section's formatter oject is necessary, so obtain the formatter object from the section and save it as a member variable.

Next we will deal with methods OnConstruct() and OnDestruct() that are called when an internal object is constructed or destructed. In this example there are no internal objects that need to be constructed, so inside OnConstruct(), clearing the counter and obtaining the current time are the only actions recorded. Also, there is nothing that needs to be processed OnDestruct() so we will not override this method.

 33: void TKinokoDataFlowMeter::OnConstruct(void) throw(TKinokoException)
 34: {
 35:     _EventCount = 0;
 36:     _DataAmount = 0;
 37: 
 38:     _LastReportTime = TMushDateTime().AsLong();
 39:     _ReportInterval = 10;
 40: }

Now we finally come to processes upon receiving data packets. Since OnReceiveDataPacket() is called everytime a data packet is received and OnReceiveEventTrailerPacket() is called everytime a data trailer packet is received, we will override these two methods and implement them.

Since this component only records the data sizes and event counts, the only process that needs to happen upon receiving data packets is recording the size. When event trailers are received, the number of events recorded is incremented.

In OnReceiveEventTrailer(), time elapsed after the last dataflow data was passed on to the stream is calculated, and if this time interval exceeds the time interval setting, event trailers are calculated, a data packet is passed to the stream and the event counter is cleared.

 47: void TKinokoDataFlowMeter::OnReceiveDataPacket(void* Packet, long PacketSize) throw(TKinokoException)
 48: {
 49:     _DataAmount += PacketSize;
 50: }
 51: 
 52: void TKinokoDataFlowMeter::OnReceiveEventTrailerPacket(void* Packet, long PacketSize) throw(TKinokoException)
 53: {
 54:     _EventCount++;
 55: 
 56:     long PastTime = TMushDateTime().AsLong() - _LastReportTime;
 57:     if (PastTime > _ReportInterval) {
 58:         int EventRate = _EventCount / PastTime;
 59:         int DataFlow = _DataAmount / PastTime;
 60:         SendReportPacket(EventRate, DataFlow);
 61: 
 62:         _EventCount -= EventRate * PastTime;
 63:         _DataAmount -= DataFlow * PastTime;
 64:         _LastReportTime += PastTime;
 65:     }
 66: }

Sending out data packets is done through method SendReportPacket(). The implementation of this method is shown below.

 68: void TKinokoDataFlowMeter::SendReportPacket(int EventRate, int DataFlow) throw(TKinokoException)
 69: {
 70:     int DataSize = _Formatter->DataSize();
 71:     int PacketSize = _Formatter->PacketSizeFor(DataSize);
 72: 
 73:     void* Buffer;
 74:     do {
 75:         _OutputStream->NextEntry(Buffer, PacketSize);
 76:     } while (Buffer == 0);
 77: 
 78:     _Formatter->WriteHeaderTo(Buffer, DataSize);
 79:     _Formatter->WriteTo(Buffer, _EventRateIndex, EventRate);
 80:     _Formatter->WriteTo(Buffer, _DataFlowIndex, DataFlow);
 81: 
 82:     _OutputStream->Flush(Buffer, PacketSize);
 83: 
 84:     SendEventTrailerPacket();
 85: }
First memory size necessary for the packet is calculated (lines 70-71). DataSize is the size of the storage part that stores actual data NOT including the packet header. The Formatter's PacketSizeFor() method takes this data size as its argument and calculates the packet size. In the case of this example, Tagged Section has a fixed data size, but take notice that all other section types have different data size every time.

After calculating packet size, memory size enough for this packet is secured in the stream (lines 73-76). _OutputStream returns 0 when writing is temporary not possible (when the output is buffer and there is no space). In this example, it is set so that when this does happen, the program simply asks for memory again.

Then the formatter object is used to write in values to each field in the packet (lines 78-80). After that, method Flush() of the _OutputStream is used to send out packets to the stream and release buffer memory (line 82. method Flush() does both of these).

After sending every data packet out, call SendEventTrailer(). Do not forget to send the event trailer packet after that, because at the low end of the stream the end of an event is judged with the trailer packet.

The section type Tagged is the only exception where the data size and packet size are the same every single time, so a fixed memory can be secured and reused like the following.

    //// write the following in OnConstruct() ////
    if (_Buffer == 0) {
        int DataSize = _Formatter->DataSize();
        int PacketSize = _Formatter->PacketSizeFor(DataSize);
        _Buffer = new char[PacketSize];
    }

- - - - - - - - - - - - - -separate here- - - - - - - - - - - - - -
    //// the following corresponds to the
    equivalent of lines 70-83 in SendReportPacket() ////

    _Formatter->WriteHeaderTo(Buffer, DataSize);
    _Formatter->WriteTo(Buffer, _EventRateIndex, EventRate);
    _Formatter->WriteTo(Buffer, _DataFlowIndex, DataFlow);

    SendPacket(Buffer, PacketSize);

- - - - - - - - - - - - - -separate here- - - - - - - - - - - - - -
    //// write the following in OnDestruct() ////

    delete[] _Buffer;
    _Buffer = 0;

With these codes, a component (or its core part)that receiveds data from the input data stream, calculates dataflow, and sends it to the output stream is completed. However, this component absorbes all the data that it received so it is actually not a very realistic component (only the dataflow packet gets passed to the lower ends of the stream). Most likely components in the lower end of the stream would want all data.

So now we will upgrade this component so that all the data will continue down the stream and also the generated dataflow information will too. Actually, this is not very hard. Override the method OnReceivePacket() that always gets called after receiving a packet so that packets get sent again to the stream.

 42: void TKinokoDataFlowMeter::OnReceivePacket(void* Packet, long PacketSize) throw(TKinokoException)
 43: {
 44:     SendPacket(Packet, PacketSize);
 45: }

Finally, an instance of the class generated by function main() is created and wrapped by component object for the analysiss framework, and then that is wrapped by a component process object.

 10: #include "KinokoDataProcessorCom.hh" 
 11: #include "KinokoDataFlowMeter.hh"
 12: 
 13: int main(int argc, char** argv)
 14: {
 15:     TMushArgumentList ArgumentList(argc, argv);
 16:     
 17:     TKinokoDataProcessor* MyDataProcessor = new TKinokoDataFlowMeter();
 18:     TKcomComponent* Component = new TKinokoDataProcessorCom(MyDataProcessor);
 19:     TKcomProcess* ComProcess = new TKcomProcess(Component);    
 20: 
 21:     try {
 22:         ComProcess->Start(ArgumentList);
 23:     }
 24:     catch (TKcomException &e) {
 25:         cerr << "ERROR: " << argv[0] << ": " << e << endl;
 26:     }
 27: 
 28:     delete ComProcess;
 29:     delete Component;
 30:     delete MyDataProcessor;
 31: 
 32:     return 0;
 33: }

Adding New Database Interfaces

Extending the Script

Making New Shells


Edited by: Enomoto Sanshiro