<commit_before>// This file is part of the dune-gdt project:
//   https://github.com/dune-community/dune-gdt
// Copyright 2010-2017 dune-gdt developers and contributors. All rights reserved.
// License: Dual licensed as BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
//      or  GPL-2.0+ (http://opensource.org/licenses/gpl-license)
//          with "runtime exception" (http://www.dune-project.org/license.html)
// Authors:
//   Tobias Leibner  (2017)

#ifndef DUNE_GDT_HYPERBOLIC_PROBLEMS_MOMENTMODELS_TRIANGULATION_HH
#define DUNE_GDT_HYPERBOLIC_PROBLEMS_MOMENTMODELS_TRIANGULATION_HH

#include <memory>
#include <vector>
#include <string>

#include <dune/gdt/discretefunction/default.hh>
#include <dune/gdt/operators/l2.hh>
#include <dune/gdt/spaces/cg.hh>

#include <dune/xt/common/string.hh>
#include <dune/xt/functions/affine.hh>
#include <dune/xt/grid/gridprovider/cube.hh>
#include <dune/xt/la/container.hh>

#include <dune/pdelab/common/crossproduct.hh>

namespace Dune {
namespace GDT {
namespace Hyperbolic {
namespace Problems {

template <class FieldType, size_t dimDomain>
class Vertex
{
public:
  typedef XT::Common::FieldVector<FieldType, dimDomain> DomainType;

  Vertex() = default;

  Vertex(DomainType pos, size_t index)
    : position_(pos)
    , index_(index)
  {
  }

  const DomainType& position() const
  {
    return position_;
  }

  DomainType& position()
  {
    return position_;
  }

  size_t index() const
  {
    return index_;
  }

  void set_child_with(const std::shared_ptr<Vertex>& parent, std::shared_ptr<Vertex>& child)
  {
    children_.insert({parent->index(), child});
  }

  bool has_child_with(const std::shared_ptr<Vertex>& parent)
  {
    return children_.count(parent->index());
  }

  const std::shared_ptr<Vertex>& child(const std::shared_ptr<Vertex>& parent)
  {
    return children_[parent->index()];
  }

private:
  DomainType position_;
  size_t index_;
  std::map<size_t, std::shared_ptr<Vertex>> children_;
}; // class Vertex

template <class RangeFieldImp = double>
class SphericalTriangle
{
  typedef SphericalTriangle<RangeFieldImp> ThisType;

public:
  typedef Vertex<RangeFieldImp, 3> VertexType;
  typedef typename VertexType::DomainType DomainType;
  typedef typename std::vector<std::shared_ptr<VertexType>> TriangulationVerticesVectorType;
  typedef typename Dune::FieldVector<std::shared_ptr<VertexType>, 3> VertexVectorType;
  typedef typename Dune::FieldVector<std::shared_ptr<ThisType>, 4> SubtrianglesVectorType;
  typedef typename Dune::QuadraturePoint<RangeFieldImp, 3> QuadraturePointType;

  SphericalTriangle() = default;
  SphericalTriangle(ThisType&& other) = default;
  ThisType& operator=(ThisType&& other) = default;

  SphericalTriangle(TriangulationVerticesVectorType& triangulation_vertices,
                    const VertexVectorType& vertices,
                    std::atomic<size_t>* current_face_index,
                    std::atomic<size_t>* current_vertex_index,
                    std::mutex* triangulation_vertices_mutex)
    : triangulation_vertices_(triangulation_vertices)
    , vertices_(vertices)
    , current_face_index_(current_face_index)
    , current_vertex_index_(current_vertex_index)
    , triangulation_vertices_mutex_(triangulation_vertices_mutex)
    , index_((*current_face_index_)++)
    , subtriangle_once_flag_(new std::once_flag)
  {
    calculate_barycentre_rule();
  }

  SphericalTriangle(TriangulationVerticesVectorType& triangulation_vertices,
                    const std::shared_ptr<VertexType> vertex_1,
                    const std::shared_ptr<VertexType> vertex_2,
                    const std::shared_ptr<VertexType> vertex_3,
                    std::atomic<size_t>* current_face_index,
                    std::atomic<size_t>* current_vertex_index,
                    std::mutex* triangulation_vertices_mutex)
    : triangulation_vertices_(triangulation_vertices)
    , vertices_{vertex_1, vertex_2, vertex_3}
    , current_face_index_(current_face_index)
    , current_vertex_index_(current_vertex_index)
    , triangulation_vertices_mutex_(triangulation_vertices_mutex)
    , index_((*current_face_index_)++)
    , subtriangle_once_flag_(new std::once_flag)
  {
    calculate_barycentre_rule();
  }

  const SubtrianglesVectorType& subtriangles() const
  {
    std::call_once(*subtriangle_once_flag_, [&] { initialize_subtriangles(); });
    return subtriangles_;
  }

  SubtrianglesVectorType& subtriangles()
  {
    std::call_once(*subtriangle_once_flag_, [&] { initialize_subtriangles(); });
    return subtriangles_;
  }

  const VertexVectorType& vertices()
  {
    return vertices_;
  }

  const QuadraturePointType& quadrature_point() const
  {
    return *quadrature_point_;
  }

  size_t index()
  {
    return index_;
  }

private:
  void initialize_subtriangles() const
  {
    std::lock_guard<std::mutex> vertices_lock(*triangulation_vertices_mutex_);
    VertexVectorType midpoints;
    for (size_t ii = 0; ii < 3; ++ii) {
      auto& vertex1 = vertices_[ii];
      auto& vertex2 = vertices_[(1 + ii) % 3];
      auto midpoint_position = vertex1->position() + vertex2->position();
      midpoint_position /= midpoint_position.two_norm();
      if (vertex1->has_child_with(vertex2)) {
        midpoints[ii] = vertex1->child(vertex2);
      } else {
        triangulation_vertices_.emplace_back(
            std::make_shared<VertexType>(midpoint_position, (*current_vertex_index_)++));
        vertex1->set_child_with(vertex2, triangulation_vertices_.back());
        vertex2->set_child_with(vertex1, triangulation_vertices_.back());
        midpoints[ii] = triangulation_vertices_.back();
      }
    } // ii
    subtriangles_[0] = std::make_shared<ThisType>(triangulation_vertices_,
                                                  vertices_[0],
                                                  midpoints[0],
                                                  midpoints[2],
                                                  current_face_index_,
                                                  current_vertex_index_,
                                                  triangulation_vertices_mutex_);
    subtriangles_[1] = std::make_shared<ThisType>(triangulation_vertices_,
                                                  vertices_[1],
                                                  midpoints[1],
                                                  midpoints[0],
                                                  current_face_index_,
                                                  current_vertex_index_,
                                                  triangulation_vertices_mutex_);
    subtriangles_[2] = std::make_shared<ThisType>(triangulation_vertices_,
                                                  vertices_[2],
                                                  midpoints[2],
                                                  midpoints[1],
                                                  current_face_index_,
                                                  current_vertex_index_,
                                                  triangulation_vertices_mutex_);
    subtriangles_[3] = std::make_shared<ThisType>(triangulation_vertices_,
                                                  midpoints[0],
                                                  midpoints[1],
                                                  midpoints[2],
                                                  current_face_index_,
                                                  current_vertex_index_,
                                                  triangulation_vertices_mutex_);
  } // initialize_subtriangles()

  void calculate_barycentre_rule() const
  {
    typedef XT::Common::FieldVector<RangeFieldImp, 3> FieldVectorType;
    FieldVectorType ff =
        FieldVectorType(vertices_[0]->position() + vertices_[1]->position() + vertices_[2]->position()) / 3.;
    const RangeFieldImp norm_ff = ff.two_norm();
    const FieldVectorType bb = ff / norm_ff;
    const RangeFieldImp norm_ff_3 = std::pow(norm_ff, 3);
    const FieldVectorType vertices_1_minus_0 = vertices_[1]->position() - vertices_[0]->position();
    const FieldVectorType vertices_2_minus_0 = vertices_[2]->position() - vertices_[0]->position();
    const FieldVectorType partial_s_gg = vertices_2_minus_0 / norm_ff - ff * (ff * vertices_2_minus_0 / norm_ff_3);
    const FieldVectorType partial_t_gg = vertices_1_minus_0 / norm_ff - ff * (ff * vertices_1_minus_0 / norm_ff_3);
    const RangeFieldImp weight = Dune::PDELab::crossproduct(partial_s_gg, partial_t_gg).two_norm() / 2.;
    quadrature_point_ = std::make_unique<QuadraturePointType>(bb, weight);
  }

  TriangulationVerticesVectorType& triangulation_vertices_;
  const VertexVectorType vertices_;
  mutable std::unique_ptr<QuadraturePointType> quadrature_point_;
  mutable SubtrianglesVectorType subtriangles_;
  mutable std::atomic<size_t>* current_face_index_;
  mutable std::atomic<size_t>* current_vertex_index_;
  mutable std::mutex* triangulation_vertices_mutex_;
  size_t index_;
  std::unique_ptr<std::once_flag> subtriangle_once_flag_;
}; // class SphericalTriangle<...>

template <class RangeFieldImp = double>
class SphericalTriangulation
{
public:
  typedef SphericalTriangle<RangeFieldImp> TriangleType;
  typedef std::vector<std::shared_ptr<TriangleType>> TriangleVectorType;
  typedef typename TriangleType::TriangulationVerticesVectorType VertexVectorType;
  typedef typename TriangleType::VertexType VertexType;
  typedef typename VertexType::DomainType DomainType;

  SphericalTriangulation(const std::vector<DomainType>& initial_points, size_t num_refinements = 0)
    : current_face_index_(0)
    , current_vertex_index_(0)
  {
    calculate_faces(initial_points);
    refine(num_refinements);
  }

  const TriangleVectorType& faces() const
  {
    return faces_;
  }

  TriangleVectorType& faces()
  {
    return faces_;
  }

  const VertexVectorType& vertices() const
  {
    return vertices_;
  }

  VertexVectorType& vertices()
  {
    return vertices_;
  }

  // 3D quadrature on sphere (from http://www.unizar.es/galdeano/actas_pau/PDFVIII/pp61-69.pdf)
  Dune::QuadratureRule<RangeFieldImp, 3> quadrature_rule(size_t refinements = 0) const
  {
    TriangleVectorType quadrature_faces = get_subtriangles(refinements);
    Dune::QuadratureRule<RangeFieldImp, 3> ret;
    for (size_t ii = 0; ii < quadrature_faces.size(); ++ii)
      ret.push_back(quadrature_faces[ii]->quadrature_point());
    return ret;
  }

private:
  void refine(size_t times = 1)
  {
    set_faces_to_subtriangles(times);
    vertices_ = all_vertices_;
  } // void refine(...)

  TriangleVectorType get_subtriangles(size_t refinements = 1) const
  {
    TriangleVectorType subtriangles = faces_;
    while (refinements-- > 0)
      get_subtriangles(subtriangles);
    return subtriangles;
  } // ... get_subtriangles(...)

  void get_subtriangles(TriangleVectorType& subtriangles) const
  {
    const size_t old_size = subtriangles.size();
    subtriangles.resize(4. * old_size);
    for (size_t ii = 0; ii < old_size; ++ii) {
      const auto& local_subtriangles = subtriangles[ii]->subtriangles();
      for (size_t jj = 0; jj < 4; ++jj)
        subtriangles[(3 - jj) * old_size + ii] = local_subtriangles[jj];
    }
  }

  void set_faces_to_subtriangles(size_t refinements = 1)
  {
    while (refinements-- > 0) {
      current_face_index_ = 0;
      get_subtriangles(faces_);
    }
  }

  void calculate_faces(const std::vector<DomainType>& points0)
  {
    for (const auto& point : points0)
      all_vertices_.emplace_back(std::make_shared<VertexType>(point, current_vertex_index_++));
    vertices_ = all_vertices_;
    const auto all_vertices = all_vertices_;
    auto vertices0 = all_vertices_;
    while (vertices0.size() > 0) {
      const auto v0 = vertices0.back();
      vertices0.pop_back();
      auto vertices1 = vertices0;
      while (vertices1.size() > 0) {
        const auto v1 = vertices1.back();
        vertices1.pop_back();
        for (const auto& v2 : vertices1) {
          // calculate plane equation defined by three points
          const auto v0v1 = v1->position() - v0->position();
          const auto v0v2 = v2->position() - v0->position();
          const auto normal = PDELab::crossproduct(v0v1, v0v2);
          if (XT::Common::FloatCmp::ne(normal.two_norm2(), 0.)) {
            bool is_face = true;
            double max_value = std::numeric_limits<double>::lowest();
            double min_value = std::numeric_limits<double>::max();
            for (const auto& v3 : all_vertices) {
              const auto v0v3 = v3->position() - v0->position();
              const auto value = normal * v0v3;
              max_value = std::max(max_value, value);
              min_value = std::min(min_value, value);
              if (XT::Common::FloatCmp::lt(min_value * max_value, 0.)) {
                is_face = false;
                break;
              }
            } // p3
            if (is_face) {
              // if max_value is <= 0, all values are less or equal zero,
              // i.e the normal points outwards and thus p0, p1, p2 are oriented counterclockwise, which is what we want
              if (XT::Common::FloatCmp::le(max_value, 0.))
                faces_.emplace_back(std::make_shared<TriangleType>(
                    all_vertices_, v0, v1, v2, &current_face_index_, &current_vertex_index_, &all_vertices_mutex_));
              else
                faces_.emplace_back(std::make_shared<TriangleType>(
                    all_vertices_, v0, v2, v1, &current_face_index_, &current_vertex_index_, &all_vertices_mutex_));
            } // if (is_face)
          } // check if points define a plane
        } // p2
      } // p1
    } // p0
  } // void calculate_faces(...)

