\chapter{Writing new SICS Commands} If you wish to write a new command or add a completely new piece of hardware to SICS, this is the chapter to study. There are two ways to implement new commands into SICS: through the internal Tcl scripting language and in ANSI--C. This section describes command writing in ANSI--C. You should consider using Tcl when: \begin{itemize} \item The new command is very instrument specific. Rather extend a SICS command in order to support your script. \item The new command is a local syntax fudge. \item The hardware device is an auxiliary, such a He level meter etc. \end{itemize} On the other hand there are indications when to write in ANSI--C: \begin{itemize} \item Complex calculations need to be carried out. \item Large amounts of data need to be handled. \item The code requires extensive bit twiddling. \item A new general SICS facility is provided. \end{itemize} \section{Mapping Object Oriented Concepts into ANSI--C} SICS is in principle an object oriented system. However, it is implemented in ANSI--C. Therefore object oriented concepts must be mapped into C. The three object oriented concepts which need to be mapped are: \begin{itemize} \item Data Encapsulation. \item Polymorphism. \item Inheritance. \end{itemize} Of these, data encapsulation is by far the most important concept. Objects in computer science can be understood as little boxes which contain some data describing their state and which understand messages sent by other objects. If such a message comes in, the object performs some action, perhaps changes its internal state or sends new messages to other objects. It is understood that changes to the internal data of the object can be achieved only through messages to the object and not by directly manipulating variables from the outside. In ANSI--C an object maps to a structure holding the objects data and the messages map to functions which act upon the data structure. In order to do this, the functions must take a pointer to the objects data structure as first parameter. In order to prevent messing with an objects data structure, only a pointer to a structure is declared in the header file. The actual definition of the data structure happens only in the implementation file. All functions belonging to an object are defined in that implementation file and have full access to the data structure. Users of the object see only the header file and thus only a pointer to the objects data structure which prevents them from messing with the objects data directly. In order to illustrate the concepts lets look at a primitive integer object defined in such a way. \begin{verbatim} /*----------------------------------------------------------------------- ExampleInt.h ------------------------------------------------------------------------*/ typedef struct __ExampleInt *pExampleInt; int GetInt(pExampleInt self); void SetInt(pExampleInt self, int iNew); /*------------------- EOF ExampleInt.h---------------------------------*/ /*---------------------------------------------------------------------- ExampleInt.c, Implementation file -----------------------------------------------------------------------*/ typedef struct __ExampleInt { int iExample; } ExampleInt; /*--------------------------------------------------------------------*/ int GetInt(pExampleInt self) { return self->iExample; } /*---------------------------------------------------------------------*/ void SetInt(pExampleInt self, int iNew) { self->iExample = iNew; } \end{verbatim} Using this scheme all code changing the internal state of an object lives in one file. Changes to the objects data structure affect only the implementation file and no other files. This scheme is used for almost all SICS objects. A few system objects and older SICS objects define their data structures in header files. This is either a relic or had to be done for performance reasons. The next concept is polymorphism. This describes the situation when a group of objects respond to the same message but doing different things. For instance a whole set of objects would implement a write functionality which writes the objects state to a file. Higher level would then not need to know of which type the actual object is, it just can send the write message and the rest is taken care of by the object. This concept is used for all hardware drivers in SICS. Mapping this to C requires to expose the objects data structure and let the data structure include a pointer to that polymorphic function. As an example, the ExampleInteger with a write function: \begin{verbatim} /*----------------------------------------------------------------------- ExampleInt.h ------------------------------------------------------------------------*/ typedef struct __ExampleInt{ int iExample; void (*write)(struct __ExampleInt *self, FILE *fd); } *pExampleInt, ExampleInt; pExampleInt MakeInt(int iNew); int GetInt(pExampleInt self); void SetInt(pExampleInt self, int iNew); /*------------------- EOF ExampleInt.h---------------------------------*/ /*---------------------------------------------------------------------- ExampleInt.c, Implementation file -----------------------------------------------------------------------*/ static void ExampleWrite(struct _-ExampleInt *self, FILE *fd) { fprintf(fd,"INT = %d",self->iExample); } /*---------------------------------------------------------------------*/ pExampleInt MakeInt(int iNew) { pExampleInt pNew = NULL; pNew = (pExampleInt)malloc(sizeof(ExampleInt)); pNew->iExample = iNew; pNew->write = ExampleWrite; return pNew; } . . . \end{verbatim} This can then be called: \begin{verbatim} void SomeFunc() { pExampleInt pTest; pTest = MakeInt(25); . . . pTest->write(pTest,fd); } \end{verbatim} This example also illustrates the concept of a special function which creates a new object of the appropriate type and initializes its data structure properly. The last concept to discuss is inheritance. Inheritance can be used when an object is a derivative of another object. For instance a truck is a derivative of a motor car. Much of the behavior of a motor car will be the same as for a truck. In order to prevent rewriting of code, the truck should use the same data structures and code as the motor car. And add or modify only what is special. Inheritance is not much used in SICS. It can be implemented by overlaying data structures. This means the derived classes data structure has the same fields in the same order as the parent class and adds its specials at the end. For example: \begin{verbatim} typedef struct __MotorCar { int iWheels; float fSpeed; } *pMotorCar, MotorCar; typedef struct __Truck { int iWheels; float fSpeed; /* same as MotorCar */ double dPayLoad; /* special for Truck */ } *pTruck, Truck; \end{verbatim} Thus functions defined for motor car can operate on trucks as well. For more details study the relationship between the ITC4 controller and general environment controllers. This is the only place where SICS currently uses inheritance. \section{Command Writing Basics} \subsubsection{The Object Wrapper Function} The first thing needed in order to implement a new command in SICS is the object wrapper function. This function has the following signature: \begin{verbatim} int ObjectWrapper(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]); \end{verbatim} The parameters are: \begin{description} \item[pCon] A pointer to the connection object representing the client invoking this command. \item[pSics] A pointer to the SICS interpreter. \item[pData] A pointer to a user data structure for this object. Can be NULL if no such thing exists. \item[argc] Number of arguments in argv to this function. \item[argv] Arguments to this function. The argc, argv scheme is the same as for a C--language main function. The first argument is always the name of the object. \end{description} This object wrapper function must evaluate its arguments, do what it is meant to do and write any results to the client connection. In the case of an error this function must return 0, in case of success 1. \subsubsection{The Object Data Structure} Only the most primitive objects or commands get away without an own data structure. Usually a data structure will be needed by SICS objects in order to keep configuration parameters etc. A pointer to such a datastructure is passed into the object wrapper function as the pointer pData. This object data structure has to fulfill some conditions in order to live happily within SICS. And actually, not only the datastructure is needed but also a function which is able to release any memory allocated by the datastructure. SICS needs this function in order to clean things up properly. A valid SICS object structure has to look like this: \begin{verbatim} typedef struct __MyObject { pObjectDescriptor pDes; int iMyExtremlyImportantInt; . . . } MyObject; \end{verbatim} Please note that the first item in the data structure MUST be a pointer to an SICS object descriptor. Add your own stuff below that. If you do not adhere to this requirement, SICS will dump core on you rather sooner than later. SICS needs this object descriptor for its own internal purposes. The object descriptor is again a data structure with this signature: \begin{verbatim} typedef struct { char *name; int (*SaveStatus)(void *self, char *name,FILE *fd); void *(*GetInterface)(void *self, int iInterfaceID); } ObjectDescriptor, *pObjectDescriptor; \end{verbatim} \begin{description} \item[name] This is a type identifier for the object. SICS uses this identifier for run time type identification (RTTI). For example this field says Motor for motors, Counter for counters etc. \item[SaveStatus] is the function called by the SICS status backup mechanism. The default implementation of this function does nothing. But if your new object has to store configuration commands into the status file you should create an own function with the same signature and assign this function to the object descriptors SaveStatus. A suitable function will print the necessary commands to achieve the same configuration as the current state into the file represented by fd. \item[GetInterface] SICS objects can have various capabilities: a motor can be driven or scanned, you may count on a counter etc. Such capabilities are expressed as interfaces(see \ref{interface}) in SICS. There is an integer ID for each of those interfaces. GetInterface now returns a suitable interface in return for a given interface ID or NULL if the object does not implement the interface. The default version of this function retuns NULL always. If your object implements an interface, this function has to be overloaded to return this interface on demand. \end{description} A default object descriptor can be created with: \begin{verbatim} pObjectDescriptor CreateDescriptor(type); \end{verbatim} with type being the object type identifier. A descriptor can be deleted with: \begin{verbatim} DeleteDescriptor(pDes); \end{verbatim} As already mentioned, a function to release any memory allocated for the object data structure is also needed. Its signature is simple: \begin{verbatim} void KillObject(void *pdata); \end{verbatim} with pdata being the object to delete. \subsection{Installing the new Command into the SICS Interpreter} A new command can be installed into SICS with the function: \begin{verbatim} int AddCommand(SicsInterp *pSics, char *name, ObjectFunc ofunc, KillFunc killo, void *objectData); \end{verbatim} with pSics being the interpreter into which to install the command, name the name of the command, ofunc its wrapper function, killo the object data structure deletion function and objectData being a pointer to the object data structure. If no data structure is defined for the command, killo and objectData must be NULL. Now the question arise where AddCommand has to be placed. Various cases have to be distinguished: The first case is that the new command is relevant to SICS users in all corners of the world. Then the new command should be added to the function InitIniCommand in file ofac.c. The second case is that the command is specific to a certain instrument or a special hardware or implements a local fashion of dealing with things. Then the new command should be installed from the function AddSiteCommand in the site data structure (see \ref{site}). Another scheme becomes valid when multiple instances of the object need to be created. \label{factory} For instance the object represents a motor of which you may have many. Then it is useful to create a factory command. This is a special SICS command which creates the desired objects and installs them in the interpreter. This factory command then has to be installed according to the logic given above. Usually such objects are installed in the SICS initialization file. After processing this initialization file the factory command is not useful any longer. Then such factory commands should be removed from the interpreter with: \begin{verbatim} RemoveCommand(SicsInterp *pSics, char *name); \end{verbatim} This has to be placed into the sites member function RemoveSiteCommand or into KillIniCommand in ofac.c, depending where you installed the command in the first place. Be careful with commands for deleting objects though. SICS objects may be aliased, used by other objects or connected through the callback interface (see \ref{inter}). SICS does not implement proper bookkeeping on all these relationships and thus deleting a command from SICS without taking this into account may cause SICS to dump core on you. \section{Interacting with the Client Connection}\label{gow} A SICS command writer needs to interact with the client connection for a variety of reasons: \begin{itemize} \item To write error messages and other output \item To prompt for more data \item To check if the user has appropriate privileges for the command implemented. \item To tell upper level code that something went very wrong. \end{itemize} As these tasks are so common you will find that the connection object has to be passed frequently into lower level code as an argument. \subsection{Writing and Reading to the Client} All I/O to the client has to be processed through the I/O functions for connections defined in conman.h and implemented in conman.c. The most common of these are SCWrite and SCPrompt. These function will now be inspected in more detail: \begin{verbatim} int SCWrite(SConnection *pCon, char *pText, int eCode); int SCPrompt(SConnection *pCon, char *pPrompt, char *pBuffer, int iBufLen); \end{verbatim} SCWrite writes the data pText to the connection specified by pCon. The parameter eCode denotes the output code of the data in pText. SICS clients can choose to suppress some I/O from the SICS server. For instance a GUI--client might chooses not to receive scan status reports. For this purpose it was necessary to stick an output code onto each message to the client. Possible output codes are: eError, eWarning, eValue, eStatus and some internal codes. The names are self explaining. eValue denotes a value which has been explicitly asked for by the client. The rule specified above becomes understandable and thus bearable when looking at all the things SCWrite does with the message: \begin{itemize} \item It is written to the client connection socket, subject to the output code specified. \item The message is written to all log files configured for the client connection. \item If the client privilege is user or manager, the data is written to the command log. \item The message is written to the server log together with the socket number of the connection. \item SCWrite stores the message into the Tcl macro interpreter in order to enable processing of data from SICS commands in Tcl scripts. \item SCWrite suppresses all messages to the client while executing a macro. This stops spurious output to appear at the client connection when running a command defined in the macro language. The exception are messages of type eError and eWarning. Such messages are always sent to the client. \end{itemize} SCPrompt prompts the user at the client connection for data. The prompt string pPrompt is sent. Data entered by the user is returned in buffer pBuffer. Maximum iBufLen character are returned. While waiting for client to provide data, the SICS task switcher runs. There is another convenience function SCSendOK(SConnection *pCon) which is just a wrapper around SCWrite. SCSendOk sends an 'OK' to the client. It is good practice to let the user know that the operation requested had been performed. There are some more conventions which are useful to adhere to: \begin{itemize} \item All error messages start with the string ERROR: \item All warnings start with the string WARNING: \item All requested values are returned in the format name = value. \end{itemize} There exist special functions to send mass data through a connection in either uuencoded or zipped form. The latter works only through plain socket connections, telnet will mess up binary data. Please note, that numeric data sent by the SICS server needs to be in network byte order in order to be understood by the Java clients. Further functions allow to tailor writing behavious further by overloading the operations performed by SCWrite. This is documented in conman.h \subsection{Checking Client Privileges} One task any SICS object has to perform is to check if a client, represented through a connection object, is privileged to perform a requested operation. The most useful function to do this is: \begin{verbatim} int SCMatchRights(SConnection *pCon, int rights); \end{verbatim} This function not only checks if the user has at least the user rights given as parameter rights but also checks if the SICS server is currently performing a scan or something. It is generally a bad idea to change parameters while the instrument is taking a measurement. If all is good 1 is returned, 0 in case of trouble. All troubles will already have been reported to the client by this function. SICS knows four levels of user rights: \begin{description} \item[usSpy] may look at things but change nothing. \item[usUser] may do those things a normal user is supposed to do. \item[usMugger] may perform configuration tasks. \item[usInternal] absolutely no restrictions, used only internally. \end{description} There are further functions for requesting client and setting client rights codes. These functions are all defined in conman.h. \subsection{Interrupting} On occasion a SICS object may come to the conclusion that an error is so bad that the measurement needs to be stopped. Clearly a means is needed to communicate this to upper level code. This means is setting an interrupt on the connection. The current active interrupt is located at the connection object and can be retrieved with {\bf SCGetInterrupt} and set with {\bf SCSetInterrupt}. Interrupt codes are defined in interrupt.h and are ordered into a hierarchy: \begin{description} \item[eContinue] Everything is just fine. \item[eAbortOperation] Stop the current scan point or whatever is done, but do not stop altogether. \item[eAbortScan] Abort the current scan, but continue processing of further commands in R\"unbuffers or command files. \item[eAbortBatch] Aborts everything, operations, scans and batch processing and leaves the system ready to enter new commands. \item[eHaltSystem] As eAbortBatch, but lock the system. \item[eFreeSystem] Unlocks a system halted with eHaltSystem. \item[eEndServer] Makes the SICS server run down and exit. For internal usage only. \end{description} Higher level SICS objects may come to the conclusion that the error reported by lower level code is actually not that critical and clear any pending interrupts by setting the interrupt code to eContinue and thus consume the interrupt. \subsection{Communicating with all Clients} Two facilities exist which allow one SICS command to reach out to all connected clients. \begin{itemize} \item There is a function which writes a message to all clients. This is ServerWriteGlobal(char *text, int outCode); \item There exists a global status code which can be set with SetStatus and retrieved with GetStatus. See status.h for more details. Use this when your SICS command starts lengthy operations such as driving or counting. \end{itemize} \section{Using other SICS Objects} In most cases a new command needs to make use of other SICS objects. Before another SICS object can be used, it must be found within the SICS interpreter. In order do this the name of the object is obviously needed. This must be a configuration parameter or passed in as a argument to the command. In general it is also necessary to check if this name points to the right type of object. All this can be achieved with the function: \begin{verbatim} void *FindCommandData(SicsInterp *pSics, char *name, char *type); \end{verbatim} This function tries to find a command name in the interpreter and also checks if the objects type descriptor (the name parameter in the object descriptor structure) matches type. If this is so, a pointer to the objects data structure is returned. If one of the test fails, NULL is returned. Suitable parameters for type can be found by searching for CreateDescriptor in the implementation file of the desired object. After a cast to the proper pointer type, all the functions defined for the object and documented in its header file can be used. \subsection{Running Motors and Counters} There are special rules which apply if a new command is written which coordinates motors and counters. For instance a special scan or drive command. It is important that such higher level code starts motors, virtual motors and counters through the interfaces defined by the device executor. The device executor guarantees proper monitoring of the device. The relevant functions are: \begin{verbatim} int StartMotor(pExeList self, SicsInterp *pSics, SConnection *pCon, char *name, float fNew); int StartCounter(pExeList self, SicsInterp *pSics, SConnection *pCon, char *name); \end{verbatim} StartMotor starts the motor name to run to the new value fNew. StartCounter starts the counter name. The counter must have been loaded with proper presets etc. with the appropriate function calls. The device executor hides behind the pExeList pointer. This is always accessible through the global pointer: \verb+pServ->pExecutor+. Once a counter or motor has been started, quite often the command can only continue if the operation has finished. But during this time the SICS server should be responsive to other clients. In order to do this we have to wait for the device executor task to finish. A code fragment implementing all this for a count operation is shown below: \begin{verbatim} /*-------------- count */ iRet = StartCounter(pServ->pExecutor, pSics, pCon, ``ScanCounter''); if(!iRet) { SCWrite(self->pCon,"ERROR: Cannot Count, Scan aborted",eError); return 0; } /* get the ID of the device executor task */ lTask = GetDevexecID(pServ->pExecutor); /* get ID of device executor task */ if(lTask > 0); { /* wait for the device executor to finish */ TaskWait(pServ->pTasker,lTask); } /* finished, check for interrupts. Whatever happened, user interrupt or HW interrupt, it will be on our connection */ iInt = SCGetInterrupt(self->pCon); switch(iInt) { case eContinue: break; case eAbortOperation: continue; break; case eAbortScan: SCWrite(self->pCon,"ERROR: Scan aborted",eError); /* eat the interrupt, the requested op has been done */ SCSetInterrupt(self->pCon,eContinue); return 0; break; default: /* all others */ SCWrite(self->pCon,"ERROR: Scan aborted",eError); return 0; break; } \end{verbatim} This code also shows the necessary error checking. It also shows how to check for possible interrupts after such an operation. It is very advisable to do this because the user may have interrupted the process. And she might not be all to happy if the new command just continues with the next step rather than aborting the process. \section{SICS Interfaces}\label{interface}\label{inter} The point about SICS interfaces can best be deduced from an example: Everybody expects that a motor can be operated through a drive command or scanned in a scan. But there are other things which should be operated through a drive or scan commands too: environment controllers (temperature), virtual motors, chopper speeds, chopper phases and possibly other things. In order to make upper level scan or drive commands independent of the implementation of the actual operation it is useful to have an abstract interface for everything which can be driven or scanned. This is the drivable interface. Any such object which should be driven or scanned has to implement this interface. Several of these interfaces exist in SICS: \begin{description} \item[Drivable] The drivable interface for everything which can be moved and takes some time to complete its movement. \item[Countable] Everything which counts: counters, histogram memories etc. \item[Environment] An interface which allows for monitoring of a parameter through the environment monitor. Usually these are sample environment things but it could also be chopper phases etc. \item[Callback] This is an interface which allows object A to call a special function, the callback function, in the context of object B whenever a certain event in A occurs. This is a way to automatically link object together in a component programming manner. This is also used for automatic notification of status clients when instrument parameters change. \end{description} There are several situations when the SICS interfaces have to be considered: \begin{itemize} \item When hacking SICS kernel code or replacing parts of it. \item The driveable interface should be implemented by virtual motors. Virtual motors are objects which realize complex movements possibly involving multiple motors. Examples include driving wavelength (theta,two theta and possibly curvature motors have to be moved) or omega two theta. \item Any time objects are introduced into SICS which repesent completely new hardware. \item When automatical notifications between objects are needed, use the callback interface. \end{itemize} Adding any such interface to your new SICS object involves the following steps: \begin{itemize} \item Add a data structure representing the interface to your objects data structure. \item Write all the functions required by the interface. \item Populate the interface data structure with the pointers to your function implementations. \item Write a new GetInterface function for the object descriptor which returns your new interface when requested and assign it to your object descriptors GetInterface field. SICS needs this in order to be able to find the objects new interface. \end{itemize} The interfaces available are documented in the files interface.w, interface.h and interface.tex in the main SICS directory and through numerous examples in the source code. A not overly complex example for the implementation of an interface is the code in o2t.* which implements the coupled driving of two motors where the second is always the double of the value of the first. This is for omega two-theta scans.