<commit_before>#include "selfdrive/ui/replay/replay.h"

#include <QApplication>
#include <QDebug>

#include <capnp/dynamic.h>
#include "cereal/services.h"
#include "selfdrive/common/timing.h"
#include "selfdrive/common/params.h"
#include "selfdrive/hardware/hw.h"
#include "selfdrive/ui/replay/util.h"

Replay::Replay(QString route, QStringList allow, QStringList block, SubMaster *sm_, bool dcam, bool ecam, QString data_dir, QObject *parent)
    : sm(sm_), load_dcam(dcam), load_ecam(ecam), QObject(parent) {
  std::vector<const char *> s;
  auto event_struct = capnp::Schema::from<cereal::Event>().asStruct();
  sockets_.resize(event_struct.getUnionFields().size());
  for (const auto &it : services) {
    if ((allow.empty() || allow.contains(it.name)) && !block.contains(it.name)) {
      uint16_t which = event_struct.getFieldByName(it.name).getProto().getDiscriminantValue();
      sockets_[which] = it.name;
      s.push_back(it.name);
    }
  }
  qDebug() << "services " << s;

  if (sm == nullptr) {
    pm = new PubMaster(s);
  }
  route_ = std::make_unique<Route>(route, data_dir);
  events_ = new std::vector<Event *>();

  connect(this, &Replay::seekTo, this, &Replay::doSeek);
  connect(this, &Replay::segmentChanged, this, &Replay::queueSegment);
}

Replay::~Replay() {
  qDebug() << "shutdown: in progress...";
  exit_ = updating_events_ = true;
  if (stream_thread_) {
    stream_cv_.notify_one();
    stream_thread_->quit();
    stream_thread_->wait();
  }

  delete pm;
  delete events_;
  segments_.clear();
  camera_server_.reset(nullptr);
  qDebug() << "shutdown: done";
}

bool Replay::load() {
  if (!route_->load()) {
    qDebug() << "failed to load route" << route_->name() << "from server";
    return false;
  }

  for (auto &[n, f] : route_->segments()) {
    if ((!f.rlog.isEmpty() || !f.qlog.isEmpty()) && (!f.road_cam.isEmpty() || !f.qcamera.isEmpty())) {
      segments_[n] = nullptr;
    }
  }
  if (segments_.empty()) {
    qDebug() << "no valid segments in route" << route_->name();
    return false;
  }
  qDebug() << "load route" << route_->name() << "with" << segments_.size() << "valid segments";
  return true;
}

void Replay::start(int seconds) {
  seekTo(route_->identifier().segment_id * 60 + seconds, false);
}

void Replay::updateEvents(const std::function<bool()> &lambda) {
  // set updating_events to true to force stream thread relase the lock and wait for evnets_udpated.
  updating_events_ = true;
  {
    std::unique_lock lk(stream_lock_);
    events_updated_ = lambda();
    updating_events_ = false;
  }
  stream_cv_.notify_one();
}

void Replay::doSeek(int seconds, bool relative) {
  updateEvents([&]() {
    if (relative) {
      seconds += currentSeconds();
    }
    qInfo() << "seeking to" << seconds;
    const int max_segment_number = segments_.rbegin()->first;
    cur_mono_time_ = route_start_ts_ + std::clamp(seconds, 0, (max_segment_number + 1) * 60) * 1e9;
    current_segment_ = std::min(seconds / 60, max_segment_number);
    return false;
  });
  queueSegment();
}

void Replay::pause(bool pause) {
  updateEvents([=]() {
    qDebug() << (pause ? "paused..." : "resuming");
    if (pause) {
      qInfo() << "at " << currentSeconds() << "s";
    }
    paused_ = pause;
    return true;
  });
}

void Replay::setCurrentSegment(int n) {
  if (current_segment_.exchange(n) != n) {
    emit segmentChanged();
  }
}

void Replay::segmentLoadFinished(bool success) {
  if (!success) {
    Segment *seg = qobject_cast<Segment *>(sender());
    qInfo() << "failed to load segment " << seg->seg_num << ", removing it from current replay list";
    segments_.erase(seg->seg_num);
  }
  queueSegment();
}

void Replay::queueSegment() {
  // get the current segment window
  SegmentMap::iterator begin, cur, end;
  begin = cur = end = segments_.lower_bound(current_segment_);
  if (cur != segments_.end() && cur->second == nullptr) {
    // just load one segment on starting replay or seeking
    end++;
  } else {
    for (int i = 0; i < BACKWARD_SEGS && begin != segments_.begin(); ++i) {
      --begin;
    }
    for (int i = 0; i <= FORWARD_SEGS && end != segments_.end(); ++i) {
      ++end;
    }
  }

  // load & merge segments
  for (auto it = begin; it != end; ++it) {
    auto &[n, seg] = *it;
    if (!seg) {
      seg = std::make_unique<Segment>(n, route_->at(n), load_dcam, load_ecam);
      QObject::connect(seg.get(), &Segment::loadFinished, this, &Replay::segmentLoadFinished);
      qInfo() << "loading segment" << n << "...";
    }
  }
  mergeSegments(begin, end);

  // free segments out of current semgnt window.
  for (auto it = segments_.begin(); it != begin; ++it) {
    it->second.reset(nullptr);
  }
  for (auto it = end; it != segments_.end(); ++it) {
    it->second.reset(nullptr);
  }

  // start stream thread
  bool current_segment_loaded = (cur != segments_.end() && cur->second->isLoaded());
  if (stream_thread_ == nullptr && current_segment_loaded) {
    startStream(cur->second.get());
  }

  enableHttpLogging(!current_segment_loaded);
}

void Replay::mergeSegments(const SegmentMap::iterator &begin, const SegmentMap::iterator &end) {
  // segments must be merged in sequence.
  std::vector<int> segments_need_merge;
  for (auto it = begin; it != end && it->second->isLoaded(); ++it) {
    segments_need_merge.push_back(it->first);
  }

  if (segments_need_merge != segments_merged_) {
    qDebug() << "merge segments" << segments_need_merge;
    std::vector<Event *> *new_events = new std::vector<Event *>();
    new_events->reserve(std::accumulate(segments_need_merge.begin(), segments_need_merge.end(), 0,
                                        [=](int v, int n) { return v + segments_[n]->log->events.size(); }));
    for (int n : segments_need_merge) {
      auto &e = segments_[n]->log->events;
      auto middle = new_events->insert(new_events->end(), e.begin(), e.end());
      std::inplace_merge(new_events->begin(), middle, new_events->end(), Event::lessThan());
    }

    auto prev_events = events_;
    updateEvents([&]() {
      events_ = new_events;
      segments_merged_ = segments_need_merge;
      return true;
    });
    delete prev_events;
  } else {
    updateEvents([=]() { return true; });
  }
}

void Replay::startStream(const Segment *cur_segment) {
  const auto &events = cur_segment->log->events;

  // get route start time from initData
  auto it = std::find_if(events.begin(), events.end(), [](auto e) { return e->which == cereal::Event::Which::INIT_DATA; });
  route_start_ts_ = it != events.end() ? (*it)->mono_time : events[0]->mono_time;
  cur_mono_time_ += route_start_ts_;

  // write CarParams
  it = std::find_if(events.begin(), events.end(), [](auto e) { return e->which == cereal::Event::Which::CAR_PARAMS; });
  if (it != events.end()) {
    auto bytes = (*it)->bytes();
    Params().put("CarParams", (const char *)bytes.begin(), bytes.size());
  } else {
    qInfo() << "failed to read CarParams from current segment";
  }

  // start camera server
  std::pair<int, int> camera_size[MAX_CAMERAS] = {};
  for (auto type : ALL_CAMERAS) {
    if (auto &fr = cur_segment->frames[type]) {
      camera_size[type] = {fr->width, fr->height};
    }
  }
  camera_server_ = std::make_unique<CameraServer>(camera_size);

  // start stream thread
  stream_thread_ = QThread::create(&Replay::stream, this);
  QObject::connect(stream_thread_, &QThread::finished, stream_thread_, &QThread::deleteLater);
  stream_thread_->start();
}

void Replay::publishMessage(const Event *e) {
  if (sm == nullptr) {
    auto bytes = e->bytes();
    int ret = pm->send(sockets_[e->which], (capnp::byte *)bytes.begin(), bytes.size());
    if (ret == -1) {
      qDebug() << "stop publishing" << sockets_[e->which] << "due to multiple publishers error";
      sockets_[e->which] = nullptr;
    }
  } else {
    sm->update_msgs(nanos_since_boot(), {{sockets_[e->which], e->event}});
  }
}

void Replay::publishFrame(const Event *e) {
  static const std::map<cereal::Event::Which, CameraType> cam_types{
      {cereal::Event::ROAD_ENCODE_IDX, RoadCam},
      {cereal::Event::DRIVER_ENCODE_IDX, DriverCam},
      {cereal::Event::WIDE_ROAD_ENCODE_IDX, WideRoadCam},
  };
  if ((e->which == cereal::Event::DRIVER_ENCODE_IDX && !load_dcam) || (e->which == cereal::Event::WIDE_ROAD_ENCODE_IDX && !load_ecam)) {
    return;
  }
  auto eidx = capnp::AnyStruct::Reader(e->event).getPointerSection()[0].getAs<cereal::EncodeIndex>();
  if (eidx.getType() == cereal::EncodeIndex::Type::FULL_H_E_V_C && isSegmentLoaded(eidx.getSegmentNum())) {
    CameraType cam = cam_types.at(e->which);
    camera_server_->pushFrame(cam, segments_[eidx.getSegmentNum()]->frames[cam].get(), eidx);
  }
}