  TriangleVectorType faces_;
  VertexVectorType vertices_;
  VertexVectorType all_vertices_;
  mutable std::mutex all_vertices_mutex_;
  std::atomic<size_t> current_face_index_;
  std::atomic<size_t> current_vertex_index_;
}; // class SphericalTriangulation<...>

} // namespace Problems
} // namespace Hyperbolic
} // namespace GDT
} // namespace Dune

#endif // DUNE_GDT_HYPERBOLIC_PROBLEMS_MOMENTMODELS_TRIANGULATION_HH
<commit_msg>[test.hyperbolic] guard against missing pdelab<commit_after>// This file is part of the dune-gdt project:
//   https://github.com/dune-community/dune-gdt
// Copyright 2010-2017 dune-gdt developers and contributors. All rights reserved.
// License: Dual licensed as BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
//      or  GPL-2.0+ (http://opensource.org/licenses/gpl-license)
//          with "runtime exception" (http://www.dune-project.org/license.html)
// Authors:
//   Tobias Leibner  (2017)

#ifndef DUNE_GDT_HYPERBOLIC_PROBLEMS_MOMENTMODELS_TRIANGULATION_HH
#define DUNE_GDT_HYPERBOLIC_PROBLEMS_MOMENTMODELS_TRIANGULATION_HH

#include <memory>
#include <vector>
#include <string>

#include <dune/common/typetraits.hh>

#include <dune/xt/common/string.hh>
#include <dune/xt/la/container.hh>
#include <dune/xt/grid/gridprovider/cube.hh>
#include <dune/xt/functions/affine.hh>

#include <dune/gdt/discretefunction/default.hh>
#include <dune/gdt/operators/l2.hh>
#include <dune/gdt/spaces/cg.hh>

#if HAVE_DUNE_PDELAB
#include <dune/pdelab/common/crossproduct.hh>
#endif

namespace Dune {
namespace GDT {
namespace Hyperbolic {
namespace Problems {

#if HAVE_DUNE_PDELAB

template <class FieldType, size_t dimDomain>
class Vertex
{
public:
  typedef XT::Common::FieldVector<FieldType, dimDomain> DomainType;

  Vertex() = default;

  Vertex(DomainType pos, size_t index)
    : position_(pos)
    , index_(index)
  {
  }

  const DomainType& position() const
  {
    return position_;
  }

  DomainType& position()
  {
    return position_;
  }

  size_t index() const
  {
    return index_;
  }

  void set_child_with(const std::shared_ptr<Vertex>& parent, std::shared_ptr<Vertex>& child)
  {
    children_.insert({parent->index(), child});
  }

  bool has_child_with(const std::shared_ptr<Vertex>& parent)
  {
    return children_.count(parent->index());
  }

  const std::shared_ptr<Vertex>& child(const std::shared_ptr<Vertex>& parent)
  {
    return children_[parent->index()];
  }

private:
  DomainType position_;
  size_t index_;
  std::map<size_t, std::shared_ptr<Vertex>> children_;
}; // class Vertex

template <class RangeFieldImp = double>
class SphericalTriangle
{
  typedef SphericalTriangle<RangeFieldImp> ThisType;

public:
  typedef Vertex<RangeFieldImp, 3> VertexType;
  typedef typename VertexType::DomainType DomainType;
  typedef typename std::vector<std::shared_ptr<VertexType>> TriangulationVerticesVectorType;
  typedef typename Dune::FieldVector<std::shared_ptr<VertexType>, 3> VertexVectorType;
  typedef typename Dune::FieldVector<std::shared_ptr<ThisType>, 4> SubtrianglesVectorType;
  typedef typename Dune::QuadraturePoint<RangeFieldImp, 3> QuadraturePointType;

  SphericalTriangle() = default;
  SphericalTriangle(ThisType&& other) = default;
  ThisType& operator=(ThisType&& other) = default;

  SphericalTriangle(TriangulationVerticesVectorType& triangulation_vertices,
                    const VertexVectorType& vertices,
                    std::atomic<size_t>* current_face_index,
                    std::atomic<size_t>* current_vertex_index,
                    std::mutex* triangulation_vertices_mutex)
    : triangulation_vertices_(triangulation_vertices)
    , vertices_(vertices)
    , current_face_index_(current_face_index)
    , current_vertex_index_(current_vertex_index)
    , triangulation_vertices_mutex_(triangulation_vertices_mutex)
    , index_((*current_face_index_)++)
    , subtriangle_once_flag_(new std::once_flag)
  {
    calculate_barycentre_rule();
  }

  SphericalTriangle(TriangulationVerticesVectorType& triangulation_vertices,
                    const std::shared_ptr<VertexType> vertex_1,
                    const std::shared_ptr<VertexType> vertex_2,
                    const std::shared_ptr<VertexType> vertex_3,
                    std::atomic<size_t>* current_face_index,
                    std::atomic<size_t>* current_vertex_index,
                    std::mutex* triangulation_vertices_mutex)
    : triangulation_vertices_(triangulation_vertices)
    , vertices_{vertex_1, vertex_2, vertex_3}
    , current_face_index_(current_face_index)
    , current_vertex_index_(current_vertex_index)
    , triangulation_vertices_mutex_(triangulation_vertices_mutex)
    , index_((*current_face_index_)++)
    , subtriangle_once_flag_(new std::once_flag)
  {
    calculate_barycentre_rule();
  }

  const SubtrianglesVectorType& subtriangles() const
  {
    std::call_once(*subtriangle_once_flag_, [&] { initialize_subtriangles(); });
    return subtriangles_;
  }

  SubtrianglesVectorType& subtriangles()
  {
    std::call_once(*subtriangle_once_flag_, [&] { initialize_subtriangles(); });
    return subtriangles_;
  }

  const VertexVectorType& vertices()
  {
    return vertices_;
  }

  const QuadraturePointType& quadrature_point() const
  {
    return *quadrature_point_;
  }

  size_t index()
  {
    return index_;
  }

private:
  void initialize_subtriangles() const
  {
    std::lock_guard<std::mutex> vertices_lock(*triangulation_vertices_mutex_);
    VertexVectorType midpoints;
    for (size_t ii = 0; ii < 3; ++ii) {
      auto& vertex1 = vertices_[ii];
      auto& vertex2 = vertices_[(1 + ii) % 3];
      auto midpoint_position = vertex1->position() + vertex2->position();
      midpoint_position /= midpoint_position.two_norm();
      if (vertex1->has_child_with(vertex2)) {
        midpoints[ii] = vertex1->child(vertex2);
      } else {
        triangulation_vertices_.emplace_back(
            std::make_shared<VertexType>(midpoint_position, (*current_vertex_index_)++));
        vertex1->set_child_with(vertex2, triangulation_vertices_.back());
        vertex2->set_child_with(vertex1, triangulation_vertices_.back());
        midpoints[ii] = triangulation_vertices_.back();
      }
    } // ii
    subtriangles_[0] = std::make_shared<ThisType>(triangulation_vertices_,
                                                  vertices_[0],
                                                  midpoints[0],
                                                  midpoints[2],
                                                  current_face_index_,
                                                  current_vertex_index_,
                                                  triangulation_vertices_mutex_);
    subtriangles_[1] = std::make_shared<ThisType>(triangulation_vertices_,
                                                  vertices_[1],
                                                  midpoints[1],
                                                  midpoints[0],
                                                  current_face_index_,
                                                  current_vertex_index_,
                                                  triangulation_vertices_mutex_);
    subtriangles_[2] = std::make_shared<ThisType>(triangulation_vertices_,
                                                  vertices_[2],
                                                  midpoints[2],
                                                  midpoints[1],
                                                  current_face_index_,
                                                  current_vertex_index_,
                                                  triangulation_vertices_mutex_);
    subtriangles_[3] = std::make_shared<ThisType>(triangulation_vertices_,
                                                  midpoints[0],
                                                  midpoints[1],
                                                  midpoints[2],
                                                  current_face_index_,
                                                  current_vertex_index_,
                                                  triangulation_vertices_mutex_);
  } // initialize_subtriangles()

  void calculate_barycentre_rule() const
  {
    typedef XT::Common::FieldVector<RangeFieldImp, 3> FieldVectorType;
    FieldVectorType ff =
        FieldVectorType(vertices_[0]->position() + vertices_[1]->position() + vertices_[2]->position()) / 3.;
    const RangeFieldImp norm_ff = ff.two_norm();
    const FieldVectorType bb = ff / norm_ff;
    const RangeFieldImp norm_ff_3 = std::pow(norm_ff, 3);
    const FieldVectorType vertices_1_minus_0 = vertices_[1]->position() - vertices_[0]->position();
    const FieldVectorType vertices_2_minus_0 = vertices_[2]->position() - vertices_[0]->position();
    const FieldVectorType partial_s_gg = vertices_2_minus_0 / norm_ff - ff * (ff * vertices_2_minus_0 / norm_ff_3);
    const FieldVectorType partial_t_gg = vertices_1_minus_0 / norm_ff - ff * (ff * vertices_1_minus_0 / norm_ff_3);
    const RangeFieldImp weight = Dune::PDELab::crossproduct(partial_s_gg, partial_t_gg).two_norm() / 2.;
    quadrature_point_ = std::make_unique<QuadraturePointType>(bb, weight);
  }

  TriangulationVerticesVectorType& triangulation_vertices_;
  const VertexVectorType vertices_;
  mutable std::unique_ptr<QuadraturePointType> quadrature_point_;
  mutable SubtrianglesVectorType subtriangles_;
  mutable std::atomic<size_t>* current_face_index_;
  mutable std::atomic<size_t>* current_vertex_index_;
  mutable std::mutex* triangulation_vertices_mutex_;
  size_t index_;
  std::unique_ptr<std::once_flag> subtriangle_once_flag_;
}; // class SphericalTriangle<...>

template <class RangeFieldImp = double>
class SphericalTriangulation
{
public:
  typedef SphericalTriangle<RangeFieldImp> TriangleType;
  typedef std::vector<std::shared_ptr<TriangleType>> TriangleVectorType;
  typedef typename TriangleType::TriangulationVerticesVectorType VertexVectorType;
  typedef typename TriangleType::VertexType VertexType;
  typedef typename VertexType::DomainType DomainType;

  SphericalTriangulation(const std::vector<DomainType>& initial_points, size_t num_refinements = 0)
    : current_face_index_(0)
    , current_vertex_index_(0)
  {
    calculate_faces(initial_points);
    refine(num_refinements);
  }

  const TriangleVectorType& faces() const
  {
    return faces_;
  }

  TriangleVectorType& faces()
  {
    return faces_;
  }

  const VertexVectorType& vertices() const
  {
    return vertices_;
  }

  VertexVectorType& vertices()
  {
    return vertices_;
  }

  // 3D quadrature on sphere (from http://www.unizar.es/galdeano/actas_pau/PDFVIII/pp61-69.pdf)
  Dune::QuadratureRule<RangeFieldImp, 3> quadrature_rule(size_t refinements = 0) const
  {
    TriangleVectorType quadrature_faces = get_subtriangles(refinements);
    Dune::QuadratureRule<RangeFieldImp, 3> ret;
    for (size_t ii = 0; ii < quadrature_faces.size(); ++ii)
      ret.push_back(quadrature_faces[ii]->quadrature_point());
    return ret;
  }

private:
  void refine(size_t times = 1)
  {
    set_faces_to_subtriangles(times);
    vertices_ = all_vertices_;
  } // void refine(...)

  TriangleVectorType get_subtriangles(size_t refinements = 1) const
  {
    TriangleVectorType subtriangles = faces_;
    while (refinements-- > 0)
      get_subtriangles(subtriangles);
    return subtriangles;
  } // ... get_subtriangles(...)

  void get_subtriangles(TriangleVectorType& subtriangles) const
  {
    const size_t old_size = subtriangles.size();
    subtriangles.resize(4. * old_size);
    for (size_t ii = 0; ii < old_size; ++ii) {
      const auto& local_subtriangles = subtriangles[ii]->subtriangles();
      for (size_t jj = 0; jj < 4; ++jj)
        subtriangles[(3 - jj) * old_size + ii] = local_subtriangles[jj];
    }
  }

  void set_faces_to_subtriangles(size_t refinements = 1)
  {
    while (refinements-- > 0) {
      current_face_index_ = 0;
      get_subtriangles(faces_);
    }
  }

  void calculate_faces(const std::vector<DomainType>& points0)
  {
    for (const auto& point : points0)
      all_vertices_.emplace_back(std::make_shared<VertexType>(point, current_vertex_index_++));
    vertices_ = all_vertices_;
    const auto all_vertices = all_vertices_;
    auto vertices0 = all_vertices_;
    while (vertices0.size() > 0) {
      const auto v0 = vertices0.back();
      vertices0.pop_back();
      auto vertices1 = vertices0;
      while (vertices1.size() > 0) {
        const auto v1 = vertices1.back();
        vertices1.pop_back();
        for (const auto& v2 : vertices1) {
          // calculate plane equation defined by three points
          const auto v0v1 = v1->position() - v0->position();
          const auto v0v2 = v2->position() - v0->position();
          const auto normal = PDELab::crossproduct(v0v1, v0v2);
          if (XT::Common::FloatCmp::ne(normal.two_norm2(), 0.)) {
            bool is_face = true;
            double max_value = std::numeric_limits<double>::lowest();
            double min_value = std::numeric_limits<double>::max();
            for (const auto& v3 : all_vertices) {
              const auto v0v3 = v3->position() - v0->position();
              const auto value = normal * v0v3;
              max_value = std::max(max_value, value);
              min_value = std::min(min_value, value);
              if (XT::Common::FloatCmp::lt(min_value * max_value, 0.)) {
                is_face = false;
                break;
              }
            } // p3
            if (is_face) {
              // if max_value is <= 0, all values are less or equal zero,
              // i.e the normal points outwards and thus p0, p1, p2 are oriented counterclockwise, which is what we want
              if (XT::Common::FloatCmp::le(max_value, 0.))
                faces_.emplace_back(std::make_shared<TriangleType>(
                    all_vertices_, v0, v1, v2, &current_face_index_, &current_vertex_index_, &all_vertices_mutex_));
              else
                faces_.emplace_back(std::make_shared<TriangleType>(
                    all_vertices_, v0, v2, v1, &current_face_index_, &current_vertex_index_, &all_vertices_mutex_));
            } // if (is_face)
          } // check if points define a plane
        } // p2
      } // p1
    } // p0
  } // void calculate_faces(...)

