<commit_before>// Copyright (c) 2011 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 "base/json/json_reader.h"
#include "base/time.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/extensions/apps_promo.h"
#include "chrome/browser/prefs/browser_prefs.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/web_resource/promo_resource_service.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/testing_browser_process.h"
#include "chrome/test/testing_pref_service.h"
#include "chrome/test/testing_profile.h"
#include "testing/gtest/include/gtest/gtest.h"

typedef testing::Test PromoResourceServiceTest;

// Verifies that custom dates read from a web resource server are written to
// the preferences file.
TEST_F(PromoResourceServiceTest, UnpackLogoSignal) {
  // Set up a testing profile and create a promo resource service.
  TestingProfile profile;
  TestingPrefService local_state;
  TestingBrowserProcess* testing_browser_process =
      static_cast<TestingBrowserProcess*>(g_browser_process);
  testing_browser_process->SetPrefService(&local_state);
  browser::RegisterLocalState(&local_state);

  scoped_refptr<PromoResourceService> web_resource_service(
      new PromoResourceService(&profile));

  // Set up start and end dates in a Dictionary as if parsed from the service.
  std::string json = "{ "
                     "  \"topic\": {"
                     "    \"answers\": ["
                     "       {"
                     "        \"name\": \"custom_logo_start\","
                     "        \"inproduct\": \"31/01/10 01:00 GMT\""
                     "       },"
                     "       {"
                     "        \"name\": \"custom_logo_end\","
                     "        \"inproduct\": \"31/01/12 01:00 GMT\""
                     "       }"
                     "    ]"
                     "  }"
                     "}";
  scoped_ptr<DictionaryValue> test_json(static_cast<DictionaryValue*>(
      base::JSONReader::Read(json, false)));

  // Check that prefs are set correctly.
  web_resource_service->UnpackLogoSignal(*(test_json.get()));
  PrefService* prefs = profile.GetPrefs();
  ASSERT_TRUE(prefs != NULL);

  double logo_start =
      prefs->GetDouble(prefs::kNTPCustomLogoStart);
  EXPECT_EQ(logo_start, 1264899600);  // unix epoch for Jan 31 2010 0100 GMT.
  double logo_end =
      prefs->GetDouble(prefs::kNTPCustomLogoEnd);
  EXPECT_EQ(logo_end, 1327971600);  // unix epoch for Jan 31 2012 0100 GMT.

  // Change the start only and recheck.
  json = "{ "
         "  \"topic\": {"
         "    \"answers\": ["
         "       {"
         "        \"name\": \"custom_logo_start\","
         "        \"inproduct\": \"28/02/10 14:00 GMT\""
         "       },"
         "       {"
         "        \"name\": \"custom_logo_end\","
         "        \"inproduct\": \"31/01/12 01:00 GMT\""
         "       }"
         "    ]"
         "  }"
         "}";
  test_json->Clear();
  test_json.reset(static_cast<DictionaryValue*>(
      base::JSONReader::Read(json, false)));

  // Check that prefs are set correctly.
  web_resource_service->UnpackLogoSignal(*(test_json.get()));

  logo_start = prefs->GetDouble(prefs::kNTPCustomLogoStart);
  EXPECT_EQ(logo_start, 1267365600);  // date changes to Feb 28 2010 1400 GMT.

  // If no date is included in the prefs, reset custom logo dates to 0.
  json = "{ "
         "  \"topic\": {"
         "    \"answers\": ["
         "       {"
         "       }"
         "    ]"
         "  }"
         "}";
  test_json->Clear();
  test_json.reset(static_cast<DictionaryValue*>(
      base::JSONReader::Read(json, false)));

  // Check that prefs are set correctly.
  web_resource_service->UnpackLogoSignal(*(test_json.get()));
  logo_start = prefs->GetDouble(prefs::kNTPCustomLogoStart);
  EXPECT_EQ(logo_start, 0);  // date value reset to 0;
  logo_end = prefs->GetDouble(prefs::kNTPCustomLogoEnd);
  EXPECT_EQ(logo_end, 0);  // date value reset to 0;
}

TEST_F(PromoResourceServiceTest, UnpackPromoSignal) {
  // Set up a testing profile and create a promo resource service.
  TestingProfile profile;
  TestingPrefService local_state;
  TestingBrowserProcess* testing_browser_process =
      static_cast<TestingBrowserProcess*>(g_browser_process);
  testing_browser_process->SetPrefService(&local_state);
  browser::RegisterLocalState(&local_state);

  scoped_refptr<PromoResourceService> web_resource_service(
      new PromoResourceService(&profile));

  // Set up start and end dates and promo line in a Dictionary as if parsed
  // from the service.
  std::string json = "{ "
                     "  \"topic\": {"
                     "    \"answers\": ["
                     "       {"
                     "        \"name\": \"promo_start\","
                     "        \"question\": \"3:2\","
                     "        \"tooltip\": \"Eat more pie!\","
                     "        \"inproduct\": \"31/01/10 01:00 GMT\""
                     "       },"
                     "       {"
                     "        \"name\": \"promo_end\","
                     "        \"inproduct\": \"31/01/12 01:00 GMT\""
                     "       }"
                     "    ]"
                     "  }"
                     "}";
  scoped_ptr<DictionaryValue> test_json(static_cast<DictionaryValue*>(
      base::JSONReader::Read(json, false)));

  // Initialize a message loop for this to run on.
  MessageLoop loop;

  // Check that prefs are set correctly.
  web_resource_service->UnpackPromoSignal(*(test_json.get()));
  PrefService* prefs = profile.GetPrefs();
  ASSERT_TRUE(prefs != NULL);

  std::string promo_line = prefs->GetString(prefs::kNTPPromoLine);
  EXPECT_EQ(promo_line, "Eat more pie!");

  int promo_group = prefs->GetInteger(prefs::kNTPPromoGroup);
  EXPECT_GE(promo_group, 0);
  EXPECT_LT(promo_group, 16);

  int promo_build_type = prefs->GetInteger(prefs::kNTPPromoBuild);
  EXPECT_EQ(promo_build_type & PromoResourceService::DEV_BUILD,
            PromoResourceService::DEV_BUILD);
  EXPECT_EQ(promo_build_type & PromoResourceService::BETA_BUILD,
            PromoResourceService::BETA_BUILD);
  EXPECT_EQ(promo_build_type & PromoResourceService::STABLE_BUILD, 0);

  int promo_time_slice = prefs->GetInteger(prefs::kNTPPromoGroupTimeSlice);
  EXPECT_EQ(promo_time_slice, 2);

  double promo_start =
      prefs->GetDouble(prefs::kNTPPromoStart);
  int64 actual_start = 1264899600 +  // unix epoch for Jan 31 2010 0100 GMT.
      promo_group * 2 * 60 * 60;
  EXPECT_EQ(promo_start, actual_start);

  double promo_end =
      prefs->GetDouble(prefs::kNTPPromoEnd);
  EXPECT_EQ(promo_end, 1327971600);  // unix epoch for Jan 31 2012 0100 GMT.
}

TEST_F(PromoResourceServiceTest, UnpackWebStoreSignal) {
  // Set up a testing profile and create a promo resource service.
  TestingProfile profile;
  TestingPrefService local_state;
  TestingBrowserProcess* testing_browser_process =
      static_cast<TestingBrowserProcess*>(g_browser_process);
  testing_browser_process->SetPrefService(&local_state);

  browser::RegisterLocalState(&local_state);

  scoped_refptr<PromoResourceService> web_resource_service(
      new PromoResourceService(&profile));

  web_resource_service->set_channel("dev");

  // Set up start and end dates and promo line in a Dictionary as if parsed
  // from the service.
  std::string json = "{ "
                     "  \"topic\": {"
                     "    \"answers\": ["
                     "       {"
                     "        \"answer_id\": \"341252\","
                     "        \"name\": \"webstore_promo:15\","
                     "        \"question\": \"The header!\","
                     "        \"inproduct_target\": \"The button label!\","
                     "        \"inproduct\": \"http://link.com\","
                     "        \"tooltip\": \"No thanks, hide this.\""
                     "       }"
                     "    ]"
                     "  }"
                     "}";
  scoped_ptr<DictionaryValue> test_json(static_cast<DictionaryValue*>(
      base::JSONReader::Read(json, false)));

  // Initialize a message loop for this to run on.
  MessageLoop loop;

  // Check that prefs are set correctly.
  web_resource_service->UnpackWebStoreSignal(*(test_json.get()));
  PrefService* prefs = profile.GetPrefs();
  ASSERT_TRUE(prefs != NULL);

  EXPECT_EQ("341252", AppsPromo::GetPromoId());
  EXPECT_EQ("The header!", AppsPromo::GetPromoHeaderText());
  EXPECT_EQ("The button label!", AppsPromo::GetPromoButtonText());
  EXPECT_EQ(GURL("http://link.com"), AppsPromo::GetPromoLink());
  EXPECT_EQ("No thanks, hide this.", AppsPromo::GetPromoExpireText());
}

