BigW Consortium Gitlab

Commit f94500f6 by Zahid Chowdhury

Add locationService to use GPS if available & accurate, otherwise use wifi with…

Add locationService to use GPS if available & accurate, otherwise use wifi with Combain service to get location
parent a0e5b5c6
#include <curl/curl.h>
#include "CombainHttp.h"
#include "legato.h"
#include "interfaces.h"
static ThreadSafeQueue<std::tuple<ma_combainLocation_LocReqHandleRef_t, std::string>> *RequestJson;
static ThreadSafeQueue<std::tuple<ma_combainLocation_LocReqHandleRef_t, std::string>> *ResponseJson;
static le_event_Id_t ResponseAvailableEvent;
static struct ReceiveBuffer
{
size_t used;
uint8_t data[1024];
} HttpReceiveBuffer;
void CombainHttpInit(
ThreadSafeQueue<std::tuple<ma_combainLocation_LocReqHandleRef_t, std::string>> *requestJson,
ThreadSafeQueue<std::tuple<ma_combainLocation_LocReqHandleRef_t, std::string>> *responseJson,
le_event_Id_t responseAvailableEvent)
{
RequestJson = requestJson;
ResponseJson = responseJson;
ResponseAvailableEvent = responseAvailableEvent;
CURLcode res = curl_global_init(CURL_GLOBAL_ALL);
LE_ASSERT(res == 0);
}
void CombainHttpDeinit(void)
{
curl_global_cleanup();
}
static size_t WriteMemCallback(void *contents, size_t size, size_t nmemb, void *userp)
{
auto b = reinterpret_cast<ReceiveBuffer *>(userp);
const size_t numBytes = nmemb * size;
const size_t available = sizeof(b->data) - (b->used + 1);
const size_t numToCopy = std::min(available, numBytes);
memcpy(&b->data[b->used], contents, numToCopy);
b->used += numToCopy;
b->data[b->used] = 0;
return numToCopy;
}
void *CombainHttpThreadFunc(void *context)
{
le_cfg_ConnectService();
char combainApiKey[32];
const le_result_t cfgRes = le_cfg_QuickGetString(
"/ApiKey", combainApiKey, sizeof(combainApiKey) - 1, "");
LE_FATAL_IF(
cfgRes != LE_OK || combainApiKey[0] == '\0',
"Failed to read Combain API Key from config tree");
char combainUrl[128] = "https://cps.combain.com?key=";
strncat(combainUrl, combainApiKey, sizeof(combainUrl) - (1 + strlen(combainUrl)));
do {
auto t = RequestJson->dequeue();
// Bug existed as we kept tacking onto the existing buffer
HttpReceiveBuffer.used = 0;
HttpReceiveBuffer.data[0] = 0;
ma_combainLocation_LocReqHandleRef_t handle = std::get<0>(t);
std::string &requestBody = std::get<1>(t);
CURL* curl = curl_easy_init();
LE_ASSERT(curl);
LE_ASSERT(curl_easy_setopt(curl, CURLOPT_URL, combainUrl) == CURLE_OK);
struct curl_slist *httpHeaders = NULL;
httpHeaders = curl_slist_append(httpHeaders, "Content-Type:application/json");
LE_ASSERT(httpHeaders != NULL);
LE_ASSERT(curl_easy_setopt(curl, CURLOPT_HTTPHEADER, httpHeaders) == CURLE_OK);
LE_ASSERT(curl_easy_setopt(curl, CURLOPT_COPYPOSTFIELDS, requestBody.c_str()) == CURLE_OK);
LE_INFO("SENDING %d char: %s", requestBody.length(), requestBody.c_str());
LE_ASSERT(curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemCallback) == CURLE_OK);
LE_ASSERT(curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&HttpReceiveBuffer) == CURLE_OK);
const CURLcode res = curl_easy_perform(curl);
if (res != CURLE_OK)
{
LE_ERROR("libcurl returned error (%d): %s", res, curl_easy_strerror(res));
// TODO: better way to encode CURL errors?
ResponseJson->enqueue(std::make_tuple(handle, ""));
}
std::string json((char*)HttpReceiveBuffer.data, HttpReceiveBuffer.used);
LE_INFO("RECEIVED %d char: %s", HttpReceiveBuffer.used, (char*) HttpReceiveBuffer.data);
ResponseJson->enqueue(std::make_tuple(handle, json));
le_event_Report(ResponseAvailableEvent, NULL, 0);
curl_easy_cleanup(curl);
curl_slist_free_all(httpHeaders);
} while (true);
}
#ifndef COMBAIN_HTTP_H
#define COMBAIN_HTTP_H
#include "legato.h"
#include "interfaces.h"
#include "ThreadSafeQueue.h"
void CombainHttpInit(
ThreadSafeQueue<std::tuple<ma_combainLocation_LocReqHandleRef_t, std::string>> *requestJson,
ThreadSafeQueue<std::tuple<ma_combainLocation_LocReqHandleRef_t, std::string>> *responseJson,
le_event_Id_t responseAvailableEvent);
void CombainHttpDeinit(void);
void *CombainHttpThreadFunc(void *context);
#endif // COMBAIN_HTTP_H
#include "CombainRequestBuilder.h"
#include <jansson.h>
#include <sstream>
#include <iomanip>
static std::string macAddrToString(const uint8_t *mac);
static std::string ssidToString(const uint8_t *ssid, size_t ssidLen);
static std::string cellularTechnologyToString(ma_combainLocation_CellularTech_t cellTech);
WifiApScanItem::WifiApScanItem(
const uint8_t *bssid,
size_t bssidLen,
const uint8_t *ssid,
size_t ssidLen,
int16_t signalStrength)
{
if (bssidLen != 6)
{
throw std::runtime_error("BSSID length must be 6");
}
std::copy(&bssid[0], &bssid[6], &this->bssid[0]);
if (ssidLen > sizeof(this->ssid))
{
throw std::runtime_error("SSID is too long");
}
std::copy(&ssid[0], &ssid[ssidLen], &this->ssid[0]);
this->ssidLen = ssidLen;
if (signalStrength >= 0)
{
throw std::runtime_error("Signal strength should be negative");
}
this->signalStrength = signalStrength;
}
void CombainRequestBuilder::appendWifiAccessPoint(const WifiApScanItem& ap)
{
this->wifiAps.push_back(ap);
}
void CombainRequestBuilder::appendCellTower(const CellTowerScanItem& tower)
{
this->cellTowers.push_back(tower);
}
std::string CombainRequestBuilder::generateRequestBody(void) const
{
json_t *body = json_object();
if (!this->wifiAps.empty())
{
json_t *wifiApsArray = json_array();
for (auto const& ap : this->wifiAps)
{
json_t *jsonAp = json_object();
json_object_set_new(jsonAp, "macAddress", json_string(macAddrToString(ap.bssid).c_str()));
json_object_set_new(jsonAp, "ssid", json_string(ssidToString(ap.ssid, ap.ssidLen).c_str()));
json_object_set_new(jsonAp, "signalStrength", json_integer(ap.signalStrength));
json_array_append_new(wifiApsArray, jsonAp);
}
json_object_set_new(body, "wifiAccessPoints", wifiApsArray);
}
if (!this->cellTowers.empty())
{
json_t *cellTowersArray = json_array();
for (auto const& tower: this->cellTowers)
{
json_t *jsonTower = json_object();
json_object_set_new(jsonTower, "radioType", json_string(cellularTechnologyToString(tower.cellularTechnology).c_str()));
json_object_set_new(jsonTower, "mobileCountryCode", json_integer(tower.mcc));
json_object_set_new(jsonTower, "mobileNetworkCode", json_integer(tower.mnc));
json_object_set_new(jsonTower, "locationAccessCode", json_integer(tower.lac));
json_object_set_new(jsonTower, "cellId", json_integer(tower.cellId));
}
json_object_set_new(body, "cellTowers", cellTowersArray);
}
char* s = json_dumps(body, JSON_COMPACT);
LE_ASSERT(s != NULL);
std::string res(s);
free(s);
json_decref(body);
return res;
}
//----------------- STATIC
static std::string macAddrToString(const uint8_t *mac)
{
std::ostringstream s;
for (auto i = 0; i < 6; i++)
{
s << std::setw(2) << std::setfill('0') << std::hex << static_cast<unsigned int>(mac[i]);
if (i != 5)
{
s << ':';
}
}
return s.str();
}
static std::string ssidToString(const uint8_t *ssid, size_t ssidLen)
{
std::string s(reinterpret_cast<const char *>(ssid), ssidLen);
return s;
}
static std::string cellularTechnologyToString(ma_combainLocation_CellularTech_t cellTech)
{
switch (cellTech)
{
case MA_COMBAINLOCATION_CELL_TECH_GSM:
return "gsm";
break;
case MA_COMBAINLOCATION_CELL_TECH_CDMA:
return "cdma";
break;
case MA_COMBAINLOCATION_CELL_TECH_LTE:
return "lte";
break;
case MA_COMBAINLOCATION_CELL_TECH_WCDMA:
return "wcdma";
break;
default:
throw std::runtime_error("Invalid cellular technology");
break;
}
return "";
}
#ifndef COMBAIN_REQUEST_BUILDER_H
#define COMBAIN_REQUEST_BUILDER_H
#include "legato.h"
#include "interfaces.h"
#include <string>
#include <list>
struct WifiApScanItem
{
WifiApScanItem(
const uint8_t *bssid,
size_t bssidLen,
const uint8_t *ssid,
size_t ssidLen,
int16_t signalStrength);
uint8_t bssid[6];
uint8_t ssid[32];
size_t ssidLen;
int16_t signalStrength;
};
struct CellTowerScanItem
{
ma_combainLocation_CellularTech_t cellularTechnology;
uint16_t mcc;
uint16_t mnc;
uint32_t lac;
uint32_t cellId;
int32_t signalStrength;
};
class CombainRequestBuilder
{
public:
void appendWifiAccessPoint(const WifiApScanItem& ap);
void appendCellTower(const CellTowerScanItem& tower);
std::string generateRequestBody(void) const;
private:
std::list<WifiApScanItem> wifiAps;
std::list<CellTowerScanItem> cellTowers;
};
#endif // COMBAIN_REQUEST_BUILDER_H
#ifndef COMBAIN_REQUEST_BUILDER_H
#define COMBAIN_REQUEST_BUILDER_H
#include "legato.h"
#include "interfaces.h"
#include <string>
#include <list>
struct WifiApScanItem
{
WifiApScanItem(
const uint8_t *bssid,
size_t bssidLen,
const uint8_t *ssid,
size_t ssidLen,
int16_t signalStrength);
uint8_t bssid[6];
uint8_t ssid[32];
size_t ssidLen;
int16_t signalStrength;
};
struct CellTowerScanItem
{
ma_combainLocation_CellularTech_t cellularTechnology;
uint16_t mcc;
uint16_t mnc;
uint32_t lac;
uint32_t cellId;
int32_t signalStrength;
};
typedef struct GpsScanReading
{
int32_t lat;
int32_t lon;
int32_t hAccuracy;
int32_t alt;
int32_t vAccuracy;
} GpsScan_t ;
class CombainRequestBuilder
{
public:
void appendWifiAccessPoint(const WifiApScanItem& ap);
void appendCellTower(const CellTowerScanItem& tower);
void CreateGps(const GpsScan_t& gps);
std::string generateRequestBody(void) const;
private:
std::list<WifiApScanItem> wifiAps;
std::list<CellTowerScanItem> cellTowers;
GpsScan_t *gpsP;
};
#endif // COMBAIN_REQUEST_BUILDER_H
#include "CombainResult.h"
CombainResult::CombainResult(ma_combainLocation_Result_t type)
: type(type)
{}
ma_combainLocation_Result_t CombainResult::getType(void) const
{
return this->type;
}
CombainSuccessResponse::CombainSuccessResponse(
double latitude, double longitude, double accuracyInMeters)
: CombainResult(MA_COMBAINLOCATION_RESULT_SUCCESS),
latitude(latitude),
longitude(longitude),
accuracyInMeters(accuracyInMeters)
{}
CombainErrorResponse::CombainErrorResponse(
uint16_t code, const std::string &message, std::initializer_list<CombainError> errors)
: CombainResult(MA_COMBAINLOCATION_RESULT_ERROR),
code(code),
message(message),
errors(errors)
{}
CombainResponseParseFailure::CombainResponseParseFailure(const std::string &unparsed)
: CombainResult(MA_COMBAINLOCATION_RESULT_RESPONSE_PARSE_FAILURE),
unparsed(unparsed)
{}
CombainCommunicationFailure::CombainCommunicationFailure(void)
: CombainResult(MA_COMBAINLOCATION_RESULT_COMMUNICATION_FAILURE)
{}
#ifndef COMBAIN_RESULT_H
#define COMBAIN_RESULT_H
#include "legato.h"
#include "interfaces.h"
#include <initializer_list>
#include <list>
#include <string>
class CombainResult
{
public:
explicit CombainResult(ma_combainLocation_Result_t type);
ma_combainLocation_Result_t getType(void) const;
private:
ma_combainLocation_Result_t type;
};
struct CombainSuccessResponse : public CombainResult
{
CombainSuccessResponse(double latitude, double longitude, double accuracyInMeters);
double latitude;
double longitude;
double accuracyInMeters;
};
struct CombainError
{
std::string domain;
std::string reason;
std::string message;
};
struct CombainErrorResponse : public CombainResult
{
CombainErrorResponse(
uint16_t code, const std::string &message, std::initializer_list<CombainError> errors);
uint16_t code;
std::string message;
std::list<CombainError> errors;
};
struct CombainResponseParseFailure : public CombainResult
{
explicit CombainResponseParseFailure(const std::string& unparsed);
std::string unparsed;
};
struct CombainCommunicationFailure : public CombainResult
{
CombainCommunicationFailure(void);
};
#endif // COMBAIN_RESULT_H
cxxflags:
{
-std=c++14
-I$LEGATO_BUILD/framework/libjansson/include
}
sources:
{
combainLocationApi.cpp
CombainRequestBuilder.cpp
CombainResult.cpp
CombainHttp.cpp
}
provides:
{
api:
{
$CURDIR/../ma_combainLocation.api
}
}
ldflags:
{
-ljansson
}
requires:
{
api:
{
le_cfg.api
}
lib:
{
curl
}
component:
{
${LEGATO_ROOT}/components/3rdParty/curl
}
file:
{
/legato/systems/current/lib/libjansson.so /usr/lib/
/lib/libz.so.1 /lib/
/lib/libnss_dns.so.2 /lib/
/lib/libnss_dns-2.24.so /lib/
/lib/libresolv.so.2 /lib/
/lib/libresolv-2.24.so /lib/
/etc/resolv.conf /etc/
/etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
/usr/bin/curl /usr/bin/
}
}
bundles:
{
file:
{
#if file_exists(${LEGATO_SYSROOT}/usr/lib/libgnutls.so.30)
${LEGATO_SYSROOT}/usr/lib/libgnutls.so.30 /usr/lib/
#elif file_exists(${LEGATO_SYSROOT}/usr/lib/libgnutls.so.28)
${LEGATO_SYSROOT}/usr/lib/libgnutls.so.28 /usr/lib/
#endif
#if file_exists(${LEGATO_SYSROOT}/usr/lib/libidn.so.11)
${LEGATO_SYSROOT}/usr/lib/libidn.so.11 /usr/lib/
#endif
#if file_exists(${LEGATO_SYSROOT}/usr/lib/libnettle.so.6)
${LEGATO_SYSROOT}/usr/lib/libnettle.so.6 /usr/lib/
#elif file_exists(${LEGATO_SYSROOT}/usr/lib/libnettle.so.4)
${LEGATO_SYSROOT}/usr/lib/libnettle.so.4 /usr/lib/
#endif
#if file_exists(${LEGATO_SYSROOT}/usr/lib/libhogweed.so.4)
${LEGATO_SYSROOT}/usr/lib/libhogweed.so.4 /usr/lib/
#elif file_exists(${LEGATO_SYSROOT}/usr/lib/libhogweed.so.2)
${LEGATO_SYSROOT}/usr/lib/libhogweed.so.2 /usr/lib/
#endif
#if file_exists(${LEGATO_SYSROOT}/usr/lib/libgmp.so.10.3.1)
${LEGATO_SYSROOT}/usr/lib/libgmp.so.10.3.1 /usr/lib/
${LEGATO_SYSROOT}/usr/lib/libgmp.so.10 /usr/lib/
#elif file_exists(${LEGATO_SYSROOT}/usr/lib/libgmp.so.10.2.0)
${LEGATO_SYSROOT}/usr/lib/libgmp.so.10.2.0 /usr/lib/
${LEGATO_SYSROOT}/usr/lib/libgmp.so.10 /usr/lib/
#endif
}
}
\ No newline at end of file
#ifndef THREAD_SAFE_QUEUE_H
#define THREAD_SAFE_QUEUE_H
#include <queue>
#include <mutex>
#include <condition_variable>
// A threadsafe-queue.
template <class T> class ThreadSafeQueue
{
public:
ThreadSafeQueue(void)
: q() , m() , c()
{}
~ThreadSafeQueue(void)
{}
// Add an element to the queue.
void enqueue(T t)
{
std::lock_guard<std::mutex> lock(this->m);
this->q.push(t);
this->c.notify_one();
}
// Get the "front"-element.
// If the queue is empty, wait till a element is avaiable.
T dequeue(void)
{
std::unique_lock<std::mutex> lock(this->m);
this->c.wait(lock, [this]{ return !this->q.empty(); });
while(this->q.empty())
{
// release lock as long as the wait and reaquire it afterwards.
this->c.wait(lock);
}
T val = this->q.front();
this->q.pop();
return val;
}
private:
std::queue<T> q;
mutable std::mutex m;
std::condition_variable c;
};
#endif // THREAD_SAFE_QUEUE_H
sandboxed: true
start: manual
version: 1.0
executables:
{
combainLocation = ( combain )
}
processes:
{
run:
{
( combainLocation )
}
envVars:
{
LE_LOG_LEVEL = DEBUG
}
faultAction: stopApp
}
extern:
{
combainLocation.combain.ma_combainLocation
}
requires:
{
configTree:
{
[r] .
}
}
//--------------------------------------------------------------------------------------------------
/**
* Component definition file for the positioning sensor
*/
//--------------------------------------------------------------------------------------------------
requires:
{
api:
{
positioning/le_posCtrl.api
positioning/le_pos.api
dhubIO = io.api
${CURDIR}/../../CombainLocation/ma_combainLocation.api
wifi/le_wifiClient.api
//modemServices/le_mrc.api
}
component:
{
periodicSensor
}
}
cflags:
{
"-std=c99"
}
sources:
{
location.c
}
sandboxed: true
start: manual
version: 0.0.1
extern:
{
}
executables:
{
location = ( components )
}
processes:
{
run:
{
( location )
}
//faultAction: restartApp
envVars:
{
LE_LOG_LEVEL = DEBUG
}
}
bindings:
{
location.components.le_pos -> positioningService.le_pos
location.components.le_posCtrl -> positioningService.le_posCtrl
location.periodicSensor.dhubIO -> dataHub.io
location.components.dhubIO -> dataHub.io
location.components.ma_combainLocation -> combainLocation.ma_combainLocation
location.components.le_wifiClient -> wifiService.le_wifiClient
//location.components.le_mrc -> modemService.le_mrc
}
DEFINE WIFI_BSSID_BYTES = 6;
DEFINE WIFI_SSID_MAX_BYTES = 32;
//--------------------------------------------------------------------------------------------------
/**
* An opaque object for a location request.
*/
//--------------------------------------------------------------------------------------------------
REFERENCE LocReqHandle;
//--------------------------------------------------------------------------------------------------
/**
* Creates a location request object in the service.
*/
//--------------------------------------------------------------------------------------------------
FUNCTION LocReqHandle CreateLocationRequest();
//--------------------------------------------------------------------------------------------------
/**
* Append information about one WiFi access point to the request object
*/
//--------------------------------------------------------------------------------------------------
FUNCTION le_result_t AppendWifiAccessPoint
(
LocReqHandle handle IN, ///< Handle from CreateLocationRequest()
uint8 bssid[WIFI_BSSID_BYTES] IN, ///< BSSID of the WiFi AP. Must be exactly 6 bytes.
uint8 ssid[WIFI_SSID_MAX_BYTES] IN, ///< SSID of the WiFi AP. Not necessarily a string. Do not
///< null terminate.
int16 signalStrength IN ///< Signal strength in dB. E.g. -62.
);
ENUM CellularTech
{
CELL_TECH_GSM,
CELL_TECH_CDMA,
CELL_TECH_LTE,
CELL_TECH_WCDMA
};
FUNCTION le_result_t AppendCellTower
(
LocReqHandle handle IN, ///< Handle from CreateLocationRequest()
CellularTech cellularTechnology,
uint16 mcc,
uint16 mnc, ///< Use systemId, (sid) for CDMA
uint32 lac, ///< Network id for CDMA
uint32 cellId, ///< Basestation Id for CDMAj
int32 signalStrength ///< Signal strength in dBm
);
//--------------------------------------------------------------------------------------------------
/**
* Result type that is passed to the callback when after submitting a request
*/
//--------------------------------------------------------------------------------------------------
ENUM Result
{
RESULT_SUCCESS,
RESULT_ERROR,
RESULT_RESPONSE_PARSE_FAILURE,
RESULT_COMMUNICATION_FAILURE,
};
//--------------------------------------------------------------------------------------------------
/**
* Handler that will be called when a result of a request is available
*/
//--------------------------------------------------------------------------------------------------
HANDLER LocationResultHandler
(
LocReqHandle handle, ///< Handle of the request
Result result ///< What type of result is available
);
//--------------------------------------------------------------------------------------------------
/**
* Submits the location request to the Combain server for processing.
*/
//--------------------------------------------------------------------------------------------------
FUNCTION le_result_t SubmitLocationRequest
(
LocReqHandle handle IN, ///< Handle of the request to submit
LocationResultHandler resultHandler ///< Handler that will be called on completion of the
///< request if this function returns LE_OK
);
//--------------------------------------------------------------------------------------------------
/**
* Destroys a previously created request freeing the resources allocated in the service. Note that
* the GetSuccessResponse() and GetErrorResponse() functions implicitly destroy the request, so
* calling this function is only necessary if the client decides not to submit the request after
* creating it or decides not to retrieve the response.
*/
//--------------------------------------------------------------------------------------------------
FUNCTION DestroyLocationRequest
(
LocReqHandle handle IN ///< Handle of the request to destroy
);
//--------------------------------------------------------------------------------------------------
/**
* Gets the response data from a successfully completed request. Note that the request is implicitly
* destroyed by calling this function.
*
* @return LE_OK on success. If the result is not LE_OK, the content of the out parameters is
* undefined.
*/
//--------------------------------------------------------------------------------------------------
FUNCTION le_result_t GetSuccessResponse
(
LocReqHandle handle IN,
double latitude OUT,
double longitude OUT,
double accuracyInMeters OUT
// TODO: geoname, address fallback?
);
//--------------------------------------------------------------------------------------------------
/**
* Gets the response data from a request which failed to complete. Note that the request is
* implicitly destroyed by calling this function.
*
* @return LE_OK on success. If the result is not LE_OK, the content of the out parameters is
* undefined.
*/
//--------------------------------------------------------------------------------------------------
FUNCTION le_result_t GetErrorResponse
(
LocReqHandle handle IN,
string firstDomain[64] OUT,
string firstReason[64] OUT,
string firstMessage[128] OUT,
uint16 code OUT,
string message[128] OUT
);
//--------------------------------------------------------------------------------------------------
/**
* Gets the unparsed data from a parse failure result
*
* @return LE_OK on success. If the result is not LE_OK, the content of the out parameter is
* undefined.
*/
//--------------------------------------------------------------------------------------------------
FUNCTION le_result_t GetParseFailureResult
(
LocReqHandle handle IN,
string unparsedResponse[256] OUT
);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment