<commit_before>#include "drawer.hpp"
#include "proto_to_styles.hpp"
#include "feature_styler.hpp"

#include "../std/bind.hpp"

#include "../indexer/drawing_rules.hpp"
#include "../indexer/scales.hpp"

#include "../graphics/defines.hpp"
#include "../graphics/screen.hpp"
#include "../graphics/resource_manager.hpp"
#include "../graphics/straight_text_element.hpp"

#ifdef OMIM_PRODUCTION
  #include "../indexer/drules_struct_lite.pb.h"
#else
  #include "../indexer/drules_struct.pb.h"
#endif

#include "../indexer/feature.hpp"

#include "../geometry/screenbase.hpp"

#include "../base/logging.hpp"
#include "../base/buffer_vector.hpp"

Drawer::Params::Params()
  : m_visualScale(1)
{
}

Drawer::Drawer(Params const & params)
  : m_visualScale(params.m_visualScale)
{
  m_pScreen.reset(new graphics::Screen(params));

  for (unsigned i = 0; i < m_pScreen->pipelinesCount(); ++i)
    m_pScreen->addClearPageFn(i, bind(&Drawer::ClearResourceCache, ThreadSlot(), i), 0);
}

namespace
{
  struct DoMakeInvalidRule
  {
    size_t m_threadSlot;
    uint32_t m_pipelineIDMask;

    DoMakeInvalidRule(size_t threadSlot, uint8_t pipelineID)
      : m_threadSlot(threadSlot), m_pipelineIDMask(pipelineID << 24)
    {}

    void operator() (int, int, int, drule::BaseRule * p)
    {
      if ((p->GetID(m_threadSlot) & 0xFF000000) == m_pipelineIDMask)
        p->MakeEmptyID(m_threadSlot);
      if ((p->GetID2(m_threadSlot) & 0xFF000000) == m_pipelineIDMask)
        p->MakeEmptyID2(m_threadSlot);
    }
  };
}

void Drawer::ClearResourceCache(size_t threadSlot, uint8_t pipelineID)
{
  drule::rules().ForEachRule(DoMakeInvalidRule(threadSlot, pipelineID));
}

void Drawer::beginFrame()
{
  m_pScreen->beginFrame();
}

void Drawer::endFrame()
{
  m_pScreen->endFrame();
}

void Drawer::clear(graphics::Color const & c, bool clearRT, float depth, bool clearDepth)
{
  m_pScreen->clear(c, clearRT, depth, clearDepth);
}

void Drawer::onSize(int w, int h)
{
  m_pScreen->onSize(w, h);
}

void Drawer::drawSymbol(m2::PointD const & pt,
                        string const & symbolName,
                        graphics::EPosition pos,
                        double depth)
{
  m_pScreen->drawSymbol(pt, symbolName, pos, depth);
}

void Drawer::drawCircle(m2::PointD const & pt,
                        graphics::EPosition pos,
                        di::DrawRule const & rule,
                        di::FeatureInfo::FeatureID const & id)
{
  graphics::Circle::Info ci;
  ConvertStyle(rule.m_rule->GetCircle(), m_visualScale, ci);

  m_pScreen->drawCircle(pt, ci, pos, rule.m_depth);
}

void Drawer::drawSymbol(m2::PointD const & pt,
                        graphics::EPosition pos,
                        di::DrawRule const & rule,
                        di::FeatureInfo::FeatureID const & id)
{
  graphics::Icon::Info info;
  ConvertStyle(rule.m_rule->GetSymbol(), info);

  graphics::SymbolElement::Params params;
  params.m_depth = rule.m_depth;
  params.m_position = pos;
  params.m_pivot = pt;
  params.m_info = info;
  params.m_renderer = m_pScreen.get();
  params.m_userInfo.m_mwmID = id.first;
  params.m_userInfo.m_offset = id.second;

  m_pScreen->drawSymbol(params);
}

void Drawer::drawPath(di::PathInfo const & path, di::DrawRule const * rules, size_t count)
{
  // if any rule needs caching - cache as a whole vector
  bool flag = false;
  for (size_t i = 0; i < count; ++i)
  {
    if (rules[i].GetID(m_pScreen->threadSlot()) == drule::BaseRule::empty_id)
    {
      flag = true;
      break;
    }
  }

  buffer_vector<graphics::Pen::Info, 8> penInfos(count);
  buffer_vector<graphics::Resource::Info const*, 8> infos(count);
  buffer_vector<uint32_t, 8> resIDs(count);

  if (flag)
  {
    // collect graphics::PenInfo into array and pack them as a whole
    for (size_t i = 0; i < count; ++i)
    {
      ConvertStyle(rules[i].m_rule->GetLine(), m_visualScale, penInfos[i]);

      infos[i] = &penInfos[i];

      if (rules[i].m_transparent)
        penInfos[i].m_color.a = 100;

      resIDs[i] = m_pScreen->invalidHandle();
    }

    // map array of pens
    if (m_pScreen->mapInfo(&infos[0], &resIDs[0], count))
    {
      for (size_t i = 0; i < count; ++i)
        rules[i].SetID(ThreadSlot(), resIDs[i]);
    }
    else
    {
      LOG(LERROR, ("couldn't successfully pack a sequence of path styles as a whole"));
      return;
    }
  }

  // draw path with array of rules
  for (size_t i = 0; i < count; ++i)
    m_pScreen->drawPath(&path.m_path[0],
                        path.m_path.size(),
                        -path.GetOffset(),
                        rules[i].GetID(ThreadSlot()),
                        rules[i].m_depth);
}

void Drawer::drawArea(di::AreaInfo const & area, di::DrawRule const & rule)
{
  // DO NOT cache 'id' in pRule, because one rule can use in drawPath and drawArea.
  // Leave CBaseRule::m_id for drawPath. mapColor working fast enough.

  graphics::Brush::Info info;
  ConvertStyle(rule.m_rule->GetArea(), info);

  uint32_t const id = m_pScreen->mapInfo(info);
  ASSERT ( id != -1, () );

  m_pScreen->drawTrianglesList(&area.m_path[0], area.m_path.size(), id, rule.m_depth);
}

void Drawer::drawText(m2::PointD const & pt,
                      graphics::EPosition pos,
                      di::FeatureStyler const & fs,
                      di::DrawRule const & rule,
                      di::FeatureInfo::FeatureID const & id)
{
  graphics::FontDesc primaryFont;
  m2::PointD primaryOffset;
  ConvertStyle(rule.m_rule->GetCaption(0), m_visualScale, primaryFont, primaryOffset);
  primaryFont.SetRank(fs.m_popRank);

  graphics::FontDesc secondaryFont;
  m2::PointD secondaryOffset;
  if (rule.m_rule->GetCaption(1))
  {
    ConvertStyle(rule.m_rule->GetCaption(1), m_visualScale, secondaryFont, secondaryOffset);
    secondaryFont.SetRank(fs.m_popRank);
  }

  graphics::StraightTextElement::Params params;
  params.m_depth = rule.m_depth;
  params.m_fontDesc = primaryFont;
  params.m_auxFontDesc = secondaryFont;
  params.m_offset = primaryOffset;
  params.m_log2vis = true;
  params.m_pivot = pt;
  params.m_position = pos;
  params.m_logText = strings::MakeUniString(fs.m_primaryText);
  params.m_auxLogText = strings::MakeUniString(fs.m_secondaryText);
  params.m_doSplit = true;
  params.m_useAllParts = false;
  params.m_userInfo.m_mwmID = id.first;
  params.m_userInfo.m_offset = id.second;

  m_pScreen->drawTextEx(params);
}

void Drawer::drawPathText(di::PathInfo const & path,
                          di::FeatureStyler const & fs,
                          di::DrawRule const & rule)
{
  graphics::FontDesc font;
  m2::PointD offset;
  ConvertStyle(rule.m_rule->GetCaption(0), m_visualScale, font, offset);

  if (fs.m_offsets.empty())
    return;

  m_pScreen->drawPathText(font,
                          &path.m_path[0],
                          path.m_path.size(),
                          fs.GetPathName(),
                          path.GetFullLength(),
                          path.GetOffset(),
                          &fs.m_offsets[0],
                          fs.m_offsets.size(),
                          rule.m_depth);
}

void Drawer::drawPathNumber(di::PathInfo const & path,
                            di::FeatureStyler const & fs)
{
  int const textHeight = static_cast<int>(12 * m_visualScale);
  m2::PointD pt;
  double const length = path.GetFullLength();
  if (length >= (fs.m_refText.size() + 2) * textHeight)
  {
    size_t const count = size_t(length / 1000.0) + 2;

    for (size_t j = 1; j < count; ++j)
    {
      if (path.GetSmPoint(double(j) / double(count), pt))
      {
        graphics::FontDesc fontDesc(
          textHeight,
          graphics::Color(150, 75, 0, 255),   // brown
          true,
          graphics::Color(255, 255, 255, 255));

        m_pScreen->drawText(fontDesc,
                            pt,
                            graphics::EPosCenter,
                            fs.m_refText,
                            graphics::maxDepth - 10,
                            true);
      }
    }
  }
}

graphics::Screen * Drawer::screen() const
{
  return m_pScreen.get();
}

double Drawer::VisualScale() const
{
  return m_visualScale;
}

void Drawer::SetScale(int level)
{
  m_level = level;
}

void Drawer::Draw(di::FeatureInfo const & fi)
{
  di::FeatureInfo::FeatureID const & id = fi.m_id;
  buffer_vector<di::DrawRule, 8> const & rules = fi.m_styler.m_rules;
  buffer_vector<di::DrawRule, 8> pathRules;

  bool const isPath = !fi.m_pathes.empty();
  bool const isArea = !fi.m_areas.empty();

  // separating path rules from other
  for (size_t i = 0; i < rules.size(); ++i)
  {
    drule::BaseRule const * pRule = rules[i].m_rule;

    bool const hasSymbol = pRule->GetSymbol() != 0;
    bool const isCaption = pRule->GetCaption(0) != 0;

    if (!isCaption && isPath && !hasSymbol && (pRule->GetLine() != 0))
      pathRules.push_back(rules[i]);
  }

  if (!pathRules.empty())
  {
    for (list<di::PathInfo>::const_iterator i = fi.m_pathes.begin(); i != fi.m_pathes.end(); ++i)
      drawPath(*i, pathRules.data(), pathRules.size());
  }

  for (size_t i = 0; i < rules.size(); ++i)
  {
    drule::BaseRule const * pRule = rules[i].m_rule;
    double const depth = rules[i].m_depth;

    bool const isCaption = pRule->GetCaption(0) != 0;
    bool const hasSymbol = pRule->GetSymbol() != 0;
    bool const isCircle = pRule->GetCircle() != 0;

    if (!isCaption)
    {
      // draw area
      if (isArea)
      {
        bool const isFill = pRule->GetArea() != 0;
        bool const hasSym = hasSymbol && ((pRule->GetType() & drule::way) != 0);

        for (list<di::AreaInfo>::const_iterator i = fi.m_areas.begin(); i != fi.m_areas.end(); ++i)
        {
          if (isFill)
            drawArea(*i, di::DrawRule(pRule, depth, false));
          else if (hasSym)
            drawSymbol(i->GetCenter(), graphics::EPosCenter, di::DrawRule(pRule, depth, false), id);
        }
      }

      // draw point symbol
      if (!isPath && !isArea && ((pRule->GetType() & drule::node) != 0))
      {
        if (hasSymbol)
          drawSymbol(fi.m_point, graphics::EPosCenter, di::DrawRule(pRule, depth, false), id);
        else if (isCircle)
          drawCircle(fi.m_point, graphics::EPosCenter, di::DrawRule(pRule, depth, false), id);
      }
    }
    else
    {
      if (!fi.m_styler.m_primaryText.empty() && (pRule->GetCaption(0) != 0))
      {
        bool isN = ((pRule->GetType() & drule::way) != 0);

        graphics::EPosition textPosition = graphics::EPosCenter;
        if (pRule->GetCaption(0)->has_offset_y())
        {
          if (pRule->GetCaption(0)->offset_y() > 0)
            textPosition = graphics::EPosUnder;
          else
            textPosition = graphics::EPosAbove;
        }

        // draw area text
        if (isArea/* && isN*/)
        {
          for (list<di::AreaInfo>::const_iterator i = fi.m_areas.begin(); i != fi.m_areas.end(); ++i)
            drawText(i->GetCenter(), textPosition, fi.m_styler, di::DrawRule(pRule, depth, false), id);
        }

        // draw way name
        if (isPath && !isArea && isN && !fi.m_styler.FilterTextSize(pRule))
        {
          for (list<di::PathInfo>::const_iterator i = fi.m_pathes.begin(); i != fi.m_pathes.end(); ++i)
            drawPathText(*i, fi.m_styler, di::DrawRule(pRule, depth, false));
        }

        // draw point text
        isN = ((pRule->GetType() & drule::node) != 0);
        if (!isPath && !isArea && isN)
          drawText(fi.m_point, textPosition, fi.m_styler, di::DrawRule(pRule, depth, false), id);
      }
    }
  }

  // draw road numbers
  if (isPath && !fi.m_styler.m_refText.empty() && m_level >= 12)
  {
    for (list<di::PathInfo>::const_iterator i = fi.m_pathes.begin(); i != fi.m_pathes.end(); ++i)
      drawPathNumber(*i, fi.m_styler);
  }
}

int Drawer::ThreadSlot() const
{
  return m_pScreen->threadSlot();
}
<commit_msg>logging values, when "sequence could not be packed" error occurs.<commit_after>#include "drawer.hpp"
#include "proto_to_styles.hpp"
#include "feature_styler.hpp"

#include "../std/bind.hpp"

#include "../indexer/drawing_rules.hpp"
#include "../indexer/scales.hpp"

#include "../graphics/defines.hpp"
#include "../graphics/screen.hpp"
#include "../graphics/resource_manager.hpp"
#include "../graphics/straight_text_element.hpp"

#ifdef OMIM_PRODUCTION
  #include "../indexer/drules_struct_lite.pb.h"
#else
  #include "../indexer/drules_struct.pb.h"
#endif

#include "../indexer/feature.hpp"

#include "../geometry/screenbase.hpp"

#include "../base/logging.hpp"
#include "../base/buffer_vector.hpp"

Drawer::Params::Params()
  : m_visualScale(1)
{
}

Drawer::Drawer(Params const & params)
  : m_visualScale(params.m_visualScale)
{
  m_pScreen.reset(new graphics::Screen(params));

  for (unsigned i = 0; i < m_pScreen->pipelinesCount(); ++i)
    m_pScreen->addClearPageFn(i, bind(&Drawer::ClearResourceCache, ThreadSlot(), i), 0);
}

namespace
{
  struct DoMakeInvalidRule
  {
    size_t m_threadSlot;
    uint32_t m_pipelineIDMask;

    DoMakeInvalidRule(size_t threadSlot, uint8_t pipelineID)
      : m_threadSlot(threadSlot), m_pipelineIDMask(pipelineID << 24)
    {}

    void operator() (int, int, int, drule::BaseRule * p)
    {
      if ((p->GetID(m_threadSlot) & 0xFF000000) == m_pipelineIDMask)
        p->MakeEmptyID(m_threadSlot);
      if ((p->GetID2(m_threadSlot) & 0xFF000000) == m_pipelineIDMask)
        p->MakeEmptyID2(m_threadSlot);
    }
  };
}

void Drawer::ClearResourceCache(size_t threadSlot, uint8_t pipelineID)
{
  drule::rules().ForEachRule(DoMakeInvalidRule(threadSlot, pipelineID));
}

void Drawer::beginFrame()
{
  m_pScreen->beginFrame();
}

void Drawer::endFrame()
{
  m_pScreen->endFrame();
}

void Drawer::clear(graphics::Color const & c, bool clearRT, float depth, bool clearDepth)
{
  m_pScreen->clear(c, clearRT, depth, clearDepth);
}

void Drawer::onSize(int w, int h)
{
  m_pScreen->onSize(w, h);
}

void Drawer::drawSymbol(m2::PointD const & pt,
                        string const & symbolName,
                        graphics::EPosition pos,
                        double depth)
{
  m_pScreen->drawSymbol(pt, symbolName, pos, depth);
}

void Drawer::drawCircle(m2::PointD const & pt,
                        graphics::EPosition pos,
                        di::DrawRule const & rule,
                        di::FeatureInfo::FeatureID const & id)
{
  graphics::Circle::Info ci;
  ConvertStyle(rule.m_rule->GetCircle(), m_visualScale, ci);

  m_pScreen->drawCircle(pt, ci, pos, rule.m_depth);
}

void Drawer::drawSymbol(m2::PointD const & pt,
                        graphics::EPosition pos,
                        di::DrawRule const & rule,
                        di::FeatureInfo::FeatureID const & id)
{
  graphics::Icon::Info info;
  ConvertStyle(rule.m_rule->GetSymbol(), info);

  graphics::SymbolElement::Params params;
  params.m_depth = rule.m_depth;
  params.m_position = pos;
  params.m_pivot = pt;
  params.m_info = info;
  params.m_renderer = m_pScreen.get();
  params.m_userInfo.m_mwmID = id.first;
  params.m_userInfo.m_offset = id.second;

  m_pScreen->drawSymbol(params);
}

void Drawer::drawPath(di::PathInfo const & path, di::DrawRule const * rules, size_t count)
{
  // if any rule needs caching - cache as a whole vector
  bool flag = false;
  for (size_t i = 0; i < count; ++i)
  {
    if (rules[i].GetID(m_pScreen->threadSlot()) == drule::BaseRule::empty_id)
    {
      flag = true;
      break;
    }
  }

  buffer_vector<graphics::Pen::Info, 8> penInfos(count);
  buffer_vector<graphics::Resource::Info const*, 8> infos(count);
  buffer_vector<uint32_t, 8> resIDs(count);

  if (flag)
  {
    // collect graphics::PenInfo into array and pack them as a whole
    for (size_t i = 0; i < count; ++i)
    {
      ConvertStyle(rules[i].m_rule->GetLine(), m_visualScale, penInfos[i]);

      infos[i] = &penInfos[i];

      if (rules[i].m_transparent)
        penInfos[i].m_color.a = 100;

      resIDs[i] = m_pScreen->invalidHandle();
    }

    // map array of pens
    if (m_pScreen->mapInfo(&infos[0], &resIDs[0], count))
    {
      for (size_t i = 0; i < count; ++i)
        rules[i].SetID(ThreadSlot(), resIDs[i]);
    }
    else
    {
      LOG(LERROR, ("couldn't successfully pack a sequence of path styles as a whole"));
      buffer_vector<m2::PointU, 8> rects;
      for (unsigned i = 0; i < count; ++i)
        rects.push_back(infos[i]->resourceSize());
      LOG(LERROR, (rects));

      return;
    }
  }

  // draw path with array of rules
  for (size_t i = 0; i < count; ++i)
    m_pScreen->drawPath(&path.m_path[0],
                        path.m_path.size(),
                        -path.GetOffset(),
                        rules[i].GetID(ThreadSlot()),
                        rules[i].m_depth);
}

void Drawer::drawArea(di::AreaInfo const & area, di::DrawRule const & rule)
{
  // DO NOT cache 'id' in pRule, because one rule can use in drawPath and drawArea.
  // Leave CBaseRule::m_id for drawPath. mapColor working fast enough.

  graphics::Brush::Info info;
  ConvertStyle(rule.m_rule->GetArea(), info);

  uint32_t const id = m_pScreen->mapInfo(info);
  ASSERT ( id != -1, () );

  m_pScreen->drawTrianglesList(&area.m_path[0], area.m_path.size(), id, rule.m_depth);
}

void Drawer::drawText(m2::PointD const & pt,
                      graphics::EPosition pos,
                      di::FeatureStyler const & fs,
                      di::DrawRule const & rule,
                      di::FeatureInfo::FeatureID const & id)
{
  graphics::FontDesc primaryFont;
  m2::PointD primaryOffset;
  ConvertStyle(rule.m_rule->GetCaption(0), m_visualScale, primaryFont, primaryOffset);
  primaryFont.SetRank(fs.m_popRank);

  graphics::FontDesc secondaryFont;
  m2::PointD secondaryOffset;
  if (rule.m_rule->GetCaption(1))
  {
    ConvertStyle(rule.m_rule->GetCaption(1), m_visualScale, secondaryFont, secondaryOffset);
    secondaryFont.SetRank(fs.m_popRank);
  }

  graphics::StraightTextElement::Params params;
  params.m_depth = rule.m_depth;
  params.m_fontDesc = primaryFont;
  params.m_auxFontDesc = secondaryFont;
  params.m_offset = primaryOffset;
  params.m_log2vis = true;
  params.m_pivot = pt;
  params.m_position = pos;
  params.m_logText = strings::MakeUniString(fs.m_primaryText);
  params.m_auxLogText = strings::MakeUniString(fs.m_secondaryText);
  params.m_doSplit = true;
  params.m_useAllParts = false;
  params.m_userInfo.m_mwmID = id.first;
  params.m_userInfo.m_offset = id.second;

  m_pScreen->drawTextEx(params);
}

void Drawer::drawPathText(di::PathInfo const & path,
                          di::FeatureStyler const & fs,
                          di::DrawRule const & rule)
{
  graphics::FontDesc font;
  m2::PointD offset;
  ConvertStyle(rule.m_rule->GetCaption(0), m_visualScale, font, offset);

  if (fs.m_offsets.empty())
    return;

  m_pScreen->drawPathText(font,
                          &path.m_path[0],
                          path.m_path.size(),
                          fs.GetPathName(),
                          path.GetFullLength(),
                          path.GetOffset(),
                          &fs.m_offsets[0],
                          fs.m_offsets.size(),
                          rule.m_depth);
}

void Drawer::drawPathNumber(di::PathInfo const & path,
                            di::FeatureStyler const & fs)
{
  int const textHeight = static_cast<int>(12 * m_visualScale);
  m2::PointD pt;
  double const length = path.GetFullLength();
  if (length >= (fs.m_refText.size() + 2) * textHeight)
  {
    size_t const count = size_t(length / 1000.0) + 2;

    for (size_t j = 1; j < count; ++j)
    {
      if (path.GetSmPoint(double(j) / double(count), pt))
      {
        graphics::FontDesc fontDesc(
          textHeight,
          graphics::Color(150, 75, 0, 255),   // brown
          true,
          graphics::Color(255, 255, 255, 255));

        m_pScreen->drawText(fontDesc,
                            pt,
                            graphics::EPosCenter,
                            fs.m_refText,
                            graphics::maxDepth - 10,
                            true);
      }
    }
  }
}

graphics::Screen * Drawer::screen() const
{
  return m_pScreen.get();
}

double Drawer::VisualScale() const
{
  return m_visualScale;
}

void Drawer::SetScale(int level)
{
  m_level = level;
}

void Drawer::Draw(di::FeatureInfo const & fi)
{
  di::FeatureInfo::FeatureID const & id = fi.m_id;
  buffer_vector<di::DrawRule, 8> const & rules = fi.m_styler.m_rules;
  buffer_vector<di::DrawRule, 8> pathRules;

  bool const isPath = !fi.m_pathes.empty();
  bool const isArea = !fi.m_areas.empty();

  // separating path rules from other
  for (size_t i = 0; i < rules.size(); ++i)
  {
    drule::BaseRule const * pRule = rules[i].m_rule;

    bool const hasSymbol = pRule->GetSymbol() != 0;
    bool const isCaption = pRule->GetCaption(0) != 0;

    if (!isCaption && isPath && !hasSymbol && (pRule->GetLine() != 0))
      pathRules.push_back(rules[i]);
  }

  if (!pathRules.empty())
  {
    for (list<di::PathInfo>::const_iterator i = fi.m_pathes.begin(); i != fi.m_pathes.end(); ++i)
      drawPath(*i, pathRules.data(), pathRules.size());
  }

  for (size_t i = 0; i < rules.size(); ++i)
  {
    drule::BaseRule const * pRule = rules[i].m_rule;
    double const depth = rules[i].m_depth;

    bool const isCaption = pRule->GetCaption(0) != 0;
    bool const hasSymbol = pRule->GetSymbol() != 0;
    bool const isCircle = pRule->GetCircle() != 0;

    if (!isCaption)
    {
      // draw area
      if (isArea)
      {
        bool const isFill = pRule->GetArea() != 0;
        bool const hasSym = hasSymbol && ((pRule->GetType() & drule::way) != 0);

        for (list<di::AreaInfo>::const_iterator i = fi.m_areas.begin(); i != fi.m_areas.end(); ++i)
        {
          if (isFill)
            drawArea(*i, di::DrawRule(pRule, depth, false));
          else if (hasSym)
            drawSymbol(i->GetCenter(), graphics::EPosCenter, di::DrawRule(pRule, depth, false), id);
        }
      }

      // draw point symbol
      if (!isPath && !isArea && ((pRule->GetType() & drule::node) != 0))
      {
        if (hasSymbol)
          drawSymbol(fi.m_point, graphics::EPosCenter, di::DrawRule(pRule, depth, false), id);
        else if (isCircle)
          drawCircle(fi.m_point, graphics::EPosCenter, di::DrawRule(pRule, depth, false), id);
      }
    }
    else
    {
      if (!fi.m_styler.m_primaryText.empty() && (pRule->GetCaption(0) != 0))
      {
        bool isN = ((pRule->GetType() & drule::way) != 0);

        graphics::EPosition textPosition = graphics::EPosCenter;
        if (pRule->GetCaption(0)->has_offset_y())
        {
          if (pRule->GetCaption(0)->offset_y() > 0)
            textPosition = graphics::EPosUnder;
          else
            textPosition = graphics::EPosAbove;
        }

        // draw area text
        if (isArea/* && isN*/)
        {
          for (list<di::AreaInfo>::const_iterator i = fi.m_areas.begin(); i != fi.m_areas.end(); ++i)
            drawText(i->GetCenter(), textPosition, fi.m_styler, di::DrawRule(pRule, depth, false), id);
        }

        // draw way name
        if (isPath && !isArea && isN && !fi.m_styler.FilterTextSize(pRule))
        {
          for (list<di::PathInfo>::const_iterator i = fi.m_pathes.begin(); i != fi.m_pathes.end(); ++i)
            drawPathText(*i, fi.m_styler, di::DrawRule(pRule, depth, false));
        }

        // draw point text
        isN = ((pRule->GetType() & drule::node) != 0);
        if (!isPath && !isArea && isN)
          drawText(fi.m_point, textPosition, fi.m_styler, di::DrawRule(pRule, depth, false), id);
      }
    }
  }

  // draw road numbers
  if (isPath && !fi.m_styler.m_refText.empty() && m_level >= 12)
  {
    for (list<di::PathInfo>::const_iterator i = fi.m_pathes.begin(); i != fi.m_pathes.end(); ++i)
      drawPathNumber(*i, fi.m_styler);
  }
}

int Drawer::ThreadSlot() const
{
  return m_pScreen->threadSlot();
}
<|endoftext|>


<commit_before>#ifndef _MEM__LOG__ERRORS__HPP_
#define _MEM__LOG__ERRORS__HPP_

// ----------------------------------------------------------------------------
// !!! DONT EDIT MANUALLY !!!
// Generated
//    by <utils/GenErrorClasses.py>
//    from <src/mem/log/errors.errors-in>
// ----------------------------------------------------------------------------

#include "mem/log/Message.hpp"

namespace mem { namespace log {

class EntryPointNotFound : public FatalError
{
   public: inline void format() {formatMessage("Entry point not found ");formatDescription("Declare a main function (anywhere) :%NEWLINE   main () -> int ");}
};

class CannotInstantiatePointerType : public Error
{
   public: std::string _PointerTypeName;

   public: inline void sPointerTypeName(std::string PointerTypeName) {_PointerTypeName=PointerTypeName;}
   public: inline void format() {formatMessage("Pointer types cannot be instantiated ");formatDescription("Trying to instantiate pointer type %s ", _PointerTypeName.c_str());}
};

class SpecificTypeExpected : public Error
{
   public: std::string _ExpectedTypeName;
   public: std::string _TypeName;

   public: inline void sExpectedTypeName(std::string ExpectedTypeName) {_ExpectedTypeName=ExpectedTypeName;}
   public: inline void sTypeName(std::string TypeName) {_TypeName=TypeName;}
   public: inline void format() {formatMessage("Expression of type %s expected, but got %s instead ", _ExpectedTypeName.c_str(), _TypeName.c_str());}
};

class BadParameterCount : public Error
{
   public: int _ExpectedParamCount;
   public: int _ParamCount;

   public: inline void sExpectedParamCount(int ExpectedParamCount) {_ExpectedParamCount=ExpectedParamCount;}
   public: inline void sParamCount(int ParamCount) {_ParamCount=ParamCount;}
   public: inline void format() {formatMessage("Bad parameter count : expected %d, but got %d ", _ExpectedParamCount, _ParamCount);}
};

class SymbolNotFound : public Error
{
   public: std::string _ScopeName;
   public: std::string _SymbolName;

   public: inline void sScopeName(std::string ScopeName) {_ScopeName=ScopeName;}
   public: inline void sSymbolName(std::string SymbolName) {_SymbolName=SymbolName;}
   public: inline void format() {formatMessage("Symbol %s not found in %s ", _SymbolName.c_str(), _ScopeName.c_str());}
};

class VariableAlreadyDefined : public Error
{
   public: std::string _VarName;

   public: inline void sVarName(std::string VarName) {_VarName=VarName;}
   public: inline void format() {formatMessage("Variable %s already defined ", _VarName.c_str());}
};

class CallNonFunction : public Error
{
   public: std::string _ObjectTypeName;
   public: std::string _ObjectName;

   public: inline void sObjectTypeName(std::string ObjectTypeName) {_ObjectTypeName=ObjectTypeName;}
   public: inline void sObjectName(std::string ObjectName) {_ObjectName=ObjectName;}
   public: inline void format() {formatMessage("Trying to call an object (%s) that is not a function, but a %s ", _ObjectName.c_str(), _ObjectTypeName.c_str());}
};

class TypeExpected : public Error
{
   public: std::string _SymbolName;

   public: inline void sSymbolName(std::string SymbolName) {_SymbolName=SymbolName;}
   public: inline void format() {formatMessage("%s is not a type ", _SymbolName.c_str());}
};

class InvalidTypeInVarAssignment : public Error
{
   public: std::string _ExpectedTypeName;
   public: std::string _VariableName;
   public: std::string _TypeName;

   public: inline void sExpectedTypeName(std::string ExpectedTypeName) {_ExpectedTypeName=ExpectedTypeName;}
   public: inline void sVariableName(std::string VariableName) {_VariableName=VariableName;}
   public: inline void sTypeName(std::string TypeName) {_TypeName=TypeName;}
   public: inline void format() {formatMessage("Types mismatch in variable assigment ");formatDescription("Variable %s expects value of type %s, but got %s instead ", _VariableName.c_str(), _ExpectedTypeName.c_str(), _TypeName.c_str());}
};

class CircularClassDependency : public FatalError
{
   public: std::string _BaseClassName;
   public: std::string _DepClassName;

   public: inline void sBaseClassName(std::string BaseClassName) {_BaseClassName=BaseClassName;}
   public: inline void sDepClassName(std::string DepClassName) {_DepClassName=DepClassName;}
   public: inline void format() {formatMessage("Circular dependency ");formatDescription("Class %s depends on class %s and vice verso ", _BaseClassName.c_str(), _DepClassName.c_str());}
};

class UnsupportedArithmeticOperation : public FatalError
{
   public: std::string _LeftTypeName;
   public: std::string _OpName;
   public: std::string _RightTypeName;

   public: inline void sLeftTypeName(std::string LeftTypeName) {_LeftTypeName=LeftTypeName;}
   public: inline void sOpName(std::string OpName) {_OpName=OpName;}
   public: inline void sRightTypeName(std::string RightTypeName) {_RightTypeName=RightTypeName;}
   public: inline void format() {formatMessage("Unsupported arithmetic operation : %s %s %s ", _LeftTypeName.c_str(), _OpName.c_str(), _RightTypeName.c_str());}
};

} }

#endif
<commit_msg>Rm src/mem/log/errors.hpp from source control<commit_after><|endoftext|>


<commit_before>// Greatest Common Divisor & Lowest Common Multiple
ll gcd(ll a, ll b) { return b ? gcd(b, a%b) : a; }
ll lcm(ll a, ll b) { return a/gcd(a, b)*b; }

// Multiply caring overflow
ll mulmod(ll a, ll b, ll m = MOD) {
  ll r=0;
  for (a %= m; b; b>>=1, a=(a*2)%m) if (b&1) r=(r+a)%m;
  return r;
}

// Another option for mulmod is using long double
ull mulmod(ull a, ull b, ull m = MOD) {
  ull q = (ld) a * (ld) b / (ld) m;
  ull r = a * b - q * m;
  return (r + m) % m;
}

// Fast exponential
ll fexp(ll a, ll b, ll m = MOD) {
  ll r=1;
  for (a %= m; b; b>>=1, a=(a*a)%m) if (b&1) r=(r*a)%m;
  return r;
}

// Multiplicative Inverse
ll inv[N];
inv[1] = 1;
for (int i = 2; i < MOD; ++i)
  inv[i] = (MOD - (MOD/i)*inv[MOD%i]%MOD)%MOD;

// Fibonacci
// Fn = Fn-1 + Fn-2
// F0 = 0 ; F1 = 1
f[0] = 0; f[1] = 1;
for (int i = 2; i <= n; i++) f[i] = f[i-1] + f[i-2];

// Fibonacci in logarithm time
// f(2*k) = f(k)*(2*f(k + 1) - f(k))
// f(2*k + 1) = f(k)^2 + f(k + 1)^2

// Catalan
// Cn = (2n)! / ((n+1)! * n!)
// 1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, 208012, 742900, 2674440
cat[n] = (2*(2*n-1)/(n+1)) * cat[n-1]

// Stirling
// S(n, 1) = S(n, n) = 1
// S(n, k) = k*S(n-1, k) + S(n-1, k-1)

// Burnside's Lemma
// Counts the number of equivalence classes in a set.
// Let G be a group of operations acting on a set X. The number of equivalence classes given those operations |X/G| satisfies:
//
// |X/G| = 1/|G| * sum(I(g)) for each g in G
//
// Being I(g) the number of fixed points given the operation g.
<commit_msg>Update basics.cpp<commit_after>// Greatest Common Divisor & Lowest Common Multiple
ll gcd(ll a, ll b) { return b ? gcd(b, a%b) : a; }
ll lcm(ll a, ll b) { return a/gcd(a, b)*b; }

// Multiply caring overflow
ll mulmod(ll a, ll b, ll m = MOD) {
  ll r=0;
  for (a %= m; b; b>>=1, a=(a*2)%m) if (b&1) r=(r+a)%m;
  return r;
}

// Another option for mulmod is using long double
ull mulmod(ull a, ull b, ull m = MOD) {
  ull q = (ld) a * (ld) b / (ld) m;
  ull r = a * b - q * m;
  return (r + m) % m;
}

// Fast exponential
ll fexp(ll a, ll b, ll m = MOD) {
  ll r=1;
  for (a %= m; b; b>>=1, a=(a*a)%m) if (b&1) r=(r*a)%m;
  return r;
}

// Multiplicative Inverse
ll inv[N];
inv[1] = 1;
for (int i = 2; i < MOD; ++i)
  inv[i] = (MOD - (MOD/i)*inv[MOD%i]%MOD)%MOD;

// Fibonacci
// Fn = Fn-1 + Fn-2
// F0 = 0 ; F1 = 1
f[0] = 0; f[1] = 1;
for (int i = 2; i <= n; i++) f[i] = f[i-1] + f[i-2];

// Recurrence using matriz
// h[i+2] = a1*h[i+1] + a0*h[i]
// [ h[i] h[i-1] ] = [ h[1] h[0] ] * [ a1 1 ] ^ (i - 1)
//                                   [ a0 0 ]

// Fibonacci in logarithm time
// f(2*k) = f(k)*(2*f(k + 1) - f(k))
// f(2*k + 1) = f(k)^2 + f(k + 1)^2

// Catalan
// Cn = (2n)! / ((n+1)! * n!)
// 1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, 208012, 742900, 2674440
cat[n] = (2*(2*n-1)/(n+1)) * cat[n-1]

// Stirling
// S(n, 1) = S(n, n) = 1
// S(n, k) = k*S(n-1, k) + S(n-1, k-1)

// Burnside's Lemma
// Counts the number of equivalence classes in a set.
// Let G be a group of operations acting on a set X. The number of equivalence classes given those operations |X/G| satisfies:
//
// |X/G| = 1/|G| * sum(I(g)) for each g in G
//
// Being I(g) the number of fixed points given the operation g.
<|endoftext|>


<commit_before>//
// Licensed to Green Energy Corp (www.greenenergycorp.com) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  Green Enery Corp licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.
//

#include "AsyncStackManager.h"
#include "LinkChannel.h"

#include <boost/asio.hpp>
#include <boost/foreach.hpp>
#include <boost/bind.hpp>

#include <APL/TimerSourceASIO.h>
#include <APL/Exception.h>
#include <APL/Logger.h>
#include <APL/IPhysicalLayerAsync.h>
#include <APL/SuspendTimerSource.h>

#include <DNP3/MasterStack.h>
#include <DNP3/SlaveStack.h>
#include <DNP3/DeviceTemplate.h>
#include <DNP3/VtoRouter.h>
#include <DNP3/VtoConfig.h>