TEST_F(PromoResourceServiceTest, IsBuildTargeted) {
  // canary
  EXPECT_FALSE(PromoResourceService::IsBuildTargeted("canary", 1));
  EXPECT_FALSE(PromoResourceService::IsBuildTargeted("canary", 3));
  EXPECT_FALSE(PromoResourceService::IsBuildTargeted("canary", 7));
  EXPECT_TRUE(PromoResourceService::IsBuildTargeted("canary", 15));
  EXPECT_TRUE(PromoResourceService::IsBuildTargeted("canary", 8));
  EXPECT_TRUE(PromoResourceService::IsBuildTargeted("canary", 11));

  // dev
  EXPECT_TRUE(PromoResourceService::IsBuildTargeted("dev", 1));
  EXPECT_TRUE(PromoResourceService::IsBuildTargeted("dev", 3));
  EXPECT_TRUE(PromoResourceService::IsBuildTargeted("dev", 7));
  EXPECT_TRUE(PromoResourceService::IsBuildTargeted("dev", 15));
  EXPECT_FALSE(PromoResourceService::IsBuildTargeted("dev", 8));
  EXPECT_TRUE(PromoResourceService::IsBuildTargeted("dev", 11));

  // beta
  EXPECT_FALSE(PromoResourceService::IsBuildTargeted("beta", 1));
  EXPECT_TRUE(PromoResourceService::IsBuildTargeted("beta", 3));
  EXPECT_TRUE(PromoResourceService::IsBuildTargeted("beta", 7));
  EXPECT_TRUE(PromoResourceService::IsBuildTargeted("beta", 15));
  EXPECT_FALSE(PromoResourceService::IsBuildTargeted("beta", 8));
  EXPECT_TRUE(PromoResourceService::IsBuildTargeted("beta", 11));

  // stable
  EXPECT_FALSE(PromoResourceService::IsBuildTargeted("", 1));
  EXPECT_FALSE(PromoResourceService::IsBuildTargeted("", 3));
  EXPECT_TRUE(PromoResourceService::IsBuildTargeted("", 7));
  EXPECT_TRUE(PromoResourceService::IsBuildTargeted("", 15));
  EXPECT_FALSE(PromoResourceService::IsBuildTargeted("", 8));
  EXPECT_FALSE(PromoResourceService::IsBuildTargeted("", 11));
  EXPECT_TRUE(PromoResourceService::IsBuildTargeted("", 12));
}
<commit_msg>Fix chromeos unit tests by cleaning up the local state in PromoResourceServiceTest.<commit_after>// Copyright (c) 2011 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 "base/json/json_reader.h"
#include "base/time.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/extensions/apps_promo.h"
#include "chrome/browser/prefs/browser_prefs.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/web_resource/promo_resource_service.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/testing_browser_process.h"
#include "chrome/test/testing_pref_service.h"
#include "chrome/test/testing_profile.h"
#include "testing/gtest/include/gtest/gtest.h"

typedef testing::Test PromoResourceServiceTest;

// Verifies that custom dates read from a web resource server are written to
// the preferences file.
TEST_F(PromoResourceServiceTest, UnpackLogoSignal) {
  // Set up a testing profile and create a promo resource service.
  TestingProfile profile;
  TestingPrefService local_state;
  TestingBrowserProcess* testing_browser_process =
      static_cast<TestingBrowserProcess*>(g_browser_process);
  testing_browser_process->SetPrefService(&local_state);
  browser::RegisterLocalState(&local_state);

  scoped_refptr<PromoResourceService> web_resource_service(
      new PromoResourceService(&profile));

  // Set up start and end dates in a Dictionary as if parsed from the service.
  std::string json = "{ "
                     "  \"topic\": {"
                     "    \"answers\": ["
                     "       {"
                     "        \"name\": \"custom_logo_start\","
                     "        \"inproduct\": \"31/01/10 01:00 GMT\""
                     "       },"
                     "       {"
                     "        \"name\": \"custom_logo_end\","
                     "        \"inproduct\": \"31/01/12 01:00 GMT\""
                     "       }"
                     "    ]"
                     "  }"
                     "}";
  scoped_ptr<DictionaryValue> test_json(static_cast<DictionaryValue*>(
      base::JSONReader::Read(json, false)));

  // Check that prefs are set correctly.
  web_resource_service->UnpackLogoSignal(*(test_json.get()));
  PrefService* prefs = profile.GetPrefs();
  ASSERT_TRUE(prefs != NULL);

  double logo_start =
      prefs->GetDouble(prefs::kNTPCustomLogoStart);
  EXPECT_EQ(logo_start, 1264899600);  // unix epoch for Jan 31 2010 0100 GMT.
  double logo_end =
      prefs->GetDouble(prefs::kNTPCustomLogoEnd);
  EXPECT_EQ(logo_end, 1327971600);  // unix epoch for Jan 31 2012 0100 GMT.

  // Change the start only and recheck.
  json = "{ "
         "  \"topic\": {"
         "    \"answers\": ["
         "       {"
         "        \"name\": \"custom_logo_start\","
         "        \"inproduct\": \"28/02/10 14:00 GMT\""
         "       },"
         "       {"
         "        \"name\": \"custom_logo_end\","
         "        \"inproduct\": \"31/01/12 01:00 GMT\""
         "       }"
         "    ]"
         "  }"
         "}";
  test_json->Clear();
  test_json.reset(static_cast<DictionaryValue*>(
      base::JSONReader::Read(json, false)));

  // Check that prefs are set correctly.
  web_resource_service->UnpackLogoSignal(*(test_json.get()));

  logo_start = prefs->GetDouble(prefs::kNTPCustomLogoStart);
  EXPECT_EQ(logo_start, 1267365600);  // date changes to Feb 28 2010 1400 GMT.

  // If no date is included in the prefs, reset custom logo dates to 0.
  json = "{ "
         "  \"topic\": {"
         "    \"answers\": ["
         "       {"
         "       }"
         "    ]"
         "  }"
         "}";
  test_json->Clear();
  test_json.reset(static_cast<DictionaryValue*>(
      base::JSONReader::Read(json, false)));

  // Check that prefs are set correctly.
  web_resource_service->UnpackLogoSignal(*(test_json.get()));
  logo_start = prefs->GetDouble(prefs::kNTPCustomLogoStart);
  EXPECT_EQ(logo_start, 0);  // date value reset to 0;
  logo_end = prefs->GetDouble(prefs::kNTPCustomLogoEnd);
  EXPECT_EQ(logo_end, 0);  // date value reset to 0;

  testing_browser_process->SetPrefService(NULL);
}

TEST_F(PromoResourceServiceTest, UnpackPromoSignal) {
  // Set up a testing profile and create a promo resource service.
  TestingProfile profile;
  TestingPrefService local_state;
  TestingBrowserProcess* testing_browser_process =
      static_cast<TestingBrowserProcess*>(g_browser_process);
  testing_browser_process->SetPrefService(&local_state);
  browser::RegisterLocalState(&local_state);

  scoped_refptr<PromoResourceService> web_resource_service(
      new PromoResourceService(&profile));

  // Set up start and end dates and promo line in a Dictionary as if parsed
  // from the service.
  std::string json = "{ "
                     "  \"topic\": {"
                     "    \"answers\": ["
                     "       {"
                     "        \"name\": \"promo_start\","
                     "        \"question\": \"3:2\","
                     "        \"tooltip\": \"Eat more pie!\","
                     "        \"inproduct\": \"31/01/10 01:00 GMT\""
                     "       },"
                     "       {"
                     "        \"name\": \"promo_end\","
                     "        \"inproduct\": \"31/01/12 01:00 GMT\""
                     "       }"
                     "    ]"
                     "  }"
                     "}";
  scoped_ptr<DictionaryValue> test_json(static_cast<DictionaryValue*>(
      base::JSONReader::Read(json, false)));

  // Initialize a message loop for this to run on.
  MessageLoop loop;

  // Check that prefs are set correctly.
  web_resource_service->UnpackPromoSignal(*(test_json.get()));
  PrefService* prefs = profile.GetPrefs();
  ASSERT_TRUE(prefs != NULL);

  std::string promo_line = prefs->GetString(prefs::kNTPPromoLine);
  EXPECT_EQ(promo_line, "Eat more pie!");

  int promo_group = prefs->GetInteger(prefs::kNTPPromoGroup);
  EXPECT_GE(promo_group, 0);
  EXPECT_LT(promo_group, 16);

  int promo_build_type = prefs->GetInteger(prefs::kNTPPromoBuild);
  EXPECT_EQ(promo_build_type & PromoResourceService::DEV_BUILD,
            PromoResourceService::DEV_BUILD);
  EXPECT_EQ(promo_build_type & PromoResourceService::BETA_BUILD,
            PromoResourceService::BETA_BUILD);
  EXPECT_EQ(promo_build_type & PromoResourceService::STABLE_BUILD, 0);

  int promo_time_slice = prefs->GetInteger(prefs::kNTPPromoGroupTimeSlice);
  EXPECT_EQ(promo_time_slice, 2);

  double promo_start =
      prefs->GetDouble(prefs::kNTPPromoStart);
  int64 actual_start = 1264899600 +  // unix epoch for Jan 31 2010 0100 GMT.
      promo_group * 2 * 60 * 60;
  EXPECT_EQ(promo_start, actual_start);

  double promo_end =
      prefs->GetDouble(prefs::kNTPPromoEnd);
  EXPECT_EQ(promo_end, 1327971600);  // unix epoch for Jan 31 2012 0100 GMT.

  testing_browser_process->SetPrefService(NULL);
}

