<commit_before>// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "device/battery/battery_status_manager_linux.h"

#include "base/macros.h"
#include "base/metrics/histogram.h"
#include "base/threading/thread.h"
#include "base/values.h"
#include "dbus/bus.h"
#include "dbus/message.h"
#include "dbus/object_path.h"
#include "dbus/object_proxy.h"
#include "dbus/property.h"
#include "dbus/values_util.h"
#include "device/battery/battery_status_manager.h"

namespace device {

namespace {

const char kUPowerServiceName[] = "org.freedesktop.UPower";
const char kUPowerDeviceName[] = "org.freedesktop.UPower.Device";
const char kUPowerPath[] = "/org/freedesktop/UPower";
const char kUPowerDeviceSignalChanged[] = "Changed";
const char kUPowerEnumerateDevices[] = "EnumerateDevices";
const char kBatteryNotifierThreadName[] = "BatteryStatusNotifier";

// UPowerDeviceType reflects the possible UPower.Device.Type values,
// see upower.freedesktop.org/docs/Device.html#Device:Type.
enum UPowerDeviceType {
  UPOWER_DEVICE_TYPE_UNKNOWN = 0,
  UPOWER_DEVICE_TYPE_LINE_POWER = 1,
  UPOWER_DEVICE_TYPE_BATTERY = 2,
  UPOWER_DEVICE_TYPE_UPS = 3,
  UPOWER_DEVICE_TYPE_MONITOR = 4,
  UPOWER_DEVICE_TYPE_MOUSE = 5,
  UPOWER_DEVICE_TYPE_KEYBOARD = 6,
  UPOWER_DEVICE_TYPE_PDA = 7,
  UPOWER_DEVICE_TYPE_PHONE = 8,
};

typedef std::vector<dbus::ObjectPath> PathsVector;

double GetPropertyAsDouble(const base::DictionaryValue& dictionary,
                           const std::string& property_name,
                           double default_value) {
  double value = default_value;
  return dictionary.GetDouble(property_name, &value) ? value : default_value;
}

bool GetPropertyAsBoolean(const base::DictionaryValue& dictionary,
                          const std::string& property_name,
                          bool default_value) {
  bool value = default_value;
  return dictionary.GetBoolean(property_name, &value) ? value : default_value;
}

scoped_ptr<base::DictionaryValue> GetPropertiesAsDictionary(
    dbus::ObjectProxy* proxy) {
  dbus::MethodCall method_call(dbus::kPropertiesInterface,
                               dbus::kPropertiesGetAll);
  dbus::MessageWriter builder(&method_call);
  builder.AppendString(kUPowerDeviceName);

  scoped_ptr<dbus::Response> response(
      proxy->CallMethodAndBlock(&method_call,
                                dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
  if (response) {
    dbus::MessageReader reader(response.get());
    scoped_ptr<base::Value> value(dbus::PopDataAsValue(&reader));
    base::DictionaryValue* dictionary_value = NULL;
    if (value && value->GetAsDictionary(&dictionary_value)) {
      ignore_result(value.release());
      return scoped_ptr<base::DictionaryValue>(dictionary_value);
    }
  }
  return scoped_ptr<base::DictionaryValue>();
}

scoped_ptr<PathsVector> GetPowerSourcesPaths(dbus::ObjectProxy* proxy) {
  scoped_ptr<PathsVector> paths(new PathsVector());
  if (!proxy)
    return paths.Pass();

  dbus::MethodCall method_call(kUPowerServiceName, kUPowerEnumerateDevices);
  scoped_ptr<dbus::Response> response(
      proxy->CallMethodAndBlock(&method_call,
                                dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));

  if (response) {
    dbus::MessageReader reader(response.get());
    reader.PopArrayOfObjectPaths(paths.get());
  }
  return paths.Pass();;
}

void UpdateNumberBatteriesHistogram(int count) {
  UMA_HISTOGRAM_CUSTOM_COUNTS(
      "BatteryStatus.NumberBatteriesLinux", count, 1, 5, 6);
}

// Class that represents a dedicated thread which communicates with DBus to
// obtain battery information and receives battery change notifications.
class BatteryStatusNotificationThread : public base::Thread {
 public:
  BatteryStatusNotificationThread(
      const BatteryStatusService::BatteryUpdateCallback& callback)
      : base::Thread(kBatteryNotifierThreadName),
        callback_(callback),
        battery_proxy_(NULL) {}

  ~BatteryStatusNotificationThread() override {
    // Make sure to shutdown the dbus connection if it is still open in the very
    // end. It needs to happen on the BatteryStatusNotificationThread.
    message_loop()->PostTask(
        FROM_HERE,
        base::Bind(&BatteryStatusNotificationThread::ShutdownDBusConnection,
                   base::Unretained(this)));

    // Drain the message queue of the BatteryStatusNotificationThread and stop.
    Stop();
  }

  void StartListening() {
    DCHECK(OnWatcherThread());

    if (system_bus_.get())
      return;

    InitDBus();
    dbus::ObjectProxy* power_proxy =
        system_bus_->GetObjectProxy(kUPowerServiceName,
                                    dbus::ObjectPath(kUPowerPath));
    scoped_ptr<PathsVector> device_paths = GetPowerSourcesPaths(power_proxy);
    int num_batteries = 0;

    for (size_t i = 0; i < device_paths->size(); ++i) {
      const dbus::ObjectPath& device_path = device_paths->at(i);
      dbus::ObjectProxy* device_proxy = system_bus_->GetObjectProxy(
          kUPowerServiceName, device_path);
      scoped_ptr<base::DictionaryValue> dictionary =
          GetPropertiesAsDictionary(device_proxy);

      if (!dictionary)
        continue;

      bool is_present = GetPropertyAsBoolean(*dictionary, "IsPresent", false);
      uint32 type = static_cast<uint32>(
          GetPropertyAsDouble(*dictionary, "Type", UPOWER_DEVICE_TYPE_UNKNOWN));

      if (!is_present || type != UPOWER_DEVICE_TYPE_BATTERY) {
        system_bus_->RemoveObjectProxy(kUPowerServiceName,
                                       device_path,
                                       base::Bind(&base::DoNothing));
        continue;
      }

      if (battery_proxy_) {
        // TODO(timvolodine): add support for multiple batteries. Currently we
        // only collect information from the first battery we encounter
        // (crbug.com/400780).
        LOG(WARNING) << "multiple batteries found, "
                     << "using status data of the first battery only.";
      } else {
        battery_proxy_ = device_proxy;
      }
      num_batteries++;
    }

    UpdateNumberBatteriesHistogram(num_batteries);

    if (!battery_proxy_) {
      callback_.Run(BatteryStatus());
      return;
    }

    battery_proxy_->ConnectToSignal(
        kUPowerDeviceName,
        kUPowerDeviceSignalChanged,
        base::Bind(&BatteryStatusNotificationThread::BatteryChanged,
                   base::Unretained(this)),
        base::Bind(&BatteryStatusNotificationThread::OnSignalConnected,
                   base::Unretained(this)));
  }

  void StopListening() {
    DCHECK(OnWatcherThread());
    ShutdownDBusConnection();
  }

 private:
  bool OnWatcherThread() {
    return task_runner()->BelongsToCurrentThread();
  }

  void InitDBus() {
    DCHECK(OnWatcherThread());

    dbus::Bus::Options options;
    options.bus_type = dbus::Bus::SYSTEM;
    options.connection_type = dbus::Bus::PRIVATE;
    system_bus_ = new dbus::Bus(options);
  }

  void ShutdownDBusConnection() {
    DCHECK(OnWatcherThread());

    if (!system_bus_.get())
      return;

    // Shutdown DBus connection later because there may be pending tasks on
    // this thread.
    message_loop()->PostTask(FROM_HERE,
                             base::Bind(&dbus::Bus::ShutdownAndBlock,
                                        system_bus_));
    system_bus_ = NULL;
    battery_proxy_ = NULL;
  }

  void OnSignalConnected(const std::string& interface_name,
                         const std::string& signal_name,
                         bool success) {
    DCHECK(OnWatcherThread());

    if (interface_name != kUPowerDeviceName ||
        signal_name != kUPowerDeviceSignalChanged) {
      return;
    }

    if (!system_bus_.get())
      return;

    if (success) {
      BatteryChanged(NULL);
    } else {
      // Failed to register for "Changed" signal, execute callback with the
      // default values.
      callback_.Run(BatteryStatus());
    }
  }

  void BatteryChanged(dbus::Signal* signal /* unsused */) {
    DCHECK(OnWatcherThread());

    if (!system_bus_.get())
      return;

    scoped_ptr<base::DictionaryValue> dictionary =
        GetPropertiesAsDictionary(battery_proxy_);
    if (dictionary)
      callback_.Run(ComputeWebBatteryStatus(*dictionary));
    else
      callback_.Run(BatteryStatus());
  }

  BatteryStatusService::BatteryUpdateCallback callback_;
  scoped_refptr<dbus::Bus> system_bus_;
  dbus::ObjectProxy* battery_proxy_;  // owned by the bus

  DISALLOW_COPY_AND_ASSIGN(BatteryStatusNotificationThread);
};

// Creates a notification thread and delegates Start/Stop calls to it.
class BatteryStatusManagerLinux : public BatteryStatusManager {
 public:
  explicit BatteryStatusManagerLinux(
      const BatteryStatusService::BatteryUpdateCallback& callback)
      : callback_(callback) {}

  ~BatteryStatusManagerLinux() override {}

 private:
  // BatteryStatusManager:
  bool StartListeningBatteryChange() override {
      return false;

    notifier_thread_->message_loop()->PostTask(
        FROM_HERE,
        base::Bind(&BatteryStatusNotificationThread::StartListening,
                   base::Unretained(notifier_thread_.get())));
    return true;
  }

  void StopListeningBatteryChange() override {
    if (!notifier_thread_)
      return;

    notifier_thread_->message_loop()->PostTask(
        FROM_HERE,
        base::Bind(&BatteryStatusNotificationThread::StopListening,
                   base::Unretained(notifier_thread_.get())));
  }

  // Starts the notifier thread if not already started and returns true on
  // success.
  bool StartNotifierThreadIfNecessary() {
    if (notifier_thread_)
      return true;

    base::Thread::Options thread_options(base::MessageLoop::TYPE_IO, 0);
    notifier_thread_.reset(new BatteryStatusNotificationThread(callback_));
    if (!notifier_thread_->StartWithOptions(thread_options)) {
      notifier_thread_.reset();
      LOG(ERROR) << "Could not start the " << kBatteryNotifierThreadName
                 << " thread";
      return false;
    }
    return true;
  }

  BatteryStatusService::BatteryUpdateCallback callback_;
  scoped_ptr<BatteryStatusNotificationThread> notifier_thread_;

  DISALLOW_COPY_AND_ASSIGN(BatteryStatusManagerLinux);
};

}  // namespace

BatteryStatus ComputeWebBatteryStatus(const base::DictionaryValue& dictionary) {
  BatteryStatus status;
  if (!dictionary.HasKey("State"))
    return status;

  uint32 state = static_cast<uint32>(
      GetPropertyAsDouble(dictionary, "State", UPOWER_DEVICE_STATE_UNKNOWN));
  status.charging = state != UPOWER_DEVICE_STATE_DISCHARGING &&
                    state != UPOWER_DEVICE_STATE_EMPTY;
  double percentage = GetPropertyAsDouble(dictionary, "Percentage", 100);
  // Convert percentage to a value between 0 and 1 with 2 digits of precision.
  // This is to bring it in line with other platforms like Mac and Android where
  // we report level with 1% granularity. It also serves the purpose of reducing
  // the possibility of fingerprinting and triggers less level change events on
  // the blink side.
  // TODO(timvolodine): consider moving this rounding to the blink side.
  status.level = round(percentage) / 100.f;

  switch (state) {
    case UPOWER_DEVICE_STATE_CHARGING : {
      double time_to_full = GetPropertyAsDouble(dictionary, "TimeToFull", 0);
      status.charging_time =
          (time_to_full > 0) ? time_to_full
                             : std::numeric_limits<double>::infinity();
      break;
    }
    case UPOWER_DEVICE_STATE_DISCHARGING : {
      double time_to_empty = GetPropertyAsDouble(dictionary, "TimeToEmpty", 0);
      // Set dischargingTime if it's available. Otherwise leave the default
      // value which is +infinity.
      if (time_to_empty > 0)
        status.discharging_time = time_to_empty;
      status.charging_time = std::numeric_limits<double>::infinity();
      break;
    }
    case UPOWER_DEVICE_STATE_FULL : {
      break;
    }
    default: {
      status.charging_time = std::numeric_limits<double>::infinity();
    }
  }
  return status;
}

// static
scoped_ptr<BatteryStatusManager> BatteryStatusManager::Create(
    const BatteryStatusService::BatteryUpdateCallback& callback) {
  return scoped_ptr<BatteryStatusManager>(
      new BatteryStatusManagerLinux(callback));
}

}  // namespace device
<commit_msg>Fix non-working battery status manager on Linux.<commit_after>// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "device/battery/battery_status_manager_linux.h"

#include "base/macros.h"
#include "base/metrics/histogram.h"
#include "base/threading/thread.h"
#include "base/values.h"
#include "dbus/bus.h"
#include "dbus/message.h"
#include "dbus/object_path.h"
#include "dbus/object_proxy.h"
#include "dbus/property.h"
#include "dbus/values_util.h"
#include "device/battery/battery_status_manager.h"

namespace device {

namespace {

const char kUPowerServiceName[] = "org.freedesktop.UPower";
const char kUPowerDeviceName[] = "org.freedesktop.UPower.Device";
const char kUPowerPath[] = "/org/freedesktop/UPower";
const char kUPowerDeviceSignalChanged[] = "Changed";
const char kUPowerEnumerateDevices[] = "EnumerateDevices";
const char kBatteryNotifierThreadName[] = "BatteryStatusNotifier";

// UPowerDeviceType reflects the possible UPower.Device.Type values,
// see upower.freedesktop.org/docs/Device.html#Device:Type.
enum UPowerDeviceType {
  UPOWER_DEVICE_TYPE_UNKNOWN = 0,
  UPOWER_DEVICE_TYPE_LINE_POWER = 1,
  UPOWER_DEVICE_TYPE_BATTERY = 2,
  UPOWER_DEVICE_TYPE_UPS = 3,
  UPOWER_DEVICE_TYPE_MONITOR = 4,
  UPOWER_DEVICE_TYPE_MOUSE = 5,
  UPOWER_DEVICE_TYPE_KEYBOARD = 6,
  UPOWER_DEVICE_TYPE_PDA = 7,
  UPOWER_DEVICE_TYPE_PHONE = 8,
};

typedef std::vector<dbus::ObjectPath> PathsVector;

double GetPropertyAsDouble(const base::DictionaryValue& dictionary,
                           const std::string& property_name,
                           double default_value) {
  double value = default_value;
  return dictionary.GetDouble(property_name, &value) ? value : default_value;
}

bool GetPropertyAsBoolean(const base::DictionaryValue& dictionary,
                          const std::string& property_name,
                          bool default_value) {
  bool value = default_value;
  return dictionary.GetBoolean(property_name, &value) ? value : default_value;
}

scoped_ptr<base::DictionaryValue> GetPropertiesAsDictionary(
    dbus::ObjectProxy* proxy) {
  dbus::MethodCall method_call(dbus::kPropertiesInterface,
                               dbus::kPropertiesGetAll);
  dbus::MessageWriter builder(&method_call);
  builder.AppendString(kUPowerDeviceName);

  scoped_ptr<dbus::Response> response(
      proxy->CallMethodAndBlock(&method_call,
                                dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
  if (response) {
    dbus::MessageReader reader(response.get());
    scoped_ptr<base::Value> value(dbus::PopDataAsValue(&reader));
    base::DictionaryValue* dictionary_value = NULL;
    if (value && value->GetAsDictionary(&dictionary_value)) {
      ignore_result(value.release());
      return scoped_ptr<base::DictionaryValue>(dictionary_value);
    }
  }
  return scoped_ptr<base::DictionaryValue>();
}

scoped_ptr<PathsVector> GetPowerSourcesPaths(dbus::ObjectProxy* proxy) {
  scoped_ptr<PathsVector> paths(new PathsVector());
  if (!proxy)
    return paths.Pass();

  dbus::MethodCall method_call(kUPowerServiceName, kUPowerEnumerateDevices);
  scoped_ptr<dbus::Response> response(
      proxy->CallMethodAndBlock(&method_call,
                                dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));

  if (response) {
    dbus::MessageReader reader(response.get());
    reader.PopArrayOfObjectPaths(paths.get());
  }
  return paths.Pass();;
}

void UpdateNumberBatteriesHistogram(int count) {
  UMA_HISTOGRAM_CUSTOM_COUNTS(
      "BatteryStatus.NumberBatteriesLinux", count, 1, 5, 6);
}

// Class that represents a dedicated thread which communicates with DBus to
// obtain battery information and receives battery change notifications.
class BatteryStatusNotificationThread : public base::Thread {
 public:
  BatteryStatusNotificationThread(
      const BatteryStatusService::BatteryUpdateCallback& callback)
      : base::Thread(kBatteryNotifierThreadName),
        callback_(callback),
        battery_proxy_(NULL) {}

  ~BatteryStatusNotificationThread() override {
    // Make sure to shutdown the dbus connection if it is still open in the very
    // end. It needs to happen on the BatteryStatusNotificationThread.
    message_loop()->PostTask(
        FROM_HERE,
        base::Bind(&BatteryStatusNotificationThread::ShutdownDBusConnection,
                   base::Unretained(this)));

    // Drain the message queue of the BatteryStatusNotificationThread and stop.
    Stop();
  }

  void StartListening() {
    DCHECK(OnWatcherThread());

    if (system_bus_.get())
      return;

    InitDBus();
    dbus::ObjectProxy* power_proxy =
        system_bus_->GetObjectProxy(kUPowerServiceName,
                                    dbus::ObjectPath(kUPowerPath));
    scoped_ptr<PathsVector> device_paths = GetPowerSourcesPaths(power_proxy);
    int num_batteries = 0;

    for (size_t i = 0; i < device_paths->size(); ++i) {
      const dbus::ObjectPath& device_path = device_paths->at(i);
      dbus::ObjectProxy* device_proxy = system_bus_->GetObjectProxy(
          kUPowerServiceName, device_path);
      scoped_ptr<base::DictionaryValue> dictionary =
          GetPropertiesAsDictionary(device_proxy);

      if (!dictionary)
        continue;

      bool is_present = GetPropertyAsBoolean(*dictionary, "IsPresent", false);
      uint32 type = static_cast<uint32>(
          GetPropertyAsDouble(*dictionary, "Type", UPOWER_DEVICE_TYPE_UNKNOWN));

      if (!is_present || type != UPOWER_DEVICE_TYPE_BATTERY) {
        system_bus_->RemoveObjectProxy(kUPowerServiceName,
                                       device_path,
                                       base::Bind(&base::DoNothing));
        continue;
      }

      if (battery_proxy_) {
        // TODO(timvolodine): add support for multiple batteries. Currently we
        // only collect information from the first battery we encounter
        // (crbug.com/400780).
        LOG(WARNING) << "multiple batteries found, "
                     << "using status data of the first battery only.";
      } else {
        battery_proxy_ = device_proxy;
      }
      num_batteries++;
    }

    UpdateNumberBatteriesHistogram(num_batteries);

    if (!battery_proxy_) {
      callback_.Run(BatteryStatus());
      return;
    }

    battery_proxy_->ConnectToSignal(
        kUPowerDeviceName,
        kUPowerDeviceSignalChanged,
        base::Bind(&BatteryStatusNotificationThread::BatteryChanged,
                   base::Unretained(this)),
        base::Bind(&BatteryStatusNotificationThread::OnSignalConnected,
                   base::Unretained(this)));
  }

  void StopListening() {
    DCHECK(OnWatcherThread());
    ShutdownDBusConnection();
  }

 private:
  bool OnWatcherThread() {
    return task_runner()->BelongsToCurrentThread();
  }

  void InitDBus() {
    DCHECK(OnWatcherThread());

    dbus::Bus::Options options;
    options.bus_type = dbus::Bus::SYSTEM;
    options.connection_type = dbus::Bus::PRIVATE;
    system_bus_ = new dbus::Bus(options);
  }

  void ShutdownDBusConnection() {
    DCHECK(OnWatcherThread());

    if (!system_bus_.get())
      return;

    // Shutdown DBus connection later because there may be pending tasks on
    // this thread.
    message_loop()->PostTask(FROM_HERE,
                             base::Bind(&dbus::Bus::ShutdownAndBlock,
                                        system_bus_));
    system_bus_ = NULL;
    battery_proxy_ = NULL;
  }

  void OnSignalConnected(const std::string& interface_name,
                         const std::string& signal_name,
                         bool success) {
    DCHECK(OnWatcherThread());

    if (interface_name != kUPowerDeviceName ||
        signal_name != kUPowerDeviceSignalChanged) {
      return;
    }

    if (!system_bus_.get())
      return;

    if (success) {
      BatteryChanged(NULL);
    } else {
      // Failed to register for "Changed" signal, execute callback with the
      // default values.
      callback_.Run(BatteryStatus());
    }
  }

  void BatteryChanged(dbus::Signal* signal /* unsused */) {
    DCHECK(OnWatcherThread());

    if (!system_bus_.get())
      return;

    scoped_ptr<base::DictionaryValue> dictionary =
        GetPropertiesAsDictionary(battery_proxy_);
    if (dictionary)
      callback_.Run(ComputeWebBatteryStatus(*dictionary));
    else
      callback_.Run(BatteryStatus());
  }

  BatteryStatusService::BatteryUpdateCallback callback_;
  scoped_refptr<dbus::Bus> system_bus_;
  dbus::ObjectProxy* battery_proxy_;  // owned by the bus

  DISALLOW_COPY_AND_ASSIGN(BatteryStatusNotificationThread);
};

// Creates a notification thread and delegates Start/Stop calls to it.
class BatteryStatusManagerLinux : public BatteryStatusManager {
 public:
  explicit BatteryStatusManagerLinux(
      const BatteryStatusService::BatteryUpdateCallback& callback)
      : callback_(callback) {}

  ~BatteryStatusManagerLinux() override {}

 private:
  // BatteryStatusManager:
  bool StartListeningBatteryChange() override {
    if (!StartNotifierThreadIfNecessary())
      return false;

    notifier_thread_->message_loop()->PostTask(
        FROM_HERE,
        base::Bind(&BatteryStatusNotificationThread::StartListening,
                   base::Unretained(notifier_thread_.get())));
    return true;
  }

  void StopListeningBatteryChange() override {
    if (!notifier_thread_)
      return;

    notifier_thread_->message_loop()->PostTask(
        FROM_HERE,
        base::Bind(&BatteryStatusNotificationThread::StopListening,
                   base::Unretained(notifier_thread_.get())));
  }

  // Starts the notifier thread if not already started and returns true on
  // success.
  bool StartNotifierThreadIfNecessary() {
    if (notifier_thread_)
      return true;

    base::Thread::Options thread_options(base::MessageLoop::TYPE_IO, 0);
    notifier_thread_.reset(new BatteryStatusNotificationThread(callback_));
    if (!notifier_thread_->StartWithOptions(thread_options)) {
      notifier_thread_.reset();
      LOG(ERROR) << "Could not start the " << kBatteryNotifierThreadName
                 << " thread";
      return false;
    }
    return true;
  }

  BatteryStatusService::BatteryUpdateCallback callback_;
  scoped_ptr<BatteryStatusNotificationThread> notifier_thread_;

  DISALLOW_COPY_AND_ASSIGN(BatteryStatusManagerLinux);
};

}  // namespace

BatteryStatus ComputeWebBatteryStatus(const base::DictionaryValue& dictionary) {
  BatteryStatus status;
  if (!dictionary.HasKey("State"))
    return status;

  uint32 state = static_cast<uint32>(
      GetPropertyAsDouble(dictionary, "State", UPOWER_DEVICE_STATE_UNKNOWN));
  status.charging = state != UPOWER_DEVICE_STATE_DISCHARGING &&
                    state != UPOWER_DEVICE_STATE_EMPTY;
  double percentage = GetPropertyAsDouble(dictionary, "Percentage", 100);
  // Convert percentage to a value between 0 and 1 with 2 digits of precision.
  // This is to bring it in line with other platforms like Mac and Android where
  // we report level with 1% granularity. It also serves the purpose of reducing
  // the possibility of fingerprinting and triggers less level change events on
  // the blink side.
  // TODO(timvolodine): consider moving this rounding to the blink side.
  status.level = round(percentage) / 100.f;

  switch (state) {
    case UPOWER_DEVICE_STATE_CHARGING : {
      double time_to_full = GetPropertyAsDouble(dictionary, "TimeToFull", 0);
      status.charging_time =
          (time_to_full > 0) ? time_to_full
                             : std::numeric_limits<double>::infinity();
      break;
    }
    case UPOWER_DEVICE_STATE_DISCHARGING : {
      double time_to_empty = GetPropertyAsDouble(dictionary, "TimeToEmpty", 0);
      // Set dischargingTime if it's available. Otherwise leave the default
      // value which is +infinity.
      if (time_to_empty > 0)
        status.discharging_time = time_to_empty;
      status.charging_time = std::numeric_limits<double>::infinity();
      break;
    }
    case UPOWER_DEVICE_STATE_FULL : {
      break;
    }
    default: {
      status.charging_time = std::numeric_limits<double>::infinity();
    }
  }
  return status;
}

// static
scoped_ptr<BatteryStatusManager> BatteryStatusManager::Create(
    const BatteryStatusService::BatteryUpdateCallback& callback) {
  return scoped_ptr<BatteryStatusManager>(
      new BatteryStatusManagerLinux(callback));
}

}  // namespace device
<|endoftext|>


<commit_before>//
// SimulatedGNSSSensor.cpp
//
// $Id$
//
// Copyright (c) 2015, Applied Informatics Software Engineering GmbH.
// All rights reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

#include "SimulatedGNSSSensor.h"
#include "Poco/Util/TimerTask.h"
#include "Poco/SAX/DefaultHandler.h"
#include "Poco/SAX/Attributes.h"
#include "Poco/SAX/SAXParser.h"
#include "Poco/Format.h"
#include "Poco/NumberFormatter.h"
#include "Poco/NumberParser.h"
#include "Poco/DateTimeParser.h"
#include "Poco/DateTimeFormat.h"
#include "Poco/String.h"

namespace IoT {
namespace Simulation {

const std::string SimulatedGNSSSensor::NAME("GNSS Sensor Simulation (GPX)");
const std::string SimulatedGNSSSensor::SYMBOLIC_NAME("io.macchina.gnss.gpx");

class PositionUpdate: public Poco::Util::TimerTask
{
public:
	PositionUpdate(SimulatedGNSSSensor& sensor, const Poco::Geo::LatLon& latLon, double altitude):
		_sensor(sensor),
		_latLon(latLon),
		_altitude(altitude)
	{
	}