  TriangleVectorType faces_;
  VertexVectorType vertices_;
  VertexVectorType all_vertices_;
  mutable std::mutex all_vertices_mutex_;
  std::atomic<size_t> current_face_index_;
  std::atomic<size_t> current_vertex_index_;
}; // class SphericalTriangulation<...>

#else // HAVE_DUNE_PDELAB

template <class FieldType, size_t dimDomain>
class Vertex
{
  static_assert(AlwaysFalse<FieldType>::value, "You are missing dune-pdelab!");
};

template <class RangeFieldImp = double>
class SphericalTriangle
{
  static_assert(AlwaysFalse<RangeFieldImp>::value, "You are missing dune-pdelab!");
};

template <class RangeFieldImp = double>
class SphericalTriangulation
{
  static_assert(AlwaysFalse<RangeFieldImp>::value, "You are missing dune-pdelab!");
};

#endif // HAVE_DUNE_PDELAB

} // namespace Problems
} // namespace Hyperbolic
} // namespace GDT
} // namespace Dune

#endif // DUNE_GDT_HYPERBOLIC_PROBLEMS_MOMENTMODELS_TRIANGULATION_HH
<|endoftext|>


<commit_before>#include <sltypes.h>
#include <slnt.h>
#include <slntuapi.h>
#include <pushbase.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <slc.h>
#include <slmodule.h>

#include "..\push.h"
#include "..\ring0.h"
#include "..\ini.h"

#include "hwinfo.h"
#include "CPU/intel.h"
#include "disk.h"
#include "GPU/d3dkmt.h"

UINT64  g_hNVPMContext;
BOOLEAN    PushGpuLoadD3DKMT = FALSE;
DWORD dwMappedMemAddr;
PUSH_HARDWARE_INFORMATION hardware;

#define REGISTER_VENDORID   0x00
#define REGISTER_CLASSCODE  0x08
#define REGISTER_BAR0       0x10
#define REGISTER_BAR2       0x18
#include "GPU\gpu.h"
CGPU* HwGpu;

UINT16
GetEngineClock()
{
    if (!HwGpu)
        return 0;

    return HwGpu->GetEngineClock();
}

UINT16
GetMemoryClock()
{
    if (!HwGpu)
        return 0;

    return HwGpu->GetMemoryClock();
}

UINT8
GetGpuTemp()
{
    if (!HwGpu)
        return 0;

    return HwGpu->GetTemperature();
}

UINT8
GetGpuLoadHardware()
{
    if (!HwGpu)
        return 0;

    return HwGpu->GetLoad();
}

UINT8
GetGpuLoad()
{
    if (PushGpuLoadD3DKMT)
        //return GetGpuLoadD3DKMT();
        return D3DKMTGetGpuUsage();
    else
        return GetGpuLoadHardware();
}

UINT8
GetCpuTemp()
{
    return GetIntelTemp();
}

UINT64
D3DKMTGetMemoryUsage();

UINT32
GetVramUsed()
{
    UINT64 total, free, used;

    if (!HwGpu)
        return 0;

    total = HwGpu->GetTotalMemory();
    free = HwGpu->GetFreeMemory();
    used = total - free;

    return used / 1048576;
}

UINT8
GetVramUsage()
{
    return 100 * ( (float) hardware.DisplayDevice.FrameBuffer.Used / (float) hardware.DisplayDevice.FrameBuffer.Total );
}

UINT32
GetFrameBufferSize()
{
    return HwGpu->GetTotalMemory();
}

UINT32
GetRamUsed()
{
    SYSTEM_BASIC_PERFORMANCE_INFORMATION performanceInfo;
    UINT64 totalPageFile, availablePageFile;

    NtQuerySystemInformation(
        SystemBasicPerformanceInformation,
        &performanceInfo,
        sizeof(SYSTEM_BASIC_PERFORMANCE_INFORMATION),
        NULL
        );

    totalPageFile = performanceInfo.CommitLimit;
    totalPageFile *= PushPageSize;

    availablePageFile = performanceInfo.CommitLimit - performanceInfo.CommittedPages;
    availablePageFile *= PushPageSize;

    return (totalPageFile - availablePageFile) / 1048576;
}

UINT8
GetRamUsage()
{
    return 100 * ( (float) hardware.Memory.Used / (float) hardware.Memory.Total);
}

UINT8
GetCpuLoad()
{
    ULARGE_INTEGER idle, kernel, user;
    static ULARGE_INTEGER  idle_old, kernel_old, user_old;
    INT32 usage;

    GetSystemTimes( (FILETIME *) &idle, (FILETIME *) &kernel, (FILETIME *) &user);

    usage = (((((kernel.QuadPart - kernel_old.QuadPart) + (user.QuadPart - user_old.QuadPart)) - (idle.QuadPart - idle_old.QuadPart)) * (100)) / ((kernel.QuadPart - kernel_old.QuadPart) + (user.QuadPart    - user_old.QuadPart)));

    idle_old.QuadPart   = idle.QuadPart;
    user_old.QuadPart   = user.QuadPart;
    kernel_old.QuadPart = kernel.QuadPart;

    return usage;
}

typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION
{
    LARGE_INTEGER   IdleTime;
    LARGE_INTEGER   KernelTime;
    LARGE_INTEGER   UserTime;
    LARGE_INTEGER   Reserved1[2];
    ULONG           Reserved2;
} SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION;

#define SUCCEEDED(hr) (((long)(hr)) >= 0)

FLOAT
GetCpuCoreUsage( UINT8 coreNumber )
{
    LARGE_INTEGER perfCounter;
    CORE_LIST *coreListEntry = &hardware.Processor.coreList;

    while (coreListEntry->number != coreNumber)
    {
        coreListEntry = coreListEntry->nextEntry;
    }

    if (!PushSharedMemory->performanceFrequency)
        //init high-resolution timer if it is not initialized yet
        NtQueryPerformanceCounter(&perfCounter, (LARGE_INTEGER*) &PushSharedMemory->performanceFrequency );

    if (PushSharedMemory->performanceFrequency)
        //we can perform calcualtion only if high-resolution timer is available
    {
        NtQueryPerformanceCounter(&perfCounter, NULL);
            //query high-resolution time counter

        if (perfCounter.QuadPart - coreListEntry->perfCounter.QuadPart >= PushSharedMemory->performanceFrequency)
            //update usage once per second
        {
            UINT32 size = sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION) * hardware.Processor.NumberOfCores;

            SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *info = (SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION*) RtlAllocateHeap(PushHeapHandle, 0, size);

            if (SUCCEEDED(NtQuerySystemInformation(SystemProcessorPerformanceInformation, info, size, NULL)))
                //query CPU usage
            {
                if (coreListEntry->idleTime.QuadPart)
                    //ensure that this function was already called at least once
                    //and we have the previous idle time value
                {
                    coreListEntry->usage = 100.0f - 0.00001f * (info[coreListEntry->number].IdleTime.QuadPart - coreListEntry->idleTime.QuadPart) * PushSharedMemory->performanceFrequency / (perfCounter.QuadPart - coreListEntry->perfCounter.QuadPart);
                        //calculate new CPU usage value by estimating amount of time
                        //CPU was in idle during the last second

                    //clip calculated CPU usage to [0-100] range to filter calculation non-ideality

                    if (coreListEntry->usage < 0.0f)
                        coreListEntry->usage = 0.0f;

                    if (coreListEntry->usage > 100.0f)
                        coreListEntry->usage = 100.0f;

                }

                coreListEntry->idleTime = info[coreListEntry->number].IdleTime;
                    //save new idle time for specified CPU
                coreListEntry->perfCounter = perfCounter;
                    //save new performance counter for specified CPU

                //SlFree(info);
                RtlFreeHeap(PushHeapHandle, 0, info);
            }
        }
    }

    return coreListEntry->usage;
}

UINT8
GetMaxCoreLoad()
{
    FLOAT usage = 0.0f;
    FLOAT maxUsage = 0.0f;
    UINT8 i = 0;

    for (i = 0; i < hardware.Processor.NumberOfCores; i++)
    {
        usage = GetCpuCoreUsage(i);

        if (usage > maxUsage)
        {
            maxUsage = usage;
        }
    }

    return maxUsage;
}

UINT32
GetDiskReadWriteRate()
{
    return DiskGetBytesDelta();
}

UINT16
GetDiskResponseTime()
{
    return DiskGetResponseTime();
}

VOID
HwForceMaxClocks()
{
    HwGpu->ForceMaximumClocks();
}

DWORD GetGpuAddress()
{
    UINT8   bus             = 0;
    UINT8   dev             = 0;
    UINT8   func            = 0;
    DWORD   pciAddress      = 0xFFFFFFFF;
    DWORD   conf[3]         = {0};
    BOOLEAN    multiFuncFlag   = FALSE;
    BYTE    type            = 0;

    for(bus = 0; bus <= 255; bus++)
    {
        for(dev = 0; dev < 31; dev++)
        {
            multiFuncFlag = FALSE;

            for(func = 0; func < 7; func++)
            {
                if(multiFuncFlag == FALSE && func > 0)
                {
                    break;
                }

                pciAddress = PciBusDevFunc(bus, dev, func);

                if (!R0ReadPciConfig(pciAddress, 0, (BYTE *)conf, sizeof(conf)))
                    return NULL;

                if(func == 0) // Is Multi Function Device
                {
                    if (!R0ReadPciConfig(pciAddress, 0x0E, (BYTE *)&type, sizeof(type)))
                        return NULL;

                    if(type & 0x80)
                    {
                        multiFuncFlag = TRUE;
                    }

                }

                if((conf[2] & 0xFFFFFF00) ==
                        (((DWORD)0x03 << 24) |
                        ((DWORD)0x00 << 16) |
                        ((DWORD)0x00 << 8))
                    )
                {
                    return pciAddress;
                }

            }
        }
    }
    return 0xFFFFFFFF;
}

VOID* HwMmio;
DWORD
ReadGpuRegister( DWORD Address )
{
    DWORD* val=0;
    DWORD address;
    DWORD ret;

    address = ((DWORD)HwMmio) + Address;

    val = (DWORD*)address;
    ret = *val;
    return ret;
}

DWORD
GetBarAddress( DWORD Bar )
{
    DWORD barAddress;

    R0ReadPciConfig(
        hardware.DisplayDevice.pciAddress,
        Bar,
        (BYTE *)&barAddress,
        sizeof(barAddress)
        );

    // 16-Byte alligned address;
    return (barAddress & 0x0fffffff0);
}

UINT32
GetBarSize(
    DWORD BarAddress
    )
{
   unsigned pos = 0;

   while (!(BarAddress & 1))
   {
      BarAddress >>= 1;
      ++pos;
   }

    return 1 << (pos - 1);
}

VOID
InitGpuHardware()
{
    DWORD bar;

    hardware.DisplayDevice.pciAddress = GetGpuAddress();

    R0ReadPciConfig(
        hardware.DisplayDevice.pciAddress,
        REGISTER_VENDORID,
        (BYTE *) &hardware.DisplayDevice.VendorId,
        sizeof(hardware.DisplayDevice.VendorId)
        );

    if (hardware.DisplayDevice.VendorId == NVIDIA)
        bar = REGISTER_BAR0;
    else if (hardware.DisplayDevice.VendorId == AMD)
        bar = REGISTER_BAR2;
    else
        return;

    hardware.DisplayDevice.BarAddress = GetBarAddress(bar);

    HwMmio = R0MapPhysicalMemory(
                hardware.DisplayDevice.BarAddress,
                GetBarSize(hardware.DisplayDevice.BarAddress)
                );
}

VOID
GetHardwareInfo()
{
    SYSTEM_BASIC_INFORMATION basicInfo;
    SYSTEM_BASIC_PERFORMANCE_INFORMATION performanceInfo;
    int i = 0;
    UINT64 totalPageFile;
    CORE_LIST *coreListEntry;

    InitGpuHardware();

    HwGpu = CreateGpuInterface( hardware.DisplayDevice.VendorId );

    if (HwGpu)
    {
        hardware.DisplayDevice.FrameBuffer.Total = HwGpu->GetTotalMemory();
        hardware.DisplayDevice.EngineClockMax   = HwGpu->GetMaximumEngineClock();
        hardware.DisplayDevice.MemoryClockMax   = HwGpu->GetMaximumMemoryClock();
    }

    if (IniReadBoolean(L"Settings", L"GpuUsageD3DKMT", FALSE))
        PushGpuLoadD3DKMT = TRUE;

    /*if (PushGpuLoadD3DKMT || hardware.DisplayDevice.VendorId)
        //InitializeD3DKMT();
        D3DKMTInitialize();*/

    // Get the number of processors in the system
    NtQuerySystemInformation(
        SystemBasicInformation,
        &basicInfo,
        sizeof(SYSTEM_BASIC_INFORMATION),
        0
        );

    hardware.Processor.NumberOfCores = basicInfo.NumberOfProcessors;
    PushPageSize = basicInfo.PageSize;

    coreListEntry = &hardware.Processor.coreList;

    for (i = 0; i < hardware.Processor.NumberOfCores; i++)
    {
        coreListEntry->nextEntry = (CORE_LIST*)RtlAllocateHeap(
                                    PushHeapHandle,
                                    0,
                                    sizeof(CORE_LIST)
                                    );

        coreListEntry->number                   = i;
        coreListEntry->idleTime.QuadPart        = 0;
        coreListEntry->perfCounter.QuadPart     = 0;
        coreListEntry->usage                    = 0.0f;

        coreListEntry = coreListEntry->nextEntry;
    }

    //InitGeForce(hardware.DisplayDevice.coreFamily);

    //initialize memory info
    NtQuerySystemInformation(
        SystemBasicPerformanceInformation,
        &performanceInfo,
        sizeof(SYSTEM_BASIC_PERFORMANCE_INFORMATION),
        NULL
        );

    totalPageFile = performanceInfo.CommitLimit;
    totalPageFile *= PushPageSize;

    hardware.Memory.Total = (totalPageFile >> 20);

    // Start disk monitoring;
    DiskStartMonitoring();
}