TEST_F(PromoResourceServiceTest, UnpackWebStoreSignal) {
  // Set up a testing profile and create a promo resource service.
  TestingProfile profile;
  TestingPrefService local_state;
  TestingBrowserProcess* testing_browser_process =
      static_cast<TestingBrowserProcess*>(g_browser_process);
  testing_browser_process->SetPrefService(&local_state);

  browser::RegisterLocalState(&local_state);

  scoped_refptr<PromoResourceService> web_resource_service(
      new PromoResourceService(&profile));

  web_resource_service->set_channel("dev");

  // Set up start and end dates and promo line in a Dictionary as if parsed
  // from the service.
  std::string json = "{ "
                     "  \"topic\": {"
                     "    \"answers\": ["
                     "       {"
                     "        \"answer_id\": \"341252\","
                     "        \"name\": \"webstore_promo:15\","
                     "        \"question\": \"The header!\","
                     "        \"inproduct_target\": \"The button label!\","
                     "        \"inproduct\": \"http://link.com\","
                     "        \"tooltip\": \"No thanks, hide this.\""
                     "       }"
                     "    ]"
                     "  }"
                     "}";
  scoped_ptr<DictionaryValue> test_json(static_cast<DictionaryValue*>(
      base::JSONReader::Read(json, false)));

  // Initialize a message loop for this to run on.
  MessageLoop loop;

  // Check that prefs are set correctly.
  web_resource_service->UnpackWebStoreSignal(*(test_json.get()));
  PrefService* prefs = profile.GetPrefs();
  ASSERT_TRUE(prefs != NULL);

  EXPECT_EQ("341252", AppsPromo::GetPromoId());
  EXPECT_EQ("The header!", AppsPromo::GetPromoHeaderText());
  EXPECT_EQ("The button label!", AppsPromo::GetPromoButtonText());
  EXPECT_EQ(GURL("http://link.com"), AppsPromo::GetPromoLink());
  EXPECT_EQ("No thanks, hide this.", AppsPromo::GetPromoExpireText());

  testing_browser_process->SetPrefService(NULL);
}

TEST_F(PromoResourceServiceTest, IsBuildTargeted) {
  // canary
  EXPECT_FALSE(PromoResourceService::IsBuildTargeted("canary", 1));
  EXPECT_FALSE(PromoResourceService::IsBuildTargeted("canary", 3));
  EXPECT_FALSE(PromoResourceService::IsBuildTargeted("canary", 7));
  EXPECT_TRUE(PromoResourceService::IsBuildTargeted("canary", 15));
  EXPECT_TRUE(PromoResourceService::IsBuildTargeted("canary", 8));
  EXPECT_TRUE(PromoResourceService::IsBuildTargeted("canary", 11));

  // dev
  EXPECT_TRUE(PromoResourceService::IsBuildTargeted("dev", 1));
  EXPECT_TRUE(PromoResourceService::IsBuildTargeted("dev", 3));
  EXPECT_TRUE(PromoResourceService::IsBuildTargeted("dev", 7));
  EXPECT_TRUE(PromoResourceService::IsBuildTargeted("dev", 15));
  EXPECT_FALSE(PromoResourceService::IsBuildTargeted("dev", 8));
  EXPECT_TRUE(PromoResourceService::IsBuildTargeted("dev", 11));

  // beta
  EXPECT_FALSE(PromoResourceService::IsBuildTargeted("beta", 1));
  EXPECT_TRUE(PromoResourceService::IsBuildTargeted("beta", 3));
  EXPECT_TRUE(PromoResourceService::IsBuildTargeted("beta", 7));
  EXPECT_TRUE(PromoResourceService::IsBuildTargeted("beta", 15));
  EXPECT_FALSE(PromoResourceService::IsBuildTargeted("beta", 8));
  EXPECT_TRUE(PromoResourceService::IsBuildTargeted("beta", 11));

  // stable
  EXPECT_FALSE(PromoResourceService::IsBuildTargeted("", 1));
  EXPECT_FALSE(PromoResourceService::IsBuildTargeted("", 3));
  EXPECT_TRUE(PromoResourceService::IsBuildTargeted("", 7));
  EXPECT_TRUE(PromoResourceService::IsBuildTargeted("", 15));
  EXPECT_FALSE(PromoResourceService::IsBuildTargeted("", 8));
  EXPECT_FALSE(PromoResourceService::IsBuildTargeted("", 11));
  EXPECT_TRUE(PromoResourceService::IsBuildTargeted("", 12));
}
<|endoftext|>


<commit_before>#ifndef RECURSE_BODYPARSER_HPP
#define RECURSE_BODYPARSER_HPP

#include <QJsonDocument>
#include <QJsonObject>

auto bodyparser()
{
    return [](auto &req, auto &res, auto next) {
        if (req.body.isEmpty())
            return next();

        if (req.header["content-type"] == "application/json") {
            QJsonDocument json = QJsonDocument::fromJson(req.body.toUtf8());
            const QVariantMap map = json.object().toVariantMap();
            QMap<QString, QVariant>::const_iterator i;

            for (i = map.constBegin(); i != map.constEnd(); ++i) {
                req.body_ph[i.key()] = i.value();
            }
        }
        else if (req.header["content-type"] == "application/x-www-form-urlencoded") {
        }

        next();
    };
}

#endif // RECURSE_BODYPARSER_HPP
<commit_msg>use req.body_parsed instead of req.body_ph<commit_after>#ifndef RECURSE_BODYPARSER_HPP
#define RECURSE_BODYPARSER_HPP

#include <QJsonDocument>
#include <QJsonObject>

auto bodyparser()
{
    return [](auto &req, auto &res, auto next) {
        if (req.body.isEmpty())
            return next();

        if (req.header["content-type"] == "application/json") {
            QJsonDocument json = QJsonDocument::fromJson(req.body.toUtf8());
            const QVariantMap map = json.object().toVariantMap();
            QMap<QString, QVariant>::const_iterator i;

            for (i = map.constBegin(); i != map.constEnd(); ++i) {
                req.body_parsed[i.key()] = i.value();
            }
        }
        else if (req.header["content-type"] == "application/x-www-form-urlencoded") {
        }

        next();
    };
}

#endif // RECURSE_BODYPARSER_HPP
<|endoftext|>


<commit_before>// Copyright 2020 Global Phasing Ltd.
//
// Reindex merged or unmerged MTZ file.
// TODO: handle operations that results in non-integral indices.

#include <cstdio>
#include <cstring>  // for strpbrk
#include <algorithm>
#ifndef GEMMI_ALL_IN_ONE
# define GEMMI_WRITE_IMPLEMENTATION 1
#endif
#include <gemmi/mtz.hpp>
#include <gemmi/gz.hpp>       // for MaybeGzipped
#include <gemmi/version.hpp>  // for GEMMI_VERSION
#define GEMMI_PROG reindex
#include "options.h"

using std::fprintf;

namespace {

enum OptionIndex { Hkl=4, NoHistory, NoSort };

const option::Descriptor Usage[] = {
  { NoOp, 0, "", "", Arg::None,
    "Usage:\n  " EXE_NAME " [options] INPUT_MTZ OUTPUT_MTZ"
    "\nOptions:"},
  CommonUsage[Help],
  CommonUsage[Version],
  CommonUsage[Verbose],
  { Hkl, 0, "", "hkl", Arg::Required,
    "  --hkl=OP  \tReindexing transform as triplet (e.g. k,h,-l)." },
  { NoHistory, 0, "", "no-history", Arg::None,
    "  --no-history  \tDo not add 'Reindexed with...' line to mtz HISTORY." },
  { NoSort, 0, "", "no-sort", Arg::None,
    "  --no-sort  \tDo not reorder reflections." },
  { NoOp, 0, "", "", Arg::None,
    "\nInput file can be gzipped." },
  { 0, 0, 0, 0, 0, 0 }
};

} // anonymous namespace

int GEMMI_MAIN(int argc, char **argv) {
  OptParser p(EXE_NAME);
  p.simple_parse(argc, argv, Usage);
  p.require_positional_args(2);
  bool verbose = p.options[Verbose];
  const char* input_path = p.nonOption(0);
  const char* output_path = p.nonOption(1);
  if (!p.options[Hkl]) {
    fprintf(stderr, "Specify transform with option --hkl\n");
    return 1;
  }
  const char* hkl_arg = p.options[Hkl].arg;
  gemmi::Op op = gemmi::parse_triplet(hkl_arg);
  if (std::strpbrk(hkl_arg, "xyzabcXYZABC"))
    gemmi::fail("specify OP in terms of h, k and l");
  if (op.tran != gemmi::Op::Tran{{0, 0, 0}})
    gemmi::fail("reindexing operator should not have a translation");
  // transpose rotation
  gemmi::Op real_space_op{op.transposed_rot(), {0, 0, 0}};
  if (verbose)
    fprintf(stderr, "real space transformation: %s\n", real_space_op.triplet().c_str());
  gemmi::Mtz mtz;
  if (verbose) {
    fprintf(stderr, "Reading %s ...\n", input_path);
    mtz.warnings = stderr;
  }
  try {
    mtz.read_input(gemmi::MaybeGzipped(input_path), true);
    mtz.switch_to_original_hkl();
  } catch (std::runtime_error& e) {
    fprintf(stderr, "ERROR reading %s: %s\n", input_path, e.what());
    return 1;
  }
  // reindex
  for (size_t n = 0; n < mtz.data.size(); n += mtz.columns.size())
    mtz.set_hkl(n, real_space_op.apply_to_hkl(mtz.get_hkl(n)));
  // hand change requires data modification
  if (op.det_rot() < 0)
    for (gemmi::Mtz::Column& column : mtz.columns) {
      // negate anomalous difference
      if (column.type == 'D') {
        for (float& value : column)
          value = -value;
        if (verbose)
          fprintf(stderr, "Column %s: anomalous difference negated.\n",
                  column.label.c_str());
        continue;
      }
      // swap (+) and (-)
      size_t pos = column.label.find("(+)");
      if (pos != std::string::npos) {
        std::string minus_label = column.label;
        minus_label[pos+1] = '-';
        gemmi::Mtz::Column* minus_column =
            mtz.column_with_label(minus_label, &column.dataset());
        if (minus_column) {
          for (size_t n = 0; n < mtz.data.size(); n += mtz.columns.size())
            std::swap(mtz.data[n + column.idx],
                      mtz.data[n + minus_column->idx]);
          if (verbose)
            fprintf(stderr, "Swapped values from %s and %s.\n",
                    column.label.c_str(), minus_label.c_str());
        } else {
          fprintf(stderr, "Warning: matching pair not found for: %s\n",
                  column.label.c_str());
        }
      }
    }
  // change space group
  mtz.spacegroup = gemmi::find_spacegroup_by_change_of_basis(mtz.spacegroup, op.inverse());
  if (mtz.spacegroup) {
    if (verbose)
      fprintf(stderr, "Space group changed from %s to %s\n.",
              mtz.spacegroup_name.c_str(), mtz.spacegroup->xhm().c_str());
  } else {
    fprintf(stderr, "WARNING: new space group name could not be determined.\n");
  }

  if (mtz.is_merged())
    mtz.ensure_asu();
  else
    mtz.switch_to_asu_hkl();
  if (!p.options[NoSort])
    mtz.sort();
  if (!p.options[NoHistory])
    mtz.history.emplace(mtz.history.begin(),
                        "Reindexed with gemmi-reindex " GEMMI_VERSION);
  if (verbose)
    fprintf(stderr, "Writing %s ...\n", output_path);
  mtz.write_to_file(output_path);
  return 0;
}

