Developers »
Web Services » Here
Web Services Tutorial with C++
Last revision: 2007-12-05
Target audience: developers, system administrators
In this tutorial, we will see how to use Lokad Web Services in a simple Console Application written in C++. Lokad specializes in business time-series forecasting, but this document is neither an introduction to Lokad nor an introduction to Web Services.
Download: source code for this tutorialRequirements
(*) We suggest to use the
Sandbox server because its usage is free for development purposes.
Introduction
Web Services are an industrial-strength standard currently embraced by most (if not all) major software vendors. Lokad features Web Services: all the operations that can be performed from the web application can also be performed programmatically through our Web Services. The Lokad Web Services can be reached at
In this tutorial, we will see how to use Lokad Web Services in a simple Console Application written in C++. First, we will see how proxy classes can be generated thanks to wsdl2h.exe and soapcpp2.exe. Then we will examine how to use those classes to perform the typical Lokad operation life-cycle: upload data and retrieve forecasts.
Project setup with wsdl2h.exe and soapcpp2.exe
wsdl2h.exe is a very convenient tool provided with the
gSOAP library for consuming Web Services. From a high level point of view,
wsdl2h.exe retrieves the XML description of the Web Services and generates a C++ header with declaration of proxy classes that can be included in a C++ project.

The syntax required to generate a Lokad.h proxy interface header for the Lokad Web Services is
wsdl2h.exe -NLokad -oLokad.h http://ws.lokad.com/TimeSeries.asmx?wsdl
For the sake of clarity, let's review the arguments provided to wsdl2h.exe.
-NLokad indicates the namespace that will contain the generated code.-oLokad.h defines the output file where the code should be written.- The URL specifies the Web Services to be proxied.
soapcpp2.exe is a tool provided for generating proxy implementation in C++. The implementation is based on header file generated with
wsdl2h.exe.

The syntax required to generate a proxy implementation for the Lokad Web Services is
soapcpp2.exe -C -w -x -Iimport Lokad.h
Again, let's review the arguments provided to soapcpp2.exe.
-C indicates that only client-side code will be generated.-w and -x avoid generating lots of XML sample message files.-Iimport indicates that import folder will be used for #import.- The header file specifies the Web Service interface
Let's now create a new console application from Visual Studio 2005. Go to File » New » Project » Visual C++ and choose Win32 Console Application. Remember not to check "Precompiled header" option which is checked by default.

In the newly created project, add the
Lokad.h file that you've just generated with
wsdl2h.exe and implementation files generated with
soapcpp2.exe:
soapC.cpp and
soapClient.cpp. You also need to add
stdsoap2.cpp file which is part of the
gSOAP package. Finally add following lines to
LokadWS-cpp-tutorial.cpp file:
#include "TimeSeriesSoap12.nsmap"
#include "soapTimeSeriesSoapProxy.h"
Your project should now compile.