VOID
RefreshHardwareInfo()
{
    hardware.Processor.Load                 = GetCpuLoad();
    hardware.Processor.MaxCoreUsage         = GetMaxCoreLoad();
    hardware.Processor.Temperature          = GetCpuTemp();
    hardware.DisplayDevice.Load             = GetGpuLoad();
    hardware.DisplayDevice.EngineClock      = GetEngineClock();
    hardware.DisplayDevice.MemoryClock      = GetMemoryClock();
    hardware.DisplayDevice.Temperature      = GetGpuTemp();
    hardware.DisplayDevice.FrameBuffer.Used = GetVramUsed();
    hardware.DisplayDevice.FrameBuffer.Load = GetVramUsage();
    hardware.Memory.Used                    = GetRamUsed();
    hardware.Memory.Load                    = GetRamUsage();
    hardware.Disk.ReadWriteRate             = GetDiskReadWriteRate();
    hardware.Disk.ResponseTime              = GetDiskResponseTime();

    // We actually get some information from other sources
    hardware.Processor.MaxThreadUsage = PushSharedMemory->HarwareInformation.Processor.MaxThreadUsage;

    /*if (PushSharedMemory->ThreadMonitorOSD)
    {
        hardware.Processor.MaxThreadUsage = GetMaxThreadUsage(
                                                PushSharedMemory->GameProcessID
                                                );

        if(g_ThreadOptimization && !g_ThreadListLock)
        {
            RunThreadOptimizeRoutine();
        }
    }*/
}
<commit_msg>fixed gpu hardware detection<commit_after>#include <sltypes.h>
#include <slnt.h>
#include <slntuapi.h>
#include <pushbase.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <slc.h>
#include <slmodule.h>

#include "..\push.h"
#include "..\ring0.h"
#include "..\ini.h"

#include "hwinfo.h"
#include "CPU/intel.h"
#include "disk.h"
#include "GPU/d3dkmt.h"

UINT64  g_hNVPMContext;
BOOLEAN    PushGpuLoadD3DKMT = FALSE;
DWORD dwMappedMemAddr;
PUSH_HARDWARE_INFORMATION hardware;

#define REGISTER_VENDORID   0x00
#define REGISTER_CLASSCODE  0x08
#define REGISTER_BAR0       0x10
#define REGISTER_BAR2       0x18
#include "GPU\gpu.h"
CGPU* HwGpu;

UINT16
GetEngineClock()
{
    if (!HwGpu)
        return 0;

    return HwGpu->GetEngineClock();
}

UINT16
GetMemoryClock()
{
    if (!HwGpu)
        return 0;

    return HwGpu->GetMemoryClock();
}

UINT8
GetGpuTemp()
{
    if (!HwGpu)
        return 0;

    return HwGpu->GetTemperature();
}

UINT8
GetGpuLoadHardware()
{
    if (!HwGpu)
        return 0;

    return HwGpu->GetLoad();
}

UINT8
GetGpuLoad()
{
    if (PushGpuLoadD3DKMT)
        //return GetGpuLoadD3DKMT();
        return D3DKMTGetGpuUsage();
    else
        return GetGpuLoadHardware();
}

UINT8
GetCpuTemp()
{
    return GetIntelTemp();
}

UINT64
D3DKMTGetMemoryUsage();

UINT32
GetVramUsed()
{
    UINT64 total, free, used;

    if (!HwGpu)
        return 0;

    total = HwGpu->GetTotalMemory();
    free = HwGpu->GetFreeMemory();
    used = total - free;

    return used / 1048576;
}

UINT8
GetVramUsage()
{
    return 100 * ( (float) hardware.DisplayDevice.FrameBuffer.Used / (float) hardware.DisplayDevice.FrameBuffer.Total );
}

UINT32
GetFrameBufferSize()
{
    return HwGpu->GetTotalMemory();
}

UINT32
GetRamUsed()
{
    SYSTEM_BASIC_PERFORMANCE_INFORMATION performanceInfo;
    UINT64 totalPageFile, availablePageFile;

    NtQuerySystemInformation(
        SystemBasicPerformanceInformation,
        &performanceInfo,
        sizeof(SYSTEM_BASIC_PERFORMANCE_INFORMATION),
        NULL
        );

    totalPageFile = performanceInfo.CommitLimit;
    totalPageFile *= PushPageSize;

    availablePageFile = performanceInfo.CommitLimit - performanceInfo.CommittedPages;
    availablePageFile *= PushPageSize;

    return (totalPageFile - availablePageFile) / 1048576;
}

UINT8
GetRamUsage()
{
    return 100 * ( (float) hardware.Memory.Used / (float) hardware.Memory.Total);
}

UINT8
GetCpuLoad()
{
    ULARGE_INTEGER idle, kernel, user;
    static ULARGE_INTEGER  idle_old, kernel_old, user_old;
    INT32 usage;

    GetSystemTimes( (FILETIME *) &idle, (FILETIME *) &kernel, (FILETIME *) &user);

    usage = (((((kernel.QuadPart - kernel_old.QuadPart) + (user.QuadPart - user_old.QuadPart)) - (idle.QuadPart - idle_old.QuadPart)) * (100)) / ((kernel.QuadPart - kernel_old.QuadPart) + (user.QuadPart    - user_old.QuadPart)));

    idle_old.QuadPart   = idle.QuadPart;
    user_old.QuadPart   = user.QuadPart;
    kernel_old.QuadPart = kernel.QuadPart;

    return usage;
}

typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION
{
    LARGE_INTEGER   IdleTime;
    LARGE_INTEGER   KernelTime;
    LARGE_INTEGER   UserTime;
    LARGE_INTEGER   Reserved1[2];
    ULONG           Reserved2;
} SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION;

#define SUCCEEDED(hr) (((long)(hr)) >= 0)

FLOAT
GetCpuCoreUsage( UINT8 coreNumber )
{
    LARGE_INTEGER perfCounter;
    CORE_LIST *coreListEntry = &hardware.Processor.coreList;

    while (coreListEntry->number != coreNumber)
    {
        coreListEntry = coreListEntry->nextEntry;
    }

    if (!PushSharedMemory->performanceFrequency)
        //init high-resolution timer if it is not initialized yet
        NtQueryPerformanceCounter(&perfCounter, (LARGE_INTEGER*) &PushSharedMemory->performanceFrequency );

    if (PushSharedMemory->performanceFrequency)
        //we can perform calcualtion only if high-resolution timer is available
    {
        NtQueryPerformanceCounter(&perfCounter, NULL);
            //query high-resolution time counter

        if (perfCounter.QuadPart - coreListEntry->perfCounter.QuadPart >= PushSharedMemory->performanceFrequency)
            //update usage once per second
        {
            UINT32 size = sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION) * hardware.Processor.NumberOfCores;

            SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *info = (SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION*) RtlAllocateHeap(PushHeapHandle, 0, size);

            if (SUCCEEDED(NtQuerySystemInformation(SystemProcessorPerformanceInformation, info, size, NULL)))
                //query CPU usage
            {
                if (coreListEntry->idleTime.QuadPart)
                    //ensure that this function was already called at least once
                    //and we have the previous idle time value
                {
                    coreListEntry->usage = 100.0f - 0.00001f * (info[coreListEntry->number].IdleTime.QuadPart - coreListEntry->idleTime.QuadPart) * PushSharedMemory->performanceFrequency / (perfCounter.QuadPart - coreListEntry->perfCounter.QuadPart);
                        //calculate new CPU usage value by estimating amount of time
                        //CPU was in idle during the last second

                    //clip calculated CPU usage to [0-100] range to filter calculation non-ideality

                    if (coreListEntry->usage < 0.0f)
                        coreListEntry->usage = 0.0f;

                    if (coreListEntry->usage > 100.0f)
                        coreListEntry->usage = 100.0f;

                }

                coreListEntry->idleTime = info[coreListEntry->number].IdleTime;
                    //save new idle time for specified CPU
                coreListEntry->perfCounter = perfCounter;
                    //save new performance counter for specified CPU

                //SlFree(info);
                RtlFreeHeap(PushHeapHandle, 0, info);
            }
        }
    }

    return coreListEntry->usage;
}

UINT8
GetMaxCoreLoad()
{
    FLOAT usage = 0.0f;
    FLOAT maxUsage = 0.0f;
    UINT8 i = 0;

    for (i = 0; i < hardware.Processor.NumberOfCores; i++)
    {
        usage = GetCpuCoreUsage(i);

        if (usage > maxUsage)
        {
            maxUsage = usage;
        }
    }

    return maxUsage;
}

UINT32
GetDiskReadWriteRate()
{
    return DiskGetBytesDelta();
}

UINT16
GetDiskResponseTime()
{
    return DiskGetResponseTime();
}

VOID
HwForceMaxClocks()
{
    HwGpu->ForceMaximumClocks();
}

DWORD GetGpuAddress()
{
    UINT8   bus             = 0;
    UINT8   dev             = 0;
    UINT8   func            = 0;
    DWORD   pciAddress      = 0xFFFFFFFF;
    DWORD   conf[3]         = {0};
    BOOLEAN    multiFuncFlag   = FALSE;
    BYTE    type            = 0;

    for(bus = 0; bus <= 255; bus++)
    {
        for(dev = 0; dev < 31; dev++)
        {
            multiFuncFlag = FALSE;

            for(func = 0; func < 7; func++)
            {
                if(multiFuncFlag == FALSE && func > 0)
                {
                    break;
                }

                pciAddress = PciBusDevFunc(bus, dev, func);

                R0ReadPciConfig(pciAddress, 0, (BYTE *)conf, sizeof(conf));

                if(func == 0) // Is Multi Function Device
                {
                    R0ReadPciConfig(pciAddress, 0x0E, (BYTE *)&type, sizeof(type));

                    if(type & 0x80)
                    {
                        multiFuncFlag = TRUE;
                    }

                }

                if((conf[2] & 0xFFFFFF00) ==
                        (((DWORD)0x03 << 24) |
                        ((DWORD)0x00 << 16) |
                        ((DWORD)0x00 << 8))
                    )
                {
                    return pciAddress;
                }

            }
        }
    }
    return 0xFFFFFFFF;
}

VOID* HwMmio;
DWORD
ReadGpuRegister( DWORD Address )
{
    DWORD* val=0;
    DWORD address;
    DWORD ret;

    address = ((DWORD)HwMmio) + Address;

    val = (DWORD*)address;
    ret = *val;
    return ret;
}

DWORD
GetBarAddress( DWORD Bar )
{
    DWORD barAddress;

    R0ReadPciConfig(
        hardware.DisplayDevice.pciAddress,
        Bar,
        (BYTE *)&barAddress,
        sizeof(barAddress)
        );

    // 16-Byte alligned address;
    return (barAddress & 0x0fffffff0);
}

UINT32
GetBarSize(
    DWORD BarAddress
    )
{
   unsigned pos = 0;

   while (!(BarAddress & 1))
   {
      BarAddress >>= 1;
      ++pos;
   }

    return 1 << (pos - 1);
}

VOID
InitGpuHardware()
{
    DWORD bar;

    hardware.DisplayDevice.pciAddress = GetGpuAddress();

    R0ReadPciConfig(
        hardware.DisplayDevice.pciAddress,
        REGISTER_VENDORID,
        (BYTE *) &hardware.DisplayDevice.VendorId,
        sizeof(hardware.DisplayDevice.VendorId)
        );

    if (hardware.DisplayDevice.VendorId == NVIDIA)
        bar = REGISTER_BAR0;
    else if (hardware.DisplayDevice.VendorId == AMD)
        bar = REGISTER_BAR2;
    else
        return;

    hardware.DisplayDevice.BarAddress = GetBarAddress(bar);

    HwMmio = R0MapPhysicalMemory(
                hardware.DisplayDevice.BarAddress,
                GetBarSize(hardware.DisplayDevice.BarAddress)
                );
}