// vim:sw=2:ts=2:et:path^=../include,../third_party
<commit_msg>reindex: change unit cell<commit_after>// Copyright 2020 Global Phasing Ltd.
//
// Reindex merged or unmerged MTZ file.
// TODO: handle operations that results in non-integral indices.

#include <cstdio>
#include <cstring>  // for strpbrk
#include <algorithm>
#ifndef GEMMI_ALL_IN_ONE
# define GEMMI_WRITE_IMPLEMENTATION 1
#endif
#include <gemmi/mtz.hpp>
#include <gemmi/gz.hpp>       // for MaybeGzipped
#include <gemmi/version.hpp>  // for GEMMI_VERSION
#define GEMMI_PROG reindex
#include "options.h"

using std::fprintf;

namespace {

enum OptionIndex { Hkl=4, NoHistory, NoSort };

const option::Descriptor Usage[] = {
  { NoOp, 0, "", "", Arg::None,
    "Usage:\n  " EXE_NAME " [options] INPUT_MTZ OUTPUT_MTZ"
    "\nOptions:"},
  CommonUsage[Help],
  CommonUsage[Version],
  CommonUsage[Verbose],
  { Hkl, 0, "", "hkl", Arg::Required,
    "  --hkl=OP  \tReindexing transform as triplet (e.g. k,h,-l)." },
  { NoHistory, 0, "", "no-history", Arg::None,
    "  --no-history  \tDo not add 'Reindexed with...' line to mtz HISTORY." },
  { NoSort, 0, "", "no-sort", Arg::None,
    "  --no-sort  \tDo not reorder reflections." },
  { NoOp, 0, "", "", Arg::None,
    "\nInput file can be gzipped." },
  { 0, 0, 0, 0, 0, 0 }
};

} // anonymous namespace

int GEMMI_MAIN(int argc, char **argv) {
  OptParser p(EXE_NAME);
  p.simple_parse(argc, argv, Usage);
  p.require_positional_args(2);
  bool verbose = p.options[Verbose];
  const char* input_path = p.nonOption(0);
  const char* output_path = p.nonOption(1);
  if (!p.options[Hkl]) {
    fprintf(stderr, "Specify transform with option --hkl\n");
    return 1;
  }
  const char* hkl_arg = p.options[Hkl].arg;
  gemmi::Op op = gemmi::parse_triplet(hkl_arg);
  if (std::strpbrk(hkl_arg, "xyzabcXYZABC"))
    gemmi::fail("specify OP in terms of h, k and l");
  if (op.tran != gemmi::Op::Tran{{0, 0, 0}})
    gemmi::fail("reindexing operator should not have a translation");
  // transpose rotation
  // TODO: it may be wrong if op.rot is not a rotation matrix,
  // i.e. when cell volume changes
  gemmi::Op real_space_op{op.transposed_rot(), {0, 0, 0}};
  if (verbose)
    fprintf(stderr, "real space transformation: %s\n", real_space_op.triplet().c_str());
  gemmi::Mtz mtz;
  if (verbose) {
    fprintf(stderr, "Reading %s ...\n", input_path);
    mtz.warnings = stderr;
  }
  try {
    mtz.read_input(gemmi::MaybeGzipped(input_path), true);
    mtz.switch_to_original_hkl();
  } catch (std::runtime_error& e) {
    fprintf(stderr, "ERROR reading %s: %s\n", input_path, e.what());
    return 1;
  }
  // reindex
  for (size_t n = 0; n < mtz.data.size(); n += mtz.columns.size())
    mtz.set_hkl(n, real_space_op.apply_to_hkl(mtz.get_hkl(n)));
  // hand change requires data modification
  if (op.det_rot() < 0)
    for (gemmi::Mtz::Column& column : mtz.columns) {
      // negate anomalous difference
      if (column.type == 'D') {
        for (float& value : column)
          value = -value;
        if (verbose)
          fprintf(stderr, "Column %s: anomalous difference negated.\n",
                  column.label.c_str());
        continue;
      }
      // swap (+) and (-)
      size_t pos = column.label.find("(+)");
      if (pos != std::string::npos) {
        std::string minus_label = column.label;
        minus_label[pos+1] = '-';
        gemmi::Mtz::Column* minus_column =
            mtz.column_with_label(minus_label, &column.dataset());
        if (minus_column) {
          for (size_t n = 0; n < mtz.data.size(); n += mtz.columns.size())
            std::swap(mtz.data[n + column.idx],
                      mtz.data[n + minus_column->idx]);
          if (verbose)
            fprintf(stderr, "Swapped values from %s and %s.\n",
                    column.label.c_str(), minus_label.c_str());
        } else {
          fprintf(stderr, "Warning: matching pair not found for: %s\n",
                  column.label.c_str());
        }
      }
    }
  // change space group
  mtz.spacegroup = gemmi::find_spacegroup_by_change_of_basis(mtz.spacegroup, op.inverse());
  if (mtz.spacegroup) {
    if (verbose)
      fprintf(stderr, "Space group changed from %s to %s\n.",
              mtz.spacegroup_name.c_str(), mtz.spacegroup->xhm().c_str());
  } else {
    fprintf(stderr, "WARNING: new space group name could not be determined.\n");
  }
  // change unit cell
  // TODO: change each unit cell separately, including batch headers
  gemmi::Mat33 mat = mtz.cell.orth.mat.multiply(gemmi::rot_as_mat33(real_space_op));
  gemmi::UnitCell new_cell;
  new_cell.set_from_vectors(mat.column_copy(0), mat.column_copy(1), mat.column_copy(2));
  mtz.set_cell_for_all(new_cell);
  mat = mtz.cell.orth.mat;

  if (mtz.is_merged())
    mtz.ensure_asu();
  else
    mtz.switch_to_asu_hkl();
  if (!p.options[NoSort])
    mtz.sort();
  if (!p.options[NoHistory])
    mtz.history.emplace(mtz.history.begin(),
                        "Reindexed with gemmi-reindex " GEMMI_VERSION);
  if (verbose)
    fprintf(stderr, "Writing %s ...\n", output_path);
  mtz.write_to_file(output_path);
  return 0;
}

// vim:sw=2:ts=2:et:path^=../include,../third_party
<|endoftext|>


<commit_before>#include <node.h>
#include <nan.h>
#include <v8.h>
#include <node_buffer.h>

// dependency
#include <string.h>
#include <stdlib.h>
#include <mcrypt.h>

using namespace v8;
using namespace node;

NAN_METHOD(Rijndael) {
  NanScope();

  MCRYPT rijndael_module;

  int err = 0;
  char* error_message;
  int data_size;
  void* data;
  char* text;
  char* key;
  char* iv = NULL;

  int text_len;
  int key_len;
  bool encrypt;
  char* mode;

  if (args.Length() < 1 || !Buffer::HasInstance(args[0])) {
    err = 1; error_message = (char*) "data must be a buffer";
  } else if (args.Length() < 2 || !Buffer::HasInstance(args[1])) {
    err = 1; error_message = (char*) "key must be a buffer";
  } else if (args.Length() < 3 || !args[2]->IsBoolean()) {
    err = 1; error_message = (char*) "encryption must be a boolean";
  } else if (args.Length() < 4) {
    err = 1; error_message = (char*) "block mode must be a string";
  } else if (args.Length() < 5) {
    err = 1; error_message = (char*) "iv must be a buffer or null";
  }

  if (err == 1) {
    NanThrowTypeError(error_message);
    NanReturnUndefined();
  }

  if (Buffer::HasInstance(args[4])) {
    iv = Buffer::Data(args[4]);
  }

  String::Utf8Value modeStr(args[3]->ToString());

  text = Buffer::Data(args[0]);
  key = Buffer::Data(args[1]);
  encrypt = args[2]->BooleanValue();
  mode = *modeStr;

  text_len = Buffer::Length(args[0]);
  key_len = Buffer::Length(args[1]);

  if (key_len != 16 && key_len != 24 && key_len != 32) {
    NanThrowError("key length does not match algorithm parameters");
    NanReturnUndefined();
  }

  rijndael_module = mcrypt_module_open((char*) "rijndael-256", NULL, mode, NULL);
  if (rijndael_module == MCRYPT_FAILED) {
    NanThrowError("rijndael mcrypt module failed to load");
    NanReturnUndefined();
  }

  err = mcrypt_generic_init(rijndael_module, (void*) key, key_len, iv);
  if (err < 0) {
    mcrypt_module_close(rijndael_module);
    NanThrowError(mcrypt_strerror(err));
    NanReturnUndefined();
  }

  data_size = (((text_len - 1) / 32) + 1) * 32;
  data = malloc(data_size);
  memset(data, 0, data_size);
  memcpy(data, text, text_len);

  if (encrypt)
    err = mcrypt_generic(rijndael_module, data, data_size);
  else
    err = mdecrypt_generic(rijndael_module, data, data_size);

  if (err < 0) {
    mcrypt_module_close(rijndael_module);
    free(data);
    NanThrowError(mcrypt_strerror(err));
    NanReturnUndefined();
  }

  Local<Object> buffer = NanNewBufferHandle((char*) data, data_size);
  mcrypt_module_close(rijndael_module);
  free(data);
  NanReturnValue(buffer);
}

