The physical environment of an application consists of an operating system, a file system, the hardware, a network of computers, and so on. In this world other actions than just signal sending are required. Examples of actions that an application wants to perform are:
Figure 185 : Structure of an Application. ----- (fig) -----In a distributed system an application might consist of several communicating SDL systems. Each SDL system will become one executable program. It might execute either as an operating system process, communicating with other operating system processes, or it might execute in a processor of its own, communicating over a network with other processors. There may, of course, also be combinations of these cases. Let us for the sake of simplicity call the operating system processes or processors nodes communicating over a network. In the case of communicating operating system (OS) processes, the network will be the facilities for process communication provided by the OS.
There are no problems in building an application consisting of several nodes communicating over a network using the C Code Generator. However, you have to implement the communication between the nodes in the environment functions.
------------------------------------------------------------------- Note: All nodes in a network do not need to be programs generated by the C Code Generator from SDL systems. As long as a node can com municate with other nodes, it might be implemented using any tech nique. -------------------------------------------------------------------The PId values (references to process instances), are a problem in a distributed world containing several communicating SDL systems. We still want, for example, "Output To Sender" to work, even if Sender refers to a process instance in another SDL system. To cope with this kind of problem, a global node number has been introduced as a component in a PId value. The global node number, which is a unique integer value assigned to each node, identifies the node where the current process instance resides, while the other component in the PId value is a local identification of the process instance within the node (SDL system).
The partitioning of an application into an SDL system and the environment functions has additional advantages. It separates external actions into the logical decision to perform the action (the decision to send a signal to the environment) and the implementation details of the action (treating the signal in the environment functions). This kind of separation reduces the complexity of the problem and allows separate testing. It also allows parallel development of the logic (the SDL system) and the interface towards the environment (the environment functions). When the signal interface between the SDL system and its environment is settled, it is possible to continue both the activities in parallel.
When an application is developed, it is usually appropriate to first simulate and debug the SDL system or systems without its environment. One of the libraries Simulation or RealTimeSimulation may then be used. You first simulate each SDL system on its own and can then simulate the systems together (if you have communicating systems) using the facility of communicating simulations. After that you probably want to debug the application with the environment functions. This may be performed with the library ApplicationDebug. You may then generate the application with the library Application.
The library Validation allows you to build validators from the code generated by the C Code Generator. A Validator has a user interface and executing principles that are similar to a Simulator. The technical approach is however different; a Validator is based on a technique called state space exploration and operates on structures called behavior trees. Its purpose is to validate an SDL system in order to find errors and to verify its consistency against Message Sequence Charts.
Figure 186 : Data Structure Representing a Signal ----- (fig) -----A general typedef xSignalRec for a signal without parameters and for a pointer to such a signal, xSignalNode, are given below. These types may be found in the file scttypes.h. These types may be used for type casting of any signal to access the general components.
typedef struct xSignalRec *xSignalNode; typedef struct xSignalRec { xSignalNode Pre; xSignalNode Suc; SDL_PId Receiver; SDL_PId Sender; xIdNode NameNode; int Prio; } xSignalRec;A xSignalRec contains the following components:
Example 14 : Generated C Code for Signal DefinitionAssume the following signal definitions in SDL:
SIGNAL S1(Integer), S2, S3(Integer, Boolean, OwnType);then the C code below will be generated:
typedef struct { SIGNAL_VARS SDL_Integer Param1; } yPDef_z0f_S1; typedef yPDef_z0f_S1 *yPDP_z0f_S1; typedef struct { SIGNAL_VARS SDL_Integer Param1; SDL_Boolean Param2; z09_OwnType Param3; } yPDef_z0h_S3; typedef yPDef_z0h_S3 *yPDP_z0h_S3;where SIGNAL_VARS is a macro defined in scttypes.h that is expanded to the common components in a signal struct.
For each signal with parameters there are two generated types, a struct type and a pointer type. The struct type contains one component for each parameter in the signal definition and the components will be named Param1, Param2 and so on. The components will be placed in the same order in the struct as the parameters are placed in the signal definition.
------------------------------------------------------------- Note: There are no generated types for a signal without parameters. -------------------------------------------------------------
typedef xLocalPIdRec *xLocalPIdNode; typedef struct { int GlobalNodeNr; xLocalPIdNode LocalPId; } SDL_PId;The global node number identifies the SDL system that the process instance belongs to, while the local PId value identifies the process instance within the system. The local PId pointer value should not be referenced outside the SDL system where it is defined.
By introducing a global node number in the PId values, these values are possible to interpret throughout an application consisting of several SDL systems. You can also define your own PId values in non-SDL defined parts of the application and still use communication with signals.
The variable SDL_NULL, which represents a null value for PIds and which is defined in the runtime library and available through the file scttypes.h, contains zero in both the global node number and the local PId component. Note that the global node number should be greater than zero in all PId values except SDL_NULL.
typedef struct xSignalIdStruct *xSignalIdNode; typedef struct xSignalIdStruct { /* components */ } xSignalIdRec;It is the nodes that represent the signal types for signals sent to and from the environment of the SDL system, that are of major interest in connection with the environment functions. For each signal type there will be a symbol table node. That node may be referenced using the name ySigN_ followed by the signal name with prefix. Such references may be used in, for example, xOutEnv to find the signal type of the signal passed as parameter.
In some cases the symbol table nodes for channels from the environment to a block in the system are of interest to refer to. In a similar way as for signals such nodes may be referenced using the name yChaN_ followed by the channel name with prefix.
You should provide the following functions to represent the environment of the SDL system:
-------------------------------------------------------------------- Note: You may find guidelines for the environment functions in the file: $sdtdir/INCLUDE/sctenv.c Please make a copy of the file and use it to build your environment functions. This file also contains some trace mechanisms that may be used to trace the execution in a target computer. This trace can, however, only be used if you have the source code for the run-time library (included in the C-Advanced Code Genera tor) and can produce a new object library with the appropriate switches. --------------------------------------------------------------------
The system interface header file, which only contains code for objects defined in the system diagram, has the following structure:
Example 15 : Contents of an .ifc FileIf an SDL signal called Sig1 is defined in the system, then the .ifc file will contain:
extern XCONST struct xSignalIdStruct ySigR_z5_Sig1; #ifndef Sig1 #define Sig1 (&ySigR_z5_Sig1) #endifThis means that the xIdNode for the signal may be referred to using the signal name directly, and not only by a name containing a prefix that cannot be predicted in advance and that may change due to changes in the system diagram.
The strategy used means that you have to regenerate the system interface header file each time you regenerate code for the system, but also that the code in the environment functions need not to be changed (only recompiled).
The following SDL names will in the same way be available directly in C:
Example 16 : Contents of an .ifc FileSuppose the following objects are defined in a system diagram:
synonym syn1 integer = 10; synonym syn2 struct1 = (. 1, 2 .); synonym syn3 boolean = external; newtype sort1 literals lit1, lit2, lit3; endnewtype; newtype struct1 struct a,b integer; endnewtype; signal sig1, sig2(integer), sig3(sort1, struct1); channel chan1 ... endchannel; channel chan2 ... endchannel;Then the following system interface header file will be generated (assuming some appropriate prefixes):
/* ******** DECLARATIONS IN UNIT minimal ******** */ /*------------------ SYNONYMS -------------------*/ #define z8_syn1 SDL_INTEGER_LIT(10) #define syn1 SDL_INTEGER_LIT(10) /*-------------------- SORTS --------------------*/ typedef enum {z60_lit1, z61_lit2, z62_lit3 } z6_sort1; #define lit1 z60_lit1 #define lit2 z61_lit2 #define lit3 z62_lit3 #define sort1 z6_sort1 typedef struct z7_struct1_s { SDL_Integer a; SDL_Integer b; } z7_struct1; #define struct1 z7_struct1 /*------------------ SYNONYMS -------------------*/ #ifndef syn3 extern SDL_Boolean syn3; #endif extern z7_struct1 z9_syn2; #define syn2 z9_syn2 /*------------------- SIGNALS -------------------*/ /* sig1 IN */ extern XCONST struct xSignalIdStruct ySigR_z3_sig1; #ifndef sig1 #define sig1 (&ySigR_z3_sig1) #endif /* sig2 IN */ typedef struct { SIGNAL_VARS SDL_Integer Param1; } yPDef_z4_sig2; typedef yPDef_z4_sig2 *yPDP_z4_sig2; #define yPDP_sig2 yPDP_z4_sig2 extern XCONST struct xSignalIdStruct ySigR_z4_sig2; #ifndef sig2 #define sig2 (&ySigR_z4_sig2) #endif /* sig3 OUT */ typedef struct { SIGNAL_VARS z6_sort1 Param1; z7_struct1 Param2; } yPDef_z5_sig3; typedef yPDef_z5_sig3 *yPDP_z5_sig3; #define yPDP_sig3 yPDP_z5_sig3 extern XCONST struct xSignalIdStruct ySigR_z5_sig3; #ifndef sig3 #define sig3 (&ySigR_z5_sig3) #endif /*------------------ CHANNELS -------------------*/ #ifndef XOPTCHAN extern struct xChannelIdStruct yChaR_z1_chan1; #define chan1 (&yChaR_z1_chan1) #endif #ifndef XOPTCHAN extern struct xChannelIdStruct yChaR_z2_chan2; #define chan2 (&yChaR_z2_chan2) #endif
#include "scttypes.h" #include "file with macros for external synonyms" #include "systemfilename.ifc" void xInitEnv XPP((void)) { } void xCloseEnv XPP((void)) { } #ifndef XNOPROTO void xOutEnv (xSignalNode *S) #else void xOutEnv (S) xSignalNode *S; #endif { } #ifndef XNOPROTO void xInEnv (SDL_Time Time_for_next_event) #else void xInEnv (Time_for_next_event) SDL_Time Time_for_next_event; #endif { } int xGlobalNodeNumber XPP((void)) { }The last function, xGlobalNodeNumber, will be discussed later, see page 466. The usage of the macros XPP and XNOPROTO makes the code possible to compile both with compilers that can handle prototypes and with compilers that cannot. If you do not need this portability, you can reduce the complexity of the function headings somewhat. In the minor examples in the remaining part of this section only versions with prototypes are shown.
void xInitEnv ( void ); void xCloseEnv ( void );In the implementation of these functions you can place the appropriate code needed to initialize and terminate the software and the hardware. The function xInitEnv will be called during the start up of the program as first action, while the xCloseEnv will be called in the function SDL_Halt. Calling SDL_Halt is the appropriate way to terminate the program. The easiest way to call SDL_Halt is to include the call in a #CODE directive in a TASK. SDL_Halt is part of the runtime library and has the following definition:
void SDL_Halt ( void );---------------------------------------------------------------------- Note: xInitEnv will be called before the SDL system is initialized, which means that no references to the SDL system are allowed in this func tion. To, for example, send signals into the system during the initial ization phase, the #MAIN directive should be used (see "Initializa tion - Directive #MAIN" on page 2040 in chapter 34, The C Code Generator). Use of this directive code can be placed after the initial ization of the SDL system, but before any transitions are executed. ----------------------------------------------------------------------
void xOutEnv ( xSignalNode *S )The xOutEnv function will have the current signal as parameter, so you have all the information contained in the signal at your disposal when you implement the actions that should be performed. The signal contains the signal type, the sending and receiving process instance and the parameters of the signal. For more information about the types used to represent signals and processes, see section "Types Representing Signals" on page 449 and "Types Representing Processes" on page 451.
Note that the parameter of xOutEnv is an address to xSignalNode, that is, an address to a pointer to a struct representing the signal. The reason for this is that the signal that is given as parameter to xOutEnv should be returned to the pool of available memory before return is made from the xOutEnv function. This is made by calling the function xReleaseSignal, which takes an address to an xSignalNode as parameter, returns the signal to the pool of available memory, and assigns 0 to the xSignalNode parameter. Thus, there should be a call
xReleaseSignal(S);before returning from xOutEnv. The xReleaseSignal function is defined as follows:
void xReleaseSignal ( xSignalNode *S );In the function xOutEnv you may use the information in the signal that is passed as parameters to the function. First it is usually suitable to determine the signal type of the signal. This is best performed by if statements containing expressions of the following form, assuming the use of the system interface header file and that the signal has the name Sig1 in SDL:
(*S)->NameNode == Sig1Suitable expressions to reach the Receiver, the Sender, and the signal parameters are:
(*S)->Receiver (*S)->Sender ((yPDP_Sig1)(*S)) -> Param1 ((yPDP_Sig1)(*S)) -> Param2(and so on)
Sender will always refer to the sending process instance, while Receiver is either a reference to a process in the environment or the value xEnv. xEnv is a PId value that refers to an environment process instance, which is used to represent the general concept of environment, without specifying an explicit process instance in the environment.
----------------------------------------------------------------------- Note: It is not possible to calculate the PId value for a process in the envi ronment, the value has to be taken from an incoming signal (sender or signal parameter). This is the normal procedure in SDL to estab lish direct communication between two processes in the same SDL system. -----------------------------------------------------------------------Receiver will refer to the process xEnv if the PId expression in an output TO refers to xEnv, or if the signal was sent in an output without a TO clause and the environment was the only possible receiver found in the scan for receivers.
Example 17 : Structure ox xOutEnv Function void xOutEnv ( xSignalNode *S ) { if ( (*S)->NameNode == Sig1 ) { /* perform appropriate actions */ xReleaseSignal(S); return; } if ( (*S)->NameNode == Sig2 ){ /* perform appropriate actions */ xReleaseSignal(S); return; } /* and so on */ }
void xInEnv (SDL_Time Time_for_next_event)To implement the sending of a signal into the SDL system, two functions are available: xGetSignal, which is used to obtain a data area suitable to represent the signal, and SDL_Output, which sends the signal to the specified receiver according to the semantic rules of SDL. These functions will be described later in this subsection.
The parameter Time_for_next_event will contain the time for the next event scheduled in the SDL system. The parameter will either be 0, which indicates that there is a transition (or a timer output) that can be executed immediately, or be greater than Now, indicating that the next event is a timer output scheduled at the specified time, or be a very large number, indicating that there is no scheduled action in the system, that is, the system is waiting for an external stimuli.
You should scan the environment, perform the current outputs, and return as fast as possible if Time has past Time_for_next_event.
If Time has not past Time_for_next_event, you have a choice to either return from the xInEnv function at once and have repeated calls of xInEnv, or stay in the xInEnv until something triggers a signal output (a signal sent to the SDL system) or until Time has past Time_for_next_event.
------------------------------------------------------------------------ Note: We recommend always to return from the xInEnv function as fast as possible to ensure that it will work appropriately together with the monitor (during debugging). Otherwise, the keyboard polling, that is, typing <RETURN> in order to interrupt the execution, will not work. ------------------------------------------------------------------------The function xGetSignal, which is one of the service functions suitable to use when a signal should be sent, returns a pointer to a data area that represents a signal instance of the type specified by the first parameter.
xSignalNode xGetSignal ( xSignalIdNode SType, SDL_PId Receiver, SDL_PId Sender );The components Receiver and Sender in the signal instance will also be given the values of the corresponding parameters.
void SDL_Output ( xSignalNode S, xIdNode ViaList[] );
Example 18 : C Code to Send a Signal to the Environment SDL_Output( xGetSignal(S1, xNotDefPId, xEnv), (xIdNode *)0 );If S2, with two integer parameters, should be sent from xEnv to the process instance referenced by the variable P, the code will be:
xSignalNode OutputSignal; /* local variable */ ... OutputSignal = xGetSignal(S2, P, xEnv); ((yPDP_S2)OutputSignal)->Param1 = 1; ((yPDP_S2)OutputSignal)->Param2 = 2; SDL_Output( OutputSignal, (xIdNode *)0 );For the details of how to reference the parameters of a signal see the subsection "Types Representing Signals" on page 449.
To introduce a via list in the output requires a variable, which should be an array of xIdNode, that contains references to the symbol table nodes representing the current channels (or signal routes) in the via list. In more detail, we need a variable
ViaList xIdNode[N];where N should be replaced by the length of the longest via list we want to represent plus one. The components in the variable should then be given appropriate values, such that component 0 is a reference to the first channel (its symbol table node) in the via list, component 1 is a reference to the second channel, and so on. The last component with a reference to a channel must be followed by a component containing a null pointer (the value (xIdNode)0). Components after the null pointer will not be referenced. Below is an example of how to create a via list of two channels, C1 and C2.
Example 19 : Via List of two Channels. ViaList xIdNode[4]; /* longest via has length 3 */ ... /* this via has length 2 */ ViaList[0] = (xIdNode)C1; ViaList[1] = (xIdNode)C2; ViaList[2] = (xIdNode)0;The variable ViaList may then be used as a ViaList parameter in a subsequent call to SDL_Output.
The structure given in the example below may serve as an idea of how to design the xInEnv function.
Example 20 : Structure of xInEnv Function void xInEnv (SDL_Time Time_for_next_event) { xSignalNode S; if ( Sig1 should be sent to the system ) { SDL_Output (xGetSignal(Sig1, xNotDefPId, xEnv), (xIdNode *)0); } if ( Sig2 should be sent to the system ) { S = xGetSignal(Sig1, xNotDefPId, xEnv); ((xPDP_Sig2)S)->Param1 = 3; ((xPDP_Sig2)S)->Param2 = SDL_True; SDL_Output (S, (xIdNode *)0); } /* and so on */ }This basic structure can be modified to suit your own needs. The if statements could, for example, be substituted for while statements. The signal types might be sorted in some "priority order" and a return can be introduced last in the if statements. This means that only one signal is sent during a xInEnv call, which reduces the latency.
The usage of the #EXTSIG directive is described in chapter 34, The C Code Generator in the section "Modifying Outputs - Directive #EXTSIG, #ALT, #TRANSFER" on page 2040. This information is not repeated here.
By using the #EXTSIG directive the following overhead can be avoided:
Example 21 : Including Environment Functions in SDL System /*#CODE #BODY ... code for the environment functions ... */In this case you cannot use the system interface header file, but instead you have all the necessary declarations already at your disposal, as the functions will be part of the SDL system. The only problem you will encounter is the prefixing of SDL names when they are translated to C. The #SDL directive should be used to handle this problem (or the #NAME directive), see chapter 34, The C Code Generator, sections "Accessing SDL Names in C Code - Directive #SDL" on page 2027 and "Specifying Names in Generated Code - Directive #NAME" on page 2038. The following table shows how to obtain C names for some SDL objects of interest:
#(Synonym name) #(Newtype or syntype name) ySigN_#(Signal name) yPDP_#(Signal name) yChaN_#(Channel name)
int xGlobalNodeNumber ( void )The returned integer should be greater than zero and should be unique among the communicating SDL systems that constitutes an application. If the application consists of only one application then this number is of minor interest (it still has to be set). The global node number is used in PId values to identify the node (OS process / processor) that the process instance belongs to. PId values are thereby universally accessible and you may, for example, in a simple way make "Output To Sender" work between processes in different SDL systems (OS processes / processors).
When an application consisting of several communicating SDL systems is designed, you have to map the global node number to the current OS process or processor, to be able to transmit signals addressed to non-local PIds to the correct OS process or processor. This will be part of the xOutEnv function.
The function main will be generated into the .c file that represents the SDL system and will have the following structure:
Example 22 : Structure of Generated Program void main ( void ) { xMainInit(yInit); Code from #MAIN directive xMainLoop(); }where xMainInit and xMainLoop are runtime library functions with the following structure:
void xMainInit ( void (*Init_System)() ) { xInitEnv(); Init of internal data structures in the runtime library; (*Init_System)(); } void xMainLoop ( void ) { while (1) { xInEnv(...); if ( Timer output is possible ) SDL_OutputTimerSignal(); else if ( Process transition is possible ) Call appropriate PAD function; } }The function xMainLoop contains an endless loop. The appropriate way to stop the execution of the program is to call the runtime library function SDL_Halt. The call of this C function should normally be included in an appropriate task using the directive #CODE. SDL_Halt has the following structure:
void SDL_Halt ( void ) { xCloseEnv(); exit(0); }To complete this overview, which emphasizes the usage of the environment functions, we have to deal with the xOutEnv function. Within PAD functions, the runtime library function SDL_Output is called to implement outputs of signals. When SDL_Output identifies the receiver of a signal to be a process instance that is not part of the current SDL system, SDL_Output will call the xOutEnv function.
There are some observations we can make from this short description.
The diagrams for the SDL system may be found in "Appendix A: The SDL System" on page 481. In the section "Where to find the Example" on page 479, references to where to find the source code for this example are given.
There are six cases that should be tested:
Example 23 : Execution Trace of Generated Application Start program Phone.sim.sct Command : set-trace 6 Trace for System Phone set to 6 Command : next-transition *** TRANSITION START * PId : PhonePr:1 * State : start state * Now : 0.0000 *** NEXTSTATE idle Command : output-via Signal name : TermInput Parameter 1 (charstring) : `hello' Channel name : Signal TermInput was sent to PhonePr:1 from env:1 Command : next-transition *** TRANSITION START * PId : PhonePr:1 * State : idle * Input : TermInput * Sender : env:1 * Now : 0.0000 * Parameter(s) : `hello' * DECISION Value: true * DECISION Value: false * OUTPUT of Message to env:1 * Parameter(s) : `hello', PhonePr:1 *** NEXTSTATE idle Command : output-via TermInput `.' - Signal TermInput was sent to PhonePr:1 from env:1 Command : next-transition *** TRANSITION START * PId : PhonePr:1 * State : idle * Input : TermInput * Sender : env:1 * Now : 0.0000 * Parameter(s) : `.' * DECISION Value: true * DECISION Value: true * OUTPUT of Terminate to env:1 * Parameter(s) : PhonePr:1 *** NEXTSTATE idle Command : output-via Message Parameter 1 (charstring) : `hello' Parameter 2 (pid) : env Channel name : Signal Message was sent to PhonePr:1 from env:1 Command : next-transition *** TRANSITION START * PId : PhonePr:1 * State : idle * Input : Message * Sender : env:1 * Now : 0.0000 * Parameter(s) : `hello', env:1 * DECISION Value: false * OUTPUT of Message to env:1 * Parameter(s) : `hello', env:1 * OUTPUT of Display to env:1 * Parameter(s) : `hello' *** NEXTSTATE idle Command : output-via Message Parameter 1 (charstring) : `hello' Parameter 2 (pid) : PhonePr:1 Channel name : Signal Message was sent to PhonePr:1 from env:1 Command : next-transition *** TRANSITION START * PId : PhonePr:1 * State : idle * Input : Message * Sender : env:1 * Now : 0.0000 * Parameter(s) : `hello', PhonePr:1 * DECISION Value: true * OUTPUT of Display to env:1 * Parameter(s) : `hello' *** NEXTSTATE idle Command : output-via Terminate Parameter 1 (pid) : env Channel name : Signal Terminate was sent to PhonePr:1 from env:1 Command : next-transition *** TRANSITION START * PId : PhonePr:1 * State : idle * Input : Terminate * Sender : env:1 * Now : 0.0000 * Parameter(s) : env:1 * DECISION Value: false * OUTPUT of Terminate to env:1 * Parameter(s) : env:1 * TASK Halt Example 24 Start program Phone.sim.sct Command : set-trace 6 Trace for System Phone set to 6 Command : next-transition *** TRANSITION START * PId : PhonePr:1 * State : start state * Now : 0.0000 *** NEXTSTATE idle Command : output-via Terminate Parameter 1 (pid) : PhonePr:1 Channel name : Signal Terminate was sent to PhonePr:1 from env:1 Command : next-transition *** TRANSITION START * PId : PhonePr:1 * State : idle * Input : Terminate * Sender : env:1 * Now : 0.0000 * Parameter(s) : PhonePr:1 * DECISION Value: true * TASK HaltBy running the system with the SDL monitor, as in the examples above, you may debug the system at the SDL level. The overall behavior of the system can thus be tested.
If you find it meaningful, it is possible to start two instances of the simulation and have the simulators communicate with each other. Then Message and Terminate signals sent to the environment in one of the simulations will appear as signals coming from the environment in the other.
---------------------------------------------------------- Note: Do not forget the monitor command StartSDTEnv to make the simulation programs start communicating. ----------------------------------------------------------
To simplify the example we assume that each instance of the application is started in a window of its own (a shell tool window under for instance X Windows, where UNIX commands can be entered). This means that we will have no problems with the interpretation of stdin and stdout in the programs.
The name of the socket for incoming messages for a certain instance of the application will be the string "Phone" concatenated with the UNIX process number for the current program. The socket will be created in the directory /tmp. Each application instance will print this number during the initialization and will then ask for the process number of the application instance where it should send its messages. You have to enter these numbers in such a way as to form a ring among the applications.
As the PhoneEnv.c file includes scttypes.h and uses some C macros, it should be compiled using the same compiler options as the C file for the SDL system. For information how to extend the generated make file to handle also non-generated files, please see "Compile and Link Options" on page 1110 in chapter 22, The SDT Organizer.
In the code for the environment functions a number of UNIX functions are used. Their basic behavior is described below. For any details please see the UNIX manuals available from SUN Microsystems.
------------------------------------------------------------------- Function name Functionality ------------------------------------------------------------------- getpid Returns the UNIX process number for the cur rent program. socket Returns a new, unnamed socket. bind Binds a socket to a name in the file system. listen Starts listen for other programs trying to connect to this socket. connect Should be called by other programs that want to establish a connection to the current socket. accept Accepts a connection request. select Returns 1 if anything readable can be found in any of the specified file descriptors, where a file descriptor can represent a file, a socket, and the terminal (stdin and stdout). read, write Reads or writes on a file (a file descriptor). close Closes a file. unlink Removes a file. -------------------------------------------------------------------If we now look at the code for the environment functions (see "Appendix B: The Environment Functions" on page 484), we see that xInitEnv mainly performs the following actions:
The xInEnv and xOutEnv functions follow the guidelines for these functions given in the reference section. In xInEnv the select function is used to determine if any messages are ready to be received from the terminal (stdin) or from the incoming socket. An available message is then read and the information is converted to an SDL signal, which is sent to the SDL system using the SDL_Output function. In xOutEnv a test on the NameNode in the signal is used to determine the signal type. Depending on the signal type the appropriate information is written either on the outgoing socket or on the terminal (stdout).
When the environment functions (xInEnv) read information from the keyboard there is, however, a problem in using xInEnv together with the monitor. In our system, for instance, a line typed on the keyboard may either be a monitor command or a line typed to the SDL system. As both the monitor and xInEnv are polling for lines from stdin, the interpretation of a typed line depends on which one first finds the line.
A better way is to eliminate this undeterministic behavior by not polling for typed lines in xInEnv. Instead, you may use the monitor command:
Output-Via TermInput 'the line'to simulate a line typed on the keyboard. In this way all the other parts of the environment functions can be tested under the monitor. If you enclose the sections in xInEnv handling keyboard polling between #ifndef XMONITOR and #endif this code is removed when the monitor is used; that is if the library ApplicationDebug is used (see the code for xInEnv in "Appendix B: The Environment Functions" on page 484).
A C source code debugger is of course also useful when debugging the environment functions. The initialization phase, xInitEnv, is probably the most difficult part to get working correctly in our system. All the source code for this function is available, and a C debugger can be used.
While debugging generated code from SDL at the C level, it is always easy to find the currently executing SDL symbol, by using the SDT references (see "Syntax" on page 2476 in chapter 41, References in SDT-3) in the C code and the Go To Source menu choice in the Edit menu in the SDT Organizer. For more details please see page 1094 in chapter 22, The SDT Organizer.
When you run the Phone system, start the program from two (or more) shell tools. Each instance of the program will then print:
My Pid: 2311 Connect me to:You should answer these questions in such a way that a ring is formed by the programs. When the initialization is completed for a program it prints:
******** Welcome to SDT Phone System ******** phone ->The program is now ready to receive lines printed on the keyboard and messages sent from other programs. A Display signal received from another program is printed as follows:
display -> the line received in Display signal
$sdtrelease/examples/phone$sdtrelease is an environment variable which is set up at installation of SDT, and designates the SDT installation root directory.
Use these files if you only want to look at the source files and try the executing versions of the program. Otherwise you should copy the files to one of your own directories. Please be sure not to change the original files.
In the directory you will find the following files:
------------------------------------------------------------ File name Purpose ------------------------------------------------------------ Phone.sys Represents the SDL system PhoneBl.blk Represents the SDL block PhonePr.spr Represents the SDL process Phone.pr The generated PR file after GR to PR Phone.c The generated C file after C code generation Phone.ifc The generated .ifc file PhoneEnv.c Contains the environment functions Phone.m The generated make file Phone.sim.sct An executable program made with the library Simulation Phone.deb.sct An executable program made with the library ApplicationDebug Phone.appl.sct An executable program made with the library Application ------------------------------------------------------------The executable programs containing the monitor, Phone.sim.sct and Phone.deb.sct, may either be executed from SDT or directly from a UNIX shell.
Figure 187 : The System Phone. ----- (fig) -----
Figure 188 : The Block PhoneBl. ----- (fig) -----
Figure 189 : The Process PhonePr. ----- (fig) -----
/****+****************************************************** 00 sctEnv.c for SimplePhoneSys ***********************************************************/ #include "scttypes.h" #include "Phone.ifc" #include <sys/types.h> #include <sys/time.h> #include <sys/socket.h> #include <sys/un.h> int Out_Socket, In_Socket; struct sockaddr_un Connection_Socket_Addr; struct sockaddr_un Connected_Socket_Addr; #ifdef XENV /*#if !defined(XPMCOMM) && defined(XENV)*/ /*---+------------------------------------------------------- xGlobalNodeNumber extern -----------------------------------------------------------*/ #ifndef XNOPROTO int xGlobalNodeNumber( void ) #else int xGlobalNodeNumber() #endif { static int ProcId = -1; if (ProcId < 0) ProcId = getpid(); return (ProcId); } /*#endif*/ /*---+------------------------------------------------------- xInitEnv extern -----------------------------------------------------------*/ #ifndef XNOPROTO void xInitEnv( void ) #else void xInitEnv() #endif { fd_set readfds; int addr_size; int Connection_Socket; char TmpStr[132]; struct timeval t; t.tv_sec = 60; t.tv_usec = 0; if ((Connection_Socket = socket(PF_UNIX,SOCK_STREAM,0)) < 0) { printf("\nError: No Connection_Socket available!\n"); SDL_Halt(); } if ( (Out_Socket = socket(PF_UNIX,SOCK_STREAM,0)) < 0 ) { printf("\nError: No Out_Socket available!\n"); SDL_Halt(); } sprintf(Connection_Socket_Addr.sun_path, "/tmp/Phone%d", xGlobalNodeNumber()); Connection_Socket_Addr.sun_family = PF_UNIX; if ( 0 > bind(Connection_Socket, &Connection_Socket_Addr, strlen(Connection_Socket_Addr.sun_path)+2) ) { printf("\nError: Bind did not succeed!\n"); SDL_Halt(); } listen(Connection_Socket, 3); printf("\nMy Pid: %d\n", xGlobalNodeNumber()); printf("\nConnect me to: "); FD_ZERO(&readfds); FD_SET(1,&readfds); if ( 0 < select(getdtablesize(), &readfds, (fd_set*)0, (fd_set*)0, &t) ) { if ( FD_ISSET(1, &readfds) ) { (void)gets(TmpStr); sscanf(TmpStr, "%s", TmpStr); sprintf(Connected_Socket_Addr.sun_path, "/tmp/Phone%s", TmpStr); Connected_Socket_Addr.sun_family = PF_UNIX; connect(Out_Socket, &Connected_Socket_Addr, strlen(Connected_Socket_Addr.sun_path)+2); } } else { printf("\nError: Timed out\n"); SDL_Halt(); } FD_ZERO(&readfds); FD_SET(Connection_Socket,&readfds); if ( 0 < select(getdtablesize(), &readfds, (fd_set*)0, (fd_set*)0, &t) ) { if ( FD_ISSET(Connection_Socket, &readfds) ) { addr_size = strlen(Connection_Socket_Addr.sun_path)+2; In_Socket = accept(Connection_Socket, &Connection_Socket_Addr, &addr_size); } } else { printf("\nError: Timed out\n"); SDL_Halt(); } printf("\n\n******** Welcome to SDT Phone System ********\n"); printf("\nphone -> "); } /*---+------------------------------------------------------ xCloseEnv extern -----------------------------------------------------------*/ #ifndef XNOPROTO void xCloseEnv( void ) #else void xCloseEnv() #endif { close(Out_Socket); close(In_Socket); unlink(Connected_Socket_Addr.sun_path); unlink(Connection_Socket_Addr.sun_path); printf("\nClosing this session.\n"); } /*---+------------------------------------------------------- xInEnv extern -----------------------------------------------------------*/ #ifndef XNOPROTO void xInEnv( SDL_Time Time_for_next_event ) #else void xInEnv( Time_for_next_event ) SDL_Time Time_for_next_event; #endif { struct timeval t; fd_set readfds; char *Instr; int NrOfReadChars; char SignalName; xSignalNode OutputSignal; int i = 0; char chr; t.tv_sec = 0; t.tv_usec = 1000; FD_ZERO(&readfds); #ifndef XMONITOR FD_SET(1,&readfds); #endif FD_SET(In_Socket,&readfds); if ( select(getdtablesize(),&readfds,0,0,&t) > 0 ) { #ifndef XMONITOR /*SDL-signal TermInput */ if FD_ISSET(1, &readfds) { Instr = (char *)xAlloc(132); Instr[0]='L'; Instr++; (void)gets(Instr); OutputSignal = xGetSignal(TermInput, xNotDefPId, xEnv); xAss_SDL_Charstring( &((yPDP_TermInput)OutputSignal)->Param1, --Instr,XASS); SDL_Output(OutputSignal, (xIdNode *)NIL); xFree((void**)&Instr); } #endif if FD_ISSET(In_Socket, &readfds) { Instr = (char *)xAlloc(151); do { read(In_Socket, &chr, 1); Instr[i++] = chr; } while ( chr!='\0' ); sscanf(Instr, "%c", &SignalName); if ( SignalName == `M' ) { /* SDL-signal Message */ OutputSignal = xGetSignal(Message, xNotDefPId, xEnv); sscanf( Instr+1, "%d %x%n", &(((yPDP_Message)OutputSignal)->Param2.GlobalNodeNr), &(((yPDP_Message)OutputSignal)->Param2.LocalPId), &NrOfReadChars); xAss_SDL_Charstring( &((yPDP_Message)OutputSignal)->Param1, (Instr+NrOfReadChars+2),XASS); SDL_Output(OutputSignal, (xIdNode *)NIL); } else if ( SignalName == `T' ) { /* SDL-signal Terminate */ OutputSignal = xGetSignal(Terminate, xNotDefPId, xEnv); sscanf( Instr+1, "%d %x", &(((yPDP_Terminate)OutputSignal)->Param1.GlobalNodeNr), &(((yPDP_Terminate)OutputSignal)->Param1.LocalPId)); SDL_Output(OutputSignal, (xIdNode*)0); } xFree((void**)&Instr); } } } /*---+------------------------------------------------------- xOutEnv extern -----------------------------------------------------------*/ #ifndef XNOPROTO void xOutEnv( xSignalNode *S ) #else void xOutEnv( S ) xSignalNode *S; #endif { char Outstr[150]; /* SDL-signal Message */ if ( (*S)->NameNode == Message ) { sprintf(Outstr, "M %d %x %.*s", ((yPDP_Message)(*S))->Param2.GlobalNodeNr, ((yPDP_Message)(*S))->Param2.LocalPId, strlen(((yPDP_Message)(*S))->Param1), ((yPDP_Message)(*S))->Param1); write(Out_Socket, Outstr, strlen(Outstr)+1); xReleaseSignal(S); return; } /* SDL-signal Terminate */ if ( (*S)->NameNode == Terminate) { sprintf(Outstr, "T %d %x", ((yPDP_Terminate)(*S))->Param1.GlobalNodeNr, ((yPDP_Terminate)(*S))->Param1.LocalPId); write(Out_Socket, Outstr, strlen(Outstr)+1); xReleaseSignal(S); return; } /* SDL-signal Display */ if ( (*S)->NameNode == Display ) { printf("\ndisplay ->%.*s", strlen(((yPDP_Display)(*S))->Param1), ++((yPDP_Display)(*S))->Param1); printf("\nphone -> "); xReleaseSignal(S); return; } } #endif
Table of Contents Next Chapter