12.12 The WarpEngine Technology

 

Main topics:

 

12.12.1 Introduction
 

12.12.2 The graphic user interface

12.12.2.1 The server personality

12.12.2.2 The client personality

12.12.3 WarpEngine pre-installed projects

12.12.3.1 Test projects

12.12.3.2 MOPAC - Generic calculation

12.12.4 Developing a new WarpEngine application

12.12.4.1 Global variables

12.12.4.2 Events managed through the server script

12.12.4.3 Events managed through the client script

12.12.4.4 WarpEngine APIs

12.12.4.5 Macros

12.12.4.6 Minimalist server code
12.12.4.7 Minimalist client code
 

 

12.12.1 Introduction

The collaborative computing term includes technologies and informatics resources based on a network communication system that allows the documents and projects to be shared between users. All activities can be managed by a variety of devices such as desktops, laptops, tablets and smartphones. In a computational chemistry laboratory, one of the most common need of a researcher is not only to share information and data between the collaborators, but also computational resources for ab-initio calculations, semi-empirical calculations, MD simulations, virtual screening, data mining, etc.

The typical scenario of a computational chemistry laboratory is shown in the following scheme:

 


Typical scenario of a computational chemistry laboratory

 


Several workstations are connected each other by network devices granting the access to local resources, provided by one or more servers, and to Internet trough a firewall. Therefore, the global computation power given by all PCs taken together is very high, but is "fragmented" on the net, making hard the use to run a single complex calculation. Moreover, the situation is more complex by because the heterogeneity of hardware and operating systems.

To overcome these problems, WarpEngine was developed whose main features are summarized here:

WarpEngine is a client/server-based technology implemented in C++ language as part of the PowerNet plug-in and, in other words, is integral part of VEGA ZZ. The same software can run as server or client in both LAN and WAN (with small limitations in this second case for obvious security problems).

The flowchart of the server side is depicted here:

 

 

 

Project manager handles the user-defined calculation projects (usually stored in ...\VEGA ZZ\Data\WarpEngine directory) even more than one at the same time, while Job manager distributes the jobs to the clients through Client manager whose function is to watch the client activities according to user-defined policies. Moreover, Client manager can undertake certain actions automatically in some critical situations such as the client disconnection when an abnormal client activity is detected. The connection to the clients is implemented by TCP/IP protocol (more in detail, by HTTP server included in PowerNet plug-in) and  protected by the IP filter and, optionally, by an encrypted tunnel. UDP server is used to detect and recruit the clients in the local network.

Project manager is one of the most important module at the server-side, as shown in the following scheme:

 

Project managerat server-side

 

It does several important actions such as:

The client-side architecture is complementary if compared to that of the server-side as shown below:

 

Client flowchart

 

UDP client and HTTP client manage the communication with the server, Project manager handles the calculation project and controls the Multithreaded worker that executes the real calculation with the support of VEGA ZZ core that offers high-level functions to simplify the implementation. In particular, UDP client allows the servers to be detected in the local network without the need to specify their address and HTTP client is used to transfer the input/output data and send/receive control messages through GET/POST HTTP methods.

 

Project manager at client-side

 

When the client connects itself to the server, the XML file defining the project is downloaded and parsed. Hence, other additional files are downloaded according to the content of the project file such as the client script, data files and, optionally, program executables. After the download phase, the client script is built and executed as Client module that includes additional functions for the Multithreaded worker for the specific calculation. The Client module defines the actions to perform the calculation such as the download of the input data, the data processing and the upload of the output.

 

 

12.12.2 The graphic user interface

As explained above, VEGA ZZ includes both client and serve code, so when you  start WarpEngine you can decide its running personality. If you want to test locally some applications, you can run two WarpEngine instances from two different VEGA ZZ on the same node, the former with the server personality and the latter with the client one.

 

12.12.2.1 The server personality

To start WarpEngine in server mode to setup a new calculation, you must select File WarpEngine Server: If your operating system is Windows and the User Account Control (UAC) is enabled, administrative rights are required. If  they aren't available, VEGA ZZ asks you to restart the program to obtain the rights.

 

WarpEngine server - Projects tab

 