#include <iostream>

using namespace std;

namespace apl
{
namespace dnp
{

template <class T, class U>
static std::vector<U> GetKeys(T& arMap)
{
	std::vector<U> ret;
	for(typename T::iterator i = arMap.begin(); i != arMap.end(); ++i) {
		ret.push_back(i->first);
	}
	return ret;
}

AsyncStackManager::AsyncStackManager(Logger* apLogger) :
	Loggable(apLogger),
	mService(),
	mTimerSrc(mService.Get()),
	mSuspendTimerSource(&mTimerSrc),
	mMgr(apLogger->GetSubLogger("channels", LEV_WARNING), mService.Get()),
	mScheduler(&mTimerSrc),
	mVtoManager(apLogger->GetSubLogger("vto"), &mTimerSrc, &mMgr),
	mThread(this),
	mpInfiniteTimer(mTimerSrc.StartInfinite(boost::bind(&AsyncStackManager::NullActionForInfiniteTimer)))
{
	mThread.Start();
}

AsyncStackManager::~AsyncStackManager()
{
	/*
	 * Tell every channel to stop
	 */
	this->Shutdown();

	// if we've cleaned up correctly, canceling the infinite timer will cause the thread to stop executing
	mpInfiniteTimer->Cancel();
	LOG_BLOCK(LEV_DEBUG, "Joining on io_service thread");
	mThread.WaitForStop();
	LOG_BLOCK(LEV_DEBUG, "Join complete on io_service thread");
}

std::vector<std::string> AsyncStackManager::GetStackNames()
{
	return GetKeys<StackToChannelMap, string>(mStackNameToChannel);
}

std::vector<std::string> AsyncStackManager::GetPortNames()
{
	return GetKeys<ChannelToChannelMap, string>(mChannelNameToChannel);
}

void AsyncStackManager::AddTCPClient(const std::string& arName, PhysLayerSettings aSettings, const std::string& arAddr, boost::uint16_t aPort)
{
	mMgr.AddTCPClient(arName, aSettings, arAddr, aPort);
}

void AsyncStackManager::AddTCPServer(const std::string& arName, PhysLayerSettings aSettings, const std::string& arEndpoint, boost::uint16_t aPort)
{
	mMgr.AddTCPServer(arName, aSettings, arEndpoint, aPort);
}

void AsyncStackManager::AddSerial(const std::string& arName, PhysLayerSettings aSettings, SerialSettings aSerial)
{
	mMgr.AddSerial(arName, aSettings, aSerial);
}

ICommandAcceptor* AsyncStackManager::AddMaster( const std::string& arPortName, const std::string& arStackName, FilterLevel aLevel, IDataObserver* apPublisher,
        const MasterStackConfig& arCfg)
{
	LinkChannel* pChannel = this->GetOrCreateChannel(arPortName);
	Logger* pLogger = mpLogger->GetSubLogger(arStackName, aLevel);
	pLogger->SetVarName(arStackName);

	boost::shared_ptr<MasterStack> pMaster(new MasterStack(pLogger, &mTimerSrc, apPublisher, pChannel->GetGroup(), arCfg));
	LinkRoute route(arCfg.link.RemoteAddr, arCfg.link.LocalAddr);

	this->OnAddStack(arStackName, pMaster, pChannel, route);

	// add any vto routers we've configured
	BOOST_FOREACH(VtoRouterConfig s, arCfg.vto.mRouterConfigs) {
		this->StartVtoRouter(s.mPhysicalLayerName, arStackName, s.mSettings);
	}

	return pMaster->mMaster.GetCmdAcceptor();
}

IDataObserver* AsyncStackManager::AddSlave( const std::string& arPortName, const std::string& arStackName, FilterLevel aLevel, ICommandAcceptor* apCmdAcceptor,
        const SlaveStackConfig& arCfg)
{
	LinkChannel* pChannel = this->GetOrCreateChannel(arPortName);
	Logger* pLogger = mpLogger->GetSubLogger(arStackName, aLevel);
	pLogger->SetVarName(arStackName);

	boost::shared_ptr<SlaveStack> pSlave(new SlaveStack(pLogger, &mTimerSrc, apCmdAcceptor, arCfg));

	LinkRoute route(arCfg.link.RemoteAddr, arCfg.link.LocalAddr);
	this->OnAddStack(arStackName, pSlave, pChannel, route);

	// add any vto routers we've configured
	BOOST_FOREACH(VtoRouterConfig s, arCfg.vto.mRouterConfigs) {
		this->StartVtoRouter(s.mPhysicalLayerName, arStackName, s.mSettings);
	}

	return pSlave->mSlave.GetDataObserver();
}

void AsyncStackManager::AddVtoChannel(const std::string& arStackName,
                                      IVtoCallbacks* apCallbacks)
{
	Stack* pStack = this->GetStackByName(arStackName);
	pStack->GetVtoWriter()->AddVtoCallback(apCallbacks);
	pStack->GetVtoReader()->AddVtoChannel(apCallbacks);
}

void AsyncStackManager::RemoveVtoChannel(const std::string& arStackName, IVtoCallbacks* apCallbacks)
{
	Stack* pStack = this->GetStackByName(arStackName);
	pStack->GetVtoWriter()->RemoveVtoCallback(apCallbacks);
	pStack->GetVtoReader()->RemoveVtoChannel(apCallbacks);
}

void AsyncStackManager::StartVtoRouter(const std::string& arPortName,
                                       const std::string& arStackName, const VtoRouterSettings& arSettings)
{
	Stack* pStack = this->GetStackByName(arStackName);
	VtoRouter* pRouter = mVtoManager.StartRouter(arPortName, arSettings, pStack->GetVtoWriter());
	this->AddVtoChannel(arStackName, pRouter);
}

void AsyncStackManager::StopVtoRouter(const std::string& arStackName, boost::uint8_t aVtoChannelId)
{
	Stack* pStack = this->GetStackByName(arStackName);
	IVtoWriter* pWriter = pStack->GetVtoWriter();
	VtoRouterManager::RouterRecord rec = mVtoManager.GetRouterOnWriter(pWriter, aVtoChannelId);
	this->RemoveVtoChannel(arStackName, rec.mpRouter.get());
	mVtoManager.StopRouter(pWriter, aVtoChannelId);
}

void AsyncStackManager::StopAllRoutersOnStack(const std::string& arStackName)
{
	IVtoWriter* pWriter = this->GetVtoWriter(arStackName);
	//mVtoManager.StopAllRoutersOnWriter(pWriter);
	//TODO - figure out why this is commented out
}

IVtoWriter* AsyncStackManager::GetVtoWriter(const std::string& arStackName)
{
	return this->GetStackByName(arStackName)->GetVtoWriter();
}

// Remove a port and all associated stacks
void AsyncStackManager::RemovePort(const std::string& arPortName)
{
	LinkChannel* pChannel = this->GetChannelOrExcept(arPortName);
	vector<string> stacks = this->StacksOnChannel(arPortName);
	BOOST_FOREACH(string s, stacks) {
		this->SeverStack(pChannel, s);
	}

	this->mScheduler.ReleaseGroup(pChannel->GetGroup());

	pChannel->WaitUntilShutdown();
	mChannelNameToChannel.erase(arPortName);

	// remove the physical layer from the list
	mMgr.Remove(arPortName);
}

std::vector<std::string> AsyncStackManager::StacksOnChannel(const std::string& arPortName)
{
	std::vector<std::string> ret;
	for(StackToChannelMap::iterator i = this->mStackNameToChannel.begin(); i != mStackNameToChannel.end(); ++i) {
		if(i->second->Name() == arPortName) {
			ret.push_back(i->first);
		}
	}
	return ret;
}

void AsyncStackManager::RemoveStack(const std::string& arStackName)
{
	LinkChannel* pChannel = this->GetChannelByStackName(arStackName);
	this->SeverStack(pChannel, arStackName);
}

void AsyncStackManager::SeverStack(LinkChannel* apChannel, const std::string& arStackName)
{
	/*
	IVtoWriter* pWriter = this->GetStackByName(arStackName)->GetVtoWriter();
	std::vector<VtoRouterManager::RouterRecord> records = mVtoManager.GetAllRoutersOnWriter(pWriter);
	BOOST_FOREACH(VtoRouterManager::RouterRecord rec, records) {
		this->RemoveVtoChannel(arStackName, rec.mpRouter);
		mVtoManager.StopRouter(pWriter, rec.mVtoChannelId);
	}
	*/

	// when removing a stack, we need to pause execution
	{
		Transaction tr(&mSuspendTimerSource);
		apChannel->RemoveStackFromChannel(arStackName);
	}

	mStackNameToChannel.erase(arStackName);
	mStackNameToStack.erase(arStackName);
}

LinkChannel* AsyncStackManager::GetChannelByStackName(const std::string& arStackName)
{
	StackToChannelMap::iterator i = mStackNameToChannel.find(arStackName);
	if(i == mStackNameToChannel.end()) throw ArgumentException(LOCATION, "Unknown stack: " + arStackName);
	return i->second;
}

Stack* AsyncStackManager::GetStackByName(const std::string& arStackName)
{
	StackMap::iterator i = mStackNameToStack.find(arStackName);
	if (i == mStackNameToStack.end()) {
		throw ArgumentException(LOCATION, "Unknown stack");
	}

	return i->second.get();
}

void AsyncStackManager::Shutdown()
{
	vector<string> ports = this->GetPortNames();
	BOOST_FOREACH(string s, ports) {
		LOG_BLOCK(LEV_INFO, "Removing port: " << s);
		this->RemovePort(s);
		LOG_BLOCK(LEV_INFO, "Done removing Port: " << s);
	}
}

LinkChannel* AsyncStackManager::GetOrCreateChannel(const std::string& arName)
{
	LinkChannel* pChannel = this->GetChannelMaybeNull(arName);
	return (pChannel == NULL) ? this->CreateChannel(arName) : pChannel;
}

LinkChannel* AsyncStackManager::GetChannelOrExcept(const std::string& arName)
{
	LinkChannel* pChannel = this->GetChannelMaybeNull(arName);
	if(pChannel == NULL) throw ArgumentException(LOCATION, "Channel doesn't exist: " + arName);
	return pChannel;
}

LinkChannel* AsyncStackManager::CreateChannel(const std::string& arName)
{
	if(GetChannelMaybeNull(arName) != NULL) throw ArgumentException(LOCATION, "Channel already exists with name: " + arName);

	PhysLayerSettings s = mMgr.GetSettings(arName);
	IPhysicalLayerAsync* pPhys = mMgr.AcquireLayer(arName);
	Logger* pChannelLogger = mpLogger->GetSubLogger(arName, s.LogLevel);
	pChannelLogger->SetVarName(arName);
	AsyncTaskGroup* pGroup = mScheduler.CreateNewGroup();

	boost::shared_ptr<LinkChannel> pChannel(new LinkChannel(pChannelLogger, arName, &mTimerSrc, pPhys, pGroup, s.RetryTimeout));
	mChannelNameToChannel[arName] = pChannel;
	return pChannel.get();
}

LinkChannel* AsyncStackManager::GetChannelMaybeNull(const std::string& arName)
{
	ChannelToChannelMap::iterator i = mChannelNameToChannel.find(arName);
	return (i == mChannelNameToChannel.end()) ? NULL : i->second.get();
}

void AsyncStackManager::Run()
{
	size_t num = 0;

	do {
		try {
			num = mService.Get()->run();
		}
		catch(const std::exception& ex) {
			LOG_BLOCK(LEV_ERROR, "Unhandled exception: " << ex.what());
		}
	}
	while(num > 0);

	mService.Get()->reset();
}

void AsyncStackManager::OnAddStack(const std::string& arStackName, boost::shared_ptr<Stack> apStack, LinkChannel* apChannel, const LinkRoute& arRoute)
{

	{
		// when binding the stack to the router, we need to pause excution
		Transaction tr(&mSuspendTimerSource);
		apChannel->BindStackToChannel(arStackName, apStack.get(), arRoute);
	}

	mStackNameToChannel[arStackName] = apChannel;	// map the stack name to a LinkChannel object
	mStackNameToStack[arStackName] = apStack;		// map the stack name to a Stack object
}

}
}

/* vim: set ts=4 sw=4: */
<commit_msg>Fixed AsyncStackManager to shutdown VtoRouter(s) in correct order<commit_after>//
// Licensed to Green Energy Corp (www.greenenergycorp.com) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  Green Enery Corp licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.
//

#include "AsyncStackManager.h"
#include "LinkChannel.h"

#include <boost/asio.hpp>
#include <boost/foreach.hpp>
#include <boost/bind.hpp>

