<commit_before>///////////////////////////////////////////////////////////////////////////////
// FILE:          MCCDAQ.cpp
// PROJECT:       Micro-Manager
// SUBSYSTEM:     DeviceAdapters
//-----------------------------------------------------------------------------
// DESCRIPTION:   Measurement Computing DAQ Adapter
// COPYRIGHT:     University of California, Berkeley, 2013
// LICENSE:       This file is distributed under the BSD license.
//                License text is included with the source distribution.
//
//                This file is distributed in the hope that it will be useful,
//                but WITHOUT ANY WARRANTY; without even the implied warranty
//                of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
//                IN NO EVENT SHALL THE COPYRIGHT OWNER OR
//                CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
//                INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES.
// AUTHOR:        Matthew Bakalar, matthew.bakalar@gmail.com, 08/22/2013
//
//

#include "MCCDAQ.h"
#include "../../MMDevice/ModuleInterface.h"
#include "cbw.h"
#include <sstream>

const int BOARDREADY = 100;

/* MCCDaq board handle */

typedef struct tag_board {
   int initialized;		/* board has been initialized == BOARDREADY */
   int board_num;         /* board number            */
   int analog_range;	/* voltage range for analog output */
   int status;       /* board error status       */
} MCCBoard;

static MCCBoard board;

const int NUM_CHANNELS = 8;

inline std::string DeviceNameMCCShutter(int channel)
{
   std::ostringstream chanStrm;
   chanStrm << channel;
   return "MCC-Shutter-" + chanStrm.str();
}

inline std::string DeviceNameMCCDA(int channel)
{
   std::ostringstream chanStrm;
   chanStrm << channel;
   return "MCC-DAC-" + chanStrm.str();
}

const char* g_volts = "Volts";
const char* g_PropertyMin = "MinV";
const char* g_PropertyMax = "MaxV";

// Global state of the shutter to enable simulation of the shutter device.
// The virtual shutter device uses this global variable to restore state of the switch
unsigned g_shutterState = 0;

using namespace std;

/*
 * Initilize the global board handle
 */
int InitializeTheBoard()
{
   if (board.initialized == BOARDREADY)
      return DEVICE_OK;

   // initialize the board. Use board number 0
   // #TODO Load board number, voltage range as a parameter
   board.board_num = 0;
   board.analog_range = UNI5VOLTS;
   board.initialized = BOARDREADY;

   return DEVICE_OK;
}

///////////////////////////////////////////////////////////////////////////////
// Exported MMDevice API
///////////////////////////////////////////////////////////////////////////////
MODULE_API void InitializeModuleData()
{
   for (int i = 0; i < NUM_CHANNELS; i++)
   {
      std::ostringstream chanStrm;
      chanStrm << i;

      RegisterDevice(DeviceNameMCCShutter(i).c_str(), MM::ShutterDevice,
            ("MCC DAQ Shutter " + chanStrm.str()).c_str());
      RegisterDevice(DeviceNameMCCDA(i).c_str(), MM::SignalIODevice,
            ("MCC DAQ DA " + chanStrm.str()).c_str());
   }
}

MODULE_API MM::Device* CreateDevice(const char* deviceName)
{
   if (deviceName == 0)
      return 0;

   for (int i = 0; i < NUM_CHANNELS; i++)
   {
      if (deviceName == DeviceNameMCCShutter(i))
         return new MCCDaqShutter(i, deviceName);
      if (deviceName == DeviceNameMCCDA(i))
         return new MCCDaqDA(i, deviceName);
   }

   return 0;
}

MODULE_API void DeleteDevice(MM::Device* pDevice)
{
   delete pDevice;
}

///////////////////////////////////////////////////////////////////////////////
// MCCDaqDA implementation
// ~~~~~~~~~~~~~~~~~~~~~~

MCCDaqDA::MCCDaqDA(unsigned channel, const char* name) :
      busy_(false), minV_(0.0), maxV_(0.0), volts_(0.0), gatedVolts_(0.0), encoding_(0), resolution_(0), channel_(channel), name_(name), gateOpen_(true)
{
   InitializeDefaultErrorMessages();

   // add custom error messages
   SetErrorText(ERR_UNKNOWN_POSITION, "Invalid position (state) specified");
   SetErrorText(ERR_INITIALIZE_FAILED, "Initialization of the device failed");
   SetErrorText(ERR_WRITE_FAILED, "Failed to write data to the device");
   SetErrorText(ERR_CLOSE_FAILED, "Failed closing the device");

   SetErrorText(BADBOARD, "MCC bad board error");
   SetErrorText(BADPORTNUM, "MCC bad port num error");
   SetErrorText(BADADCHAN, "Bad AD channel error");

   CreateProperty(g_PropertyMin, "0.0", MM::Float, false, 0, true);
   CreateProperty(g_PropertyMax, "5.0", MM::Float, false, 0, true);

}

MCCDaqDA::~MCCDaqDA()
{
   Shutdown();
}

void MCCDaqDA::GetName(char* name) const
{
   CDeviceUtils::CopyLimitedString(name, name_.c_str());
}

int MCCDaqDA::Initialize()
{
   int ret = InitializeTheBoard();
   if (ret != DEVICE_OK)
      return ret;

   // obtain scaling info
   minV_ = 0.0;
   maxV_ = 5.0;

   // set property list
   // -----------------

   // Name
   int nRet = CreateProperty(MM::g_Keyword_Name, name_.c_str(), MM::String, true);
   if (DEVICE_OK != nRet)
      return nRet;

   // Description
   nRet = CreateProperty(MM::g_Keyword_Description, "MCC DAC driver", MM::String, true);
   if (DEVICE_OK != nRet)
      return nRet;

   // Voltage
   // -----
   CPropertyAction* pAct = new CPropertyAction (this, &MCCDaqDA::OnVolts);
   nRet = CreateProperty(g_volts, "0.0", MM::Float, false, pAct);
   if (nRet != DEVICE_OK)
      return nRet;

   nRet = GetProperty(g_PropertyMin, minV_);
   assert (nRet == DEVICE_OK);

   nRet = GetProperty(g_PropertyMax, maxV_);
   assert (nRet == DEVICE_OK);

   nRet = SetPropertyLimits(g_volts, minV_, maxV_);
   if (nRet != DEVICE_OK)
      return nRet;

   nRet = UpdateStatus();
   if (nRet != DEVICE_OK)
      return nRet;

   initialized_ = true;

   return DEVICE_OK;
}

int MCCDaqDA::Shutdown()
{
   initialized_ = false;
   return DEVICE_OK;
}

int MCCDaqDA::SetGateOpen(bool open)
{
   gateOpen_ = open;
   SetVolts(volts_);
   return DEVICE_OK;
}

int MCCDaqDA::SetSignal(double volts)
{
   return SetProperty(g_volts, CDeviceUtils::ConvertToString(volts));
}

int MCCDaqDA::WriteToPort(unsigned short value)
{
   int ret = cbAOut(board.board_num, channel_, board.analog_range, value);
   if (ret != NOERRORS)
      return ret;

   return DEVICE_OK;
}

int MCCDaqDA::SetVolts(double volts)
{
   if (volts > maxV_ || volts < minV_)
      return DEVICE_RANGE_EXCEEDED;

   volts_ = volts;

   unsigned short value = (unsigned short) ( (volts/maxV_) * 65535 );
   if (!gateOpen_)
      value = 0;

   return WriteToPort(value);
}

int MCCDaqDA::GetLimits(double& minVolts, double& maxVolts)
{
   int nRet = GetProperty(g_PropertyMin, minVolts);
   if (nRet == DEVICE_OK)
      return nRet;

   nRet = GetProperty(g_PropertyMax, maxVolts);
   if (nRet == DEVICE_OK)
      return nRet;

   return DEVICE_OK;
}

///////////////////////////////////////////////////////////////////////////////
// Action handlers
///////////////////////////////////////////////////////////////////////////////

int MCCDaqDA::OnVolts(MM::PropertyBase* pProp, MM::ActionType eAct)
{
   if (eAct == MM::BeforeGet)
   {
      // nothing to do, let the caller to use cached property
   }
   else if (eAct == MM::AfterSet)
   {
      double volts;
      pProp->Get(volts);
      return SetVolts(volts);
   }

   return DEVICE_OK;
}

///////////////////////////////////////////////////////////////////////////////
// MCCDaq implementation
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~

MCCDaqShutter::MCCDaqShutter(unsigned channel, const char* name) : initialized_(false), channel_(channel), name_(name), openTimeUs_(0), ports_()
{
   InitializeDefaultErrorMessages();
   EnableDelay();

   SetErrorText(BADBOARD, "MCC bad board error");
   SetErrorText(BADPORTNUM, "MCC bad port num error");
   SetErrorText(BADADCHAN, "Bad AD channel error");
}

MCCDaqShutter::~MCCDaqShutter()
{
   Shutdown();
}

void MCCDaqShutter::GetName(char* name) const
{
   assert(name_.length() < CDeviceUtils::GetMaxStringLength());
   CDeviceUtils::CopyLimitedString(name, name_.c_str());
}

bool MCCDaqShutter::Busy()
{
   long interval = GetClockTicksUs() - openTimeUs_;

   if (interval/1000.0 < GetDelayMs() && interval > 0)
   {
      return true;
   }
   else
   {
       return false;
   }
}