The main window show all available projects (see below to add a new one). In the Project tab, you can select the projects that you want to run. Although WarpEngine allows more than one project to be executed at the same time, there are calculations that needs the exclusive use of the resources and, for this reason, can be executed alone. For each project, the identification number (ID), the short description (Name), the status of the calculation (Status), the number of errors occurred during the calculation (Errors), the elapsed time to end the calculation (ETA) and the time spent by the calculation (Time) are shown as table. Status, Errors, ETA and Time are updated in real time when the project is running. In the Summary of projects box are shown the main information on the projects: the number of available projects (Available), the number of enabled/checked projects (Enabled) and the number of completed projects (Completed).

Clicking the Start button, the selected projects are executed. Additional data could be requested to the user according to the server script managing the calculation. For example, in same cases you must select the database to process or put some parameters, in other cases a full graphic interface is shown for the set-up of the calculation (e.g. PLANTS). When the set-up phase is completed, the server is ready for the incoming connection and you can minimize it on the system tray bar clicking Hide button.

 

WarpEngine minimized on the system tray

 

Clicking the WarpEngine icon on the tray bar with the right mouse button, you can show its menu:

 

WarpEngine server menu

whose functions are summarized in the following table:

 

Menu item Description
Monitor off Switch off the LCD monitor.
Show "Don't power off" Show a large "WarpEngine - Don't power off" message on the desktop to prevent accidental power off by other users. You can hide the message double clicking it.
Show Show VEGA ZZ and WarpEngine windows.
Quit Stop WarpEngine server and close the program. A dialog box is shown to confirm the action.

To stop the server without closing the program, you can click the Stop button: the action must be confirmed by a dialog box.

The Clients tab shows the connected clients, their status and some statistics about their activity:

 

WarpEngine server - Clients tab

 

More in detail, for each client the identification code (ID), the host name (Name), the IP address (IP), the ID of the project on which the client is working (PID), the session ID (SID) used to identify different WarpEngine instances running on the same client, the number of working threads (Threads), the number of completed jobs (Jobs), the calculation speed expressed as number of jobs per minute (Speed (jobs/min)), the number or recoverable errors occurred, the time passed from the first contact (Uptime) and the hour of the last client contact (Last contact) are shown. Moreover, global information is reported at the bottom box: the number of active clients (Active clients), the number of completed jobs (Completed jobs), the total number of thread running at the same time (Total threads), the total number of jobs required to complete the calculation (Total jobs), the average number of jobs processed by each client (Average jobs/client) and the average number of jobs completed by each thread (Average jobs/thread).

After five consecutive errors, the client is automatically disconnected from the server and you can decide to disconnect manually the client by the context menu. By this menu, you can also recruit all client of the local network that are in waiting status. In this way, you don't need to connect manually each client to the server.

In Performances tab, you can monitor the performances of the calculation system in real time:

 

WarpEngine server - Performances tab

 

Here, you can check the average speed, the maximum average speed, the current speed (all these quantities are expressed as number of completed jobs per minute) and the CPU load of the server (as percentage).

 

12.12.2.2 The client personality

As for the server mode, to start WarpEngine with the client personality, you must select File WarpEngine Client. Also in this case, administrative rights are required. You can decide to start the client and to connect it manually to a specific server or leave it in a waiting status to be recruited by a server trough a broadcast message. This second operative mode is available only if the server is in the same network of the client or the client can access to the same local network through a Virtual Private Network (VPN).

 

WarpEngine Client

 

The servers are automatically detected if they are in the local network (Server column). Alternatively, they can be added manually clicking the Add button or choosing the Add item in the context menu:

 

Add a new server

 

In this dialog window, you can specify the server name (Name, used only as alias), the IP address (Address) and the communication port (Port). Clicking Add, you can add the server to the list. The server is not saved and needs to be added every time that you need it.

Before to add the client to the calculation pool, you can choose the action performed at the end of the calculation: None (nothing is done), Close VEGA ZZ (closes the program) and Shutdown the system (power off the system).

To connect the client to the server and run the calculation, you must select the server and choose the project, thus click the Run button. The program is automatically minimized on the system tray showing a notification. You can minimize the client on the system tray without attaching it to a server just clicking the Hide button. In this way, the client remains in waiting mode and can be recruited by the server.

 

