Call us: +1 (716) 989 6531 or email at:

Forecasting Software for sales, demand and call volumes

RSS RSS

Navigation





Search the wiki
»

PoweredBy

Web Services Tutorial with C++ for Time-Series Forecasting

RSS
Developers » Web Services » Here

Deprecated content. Please refer to the Programmers Guide to Forecasting API v3 instead.

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 tutorial

Requirements
(*) We suggest to use the sandbox 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.

Image

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.

Image

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.

Image

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.

Image

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.

Image

Google Code

All add-ons developed by Lokad have been released as open source under the BSD license on Google Code.

The BSD license is compatible with closed-source commercial applications. Integrating a forecasting technology has never been easier.

What people say

From a developer standpoint, we found that the Lokad API was very well documented and easy to use. The Lokad team was also extremely responsive to any questions we had. Most importantly, we were able to develop a Lokad plugin for our data browser in very short order. Jeff Engel, Kirix Corporation