int MCCDaqShutter::Initialize()
{
   int ret = InitializeTheBoard();
   if (ret != DEVICE_OK)
      return ret;

   // Define ports for this shutter
   // #TODO Load port from parameter
   // ports_.push_back(FIRSTPORTA);
   ports_.push_back(FIRSTPORTB);

   // initialize the digital ports as output ports
   // #TODO Only initialize the needed ports
   for(unsigned int i=0; i < ports_.size(); i++)
   {
	   int ret = cbDConfigPort(board.board_num, ports_[i], DIGITALOUT);
	   if (ret != NOERRORS)
	   	   return ret;
   }

   // set property list
   // -----------------

   // Name
   ret = CreateProperty(MM::g_Keyword_Name, name_.c_str(), MM::String, true);
   if (DEVICE_OK != ret)
      return ret;

   // Description
   ret = CreateProperty(MM::g_Keyword_Description, "MCC shutter driver", MM::String, true);
   if (DEVICE_OK != ret)
      return ret;

   // OnOff
   // ------
   CPropertyAction* pAct = new CPropertyAction (this, &MCCDaqShutter::OnOnOff);
   ret = CreateProperty("OnOff", "0", MM::Integer, false, pAct);
   if (ret != DEVICE_OK)
      return ret;

   // set shutter into the off state
   WriteToPort(0);

   vector<string> vals;
   vals.push_back("0");
   vals.push_back("1");
   ret = SetAllowedValues("OnOff", vals);
   if (ret != DEVICE_OK)
      return ret;

   ret = UpdateStatus();
   if (ret != DEVICE_OK)
      return ret;

   initialized_ = true;
   openTimeUs_ = GetClockTicksUs();

   return DEVICE_OK;
}

int MCCDaqShutter::Shutdown()
{
   if (initialized_)
   {
      initialized_ = false;
   }
   return DEVICE_OK;
}

int MCCDaqShutter::SetOpen(bool open)
{
   if (open)
      return SetProperty("OnOff", "1");
   else
      return SetProperty("OnOff", "0");
}

int MCCDaqShutter::GetOpen(bool& open)
{
   char buf[MM::MaxStrLength];
   int ret = GetProperty("OnOff", buf);
   if (ret != DEVICE_OK)
      return ret;
   long pos = atol(buf);
   pos > 0 ? open = true : open = false;

   return DEVICE_OK;
}

int MCCDaqShutter::Fire(double /*deltaT*/)
{
   return DEVICE_UNSUPPORTED_COMMAND;
}

int MCCDaqShutter::WriteToPort(unsigned short value)
{
   for(unsigned int i=0; i < ports_.size(); i++)
   {
	   int ret = cbDOut(board.board_num, ports_[i], value);
	   if (ret != NOERRORS)
	       return ret;
   }

   return DEVICE_OK;
}

///////////////////////////////////////////////////////////////////////////////
// Action handlers
///////////////////////////////////////////////////////////////////////////////

int MCCDaqShutter::OnOnOff(MM::PropertyBase* pProp, MM::ActionType eAct)
{
   if (eAct == MM::BeforeGet)
   {
      // use cached state
   }
   else if (eAct == MM::AfterSet)
   {
      long pos;
      pProp->Get(pos);
      int ret;
      if (pos == 0)
         ret = WriteToPort(0); // turn everything off
      else
         // ret = WriteToPort(g_switchState); // restore old setting
		 ret = WriteToPort(1 << channel_);
      if (ret != DEVICE_OK)
         return ret;
      g_shutterState = pos;
      openTimeUs_ = GetClockTicksUs();
   }

   return DEVICE_OK;
}
<commit_msg>[MCCDAQ] Initialize the board's voltage range based on the configured max voltage<commit_after>///////////////////////////////////////////////////////////////////////////////
// FILE:          MCCDAQ.cpp
// PROJECT:       Micro-Manager
// SUBSYSTEM:     DeviceAdapters
//-----------------------------------------------------------------------------
// DESCRIPTION:   Measurement Computing DAQ Adapter
// COPYRIGHT:     University of California, Berkeley, 2013
// LICENSE:       This file is distributed under the BSD license.
//                License text is included with the source distribution.
//
//                This file is distributed in the hope that it will be useful,
//                but WITHOUT ANY WARRANTY; without even the implied warranty
//                of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
//                IN NO EVENT SHALL THE COPYRIGHT OWNER OR
//                CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
//                INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES.
// AUTHOR:        Matthew Bakalar, matthew.bakalar@gmail.com, 08/22/2013
//
//

#include "MCCDAQ.h"
#include "../../MMDevice/ModuleInterface.h"
#include "cbw.h"
#include <sstream>

const int BOARDREADY = 100;

/* MCCDaq board handle */

typedef struct tag_board {
   int initialized;		/* board has been initialized == BOARDREADY */
   int board_num;         /* board number            */
   int analog_range;	/* voltage range for analog output */
   int status;       /* board error status       */
} MCCBoard;

static MCCBoard board;

const int NUM_CHANNELS = 8;

inline std::string DeviceNameMCCShutter(int channel)
{
   std::ostringstream chanStrm;
   chanStrm << channel;
   return "MCC-Shutter-" + chanStrm.str();
}

inline std::string DeviceNameMCCDA(int channel)
{
   std::ostringstream chanStrm;
   chanStrm << channel;
   return "MCC-DAC-" + chanStrm.str();
}

const char* g_volts = "Volts";
const char* g_PropertyMin = "MinV";
const char* g_PropertyMax = "MaxV";

// Global state of the shutter to enable simulation of the shutter device.
// The virtual shutter device uses this global variable to restore state of the switch
unsigned g_shutterState = 0;

using namespace std;

/*
 * Initilize the global board handle
 * voltMode should be e.g. UNI5VOLTS, UNI10VOLTS, etc.
 */
int InitializeTheBoard(int voltMode)
{
   if (board.initialized == BOARDREADY)
      return DEVICE_OK;

   // initialize the board. Use board number 0
   // #TODO Load board number, voltage range as a parameter
   board.board_num = 0;
   board.analog_range = voltMode;

   board.initialized = BOARDREADY;

   return DEVICE_OK;
}

///////////////////////////////////////////////////////////////////////////////
// Exported MMDevice API
///////////////////////////////////////////////////////////////////////////////
MODULE_API void InitializeModuleData()
{
   for (int i = 0; i < NUM_CHANNELS; i++)
   {
      std::ostringstream chanStrm;
      chanStrm << i;

      RegisterDevice(DeviceNameMCCShutter(i).c_str(), MM::ShutterDevice,
            ("MCC DAQ Shutter " + chanStrm.str()).c_str());
      RegisterDevice(DeviceNameMCCDA(i).c_str(), MM::SignalIODevice,
            ("MCC DAQ DA " + chanStrm.str()).c_str());
   }
}

MODULE_API MM::Device* CreateDevice(const char* deviceName)
{
   if (deviceName == 0)
      return 0;

   for (int i = 0; i < NUM_CHANNELS; i++)
   {
      if (deviceName == DeviceNameMCCShutter(i))
         return new MCCDaqShutter(i, deviceName);
      if (deviceName == DeviceNameMCCDA(i))
         return new MCCDaqDA(i, deviceName);
   }

   return 0;
}

MODULE_API void DeleteDevice(MM::Device* pDevice)
{
   delete pDevice;
}

///////////////////////////////////////////////////////////////////////////////
// MCCDaqDA implementation
// ~~~~~~~~~~~~~~~~~~~~~~

MCCDaqDA::MCCDaqDA(unsigned channel, const char* name) :
      busy_(false), minV_(0.0), maxV_(0.0), volts_(0.0), gatedVolts_(0.0), encoding_(0), resolution_(0), channel_(channel), name_(name), gateOpen_(true)
{
   InitializeDefaultErrorMessages();

   // add custom error messages
   SetErrorText(ERR_UNKNOWN_POSITION, "Invalid position (state) specified");
   SetErrorText(ERR_INITIALIZE_FAILED, "Initialization of the device failed");
   SetErrorText(ERR_WRITE_FAILED, "Failed to write data to the device");
   SetErrorText(ERR_CLOSE_FAILED, "Failed closing the device");

   SetErrorText(BADBOARD, "MCC bad board error");
   SetErrorText(BADPORTNUM, "MCC bad port num error");
   SetErrorText(BADADCHAN, "Bad AD channel error");

   CreateProperty(g_PropertyMin, "0.0", MM::Float, false, 0, true);
   CreateProperty(g_PropertyMax, "5.0", MM::Float, false, 0, true);

}

MCCDaqDA::~MCCDaqDA()
{
   Shutdown();
}

void MCCDaqDA::GetName(char* name) const
{
   CDeviceUtils::CopyLimitedString(name, name_.c_str());
}

int MCCDaqDA::Initialize()
{
   // obtain scaling info
   minV_ = 0.0;
   maxV_ = 5.0;

   // set property list
   // -----------------

   // Name
   int nRet = CreateProperty(MM::g_Keyword_Name, name_.c_str(), MM::String, true);
   if (DEVICE_OK != nRet)
      return nRet;

   // Description
   nRet = CreateProperty(MM::g_Keyword_Description, "MCC DAC driver", MM::String, true);
   if (DEVICE_OK != nRet)
      return nRet;

   // Voltage
   // -----
   CPropertyAction* pAct = new CPropertyAction (this, &MCCDaqDA::OnVolts);
   nRet = CreateProperty(g_volts, "0.0", MM::Float, false, pAct);
   if (nRet != DEVICE_OK)
      return nRet;

   nRet = GetProperty(g_PropertyMin, minV_);
   assert (nRet == DEVICE_OK);

   nRet = GetProperty(g_PropertyMax, maxV_);
   assert (nRet == DEVICE_OK);

   nRet = SetPropertyLimits(g_volts, minV_, maxV_);
   if (nRet != DEVICE_OK)
      return nRet;

   // There are a ton of supported voltage modes, and we can't use a switch
   // statement because of the floating point nature. This approach involves
   // a lot of redundant assignments but is straightforward.
   int voltMode = UNIPT01VOLTS;
   if (maxV_ > .01) {
      voltMode = UNIPT02VOLTS;
   }
   if (maxV_ > .02) {
      voltMode = UNIPT05VOLTS;
   }
   if (maxV_ > .05) {
      voltMode = UNIPT1VOLTS;
   }
   if (maxV_ > .1) {
      voltMode = UNIPT2VOLTS;
   }
   if (maxV_ > .2) {
      voltMode = UNIPT25VOLTS;
   }
   if (maxV_ > .25) {
      voltMode = UNIPT5VOLTS;
   }
   if (maxV_ > .5) {
      voltMode = UNI1VOLTS;
   }
   if (maxV_ > 1) {
      voltMode = UNI1PT25VOLTS;
   }
   if (maxV_ > 1.25) {
      voltMode = UNI1PT67VOLTS;
   }
   if (maxV_ > 1.67) {
      voltMode = UNI2VOLTS;
   }
   if (maxV_ > 2) {
      voltMode = UNI2PT5VOLTS;
   }
   if (maxV_ > 2.5) {
      voltMode = UNI4VOLTS;
   }
   if (maxV_ > 4) {
      voltMode = UNI5VOLTS;
   }
   if (maxV_ > 5) {
      voltMode = UNI10VOLTS;
   }

   int ret = InitializeTheBoard(voltMode);
   if (ret != DEVICE_OK)
      return ret;

   nRet = UpdateStatus();
   if (nRet != DEVICE_OK)
      return nRet;

   initialized_ = true;

   return DEVICE_OK;
}