WarpEngine minimized on the system tray

 

As for the WarpEngine server, if you click the icon on the system tray, you can show the control menu:

 

WarpEngine client menu

 

In addition, you can change the action performed at the end of the calculation, selecting the End action submenu as explained above.

 

12.12.3 WarpEngine pre-installed projects

VEGA ZZ package includes WarpEngine projects ready-to-use: some of them are for test purposes, while others are specific for intensive calculations.

 

 

12.12.3.1 Test projects

These projects named with TEST prefix are suitable to test the network configuration, to check the client/server communication and to evaluate the transfer performances.

 

Project name Description
TEST - Communication test This is a simple stress-test to check the client/server communication in extreme conditions. When you start the server, a file requester is shown to choose the output file name that will be in CSV format, thus the project asks you if you want to switch off all messages shown in VEGA ZZ console. This function is useful to reduce the latencies an test the full power of the network connection.

The server send a packet of data to each client and collect the answers in the output CSV file. The test is repeated 10000 times and you can evaluate the speed in the Performances tab of the server.

The output file includes four columns: the job number (JOBID, from 1 to 10000), the identification number of the client (CLIENTID), the identification number of the thread processing the message (THREADID, from 0 to n, where n is the maximum number of cores/threads minus 1 manageable by the client) and a progressive number as returning message (RESULT).

TEST - Database test Also this project is a stress test to check if the database functions implemented in WarpEngine work properly. It requires a SQL database of molecules in any format supported by VEGA ZZ (for more information, click here). The server asks you also for the output file name that will be written in CSV format.

The server send a molecule to the client and receive a confirm message. If the operation is successfully completed the output file is updated. It includes two columns: the identification number of the molecule (JOBID) and the molecule name (MOLECULE).

TEST - Database sort test This project is quite similar if compared to the previous one, but the main difference consists of how the output is written. The client downloads a molecule from the server, calculates its mass and returns this value as answer. The server sorts the results by mass and periodically (every 60 seconds) writes the output file. This file includes three columns: the job identification number (JOBID), the molecule name (MOLECULE) and the mass (MASS).

 

12.12.3.2 MOPAC - Generic calculation

By this project, you can perform MOPAC calculations on molecules included into a database. MOPAC 7.01-4, that is included in VEGA ZZ package, is used by default but if a newer release is installed (click here for the installation guide), it is automatically detected and used. Don't mix different MOPAC versions: all clients must have the same MOPAC release.

When you start the server, you can select the input database including the molecules to process, the output CSV file in which the parameters calculated for each molecule are stored  and the keywords required by MOPAC. The pre-defined keywords are AM1 PRECISE GEO-OK, while  CHARGE keyword is not required is  because the total charge is calculated automatically.

 

MOPAC keywords

 

A new database is also created with the same format of the input database and with the same name of the CSV file adding - Mopac suffix to store the structures optimized by MOPAC.

If any molecule is skipped during the calculation, a CSV file with the same name of the output file is created with skipped suffix in which the job identification code (JOBID) and the molecule name (MOLECULE) of the molecule with errors is saved.

If you stop the calculation accidentally or a heavy problem occurs to  the server, you can restart the calculation just starting the server and setting up the calculation with the same parameters and files (including the output).

 

MOPAC restart

 

When you confirm to overwrite the output file a requester is shown to ask you if you want to restart the calculation.

The CSV output file includes the first two columns that are common to all calculation types: the identification number of the job (JOBID) and the molecule name (MOLECULE).  Additional columns are added according to the specified MOPAC keywords and the relative values are retrieved from .arc files. If you add SUPER as keyword, the superdelocalizability data is read from the MOPAC output file.

 

 

12.12.3.3 PLANTS - Generic docking

WarpEngine allows large scale virtual screening campaign to be performed in easy way by PLANTS software. This program must be installed at least on the server because the PLANTS server sends not only the data required for the calculation (protein target, control file and ligands to dock) but also the executable of the docking program. The executable is digitally signed in order to avoid the injection of malicious programs. To install PLANTS, you must follow the steps shown in the activation and installation section.