void init(Handle<Object> exports) {
  exports->Set(NanNew<String>("rijndael"),
    NanNew<FunctionTemplate>(Rijndael)->GetFunction());
}

NODE_MODULE(rijndael, init)
<commit_msg>call "mcrypt_generic_deinit" to fix memory leaks<commit_after>#include <node.h>
#include <nan.h>
#include <v8.h>
#include <node_buffer.h>

// dependency
#include <string.h>
#include <stdlib.h>
#include <mcrypt.h>

using namespace v8;
using namespace node;

NAN_METHOD(Rijndael) {
  NanScope();

  MCRYPT rijndael_module;

  int err = 0;
  char* error_message;
  int data_size;
  void* data;
  char* text;
  char* key;
  char* iv = NULL;

  int text_len;
  int key_len;
  bool encrypt;
  char* mode;

  if (args.Length() < 1 || !Buffer::HasInstance(args[0])) {
    err = 1; error_message = (char*) "data must be a buffer";
  } else if (args.Length() < 2 || !Buffer::HasInstance(args[1])) {
    err = 1; error_message = (char*) "key must be a buffer";
  } else if (args.Length() < 3 || !args[2]->IsBoolean()) {
    err = 1; error_message = (char*) "encryption must be a boolean";
  } else if (args.Length() < 4) {
    err = 1; error_message = (char*) "block mode must be a string";
  } else if (args.Length() < 5) {
    err = 1; error_message = (char*) "iv must be a buffer or null";
  }

  if (err == 1) {
    NanThrowTypeError(error_message);
    NanReturnUndefined();
  }

  if (Buffer::HasInstance(args[4])) {
    iv = Buffer::Data(args[4]);
  }

  String::Utf8Value modeStr(args[3]->ToString());

  text = Buffer::Data(args[0]);
  key = Buffer::Data(args[1]);
  encrypt = args[2]->BooleanValue();
  mode = *modeStr;

  text_len = Buffer::Length(args[0]);
  key_len = Buffer::Length(args[1]);

  if (key_len != 16 && key_len != 24 && key_len != 32) {
    NanThrowError("key length does not match algorithm parameters");
    NanReturnUndefined();
  }

  rijndael_module = mcrypt_module_open((char*) "rijndael-256", NULL, mode, NULL);
  if (rijndael_module == MCRYPT_FAILED) {
    NanThrowError("rijndael mcrypt module failed to load");
    NanReturnUndefined();
  }

  err = mcrypt_generic_init(rijndael_module, (void*) key, key_len, iv);
  if (err < 0) {
    mcrypt_module_close(rijndael_module);
    NanThrowError(mcrypt_strerror(err));
    NanReturnUndefined();
  }

  data_size = (((text_len - 1) / 32) + 1) * 32;
  data = malloc(data_size);
  memset(data, 0, data_size);
  memcpy(data, text, text_len);

  if (encrypt)
    err = mcrypt_generic(rijndael_module, data, data_size);
  else
    err = mdecrypt_generic(rijndael_module, data, data_size);

  if (err < 0) {
    mcrypt_module_close(rijndael_module);
    free(data);
    NanThrowError(mcrypt_strerror(err));
    NanReturnUndefined();
  }

  err = mcrypt_generic_deinit(rijndael_module);

  if (err < 0) {
    mcrypt_module_close(rijndael_module);
    free(data);
    NanThrowError(mcrypt_strerror(err));
    NanReturnUndefined();
  }

  Local<Object> buffer = NanNewBufferHandle((char*) data, data_size);
  mcrypt_module_close(rijndael_module);
  free(data);
  NanReturnValue(buffer);
}

void init(Handle<Object> exports) {
  exports->Set(NanNew<String>("rijndael"),
    NanNew<FunctionTemplate>(Rijndael)->GetFunction());
}

NODE_MODULE(rijndael, init)
<|endoftext|>


<commit_before>#ifndef VEXCL_VEXCL_HPP
#define VEXCL_VEXCL_HPP

/*
The MIT License

Copyright (c) 2012 Denis Demidov <ddemidov@ksu.ru>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

/**
 * \file   vexcl.hpp
 * \author Denis Demidov <ddemidov@ksu.ru>
 * \brief  Vector expression template library for OpenCL.
 */