int MCCDaqDA::Shutdown()
{
   initialized_ = false;
   return DEVICE_OK;
}

int MCCDaqDA::SetGateOpen(bool open)
{
   gateOpen_ = open;
   SetVolts(volts_);
   return DEVICE_OK;
}

int MCCDaqDA::SetSignal(double volts)
{
   return SetProperty(g_volts, CDeviceUtils::ConvertToString(volts));
}

int MCCDaqDA::WriteToPort(unsigned short value)
{
   int ret = cbAOut(board.board_num, channel_, board.analog_range, value);
   if (ret != NOERRORS)
      return ret;

   return DEVICE_OK;
}

int MCCDaqDA::SetVolts(double volts)
{
   if (volts > maxV_ || volts < minV_)
      return DEVICE_RANGE_EXCEEDED;

   volts_ = volts;

   unsigned short value = (unsigned short) ( (volts/maxV_) * 65535 );
   if (!gateOpen_)
      value = 0;

   return WriteToPort(value);
}

int MCCDaqDA::GetLimits(double& minVolts, double& maxVolts)
{
   int nRet = GetProperty(g_PropertyMin, minVolts);
   if (nRet == DEVICE_OK)
      return nRet;

   nRet = GetProperty(g_PropertyMax, maxVolts);
   if (nRet == DEVICE_OK)
      return nRet;

   return DEVICE_OK;
}

///////////////////////////////////////////////////////////////////////////////
// Action handlers
///////////////////////////////////////////////////////////////////////////////

int MCCDaqDA::OnVolts(MM::PropertyBase* pProp, MM::ActionType eAct)
{
   if (eAct == MM::BeforeGet)
   {
      // nothing to do, let the caller to use cached property
   }
   else if (eAct == MM::AfterSet)
   {
      double volts;
      pProp->Get(volts);
      return SetVolts(volts);
   }

   return DEVICE_OK;
}

///////////////////////////////////////////////////////////////////////////////
// MCCDaq implementation
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~

MCCDaqShutter::MCCDaqShutter(unsigned channel, const char* name) : initialized_(false), channel_(channel), name_(name), openTimeUs_(0), ports_()
{
   InitializeDefaultErrorMessages();
   EnableDelay();

   SetErrorText(BADBOARD, "MCC bad board error");
   SetErrorText(BADPORTNUM, "MCC bad port num error");
   SetErrorText(BADADCHAN, "Bad AD channel error");
}

MCCDaqShutter::~MCCDaqShutter()
{
   Shutdown();
}

void MCCDaqShutter::GetName(char* name) const
{
   assert(name_.length() < CDeviceUtils::GetMaxStringLength());
   CDeviceUtils::CopyLimitedString(name, name_.c_str());
}

bool MCCDaqShutter::Busy()
{
   long interval = GetClockTicksUs() - openTimeUs_;

   if (interval/1000.0 < GetDelayMs() && interval > 0)
   {
      return true;
   }
   else
   {
       return false;
   }
}

int MCCDaqShutter::Initialize()
{
   int ret = InitializeTheBoard(UNI5VOLTS);
   if (ret != DEVICE_OK)
      return ret;

   // Define ports for this shutter
   // #TODO Load port from parameter
   // ports_.push_back(FIRSTPORTA);
   ports_.push_back(FIRSTPORTB);

   // initialize the digital ports as output ports
   // #TODO Only initialize the needed ports
   for(unsigned int i=0; i < ports_.size(); i++)
   {
	   int ret = cbDConfigPort(board.board_num, ports_[i], DIGITALOUT);
	   if (ret != NOERRORS)
	   	   return ret;
   }

   // set property list
   // -----------------

   // Name
   ret = CreateProperty(MM::g_Keyword_Name, name_.c_str(), MM::String, true);
   if (DEVICE_OK != ret)
      return ret;

   // Description
   ret = CreateProperty(MM::g_Keyword_Description, "MCC shutter driver", MM::String, true);
   if (DEVICE_OK != ret)
      return ret;

   // OnOff
   // ------
   CPropertyAction* pAct = new CPropertyAction (this, &MCCDaqShutter::OnOnOff);
   ret = CreateProperty("OnOff", "0", MM::Integer, false, pAct);
   if (ret != DEVICE_OK)
      return ret;

   // set shutter into the off state
   WriteToPort(0);

   vector<string> vals;
   vals.push_back("0");
   vals.push_back("1");
   ret = SetAllowedValues("OnOff", vals);
   if (ret != DEVICE_OK)
      return ret;

   ret = UpdateStatus();
   if (ret != DEVICE_OK)
      return ret;

   initialized_ = true;
   openTimeUs_ = GetClockTicksUs();

   return DEVICE_OK;
}

int MCCDaqShutter::Shutdown()
{
   if (initialized_)
   {
      initialized_ = false;
   }
   return DEVICE_OK;
}

int MCCDaqShutter::SetOpen(bool open)
{
   if (open)
      return SetProperty("OnOff", "1");
   else
      return SetProperty("OnOff", "0");
}

int MCCDaqShutter::GetOpen(bool& open)
{
   char buf[MM::MaxStrLength];
   int ret = GetProperty("OnOff", buf);
   if (ret != DEVICE_OK)
      return ret;
   long pos = atol(buf);
   pos > 0 ? open = true : open = false;

   return DEVICE_OK;
}

int MCCDaqShutter::Fire(double /*deltaT*/)
{
   return DEVICE_UNSUPPORTED_COMMAND;
}

int MCCDaqShutter::WriteToPort(unsigned short value)
{
   for(unsigned int i=0; i < ports_.size(); i++)
   {
	   int ret = cbDOut(board.board_num, ports_[i], value);
	   if (ret != NOERRORS)
	       return ret;
   }

   return DEVICE_OK;
}

///////////////////////////////////////////////////////////////////////////////
// Action handlers
///////////////////////////////////////////////////////////////////////////////

int MCCDaqShutter::OnOnOff(MM::PropertyBase* pProp, MM::ActionType eAct)
{
   if (eAct == MM::BeforeGet)
   {
      // use cached state
   }
   else if (eAct == MM::AfterSet)
   {
      long pos;
      pProp->Get(pos);
      int ret;
      if (pos == 0)
         ret = WriteToPort(0); // turn everything off
      else
         // ret = WriteToPort(g_switchState); // restore old setting
		 ret = WriteToPort(1 << channel_);
      if (ret != DEVICE_OK)
         return ret;
      g_shutterState = pos;
      openTimeUs_ = GetClockTicksUs();
   }

   return DEVICE_OK;
}
<|endoftext|>


<commit_before>/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright 2011-2018 Dominik Charousset                                     *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

#pragma once

#include "caf/config.hpp"

#include <new>
#include <memory>
#include <ostream>
#include <type_traits>

#include "caf/unit.hpp"
#include "caf/error.hpp"
#include "caf/unifyn.hpp"

namespace caf {

/// Helper class to construct an `expected<T>` that represents no error.
/// @relates expected
struct no_error_t {};

/// The only instance of ::no_error_t.
/// @relates expected
constexpr no_error_t no_error = no_error_t{};

/// Represents the result of a computation which can either complete
/// successfully with an instance of type `T` or fail with an `error`.
/// @tparam T The type of the result.
template <typename T>
class expected {
public:
  // -- member types -----------------------------------------------------------

  using value_type = T;

  // -- static member variables ------------------------------------------------

  /// Stores whether move construct and move assign never throw.
  static constexpr bool nothrow_move
    = std::is_nothrow_move_constructible<T>::value
      && std::is_nothrow_move_assignable<T>::value;

  /// Stores whether copy construct and copy assign never throw.
  static constexpr bool nothrow_copy
    = std::is_nothrow_copy_constructible<T>::value
      && std::is_nothrow_copy_assignable<T>::value;

  // -- constructors, destructors, and assignment operators --------------------

  template <class U>
  expected(U x,
           typename std::enable_if<std::is_convertible<U, T>::value>::type* = nullptr)
      : engaged_(true) {
    new (&value_) T(std::move(x));
  }

  expected(T&& x) noexcept(nothrow_move) : engaged_(true) {
    new (&value_) T(std::move(x));
  }

  expected(const T& x) noexcept(nothrow_copy) : engaged_(true) {
    new (&value_) T(x);
  }

  expected(caf::error e) noexcept : engaged_(false) {
    new (&error_) caf::error{std::move(e)};
  }

  expected(no_error_t) noexcept : engaged_(false) {
    new (&error_) caf::error{};
  }

  expected(const expected& other) noexcept(nothrow_copy) {
    construct(other);
  }

  template <class Code, class E = enable_if_has_make_error_t<Code>>
  expected(Code code) : engaged_(false) {
    new (&error_) caf::error(make_error(code));
  }

  expected(expected&& other) noexcept(nothrow_move) {
    construct(std::move(other));
  }

  ~expected() {
    destroy();
  }