void Replay::stream() {
  float last_print = 0;
  cereal::Event::Which cur_which = cereal::Event::Which::INIT_DATA;

  std::unique_lock lk(stream_lock_);

  while (true) {
    stream_cv_.wait(lk, [=]() { return exit_ || (events_updated_ && !paused_); });
    events_updated_ = false;
    if (exit_) break;

    Event cur_event(cur_which, cur_mono_time_);
    auto eit = std::upper_bound(events_->begin(), events_->end(), &cur_event, Event::lessThan());
    if (eit == events_->end()) {
      qDebug() << "waiting for events...";
      continue;
    }

    uint64_t evt_start_ts = cur_mono_time_;
    uint64_t loop_start_ts = nanos_since_boot();

    for (auto end = events_->end(); !updating_events_ && eit != end; ++eit) {
      const Event *evt = (*eit);
      cur_which = evt->which;
      cur_mono_time_ = evt->mono_time;
      const int current_ts = currentSeconds();
      if (last_print > current_ts || (current_ts - last_print) > 5.0) {
        last_print = current_ts;
        qInfo() << "at " << current_ts << "s";
      }
      setCurrentSegment(current_ts / 60);

      // migration for pandaState -> pandaStates to keep UI working for old segments
      if (cur_which == cereal::Event::Which::PANDA_STATE_D_E_P_R_E_C_A_T_E_D) {
        MessageBuilder msg;
        auto ps = msg.initEvent().initPandaStates(1);
        ps[0].setIgnitionLine(true);
        ps[0].setPandaType(cereal::PandaState::PandaType::DOS);
        pm->send(sockets_[cereal::Event::Which::PANDA_STATES], msg);
      }

      if (cur_which < sockets_.size() && sockets_[cur_which] != nullptr) {
        // keep time
        long etime = cur_mono_time_ - evt_start_ts;
        long rtime = nanos_since_boot() - loop_start_ts;
        long behind_ns = etime - rtime;
        // if behind_ns is greater than 1 second, it means that an invalid segemnt is skipped by seeking/replaying
        if (behind_ns >= 1 * 1e9) {
          // reset start times
          evt_start_ts = cur_mono_time_;
          loop_start_ts = nanos_since_boot();
        } else if (behind_ns > 0) {
          precise_nano_sleep(behind_ns);
        }

        if (evt->frame) {
          publishFrame(evt);
        } else {
          publishMessage(evt);
        }
      }
    }
    // wait for frame to be sent before unlock.(frameReader may be deleted after unlock)
    camera_server_->waitFinish();

    if (eit == events_->end() && (current_segment_ == segments_.rbegin()->first) && isSegmentLoaded(current_segment_)) {
      qInfo() << "reaches the end of route, restart from beginning";
      emit seekTo(0, false);
    }
  }
}
<commit_msg>replay: fix replay looping (#22618)<commit_after>#include "selfdrive/ui/replay/replay.h"

#include <QApplication>
#include <QDebug>

#include <capnp/dynamic.h>
#include "cereal/services.h"
#include "selfdrive/common/timing.h"
#include "selfdrive/common/params.h"
#include "selfdrive/hardware/hw.h"
#include "selfdrive/ui/replay/util.h"

Replay::Replay(QString route, QStringList allow, QStringList block, SubMaster *sm_, bool dcam, bool ecam, QString data_dir, QObject *parent)
    : sm(sm_), load_dcam(dcam), load_ecam(ecam), QObject(parent) {
  std::vector<const char *> s;
  auto event_struct = capnp::Schema::from<cereal::Event>().asStruct();
  sockets_.resize(event_struct.getUnionFields().size());
  for (const auto &it : services) {
    if ((allow.empty() || allow.contains(it.name)) && !block.contains(it.name)) {
      uint16_t which = event_struct.getFieldByName(it.name).getProto().getDiscriminantValue();
      sockets_[which] = it.name;
      s.push_back(it.name);
    }
  }
  qDebug() << "services " << s;

  if (sm == nullptr) {
    pm = new PubMaster(s);
  }
  route_ = std::make_unique<Route>(route, data_dir);
  events_ = new std::vector<Event *>();

  connect(this, &Replay::seekTo, this, &Replay::doSeek);
  connect(this, &Replay::segmentChanged, this, &Replay::queueSegment);
}

Replay::~Replay() {
  qDebug() << "shutdown: in progress...";
  exit_ = updating_events_ = true;
  if (stream_thread_) {
    stream_cv_.notify_one();
    stream_thread_->quit();
    stream_thread_->wait();
  }

  delete pm;
  delete events_;
  segments_.clear();
  camera_server_.reset(nullptr);
  qDebug() << "shutdown: done";
}

bool Replay::load() {
  if (!route_->load()) {
    qDebug() << "failed to load route" << route_->name() << "from server";
    return false;
  }

  for (auto &[n, f] : route_->segments()) {
    if ((!f.rlog.isEmpty() || !f.qlog.isEmpty()) && (!f.road_cam.isEmpty() || !f.qcamera.isEmpty())) {
      segments_[n] = nullptr;
    }
  }
  if (segments_.empty()) {
    qDebug() << "no valid segments in route" << route_->name();
    return false;
  }
  qDebug() << "load route" << route_->name() << "with" << segments_.size() << "valid segments";
  return true;
}

void Replay::start(int seconds) {
  seekTo(route_->identifier().segment_id * 60 + seconds, false);
}

void Replay::updateEvents(const std::function<bool()> &lambda) {
  // set updating_events to true to force stream thread relase the lock and wait for evnets_udpated.
  updating_events_ = true;
  {
    std::unique_lock lk(stream_lock_);
    events_updated_ = lambda();
    updating_events_ = false;
  }
  stream_cv_.notify_one();
}

void Replay::doSeek(int seconds, bool relative) {
  updateEvents([&]() {
    if (relative) {
      seconds += currentSeconds();
    }
    qInfo() << "seeking to" << seconds;
    const int max_segment_number = segments_.rbegin()->first;
    cur_mono_time_ = route_start_ts_ + std::clamp(seconds, 0, (max_segment_number + 1) * 60) * 1e9;
    current_segment_ = std::min(seconds / 60, max_segment_number);
    return false;
  });
  queueSegment();
}

void Replay::pause(bool pause) {
  updateEvents([=]() {
    qDebug() << (pause ? "paused..." : "resuming");
    if (pause) {
      qInfo() << "at " << currentSeconds() << "s";
    }
    paused_ = pause;
    return true;
  });
}

void Replay::setCurrentSegment(int n) {
  if (current_segment_.exchange(n) != n) {
    emit segmentChanged();
  }
}

void Replay::segmentLoadFinished(bool success) {
  if (!success) {
    Segment *seg = qobject_cast<Segment *>(sender());
    qInfo() << "failed to load segment " << seg->seg_num << ", removing it from current replay list";
    segments_.erase(seg->seg_num);
  }
  queueSegment();
}

void Replay::queueSegment() {
  // get the current segment window
  SegmentMap::iterator begin, cur, end;
  begin = cur = end = segments_.lower_bound(current_segment_);
  if (cur != segments_.end() && cur->second == nullptr) {
    // just load one segment on starting replay or seeking
    end++;
  } else {
    for (int i = 0; i < BACKWARD_SEGS && begin != segments_.begin(); ++i) {
      --begin;
    }
    for (int i = 0; i <= FORWARD_SEGS && end != segments_.end(); ++i) {
      ++end;
    }
  }

  // load & merge segments
  for (auto it = begin; it != end; ++it) {
    auto &[n, seg] = *it;
    if (!seg) {
      seg = std::make_unique<Segment>(n, route_->at(n), load_dcam, load_ecam);
      QObject::connect(seg.get(), &Segment::loadFinished, this, &Replay::segmentLoadFinished);
      qInfo() << "loading segment" << n << "...";
    }
  }
  mergeSegments(begin, end);

  // free segments out of current semgnt window.
  for (auto it = segments_.begin(); it != begin; ++it) {
    it->second.reset(nullptr);
  }
  for (auto it = end; it != segments_.end(); ++it) {
    it->second.reset(nullptr);
  }

  // start stream thread
  bool current_segment_loaded = (cur != segments_.end() && cur->second->isLoaded());
  if (stream_thread_ == nullptr && current_segment_loaded) {
    startStream(cur->second.get());
  }

  enableHttpLogging(!current_segment_loaded);
}

void Replay::mergeSegments(const SegmentMap::iterator &begin, const SegmentMap::iterator &end) {
  // segments must be merged in sequence.
  std::vector<int> segments_need_merge;
  for (auto it = begin; it != end && it->second->isLoaded(); ++it) {
    segments_need_merge.push_back(it->first);
  }

  if (segments_need_merge != segments_merged_) {
    qDebug() << "merge segments" << segments_need_merge;
    std::vector<Event *> *new_events = new std::vector<Event *>();
    new_events->reserve(std::accumulate(segments_need_merge.begin(), segments_need_merge.end(), 0,
                                        [=](int v, int n) { return v + segments_[n]->log->events.size(); }));
    for (int n : segments_need_merge) {
      auto &e = segments_[n]->log->events;
      auto middle = new_events->insert(new_events->end(), e.begin(), e.end());
      std::inplace_merge(new_events->begin(), middle, new_events->end(), Event::lessThan());
    }

    auto prev_events = events_;
    updateEvents([&]() {
      events_ = new_events;
      segments_merged_ = segments_need_merge;
      return true;
    });
    delete prev_events;
  } else {
    updateEvents([=]() { return true; });
  }
}

void Replay::startStream(const Segment *cur_segment) {
  const auto &events = cur_segment->log->events;

  // get route start time from initData
  auto it = std::find_if(events.begin(), events.end(), [](auto e) { return e->which == cereal::Event::Which::INIT_DATA; });
  route_start_ts_ = it != events.end() ? (*it)->mono_time : events[0]->mono_time;
  cur_mono_time_ += route_start_ts_;

  // write CarParams
  it = std::find_if(events.begin(), events.end(), [](auto e) { return e->which == cereal::Event::Which::CAR_PARAMS; });
  if (it != events.end()) {
    auto bytes = (*it)->bytes();
    Params().put("CarParams", (const char *)bytes.begin(), bytes.size());
  } else {
    qInfo() << "failed to read CarParams from current segment";
  }

  // start camera server
  std::pair<int, int> camera_size[MAX_CAMERAS] = {};
  for (auto type : ALL_CAMERAS) {
    if (auto &fr = cur_segment->frames[type]) {
      camera_size[type] = {fr->width, fr->height};
    }
  }
  camera_server_ = std::make_unique<CameraServer>(camera_size);

  // start stream thread
  stream_thread_ = QThread::create(&Replay::stream, this);
  QObject::connect(stream_thread_, &QThread::finished, stream_thread_, &QThread::deleteLater);
  stream_thread_->start();
}

void Replay::publishMessage(const Event *e) {
  if (sm == nullptr) {
    auto bytes = e->bytes();
    int ret = pm->send(sockets_[e->which], (capnp::byte *)bytes.begin(), bytes.size());
    if (ret == -1) {
      qDebug() << "stop publishing" << sockets_[e->which] << "due to multiple publishers error";
      sockets_[e->which] = nullptr;
    }
  } else {
    sm->update_msgs(nanos_since_boot(), {{sockets_[e->which], e->event}});
  }
}

void Replay::publishFrame(const Event *e) {
  static const std::map<cereal::Event::Which, CameraType> cam_types{
      {cereal::Event::ROAD_ENCODE_IDX, RoadCam},
      {cereal::Event::DRIVER_ENCODE_IDX, DriverCam},
      {cereal::Event::WIDE_ROAD_ENCODE_IDX, WideRoadCam},
  };
  if ((e->which == cereal::Event::DRIVER_ENCODE_IDX && !load_dcam) || (e->which == cereal::Event::WIDE_ROAD_ENCODE_IDX && !load_ecam)) {
    return;
  }
  auto eidx = capnp::AnyStruct::Reader(e->event).getPointerSection()[0].getAs<cereal::EncodeIndex>();
  if (eidx.getType() == cereal::EncodeIndex::Type::FULL_H_E_V_C && isSegmentLoaded(eidx.getSegmentNum())) {
    CameraType cam = cam_types.at(e->which);
    camera_server_->pushFrame(cam, segments_[eidx.getSegmentNum()]->frames[cam].get(), eidx);
  }
}

void Replay::stream() {
  float last_print = 0;
  cereal::Event::Which cur_which = cereal::Event::Which::INIT_DATA;

  std::unique_lock lk(stream_lock_);

  while (true) {
    stream_cv_.wait(lk, [=]() { return exit_ || (events_updated_ && !paused_); });
    events_updated_ = false;
    if (exit_) break;

    Event cur_event(cur_which, cur_mono_time_);
    auto eit = std::upper_bound(events_->begin(), events_->end(), &cur_event, Event::lessThan());
    if (eit == events_->end()) {
      qDebug() << "waiting for events...";
      continue;
    }

    uint64_t evt_start_ts = cur_mono_time_;
    uint64_t loop_start_ts = nanos_since_boot();

    for (auto end = events_->end(); !updating_events_ && eit != end; ++eit) {
      const Event *evt = (*eit);
      cur_which = evt->which;
      cur_mono_time_ = evt->mono_time;
      const int current_ts = currentSeconds();
      if (last_print > current_ts || (current_ts - last_print) > 5.0) {
        last_print = current_ts;
        qInfo() << "at " << current_ts << "s";
      }
      setCurrentSegment(current_ts / 60);

      // migration for pandaState -> pandaStates to keep UI working for old segments
      if (cur_which == cereal::Event::Which::PANDA_STATE_D_E_P_R_E_C_A_T_E_D) {
        MessageBuilder msg;
        auto ps = msg.initEvent().initPandaStates(1);
        ps[0].setIgnitionLine(true);
        ps[0].setPandaType(cereal::PandaState::PandaType::DOS);
        pm->send(sockets_[cereal::Event::Which::PANDA_STATES], msg);
      }

      if (cur_which < sockets_.size() && sockets_[cur_which] != nullptr) {
        // keep time
        long etime = cur_mono_time_ - evt_start_ts;
        long rtime = nanos_since_boot() - loop_start_ts;
        long behind_ns = etime - rtime;
        // if behind_ns is greater than 1 second, it means that an invalid segemnt is skipped by seeking/replaying
        if (behind_ns >= 1 * 1e9) {
          // reset start times
          evt_start_ts = cur_mono_time_;
          loop_start_ts = nanos_since_boot();
        } else if (behind_ns > 0) {
          precise_nano_sleep(behind_ns);
        }

        if (evt->frame) {
          publishFrame(evt);
        } else {
          publishMessage(evt);
        }
      }
    }
    // wait for frame to be sent before unlock.(frameReader may be deleted after unlock)
    camera_server_->waitFinish();

    if (eit == events_->end()) {
      int last_segment = segments_.rbegin()->first;
      if (current_segment_ >= last_segment && isSegmentLoaded(last_segment)) {
        qInfo() << "reaches the end of route, restart from beginning";
        emit seekTo(0, false);
      }
    }
  }
}
<|endoftext|>


<commit_before>#include "cdriver.h"

struct Apex {
	double axis_min, axis_max;	// Limits for the movement of this axis.
	double rodlength, radius;	// Length of the tie rod and the horizontal distance between the vertical position and the zero position.
	double x, y, z;		// Position of tower on the base plane, and the carriage height at zero position.
};

struct Delta_private {
	Apex apex[3];
	double angle;			// Adjust the front of the printer.
};

#define PRIVATE(s) (*reinterpret_cast <Delta_private *>(s->type_data))
#define APEX(s, a) (PRIVATE(s).apex[a])

static bool check_delta(Space *s, uint8_t a, double *target) {	// {{{
	double dx = target[0] - APEX(s, a).x;
	double dy = target[1] - APEX(s, a).y;
	double r2 = dx * dx + dy * dy;
	double amax = APEX(s, a).axis_max < APEX(s, a).rodlength ? APEX(s, a).axis_max : APEX(s, a).rodlength;
	if (r2 > amax * amax) {
		debug ("not ok 1: %f %f %f %f %f %f %f", target[0], target[1], dx, dy, r2, APEX(s, a).rodlength, APEX(s, a).axis_max);
		// target is too far away from axis.  Pull it towards axis so that it is on the edge.
		// target = axis + (target - axis) * (l - epsilon) / r.
		double factor(amax / sqrt(r2));
		target[0] = APEX(s, a).x + (target[0] - APEX(s, a).x) * factor;
		target[1] = APEX(s, a).y + (target[1] - APEX(s, a).y) * factor;
		return false;
	}
	// Inner product shows if projection is inside or outside the printable region.
	double projection = -(dx / APEX(s, a).radius * APEX(s, a).x + dy / APEX(s, a).radius * APEX(s, a).y);
	double amin = APEX(s, a).axis_min < -APEX(s, a).rodlength ? -APEX(s, a).rodlength : APEX(s, a).axis_min;
	if (projection < amin) {
		debug ("not ok 2: %f %f %f %f %f", projection, dx, dy, APEX(s, a).x, APEX(s, a).y);
		// target is on the wrong side of axis.  Pull it towards plane so it is on the edge.
		target[0] -= ((amin - projection) / APEX(s, a).radius - .001) * APEX(s, a).x;
		target[1] -= ((amin - projection) / APEX(s, a).radius - .001) * APEX(s, a).y;
		// Assume this was a small correction; that way, things will work even if numerical errors cause this to be called for the real move.
		return false;
	}
	//debug("ok %d %d %f", s->id, a, projection);
	return true;
}	// }}}

static inline double delta_to_axis(Space *s, uint8_t a, bool *ok) {
	double dx = s->axis[0]->settings.target - APEX(s, a).x;
	double dy = s->axis[1]->settings.target - APEX(s, a).y;
	double dz = s->axis[2]->settings.target - APEX(s, a).z;
	double r2 = dx * dx + dy * dy;
	double l2 = APEX(s, a).rodlength * APEX(s, a).rodlength;
	double dest = sqrt(l2 - r2) + dz;
	//debug("dta dx %f dy %f dz %f z %f, r %f target %f", dx, dy, dz, APEX(s, a).z, r, target);
	return dest;
}

static void xyz2motors(Space *s, double *motors, bool *ok) {
	if (isnan(s->axis[0]->settings.target) || isnan(s->axis[1]->settings.target) || isnan(s->axis[2]->settings.target)) {
		// Fill up missing targets.
		for (uint8_t aa = 0; aa < 3; ++aa) {
			if (isnan(s->axis[aa]->settings.target))
				s->axis[aa]->settings.target = s->axis[aa]->settings.current;
		}
	}
	for (uint8_t a = 0; a < 3; ++a) {
		if (motors)
			motors[a] = delta_to_axis(s, a, ok);
		else
			s->motor[a]->settings.endpos = delta_to_axis(s, a, ok);
	}
}

static void reset_pos (Space *s) {
	// All axes' current_pos must be valid and equal, in other words, x=y=0.
	double p[3];
	for (uint8_t i = 0; i < 3; ++i)
		p[i] = s->motor[i]->settings.current_pos / s->motor[i]->steps_per_unit;
	if (p[0] != p[1] || p[0] != p[2]) {
		//debug("resetpos fails");
		s->axis[0]->settings.source = NAN;
		s->axis[1]->settings.source = NAN;
		s->axis[2]->settings.source = NAN;
	}
	else {
		//debug("resetpos %f", p[0]);
		s->axis[0]->settings.source = 0;
		s->axis[1]->settings.source = 0;
		s->axis[2]->settings.source = p[0];
	}
}

static void check_position(Space *s, double *data) {
	if (isnan(data[0]) || isnan(data[1])) {
		// Cannot check; assume it's ok.
		return;
	}
	for (uint8_t counter = 0; counter < 2; ++counter) {
		bool ok = true;
		for (uint8_t a = 0; a < s->num_axes; ++a)
			ok &= check_delta(s, a, data);
		if (ok)
			break;
	}
}

static void load(Space *s, uint8_t old_type, int32_t &addr) {
	if (!s->setup_nums(3, 3)) {
		debug("Failed to set up delta axes");
		s->cancel_update();
		return;
	}
	for (uint8_t a = 0; a < 3; ++a) {
		APEX(s, a).axis_min = read_float(addr);
		APEX(s, a).axis_max = read_float(addr);
		APEX(s, a).rodlength = read_float(addr);
		APEX(s, a).radius = read_float(addr);
	}
	PRIVATE(s).angle = read_float(addr);
	if (isinf(PRIVATE(s).angle) || isnan(PRIVATE(s).angle))
		PRIVATE(s).angle = 0;
#define sin210 -.5
#define cos210 -0.8660254037844386	// .5*sqrt(3)
#define sin330 -.5
#define cos330 0.8660254037844386	// .5*sqrt(3)
#define sin90 1
	// Coordinates of axes (at angles 210, 330, 90; each with its own radius).
	double x[3], y[3];
	x[0] = APEX(s, 0).radius * cos210;
	y[0] = APEX(s, 0).radius * sin210;
	x[1] = APEX(s, 1).radius * cos330;
	y[1] = APEX(s, 1).radius * sin330;
	x[2] = 0;
	y[2] = APEX(s, 2).radius * sin90;
	for (uint8_t a = 0; a < 3; ++a) {
		APEX(s, a).x = x[a] * cos(PRIVATE(s).angle) - y[a] * sin(PRIVATE(s).angle);
		APEX(s, a).y = y[a] * cos(PRIVATE(s).angle) + x[a] * sin(PRIVATE(s).angle);
		APEX(s, a).z = sqrt(APEX(s, a).rodlength * APEX(s, a).rodlength - APEX(s, a).radius * APEX(s, a).radius);
	}
}

static void save(Space *s, int32_t &addr) {
	for (uint8_t a = 0; a < 3; ++a) {
		write_float(addr, APEX(s, a).axis_min);
		write_float(addr, APEX(s, a).axis_max);
		write_float(addr, APEX(s, a).rodlength);
		write_float(addr, APEX(s, a).radius);
	}
	write_float(addr, PRIVATE(s).angle);
}

static bool init(Space *s) {
	s->type_data = new Delta_private;
	if (!s->type_data)
		return false;
	return true;
}

static void free(Space *s) {
	delete reinterpret_cast <Delta_private *>(s->type_data);
}

static double change0(Space *s, int axis, double value) {
	return value;
}

static double unchange0(Space *s, int axis, double value) {
	return value;
}

static double probe_speed(Space *s) {
	double max_spu = 0;
	for (int i = 0; i < s->num_motors; ++i)
		if (max_spu < s->motor[i]->steps_per_unit)
			max_spu = s->motor[i]->steps_per_unit;
	return 1e6 / hwtime_step / max_spu;
}

void Delta_init(int num) {
	space_types[num].xyz2motors = xyz2motors;
	space_types[num].reset_pos = reset_pos;
	space_types[num].check_position = check_position;
	space_types[num].load = load;
	space_types[num].save = save;
	space_types[num].init = init;
	space_types[num].free = free;
	space_types[num].change0 = change0;
	space_types[num].unchange0 = unchange0;
	space_types[num].probe_speed = probe_speed;
}
<commit_msg>add delta::afree<commit_after>#include "cdriver.h"

struct Apex {
	double axis_min, axis_max;	// Limits for the movement of this axis.
	double rodlength, radius;	// Length of the tie rod and the horizontal distance between the vertical position and the zero position.
	double x, y, z;		// Position of tower on the base plane, and the carriage height at zero position.
};

struct Delta_private {
	Apex apex[3];
	double angle;			// Adjust the front of the printer.
};

#define PRIVATE(s) (*reinterpret_cast <Delta_private *>(s->type_data))
#define APEX(s, a) (PRIVATE(s).apex[a])

static bool check_delta(Space *s, uint8_t a, double *target) {	// {{{
	double dx = target[0] - APEX(s, a).x;
	double dy = target[1] - APEX(s, a).y;
	double r2 = dx * dx + dy * dy;
	double amax = APEX(s, a).axis_max < APEX(s, a).rodlength ? APEX(s, a).axis_max : APEX(s, a).rodlength;
	if (r2 > amax * amax) {
		debug ("not ok 1: %f %f %f %f %f %f %f", target[0], target[1], dx, dy, r2, APEX(s, a).rodlength, APEX(s, a).axis_max);
		// target is too far away from axis.  Pull it towards axis so that it is on the edge.
		// target = axis + (target - axis) * (l - epsilon) / r.
		double factor(amax / sqrt(r2));
		target[0] = APEX(s, a).x + (target[0] - APEX(s, a).x) * factor;
		target[1] = APEX(s, a).y + (target[1] - APEX(s, a).y) * factor;
		return false;
	}
	// Inner product shows if projection is inside or outside the printable region.
	double projection = -(dx / APEX(s, a).radius * APEX(s, a).x + dy / APEX(s, a).radius * APEX(s, a).y);
	double amin = APEX(s, a).axis_min < -APEX(s, a).rodlength ? -APEX(s, a).rodlength : APEX(s, a).axis_min;
	if (projection < amin) {
		debug ("not ok 2: %f %f %f %f %f", projection, dx, dy, APEX(s, a).x, APEX(s, a).y);
		// target is on the wrong side of axis.  Pull it towards plane so it is on the edge.
		target[0] -= ((amin - projection) / APEX(s, a).radius - .001) * APEX(s, a).x;
		target[1] -= ((amin - projection) / APEX(s, a).radius - .001) * APEX(s, a).y;
		// Assume this was a small correction; that way, things will work even if numerical errors cause this to be called for the real move.
		return false;
	}
	//debug("ok %d %d %f", s->id, a, projection);
	return true;
}	// }}}

static inline double delta_to_axis(Space *s, uint8_t a, bool *ok) {
	double dx = s->axis[0]->settings.target - APEX(s, a).x;
	double dy = s->axis[1]->settings.target - APEX(s, a).y;
	double dz = s->axis[2]->settings.target - APEX(s, a).z;
	double r2 = dx * dx + dy * dy;
	double l2 = APEX(s, a).rodlength * APEX(s, a).rodlength;
	double dest = sqrt(l2 - r2) + dz;
	//debug("dta dx %f dy %f dz %f z %f, r %f target %f", dx, dy, dz, APEX(s, a).z, r, target);
	return dest;
}

static void xyz2motors(Space *s, double *motors, bool *ok) {
	if (isnan(s->axis[0]->settings.target) || isnan(s->axis[1]->settings.target) || isnan(s->axis[2]->settings.target)) {
		// Fill up missing targets.
		for (uint8_t aa = 0; aa < 3; ++aa) {
			if (isnan(s->axis[aa]->settings.target))
				s->axis[aa]->settings.target = s->axis[aa]->settings.current;
		}
	}
	for (uint8_t a = 0; a < 3; ++a) {
		if (motors)
			motors[a] = delta_to_axis(s, a, ok);
		else
			s->motor[a]->settings.endpos = delta_to_axis(s, a, ok);
	}
}

static void reset_pos (Space *s) {
	// All axes' current_pos must be valid and equal, in other words, x=y=0.
	double p[3];
	for (uint8_t i = 0; i < 3; ++i)
		p[i] = s->motor[i]->settings.current_pos / s->motor[i]->steps_per_unit;
	if (p[0] != p[1] || p[0] != p[2]) {
		//debug("resetpos fails");
		s->axis[0]->settings.source = NAN;
		s->axis[1]->settings.source = NAN;
		s->axis[2]->settings.source = NAN;
	}
	else {
		//debug("resetpos %f", p[0]);
		s->axis[0]->settings.source = 0;
		s->axis[1]->settings.source = 0;
		s->axis[2]->settings.source = p[0];
	}
}

static void check_position(Space *s, double *data) {
	if (isnan(data[0]) || isnan(data[1])) {
		// Cannot check; assume it's ok.
		return;
	}
	for (uint8_t counter = 0; counter < 2; ++counter) {
		bool ok = true;
		for (uint8_t a = 0; a < s->num_axes; ++a)
			ok &= check_delta(s, a, data);
		if (ok)
			break;
	}
}

static void load(Space *s, uint8_t old_type, int32_t &addr) {
	if (!s->setup_nums(3, 3)) {
		debug("Failed to set up delta axes");
		s->cancel_update();
		return;
	}
	for (uint8_t a = 0; a < 3; ++a) {
		APEX(s, a).axis_min = read_float(addr);
		APEX(s, a).axis_max = read_float(addr);
		APEX(s, a).rodlength = read_float(addr);
		APEX(s, a).radius = read_float(addr);
	}
	PRIVATE(s).angle = read_float(addr);
	if (isinf(PRIVATE(s).angle) || isnan(PRIVATE(s).angle))
		PRIVATE(s).angle = 0;
#define sin210 -.5
#define cos210 -0.8660254037844386	// .5*sqrt(3)
#define sin330 -.5
#define cos330 0.8660254037844386	// .5*sqrt(3)
#define sin90 1
	// Coordinates of axes (at angles 210, 330, 90; each with its own radius).
	double x[3], y[3];
	x[0] = APEX(s, 0).radius * cos210;
	y[0] = APEX(s, 0).radius * sin210;
	x[1] = APEX(s, 1).radius * cos330;
	y[1] = APEX(s, 1).radius * sin330;
	x[2] = 0;
	y[2] = APEX(s, 2).radius * sin90;
	for (uint8_t a = 0; a < 3; ++a) {
		APEX(s, a).x = x[a] * cos(PRIVATE(s).angle) - y[a] * sin(PRIVATE(s).angle);
		APEX(s, a).y = y[a] * cos(PRIVATE(s).angle) + x[a] * sin(PRIVATE(s).angle);
		APEX(s, a).z = sqrt(APEX(s, a).rodlength * APEX(s, a).rodlength - APEX(s, a).radius * APEX(s, a).radius);
	}
}

static void save(Space *s, int32_t &addr) {
	for (uint8_t a = 0; a < 3; ++a) {
		write_float(addr, APEX(s, a).axis_min);
		write_float(addr, APEX(s, a).axis_max);
		write_float(addr, APEX(s, a).rodlength);
		write_float(addr, APEX(s, a).radius);
	}
	write_float(addr, PRIVATE(s).angle);
}

static bool init(Space *s) {
	s->type_data = new Delta_private;
	if (!s->type_data)
		return false;
	return true;
}

static void free(Space *s) {
	delete reinterpret_cast <Delta_private *>(s->type_data);
}

static void afree(Space *s, int a) {
}

static double change0(Space *s, int axis, double value) {
	return value;
}

static double unchange0(Space *s, int axis, double value) {
	return value;
}

static double probe_speed(Space *s) {
	double max_spu = 0;
	for (int i = 0; i < s->num_motors; ++i)
		if (max_spu < s->motor[i]->steps_per_unit)
			max_spu = s->motor[i]->steps_per_unit;
	return 1e6 / hwtime_step / max_spu;
}

void Delta_init(int num) {
	space_types[num].xyz2motors = xyz2motors;
	space_types[num].reset_pos = reset_pos;
	space_types[num].check_position = check_position;
	space_types[num].load = load;
	space_types[num].save = save;
	space_types[num].init = init;
	space_types[num].free = free;
	space_types[num].afree = afree;
	space_types[num].change0 = change0;
	space_types[num].unchange0 = unchange0;
	space_types[num].probe_speed = probe_speed;
}
<|endoftext|>


<commit_before>#pragma once

#include <string>
#include <functional>

#include <boost/optional.hpp>

namespace rusql { namespace mysql {
	template <typename T>
	struct type_traits;

	typedef std::function<void(MYSQL_BIND&)> OutputProcessor;

	//! A collection a functions that map C++ types in one way or another to what MySQL wants (buffer, is_null, field length, etc.)
	namespace field {
		//! A collection of functors that get the char const* to any type of variable, to pass to MYSQL_BIND for example.
		namespace buffer {
			//! All primitve types (int, double, etc.) can be simply cast to a char* and be done with it.
			struct Primitive {
				template <typename T>
				static char* get(T& x){
					return reinterpret_cast<char*>(&x);
				}
			};

			struct String {
				static char* get(std::string& x){
					return &x[0];
				}
			};

			struct CharPointer {
				static char* get(char* x){
					return x;
				}
			};

			//! For boost::optional, returning the pointer only if the object was set.
			struct Optional {
				template <typename T>
				static char* get(boost::optional<T> &x) {
					if(x) {
						return type_traits<T>::data::get(x.get());
					} else {
						return nullptr;
					}
				}
			};

			//! For those types without data, such as boost::none_t
			struct Null {
				template <typename T>
				static char* get(T&) {
					return nullptr;
				}
			};
		}

		namespace length {
			//! For fields with a fixed length, such as int, double, etc.
			struct Fixed {
				template <typename T>
				static size_t get(T const&){
					return 0;
				}
			};

			struct String {
				static size_t get(std::string x){
					return x.size();
				}
			};

			struct Optional {
				template <typename T>
				static size_t get(boost::optional<T> const& x){
					if(x){
						return type_traits<T>::length::get(*x);
					} else {
						return 0;
					}
				}
			};
		}

		namespace type {
			template <enum_field_types type>
			struct Tag {
				template <typename T>
				static constexpr enum_field_types get(T const&){
					return type;
				}
			};

			typedef Tag<MYSQL_TYPE_TINY> Tiny;
			typedef Tag<MYSQL_TYPE_SHORT> Short;
			typedef Tag<MYSQL_TYPE_LONG> Long;
			typedef Tag<MYSQL_TYPE_LONGLONG> LongLong;
			typedef Tag<MYSQL_TYPE_STRING> String;
			typedef Tag<MYSQL_TYPE_NULL> Null;

			struct Optional {
				template <typename T>
				static enum_field_types get(boost::optional<T> const& x){
					if(x){
						return type_traits<T>::type::get(*x);
					} else {
						return MYSQL_TYPE_NULL;
					}
				}
			};

			struct Pointer {
				template <typename T>
				static enum_field_types get(T const * const x){
					if(x){
						return type_traits<T>::type::get(*x);
					} else {
						return MYSQL_TYPE_NULL;
					}
				}
			};
		}

		// Need this as well because boost::optional as "output" (will be written to) is not a MYSQL_TYPE_NULL but simply a T.
		namespace output_type {
			struct Optional {
				template <typename T>
				static enum_field_types get(boost::optional<T> const& x){
					return type_traits<T>::output_type::get(x);
				}
			};
		}

		namespace is_unsigned {
			struct No {
				template <typename T>
				static constexpr bool get(T const&){
					return false;
				}
			};

			struct Yes {
				template <typename T>
				static constexpr bool get(T const&){
					return true;
				}
			};

			struct Optional {
				template <typename T>
				static bool get(boost::optional<T> const& x){
					if(x){
						return type_traits<T>::is_unsigned::get(*x);
					} else {
						return false;
					}
				}
			};
		}

		//! Post-processors for output data
		namespace post_processors {
			struct String {
				static OutputProcessor get(std::string &x) {
					return [&x](MYSQL_BIND&) {
						x.resize(strlen(x.c_str()));
					};
				}
			};

			struct Optional {
				template <typename T>
				static OutputProcessor get(boost::optional<T> &x) {
					return [&x](MYSQL_BIND &b) {
						// we initialized x so MySQL could write to it; if
						// the field turned out to be NULL, we will uninitialize it here
						assert(x);
						assert(b.is_null);
						if(*b.is_null) {
							x = boost::none;
						} else {
							auto output_processor = type_traits<T>::output_processor::get(*x);
							output_processor(b);
						}
					};
				}
			};

			struct NoPostProcessing {
				template <typename T>
				static OutputProcessor get(T&) {
					return [](MYSQL_BIND&) {};
				}
			};
		}
	}

	struct NoProcessing { typedef field::post_processors::NoPostProcessing output_processor; };
	struct Primitive : NoProcessing
	                    { typedef field::buffer::Primitive data; };
	struct Fixed        { typedef field::length::Fixed length; };
	struct Unsigned     { typedef field::is_unsigned::Yes is_unsigned; };
	struct Signed       { typedef field::is_unsigned::No is_unsigned; };

	template <>
	struct type_traits<uint16_t> : Primitive, Fixed, Unsigned {
		typedef field::type::Short type;
		typedef type output_type;
	};

	template <>
	struct type_traits<uint32_t> : Primitive, Fixed, Unsigned {
		typedef field::type::Long type;
		typedef type output_type;
	};

	template <>
	struct type_traits<uint64_t> : Primitive, Fixed, Unsigned {
		typedef field::type::LongLong type;
		typedef type output_type;
	};

	template <>
	struct type_traits<int16_t> : Primitive, Fixed, Signed {
		typedef field::type::Short type;
		typedef type output_type;
	};

	template <>
	struct type_traits<int32_t> : Primitive, Fixed, Signed {
		typedef field::type::Long type;
		typedef type output_type;
	};

	template <>
	struct type_traits<int64_t> : Primitive, Fixed, Signed {
		typedef field::type::LongLong type;
		typedef type output_type;
	};

	template <>
	struct type_traits<bool> : Primitive, Fixed, Unsigned {
		typedef field::type::Tiny type;
		typedef type output_type;
	};

	template <>
	struct type_traits<std::string> : Unsigned {
		typedef field::type::String type;
		typedef type output_type;
		typedef field::buffer::String data;
		typedef field::length::String length;
		typedef field::post_processors::String output_processor;
	};

	template <typename T>
	struct type_traits<boost::optional<T>> {
		typedef field::type::Optional type;
		typedef field::output_type::Optional output_type;
		typedef field::buffer::Optional data;
		typedef field::length::Optional length;
		typedef field::is_unsigned::Optional is_unsigned;
		typedef field::post_processors::Optional output_processor;
	};

	template <size_t size>
	struct type_traits<char[size]> : Unsigned {
		typedef field::type::String type;
		typedef type output_type;
		typedef field::buffer::CharPointer data;
		typedef field::length::String length;
	};

	template <>
	struct type_traits<char*> : Unsigned {
		typedef field::type::String type;
		typedef type output_type;
		typedef field::buffer::CharPointer data;
		typedef field::length::String length;
	};

	template <>
	struct type_traits<char const*> : Unsigned {
		typedef field::type::String type;
		typedef type output_type;
		typedef field::buffer::CharPointer data;
		typedef field::length::String length;
	};

	template <>
	struct type_traits<boost::none_t> : Unsigned {
		typedef field::type::Null type;
		typedef type output_type;
		typedef field::buffer::Null data;
		typedef field::length::Fixed length;
	};
}}
<commit_msg>Add post-processing instructions for more types, so they work again<commit_after>#pragma once

#include <string>
#include <functional>

#include <boost/optional.hpp>

namespace rusql { namespace mysql {
	template <typename T>
	struct type_traits;

	typedef std::function<void(MYSQL_BIND&)> OutputProcessor;

	//! A collection a functions that map C++ types in one way or another to what MySQL wants (buffer, is_null, field length, etc.)
	namespace field {
		//! A collection of functors that get the char const* to any type of variable, to pass to MYSQL_BIND for example.
		namespace buffer {
			//! All primitve types (int, double, etc.) can be simply cast to a char* and be done with it.
			struct Primitive {
				template <typename T>
				static char* get(T& x){
					return reinterpret_cast<char*>(&x);
				}
			};

			struct String {
				static char* get(std::string& x){
					return &x[0];
				}
			};

			struct CharPointer {
				static char* get(char* x){
					return x;
				}
			};

			//! For boost::optional, returning the pointer only if the object was set.
			struct Optional {
				template <typename T>
				static char* get(boost::optional<T> &x) {
					if(x) {
						return type_traits<T>::data::get(x.get());
					} else {
						return nullptr;
					}
				}
			};

			//! For those types without data, such as boost::none_t
			struct Null {
				template <typename T>
				static char* get(T&) {
					return nullptr;
				}
			};
		}

		namespace length {
			//! For fields with a fixed length, such as int, double, etc.
			struct Fixed {
				template <typename T>
				static size_t get(T const&){
					return 0;
				}
			};

			struct String {
				static size_t get(std::string x){
					return x.size();
				}
			};

			struct Optional {
				template <typename T>
				static size_t get(boost::optional<T> const& x){
					if(x){
						return type_traits<T>::length::get(*x);
					} else {
						return 0;
					}
				}
			};
		}

		namespace type {
			template <enum_field_types type>
			struct Tag {
				template <typename T>
				static constexpr enum_field_types get(T const&){
					return type;
				}
			};

			typedef Tag<MYSQL_TYPE_TINY> Tiny;
			typedef Tag<MYSQL_TYPE_SHORT> Short;
			typedef Tag<MYSQL_TYPE_LONG> Long;
			typedef Tag<MYSQL_TYPE_LONGLONG> LongLong;
			typedef Tag<MYSQL_TYPE_STRING> String;
			typedef Tag<MYSQL_TYPE_NULL> Null;

			struct Optional {
				template <typename T>
				static enum_field_types get(boost::optional<T> const& x){
					if(x){
						return type_traits<T>::type::get(*x);
					} else {
						return MYSQL_TYPE_NULL;
					}
				}
			};

			struct Pointer {
				template <typename T>
				static enum_field_types get(T const * const x){
					if(x){
						return type_traits<T>::type::get(*x);
					} else {
						return MYSQL_TYPE_NULL;
					}
				}
			};
		}

		// Need this as well because boost::optional as "output" (will be written to) is not a MYSQL_TYPE_NULL but simply a T.
		namespace output_type {
			struct Optional {
				template <typename T>
				static enum_field_types get(boost::optional<T> const& x){
					return type_traits<T>::output_type::get(x);
				}
			};
		}

		namespace is_unsigned {
			struct No {
				template <typename T>
				static constexpr bool get(T const&){
					return false;
				}
			};

			struct Yes {
				template <typename T>
				static constexpr bool get(T const&){
					return true;
				}
			};

			struct Optional {
				template <typename T>
				static bool get(boost::optional<T> const& x){
					if(x){
						return type_traits<T>::is_unsigned::get(*x);
					} else {
						return false;
					}
				}
			};
		}

		//! Post-processors for output data
		namespace post_processors {
			struct String {
				static OutputProcessor get(std::string &x) {
					return [&x](MYSQL_BIND&) {
						x.resize(strlen(x.c_str()));
					};
				}
			};

			struct Optional {
				template <typename T>
				static OutputProcessor get(boost::optional<T> &x) {
					return [&x](MYSQL_BIND &b) {
						// we initialized x so MySQL could write to it; if
						// the field turned out to be NULL, we will uninitialize it here
						assert(x);
						assert(b.is_null);
						if(*b.is_null) {
							x = boost::none;
						} else {
							auto output_processor = type_traits<T>::output_processor::get(*x);
							output_processor(b);
						}
					};
				}
			};

			struct NoPostProcessing {
				template <typename T>
				static OutputProcessor get(T&) {
					return [](MYSQL_BIND&) {};
				}
			};
		}
	}

	struct NoProcessing { typedef field::post_processors::NoPostProcessing output_processor; };
	struct Primitive : NoProcessing
	                    { typedef field::buffer::Primitive data; };
	struct Fixed        { typedef field::length::Fixed length; };
	struct Unsigned     { typedef field::is_unsigned::Yes is_unsigned; };
	struct Signed       { typedef field::is_unsigned::No is_unsigned; };

	template <>
	struct type_traits<uint16_t> : Primitive, Fixed, Unsigned {
		typedef field::type::Short type;
		typedef type output_type;
	};

	template <>
	struct type_traits<uint32_t> : Primitive, Fixed, Unsigned {
		typedef field::type::Long type;
		typedef type output_type;
	};

	template <>
	struct type_traits<uint64_t> : Primitive, Fixed, Unsigned {
		typedef field::type::LongLong type;
		typedef type output_type;
	};

	template <>
	struct type_traits<int16_t> : Primitive, Fixed, Signed {
		typedef field::type::Short type;
		typedef type output_type;
	};

	template <>
	struct type_traits<int32_t> : Primitive, Fixed, Signed {
		typedef field::type::Long type;
		typedef type output_type;
	};

	template <>
	struct type_traits<int64_t> : Primitive, Fixed, Signed {
		typedef field::type::LongLong type;
		typedef type output_type;
	};

	template <>
	struct type_traits<bool> : Primitive, Fixed, Unsigned {
		typedef field::type::Tiny type;
		typedef type output_type;
	};

	template <>
	struct type_traits<std::string> : Unsigned {
		typedef field::type::String type;
		typedef type output_type;
		typedef field::buffer::String data;
		typedef field::length::String length;
		typedef field::post_processors::String output_processor;
	};

	template <typename T>
	struct type_traits<boost::optional<T>> {
		typedef field::type::Optional type;
		typedef field::output_type::Optional output_type;
		typedef field::buffer::Optional data;
		typedef field::length::Optional length;
		typedef field::is_unsigned::Optional is_unsigned;
		typedef field::post_processors::Optional output_processor;
	};

	template <size_t size>
	struct type_traits<char[size]> : Unsigned, NoProcessing {
		typedef field::type::String type;
		typedef type output_type;
		typedef field::buffer::CharPointer data;
		typedef field::length::String length;
	};

	template <>
	struct type_traits<char*> : Unsigned, NoProcessing {
		typedef field::type::String type;
		typedef type output_type;
		typedef field::buffer::CharPointer data;
		typedef field::length::String length;
	};

	template <>
	struct type_traits<char const*> : Unsigned, NoProcessing {
		typedef field::type::String type;
		typedef type output_type;
		typedef field::buffer::CharPointer data;
		typedef field::length::String length;
	};

	template <>
	struct type_traits<boost::none_t> : Unsigned, NoProcessing {
		typedef field::type::Null type;
		typedef type output_type;
		typedef field::buffer::Null data;
		typedef field::length::Fixed length;
	};
}}
<|endoftext|>


<commit_before><commit_msg>Removed commented out old code and an unused variable.<commit_after><|endoftext|>


<commit_before><commit_msg>starting to make TpcEffMaker mc/rc aware so that it can override cuts on MC tracks only<commit_after><|endoftext|>


<commit_before><commit_msg>FIX: It wasnt exporting symbols when compiled with visual studio UPDATE: Now using the platform.h<commit_after><|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include "abstracteditorsupport.h"

#include "cppfilesettingspage.h"
#include "cppmodelmanagerinterface.h"
#include <cplusplus/Overview.h>

#include <QDebug>

namespace CppTools {

AbstractEditorSupport::AbstractEditorSupport(CppModelManagerInterface *modelmanager) :
    m_modelmanager(modelmanager), m_revision(0)
{
}

AbstractEditorSupport::~AbstractEditorSupport()
{
}

void AbstractEditorSupport::updateDocument()
{
    ++m_revision;
    m_modelmanager->updateSourceFiles(QStringList(fileName()));
}

QString AbstractEditorSupport::functionAt(const CppModelManagerInterface *modelManager,
                                          const QString &fileName,
                                          int line, int column)
{
    if (!modelManager)
        return QString();

    const CPlusPlus::Snapshot snapshot = modelManager->snapshot();
    if (const CPlusPlus::Document::Ptr document = snapshot.document(fileName))
        return document->functionAt(line, column);

    return QString();
}

QString AbstractEditorSupport::licenseTemplate(const QString &file, const QString &className)
{
    return Internal::CppFileSettings::licenseTemplate(file, className);
}

} // namespace CppTools

<commit_msg>CppTools: Fix highlighting of ui_*.h files<commit_after>/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include "abstracteditorsupport.h"

#include "cppfilesettingspage.h"
#include "cppmodelmanagerinterface.h"
#include <cplusplus/Overview.h>

#include <QDebug>

namespace CppTools {

AbstractEditorSupport::AbstractEditorSupport(CppModelManagerInterface *modelmanager) :
    m_modelmanager(modelmanager), m_revision(1)
{
}

AbstractEditorSupport::~AbstractEditorSupport()
{
}

void AbstractEditorSupport::updateDocument()
{
    ++m_revision;
    m_modelmanager->updateSourceFiles(QStringList(fileName()));
}

QString AbstractEditorSupport::functionAt(const CppModelManagerInterface *modelManager,
                                          const QString &fileName,
                                          int line, int column)
{
    if (!modelManager)
        return QString();

    const CPlusPlus::Snapshot snapshot = modelManager->snapshot();
    if (const CPlusPlus::Document::Ptr document = snapshot.document(fileName))
        return document->functionAt(line, column);

    return QString();
}

QString AbstractEditorSupport::licenseTemplate(const QString &file, const QString &className)
{
    return Internal::CppFileSettings::licenseTemplate(file, className);
}

} // namespace CppTools

<|endoftext|>


<commit_before>/*******************************************************************************

Licensed to the OpenCOR team under one or more contributor license agreements.
See the NOTICE.txt file distributed with this work for additional information
regarding copyright ownership. The OpenCOR team 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.

*******************************************************************************/

//==============================================================================
// Viewer widget
//==============================================================================

#include "viewerwidget.h"

//==============================================================================

#include <Qt>

//==============================================================================

#include <QAction>
#include <QCursor>
#include <QDomDocument>
#include <QIcon>
#include <QMenu>
#include <QPainter>
#include <QPaintEvent>
#include <QPalette>
#include <QPoint>
#include <QRectF>

//==============================================================================

#include "qwt_mathml_text_engine.h"

//==============================================================================

namespace OpenCOR {
namespace Viewer {

//==============================================================================

ViewerWidget::ViewerWidget(QWidget *pParent) :
    Widget(pParent),
    mMathmlDocument(QwtMathMLDocument()),
    mOneOverMathmlDocumentWidth(0),
    mOneOverMathmlDocumentHeight(0),
    mContents(QString()),
    mError(false)
{
    // Create our context menu

    mContextMenu = new QMenu(this);

    mOptimiseFontSizeAction = newAction(this);
    mDigitGroupingAction = newAction(this);

    connect(mOptimiseFontSizeAction, SIGNAL(triggered()),
            this, SLOT(update()));
    connect(mOptimiseFontSizeAction, SIGNAL(toggled(bool)),
            this, SIGNAL(optimiseFontSizeChanged(const bool &)));

    connect(mDigitGroupingAction, SIGNAL(triggered()),
            this, SLOT(updateContents()));
    connect(mDigitGroupingAction, SIGNAL(toggled(bool)),
            this, SIGNAL(digitGroupingChanged(const bool &)));

    mContextMenu->addAction(mOptimiseFontSizeAction);
    mContextMenu->addSeparator();
    mContextMenu->addAction(mDigitGroupingAction);

    // We want a context menu

    setContextMenuPolicy(Qt::CustomContextMenu);

    connect(this, SIGNAL(customContextMenuRequested(const QPoint &)),
            this, SLOT(showCustomContextMenu(const QPoint &)));

    // Retranslate ourselves, so that our action is properly set

    retranslateUi();
}

//==============================================================================

void ViewerWidget::retranslateUi()
{
    // Retranslate our actions

    mOptimiseFontSizeAction->setText(tr("Optimise Font Size"));
    mDigitGroupingAction->setText(tr("Digit Grouping"));
}

//==============================================================================

QString ViewerWidget::contents() const
{
    // Return our contents

    return mContents;
}

//==============================================================================

void ViewerWidget::setContents(const QString &pContents)
{
    // Set our contents

    if (!pContents.compare(mContents))
        return;

    // Try to set our contents to our MathML document

    mContents = pContents;

    if (mMathmlDocument.setContent(pContents)) {
        mError = false;

        // Process and reset our contents, shoudl we want to do digit grouping

        if (digitGrouping())
            mMathmlDocument.setContent(processedContents());

        // Determine (the inverse of) the size of our contents when rendered
        // using a font size of 100 points
        // Note: when setting the contents, QwtMathMLDocument recomputes its
        //       layout. Now, because we want the contents to be rendered as
        //       optimally as possible, we use a big font size, so that when we
        //       actually need to render the contents (see paintEvent()), we can
        //       do so optimally...

        mMathmlDocument.setBaseFontPointSize(100);

        QSizeF mathmlDocumentSize = mMathmlDocument.size();

        mOneOverMathmlDocumentWidth  = 1.0/mathmlDocumentSize.width();
        mOneOverMathmlDocumentHeight = 1.0/mathmlDocumentSize.height();
    } else {
        mError = pContents.size();
    }

    // Update ourselves

    update();
}

//==============================================================================

bool ViewerWidget::error() const
{
    // Return whether there is an error

    return mError;
}

//==============================================================================

void ViewerWidget::setError(const bool &pError)
{
    // Keep track of whether there is an error

    if (pError == mError)
        return;

    mContents = QString();
    mError = pError;

    // Update ourselves

    update();
}

//==============================================================================

bool ViewerWidget::optimiseFontSize() const
{
    // Return whether we optimise our font size

    return mOptimiseFontSizeAction->isChecked();
}

//==============================================================================

void ViewerWidget::setOptimiseFontSize(const bool &pOptimiseFontSize)
{
    // Keep track of whether we should optimise our font size

    if (pOptimiseFontSize == optimiseFontSize())
        return;

    mOptimiseFontSizeAction->setChecked(pOptimiseFontSize);

    // Let people know about the new value

    emit optimiseFontSizeChanged(pOptimiseFontSize);

    // Update ourselves

    update();
}

//==============================================================================

bool ViewerWidget::digitGrouping() const
{
    // Return whether we do digit grouping

    return mDigitGroupingAction->isChecked();
}

//==============================================================================

void ViewerWidget::setDigitGrouping(const bool &pDigitGrouping)
{
    // Keep track of whether we do digit grouping

    if (pDigitGrouping == digitGrouping())
        return;

    mDigitGroupingAction->setChecked(pDigitGrouping);

    // Let people know about the new value

    emit digitGroupingChanged(pDigitGrouping);

    // Update ourselves

    update();
}

//==============================================================================

void ViewerWidget::paintEvent(QPaintEvent *pEvent)
{
    // Make sure that we have a width and height

    QRectF rect = pEvent->rect();

    if (!rect.width() || !rect.height())
        return;

    // Clear our background

    QPainter painter(this);
    QColor backgroundColor = QColor(palette().color(QPalette::Base));

    painter.fillRect(rect, backgroundColor);

    // Render our contents or show a warning sign, depending on whether our
    // contents is valid

    if (mError) {
        // We want to show that there is an error, so render a stretched warning
        // icon in our center

        QIcon icon = QIcon(":Viewer_warning");
        QSize iconSize = icon.availableSizes().first();

        int painterRectWidth = iconSize.width();
        int painterRectHeight = iconSize.height();

        if (rect.width() < rect.height())
            painterRectHeight *= rect.height()/rect.width();
        else
            painterRectWidth *= rect.width()/rect.height();

        if (painterRectHeight % 2)
            ++painterRectHeight;

        if (painterRectWidth % 2)
            ++painterRectWidth;

        QRect painterRect = QRect(0, 0, painterRectWidth, painterRectHeight);
        // Note: both painterRectWidth and painterRectHeight have even values.
        //       This ensures that when we get resized, then the warning icon
        //       remains nicely centered (while it would otherwise shifts to the
        //       left/right or top/bottom, depending on our width or height,
        //       respectively...

        painter.setWindow(painterRect);

        icon.paint(&painter, painterRect);
    } else {
        // Customise our MathML document
        // Note: to go for 100% of the 'optimal' font size might result in the
        //       edges of the contents being clipped on Windows (compared to
        //       Linux and OS X) or in some cases on Linux and OS X (e.g. if the
        //       contents includes a square root), hence we go for 75% of the
        //       'optimal' font size instead...

        mMathmlDocument.setBackgroundColor(backgroundColor);
        mMathmlDocument.setForegroundColor(QColor(palette().color(QPalette::Text)));

        mMathmlDocument.setBaseFontPointSize(mOptimiseFontSizeAction->isChecked()?
                                                 qRound(75.0*qMin(mOneOverMathmlDocumentWidth*width(),
                                                                  mOneOverMathmlDocumentHeight*height())):
                                                 font().pointSize());

        // Render our contents

        QSizeF mathmlDocumentSize = mMathmlDocument.size();

        mMathmlDocument.paint(&painter, QPointF(0.5*(rect.width()-mathmlDocumentSize.width()),
                                                0.5*(rect.height()-mathmlDocumentSize.height())));
    }

    // Accept the event

    pEvent->accept();
}

//==============================================================================

QSize ViewerWidget::minimumSizeHint() const
{
    // Suggest a default minimum size for our viewer widget

    return defaultSize(0.03);
}

//==============================================================================

QSize ViewerWidget::sizeHint() const
{
    // Suggest a default size for our viewer widget
    // Note: this is critical if we want a docked widget, with a viewer widget
    //       on it, to have a decent size when docked to the main window...

    return defaultSize(0.1);
}

//==============================================================================

QAction * ViewerWidget::newAction(QObject *pParent)
{
    // Create and return a checkable and checked action

    QAction *res = new QAction(pParent);

    res->setCheckable(true);
    res->setChecked(true);

    return res;
}

//==============================================================================

void ViewerWidget::processNode(const QDomNode &pDomNode) const
{
    // Go through the node's children and process them

    for (int i = 0, iMax = pDomNode.childNodes().count(); i < iMax; ++i) {
        QDomNode domNode = pDomNode.childNodes().at(i);

        // Check whether we want to do digit grouping and whether the node's
        // parent is an mn element

        if (digitGrouping() && !domNode.parentNode().nodeName().compare("mn")) {
            // We want to do digit grouping and whether the node's parent is an
            // mn element, so check whether the value of that element is a valid
            // number

            bool domNodeValueValid;
            QString domNodeValue = domNode.nodeValue();

            domNodeValue.toDouble(&domNodeValueValid);

            if (domNodeValueValid) {
                // The number is valid, so do digit grouping on it

                int decimalPointPos = domNodeValue.indexOf(".");

                if (decimalPointPos != -1) {
                    QString beforeDecimalPoint = domNodeValue.left(decimalPointPos);
                    domNodeValue = domNodeValue.right(domNodeValue.length()-decimalPointPos);
                    bool maybeDigit = true;
                    int nbOfDigits = -1;

                    for (int i = beforeDecimalPoint.length()-1; i >= 0; --i) {
                        if (maybeDigit && beforeDecimalPoint[i].isDigit()) {
                            if (++nbOfDigits == 3) {
                                domNodeValue = ","+domNodeValue;

                                nbOfDigits = 0;
                            }
                        } else {
                            maybeDigit = false;
                        }

                        domNodeValue = beforeDecimalPoint[i]+domNodeValue;
                    }

                    domNode.setNodeValue(domNodeValue);
                }
            }
        }

        processNode(domNode);
    }
}

//==============================================================================

QString ViewerWidget::processedContents() const
{
    // Process and return our processed contents

    if (mContents.isEmpty())
        return QString();

    QDomDocument domDocument;

    if (domDocument.setContent(mContents)) {
        QDomNode domNode = domDocument.documentElement();

        processNode(domNode);

        return domDocument.toString(-1);
    } else {
        // We should never reach this point (since we should only come here if
        // our contents is valid), but better be safe than sorry...

        return QString();
    }
}

//==============================================================================

void ViewerWidget::showCustomContextMenu(const QPoint &pPosition) const
{
    Q_UNUSED(pPosition);

    // Show our custom context menu

    mContextMenu->exec(QCursor::pos());
}

//==============================================================================

void ViewerWidget::updateContents()
{
    // Update our contents and therefore ourselves (through setContents())

    QString contents = mContents;

    mContents = QString();

    setContents(contents);
}

//==============================================================================

}   // namespace Viewer
}   // namespace OpenCOR

//==============================================================================
// End of file
//==============================================================================
<commit_msg>Some minor cleaning up [ci skip].<commit_after>/*******************************************************************************

Licensed to the OpenCOR team under one or more contributor license agreements.
See the NOTICE.txt file distributed with this work for additional information
regarding copyright ownership. The OpenCOR team 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.

*******************************************************************************/

//==============================================================================
// Viewer widget
//==============================================================================

#include "viewerwidget.h"

//==============================================================================

#include <Qt>

//==============================================================================

#include <QAction>
#include <QCursor>
#include <QDomDocument>
#include <QIcon>
#include <QMenu>
#include <QPainter>
#include <QPaintEvent>
#include <QPalette>
#include <QPoint>
#include <QRectF>

//==============================================================================

#include "qwt_mathml_text_engine.h"

//==============================================================================

namespace OpenCOR {
namespace Viewer {

//==============================================================================

ViewerWidget::ViewerWidget(QWidget *pParent) :
    Widget(pParent),
    mMathmlDocument(QwtMathMLDocument()),
    mOneOverMathmlDocumentWidth(0),
    mOneOverMathmlDocumentHeight(0),
    mContents(QString()),
    mError(false)
{
    // Create our context menu

    mContextMenu = new QMenu(this);

    mOptimiseFontSizeAction = newAction(this);
    mDigitGroupingAction = newAction(this);

    connect(mOptimiseFontSizeAction, SIGNAL(triggered()),
            this, SLOT(update()));
    connect(mOptimiseFontSizeAction, SIGNAL(toggled(bool)),
            this, SIGNAL(optimiseFontSizeChanged(const bool &)));

    connect(mDigitGroupingAction, SIGNAL(triggered()),
            this, SLOT(updateContents()));
    connect(mDigitGroupingAction, SIGNAL(toggled(bool)),
            this, SIGNAL(digitGroupingChanged(const bool &)));

    mContextMenu->addAction(mOptimiseFontSizeAction);
    mContextMenu->addSeparator();
    mContextMenu->addAction(mDigitGroupingAction);

    // We want a context menu

    setContextMenuPolicy(Qt::CustomContextMenu);

    connect(this, SIGNAL(customContextMenuRequested(const QPoint &)),
            this, SLOT(showCustomContextMenu(const QPoint &)));

    // Retranslate ourselves, so that our action is properly set

    retranslateUi();
}

//==============================================================================

void ViewerWidget::retranslateUi()
{
    // Retranslate our actions

    mOptimiseFontSizeAction->setText(tr("Optimise Font Size"));
    mDigitGroupingAction->setText(tr("Digit Grouping"));
}

//==============================================================================

QString ViewerWidget::contents() const
{
    // Return our contents

    return mContents;
}

//==============================================================================

void ViewerWidget::setContents(const QString &pContents)
{
    // Set our contents

    if (!pContents.compare(mContents))
        return;

    // Try to set our contents to our MathML document

    mContents = pContents;

    if (mMathmlDocument.setContent(pContents)) {
        mError = false;

        // Process and reset our contents, if needed

        if (digitGrouping())
            mMathmlDocument.setContent(processedContents());

        // Determine (the inverse of) the size of our contents when rendered
        // using a font size of 100 points
        // Note: when setting the contents, QwtMathMLDocument recomputes its
        //       layout. Now, because we want the contents to be rendered as
        //       optimally as possible, we use a big font size, so that when we
        //       actually need to render the contents (see paintEvent()), we can
        //       do so optimally...

        mMathmlDocument.setBaseFontPointSize(100);

        QSizeF mathmlDocumentSize = mMathmlDocument.size();

        mOneOverMathmlDocumentWidth  = 1.0/mathmlDocumentSize.width();
        mOneOverMathmlDocumentHeight = 1.0/mathmlDocumentSize.height();
    } else {
        mError = pContents.size();
    }

    // Update ourselves

    update();
}

//==============================================================================

bool ViewerWidget::error() const
{
    // Return whether there is an error

    return mError;
}

//==============================================================================

void ViewerWidget::setError(const bool &pError)
{
    // Keep track of whether there is an error

    if (pError == mError)
        return;

    mContents = QString();
    mError = pError;

    // Update ourselves

    update();
}

//==============================================================================

bool ViewerWidget::optimiseFontSize() const
{
    // Return whether we optimise our font size

    return mOptimiseFontSizeAction->isChecked();
}

//==============================================================================

void ViewerWidget::setOptimiseFontSize(const bool &pOptimiseFontSize)
{
    // Keep track of whether we should optimise our font size

    if (pOptimiseFontSize == optimiseFontSize())
        return;

    mOptimiseFontSizeAction->setChecked(pOptimiseFontSize);

    // Let people know about the new value

    emit optimiseFontSizeChanged(pOptimiseFontSize);

    // Update ourselves

    update();
}

//==============================================================================

bool ViewerWidget::digitGrouping() const
{
    // Return whether we do digit grouping

    return mDigitGroupingAction->isChecked();
}

//==============================================================================

void ViewerWidget::setDigitGrouping(const bool &pDigitGrouping)
{
    // Keep track of whether we do digit grouping

    if (pDigitGrouping == digitGrouping())
        return;

    mDigitGroupingAction->setChecked(pDigitGrouping);

    // Let people know about the new value

    emit digitGroupingChanged(pDigitGrouping);

    // Update ourselves

    update();
}

//==============================================================================

void ViewerWidget::paintEvent(QPaintEvent *pEvent)
{
    // Make sure that we have a width and height

    QRectF rect = pEvent->rect();

    if (!rect.width() || !rect.height())
        return;

    // Clear our background

    QPainter painter(this);
    QColor backgroundColor = QColor(palette().color(QPalette::Base));

    painter.fillRect(rect, backgroundColor);

    // Render our contents or show a warning sign, depending on whether our
    // contents is valid

    if (mError) {
        // We want to show that there is an error, so render a stretched warning
        // icon in our center

        QIcon icon = QIcon(":Viewer_warning");
        QSize iconSize = icon.availableSizes().first();

        int painterRectWidth = iconSize.width();
        int painterRectHeight = iconSize.height();

        if (rect.width() < rect.height())
            painterRectHeight *= rect.height()/rect.width();
        else
            painterRectWidth *= rect.width()/rect.height();

        if (painterRectHeight % 2)
            ++painterRectHeight;

        if (painterRectWidth % 2)
            ++painterRectWidth;

        QRect painterRect = QRect(0, 0, painterRectWidth, painterRectHeight);
        // Note: both painterRectWidth and painterRectHeight have even values.
        //       This ensures that when we get resized, then the warning icon
        //       remains nicely centered (while it would otherwise shifts to the
        //       left/right or top/bottom, depending on our width or height,
        //       respectively...

        painter.setWindow(painterRect);

        icon.paint(&painter, painterRect);
    } else {
        // Customise our MathML document
        // Note: to go for 100% of the 'optimal' font size might result in the
        //       edges of the contents being clipped on Windows (compared to
        //       Linux and OS X) or in some cases on Linux and OS X (e.g. if the
        //       contents includes a square root), hence we go for 75% of the
        //       'optimal' font size instead...

        mMathmlDocument.setBackgroundColor(backgroundColor);
        mMathmlDocument.setForegroundColor(QColor(palette().color(QPalette::Text)));

        mMathmlDocument.setBaseFontPointSize(mOptimiseFontSizeAction->isChecked()?
                                                 qRound(75.0*qMin(mOneOverMathmlDocumentWidth*width(),
                                                                  mOneOverMathmlDocumentHeight*height())):
                                                 font().pointSize());

        // Render our contents

        QSizeF mathmlDocumentSize = mMathmlDocument.size();

        mMathmlDocument.paint(&painter, QPointF(0.5*(rect.width()-mathmlDocumentSize.width()),
                                                0.5*(rect.height()-mathmlDocumentSize.height())));
    }

    // Accept the event

    pEvent->accept();
}

//==============================================================================

QSize ViewerWidget::minimumSizeHint() const
{
    // Suggest a default minimum size for our viewer widget

    return defaultSize(0.03);
}

//==============================================================================

QSize ViewerWidget::sizeHint() const
{
    // Suggest a default size for our viewer widget
    // Note: this is critical if we want a docked widget, with a viewer widget
    //       on it, to have a decent size when docked to the main window...

    return defaultSize(0.1);
}

//==============================================================================

QAction * ViewerWidget::newAction(QObject *pParent)
{
    // Create and return a checkable and checked action

    QAction *res = new QAction(pParent);

    res->setCheckable(true);
    res->setChecked(true);

    return res;
}

//==============================================================================

void ViewerWidget::processNode(const QDomNode &pDomNode) const
{
    // Go through the node's children and process them

    for (int i = 0, iMax = pDomNode.childNodes().count(); i < iMax; ++i) {
        QDomNode domNode = pDomNode.childNodes().at(i);

        // Check whether we want to do digit grouping and whether the node's
        // parent is an mn element

        if (digitGrouping() && !domNode.parentNode().nodeName().compare("mn")) {
            // We want to do digit grouping and whether the node's parent is an
            // mn element, so check whether the value of that element is a valid
            // number

            bool domNodeValueValid;
            QString domNodeValue = domNode.nodeValue();

            domNodeValue.toDouble(&domNodeValueValid);

            if (domNodeValueValid) {
                // The number is valid, so do digit grouping on it

                int decimalPointPos = domNodeValue.indexOf(".");

                if (decimalPointPos != -1) {
                    QString beforeDecimalPoint = domNodeValue.left(decimalPointPos);
                    domNodeValue = domNodeValue.right(domNodeValue.length()-decimalPointPos);
                    bool maybeDigit = true;
                    int nbOfDigits = -1;

                    for (int i = beforeDecimalPoint.length()-1; i >= 0; --i) {
                        if (maybeDigit && beforeDecimalPoint[i].isDigit()) {
                            if (++nbOfDigits == 3) {
                                domNodeValue = ","+domNodeValue;

                                nbOfDigits = 0;
                            }
                        } else {
                            maybeDigit = false;
                        }

                        domNodeValue = beforeDecimalPoint[i]+domNodeValue;
                    }

                    domNode.setNodeValue(domNodeValue);
                }
            }
        }

        processNode(domNode);
    }
}

//==============================================================================

QString ViewerWidget::processedContents() const
{
    // Process and return our processed contents

    if (mContents.isEmpty())
        return QString();

    QDomDocument domDocument;

    if (domDocument.setContent(mContents)) {
        QDomNode domNode = domDocument.documentElement();

        processNode(domNode);

        return domDocument.toString(-1);
    } else {
        // We should never reach this point (since we should only come here if
        // our contents is valid), but better be safe than sorry...

        return QString();
    }
}

//==============================================================================

void ViewerWidget::showCustomContextMenu(const QPoint &pPosition) const
{
    Q_UNUSED(pPosition);

    // Show our custom context menu

    mContextMenu->exec(QCursor::pos());
}

//==============================================================================

void ViewerWidget::updateContents()
{
    // Update our contents and therefore ourselves (through setContents())

    QString contents = mContents;

    mContents = QString();

    setContents(contents);
}

//==============================================================================

}   // namespace Viewer
}   // namespace OpenCOR

//==============================================================================
// End of file
//==============================================================================
<|endoftext|>


<commit_before>#include "srv_partymember.h"

namespace RoseCommon {

SrvPartyMember::SrvPartyMember() : CRosePacket(ePacketType::PAKWC_PARTY_MEMBER) {}

SrvPartyMember::SrvPartyMember(uint8_t rules, bool isDelete, const std::vector<Entity> &list) : CRosePacket(ePacketType::PAKWC_PARTY_MEMBER), rules_(rules), isDelete_(isDelete), list_(list) {}

uint8_t SrvPartyMember::rules() const {
	return rules_;
}

bool SrvPartyMember::isDelete() const {
	return isDelete_;
}

const std::vector<Entity> &SrvPartyMember::list() const {
	return list_;
}

void SrvPartyMember::pack() {
	*this << rules_;
    if (isDelete_) {
        *this << (int8_t)-1;
        *this << list_[0].component<BasicInfo>()->tag_; // leaving
        *this << list_[1].component<BasicInfo>()->tag_; // leader
        return;
    }

    *this << (uint8_t)list_.size();
    for (auto it : list_) {
        auto basic = it.component<BasicInfo>();
        auto advanced = it.component<AdvancedInfo>();
        auto stats = it.component<Stats>();
        auto character = it.component<CharacterInfo>();
        *this << basic->tag_;
        *this << basic->id_;
        *this << (int16_t)stats->maxHp_;
        *this << (int16_t)advanced->hp_;
        *this << character->statusFlag_;
        *this << stats->con_;
        *this << (uint8_t)0; // TODO : RECOVERY HP
        *this << (uint8_t)0; // TODO : RECOVERY MP
        *this << character->stamina_;
        *this << basic->name_;
    }
}

}
<commit_msg>Updated packets with new client<commit_after>#include "srv_partymember.h"

namespace RoseCommon {

SrvPartyMember::SrvPartyMember() : CRosePacket(ePacketType::PAKWC_PARTY_MEMBER) {}

SrvPartyMember::SrvPartyMember(uint8_t rules, bool isDelete, const std::vector<Entity> &list) : CRosePacket(ePacketType::PAKWC_PARTY_MEMBER), rules_(rules), isDelete_(isDelete), list_(list) {}

uint8_t SrvPartyMember::rules() const {
	return rules_;
}

bool SrvPartyMember::isDelete() const {
	return isDelete_;
}

const std::vector<Entity> &SrvPartyMember::list() const {
	return list_;
}

void SrvPartyMember::pack() {
	*this << rules_;
    if (isDelete_) {
        *this << (int8_t)-1;
        *this << list_[0].component<BasicInfo>()->tag_; // leaving
        *this << list_[1].component<BasicInfo>()->tag_; // leader
        return;
    }

    *this << (uint8_t)list_.size();
    for (auto it : list_) {
        auto basic = it.component<BasicInfo>();
        auto advanced = it.component<AdvancedInfo>();
        auto stats = it.component<Stats>();
        auto character = it.component<CharacterInfo>();
        *this << basic->tag_;
        *this << basic->id_;
        *this << stats->maxHp_;
        *this << advanced->hp_;
        *this << character->statusFlag_;
        *this << stats->con_;
        *this << (uint16_t)0; // TODO : RECOVERY HP
        *this << (uint16_t)0; // TODO : RECOVERY MP
        *this << character->stamina_;
        *this << basic->name_;
    }
}

}
<|endoftext|>


<commit_before>// Copyright (C) 2019 The Android Open Source Project
//
// 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 <random>

#include <benchmark/benchmark.h>

#include "src/trace_processor/tables/macros.h"

namespace perfetto {
namespace trace_processor {
namespace {

#define PERFETTO_TP_TEST_TABLE(NAME, PARENT, C) \
  NAME(TestTable, "test_table")                 \
  PERFETTO_TP_ROOT_TABLE(PARENT, C)

PERFETTO_TP_TABLE(PERFETTO_TP_TEST_TABLE);

}  // namespace
}  // namespace trace_processor
}  // namespace perfetto

using perfetto::trace_processor::SqlValue;
using perfetto::trace_processor::StringPool;
using perfetto::trace_processor::TestTable;

static void BM_TableInsert(benchmark::State& state) {
  StringPool pool;
  TestTable table(&pool, nullptr);

  for (auto _ : state) {
    benchmark::DoNotOptimize(table.Insert({}));
  }
}
BENCHMARK(BM_TableInsert);

static void BM_TableFilterIdColumn(benchmark::State& state) {
  StringPool pool;
  TestTable table(&pool, nullptr);

  uint32_t size = static_cast<uint32_t>(state.range(0));
  for (uint32_t i = 0; i < size; ++i)
    table.Insert({});

  for (auto _ : state) {
    benchmark::DoNotOptimize(table.Filter({table.id().eq(SqlValue::Long(30))}));
  }
}
BENCHMARK(BM_TableFilterIdColumn)
    ->RangeMultiplier(8)
    ->Range(1024, 2 * 1024 * 1024);
<commit_msg>trace_processor: add benchmark for full table scans<commit_after>// Copyright (C) 2019 The Android Open Source Project
//
// 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 <random>

#include <benchmark/benchmark.h>

#include "src/trace_processor/tables/macros.h"

namespace perfetto {
namespace trace_processor {
namespace {

#define PERFETTO_TP_TEST_TABLE(NAME, PARENT, C) \
  NAME(TestTable, "test_table")                 \
  PERFETTO_TP_ROOT_TABLE(PARENT, C)             \
  C(uint32_t, a)

PERFETTO_TP_TABLE(PERFETTO_TP_TEST_TABLE);

}  // namespace
}  // namespace trace_processor
}  // namespace perfetto

using perfetto::trace_processor::SqlValue;
using perfetto::trace_processor::StringPool;
using perfetto::trace_processor::TestTable;

static void BM_TableInsert(benchmark::State& state) {
  StringPool pool;
  TestTable table(&pool, nullptr);

  for (auto _ : state) {
    benchmark::DoNotOptimize(table.Insert({}));
  }
}
BENCHMARK(BM_TableInsert);

static void BM_TableFilterIdColumn(benchmark::State& state) {
  StringPool pool;
  TestTable table(&pool, nullptr);

  uint32_t size = static_cast<uint32_t>(state.range(0));
  for (uint32_t i = 0; i < size; ++i)
    table.Insert({});

  for (auto _ : state) {
    benchmark::DoNotOptimize(table.Filter({table.id().eq(SqlValue::Long(30))}));
  }
}
BENCHMARK(BM_TableFilterIdColumn)
    ->RangeMultiplier(8)
    ->Range(1024, 2 * 1024 * 1024);

static void BM_TableFilterEqMatchMany(benchmark::State& state) {
  StringPool pool;
  TestTable table(&pool, nullptr);

  // We want the number of partitions to be significantly less than the size of
  // the RowMap. This matches the setup of columns like track_id in the event
  // table where there are a large number of events but a much smaller number of
  // tracks all those events are associated with.
  uint32_t size = static_cast<uint32_t>(state.range(0));
  uint32_t partitions = size / 1024;

  constexpr uint32_t kRandomSeed = 42;
  std::minstd_rand0 rnd_engine(kRandomSeed);
  for (uint32_t i = 0; i < size; ++i) {
    TestTable::Row row(static_cast<uint32_t>(rnd_engine() % partitions));
    table.Insert(row);
  }

  for (auto _ : state) {
    benchmark::DoNotOptimize(table.Filter({table.a().eq(SqlValue::Long(0))}));
  }
}
BENCHMARK(BM_TableFilterEqMatchMany)
    ->RangeMultiplier(8)
    ->Range(1024, 2 * 1024 * 1024);
<|endoftext|>


<commit_before>//
// Created by piouffb on 12/19/15.
//

#include "aJSON.h"
#include "ESP8266.h"
#include "main.h"

#define SSID        "FBI_Security_Van"
#define PASSWORD    "22 Rue du 14 Juillet"

#define STR_STATUS_OK       "OK"
#define STR_STATUS_ERROR    "ERROR"

ESP8266 wifi(Serial2);

/* -------------------- Init -------------------- */

void initServer(void)
{
    Serial.begin(9600);
    Serial.print("Setup begin\r\n");

    if (!wifi.setOprToStationSoftAP()) {
        Serial.print("OPR to station failed\r\n");
    }

    if (wifi.joinAP(SSID, PASSWORD)) {
        Serial.print("IP: ");
        Serial.println(wifi.getLocalIP().c_str());
    } else {
        Serial.print("Join AP failure\r\n");
    }

    if (!wifi.enableMUX()) {
        Serial.print("Enable MUX failed\r\n");
    }

    if (wifi.startTCPServer(8090)) {
        Serial.print("TCP server started\r\n");
    } else {
        Serial.print("TCP server failed to start\r\n");
    }

    if (!wifi.setTCPServerTimeout(10)) {
        Serial.print("Set timeout failed\r\n");
    }
}

void setup(void)
{
    initServer();
}

/* -------------------- Loop -------------------- */

void buildJsonResponse(aJsonObject* response, const char* status, aJsonObject* body = NULL, const char* message = "")
{
    aJson.addStringToObject(response, "status", status);
    aJson.addStringToObject(response, "message", message);
    if (body != NULL) {
        aJson.addItemToObject(response, "body", body);
    }
}

aJsonObject* processCommand(String command)
{
    aJsonObject* response = aJson.createObject();

    if (command.compareTo("get_status_current")) {
        buildJsonResponse(response, STR_STATUS_OK, NULL, "It works !");
    }
//    else if (command.compareTo("get_status_history")) {
//        //TODO (add dateFrom to request)
//    }
    else
        buildJsonResponse(response, STR_STATUS_ERROR, NULL, "Unknown command");
    return response;
}

void loop(void)
{
    uint8_t buffer[128] = {0};
    uint8_t muxId;

    // Wait for a request
    uint32_t requestLen = wifi.recv(&muxId, buffer, sizeof(buffer));

    if (requestLen > 0) {
        Serial.print("Got something intresting\n");

        // Convert buffer to char[]
        char charBuffer[128];
        for (uint32_t i = 0; i < requestLen; i++) {
            charBuffer[i] = (char)(buffer[i]);
        }

        // Get response
        aJsonObject* responseJson = processCommand(charBuffer);
        String response = aJson.print(responseJson);
        Serial.println(response);

        // Put response in buffer
        for (uint32_t i = 0; i < response.length(); i++) {
            buffer[i] = (uint8_t)(response[i]);
        }

        // Send response
        if (!wifi.send(muxId, buffer, response.length())) {
            Serial.print("Send response failed\r\n");
        }

        // Release TCP connection
        if (!wifi.releaseTCP(muxId)) {
            Serial.print("Release tcp failed for :");
            Serial.print(muxId);
        }
    }
}

<commit_msg>Add TODO<commit_after>//
// Created by piouffb on 12/19/15.
//

#include "aJSON.h"
#include "ESP8266.h"
#include "main.h"

#define SSID        "FBI_Security_Van"
#define PASSWORD    "22 Rue du 14 Juillet"

#define STR_STATUS_OK       "OK"
#define STR_STATUS_ERROR    "ERROR"

ESP8266 wifi(Serial2);

/* -------------------- Init -------------------- */

void initServer(void)
{
    Serial.begin(9600);
    Serial.print("Setup begin\r\n");

    if (!wifi.setOprToStationSoftAP()) {
        Serial.print("OPR to station failed\r\n");
    }

    if (wifi.joinAP(SSID, PASSWORD)) {
        Serial.print("IP: ");
        Serial.println(wifi.getLocalIP().c_str());
    } else {
        Serial.print("Join AP failure\r\n");
    }

    if (!wifi.enableMUX()) {
        Serial.print("Enable MUX failed\r\n");
    }

    if (wifi.startTCPServer(8090)) {
        Serial.print("TCP server started\r\n");
    } else {
        Serial.print("TCP server failed to start\r\n");
    }

    if (!wifi.setTCPServerTimeout(10)) {
        Serial.print("Set timeout failed\r\n");
    }
}

void setup(void)
{
    initServer();
}

/* -------------------- Loop -------------------- */

void buildJsonResponse(aJsonObject* response, const char* status, aJsonObject* body = NULL, const char* message = "")
{
    aJson.addStringToObject(response, "status", status);
    aJson.addStringToObject(response, "message", message);
    if (body != NULL) {
        aJson.addItemToObject(response, "body", body);
    }
}

aJsonObject* processCommand(String command)
{
    aJsonObject* response = aJson.createObject();

    if (command.compareTo("get_status_current")) {
        buildJsonResponse(response, STR_STATUS_OK, NULL, "It works !");
    }
//    else if (command.compareTo("get_status_history")) {
//        //TODO (add dateFrom to request)
//    }
    else
        buildJsonResponse(response, STR_STATUS_ERROR, NULL, "Unknown command");
    return response;
}

void loop(void)
{
    uint8_t buffer[128] = {0};
    uint8_t muxId;

    // Wait for a request
    uint32_t requestLen = wifi.recv(&muxId, buffer, sizeof(buffer));

    if (requestLen > 0) {
        Serial.print("Got something intresting\n");

        // Convert buffer to char[]
        char charBuffer[128];
        for (uint32_t i = 0; i < requestLen; i++) {
            charBuffer[i] = (char)(buffer[i]);
        }

        // Get response
        aJsonObject* responseJson = processCommand(charBuffer);
        //TODO: Delete JSON Object
        String response = aJson.print(responseJson);
        Serial.println(response);

        // Put response in buffer
        for (uint32_t i = 0; i < response.length(); i++) {
            buffer[i] = (uint8_t)(response[i]);
        }

        // Send response
        if (!wifi.send(muxId, buffer, response.length())) {
            Serial.print("Send response failed\r\n");
        }

        // Release TCP connection
        if (!wifi.releaseTCP(muxId)) {
            Serial.print("Release tcp failed for :");
            Serial.print(muxId);
        }
    }
}

<|endoftext|>