#include <APL/TimerSourceASIO.h>
#include <APL/Exception.h>
#include <APL/Logger.h>
#include <APL/IPhysicalLayerAsync.h>
#include <APL/SuspendTimerSource.h>

#include <DNP3/MasterStack.h>
#include <DNP3/SlaveStack.h>
#include <DNP3/DeviceTemplate.h>
#include <DNP3/VtoRouter.h>
#include <DNP3/VtoConfig.h>

#include <iostream>

using namespace std;

namespace apl
{
namespace dnp
{

template <class T, class U>
static std::vector<U> GetKeys(T& arMap)
{
	std::vector<U> ret;
	for(typename T::iterator i = arMap.begin(); i != arMap.end(); ++i) {
		ret.push_back(i->first);
	}
	return ret;
}

AsyncStackManager::AsyncStackManager(Logger* apLogger) :
	Loggable(apLogger),
	mService(),
	mTimerSrc(mService.Get()),
	mSuspendTimerSource(&mTimerSrc),
	mMgr(apLogger->GetSubLogger("channels", LEV_WARNING), mService.Get()),
	mScheduler(&mTimerSrc),
	mVtoManager(apLogger->GetSubLogger("vto"), &mTimerSrc, &mMgr),
	mThread(this),
	mpInfiniteTimer(mTimerSrc.StartInfinite(boost::bind(&AsyncStackManager::NullActionForInfiniteTimer)))
{
	mThread.Start();
}

AsyncStackManager::~AsyncStackManager()
{
	/*
	 * Tell every channel to stop
	 */
	this->Shutdown();

	// if we've cleaned up correctly, canceling the infinite timer will cause the thread to stop executing
	mpInfiniteTimer->Cancel();
	LOG_BLOCK(LEV_DEBUG, "Joining on io_service thread");
	mThread.WaitForStop();
	LOG_BLOCK(LEV_DEBUG, "Join complete on io_service thread");
}

std::vector<std::string> AsyncStackManager::GetStackNames()
{
	return GetKeys<StackToChannelMap, string>(mStackNameToChannel);
}

std::vector<std::string> AsyncStackManager::GetPortNames()
{
	return GetKeys<ChannelToChannelMap, string>(mChannelNameToChannel);
}

void AsyncStackManager::AddTCPClient(const std::string& arName, PhysLayerSettings aSettings, const std::string& arAddr, boost::uint16_t aPort)
{
	mMgr.AddTCPClient(arName, aSettings, arAddr, aPort);
}

void AsyncStackManager::AddTCPServer(const std::string& arName, PhysLayerSettings aSettings, const std::string& arEndpoint, boost::uint16_t aPort)
{
	mMgr.AddTCPServer(arName, aSettings, arEndpoint, aPort);
}

void AsyncStackManager::AddSerial(const std::string& arName, PhysLayerSettings aSettings, SerialSettings aSerial)
{
	mMgr.AddSerial(arName, aSettings, aSerial);
}

ICommandAcceptor* AsyncStackManager::AddMaster( const std::string& arPortName, const std::string& arStackName, FilterLevel aLevel, IDataObserver* apPublisher,
        const MasterStackConfig& arCfg)
{
	LinkChannel* pChannel = this->GetOrCreateChannel(arPortName);
	Logger* pLogger = mpLogger->GetSubLogger(arStackName, aLevel);
	pLogger->SetVarName(arStackName);

	boost::shared_ptr<MasterStack> pMaster(new MasterStack(pLogger, &mTimerSrc, apPublisher, pChannel->GetGroup(), arCfg));
	LinkRoute route(arCfg.link.RemoteAddr, arCfg.link.LocalAddr);

	this->OnAddStack(arStackName, pMaster, pChannel, route);

	// add any vto routers we've configured
	BOOST_FOREACH(VtoRouterConfig s, arCfg.vto.mRouterConfigs) {
		this->StartVtoRouter(s.mPhysicalLayerName, arStackName, s.mSettings);
	}

	return pMaster->mMaster.GetCmdAcceptor();
}

IDataObserver* AsyncStackManager::AddSlave( const std::string& arPortName, const std::string& arStackName, FilterLevel aLevel, ICommandAcceptor* apCmdAcceptor,
        const SlaveStackConfig& arCfg)
{
	LinkChannel* pChannel = this->GetOrCreateChannel(arPortName);
	Logger* pLogger = mpLogger->GetSubLogger(arStackName, aLevel);
	pLogger->SetVarName(arStackName);

	boost::shared_ptr<SlaveStack> pSlave(new SlaveStack(pLogger, &mTimerSrc, apCmdAcceptor, arCfg));

	LinkRoute route(arCfg.link.RemoteAddr, arCfg.link.LocalAddr);
	this->OnAddStack(arStackName, pSlave, pChannel, route);

	// add any vto routers we've configured
	BOOST_FOREACH(VtoRouterConfig s, arCfg.vto.mRouterConfigs) {
		this->StartVtoRouter(s.mPhysicalLayerName, arStackName, s.mSettings);
	}

	return pSlave->mSlave.GetDataObserver();
}

void AsyncStackManager::AddVtoChannel(const std::string& arStackName,
                                      IVtoCallbacks* apCallbacks)
{
	Stack* pStack = this->GetStackByName(arStackName);
	pStack->GetVtoWriter()->AddVtoCallback(apCallbacks);
	pStack->GetVtoReader()->AddVtoChannel(apCallbacks);
}

void AsyncStackManager::RemoveVtoChannel(const std::string& arStackName, IVtoCallbacks* apCallbacks)
{
	Stack* pStack = this->GetStackByName(arStackName);
	pStack->GetVtoWriter()->RemoveVtoCallback(apCallbacks);
	pStack->GetVtoReader()->RemoveVtoChannel(apCallbacks);
}

void AsyncStackManager::StartVtoRouter(const std::string& arPortName,
                                       const std::string& arStackName, const VtoRouterSettings& arSettings)
{
	Stack* pStack = this->GetStackByName(arStackName);
	VtoRouter* pRouter = mVtoManager.StartRouter(arPortName, arSettings, pStack->GetVtoWriter());
	this->AddVtoChannel(arStackName, pRouter);
}

void AsyncStackManager::StopVtoRouter(const std::string& arStackName, boost::uint8_t aVtoChannelId)
{
	Stack* pStack = this->GetStackByName(arStackName);
	IVtoWriter* pWriter = pStack->GetVtoWriter();
	VtoRouterManager::RouterRecord rec = mVtoManager.GetRouterOnWriter(pWriter, aVtoChannelId);
	this->RemoveVtoChannel(arStackName, rec.mpRouter.get());
	mVtoManager.StopRouter(pWriter, aVtoChannelId);
}

void AsyncStackManager::StopAllRoutersOnStack(const std::string& arStackName)
{
	IVtoWriter* pWriter = this->GetVtoWriter(arStackName);
	//mVtoManager.StopAllRoutersOnWriter(pWriter);
	//TODO - figure out why this is commented out
}

IVtoWriter* AsyncStackManager::GetVtoWriter(const std::string& arStackName)
{
	return this->GetStackByName(arStackName)->GetVtoWriter();
}

// Remove a port and all associated stacks
void AsyncStackManager::RemovePort(const std::string& arPortName)
{
	LinkChannel* pChannel = this->GetChannelOrExcept(arPortName);
	vector<string> stacks = this->StacksOnChannel(arPortName);
	BOOST_FOREACH(string s, stacks) {
		this->SeverStack(pChannel, s);
	}

	this->mScheduler.ReleaseGroup(pChannel->GetGroup());

	pChannel->WaitUntilShutdown();
	mChannelNameToChannel.erase(arPortName);

	// remove the physical layer from the list
	mMgr.Remove(arPortName);
}

std::vector<std::string> AsyncStackManager::StacksOnChannel(const std::string& arPortName)
{
	std::vector<std::string> ret;
	for(StackToChannelMap::iterator i = this->mStackNameToChannel.begin(); i != mStackNameToChannel.end(); ++i) {
		if(i->second->Name() == arPortName) {
			ret.push_back(i->first);
		}
	}
	return ret;
}

void AsyncStackManager::RemoveStack(const std::string& arStackName)
{
	LinkChannel* pChannel = this->GetChannelByStackName(arStackName);
	this->SeverStack(pChannel, arStackName);
}

void AsyncStackManager::SeverStack(LinkChannel* apChannel, const std::string& arStackName)
{
	// Decouple and stop the stack first, this doesn't delete it yet
	{
		Transaction tr(&mSuspendTimerSource); //need to pause execution so that this action is safe
		apChannel->RemoveStackFromChannel(arStackName);
	}

	// Now stop any associated vto routers
	IVtoWriter* pWriter = this->GetStackByName(arStackName)->GetVtoWriter();
	std::vector<VtoRouterManager::RouterRecord> records = mVtoManager.GetAllRoutersOnWriter(pWriter);
	BOOST_FOREACH(VtoRouterManager::RouterRecord rec, records) {
		this->RemoveVtoChannel(arStackName, rec.mpRouter.get());
		mVtoManager.StopRouter(pWriter, rec.mVtoChannelId); //router gets deleted here
	}

	mStackNameToChannel.erase(arStackName);
	mStackNameToStack.erase(arStackName); //erasing this will cause the shared_ptr to delete the stack
}

LinkChannel* AsyncStackManager::GetChannelByStackName(const std::string& arStackName)
{
	StackToChannelMap::iterator i = mStackNameToChannel.find(arStackName);
	if(i == mStackNameToChannel.end()) throw ArgumentException(LOCATION, "Unknown stack: " + arStackName);
	return i->second;
}

Stack* AsyncStackManager::GetStackByName(const std::string& arStackName)
{
	StackMap::iterator i = mStackNameToStack.find(arStackName);
	if (i == mStackNameToStack.end()) {
		throw ArgumentException(LOCATION, "Unknown stack");
	}

	return i->second.get();
}

void AsyncStackManager::Shutdown()
{
	vector<string> ports = this->GetPortNames();
	BOOST_FOREACH(string s, ports) {
		LOG_BLOCK(LEV_INFO, "Removing port: " << s);
		this->RemovePort(s);
		LOG_BLOCK(LEV_INFO, "Done removing Port: " << s);
	}
}

LinkChannel* AsyncStackManager::GetOrCreateChannel(const std::string& arName)
{
	LinkChannel* pChannel = this->GetChannelMaybeNull(arName);
	return (pChannel == NULL) ? this->CreateChannel(arName) : pChannel;
}

LinkChannel* AsyncStackManager::GetChannelOrExcept(const std::string& arName)
{
	LinkChannel* pChannel = this->GetChannelMaybeNull(arName);
	if(pChannel == NULL) throw ArgumentException(LOCATION, "Channel doesn't exist: " + arName);
	return pChannel;
}

LinkChannel* AsyncStackManager::CreateChannel(const std::string& arName)
{
	if(GetChannelMaybeNull(arName) != NULL) throw ArgumentException(LOCATION, "Channel already exists with name: " + arName);

	PhysLayerSettings s = mMgr.GetSettings(arName);
	IPhysicalLayerAsync* pPhys = mMgr.AcquireLayer(arName);
	Logger* pChannelLogger = mpLogger->GetSubLogger(arName, s.LogLevel);
	pChannelLogger->SetVarName(arName);
	AsyncTaskGroup* pGroup = mScheduler.CreateNewGroup();

	boost::shared_ptr<LinkChannel> pChannel(new LinkChannel(pChannelLogger, arName, &mTimerSrc, pPhys, pGroup, s.RetryTimeout));
	mChannelNameToChannel[arName] = pChannel;
	return pChannel.get();
}

LinkChannel* AsyncStackManager::GetChannelMaybeNull(const std::string& arName)
{
	ChannelToChannelMap::iterator i = mChannelNameToChannel.find(arName);
	return (i == mChannelNameToChannel.end()) ? NULL : i->second.get();
}

void AsyncStackManager::Run()
{
	size_t num = 0;

	do {
		try {
			num = mService.Get()->run();
		}
		catch(const std::exception& ex) {
			LOG_BLOCK(LEV_ERROR, "Unhandled exception: " << ex.what());
		}
	}
	while(num > 0);

	mService.Get()->reset();
}

void AsyncStackManager::OnAddStack(const std::string& arStackName, boost::shared_ptr<Stack> apStack, LinkChannel* apChannel, const LinkRoute& arRoute)
{

	{
		// when binding the stack to the router, we need to pause excution
		Transaction tr(&mSuspendTimerSource);
		apChannel->BindStackToChannel(arStackName, apStack.get(), arRoute);
	}

	mStackNameToChannel[arStackName] = apChannel;	// map the stack name to a LinkChannel object
	mStackNameToStack[arStackName] = apStack;		// map the stack name to a Stack object
}

}
}