  expected& operator=(const expected& other) noexcept(nothrow_copy) {
    if (engaged_ && other.engaged_)
      value_ = other.value_;
    else if (!engaged_ && !other.engaged_)
      error_ = other.error_;
    else {
      destroy();
      construct(other);
    }
    return *this;
  }

  expected& operator=(expected&& other) noexcept(nothrow_move) {
    if (engaged_ && other.engaged_)
      value_ = std::move(other.value_);
    else if (!engaged_ && !other.engaged_)
      error_ = std::move(other.error_);
    else {
      destroy();
      construct(std::move(other));
    }
    return *this;
  }

  expected& operator=(const T& x) noexcept(nothrow_copy) {
    if (engaged_) {
      value_ = x;
    } else {
      destroy();
      engaged_ = true;
      new (&value_) T(x);
    }
    return *this;
  }

  expected& operator=(T&& x) noexcept(nothrow_move) {
    if (engaged_) {
      value_ = std::move(x);
    } else {
      destroy();
      engaged_ = true;
      new (&value_) T(std::move(x));
    }
    return *this;
  }

  template <class U>
  typename std::enable_if<std::is_convertible<U, T>::value, expected&>::type
  operator=(U x) {
    return *this = T{std::move(x)};
  }

  expected& operator=(caf::error e) noexcept {
    if (!engaged_)
      error_ = std::move(e);
    else {
      destroy();
      engaged_ = false;
      new (&error_) caf::error(std::move(e));
    }
    return *this;
  }

  template <class Code>
  enable_if_has_make_error_t<Code, expected&> operator=(Code code) {
    return *this = make_error(code);
  }

  // -- modifiers --------------------------------------------------------------

  /// @copydoc cvalue
  T& value() noexcept {
    CAF_ASSERT(engaged_);
    return value_;
  }

  /// @copydoc cvalue
  T& operator*() noexcept {
    return value();
  }

  /// @copydoc cvalue
  T* operator->() noexcept {
    return &value();
  }

  /// @copydoc cerror
  caf::error& error() noexcept {
    CAF_ASSERT(!engaged_);
    return error_;
  }

  // -- observers --------------------------------------------------------------

  /// Returns the contained value.
  /// @pre `engaged() == true`.
  const T& cvalue() const noexcept {
    CAF_ASSERT(engaged_);
    return value_;
  }

  /// @copydoc cvalue
  const T& value() const noexcept {
    CAF_ASSERT(engaged_);
    return value_;
  }

  /// @copydoc cvalue
  const T& operator*() const noexcept {
    return value();
  }

  /// @copydoc cvalue
  const T* operator->() const noexcept {
    return &value();
  }

  /// @copydoc engaged
  explicit operator bool() const noexcept {
    return engaged();
  }

  /// Returns `true` if the object holds a value (is engaged).
  bool engaged() const noexcept {
    return engaged_;
  }

  /// Returns the contained error.
  /// @pre `engaged() == false`.
  const caf::error& cerror() const noexcept {
    CAF_ASSERT(!engaged_);
    return error_;
  }

  /// @copydoc cerror
  const caf::error& error() const noexcept {
    CAF_ASSERT(!engaged_);
    return error_;
  }

private:
  void construct(expected&& other) noexcept(nothrow_move) {
    if (other.engaged_)
      new (&value_) T(std::move(other.value_));
    else
      new (&error_) caf::error(std::move(other.error_));
    engaged_ = other.engaged_;
  }

  void construct(const expected& other) noexcept(nothrow_copy) {
    if (other.engaged_)
      new (&value_) T(other.value_);
    else
      new (&error_) caf::error(other.error_);
    engaged_ = other.engaged_;
  }

  void destroy() {
    if (engaged_)
      value_.~T();
    else
      error_.~error();
  }

  bool engaged_;

  union {
    T value_;
    caf::error error_;
  };
};

/// @relates expected
template <class T>
auto operator==(const expected<T>& x, const expected<T>& y)
-> decltype(*x == *y) {
  return x && y ? *x == *y : (!x && !y ? x.error() == y.error() : false);
}

/// @relates expected
template <class T, class U>
auto operator==(const expected<T>& x, const U& y) -> decltype(*x == y) {
  return x ? *x == y : false;
}

/// @relates expected
template <class T, class U>
auto operator==(const T& x, const expected<U>& y) -> decltype(x == *y) {
  return y == x;
}

/// @relates expected
template <class T>
bool operator==(const expected<T>& x, const error& y) {
  return x ? false : x.error() == y;
}

/// @relates expected
template <class T>
bool operator==(const error& x, const expected<T>& y) {
  return y == x;
}

/// @relates expected
template <class T, class E>
enable_if_has_make_error_t<E, bool> operator==(const expected<T>& x, E y) {
  return x == make_error(y);
}

/// @relates expected
template <class T, class E>
enable_if_has_make_error_t<E, bool> operator==(E x, const expected<T>& y) {
  return y == make_error(x);
}

/// @relates expected
template <class T>
auto operator!=(const expected<T>& x, const expected<T>& y)
-> decltype(*x == *y) {
  return !(x == y);
}

/// @relates expected
template <class T, class U>
auto operator!=(const expected<T>& x, const U& y) -> decltype(*x == y) {
  return !(x == y);
}

/// @relates expected
template <class T, class U>
auto operator!=(const T& x, const expected<U>& y) -> decltype(x == *y) {
  return !(x == y);
}

/// @relates expected
template <class T>
bool operator!=(const expected<T>& x, const error& y) {
  return !(x == y);
}

/// @relates expected
template <class T>
bool operator!=(const error& x, const expected<T>& y) {
  return !(x == y);
}

/// @relates expected
template <class T, class E>
enable_if_has_make_error_t<E, bool> operator!=(const expected<T>& x, E y) {
  return !(x == y);
}

/// @relates expected
template <class T, class E>
enable_if_has_make_error_t<E, bool> operator!=(E x, const expected<T>& y) {
  return !(x == y);
}

/// The pattern `expected<void>` shall be used for functions that may generate
/// an error but would otherwise return `bool`.
template <>
class expected<void> {
public:
  expected() = default;

  expected(unit_t) noexcept {
    // nop
  }

  expected(no_error_t) noexcept {
    // nop
  }

  expected(caf::error e) noexcept : error_(std::move(e)) {
    // nop
  }

  expected(const expected& other)  noexcept : error_(other.error_) {
    // nop
  }

  expected(expected&& other) noexcept : error_(std::move(other.error_)) {
    // nop
  }

  template <class Code, class E = enable_if_has_make_error_t<Code>>
  expected(Code code) : error_(make_error(code)) {
    // nop
  }

  expected& operator=(const expected& other) = default;

  expected& operator=(expected&& other) noexcept {
    error_ = std::move(other.error_);
    return *this;
  }

  explicit operator bool() const {
    return !error_;
  }

  const caf::error& error() const {
    return error_;
  }

private:
  caf::error error_;
};

/// @relates expected
inline bool operator==(const expected<void>& x, const expected<void>& y) {
  return (x && y) || (!x && !y && x.error() == y.error());
}

/// @relates expected
inline bool operator!=(const expected<void>& x, const expected<void>& y) {
  return !(x == y);
}

template <>
class expected<unit_t> : public expected<void> {
public:
  using expected<void>::expected;
};

template <class T>
std::string to_string(const expected<T>& x) {
  if (x)
    return deep_to_string(*x);
  return "!" + to_string(x.error());
}

inline std::string to_string(const expected<void>& x) {
  if (x)
    return "unit";
  return "!" + to_string(x.error());
}

/// @cond PRIVATE
/// Assigns the value of `expr` (which must return an `expected`)
/// to a new variable named `var` or throws a `std::runtime_error` on error.
/// @relates expected
/// @experimental
#define CAF_EXP_THROW(var, expr)                                               \
  auto CAF_UNIFYN(tmp_var_) = expr;                                            \
  if (!CAF_UNIFYN(tmp_var_))                                                  \
    CAF_RAISE_ERROR(to_string(CAF_UNIFYN(tmp_var_).error()));                  \
  auto& var = *CAF_UNIFYN(tmp_var_)
/// @endcond

} // namespace caf

namespace std {

template <class T>
auto operator<<(ostream& oss, const caf::expected<T>& x)
-> decltype(oss << *x) {
  if (x)
    oss << *x;
  else
    oss << "!" << to_string(x.error());
  return oss;
}

} // namespace std

<commit_msg>Add missing include<commit_after>/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright 2011-2018 Dominik Charousset                                     *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

#pragma once

#include "caf/config.hpp"

#include <new>
#include <memory>
#include <ostream>
#include <type_traits>

#include "caf/deep_to_string.hpp"
#include "caf/error.hpp"
#include "caf/unifyn.hpp"
#include "caf/unit.hpp"

namespace caf {

/// Helper class to construct an `expected<T>` that represents no error.
/// @relates expected
struct no_error_t {};

/// The only instance of ::no_error_t.
/// @relates expected
constexpr no_error_t no_error = no_error_t{};

/// Represents the result of a computation which can either complete
/// successfully with an instance of type `T` or fail with an `error`.
/// @tparam T The type of the result.
template <typename T>
class expected {
public:
  // -- member types -----------------------------------------------------------

  using value_type = T;

  // -- static member variables ------------------------------------------------

  /// Stores whether move construct and move assign never throw.
  static constexpr bool nothrow_move
    = std::is_nothrow_move_constructible<T>::value
      && std::is_nothrow_move_assignable<T>::value;

  /// Stores whether copy construct and copy assign never throw.
  static constexpr bool nothrow_copy
    = std::is_nothrow_copy_constructible<T>::value
      && std::is_nothrow_copy_assignable<T>::value;

  // -- constructors, destructors, and assignment operators --------------------

  template <class U>
  expected(U x,
           typename std::enable_if<std::is_convertible<U, T>::value>::type* = nullptr)
      : engaged_(true) {
    new (&value_) T(std::move(x));
  }

  expected(T&& x) noexcept(nothrow_move) : engaged_(true) {
    new (&value_) T(std::move(x));
  }