When you start WarpEngine with the server personality selecting PLANTS - Generic docking as project, a dialog to setup the calculation is shown:

 

PLANTS project setup

 

The setup procedure is quite similar to that for the normal PLANTS docking. More in detail, you can set:

Clicking the Start button, the server is initialized and waits for incoming connections. As for MOPAC, If the output CSV file is already present by because a previous calculation was performed without finishing it, a dialog window asks you if you want restart the calculation or repeat it from the beginning.

 

 

12.12.4 Developing a new WarpEngine application

The general purpose WarpEngine core can be adapted to specific calculations through server and client scripts. As explained above, they must be written in C-Script language that allows the direct access to WarpEngine and HyperDrive APIs.

The WarpEngine files are located in ...\VEGA ZZ\Data\WarpEngine directory whose full path changes on the basis of operating system version. To find it in easy way, you can type OpenDataDir in VEGA Console (command prompt) or select Help Explore data directory in  VEGA ZZ menu bar.

Here is the typical directory tree:

 

VEGA ZZ
  Config
    Data
      WarpEngine
        Projects
          Project_1
        Project_2
        Project_n
      Templates
        Template_1
        Template_2
        Template_n
  Scripts

 

The Projects directory includes the data for each project that must be placed in sub-directory (e.g. Project_1), while Templates directory contains the auxiliary files that can be used without changes in different projects.

In each project directory, project.xml file must be present to describe the calculation and the following scheme shows the meaning of each XML tag:

 

Configuration file

 

Description


<weproject version="1.0" enabled="0"
multithread="1" runalone="1">
     

Main tag:

  • version Version number of the configuration file.

  • enabled If this flag is set to 1, the project will appears already activated when you open the project manager.

  • multithread This flag set to 1 means that the calculation uses the maximum number of threads available for each node.

  • runalone If it is set to 1, this project can be executed only alone, otherwise (set to 0) the project can run together the other ones.


  <startuplogo
  file="%WEPRJECTSDIR%/Project_1/logo.png"
  delay="2000" fadespeed="250">
 

Splash logo shown at start-up (optional):

  • file Full path file name of the logo in PNG format. Transparencies (alpha channel) are supported.

  • delay Time in ms during which the logo is shown.

  • fadespeed Time in ms for fade-in and fade-out transition of the logo (0 = appears and disappears immediately).


  <name lang="english">Project title</name>
 

Project name. This tag supports the language localization by lang option and therefore it can be present one time fore each language that you want to support:

  • lang localization language (e.g. dansk, deutsch, english, espa˝ol, franšais, italiano, etc).


  <description lang="english">
    Description of the project
  </description>
 

Description of the project. Also in this case, the description can be localized by lang option:

  • lang localization language (e.g. dansk, deutsch, english, espa˝ol, franšais, italiano, etc).


  <arch>ALL</arch>
 

Type of hardware architectures supported by the project: ALL (= all supported by WarpEngine), LINUX_ARM, LINUX_X64, LINUX_X86, WIN_ARM, WIN_X64 and WIN_X86.


  <client src="%WETEMPLATEDIR%/Template_1/client.c"/>
  Client script (optional). It must be specified if the client script is not in the project directory:
  • src full file path of the script

  <server src="%WETEMPLATEDIR%/Template_1/server.c"/>
  Same of above but for the server.

  <cliramdisk enabled="1" basesize="20" threadsize="1"/>
 

Client ram disk used for the temporary files. It is a block of ram used as disk drive to improve the I/O performances and to preserve the integrity of drives that are sensible to continuous read/write operations such as SSDs. The ram disk is provided by ImDisk driver that must be installed on the client.

  • enabled enable (1) / disable (0) the ram disk at client side.

  • basesize size in Mbytes of ram reserved for the disk.

  • threadsize increase in Mbytes of the total amount for each thread. For example, if you set basesize="20", threadsize="1" and you have 4 working thread in the calculation node, the total amount of memory allocated for the ram disk will be 24 Mbytes.

WARNING:

If ImDisk is not installed, the calculation runs anyway and the temporary files are created in the system disk. A warning message is shown.


  <srvramdisk enabled="1" size="20"/>
 

It is the same of above, but for the server side.

  • enabled enable (1) / disable (0) the ram disk.

  • size size in Mbytes of ram reserved for the disk.


  <download>
  Begin of the download section in which the files needed by the client must be specified.

    <file src="client.c"
    map="%WETEMPLATEDIR%/Template_1/client.c"
    unzip="0"/>
    <file src="data.zip"
    map="%WETEMPLATEDIR%/Template_1/data.zip"
    dest="%WorkDir%" unzip="1"/>
 

File to download:

  • dest is the destination directory in which the file will be downloaded.

  • map this options maps the file into the virtual file system of the HTTP server.

  • src file name to download. If you don't specify the path, you imply that the file must be in the project directory.

  • unzip if you set to 1 this flag and the file is a zip archive, it will be automatically uncompressed by the client after the download.

 


  </download>
 

End of download section.



</weproject>
 

End of the project configuration file.


The empty project (see ...\Projects\EMPTY directory) is a good starting point to build a new calculation project. It includes a pre-defined project.xml file in which you can define the main parameters as explained above and client.c and server.c scripts. Both scripts supports the language localization, includes different functions called when a specific event occurs and use extensively the HyperDrive library.

To modify these scripts, you must have basic skills in C programming (C-Scripts are fully C-99 compliant).

 

 

12.12.4.1 Global variables

Both client and server scripts can access to global variables declared in .../VEGA ZZ/Tcc/include/vega/vgplugin.h, .../VEGA ZZ/Tcc/include/vega/wengine.h and .../VEGA ZZ/Tcc/include/vega/wetypes.h

 

 

12.12.4.2 Events managed through the server script

This script implements the actions given as answer for a specific event at server-side. In the following flowchart are summarized the event handled by the server script:

 

Event managed by the server script

 

The system and the client requests are dispatched by the event handler calling the specific functions of the server script that implement user-defined actions. Each event function return a code to inform the Event handler if the action successes or not.

More in detail, the event are managed by the following functions:

 

 

12.12.4.3 Events managed through the client script

Also the client script can handle several events that are depicted in the following scheme:

 

Event managed by the client script

 

In particular, here is the description of each event function:

 

12.12.4.4 WarpEngine APIs

The prototypes of WarpEngine APIs are defined in .../VEGA ZZ/Tcc/include/vega/wengine.h header.

 

 

APIs for both client and server scripts:

 

 

 

APIs only for client scripts:

 

 

 

12.12.4.5 Macros

Several macros are defined in .../VEGA ZZ/Tcc/include/vega/wengine.h header in oder to help you in script programming.

 

HD_DLONG a = 10;

printf("64 bit integer: %" _FMT64 "d\n", a);
HD_ATOM *Atm;

for(Atm = VegaFisrtAtom; Atm; Atm = Atm -> Ptr) {

  /* Put here your code */

}

 

12.12.4.6 Minimalist server code

This section shows the minimalist server script that you can use to process a database of molecules. It is a simplified version of EMPTY project

/*********************************
**** WarpEngine Server Script ****
*********************************/

/* Save this file as ...\VEGA ZZ\Data\WarpEngine\Projects\Example\server.c */

/* WarpEngine header */

#include <wengine.h>

/* HyperDrive headers */

#include <hdargs.h>
#include <hddir.h>
#include <hdtime.h>

/* Standard C headers */

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

/**** Global variables ****/

HD_BOOL         Running    = TRUE;       // Flag to control the server activity (FALSE = stop it, boolean variable)
HD_BYTE *       StatVect   = NULL;       // Vector to monitor the job activities (see OnGetComplexity(), byte pointer)
HD_STRING       hDbIn      = NULL;       // Handle of the database to process (HyperDrive string)
HD_STRING       DbInName   = NULL;       // Name of the database to process (HyperDrive string)
HD_UDLONG       NumJob     = 0LL;        // Number of jobs to complete (64 bit unsigned integer)


/**** Show an error ****/

HD_VOID Error(const HD_CHAR *Err)
{
  /* Uses VEGA ZZ MessageBox to show the error */

  VegaCmdSafe(NULL, "MessageBox \"%s.\" \"ERROR\" 16", Err);
}


/**** Destroy event ****/

HD_BOOL OnDestroy(HD_VOID)
{
  Running = FALSE;                                        // Set the run flag to FALSE

  /*
   * Put here your code
   */

  /* Free the resources */

  HD_FreeSafe(StatVect);                                  // Free the vector to monitor the jobs
  HD_StrFree(DbInName);                                   // Free the HyperDrive string of database name

  /* Close the database */

  if (!HD_StrIsEmpty(hDbIn)) {                            // Check if the handle is not empty
    VegaCmdSafe(NULL, "DbLock %a 0", hDbIn);              // Unlock the database
    VegaCmdSafe(NULL, "DbClose %a", hDbIn);               // Close the database
  }

  /* Print into VEGA ZZ console */

  VegaPrint("* Server terminated\n");

  return TRUE;
}


/**** Get the complexity ****/

HD_BYTE * OnGetComplexity(HD_UDLONG *JobTot)
{
  HD_STRING      Res = HD_StrNew();                       // Create a new HyperDrive string variable

  /* Ask to VEGA how many molecules are in the database */

  if (!VegaCmdSafe(Res, "DbInfo %a Molecules", hDbIn)) {  // If it falis,
    Error("Unable to get the number of molecules");       // the error message is shown,
    HD_StrFree(Res);                                      // the Res string is freed 
    return NULL;                                          // and the function return a NULL pointer (error condition)
  }

  /* Convert the string to a 64 bit unsigned integer */

  NumJob = HD_Str2ULint(Res);
  HD_StrFree(Res);                                        // Res is no more needed, so it is freed

  if (NumJob == 0LL) {                                    // If NumJob is 0 (the database is empty),
    Error("Empty database");                              // the error message is shown
    return NULL;                                          // and the function return a NULL pointer (error condition)
  }

  VegaPrint("* Molecules in database: %llu\n", NumJob);   // Print how many molecules are in the database

  /**** Allocate the byte vector to track the jobs ****/

  StatVect = (HD_BYTE *)HD_Calloc(NumJob);                // HD_Calloc() initilizes the vectot to 0
  if (StatVect == NULL) {                                 // If the vector pointer is NULL,
    Error("Out of memory");                               // the error message is shown
    return NULL;                                          // and the function return a NULL pointer (error condition)
  }

  *JobTot = NumJob;                                       // Put the number of jobs

 /*
  * Put here the user code
  */

  /* Show waiting message */

  VegaPrint("* Waiting for incoming connections ...\n\n");

  /* Return the job vector poiner */

  return StatVect;
}


/**** Get the data for the job ****/

HD_BOOL OnGetData(HD_STRLIST StrList, HD_UDLONG JobID, HD_ULONG SrvIP)
{
  HD_STRING      IpStr = HD_StrNew();                     // Create new HyperDrive string variables
  HD_STRING      DbURL = HD_StrNew();

  /* Convert the IP V4 address to string */

  HD_StrFromIPV4(IpStr, SrvIP);

  /* Create the URL to get the molecule */

  HD_StrPrintC(DbURL, "http://%a:%d/dbase.ebd?Cmd=getmol&#38;Db=%a&#38;RowID=%llu",
               IpStr,                                     // Server IP address
               *WeInfo -> SrvPort,                        // Server IP port
               DbInName,                                  // Database name
               JobID);                                    // Job ID = Molecule ID to download

  /* Put the URL string in the the answer list */

  HD_StrListAdd(StrList, DbURL);

  /* Free the resources */

  HD_StrFree(IpStr);
  HD_StrFree(DbURL);

  /* Show the message */

  VegaPrint("* Sending job %llu\n", JobID);

  return TRUE;
}


/**** Initialization code ****/