/* vim: set ts=4 sw=4: */
<|endoftext|>


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

#include "base/command_line.h"
#include "base/message_loop.h"
#include "base/scoped_ptr.h"
#include "chrome/browser/automation/ui_controls.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/browser_window.h"
#include "chrome/browser/chrome_thread.h"
#include "chrome/browser/chromeos/cros/cros_in_process_browser_test.h"
#include "chrome/browser/chromeos/cros/mock_input_method_library.h"
#include "chrome/browser/chromeos/cros/mock_screen_lock_library.h"
#include "chrome/browser/chromeos/login/mock_authenticator.h"
#include "chrome/browser/chromeos/login/screen_locker.h"
#include "chrome/browser/chromeos/login/screen_locker_tester.h"
#include "chrome/browser/chromeos/login/user_manager.h"
#include "chrome/browser/views/browser_dialogs.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/notification_type.h"
#include "chrome/test/ui_test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "views/controls/textfield/textfield.h"
#include "views/window/window_gtk.h"

namespace {

// An object that wait for lock state and fullscreen state.
class Waiter : public NotificationObserver {
 public:
  explicit Waiter(Browser* browser)
      : browser_(browser) {
    registrar_.Add(this,
                   NotificationType::SCREEN_LOCK_STATE_CHANGED,
                   NotificationService::AllSources());
    handler_id_ = g_signal_connect(
        G_OBJECT(browser_->window()->GetNativeHandle()),
        "window-state-event",
        G_CALLBACK(OnWindowStateEventThunk),
        this);
  }

  ~Waiter() {
    g_signal_handler_disconnect(
        G_OBJECT(browser_->window()->GetNativeHandle()),
        handler_id_);
  }

  virtual void Observe(NotificationType type,
                       const NotificationSource& source,
                       const NotificationDetails& details) {
    DCHECK(type == NotificationType::SCREEN_LOCK_STATE_CHANGED);
    MessageLoop::current()->Quit();
  }

  // Wait until the two conditions are met.
  void Wait(bool locker_state, bool fullscreen) {
    scoped_ptr<chromeos::test::ScreenLockerTester>
        tester(chromeos::ScreenLocker::GetTester());
    while (tester->IsLocked() != locker_state ||
           browser_->window()->IsFullscreen() != fullscreen) {
      ui_test_utils::RunMessageLoop();
    }
    // Make sure all pending tasks are executed.
    ui_test_utils::RunAllPendingInMessageLoop();
  }

  CHROMEGTK_CALLBACK_1(Waiter, gboolean, OnWindowStateEvent,
                       GdkEventWindowState*);

 private:
  Browser* browser_;
  gulong handler_id_;
  NotificationRegistrar registrar_;

  DISALLOW_COPY_AND_ASSIGN(Waiter);
};

gboolean Waiter::OnWindowStateEvent(GtkWidget* widget,
                                    GdkEventWindowState* event) {
  MessageLoop::current()->Quit();
  return false;
}

}  // namespace

namespace chromeos {

class ScreenLockerTest : public CrosInProcessBrowserTest {
 public:
  ScreenLockerTest() : mock_screen_lock_library_(NULL),
                       mock_input_method_library_(NULL) {
  }

 protected:
  MockScreenLockLibrary *mock_screen_lock_library_;
  MockInputMethodLibrary *mock_input_method_library_;

  // Test the no password mode with different unlock scheme given by
  // |unlock| function.
  void TestNoPassword(void (unlock)(views::Widget*)) {
    EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenUnlockRequested())
        .Times(1)
        .RetiresOnSaturation();
    EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenLockCompleted())
        .Times(1)
        .RetiresOnSaturation();
    UserManager::Get()->OffTheRecordUserLoggedIn();
    ScreenLocker::Show();
    scoped_ptr<test::ScreenLockerTester> tester(ScreenLocker::GetTester());
    tester->EmulateWindowManagerReady();
    ui_test_utils::WaitForNotification(
        NotificationType::SCREEN_LOCK_STATE_CHANGED);
    EXPECT_TRUE(tester->IsLocked());
    tester->InjectMockAuthenticator("", "");

    unlock(tester->GetWidget());

    ui_test_utils::RunAllPendingInMessageLoop();
    EXPECT_TRUE(tester->IsLocked());

    // Emulate LockScreen request from PowerManager (via SessionManager).
    ScreenLocker::Hide();
    ui_test_utils::RunAllPendingInMessageLoop();
    EXPECT_FALSE(tester->IsLocked());
  }

 private:
  virtual void SetUpInProcessBrowserTestFixture() {
    cros_mock_->InitStatusAreaMocks();
    cros_mock_->InitMockScreenLockLibrary();
    mock_screen_lock_library_ = cros_mock_->mock_screen_lock_library();
    mock_input_method_library_ = cros_mock_->mock_input_method_library();
    EXPECT_CALL(*mock_screen_lock_library_, AddObserver(testing::_))
        .Times(1)
        .RetiresOnSaturation();
    EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenUnlockCompleted())
        .Times(1)
        .RetiresOnSaturation();
    // Expectations for the status are on the screen lock window.
    cros_mock_->SetStatusAreaMocksExpectations();
    // Expectations for the status area on the browser window.
    cros_mock_->SetStatusAreaMocksExpectations();
  }

  virtual void SetUpCommandLine(CommandLine* command_line) {
    command_line->AppendSwitchASCII(switches::kLoginProfile, "user");
    command_line->AppendSwitch(switches::kNoFirstRun);
  }

  DISALLOW_COPY_AND_ASSIGN(ScreenLockerTest);
};

IN_PROC_BROWSER_TEST_F(ScreenLockerTest, TestBasic) {
  EXPECT_CALL(*mock_input_method_library_, GetNumActiveInputMethods())
      .Times(1)
      .WillRepeatedly((testing::Return(0)))
      .RetiresOnSaturation();
  EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenUnlockRequested())
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenLockCompleted())
      .Times(1)
      .RetiresOnSaturation();
  UserManager::Get()->UserLoggedIn("user");
  ScreenLocker::Show();
  scoped_ptr<test::ScreenLockerTester> tester(ScreenLocker::GetTester());
  tester->EmulateWindowManagerReady();
  ui_test_utils::WaitForNotification(
      NotificationType::SCREEN_LOCK_STATE_CHANGED);

  // Test to make sure that the widget is actually appearing and is of
  // reasonable size, preventing a regression of
  // http://code.google.com/p/chromium-os/issues/detail?id=5987
  gfx::Rect lock_bounds;
  tester->GetChildWidget()->GetBounds(&lock_bounds, true);
  EXPECT_GT(lock_bounds.width(), 10);
  EXPECT_GT(lock_bounds.height(), 10);

  tester->InjectMockAuthenticator("user", "pass");
  EXPECT_TRUE(tester->IsLocked());
  tester->EnterPassword("fail");
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_TRUE(tester->IsLocked());
  tester->EnterPassword("pass");
  ui_test_utils::RunAllPendingInMessageLoop();
  // Successful authentication simply send a unlock request to PowerManager.
  EXPECT_TRUE(tester->IsLocked());

  // Emulate LockScreen request from PowerManager (via SessionManager).
  // TODO(oshima): Find out better way to handle this in mock.
  ScreenLocker::Hide();
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_FALSE(tester->IsLocked());
}

IN_PROC_BROWSER_TEST_F(ScreenLockerTest, TestFullscreenExit) {
  EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenUnlockRequested())
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenLockCompleted())
      .Times(1)
      .RetiresOnSaturation();
  scoped_ptr<test::ScreenLockerTester> tester(ScreenLocker::GetTester());
  {
    Waiter waiter(browser());
    browser()->ToggleFullscreenMode();
    waiter.Wait(false /* not locked */, true /* full screen */);
    EXPECT_TRUE(browser()->window()->IsFullscreen());
    EXPECT_FALSE(tester->IsLocked());
  }
  {
    Waiter waiter(browser());
    UserManager::Get()->UserLoggedIn("user");
    ScreenLocker::Show();
    tester->EmulateWindowManagerReady();
    waiter.Wait(true /* locked */, false /* full screen */);
    EXPECT_FALSE(browser()->window()->IsFullscreen());
    EXPECT_TRUE(tester->IsLocked());
  }
  tester->InjectMockAuthenticator("user", "pass");
  tester->EnterPassword("pass");
  ScreenLocker::Hide();
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_FALSE(tester->IsLocked());
}

void MouseMove(views::Widget* widget) {
  ui_controls::SendMouseMove(10, 10);
}

IN_PROC_BROWSER_TEST_F(ScreenLockerTest, TestNoPasswordWithMouseMove) {
  TestNoPassword(MouseMove);
}

void MouseClick(views::Widget* widget) {
  ui_controls::SendMouseClick(ui_controls::RIGHT);
}

IN_PROC_BROWSER_TEST_F(ScreenLockerTest, TestNoPasswordWithMouseClick) {
  TestNoPassword(MouseClick);
}

void KeyPress(views::Widget* widget) {
  ui_controls::SendKeyPress(GTK_WINDOW(widget->GetNativeView()),
                            app::VKEY_SPACE, false, false, false, false);
}

IN_PROC_BROWSER_TEST_F(ScreenLockerTest, TestNoPasswordWithKeyPress) {
  TestNoPassword(KeyPress);
}

IN_PROC_BROWSER_TEST_F(ScreenLockerTest, TestShowTwice) {
  EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenLockCompleted())
      .Times(2)
      .RetiresOnSaturation();

  UserManager::Get()->UserLoggedIn("user");
  ScreenLocker::Show();
  scoped_ptr<test::ScreenLockerTester> tester(ScreenLocker::GetTester());
  tester->EmulateWindowManagerReady();
  ui_test_utils::WaitForNotification(
      NotificationType::SCREEN_LOCK_STATE_CHANGED);
  EXPECT_TRUE(tester->IsLocked());

  // Calling Show again simply send LockCompleted signal.
  ScreenLocker::Show();
  EXPECT_TRUE(tester->IsLocked());

  // Close the locker to match expectations.
  ScreenLocker::Hide();
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_FALSE(tester->IsLocked());
}

}  // namespace chromeos
<commit_msg>Wait for all tasks to finish before exitting the test.<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/command_line.h"
#include "base/message_loop.h"
#include "base/scoped_ptr.h"
#include "chrome/browser/automation/ui_controls.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/browser_window.h"
#include "chrome/browser/chrome_thread.h"
#include "chrome/browser/chromeos/cros/cros_in_process_browser_test.h"
#include "chrome/browser/chromeos/cros/mock_input_method_library.h"
#include "chrome/browser/chromeos/cros/mock_screen_lock_library.h"
#include "chrome/browser/chromeos/login/mock_authenticator.h"
#include "chrome/browser/chromeos/login/screen_locker.h"
#include "chrome/browser/chromeos/login/screen_locker_tester.h"
#include "chrome/browser/chromeos/login/user_manager.h"
#include "chrome/browser/views/browser_dialogs.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/notification_type.h"
#include "chrome/test/ui_test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "views/controls/textfield/textfield.h"
#include "views/window/window_gtk.h"

namespace {

// An object that wait for lock state and fullscreen state.
class Waiter : public NotificationObserver {
 public:
  explicit Waiter(Browser* browser)
      : browser_(browser) {
    registrar_.Add(this,
                   NotificationType::SCREEN_LOCK_STATE_CHANGED,
                   NotificationService::AllSources());
    handler_id_ = g_signal_connect(
        G_OBJECT(browser_->window()->GetNativeHandle()),
        "window-state-event",
        G_CALLBACK(OnWindowStateEventThunk),
        this);
  }

  ~Waiter() {
    g_signal_handler_disconnect(
        G_OBJECT(browser_->window()->GetNativeHandle()),
        handler_id_);
  }

  virtual void Observe(NotificationType type,
                       const NotificationSource& source,
                       const NotificationDetails& details) {
    DCHECK(type == NotificationType::SCREEN_LOCK_STATE_CHANGED);
    MessageLoop::current()->Quit();
  }

  // Wait until the two conditions are met.
  void Wait(bool locker_state, bool fullscreen) {
    scoped_ptr<chromeos::test::ScreenLockerTester>
        tester(chromeos::ScreenLocker::GetTester());
    while (tester->IsLocked() != locker_state ||
           browser_->window()->IsFullscreen() != fullscreen) {
      ui_test_utils::RunMessageLoop();
    }
    // Make sure all pending tasks are executed.
    ui_test_utils::RunAllPendingInMessageLoop();
  }