  expected(const T& x) noexcept(nothrow_copy) : engaged_(true) {
    new (&value_) T(x);
  }

  expected(caf::error e) noexcept : engaged_(false) {
    new (&error_) caf::error{std::move(e)};
  }

  expected(no_error_t) noexcept : engaged_(false) {
    new (&error_) caf::error{};
  }

  expected(const expected& other) noexcept(nothrow_copy) {
    construct(other);
  }

  template <class Code, class E = enable_if_has_make_error_t<Code>>
  expected(Code code) : engaged_(false) {
    new (&error_) caf::error(make_error(code));
  }

  expected(expected&& other) noexcept(nothrow_move) {
    construct(std::move(other));
  }

  ~expected() {
    destroy();
  }

  expected& operator=(const expected& other) noexcept(nothrow_copy) {
    if (engaged_ && other.engaged_)
      value_ = other.value_;
    else if (!engaged_ && !other.engaged_)
      error_ = other.error_;
    else {
      destroy();
      construct(other);
    }
    return *this;
  }

  expected& operator=(expected&& other) noexcept(nothrow_move) {
    if (engaged_ && other.engaged_)
      value_ = std::move(other.value_);
    else if (!engaged_ && !other.engaged_)
      error_ = std::move(other.error_);
    else {
      destroy();
      construct(std::move(other));
    }
    return *this;
  }

  expected& operator=(const T& x) noexcept(nothrow_copy) {
    if (engaged_) {
      value_ = x;
    } else {
      destroy();
      engaged_ = true;
      new (&value_) T(x);
    }
    return *this;
  }

  expected& operator=(T&& x) noexcept(nothrow_move) {
    if (engaged_) {
      value_ = std::move(x);
    } else {
      destroy();
      engaged_ = true;
      new (&value_) T(std::move(x));
    }
    return *this;
  }

  template <class U>
  typename std::enable_if<std::is_convertible<U, T>::value, expected&>::type
  operator=(U x) {
    return *this = T{std::move(x)};
  }

  expected& operator=(caf::error e) noexcept {
    if (!engaged_)
      error_ = std::move(e);
    else {
      destroy();
      engaged_ = false;
      new (&error_) caf::error(std::move(e));
    }
    return *this;
  }

  template <class Code>
  enable_if_has_make_error_t<Code, expected&> operator=(Code code) {
    return *this = make_error(code);
  }

  // -- modifiers --------------------------------------------------------------

  /// @copydoc cvalue
  T& value() noexcept {
    CAF_ASSERT(engaged_);
    return value_;
  }

  /// @copydoc cvalue
  T& operator*() noexcept {
    return value();
  }

  /// @copydoc cvalue
  T* operator->() noexcept {
    return &value();
  }

  /// @copydoc cerror
  caf::error& error() noexcept {
    CAF_ASSERT(!engaged_);
    return error_;
  }

  // -- observers --------------------------------------------------------------

  /// Returns the contained value.
  /// @pre `engaged() == true`.
  const T& cvalue() const noexcept {
    CAF_ASSERT(engaged_);
    return value_;
  }

  /// @copydoc cvalue
  const T& value() const noexcept {
    CAF_ASSERT(engaged_);
    return value_;
  }

  /// @copydoc cvalue
  const T& operator*() const noexcept {
    return value();
  }

  /// @copydoc cvalue
  const T* operator->() const noexcept {
    return &value();
  }

  /// @copydoc engaged
  explicit operator bool() const noexcept {
    return engaged();
  }

  /// Returns `true` if the object holds a value (is engaged).
  bool engaged() const noexcept {
    return engaged_;
  }

  /// Returns the contained error.
  /// @pre `engaged() == false`.
  const caf::error& cerror() const noexcept {
    CAF_ASSERT(!engaged_);
    return error_;
  }

  /// @copydoc cerror
  const caf::error& error() const noexcept {
    CAF_ASSERT(!engaged_);
    return error_;
  }

private:
  void construct(expected&& other) noexcept(nothrow_move) {
    if (other.engaged_)
      new (&value_) T(std::move(other.value_));
    else
      new (&error_) caf::error(std::move(other.error_));
    engaged_ = other.engaged_;
  }

  void construct(const expected& other) noexcept(nothrow_copy) {
    if (other.engaged_)
      new (&value_) T(other.value_);
    else
      new (&error_) caf::error(other.error_);
    engaged_ = other.engaged_;
  }

  void destroy() {
    if (engaged_)
      value_.~T();
    else
      error_.~error();
  }

  bool engaged_;

  union {
    T value_;
    caf::error error_;
  };
};

/// @relates expected
template <class T>
auto operator==(const expected<T>& x, const expected<T>& y)
-> decltype(*x == *y) {
  return x && y ? *x == *y : (!x && !y ? x.error() == y.error() : false);
}

/// @relates expected
template <class T, class U>
auto operator==(const expected<T>& x, const U& y) -> decltype(*x == y) {
  return x ? *x == y : false;
}

/// @relates expected
template <class T, class U>
auto operator==(const T& x, const expected<U>& y) -> decltype(x == *y) {
  return y == x;
}

/// @relates expected
template <class T>
bool operator==(const expected<T>& x, const error& y) {
  return x ? false : x.error() == y;
}

/// @relates expected
template <class T>
bool operator==(const error& x, const expected<T>& y) {
  return y == x;
}

/// @relates expected
template <class T, class E>
enable_if_has_make_error_t<E, bool> operator==(const expected<T>& x, E y) {
  return x == make_error(y);
}

/// @relates expected
template <class T, class E>
enable_if_has_make_error_t<E, bool> operator==(E x, const expected<T>& y) {
  return y == make_error(x);
}

/// @relates expected
template <class T>
auto operator!=(const expected<T>& x, const expected<T>& y)
-> decltype(*x == *y) {
  return !(x == y);
}

/// @relates expected
template <class T, class U>
auto operator!=(const expected<T>& x, const U& y) -> decltype(*x == y) {
  return !(x == y);
}

/// @relates expected
template <class T, class U>
auto operator!=(const T& x, const expected<U>& y) -> decltype(x == *y) {
  return !(x == y);
}

/// @relates expected
template <class T>
bool operator!=(const expected<T>& x, const error& y) {
  return !(x == y);
}

/// @relates expected
template <class T>
bool operator!=(const error& x, const expected<T>& y) {
  return !(x == y);
}

/// @relates expected
template <class T, class E>
enable_if_has_make_error_t<E, bool> operator!=(const expected<T>& x, E y) {
  return !(x == y);
}

/// @relates expected
template <class T, class E>
enable_if_has_make_error_t<E, bool> operator!=(E x, const expected<T>& y) {
  return !(x == y);
}

/// The pattern `expected<void>` shall be used for functions that may generate
/// an error but would otherwise return `bool`.
template <>
class expected<void> {
public:
  expected() = default;

  expected(unit_t) noexcept {
    // nop
  }

  expected(no_error_t) noexcept {
    // nop
  }

  expected(caf::error e) noexcept : error_(std::move(e)) {
    // nop
  }

  expected(const expected& other)  noexcept : error_(other.error_) {
    // nop
  }

  expected(expected&& other) noexcept : error_(std::move(other.error_)) {
    // nop
  }

  template <class Code, class E = enable_if_has_make_error_t<Code>>
  expected(Code code) : error_(make_error(code)) {
    // nop
  }

  expected& operator=(const expected& other) = default;

  expected& operator=(expected&& other) noexcept {
    error_ = std::move(other.error_);
    return *this;
  }

  explicit operator bool() const {
    return !error_;
  }

  const caf::error& error() const {
    return error_;
  }

private:
  caf::error error_;
};

/// @relates expected
inline bool operator==(const expected<void>& x, const expected<void>& y) {
  return (x && y) || (!x && !y && x.error() == y.error());
}

/// @relates expected
inline bool operator!=(const expected<void>& x, const expected<void>& y) {
  return !(x == y);
}

template <>
class expected<unit_t> : public expected<void> {
public:
  using expected<void>::expected;
};

template <class T>
std::string to_string(const expected<T>& x) {
  if (x)
    return deep_to_string(*x);
  return "!" + to_string(x.error());
}

inline std::string to_string(const expected<void>& x) {
  if (x)
    return "unit";
  return "!" + to_string(x.error());
}

/// @cond PRIVATE
/// Assigns the value of `expr` (which must return an `expected`)
/// to a new variable named `var` or throws a `std::runtime_error` on error.
/// @relates expected
/// @experimental
#define CAF_EXP_THROW(var, expr)                                               \
  auto CAF_UNIFYN(tmp_var_) = expr;                                            \
  if (!CAF_UNIFYN(tmp_var_))                                                  \
    CAF_RAISE_ERROR(to_string(CAF_UNIFYN(tmp_var_).error()));                  \
  auto& var = *CAF_UNIFYN(tmp_var_)
/// @endcond

} // namespace caf

namespace std {

template <class T>
auto operator<<(ostream& oss, const caf::expected<T>& x)
-> decltype(oss << *x) {
  if (x)
    oss << *x;
  else
    oss << "!" << to_string(x.error());
  return oss;
}

} // namespace std

<|endoftext|>


<commit_before>/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright 2011-2018 Dominik Charousset                                     *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

#include "caf/detail/uri_impl.hpp"

#include "caf/detail/parser/read_uri.hpp"
#include "caf/error.hpp"
#include "caf/ip_address.hpp"
#include "caf/string_algorithms.hpp"