	void run()
	{
		_sensor.updatePosition(_latLon, _altitude);
	}

private:
	SimulatedGNSSSensor& _sensor;
	Poco::Geo::LatLon _latLon;
	double _altitude;
};

class LoadGPX: public Poco::Util::TimerTask
{
public:
	LoadGPX(SimulatedGNSSSensor& sensor, const std::string& gpxPath):
		_sensor(sensor),
		_gpxPath(gpxPath)
	{
	}

	void run()
	{
		_sensor.loadGPX(_gpxPath);
	}

private:
	SimulatedGNSSSensor& _sensor;
	std::string _gpxPath;
};

class GPXHandler: public Poco::XML::DefaultHandler
{
public:
	enum State
	{
		GPX_STATE_IDLE,
		GPX_STATE_TRKPT,
		GPX_STATE_ELE,
		GPX_STATE_TIME
	};

	GPXHandler(SimulatedGNSSSensor& sensor):
		_sensor(sensor),
		_startTime(0)
	{
	}

	void startElement(const std::string& uri, const std::string& localName, const std::string& qname, const Poco::XML::Attributes& attributes)
	{
		if (localName == "trkpt")
		{
			_state = GPX_STATE_TRKPT;
			_lat = attributes.getValue("lat");
			_lon = attributes.getValue("lon");
		}
		else if (localName == "ele")
		{
			_state = GPX_STATE_ELE;
			_ele.clear();
		}
		else if (localName == "time")
		{
			_state = GPX_STATE_TIME;
			_time.clear();
		}
	}