VOID
GetHardwareInfo()
{
    SYSTEM_BASIC_INFORMATION basicInfo;
    SYSTEM_BASIC_PERFORMANCE_INFORMATION performanceInfo;
    int i = 0;
    UINT64 totalPageFile;
    CORE_LIST *coreListEntry;

    InitGpuHardware();

    HwGpu = CreateGpuInterface( hardware.DisplayDevice.VendorId );

    if (HwGpu)
    {
        hardware.DisplayDevice.FrameBuffer.Total = HwGpu->GetTotalMemory();
        hardware.DisplayDevice.EngineClockMax   = HwGpu->GetMaximumEngineClock();
        hardware.DisplayDevice.MemoryClockMax   = HwGpu->GetMaximumMemoryClock();
    }

    if (IniReadBoolean(L"Settings", L"GpuUsageD3DKMT", FALSE))
        PushGpuLoadD3DKMT = TRUE;

    /*if (PushGpuLoadD3DKMT || hardware.DisplayDevice.VendorId)
        //InitializeD3DKMT();
        D3DKMTInitialize();*/

    // Get the number of processors in the system
    NtQuerySystemInformation(
        SystemBasicInformation,
        &basicInfo,
        sizeof(SYSTEM_BASIC_INFORMATION),
        0
        );

    hardware.Processor.NumberOfCores = basicInfo.NumberOfProcessors;
    PushPageSize = basicInfo.PageSize;

    coreListEntry = &hardware.Processor.coreList;

    for (i = 0; i < hardware.Processor.NumberOfCores; i++)
    {
        coreListEntry->nextEntry = (CORE_LIST*)RtlAllocateHeap(
                                    PushHeapHandle,
                                    0,
                                    sizeof(CORE_LIST)
                                    );

        coreListEntry->number                   = i;
        coreListEntry->idleTime.QuadPart        = 0;
        coreListEntry->perfCounter.QuadPart     = 0;
        coreListEntry->usage                    = 0.0f;

        coreListEntry = coreListEntry->nextEntry;
    }

    //InitGeForce(hardware.DisplayDevice.coreFamily);

    //initialize memory info
    NtQuerySystemInformation(
        SystemBasicPerformanceInformation,
        &performanceInfo,
        sizeof(SYSTEM_BASIC_PERFORMANCE_INFORMATION),
        NULL
        );

    totalPageFile = performanceInfo.CommitLimit;
    totalPageFile *= PushPageSize;

    hardware.Memory.Total = (totalPageFile >> 20);

    // Start disk monitoring;
    DiskStartMonitoring();
}

VOID
RefreshHardwareInfo()
{
    hardware.Processor.Load                 = GetCpuLoad();
    hardware.Processor.MaxCoreUsage         = GetMaxCoreLoad();
    hardware.Processor.Temperature          = GetCpuTemp();
    hardware.DisplayDevice.Load             = GetGpuLoad();
    hardware.DisplayDevice.EngineClock      = GetEngineClock();
    hardware.DisplayDevice.MemoryClock      = GetMemoryClock();
    hardware.DisplayDevice.Temperature      = GetGpuTemp();
    hardware.DisplayDevice.FrameBuffer.Used = GetVramUsed();
    hardware.DisplayDevice.FrameBuffer.Load = GetVramUsage();
    hardware.Memory.Used                    = GetRamUsed();
    hardware.Memory.Load                    = GetRamUsage();
    hardware.Disk.ReadWriteRate             = GetDiskReadWriteRate();
    hardware.Disk.ResponseTime              = GetDiskResponseTime();

    // We actually get some information from other sources
    hardware.Processor.MaxThreadUsage = PushSharedMemory->HarwareInformation.Processor.MaxThreadUsage;

    /*if (PushSharedMemory->ThreadMonitorOSD)
    {
        hardware.Processor.MaxThreadUsage = GetMaxThreadUsage(
                                                PushSharedMemory->GameProcessID
                                                );

        if(g_ThreadOptimization && !g_ThreadListLock)
        {
            RunThreadOptimizeRoutine();
        }
    }*/
}
<|endoftext|>


<commit_before>// Tests for the RooCacheManager
// Author: Jonas Rembser, CERN, May 2021

#include "RooObjCacheManager.h"
#include "RooRealVar.h"

#include "gtest/gtest.h"

#include <string>
#include <vector>

TEST(RooCacheManager, TestSelectFromArgSet)
{
   // Test RooCacheManager::selectFromSet1 and RooCacheManager::selectFromSet2.

   // the cached class doesn't matter for this test, it just has to be an object that the cache is going to own
   class CacheElem : public RooAbsCacheElement {
   public:
      virtual ~CacheElem() {}
      virtual RooArgList containedArgs(Action) { return {}; }
   };

   // RooObjCacheManager is a wrapper around RooCacheManager
   RooObjCacheManager mgr{nullptr, 100};

   // fill some vector with RooAbsReals for testing
   std::vector<RooRealVar> vars;
   for (int i = 0; i < 10; ++i) {
      auto name = std::string("v") + std::to_string(i);
      vars.emplace_back(name.c_str(), name.c_str(), static_cast<double>(i));
   }

   RooArgSet nset1{vars[0], vars[1], "nset1"};
   RooArgSet iset1{vars[2], vars[3], vars[4], "nset2"};

   RooArgSet nset2{vars[5], vars[6], vars[7], "nset1"};
   RooArgSet iset2{vars[8], vars[9], "nset2"};

   int idx1 = mgr.setObj(&nset1, &iset1, new CacheElem);
   int idx2 = mgr.setObj(&nset2, &iset2, new CacheElem);

   std::cout << idx1 << std::endl;
   std::cout << idx2 << std::endl;

   auto sel11 = mgr.selectFromSet1({vars[0], vars[4]}, idx1);
   auto sel12 = mgr.selectFromSet2({vars[2], vars[4]}, idx1);

   auto sel21 = mgr.selectFromSet1({vars[1], vars[6]}, idx2);
   auto sel22 = mgr.selectFromSet2({vars[0], vars[1]}, idx2);

   // check if the expected number of args were selected
   EXPECT_EQ(sel11.size(), 1);
   EXPECT_EQ(sel12.size(), 2);
   EXPECT_EQ(sel21.size(), 1);
   EXPECT_EQ(sel22.size(), 0);

   // check if the correct args were selected
   EXPECT_TRUE(sel11.containsInstance(vars[0]));
   EXPECT_TRUE(sel12.containsInstance(vars[2]));
   EXPECT_TRUE(sel12.containsInstance(vars[4]));
   EXPECT_TRUE(sel21.containsInstance(vars[6]));
}
<commit_msg>[RF] Test if RooAbsArg _cacheList is still valid after reading model<commit_after>// Tests for the RooCacheManager
// Author: Jonas Rembser, CERN, May 2021

#include "RooAbsPdf.h"
#include "RooGenericPdf.h"
#include "RooObjCacheManager.h"
#include "RooRealVar.h"

#include "TFile.h"

#include "gtest/gtest.h"

#include <string>
#include <vector>

TEST(RooCacheManager, TestSelectFromArgSet)
{
   // Test RooCacheManager::selectFromSet1 and RooCacheManager::selectFromSet2.

   // the cached class doesn't matter for this test, it just has to be an object that the cache is going to own
   class CacheElem : public RooAbsCacheElement {
   public:
      virtual ~CacheElem() {}
      virtual RooArgList containedArgs(Action) { return {}; }
   };

   // RooObjCacheManager is a wrapper around RooCacheManager
   RooObjCacheManager mgr{nullptr, 100};

   // fill some vector with RooAbsReals for testing
   std::vector<RooRealVar> vars;
   for (int i = 0; i < 10; ++i) {
      auto name = std::string("v") + std::to_string(i);
      vars.emplace_back(name.c_str(), name.c_str(), static_cast<double>(i));
   }

   RooArgSet nset1{vars[0], vars[1], "nset1"};
   RooArgSet iset1{vars[2], vars[3], vars[4], "nset2"};

   RooArgSet nset2{vars[5], vars[6], vars[7], "nset1"};
   RooArgSet iset2{vars[8], vars[9], "nset2"};

   int idx1 = mgr.setObj(&nset1, &iset1, new CacheElem);
   int idx2 = mgr.setObj(&nset2, &iset2, new CacheElem);

   auto sel11 = mgr.selectFromSet1({vars[0], vars[4]}, idx1);
   auto sel12 = mgr.selectFromSet2({vars[2], vars[4]}, idx1);

   auto sel21 = mgr.selectFromSet1({vars[1], vars[6]}, idx2);
   auto sel22 = mgr.selectFromSet2({vars[0], vars[1]}, idx2);

   // check if the expected number of args were selected
   EXPECT_EQ(sel11.size(), 1);
   EXPECT_EQ(sel12.size(), 2);
   EXPECT_EQ(sel21.size(), 1);
   EXPECT_EQ(sel22.size(), 0);

   // check if the correct args were selected
   EXPECT_TRUE(sel11.containsInstance(vars[0]));
   EXPECT_TRUE(sel12.containsInstance(vars[2]));
   EXPECT_TRUE(sel12.containsInstance(vars[4]));
   EXPECT_TRUE(sel21.containsInstance(vars[6]));
}

TEST(RooCacheManager, TestAbsArgCacheListConsistency)
{
   // Every instance of a RooAbsCache or inherigin class that is the member of
   // a RooFit arg is automatically added to the RooAbsArg::_cacheList data
   // member by reference.
   //
   // This test makes sure that the _cacheList still has the correct pointers
   // after reading back a RooFit model. Now that the RooAbsCache and child
   // classes don't take part in the IO anymore it should be no problem, but in
   // the past there were inconsistencies.
   {
      RooRealVar x("x", "x", 0, -10, 10);
      RooGenericPdf pdf("pdf", "pdf", "x", RooArgList(x));

      uintptr_t pdfAddr = (uintptr_t)&pdf;
      uintptr_t cacheAddr = (uintptr_t)pdf.getCache(0);
      // Check if the cache pointer actually points to a data member of the pdf.
      // We can't get the actual address of the private _normMgr member, but we
      // can still check if the cache points to a member by checking the range
      // of the address.
      EXPECT_TRUE(cacheAddr >= pdfAddr && cacheAddr < pdfAddr + sizeof(RooGenericPdf));
      EXPECT_EQ(pdf.numCaches(), 1);

      TFile f1("testRooCacheManager_1.root", "RECREATE");

      x.Write();
      pdf.Write();
   }

   {
      TFile f1("testRooCacheManager_1.root", "READ");
      auto pdf = f1.Get<RooAbsPdf>("pdf");

      uintptr_t pdfAddr = (uintptr_t)pdf;
      uintptr_t cacheAddr = (uintptr_t)pdf->getCache(0);
      // Same trick to check if the cache points to a pdf data member as above.
      EXPECT_TRUE(cacheAddr >= pdfAddr && cacheAddr < pdfAddr + sizeof(RooGenericPdf));
      EXPECT_EQ(pdf->numCaches(), 1);
   }
}
<|endoftext|>


<commit_before>// Copyright © 2016 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

#include <Arduino.h>
#include <TheThingsNetwork.h>

#define debugPrintLn(...) { if (debugStream) debugStream->println(__VA_ARGS__); }
#define debugPrint(...) { if (debugStream) debugStream->print(__VA_ARGS__); }

String TheThingsNetwork::readLine(int waitTime) {
  unsigned long start = millis();
  while (millis() < start + waitTime) {
    String line = modemStream->readStringUntil('\n');
    if (line.length() > 0) {
      return line.substring(0, line.length() - 1);
    }
  }
  return "";
}

bool TheThingsNetwork::waitForOK(int waitTime, String okMessage) {
  String line = readLine(waitTime);
  if (line == "") {
    debugPrintLn(F("Wait for OK time-out expired"));
    return false;
  }

  if (line != okMessage) {
    debugPrint(F("Response is not OK: "));
    debugPrintLn(line);
    return false;
  }

  return true;
}

String TheThingsNetwork::readValue(String cmd) {
  modemStream->println(cmd);
  return readLine();
}

bool TheThingsNetwork::sendCommand(String cmd, int waitTime) {
  debugPrint(F("Sending: "));
  debugPrintLn(cmd);

  modemStream->println(cmd);

  return waitForOK(waitTime);
}

bool TheThingsNetwork::sendCommand(String cmd, String value, int waitTime) {
  int l = value.length();
  byte buf[l];
  value.getBytes(buf, l);

  return sendCommand(cmd, buf, l, waitTime);
}

char btohexa_high(unsigned char b) {
  b >>= 4;
  return (b > 0x9u) ? b + 'A' - 10 : b + '0';
}

char btohexa_low(unsigned char b) {
  b &= 0x0F;
  return (b > 0x9u) ? b + 'A' - 10 : b + '0';
}

bool TheThingsNetwork::sendCommand(String cmd, const byte *buf, int length, int waitTime) {
  debugPrint(F("Sending: "));
  debugPrint(cmd);
  debugPrint(F(" with "));
  debugPrint(length);
  debugPrintLn(F(" bytes"));

  modemStream->print(cmd + " ");

  for (int i = 0; i < length; i++) {
    modemStream->print(btohexa_high(buf[i]));
    modemStream->print(btohexa_low(buf[i]));
  }
  modemStream->println();

  return waitForOK(waitTime);
}

void TheThingsNetwork::reset(bool adr) {
  #if !TTN_ADR_SUPPORTED
    adr = false;
  #endif

  modemStream->println(F("sys reset"));
  String version = readLine(3000);
  if (version == "") {
    debugPrintLn(F("Invalid version"));
    return;
  }

  model = version.substring(0, version.indexOf(' '));
  debugPrint(F("Version is "));
  debugPrint(version);
  debugPrint(F(", model is "));
  debugPrintLn(model);

  String devEui = readValue(F("sys get hweui"));
  String str = "";
  str.concat(F("mac set deveui "));
  str.concat(devEui);
  sendCommand(str);

  String devEui = readValue(F("sys get hweui"));
  String str = "";
  str.concat(F("mac set deveui "));
  str.concat(devEui);
  sendCommand(str);

  str = "";
  str.concat(F("mac set adr "));
  if(adr){
    str.concat(F("on"));
  } else {
    str.concat(F("off"));
  }
  sendCommand(str);
}

void TheThingsNetwork::onMessage(void (*cb)(const byte* payload, int length, int port)) {
  this->messageCallback = cb;
}

bool TheThingsNetwork::personalize(const byte devAddr[4], const byte nwkSKey[16], const byte appSKey[16]) {
  reset();
  sendCommand(F("mac set devaddr"), devAddr, 4);
  sendCommand(F("mac set nwkskey"), nwkSKey, 16);
  sendCommand(F("mac set appskey"), appSKey, 16);
  return personalize();
}