namespace caf {
namespace detail {

// -- constructors, destructors, and assignment operators ----------------------

uri_impl::uri_impl() : rc_(1) {
  // nop
}

// -- member variables ---------------------------------------------------------

uri_impl uri_impl::default_instance;

// -- modifiers ----------------------------------------------------------------

void uri_impl::assemble_str() {
  add_encoded(scheme);
  str += ':';
  if (authority.empty()) {
    CAF_ASSERT(!path.empty());
    add_encoded(path, true);
  } else {
    str += "//";
    if (!authority.userinfo.empty()) {
      add_encoded(authority.userinfo);
      str += '@';
    }
    auto addr = get_if<ip_address>(&authority.host);
    if (addr == nullptr) {
      add_encoded(get<std::string>(authority.host));
    } else {
      str += '[';
      str += to_string(*addr);
      str += ']';
    }
    if (authority.port != 0) {
      str += ':';
      str += std::to_string(authority.port);
    }
    if (!path.empty()) {
      addr += '/';
      add_encoded(path, true);
    }
  }
  if (!query.empty()) {
    str += '?';
    auto i = query.begin();
    auto add_kvp = [&](decltype(*i) kvp) {
      add_encoded(kvp.first);
      str += '=';
      add_encoded(kvp.second);
    };
    add_kvp(*i);
    ++i;
    for (; i != query.end(); ++i) {
      str += '&';
      add_kvp(*i);
    }
  }
  if (!fragment.empty()) {
    str += '#';
    add_encoded(fragment);
  }
}

void uri_impl::add_encoded(string_view x, bool is_path) {
  for (auto ch : x)
    switch (ch) {
      case '/':
        if (is_path) {
          str += ch;
          break;
        }
        // fall through
      case ' ':
      case ':':
      case '?':
      case '#':
      case '[':
      case ']':
      case '@':
      case '!':
      case '$':
      case '&':
      case '\'':
      case '"':
      case '(':
      case ')':
      case '*':
      case '+':
      case ',':
      case ';':
      case '=':
        str += '%';
        detail::append_hex(str, reinterpret_cast<uint8_t*>(&ch), 1);
        break;
      default:
        str += ch;
    }
}

// -- friend functions ---------------------------------------------------------

void intrusive_ptr_add_ref(const uri_impl* p) {
  p->rc_.fetch_add(1, std::memory_order_relaxed);
}

void intrusive_ptr_release(const uri_impl* p) {
  if (p->rc_ == 1 || p->rc_.fetch_sub(1, std::memory_order_acq_rel) == 1)
    delete p;
}

} // namespace detail
} // namespace caf
<commit_msg>Fix assembly of the path component<commit_after>/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright 2011-2018 Dominik Charousset                                     *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

#include "caf/detail/uri_impl.hpp"

#include "caf/detail/parser/read_uri.hpp"
#include "caf/error.hpp"
#include "caf/ip_address.hpp"
#include "caf/string_algorithms.hpp"

namespace caf {
namespace detail {

// -- constructors, destructors, and assignment operators ----------------------

uri_impl::uri_impl() : rc_(1) {
  // nop
}

// -- member variables ---------------------------------------------------------

uri_impl uri_impl::default_instance;

// -- modifiers ----------------------------------------------------------------

void uri_impl::assemble_str() {
  add_encoded(scheme);
  str += ':';
  if (authority.empty()) {
    CAF_ASSERT(!path.empty());
    add_encoded(path, true);
  } else {
    str += "//";
    if (!authority.userinfo.empty()) {
      add_encoded(authority.userinfo);
      str += '@';
    }
    auto addr = get_if<ip_address>(&authority.host);
    if (addr == nullptr) {
      add_encoded(get<std::string>(authority.host));
    } else {
      str += '[';
      str += to_string(*addr);
      str += ']';
    }
    if (authority.port != 0) {
      str += ':';
      str += std::to_string(authority.port);
    }
    if (!path.empty()) {
      str += '/';
      add_encoded(path, true);
    }
  }
  if (!query.empty()) {
    str += '?';
    auto i = query.begin();
    auto add_kvp = [&](decltype(*i) kvp) {
      add_encoded(kvp.first);
      str += '=';
      add_encoded(kvp.second);
    };
    add_kvp(*i);
    for (++i; i != query.end(); ++i) {
      str += '&';
      add_kvp(*i);
    }
  }
  if (!fragment.empty()) {
    str += '#';
    add_encoded(fragment);
  }
}

void uri_impl::add_encoded(string_view x, bool is_path) {
  for (auto ch : x)
    switch (ch) {
      case '/':
        if (is_path) {
          str += ch;
          break;
        }
        // fall through
      case ' ':
      case ':':
      case '?':
      case '#':
      case '[':
      case ']':
      case '@':
      case '!':
      case '$':
      case '&':
      case '\'':
      case '"':
      case '(':
      case ')':
      case '*':
      case '+':
      case ',':
      case ';':
      case '=':
        str += '%';
        detail::append_hex(str, reinterpret_cast<uint8_t*>(&ch), 1);
        break;
      default:
        str += ch;
    }
}

// -- friend functions ---------------------------------------------------------

void intrusive_ptr_add_ref(const uri_impl* p) {
  p->rc_.fetch_add(1, std::memory_order_relaxed);
}

void intrusive_ptr_release(const uri_impl* p) {
  if (p->rc_ == 1 || p->rc_.fetch_sub(1, std::memory_order_acq_rel) == 1)
    delete p;
}

} // namespace detail
} // namespace caf
<|endoftext|>


<commit_before>#include "union_storage.h"
#include "storage/memstorage.h"
#include "storage/capacitor.h"
#include "utils/exception.h"
#include "utils/locker.h"
#include "storage/page_manager.h"
#include <cassert>
#include <algorithm>

using namespace dariadb;
using namespace dariadb::storage;

class UnionStorage::Private {
public:
	Private(const PageManager::Params&page_storage_params,
		dariadb::storage::Capacitor::Params cap_params,
		dariadb::storage::UnionStorage::Limits limits) :
		mem_storage{ new MemoryStorage(page_storage_params.chunk_size) },
		_page_manager_params(page_storage_params),
		_cap_params(cap_params),
       _limits(limits)
	{
		dariadb::storage::ChunkPool::instance()->start(_limits.max_mem_chunks);

		mem_cap = new Capacitor(mem_storage, _cap_params);
		mem_storage_raw = dynamic_cast<MemoryStorage*>(mem_storage.get());
		assert(mem_storage_raw != nullptr);

		PageManager::start(_page_manager_params);

		auto open_chunks = PageManager::instance()->get_open_chunks();
        mem_storage_raw->append(open_chunks);
	}
	~Private() {
		this->flush();
        if (_limits.max_mem_chunks != 0) {
            auto all_chunks = this->mem_storage_raw->drop_all();
            PageManager::instance()->append(all_chunks); //use specified in ctor
        }
		delete mem_cap;
		PageManager::stop();

	}

	Time minTime() {
        std::lock_guard<std::recursive_mutex> lg(_locker);
        if(PageManager::instance()->chunks_in_cur_page()>0){
            return PageManager::instance()->minTime();
        }else{
            return mem_storage->minTime();
        }
	}

	Time maxTime() {
		return mem_storage->maxTime();
	}

	append_result append(const Meas::PMeas begin, const size_t size) {
		append_result result{};
		for (size_t i = 0; i < size; i++) {
			result = result + this->append(begin[i]);
		}
		return result;
	}

	append_result append(const Meas &value) {
		//std::lock_guard<std::mutex> lg(_locker);
		append_result result{};
        if (!mem_cap->append(value)){
        //if(mem_storage_raw->append(value).writed!=1){
            assert(false);
			result.ignored++;
		}
		else {
			result.writed++;
		}

        drop_old_chunks();
		return result;
	}

	void drop_old_chunks() {
		if (_limits.max_mem_chunks == 0) {
			if (_limits.old_mem_chunks != 0) {
                //std::lock_guard<std::mutex> lg(_drop_locker);
				auto old_chunks = mem_storage_raw->drop_old_chunks(_limits.old_mem_chunks);
                PageManager::instance()->append(old_chunks);
			}
		}
		else {
            //std::lock_guard<std::mutex> lg(_drop_locker);
			auto old_chunks = mem_storage_raw->drop_old_chunks_by_limit(_limits.max_mem_chunks);
#ifdef DEBUG
            for(auto c:old_chunks){
                assert(c->_buffer_t.size()!=0);
            }
#endif
            PageManager::instance()->append(old_chunks);
		}
	}

	void subscribe(const IdArray&ids, const Flag& flag, const ReaderClb_ptr &clbk) {
		mem_storage->subscribe(ids, flag, clbk);
	}

	Reader_ptr currentValue(const IdArray&ids, const Flag& flag) {
		return mem_storage->currentValue(ids, flag);
	}

	void flush() {
        std::lock_guard<std::recursive_mutex> lg(_locker);
		this->mem_cap->flush();
		this->drop_old_chunks();
		PageManager::instance()->flush();
	}

	UnionStorage::QueueSizes queue_size() const {
		QueueSizes result;
		result.page = PageManager::instance()->in_queue_size();
		result.mem = this->mem_storage_raw->queue_size();
		result.cap = this->mem_cap->size();
		return result;
	}

	class UnionCursor : public Cursor {
	public:
		Cursor_ptr _page_cursor;
		Cursor_ptr _mem_cursor;
		UnionCursor(Cursor_ptr &page_cursor, Cursor_ptr&mem_cursor):
			_page_cursor{page_cursor},
			_mem_cursor(mem_cursor)
		{
			this->reset_pos();
		}
		~UnionCursor() {
			_page_cursor = nullptr;
			_mem_cursor = nullptr;
		}

		bool is_end()const override {
			return
				(_page_cursor==nullptr?true:_page_cursor->is_end())
				&& (_mem_cursor==nullptr?true:_mem_cursor->is_end());
		}

		void readNext(Cursor::Callback*cbk)  override
		{
			if (!is_end()) {
				if ((_page_cursor!=nullptr) && (!_page_cursor->is_end())) {
					_page_cursor->readNext(cbk);
					return;
				}
				else
				{
					if ((_mem_cursor!=nullptr)&&(!_mem_cursor->is_end())) {
						_mem_cursor->readNext(cbk);
						return;
					}
				}
			}
		}