/**
\mainpage VexCL

VexCL is vector expression template library for OpenCL. It has been created for
ease of C++ based OpenCL development.  Multi-device (and multi-platform)
computations are supported. The source code is available at
https://github.com/ddemidov/vexcl.

\section devlist Selection of compute devices

You can select any number of available compute devices, which satisfy provided
filters. Filter is a functor returning bool and acting on a cl::Device
parameter. Several standard filters are provided, such as device type or name
filter, double precision support etc. Filters can be combined with logical
operators. In the example below all devices with names matching "Radeon" and
supporting double precision are selected:
\code
#include <vexcl/vexcl.hpp>
using namespace vex;
int main() {
    vex::Context ctx(Filter::Name("Radeon") && Filter::DoublePrecision);
    std::cout << ctx << std::endl;
}
\endcode
vex::Context object holds list of initialized OpenCL contexts and command
queues for each filtered device. If you just need list of available devices
without creating contexts and queues on them, then look for device_list()
function in documenation.

\section vector Memory allocation and vector arithmetic

Once you got queue list, you can allocate OpenCL buffers on the associated
devices. vex::vector constructor accepts std::vector of cl::CommandQueue.  The
contents of the created vector will be partitioned between each queue
(presumably, each of the provided queues is linked with separate device).  Size
of each partition will be proportional to relative device bandwidth unless
macro VEXCL_DUMB_PARTITIONING is defined, in which case equal partitioning
scheme will be applied. Device bandwidth is measured first time it is requested
by launch of small test kernel.

Multi-platform computation is supported (that is, you can spread your vectors
across devices by different vendors), but should be used with caution: all
computations will be performed with the speed of the slowest device selected.

In the example below host vector is allocated and initialized, then copied to
all GPU devices found in the system. A couple of empty device vectors are
allocated as well:
\code
const size_t n = 1 << 20;
std::vector<double> x(n);
std::generate(x.begin(), x.end(), [](){ return (double)rand() / RAND_MAX; });

vex::Context ctx(Filter::Type(CL_DEVICE_TYPE_GPU));

vex::vector<double> X(ctx.queue(), x);
vex::vector<double> Y(ctx.queue(), n);
vex::vector<double> Z(ctx.queue(), n);
\endcode

You can now use simple vector arithmetic with device vector. For every
expression you use, appropriate kernel is compiled (first time it is
encountered in your program) and called automagically. If you want to see
sources of the generated kernels on the standard output, define
VEXCL_SHOW_KERNELS macro before including VexCL headers.

Vectors are processed in parallel across all devices they were allocated on:
\code
Y = 42;
Z = sqrt(2 * X) + cos(Y);
\endcode

You can copy the result back to host or you can use vector::operator[] to
read (or write) vector elements directly. Though latter technique is very
ineffective and should be used for debugging purposes only.
\code
copy(Z, x);
assert(x[42] == Z[42]);
\endcode

Another frequently performed operation is reduction of a vector expression to
single value, such as summation. This can be done with vex::Reductor class:
\code
Reductor<double> sum(ctx.queue());

std::cout << sum(Z) << std::endl;
std::cout << sum(sqrt(2 * X) + cos(Y)) << std::endl;
\endcode

\section stencil Stencil convolution

Stencil convolution operation comes in handy in many situations. For example,
it allows us to apply a moving average filter to a device vector. All you need
is to construct a vex::stencil object:
\code
// Moving average with 5-points window.
std::vector<double> sdata(5, 0.2);
stencil(ctx.queue(), sdata, sdata.size() / 2);

vex::vector<double> x(ctx.queue(), 1024 * 1024);
vex::vector<double> y(ctx.queue(), 1024 * 1024);

x = 1;
y = x * s; // convolve x with s
\endcode

\section spmv Sparse matrix-vector multiplication

One of the most common operations in linear algebra is matrix-vector
multiplication. Class vex::SpMat holds representation of a sparse matrix,
spanning several devices. In the example below it is used for solution of a
system of linear equations with conjugate gradients method:
\code
typedef double real;
// Solve system of linear equations A u = f with conjugate gradients method.
// Input matrix is represented in CSR format (parameters row, col, and val).
void cg_gpu(
	const std::vector<size_t> &row, // Indices to col and val vectors.
	const std::vector<size_t> &col, // Column numbers of non-zero elements.
	const std::vector<real>   &val, // Values of non-zero elements.
	const std::vector<real>   &rhs, // Right-hand side.
	std::vector<real> &x            // In: initial approximation; out: result.
	)
{
    // Init OpenCL.
    vex::Context ctx(Filter::Type(CL_DEVICE_TYPE_GPU));

    // Move data to compute devices.
    size_t n = x.size();
    vex::SpMat<real>  A(ctx.queue(), n, row.data(), col.data(), val.data());
    vex::vector<real> f(ctx.queue(), rhs);
    vex::vector<real> u(ctx.queue(), x);
    vex::vector<real> r(ctx.queue(), n);
    vex::vector<real> p(ctx.queue(), n);
    vex::vector<real> q(ctx.queue(), n);

    Reductor<real,MAX> max(ctx.queue());
    Reductor<real,SUM> sum(ctx.queue());

    // Solve equation Au = f with conjugate gradients method.
    real rho1, rho2;
    r = f - A * u;

    for(uint iter = 0; max(fabs(r)) > 1e-8 && iter < n; iter++) {
	rho1 = sum(r * r);

	if (iter == 0) {
	    p = r;
	} else {
	    real beta = rho1 / rho2;
	    p = r + beta * p;
	}

	q = A * p;

	real alpha = rho1 / sum(p * q);

	u += alpha * p;
	r -= alpha * q;

	rho2 = rho1;
    }

    // Get result to host.
    copy(u, x);
}
\endcode

\section userfun User-defined functions

Simple arithmetic expressions are sometimes not enough. Imagine that you need
to count how many elements in vector x are greater that their counterparts in
vector y. This may be achieved by introduction of custom function. In order
to build such a function, you need to supply its body, its return type and
types of its arguments. After that, you can apply the function to any valid
vector expressions:
\code
// Function body has to be defined at global scope, and it has to be of `extern
// const char[]` type. This allows us to use it as a template parameter.
extern const char one_greater_than_other[] = "return prm1 > prm2 ? 1 : 0;";
UserFunction<one_greater_than_other, size_t(float, float)> greater;

size_t count_if_greater(
    const Reductor<size_t, SUM> &sum,
    const vex::vector<float> &x,
    const vex::vector<float> &y
    )
{
    return sum(greater(x, y));
}
\endcode

You could also write sum(greater(x + y, 5 * y)), or use any other expressions
as parameters to the greater() call. Note that in the function body
parameters are always named as prm1, prm2, etc.

\section multivector Multi-component vectors

Class template vex::multivector<T,N> allows to store several equally sized
device vectors and perform computations on all components synchronously.
Operations are delegated to the underlying vectors. Expressions may include
std::array<T,N> values, where N is equal to the number of multivector
components. Each component gets corresponding element of std::array<> when
expression is applied.
\code
const size_t n = 1 << 20;
std::vector<double> host(n * 3);
std::generate(host.begin(), host.end(), rand);

vex::multivector<double,3> x(ctx.queue(), host);
vex::multivector<double,3> y(ctx.queue(), n);

std::array<int, 3> c = {4, 5, 6};

y = 2 * cos(x) - c;

std::array<double,3> v = y[42];
assert(fabs(v[1] - (2 * cos(host[n + 42]) - c[1])) < 1e-8);
\endcode

Components of a multivector may be accessed with operator():
\code
vex::vector<double> z = y(1);
\endcode

\section custkern Using custom kernels

Custom kernels are of course possible as well. vector::operator(uint)
returns cl::Buffer object for a specified device:
\code
vex::Context ctx(Filter::Type(CL_DEVICE_TYPE_GPU));

const size_t n = 1 << 20;
vex::vector<float> x(ctx.queue(), n);

auto program = build_sources(context, std::string(
    "kernel void dummy(ulong size, global float *x)\n"
    "{\n"
    "    size_t i = get_global_id(0);\n"
    "    if (i < size) x[i] = 4.2;\n"
    "}\n"
    ));

for(uint d = 0; d < ctx.size(); d++) {
    auto dummy = cl::Kernel(program, "dummy").bind(ctx.queue()[d], alignup(n, 256), 256);
    dummy((cl_ulong)x.part_size(d), x(d));
}

Reductor<float,SUM> sum(ctx.queue());
std::cout << sum(x) << std::endl;
\endcode

\section scalability Scalability

In the images below, scalability of the library with respect to number of
compute devices is shown. Effective performance (GFLOPS) and bandwidth (GB/sec)
were measured by launching big number of test kernels on one, two, or three
Nvidia Tesla C2070 cards. The results shown are averaged over 20 runs.

The details of the experiments may be found in
examples/benchmark.cpp file.  Basically, performance of the following code
was measured:

\code
// Vector arithmetic
a += b + c * d;

// Reduction
double s = sum(a * b);

// SpMV
y += A * x;
\endcode

\image html perf.png ""

\section compilers Supported compilers

VexCL makes heavy use of C++11 features, so your compiler has to be modern
enough. GCC version 4.6 and above is fully supported. Microsoft Visual C++ 2010
manages to compile the project with some features disabled: since it does not
support variadic templates, only one-argument builtin functions are enabled;
user functions are not available at all.
*/

#ifdef WIN32
#  pragma warning(push)
#  pragma warning(disable : 4290)
#  define NOMINMAX
#endif

#define __CL_ENABLE_EXCEPTIONS

#include <CL/cl.hpp>
#include <iostream>

#include <vexcl/util.hpp>
#include <vexcl/devlist.hpp>
#include <vexcl/vector.hpp>
#include <vexcl/reduce.hpp>
#include <vexcl/spmat.hpp>
#include <vexcl/stencil.hpp>
#include <vexcl/profiler.hpp>

#ifdef WIN32
#  pragma warning(pop)
#endif
#endif
<commit_msg>touched documentation<commit_after>#ifndef VEXCL_VEXCL_HPP
#define VEXCL_VEXCL_HPP

/*
The MIT License

Copyright (c) 2012 Denis Demidov <ddemidov@ksu.ru>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

/**
 * \file   vexcl.hpp
 * \author Denis Demidov <ddemidov@ksu.ru>
 * \brief  Vector expression template library for OpenCL.
 */