bool TheThingsNetwork::personalize() {
  configure_channels(this->sf, this->fsb);
  sendCommand(F("mac join abp"));
  String response = readLine();
  if (response != F("accepted")) {
    debugPrint(F("Personalize not accepted: "));
    debugPrintLn(response);
    return false;
  }

  debugPrint(F("Personalize accepted. Status: "));
  debugPrintLn(readValue(F("mac get status")));
  return true;
}

bool TheThingsNetwork::provision(const byte appEui[8], const byte appKey[16]) {
  sendCommand(F("mac set appeui"), appEui, 8);
  sendCommand(F("mac set appkey"), appKey, 16);
  return sendCommand(F("mac save"), 50000);
}

bool TheThingsNetwork::join(int retries, long int retryDelay) {
  configure_channels(this->sf, this->fsb);
  String devEui = readValue(F("sys get hweui"));
  String str = "";
  str.concat(F("mac set deveui "));
  str.concat(devEui);
  sendCommand(str);
  while (retries != 0) {
    if (retries > 0) {
      retries--;
    }
    if (!sendCommand(F("mac join otaa"))) {
      debugPrintLn(F("Send join command failed"));
      delay(retryDelay);
      continue;
    }
    String response = readLine(10000);
    if (response != F("accepted")) {
      debugPrint(F("Join not accepted: "));
      debugPrintLn(response);
      delay(retryDelay);
      continue;
    }
    debugPrint(F("Join accepted. Status: "));
    debugPrintLn(readValue(F("mac get status")));
    debugPrint(F("DevAddr: "));
    debugPrintLn(readValue(F("mac get devaddr")));
    return true;
  }
  return false;
}

bool TheThingsNetwork::join(const byte appEui[8], const byte appKey[16], int retries, long int retryDelay) {
  reset();
  provision(appEui, appKey);
  configure_channels();
  return join(retries, retryDelay);
}

int TheThingsNetwork::sendBytes(const byte* payload, int length, int port, bool confirm) {
  String str = "";
  str.concat(F("mac tx "));
  str.concat(confirm ? F("cnf ") : F("uncnf "));
  str.concat(port);
  if (!sendCommand(str, payload, length)) {
    debugPrintLn(F("Send command failed"));
    return -1;
  }

  String response = readLine(10000);
  if (response == "") {
    debugPrintLn(F("Time-out"));
    return -2;
  }
  if (response == F("mac_tx_ok")) {
    debugPrintLn(F("Successful transmission"));
    return 1;
  }
  if (response.startsWith(F("mac_rx"))) {
    int portEnds = response.indexOf(" ", 7);
    int downlinkPort = response.substring(7, portEnds).toInt();
    String data = response.substring(portEnds + 1);
    int downlinkLength = data.length() / 2;
    byte downlink[64];
    for (int i = 0, d = 0; i < downlinkLength; i++, d += 2)
      downlink[i] = TTN_HEX_PAIR_TO_BYTE(data[d], data[d+1]);
    debugPrint(F("Successful transmission. Received "));
    debugPrint(downlinkLength);
    debugPrintLn(F(" bytes"));
    if (this->messageCallback)
      this->messageCallback(downlink, downlinkLength, downlinkPort);
    return 2;
  }

  debugPrint(F("Unexpected response: "));
  debugPrintLn(response);
  return -10;
}

int TheThingsNetwork::poll(int port, bool confirm) {
  byte payload[] = { 0x00 };
  return sendBytes(payload, 1, port, confirm);
}

void TheThingsNetwork::showStatus() {
  debugPrint(F("EUI: "));
  debugPrintLn(readValue(F("sys get hweui")));
  debugPrint(F("Battery: "));
  debugPrintLn(readValue(F("sys get vdd")));
  debugPrint(F("AppEUI: "));
  debugPrintLn(readValue(F("mac get appeui")));
  debugPrint(F("DevEUI: "));
  debugPrintLn(readValue(F("mac get deveui")));

  if (this->model == F("RN2483")) {
    debugPrint(F("Band: "));
    debugPrintLn(readValue(F("mac get band")));
  }

  debugPrint(F("Data Rate: "));
  debugPrintLn(readValue(F("mac get dr")));
  debugPrint(F("RX Delay 1: "));
  debugPrintLn(readValue(F("mac get rxdelay1")));
  debugPrint(F("RX Delay 2: "));
  debugPrintLn(readValue(F("mac get rxdelay2")));
}

void TheThingsNetwork::configure_EU868(int sf) {
  int ch;
  int dr = -1;
  long int freq = 867100000;
  String str = "";

  str.concat(F("mac set rx2 3 869525000"));
  sendCommand(str);
  str = "";
  for (ch = 0; ch <= 7; ch++) {
    if (ch >= 3) {
      str.concat(F("mac set ch freq "));
      str.concat(ch);
      str.concat(F(" "));
      str.concat(freq);
      sendCommand(str);
      str = "";
      str.concat(F("mac set ch drrange "));
      str.concat(ch);
      str.concat(F(" 0 5"));
      sendCommand(str);
      str = "";
      str.concat(F("mac set ch status "));
      str.concat(ch);
      str.concat(F(" on"));
      sendCommand(str);
      str = "";
      freq = freq + 200000;
    }
    str.concat(F("mac set ch dcycle "));
    str.concat(ch);
    str.concat(F(" 799"));
    sendCommand(str);
    str = "";
  }
  str.concat(F("mac set ch drrange 1 0 6"));
  sendCommand(str);
  str = "";
  str.concat(F("mac set pwridx "));
  str.concat(TTN_PWRIDX_868);
  sendCommand(str);
  switch (sf) {
    case 7:
      dr = 5;
      break;
    case 8:
      dr = 4;
      break;
    case 9:
      dr = 3;
      break;
    case 10:
      dr = 2;
      break;
    case 11:
      dr = 1;
      break;
    case 12:
      dr = 0;
      break;
    default:
      debugPrintLn(F("Invalid SF"))
      break;
  }
  if (dr > -1){
    str = "";
    str.concat(F("mac set dr "));
    str.concat(dr);
    sendCommand(str);
  }
}

void TheThingsNetwork::configure_US915(int sf, int fsb) {
  int ch;
  int dr = -1;
  String str = "";
  int chLow = fsb > 0 ? (fsb - 1) * 8 : 0;
  int chHigh = fsb > 0 ? chLow + 7 : 71;
  int ch500 = fsb + 63;

  sendCommand(F("radio set freq 904200000"));
  str = "";
  str.concat(F("mac set pwridx "));
  str.concat(TTN_PWRIDX_915);
  sendCommand(str);
  for (ch = 0; ch < 72; ch++) {
    str = "";
    str.concat(F("mac set ch status "));
    str.concat(ch);
    (ch == ch500 || ch <= chHigh && ch >= chLow) ? str.concat(F(" on")) : str.concat(F(" off"));
    sendCommand(str);
    if (ch < 63 && (ch == ch500 || ch <= chHigh && ch >= chLow)) {
      str = "";
      str.concat(F("mac set ch drrange "));
      str.concat(ch);
      str.concat(F(" 0 3"));
      sendCommand(str);
      str = "";
    }
  }
  switch (sf) {
    case 7:
      dr = 3;
      break;
    case 8:
     dr = 2;
     break;
    case 9:
      dr = 1;
      break;
    case 10:
      dr = 0;
      break;
    default:
      debugPrintLn(F("Invalid SF"))
      break;
  }
  if (dr > -1){
    str = "";
    str.concat(F("mac set dr "));
    str.concat(dr);
    sendCommand(str);
  }
}

void TheThingsNetwork::configure_channels(int sf, int fsb) {
  switch (this->fp) {
    case TTN_FP_EU868:
      configure_EU868(sf);
      break;
    case TTN_FP_US915:
      configure_US915(sf, fsb);
      break;
    default:
      debugPrintLn("Invalid frequency plan");
      break;
  }
}

TheThingsNetwork::TheThingsNetwork(Stream& modemStream, Stream& debugStream, fp_ttn_t fp, int sf, int fsb) {
  this->debugStream = &debugStream;
  this->modemStream = &modemStream;
  this->fp = fp;
  this->sf = sf;
  this->fsb = fsb;
}
<commit_msg>replaced inline by if statements in configure_US915()<commit_after>// Copyright © 2016 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

#include <Arduino.h>
#include <TheThingsNetwork.h>

#define debugPrintLn(...) { if (debugStream) debugStream->println(__VA_ARGS__); }
#define debugPrint(...) { if (debugStream) debugStream->print(__VA_ARGS__); }

String TheThingsNetwork::readLine(int waitTime) {
  unsigned long start = millis();
  while (millis() < start + waitTime) {
    String line = modemStream->readStringUntil('\n');
    if (line.length() > 0) {
      return line.substring(0, line.length() - 1);
    }
  }
  return "";
}

bool TheThingsNetwork::waitForOK(int waitTime, String okMessage) {
  String line = readLine(waitTime);
  if (line == "") {
    debugPrintLn(F("Wait for OK time-out expired"));
    return false;
  }

  if (line != okMessage) {
    debugPrint(F("Response is not OK: "));
    debugPrintLn(line);
    return false;
  }

  return true;
}

String TheThingsNetwork::readValue(String cmd) {
  modemStream->println(cmd);
  return readLine();
}

bool TheThingsNetwork::sendCommand(String cmd, int waitTime) {
  debugPrint(F("Sending: "));
  debugPrintLn(cmd);

  modemStream->println(cmd);

  return waitForOK(waitTime);
}

bool TheThingsNetwork::sendCommand(String cmd, String value, int waitTime) {
  int l = value.length();
  byte buf[l];
  value.getBytes(buf, l);

  return sendCommand(cmd, buf, l, waitTime);
}

char btohexa_high(unsigned char b) {
  b >>= 4;
  return (b > 0x9u) ? b + 'A' - 10 : b + '0';
}

char btohexa_low(unsigned char b) {
  b &= 0x0F;
  return (b > 0x9u) ? b + 'A' - 10 : b + '0';
}

bool TheThingsNetwork::sendCommand(String cmd, const byte *buf, int length, int waitTime) {
  debugPrint(F("Sending: "));
  debugPrint(cmd);
  debugPrint(F(" with "));
  debugPrint(length);
  debugPrintLn(F(" bytes"));

  modemStream->print(cmd + " ");

  for (int i = 0; i < length; i++) {
    modemStream->print(btohexa_high(buf[i]));
    modemStream->print(btohexa_low(buf[i]));
  }
  modemStream->println();

  return waitForOK(waitTime);
}

void TheThingsNetwork::reset(bool adr) {
  #if !TTN_ADR_SUPPORTED
    adr = false;
  #endif

  modemStream->println(F("sys reset"));
  String version = readLine(3000);
  if (version == "") {
    debugPrintLn(F("Invalid version"));
    return;
  }

  model = version.substring(0, version.indexOf(' '));
  debugPrint(F("Version is "));
  debugPrint(version);
  debugPrint(F(", model is "));
  debugPrintLn(model);

  String devEui = readValue(F("sys get hweui"));
  String str = "";
  str.concat(F("mac set deveui "));
  str.concat(devEui);
  sendCommand(str);

  String devEui = readValue(F("sys get hweui"));
  String str = "";
  str.concat(F("mac set deveui "));
  str.concat(devEui);
  sendCommand(str);

  str = "";
  str.concat(F("mac set adr "));
  if(adr){
    str.concat(F("on"));
  } else {
    str.concat(F("off"));
  }
  sendCommand(str);
}

void TheThingsNetwork::onMessage(void (*cb)(const byte* payload, int length, int port)) {
  this->messageCallback = cb;
}

bool TheThingsNetwork::personalize(const byte devAddr[4], const byte nwkSKey[16], const byte appSKey[16]) {
  reset();
  sendCommand(F("mac set devaddr"), devAddr, 4);
  sendCommand(F("mac set nwkskey"), nwkSKey, 16);
  sendCommand(F("mac set appskey"), appSKey, 16);
  return personalize();
}

bool TheThingsNetwork::personalize() {
  configure_channels(this->sf, this->fsb);
  sendCommand(F("mac join abp"));
  String response = readLine();
  if (response != F("accepted")) {
    debugPrint(F("Personalize not accepted: "));
    debugPrintLn(response);
    return false;
  }

  debugPrint(F("Personalize accepted. Status: "));
  debugPrintLn(readValue(F("mac get status")));
  return true;
}

bool TheThingsNetwork::provision(const byte appEui[8], const byte appKey[16]) {
  sendCommand(F("mac set appeui"), appEui, 8);
  sendCommand(F("mac set appkey"), appKey, 16);
  return sendCommand(F("mac save"), 50000);
}

bool TheThingsNetwork::join(int retries, long int retryDelay) {
  configure_channels(this->sf, this->fsb);
  String devEui = readValue(F("sys get hweui"));
  String str = "";
  str.concat(F("mac set deveui "));
  str.concat(devEui);
  sendCommand(str);
  while (retries != 0) {
    if (retries > 0) {
      retries--;
    }
    if (!sendCommand(F("mac join otaa"))) {
      debugPrintLn(F("Send join command failed"));
      delay(retryDelay);
      continue;
    }
    String response = readLine(10000);
    if (response != F("accepted")) {
      debugPrint(F("Join not accepted: "));
      debugPrintLn(response);
      delay(retryDelay);
      continue;
    }
    debugPrint(F("Join accepted. Status: "));
    debugPrintLn(readValue(F("mac get status")));
    debugPrint(F("DevAddr: "));
    debugPrintLn(readValue(F("mac get devaddr")));
    return true;
  }
  return false;
}

bool TheThingsNetwork::join(const byte appEui[8], const byte appKey[16], int retries, long int retryDelay) {
  reset();
  provision(appEui, appKey);
  configure_channels();
  return join(retries, retryDelay);
}