		void reset_pos() override {
			if (_page_cursor != nullptr) {
				_page_cursor->reset_pos();
			}
			if (_mem_cursor != nullptr) {
				_mem_cursor->reset_pos();
			}
		}
	};

	class UnionCursorSet : public Cursor {
	public:
		CursorList _cursors;
		CursorList::iterator it;
		bool _is_end;
		UnionCursorSet() {
		}
		~UnionCursorSet() {
			_cursors.clear();
		}

		void add_cursor(const Cursor_ptr &cptr) {
			_cursors.push_back(cptr);
			reset_pos();
		}

		bool is_end()const override {
			return _is_end;
		}

		void readNext(Cursor::Callback*cbk)  override {
			if (it == _cursors.end()) {
				_is_end = true;
			}
			else {
				Cursor_ptr c = *it;
				if (c->is_end()) {//TODO refact.
					if (it == _cursors.end()) {
						_is_end = true;
						Chunk_Ptr empty;
						cbk->call(empty);
						return;
					}
					else {
						++it;
						if (it == _cursors.end()) {
							_is_end = true;
							Chunk_Ptr empty;
							cbk->call(empty);
							return;
						}
					}
				}
				c = *it;
				c->readNext(cbk);
			}
			if (_is_end) {
				Chunk_Ptr empty;
				cbk->call(empty);
			}
		}

		void reset_pos() override {
			it = _cursors.begin();
			_is_end = false;
			for (auto&c : _cursors) {
				c->reset_pos();
			}
		}
	};

	Cursor_ptr chunksByIterval(const IdArray &ids, Flag flag, Time from, Time to) {
        std::lock_guard<std::recursive_mutex> lg(_locker);
		IdArray id_a = ids;
		if (id_a.empty()) {
			id_a = getIds();
		}
		UnionCursorSet *raw_result = new UnionCursorSet();
		Cursor_ptr result{ raw_result };
		for (auto id : id_a) {
			IdArray cur_ids(1);
			cur_ids[0] = id;
			Cursor_ptr page_chunks, mem_chunks;
			dariadb::Time minT, maxT;
			if (!mem_storage_raw->minMaxTime(id, &minT, &maxT)) {
				page_chunks = PageManager::instance()->chunksByIterval(cur_ids, flag, from, to);
			}
			else {
				if (minT <= from && maxT >= to) {
					mem_chunks = mem_storage_raw->chunksByIterval(cur_ids, flag, from, to);
				}
				else {
					page_chunks = PageManager::instance()->chunksByIterval(cur_ids, flag, from, PageManager::instance()->maxTime());
					mem_chunks = mem_storage_raw->chunksByIterval(cur_ids, flag, minT, to);
				}
			}

			Cursor_ptr sub_result{ new UnionCursor{page_chunks,mem_chunks} };
			raw_result->add_cursor(sub_result);
		}
		return result;
	}

	IdToChunkMap chunksBeforeTimePoint(const IdArray &ids, Flag flag, Time timePoint) {
        std::lock_guard<std::recursive_mutex> lg(_locker);
        IdArray id_a = ids;
        if (id_a.empty()) {
            id_a = getIds();
        }
        IdToChunkMap result;
        for(auto id:id_a){
            dariadb::Time minT, maxT;
            IdToChunkMap subRes;
            if (!mem_storage_raw->minMaxTime(id, &minT, &maxT)) {
                subRes = PageManager::instance()->chunksBeforeTimePoint(ids, flag, timePoint);
            }else{
                if (minT <= timePoint) {
                    subRes = mem_storage_raw->chunksBeforeTimePoint(ids, flag, timePoint);
                }
                else {
                    subRes=PageManager::instance()->chunksBeforeTimePoint(ids, flag, timePoint);
                }
            }
            for(auto kv:subRes){
                result[kv.first]=kv.second;
            }
        }
        return result;
	}

	bool minMaxTime(dariadb::Id id, dariadb::Time*minResult, dariadb::Time*maxResult) {
		dariadb::Time subMin1, subMax1;
		auto pr = PageManager::instance()->minMaxTime(id, &subMin1, &subMax1);
		dariadb::Time subMin2, subMax2;
		auto mr = mem_storage_raw->minMaxTime(id, &subMin2, &subMax2);

		if (!pr) {
			*minResult = subMin2;
			*maxResult = subMax2;
			return mr;
		}
		if (!mr) {
			*minResult = subMin1;
			*maxResult = subMax1;
			return pr;
		}
		*minResult = std::min(subMin1, subMin2);
		*maxResult = std::max(subMax1, subMax2);
		return true;
	}

	IdArray getIds() {
        std::lock_guard<std::recursive_mutex> lg(_locker);
		auto page_ids = PageManager::instance()->getIds();
		auto mem_ids = mem_storage_raw->getIds();
		dariadb::IdSet s;
		for (auto v : page_ids) {
			s.insert(v);
		}
		for (auto v : mem_ids) {
			s.insert(v);
		}
		return dariadb::IdArray{ s.begin(),s.end() };
	}

    size_t chunks_in_memory()const{
        std::lock_guard<std::recursive_mutex> lg(_locker);
        return mem_storage_raw->chunks_total_size();
    }

	storage::BaseStorage_ptr mem_storage;
	storage::MemoryStorage* mem_storage_raw;
	storage::Capacitor* mem_cap;

	storage::PageManager::Params _page_manager_params;
	dariadb::storage::Capacitor::Params _cap_params;
	dariadb::storage::UnionStorage::Limits _limits;
    mutable std::recursive_mutex _locker, _drop_locker;
};

UnionStorage::UnionStorage(storage::PageManager::Params page_manager_params,
	dariadb::storage::Capacitor::Params cap_params,
    const dariadb::storage::UnionStorage::Limits&limits):
	_impl{ new UnionStorage::Private(page_manager_params,
									cap_params, limits) }
{
}

UnionStorage::~UnionStorage() {
    _impl=nullptr;
}

Time UnionStorage::minTime() {
	return _impl->minTime();
}

Time UnionStorage::maxTime() {
	return _impl->maxTime();
}

append_result UnionStorage::append(const Meas::PMeas begin, const size_t size) {
	return _impl->append(begin, size);
}

append_result UnionStorage::append(const Meas &value) {
	return _impl->append(value);
}

void UnionStorage::subscribe(const IdArray&ids, const Flag& flag, const ReaderClb_ptr &clbk) {
	_impl->subscribe(ids, flag, clbk);
}

Reader_ptr UnionStorage::currentValue(const IdArray&ids, const Flag& flag) {
	return _impl->currentValue(ids, flag);
}

void  UnionStorage::flush() {
	_impl->flush();
}

bool UnionStorage::minMaxTime(dariadb::Id id, dariadb::Time*minResult, dariadb::Time*maxResult) {
	return _impl->minMaxTime(id, minResult, maxResult);
}

Cursor_ptr UnionStorage::chunksByIterval(const IdArray &ids, Flag flag, Time from, Time to) {
	return _impl->chunksByIterval(ids, flag, from, to);
}

IdToChunkMap UnionStorage::chunksBeforeTimePoint(const IdArray &ids, Flag flag, Time timePoint) {
	return _impl->chunksBeforeTimePoint(ids, flag, timePoint);
}

IdArray UnionStorage::getIds() {
	return _impl->getIds();
}

size_t UnionStorage::chunks_in_memory()const{
    return _impl->chunks_in_memory();
}

UnionStorage::QueueSizes UnionStorage::queue_size() const{
	return  _impl->queue_size();
}
<commit_msg>US: chunkBefore alg change.<commit_after>#include "union_storage.h"
#include "storage/memstorage.h"
#include "storage/capacitor.h"
#include "utils/exception.h"
#include "utils/locker.h"
#include "storage/page_manager.h"
#include <cassert>
#include <algorithm>

using namespace dariadb;
using namespace dariadb::storage;

class UnionStorage::Private {
public:
	Private(const PageManager::Params&page_storage_params,
		dariadb::storage::Capacitor::Params cap_params,
		dariadb::storage::UnionStorage::Limits limits) :
		mem_storage{ new MemoryStorage(page_storage_params.chunk_size) },
		_page_manager_params(page_storage_params),
		_cap_params(cap_params),
       _limits(limits)
	{
		dariadb::storage::ChunkPool::instance()->start(_limits.max_mem_chunks);

		mem_cap = new Capacitor(mem_storage, _cap_params);
		mem_storage_raw = dynamic_cast<MemoryStorage*>(mem_storage.get());
		assert(mem_storage_raw != nullptr);

		PageManager::start(_page_manager_params);

		auto open_chunks = PageManager::instance()->get_open_chunks();
        mem_storage_raw->append(open_chunks);
	}
	~Private() {
		this->flush();
        if (_limits.max_mem_chunks != 0) {
            auto all_chunks = this->mem_storage_raw->drop_all();
            PageManager::instance()->append(all_chunks); //use specified in ctor
        }
		delete mem_cap;
		PageManager::stop();

	}

	Time minTime() {
        std::lock_guard<std::recursive_mutex> lg(_locker);
        if(PageManager::instance()->chunks_in_cur_page()>0){
            return PageManager::instance()->minTime();
        }else{
            return mem_storage->minTime();
        }
	}

	Time maxTime() {
		return mem_storage->maxTime();
	}

	append_result append(const Meas::PMeas begin, const size_t size) {
		append_result result{};
		for (size_t i = 0; i < size; i++) {
			result = result + this->append(begin[i]);
		}
		return result;
	}

	append_result append(const Meas &value) {
		//std::lock_guard<std::mutex> lg(_locker);
		append_result result{};
        if (!mem_cap->append(value)){
        //if(mem_storage_raw->append(value).writed!=1){
            assert(false);
			result.ignored++;
		}
		else {
			result.writed++;
		}

        drop_old_chunks();
		return result;
	}