/**
\mainpage VexCL

VexCL is vector expression template library for OpenCL. It has been created for
ease of C++ based OpenCL development.  Multi-device (and multi-platform)
computations are supported. The source code is available at
https://github.com/ddemidov/vexcl.

\section devlist Selection of compute devices

You can select any number of available compute devices, which satisfy provided
filters. Filter is a functor returning bool and acting on a cl::Device
parameter. Several standard filters are provided, such as device type or name
filter, double precision support etc. Filters can be combined with logical
operators. In the example below all devices with names matching "Radeon" and
supporting double precision are selected:
\code
#include <vexcl/vexcl.hpp>
using namespace vex;
int main() {
    vex::Context ctx(Filter::Name("Radeon") && Filter::DoublePrecision);
    std::cout << ctx << std::endl;
}
\endcode
vex::Context object holds list of initialized OpenCL contexts and command
queues for each filtered device. If you just need list of available devices
without creating contexts and queues on them, then look for device_list()
function in documenation.

\section vector Memory allocation and vector arithmetic

Once you got queue list, you can allocate OpenCL buffers on the associated
devices. vex::vector constructor accepts std::vector of cl::CommandQueue.  The
contents of the created vector will be partitioned between each queue
(presumably, each of the provided queues is linked with separate device).  Size
of each partition will be proportional to relative device bandwidth unless
macro VEXCL_DUMB_PARTITIONING is defined, in which case equal partitioning
scheme will be applied. Device bandwidth is measured first time it is requested
by launch of small test kernel.

Multi-platform computation is supported (that is, you can spread your vectors
across devices by different vendors), but should be used with caution: all
computations will be performed with the speed of the slowest device selected.

In the example below host vector is allocated and initialized, then copied to
all GPU devices found in the system. A couple of empty device vectors are
allocated as well:
\code
const size_t n = 1 << 20;
std::vector<double> x(n);
std::generate(x.begin(), x.end(), [](){ return (double)rand() / RAND_MAX; });

vex::Context ctx(Filter::Type(CL_DEVICE_TYPE_GPU));

vex::vector<double> X(ctx.queue(), x);
vex::vector<double> Y(ctx.queue(), n);
vex::vector<double> Z(ctx.queue(), n);
\endcode

You can now use simple vector arithmetic with device vector. For every
expression you use, appropriate kernel is compiled (first time it is
encountered in your program) and called automagically. If you want to see
sources of the generated kernels on the standard output, define
VEXCL_SHOW_KERNELS macro before including VexCL headers.

Vectors are processed in parallel across all devices they were allocated on:
\code
Y = 42;
Z = sqrt(2 * X) + cos(Y);
\endcode

You can copy the result back to host or you can use vector::operator[] to
read (or write) vector elements directly. Though latter technique is very
ineffective and should be used for debugging purposes only.
\code
copy(Z, x);
assert(x[42] == Z[42]);
\endcode

Another frequently performed operation is reduction of a vector expression to
single value, such as summation. This can be done with vex::Reductor class:
\code
Reductor<double> sum(ctx.queue());

std::cout << sum(Z) << std::endl;
std::cout << sum(sqrt(2 * X) + cos(Y)) << std::endl;
\endcode

\section stencil Stencil convolution

Stencil convolution operation comes in handy in many situations. For example,
it allows us to apply a moving average filter to a device vector. All you need
is to construct a vex::stencil object:
\code
// Moving average with 5-points window.
std::vector<double> sdata(5, 0.2);
stencil(ctx.queue(), sdata, sdata.size() / 2);

vex::vector<double> x(ctx.queue(), 1024 * 1024);
vex::vector<double> y(ctx.queue(), 1024 * 1024);

x = 1;
y = x * s; // convolve x with s
\endcode

\section spmv Sparse matrix-vector multiplication

One of the most common operations in linear algebra is matrix-vector
multiplication. Class vex::SpMat holds representation of a sparse matrix,
spanning several devices. In the example below it is used for solution of a
system of linear equations with conjugate gradients method:
\code
typedef double real;
// Solve system of linear equations A u = f with conjugate gradients method.
// Input matrix is represented in CSR format (parameters row, col, and val).
void cg_gpu(
	const std::vector<size_t> &row, // Indices to col and val vectors.
	const std::vector<size_t> &col, // Column numbers of non-zero elements.
	const std::vector<real>   &val, // Values of non-zero elements.
	const std::vector<real>   &rhs, // Right-hand side.
	std::vector<real> &x            // In: initial approximation; out: result.
	)
{
    // Init OpenCL.
    vex::Context ctx(Filter::Type(CL_DEVICE_TYPE_GPU));

    // Move data to compute devices.
    size_t n = x.size();
    vex::SpMat<real>  A(ctx.queue(), n, row.data(), col.data(), val.data());
    vex::vector<real> f(ctx.queue(), rhs);
    vex::vector<real> u(ctx.queue(), x);
    vex::vector<real> r(ctx.queue(), n);
    vex::vector<real> p(ctx.queue(), n);
    vex::vector<real> q(ctx.queue(), n);

    Reductor<real,MAX> max(ctx.queue());
    Reductor<real,SUM> sum(ctx.queue());

    // Solve equation Au = f with conjugate gradients method.
    real rho1, rho2;
    r = f - A * u;

    for(uint iter = 0; max(fabs(r)) > 1e-8 && iter < n; iter++) {
	rho1 = sum(r * r);

	if (iter == 0) {
	    p = r;
	} else {
	    real beta = rho1 / rho2;
	    p = r + beta * p;
	}

	q = A * p;

	real alpha = rho1 / sum(p * q);

	u += alpha * p;
	r -= alpha * q;

	rho2 = rho1;
    }

    // Get result to host.
    copy(u, x);
}
\endcode

\section userfun User-defined functions

Simple arithmetic expressions are sometimes not enough. Imagine that you need
to count how many elements in vector x are greater that their counterparts in
vector y. This may be achieved by introduction of custom function. In order
to build such a function, you need to supply its body, its return type and
types of its arguments. After that, you can apply the function to any valid
vector expressions:
\code
// Function body has to be defined at global scope, and it has to be of `extern
// const char[]` type. This allows us to use it as a template parameter.
extern const char greater_body[] = "return prm1 > prm2 ? 1 : 0;";
UserFunction<greater_body, size_t(float, float)> greater;

size_t count_if_greater(
    const Reductor<size_t, SUM> &sum,
    const vex::vector<float> &x,
    const vex::vector<float> &y
    )
{
    return sum(greater(x, y));
}
\endcode

You could also write sum(greater(x + y, 5 * y)), or use any other expressions
as parameters to the greater() call. Note that in the function body
parameters are always named as prm1, prm2, etc.

\section multivector Multi-component vectors

Class template vex::multivector<T,N> allows to store several equally sized
device vectors and perform computations on all components synchronously.
Operations are delegated to the underlying vectors. Expressions may include
std::array<T,N> values, where N is equal to the number of multivector
components. Each component gets corresponding element of std::array<> when
expression is applied.
\code
const size_t n = 1 << 20;
std::vector<double> host(n * 3);
std::generate(host.begin(), host.end(), rand);

vex::multivector<double,3> x(ctx.queue(), host);
vex::multivector<double,3> y(ctx.queue(), n);

std::array<int, 3> c = {4, 5, 6};

y = 2 * cos(x) - c;

std::array<double,3> v = y[42];
assert(fabs(v[1] - (2 * cos(host[n + 42]) - c[1])) < 1e-8);
\endcode

Components of a multivector may be accessed with operator():
\code
vex::vector<double> z = y(1);
\endcode

\section custkern Using custom kernels

Custom kernels are of course possible as well. vector::operator(uint)
returns cl::Buffer object for a specified device:
\code
vex::Context ctx(Filter::Type(CL_DEVICE_TYPE_GPU));

const size_t n = 1 << 20;
vex::vector<float> x(ctx.queue(), n);

auto program = build_sources(context, std::string(
    "kernel void dummy(ulong size, global float *x)\n"
    "{\n"
    "    size_t i = get_global_id(0);\n"
    "    if (i < size) x[i] = 4.2;\n"
    "}\n"
    ));

for(uint d = 0; d < ctx.size(); d++) {
    auto dummy = cl::Kernel(program, "dummy").bind(ctx.queue()[d], alignup(n, 256), 256);
    dummy((cl_ulong)x.part_size(d), x(d));
}

Reductor<float,SUM> sum(ctx.queue());
std::cout << sum(x) << std::endl;
\endcode

\section scalability Scalability

In the images below, scalability of the library with respect to number of
compute devices is shown. Effective performance (GFLOPS) and bandwidth (GB/sec)
were measured by launching big number of test kernels on one, two, or three
Nvidia Tesla C2070 cards. The results shown are averaged over 20 runs.

The details of the experiments may be found in
examples/benchmark.cpp file.  Basically, performance of the following code
was measured:

\code
// Vector arithmetic
a += b + c * d;

// Reduction
double s = sum(a * b);

// SpMV
y += A * x;
\endcode

\image html perf.png ""

\section compilers Supported compilers

VexCL makes heavy use of C++11 features, so your compiler has to be modern
enough. GCC version 4.6 and above is fully supported. Microsoft Visual C++ 2010
manages to compile the project with some features disabled: since it does not
support variadic templates, only one-argument builtin functions are enabled;
user functions are not available at all.
*/

#ifdef WIN32
#  pragma warning(push)
#  pragma warning(disable : 4290)
#  define NOMINMAX
#endif

#define __CL_ENABLE_EXCEPTIONS

#include <CL/cl.hpp>
#include <iostream>

#include <vexcl/util.hpp>
#include <vexcl/devlist.hpp>
#include <vexcl/vector.hpp>
#include <vexcl/reduce.hpp>
#include <vexcl/spmat.hpp>
#include <vexcl/stencil.hpp>
#include <vexcl/profiler.hpp>

#ifdef WIN32
#  pragma warning(pop)
#endif
#endif
<|endoftext|>


<commit_before>#include <math.h>
#include <robot_p/robothw.h>
#include <boost/math/constants/constants.hpp>

using namespace robotp;

RobotHW::RobotHW(ros::NodeHandle &nh_private)
  : jnt_state_interface()
  , jnt_vel_interface()
  , serial_()
  , ticks_per_revolution_(258)
  , input_buff_()
  , alarm_(false)

{
  // connect and register the joint state interface
  hardware_interface::JointStateHandle state_handle_left(
      "front_left_wheel", &pos_[LEFT], &vel_[LEFT], &eff_[LEFT]);
  jnt_state_interface.registerHandle(state_handle_left);

  hardware_interface::JointStateHandle state_handle_right(
      "front_right_wheel", &pos_[RIGHT], &vel_[RIGHT], &eff_[RIGHT]);
  jnt_state_interface.registerHandle(state_handle_right);

  registerInterface(&jnt_state_interface);

  // connect and register the joint position interface
  hardware_interface::JointHandle vel_handle_left(
      jnt_state_interface.getHandle("front_left_wheel"), &cmd_[LEFT]);
  jnt_vel_interface.registerHandle(vel_handle_left);

  hardware_interface::JointHandle vel_handle_right(
      jnt_state_interface.getHandle("front_right_wheel"), &cmd_[RIGHT]);
  jnt_vel_interface.registerHandle(vel_handle_right);

  registerInterface(&jnt_vel_interface);

  // initialize parameters
  std::string port;
  float Kp, Ki, Kd;

  nh_private.param<double>("ticks_per_revolution", ticks_per_revolution_, 258);

  nh_private.param<std::string>("serial_port", port, "/dev/ttyS0");
  nh_private.param<float>("Kp", Kp, 1);
  nh_private.param<float>("Ki", Kd, 0.3);
  nh_private.param<float>("Kd", Ki, 0.5);

  serial_.setPort(port);
  serial_.setBaudrate(57600);
  try {
    serial_.open();
  } catch (serial::SerialException e) {
    ROS_ERROR_STREAM("[ROBOTHW]: Serial exception: " << e.what());
    return;
  } catch (serial::IOException e) {
    ROS_ERROR_STREAM("ROBOT_P_CONTROL: Serial IO exception: " << e.what());
    return;
  }
  sendMsg(createStartMsg());
  sendMsg(createPIDMsg('P', Kp));
  sendMsg(createPIDMsg('I', Ki));
  sendMsg(createPIDMsg('D', Kd));
}

robotp::RobotHW::~RobotHW()
{
  sendMsg(createStopMsg());
}

