<commit_before>//* This file is part of the MOOSE framework
//* https://www.mooseframework.org
//*
//* All rights reserved, see COPYRIGHT for full restrictions
//* https://github.com/idaholab/moose/blob/master/COPYRIGHT
//*
//* Licensed under LGPL 2.1, please see LICENSE for details
//* https://www.gnu.org/licenses/lgpl-2.1.html

#include "FVFluxKernel.h"

#include "MooseVariableFV.h"
#include "SystemBase.h"
#include "MooseMesh.h"
#include "ADUtils.h"

#include "libmesh/elem.h"
#include "libmesh/system.h"

InputParameters
FVFluxKernel::validParams()
{
  InputParameters params = FVKernel::validParams();
  params += TwoMaterialPropertyInterface::validParams();
  params.registerSystemAttributeName("FVFluxKernel");
  params.addParam<bool>("force_boundary_execution",
                        false,
                        "Whether to force execution of this object on all external boundaries.");
  params.addParam<std::vector<BoundaryName>>(
      "boundaries_to_force",
      std::vector<BoundaryName>(),
      "The set of sidesets to force execution of this FVFluxKernel on. "
      "Setting force_boundary_execution to true is equivalent to listing all external "
      "mesh boundaries in this parameter.");
  params.addParam<std::vector<BoundaryName>>(
      "boundaries_to_avoid",
      std::vector<BoundaryName>(),
      "The set of sidesets to not execute this FVFluxKernel on. "
      "This takes precedence over force_boundary_execution to restrict to less external boundaries."
      " By defauly flux kernels are executed on all internal boundaries, and no external ones.");

  params.addParamNamesToGroup("force_boundary_execution boundaries_to_force boundaries_to_avoid",
                              "Boundary execution modification parameters");
  return params;
}

FVFluxKernel::FVFluxKernel(const InputParameters & params)
  : FVKernel(params),
    TwoMaterialPropertyInterface(this, blockIDs(), {}),
    NeighborMooseVariableInterface(
        this, false, Moose::VarKindType::VAR_NONLINEAR, Moose::VarFieldType::VAR_FIELD_STANDARD),
    NeighborCoupleableMooseVariableDependencyIntermediateInterface(
        this, false, false, /*is_fv=*/true),
    _var(*mooseVariableFV()),
    _u_elem(_var.adSln()),
    _u_neighbor(_var.adSlnNeighbor()),
    _force_boundary_execution(getParam<bool>("force_boundary_execution"))
{
  addMooseVariableDependency(&_var);

  const auto & vec = getParam<std::vector<BoundaryName>>("boundaries_to_force");
  for (const auto & name : vec)
    _boundaries_to_force.insert(_mesh.getBoundaryID(name));

  const auto & avoid_vec = getParam<std::vector<BoundaryName>>("boundaries_to_avoid");
  for (const auto & name : avoid_vec)
  {
    const auto bid = _mesh.getBoundaryID(name);
    _boundaries_to_avoid.insert(bid);
    if (_boundaries_to_force.find(bid) != _boundaries_to_force.end())
      paramError(
          "boundaries_to_avoid",
          "A boundary may not be specified in both boundaries_to_avoid and boundaries_to_force");
  }
}

bool
FVFluxKernel::onBoundary(const FaceInfo & fi) const
{
  return Moose::FV::onBoundary(*this, fi);
}

// Note the lack of quadrature point loops in the residual/jacobian compute
// functions. This is because finite volumes currently only works with
// constant monomial elements. We only have one quadrature point regardless of
// problem dimension and just multiply by the face area.

bool
FVFluxKernel::skipForBoundary(const FaceInfo & fi) const
{
  // Boundaries to avoid come first, since they are always obeyed
  if (avoidBoundary(fi))
    return true;

  // Blanket forcing on boundary
  if (_force_boundary_execution)
    return false;

  // Selected boundaries to force
  for (const auto bnd_to_force : _boundaries_to_force)
    if (fi.boundaryIDs().find(bnd_to_force) != fi.boundaryIDs().end())
      return false;

  // We're not on a boundary, so in practice we're not 'skipping'
  if (!onBoundary(fi))
    return false;

  // If we have flux bcs then we do skip
  const auto & flux_pr = _var.getFluxBCs(fi);
  if (flux_pr.first)
    return true;

  // If we don't have flux bcs *and* we do have dirichlet bcs then we don't skip. If we don't have
  // either then we assume natural boundary condition and we should skip
  return !_var.getDirichletBC(fi).first;
}

void
FVFluxKernel::computeResidual(const FaceInfo & fi)
{
  if (skipForBoundary(fi))
    return;

  _face_info = &fi;
  _normal = fi.normal();
  _face_type = fi.faceType(_var.name());
  auto r = MetaPhysicL::raw_value(fi.faceArea() * fi.faceCoord() * computeQpResidual());

  // residual contributions for a flux kernel go to both neighboring faces.
  // They are equal in magnitude but opposite in direction due to the outward
  // facing unit normals of the face for each neighboring elements being
  // oriented oppositely.  We calculate the residual contribution once using
  // the lower-id-elem-oriented _normal and just use the resulting residual's
  // negative for the contribution to the neighbor element.

  // The fancy face type if condition checks here are because we might
  // currently be running on a face for which this kernel's variable is only
  // defined on one side. If this is the case, we need to only calculate+add
  // the residual contribution if there is a dirichlet bc for the active
  // face+variable.  We always need to add the residual contribution when the
  // variable is defined on both sides of the face.  If the variable is only
  // defined on one side and there is NOT a dirichlet BC, then there is either
  // a flux BC or a natural BC - in either of those cases we don't want to add
  // any residual contributions from regular flux kernels.
  if (_face_type == FaceInfo::VarFaceNeighbors::ELEM ||
      _face_type == FaceInfo::VarFaceNeighbors::BOTH)
  {
    // residual contribution of this kernel to the elem element
    prepareVectorTag(_assembly, _var.number());
    _local_re(0) = r;
    accumulateTaggedLocalResidual();
  }
  if (_face_type == FaceInfo::VarFaceNeighbors::NEIGHBOR ||
      _face_type == FaceInfo::VarFaceNeighbors::BOTH)
  {
    // residual contribution of this kernel to the neighbor element
    prepareVectorTagNeighbor(_assembly, _var.number());
    _local_re(0) = -r;
    accumulateTaggedLocalResidual();
  }
}

void
FVFluxKernel::computeJacobian(Moose::DGJacobianType type, const ADReal & residual)
{
  auto & ce = _assembly.couplingEntries();
  for (const auto & it : ce)
  {
    MooseVariableFieldBase & ivariable = *(it.first);
    MooseVariableFieldBase & jvariable = *(it.second);

    // We currently only support coupling to other FV variables
    // Remove this when we enable support for it.
    if (!jvariable.isFV())
      continue;

    if ((type == Moose::ElementElement || type == Moose::NeighborElement) &&
        !jvariable.activeOnSubdomain(_face_info->elemSubdomainID()))
      continue;
    else if ((type == Moose::ElementNeighbor || type == Moose::NeighborNeighbor) &&
             !jvariable.activeOnSubdomain(_face_info->neighborSubdomainID()))
      continue;

    unsigned int ivar = ivariable.number();
    unsigned int jvar = jvariable.number();

    if (ivar != _var.number())
      continue;

    SystemBase & sys = _subproblem.systemBaseNonlinear();
    auto dofs_per_elem = sys.getMaxVarNDofsPerElem();

    auto ad_offset = Moose::adOffset(jvar, dofs_per_elem, type, sys.system().n_vars());

    prepareMatrixTagNeighbor(_assembly, ivar, jvar, type);

    mooseAssert(
        _local_ke.m() == 1,
        "We are currently only supporting constant monomials for finite volume calculations");
    mooseAssert(
        _local_ke.n() == 1,
        "We are currently only supporting constant monomials for finite volume calculations");
    mooseAssert((type == Moose::ElementElement || type == Moose::NeighborElement)
                    ? jvariable.dofIndices().size() == 1
                    : jvariable.dofIndicesNeighbor().size() == 1,
                "The AD derivative indexing below only makes sense for constant monomials, e.g. "
                "for a number of dof indices equal to  1");

#ifndef MOOSE_SPARSE_AD
    mooseAssert(ad_offset < MOOSE_AD_MAX_DOFS_PER_ELEM,
                "Out of bounds access in derivative vector.");
#endif
    _local_ke(0, 0) = residual.derivatives()[ad_offset];

    accumulateTaggedLocalMatrix();
  }
}

void
FVFluxKernel::computeJacobian(const FaceInfo & fi)
{
  if (skipForBoundary(fi))
    return;

  _face_info = &fi;
  _normal = fi.normal();
  _face_type = fi.faceType(_var.name());
  const ADReal r = fi.faceArea() * fi.faceCoord() * computeQpResidual();

  // The fancy face type if condition checks here are because we might
  // currently be running on a face for which this kernel's variable is only
  // defined on one side. If this is the case, we need to only calculate+add
  // the jacobian contribution if there is a dirichlet bc for the active
  // face+variable.  We always need to add the jacobian contribution when the
  // variable is defined on both sides of the face.  If the variable is only
  // defined on one side and there is NOT a dirichlet BC, then there is either
  // a flux BC or a natural BC - in either of those cases we don't want to add
  // any jacobian contributions from regular flux kernels.
  if (_face_type == FaceInfo::VarFaceNeighbors::ELEM ||
      _face_type == FaceInfo::VarFaceNeighbors::BOTH)
  {
    mooseAssert(_var.dofIndices().size() == 1, "We're currently built to use CONSTANT MONOMIALS");

    auto element_functor = [&](const ADReal & residual, dof_id_type, const std::set<TagID> &)
    {
      // jacobian contribution of the residual for the elem element to the elem element's DOF:
      // d/d_elem (residual_elem)
      computeJacobian(Moose::ElementElement, residual);

      mooseAssert(
          (_face_type == FaceInfo::VarFaceNeighbors::ELEM) ==
              (_var.dofIndicesNeighbor().size() == 0),
          "If the variable is only defined on the elem hand side of the face, then that "
          "means it should have no dof indices on the neighbor/neighbor element. Conversely if "
          "the variable is defined on both sides of the face, then it should have a non-zero "
          "number of degrees of freedom on the neighbor/neighbor element");

      // only add residual to neighbor if the variable is defined there.
      if (_face_type == FaceInfo::VarFaceNeighbors::BOTH)
        // jacobian contribution of the residual for the elem element to the neighbor element's DOF:
        // d/d_neighbor (residual_elem)
        computeJacobian(Moose::ElementNeighbor, residual);
    };

    _assembly.processDerivatives(r, _var.dofIndices()[0], _matrix_tags, element_functor);
  }

  if (_face_type == FaceInfo::VarFaceNeighbors::NEIGHBOR ||
      _face_type == FaceInfo::VarFaceNeighbors::BOTH)
  {
    mooseAssert((_face_type == FaceInfo::VarFaceNeighbors::NEIGHBOR) ==
                    (_var.dofIndices().size() == 0),
                "If the variable is only defined on the neighbor hand side of the face, then that "
                "means it should have no dof indices on the elem element. Conversely if "
                "the variable is defined on both sides of the face, then it should have a non-zero "
                "number of degrees of freedom on the elem element");

    // We switch the sign for the neighbor residual
    ADReal neighbor_r = -r;

    mooseAssert(_var.dofIndicesNeighbor().size() == 1,
                "We're currently built to use CONSTANT MONOMIALS");

    auto neighbor_functor = [&](const ADReal & residual, dof_id_type, const std::set<TagID> &)
    {
      // only add residual to elem if the variable is defined there.
      if (_face_type == FaceInfo::VarFaceNeighbors::BOTH)
        // jacobian contribution of the residual for the neighbor element to the elem element's DOF:
        // d/d_elem (residual_neighbor)
        computeJacobian(Moose::NeighborElement, residual);

      // jacobian contribution of the residual for the neighbor element to the neighbor element's
      // DOF: d/d_neighbor (residual_neighbor)
      computeJacobian(Moose::NeighborNeighbor, residual);
    };

    _assembly.processDerivatives(
        neighbor_r, _var.dofIndicesNeighbor()[0], _matrix_tags, neighbor_functor);
  }
}

ADReal
FVFluxKernel::gradUDotNormal() const
{
  mooseAssert(_face_info, "the face info should be non-null");

  // Utlimately this will be a property of the kernel
  const bool correct_skewness =
      (_var.faceInterpolationMethod() == Moose::FV::InterpMethod::SkewCorrectedAverage);

  return Moose::FV::gradUDotNormal(
      _var(elemFromFace()), _var(neighborFromFace()), *_face_info, _var, correct_skewness);
}

Moose::ElemFromFaceArg
FVFluxKernel::elemFromFace(const bool correct_skewness) const
{
  mooseAssert(_face_info, "the face info should be non-null");
  return Moose::FV::elemFromFace(*this, *_face_info, correct_skewness);
}

Moose::ElemFromFaceArg
FVFluxKernel::neighborFromFace(const bool correct_skewness) const
{
  mooseAssert(_face_info, "the face info should be non-null");
  return Moose::FV::neighborFromFace(*this, *_face_info, correct_skewness);
}

std::pair<SubdomainID, SubdomainID>
FVFluxKernel::faceArgSubdomains(const FaceInfo * face_info) const
{
  if (!face_info)
    face_info = _face_info;

  mooseAssert(face_info, "the face info should be non-null");

  return Moose::FV::faceArgSubdomains(*this, *face_info);
}

Moose::SingleSidedFaceArg
FVFluxKernel::singleSidedFaceArg(const FaceInfo * fi,
                                 const Moose::FV::LimiterType limiter_type,
                                 const bool correct_skewness) const
{
  if (!fi)
    fi = _face_info;
  const bool use_elem = fi->faceType(_var.name()) == FaceInfo::VarFaceNeighbors::ELEM;
  const auto sub_id = use_elem ? fi->elem().subdomain_id() : fi->neighborPtr()->subdomain_id();

  return {fi, limiter_type, true, correct_skewness, sub_id};
}

bool
FVFluxKernel::avoidBoundary(const FaceInfo & fi) const
{
  for (const auto bnd_id : fi.boundaryIDs())
    if (_boundaries_to_avoid.count(bnd_id))
      return true;
  return false;
}
<commit_msg>Address Alex's review<commit_after>//* This file is part of the MOOSE framework
//* https://www.mooseframework.org
//*
//* All rights reserved, see COPYRIGHT for full restrictions
//* https://github.com/idaholab/moose/blob/master/COPYRIGHT
//*
//* Licensed under LGPL 2.1, please see LICENSE for details
//* https://www.gnu.org/licenses/lgpl-2.1.html

#include "FVFluxKernel.h"

#include "MooseVariableFV.h"
#include "SystemBase.h"
#include "MooseMesh.h"
#include "ADUtils.h"

#include "libmesh/elem.h"
#include "libmesh/system.h"

InputParameters
FVFluxKernel::validParams()
{
  InputParameters params = FVKernel::validParams();
  params += TwoMaterialPropertyInterface::validParams();
  params.registerSystemAttributeName("FVFluxKernel");
  params.addParam<bool>("force_boundary_execution",
                        false,
                        "Whether to force execution of this object on all external boundaries.");
  params.addParam<std::vector<BoundaryName>>(
      "boundaries_to_force",
      std::vector<BoundaryName>(),
      "The set of sidesets to force execution of this FVFluxKernel on. "
      "Setting force_boundary_execution to true is equivalent to listing all external "
      "mesh boundaries in this parameter.");
  params.addParam<std::vector<BoundaryName>>(
      "boundaries_to_avoid",
      std::vector<BoundaryName>(),
      "The set of sidesets to not execute this FVFluxKernel on. "
      "This takes precedence over force_boundary_execution to restrict to less external boundaries."
      " By default flux kernels are executed on all internal boundaries and Dirichlet boundary "
      "conditions.");

  params.addParamNamesToGroup("force_boundary_execution boundaries_to_force boundaries_to_avoid",
                              "Boundary execution modification parameters");
  return params;
}

FVFluxKernel::FVFluxKernel(const InputParameters & params)
  : FVKernel(params),
    TwoMaterialPropertyInterface(this, blockIDs(), {}),
    NeighborMooseVariableInterface(
        this, false, Moose::VarKindType::VAR_NONLINEAR, Moose::VarFieldType::VAR_FIELD_STANDARD),
    NeighborCoupleableMooseVariableDependencyIntermediateInterface(
        this, false, false, /*is_fv=*/true),
    _var(*mooseVariableFV()),
    _u_elem(_var.adSln()),
    _u_neighbor(_var.adSlnNeighbor()),
    _force_boundary_execution(getParam<bool>("force_boundary_execution"))
{
  addMooseVariableDependency(&_var);

  const auto & vec = getParam<std::vector<BoundaryName>>("boundaries_to_force");
  for (const auto & name : vec)
    _boundaries_to_force.insert(_mesh.getBoundaryID(name));

  const auto & avoid_vec = getParam<std::vector<BoundaryName>>("boundaries_to_avoid");
  for (const auto & name : avoid_vec)
  {
    const auto bid = _mesh.getBoundaryID(name);
    _boundaries_to_avoid.insert(bid);
    if (_boundaries_to_force.find(bid) != _boundaries_to_force.end())
      paramError(
          "boundaries_to_avoid",
          "A boundary may not be specified in both boundaries_to_avoid and boundaries_to_force");
  }
}

bool
FVFluxKernel::onBoundary(const FaceInfo & fi) const
{
  return Moose::FV::onBoundary(*this, fi);
}

// Note the lack of quadrature point loops in the residual/jacobian compute
// functions. This is because finite volumes currently only works with
// constant monomial elements. We only have one quadrature point regardless of
// problem dimension and just multiply by the face area.

bool
FVFluxKernel::skipForBoundary(const FaceInfo & fi) const
{
  // Boundaries to avoid come first, since they are always obeyed
  if (avoidBoundary(fi))
    return true;

  // We're not on a boundary, so in practice we're not 'skipping'
  if (!onBoundary(fi))
    return false;

  // Blanket forcing on boundary
  if (_force_boundary_execution)
    return false;

  // Selected boundaries to force
  for (const auto bnd_to_force : _boundaries_to_force)
    if (fi.boundaryIDs().count(bnd_to_force))
      return false;

  // If we have flux bcs then we do skip
  const auto & flux_pr = _var.getFluxBCs(fi);
  if (flux_pr.first)
    return true;

  // If we don't have flux bcs *and* we do have dirichlet bcs then we don't skip. If we don't have
  // either then we assume natural boundary condition and we should skip
  return !_var.getDirichletBC(fi).first;
}

void
FVFluxKernel::computeResidual(const FaceInfo & fi)
{
  if (skipForBoundary(fi))
    return;

  _face_info = &fi;
  _normal = fi.normal();
  _face_type = fi.faceType(_var.name());
  auto r = MetaPhysicL::raw_value(fi.faceArea() * fi.faceCoord() * computeQpResidual());

  // residual contributions for a flux kernel go to both neighboring faces.
  // They are equal in magnitude but opposite in direction due to the outward
  // facing unit normals of the face for each neighboring elements being
  // oriented oppositely.  We calculate the residual contribution once using
  // the lower-id-elem-oriented _normal and just use the resulting residual's
  // negative for the contribution to the neighbor element.

  // The fancy face type if condition checks here are because we might
  // currently be running on a face for which this kernel's variable is only
  // defined on one side. If this is the case, we need to only calculate+add
  // the residual contribution if there is a dirichlet bc for the active
  // face+variable.  We always need to add the residual contribution when the
  // variable is defined on both sides of the face.  If the variable is only
  // defined on one side and there is NOT a dirichlet BC, then there is either
  // a flux BC or a natural BC - in either of those cases we don't want to add
  // any residual contributions from regular flux kernels.
  if (_face_type == FaceInfo::VarFaceNeighbors::ELEM ||
      _face_type == FaceInfo::VarFaceNeighbors::BOTH)
  {
    // residual contribution of this kernel to the elem element
    prepareVectorTag(_assembly, _var.number());
    _local_re(0) = r;
    accumulateTaggedLocalResidual();
  }
  if (_face_type == FaceInfo::VarFaceNeighbors::NEIGHBOR ||
      _face_type == FaceInfo::VarFaceNeighbors::BOTH)
  {
    // residual contribution of this kernel to the neighbor element
    prepareVectorTagNeighbor(_assembly, _var.number());
    _local_re(0) = -r;
    accumulateTaggedLocalResidual();
  }
}

void
FVFluxKernel::computeJacobian(Moose::DGJacobianType type, const ADReal & residual)
{
  auto & ce = _assembly.couplingEntries();
  for (const auto & it : ce)
  {
    MooseVariableFieldBase & ivariable = *(it.first);
    MooseVariableFieldBase & jvariable = *(it.second);

    // We currently only support coupling to other FV variables
    // Remove this when we enable support for it.
    if (!jvariable.isFV())
      continue;

    if ((type == Moose::ElementElement || type == Moose::NeighborElement) &&
        !jvariable.activeOnSubdomain(_face_info->elemSubdomainID()))
      continue;
    else if ((type == Moose::ElementNeighbor || type == Moose::NeighborNeighbor) &&
             !jvariable.activeOnSubdomain(_face_info->neighborSubdomainID()))
      continue;

    unsigned int ivar = ivariable.number();
    unsigned int jvar = jvariable.number();

    if (ivar != _var.number())
      continue;

    SystemBase & sys = _subproblem.systemBaseNonlinear();
    auto dofs_per_elem = sys.getMaxVarNDofsPerElem();

    auto ad_offset = Moose::adOffset(jvar, dofs_per_elem, type, sys.system().n_vars());

    prepareMatrixTagNeighbor(_assembly, ivar, jvar, type);

    mooseAssert(
        _local_ke.m() == 1,
        "We are currently only supporting constant monomials for finite volume calculations");
    mooseAssert(
        _local_ke.n() == 1,
        "We are currently only supporting constant monomials for finite volume calculations");
    mooseAssert((type == Moose::ElementElement || type == Moose::NeighborElement)
                    ? jvariable.dofIndices().size() == 1
                    : jvariable.dofIndicesNeighbor().size() == 1,
                "The AD derivative indexing below only makes sense for constant monomials, e.g. "
                "for a number of dof indices equal to  1");

#ifndef MOOSE_SPARSE_AD
    mooseAssert(ad_offset < MOOSE_AD_MAX_DOFS_PER_ELEM,
                "Out of bounds access in derivative vector.");
#endif
    _local_ke(0, 0) = residual.derivatives()[ad_offset];

    accumulateTaggedLocalMatrix();
  }
}

void
FVFluxKernel::computeJacobian(const FaceInfo & fi)
{
  if (skipForBoundary(fi))
    return;

  _face_info = &fi;
  _normal = fi.normal();
  _face_type = fi.faceType(_var.name());
  const ADReal r = fi.faceArea() * fi.faceCoord() * computeQpResidual();

  // The fancy face type if condition checks here are because we might
  // currently be running on a face for which this kernel's variable is only
  // defined on one side. If this is the case, we need to only calculate+add
  // the jacobian contribution if there is a dirichlet bc for the active
  // face+variable.  We always need to add the jacobian contribution when the
  // variable is defined on both sides of the face.  If the variable is only
  // defined on one side and there is NOT a dirichlet BC, then there is either
  // a flux BC or a natural BC - in either of those cases we don't want to add
  // any jacobian contributions from regular flux kernels.
  if (_face_type == FaceInfo::VarFaceNeighbors::ELEM ||
      _face_type == FaceInfo::VarFaceNeighbors::BOTH)
  {
    mooseAssert(_var.dofIndices().size() == 1, "We're currently built to use CONSTANT MONOMIALS");

    auto element_functor = [&](const ADReal & residual, dof_id_type, const std::set<TagID> &)
    {
      // jacobian contribution of the residual for the elem element to the elem element's DOF:
      // d/d_elem (residual_elem)
      computeJacobian(Moose::ElementElement, residual);

      mooseAssert(
          (_face_type == FaceInfo::VarFaceNeighbors::ELEM) ==
              (_var.dofIndicesNeighbor().size() == 0),
          "If the variable is only defined on the elem hand side of the face, then that "
          "means it should have no dof indices on the neighbor/neighbor element. Conversely if "
          "the variable is defined on both sides of the face, then it should have a non-zero "
          "number of degrees of freedom on the neighbor/neighbor element");

      // only add residual to neighbor if the variable is defined there.
      if (_face_type == FaceInfo::VarFaceNeighbors::BOTH)
        // jacobian contribution of the residual for the elem element to the neighbor element's DOF:
        // d/d_neighbor (residual_elem)
        computeJacobian(Moose::ElementNeighbor, residual);
    };

    _assembly.processDerivatives(r, _var.dofIndices()[0], _matrix_tags, element_functor);
  }

  if (_face_type == FaceInfo::VarFaceNeighbors::NEIGHBOR ||
      _face_type == FaceInfo::VarFaceNeighbors::BOTH)
  {
    mooseAssert((_face_type == FaceInfo::VarFaceNeighbors::NEIGHBOR) ==
                    (_var.dofIndices().size() == 0),
                "If the variable is only defined on the neighbor hand side of the face, then that "
                "means it should have no dof indices on the elem element. Conversely if "
                "the variable is defined on both sides of the face, then it should have a non-zero "
                "number of degrees of freedom on the elem element");

    // We switch the sign for the neighbor residual
    ADReal neighbor_r = -r;

    mooseAssert(_var.dofIndicesNeighbor().size() == 1,
                "We're currently built to use CONSTANT MONOMIALS");

    auto neighbor_functor = [&](const ADReal & residual, dof_id_type, const std::set<TagID> &)
    {
      // only add residual to elem if the variable is defined there.
      if (_face_type == FaceInfo::VarFaceNeighbors::BOTH)
        // jacobian contribution of the residual for the neighbor element to the elem element's DOF:
        // d/d_elem (residual_neighbor)
        computeJacobian(Moose::NeighborElement, residual);

      // jacobian contribution of the residual for the neighbor element to the neighbor element's
      // DOF: d/d_neighbor (residual_neighbor)
      computeJacobian(Moose::NeighborNeighbor, residual);
    };

    _assembly.processDerivatives(
        neighbor_r, _var.dofIndicesNeighbor()[0], _matrix_tags, neighbor_functor);
  }
}

ADReal
FVFluxKernel::gradUDotNormal() const
{
  mooseAssert(_face_info, "the face info should be non-null");

  // Utlimately this will be a property of the kernel
  const bool correct_skewness =
      (_var.faceInterpolationMethod() == Moose::FV::InterpMethod::SkewCorrectedAverage);

  return Moose::FV::gradUDotNormal(
      _var(elemFromFace()), _var(neighborFromFace()), *_face_info, _var, correct_skewness);
}

Moose::ElemFromFaceArg
FVFluxKernel::elemFromFace(const bool correct_skewness) const
{
  mooseAssert(_face_info, "the face info should be non-null");
  return Moose::FV::elemFromFace(*this, *_face_info, correct_skewness);
}

Moose::ElemFromFaceArg
FVFluxKernel::neighborFromFace(const bool correct_skewness) const
{
  mooseAssert(_face_info, "the face info should be non-null");
  return Moose::FV::neighborFromFace(*this, *_face_info, correct_skewness);
}

std::pair<SubdomainID, SubdomainID>
FVFluxKernel::faceArgSubdomains(const FaceInfo * face_info) const
{
  if (!face_info)
    face_info = _face_info;

  mooseAssert(face_info, "the face info should be non-null");

  return Moose::FV::faceArgSubdomains(*this, *face_info);
}

Moose::SingleSidedFaceArg
FVFluxKernel::singleSidedFaceArg(const FaceInfo * fi,
                                 const Moose::FV::LimiterType limiter_type,
                                 const bool correct_skewness) const
{
  if (!fi)
    fi = _face_info;
  const bool use_elem = fi->faceType(_var.name()) == FaceInfo::VarFaceNeighbors::ELEM;
  const auto sub_id = use_elem ? fi->elem().subdomain_id() : fi->neighborPtr()->subdomain_id();

  return {fi, limiter_type, true, correct_skewness, sub_id};
}

bool
FVFluxKernel::avoidBoundary(const FaceInfo & fi) const
{
  for (const auto bnd_id : fi.boundaryIDs())
    if (_boundaries_to_avoid.count(bnd_id))
      return true;
  return false;
}
<|endoftext|>


<commit_before>/** \copyright
 * Copyright (c) 2013, Balazs Racz
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are  permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * \file main.cxx
 *
 * An application which acts as an openlcb hub with the GC protocol.
 *
 * @author Balazs Racz
 * @date 3 Aug 2013
 */

#include <fcntl.h>
#include <getopt.h>
#include <stdio.h>
#include <termios.h> /* tc* functions */
#include <unistd.h>

#include <memory>

#include "os/os.h"
#include "utils/constants.hxx"
#include "utils/Hub.hxx"
#include "utils/GridConnectHub.hxx"
#include "utils/GcTcpHub.hxx"
#include "executor/Executor.hxx"
#include "executor/Service.hxx"

Executor<1> g_executor("g_executor", 0, 1024);
Service g_service(&g_executor);
CanHubFlow can_hub0(&g_service);

OVERRIDE_CONST(gc_generate_newlines, 1);

int port = 12021;
const char *device_path = nullptr;
int upstream_port = 12021;
const char *upstream_host = nullptr;
bool timestamped = false;

void usage(const char *e)
{
    fprintf(stderr, "Usage: %s [-p port] [-d device_path] [-u upstream_host] "
                    "[-q upstream_port] [-t]\n\n",
            e);
    fprintf(stderr, "GridConnect CAN HUB.\nListens to a specific TCP port, "
                    "reads CAN packets from the incoming connections using "
                    "the GridConnect protocol, and forwards all incoming "
                    "packets to all other participants.\n\nArguments:\n");
    fprintf(stderr, "\t-p port     specifies the port number to listen on, "
                    "default is 12021.\n");
    fprintf(stderr, "\t-d device   is a path to a physical device doing "
                    "serial-CAN or USB-CAN. If specified, opens device and "
                    "adds it to the hub.\n");
    fprintf(stderr, "\t-u upstream_host   is the host name for an upstream "
                    "hub. If specified, this hub will connect to an upstream "
                    "hub.\n");
    fprintf(stderr,
            "\t-q upstream_port   is the port number for the upstream hub.\n");
    fprintf(stderr,
            "\t-t prints timestamps for each packet.\n");
    exit(1);
}

void parse_args(int argc, char *argv[])
{
    int opt;
    while ((opt = getopt(argc, argv, "hp:d:u:q:t")) >= 0)
    {
        switch (opt)
        {
            case 'h':
                usage(argv[0]);
                break;
            case 'd':
                device_path = optarg;
                break;
            case 'p':
                port = atoi(optarg);
                break;
            case 'u':
                upstream_host = optarg;
                break;
            case 'q':
                upstream_port = atoi(optarg);
                break;
            case 't':
                timestamped = true;
                break;
            default:
                fprintf(stderr, "Unknown option %c\n", opt);
                usage(argv[0]);
        }
    }
}

class ConnectionClient
{
public:
    /** Test the connection whether it is alive; establish the connection if it
     * is dead. */
    virtual void ping() = 0;
};

class DeviceClosedNotify : public Notifiable
{
public:
    DeviceClosedNotify(int *fd, string name)
        : fd_(fd)
        , name_(name)
    {
    }
    void notify() override
    {
        LOG_ERROR("Connection to %s closed.", name_.c_str());
        *fd_ = -1;
    }

private:
    int *fd_;
    string name_;
};

class FdConnectionClient : public ConnectionClient
{
public:
    FdConnectionClient(const string &name) : closedNotify_(&fd_, name)
    {
    }

    void ping() OVERRIDE
    {
        if (fd_ < 0)
        {
            try_connect();
        }
    }

protected:
    virtual void try_connect() = 0;

    int fd_{-1};
    DeviceClosedNotify closedNotify_;
};

class DeviceConnectionClient : public FdConnectionClient
{
public:
    DeviceConnectionClient(const string &name, const string &dev)
        : FdConnectionClient(name)
        , dev_(dev)
    {
    }

private:
    void try_connect() OVERRIDE
    {
        fd_ = ::open(dev_.c_str(), O_RDWR);
        if (fd_ >= 0)
        {
            // Sets up the terminal in raw mode. Otherwise linux might echo
            // characters coming in from the device and that will make
            // packets go back to where they came from.
            HASSERT(!tcflush(fd_, TCIOFLUSH));
            struct termios settings;
            HASSERT(!tcgetattr(fd_, &settings));
            cfmakeraw(&settings);
            cfsetspeed(&settings, B115200);
            HASSERT(!tcsetattr(fd_, TCSANOW, &settings));
            LOG(INFO, "Opened device %s.\n", device_path);
            create_gc_port_for_can_hub(&can_hub0, fd_, &closedNotify_);
        }
        else
        {
            LOG_ERROR("Failed to open device %s: %s\n", device_path,
                      strerror(errno));
            fd_ = -1;
        }
    }

    string dev_;
};

class UpstreamConnectionClient : public FdConnectionClient
{
public:
    UpstreamConnectionClient(const string &name, const string &host, int port)
        : FdConnectionClient(name)
        , host_(host)
        , port_(port)
    {
    }

private:
    void try_connect() OVERRIDE
    {
        fd_ = ConnectSocket(upstream_host, upstream_port);
        if (fd_ >= 0)
        {
            LOG_ERROR("Connected to %s:%d\n", host_.c_str(), port_);
            create_gc_port_for_can_hub(&can_hub0, fd_, &closedNotify_);
        }
        else
        {
            LOG_ERROR("Failed to connect to %s:%d: %s\n", host_.c_str(), port_,
                      strerror(errno));
            fd_ = -1;
        }
    }

    string host_;
    int port_;
};

/** Entry point to application.
 * @param argc number of command line arguments
 * @param argv array of command line arguments
 * @return 0, should never return
 */
int appl_main(int argc, char *argv[])
{
    parse_args(argc, argv);
    GcPacketPrinter packet_printer(&can_hub0, timestamped);
    GcTcpHub hub(&can_hub0, port);
    vector<std::unique_ptr<ConnectionClient>> connections;

    if (upstream_host)
    {
        connections.emplace_back(new UpstreamConnectionClient(
            "upstream", upstream_host, upstream_port));
    }

    if (device_path)
    {
        connections.emplace_back(
            new DeviceConnectionClient("device", device_path));
    }

    while (1)
    {
        for (const auto &p : connections)
        {
            p->ping();
        }
        sleep(1);
    }
    return 0;
}
<commit_msg>Do not put newlines between gridconnect frames from the hub.<commit_after>/** \copyright
 * Copyright (c) 2013, Balazs Racz
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are  permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * \file main.cxx
 *
 * An application which acts as an openlcb hub with the GC protocol.
 *
 * @author Balazs Racz
 * @date 3 Aug 2013
 */

#include <fcntl.h>
#include <getopt.h>
#include <stdio.h>
#include <termios.h> /* tc* functions */
#include <unistd.h>

#include <memory>

#include "os/os.h"
#include "utils/constants.hxx"
#include "utils/Hub.hxx"
#include "utils/GridConnectHub.hxx"
#include "utils/GcTcpHub.hxx"
#include "executor/Executor.hxx"
#include "executor/Service.hxx"

Executor<1> g_executor("g_executor", 0, 1024);
Service g_service(&g_executor);
CanHubFlow can_hub0(&g_service);

OVERRIDE_CONST(gc_generate_newlines, 0);

int port = 12021;
const char *device_path = nullptr;
int upstream_port = 12021;
const char *upstream_host = nullptr;
bool timestamped = false;

void usage(const char *e)
{
    fprintf(stderr, "Usage: %s [-p port] [-d device_path] [-u upstream_host] "
                    "[-q upstream_port] [-t]\n\n",
            e);
    fprintf(stderr, "GridConnect CAN HUB.\nListens to a specific TCP port, "
                    "reads CAN packets from the incoming connections using "
                    "the GridConnect protocol, and forwards all incoming "
                    "packets to all other participants.\n\nArguments:\n");
    fprintf(stderr, "\t-p port     specifies the port number to listen on, "
                    "default is 12021.\n");
    fprintf(stderr, "\t-d device   is a path to a physical device doing "
                    "serial-CAN or USB-CAN. If specified, opens device and "
                    "adds it to the hub.\n");
    fprintf(stderr, "\t-u upstream_host   is the host name for an upstream "
                    "hub. If specified, this hub will connect to an upstream "
                    "hub.\n");
    fprintf(stderr,
            "\t-q upstream_port   is the port number for the upstream hub.\n");
    fprintf(stderr,
            "\t-t prints timestamps for each packet.\n");
    exit(1);
}

void parse_args(int argc, char *argv[])
{
    int opt;
    while ((opt = getopt(argc, argv, "hp:d:u:q:t")) >= 0)
    {
        switch (opt)
        {
            case 'h':
                usage(argv[0]);
                break;
            case 'd':
                device_path = optarg;
                break;
            case 'p':
                port = atoi(optarg);
                break;
            case 'u':
                upstream_host = optarg;
                break;
            case 'q':
                upstream_port = atoi(optarg);
                break;
            case 't':
                timestamped = true;
                break;
            default:
                fprintf(stderr, "Unknown option %c\n", opt);
                usage(argv[0]);
        }
    }
}

class ConnectionClient
{
public:
    /** Test the connection whether it is alive; establish the connection if it
     * is dead. */
    virtual void ping() = 0;
};

class DeviceClosedNotify : public Notifiable
{
public:
    DeviceClosedNotify(int *fd, string name)
        : fd_(fd)
        , name_(name)
    {
    }
    void notify() override
    {
        LOG_ERROR("Connection to %s closed.", name_.c_str());
        *fd_ = -1;
    }

private:
    int *fd_;
    string name_;
};

class FdConnectionClient : public ConnectionClient
{
public:
    FdConnectionClient(const string &name) : closedNotify_(&fd_, name)
    {
    }

    void ping() OVERRIDE
    {
        if (fd_ < 0)
        {
            try_connect();
        }
    }

protected:
    virtual void try_connect() = 0;

    int fd_{-1};
    DeviceClosedNotify closedNotify_;
};

class DeviceConnectionClient : public FdConnectionClient
{
public:
    DeviceConnectionClient(const string &name, const string &dev)
        : FdConnectionClient(name)
        , dev_(dev)
    {
    }

private:
    void try_connect() OVERRIDE
    {
        fd_ = ::open(dev_.c_str(), O_RDWR);
        if (fd_ >= 0)
        {
            // Sets up the terminal in raw mode. Otherwise linux might echo
            // characters coming in from the device and that will make
            // packets go back to where they came from.
            HASSERT(!tcflush(fd_, TCIOFLUSH));
            struct termios settings;
            HASSERT(!tcgetattr(fd_, &settings));
            cfmakeraw(&settings);
            cfsetspeed(&settings, B115200);
            HASSERT(!tcsetattr(fd_, TCSANOW, &settings));
            LOG(INFO, "Opened device %s.\n", device_path);
            create_gc_port_for_can_hub(&can_hub0, fd_, &closedNotify_);
        }
        else
        {
            LOG_ERROR("Failed to open device %s: %s\n", device_path,
                      strerror(errno));
            fd_ = -1;
        }
    }

    string dev_;
};

class UpstreamConnectionClient : public FdConnectionClient
{
public:
    UpstreamConnectionClient(const string &name, const string &host, int port)
        : FdConnectionClient(name)
        , host_(host)
        , port_(port)
    {
    }

private:
    void try_connect() OVERRIDE
    {
        fd_ = ConnectSocket(upstream_host, upstream_port);
        if (fd_ >= 0)
        {
            LOG_ERROR("Connected to %s:%d\n", host_.c_str(), port_);
            create_gc_port_for_can_hub(&can_hub0, fd_, &closedNotify_);
        }
        else
        {
            LOG_ERROR("Failed to connect to %s:%d: %s\n", host_.c_str(), port_,
                      strerror(errno));
            fd_ = -1;
        }
    }

    string host_;
    int port_;
};

/** Entry point to application.
 * @param argc number of command line arguments
 * @param argv array of command line arguments
 * @return 0, should never return
 */
int appl_main(int argc, char *argv[])
{
    parse_args(argc, argv);
    GcPacketPrinter packet_printer(&can_hub0, timestamped);
    GcTcpHub hub(&can_hub0, port);
    vector<std::unique_ptr<ConnectionClient>> connections;

    if (upstream_host)
    {
        connections.emplace_back(new UpstreamConnectionClient(
            "upstream", upstream_host, upstream_port));
    }

    if (device_path)
    {
        connections.emplace_back(
            new DeviceConnectionClient("device", device_path));
    }

    while (1)
    {
        for (const auto &p : connections)
        {
            p->ping();
        }
        sleep(1);
    }
    return 0;
}
<|endoftext|>


<commit_before>#include <gmock/gmock.h>
#include "../bart_test_helper.h"

#include <sys/stat.h>
#include <exception>
#include <fstream>

#include <gtest/gtest.h>

class TestHelperIntTest : public ::testing::Test {
 protected:
  std::string gold_files_directory = "test_data/";
  btest::BARTTestHelper test_helper;
  void TearDown() override;
};

void TestHelperIntTest::TearDown() {
  std::string report_directory = test_helper.GetReportDirectory();
  if (report_directory.empty() &&
      (btest::GlobalBARTTestHelper().GetReportDirectory() != report_directory))
    rmdir(test_helper.GetReportDirectory().c_str());
}

TEST_F(TestHelperIntTest, IntegrationTestGoodNoReport) {
  //btest::BARTTestHelper test_helper(false, gold_files_directory);
  // Make actual file
  std::string filename = "bart_test_helper";
  std::ofstream actual_stream(filename, std::ios_base::out);
  actual_stream << "1234\n";
  actual_stream.close();
  EXPECT_TRUE(test_helper.GoldTest(filename)) << test_helper.GetFailMessage();
  // Check deleted
  struct stat sb;
  EXPECT_FALSE(stat(filename.c_str(), &sb) == 0);
}

TEST_F(TestHelperIntTest, IntegrationTestBadNoReport) {
  //btest::BARTTestHelper test_helper(false, gold_files_directory);
  // Make actual file
  std::string filename = "bart_test_helper";
  std::ofstream actual_stream(filename, std::ios_base::out);
  actual_stream << "1235";
  actual_stream.close();
  EXPECT_FALSE(test_helper.GoldTest(filename)) << test_helper.GetFailMessage();
  // Check deleted
  struct stat sb;
  EXPECT_FALSE(stat(filename.c_str(), &sb) == 0);
}

TEST_F(TestHelperIntTest, IntegrationTestNoActual) {
  //btest::BARTTestHelper test_helper(false, gold_files_directory);
  // Make actual file
  std::string filename = "bart_test_helper";
  EXPECT_FALSE(test_helper.GoldTest(filename));
  EXPECT_EQ(test_helper.GetFailMessage(), "Bad test file");
}

TEST_F(TestHelperIntTest, IntegrationTestBadGoldFileNoReport) {
  std::string bad_gold_dir = "/testing_files";
  test_helper.ReInit(false, bad_gold_dir);
  // Make actual file
  std::string filename = "bart_test_helper";
  std::ofstream actual_stream(filename, std::ios_base::out);
  actual_stream << "1235";
  actual_stream.close();
  EXPECT_FALSE(test_helper.GoldTest(filename));
  EXPECT_EQ(test_helper.GetFailMessage(), "Bad gold file");
  // Check deleted
  struct stat sb;
  EXPECT_FALSE(stat(filename.c_str(), &sb) == 0);
}

TEST_F(TestHelperIntTest, IntegrationTestBadGoldReport) {
  test_helper.ReInit(true, gold_files_directory);
  // Make actual file
  std::string filename = "bart_test_helper_2";
  std::string report_directory = test_helper.GetReportDirectory();

  std::ofstream actual_stream(filename, std::ios_base::out);
  actual_stream << "1235";
  actual_stream.close();
  EXPECT_FALSE(test_helper.GoldTest(filename)) << test_helper.GetFailMessage();
  // Check deleted
  struct stat sb;
  EXPECT_FALSE(stat(filename.c_str(), &sb) == 0);
    std::string new_name(report_directory + "/" + filename);
  EXPECT_TRUE(
      stat(new_name.c_str(), &sb) == 0 &&
      S_ISREG(sb.st_mode));
  EXPECT_FALSE(
      stat((new_name + ".diff").c_str(), &sb) == 0 &&
      S_ISREG(sb.st_mode));
  remove(new_name.c_str());
  //rmdir(report_directory.c_str());
}

TEST_F(TestHelperIntTest, IntegrationTestBadReport) {
  test_helper.ReInit(true, gold_files_directory);
  // Make actual file
  std::string filename = "bart_test_helper";
  std::string report_directory = test_helper.GetReportDirectory();

  std::ofstream actual_stream(filename, std::ios_base::out);
  actual_stream << "12\n34";
  actual_stream.close();
  EXPECT_FALSE(test_helper.GoldTest(filename))  << test_helper.GetFailMessage();
  // Check deleted
  struct stat sb;
  std::string new_name(report_directory + "/" + filename);
  std::string diff_name = new_name + ".diff";
  //Files in correct places
  EXPECT_FALSE(stat(filename.c_str(), &sb) == 0);
  EXPECT_TRUE(
      stat(new_name.c_str(), &sb) == 0 &&
      S_ISREG(sb.st_mode));
  EXPECT_TRUE(
      stat(diff_name.c_str(), &sb) == 0 &&
      S_ISREG(sb.st_mode));

  // Cleanup
  remove(new_name.c_str());
  remove(diff_name.c_str());
  //rmdir(report_directory.c_str());
}
<commit_msg>fixed alphabetical order<commit_after>#include <gmock/gmock.h>
#include "../bart_test_helper.h"

#include <exception>
#include <fstream>
#include <sys/stat.h>

#include <gtest/gtest.h>

class TestHelperIntTest : public ::testing::Test {
 protected:
  std::string gold_files_directory = "test_data/";
  btest::BARTTestHelper test_helper;
  void TearDown() override;
};

void TestHelperIntTest::TearDown() {
  std::string report_directory = test_helper.GetReportDirectory();
  if (report_directory.empty() &&
      (btest::GlobalBARTTestHelper().GetReportDirectory() != report_directory))
    rmdir(test_helper.GetReportDirectory().c_str());
}

TEST_F(TestHelperIntTest, IntegrationTestGoodNoReport) {
  //btest::BARTTestHelper test_helper(false, gold_files_directory);
  // Make actual file
  std::string filename = "bart_test_helper";
  std::ofstream actual_stream(filename, std::ios_base::out);
  actual_stream << "1234\n";
  actual_stream.close();
  EXPECT_TRUE(test_helper.GoldTest(filename)) << test_helper.GetFailMessage();
  // Check deleted
  struct stat sb;
  EXPECT_FALSE(stat(filename.c_str(), &sb) == 0);
}

TEST_F(TestHelperIntTest, IntegrationTestBadNoReport) {
  //btest::BARTTestHelper test_helper(false, gold_files_directory);
  // Make actual file
  std::string filename = "bart_test_helper";
  std::ofstream actual_stream(filename, std::ios_base::out);
  actual_stream << "1235";
  actual_stream.close();
  EXPECT_FALSE(test_helper.GoldTest(filename)) << test_helper.GetFailMessage();
  // Check deleted
  struct stat sb;
  EXPECT_FALSE(stat(filename.c_str(), &sb) == 0);
}

TEST_F(TestHelperIntTest, IntegrationTestNoActual) {
  //btest::BARTTestHelper test_helper(false, gold_files_directory);
  // Make actual file
  std::string filename = "bart_test_helper";
  EXPECT_FALSE(test_helper.GoldTest(filename));
  EXPECT_EQ(test_helper.GetFailMessage(), "Bad test file");
}

TEST_F(TestHelperIntTest, IntegrationTestBadGoldFileNoReport) {
  std::string bad_gold_dir = "/testing_files";
  test_helper.ReInit(false, bad_gold_dir);
  // Make actual file
  std::string filename = "bart_test_helper";
  std::ofstream actual_stream(filename, std::ios_base::out);
  actual_stream << "1235";
  actual_stream.close();
  EXPECT_FALSE(test_helper.GoldTest(filename));
  EXPECT_EQ(test_helper.GetFailMessage(), "Bad gold file");
  // Check deleted
  struct stat sb;
  EXPECT_FALSE(stat(filename.c_str(), &sb) == 0);
}

TEST_F(TestHelperIntTest, IntegrationTestBadGoldReport) {
  test_helper.ReInit(true, gold_files_directory);
  // Make actual file
  std::string filename = "bart_test_helper_2";
  std::string report_directory = test_helper.GetReportDirectory();

  std::ofstream actual_stream(filename, std::ios_base::out);
  actual_stream << "1235";
  actual_stream.close();
  EXPECT_FALSE(test_helper.GoldTest(filename)) << test_helper.GetFailMessage();
  // Check deleted
  struct stat sb;
  EXPECT_FALSE(stat(filename.c_str(), &sb) == 0);
    std::string new_name(report_directory + "/" + filename);
  EXPECT_TRUE(
      stat(new_name.c_str(), &sb) == 0 &&
      S_ISREG(sb.st_mode));
  EXPECT_FALSE(
      stat((new_name + ".diff").c_str(), &sb) == 0 &&
      S_ISREG(sb.st_mode));
  remove(new_name.c_str());
  //rmdir(report_directory.c_str());
}

TEST_F(TestHelperIntTest, IntegrationTestBadReport) {
  test_helper.ReInit(true, gold_files_directory);
  // Make actual file
  std::string filename = "bart_test_helper";
  std::string report_directory = test_helper.GetReportDirectory();

  std::ofstream actual_stream(filename, std::ios_base::out);
  actual_stream << "12\n34";
  actual_stream.close();
  EXPECT_FALSE(test_helper.GoldTest(filename))  << test_helper.GetFailMessage();
  // Check deleted
  struct stat sb;
  std::string new_name(report_directory + "/" + filename);
  std::string diff_name = new_name + ".diff";
  //Files in correct places
  EXPECT_FALSE(stat(filename.c_str(), &sb) == 0);
  EXPECT_TRUE(
      stat(new_name.c_str(), &sb) == 0 &&
      S_ISREG(sb.st_mode));
  EXPECT_TRUE(
      stat(diff_name.c_str(), &sb) == 0 &&
      S_ISREG(sb.st_mode));

  // Cleanup
  remove(new_name.c_str());
  remove(diff_name.c_str());
  //rmdir(report_directory.c_str());
}
<|endoftext|>


<commit_before>#include <stack>
#include <vector>
#include <sstream>

#include "ExpressionTree.h"
#include "ExpressionUtil.h"

namespace
{
	const char* ops[] = { "+", "-", "*", "/", "=", "^", "u-", "sin", "cos", "tan", "(" };
	const int init_input_priority[] = { 4, 4, 5, 5, 1, 6, 7, 7, 7, 7, 8 };
	const int init_stack_priority[] = { 4, 4, 5, 5, 1, 6, 7, 7, 7, 7, 0 };
	const int n_ops= sizeof(ops) / sizeof(char*);

	const std::vector<std::string> operators(ops, ops + n_ops);

	const std::string digits("0123456789");
	const std::string integer_chars(digits);
	const std::string real_chars(digits + '.');
	const std::string identifier_chars("abcdefghijklmnopqrstuvwxyz");
	const std::string operand_chars(identifier_chars + digits + '.');

	typedef std::map<std::string, int> priority_table;

	priority_table init_priority_table(const char* ops[], const int* priority, const int n_ops)
	{
		priority_table temp;

		for (int i = 0; i < n_ops; ++i)
		{
			temp.insert(std::make_pair(std::string(ops[i]), priority[i]));
		}

		return temp;
	}

	const priority_table input_priority = init_priority_table(ops, init_input_priority, n_ops);
	const priority_table stack_priority = init_priority_table(ops, init_stack_priority, n_ops);
}

std::string FormatInfix(const std::string& infix)
{
	std::string::const_iterator bos = infix.begin();
	std::string::const_iterator eos = infix.end();
	std::string formatted;

	for (std::string::const_iterator it = bos; it != eos; ++it)
	{
		if (std::find(operators.begin(), operators.end(), std::string(1, *it)) != operators.end() || *it == '(' || *it == ')')
		{
			if (it != bos && *(it - 1) != ' ' && *(formatted.end() - 1) != ' ' )
			{
				formatted.append(1, ' ');
			}

			formatted.append(1, *it);

			if ((it + 1) != eos && *(it + 1) != ' ')
			{
				formatted.append(1, ' ');
			}
		}
		else
		{
			if (*it != ' ')
			{
				formatted.append(1, *it);
			}
			else if (it != bos && *(it - 1) != ' ')
			{
				formatted.append(1, *it);
			}
		}
	}

	return formatted;
}

std::string InfixToPostfix(const std::string& infix)
{
	std::stack<std::string> operatorStack;
	std::string token;
	std::string previousToken = "";
	int parenCount = 0;
	bool isUnary = true;

	std::istringstream is(FormatInfix(infix));
	std::string postfix;

	while (is >> token)
	{
		if (token != "(" && std::find(operators.begin(), operators.end(), token) != operators.end())
		{
			if (previousToken == "(")
			{
				throw InfixError("Operator can't evaluate with invalid operands");
			}

			if (isUnary && token == "-")
			{
				token = "u" + token;
			}
			else
			{
				while (!operatorStack.empty() && input_priority.find(token)->second <= stack_priority.find(operatorStack.top())->second)
				{
					postfix += operatorStack.top() + ' ';
					operatorStack.pop();
				}
			}

			operatorStack.push(token);
			isUnary = true;
		}
		else if (token == "(")
		{
			operatorStack.push(token);
			++parenCount;
		}
		else if (token == ")")
		{
			if (parenCount == 0)
			{
				throw InfixError("Invalid Paren");
			}
			if (previousToken == "(" && !postfix.empty())
			{
				throw InfixError("Invalid Paren");
			}

			while (!operatorStack.empty() && operatorStack.top() != "(")
			{
				postfix += operatorStack.top() + ' ';
				operatorStack.pop();
			}

			operatorStack.pop();
			--parenCount;
		}
		else if (token.find_first_not_of(operand_chars) == std::string::npos)
		{
			if (previousToken == ")")
			{
				throw InfixError("Operator can't evaluate with invalid operands");
			}

			postfix += token + ' ';
			isUnary = false;
		}
		else
		{
			throw InfixError("Undefined Symbol");
		}

		previousToken = token;
	}

	if (parenCount > 0)
	{
		throw InfixError("Invalid Paren");
	}

	while (!operatorStack.empty())
	{
		postfix += operatorStack.top() + ' ';
		operatorStack.pop();
	}

	if (!postfix.empty())
	{
		postfix.erase(postfix.size() - 1);
	}

	size_t equalPos = infix.find("=");

	if (equalPos != std::string::npos)
	{
		if (infix.substr(0, equalPos).find_first_not_of(identifier_chars + " " + "\t") != std::string::npos)
		{
			throw InfixError("Invalid Variable");
		}
		if (equalPos != infix.rfind("="))
		{
			throw InfixError("Invalid Variable");
		}
	}

	return postfix;
}<commit_msg>Rearrange priority and add left paren<commit_after>#include <stack>
#include <vector>
#include <sstream>

#include "ExpressionTree.h"
#include "ExpressionUtil.h"

namespace
{
	const char* ops[] = { "+", "-", "*", "/", "=", "^", "u-", "sin", "cos", "tan", "(" };
	const int init_input_priority[] = { 4, 4, 5, 5, 1, 6, 7, 7, 7, 7, 8 };
	const int init_stack_priority[] = { 4, 4, 5, 5, 1, 6, 7, 7, 7, 7, 0 };
	const int n_ops= sizeof(ops) / sizeof(char*);

	const std::vector<std::string> operators(ops, ops + n_ops);

	const std::string digits("0123456789");
	const std::string integer_chars(digits);
	const std::string real_chars(digits + '.');
	const std::string identifier_chars("abcdefghijklmnopqrstuvwxyz");
	const std::string operand_chars(identifier_chars + digits + '.');

	typedef std::map<std::string, int> priority_table;

	priority_table init_priority_table(const char* ops[], const int* priority, const int n_ops)
	{
		priority_table temp;

		for (int i = 0; i < n_ops; ++i)
		{
			temp.insert(std::make_pair(std::string(ops[i]), priority[i]));
		}

		return temp;
	}

	const priority_table input_priority = init_priority_table(ops, init_input_priority, n_ops);
	const priority_table stack_priority = init_priority_table(ops, init_stack_priority, n_ops);
}

std::string FormatInfix(const std::string& infix)
{
	std::string::const_iterator bos = infix.begin();
	std::string::const_iterator eos = infix.end();
	std::string formatted;

	for (std::string::const_iterator it = bos; it != eos; ++it)
	{
		if (std::find(operators.begin(), operators.end(), std::string(1, *it)) != operators.end() || *it == '(' || *it == ')')
		{
			if (it != bos && *(it - 1) != ' ' && *(formatted.end() - 1) != ' ' )
			{
				formatted.append(1, ' ');
			}

			formatted.append(1, *it);

			if ((it + 1) != eos && *(it + 1) != ' ')
			{
				formatted.append(1, ' ');
			}
		}
		else
		{
			if (*it != ' ')
			{
				formatted.append(1, *it);
			}
			else if (it != bos && *(it - 1) != ' ')
			{
				formatted.append(1, *it);
			}
		}
	}

	return formatted;
}

std::string InfixToPostfix(const std::string& infix)
{
	std::stack<std::string> operatorStack;
	std::string token;
	std::string previousToken = "";
	int parenCount = 0;
	bool isUnary = true;

	std::istringstream is(FormatInfix(infix));
	std::string postfix;

	while (is >> token)
	{
		if (token != "(" && std::find(operators.begin(), operators.end(), token) != operators.end())
		{
			if (previousToken == "(")
			{
				throw InfixError("Operator can't evaluate with invalid operands");
			}

			if (isUnary && token == "-")
			{
				token = "u" + token;
			}
			else
			{
				while (!operatorStack.empty() && input_priority.find(token)->second <= stack_priority.find(operatorStack.top())->second)
				{
					postfix += operatorStack.top() + ' ';
					operatorStack.pop();
				}
			}

			operatorStack.push(token);
			isUnary = true;
		}
		else if (token == "(")
		{
			operatorStack.push(token);
			++parenCount;
		}
		else if (token == ")")
		{
			if (parenCount == 0)
			{
				throw InfixError("Invalid Paren");
			}
			if (previousToken == "(" && !postfix.empty())
			{
				throw InfixError("Invalid Paren");
			}

			while (!operatorStack.empty() && operatorStack.top() != "(")
			{
				postfix += operatorStack.top() + ' ';
				operatorStack.pop();
			}

			operatorStack.pop();
			--parenCount;
		}
		else if (token.find_first_not_of(operand_chars) == std::string::npos)
		{
			if (previousToken == ")")
			{
				throw InfixError("Operator can't evaluate with invalid operands");
			}

			postfix += token + ' ';
			isUnary = false;
		}
		else
		{
			throw InfixError("Undefined Symbol");
		}

		previousToken = token;
	}

	if (parenCount > 0)
	{
		throw InfixError("Invalid Paren");
	}

	while (!operatorStack.empty())
	{
		postfix += operatorStack.top() + ' ';
		operatorStack.pop();
	}

	if (!postfix.empty())
	{
		postfix.erase(postfix.size() - 1);
	}

	size_t equalPos = infix.find("=");

	if (equalPos != std::string::npos)
	{
		if (infix.substr(0, equalPos).find_first_not_of(identifier_chars + " " + "\t") != std::string::npos)
		{
			throw InfixError("Invalid Variable");
		}
		if (equalPos != infix.rfind("="))
		{
			throw InfixError("Invalid Variable");
		}
	}

	return postfix;
}
<|endoftext|>


<commit_before>// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
//
// File contains modifications by: The Gulden developers
// All modifications:
// Copyright (c) 2016-2018 The Gulden developers
// Authored by: Malcolm MacLeod (mmacleod@webmail.co.za)
// Distributed under the GULDEN software license, see the accompanying
// file COPYING

#include "validation.h"
#include "validationinterface.h"
#include "witnessvalidation.h"
#include "versionbitsvalidation.h"
#include <consensus/validation.h>

#include "blockstore.h"
#include "txdb.h"
#include "net.h"
#include "chainparams.h"
#include "versionbits.h"

/** Convert CValidationState to a human-readable message for logging */
std::string FormatStateMessage(const CValidationState &state)
{
    return strprintf("%s%s (code %i)",
        state.GetRejectReason(),
        state.GetDebugMessage().empty() ? "" : ", "+state.GetDebugMessage(),
        state.GetRejectCode());
}

/** Return transaction in txOut, and if it was found inside a block, its hash is placed in hashBlock */
bool GetTransaction(const uint256 &hash, CTransactionRef &txOut, const CChainParams& params, uint256 &hashBlock, bool fAllowSlow)
{
    CBlockIndex *pindexSlow = NULL;

    LOCK(cs_main); // Required for ReadBlockFromDisk.

    CTransactionRef ptx = mempool.get(hash);
    if (ptx)
    {
        txOut = ptx;
        return true;
    }

    if (fTxIndex) {
        CDiskTxPos postx;
        if (pblocktree->ReadTxIndex(hash, postx)) {
            CFile file(blockStore.GetBlockFile(postx, true), SER_DISK, CLIENT_VERSION);
            if (file.IsNull())
                return error("%s: OpenBlockFile failed", __func__);
            CBlockHeader header;
            try {
                file >> header;
                fseek(file.Get(), postx.nTxOffset, SEEK_CUR);
                file >> txOut;
            } catch (const std::exception& e) {
                return error("%s: Deserialize or I/O error - %s", __func__, e.what());
            }
            hashBlock = header.GetHashPoW2();
            if (txOut->GetHash() != hash)
                return error("%s: txid mismatch", __func__);
            return true;
        }
    }

    if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it
        const Coin& coin = AccessByTxid(*pcoinsTip, hash);
        if (!coin.IsSpent()) pindexSlow = chainActive[coin.nHeight];
    }

    if (pindexSlow) {
        CBlock block;
        if (ReadBlockFromDisk(block, pindexSlow, params)) {
            for (const auto& tx : block.vtx) {
                if (tx->GetHash() == hash) {
                    txOut = tx;
                    hashBlock = pindexSlow->GetBlockHashPoW2();
                    return true;
                }
            }
        }
    }

    return false;
}

CAmount GetBlockSubsidy(int nHeight)
{
    CAmount nSubsidy = 0;
    if(nHeight == 1) //First block premine
    {
        nSubsidy = 170000000 * COIN;
    }
    else if(nHeight <= 250000) // 1000 Gulden per block for first 250k blocks
    {
        nSubsidy = 1000 * COIN;
    }
    else if(nHeight <= 12850000) // Switch to fixed reward of 100 Gulden per block (no halving) - to continue until original coin target is met.
    {
        nSubsidy = 100 * COIN;
    }
    return nSubsidy;
}

bool IsInitialBlockDownload()
{
    //AssertLockHeld(cs_main);
    const CChainParams& chainParams = Params();

    // Once this function has returned false, it must remain false.
    static std::atomic<bool> latchToFalse{false};
    // Optimization: pre-test latch before taking the lock.
    if (latchToFalse.load(std::memory_order_relaxed))
        return false;

    if (latchToFalse.load(std::memory_order_relaxed))
        return false;
    if (fImporting || fReindex)
        return true;
    if (chainActive.Tip() == NULL)
        return true;
    if (chainActive.Tip()->nChainWork < UintToArith256(chainParams.GetConsensus().nMinimumChainWork))
        return true;
    if (chainActive.Tip()->GetBlockTime() < (GetTime() - nMaxTipAge))
        return true;
    LogPrintf("Leaving InitialBlockDownload (latching to false)\n");
    latchToFalse.store(true, std::memory_order_relaxed);
    return false;
}

bool HaveRequiredPeerUpgradePercent(int nRequiredProtoVersion, unsigned int nRequiredPercent)
{
    std::vector<CNodeStats> vstats;
    g_connman->GetNodeStats(vstats);

    // Insufficient peers to determine.
    if (vstats.size() < 3)
    {
        return true;
    }

    int nUpgradeCount = 0;
    for (const CNodeStats& stats : vstats)
    {
        if (stats.nVersion >= nRequiredProtoVersion)
        {
            ++nUpgradeCount;
        }
    }
    return (100 * nUpgradeCount) / vstats.size() > nRequiredPercent;
}

int32_t ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Params& params)
{
    LOCK(cs_main);
    int32_t nVersion = VERSIONBITS_TOP_BITS;

    for (int i = 0; i < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; i++)
    {
        ThresholdState state = VersionBitsState(pindexPrev, params, (Consensus::DeploymentPos)i, versionbitscache);
        if (state == THRESHOLD_LOCKED_IN)
        {
            nVersion |= VersionBitsMask(params, (Consensus::DeploymentPos)i);
        }
        else if (state == THRESHOLD_STARTED)
        {
            if (params.vDeployments[i].protoVersion == 0 || HaveRequiredPeerUpgradePercent(params.vDeployments[i].protoVersion, params.vDeployments[i].requiredProtoUpgradePercent))
            {
                nVersion |= VersionBitsMask(params, (Consensus::DeploymentPos)i);
            }
        }
    }

    return nVersion;
}

//! Guess how far we are in the verification process at the given block index
double GuessVerificationProgress(const ChainTxData& data, CBlockIndex *pindex) {
    if (pindex == NULL)
        return 0.0;

    int64_t nNow = time(NULL);

    double fTxTotal;

    if (pindex->nChainTx <= data.nTxCount) {
        fTxTotal = data.nTxCount + (nNow - data.nTime) * data.dTxRate;
    } else {
        fTxTotal = pindex->nChainTx + (nNow - pindex->GetBlockTime()) * data.dTxRate;
    }

    return pindex->nChainTx / fTxTotal;
}
<commit_msg>Temporarily disable phase 4 signalling, it will be re-enabled later on as we approach phase 4 activation.<commit_after>// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
//
// File contains modifications by: The Gulden developers
// All modifications:
// Copyright (c) 2016-2018 The Gulden developers
// Authored by: Malcolm MacLeod (mmacleod@webmail.co.za)
// Distributed under the GULDEN software license, see the accompanying
// file COPYING

#include "validation.h"
#include "validationinterface.h"
#include "witnessvalidation.h"
#include "versionbitsvalidation.h"
#include <consensus/validation.h>

#include "blockstore.h"
#include "txdb.h"
#include "net.h"
#include "chainparams.h"
#include "versionbits.h"

/** Convert CValidationState to a human-readable message for logging */
std::string FormatStateMessage(const CValidationState &state)
{
    return strprintf("%s%s (code %i)",
        state.GetRejectReason(),
        state.GetDebugMessage().empty() ? "" : ", "+state.GetDebugMessage(),
        state.GetRejectCode());
}

/** Return transaction in txOut, and if it was found inside a block, its hash is placed in hashBlock */
bool GetTransaction(const uint256 &hash, CTransactionRef &txOut, const CChainParams& params, uint256 &hashBlock, bool fAllowSlow)
{
    CBlockIndex *pindexSlow = NULL;

    LOCK(cs_main); // Required for ReadBlockFromDisk.

    CTransactionRef ptx = mempool.get(hash);
    if (ptx)
    {
        txOut = ptx;
        return true;
    }

    if (fTxIndex) {
        CDiskTxPos postx;
        if (pblocktree->ReadTxIndex(hash, postx)) {
            CFile file(blockStore.GetBlockFile(postx, true), SER_DISK, CLIENT_VERSION);
            if (file.IsNull())
                return error("%s: OpenBlockFile failed", __func__);
            CBlockHeader header;
            try {
                file >> header;
                fseek(file.Get(), postx.nTxOffset, SEEK_CUR);
                file >> txOut;
            } catch (const std::exception& e) {
                return error("%s: Deserialize or I/O error - %s", __func__, e.what());
            }
            hashBlock = header.GetHashPoW2();
            if (txOut->GetHash() != hash)
                return error("%s: txid mismatch", __func__);
            return true;
        }
    }

    if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it
        const Coin& coin = AccessByTxid(*pcoinsTip, hash);
        if (!coin.IsSpent()) pindexSlow = chainActive[coin.nHeight];
    }

    if (pindexSlow) {
        CBlock block;
        if (ReadBlockFromDisk(block, pindexSlow, params)) {
            for (const auto& tx : block.vtx) {
                if (tx->GetHash() == hash) {
                    txOut = tx;
                    hashBlock = pindexSlow->GetBlockHashPoW2();
                    return true;
                }
            }
        }
    }

    return false;
}

CAmount GetBlockSubsidy(int nHeight)
{
    CAmount nSubsidy = 0;
    if(nHeight == 1) //First block premine
    {
        nSubsidy = 170000000 * COIN;
    }
    else if(nHeight <= 250000) // 1000 Gulden per block for first 250k blocks
    {
        nSubsidy = 1000 * COIN;
    }
    else if(nHeight <= 12850000) // Switch to fixed reward of 100 Gulden per block (no halving) - to continue until original coin target is met.
    {
        nSubsidy = 100 * COIN;
    }
    return nSubsidy;
}

bool IsInitialBlockDownload()
{
    //AssertLockHeld(cs_main);
    const CChainParams& chainParams = Params();

    // Once this function has returned false, it must remain false.
    static std::atomic<bool> latchToFalse{false};
    // Optimization: pre-test latch before taking the lock.
    if (latchToFalse.load(std::memory_order_relaxed))
        return false;

    if (latchToFalse.load(std::memory_order_relaxed))
        return false;
    if (fImporting || fReindex)
        return true;
    if (chainActive.Tip() == NULL)
        return true;
    if (chainActive.Tip()->nChainWork < UintToArith256(chainParams.GetConsensus().nMinimumChainWork))
        return true;
    if (chainActive.Tip()->GetBlockTime() < (GetTime() - nMaxTipAge))
        return true;
    LogPrintf("Leaving InitialBlockDownload (latching to false)\n");
    latchToFalse.store(true, std::memory_order_relaxed);
    return false;
}

bool HaveRequiredPeerUpgradePercent(int nRequiredProtoVersion, unsigned int nRequiredPercent)
{
    std::vector<CNodeStats> vstats;
    g_connman->GetNodeStats(vstats);

    // Insufficient peers to determine.
    if (vstats.size() < 3)
    {
        return true;
    }

    int nUpgradeCount = 0;
    for (const CNodeStats& stats : vstats)
    {
        if (stats.nVersion >= nRequiredProtoVersion)
        {
            ++nUpgradeCount;
        }
    }
    return (100 * nUpgradeCount) / vstats.size() > nRequiredPercent;
}