	void endElement(const std::string& uri, const std::string& localName, const std::string& qname)
	{
		if (localName == "trkpt")
		{
			try
			{
				int tzd;
				Poco::Timestamp ts = Poco::DateTimeParser::parse(Poco::DateTimeFormat::ISO8601_FORMAT, _time, tzd).timestamp();
				Poco::Geo::LatLon latLon(
					Poco::Geo::Angle::fromDegrees(Poco::NumberParser::parseFloat(_lat)),
					Poco::Geo::Angle::fromDegrees(Poco::NumberParser::parseFloat(_lon)));
				double altitude = Poco::NumberParser::parseFloat(_ele);

				if (_startTime == 0)
				{
					_startTime = ts;
				}
				Poco::Timestamp::TimeDiff diff = (ts - _startTime)/_sensor._speedUp;
				_endClock = _startClock;
				_endClock += diff;
				_sensor._timer.schedule(new PositionUpdate(_sensor, latLon, altitude), _endClock);
			}
			catch (Poco::Exception& exc)
			{
				_sensor._logger.log(exc);
			}

			_state = GPX_STATE_IDLE;
		}
		else if (localName == "ele")
		{
			Poco::trimInPlace(_ele);
			_state = GPX_STATE_TRKPT;
		}
		else if (localName == "time")
		{
			Poco::trimInPlace(_time);
			_state = GPX_STATE_TRKPT;
		}
	}

	void characters(const char ch[], int start, int length)
	{
		if (_state == GPX_STATE_ELE)
		{
			_ele.append(ch + start, length);
		}
		else if (_state == GPX_STATE_TIME)
		{
			_time.append(ch + start, length);
		}
	}

	const Poco::Clock& endClock() const
	{
		return _endClock;
	}

private:
	SimulatedGNSSSensor& _sensor;
	State _state;
	Poco::Timestamp _startTime;
	std::string _lat;
	std::string _lon;
	std::string _ele;
	std::string _time;
	Poco::Clock _startClock;
	Poco::Clock _endClock;
};

SimulatedGNSSSensor::SimulatedGNSSSensor(const Params& params):
	_gpxPath(params.gpxPath),
	_loopReplay(params.loopReplay),
	_speedUp(params.speedUp),
	_lastValidPosition(0),
	_lastPositionUpdate(0),
	_positionAvailable(false),
	_course(-1),
	_speed(-1),
	_altitude(-9999),
	_period(0),
	_delta(0),
	_timeout(30000),
	_logger(Poco::Logger::get("IoT.Simulation.SimulatedGNSSSensor"))
{
	addProperty("symbolicName", &SimulatedGNSSSensor::getSymbolicName);
	addProperty("name", &SimulatedGNSSSensor::getName);
	addProperty("displayValue", &SimulatedGNSSSensor::getDisplayValue);
	addProperty("positionChangedPeriod", &SimulatedGNSSSensor::getPositionChangedPeriod, &SimulatedGNSSSensor::setPositionChangedPeriod);
	addProperty("positionChangedDelta", &SimulatedGNSSSensor::getPositionChangedDelta, &SimulatedGNSSSensor::setPositionChangedDelta);
	addProperty("positionTimeout", &SimulatedGNSSSensor::getPositionTimeout, &SimulatedGNSSSensor::setPositionTimeout);
	addProperty("gpxPath", &SimulatedGNSSSensor::getGPXPath);

	loadGPX(_gpxPath);
}

SimulatedGNSSSensor::~SimulatedGNSSSensor()
{
	try
	{
		stop();
	}
	catch (Poco::Exception&)
	{
		poco_unexpected();
	}
}

void SimulatedGNSSSensor::stop()
{
	_timer.cancel(true);
}

bool SimulatedGNSSSensor::positionAvailable() const
{
	Poco::FastMutex::ScopedLock lock(_mutex);

	return _positionAvailable;
}

IoT::Devices::LatLon SimulatedGNSSSensor::position() const
{
	Poco::FastMutex::ScopedLock lock(_mutex);

	return _position;
}

double SimulatedGNSSSensor::course() const
{
	Poco::FastMutex::ScopedLock lock(_mutex);

	return _course;
}

double SimulatedGNSSSensor::speed() const
{
	Poco::FastMutex::ScopedLock lock(_mutex);

	return _speed;
}

double SimulatedGNSSSensor::magneticVariation() const
{
	return -1;
}

double SimulatedGNSSSensor::altitude() const
{
	Poco::FastMutex::ScopedLock lock(_mutex);

	return _altitude;
}

double SimulatedGNSSSensor::hdop() const
{
	return -9999;
}

Poco::Any SimulatedGNSSSensor::getName(const std::string&) const
{
	return NAME;
}

Poco::Any SimulatedGNSSSensor::getSymbolicName(const std::string&) const
{
	return SYMBOLIC_NAME;
}

Poco::Any SimulatedGNSSSensor::getGPXPath(const std::string&) const
{
	return _gpxPath;
}

Poco::Any SimulatedGNSSSensor::getPositionChangedPeriod(const std::string&) const
{
	Poco::FastMutex::ScopedLock lock(_mutex);

	return Poco::Any(_period);
}

void SimulatedGNSSSensor::setPositionChangedPeriod(const std::string&, const Poco::Any& value)
{
	Poco::FastMutex::ScopedLock lock(_mutex);

	_period = Poco::AnyCast<int>(value);
}

Poco::Any SimulatedGNSSSensor::getPositionChangedDelta(const std::string&) const
{
	Poco::FastMutex::ScopedLock lock(_mutex);

	return Poco::Any(_delta);
}

void SimulatedGNSSSensor::setPositionChangedDelta(const std::string&, const Poco::Any& value)
{
	Poco::FastMutex::ScopedLock lock(_mutex);

	_delta = Poco::AnyCast<double>(value);
}

Poco::Any SimulatedGNSSSensor::getPositionTimeout(const std::string&) const
{
	Poco::FastMutex::ScopedLock lock(_mutex);

	return Poco::Any(_timeout);
}

void SimulatedGNSSSensor::setPositionTimeout(const std::string&, const Poco::Any& value)
{
	Poco::FastMutex::ScopedLock lock(_mutex);

	_timeout = Poco::AnyCast<int>(value);
}

Poco::Any SimulatedGNSSSensor::getDisplayValue(const std::string&) const
{
	if (positionAvailable())
	{
		IoT::Devices::LatLon latLon = position();
		std::string value = Poco::format("%02.6f,%03.6f", latLon.latitude, latLon.longitude);
		double knots = speed();
		if (knots >= 0) Poco::format(value, " %.2f kn", knots);
		double degrees = course();
		if (degrees >= 0) Poco::format(value, " %.1f°", degrees);
		return value;
	}
	else return std::string("n/a");
}

void SimulatedGNSSSensor::updatePosition(const Poco::Geo::LatLon& latLon, double altitude)
{
	if (_logger.debug())
	{
		_logger.debug(Poco::format("Updating position to lat=%f, lon=%f, altitude=%f",
			latLon.latitude().degrees(),
			latLon.longitude().degrees(),
			altitude));
	}

	IoT::Devices::PositionUpdate event;
	IoT::Devices::LatLon lastPosition;
	double delta = 0;
	double period = 0;
	double distance = 0;
	Poco::Geo::LatLon lastLatLon;
	{
		Poco::FastMutex::ScopedLock lock(_mutex);

		delta = _delta;
		period = _period;

		double diffSecs = static_cast<double>(_lastValidPosition.elapsed())/Poco::Timestamp::resolution();

		_lastValidPosition.update();
		_positionAvailable = true;

		lastPosition = _position;

		lastLatLon.assign(
			Poco::Geo::Angle::fromDegrees(lastPosition.latitude),
			Poco::Geo::Angle::fromDegrees(lastPosition.longitude));

		Poco::Geo::Angle gcd = lastLatLon.greatCircleDistanceTo(latLon);
		distance = gcd.radians()*Poco::Geo::LatLon::EARTH_MEAN_RADIUS;

		_position.latitude = latLon.latitude().degrees();
		_position.longitude = latLon.longitude().degrees();
		_altitude = altitude;
		_course = Poco::Geo::Angle::normalizedDegrees(lastLatLon.greatCircleAzimuthTo(latLon).degrees());
		_speed = diffSecs > 0 ? distance/diffSecs*1.943844 : _speed; // m/s to kt
	}

	try
	{
		if (_lastPositionUpdate.isElapsed(1000*Poco::Timestamp::TimeVal(period)))
		{
			bool needUpdate = delta == 0;
			if (!needUpdate)
			{
				needUpdate = distance >= delta;
			}
			if (needUpdate)
			{
				_lastPositionUpdate.update();

				event.position = _position;
				event.course   = _course;
				event.speed    = _speed;
				event.magneticVariation = -1;
				event.timestamp.update();

				positionUpdate(this, event);
			}
		}
	}
	catch (Poco::Exception& exc)
	{
		_logger.log(exc);
	}
}

void SimulatedGNSSSensor::loadGPX(const std::string& path)
{
	_logger.information("Loading GNSS track from GPX file: " + path);

	GPXHandler gpxHandler(*this);

	Poco::XML::SAXParser parser;
	parser.setFeature(Poco::XML::XMLReader::FEATURE_NAMESPACES, true);
	parser.setFeature(Poco::XML::XMLReader::FEATURE_NAMESPACE_PREFIXES, true);
	parser.setContentHandler(&gpxHandler);

	parser.parse(path);

	if (_loopReplay)
	{
		_timer.schedule(new LoadGPX(*this, path), gpxHandler.endClock());
	}
}

} } // namespace IoT::Simulation
<commit_msg>logging message fix for consistency<commit_after>//
// SimulatedGNSSSensor.cpp
//
// $Id$
//
// Copyright (c) 2015, Applied Informatics Software Engineering GmbH.
// All rights reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

#include "SimulatedGNSSSensor.h"
#include "Poco/Util/TimerTask.h"
#include "Poco/SAX/DefaultHandler.h"
#include "Poco/SAX/Attributes.h"
#include "Poco/SAX/SAXParser.h"
#include "Poco/Format.h"
#include "Poco/NumberFormatter.h"
#include "Poco/NumberParser.h"
#include "Poco/DateTimeParser.h"
#include "Poco/DateTimeFormat.h"
#include "Poco/String.h"

namespace IoT {
namespace Simulation {

const std::string SimulatedGNSSSensor::NAME("GNSS Sensor Simulation (GPX)");
const std::string SimulatedGNSSSensor::SYMBOLIC_NAME("io.macchina.gnss.gpx");

class PositionUpdate: public Poco::Util::TimerTask
{
public:
	PositionUpdate(SimulatedGNSSSensor& sensor, const Poco::Geo::LatLon& latLon, double altitude):
		_sensor(sensor),
		_latLon(latLon),
		_altitude(altitude)
	{
	}