void RobotHW::read(const ros::Time &time, const ros::Duration &period)
{
  const size_t MESSAGE_SIZE = 6;
  size_t message_count = serial_.available() / MESSAGE_SIZE;
  std::array<double, 2> ticks = {0, 0};
  for (size_t i = 0; i < message_count; ++i) {
    if (serial_.read(input_buff_.data(), MESSAGE_SIZE) != MESSAGE_SIZE) {
      ROS_ERROR_STREAM("ROBOT_P_CONTROL:unable to read "
                       << MESSAGE_SIZE << " byte message from serial "
                                          "input");
      return;
    }
    Msg32 *msg = reinterpret_cast<Msg32 *>(input_buff_.data());
    switch (msg->id) {
      case 'T':
        // ticks per second
        ticks[0] += static_cast<double>(msg->left) * TICKS_TIME_MULTIPLIER;
        ticks[1] += static_cast<double>(msg->right) * TICKS_TIME_MULTIPLIER;
        ROS_INFO_STREAM("msg: " << msg->left << " " << msg->right);
        break;
      case 'A':
        ROS_WARN_STREAM("ROBOT_P_CONTROL: Alarm received in " << time);
        alarm_ = true;
        break;
      default:
        ROS_ERROR_STREAM(
            "ROBOT_P_CONTROL: Unknown input message type: " << msg->id);
        break;
    }
  }
  // ticks/s to rad/s
  double conversion = 2 * M_PI / ticks_per_revolution_;
  for (size_t i = 0; i < 2; ++i) {
    vel_[i] = ticks[i] * conversion;  // rad/s
    pos_[i] += vel_[i] * period.toSec();
    eff_[i] = 0;
  }
  ROS_INFO_STREAM("ROBOT_P_CONTROL: read -> speed [rad/s] L:"
                  << vel_[0] << " R: " << vel_[1]);
  ROS_INFO_STREAM("ROBOT_P_CONTROL: read -> position [rad] L:"
                  << pos_[0] << " R: " << pos_[1]);
}

void RobotHW::write()
{
  if (alarm_) {
    sendMsg(createStartMsg());
    alarm_ = false;
  } else {
    sendMsg(createVelocityMsg(cmd_[0], cmd_[1]));
  }
}

robotp::RobotHW::Msg32 robotp::RobotHW::createPIDMsg(char letter,
                                                     float data) const
{
  Msg32 msg;
  msg.id = letter;
  msg.pid_param = static_cast<uint16_t>(std::trunc(data * FLOAT_MULTIPLIER));
  msg.padding = 0;
  msg.crc = 0;
  return msg;
}

robotp::RobotHW::Msg32
robotp::RobotHW::createVelocityMsg(double left_vel, double right_vel) const
{
  Msg32 msg;
  msg.id = 'V';
  msg.crc = 0;
  // rad/s to ticks/s
  double conversion = ticks_per_revolution_ / 2 * M_PI;
  auto calcVelocity = [this, conversion](double vel) -> int16_t {
    return static_cast<int16_t>(std::round(vel * conversion)) /
           TICKS_TIME_MULTIPLIER;
  };

  msg.left = calcVelocity(left_vel);
  msg.right = calcVelocity(right_vel);
  return msg;
}

robotp::RobotHW::Msg32 robotp::RobotHW::createStartMsg() const
{
  Msg32 msg;
  msg.id = 'S';
  return msg;
}

robotp::RobotHW::Msg32 robotp::RobotHW::createStopMsg() const
{
  Msg32 msg;
  msg.id = 'E';
  return msg;
}
<commit_msg>Setting up no handshake<commit_after>#include <math.h>
#include <robot_p/robothw.h>
#include <boost/math/constants/constants.hpp>

using namespace robotp;

RobotHW::RobotHW(ros::NodeHandle &nh_private)
  : jnt_state_interface()
  , jnt_vel_interface()
  , serial_()
  , ticks_per_revolution_(258)
  , input_buff_()
  , alarm_(false)

{
  // connect and register the joint state interface
  hardware_interface::JointStateHandle state_handle_left(
      "front_left_wheel", &pos_[LEFT], &vel_[LEFT], &eff_[LEFT]);
  jnt_state_interface.registerHandle(state_handle_left);

  hardware_interface::JointStateHandle state_handle_right(
      "front_right_wheel", &pos_[RIGHT], &vel_[RIGHT], &eff_[RIGHT]);
  jnt_state_interface.registerHandle(state_handle_right);

  registerInterface(&jnt_state_interface);

  // connect and register the joint position interface
  hardware_interface::JointHandle vel_handle_left(
      jnt_state_interface.getHandle("front_left_wheel"), &cmd_[LEFT]);
  jnt_vel_interface.registerHandle(vel_handle_left);

  hardware_interface::JointHandle vel_handle_right(
      jnt_state_interface.getHandle("front_right_wheel"), &cmd_[RIGHT]);
  jnt_vel_interface.registerHandle(vel_handle_right);

  registerInterface(&jnt_vel_interface);

  // initialize parameters
  std::string port;
  float Kp, Ki, Kd;

  nh_private.param<double>("ticks_per_revolution", ticks_per_revolution_, 258);

  nh_private.param<std::string>("serial_port", port, "/dev/ttyS0");
  nh_private.param<float>("Kp", Kp, 1);
  nh_private.param<float>("Ki", Kd, 0.3);
  nh_private.param<float>("Kd", Ki, 0.5);

  serial_.setPort(port);
  serial_.setRTS(false);
  serial_.setDTR(false);
  serial_.setBaudrate(57600);
  try {
    serial_.open();
  } catch (serial::SerialException e) {
    ROS_ERROR_STREAM("[ROBOTHW]: Serial exception: " << e.what());
    return;
  } catch (serial::IOException e) {
    ROS_ERROR_STREAM("ROBOT_P_CONTROL: Serial IO exception: " << e.what());
    return;
  }
  sendMsg(createStartMsg());
  sendMsg(createPIDMsg('P', Kp));
  sendMsg(createPIDMsg('I', Ki));
  sendMsg(createPIDMsg('D', Kd));
}

robotp::RobotHW::~RobotHW()
{
  sendMsg(createStopMsg());
}

void RobotHW::read(const ros::Time &time, const ros::Duration &period)
{
  const size_t MESSAGE_SIZE = 6;
  size_t message_count = serial_.available() / MESSAGE_SIZE;
  std::array<double, 2> ticks = {0, 0};
  for (size_t i = 0; i < message_count; ++i) {
    if (serial_.read(input_buff_.data(), MESSAGE_SIZE) != MESSAGE_SIZE) {
      ROS_ERROR_STREAM("ROBOT_P_CONTROL:unable to read "
                       << MESSAGE_SIZE << " byte message from serial "
                                          "input");
      return;
    }
    Msg32 *msg = reinterpret_cast<Msg32 *>(input_buff_.data());
    switch (msg->id) {
      case 'T':
        // ticks per second
        ticks[0] += static_cast<double>(msg->left) * TICKS_TIME_MULTIPLIER;
        ticks[1] += static_cast<double>(msg->right) * TICKS_TIME_MULTIPLIER;
        ROS_INFO_STREAM("msg: " << msg->left << " " << msg->right);
        break;
      case 'A':
        ROS_WARN_STREAM("ROBOT_P_CONTROL: Alarm received in " << time);
        alarm_ = true;
        break;
      default:
        ROS_ERROR_STREAM(
            "ROBOT_P_CONTROL: Unknown input message type: " << msg->id);
        break;
    }
  }
  // ticks/s to rad/s
  double conversion = 2 * M_PI / ticks_per_revolution_;
  for (size_t i = 0; i < 2; ++i) {
    vel_[i] = ticks[i] * conversion;  // rad/s
    pos_[i] += vel_[i] * period.toSec();
    eff_[i] = 0;
  }
  ROS_INFO_STREAM("ROBOT_P_CONTROL: read -> speed [rad/s] L:"
                  << vel_[0] << " R: " << vel_[1]);
  ROS_INFO_STREAM("ROBOT_P_CONTROL: read -> position [rad] L:"
                  << pos_[0] << " R: " << pos_[1]);
}

void RobotHW::write()
{
  if (alarm_) {
    sendMsg(createStartMsg());
    alarm_ = false;
  } else {
    sendMsg(createVelocityMsg(cmd_[0], cmd_[1]));
  }
}

robotp::RobotHW::Msg32 robotp::RobotHW::createPIDMsg(char letter,
                                                     float data) const
{
  Msg32 msg;
  msg.id = letter;
  msg.pid_param = static_cast<uint16_t>(std::trunc(data * FLOAT_MULTIPLIER));
  msg.padding = 0;
  msg.crc = 0;
  return msg;
}

robotp::RobotHW::Msg32
robotp::RobotHW::createVelocityMsg(double left_vel, double right_vel) const
{
  Msg32 msg;
  msg.id = 'V';
  msg.crc = 0;
  // rad/s to ticks/s
  double conversion = ticks_per_revolution_ / 2 * M_PI;
  auto calcVelocity = [this, conversion](double vel) -> int16_t {
    return static_cast<int16_t>(std::round(vel * conversion)) /
           TICKS_TIME_MULTIPLIER;
  };

  msg.left = calcVelocity(left_vel);
  msg.right = calcVelocity(right_vel);
  return msg;
}

robotp::RobotHW::Msg32 robotp::RobotHW::createStartMsg() const
{
  Msg32 msg;
  msg.id = 'S';
  return msg;
}

robotp::RobotHW::Msg32 robotp::RobotHW::createStopMsg() const
{
  Msg32 msg;
  msg.id = 'E';
  return msg;
}
<|endoftext|>