  CHROMEGTK_CALLBACK_1(Waiter, gboolean, OnWindowStateEvent,
                       GdkEventWindowState*);

 private:
  Browser* browser_;
  gulong handler_id_;
  NotificationRegistrar registrar_;

  DISALLOW_COPY_AND_ASSIGN(Waiter);
};

gboolean Waiter::OnWindowStateEvent(GtkWidget* widget,
                                    GdkEventWindowState* event) {
  MessageLoop::current()->Quit();
  return false;
}

}  // namespace

namespace chromeos {

class ScreenLockerTest : public CrosInProcessBrowserTest {
 public:
  ScreenLockerTest() : mock_screen_lock_library_(NULL),
                       mock_input_method_library_(NULL) {
  }

 protected:
  MockScreenLockLibrary *mock_screen_lock_library_;
  MockInputMethodLibrary *mock_input_method_library_;

  // Test the no password mode with different unlock scheme given by
  // |unlock| function.
  void TestNoPassword(void (unlock)(views::Widget*)) {
    EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenUnlockRequested())
        .Times(1)
        .RetiresOnSaturation();
    EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenLockCompleted())
        .Times(1)
        .RetiresOnSaturation();
    UserManager::Get()->OffTheRecordUserLoggedIn();
    ScreenLocker::Show();
    scoped_ptr<test::ScreenLockerTester> tester(ScreenLocker::GetTester());
    tester->EmulateWindowManagerReady();
    ui_test_utils::WaitForNotification(
        NotificationType::SCREEN_LOCK_STATE_CHANGED);
    EXPECT_TRUE(tester->IsLocked());
    tester->InjectMockAuthenticator("", "");

    unlock(tester->GetWidget());

    ui_test_utils::RunAllPendingInMessageLoop();
    EXPECT_TRUE(tester->IsLocked());

    // Emulate LockScreen request from PowerManager (via SessionManager).
    ScreenLocker::Hide();
    ui_test_utils::RunAllPendingInMessageLoop();
    EXPECT_FALSE(tester->IsLocked());
  }

 private:
  virtual void SetUpInProcessBrowserTestFixture() {
    cros_mock_->InitStatusAreaMocks();
    cros_mock_->InitMockScreenLockLibrary();
    mock_screen_lock_library_ = cros_mock_->mock_screen_lock_library();
    mock_input_method_library_ = cros_mock_->mock_input_method_library();
    EXPECT_CALL(*mock_screen_lock_library_, AddObserver(testing::_))
        .Times(1)
        .RetiresOnSaturation();
    EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenUnlockCompleted())
        .Times(1)
        .RetiresOnSaturation();
    // Expectations for the status are on the screen lock window.
    cros_mock_->SetStatusAreaMocksExpectations();
    // Expectations for the status area on the browser window.
    cros_mock_->SetStatusAreaMocksExpectations();
  }

  virtual void SetUpCommandLine(CommandLine* command_line) {
    command_line->AppendSwitchASCII(switches::kLoginProfile, "user");
    command_line->AppendSwitch(switches::kNoFirstRun);
  }

  DISALLOW_COPY_AND_ASSIGN(ScreenLockerTest);
};

IN_PROC_BROWSER_TEST_F(ScreenLockerTest, TestBasic) {
  EXPECT_CALL(*mock_input_method_library_, GetNumActiveInputMethods())
      .Times(1)
      .WillRepeatedly((testing::Return(0)))
      .RetiresOnSaturation();
  EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenUnlockRequested())
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenLockCompleted())
      .Times(1)
      .RetiresOnSaturation();
  UserManager::Get()->UserLoggedIn("user");
  ScreenLocker::Show();
  scoped_ptr<test::ScreenLockerTester> tester(ScreenLocker::GetTester());
  tester->EmulateWindowManagerReady();
  ui_test_utils::WaitForNotification(
      NotificationType::SCREEN_LOCK_STATE_CHANGED);

  // Test to make sure that the widget is actually appearing and is of
  // reasonable size, preventing a regression of
  // http://code.google.com/p/chromium-os/issues/detail?id=5987
  gfx::Rect lock_bounds;
  tester->GetChildWidget()->GetBounds(&lock_bounds, true);
  EXPECT_GT(lock_bounds.width(), 10);
  EXPECT_GT(lock_bounds.height(), 10);

  tester->InjectMockAuthenticator("user", "pass");
  EXPECT_TRUE(tester->IsLocked());
  tester->EnterPassword("fail");
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_TRUE(tester->IsLocked());
  tester->EnterPassword("pass");
  ui_test_utils::RunAllPendingInMessageLoop();
  // Successful authentication simply send a unlock request to PowerManager.
  EXPECT_TRUE(tester->IsLocked());

  // Emulate LockScreen request from PowerManager (via SessionManager).
  // TODO(oshima): Find out better way to handle this in mock.
  ScreenLocker::Hide();
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_FALSE(tester->IsLocked());
}

IN_PROC_BROWSER_TEST_F(ScreenLockerTest, TestFullscreenExit) {
  EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenUnlockRequested())
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenLockCompleted())
      .Times(1)
      .RetiresOnSaturation();
  scoped_ptr<test::ScreenLockerTester> tester(ScreenLocker::GetTester());
  {
    Waiter waiter(browser());
    browser()->ToggleFullscreenMode();
    waiter.Wait(false /* not locked */, true /* full screen */);
    EXPECT_TRUE(browser()->window()->IsFullscreen());
    EXPECT_FALSE(tester->IsLocked());
  }
  {
    Waiter waiter(browser());
    UserManager::Get()->UserLoggedIn("user");
    ScreenLocker::Show();
    tester->EmulateWindowManagerReady();
    waiter.Wait(true /* locked */, false /* full screen */);
    EXPECT_FALSE(browser()->window()->IsFullscreen());
    EXPECT_TRUE(tester->IsLocked());
  }
  tester->InjectMockAuthenticator("user", "pass");
  tester->EnterPassword("pass");
  ui_test_utils::RunAllPendingInMessageLoop();
  ScreenLocker::Hide();
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_FALSE(tester->IsLocked());
}

void MouseMove(views::Widget* widget) {
  ui_controls::SendMouseMove(10, 10);
}

IN_PROC_BROWSER_TEST_F(ScreenLockerTest, TestNoPasswordWithMouseMove) {
  TestNoPassword(MouseMove);
}

void MouseClick(views::Widget* widget) {
  ui_controls::SendMouseClick(ui_controls::RIGHT);
}

IN_PROC_BROWSER_TEST_F(ScreenLockerTest, TestNoPasswordWithMouseClick) {
  TestNoPassword(MouseClick);
}

void KeyPress(views::Widget* widget) {
  ui_controls::SendKeyPress(GTK_WINDOW(widget->GetNativeView()),
                            app::VKEY_SPACE, false, false, false, false);
}

IN_PROC_BROWSER_TEST_F(ScreenLockerTest, TestNoPasswordWithKeyPress) {
  TestNoPassword(KeyPress);
}

IN_PROC_BROWSER_TEST_F(ScreenLockerTest, TestShowTwice) {
  EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenLockCompleted())
      .Times(2)
      .RetiresOnSaturation();

  UserManager::Get()->UserLoggedIn("user");
  ScreenLocker::Show();
  scoped_ptr<test::ScreenLockerTester> tester(ScreenLocker::GetTester());
  tester->EmulateWindowManagerReady();
  ui_test_utils::WaitForNotification(
      NotificationType::SCREEN_LOCK_STATE_CHANGED);
  EXPECT_TRUE(tester->IsLocked());

  // Calling Show again simply send LockCompleted signal.
  ScreenLocker::Show();
  EXPECT_TRUE(tester->IsLocked());

  // Close the locker to match expectations.
  ScreenLocker::Hide();
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_FALSE(tester->IsLocked());
}

}  // namespace chromeos
<|endoftext|>


<commit_before>/* mbed Microcontroller Library
 * Copyright (c) 2006-2019 ARM Limited
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "drivers/RawCan.h"

#if DEVICE_CAN

#include "platform/mbed_power_mgmt.h"

namespace mbed {
    RawCAN::RawCAN(PinName rd, PinName td): CAN(rd, td) {}

    RawCAN::RawCAN(PinName rd, PinName td, int hz): CAN(rd, td, hz) {}

    /* There are situations where the RX interrupt of CAN are cleared only by
     * CAN read operations and locks are not allowed in ISR context in mbed
     * hence this provides an unlocked read to resolve such problem without
     * any effect on the performance. This will work only in case of a single
     * thread accessing a CAN instance, multiple threads will lead to race conditions
    */
    int RawCAN::read(CANMessage &msg, int handle){
        int ret = can_read(&_can, &msg, handle);
        return ret;
    }

} // namespace
#endif //DEVICE_CAN<commit_msg>Added a newline at the end of RawCAN.cpp<commit_after>/* mbed Microcontroller Library
 * Copyright (c) 2006-2019 ARM Limited
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "drivers/RawCan.h"

#if DEVICE_CAN

#include "platform/mbed_power_mgmt.h"

namespace mbed {
    RawCAN::RawCAN(PinName rd, PinName td): CAN(rd, td) {}

    RawCAN::RawCAN(PinName rd, PinName td, int hz): CAN(rd, td, hz) {}

    /* There are situations where the RX interrupt of CAN are cleared only by
     * CAN read operations and locks are not allowed in ISR context in mbed
     * hence this provides an unlocked read to resolve such problem without
     * any effect on the performance. This will work only in case of a single
     * thread accessing a CAN instance, multiple threads will lead to race conditions
    */
    int RawCAN::read(CANMessage &msg, int handle){
        int ret = can_read(&_can, &msg, handle);
        return ret;
    }

} // namespace

#endif //DEVICE_CAN
<|endoftext|>


<commit_before>#include "stdafx.h"
#include "DeferredCreationNotice.h"
#include "CoreContext.h"

DeferredCreationNotice::DeferredCreationNotice(const char* name, std::shared_ptr<CoreContext> ctxt):
  m_name(name),
  m_ctxt(ctxt)
{
}

DeferredCreationNotice::~DeferredCreationNotice(void)
{
  // Broadcast:
  std::shared_ptr<CoreContext> parent = std::dynamic_pointer_cast<CoreContext, Autowirer>(m_ctxt->GetParentContext());
  if(!parent)
    // Short-circuit return, teardown must be underway
    return;

  // Parent context resolved, broadcast the notice:
  parent->BroadcastContextCreationNotice(m_name, m_ctxt);
}
<commit_msg>Fixed slight defect in deferred creation notice<commit_after>#include "stdafx.h"
#include "DeferredCreationNotice.h"
#include "CoreContext.h"

DeferredCreationNotice::DeferredCreationNotice(const char* name, std::shared_ptr<CoreContext> ctxt):
  m_name(name),
  m_ctxt(ctxt)
{
}

DeferredCreationNotice::~DeferredCreationNotice(void)
{
  // Broadcast:
  std::shared_ptr<CoreContext> parent = std::dynamic_pointer_cast<CoreContext, Autowirer>(m_ctxt->GetParentContext());
  if(!parent)
    // Short-circuit return, teardown must be underway
    return;
  if(m_name)
    // Parent context resolved, broadcast the notice:
    parent->BroadcastContextCreationNotice(m_name, m_ctxt);
}
<|endoftext|>


<commit_before>// Copyright 2016 Chirstopher Torres (Raven), L3nn0x
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http ://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "cmapisc.h"
#include "cmapserver.h"
#include "config.h"
#include "crosepacket.h"
#include "cli_login_req.h"
#include "isc_shutdown.h"
#include "isc_alive.h"
#include "platform_defines.h"
#include "entity_system.h"

using namespace RoseCommon;

CMapISC::CMapISC() : CRoseISC(), server_(nullptr) {
  //  socket_->set_type(iscPacket::ServerType::MAP_MASTER);
}

CMapISC::CMapISC(CMapServer* server, std::unique_ptr<Core::INetwork> _sock) : CRoseISC(std::move(_sock)), server_(server) {
  socket_[SocketType::Client]->set_type(to_underlying(Isc::ServerType::MAP_MASTER));
  socket_[SocketType::Client]->registerOnConnected(std::bind(&CMapISC::onConnected, this));
  socket_[SocketType::Client]->registerOnShutdown(std::bind(&CMapISC::onShutdown, this));
}

void CMapISC::add_maps(const std::vector<uint16_t>& maps) {
    for (auto m : maps) {
        this->maps.insert({m, {}});
    }
}

void CMapISC::register_map(uint16_t map, std::weak_ptr<EntitySystem> system) {
    maps[map] = system;
}