	void run()
	{
		_sensor.updatePosition(_latLon, _altitude);
	}

private:
	SimulatedGNSSSensor& _sensor;
	Poco::Geo::LatLon _latLon;
	double _altitude;
};

class LoadGPX: public Poco::Util::TimerTask
{
public:
	LoadGPX(SimulatedGNSSSensor& sensor, const std::string& gpxPath):
		_sensor(sensor),
		_gpxPath(gpxPath)
	{
	}

	void run()
	{
		_sensor.loadGPX(_gpxPath);
	}

private:
	SimulatedGNSSSensor& _sensor;
	std::string _gpxPath;
};

class GPXHandler: public Poco::XML::DefaultHandler
{
public:
	enum State
	{
		GPX_STATE_IDLE,
		GPX_STATE_TRKPT,
		GPX_STATE_ELE,
		GPX_STATE_TIME
	};

	GPXHandler(SimulatedGNSSSensor& sensor):
		_sensor(sensor),
		_startTime(0)
	{
	}

	void startElement(const std::string& uri, const std::string& localName, const std::string& qname, const Poco::XML::Attributes& attributes)
	{
		if (localName == "trkpt")
		{
			_state = GPX_STATE_TRKPT;
			_lat = attributes.getValue("lat");
			_lon = attributes.getValue("lon");
		}
		else if (localName == "ele")
		{
			_state = GPX_STATE_ELE;
			_ele.clear();
		}
		else if (localName == "time")
		{
			_state = GPX_STATE_TIME;
			_time.clear();
		}
	}

	void endElement(const std::string& uri, const std::string& localName, const std::string& qname)
	{
		if (localName == "trkpt")
		{
			try
			{
				int tzd;
				Poco::Timestamp ts = Poco::DateTimeParser::parse(Poco::DateTimeFormat::ISO8601_FORMAT, _time, tzd).timestamp();
				Poco::Geo::LatLon latLon(
					Poco::Geo::Angle::fromDegrees(Poco::NumberParser::parseFloat(_lat)),
					Poco::Geo::Angle::fromDegrees(Poco::NumberParser::parseFloat(_lon)));
				double altitude = Poco::NumberParser::parseFloat(_ele);

				if (_startTime == 0)
				{
					_startTime = ts;
				}
				Poco::Timestamp::TimeDiff diff = (ts - _startTime)/_sensor._speedUp;
				_endClock = _startClock;
				_endClock += diff;
				_sensor._timer.schedule(new PositionUpdate(_sensor, latLon, altitude), _endClock);
			}
			catch (Poco::Exception& exc)
			{
				_sensor._logger.log(exc);
			}

			_state = GPX_STATE_IDLE;
		}
		else if (localName == "ele")
		{
			Poco::trimInPlace(_ele);
			_state = GPX_STATE_TRKPT;
		}
		else if (localName == "time")
		{
			Poco::trimInPlace(_time);
			_state = GPX_STATE_TRKPT;
		}
	}

	void characters(const char ch[], int start, int length)
	{
		if (_state == GPX_STATE_ELE)
		{
			_ele.append(ch + start, length);
		}
		else if (_state == GPX_STATE_TIME)
		{
			_time.append(ch + start, length);
		}
	}