HD_BOOL OnInit(HD_VOID)
{
  /* Starting message */

  VegaPrint("\n**** WarpEngine Server Example ****\n\n");

  /* Ask for the input database */

  DbInName = HD_StrNew();
  VegaCmdSafe(DbInName, "OpenDlg \"Open input database ...\" \"\" \"All supported databases|*.accdb;*.db;*.dsn;*.mdb;*.sdf\" 1");
  if (HD_StrIsEmpty(DbInName))                            // If the database name is empty (cancel or no selection),
    return FALSE;                                         // the function return FALSE, forcing the exit

  /* Open the database */

  hDbIn = HD_StrNew();                                    // Create a new string variable in which to put the database handle
  VegaCmdSafe(hDbIn, "DbOpen \"%a\"", DbInName);          // Open the database and put in hDbIn the handle 
  if (*hDbIn -> Cstr == '0') {                            // If the database handle is 0,
    Error("Can't open the database");                     // the error message is shown
    return FALSE;                                         // and the function return FALSE, forcing the exit
  }

  /* Remove the path from the database file name */ 

  HD_StrFileName(DbInName);

  /* Lock the database to prevent the accidental close */

  VegaCmdSafe(NULL, "DbLock %a 1", hDbIn);

  /*
   * Put here other initialization code
   */

  VegaPrint("* Server initialized\n");

  return TRUE;
}


/**** Receive the data from the client ****/

HD_BOOL OnPutData(HD_STRLIST StrList, HD_UDLONG JobID, HD_LONG JobStatus,
                  HD_VOID *Data, HD_ULONG DataSize)
{
  HD_STRING       Line     = HD_StrNew();                 // Create new HyperDrive string variables
  HD_STRING       Molecule = HD_StrNew();

  /* Get the molecule name from database */

  VegaCmdSafe(Molecule, "DbMolName %a %llu", hDbIn, JobID - 1LL);

  if (HD_StrIsEmpty(Molecule))                            // If the molecule name is unknown,
    HD_StrCpyC(Molecule, "Unknown");                      // set Molecule to "Unknown"
  else                                                    // otherwise
    HD_StrTrimRight(Molecule);                            // trim the string removing spacing and tabs

  /*
   * Put here the code to manage the results
   */

  VegaPrint("* Results from job %llu (%a) received\n", JobID, Molecule);

  /* Free the resources */

  HD_StrFree(Line);
  HD_StrFree(Molecule);

  return Running;
}

 

12.12.4.7 Minimalist client code

This section shows the minimalist client script that is a simplified version of EMPTY project

/*********************************
**** WarpEngine Client Script ****
*********************************/

/* Save this file as ...\VEGA ZZ\Data\WarpEngine\Projects\Example\client.c */

/* WarpEngine header */

#include <wengine.h>

/* HyperDrive headers */

#include <hdsocket.h>
#include <hdtime.h>

/* MXML header */

#include <mxml.h>

/* Standard C headers */

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

/**** Global variables ****/

HD_MUTEX        MtxVega    = NULL;       // HyperDrive mutex handle for exclusive access to VEGA ZZ resources (handle)
HD_UDLONG       JobCount   = 0LL;        // Job counter variable (64 bit unsigned integer)
HD_ULONG        Threads    = 0;          // Number of threads (32 bit unsigned integer)


/**** Show an error ****/

HD_VOID Error(const HD_CHAR *Err)
{
  /* Uses VEGA ZZ MessageBox to show the error */

  VegaCmdSafe(NULL, "MessageBox \"%s.\" \"ERROR\" 16", Err);
}


/**** Destroy event ****/

HD_BOOL OnDestroy(HD_VOID)
{
  HD_MthCloseMutexEx(MtxVega);          // Close the mutex

  /*
   * Put here your code
   */

  return TRUE;
}


/**** Initialization code ****/

HD_BOOL OnInit(HD_VOID)
{
  /* Starting message */

  VegaPrint("\n**** WarpEngine Client Example ****\n\n");

  /* Create the mutex */

  MtxVega = HD_MthCreateMutexEx();

  /*
   * Put here other initialization code
   */

  /* Set the client status to running */ 

  WecRunSignal(TRUE);

  /* Show the message that the initialization is OK */ 

  VegaPrint("* Client initialized\n");

  return TRUE;
}


/**** Run the job ****/