Web Services Authentication
Lokad Web Services are authenticated, which means that you have to provide the username and password associated with your Lokad Web Account to access our Web Services. Technically, Lokad relies on SOAP headers to authenticate a Web Method call. The code provided below illustrates how you can check that your call is correctly authenticated.
#include <string>
#include <iostream>
using namespace std;
...
string username = "email@example.com";
string password = "mypassword";
_Lokad__IsAuthenticatedResponse isAuthenticatedResponse;
TimeSeriesSoap timeSeries;
if (timeSeries.__Lokad2__IsAuthenticated(NULL, &isAuthenticatedResponse) == SOAP_OK)
cout << "Is authenticated: " << (isAuthenticatedResponse.IsAuthenticatedResult ? "True" : "False") << endl;
else
soap_print_fault(timeSeries.soap, stderr);
cout << "Adding username/password." << endl;
Lokad__AuthHeader authHeader;
authHeader.UserName = &username;
authHeader.Password = &password;
struct SOAP_ENV__Header header;
header.Lokad__AuthHeader_ = &authHeader;
timeSeries.soap->header = &header;
if (timeSeries.__Lokad2__IsAuthenticated(NULL, &isAuthenticatedResponse) == SOAP_OK)
cout << "Is authenticated: " << (isAuthenticatedResponse.IsAuthenticatedResult ? "True" : "False") << endl;
else
soap_print_fault(timeSeries.soap, stderr);
Lokad__AuthHeader and
TimeSeriesSoap types have been generated by
wsdl2h.exe. The
TimeSeriesSoap class is a proxy implementation that let you seamlessly access Lokad Web Services in a typical C++ manner. The expected console output for the above code sample is:
Is authenticated: False
Adding username/password.
Is authenticated: True
Time-series management
Lokad specializes in time-series forecasting. Before actually performing a forecast, you first need to upload your data; and this can be easily achieved with our Web Services. Technically, a time-series is simply a named array of time-value pairs. Let's see how a time-series can be generated programmatically.
Lokad__TimeSerie GetSampleTimeSerie();
const string sampleTimeSerie = "MySampleTimeSerie";
...
Lokad__TimeSerie GetSampleTimeSerie()
{
int timeSerieLength = 10;
Lokad__TimeSerie serie;
serie.Name = (string*)&sampleTimeSerie;
serie.UtcOffset = -1.0;
serie.TimeValues = new Lokad__ArrayOfTimeValue;
srand((unsigned int)time(NULL));
for (int i = 0; i < timeSerieLength; i++ )
{
time_t now;
struct tm tmNow;
time(&now);
localtime_s(&tmNow, &now);
tmNow.tm_mday += i;
now = mktime(&tmNow);
Lokad__TimeValue* timeValue = new Lokad__TimeValue;
timeValue->Time = now;
timeValue->Value = rand()/(double)RAND_MAX;
serie.TimeValues->TimeValue.push_back(timeValue);
}
return serie;
}
The function
GetSampleTimeSerie() creates a time-series named
MySampleTimeSerie. Note that we specify first a
UtcOffset in the example above; this value is expressed in hours according to the formula
local time = UTC time + offset. This pattern is used to avoid implicit offset shifting when
time_t values get serialized and then deserialized by the
SoapFormatter.
Once the time-series is defined, 10 time-value pairs are added. In our sample, for simplicity, the values are regularly spaced (one day increment for each value) but this is not a requirement. The intervals between the successive values can vary arbitrarily.
Naming conventions: although the word serie (singular) does not exist in English, we have found it quite convenient to distinguish in source code serie (singular) from series (plural). So, when the term serie is used in our examples, it's not a mistake, it's on purpose.
The
GetSampleTimeSerie() method provides a time-series instance. Lets add this time-series to your Lokad account.
Lokad__TimeSerie timeSerie = GetSampleTimeSerie();
_Lokad__AddSerie addSerie;
_Lokad__AddSerieResponse addSerieResponse;
addSerie.serie = &timeSerie;
addSerie.soap = timeSeries.soap;
addSerie.soap->header = &header;
timeSeries.__Lokad2__AddSerie(&addSerie, &addSerieResponse);
To delete a time-series from your account, you just need to specify the name identifier of the time-series in question. You can also retrieve the names of the existing time-series in your account; the following snippet prints to the console the names of the time-series in your account.
for (unsigned int i=0; i < timeSerie.TimeValues->TimeValue.size(); i++)
{
Lokad__TimeValue* timeValue = timeSerie.TimeValues->TimeValue[i];
char buff[80];
struct tm time;
localtime_s(&time, &timeValue->Time);
asctime_s(buff, sizeof(buff), &time);
buff[strlen(buff)-1] = '\0';
cout << buff << " " << timeValue->Value << endl;
}
Forecasting task management
Before you can retrieve a time-series forecast, you need to define a forecasting task that will specify how the forecasted time-series should be computed. In the Lokad data model, a forecasting task is always associated with a particular time-series; several forecasting tasks can be associated with the same time-series.
Lokad__Task GetSampleTask();
const string sampleTask = "MySampleTask";
...
Lokad__Task GetSampleTask()
{
Lokad__Task task;
task.Name = (string*)&sampleTask;
task.Aggregator = Lokad__Aggregator__Sum;
task.Period = Lokad__Period__Day;
task.FuturePeriods = 3;
return task;
}
Tip: the time-series names should be unique within the scope of your Lokad account (you cannot have two distinct time-series identified by the same name). However, the names of the forecasting tasks only need to be unique within the scope of their base time-series. In other words, you can have several forecasting tasks identified by the same name but associated with different time-series.
You can use the
TimeSeriesSoap class to add the newly created
Lokad__Task instance to your Lokad account.
Lokad__Task task = GetSampleTask();
_Lokad__AddTask addTask;
_Lokad__AddTaskResponse addTaskResponse;
addTask.serieName = timeSerie.Name;
addTask.task = &task;
addTask.soap = timeSeries.soap;
addTask.soap->header = &header;
timeSeries.__Lokad2__AddTask(&addTask, &addTaskResponse);
Retrieving forecasts
To retrieve your forecasts through our Web Services, you need to activate the Enterprise subscription plan in your Lokad account. Note that you won't be charged upfront, but only at the end of each 30-day period (if you decide to continue with your subscription). Time-series forecasts can be retrieved using the
TimeSeries class.
_Lokad__GetForecast forecast;
_Lokad__GetForecastResponse forecastResponse;
forecast.soap = timeSeries.soap;
forecast.serieName = timeSerie.Name;
forecast.taskName = task.Name;
forecast.soap->header = &header;
if (timeSeries.__Lokad2__GetForecast(&forecast, &forecastResponse) != SOAP_OK)
{
soap_print_fault(forecastResponse.soap, stderr);
}
else
{
Lokad__ArrayOfTimeValue* timeValues = forecastResponse.GetForecastResult->TimeValues;
for(unsigned int i=0; i<timeValues->TimeValue.size(); i++)
{
Lokad__TimeValue timeValue = *timeValues->TimeValue[i];
char buff[80];
struct tm time;
localtime_s(&time, &timeValue.Time);
asctime_s(buff, sizeof(buff), &time);
buff[strlen(buff)-1] = '\0';
cout << buff << " " << timeValue.Value << endl;
}
}
As you can see in the Console Output below, 3 time-value pairs are actually returned, matching the
Task.FuturePeriods property value specified in the previous section.