	const Poco::Clock& endClock() const
	{
		return _endClock;
	}

private:
	SimulatedGNSSSensor& _sensor;
	State _state;
	Poco::Timestamp _startTime;
	std::string _lat;
	std::string _lon;
	std::string _ele;
	std::string _time;
	Poco::Clock _startClock;
	Poco::Clock _endClock;
};

SimulatedGNSSSensor::SimulatedGNSSSensor(const Params& params):
	_gpxPath(params.gpxPath),
	_loopReplay(params.loopReplay),
	_speedUp(params.speedUp),
	_lastValidPosition(0),
	_lastPositionUpdate(0),
	_positionAvailable(false),
	_course(-1),
	_speed(-1),
	_altitude(-9999),
	_period(0),
	_delta(0),
	_timeout(30000),
	_logger(Poco::Logger::get("IoT.Simulation.SimulatedGNSSSensor"))
{
	addProperty("symbolicName", &SimulatedGNSSSensor::getSymbolicName);
	addProperty("name", &SimulatedGNSSSensor::getName);
	addProperty("displayValue", &SimulatedGNSSSensor::getDisplayValue);
	addProperty("positionChangedPeriod", &SimulatedGNSSSensor::getPositionChangedPeriod, &SimulatedGNSSSensor::setPositionChangedPeriod);
	addProperty("positionChangedDelta", &SimulatedGNSSSensor::getPositionChangedDelta, &SimulatedGNSSSensor::setPositionChangedDelta);
	addProperty("positionTimeout", &SimulatedGNSSSensor::getPositionTimeout, &SimulatedGNSSSensor::setPositionTimeout);
	addProperty("gpxPath", &SimulatedGNSSSensor::getGPXPath);

	loadGPX(_gpxPath);
}

SimulatedGNSSSensor::~SimulatedGNSSSensor()
{
	try
	{
		stop();
	}
	catch (Poco::Exception&)
	{
		poco_unexpected();
	}
}

void SimulatedGNSSSensor::stop()
{
	_timer.cancel(true);
}

bool SimulatedGNSSSensor::positionAvailable() const
{
	Poco::FastMutex::ScopedLock lock(_mutex);

	return _positionAvailable;
}

IoT::Devices::LatLon SimulatedGNSSSensor::position() const
{
	Poco::FastMutex::ScopedLock lock(_mutex);

	return _position;
}

double SimulatedGNSSSensor::course() const
{
	Poco::FastMutex::ScopedLock lock(_mutex);

	return _course;
}

double SimulatedGNSSSensor::speed() const
{
	Poco::FastMutex::ScopedLock lock(_mutex);

	return _speed;
}

double SimulatedGNSSSensor::magneticVariation() const
{
	return -1;
}

double SimulatedGNSSSensor::altitude() const
{
	Poco::FastMutex::ScopedLock lock(_mutex);

	return _altitude;
}

double SimulatedGNSSSensor::hdop() const
{
	return -9999;
}

Poco::Any SimulatedGNSSSensor::getName(const std::string&) const
{
	return NAME;
}

Poco::Any SimulatedGNSSSensor::getSymbolicName(const std::string&) const
{
	return SYMBOLIC_NAME;
}

Poco::Any SimulatedGNSSSensor::getGPXPath(const std::string&) const
{
	return _gpxPath;
}

Poco::Any SimulatedGNSSSensor::getPositionChangedPeriod(const std::string&) const
{
	Poco::FastMutex::ScopedLock lock(_mutex);

	return Poco::Any(_period);
}

void SimulatedGNSSSensor::setPositionChangedPeriod(const std::string&, const Poco::Any& value)
{
	Poco::FastMutex::ScopedLock lock(_mutex);

	_period = Poco::AnyCast<int>(value);
}

Poco::Any SimulatedGNSSSensor::getPositionChangedDelta(const std::string&) const
{
	Poco::FastMutex::ScopedLock lock(_mutex);

	return Poco::Any(_delta);
}

void SimulatedGNSSSensor::setPositionChangedDelta(const std::string&, const Poco::Any& value)
{
	Poco::FastMutex::ScopedLock lock(_mutex);

	_delta = Poco::AnyCast<double>(value);
}

Poco::Any SimulatedGNSSSensor::getPositionTimeout(const std::string&) const
{
	Poco::FastMutex::ScopedLock lock(_mutex);

	return Poco::Any(_timeout);
}

void SimulatedGNSSSensor::setPositionTimeout(const std::string&, const Poco::Any& value)
{
	Poco::FastMutex::ScopedLock lock(_mutex);

	_timeout = Poco::AnyCast<int>(value);
}

Poco::Any SimulatedGNSSSensor::getDisplayValue(const std::string&) const
{
	if (positionAvailable())
	{
		IoT::Devices::LatLon latLon = position();
		std::string value = Poco::format("%02.6f,%03.6f", latLon.latitude, latLon.longitude);
		double knots = speed();
		if (knots >= 0) Poco::format(value, " %.2f kn", knots);
		double degrees = course();
		if (degrees >= 0) Poco::format(value, " %.1f°", degrees);
		return value;
	}
	else return std::string("n/a");
}

void SimulatedGNSSSensor::updatePosition(const Poco::Geo::LatLon& latLon, double altitude)
{
	if (_logger.debug())
	{
		_logger.debug(Poco::format("Updating position to lat=%f, lon=%f, alt=%f",
			latLon.latitude().degrees(),
			latLon.longitude().degrees(),
			altitude));
	}

	IoT::Devices::PositionUpdate event;
	IoT::Devices::LatLon lastPosition;
	double delta = 0;
	double period = 0;
	double distance = 0;
	Poco::Geo::LatLon lastLatLon;
	{
		Poco::FastMutex::ScopedLock lock(_mutex);

		delta = _delta;
		period = _period;

		double diffSecs = static_cast<double>(_lastValidPosition.elapsed())/Poco::Timestamp::resolution();

		_lastValidPosition.update();
		_positionAvailable = true;

		lastPosition = _position;

		lastLatLon.assign(
			Poco::Geo::Angle::fromDegrees(lastPosition.latitude),
			Poco::Geo::Angle::fromDegrees(lastPosition.longitude));

		Poco::Geo::Angle gcd = lastLatLon.greatCircleDistanceTo(latLon);
		distance = gcd.radians()*Poco::Geo::LatLon::EARTH_MEAN_RADIUS;

		_position.latitude = latLon.latitude().degrees();
		_position.longitude = latLon.longitude().degrees();
		_altitude = altitude;
		_course = Poco::Geo::Angle::normalizedDegrees(lastLatLon.greatCircleAzimuthTo(latLon).degrees());
		_speed = diffSecs > 0 ? distance/diffSecs*1.943844 : _speed; // m/s to kt
	}

	try
	{
		if (_lastPositionUpdate.isElapsed(1000*Poco::Timestamp::TimeVal(period)))
		{
			bool needUpdate = delta == 0;
			if (!needUpdate)
			{
				needUpdate = distance >= delta;
			}
			if (needUpdate)
			{
				_lastPositionUpdate.update();

				event.position = _position;
				event.course   = _course;
				event.speed    = _speed;
				event.magneticVariation = -1;
				event.timestamp.update();

				positionUpdate(this, event);
			}
		}
	}
	catch (Poco::Exception& exc)
	{
		_logger.log(exc);
	}
}

void SimulatedGNSSSensor::loadGPX(const std::string& path)
{
	_logger.information("Loading GNSS track from GPX file: " + path);

	GPXHandler gpxHandler(*this);

	Poco::XML::SAXParser parser;
	parser.setFeature(Poco::XML::XMLReader::FEATURE_NAMESPACES, true);
	parser.setFeature(Poco::XML::XMLReader::FEATURE_NAMESPACE_PREFIXES, true);
	parser.setContentHandler(&gpxHandler);

	parser.parse(path);

	if (_loopReplay)
	{
		_timer.schedule(new LoadGPX(*this, path), gpxHandler.endClock());
	}
}

} } // namespace IoT::Simulation
<|endoftext|>


<commit_before>#include "InputParticle.h"

#include <maya/MFnAttribute.h>
#include <maya/MFnParticleSystem.h>
#include <maya/MDoubleArray.h>
#include <maya/MGlobal.h>
#include <maya/MMatrix.h>
#include <maya/MPlug.h>
#include <maya/MPlugArray.h>
#include <maya/MStringArray.h>
#include <maya/MVectorArray.h>

#include "hapiutil.h"
#include "util.h"

InputParticle::InputParticle(int assetId, int inputIdx) :
    Input(assetId, inputIdx),
    myInputAssetId(0)
{
    Util::PythonInterpreterLock pythonInterpreterLock;

    CHECK_HAPI(HAPI_CreateInputAsset(
                Util::theHAPISession.get(),
                &myInputAssetId,
                NULL
                ));

    if(!Util::statusCheckLoop())
    {
        DISPLAY_ERROR(MString("Unexpected error when creating input asset."));
    }

    myInputObjectId = 0;
    myInputGeoId = 0;

    CHECK_HAPI(HAPI_ConnectAssetGeometry(
                Util::theHAPISession.get(),
                myInputAssetId, myInputObjectId,
                myAssetId, myInputIdx
                ));
}

InputParticle::~InputParticle()
{
    CHECK_HAPI(HAPI_DestroyAsset(
                Util::theHAPISession.get(),
                myInputAssetId
                ));
}

Input::AssetInputType
InputParticle::assetInputType() const
{
    return Input::AssetInputType_Particle;
}

void
InputParticle::setInputTransform(MDataHandle &dataHandle)
{
    MMatrix transformMatrix = dataHandle.asMatrix();

    float matrix[16];
    transformMatrix.get(reinterpret_cast<float(*)[4]>(matrix));

    HAPI_TransformEuler transformEuler;
    HAPI_ConvertMatrixToEuler(
            Util::theHAPISession.get(),
            matrix,
            HAPI_SRT,
            HAPI_XYZ,
            &transformEuler
            );
    HAPI_SetObjectTransform(
            Util::theHAPISession.get(),
            myInputAssetId,
            myInputObjectId,
            &transformEuler
            );
}

void
InputParticle::setAttributePointData(
        const char* attributeName,
        HAPI_StorageType storage,
        int count,
        int tupleSize,
        void* data
        )
{
    HAPI_AttributeInfo attributeInfo;
    attributeInfo.exists = true;
    attributeInfo.owner = HAPI_ATTROWNER_POINT;
    attributeInfo.storage = storage;
    attributeInfo.count = count;
    attributeInfo.tupleSize = tupleSize;

    HAPI_AddAttribute(
            Util::theHAPISession.get(),
            myInputAssetId, myInputObjectId, myInputGeoId,
            attributeName,
            &attributeInfo
            );

    switch(storage)
    {
        case HAPI_STORAGETYPE_FLOAT:
            HAPI_SetAttributeFloatData(
                    Util::theHAPISession.get(),
                    myInputAssetId, myInputObjectId, myInputGeoId,
                    attributeName,
                    &attributeInfo,
                    static_cast<float*>(data),
                    0, count
                    );
            break;
        case HAPI_STORAGETYPE_INT:
            HAPI_SetAttributeIntData(
                    Util::theHAPISession.get(),
                    myInputAssetId, myInputObjectId, myInputGeoId,
                    attributeName,
                    &attributeInfo,
                    static_cast<int*>(data),
                    0, count
                    );
            break;
        default:
            break;
    }
}

void
InputParticle::setInputGeo(
        MDataBlock &dataBlock,
        const MPlug &plug
        )
{
    MStatus status;

    // get particle node
    MObject particleObj;
    {
        MPlugArray plugArray;
        plug.connectedTo(plugArray, true, false, &status);
        CHECK_MSTATUS(status);

        if(plugArray.length() == 1)
        {
            particleObj = plugArray[0].node();
        }
    }

    if(particleObj.isNull())
    {
        return;
    }

    MFnParticleSystem particleFn(particleObj);

    // get original particle node
    MObject originalParticleObj;
    // status parameter is needed due to a bug in Maya API
    if(particleFn.isDeformedParticleShape(&status))
    {
        originalParticleObj = particleFn.originalParticleShape(&status);
    }
    else
    {
        originalParticleObj = particleObj;
    }

    MFnParticleSystem originalParticleFn(originalParticleObj);

    // set up part info
    HAPI_PartInfo partInfo;
    HAPI_PartInfo_Init(&partInfo);
    partInfo.id = 0;
    partInfo.faceCount = 0;
    partInfo.vertexCount = 0;
    partInfo.pointCount = particleFn.count();

    HAPI_SetPartInfo(
            Util::theHAPISession.get(),
            myInputAssetId, myInputObjectId, myInputGeoId,
            &partInfo
            );

    // set per-particle attributes
    {
        // shared buffers for passing data
        MVectorArray vectorArray;
        MDoubleArray doubleArray;
        char* data = new char[sizeof(float) * 3 * particleFn.count()];

        // id
        {
            MIntArray ids;
            // Must get the IDs from the original particle node. Maya will
            // crash if we try to get the IDs from the deformed particle node.
            originalParticleFn.particleIds(ids);

            CHECK_HAPI(hapiSetPointAttribute(
                    myInputAssetId, myInputObjectId, myInputGeoId,
                    1,
                    "id",
                    ids
                    ));
        }

        // 0: vector
        // 1: double
        for(int i = 0; i < 2; i++)
        {
            int tupleSize = 0;
            MString getAttributesCommand = "particle -q ";
            switch(i)
            {
                case 0:
                    tupleSize = 3;
                    getAttributesCommand += "-perParticleVector ";
                    break;
                case 1:
                    tupleSize = 1;
                    getAttributesCommand += "-perParticleDouble ";
                    break;
            }

            // query the original particle for names of the per-particle attributes
            getAttributesCommand += originalParticleFn.fullPathName();

            // get the per-particle attribute names
            MStringArray attributeNames;
            MGlobal::executeCommand(getAttributesCommand, attributeNames);

            // explicitly include some special per-particle attributes that
            // aren't returned by the MEL command
            switch(i)
            {
                case 0:
                    break;
                case 1:
                    attributeNames.append("age");
                    break;
            }

            for(unsigned int j = 0; j < attributeNames.length(); j++)
            {
                const MString attributeName = attributeNames[j];

                MObject attributeObj = originalParticleFn.attribute(attributeName);
                if(attributeObj.isNull())
                {
                    continue;
                }

                // mimics "listAttr -v -w" from AEokayAttr
                MFnAttribute attributeFn(attributeObj);
                if(!(!attributeFn.isHidden() && attributeFn.isWritable())
                        && (attributeName != "age"
                            && attributeName != "finalLifespanPP"))
                {
                    continue;
                }

                // get the per-particle data
                switch(i)
                {
                    case 0:
                        if(attributeName == "position")
                        {
                            // Need to use position() so that we get the right
                            // positions in the case of deformed particles.
                            particleFn.position(vectorArray);
                        }
                        else
                        {
                            // Maya will automatically use the original
                            // particle node in the case of deformed particles.
                            particleFn.getPerParticleAttribute(
                                    attributeName,
                                    vectorArray
                                    );
                        }

                        vectorArray.get(reinterpret_cast<float(*)[3]>(data));
                        break;
                    case 1:
                        // Maya will automatically use the original
                        // particle node in the case of deformed particles.
                        particleFn.getPerParticleAttribute(
                                attributeName,
                                doubleArray
                                );

                        doubleArray.get(reinterpret_cast<float(*)>(data));
                        break;
                }

                // map the parameter name
                const char* parameterName = attributeName.asChar();
                switch(i)
                {
                    case 0:
                        if(strcmp(parameterName, "position") == 0)
                        {
                            parameterName = "P";
                        }
                        else if(strcmp(parameterName, "velocity") == 0)
                        {
                            parameterName = "v";
                        }
                        else if(strcmp(parameterName, "acceleration") == 0)
                        {
                            parameterName = "force";
                        }
                        else if(strcmp(parameterName, "rgbPP") == 0)
                        {
                            parameterName = "Cd";
                        }
                        break;
                    case 1:
                        if(strcmp(parameterName, "opacityPP") == 0)
                        {
                            parameterName = "Alpha";
                        }
                        else if(strcmp(parameterName, "radiusPP") == 0)
                        {
                            parameterName = "pscale";
                        }
                        else if(strcmp(parameterName, "finalLifespanPP") == 0)
                        {
                            parameterName = "life";
                        }
                        break;
                }

                setAttributePointData(
                        parameterName,
                        HAPI_STORAGETYPE_FLOAT,
                        particleFn.count(),
                        tupleSize,
                        data
                        );
            }

            switch(i)
            {
                case 0:
                    vectorArray.clear();
                    break;
                case 1:
                    doubleArray.clear();
                    break;
            }
        }

        delete [] data;
    }

    Input::setInputPlugMetaData(
            plug,
            myInputAssetId, myInputObjectId, myInputGeoId
            );

    // Commit it
    HAPI_CommitGeo(
            Util::theHAPISession.get(),
            myInputAssetId, myInputObjectId, myInputGeoId
            );
}
<commit_msg>Unroll the vector/double loop for processing per-particle attributes<commit_after>#include "InputParticle.h"

#include <maya/MFnAttribute.h>
#include <maya/MFnParticleSystem.h>
#include <maya/MDoubleArray.h>
#include <maya/MGlobal.h>
#include <maya/MMatrix.h>
#include <maya/MPlug.h>
#include <maya/MPlugArray.h>
#include <maya/MStringArray.h>
#include <maya/MVectorArray.h>

#include "hapiutil.h"
#include "util.h"

InputParticle::InputParticle(int assetId, int inputIdx) :
    Input(assetId, inputIdx),
    myInputAssetId(0)
{
    Util::PythonInterpreterLock pythonInterpreterLock;

    CHECK_HAPI(HAPI_CreateInputAsset(
                Util::theHAPISession.get(),
                &myInputAssetId,
                NULL
                ));

    if(!Util::statusCheckLoop())
    {
        DISPLAY_ERROR(MString("Unexpected error when creating input asset."));
    }

    myInputObjectId = 0;
    myInputGeoId = 0;

    CHECK_HAPI(HAPI_ConnectAssetGeometry(
                Util::theHAPISession.get(),
                myInputAssetId, myInputObjectId,
                myAssetId, myInputIdx
                ));
}

InputParticle::~InputParticle()
{
    CHECK_HAPI(HAPI_DestroyAsset(
                Util::theHAPISession.get(),
                myInputAssetId
                ));
}

Input::AssetInputType
InputParticle::assetInputType() const
{
    return Input::AssetInputType_Particle;
}

void
InputParticle::setInputTransform(MDataHandle &dataHandle)
{
    MMatrix transformMatrix = dataHandle.asMatrix();

    float matrix[16];
    transformMatrix.get(reinterpret_cast<float(*)[4]>(matrix));

    HAPI_TransformEuler transformEuler;
    HAPI_ConvertMatrixToEuler(
            Util::theHAPISession.get(),
            matrix,
            HAPI_SRT,
            HAPI_XYZ,
            &transformEuler
            );
    HAPI_SetObjectTransform(
            Util::theHAPISession.get(),
            myInputAssetId,
            myInputObjectId,
            &transformEuler
            );
}

void
InputParticle::setAttributePointData(
        const char* attributeName,
        HAPI_StorageType storage,
        int count,
        int tupleSize,
        void* data
        )
{
    HAPI_AttributeInfo attributeInfo;
    attributeInfo.exists = true;
    attributeInfo.owner = HAPI_ATTROWNER_POINT;
    attributeInfo.storage = storage;
    attributeInfo.count = count;
    attributeInfo.tupleSize = tupleSize;

    HAPI_AddAttribute(
            Util::theHAPISession.get(),
            myInputAssetId, myInputObjectId, myInputGeoId,
            attributeName,
            &attributeInfo
            );

    switch(storage)
    {
        case HAPI_STORAGETYPE_FLOAT:
            HAPI_SetAttributeFloatData(
                    Util::theHAPISession.get(),
                    myInputAssetId, myInputObjectId, myInputGeoId,
                    attributeName,
                    &attributeInfo,
                    static_cast<float*>(data),
                    0, count
                    );
            break;
        case HAPI_STORAGETYPE_INT:
            HAPI_SetAttributeIntData(
                    Util::theHAPISession.get(),
                    myInputAssetId, myInputObjectId, myInputGeoId,
                    attributeName,
                    &attributeInfo,
                    static_cast<int*>(data),
                    0, count
                    );
            break;
        default:
            break;
    }
}

void
InputParticle::setInputGeo(
        MDataBlock &dataBlock,
        const MPlug &plug
        )
{
    MStatus status;

    // get particle node
    MObject particleObj;
    {
        MPlugArray plugArray;
        plug.connectedTo(plugArray, true, false, &status);
        CHECK_MSTATUS(status);

        if(plugArray.length() == 1)
        {
            particleObj = plugArray[0].node();
        }
    }

    if(particleObj.isNull())
    {
        return;
    }

    MFnParticleSystem particleFn(particleObj);

    // get original particle node
    MObject originalParticleObj;
    // status parameter is needed due to a bug in Maya API
    if(particleFn.isDeformedParticleShape(&status))
    {
        originalParticleObj = particleFn.originalParticleShape(&status);
    }
    else
    {
        originalParticleObj = particleObj;
    }

    MFnParticleSystem originalParticleFn(originalParticleObj);

    // set up part info
    HAPI_PartInfo partInfo;
    HAPI_PartInfo_Init(&partInfo);
    partInfo.id = 0;
    partInfo.faceCount = 0;
    partInfo.vertexCount = 0;
    partInfo.pointCount = particleFn.count();

    HAPI_SetPartInfo(
            Util::theHAPISession.get(),
            myInputAssetId, myInputObjectId, myInputGeoId,
            &partInfo
            );

    // set per-particle attributes
    {
        // id
        {
            MIntArray ids;
            // Must get the IDs from the original particle node. Maya will
            // crash if we try to get the IDs from the deformed particle node.
            originalParticleFn.particleIds(ids);

            CHECK_HAPI(hapiSetPointAttribute(
                    myInputAssetId, myInputObjectId, myInputGeoId,
                    1,
                    "id",
                    ids
                    ));
        }

        // vector attributes
        {
            MVectorArray vectorArray;

            // query the original particle for names of the per-particle
            // attributes
            MString getAttributesCommand = "particle -q -perParticleVector ";
            getAttributesCommand += originalParticleFn.fullPathName();

            // get the per-particle attribute names
            MStringArray attributeNames;
            MGlobal::executeCommand(getAttributesCommand, attributeNames);

            for(unsigned int i = 0; i < attributeNames.length(); i++)
            {
                const MString attributeName = attributeNames[i];

                MObject attributeObj = originalParticleFn.attribute(attributeName);
                if(attributeObj.isNull())
                {
                    continue;
                }

                // mimics "listAttr -v -w" from AEokayAttr
                MFnAttribute attributeFn(attributeObj);
                if(!(!attributeFn.isHidden() && attributeFn.isWritable())
                        && (attributeName != "age"
                            && attributeName != "finalLifespanPP"))
                {
                    continue;
                }

                // get the per-particle data
                if(attributeName == "position")
                {
                    // Need to use position() so that we get the right
                    // positions in the case of deformed particles.
                    particleFn.position(vectorArray);
                }
                else
                {
                    // Maya will automatically use the original
                    // particle node in the case of deformed particles.
                    particleFn.getPerParticleAttribute(
                            attributeName,
                            vectorArray
                            );
                }

                // When particle node is initially loaded from a scene file, if
                // the attribute is driven by expressions, then
                // MFnParticleSystem doesn't initially seem to have data.
                if(partInfo.pointCount != (int) vectorArray.length())
                {
                    vectorArray.setLength(partInfo.pointCount);
                }

                // map the attribute name
                const char* mappedAttributeName = attributeName.asChar();
                if(strcmp(mappedAttributeName, "position") == 0)
                {
                    mappedAttributeName = "P";
                }
                else if(strcmp(mappedAttributeName, "velocity") == 0)
                {
                    mappedAttributeName = "v";
                }
                else if(strcmp(mappedAttributeName, "acceleration") == 0)
                {
                    mappedAttributeName = "force";
                }
                else if(strcmp(mappedAttributeName, "rgbPP") == 0)
                {
                    mappedAttributeName = "Cd";
                }

                CHECK_HAPI(hapiSetPointAttribute(
                            myInputAssetId, myInputObjectId, myInputGeoId,
                            3,
                            mappedAttributeName,
                            Util::reshapeArray<
                                3,
                                std::vector<double>
                                >(vectorArray)
                            ));
            }
        }

        // double attributes
        {
            MDoubleArray doubleArray;

            // query the original particle for names of the per-particle
            // attributes
            MString getAttributesCommand = "particle -q -perParticleDouble ";
            getAttributesCommand += originalParticleFn.fullPathName();

            // get the per-particle attribute names
            MStringArray attributeNames;
            MGlobal::executeCommand(getAttributesCommand, attributeNames);

            // explicitly include some special per-particle attributes that
            // aren't returned by the MEL command
            attributeNames.append("age");

            for(unsigned int i = 0; i < attributeNames.length(); i++)
            {
                const MString attributeName = attributeNames[i];

                MObject attributeObj = originalParticleFn.attribute(attributeName);
                if(attributeObj.isNull())
                {
                    continue;
                }

                // mimics "listAttr -v -w" from AEokayAttr
                MFnAttribute attributeFn(attributeObj);
                if(!(!attributeFn.isHidden() && attributeFn.isWritable())
                        && (attributeName != "age"
                            && attributeName != "finalLifespanPP"))
                {
                    continue;
                }

                // get the per-particle data
                // Maya will automatically use the original
                // particle node in the case of deformed particles.
                particleFn.getPerParticleAttribute(
                        attributeName,
                        doubleArray
                        );

                // When particle node is initially loaded from a scene file, if
                // the attribute is driven by expressions, then
                // MFnParticleSystem doesn't initially seem to have data.
                if(partInfo.pointCount != (int) doubleArray.length())
                {
                    doubleArray.setLength(partInfo.pointCount);
                }

                // map the parameter name
                const char* mappedAttributeName = attributeName.asChar();
                if(strcmp(mappedAttributeName, "opacityPP") == 0)
                {
                    mappedAttributeName = "Alpha";
                }
                else if(strcmp(mappedAttributeName, "radiusPP") == 0)
                {
                    mappedAttributeName = "pscale";
                }
                else if(strcmp(mappedAttributeName, "finalLifespanPP") == 0)
                {
                    mappedAttributeName = "life";
                }

                CHECK_HAPI(hapiSetPointAttribute(
                            myInputAssetId, myInputObjectId, myInputGeoId,
                            1,
                            mappedAttributeName,
                            doubleArray
                            ));
            }
        }
    }

    Input::setInputPlugMetaData(
            plug,
            myInputAssetId, myInputObjectId, myInputGeoId
            );

    // Commit it
    HAPI_CommitGeo(
            Util::theHAPISession.get(),
            myInputAssetId, myInputObjectId, myInputGeoId
            );
}
<|endoftext|>


<commit_before>/* ---------------------------------------------------------------------
 * Numenta Platform for Intelligent Computing (NuPIC)
 * Copyright (C) 2016, Numenta, Inc.  Unless you have an agreement
 * with Numenta, Inc., for a separate license for this software code, the
 * following terms and conditions apply:
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero Public License version 3 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero Public License for more details.
 *
 * You should have received a copy of the GNU Affero Public License
 * along with this program.  If not, see http://www.gnu.org/licenses.
 *
 * http://numenta.org/licenses/
 * ---------------------------------------------------------------------
 */

/** @file
 * Implementation of Network test
 */

#include "gtest/gtest.h"

#include <nupic/engine/NetworkFactory.hpp>
#include <nupic/engine/Network.hpp>
#include <nupic/engine/NuPIC.hpp>
#include <nupic/engine/Region.hpp>
#include <nupic/os/Path.hpp>
#include <nupic/utils/Log.hpp>

using namespace nupic;

// macro to ease nesting
#define GP Path::getParent

const std::string UNIT_TESTS_EXECUTABLE_PATH = Path::getExecutablePath();
const std::string NUPIC_CORE_PATH =  GP(GP(GP(GP(UNIT_TESTS_EXECUTABLE_PATH))));
const std::string PATH_TO_FIXTURES = Path::join(NUPIC_CORE_PATH, "/src/test/unit/engine/fixtures");

TEST(NetworkFactory, ValidYamlTest)
{
  NetworkFactory nf;
  Network *n;
  n = nf.createNetwork(Path::join(PATH_TO_FIXTURES, "network.yaml"));

  const Collection<Region*> regionList = n->getRegions();
  ASSERT_EQ((UInt32)3, regionList.getCount());

  // make sure no region specified in the yaml is null.
  Region *l1, *l2, *l3;
  l1 = regionList.getByName("level 1");
  l2 = regionList.getByName("level 2");
  l3 = regionList.getByName("level 3");

  ASSERT_TRUE(l1);
  ASSERT_TRUE(l2);
  ASSERT_TRUE(l3);

  ASSERT_TRUE(l1->getOutput("bottomUpOut"));
  ASSERT_TRUE(l2->getOutput("bottomUpOut"));
  ASSERT_TRUE(l3->getOutput("bottomUpOut"));

  ASSERT_TRUE(l1->getInput("bottomUpIn"));
  ASSERT_TRUE(l2->getInput("bottomUpIn"));
  ASSERT_TRUE(l3->getInput("bottomUpIn"));

  delete n;  //make sure to unregister the network.
}

TEST(NetworkFactory, MissingLinkFieldsFile)
{
  NetworkFactory nf;
  EXPECT_THROW(nf.createNetwork(Path::join(PATH_TO_FIXTURES, "missing-link-fields.yaml")),
    std::exception);
}

TEST(NetworkFactory, MissingRegionFieldsFile)
{
  NetworkFactory nf;
  EXPECT_THROW(nf.createNetwork(Path::join(PATH_TO_FIXTURES, "missing-region-fields.yaml")),
    std::exception);
}

TEST(NetworkFactory, ExtraFieldFile)
{
  NetworkFactory nf;
  EXPECT_THROW(nf.createNetwork(Path::join(PATH_TO_FIXTURES, "extra-yaml-fields.yaml")),
    std::exception);
}

TEST(NetworkFactory, NoRegionsFile)
{
  NetworkFactory nf;
  EXPECT_THROW(nf.createNetwork(Path::join(PATH_TO_FIXTURES, "no-regions.yaml")),
    std::exception);
}

TEST(NetworkFactory, NoLinksFile)
{
  NetworkFactory nf;
  EXPECT_THROW(nf.createNetwork(Path::join(PATH_TO_FIXTURES, "no-links.yaml")),
    std::exception);
}
<commit_msg>fixes path issue in tests<commit_after>/* ---------------------------------------------------------------------
 * Numenta Platform for Intelligent Computing (NuPIC)
 * Copyright (C) 2016, Numenta, Inc.  Unless you have an agreement
 * with Numenta, Inc., for a separate license for this software code, the
 * following terms and conditions apply:
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero Public License version 3 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero Public License for more details.
 *
 * You should have received a copy of the GNU Affero Public License
 * along with this program.  If not, see http://www.gnu.org/licenses.
 *
 * http://numenta.org/licenses/
 * ---------------------------------------------------------------------
 */

/** @file
 * Implementation of Network test
 */

#include "gtest/gtest.h"

#include <nupic/engine/NetworkFactory.hpp>
#include <nupic/engine/Network.hpp>
#include <nupic/engine/NuPIC.hpp>
#include <nupic/engine/Region.hpp>
#include <nupic/os/Path.hpp>
#include <nupic/utils/Log.hpp>

using namespace nupic;

// macro to ease nesting
#define GP Path::getParent

const std::string UNIT_TESTS_EXECUTABLE_PATH = Path::getExecutablePath();
const std::string NUPIC_CORE_PATH =  GP(GP(GP(GP(UNIT_TESTS_EXECUTABLE_PATH))));
const std::string PATH_TO_FIXTURES = Path::join(NUPIC_CORE_PATH, "src/test/unit/engine/fixtures");

TEST(NetworkFactory, ValidYamlTest)
{
  NetworkFactory nf;
  Network *n;
  n = nf.createNetwork(Path::join(PATH_TO_FIXTURES, "network.yaml"));

  const Collection<Region*> regionList = n->getRegions();
  ASSERT_EQ((UInt32)3, regionList.getCount());

  // make sure no region specified in the yaml is null.
  Region *l1, *l2, *l3;
  l1 = regionList.getByName("level 1");
  l2 = regionList.getByName("level 2");
  l3 = regionList.getByName("level 3");

  ASSERT_TRUE(l1);
  ASSERT_TRUE(l2);
  ASSERT_TRUE(l3);

  ASSERT_TRUE(l1->getOutput("bottomUpOut"));
  ASSERT_TRUE(l2->getOutput("bottomUpOut"));
  ASSERT_TRUE(l3->getOutput("bottomUpOut"));

  ASSERT_TRUE(l1->getInput("bottomUpIn"));
  ASSERT_TRUE(l2->getInput("bottomUpIn"));
  ASSERT_TRUE(l3->getInput("bottomUpIn"));

  delete n;  //make sure to unregister the network.
}

TEST(NetworkFactory, MissingLinkFieldsFile)
{
  NetworkFactory nf;
  EXPECT_THROW(nf.createNetwork(Path::join(PATH_TO_FIXTURES, "missing-link-fields.yaml")),
    std::exception);
}

TEST(NetworkFactory, MissingRegionFieldsFile)
{
  NetworkFactory nf;
  EXPECT_THROW(nf.createNetwork(Path::join(PATH_TO_FIXTURES, "missing-region-fields.yaml")),
    std::exception);
}

TEST(NetworkFactory, ExtraFieldFile)
{
  NetworkFactory nf;
  EXPECT_THROW(nf.createNetwork(Path::join(PATH_TO_FIXTURES, "extra-yaml-fields.yaml")),
    std::exception);
}

TEST(NetworkFactory, NoRegionsFile)
{
  NetworkFactory nf;
  EXPECT_THROW(nf.createNetwork(Path::join(PATH_TO_FIXTURES, "no-regions.yaml")),
    std::exception);
}

TEST(NetworkFactory, NoLinksFile)
{
  NetworkFactory nf;
  EXPECT_THROW(nf.createNetwork(Path::join(PATH_TO_FIXTURES, "no-links.yaml")),
    std::exception);
}
<|endoftext|>


<commit_before>/*
 * stomp_planner_manager.cpp
 *
 *  Created on: April 5, 2016
 *      Author: Jorge Nicho
 */

#include <class_loader/class_loader.h>
#include <stomp_moveit/stomp_planner_manager.h>
#include <stomp_moveit/stomp_planner.h>

namespace stomp_moveit
{

StompPlannerManager::StompPlannerManager():
    planning_interface::PlannerManager(),
    nh_("~")
{

}

StompPlannerManager::~StompPlannerManager()
{

}

bool StompPlannerManager::initialize(const robot_model::RobotModelConstPtr &model, const std::string &ns)
{
  if (!ns.empty())
  {
    nh_ = ros::NodeHandle(ns);
  }

  // each element under 'stomp' should be a group name
  std::map<std::string, XmlRpc::XmlRpcValue> group_config;
  if (!StompPlanner::getConfigData(nh_, group_config))
    return false;

  for(std::map<std::string, XmlRpc::XmlRpcValue>::iterator v = group_config.begin(); v != group_config.end(); v++)
  {
    if(!model->hasJointModelGroup(v->first))
    {
      ROS_ERROR("The robot model does not support the planning group '%s' listed by in the stomp configuration",
                v->first.c_str());
      return false;
    }

    boost::shared_ptr<StompPlanner> planner(new StompPlanner(v->first, v->second, model));
    planners_.insert(std::make_pair(v->first, planner));
  }

  return true;
}

bool StompPlannerManager::canServiceRequest(const moveit_msgs::MotionPlanRequest &req) const
{
  if(planners_.count(req.group_name) == 0)
  {
    return false;
  }

  // Get planner
  boost::shared_ptr<StompPlanner> planner = boost::static_pointer_cast<StompPlanner>(planners_.at(req.group_name));
  return planner->canServiceRequest(req);
}

void StompPlannerManager::getPlanningAlgorithms(std::vector<std::string> &algs) const
{
  algs.clear();
  if(!planners_.empty())
  {
    algs.push_back(planners_.begin()->second->getName());
  }
}

void StompPlannerManager::setPlannerConfigurations(const planning_interface::PlannerConfigurationMap &pcs)
{
  ROS_WARN_STREAM("The "<<__FUNCTION__<<" method is not applicable");
}

planning_interface::PlanningContextPtr StompPlannerManager::getPlanningContext(const planning_scene::PlanningSceneConstPtr &planning_scene,
                                                                               const planning_interface::MotionPlanRequest &req,
                                                                               moveit_msgs::MoveItErrorCodes &error_code) const
{
  error_code.val = moveit_msgs::MoveItErrorCodes::SUCCESS;

  if (req.group_name.empty())
  {
    ROS_ERROR("No group specified to plan for");
    error_code.val = moveit_msgs::MoveItErrorCodes::INVALID_GROUP_NAME;
    return planning_interface::PlanningContextPtr();
  }

  if (!planning_scene)
  {
    ROS_ERROR("No planning scene supplied as input");
    error_code.val = moveit_msgs::MoveItErrorCodes::FAILURE;
    return planning_interface::PlanningContextPtr();
  }

  if(planners_.count(req.group_name) <=0)
  {
    ROS_ERROR("STOMP does not have a planning context for group %s",req.group_name.c_str());
    error_code.val = moveit_msgs::MoveItErrorCodes::FAILURE;
    return planning_interface::PlanningContextPtr();
  }

  // Get planner
  boost::shared_ptr<StompPlanner> planner = boost::static_pointer_cast<StompPlanner>(planners_.at(req.group_name));

  if(!planner->canServiceRequest(req))
  {
    error_code.val = moveit_msgs::MoveItErrorCodes::FAILURE;
    return planning_interface::PlanningContextPtr();
  }

  // Setup Planner
  planner->clear();
  planner->setPlanningScene(planning_scene);
  planner->setMotionPlanRequest(req);

  // Return Planner
  return planner;
}

} /* namespace stomp_moveit_interface */
CLASS_LOADER_REGISTER_CLASS(stomp_moveit::StompPlannerManager, planning_interface::PlannerManager)
<commit_msg>Add/Improve error handling<commit_after>/*
 * stomp_planner_manager.cpp
 *
 *  Created on: April 5, 2016
 *      Author: Jorge Nicho
 */

#include <class_loader/class_loader.h>
#include <stomp_moveit/stomp_planner_manager.h>
#include <stomp_moveit/stomp_planner.h>

namespace stomp_moveit
{

StompPlannerManager::StompPlannerManager():
    planning_interface::PlannerManager(),
    nh_("~")
{

}

StompPlannerManager::~StompPlannerManager()
{

}

bool StompPlannerManager::initialize(const robot_model::RobotModelConstPtr &model, const std::string &ns)
{
  if (!ns.empty())
  {
    nh_ = ros::NodeHandle(ns);
  }

  // each element under 'stomp' should be a group name
  std::map<std::string, XmlRpc::XmlRpcValue> group_config;

  if (!StompPlanner::getConfigData(nh_, group_config))
    return false;

  for(std::map<std::string, XmlRpc::XmlRpcValue>::iterator v = group_config.begin(); v != group_config.end(); v++)
  {
    if(!model->hasJointModelGroup(v->first))
    {
      ROS_ERROR("The robot model does not support the planning group '%s' listed by in the stomp configuration",
                v->first.c_str());
      return false;
    }

    boost::shared_ptr<StompPlanner> planner(new StompPlanner(v->first, v->second, model));
    planners_.insert(std::make_pair(v->first, planner));
  }

  return true;
}

bool StompPlannerManager::canServiceRequest(const moveit_msgs::MotionPlanRequest &req) const
{
  if(planners_.count(req.group_name) == 0)
  {
    return false;
  }

  // Get planner
  boost::shared_ptr<StompPlanner> planner = boost::static_pointer_cast<StompPlanner>(planners_.at(req.group_name));
  return planner->canServiceRequest(req);
}

void StompPlannerManager::getPlanningAlgorithms(std::vector<std::string> &algs) const
{
  algs.clear();
  if(!planners_.empty())
  {
    algs.push_back(planners_.begin()->second->getName());
  }
}

void StompPlannerManager::setPlannerConfigurations(const planning_interface::PlannerConfigurationMap &pcs)
{
  ROS_WARN_STREAM("The "<<__FUNCTION__<<" method is not applicable");
}

planning_interface::PlanningContextPtr StompPlannerManager::getPlanningContext(const planning_scene::PlanningSceneConstPtr &planning_scene,
                                                                               const planning_interface::MotionPlanRequest &req,
                                                                               moveit_msgs::MoveItErrorCodes &error_code) const
{
  error_code.val = moveit_msgs::MoveItErrorCodes::SUCCESS;

  if (req.group_name.empty())
  {
    ROS_ERROR("No group specified to plan for");
    error_code.val = moveit_msgs::MoveItErrorCodes::INVALID_GROUP_NAME;
    return planning_interface::PlanningContextPtr();
  }

  if (!planning_scene)
  {
    ROS_ERROR("No planning scene supplied as input");
    error_code.val = moveit_msgs::MoveItErrorCodes::FAILURE;
    return planning_interface::PlanningContextPtr();
  }

  if(planners_.count(req.group_name) <=0)
  {
    ROS_ERROR("STOMP does not have a planning context for group %s",req.group_name.c_str());
    error_code.val = moveit_msgs::MoveItErrorCodes::FAILURE;
    return planning_interface::PlanningContextPtr();
  }

  // Get planner
  boost::shared_ptr<StompPlanner> planner = boost::static_pointer_cast<StompPlanner>(planners_.at(req.group_name));

  if(!planner->canServiceRequest(req))
  {
    error_code.val = moveit_msgs::MoveItErrorCodes::FAILURE;
    return planning_interface::PlanningContextPtr();
  }

  // Setup Planner
  planner->clear();
  planner->setPlanningScene(planning_scene);
  planner->setMotionPlanRequest(req);

  // Return Planner
  return planner;
}

} /* namespace stomp_moveit_interface */
CLASS_LOADER_REGISTER_CLASS(stomp_moveit::StompPlannerManager, planning_interface::PlannerManager)
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2010 Bolloré telecom
 *
 * Author:
 *	Jeremy Lainé
 *
 * Source:
 *	http://code.google.com/p/qxmpp
 *
 * This file is a part of QXmpp library.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 */

#include "QXmppConstants.h"
#include "QXmppDiscoveryIq.h"
#include "QXmppUtils.h"

#include <QDomElement>

QList<QXmppElement> QXmppDiscoveryIq::getQueryItems() const
{
    return m_queryItems;
}

void QXmppDiscoveryIq::setQueryItems(const QList<QXmppElement> &items)
{
    m_queryItems = items;
}

enum QXmppDiscoveryIq::QueryType QXmppDiscoveryIq::getQueryType() const
{
    return m_queryType;
}

void QXmppDiscoveryIq::setQueryType(enum QXmppDiscoveryIq::QueryType type)
{
    m_queryType = type;
}

bool QXmppDiscoveryIq::isDiscoveryIq( QDomElement &element )
{
    QDomElement queryElement = element.firstChildElement("query");
    return (queryElement.namespaceURI() == ns_disco_info ||
            queryElement.namespaceURI() == ns_disco_items);
}

void QXmppDiscoveryIq::parse( QDomElement &element )
{
    setFrom(element.attribute("from"));
    setTo(element.attribute("to"));
    setTypeFromStr(element.attribute("type"));
    QDomElement queryElement = element.firstChildElement("query");
    if (queryElement.namespaceURI() == ns_disco_items)
        m_queryType = ItemsQuery;
    else
        m_queryType = InfoQuery;

    QDomElement itemElement = queryElement.firstChildElement();
    while (!itemElement.isNull())
    {
        m_queryItems.append(QXmppElement(element));
        itemElement = itemElement.nextSiblingElement();
    }
}

void QXmppDiscoveryIq::toXmlElementFromChild(QXmlStreamWriter *writer) const
{
    writer->writeStartElement("query");
    helperToXmlAddAttribute(writer, "xmlns",
        m_queryType == InfoQuery ? ns_disco_info : ns_disco_items);
    foreach (const QXmppElement &item, m_queryItems)
        item.toXml(writer);
    writer->writeEndElement();
}

<commit_msg>fix typo<commit_after>/*
 * Copyright (C) 2010 Bolloré telecom
 *
 * Author:
 *	Jeremy Lainé
 *
 * Source:
 *	http://code.google.com/p/qxmpp
 *
 * This file is a part of QXmpp library.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 */

#include "QXmppConstants.h"
#include "QXmppDiscoveryIq.h"
#include "QXmppUtils.h"

#include <QDomElement>

QList<QXmppElement> QXmppDiscoveryIq::getQueryItems() const
{
    return m_queryItems;
}

void QXmppDiscoveryIq::setQueryItems(const QList<QXmppElement> &items)
{
    m_queryItems = items;
}

enum QXmppDiscoveryIq::QueryType QXmppDiscoveryIq::getQueryType() const
{
    return m_queryType;
}

void QXmppDiscoveryIq::setQueryType(enum QXmppDiscoveryIq::QueryType type)
{
    m_queryType = type;
}

bool QXmppDiscoveryIq::isDiscoveryIq( QDomElement &element )
{
    QDomElement queryElement = element.firstChildElement("query");
    return (queryElement.namespaceURI() == ns_disco_info ||
            queryElement.namespaceURI() == ns_disco_items);
}

void QXmppDiscoveryIq::parse( QDomElement &element )
{
    setFrom(element.attribute("from"));
    setTo(element.attribute("to"));
    setTypeFromStr(element.attribute("type"));
    QDomElement queryElement = element.firstChildElement("query");
    if (queryElement.namespaceURI() == ns_disco_items)
        m_queryType = ItemsQuery;
    else
        m_queryType = InfoQuery;

    QDomElement itemElement = queryElement.firstChildElement();
    while (!itemElement.isNull())
    {
        m_queryItems.append(QXmppElement(itemElement));
        itemElement = itemElement.nextSiblingElement();
    }
}

void QXmppDiscoveryIq::toXmlElementFromChild(QXmlStreamWriter *writer) const
{
    writer->writeStartElement("query");
    helperToXmlAddAttribute(writer, "xmlns",
        m_queryType == InfoQuery ? ns_disco_info : ns_disco_items);
    foreach (const QXmppElement &item, m_queryItems)
        item.toXml(writer);
    writer->writeEndElement();
}

<|endoftext|>


<commit_before>#include <iostream>
#include <assert.h>
#include "mockclass.hh"
#include "Thread/Mutex.hh"
#include "Thread/Thread.hpp"

int without_mutex()
{
  Mock m;
  LilWrapper::Thread *t1 = new LilWrapper::Thread(&Mock::increment_without_mutex, &m);
  LilWrapper::Thread *t2 = new LilWrapper::Thread(&Mock::increment_without_mutex, &m);

  t1->launch();
  t2->launch();

  delete t1;
  delete t2;
  return (m.getData());
}

int with_mutex()
{
  Mock m;
  LilWrapper::Thread *t1 = new LilWrapper::Thread(&Mock::increment_with_mutex, &m);
  LilWrapper::Thread *t2 = new LilWrapper::Thread(&Mock::increment_with_mutex, &m);

  t1->launch();
  t2->launch();

  delete t1;
  delete t2;
  return (m.getData());
}

int main()
{
  return (0);
}
<commit_msg>Randomize Mutex tests.<commit_after>#include <assert.h>
#include <time.h>
#include <stdlib.h>
#include <vector>
#include "Thread/Mutex.hh"
#include "Thread/Thread.hpp"

int increment_value;

struct mock
{
  int _data;
  LilWrapper::Mutex _access;
};

void increment(struct mock *m)
{
  m->_access.lock();
  for (int i = 0; i < increment_value; i++)
    m->_data++;
  m->_access.unlock();
}

int main()
{
  mock m;

  srand(time(NULL));

  int nb = random() % 30 + 1;
  increment_value = random() % 15 + 1;
  std::vector<LilWrapper::Thread *> threads(nb);

  m._data = 0;
  for (int i = 0; i < nb; i++)
    threads[i] = new LilWrapper::Thread(&increment, &m);

  for (int i = 0; i < nb; i++)
    threads[i]->launch();

  for (int i = 0; i < nb; i++)
    threads[i]->wait();

  assert(m._data == nb * increment_value);
  return (0);
}
<|endoftext|>


<commit_before>

#include "rpi_i2c.hpp"
#include <cossb.hpp>
#include <string>
#include <tuple>

using namespace std;

USE_COMPONENT_INTERFACE(rpi_i2c)

rpi_i2c::rpi_i2c()
:cossb::interface::icomponent(COMPONENT(rpi_i2c)){
	// TODO Auto-generated constructor stub

}

rpi_i2c::~rpi_i2c() {

}

bool rpi_i2c::setup()
{
	int address = get_profile()->get(profile::section::property, "address").asInt(1);
	address = (int)(address/10)*16+(int)(address/10);
	cossb_log->log(log::loglevel::INFO, fmt::format("Set I2C Address : 0x{0:x}", address));

	if(!bcm2835_init())
		return false;

	if(!bcm2835_i2c_begin()){
		cossb_log->log(log::loglevel::ERROR, "I2C Begin");
		return false;
	}

	if(!_read_task.get())
		_read_task = create_task(rpi_i2c::read);

	bcm2835_i2c_setSlaveAddress((unsigned char)address);
	bcm2835_i2c_setClockDivider(_clk_div);

	cossb_log->log(log::loglevel::INFO, fmt::format("Slave Address set to : 0x{0:x}", address));
	cossb_log->log(log::loglevel::INFO, fmt::format("I2C Clock Divider set to : {}", _clk_div));

	return true;
}

bool rpi_i2c::run()
{

	return true;
}

bool rpi_i2c::stop()
{
	bcm2835_i2c_end();

	if(!bcm2835_close())
		return false;

	return true;
}

void rpi_i2c::subscribe(cossb::message* const msg)
{
	//write data via I2C
	switch(msg->get_frame()->type) {
		case cossb::base::msg_type::REQUEST: break;
		case cossb::base::msg_type::DATA: {

			//subscribe emotion data
			try {
			char emotion_data = boost::any_cast<char>(*msg->get_data());
			bcm2835_i2c_write(&(const char)emotion_data, 1);

			cossb_log->log(log::loglevel::INFO, fmt::format("Write 0x{0:x} via I2C", emotion_data));
			}
			catch(const boost::bad_any_cast&){
				cossb_log->log(log::loglevel::ERROR, "Invalid type casting");
			}
		} break;
		case cossb::base::msg_type::RESPONSE: break;
		case cossb::base::msg_type::EVENT:  break;
		}
}

void rpi_i2c::read()
{
	while(1){
		try{
			char readbyte = 0x00;
			unsigned char code = bcm2835_i2c_read(&readbyte, 1);

			switch(code){
			case BCM2835_I2C_REASON_OK: {
				if(readbyte!=0x00){

					//publish data read from i2c
					cossb::message _msg(this, base::msg_type::DATA);
					_msg.set(readbyte);
					cossb_broker->publish("rpi_i2c_read", _msg);
					cossb_log->log(log::loglevel::INFO, "Publish i2c data read");
				}
			}
				break;
			case BCM2835_I2C_REASON_ERROR_NACK: cossb_log->log(log::loglevel::INFO, "Received a NACK"); break;
			case BCM2835_I2C_REASON_ERROR_CLKT: cossb_log->log(log::loglevel::WARN, "Received Clock Stretch Timeout"); break;
			case BCM2835_I2C_REASON_ERROR_DATA: cossb_log->log(log::loglevel::ERROR, "Not all data is sent / received"); break;
			}
		}
		catch(const boost::bad_any_cast&){ }
	}

}

<commit_msg>- update<commit_after>

#include "rpi_i2c.hpp"
#include <cossb.hpp>
#include <string>
#include <tuple>

using namespace std;

USE_COMPONENT_INTERFACE(rpi_i2c)

rpi_i2c::rpi_i2c()
:cossb::interface::icomponent(COMPONENT(rpi_i2c)){
	// TODO Auto-generated constructor stub

}

rpi_i2c::~rpi_i2c() {

}

bool rpi_i2c::setup()
{
	int address = get_profile()->get(profile::section::property, "address").asInt(1);
	address = (int)(address/10)*16+(int)(address/10);
	cossb_log->log(log::loglevel::INFO, fmt::format("Set I2C Address : 0x{0:x}", address));

	if(!bcm2835_init())
		return false;

	if(!bcm2835_i2c_begin()){
		cossb_log->log(log::loglevel::ERROR, "I2C Begin");
		return false;
	}

	if(!_read_task.get())
		_read_task = create_task(rpi_i2c::read);

	bcm2835_i2c_setSlaveAddress((unsigned char)address);
	bcm2835_i2c_setClockDivider(_clk_div);

	cossb_log->log(log::loglevel::INFO, fmt::format("Slave Address set to : 0x{0:x}", address));
	cossb_log->log(log::loglevel::INFO, fmt::format("I2C Clock Divider set to : {}", _clk_div));

	return true;
}

bool rpi_i2c::run()
{

	return true;
}

bool rpi_i2c::stop()
{
	bcm2835_i2c_end();

	if(!bcm2835_close())
		return false;

	return true;
}

void rpi_i2c::subscribe(cossb::message* const msg)
{
	//write data via I2C
	switch(msg->get_frame()->type) {
		case cossb::base::msg_type::REQUEST: break;
		case cossb::base::msg_type::DATA: {

			//subscribe emotion data
			try {
			char emotion_data = boost::any_cast<char>(*msg->get_data());
			bcm2835_i2c_write((const char*)&emotion_data, 1);

			cossb_log->log(log::loglevel::INFO, fmt::format("Write 0x{0:x} via I2C", emotion_data));
			}
			catch(const boost::bad_any_cast&){
				cossb_log->log(log::loglevel::ERROR, "Invalid type casting");
			}
		} break;
		case cossb::base::msg_type::RESPONSE: break;
		case cossb::base::msg_type::EVENT:  break;
		}
}

void rpi_i2c::read()
{
	while(1){
		try{
			char readbyte = 0x00;
			unsigned char code = bcm2835_i2c_read(&readbyte, 1);

			switch(code){
			case BCM2835_I2C_REASON_OK: {
				if(readbyte!=0x00){

					//publish data read from i2c
					cossb::message _msg(this, base::msg_type::DATA);
					_msg.set(readbyte);
					cossb_broker->publish("rpi_i2c_read", _msg);
					cossb_log->log(log::loglevel::INFO, "Publish i2c data read");
				}
			}
				break;
			case BCM2835_I2C_REASON_ERROR_NACK: cossb_log->log(log::loglevel::INFO, "Received a NACK"); break;
			case BCM2835_I2C_REASON_ERROR_CLKT: cossb_log->log(log::loglevel::WARN, "Received Clock Stretch Timeout"); break;
			case BCM2835_I2C_REASON_ERROR_DATA: cossb_log->log(log::loglevel::ERROR, "Not all data is sent / received"); break;
			}
		}
		catch(const boost::bad_any_cast&){ }
	}

}

<|endoftext|>