/*
 * This function is called by each thread whose number depends
 * on the number of cores and is the hyper-threading is available.
 * For example, if the client node has 2 CPUs with 8 cores each and
 * hyper-threading enabled (we assume 2 threads for each core),
 * there are:
 * 2 * 8 * 2 = 16 threads
 * that run concurrently OnRun()
 */

HD_BOOL OnRun(HD_ULONG ThreadID, HD_ULONG ClientID)
{
  HD_HTTPCLIENT           DataClient;                     // Handle of HyperDrive HTTP client
  HD_ULONG                JobID;                          // ID of the running job
  mxml_node_t *           Node;                           // Node pointer of XML structure
  mxml_node_t *           Tree;                           // Tree pointer of XML structure

  HD_STRING               Data = HD_StrNew();

  /* Trace the number of threads */

  HD_MthMutexOnEx(MtxVega);                               // The mutex activation allows the exclusive access to code (only one thread at time)
  ++Threads;                                              // Increment the total number of threads
  HD_MthMutexOffEx(MtxVega);                              // Disable the exclusive access of the code 

  /* Create a new HTTP client */

  DataClient = HD_HttpClientNew();
  if (DataClient == NULL)                                 // If it is impossible to do it,
    goto ExitErr;                                         // jump to ExitErr release the resources


  /* Set the URL (Data) to get the data for the job */

  HD_StrPrintC(Data, "http://%a:%d/getdata.ebd?ID=%u&ProjectID=%u",
               WeInfo -> Server,                          // IP address of the server
               *WeInfo -> SrvPort,                        // Port of the server
               ClientID,                                  // Client ID 
               *WeInfo -> PrjID);                         // Project ID

  if (!HD_HttpClientParseUrl(DataClient, Data))           // Parse the URL, but if it fails,
    goto ExitErr;                                         // jump to ExitErr to release the resources

  /* Here is the main loop for calculation */

  while(WecIsRunning()) {                                 // Check if the status is "run"

    /* Get the data for the job */

    HD_HttpClientGetStr(DataClient, Data);                // Download from the server the XML data of the job
    if (HD_StrIsEmpty(Data))                              // If Data is empty (an error is occurred),
      break;                                              // the main loop exits

    /* Parse the XML data */

    Tree = WeDecodeXmlReply(Data, NULL, NULL);            // Obtain the pointer of the XML tree
    if (Tree != NULL) {                                   // If the XML data is consistent ...

      /* Decode the data for the job */

      JobID = 0;                                          // Initialize the variables
      Node  = Tree;
      while ((Node = mxmlFindElement(Node, Tree, "data", "id", NULL, MXML_DESCEND)) != NULL) {
        if (atoi(mxmlElementGetAttr(Node, "id")) == 1) {
          sscanf(Node -> child -> value.opaque, "%u", &JobID);
          break;
        }
      } /* End of while */
      mxmlDelete(Tree);                                   // Free the XML tree

      /* Check if the calculation is finish */

      if (!JobID) break;

      /* Show the progress */

      VegaPrint("* Running job %u\n", JobID);

      /*
       * Put here your code to perform the calculation
       */

      /* Send the results */

      HD_MthMutexOnEx(MtxVega);
      HD_StrPrintC(Data, "&CLIENTID=%u&THREADID=%u&RESULT=%llu", ClientID, ThreadID, ++JobCount);
      HD_MthMutexOffEx(MtxVega);
      if (!WecSendData(ClientID, JobID, WE_JOBST_DONE, Data)) break;
    }
  } /* End of while */

  /* Release the resources */

ExitErr:

  HD_HttpClientFree(DataClient);                          // Free the HTTP client
  HD_StrFree(Data);                                       // Free the Data string

  HD_MthMutexOnEx(MtxVega);                               // Enable the exclusive access to the code
  --Threads;                                              // Decrease the number of working threads
  if (Threads == 0)                                       // If there are no more working threads,
    VegaPrint("* Calculation terminated\n");              // the calculation is terminated
  HD_MthMutexOffEx(MtxVega);                              // Disable the exclusive access to the code

  return TRUE;
}


/**** Terminate event ****/

HD_BOOL OnTerminate(HD_VOID)
{
  WecRunSignal(FALSE);                                    // Set the client status to stop

  return TRUE;
}