int32_t ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Params& params)
{
    LOCK(cs_main);
    int32_t nVersion = VERSIONBITS_TOP_BITS;

    for (int i = 0; i < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; i++)
    {
        ThresholdState state = VersionBitsState(pindexPrev, params, (Consensus::DeploymentPos)i, versionbitscache);
        if (state == THRESHOLD_LOCKED_IN)
        {
            nVersion |= VersionBitsMask(params, (Consensus::DeploymentPos)i);
        }
        else if (state == THRESHOLD_STARTED)
        {
            //fixme: (2.1) - Re-enable this (temporarily disable phase 4 voting)
            //if (params.vDeployments[i].protoVersion == 0 || HaveRequiredPeerUpgradePercent(params.vDeployments[i].protoVersion, params.vDeployments[i].requiredProtoUpgradePercent))
            if (params.vDeployments[i].protoVersion == 0)
            {
                nVersion |= VersionBitsMask(params, (Consensus::DeploymentPos)i);
            }
        }
    }

    return nVersion;
}

//! Guess how far we are in the verification process at the given block index
double GuessVerificationProgress(const ChainTxData& data, CBlockIndex *pindex) {
    if (pindex == NULL)
        return 0.0;

    int64_t nNow = time(NULL);

    double fTxTotal;

    if (pindex->nChainTx <= data.nTxCount) {
        fTxTotal = data.nTxCount + (nNow - data.nTime) * data.dTxRate;
    } else {
        fTxTotal = pindex->nChainTx + (nNow - pindex->GetBlockTime()) * data.dTxRate;
    }

    return pindex->nChainTx / fTxTotal;
}
<|endoftext|>


<commit_before>/***************************************************************************
 *   Software License Agreement (BSD License)                              *
 *   Copyright (C) 2012 by Markus Bader <markus.bader@tuwien.ac.at>        *
 *                                                                         *
 *   Redistribution and use in source and binary forms, with or without    *
 *   modification, are permitted provided that the following conditions    *
 *   are met:                                                              *
 *                                                                         *
 *   1. Redistributions of source code must retain the above copyright     *
 *      notice, this list of conditions and the following disclaimer.      *
 *   2. Redistributions in binary form must reproduce the above copyright  *
 *      notice, this list of conditions and the following disclaimer in    *
 *      the documentation and/or other materials provided with the         *
 *      distribution.                                                      *
 *   3. Neither the name of the copyright holder nor the names of its      *
 *      contributors may be used to endorse or promote products derived    *
 *      from this software without specific prior written permission.      *
 *                                                                         *
 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS   *
 *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT     *
 *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS     *
 *   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE        *
 *   COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,  *
 *   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,  *
 *   BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;      *
 *   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER      *
 *   CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT    *
 *   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY *
 *   WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE           *
 *   POSSIBILITY OF SUCH DAMAGE.                                           *
 ***************************************************************************/

#include <iostream>
#include <stdlib.h>
#include <signal.h>

#include "shmfw/variable.h"
#include "shmfw/vector.h"
#include "shmfw/log.h"
#include <boost/program_options.hpp>
#include <boost/thread.hpp>
#include <ncurses.h>

bool loop_program = true;

struct Prarmeters {
    bool clear;
    std::string shm_memory_name;
    unsigned int shm_memory_size;
    std::string variable_name;
    std::string log_file;
    int process_cout_level;
    int source;
    int file_level;
    int cout_level;
    bool timeout_msg;
    bool pull;
    bool buffer;
    bool timestamp;
    int timeout;
};

Prarmeters readArgs ( int argc, char **argv ) {
    namespace po = boost::program_options;

    Prarmeters params;
    po::options_description desc ( "Allowed Parameters" );
    desc.add_options()
    ( "help", "get this help message" )
    ( "clear", "clears the shared memory" )
    ( "msg_off,o", "switches the timeout message off" )
    ( "buffer", "shows the buffer id" )
    ( "timestamp_off", "switches the timestamp off" )
    ( "pull,p", "pulls for log messages (no trigger needed)" )
    ( "timeout,t", po::value<int> ( &params.timeout )->default_value ( 2000 ), "timeout for timeout message in ms" )
    ( "source,s", po::value<int> ( &params.source )->default_value ( -1 ), "source filter (-1 off)" )
    ( "process_cout_level", po::value<int> ( &params.process_cout_level )->default_value ( ShmFw::Message::NA ), "level of message to be printed by the meassage creating process" )
    ( "cout_level", po::value<int> ( &params.cout_level )->default_value ( ShmFw::Message::NA ), "level of message to be printed by this process" )
    ( "file_level", po::value<int> ( &params.file_level )->default_value ( ShmFw::Message::NA ), "level of message to be stored" )
    ( "file,f", po::value<std::string> ( &params.log_file )->default_value ( "/tmp/log" ), "log file prefix, if empty it will print of cout" )
    ( "shm_log,l", po::value<std::string> ( &params.variable_name )->default_value ( "log" ), "shared variable name of the logger" )
    ( "shm_memory_name", po::value<std::string> ( &params.shm_memory_name )->default_value ( ShmFw::DEFAULT_LOG_SEGMENT_NAME() ), "shared memory segment name" )
    ( "shm_memory_size", po::value<unsigned int> ( &params.shm_memory_size )->default_value ( ShmFw::DEFAULT_LOG_SEGMENT_SIZE() ), "shared memory segment size" );

    po::variables_map vm;
    try {
        po::store ( po::parse_command_line ( argc, argv, desc ), vm );
    } catch ( const std::exception &ex ) {
        std::cout << desc << std::endl;;
        exit ( 1 );
    }
    po::notify ( vm );

    if ( vm.count ( "help" ) )  {
        std::cout << desc << std::endl;
        exit ( 1 );
    }
    params.clear = ( vm.count ( "clear" ) > 0 );
    params.timeout_msg = ! ( vm.count ( "msg_off" ) > 0 );
    params.pull = ( vm.count ( "pull" ) > 0 );
    params.buffer = ( vm.count ( "buffer" ) > 0 );
    params.timestamp = !( vm.count ( "timestamp_off" ) > 0 );

    return params;
}

void printMsgs ( std::vector<ShmFw::Message> &msgs, std::ofstream &file, const Prarmeters &params ) {

    for ( unsigned int i = 0; i < msgs.size(); i++ ) {
	ShmFw::Message &msg = msgs[i];
        if ( !params.log_file.empty() ) {
            file << std::setw ( 4 ) << i << ": " << msg << std::endl;
        }
	//source log level/type filter
        if ( msg.getType() >= params.cout_level ) {
            if ( params.buffer )  std::cout << std::setw ( 4 ) << i << ": ";
	    if ( params.timestamp )  std::cout << boost::posix_time::to_simple_string ( msg.getTime() ).substr(12,26) << ": ";
	    if ( msg.getType() != ShmFw::Message::NA ) std::cout << ": " <<  std::setw ( 8 ) << msg.typeStr() <<  ": ";
	    std::cout << msg.getMsg();
	    std::cout << std::endl;
        }
    }
}

void dequeLog ( ShmFw::Log &log, const Prarmeters &params ) {
    std::vector<ShmFw::Message> msgs;
    std::ofstream file;
    boost::posix_time::ptime now = boost::posix_time::microsec_clock::local_time();
    std::string datedFileName = params.log_file + std::string ( "_" ) + boost::posix_time::to_iso_string ( now ) + std::string ( ".txt" );
    if ( !params.log_file.empty() ) {
        file.open ( datedFileName.c_str(), std::ios::out | std::ios::binary );
    }
    while ( loop_program ) {
        bool wait_ones = true;
        while ( ( log.timed_wait ( params.timeout ) == false ) && loop_program && wait_ones ) {
            if ( params.timeout_msg ) std::cout << "waited " << params.timeout << " ms" << std::endl;
            if ( params.pull ) wait_ones = false;
        }
        msgs.clear();
        log.lock();
#if BOOST_VERSION / 100 % 1000 <= 55
        for ( ShmFw::Log::iterator it = log->begin(); it != log->end(); it++ ) {
	    //source filter
            if ( (params.source < 0) || (params.source == ( *it ).getSource())) {
                msgs.push_back ( *it );
                log.pop_front();
	    }
        }
#else
#endif
        log.unlock();
        printMsgs ( msgs, file, params );
    }
    if ( !params.log_file.empty() ) file.close();
}

void terminate ( int param ) {
    std::cout << "Closing program!" << std::endl;
    loop_program = false;
}

int main ( int argc, char *argv[] ) {

    signal ( SIGINT, terminate );
    signal ( SIGKILL, terminate );

    Prarmeters params = readArgs ( argc, argv );
    if ( params.clear ) {
        ShmFw::Handler::removeSegment ( params.shm_memory_name );
        std::cout << "Shared Memory " << params.shm_memory_name << " cleared" << std::endl;
        exit ( 1 );
    }
    ShmFw::HandlerPtr shmHdl = ShmFw::Handler::create ( params.shm_memory_name, params.shm_memory_size );
    ShmFw::Log log ( shmHdl, params.variable_name );
    log.process_cout_level ( params.process_cout_level );
    log.unlock();
    dequeLog ( log, params );

    log.unlock();

    exit ( 0 );

}

<commit_msg>tested on ubuntu 14.04 and 16.04<commit_after>/***************************************************************************
 *   Software License Agreement (BSD License)                              *
 *   Copyright (C) 2012 by Markus Bader <markus.bader@tuwien.ac.at>        *
 *                                                                         *
 *   Redistribution and use in source and binary forms, with or without    *
 *   modification, are permitted provided that the following conditions    *
 *   are met:                                                              *
 *                                                                         *
 *   1. Redistributions of source code must retain the above copyright     *
 *      notice, this list of conditions and the following disclaimer.      *
 *   2. Redistributions in binary form must reproduce the above copyright  *
 *      notice, this list of conditions and the following disclaimer in    *
 *      the documentation and/or other materials provided with the         *
 *      distribution.                                                      *
 *   3. Neither the name of the copyright holder nor the names of its      *
 *      contributors may be used to endorse or promote products derived    *
 *      from this software without specific prior written permission.      *
 *                                                                         *
 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS   *
 *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT     *
 *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS     *
 *   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE        *
 *   COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,  *
 *   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,  *
 *   BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;      *
 *   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER      *
 *   CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT    *
 *   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY *
 *   WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE           *
 *   POSSIBILITY OF SUCH DAMAGE.                                           *
 ***************************************************************************/

#include <iostream>
#include <stdlib.h>
#include <signal.h>

#include "shmfw/variable.h"
#include "shmfw/vector.h"
#include "shmfw/log.h"
#include <boost/program_options.hpp>
#include <boost/thread.hpp>
#include <ncurses.h>

bool loop_program = true;

struct Prarmeters {
    bool clear;
    std::string shm_memory_name;
    unsigned int shm_memory_size;
    std::string variable_name;
    std::string log_file;
    int process_cout_level;
    int source;
    int file_level;
    int cout_level;
    bool timeout_msg;
    bool pull;
    bool buffer;
    bool timestamp;
    int timeout;
};