bool CMapISC::transfer(RoseCommon::Packet::IscTransfer&& P) {
    for (auto map : P.get_maps()) {
        if (auto it = maps.find(map); it != maps.end()) {
            if (auto ptr = it->second.lock()) {
                ptr->dispatch_packet(entt::null, RoseCommon::fetchPacket(static_cast<const uint8_t*>(P.get_blob().data())));
            }
        }
    }
    return true;
}

bool CMapISC::transfer_char(RoseCommon::Packet::IscTransferChar&& P) {
    for (const auto name : P.get_names()) {
        for (auto& [_, map] : maps) {
            if (auto ptr = map.lock()) {
                auto entity = ptr->get_entity_from_name(name);
                if (entity != entt::null) {
                    ptr->dispatch_packet(entity, RoseCommon::fetchPacket(static_cast<const uint8_t*>(P.get_blob().data())));
                }
            }
        }
    }
}

bool CMapISC::isChar() const { return socket_[SocketType::Client]->get_type() == Isc::ServerType::CHAR; }

bool CMapISC::handlePacket(uint8_t* _buffer) {
  switch (CRosePacket::type(_buffer)) {
    case ePacketType::ISC_ALIVE:
      return true;
    case ePacketType::PAKCS_LOGIN_REQ:
      return true;
    case ePacketType::ISC_SERVER_REGISTER:
      return serverRegister(Packet::IscServerRegister::create(_buffer));
    case ePacketType::ISC_TRANSFER:
      return transfer(Packet::IscTransfer::create(_buffer));
    case ePacketType::ISC_TRANSFER_CHAR:
      return transfer_char(Packet::IscTransferChar::create(_buffer));
    case ePacketType::ISC_SHUTDOWN:
      return true;
    default: {
      CRoseISC::handlePacket(_buffer);
      return false;
    }
  }
  return true;
}

bool CMapISC::serverRegister(RoseCommon::Packet::IscServerRegister&& P) {
  logger_->trace("CMapISC::serverRegister");

  //  int16_t _type = 0;
  //  _type = pMapServer.type();

  // 1 == char server
  // 2 == node server
  // 3 == map master server (This is the only type the login server will care
  // about)
  // 4 == map workers/threads

  //  iscPacket::ServerReg pServerReg;
  //  std::string name, ip;
  //  int32_t port = 0, type = 0, right = 0;

  //  if (_type == iscPacket::ServerType::NODE) {
  //    // This is a node and we need to figure out something to do with this
  //  } else if (_type == iscPacket::ServerType::MAP_MASTER) {
  //    name = pMapServer.name();
  //    ip = pMapServer.addr();
  //    port = pMapServer.port();
  //    type = pMapServer.type();
  //    right = pMapServer.accright();
  //  } else if (_type == iscPacket::ServerType::MAP_WORKER) {
  //    name = pMapServer.name();
  //    ip = pMapServer.addr();
  //    port = pMapServer.port();
  //    type = pMapServer.type();
  //    right = pMapServer.accright();
  //    this->set_type(_type);
  //  }

  logger_->info("ISC Server {} Connected: [{}, {}, {}:{}]\n", get_id(), RoseCommon::Isc::serverTypeName(P.get_serverType()), P.get_name(),
                P.get_addr(), P.get_port());
  return false;
}

void CMapISC::onConnected() {
  Core::Config& config = Core::Config::getInstance();
  {
    auto packet = Packet::CliLoginReq::create(
        config.mapServer().charPassword,
        config.mapServer().charUser);

    logger_->trace("Sending a packet on CMapISC: Header[{0}, 0x{1:x}]",
                   packet.get_size(), (uint16_t)packet.get_type());
    send(packet);
  }
  {
    auto packet = Packet::IscServerRegister::create(
        RoseCommon::Isc::ServerType::MAP_MASTER, config.mapServer().channelName, config.serverData().externalIp,
        config.mapServer().clientPort, config.mapServer().accessLevel, get_id());

    std::vector<uint16_t> m;
    m.reserve(maps.size());
    for (const auto& it : maps) {
        m.push_back(it.first);
    }
    packet.set_maps(m);

    logger_->trace("Sending a packet on CMapISC: Header[{0}, 0x{1:x}]", packet.get_size(),
                   static_cast<uint16_t>(packet.get_type()));
    send(packet);
  }

  if (socket_[SocketType::Client]->process_thread_.joinable() == false) {
    socket_[SocketType::Client]->process_thread_ = std::thread([this]() {
      while (is_active() == true && isChar() == true) {
        std::chrono::steady_clock::time_point update = Core::Time::GetTickCount();
        int64_t dt = std::chrono::duration_cast<std::chrono::milliseconds>(update - get_update_time()).count();
        if (dt > (1000 * 60) * 1)  // wait 1 minute before pinging
        {
          logger_->trace("Sending ISC_ALIVE");
          send(Packet::IscAlive());
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
      }
      return 0;
    });
  }
}

bool CMapISC::onShutdown() {
  logger_->trace("CMapISC::onDisconnected");
  bool result = true;

  if (is_active() == true) {
    if (get_type() == RoseCommon::Isc::ServerType::CHAR) {
      if (socket_[SocketType::Client]->reconnect() == true) {
        logger_->info("Reconnected to character server.");
        result = false;
      }
    } else {
      auto packet = Packet::IscShutdown::create(get_type(), get_id());
      std::lock_guard<std::mutex> lock(server_->GetISCListMutex());
      for (auto& server : server_->GetISCList()) {
        CMapISC* svr = static_cast<CMapISC*>(server.get());
        if (!svr) {
          continue;
        }
        if (svr->get_type() == RoseCommon::Isc::ServerType::CHAR) {
          svr->send(packet);
          break;
        }
      }
    }
  }
  return result;
}
<commit_msg>Forgot a return true<commit_after>// Copyright 2016 Chirstopher Torres (Raven), L3nn0x
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http ://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "cmapisc.h"
#include "cmapserver.h"
#include "config.h"
#include "crosepacket.h"
#include "cli_login_req.h"
#include "isc_shutdown.h"
#include "isc_alive.h"
#include "platform_defines.h"
#include "entity_system.h"

using namespace RoseCommon;

CMapISC::CMapISC() : CRoseISC(), server_(nullptr) {
  //  socket_->set_type(iscPacket::ServerType::MAP_MASTER);
}

CMapISC::CMapISC(CMapServer* server, std::unique_ptr<Core::INetwork> _sock) : CRoseISC(std::move(_sock)), server_(server) {
  socket_[SocketType::Client]->set_type(to_underlying(Isc::ServerType::MAP_MASTER));
  socket_[SocketType::Client]->registerOnConnected(std::bind(&CMapISC::onConnected, this));
  socket_[SocketType::Client]->registerOnShutdown(std::bind(&CMapISC::onShutdown, this));
}

void CMapISC::add_maps(const std::vector<uint16_t>& maps) {
    for (auto m : maps) {
        this->maps.insert({m, {}});
    }
}

void CMapISC::register_map(uint16_t map, std::weak_ptr<EntitySystem> system) {
    maps[map] = system;
}

bool CMapISC::transfer(RoseCommon::Packet::IscTransfer&& P) {
    for (auto map : P.get_maps()) {
        if (auto it = maps.find(map); it != maps.end()) {
            if (auto ptr = it->second.lock()) {
                ptr->dispatch_packet(entt::null, RoseCommon::fetchPacket(static_cast<const uint8_t*>(P.get_blob().data())));
            }
        }
    }
    return true;
}

bool CMapISC::transfer_char(RoseCommon::Packet::IscTransferChar&& P) {
    for (const auto name : P.get_names()) {
        for (auto& [_, map] : maps) {
            if (auto ptr = map.lock()) {
                auto entity = ptr->get_entity_from_name(name);
                if (entity != entt::null) {
                    ptr->dispatch_packet(entity, RoseCommon::fetchPacket(static_cast<const uint8_t*>(P.get_blob().data())));
                }
            }
        }
    }
    return true;
}

bool CMapISC::isChar() const { return socket_[SocketType::Client]->get_type() == Isc::ServerType::CHAR; }

bool CMapISC::handlePacket(uint8_t* _buffer) {
  switch (CRosePacket::type(_buffer)) {
    case ePacketType::ISC_ALIVE:
      return true;
    case ePacketType::PAKCS_LOGIN_REQ:
      return true;
    case ePacketType::ISC_SERVER_REGISTER:
      return serverRegister(Packet::IscServerRegister::create(_buffer));
    case ePacketType::ISC_TRANSFER:
      return transfer(Packet::IscTransfer::create(_buffer));
    case ePacketType::ISC_TRANSFER_CHAR:
      return transfer_char(Packet::IscTransferChar::create(_buffer));
    case ePacketType::ISC_SHUTDOWN:
      return true;
    default: {
      CRoseISC::handlePacket(_buffer);
      return false;
    }
  }
  return true;
}

bool CMapISC::serverRegister(RoseCommon::Packet::IscServerRegister&& P) {
  logger_->trace("CMapISC::serverRegister");

  //  int16_t _type = 0;
  //  _type = pMapServer.type();

  // 1 == char server
  // 2 == node server
  // 3 == map master server (This is the only type the login server will care
  // about)
  // 4 == map workers/threads

  //  iscPacket::ServerReg pServerReg;
  //  std::string name, ip;
  //  int32_t port = 0, type = 0, right = 0;

  //  if (_type == iscPacket::ServerType::NODE) {
  //    // This is a node and we need to figure out something to do with this
  //  } else if (_type == iscPacket::ServerType::MAP_MASTER) {
  //    name = pMapServer.name();
  //    ip = pMapServer.addr();
  //    port = pMapServer.port();
  //    type = pMapServer.type();
  //    right = pMapServer.accright();
  //  } else if (_type == iscPacket::ServerType::MAP_WORKER) {
  //    name = pMapServer.name();
  //    ip = pMapServer.addr();
  //    port = pMapServer.port();
  //    type = pMapServer.type();
  //    right = pMapServer.accright();
  //    this->set_type(_type);
  //  }

  logger_->info("ISC Server {} Connected: [{}, {}, {}:{}]\n", get_id(), RoseCommon::Isc::serverTypeName(P.get_serverType()), P.get_name(),
                P.get_addr(), P.get_port());
  return false;
}

void CMapISC::onConnected() {
  Core::Config& config = Core::Config::getInstance();
  {
    auto packet = Packet::CliLoginReq::create(
        config.mapServer().charPassword,
        config.mapServer().charUser);

    logger_->trace("Sending a packet on CMapISC: Header[{0}, 0x{1:x}]",
                   packet.get_size(), (uint16_t)packet.get_type());
    send(packet);
  }
  {
    auto packet = Packet::IscServerRegister::create(
        RoseCommon::Isc::ServerType::MAP_MASTER, config.mapServer().channelName, config.serverData().externalIp,
        config.mapServer().clientPort, config.mapServer().accessLevel, get_id());

    std::vector<uint16_t> m;
    m.reserve(maps.size());
    for (const auto& it : maps) {
        m.push_back(it.first);
    }
    packet.set_maps(m);

    logger_->trace("Sending a packet on CMapISC: Header[{0}, 0x{1:x}]", packet.get_size(),
                   static_cast<uint16_t>(packet.get_type()));
    send(packet);
  }

  if (socket_[SocketType::Client]->process_thread_.joinable() == false) {
    socket_[SocketType::Client]->process_thread_ = std::thread([this]() {
      while (is_active() == true && isChar() == true) {
        std::chrono::steady_clock::time_point update = Core::Time::GetTickCount();
        int64_t dt = std::chrono::duration_cast<std::chrono::milliseconds>(update - get_update_time()).count();
        if (dt > (1000 * 60) * 1)  // wait 1 minute before pinging
        {
          logger_->trace("Sending ISC_ALIVE");
          send(Packet::IscAlive());
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
      }
      return 0;
    });
  }
}

bool CMapISC::onShutdown() {
  logger_->trace("CMapISC::onDisconnected");
  bool result = true;

  if (is_active() == true) {
    if (get_type() == RoseCommon::Isc::ServerType::CHAR) {
      if (socket_[SocketType::Client]->reconnect() == true) {
        logger_->info("Reconnected to character server.");
        result = false;
      }
    } else {
      auto packet = Packet::IscShutdown::create(get_type(), get_id());
      std::lock_guard<std::mutex> lock(server_->GetISCListMutex());
      for (auto& server : server_->GetISCList()) {
        CMapISC* svr = static_cast<CMapISC*>(server.get());
        if (!svr) {
          continue;
        }
        if (svr->get_type() == RoseCommon::Isc::ServerType::CHAR) {
          svr->send(packet);
          break;
        }
      }
    }
  }
  return result;
}
<|endoftext|>