int TheThingsNetwork::sendBytes(const byte* payload, int length, int port, bool confirm) {
  String str = "";
  str.concat(F("mac tx "));
  str.concat(confirm ? F("cnf ") : F("uncnf "));
  str.concat(port);
  if (!sendCommand(str, payload, length)) {
    debugPrintLn(F("Send command failed"));
    return -1;
  }

  String response = readLine(10000);
  if (response == "") {
    debugPrintLn(F("Time-out"));
    return -2;
  }
  if (response == F("mac_tx_ok")) {
    debugPrintLn(F("Successful transmission"));
    return 1;
  }
  if (response.startsWith(F("mac_rx"))) {
    int portEnds = response.indexOf(" ", 7);
    int downlinkPort = response.substring(7, portEnds).toInt();
    String data = response.substring(portEnds + 1);
    int downlinkLength = data.length() / 2;
    byte downlink[64];
    for (int i = 0, d = 0; i < downlinkLength; i++, d += 2)
      downlink[i] = TTN_HEX_PAIR_TO_BYTE(data[d], data[d+1]);
    debugPrint(F("Successful transmission. Received "));
    debugPrint(downlinkLength);
    debugPrintLn(F(" bytes"));
    if (this->messageCallback)
      this->messageCallback(downlink, downlinkLength, downlinkPort);
    return 2;
  }

  debugPrint(F("Unexpected response: "));
  debugPrintLn(response);
  return -10;
}

int TheThingsNetwork::poll(int port, bool confirm) {
  byte payload[] = { 0x00 };
  return sendBytes(payload, 1, port, confirm);
}

void TheThingsNetwork::showStatus() {
  debugPrint(F("EUI: "));
  debugPrintLn(readValue(F("sys get hweui")));
  debugPrint(F("Battery: "));
  debugPrintLn(readValue(F("sys get vdd")));
  debugPrint(F("AppEUI: "));
  debugPrintLn(readValue(F("mac get appeui")));
  debugPrint(F("DevEUI: "));
  debugPrintLn(readValue(F("mac get deveui")));

  if (this->model == F("RN2483")) {
    debugPrint(F("Band: "));
    debugPrintLn(readValue(F("mac get band")));
  }

  debugPrint(F("Data Rate: "));
  debugPrintLn(readValue(F("mac get dr")));
  debugPrint(F("RX Delay 1: "));
  debugPrintLn(readValue(F("mac get rxdelay1")));
  debugPrint(F("RX Delay 2: "));
  debugPrintLn(readValue(F("mac get rxdelay2")));
}

void TheThingsNetwork::configure_EU868(int sf) {
  int ch;
  int dr = -1;
  long int freq = 867100000;
  String str = "";

  str.concat(F("mac set rx2 3 869525000"));
  sendCommand(str);
  str = "";
  for (ch = 0; ch <= 7; ch++) {
    if (ch >= 3) {
      str.concat(F("mac set ch freq "));
      str.concat(ch);
      str.concat(F(" "));
      str.concat(freq);
      sendCommand(str);
      str = "";
      str.concat(F("mac set ch drrange "));
      str.concat(ch);
      str.concat(F(" 0 5"));
      sendCommand(str);
      str = "";
      str.concat(F("mac set ch status "));
      str.concat(ch);
      str.concat(F(" on"));
      sendCommand(str);
      str = "";
      freq = freq + 200000;
    }
    str.concat(F("mac set ch dcycle "));
    str.concat(ch);
    str.concat(F(" 799"));
    sendCommand(str);
    str = "";
  }
  str.concat(F("mac set ch drrange 1 0 6"));
  sendCommand(str);
  str = "";
  str.concat(F("mac set pwridx "));
  str.concat(TTN_PWRIDX_868);
  sendCommand(str);
  switch (sf) {
    case 7:
      dr = 5;
      break;
    case 8:
      dr = 4;
      break;
    case 9:
      dr = 3;
      break;
    case 10:
      dr = 2;
      break;
    case 11:
      dr = 1;
      break;
    case 12:
      dr = 0;
      break;
    default:
      debugPrintLn(F("Invalid SF"))
      break;
  }
  if (dr > -1){
    str = "";
    str.concat(F("mac set dr "));
    str.concat(dr);
    sendCommand(str);
  }
}

void TheThingsNetwork::configure_US915(int sf, int fsb) {
  int ch;
  int dr = -1;
  String str = "";
  int chLow = fsb > 0 ? (fsb - 1) * 8 : 0;
  int chHigh = fsb > 0 ? chLow + 7 : 71;
  int ch500 = fsb + 63;

  sendCommand(F("radio set freq 904200000"));
  str = "";
  str.concat(F("mac set pwridx "));
  str.concat(TTN_PWRIDX_915);
  sendCommand(str);
  for (ch = 0; ch < 72; ch++) {
    str = "";
    str.concat(F("mac set ch status "));
    str.concat(ch);
    if (ch == ch500 || ch <= chHigh && ch >= chLow) {
      str.concat(F(" on"));
      sendCommand(str);
      if (ch < 63) {
        str = "";
        str.concat(F("mac set ch drrange "));
        str.concat(ch);
        str.concat(F(" 0 3"));
        sendCommand(str);
        str = "";
      }
    }
    else {
      str.concat(F(" off"));
      sendCommand(str);
    }
  }
  switch (sf) {
    case 7:
      dr = 3;
      break;
    case 8:
     dr = 2;
     break;
    case 9:
      dr = 1;
      break;
    case 10:
      dr = 0;
      break;
    default:
      debugPrintLn(F("Invalid SF"))
      break;
  }
  if (dr > -1){
    str = "";
    str.concat(F("mac set dr "));
    str.concat(dr);
    sendCommand(str);
  }
}

void TheThingsNetwork::configure_channels(int sf, int fsb) {
  switch (this->fp) {
    case TTN_FP_EU868:
      configure_EU868(sf);
      break;
    case TTN_FP_US915:
      configure_US915(sf, fsb);
      break;
    default:
      debugPrintLn("Invalid frequency plan");
      break;
  }
}

TheThingsNetwork::TheThingsNetwork(Stream& modemStream, Stream& debugStream, fp_ttn_t fp, int sf, int fsb) {
  this->debugStream = &debugStream;
  this->modemStream = &modemStream;
  this->fp = fp;
  this->sf = sf;
  this->fsb = fsb;
}
<|endoftext|>


<commit_before>#include "warnings-disable.h"

WARNINGS_DISABLE
#include <QAbstractButton>
#include <QCoreApplication>
#include <QDateTime>
#include <QLineEdit>
#include <QList>
#include <QObject>
#include <QPixmap>
#include <QPushButton>
#include <QSignalSpy>
#include <QString>
#include <QTest>
#include <QVariant>
#include <Qt>
WARNINGS_ENABLE

#include "../qtest-platform.h"

#include "LogEntry.h"
#include "TBusyLabel.h"
#include "TElidedLabel.h"
#include "TOkLabel.h"
#include "TPathComboBrowse.h"
#include "TPathLineBrowse.h"
#include "TTabWidget.h"
#include "TTextView.h"
#include "TWizard.h"
#include "TWizardPage.h"

class TestLibWidgets : public QObject
{
    Q_OBJECT

private slots:
    void initTestCase();
    void cleanupTestCase();

    void elidedLabel();
    void elidedLabel_status();
    void okLabel();
    void busylabel();
    void busylabel_on_off();
    void pathlinebrowse();
    void pathcombobrowse();
    void ttabwidget();
    void twizard();
    void textview();
};

void TestLibWidgets::initTestCase()
{
    QCoreApplication::setOrganizationName(TEST_NAME);

    // Use a custom message handler to filter out unwanted messages
    IF_NOT_VISUAL { qInstallMessageHandler(offscreenMessageOutput); }
}

void TestLibWidgets::cleanupTestCase()
{
    // Wait up to 5 seconds to delete objects scheduled with ->deleteLater()
    WAIT_FINAL;
}

void TestLibWidgets::elidedLabel()
{
    // Initialize variables.
    const QString text("this is a long long long piece of text");
    TElidedLabel *elidedlabel = new TElidedLabel();

    VISUAL_INIT(elidedlabel);

    // Set up ElidedText.
    elidedlabel->setElide(Qt::ElideRight);
    elidedlabel->setText(text);

    // The text should be fully visible if there's enough room.
    elidedlabel->setGeometry(100, 100, 1000, 20);
    QVERIFY(elidedlabel->text() == text);
    QVERIFY(elidedlabel->elideText(text).count() == text.count());
    VISUAL_WAIT;

    // The text should be elided if the widget is too narrow.
    elidedlabel->setGeometry(100, 100, 100, 20);
    QVERIFY(elidedlabel->text() == text);
    QVERIFY(elidedlabel->elideText(text).count() < text.count());
    VISUAL_WAIT;

    delete elidedlabel;
}

void TestLibWidgets::elidedLabel_status()
{
    TElidedLabel *el = new TElidedLabel();
    el->setMinimumWidth(200);

    VISUAL_INIT(el);

    el->messageNormal("normal message");
    VISUAL_WAIT;

    el->messageError("error message");
    VISUAL_WAIT;

    delete el;
}

void TestLibWidgets::okLabel()
{
    TOkLabel *ok = new TOkLabel();

    VISUAL_INIT(ok);

    ok->setStatus(TOkLabel::Ok);
    VISUAL_WAIT;

    ok->setStatus(TOkLabel::Error);
    VISUAL_WAIT;

    ok->setStatus(TOkLabel::Unset);
    VISUAL_WAIT;

    delete ok;
}

void TestLibWidgets::busylabel()
{
    TBusyLabel *bw = new TBusyLabel();

    VISUAL_INIT(bw);

    delete bw;
}

void TestLibWidgets::busylabel_on_off()
{
    TBusyLabel *bw = new TBusyLabel();

    VISUAL_INIT(bw);

    // Before starting
    VISUAL_WAIT;

    // Animation
    bw->animate(true);
    QTest::qWait(100);
    VISUAL_WAIT;

    // After animation
    bw->animate(false);
    QTest::qWait(100);
    VISUAL_WAIT;

    delete bw;
}

void TestLibWidgets::pathlinebrowse()
{
    TPathLineBrowse *plb = new TPathLineBrowse();
    QSignalSpy       sig_changed(plb, SIGNAL(textChanged(QString)));

    VISUAL_INIT(plb);

    plb->setLabel("label");
    QVERIFY(plb->label() == "label");
    VISUAL_WAIT;

    plb->setPlaceholderText("placeholder");
    QVERIFY(plb->placeholderText() == "placeholder");
    VISUAL_WAIT;

    plb->setDialogTitle("text");
    QVERIFY(plb->dialogTitle() == "text");
    VISUAL_WAIT;

    QVERIFY(sig_changed.count() == 0);
    plb->setText("text");
    QVERIFY(plb->text() == "text");
    QVERIFY(sig_changed.count() == 1);
    QVERIFY(sig_changed.takeFirst().at(0).value<QString>() == "text");
    sig_changed.clear();
    VISUAL_WAIT;

    plb->setStatusOk("");
    VISUAL_WAIT;

    plb->setStatusError("error message");
    VISUAL_WAIT;

    plb->setStatusOk("ok message");
    VISUAL_WAIT;

    plb->clear();
    QVERIFY(sig_changed.count() == 1);
    QVERIFY(sig_changed.takeFirst().at(0).value<QString>() == "");
    VISUAL_WAIT;

    // Test setText as a slot
    QLineEdit *le = new QLineEdit();
    connect(le, &QLineEdit::textChanged, plb, &TPathLineBrowse::setText);

    plb->clear();
    le->setText("indirect text");
    QVERIFY(plb->text() == "indirect text");
    QVERIFY(sig_changed.count() == 1);
    QVERIFY(sig_changed.takeFirst().at(0).value<QString>() == "indirect text");
    VISUAL_WAIT;

    delete le;
    delete plb;
}

void TestLibWidgets::pathcombobrowse()
{
    TPathComboBrowse *pcb = new TPathComboBrowse();
    QSignalSpy        sig_changed(pcb, SIGNAL(textChanged(QString)));

    VISUAL_INIT(pcb);

    pcb->setLabel("label");
    QVERIFY(pcb->label() == "label");
    VISUAL_WAIT;

    pcb->setPlaceholderText("placeholder");
    QVERIFY(pcb->placeholderText() == "placeholder");
    VISUAL_WAIT;

    pcb->setDialogTitle("text");
    QVERIFY(pcb->dialogTitle() == "text");
    VISUAL_WAIT;

    pcb->setDialogFilter("Text files (*.txt *.text)");
    QVERIFY(pcb->dialogFilter() == "Text files (*.txt *.text)");
    VISUAL_WAIT;

    QVERIFY(sig_changed.count() == 0);
    pcb->setText("text");
    QVERIFY(pcb->text() == "text");
    QVERIFY(sig_changed.count() == 1);
    QVERIFY(sig_changed.takeFirst().at(0).value<QString>() == "text");
    sig_changed.clear();
    VISUAL_WAIT;

    QVERIFY(pcb->count() == 1);
    pcb->addItem("another");
    QVERIFY(sig_changed.count() == 0);
    QVERIFY(pcb->count() == 2);
    VISUAL_WAIT;

    // Go back to the previous text
    pcb->setText("another");
    QVERIFY(sig_changed.count() == 1);
    pcb->setText("text");
    QVERIFY(sig_changed.count() == 2);
    VISUAL_WAIT;

    sig_changed.clear();
    pcb->setText("third");
    QVERIFY(pcb->text() == "third");
    QVERIFY(sig_changed.count() == 1);
    QVERIFY(sig_changed.takeFirst().at(0).value<QString>() == "third");
    VISUAL_WAIT;

    pcb->setStatusOk("");
    VISUAL_WAIT;

    pcb->setStatusError("error message");
    VISUAL_WAIT;

    pcb->setStatusOk("ok message");
    VISUAL_WAIT;

    sig_changed.clear();
    pcb->clear();
    QVERIFY(sig_changed.count() == 1);
    QVERIFY(sig_changed.takeFirst().at(0).value<QString>() == "");
    VISUAL_WAIT;

    // Test setText as a slot
    QLineEdit *le = new QLineEdit();
    connect(le, &QLineEdit::textChanged, pcb, &TPathComboBrowse::setText);

    sig_changed.clear();
    pcb->clear();
    le->setText("indirect text");
    QVERIFY(pcb->text() == "indirect text");
    QVERIFY(sig_changed.count() == 1);
    QVERIFY(sig_changed.takeFirst().at(0).value<QString>() == "indirect text");
    VISUAL_WAIT;

    delete le;
    delete pcb;
}