Prarmeters readArgs ( int argc, char **argv ) {
    namespace po = boost::program_options;

    Prarmeters params;
    po::options_description desc ( "Allowed Parameters" );
    desc.add_options()
    ( "help", "get this help message" )
    ( "clear", "clears the shared memory" )
    ( "msg_off,o", "switches the timeout message off" )
    ( "buffer", "shows the buffer id" )
    ( "timestamp_off", "switches the timestamp off" )
    ( "pull,p", "pulls for log messages (no trigger needed)" )
    ( "timeout,t", po::value<int> ( &params.timeout )->default_value ( 2000 ), "timeout for timeout message in ms" )
    ( "source,s", po::value<int> ( &params.source )->default_value ( -1 ), "source filter (-1 off)" )
    ( "process_cout_level", po::value<int> ( &params.process_cout_level )->default_value ( ShmFw::Message::NA ), "level of message to be printed by the meassage creating process" )
    ( "cout_level", po::value<int> ( &params.cout_level )->default_value ( ShmFw::Message::NA ), "level of message to be printed by this process" )
    ( "file_level", po::value<int> ( &params.file_level )->default_value ( ShmFw::Message::NA ), "level of message to be stored" )
    ( "file,f", po::value<std::string> ( &params.log_file )->default_value ( "/tmp/log" ), "log file prefix, if empty it will print of cout" )
    ( "shm_log,l", po::value<std::string> ( &params.variable_name )->default_value ( "log" ), "shared variable name of the logger" )
    ( "shm_memory_name", po::value<std::string> ( &params.shm_memory_name )->default_value ( ShmFw::DEFAULT_LOG_SEGMENT_NAME() ), "shared memory segment name" )
    ( "shm_memory_size", po::value<unsigned int> ( &params.shm_memory_size )->default_value ( ShmFw::DEFAULT_LOG_SEGMENT_SIZE() ), "shared memory segment size" );

    po::variables_map vm;
    try {
        po::store ( po::parse_command_line ( argc, argv, desc ), vm );
    } catch ( const std::exception &ex ) {
        std::cout << desc << std::endl;;
        exit ( 1 );
    }
    po::notify ( vm );

    if ( vm.count ( "help" ) )  {
        std::cout << desc << std::endl;
        exit ( 1 );
    }
    params.clear = ( vm.count ( "clear" ) > 0 );
    params.timeout_msg = ! ( vm.count ( "msg_off" ) > 0 );
    params.pull = ( vm.count ( "pull" ) > 0 );
    params.buffer = ( vm.count ( "buffer" ) > 0 );
    params.timestamp = !( vm.count ( "timestamp_off" ) > 0 );

    return params;
}

void printMsgs ( std::vector<ShmFw::Message> &msgs, std::ofstream &file, const Prarmeters &params ) {

    for ( unsigned int i = 0; i < msgs.size(); i++ ) {
	ShmFw::Message &msg = msgs[i];
        if ( !params.log_file.empty() ) {
            file << std::setw ( 4 ) << i << ": " << msg << std::endl;
        }
	//source log level/type filter
        if ( msg.getType() >= params.cout_level ) {
            if ( params.buffer )  std::cout << std::setw ( 4 ) << i << ": ";
	    if ( params.timestamp )  std::cout << boost::posix_time::to_simple_string ( msg.getTime() ).substr(12,26) << ": ";
	    if ( msg.getType() != ShmFw::Message::NA ) std::cout << ": " <<  std::setw ( 8 ) << msg.typeStr() <<  ": ";
	    std::cout << msg.getMsg();
	    std::cout << std::endl;
        }
    }
}

void dequeLog ( ShmFw::Log &log, const Prarmeters &params ) {
    std::vector<ShmFw::Message> msgs;
    std::ofstream file;
    boost::posix_time::ptime now = boost::posix_time::microsec_clock::local_time();
    std::string datedFileName = params.log_file + std::string ( "_" ) + boost::posix_time::to_iso_string ( now ) + std::string ( ".txt" );
    if ( !params.log_file.empty() ) {
        file.open ( datedFileName.c_str(), std::ios::out | std::ios::binary );
    }
    while ( loop_program ) {
        bool wait_ones = true;
        while ( ( log.timed_wait ( params.timeout ) == false ) && loop_program && wait_ones ) {
            if ( params.timeout_msg ) std::cout << "waited " << params.timeout << " ms" << std::endl;
            if ( params.pull ) wait_ones = false;
        }
        msgs.clear();
        log.lock();
#if BOOST_VERSION / 100 % 1000 <= 55
        for ( ShmFw::Log::Iterator it = log->begin(); it != log->end(); it++ ) {
	    //source filter
            if ( (params.source < 0) || (params.source == ( *it ).getSource())) {
                msgs.push_back ( *it );
                log.pop_front();
	    }
        }
#else
#endif
        log.unlock();
        printMsgs ( msgs, file, params );
    }
    if ( !params.log_file.empty() ) file.close();
}

void terminate ( int param ) {
    std::cout << "Closing program!" << std::endl;
    loop_program = false;
}

int main ( int argc, char *argv[] ) {

    signal ( SIGINT, terminate );
    signal ( SIGKILL, terminate );

    Prarmeters params = readArgs ( argc, argv );
    if ( params.clear ) {
        ShmFw::Handler::removeSegment ( params.shm_memory_name );
        std::cout << "Shared Memory " << params.shm_memory_name << " cleared" << std::endl;
        exit ( 1 );
    }
    ShmFw::HandlerPtr shmHdl = ShmFw::Handler::create ( params.shm_memory_name, params.shm_memory_size );
    ShmFw::Log log ( shmHdl, params.variable_name );
    log.process_cout_level ( params.process_cout_level );
    log.unlock();
    dequeLog ( log, params );

    log.unlock();

    exit ( 0 );

}

<|endoftext|>


<commit_before>#include "CurveCalibrator.h"

CurveCalibrator::CurveCalibrator(QString titleString, QWidget *parent) :
    AbstractCalibrator(parent),
    setpoints(new QVector<double>(5)),
    positions(new QVector<double>())
{
    QGridLayout *grid = new QGridLayout(this);
    QLabel *title = new QLabel(titleString);
    grid->addWidget(title, 0, 0, 1, 5, Qt::AlignHCenter);

    QLabel *pulseWidthTitle = new QLabel(tr("Pulse Width (us)"));
    pulseWidth = new QLabel();
    QHBoxLayout *pulseLayout = new QHBoxLayout();
    pulseLayout->addWidget(pulseWidthTitle);
    pulseLayout->addWidget(pulseWidth);
    grid->addLayout(pulseLayout, 1, 0, 1, 5, Qt::AlignHCenter);

    for (int i=0; i<=100; i=i+100/4)
        positions->append(static_cast<double>(i));

    setpoints->fill(1500);

    plot = new QwtPlot();

    grid->addWidget(plot, 2, 0, 1, 5, Qt::AlignHCenter);

    plot->setAxisScale(QwtPlot::yLeft, 1000, 2000, 200);
    plot->setAxisScale(QwtPlot::xBottom, 0, 100, 25);

    curve = new QwtPlotCurve();
    curve->setPen(QPen(QColor(QString("lime"))));
    curve->setData(*positions, *setpoints);
    curve->attach(plot);

    plot->replot();

    QPushButton *zero = new QPushButton(tr("0 %"));
    QPushButton *twentyfive = new QPushButton(tr("25 %"));
    QPushButton *fifty = new QPushButton(tr("50 %"));
    QPushButton *seventyfive = new QPushButton(tr("75 %"));
    QPushButton *hundred = new QPushButton(tr("100 %"));

    grid->addWidget(zero, 3, 0);
    grid->addWidget(twentyfive, 3, 1);
    grid->addWidget(fifty, 3, 2);
    grid->addWidget(seventyfive, 3, 3);
    grid->addWidget(hundred, 3, 4);

    this->setLayout(grid);

    signalMapper = new QSignalMapper(this);
    signalMapper->setMapping(zero, 0);
    signalMapper->setMapping(twentyfive, 1);
    signalMapper->setMapping(fifty, 2);
    signalMapper->setMapping(seventyfive, 3);
    signalMapper->setMapping(hundred, 4);

    connect(zero, SIGNAL(clicked()), signalMapper, SLOT(map()));
    connect(twentyfive, SIGNAL(clicked()), signalMapper, SLOT(map()));
    connect(fifty, SIGNAL(clicked()), signalMapper, SLOT(map()));
    connect(seventyfive, SIGNAL(clicked()), signalMapper, SLOT(map()));
    connect(hundred, SIGNAL(clicked()), signalMapper, SLOT(map()));
    connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(setSetpoint(int)));
}

CurveCalibrator::~CurveCalibrator()
{
    delete setpoints;
    delete positions;
}

void CurveCalibrator::setSetpoint(int setpoint)
{
    if (setpoint == 0 || setpoint == 4)
    {
        setpoints->replace(setpoint, static_cast<double>(logExtrema()));
    }
    else
    {
        setpoints->replace(setpoint, static_cast<double>(logAverage()));
    }

    plot->replot();

    emit setpointChanged(setpoint, static_cast<float>(setpoints->value(setpoint)));
}
<commit_msg>fixed replot of curve calibrator<commit_after>#include "CurveCalibrator.h"

CurveCalibrator::CurveCalibrator(QString titleString, QWidget *parent) :
    AbstractCalibrator(parent),
    setpoints(new QVector<double>(5)),
    positions(new QVector<double>())
{
    QGridLayout *grid = new QGridLayout(this);
    QLabel *title = new QLabel(titleString);
    grid->addWidget(title, 0, 0, 1, 5, Qt::AlignHCenter);

    QLabel *pulseWidthTitle = new QLabel(tr("Pulse Width (us)"));
    pulseWidth = new QLabel();
    QHBoxLayout *pulseLayout = new QHBoxLayout();
    pulseLayout->addWidget(pulseWidthTitle);
    pulseLayout->addWidget(pulseWidth);
    grid->addLayout(pulseLayout, 1, 0, 1, 5, Qt::AlignHCenter);

    for (int i=0; i<=100; i=i+100/4)
        positions->append(static_cast<double>(i));

    setpoints->fill(1500);

    plot = new QwtPlot();

    grid->addWidget(plot, 2, 0, 1, 5, Qt::AlignHCenter);

    plot->setAxisScale(QwtPlot::yLeft, 1000, 2000, 200);
    plot->setAxisScale(QwtPlot::xBottom, 0, 100, 25);

    curve = new QwtPlotCurve();
    curve->setPen(QPen(QColor(QString("lime"))));
    curve->setData(*positions, *setpoints);
    curve->attach(plot);

    plot->replot();

    QPushButton *zero = new QPushButton(tr("0 %"));
    QPushButton *twentyfive = new QPushButton(tr("25 %"));
    QPushButton *fifty = new QPushButton(tr("50 %"));
    QPushButton *seventyfive = new QPushButton(tr("75 %"));
    QPushButton *hundred = new QPushButton(tr("100 %"));

    grid->addWidget(zero, 3, 0);
    grid->addWidget(twentyfive, 3, 1);
    grid->addWidget(fifty, 3, 2);
    grid->addWidget(seventyfive, 3, 3);
    grid->addWidget(hundred, 3, 4);

    this->setLayout(grid);

    signalMapper = new QSignalMapper(this);
    signalMapper->setMapping(zero, 0);
    signalMapper->setMapping(twentyfive, 1);
    signalMapper->setMapping(fifty, 2);
    signalMapper->setMapping(seventyfive, 3);
    signalMapper->setMapping(hundred, 4);

    connect(zero, SIGNAL(clicked()), signalMapper, SLOT(map()));
    connect(twentyfive, SIGNAL(clicked()), signalMapper, SLOT(map()));
    connect(fifty, SIGNAL(clicked()), signalMapper, SLOT(map()));
    connect(seventyfive, SIGNAL(clicked()), signalMapper, SLOT(map()));
    connect(hundred, SIGNAL(clicked()), signalMapper, SLOT(map()));
    connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(setSetpoint(int)));
}

CurveCalibrator::~CurveCalibrator()
{
    delete setpoints;
    delete positions;
}

void CurveCalibrator::setSetpoint(int setpoint)
{
    if (setpoint == 0 || setpoint == 4)
    {
        setpoints->replace(setpoint, static_cast<double>(logExtrema()));
    }
    else
    {
        setpoints->replace(setpoint, static_cast<double>(logAverage()));
    }
    curve->setData(*positions, *setpoints);
    plot->replot();

    emit setpointChanged(setpoint, static_cast<float>(setpoints->value(setpoint)));
}
<|endoftext|>