The normal sequence to use ACME Server is:
Acme Server is capable of servicing both local and remote clients. The server uses a repository as a temporary holding place for files until they are ready to be brought into the active design by parsing.
There are two methods to get the local file into the repository. Use a traditional FTP program to transfer the file into the repository directory of the server (as set in the server.config file). The other is to transfer the file using the simple ftp messages in AcmeServer to transfer the file(OPEN_FILE_FTP, FILE_DATA_FTP, CLOSE_FILE_FTP).
Once the file has been transferred, the OPEN message will cause the server to look in the repository, and parse the file into an acme design. Once the design has been loaded, the server no longer uses the text file for anything. All operations are perfromed on the acme library design representation.
Saving a file causes the acme representation to be sent back to the client and it is the client's responsibility to save it in some persistent location.
This scheme has some limitations which the programmer needs to be concerned with:
Ensuring that you use a descriptive name for your acme designs as opposed to generic names like test.acme, system.acme, etc. can prevent these types of errors from occurring.
The message types are defined in the header file "messages.h". The message types are just mneomonics for the integer message types needed by the client and server. The second file contains the data structures needed by the PBIO messages. This is in the file messagedefs.h. Because this file actually specifies storage, it should be included by only one source file in a program.
To compile this program, be sure your include and library directories point to ../morale/acme/dataexchange and ../morale/acme/pbio and you link in the IO and DE libraries. (libIO.a and libDE.a).
The simple program below shows a C program connecting to an acme server and getting a list of top-level system components.
#include <stdio.h> #include <DE.h> //this is the dataexchange/pbio standard defs #include "messages.h" //this is the description of the ACME server messages #include "messagedefs.h" //this contains the PBIO message descriptions. IT MUST BE INCLUDED BY ONLY 1 SOURCE FILE IN A PROGRAM! DExchange de; int main (int argc, char **argv) { basic_msg message; //define a message to send to server basic_msg_ptr reply; //a pointer to hold the server response DEPort dep; char *hostName = "infinia"; int ACME_Server_Port = 9999; char *fileName="Existing.acme"; char *toolID="MyTool"; int basic_format; //the DE assigned format ID for a message type int reply_format; //hold the value for the reply message format FILE *fp; char line[512]; /************************************* ** Initialization Stuff *************************************/ de = DExchange_create(); //create the data exchange //these 2 lines register the formats that will be used for communications DExchange_register_format(de, "basic message", basic_message); DExchange_register_format(de, "long5 message", long5_message); //Now get the DE format number for later use basic_format=DEget_format_id(de,"basic message"); /************************************* ** Connect to ACME Server *************************************/ //the true here says block till connect complete dep=DExchange_initiate_conn(de, hostName, ACME_Server_Port, TRUE); /************************************** ** Transfer file from client to server **************************************/ //Now transfer the file we want to the server //first open the file at the server end for saving message.msgType=OPEN_FILE_FTP; message.param1=fileName; message.param2=""; DEport_write_data(dep, basic_format, &message); //check server result to be sure file was opened reply=(basic_msg_ptr) DEport_read_data(dep, &basic_format); if (reply->msgType != ACME_SUCCESS) { //operation failed printf("Server open for FTP failed\n"); exit(0); } //Now open the file here to read and transfer it to the server fp=fopen(fileName,"r"); message.msgType=FILE_DATA_FTP; message.param1=fileName; while(fgets(line,512,fp)!=NULL){ message.param2=line; DEport_write_data(dep, basic_format, &message); } message.msgType=CLOSE_FILE_FTP; message.param1=fileName; message.param2=""; DEport_write_data(dep, basic_format, &message); reply=(basic_msg_ptr) DEport_read_data(dep, &basic_format); /************************************* ** Open an ACME Design *************************************/ //Now build a message to request a file open for the design message.msgType=OPEN; message.param1=fileName; message.param2=toolID; //Now send the message to the server DEport_write_data(dep, basic_format, &message); //Now get the reply and check for success reply=(basic_msg_ptr) DEport_read_data(dep,&reply_format); if (reply->msgType!=ACME_SUCCESS) { printf("Open file failed at server"); exit(1); } else { //first param of reply has sys name printf("Listing Components for System %s\n",reply->param1); /*************************************** ** Send request to the server for all components ****************************************/ //Build and send the request for all components message.msgType=GET_COMPONENTS; message.param1=reply->param1; message.param2=""; DEport_write_data(dep, basic_format, &message); //Now loop getting server messages until done while (1) { reply=(basic_msg_ptr) DEport_read_data(dep,&reply_format); if (reply->msgType==COMPONENT_NAME) //is this a component name printf("Component = %s\n",reply->param1); else if (reply->msgType==DATA_DONE) //if no more components then done break; else { printf("OOPS, improper server response"); break; } } } gets(line); }
The Tcl/Tk interface requires the tcl package to dynamically load the DEComm package and source the morale.messages.tcl file. The messages file contains an easy way to reference the basic message types, just include the global MessageTypes. A sample Tcl/Tk program that creates a simple ACME system is::
#Sample program to demonstrate DEComm interface #to acmeServer #Make a new system and add a simple client-server system #load the dataexchange shared library #for UNIX must be DEComm.so for Windows DEComm.dll if {$tcl_platform(platform)=="windows"} { load DEComm.dll DEComm } else { load DEComm.so DEComm } source morale.messages.tcl tk_messageBox -type ok -message "Init done" #connect to the ACMEServer on infinia, port 9999 #returns channel number (0-5) if successful, -1 on fail set pok [InitDE 9999 "infinia"] if {$pok>=0} { puts "Connection Successful" } else { puts "Connection Failed!" exit } #Make a new top-level system with no parent and family type send out our channel SendDELongMessage $pok $MessageTypes(NEW) "MyNewSys" "None" "None" "" "" "" set result [GetDEMessage $pok] #return type is list element 0 set p0 [lindex $result 0] if {$p0==$MessageTypes(ACME_FAILURE)} { puts "Server System Create Failed" } #Make the 2 components SendDELongMessage $pok $MessageTypes(NEW_COMPONENT) "Client" "MyNewSys" "None" "" "" "" set result [GetDEMessage $pok] #return type is list element 0 set p0 [lindex $result 0] if {$p0==$MessageTypes(ACME_FAILURE)} { puts "Server Component Create Failed" } SendDELongMessage $pok $MessageTypes(NEW_COMPONENT) "Server" "MyNewSys" "None" "" "" "" set result [GetDEMessage $pok] #return type is list element 0 set p0 [lindex $result 0] if {$p0==$MessageTypes(ACME_FAILURE)} { puts "Server Component Create Failed" } #Put a port on each SendDELongMessage $pok $MessageTypes(NEW_PORT) "API_Call" "Server" "MyNewSys" "" "" "" set result [GetDEMessage $pok] #return type is list element 0 set p0 [lindex $result 0] if {$p0==$MessageTypes(ACME_FAILURE)} { puts "Server Port Create Failed" } SendDELongMessage $pok $MessageTypes(NEW_PORT) "API" "Client" "MyNewSys" "" "" "" set result [GetDEMessage $pok] #return type is list element 0 set p0 [lindex $result 0] if {$p0==$MessageTypes(ACME_FAILURE)} { puts "Server Port Create Failed" } #Make a Connector SendDELongMessage $pok $MessageTypes(NEW_CONNECTOR) "RPC" "MyNewSys" "None" "" "" "" set result [GetDEMessage $pok] #return type is list element 0 set p0 [lindex $result 0] if {$p0==$MessageTypes(ACME_FAILURE)} { puts "Server Connector Create Failed" } #Put the roles on the connector SendDELongMessage $pok $MessageTypes(NEW_ROLE) "Send" "RPC" "MyNewSys" "" "" "" set result [GetDEMessage $pok] #return type is list element 0 set p0 [lindex $result 0] if {$p0==$MessageTypes(ACME_FAILURE)} { puts "Server Role Create Failed" } SendDELongMessage $pok $MessageTypes(NEW_ROLE) "Receive" "RPC" "MyNewSys" "" "" "" set result [GetDEMessage $pok] #return type is list element 0 set p0 [lindex $result 0] if {$p0==$MessageTypes(ACME_FAILURE)} { puts "Server Role Create Failed" } #Now make an attachment to hook everything up SendDELongMessage $pok $MessageTypes(NEW_ATTACHMENT) "API_Call" "Server" "Receive" "RPC" "MyNewSys" "" set result [GetDEMessage $pok] #return type is list element 0 set p0 [lindex $result 0] if {$p0==$MessageTypes(ACME_FAILURE)} { puts "Server Attachment Create Failed" } SendDELongMessage $pok $MessageTypes(NEW_ATTACHMENT) "API" "Client" "Send" "RPC" "MyNewSys" "" set result [GetDEMessage $pok] #return type is list element 0 set p0 [lindex $result 0] if {$p0==$MessageTypes(ACME_FAILURE)} { puts "Server Attachment Create Failed" } #System Created now save it. #When we ask the server to save, it sends us the ASCII description #so we will have it locally. Loop until we get it all. SendDEMessage $pok $MessageTypes(SAVE) "ClntSvr.acme" "" set fileID [open "ClntSvr.acme" w] while {1} { #read a block into data set msg [GetDEMessage 0] set type [lindex $msg 0] set data [lindex $msg 1] if {$type!=$MessageTypes(SAVE)} { break } else { #save the line to a file puts $fileID $data } } close $fileID
The two message formats included with ACME Server are sufficient to handle the current range of messages, but what if you require a new format for specific types of data? Fortunately, PBIO was designed to allow complex data types like arrays, records and floats all to be passed in a platform independent manner. For really sophisticated uses of PBIO, refer to the documentation for the PBIO package.
Basically adding a new format requires the following steps:
1. In "messages.h" add the struct definition to
the list in the second part of the file. For instance,
let's say we want a message with 2 ints and a float and we
want to call it an Int2F message. We define a struct like:
typedef struct { int msgType; int param1; int param2; float param3; } int2f_msg, *int2f_msg_ptr;
2. In "messagedefs.h" now add the PBIO definitions. For a description of the details of this structure, refer to the PBIO documentation. In general, you can follow the simple structure to build new message definitions. We define a structure like:
IOField Int2F_message [] = { {"msgType","integer",sizeof(int),IOOffset(int2f_msg_ptr,msgType)}, {"param1","integer",sizeof(int),IOOffset(int2f_msg_ptr,param1)}, {"param2","integer",sizeof(int),IOOffset(int2f_msg_ptr,param2)}, {"param3","float",sizeof(float),IOOffset(int2f_msg_ptr,param3)}, {NULL,NULL,0,0} };
That's really all there is to creating a new format. The
next section will discuss how to integrate your new message into
the server.
Let's assume that you have created a new message format to
handle some application-specific behavior you need from
ACMEServer. We will use the Int2F format developed in the
previous section.
1. First we need to register the message with the
server. In file "acmeserver.cpp" go to the
ACMEServer::registerHandlers() method and add a registration call
for the message:
DExchange_register_format(_de, "Int2F message", Int2F_message);
2. Now you need to tell the server what function will handle the incoming message of this type. We do this by registering the function in the same ACMEServer::registerHandlers() function:
DExchange_register_function(_de,"Int2F message", handleInt2FMessage, NULL);
3. Now we have to implement the handleInt2FMessage that
we registered. Message handler functions are implemented in
the file "handlers.cpp". Obviously for a C
implementation, you don't need the extern "C" part of
the header. Otherwise, all your handler function signatures
should be the same. For additional information on handlers
and other call-back event handlers you can create with
dataexchange, refer to the detailed documentation for the
dataexchange package.
extern "C" int handleInt2FMessage (DExchange de, DEPort dep, int format_id, void *data, int data_length, void *client_data) { int2f_msg_ptr reply = (int2f_msg_ptr) data; //the data input param has the message in it printf ("Got a message of type Int2F with type= %i, p1= %i, p2=%i, p3=%d\n", reply->msgType, reply->param1, reply->param2, reply->param3); }
4. If you want to call a routine in the server there is a static reference to the server in the handler module, so you can call server functions from your handler by "theServer->myNewHandlerRoutine();" Of course, you would need to add the new method in the acmeserver.cpp and .h files.
5. To get info from the ACME design, you go through the
server which maintains a reference to the design representation
in _theDesignRep which allows you to call your new function
"_theDesignRep->myNewACMERoutine();"
[Next] [Table of Contents] [Previous]