void TestLibWidgets::ttabwidget()
{
    TTabWidget *tabwidget = new TTabWidget();
    tabwidget->setLargeLogoFilename(":/logos/tarsnap-header-h29.png");
    tabwidget->setSmallLogoFilename(":/logos/tarsnap-icon-h29.png");

    VISUAL_INIT(tabwidget);

    delete tabwidget;
}

void TestLibWidgets::twizard()
{
    TWizard *wizard = new TWizard();

    // Add logo
    QPixmap pix(32, 32);
    pix.fill(Qt::blue);
    wizard->setLogo(pix);

    // Create pages with titles
    TWizardPage *p1 = new TWizardPage();
    TWizardPage *p2 = new TWizardPage();
    p1->setTitle("page 1");
    p2->setTitle("page 2");

    // Add a "proceed" button to each page
    QPushButton *pb1 = new QPushButton(p1);
    pb1->setText("Next");
    pb1->setObjectName("nextButton");
    QPushButton *pb2 = new QPushButton(p2);
    pb2->setText("Finish");
    pb2->setObjectName("finishButton");

    // Add pages to the wizard; must be after the buttons have been added
    wizard->addPages(QList<TWizardPage *>() << p1 << p2);

    // Navigate through the pages.
    VISUAL_INIT(wizard);

    wizard->currentPage()->button(TWizardPage::NextButton)->click();
    VISUAL_WAIT;
    wizard->currentPage()->button(TWizardPage::FinishButton)->click();
    VISUAL_WAIT;

    // We should have quit the wizard
    QVERIFY(wizard->isVisible() == false);

    delete wizard;
}

void TestLibWidgets::textview()
{
    TTextView *tv = new TTextView();

    VISUAL_INIT(tv);

    tv->appendLog(LogEntry{QDateTime::currentDateTime(), "one"});
    tv->appendLog(LogEntry{QDateTime::currentDateTime(), "two"});
    tv->appendLog(LogEntry{QDateTime::currentDateTime(), "three"});
    tv->appendLog(LogEntry{QDateTime::currentDateTime(), "four"});
    VISUAL_WAIT;

    delete tv;
}

QTEST_MAIN(TestLibWidgets)
WARNINGS_DISABLE
#include "test-lib-widgets.moc"
WARNINGS_ENABLE
<commit_msg>tests/lib-widgets: skip TTabWidget on macOS with Qt < 5.15<commit_after>#include "warnings-disable.h"

WARNINGS_DISABLE
#include <QAbstractButton>
#include <QCoreApplication>
#include <QDateTime>
#include <QLineEdit>
#include <QList>
#include <QObject>
#include <QPixmap>
#include <QPushButton>
#include <QSignalSpy>
#include <QString>
#include <QTest>
#include <QVariant>
#include <Qt>
WARNINGS_ENABLE

#include "../qtest-platform.h"

#include "LogEntry.h"
#include "TBusyLabel.h"
#include "TElidedLabel.h"
#include "TOkLabel.h"
#include "TPathComboBrowse.h"
#include "TPathLineBrowse.h"
#include "TTabWidget.h"
#include "TTextView.h"
#include "TWizard.h"
#include "TWizardPage.h"

class TestLibWidgets : public QObject
{
    Q_OBJECT

private slots:
    void initTestCase();
    void cleanupTestCase();

    void elidedLabel();
    void elidedLabel_status();
    void okLabel();
    void busylabel();
    void busylabel_on_off();
    void pathlinebrowse();
    void pathcombobrowse();
    void ttabwidget();
    void twizard();
    void textview();
};

void TestLibWidgets::initTestCase()
{
    QCoreApplication::setOrganizationName(TEST_NAME);

    // Use a custom message handler to filter out unwanted messages
    IF_NOT_VISUAL { qInstallMessageHandler(offscreenMessageOutput); }
}

void TestLibWidgets::cleanupTestCase()
{
    // Wait up to 5 seconds to delete objects scheduled with ->deleteLater()
    WAIT_FINAL;
}

void TestLibWidgets::elidedLabel()
{
    // Initialize variables.
    const QString text("this is a long long long piece of text");
    TElidedLabel *elidedlabel = new TElidedLabel();

    VISUAL_INIT(elidedlabel);

    // Set up ElidedText.
    elidedlabel->setElide(Qt::ElideRight);
    elidedlabel->setText(text);

    // The text should be fully visible if there's enough room.
    elidedlabel->setGeometry(100, 100, 1000, 20);
    QVERIFY(elidedlabel->text() == text);
    QVERIFY(elidedlabel->elideText(text).count() == text.count());
    VISUAL_WAIT;

    // The text should be elided if the widget is too narrow.
    elidedlabel->setGeometry(100, 100, 100, 20);
    QVERIFY(elidedlabel->text() == text);
    QVERIFY(elidedlabel->elideText(text).count() < text.count());
    VISUAL_WAIT;

    delete elidedlabel;
}

void TestLibWidgets::elidedLabel_status()
{
    TElidedLabel *el = new TElidedLabel();
    el->setMinimumWidth(200);

    VISUAL_INIT(el);

    el->messageNormal("normal message");
    VISUAL_WAIT;

    el->messageError("error message");
    VISUAL_WAIT;

    delete el;
}

void TestLibWidgets::okLabel()
{
    TOkLabel *ok = new TOkLabel();

    VISUAL_INIT(ok);

    ok->setStatus(TOkLabel::Ok);
    VISUAL_WAIT;

    ok->setStatus(TOkLabel::Error);
    VISUAL_WAIT;

    ok->setStatus(TOkLabel::Unset);
    VISUAL_WAIT;

    delete ok;
}

void TestLibWidgets::busylabel()
{
    TBusyLabel *bw = new TBusyLabel();

    VISUAL_INIT(bw);

    delete bw;
}

void TestLibWidgets::busylabel_on_off()
{
    TBusyLabel *bw = new TBusyLabel();

    VISUAL_INIT(bw);

    // Before starting
    VISUAL_WAIT;

    // Animation
    bw->animate(true);
    QTest::qWait(100);
    VISUAL_WAIT;

    // After animation
    bw->animate(false);
    QTest::qWait(100);
    VISUAL_WAIT;

    delete bw;
}

void TestLibWidgets::pathlinebrowse()
{
    TPathLineBrowse *plb = new TPathLineBrowse();
    QSignalSpy       sig_changed(plb, SIGNAL(textChanged(QString)));

    VISUAL_INIT(plb);

    plb->setLabel("label");
    QVERIFY(plb->label() == "label");
    VISUAL_WAIT;

    plb->setPlaceholderText("placeholder");
    QVERIFY(plb->placeholderText() == "placeholder");
    VISUAL_WAIT;

    plb->setDialogTitle("text");
    QVERIFY(plb->dialogTitle() == "text");
    VISUAL_WAIT;

    QVERIFY(sig_changed.count() == 0);
    plb->setText("text");
    QVERIFY(plb->text() == "text");
    QVERIFY(sig_changed.count() == 1);
    QVERIFY(sig_changed.takeFirst().at(0).value<QString>() == "text");
    sig_changed.clear();
    VISUAL_WAIT;

    plb->setStatusOk("");
    VISUAL_WAIT;

    plb->setStatusError("error message");
    VISUAL_WAIT;

    plb->setStatusOk("ok message");
    VISUAL_WAIT;

    plb->clear();
    QVERIFY(sig_changed.count() == 1);
    QVERIFY(sig_changed.takeFirst().at(0).value<QString>() == "");
    VISUAL_WAIT;

    // Test setText as a slot
    QLineEdit *le = new QLineEdit();
    connect(le, &QLineEdit::textChanged, plb, &TPathLineBrowse::setText);

    plb->clear();
    le->setText("indirect text");
    QVERIFY(plb->text() == "indirect text");
    QVERIFY(sig_changed.count() == 1);
    QVERIFY(sig_changed.takeFirst().at(0).value<QString>() == "indirect text");
    VISUAL_WAIT;

    delete le;
    delete plb;
}

void TestLibWidgets::pathcombobrowse()
{
    TPathComboBrowse *pcb = new TPathComboBrowse();
    QSignalSpy        sig_changed(pcb, SIGNAL(textChanged(QString)));

    VISUAL_INIT(pcb);

    pcb->setLabel("label");
    QVERIFY(pcb->label() == "label");
    VISUAL_WAIT;

    pcb->setPlaceholderText("placeholder");
    QVERIFY(pcb->placeholderText() == "placeholder");
    VISUAL_WAIT;

    pcb->setDialogTitle("text");
    QVERIFY(pcb->dialogTitle() == "text");
    VISUAL_WAIT;

    pcb->setDialogFilter("Text files (*.txt *.text)");
    QVERIFY(pcb->dialogFilter() == "Text files (*.txt *.text)");
    VISUAL_WAIT;

    QVERIFY(sig_changed.count() == 0);
    pcb->setText("text");
    QVERIFY(pcb->text() == "text");
    QVERIFY(sig_changed.count() == 1);
    QVERIFY(sig_changed.takeFirst().at(0).value<QString>() == "text");
    sig_changed.clear();
    VISUAL_WAIT;

    QVERIFY(pcb->count() == 1);
    pcb->addItem("another");
    QVERIFY(sig_changed.count() == 0);
    QVERIFY(pcb->count() == 2);
    VISUAL_WAIT;

    // Go back to the previous text
    pcb->setText("another");
    QVERIFY(sig_changed.count() == 1);
    pcb->setText("text");
    QVERIFY(sig_changed.count() == 2);
    VISUAL_WAIT;

    sig_changed.clear();
    pcb->setText("third");
    QVERIFY(pcb->text() == "third");
    QVERIFY(sig_changed.count() == 1);
    QVERIFY(sig_changed.takeFirst().at(0).value<QString>() == "third");
    VISUAL_WAIT;

    pcb->setStatusOk("");
    VISUAL_WAIT;

    pcb->setStatusError("error message");
    VISUAL_WAIT;

    pcb->setStatusOk("ok message");
    VISUAL_WAIT;

    sig_changed.clear();
    pcb->clear();
    QVERIFY(sig_changed.count() == 1);
    QVERIFY(sig_changed.takeFirst().at(0).value<QString>() == "");
    VISUAL_WAIT;

    // Test setText as a slot
    QLineEdit *le = new QLineEdit();
    connect(le, &QLineEdit::textChanged, pcb, &TPathComboBrowse::setText);

    sig_changed.clear();
    pcb->clear();
    le->setText("indirect text");
    QVERIFY(pcb->text() == "indirect text");
    QVERIFY(sig_changed.count() == 1);
    QVERIFY(sig_changed.takeFirst().at(0).value<QString>() == "indirect text");
    VISUAL_WAIT;

    delete le;
    delete pcb;
}

void TestLibWidgets::ttabwidget()
{
    IF_MACOS_PRE_5_15_SKIP;

    TTabWidget *tabwidget = new TTabWidget();
    tabwidget->setLargeLogoFilename(":/logos/tarsnap-header-h29.png");
    tabwidget->setSmallLogoFilename(":/logos/tarsnap-icon-h29.png");

    VISUAL_INIT(tabwidget);

    delete tabwidget;
}

void TestLibWidgets::twizard()
{
    TWizard *wizard = new TWizard();

    // Add logo
    QPixmap pix(32, 32);
    pix.fill(Qt::blue);
    wizard->setLogo(pix);

    // Create pages with titles
    TWizardPage *p1 = new TWizardPage();
    TWizardPage *p2 = new TWizardPage();
    p1->setTitle("page 1");
    p2->setTitle("page 2");

    // Add a "proceed" button to each page
    QPushButton *pb1 = new QPushButton(p1);
    pb1->setText("Next");
    pb1->setObjectName("nextButton");
    QPushButton *pb2 = new QPushButton(p2);
    pb2->setText("Finish");
    pb2->setObjectName("finishButton");

    // Add pages to the wizard; must be after the buttons have been added
    wizard->addPages(QList<TWizardPage *>() << p1 << p2);

    // Navigate through the pages.
    VISUAL_INIT(wizard);

    wizard->currentPage()->button(TWizardPage::NextButton)->click();
    VISUAL_WAIT;
    wizard->currentPage()->button(TWizardPage::FinishButton)->click();
    VISUAL_WAIT;

    // We should have quit the wizard
    QVERIFY(wizard->isVisible() == false);

    delete wizard;
}

void TestLibWidgets::textview()
{
    TTextView *tv = new TTextView();

    VISUAL_INIT(tv);

    tv->appendLog(LogEntry{QDateTime::currentDateTime(), "one"});
    tv->appendLog(LogEntry{QDateTime::currentDateTime(), "two"});
    tv->appendLog(LogEntry{QDateTime::currentDateTime(), "three"});
    tv->appendLog(LogEntry{QDateTime::currentDateTime(), "four"});
    VISUAL_WAIT;

    delete tv;
}

QTEST_MAIN(TestLibWidgets)
WARNINGS_DISABLE
#include "test-lib-widgets.moc"
WARNINGS_ENABLE
<|endoftext|>