	void drop_old_chunks() {
		if (_limits.max_mem_chunks == 0) {
			if (_limits.old_mem_chunks != 0) {
                //std::lock_guard<std::mutex> lg(_drop_locker);
				auto old_chunks = mem_storage_raw->drop_old_chunks(_limits.old_mem_chunks);
                PageManager::instance()->append(old_chunks);
			}
		}
		else {
            //std::lock_guard<std::mutex> lg(_drop_locker);
			auto old_chunks = mem_storage_raw->drop_old_chunks_by_limit(_limits.max_mem_chunks);
#ifdef DEBUG
            for(auto c:old_chunks){
                assert(c->_buffer_t.size()!=0);
            }
#endif
            PageManager::instance()->append(old_chunks);
		}
	}

	void subscribe(const IdArray&ids, const Flag& flag, const ReaderClb_ptr &clbk) {
		mem_storage->subscribe(ids, flag, clbk);
	}

	Reader_ptr currentValue(const IdArray&ids, const Flag& flag) {
		return mem_storage->currentValue(ids, flag);
	}

	void flush() {
        std::lock_guard<std::recursive_mutex> lg(_locker);
		this->mem_cap->flush();
		this->drop_old_chunks();
		PageManager::instance()->flush();
	}

	UnionStorage::QueueSizes queue_size() const {
		QueueSizes result;
		result.page = PageManager::instance()->in_queue_size();
		result.mem = this->mem_storage_raw->queue_size();
		result.cap = this->mem_cap->size();
		return result;
	}

	class UnionCursor : public Cursor {
	public:
		Cursor_ptr _page_cursor;
		Cursor_ptr _mem_cursor;
		UnionCursor(Cursor_ptr &page_cursor, Cursor_ptr&mem_cursor):
			_page_cursor{page_cursor},
			_mem_cursor(mem_cursor)
		{
			this->reset_pos();
		}
		~UnionCursor() {
			_page_cursor = nullptr;
			_mem_cursor = nullptr;
		}

		bool is_end()const override {
			return
				(_page_cursor==nullptr?true:_page_cursor->is_end())
				&& (_mem_cursor==nullptr?true:_mem_cursor->is_end());
		}

		void readNext(Cursor::Callback*cbk)  override
		{
			if (!is_end()) {
				if ((_page_cursor!=nullptr) && (!_page_cursor->is_end())) {
					_page_cursor->readNext(cbk);
					return;
				}
				else
				{
					if ((_mem_cursor!=nullptr)&&(!_mem_cursor->is_end())) {
						_mem_cursor->readNext(cbk);
						return;
					}
				}
			}
		}

		void reset_pos() override {
			if (_page_cursor != nullptr) {
				_page_cursor->reset_pos();
			}
			if (_mem_cursor != nullptr) {
				_mem_cursor->reset_pos();
			}
		}
	};

	class UnionCursorSet : public Cursor {
	public:
		CursorList _cursors;
		CursorList::iterator it;
		bool _is_end;
		UnionCursorSet() {
		}
		~UnionCursorSet() {
			_cursors.clear();
		}

		void add_cursor(const Cursor_ptr &cptr) {
			_cursors.push_back(cptr);
			reset_pos();
		}

		bool is_end()const override {
			return _is_end;
		}

		void readNext(Cursor::Callback*cbk)  override {
			if (it == _cursors.end()) {
				_is_end = true;
			}
			else {
				Cursor_ptr c = *it;
				if (c->is_end()) {//TODO refact.
					if (it == _cursors.end()) {
						_is_end = true;
						Chunk_Ptr empty;
						cbk->call(empty);
						return;
					}
					else {
						++it;
						if (it == _cursors.end()) {
							_is_end = true;
							Chunk_Ptr empty;
							cbk->call(empty);
							return;
						}
					}
				}
				c = *it;
				c->readNext(cbk);
			}
			if (_is_end) {
				Chunk_Ptr empty;
				cbk->call(empty);
			}
		}

		void reset_pos() override {
			it = _cursors.begin();
			_is_end = false;
			for (auto&c : _cursors) {
				c->reset_pos();
			}
		}
	};

	Cursor_ptr chunksByIterval(const IdArray &ids, Flag flag, Time from, Time to) {
        std::lock_guard<std::recursive_mutex> lg(_locker);
		IdArray id_a = ids;
		if (id_a.empty()) {
			id_a = getIds();
		}
		UnionCursorSet *raw_result = new UnionCursorSet();
		Cursor_ptr result{ raw_result };
		for (auto id : id_a) {
			IdArray cur_ids(1);
			cur_ids[0] = id;
			Cursor_ptr page_chunks, mem_chunks;
			dariadb::Time minT, maxT;
			if (!mem_storage_raw->minMaxTime(id, &minT, &maxT)) {
				page_chunks = PageManager::instance()->chunksByIterval(cur_ids, flag, from, to);
			}
			else {
				if (minT <= from && maxT >= to) {
					mem_chunks = mem_storage_raw->chunksByIterval(cur_ids, flag, from, to);
				}
				else {
					page_chunks = PageManager::instance()->chunksByIterval(cur_ids, flag, from, PageManager::instance()->maxTime());
					mem_chunks = mem_storage_raw->chunksByIterval(cur_ids, flag, minT, to);
				}
			}

			Cursor_ptr sub_result{ new UnionCursor{page_chunks,mem_chunks} };
			raw_result->add_cursor(sub_result);
		}
		return result;
	}

	IdToChunkMap chunksBeforeTimePoint(const IdArray &ids, Flag flag, Time timePoint) {
        std::lock_guard<std::recursive_mutex> lg(_locker);
        IdArray id_a = ids;
        if (id_a.empty()) {
            id_a = getIds();
        }
        IdToChunkMap result;
        IdArray cur_ids{1};
        for(auto id:id_a){
            cur_ids[0]=id;
            dariadb::Time minT, maxT;
            IdToChunkMap subRes;
            if (!mem_storage_raw->minMaxTime(id, &minT, &maxT)) {
                subRes = PageManager::instance()->chunksBeforeTimePoint(cur_ids, flag, timePoint);
            }else{
                if (minT <= timePoint) {
                    subRes = mem_storage_raw->chunksBeforeTimePoint(cur_ids, flag, timePoint);
                }
                else {
                    subRes=PageManager::instance()->chunksBeforeTimePoint(cur_ids, flag, timePoint);
                }
            }
            for(auto kv:subRes){
                result[kv.first]=kv.second;
            }
        }
        return result;
	}

	bool minMaxTime(dariadb::Id id, dariadb::Time*minResult, dariadb::Time*maxResult) {
		dariadb::Time subMin1, subMax1;
		auto pr = PageManager::instance()->minMaxTime(id, &subMin1, &subMax1);
		dariadb::Time subMin2, subMax2;
		auto mr = mem_storage_raw->minMaxTime(id, &subMin2, &subMax2);

		if (!pr) {
			*minResult = subMin2;
			*maxResult = subMax2;
			return mr;
		}
		if (!mr) {
			*minResult = subMin1;
			*maxResult = subMax1;
			return pr;
		}
		*minResult = std::min(subMin1, subMin2);
		*maxResult = std::max(subMax1, subMax2);
		return true;
	}

	IdArray getIds() {
        std::lock_guard<std::recursive_mutex> lg(_locker);
		auto page_ids = PageManager::instance()->getIds();
		auto mem_ids = mem_storage_raw->getIds();
		dariadb::IdSet s;
		for (auto v : page_ids) {
			s.insert(v);
		}
		for (auto v : mem_ids) {
			s.insert(v);
		}
		return dariadb::IdArray{ s.begin(),s.end() };
	}

    size_t chunks_in_memory()const{
        std::lock_guard<std::recursive_mutex> lg(_locker);
        return mem_storage_raw->chunks_total_size();
    }

	storage::BaseStorage_ptr mem_storage;
	storage::MemoryStorage* mem_storage_raw;
	storage::Capacitor* mem_cap;

	storage::PageManager::Params _page_manager_params;
	dariadb::storage::Capacitor::Params _cap_params;
	dariadb::storage::UnionStorage::Limits _limits;
    mutable std::recursive_mutex _locker, _drop_locker;
};

UnionStorage::UnionStorage(storage::PageManager::Params page_manager_params,
	dariadb::storage::Capacitor::Params cap_params,
    const dariadb::storage::UnionStorage::Limits&limits):
	_impl{ new UnionStorage::Private(page_manager_params,
									cap_params, limits) }
{
}

UnionStorage::~UnionStorage() {
    _impl=nullptr;
}

Time UnionStorage::minTime() {
	return _impl->minTime();
}

Time UnionStorage::maxTime() {
	return _impl->maxTime();
}

append_result UnionStorage::append(const Meas::PMeas begin, const size_t size) {
	return _impl->append(begin, size);
}

append_result UnionStorage::append(const Meas &value) {
	return _impl->append(value);
}

void UnionStorage::subscribe(const IdArray&ids, const Flag& flag, const ReaderClb_ptr &clbk) {
	_impl->subscribe(ids, flag, clbk);
}

Reader_ptr UnionStorage::currentValue(const IdArray&ids, const Flag& flag) {
	return _impl->currentValue(ids, flag);
}

void  UnionStorage::flush() {
	_impl->flush();
}

bool UnionStorage::minMaxTime(dariadb::Id id, dariadb::Time*minResult, dariadb::Time*maxResult) {
	return _impl->minMaxTime(id, minResult, maxResult);
}

Cursor_ptr UnionStorage::chunksByIterval(const IdArray &ids, Flag flag, Time from, Time to) {
	return _impl->chunksByIterval(ids, flag, from, to);
}

IdToChunkMap UnionStorage::chunksBeforeTimePoint(const IdArray &ids, Flag flag, Time timePoint) {
	return _impl->chunksBeforeTimePoint(ids, flag, timePoint);
}

IdArray UnionStorage::getIds() {
	return _impl->getIds();
}

size_t UnionStorage::chunks_in_memory()const{
    return _impl->chunks_in_memory();
}

UnionStorage::QueueSizes UnionStorage::queue_size() const{
	return  _impl->queue_size();
}
<|endoftext|>