<commit_before>///////////////////////////////////////////////////////////////////////////////
// FILE:          PI_GCS.cpp
// PROJECT:       Micro-Manager
// SUBSYSTEM:     DeviceAdapters
//-----------------------------------------------------------------------------
// DESCRIPTION:   PI GCS Controller Driver
//
// AUTHOR:        Nenad Amodaj, nenad@amodaj.com, 08/28/2006
//                Steffen Rau, s.rau@pi.ws, 10/03/2008
// COPYRIGHT:     University of California, San Francisco, 2006
//                Physik Instrumente (PI) GmbH & Co. KG, 2008
// 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.
// CVS:           $Id: Nikon.cpp 574 2007-11-07 21:00:45Z nenad $
//

#ifdef WIN32
   #include <windows.h>
   #define snprintf _snprintf
#endif

#include "PI_GCS.h"
#include <string>
#include <math.h>
#include "../../MMDevice/ModuleInterface.h"
#include <sstream>

const char* g_PI_ZStageDeviceName = "PIZStage";
const char* g_PI_ZStageAxisName = "Axis";
const char* g_PI_ZStageAxisLimitUm = "Limit_um";

const char* g_PropertyWaitForResponse = "WaitForResponse";
const char* g_Yes = "Yes";
const char* g_No = "No";

using namespace std;

///////////////////////////////////////////////////////////////////////////////
// Exported MMDevice API
///////////////////////////////////////////////////////////////////////////////
MODULE_API void InitializeModuleData()
{
   AddAvailableDeviceName(g_PI_ZStageDeviceName, "PI GCS Z-stage");
}

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

   if (strcmp(deviceName, g_PI_ZStageDeviceName) == 0)
   {
      PIZStage* s = new PIZStage();
      return s;
   }

   return 0;
}

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

// General utility function:
int ClearPort(MM::Device& device, MM::Core& core, std::string port)
{
   // Clear contents of serial port
   const unsigned int bufSize = 255;
   unsigned char clear[bufSize];
   unsigned long read = bufSize;
   int ret;
   while (read == bufSize)
   {
      ret = core.ReadFromSerial(&device, port.c_str(), clear, bufSize, read);
      if (ret != DEVICE_OK)
         return ret;
   }
   return DEVICE_OK;
}

///////////////////////////////////////////////////////////////////////////////
// PIZStage

PIZStage::PIZStage() :
   port_("Undefined"),
   stepSizeUm_(0.1),
   initialized_(false),
   answerTimeoutMs_(1000),
   axisLimitUm_(500.0)
{
   InitializeDefaultErrorMessages();

   // create pre-initialization properties
   // ------------------------------------

   // Name
   CreateProperty(MM::g_Keyword_Name, g_PI_ZStageDeviceName, MM::String, true);

   // Description
   CreateProperty(MM::g_Keyword_Description, "Physik Instrumente (PI) GCS Adapter", MM::String, true);

   // Port
   CPropertyAction* pAct = new CPropertyAction (this, &PIZStage::OnPort);
   CreateProperty(MM::g_Keyword_Port, "Undefined", MM::String, false, pAct, true);

   // Axis name
   pAct = new CPropertyAction (this, &PIZStage::OnAxisName);
   CreateProperty(g_PI_ZStageAxisName, "A", MM::String, false, pAct, true);

   // axis limit in um
   pAct = new CPropertyAction (this, &PIZStage::OnAxisLimit);
   CreateProperty(g_PI_ZStageAxisLimitUm, "500.0", MM::Float, false, pAct, true);

   // TODO: This might not be neccessary and should be removed later, N.A. 2.26.10.
   // determine whether to wait for response from the stage
   //CreateProperty(g_PropertyWaitForResponse, g_Yes, MM::String, false, 0, true);
   //AddAllowedValue(g_PropertyWaitForResponse, g_Yes);
   //AddAllowedValue(g_PropertyWaitForResponse, g_No);

}

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

void PIZStage::GetName(char* Name) const
{
   CDeviceUtils::CopyLimitedString(Name, g_PI_ZStageDeviceName);
}

int PIZStage::Initialize()
{
	// test ismoving
	checkIsMoving_ = true;
	Busy();
   // switch on servo, otherwise "MOV" will fail
   ostringstream command;
   command << "SVO " << axisName_<<" 1";
   int ret = SendSerialCommand(port_.c_str(), command.str().c_str(), "\n");
   if (ret != DEVICE_OK)
      return ret;

   CDeviceUtils::SleepMs(10);
   ret = GetPositionSteps(curSteps_);
   if (ret != DEVICE_OK)
      return ret;

   // StepSize
   CPropertyAction* pAct = new CPropertyAction (this, &PIZStage::OnStepSizeUm);
   CreateProperty("StepSizeUm", "0.01", MM::Float, false, pAct);
   stepSizeUm_ = 0.01;

   // axis limits
   pAct = new CPropertyAction (this, &PIZStage::OnPosition);
   CreateProperty(MM::g_Keyword_Position, "0.0", MM::Float, false, pAct);
   SetPropertyLimits(MM::g_Keyword_Position, 0, axisLimitUm_);

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

   initialized_ = true;
   return DEVICE_OK;
}

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

bool PIZStage::Busy()
{
   if (!checkIsMoving_)
      return false;

   unsigned char c = (unsigned char) 5;
   // send command
   int ret = WriteToComPort(port_.c_str(), &c, 1);
   if (ret != DEVICE_OK)
      return false;

   // block/wait for acknowledge, or until we time out;
   string answer;
   ret = GetSerialAnswer(port_.c_str(), "\n", answer);
   if (ret != DEVICE_OK)
   {
      // "#5" failed, maybe controller does not support this
      // clear error with two "ERR?"
      GetError();
      GetError();
      checkIsMoving_ = false;
      return false;
   }

   long isMoving;
   if (!GetValue(answer, isMoving))
      return false;

   return (isMoving != 0);
}

int PIZStage::SetPositionSteps(long steps)
{
   double pos = steps * stepSizeUm_;
   return SetPositionUm(pos);
}

int PIZStage::GetPositionSteps(long& steps)
{
   double pos;
   int ret = GetPositionUm(pos);
   if (ret != DEVICE_OK)
      return ret;
   steps = (long) ((pos / stepSizeUm_) + 0.5);
   return DEVICE_OK;
}

int PIZStage::SetPositionUm(double pos)
{
   ostringstream command;
   command << "MOV " << axisName_<< " " << pos;

   // send command
   int ret = SendSerialCommand(port_.c_str(), command.str().c_str(), "\n");
   if (ret != DEVICE_OK)
      return ret;

   CDeviceUtils::SleepMs(10);
   // block/wait for acknowledge, or until we time out;
   return GetError();
}

int PIZStage::GetError()
{
   int ret = SendSerialCommand(port_.c_str(), "ERR?", "\n");
   if (ret != DEVICE_OK)
      return ret;
   string answer;
   ret = GetSerialAnswer(port_.c_str(), "\n", answer);
   if (ret != DEVICE_OK)
      return ret;

   int errNo = atoi(answer.c_str());
   if (errNo == 0)
	   return DEVICE_OK;

   return ERR_OFFSET + errNo;
}

int PIZStage::GetPositionUm(double& pos)
{
   ostringstream command;
   command << "POS? " << axisName_;

   // send command
   int ret = SendSerialCommand(port_.c_str(), command.str().c_str(), "\n");
   if (ret != DEVICE_OK)
      return ret;

   // block/wait for acknowledge, or until we time out;
   string answer;
   ret = GetSerialAnswer(port_.c_str(), "\n", answer);
   if (ret != DEVICE_OK)
      return ret;

   if (!GetValue(answer, pos))
      return ERR_UNRECOGNIZED_ANSWER;

   return DEVICE_OK;
}

int PIZStage::SetOrigin()
{
   return DEVICE_UNSUPPORTED_COMMAND;
}

int PIZStage::GetLimits(double& min, double& max)
{
   return DEVICE_UNSUPPORTED_COMMAND;
}

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

int PIZStage::OnPort(MM::PropertyBase* pProp, MM::ActionType eAct)
{
   if (eAct == MM::BeforeGet)
   {
      pProp->Set(port_.c_str());
   }
   else if (eAct == MM::AfterSet)
   {
      if (initialized_)
      {
         // revert
         pProp->Set(port_.c_str());
         return ERR_PORT_CHANGE_FORBIDDEN;
      }

      pProp->Get(port_);
   }

   return DEVICE_OK;
}

int PIZStage::OnAxisName(MM::PropertyBase* pProp, MM::ActionType eAct)
{
   if (eAct == MM::BeforeGet)
   {
      pProp->Set(axisName_.c_str());
   }
   else if (eAct == MM::AfterSet)
   {
      pProp->Get(axisName_);
   }

   return DEVICE_OK;
}

int PIZStage::OnStepSizeUm(MM::PropertyBase* pProp, MM::ActionType eAct)
{
   if (eAct == MM::BeforeGet)
   {
      pProp->Set(stepSizeUm_);
   }
   else if (eAct == MM::AfterSet)
   {
      pProp->Get(stepSizeUm_);
   }

   return DEVICE_OK;
}

int PIZStage::OnAxisLimit(MM::PropertyBase* pProp, MM::ActionType eAct)
{
   if (eAct == MM::BeforeGet)
   {
      pProp->Set(axisLimitUm_);
   }
   else if (eAct == MM::AfterSet)
   {
      pProp->Get(axisLimitUm_);
   }

   return DEVICE_OK;
}

int PIZStage::OnPosition(MM::PropertyBase* pProp, MM::ActionType eAct)
{
   if (eAct == MM::BeforeGet)
   {
      double pos;
      int ret = GetPositionUm(pos);
      if (ret != DEVICE_OK)
         return ret;

      pProp->Set(pos);
   }
   else if (eAct == MM::AfterSet)
   {
      double pos;
      pProp->Get(pos);
      int ret = SetPositionUm(pos);
      if (ret != DEVICE_OK)
         return ret;

   }

   return DEVICE_OK;
}

bool PIZStage::GetValue(string& sMessage, double& dval)
{
   if (!ExtractValue(sMessage))
      return false;

   char *pend;
   const char* szMessage = sMessage.c_str();
   double dValue = strtod(szMessage, &pend);

   // return true only if scan was stopped by spaces, linefeed or the terminating NUL and if the
   // string was not empty to start with
   if (pend != szMessage)
   {
      while( *pend!='\0' && (*pend==' '||*pend=='\n')) pend++;
      if (*pend=='\0')
      {
         dval = dValue;
         return true;
      }
   }
   return false;
}

bool PIZStage::GetValue(string& sMessage, long& lval)
{
   if (!ExtractValue(sMessage))
      return false;

   char *pend;
   const char* szMessage = sMessage.c_str();
   long lValue = strtol(szMessage, &pend, 0);

   // return true only if scan was stopped by spaces, linefeed or the terminating NUL and if the
   // string was not empty to start with
   if (pend != szMessage)
   {
      while( *pend!='\0' && (*pend==' '||*pend=='\n')) pend++;
      if (*pend=='\0')
      {
         lval = lValue;
         return true;
      }
   }
   return false;
}

bool PIZStage::ExtractValue(std::string& sMessage)
{
   // value is after last '=', if any '=' is found
   size_t p = sMessage.find_last_of('=');
   if ( p != std::string::npos )
       sMessage.erase(0,p+1);

   // trim whitspaces from right ...
   p = sMessage.find_last_not_of(" \t\r\n");
   if (p != std::string::npos)
       sMessage.erase(++p);

   // ... and left
   p = sMessage.find_first_not_of(" \n\t\r");
   if (p == std::string::npos)
      return false;

   sMessage.erase(0,p);
   return true;
}

bool PIZStage::waitForResponse()
{
   char val[MM::MaxStrLength];
   int ret = GetProperty(g_PropertyWaitForResponse, val);
   assert(ret == DEVICE_OK);
   if (strcmp(val, g_Yes) == 0)
      return true;
   else
      return false;
}
<commit_msg>Sleep increased from 10ms to 15ms in the SetPositionSteps() command. This was necessary to avoid errors during fast z-stack acquisitions.<commit_after>///////////////////////////////////////////////////////////////////////////////
// FILE:          PI_GCS.cpp
// PROJECT:       Micro-Manager
// SUBSYSTEM:     DeviceAdapters
//-----------------------------------------------------------------------------
// DESCRIPTION:   PI GCS Controller Driver
//
// AUTHOR:        Nenad Amodaj, nenad@amodaj.com, 08/28/2006
//                Steffen Rau, s.rau@pi.ws, 10/03/2008
// COPYRIGHT:     University of California, San Francisco, 2006
//                Physik Instrumente (PI) GmbH & Co. KG, 2008
// 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.
// CVS:           $Id: Nikon.cpp 574 2007-11-07 21:00:45Z nenad $
//

#ifdef WIN32
   #include <windows.h>
   #define snprintf _snprintf
#endif

#include "PI_GCS.h"
#include <string>
#include <math.h>
#include "../../MMDevice/ModuleInterface.h"
#include <sstream>

const char* g_PI_ZStageDeviceName = "PIZStage";
const char* g_PI_ZStageAxisName = "Axis";
const char* g_PI_ZStageAxisLimitUm = "Limit_um";

const char* g_PropertyWaitForResponse = "WaitForResponse";
const char* g_Yes = "Yes";
const char* g_No = "No";

using namespace std;

///////////////////////////////////////////////////////////////////////////////
// Exported MMDevice API
///////////////////////////////////////////////////////////////////////////////
MODULE_API void InitializeModuleData()
{
   AddAvailableDeviceName(g_PI_ZStageDeviceName, "PI GCS Z-stage");
}

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

   if (strcmp(deviceName, g_PI_ZStageDeviceName) == 0)
   {
      PIZStage* s = new PIZStage();
      return s;
   }

   return 0;
}

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

// General utility function:
int ClearPort(MM::Device& device, MM::Core& core, std::string port)
{
   // Clear contents of serial port
   const unsigned int bufSize = 255;
   unsigned char clear[bufSize];
   unsigned long read = bufSize;
   int ret;
   while (read == bufSize)
   {
      ret = core.ReadFromSerial(&device, port.c_str(), clear, bufSize, read);
      if (ret != DEVICE_OK)
         return ret;
   }
   return DEVICE_OK;
}

///////////////////////////////////////////////////////////////////////////////
// PIZStage

PIZStage::PIZStage() :
   port_("Undefined"),
   stepSizeUm_(0.1),
   initialized_(false),
   answerTimeoutMs_(1000),
   axisLimitUm_(500.0)
{
   InitializeDefaultErrorMessages();

   // create pre-initialization properties
   // ------------------------------------

   // Name
   CreateProperty(MM::g_Keyword_Name, g_PI_ZStageDeviceName, MM::String, true);

   // Description
   CreateProperty(MM::g_Keyword_Description, "Physik Instrumente (PI) GCS Adapter", MM::String, true);

   // Port
   CPropertyAction* pAct = new CPropertyAction (this, &PIZStage::OnPort);
   CreateProperty(MM::g_Keyword_Port, "Undefined", MM::String, false, pAct, true);

   // Axis name
   pAct = new CPropertyAction (this, &PIZStage::OnAxisName);
   CreateProperty(g_PI_ZStageAxisName, "A", MM::String, false, pAct, true);

   // axis limit in um
   pAct = new CPropertyAction (this, &PIZStage::OnAxisLimit);
   CreateProperty(g_PI_ZStageAxisLimitUm, "500.0", MM::Float, false, pAct, true);

   // TODO: This might not be neccessary and should be removed later, N.A. 2.26.10.
   // determine whether to wait for response from the stage
   //CreateProperty(g_PropertyWaitForResponse, g_Yes, MM::String, false, 0, true);
   //AddAllowedValue(g_PropertyWaitForResponse, g_Yes);
   //AddAllowedValue(g_PropertyWaitForResponse, g_No);

}

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

void PIZStage::GetName(char* Name) const
{
   CDeviceUtils::CopyLimitedString(Name, g_PI_ZStageDeviceName);
}

int PIZStage::Initialize()
{
	// test ismoving
	checkIsMoving_ = true;
	Busy();
   // switch on servo, otherwise "MOV" will fail
   ostringstream command;
   command << "SVO " << axisName_<<" 1";
   int ret = SendSerialCommand(port_.c_str(), command.str().c_str(), "\n");
   if (ret != DEVICE_OK)
      return ret;

   CDeviceUtils::SleepMs(10);
   ret = GetPositionSteps(curSteps_);
   if (ret != DEVICE_OK)
      return ret;

   // StepSize
   CPropertyAction* pAct = new CPropertyAction (this, &PIZStage::OnStepSizeUm);
   CreateProperty("StepSizeUm", "0.01", MM::Float, false, pAct);
   stepSizeUm_ = 0.01;

   // axis limits
   pAct = new CPropertyAction (this, &PIZStage::OnPosition);
   CreateProperty(MM::g_Keyword_Position, "0.0", MM::Float, false, pAct);
   SetPropertyLimits(MM::g_Keyword_Position, 0, axisLimitUm_);

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

   initialized_ = true;
   return DEVICE_OK;
}

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

bool PIZStage::Busy()
{
   if (!checkIsMoving_)
      return false;

   unsigned char c = (unsigned char) 5;
   // send command
   int ret = WriteToComPort(port_.c_str(), &c, 1);
   if (ret != DEVICE_OK)
      return false;

   // block/wait for acknowledge, or until we time out;
   string answer;
   ret = GetSerialAnswer(port_.c_str(), "\n", answer);
   if (ret != DEVICE_OK)
   {
      // "#5" failed, maybe controller does not support this
      // clear error with two "ERR?"
      GetError();
      GetError();
      checkIsMoving_ = false;
      return false;
   }

   long isMoving;
   if (!GetValue(answer, isMoving))
      return false;

   return (isMoving != 0);
}

int PIZStage::SetPositionSteps(long steps)
{
   double pos = steps * stepSizeUm_;
   return SetPositionUm(pos);
}

int PIZStage::GetPositionSteps(long& steps)
{
   double pos;
   int ret = GetPositionUm(pos);
   if (ret != DEVICE_OK)
      return ret;
   steps = (long) ((pos / stepSizeUm_) + 0.5);
   return DEVICE_OK;
}

int PIZStage::SetPositionUm(double pos)
{
   ostringstream command;
   command << "MOV " << axisName_<< " " << pos;

   // send command
   int ret = SendSerialCommand(port_.c_str(), command.str().c_str(), "\n");
   if (ret != DEVICE_OK)
      return ret;

   CDeviceUtils::SleepMs(15);
   // block/wait for acknowledge, or until we time out;
   return GetError();
}

int PIZStage::GetError()
{
   int ret = SendSerialCommand(port_.c_str(), "ERR?", "\n");
   if (ret != DEVICE_OK)
      return ret;
   string answer;
   ret = GetSerialAnswer(port_.c_str(), "\n", answer);
   if (ret != DEVICE_OK)
      return ret;

   int errNo = atoi(answer.c_str());
   if (errNo == 0)
	   return DEVICE_OK;

   return ERR_OFFSET + errNo;
}

int PIZStage::GetPositionUm(double& pos)
{
   ostringstream command;
   command << "POS? " << axisName_;

   // send command
   int ret = SendSerialCommand(port_.c_str(), command.str().c_str(), "\n");
   if (ret != DEVICE_OK)
      return ret;

   // block/wait for acknowledge, or until we time out;
   string answer;
   ret = GetSerialAnswer(port_.c_str(), "\n", answer);
   if (ret != DEVICE_OK)
      return ret;

   if (!GetValue(answer, pos))
      return ERR_UNRECOGNIZED_ANSWER;

   return DEVICE_OK;
}

int PIZStage::SetOrigin()
{
   return DEVICE_UNSUPPORTED_COMMAND;
}

int PIZStage::GetLimits(double& min, double& max)
{
   return DEVICE_UNSUPPORTED_COMMAND;
}

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

int PIZStage::OnPort(MM::PropertyBase* pProp, MM::ActionType eAct)
{
   if (eAct == MM::BeforeGet)
   {
      pProp->Set(port_.c_str());
   }
   else if (eAct == MM::AfterSet)
   {
      if (initialized_)
      {
         // revert
         pProp->Set(port_.c_str());
         return ERR_PORT_CHANGE_FORBIDDEN;
      }

      pProp->Get(port_);
   }

   return DEVICE_OK;
}

int PIZStage::OnAxisName(MM::PropertyBase* pProp, MM::ActionType eAct)
{
   if (eAct == MM::BeforeGet)
   {
      pProp->Set(axisName_.c_str());
   }
   else if (eAct == MM::AfterSet)
   {
      pProp->Get(axisName_);
   }

   return DEVICE_OK;
}

int PIZStage::OnStepSizeUm(MM::PropertyBase* pProp, MM::ActionType eAct)
{
   if (eAct == MM::BeforeGet)
   {
      pProp->Set(stepSizeUm_);
   }
   else if (eAct == MM::AfterSet)
   {
      pProp->Get(stepSizeUm_);
   }

   return DEVICE_OK;
}

int PIZStage::OnAxisLimit(MM::PropertyBase* pProp, MM::ActionType eAct)
{
   if (eAct == MM::BeforeGet)
   {
      pProp->Set(axisLimitUm_);
   }
   else if (eAct == MM::AfterSet)
   {
      pProp->Get(axisLimitUm_);
   }

   return DEVICE_OK;
}

int PIZStage::OnPosition(MM::PropertyBase* pProp, MM::ActionType eAct)
{
   if (eAct == MM::BeforeGet)
   {
      double pos;
      int ret = GetPositionUm(pos);
      if (ret != DEVICE_OK)
         return ret;

      pProp->Set(pos);
   }
   else if (eAct == MM::AfterSet)
   {
      double pos;
      pProp->Get(pos);
      int ret = SetPositionUm(pos);
      if (ret != DEVICE_OK)
         return ret;

   }

   return DEVICE_OK;
}

bool PIZStage::GetValue(string& sMessage, double& dval)
{
   if (!ExtractValue(sMessage))
      return false;

   char *pend;
   const char* szMessage = sMessage.c_str();
   double dValue = strtod(szMessage, &pend);

   // return true only if scan was stopped by spaces, linefeed or the terminating NUL and if the
   // string was not empty to start with
   if (pend != szMessage)
   {
      while( *pend!='\0' && (*pend==' '||*pend=='\n')) pend++;
      if (*pend=='\0')
      {
         dval = dValue;
         return true;
      }
   }
   return false;
}

bool PIZStage::GetValue(string& sMessage, long& lval)
{
   if (!ExtractValue(sMessage))
      return false;

   char *pend;
   const char* szMessage = sMessage.c_str();
   long lValue = strtol(szMessage, &pend, 0);

   // return true only if scan was stopped by spaces, linefeed or the terminating NUL and if the
   // string was not empty to start with
   if (pend != szMessage)
   {
      while( *pend!='\0' && (*pend==' '||*pend=='\n')) pend++;
      if (*pend=='\0')
      {
         lval = lValue;
         return true;
      }
   }
   return false;
}

bool PIZStage::ExtractValue(std::string& sMessage)
{
   // value is after last '=', if any '=' is found
   size_t p = sMessage.find_last_of('=');
   if ( p != std::string::npos )
       sMessage.erase(0,p+1);

   // trim whitspaces from right ...
   p = sMessage.find_last_not_of(" \t\r\n");
   if (p != std::string::npos)
       sMessage.erase(++p);

   // ... and left
   p = sMessage.find_first_not_of(" \n\t\r");
   if (p == std::string::npos)
      return false;

   sMessage.erase(0,p);
   return true;
}

bool PIZStage::waitForResponse()
{
   char val[MM::MaxStrLength];
   int ret = GetProperty(g_PropertyWaitForResponse, val);
   assert(ret == DEVICE_OK);
   if (strcmp(val, g_Yes) == 0)
      return true;
   else
      return false;
}
<|endoftext|>


<commit_before>#include <stdio.h>
#include <cmath>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <assert.h>

#include "common/util.h"
#include "common/swaglog.h"
#include "common/visionimg.h"
#include "ui.hpp"
#include "paint.hpp"

int write_param_float(float param, const char* param_name, bool persistent_param) {
  char s[16];
  int size = snprintf(s, sizeof(s), "%f", param);
  return Params(persistent_param).write_db_value(param_name, s, size < sizeof(s) ? size : sizeof(s));
}

static void ui_init_vision(UIState *s) {
  // Invisible until we receive a calibration message.
  s->scene.world_objects_visible = false;

  for (int i = 0; i < s->vipc_client->num_buffers; i++) {
    s->texture[i].reset(new EGLImageTexture(&s->vipc_client->buffers[i]));

    glBindTexture(GL_TEXTURE_2D, s->texture[i]->frame_tex);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

    // BGR
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_GREEN);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED);
  }
  assert(glGetError() == GL_NO_ERROR);
}

void ui_init(UIState *s) {
  s->sm = new SubMaster({"modelV2", "controlsState", "uiLayoutState", "liveCalibration", "radarState", "thermal", "frame",
                         "health", "carParams", "ubloxGnss", "driverState", "dMonitoringState", "sensorEvents"});

  s->started = false;
  s->status = STATUS_OFFROAD;
  s->scene.satelliteCount = -1;
  read_param(&s->is_metric, "IsMetric");

  s->fb = framebuffer_init("ui", 0, true, &s->fb_w, &s->fb_h);
  assert(s->fb);

  ui_nvg_init(s);

  s->last_frame = nullptr;
  s->vipc_client_rear = new VisionIpcClient("camerad", VISION_STREAM_RGB_BACK, true);
  s->vipc_client_front = new VisionIpcClient("camerad", VISION_STREAM_RGB_FRONT, true);
  s->vipc_client = s->vipc_client_rear;
}

template <class T>
static void update_line_data(const UIState *s, const cereal::ModelDataV2::XYZTData::Reader &line,
                             float y_off, float z_off, T *pvd, float max_distance) {
  const auto line_x = line.getX(), line_y = line.getY(), line_z = line.getZ();
  int max_idx = -1;
  vertex_data *v = &pvd->v[0];
  const float margin = 500.0f;
  for (int i = 0; ((i < TRAJECTORY_SIZE) and (line_x[i] < fmax(MIN_DRAW_DISTANCE, max_distance))); i++) {
    v += car_space_to_full_frame(s, line_x[i], -line_y[i] - y_off, -line_z[i] + z_off, v, margin);
    max_idx = i;
  }
  for (int i = max_idx; i >= 0; i--) {
    v += car_space_to_full_frame(s, line_x[i], -line_y[i] + y_off, -line_z[i] + z_off, v, margin);
  }
  pvd->cnt = v - pvd->v;
  assert(pvd->cnt < std::size(pvd->v));
}

static void update_model(UIState *s, const cereal::ModelDataV2::Reader &model) {
  UIScene &scene = s->scene;
  const float max_distance = fmin(model.getPosition().getX()[TRAJECTORY_SIZE - 1], MAX_DRAW_DISTANCE);
  // update lane lines
  const auto lane_lines = model.getLaneLines();
  const auto lane_line_probs = model.getLaneLineProbs();
  for (int i = 0; i < std::size(scene.lane_line_vertices); i++) {
    scene.lane_line_probs[i] = lane_line_probs[i];
    update_line_data(s, lane_lines[i], 0.025 * scene.lane_line_probs[i], 1.22, &scene.lane_line_vertices[i], max_distance);
  }

  // update road edges
  const auto road_edges = model.getRoadEdges();
  const auto road_edge_stds = model.getRoadEdgeStds();
  for (int i = 0; i < std::size(scene.road_edge_vertices); i++) {
    scene.road_edge_stds[i] = road_edge_stds[i];
    update_line_data(s, road_edges[i], 0.025, 1.22, &scene.road_edge_vertices[i], max_distance);
  }

  // update path
  const float lead_d = scene.lead_data[0].getStatus() ? scene.lead_data[0].getDRel() * 2. : MAX_DRAW_DISTANCE;
  float path_length = (lead_d > 0.) ? lead_d - fmin(lead_d * 0.35, 10.) : MAX_DRAW_DISTANCE;
  path_length = fmin(path_length, max_distance);
  update_line_data(s, model.getPosition(), 0.5, 0, &scene.track_vertices, path_length);
}

void update_sockets(UIState *s) {

  UIScene &scene = s->scene;
  SubMaster &sm = *(s->sm);

  if (sm.update(0) == 0){
    return;
  }

  if (s->started && sm.updated("controlsState")) {
    auto event = sm["controlsState"];
    scene.controls_state = event.getControlsState();

    // TODO: the alert stuff shouldn't be handled here
    auto alert_sound = scene.controls_state.getAlertSound();
    if (scene.alert_type.compare(scene.controls_state.getAlertType()) != 0) {
      if (alert_sound == AudibleAlert::NONE) {
        s->sound->stop();
      } else {
        s->sound->play(alert_sound);
      }
    }
    scene.alert_text1 = scene.controls_state.getAlertText1();
    scene.alert_text2 = scene.controls_state.getAlertText2();
    scene.alert_size = scene.controls_state.getAlertSize();
    scene.alert_type = scene.controls_state.getAlertType();
    scene.alert_blinking_rate = scene.controls_state.getAlertBlinkingRate();
    auto alertStatus = scene.controls_state.getAlertStatus();
    if (alertStatus == cereal::ControlsState::AlertStatus::USER_PROMPT) {
      s->status = STATUS_WARNING;
    } else if (alertStatus == cereal::ControlsState::AlertStatus::CRITICAL) {
      s->status = STATUS_ALERT;
    } else {
      s->status = scene.controls_state.getEnabled() ? STATUS_ENGAGED : STATUS_DISENGAGED;
    }
  }
  if (sm.updated("radarState")) {
    auto data = sm["radarState"].getRadarState();
    scene.lead_data[0] = data.getLeadOne();
    scene.lead_data[1] = data.getLeadTwo();
  }
  if (sm.updated("liveCalibration")) {
    scene.world_objects_visible = true;
    auto extrinsicl = sm["liveCalibration"].getLiveCalibration().getExtrinsicMatrix();
    for (int i = 0; i < 3 * 4; i++) {
      scene.extrinsic_matrix.v[i] = extrinsicl[i];
    }
  }
  if (sm.updated("modelV2")) {
    update_model(s, sm["modelV2"].getModelV2());
  }
  if (sm.updated("uiLayoutState")) {
    auto data = sm["uiLayoutState"].getUiLayoutState();
    s->active_app = data.getActiveApp();
    scene.sidebar_collapsed = data.getSidebarCollapsed();
  }
  if (sm.updated("thermal")) {
    scene.thermal = sm["thermal"].getThermal();
  }
  if (sm.updated("ubloxGnss")) {
    auto data = sm["ubloxGnss"].getUbloxGnss();
    if (data.which() == cereal::UbloxGnss::MEASUREMENT_REPORT) {
      scene.satelliteCount = data.getMeasurementReport().getNumMeas();
    }
  }
  if (sm.updated("health")) {
    auto health = sm["health"].getHealth();
    scene.hwType = health.getHwType();
    s->ignition = health.getIgnitionLine() || health.getIgnitionCan();
  } else if ((s->sm->frame - s->sm->rcv_frame("health")) > 5*UI_FREQ) {
    scene.hwType = cereal::HealthData::HwType::UNKNOWN;
  }
  if (sm.updated("carParams")) {
    s->longitudinal_control = sm["carParams"].getCarParams().getOpenpilotLongitudinalControl();
  }
  if (sm.updated("driverState")) {
    scene.driver_state = sm["driverState"].getDriverState();
  }
  if (sm.updated("dMonitoringState")) {
    scene.dmonitoring_state = sm["dMonitoringState"].getDMonitoringState();
    scene.is_rhd = scene.dmonitoring_state.getIsRHD();
    scene.frontview = scene.dmonitoring_state.getIsPreview();
  } else if (scene.frontview && (sm.frame - sm.rcv_frame("dMonitoringState")) > UI_FREQ/2) {
    scene.frontview = false;
  }
  if (sm.updated("sensorEvents")) {
    for (auto sensor : sm["sensorEvents"].getSensorEvents()) {
      if (sensor.which() == cereal::SensorEventData::LIGHT) {
        s->light_sensor = sensor.getLight();
      } else if (!s->started && sensor.which() == cereal::SensorEventData::ACCELERATION) {
        s->accel_sensor = sensor.getAcceleration().getV()[2];
      } else if (!s->started && sensor.which() == cereal::SensorEventData::GYRO_UNCALIBRATED) {
        s->gyro_sensor = sensor.getGyroUncalibrated().getV()[1];
      }
    }
  }

  s->started = scene.thermal.getStarted() || scene.frontview;
}

static void ui_read_params(UIState *s) {
  const uint64_t frame = s->sm->frame;

  if (frame % (5*UI_FREQ) == 0) {
    read_param(&s->is_metric, "IsMetric");
  } else if (frame % (6*UI_FREQ) == 0) {
    s->scene.athenaStatus = NET_DISCONNECTED;
    uint64_t last_ping = 0;
    if (read_param(&last_ping, "LastAthenaPingTime") == 0) {
      s->scene.athenaStatus = nanos_since_boot() - last_ping < 70e9 ? NET_CONNECTED : NET_ERROR;
    }
  }
}

void ui_update_vision(UIState *s) {
  if (!s->vipc_client->connected && s->started) {
    s->vipc_client = s->scene.frontview ? s->vipc_client_front : s->vipc_client_rear;

    if (s->vipc_client->connect(false)){
      ui_init_vision(s);
    }
  }

  if (s->vipc_client->connected){
    VisionBuf * buf = s->vipc_client->recv();
    if (buf != nullptr){
      s->last_frame = buf;
    }
  }
}

void ui_update(UIState *s) {
  ui_read_params(s);
  update_sockets(s);
  ui_update_vision(s);

  // Handle onroad/offroad transition
  if (!s->started && s->status != STATUS_OFFROAD) {
    s->status = STATUS_OFFROAD;
    s->active_app = cereal::UiLayoutState::App::HOME;
    s->scene.sidebar_collapsed = false;
    s->sound->stop();
    s->vipc_client->connected = false;
  } else if (s->started && s->status == STATUS_OFFROAD) {
    s->status = STATUS_DISENGAGED;
    s->started_frame = s->sm->frame;

    s->active_app = cereal::UiLayoutState::App::NONE;
    s->scene.sidebar_collapsed = true;
    s->scene.alert_size = cereal::ControlsState::AlertSize::NONE;
  }

  // Handle controls/fcamera timeout
  if (s->started && !s->scene.frontview && ((s->sm)->frame - s->started_frame) > 10*UI_FREQ) {
    if ((s->sm)->rcv_frame("controlsState") < s->started_frame) {
      // car is started, but controlsState hasn't been seen at all
      s->scene.alert_text1 = "openpilot Unavailable";
      s->scene.alert_text2 = "Waiting for controls to start";
      s->scene.alert_size = cereal::ControlsState::AlertSize::MID;
    } else if (((s->sm)->frame - (s->sm)->rcv_frame("controlsState")) > 5*UI_FREQ) {
      // car is started, but controls is lagging or died
      if (s->scene.alert_text2 != "Controls Unresponsive" &&
          s->scene.alert_text1 != "Camera Malfunction") {
        s->sound->play(AudibleAlert::CHIME_WARNING_REPEAT);
        LOGE("Controls unresponsive");
      }

      s->scene.alert_text1 = "TAKE CONTROL IMMEDIATELY";
      s->scene.alert_text2 = "Controls Unresponsive";
      s->scene.alert_size = cereal::ControlsState::AlertSize::FULL;
      s->status = STATUS_ALERT;
    }

    const uint64_t frame_pkt = (s->sm)->rcv_frame("frame");
    const uint64_t frame_delayed = (s->sm)->frame - frame_pkt;
    const uint64_t since_started = (s->sm)->frame - s->started_frame;
    if ((frame_pkt > s->started_frame || since_started > 15*UI_FREQ) && frame_delayed > 5*UI_FREQ) {
      // controls is fine, but rear camera is lagging or died
      s->scene.alert_text1 = "Camera Malfunction";
      s->scene.alert_text2 = "Contact Support";
      s->scene.alert_size = cereal::ControlsState::AlertSize::FULL;
      s->status = STATUS_DISENGAGED;
      s->sound->stop();
    }
  }
}
<commit_msg>remove read_param from ui_init (#19745)<commit_after>#include <stdio.h>
#include <cmath>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <assert.h>

#include "common/util.h"
#include "common/swaglog.h"
#include "common/visionimg.h"
#include "ui.hpp"
#include "paint.hpp"

int write_param_float(float param, const char* param_name, bool persistent_param) {
  char s[16];
  int size = snprintf(s, sizeof(s), "%f", param);
  return Params(persistent_param).write_db_value(param_name, s, size < sizeof(s) ? size : sizeof(s));
}

static void ui_init_vision(UIState *s) {
  // Invisible until we receive a calibration message.
  s->scene.world_objects_visible = false;

  for (int i = 0; i < s->vipc_client->num_buffers; i++) {
    s->texture[i].reset(new EGLImageTexture(&s->vipc_client->buffers[i]));

    glBindTexture(GL_TEXTURE_2D, s->texture[i]->frame_tex);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

    // BGR
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_GREEN);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED);
  }
  assert(glGetError() == GL_NO_ERROR);
}

void ui_init(UIState *s) {
  s->sm = new SubMaster({"modelV2", "controlsState", "uiLayoutState", "liveCalibration", "radarState", "thermal", "frame",
                         "health", "carParams", "ubloxGnss", "driverState", "dMonitoringState", "sensorEvents"});

  s->started = false;
  s->status = STATUS_OFFROAD;
  s->scene.satelliteCount = -1;

  s->fb = framebuffer_init("ui", 0, true, &s->fb_w, &s->fb_h);
  assert(s->fb);

  ui_nvg_init(s);

  s->last_frame = nullptr;
  s->vipc_client_rear = new VisionIpcClient("camerad", VISION_STREAM_RGB_BACK, true);
  s->vipc_client_front = new VisionIpcClient("camerad", VISION_STREAM_RGB_FRONT, true);
  s->vipc_client = s->vipc_client_rear;
}

template <class T>
static void update_line_data(const UIState *s, const cereal::ModelDataV2::XYZTData::Reader &line,
                             float y_off, float z_off, T *pvd, float max_distance) {
  const auto line_x = line.getX(), line_y = line.getY(), line_z = line.getZ();
  int max_idx = -1;
  vertex_data *v = &pvd->v[0];
  const float margin = 500.0f;
  for (int i = 0; ((i < TRAJECTORY_SIZE) and (line_x[i] < fmax(MIN_DRAW_DISTANCE, max_distance))); i++) {
    v += car_space_to_full_frame(s, line_x[i], -line_y[i] - y_off, -line_z[i] + z_off, v, margin);
    max_idx = i;
  }
  for (int i = max_idx; i >= 0; i--) {
    v += car_space_to_full_frame(s, line_x[i], -line_y[i] + y_off, -line_z[i] + z_off, v, margin);
  }
  pvd->cnt = v - pvd->v;
  assert(pvd->cnt < std::size(pvd->v));
}

static void update_model(UIState *s, const cereal::ModelDataV2::Reader &model) {
  UIScene &scene = s->scene;
  const float max_distance = fmin(model.getPosition().getX()[TRAJECTORY_SIZE - 1], MAX_DRAW_DISTANCE);
  // update lane lines
  const auto lane_lines = model.getLaneLines();
  const auto lane_line_probs = model.getLaneLineProbs();
  for (int i = 0; i < std::size(scene.lane_line_vertices); i++) {
    scene.lane_line_probs[i] = lane_line_probs[i];
    update_line_data(s, lane_lines[i], 0.025 * scene.lane_line_probs[i], 1.22, &scene.lane_line_vertices[i], max_distance);
  }

  // update road edges
  const auto road_edges = model.getRoadEdges();
  const auto road_edge_stds = model.getRoadEdgeStds();
  for (int i = 0; i < std::size(scene.road_edge_vertices); i++) {
    scene.road_edge_stds[i] = road_edge_stds[i];
    update_line_data(s, road_edges[i], 0.025, 1.22, &scene.road_edge_vertices[i], max_distance);
  }

  // update path
  const float lead_d = scene.lead_data[0].getStatus() ? scene.lead_data[0].getDRel() * 2. : MAX_DRAW_DISTANCE;
  float path_length = (lead_d > 0.) ? lead_d - fmin(lead_d * 0.35, 10.) : MAX_DRAW_DISTANCE;
  path_length = fmin(path_length, max_distance);
  update_line_data(s, model.getPosition(), 0.5, 0, &scene.track_vertices, path_length);
}

void update_sockets(UIState *s) {

  UIScene &scene = s->scene;
  SubMaster &sm = *(s->sm);

  if (sm.update(0) == 0){
    return;
  }

  if (s->started && sm.updated("controlsState")) {
    auto event = sm["controlsState"];
    scene.controls_state = event.getControlsState();

    // TODO: the alert stuff shouldn't be handled here
    auto alert_sound = scene.controls_state.getAlertSound();
    if (scene.alert_type.compare(scene.controls_state.getAlertType()) != 0) {
      if (alert_sound == AudibleAlert::NONE) {
        s->sound->stop();
      } else {
        s->sound->play(alert_sound);
      }
    }
    scene.alert_text1 = scene.controls_state.getAlertText1();
    scene.alert_text2 = scene.controls_state.getAlertText2();
    scene.alert_size = scene.controls_state.getAlertSize();
    scene.alert_type = scene.controls_state.getAlertType();
    scene.alert_blinking_rate = scene.controls_state.getAlertBlinkingRate();
    auto alertStatus = scene.controls_state.getAlertStatus();
    if (alertStatus == cereal::ControlsState::AlertStatus::USER_PROMPT) {
      s->status = STATUS_WARNING;
    } else if (alertStatus == cereal::ControlsState::AlertStatus::CRITICAL) {
      s->status = STATUS_ALERT;
    } else {
      s->status = scene.controls_state.getEnabled() ? STATUS_ENGAGED : STATUS_DISENGAGED;
    }
  }
  if (sm.updated("radarState")) {
    auto data = sm["radarState"].getRadarState();
    scene.lead_data[0] = data.getLeadOne();
    scene.lead_data[1] = data.getLeadTwo();
  }
  if (sm.updated("liveCalibration")) {
    scene.world_objects_visible = true;
    auto extrinsicl = sm["liveCalibration"].getLiveCalibration().getExtrinsicMatrix();
    for (int i = 0; i < 3 * 4; i++) {
      scene.extrinsic_matrix.v[i] = extrinsicl[i];
    }
  }
  if (sm.updated("modelV2")) {
    update_model(s, sm["modelV2"].getModelV2());
  }
  if (sm.updated("uiLayoutState")) {
    auto data = sm["uiLayoutState"].getUiLayoutState();
    s->active_app = data.getActiveApp();
    scene.sidebar_collapsed = data.getSidebarCollapsed();
  }
  if (sm.updated("thermal")) {
    scene.thermal = sm["thermal"].getThermal();
  }
  if (sm.updated("ubloxGnss")) {
    auto data = sm["ubloxGnss"].getUbloxGnss();
    if (data.which() == cereal::UbloxGnss::MEASUREMENT_REPORT) {
      scene.satelliteCount = data.getMeasurementReport().getNumMeas();
    }
  }
  if (sm.updated("health")) {
    auto health = sm["health"].getHealth();
    scene.hwType = health.getHwType();
    s->ignition = health.getIgnitionLine() || health.getIgnitionCan();
  } else if ((s->sm->frame - s->sm->rcv_frame("health")) > 5*UI_FREQ) {
    scene.hwType = cereal::HealthData::HwType::UNKNOWN;
  }
  if (sm.updated("carParams")) {
    s->longitudinal_control = sm["carParams"].getCarParams().getOpenpilotLongitudinalControl();
  }
  if (sm.updated("driverState")) {
    scene.driver_state = sm["driverState"].getDriverState();
  }
  if (sm.updated("dMonitoringState")) {
    scene.dmonitoring_state = sm["dMonitoringState"].getDMonitoringState();
    scene.is_rhd = scene.dmonitoring_state.getIsRHD();
    scene.frontview = scene.dmonitoring_state.getIsPreview();
  } else if (scene.frontview && (sm.frame - sm.rcv_frame("dMonitoringState")) > UI_FREQ/2) {
    scene.frontview = false;
  }
  if (sm.updated("sensorEvents")) {
    for (auto sensor : sm["sensorEvents"].getSensorEvents()) {
      if (sensor.which() == cereal::SensorEventData::LIGHT) {
        s->light_sensor = sensor.getLight();
      } else if (!s->started && sensor.which() == cereal::SensorEventData::ACCELERATION) {
        s->accel_sensor = sensor.getAcceleration().getV()[2];
      } else if (!s->started && sensor.which() == cereal::SensorEventData::GYRO_UNCALIBRATED) {
        s->gyro_sensor = sensor.getGyroUncalibrated().getV()[1];
      }
    }
  }

  s->started = scene.thermal.getStarted() || scene.frontview;
}

static void ui_read_params(UIState *s) {
  const uint64_t frame = s->sm->frame;

  if (frame % (5*UI_FREQ) == 0) {
    read_param(&s->is_metric, "IsMetric");
  } else if (frame % (6*UI_FREQ) == 0) {
    s->scene.athenaStatus = NET_DISCONNECTED;
    uint64_t last_ping = 0;
    if (read_param(&last_ping, "LastAthenaPingTime") == 0) {
      s->scene.athenaStatus = nanos_since_boot() - last_ping < 70e9 ? NET_CONNECTED : NET_ERROR;
    }
  }
}

void ui_update_vision(UIState *s) {
  if (!s->vipc_client->connected && s->started) {
    s->vipc_client = s->scene.frontview ? s->vipc_client_front : s->vipc_client_rear;

    if (s->vipc_client->connect(false)){
      ui_init_vision(s);
    }
  }

  if (s->vipc_client->connected){
    VisionBuf * buf = s->vipc_client->recv();
    if (buf != nullptr){
      s->last_frame = buf;
    }
  }
}

void ui_update(UIState *s) {
  ui_read_params(s);
  update_sockets(s);
  ui_update_vision(s);

  // Handle onroad/offroad transition
  if (!s->started && s->status != STATUS_OFFROAD) {
    s->status = STATUS_OFFROAD;
    s->active_app = cereal::UiLayoutState::App::HOME;
    s->scene.sidebar_collapsed = false;
    s->sound->stop();
    s->vipc_client->connected = false;
  } else if (s->started && s->status == STATUS_OFFROAD) {
    s->status = STATUS_DISENGAGED;
    s->started_frame = s->sm->frame;

    s->active_app = cereal::UiLayoutState::App::NONE;
    s->scene.sidebar_collapsed = true;
    s->scene.alert_size = cereal::ControlsState::AlertSize::NONE;
  }

  // Handle controls/fcamera timeout
  if (s->started && !s->scene.frontview && ((s->sm)->frame - s->started_frame) > 10*UI_FREQ) {
    if ((s->sm)->rcv_frame("controlsState") < s->started_frame) {
      // car is started, but controlsState hasn't been seen at all
      s->scene.alert_text1 = "openpilot Unavailable";
      s->scene.alert_text2 = "Waiting for controls to start";
      s->scene.alert_size = cereal::ControlsState::AlertSize::MID;
    } else if (((s->sm)->frame - (s->sm)->rcv_frame("controlsState")) > 5*UI_FREQ) {
      // car is started, but controls is lagging or died
      if (s->scene.alert_text2 != "Controls Unresponsive" &&
          s->scene.alert_text1 != "Camera Malfunction") {
        s->sound->play(AudibleAlert::CHIME_WARNING_REPEAT);
        LOGE("Controls unresponsive");
      }

      s->scene.alert_text1 = "TAKE CONTROL IMMEDIATELY";
      s->scene.alert_text2 = "Controls Unresponsive";
      s->scene.alert_size = cereal::ControlsState::AlertSize::FULL;
      s->status = STATUS_ALERT;
    }

    const uint64_t frame_pkt = (s->sm)->rcv_frame("frame");
    const uint64_t frame_delayed = (s->sm)->frame - frame_pkt;
    const uint64_t since_started = (s->sm)->frame - s->started_frame;
    if ((frame_pkt > s->started_frame || since_started > 15*UI_FREQ) && frame_delayed > 5*UI_FREQ) {
      // controls is fine, but rear camera is lagging or died
      s->scene.alert_text1 = "Camera Malfunction";
      s->scene.alert_text2 = "Contact Support";
      s->scene.alert_size = cereal::ControlsState::AlertSize::FULL;
      s->status = STATUS_DISENGAGED;
      s->sound->stop();
    }
  }
}
<|endoftext|>


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

Daemon GPL Source Code
Copyright (C) 2012 Unvanquished Developers

This file is part of the Daemon GPL Source Code (Daemon Source Code).

Daemon Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Daemon Source Code 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.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Daemon Source Code.  If not, see <http://www.gnu.org/licenses/>.

In addition, the Daemon Source Code is also subject to certain additional terms.
You should have received a copy of these additional terms immediately following the
terms and conditions of the GNU General Public License which accompanied the Daemon
Source Code.  If not, please request a copy in writing from id Software at the address
below.

If you have questions concerning this license or the applicable additional terms, you
may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville,
Maryland 20850 USA.

===========================================================================
*/

extern "C"
{
#include "q_shared.h"
#include "qcommon.h"
#include "../../libs/findlocale/findlocale.h"
}

#include "../../libs/tinygettext/log.hpp"
#include "../../libs/tinygettext/tinygettext.hpp"
#include "../../libs/tinygettext/file_system.hpp"

#include <sstream>
#include <iostream>

using namespace tinygettext;

// Ugly char buffer
static char gettextbuffer[ 4 ][ MAX_STRING_CHARS ];
static int num = -1;

DictionaryManager trans_manager;
DictionaryManager trans_managergame;

cvar_t            *language;
cvar_t            *trans_debug;
cvar_t            *trans_encodings;
cvar_t            *trans_languages;

#ifndef DEDICATED
extern "C" cvar_t *cl_consoleKeys; // should really #include client.h
#endif

/*
====================
DaemonInputbuf

Streambuf based class that uses the engine's File I/O functions for input
====================
*/

class DaemonInputbuf : public std::streambuf
{
private:
	static const size_t BUFFER_SIZE = 8192;
	fileHandle_t fileHandle;
	char  buffer[ BUFFER_SIZE ];
	size_t  putBack;
public:
	DaemonInputbuf( const std::string& filename ) : putBack( 1 )
	{
		char *end;

		end = buffer + BUFFER_SIZE - putBack;
		setg( end, end, end );

		FS_FOpenFileRead( filename.c_str(), &fileHandle, qfalse );
	}

	~DaemonInputbuf()
	{
		if( fileHandle )
		{
			FS_FCloseFile( fileHandle );
		}
	}

	int underflow()
	{
		if( gptr() < egptr() ) // buffer not exhausted
		{
			return traits_type::to_int_type( *gptr() );
		}

		if( !fileHandle )
		{
			return traits_type::eof();
		}

		char *base = buffer;
		char *start = base;

		if( eback() == base )
		{
			// Make arrangements for putback characters
			memmove( base, egptr() - putBack, putBack );
			start += putBack;
		}

		size_t n = FS_Read( start, BUFFER_SIZE - ( start - base ), fileHandle );

		if( n == 0 )
		{
			return traits_type::eof();
		}

		// Set buffer pointers
		setg( base, start, start + n );

		return traits_type::to_int_type( *gptr() );
	}
};

/*
====================
DaemonIstream

Simple istream based class that takes ownership of the streambuf
====================
*/

class DaemonIstream : public std::istream
{
public:
	DaemonIstream( const std::string& filename ) : std::istream( new DaemonInputbuf( filename ) ) {}
	~DaemonIstream()
	{
		delete rdbuf();
	}
};

/*
====================
DaemonFileSystem

Class used by tinygettext to read files and directorys
Uses the engine's File I/O functions for this purpose
====================
*/

class DaemonFileSystem : public FileSystem
{
public:
	DaemonFileSystem() {}

	std::vector<std::string> open_directory( const std::string& pathname )
	{
		int numFiles;
		char **files;
		std::vector<std::string> ret;

		files = FS_ListFiles( pathname.c_str(), NULL, &numFiles );

		for( int i = 0; i < numFiles; i++ )
		{
			ret.push_back( std::string( files[ i ] ) );
		}

		FS_FreeFileList( files );
		return ret;
	}

	std::auto_ptr<std::istream> open_file( const std::string& filename )
	{
		return std::auto_ptr<std::istream>( new DaemonIstream( filename ) );
	}
};

/*
====================
Logging functions used by tinygettext
====================
*/

void Trans_Error( const std::string& str )
{
	Com_Printf( "^1%s^7", str.c_str() );
}

void Trans_Warning( const std::string& str )
{
	if( trans_debug->integer != 0 )
	{
		Com_Printf( "^3%s^7", str.c_str() );
	}
}

void Trans_Info( const std::string& str )
{
	if( trans_debug->integer != 0 )
	{
		Com_Printf( "%s", str.c_str() );
	}
}

/*
====================
Trans_SetLanguage

Sets a loaded language. If desired language is not found, set closest match.
If no languages are close, force English.
====================
*/

void Trans_SetLanguage( const char* lang )
{
	Language requestLang = Language::from_env( std::string( lang ) );

	// default to english
	Language bestLang = Language::from_env( "en" );
	int bestScore = Language::match( requestLang, bestLang );

	std::set<Language> langs = trans_manager.get_languages();

	for( std::set<Language>::iterator i = langs.begin(); i != langs.end(); i++ )
	{
		int score = Language::match( requestLang, *i );

		if( score > bestScore )
		{
			bestScore = score;
			bestLang = *i;
		}
	}

	// language not found, display warning
	if( bestScore == 0 )
	{
		Com_Printf( _("^3WARNING:^7 Language \"%s\" (%s) not found. Default to \"English\" (en)\n"),
					requestLang.get_name().empty() ? _( "Unknown Language" ) : requestLang.get_name().c_str(),
					lang );
	}

	trans_manager.set_language( bestLang );
	trans_managergame.set_language( bestLang );

	Com_Printf( _( "Set language to %s\n" ), bestLang.get_name().c_str() );
}

extern "C" void Trans_UpdateLanguage_f( void )
{
	Trans_SetLanguage( language->string );

#ifndef DEDICATED
	// update the default console keys string
	Z_Free( cl_consoleKeys->resetString );
	cl_consoleKeys->resetString = CopyString( _("~ ` 0x7e 0x60") );
#endif
}

/*
============
Trans_Init
============
*/
extern "C" void Trans_Init( void )
{
	char langList[ MAX_TOKEN_CHARS ] = "";
	char encList[ MAX_TOKEN_CHARS ] = "";
	FL_Locale           *locale;
	std::set<Language>  langs;
	Language            lang;

	Cmd_AddCommand( "updatelanguage", Trans_UpdateLanguage_f );

	language = Cvar_Get( "language", "", CVAR_ARCHIVE );
	trans_debug = Cvar_Get( "trans_debug", "0", CVAR_ARCHIVE );
	trans_languages = Cvar_Get( "trans_languages", "", CVAR_ROM );
	trans_encodings = Cvar_Get( "trans_encodings", "", CVAR_ROM );

	// set tinygettext log callbacks
	Log::set_log_error_callback( &Trans_Error );
	Log::set_log_warning_callback( &Trans_Warning );
	Log::set_log_info_callback( &Trans_Info );

	trans_manager.set_filesystem( std::auto_ptr<DaemonFileSystem>( new DaemonFileSystem ) );
	trans_managergame.set_filesystem( std::auto_ptr<DaemonFileSystem>( new DaemonFileSystem ) );

	trans_manager.add_directory( "translation/client" );
	trans_managergame.add_directory( "translation/game" );

	langs = trans_manager.get_languages();

	for( std::set<Language>::iterator p = langs.begin(); p != langs.end(); p++ )
	{
		Q_strcat( langList, sizeof( langList ), va( "\"%s\" ", p->get_name().c_str() ) );
		Q_strcat( encList, sizeof( encList ), va( "\"%s%s%s\" ", p->get_language().c_str(),
												  p->get_country().c_str()[0] ? "_" : "",
												  p->get_country().c_str() ) );
	}

	Cvar_Set( "trans_languages", langList );
	Cvar_Set( "trans_encodings", encList );

	// Only detect locale if no previous language set.
	if( !language->string[0] )
	{
		FL_FindLocale( &locale, FL_MESSAGES );

		// Invalid or not found. Just use builtin language.
		if( !locale->lang || !locale->lang[0] || !locale->country || !locale->country[0] )
		{
			Cvar_Set( "language", "en" );
		}
		else
		{
			Cvar_Set( "language", va( "%s%s%s", locale->lang,
									  locale->country[0] ? "_" : "",
									  locale->country ) );
		}
	}

	Trans_SetLanguage( language->string );

	Com_Printf( _( "Loaded %lu language(s)\n" ), ( unsigned long )langs.size() );
}

const char* Trans_Gettext_Internal( const char *msgid, DictionaryManager& manager )
{
	num = ( num + 1 ) & 3;
	Q_strncpyz( gettextbuffer[ num ], manager.get_dictionary().translate( msgid ).c_str(), sizeof( gettextbuffer[ num ] ) );
	return gettextbuffer[ num ];
}

const char* Trans_Pgettext_Internal( const char *ctxt, const char *msgid, DictionaryManager& manager )
{
	num = ( num + 1 ) & 3;
	Q_strncpyz( gettextbuffer[ num ], manager.get_dictionary().translate_ctxt( ctxt, msgid ).c_str(), sizeof( gettextbuffer[ num ] ) );
	return gettextbuffer[ num ];
}

const char* Trans_GettextPlural_Internal( const char *msgid, const char *msgid_plural, int num, DictionaryManager& manager )
{
	num = ( num + 1 ) & 3;
	Q_strncpyz( gettextbuffer[ num ], manager.get_dictionary().translate_plural( msgid, msgid_plural, num ).c_str(), sizeof( gettextbuffer[ num ] ) );
	return gettextbuffer[ num ];
}

extern "C" const char* Trans_Gettext( const char *msgid )
{
	return Trans_Gettext_Internal( msgid, trans_manager );
}

extern "C" const char* Trans_Pgettext( const char *ctxt, const char *msgid )
{
	return Trans_Pgettext_Internal( ctxt, msgid, trans_manager );
}

extern "C" const char* Trans_GettextPlural( const char *msgid, const char *msgid_plural, int num )
{
	return Trans_GettextPlural_Internal( msgid, msgid_plural, num, trans_manager );
}

extern "C" const char* Trans_GettextGame( const char *msgid )
{
	return Trans_Gettext_Internal( msgid, trans_managergame );
}

extern "C" const char* Trans_PgettextGame( const char *ctxt, const char *msgid )
{
	return Trans_Pgettext_Internal( ctxt, msgid, trans_managergame );
}

extern "C" const char* Trans_GettextGamePlural( const char *msgid, const char *msgid_plural, int num )
{
	return Trans_GettextPlural_Internal( msgid, msgid_plural, num, trans_managergame );
}
<commit_msg>Fix compile error<commit_after>/*
===========================================================================

Daemon GPL Source Code
Copyright (C) 2012 Unvanquished Developers

This file is part of the Daemon GPL Source Code (Daemon Source Code).

Daemon Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Daemon Source Code 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.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Daemon Source Code.  If not, see <http://www.gnu.org/licenses/>.

In addition, the Daemon Source Code is also subject to certain additional terms.
You should have received a copy of these additional terms immediately following the
terms and conditions of the GNU General Public License which accompanied the Daemon
Source Code.  If not, please request a copy in writing from id Software at the address
below.

If you have questions concerning this license or the applicable additional terms, you
may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville,
Maryland 20850 USA.

===========================================================================
*/

extern "C"
{
#include "q_shared.h"
#include "qcommon.h"
#include "../../libs/findlocale/findlocale.h"
}

#include "../../libs/tinygettext/log.hpp"
#include "../../libs/tinygettext/tinygettext.hpp"
#include "../../libs/tinygettext/file_system.hpp"

#include <sstream>
#include <iostream>

using namespace tinygettext;

// Ugly char buffer
static char gettextbuffer[ 4 ][ MAX_STRING_CHARS ];
static int num = -1;

DictionaryManager trans_manager;
DictionaryManager trans_managergame;

cvar_t            *language;
cvar_t            *trans_debug;
cvar_t            *trans_encodings;
cvar_t            *trans_languages;

#ifndef DEDICATED
extern "C" cvar_t *cl_consoleKeys; // should really #include client.h
#endif

/*
====================
DaemonInputbuf

Streambuf based class that uses the engine's File I/O functions for input
====================
*/

class DaemonInputbuf : public std::streambuf
{
private:
	static const size_t BUFFER_SIZE = 8192;
	fileHandle_t fileHandle;
	char  buffer[ BUFFER_SIZE ];
	size_t  putBack;
public:
	DaemonInputbuf( const std::string& filename ) : putBack( 1 )
	{
		char *end;

		end = buffer + BUFFER_SIZE - putBack;
		setg( end, end, end );

		FS_FOpenFileRead( filename.c_str(), &fileHandle, qfalse );
	}

	~DaemonInputbuf()
	{
		if( fileHandle )
		{
			FS_FCloseFile( fileHandle );
		}
	}

	int underflow()
	{
		if( gptr() < egptr() ) // buffer not exhausted
		{
			return traits_type::to_int_type( *gptr() );
		}

		if( !fileHandle )
		{
			return traits_type::eof();
		}

		char *base = buffer;
		char *start = base;

		if( eback() == base )
		{
			// Make arrangements for putback characters
			memmove( base, egptr() - putBack, putBack );
			start += putBack;
		}

		size_t n = FS_Read( start, BUFFER_SIZE - ( start - base ), fileHandle );

		if( n == 0 )
		{
			return traits_type::eof();
		}

		// Set buffer pointers
		setg( base, start, start + n );

		return traits_type::to_int_type( *gptr() );
	}
};

/*
====================
DaemonIstream

Simple istream based class that takes ownership of the streambuf
====================
*/

class DaemonIstream : public std::istream
{
public:
	DaemonIstream( const std::string& filename ) : std::istream( new DaemonInputbuf( filename ) ) {}
	~DaemonIstream()
	{
		delete rdbuf();
	}
};

/*
====================
DaemonFileSystem

Class used by tinygettext to read files and directorys
Uses the engine's File I/O functions for this purpose
====================
*/

class DaemonFileSystem : public FileSystem
{
public:
	DaemonFileSystem() {}

	std::vector<std::string> open_directory( const std::string& pathname )
	{
		int numFiles;
		char **files;
		std::vector<std::string> ret;

		files = FS_ListFiles( pathname.c_str(), NULL, &numFiles );

		for( int i = 0; i < numFiles; i++ )
		{
			ret.push_back( std::string( files[ i ] ) );
		}

		FS_FreeFileList( files );
		return ret;
	}

	std::auto_ptr<std::istream> open_file( const std::string& filename )
	{
		return std::auto_ptr<std::istream>( new DaemonIstream( filename ) );
	}
};

/*
====================
Logging functions used by tinygettext
====================
*/

void Trans_Error( const std::string& str )
{
	Com_Printf( "^1%s^7", str.c_str() );
}

void Trans_Warning( const std::string& str )
{
	if( trans_debug->integer != 0 )
	{
		Com_Printf( "^3%s^7", str.c_str() );
	}
}

void Trans_Info( const std::string& str )
{
	if( trans_debug->integer != 0 )
	{
		Com_Printf( "%s", str.c_str() );
	}
}

/*
====================
Trans_SetLanguage

Sets a loaded language. If desired language is not found, set closest match.
If no languages are close, force English.
====================
*/

void Trans_SetLanguage( const char* lang )
{
	Language requestLang = Language::from_env( std::string( lang ) );

	// default to english
	Language bestLang = Language::from_env( "en" );
	int bestScore = Language::match( requestLang, bestLang );

	std::set<Language> langs = trans_manager.get_languages();

	for( std::set<Language>::iterator i = langs.begin(); i != langs.end(); i++ )
	{
		int score = Language::match( requestLang, *i );

		if( score > bestScore )
		{
			bestScore = score;
			bestLang = *i;
		}
	}

	// language not found, display warning
	if( bestScore == 0 )
	{
		Com_Printf( _("^3WARNING:^7 Language \"%s\" (%s) not found. Default to \"English\" (en)\n"),
					requestLang.get_name().empty() ? _( "Unknown Language" ) : requestLang.get_name().c_str(),
					lang );
	}

	trans_manager.set_language( bestLang );
	trans_managergame.set_language( bestLang );

	Com_Printf( _( "Set language to %s\n" ), bestLang.get_name().c_str() );
}

extern "C" void Trans_UpdateLanguage_f( void )
{
	Trans_SetLanguage( language->string );

#ifndef DEDICATED
	// update the default console keys string
	Z_Free( cl_consoleKeys->resetString );
	cl_consoleKeys->resetString = CopyString( _("~ ` 0x7e 0x60") );
#endif
}

/*
============
Trans_Init
============
*/
extern "C" void Trans_Init( void )
{
	char langList[ MAX_TOKEN_CHARS ] = "";
	char encList[ MAX_TOKEN_CHARS ] = "";
	FL_Locale           *locale;
	std::set<Language>  langs;
	Language            lang;

	Cmd_AddCommand( "updatelanguage", Trans_UpdateLanguage_f );

	language = Cvar_Get( "language", "", CVAR_ARCHIVE );
	trans_debug = Cvar_Get( "trans_debug", "0", CVAR_ARCHIVE );
	trans_languages = Cvar_Get( "trans_languages", "", CVAR_ROM );
	trans_encodings = Cvar_Get( "trans_encodings", "", CVAR_ROM );

	// set tinygettext log callbacks
	Log::set_log_error_callback( &Trans_Error );
	Log::set_log_warning_callback( &Trans_Warning );
	Log::set_log_info_callback( &Trans_Info );

	trans_manager.set_filesystem( std::auto_ptr<FileSystem>( new DaemonFileSystem ) );
	trans_managergame.set_filesystem( std::auto_ptr<FileSystem>( new DaemonFileSystem ) );

	trans_manager.add_directory( "translation/client" );
	trans_managergame.add_directory( "translation/game" );

	langs = trans_manager.get_languages();

	for( std::set<Language>::iterator p = langs.begin(); p != langs.end(); p++ )
	{
		Q_strcat( langList, sizeof( langList ), va( "\"%s\" ", p->get_name().c_str() ) );
		Q_strcat( encList, sizeof( encList ), va( "\"%s%s%s\" ", p->get_language().c_str(),
												  p->get_country().c_str()[0] ? "_" : "",
												  p->get_country().c_str() ) );
	}

	Cvar_Set( "trans_languages", langList );
	Cvar_Set( "trans_encodings", encList );

	// Only detect locale if no previous language set.
	if( !language->string[0] )
	{
		FL_FindLocale( &locale, FL_MESSAGES );

		// Invalid or not found. Just use builtin language.
		if( !locale->lang || !locale->lang[0] || !locale->country || !locale->country[0] )
		{
			Cvar_Set( "language", "en" );
		}
		else
		{
			Cvar_Set( "language", va( "%s%s%s", locale->lang,
									  locale->country[0] ? "_" : "",
									  locale->country ) );
		}
	}

	Trans_SetLanguage( language->string );

	Com_Printf( _( "Loaded %lu language(s)\n" ), ( unsigned long )langs.size() );
}

const char* Trans_Gettext_Internal( const char *msgid, DictionaryManager& manager )
{
	num = ( num + 1 ) & 3;
	Q_strncpyz( gettextbuffer[ num ], manager.get_dictionary().translate( msgid ).c_str(), sizeof( gettextbuffer[ num ] ) );
	return gettextbuffer[ num ];
}

const char* Trans_Pgettext_Internal( const char *ctxt, const char *msgid, DictionaryManager& manager )
{
	num = ( num + 1 ) & 3;
	Q_strncpyz( gettextbuffer[ num ], manager.get_dictionary().translate_ctxt( ctxt, msgid ).c_str(), sizeof( gettextbuffer[ num ] ) );
	return gettextbuffer[ num ];
}

const char* Trans_GettextPlural_Internal( const char *msgid, const char *msgid_plural, int num, DictionaryManager& manager )
{
	num = ( num + 1 ) & 3;
	Q_strncpyz( gettextbuffer[ num ], manager.get_dictionary().translate_plural( msgid, msgid_plural, num ).c_str(), sizeof( gettextbuffer[ num ] ) );
	return gettextbuffer[ num ];
}

extern "C" const char* Trans_Gettext( const char *msgid )
{
	return Trans_Gettext_Internal( msgid, trans_manager );
}

extern "C" const char* Trans_Pgettext( const char *ctxt, const char *msgid )
{
	return Trans_Pgettext_Internal( ctxt, msgid, trans_manager );
}

extern "C" const char* Trans_GettextPlural( const char *msgid, const char *msgid_plural, int num )
{
	return Trans_GettextPlural_Internal( msgid, msgid_plural, num, trans_manager );
}

extern "C" const char* Trans_GettextGame( const char *msgid )
{
	return Trans_Gettext_Internal( msgid, trans_managergame );
}

extern "C" const char* Trans_PgettextGame( const char *ctxt, const char *msgid )
{
	return Trans_Pgettext_Internal( ctxt, msgid, trans_managergame );
}

extern "C" const char* Trans_GettextGamePlural( const char *msgid, const char *msgid_plural, int num )
{
	return Trans_GettextPlural_Internal( msgid, msgid_plural, num, trans_managergame );
}
<|endoftext|>


<commit_before>/*************************************************
* Unix EntropySource Source File                 *
* (C) 1999-2008 Jack Lloyd                       *
*************************************************/

#include <botan/es_unix.h>
#include <botan/unix_cmd.h>
#include <botan/parsing.h>
#include <botan/xor_buf.h>
#include <algorithm>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/resource.h>
#include <unistd.h>

namespace Botan {

namespace {

/*************************************************
* Sort ordering by priority                      *
*************************************************/
bool Unix_Program_Cmp(const Unix_Program& a, const Unix_Program& b)
   { return (a.priority < b.priority); }

}

/*************************************************
* Unix_EntropySource Constructor                 *
*************************************************/
Unix_EntropySource::Unix_EntropySource(const std::vector<std::string>& path) :
   PATH(path)
   {
   add_default_sources(sources);
   }

/*************************************************
* Add sources to the list                        *
*************************************************/
void Unix_EntropySource::add_sources(const Unix_Program srcs[], u32bit count)
   {
   sources.insert(sources.end(), srcs, srcs + count);
   std::sort(sources.begin(), sources.end(), Unix_Program_Cmp);
   }

/*************************************************
* Unix Fast Poll                                 *
*************************************************/
u32bit Unix_EntropySource::fast_poll(byte buf[], u32bit length)
   {
   if(length == 0)
      return 0;

   u32bit buf_i = 0;

   const char* STAT_TARGETS[] = {
      "/",
      "/tmp",
      "/var/tmp",
      "/usr",
      "/home",
      "/etc/passwd",
      ".",
      "..",
      0 };

   for(u32bit j = 0; STAT_TARGETS[j]; j++)
      {
      struct stat statbuf;
      clear_mem(&statbuf, 1);
      ::stat(STAT_TARGETS[j], &statbuf);

      buf_i = xor_into_buf(buf, buf_i, length, &statbuf, sizeof(statbuf));
      }

   u32bit ids[] = {
      ::getpid(),
      ::getppid(),
      ::getuid(),
      ::geteuid(),
      ::getegid(),
      ::getpgrp(),
      ::getsid(0)
   };

   for(u32bit i = 0; i != sizeof(ids); ++i)
      buf_i = xor_into_buf(buf, buf_i, length, &ids[i], sizeof(ids[i]));

   struct ::rusage usage;

   clear_mem(&usage, 1);
   ::getrusage(RUSAGE_SELF, &usage);
   buf_i = xor_into_buf(buf, buf_i, length, &usage, sizeof(usage));

   ::getrusage(RUSAGE_CHILDREN, &usage);
   buf_i = xor_into_buf(buf, buf_i, length, &usage, sizeof(usage));

   return length;
   }

/*************************************************
* Unix Slow Poll                                 *
*************************************************/
u32bit Unix_EntropySource::slow_poll(byte buf[], u32bit length)
   {
   if(length == 0)
      return 0;

   const u32bit TRY_TO_GET = 16 * 1024;
   const u32bit MINIMAL_WORKING = 32;

   u32bit got = 0;
   u32bit buf_i = 0;

   for(u32bit j = 0; j != sources.size(); j++)
      {
      DataSource_Command pipe(sources[j].name_and_args, PATH);
      SecureVector<byte> buffer(DEFAULT_BUFFERSIZE);

      u32bit got_from_src = 0;

      while(!pipe.end_of_data())
         {
         u32bit this_loop = pipe.read(buffer, buffer.size());
         buf_i = xor_into_buf(buf, buf_i, length, buffer, this_loop);
         got_from_src += this_loop;
         }

      sources[j].working = (got_from_src >= MINIMAL_WORKING) ? true : false;
      got += got_from_src;

      if(got >= TRY_TO_GET)
         break;
      }

   return length;
   }

}
<commit_msg>Use template version of xor_into_buf in es_unix<commit_after>/*************************************************
* Unix EntropySource Source File                 *
* (C) 1999-2008 Jack Lloyd                       *
*************************************************/

#include <botan/es_unix.h>
#include <botan/unix_cmd.h>
#include <botan/parsing.h>
#include <botan/xor_buf.h>
#include <algorithm>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/resource.h>
#include <unistd.h>

namespace Botan {

namespace {

/*************************************************
* Sort ordering by priority                      *
*************************************************/
bool Unix_Program_Cmp(const Unix_Program& a, const Unix_Program& b)
   { return (a.priority < b.priority); }

}

/*************************************************
* Unix_EntropySource Constructor                 *
*************************************************/
Unix_EntropySource::Unix_EntropySource(const std::vector<std::string>& path) :
   PATH(path)
   {
   add_default_sources(sources);
   }

/*************************************************
* Add sources to the list                        *
*************************************************/
void Unix_EntropySource::add_sources(const Unix_Program srcs[], u32bit count)
   {
   sources.insert(sources.end(), srcs, srcs + count);
   std::sort(sources.begin(), sources.end(), Unix_Program_Cmp);
   }

/*************************************************
* Unix Fast Poll                                 *
*************************************************/
u32bit Unix_EntropySource::fast_poll(byte buf[], u32bit length)
   {
   if(length == 0)
      return 0;

   u32bit buf_i = 0;

   const char* STAT_TARGETS[] = {
      "/",
      "/tmp",
      "/var/tmp",
      "/usr",
      "/home",
      "/etc/passwd",
      ".",
      "..",
      0 };

   for(u32bit j = 0; STAT_TARGETS[j]; j++)
      {
      struct stat statbuf;
      clear_mem(&statbuf, 1);
      ::stat(STAT_TARGETS[j], &statbuf);

      buf_i = xor_into_buf(buf, buf_i, length, &statbuf, sizeof(statbuf));
      }

   u32bit ids[] = {
      ::getpid(),
      ::getppid(),
      ::getuid(),
      ::geteuid(),
      ::getegid(),
      ::getpgrp(),
      ::getsid(0)
   };

   for(u32bit i = 0; i != sizeof(ids); ++i)
      buf_i = xor_into_buf(buf, buf_i, length, ids[i]);

   struct ::rusage usage;

   clear_mem(&usage, 1);
   ::getrusage(RUSAGE_SELF, &usage);
   buf_i = xor_into_buf(buf, buf_i, length, &usage, sizeof(usage));

   ::getrusage(RUSAGE_CHILDREN, &usage);
   buf_i = xor_into_buf(buf, buf_i, length, &usage, sizeof(usage));

   return length;
   }

/*************************************************
* Unix Slow Poll                                 *
*************************************************/
u32bit Unix_EntropySource::slow_poll(byte buf[], u32bit length)
   {
   if(length == 0)
      return 0;

   const u32bit TRY_TO_GET = 16 * 1024;
   const u32bit MINIMAL_WORKING = 32;

   u32bit got = 0;
   u32bit buf_i = 0;

   for(u32bit j = 0; j != sources.size(); j++)
      {
      DataSource_Command pipe(sources[j].name_and_args, PATH);
      SecureVector<byte> buffer(DEFAULT_BUFFERSIZE);

      u32bit got_from_src = 0;

      while(!pipe.end_of_data())
         {
         u32bit this_loop = pipe.read(buffer, buffer.size());
         buf_i = xor_into_buf(buf, buf_i, length, buffer, this_loop);
         got_from_src += this_loop;
         }

      sources[j].working = (got_from_src >= MINIMAL_WORKING) ? true : false;
      got += got_from_src;

      if(got >= TRY_TO_GET)
         break;
      }

   return length;
   }

}
<|endoftext|>


<commit_before>#include "DynamicSvg.h"

#include <qsvgtinydocument_p.h>
#include <qsvgnode_p.h>

#include <QQuickWindow>
#include <QSGSimpleTextureNode>
#include <QDir>

DynamicSvg::DynamicSvg(QQuickItem* parent)
    : QQuickItem(parent),
      m_cachedImage(0),
      m_texProvider(0),
      m_bSceneGraphInitialized(false)
{
    QQuickItem::setFlag(QQuickItem::ItemHasContents, true);
    setAntialiasing(true);

    connect(this, SIGNAL(windowChanged(QQuickWindow*)),
            this, SLOT(onWindowChanged(QQuickWindow*)));
    connect(this, SIGNAL(sourceChanged()),
            this, SLOT(renderSvg()));
    connect(this, SIGNAL(nodeRefsChanged()),
            this, SLOT(renderSvg()));
}

DynamicSvg::~DynamicSvg()
{
//    qDebug() << "Destroying DynamicSvg";
//    QTime timer; timer.start();
//    if (m_cachedImage) delete m_cachedImage;
//    if (m_texProvider) delete m_texProvider;
//    qDeleteAll(m_ownedTextures);
//    m_ownedTextures.clear();
//    qDebug() << "Finished Destroying DynamicSvg (took" << timer.elapsed() << "ms)";
}

QUrl DynamicSvg::source() const
{
    return m_source;
}

void DynamicSvg::setSource(QUrl source)
{
    if (source == m_source) return;

    m_source = source;
    if (source.isLocalFile())
        m_renderer.load(source.path());
    else
        m_renderer.load(source.toString());
    emit sourceChanged();
}

bool DynamicSvg::containsNode(const QString &id) const
{
    return m_renderer.getDocument()->elementExists(id);
}

QQmlListProperty<SvgNodeRef> DynamicSvg::nodeRefs()
{
    return QQmlListProperty<SvgNodeRef>(this, 0,
                                        &DynamicSvg::addNodeRef,
                                        &DynamicSvg::nodeRefCount,
                                        &DynamicSvg::nodeRefAt,
                                        &DynamicSvg::clearNodeRefs);
}

void DynamicSvg::renderSvg()
{
    if (!m_renderer.isValid()) return;
    if (!window()) return;
    if (!m_bSceneGraphInitialized) return;

    int width = qMax(1.0, this->width());
    int height = qMax(1.0, this->height());

    QRectF bounds = QRectF(QPointF(0,0), QSizeF(width, height));

    qreal dpr = window()->devicePixelRatio();
    QSizeF scale(dpr, dpr);
    QSize finalSize = QSize(width * dpr, height * dpr);

    QTransform transform;
    transform.scale(scale.width(), scale.height());
    bounds = transform.mapRect(bounds);

    if (m_cachedImage) delete m_cachedImage;
    m_cachedImage = new QImage(finalSize, QImage::Format_ARGB32_Premultiplied);
    m_cachedImage->fill(Qt::transparent);
    QPainter imgPainter(m_cachedImage);
    m_renderer.render(&imgPainter, bounds);

    QSGTexture* texture = window()->createTextureFromImage(QImage(*m_cachedImage));

    m_ownedTextures.append(texture);
    m_texProvider->updateTexture(texture);
}

QImage* DynamicSvg::svgImage()
{
    if (!m_cachedImage) renderSvg();
    return m_cachedImage;
}

void DynamicSvg::setupTexProvider()
{
    if (!m_texProvider) {
        m_texProvider = new DynamicSvgTextureProvider();
        m_texProvider->m_smooth = true;
        m_texProvider->m_mipmap = false;
        renderSvg();
    }
    connect(m_texProvider, SIGNAL(textureChanged()), this, SLOT(update()));
}

QSGTextureProvider* DynamicSvg::textureProvider() const
{
    return m_texProvider;
}

void DynamicSvg::geometryChanged(const QRectF& newGeometry, const QRectF& oldGeometry)
{
    QQuickItem::geometryChanged(newGeometry, oldGeometry);
    if ((newGeometry.size() != oldGeometry.size()) && isVisible()) {
        renderSvg();
    }
}

void DynamicSvg::componentComplete()
{
    QQuickItem::componentComplete();
    if (window() && window()->openglContext())
        m_bSceneGraphInitialized = true;
    renderSvg();
}

void DynamicSvg::onWindowChanged(QQuickWindow* window)
{
    if (!window) { this->deleteLater(); return; }
    connect(window, SIGNAL(sceneGraphInitialized()), this, SLOT(onSceneGraphInitialized()));
    setupTexProvider();
}

void DynamicSvg::onSceneGraphInitialized()
{
    m_bSceneGraphInitialized = true;
    renderSvg();
}

QSGNode* DynamicSvg::updatePaintNode(QSGNode* node, UpdatePaintNodeData*)
{
    if (!m_bSceneGraphInitialized) return node;

    if (!node) {
        node = new QSGSimpleTextureNode();
        ((QSGSimpleTextureNode*)node)->setRect(this->boundingRect());
    }
    QSGSimpleTextureNode* textureNode = ((QSGSimpleTextureNode*)node);
    textureNode->setTexture(m_texProvider->texture());
    qreal dpr = window() ? window()->devicePixelRatio() : 1.0;
    int width = textureNode->texture()->textureSize().width() / dpr;
    int height = textureNode->texture()->textureSize().height() / dpr;
    textureNode->setRect(0, 0, width, height);

    return node;
}

void DynamicSvg::addNodeRef(QQmlListProperty<SvgNodeRef>* nodeRefs, SvgNodeRef* nodeRef)
{
    DynamicSvg* svg = qobject_cast<DynamicSvg*>(nodeRefs->object);
    if (!svg) return;
    nodeRef->setSvg(svg);
    svg->m_nodeRefs.append(nodeRef);
}

SvgNodeRef* DynamicSvg::nodeRefAt(QQmlListProperty<SvgNodeRef>* nodeRefs, int index)
{
    DynamicSvg* svg = qobject_cast<DynamicSvg*>(nodeRefs->object);
    if (!svg) return NULL;
    return svg->m_nodeRefs.at(index);
}

void DynamicSvg::clearNodeRefs(QQmlListProperty<SvgNodeRef>* nodeRefs)
{
    DynamicSvg* svg = qobject_cast<DynamicSvg*>(nodeRefs->object);
    if (!svg) return;
    svg->m_nodeRefs.clear();
}

int DynamicSvg::nodeRefCount(QQmlListProperty<SvgNodeRef>* nodeRefs)
{
    DynamicSvg* svg = qobject_cast<DynamicSvg*>(nodeRefs->object);
    if (!svg) return 0;
    return svg->m_nodeRefs.count();
}

QSvgNode* DynamicSvg::node(const QString &id) const
{
    return m_renderer.getDocument()->namedNode(id);
}

<commit_msg>Fixed memory leak where OpenGL textures weren't getting deleted because they were created on a different thread<commit_after>#include "DynamicSvg.h"

#include <qsvgtinydocument_p.h>
#include <qsvgnode_p.h>

#include <QQuickWindow>
#include <QSGSimpleTextureNode>
#include <QDir>

DynamicSvg::DynamicSvg(QQuickItem* parent)
    : QQuickItem(parent),
      m_cachedImage(0),
      m_texProvider(0),
      m_bSceneGraphInitialized(false)
{
    QQuickItem::setFlag(QQuickItem::ItemHasContents, true);
    setAntialiasing(true);

    connect(this, SIGNAL(windowChanged(QQuickWindow*)),
            this, SLOT(onWindowChanged(QQuickWindow*)));
    connect(this, SIGNAL(sourceChanged()),
            this, SLOT(renderSvg()));
    connect(this, SIGNAL(nodeRefsChanged()),
            this, SLOT(renderSvg()));
}

DynamicSvg::~DynamicSvg()
{
//    qDebug() << "Destroying DynamicSvg";
//    QTime timer; timer.start();
//    if (m_cachedImage) delete m_cachedImage;
//    if (m_texProvider) delete m_texProvider;
//    qDeleteAll(m_ownedTextures);
//    m_ownedTextures.clear();
//    qDebug() << "Finished Destroying DynamicSvg (took" << timer.elapsed() << "ms)";
}

QUrl DynamicSvg::source() const
{
    return m_source;
}

void DynamicSvg::setSource(QUrl source)
{
    if (source == m_source) return;

    m_source = source;
    if (source.isLocalFile())
        m_renderer.load(source.path());
    else
        m_renderer.load(source.toString());
    emit sourceChanged();
}

bool DynamicSvg::containsNode(const QString &id) const
{
    return m_renderer.getDocument()->elementExists(id);
}

QQmlListProperty<SvgNodeRef> DynamicSvg::nodeRefs()
{
    return QQmlListProperty<SvgNodeRef>(this, 0,
                                        &DynamicSvg::addNodeRef,
                                        &DynamicSvg::nodeRefCount,
                                        &DynamicSvg::nodeRefAt,
                                        &DynamicSvg::clearNodeRefs);
}

void DynamicSvg::renderSvg()
{
    if (!m_renderer.isValid()) return;
    if (!window()) return;
    if (!m_bSceneGraphInitialized) return;

    int width = qMax(1.0, this->width());
    int height = qMax(1.0, this->height());

    QRectF bounds = QRectF(QPointF(0,0), QSizeF(width, height));

    qreal dpr = window()->devicePixelRatio();
    QSizeF scale(dpr, dpr);
    QSize finalSize = QSize(width * dpr, height * dpr);

    QTransform transform;
    transform.scale(scale.width(), scale.height());
    bounds = transform.mapRect(bounds);

    if (!m_cachedImage) {
        m_cachedImage = new QImage(finalSize, QImage::Format_ARGB32_Premultiplied);
    }
    else if (m_cachedImage->size() != finalSize) {
        delete m_cachedImage;
        m_cachedImage = new QImage(finalSize, QImage::Format_ARGB32_Premultiplied);
    }
    m_cachedImage->fill(Qt::transparent);
    QPainter imgPainter(m_cachedImage);
    m_renderer.render(&imgPainter, bounds);

    update();
}

QImage* DynamicSvg::svgImage()
{
    if (!m_cachedImage) renderSvg();
    return m_cachedImage;
}

void DynamicSvg::setupTexProvider()
{
    //This provider is actually not used anymore...need to take out
    if (!m_texProvider) {
        m_texProvider = new DynamicSvgTextureProvider();
        m_texProvider->m_smooth = true;
        m_texProvider->m_mipmap = false;
        renderSvg();
    }
}

QSGTextureProvider* DynamicSvg::textureProvider() const
{
    return m_texProvider;
}

void DynamicSvg::geometryChanged(const QRectF& newGeometry, const QRectF& oldGeometry)
{
    QQuickItem::geometryChanged(newGeometry, oldGeometry);
    if ((newGeometry.size() != oldGeometry.size()) && isVisible()) {
        renderSvg();
    }
}

void DynamicSvg::componentComplete()
{
    QQuickItem::componentComplete();
    if (window() && window()->openglContext())
        m_bSceneGraphInitialized = true;
    renderSvg();
}

void DynamicSvg::onWindowChanged(QQuickWindow* window)
{
    if (!window) { this->deleteLater(); return; }
    connect(window, SIGNAL(sceneGraphInitialized()), this, SLOT(onSceneGraphInitialized()));
    setupTexProvider();
}

void DynamicSvg::onSceneGraphInitialized()
{
    m_bSceneGraphInitialized = true;
    renderSvg();
}

QSGNode* DynamicSvg::updatePaintNode(QSGNode* node, UpdatePaintNodeData*)
{
    if (!m_bSceneGraphInitialized) return node;

    if (!node) {
        node = new QSGSimpleTextureNode();
        ((QSGSimpleTextureNode*)node)->setRect(this->boundingRect());
    }
    QSGSimpleTextureNode* textureNode = ((QSGSimpleTextureNode*)node);

    QSGTexture* texture = window()->createTextureFromImage(*m_cachedImage,
          QQuickWindow::TextureOwnsGLTexture);
    m_ownedTextures.append(texture);
    textureNode->setTexture(texture);

    qreal dpr = window() ? window()->devicePixelRatio() : 1.0;
    int width = textureNode->texture()->textureSize().width() / dpr;
    int height = textureNode->texture()->textureSize().height() / dpr;
    textureNode->setRect(0, 0, width, height);

    //clear old textures
    foreach (QSGTexture* tex, m_ownedTextures) {
        if (tex != texture) {
            m_ownedTextures.removeAll(tex);
            tex->deleteLater();
        }
    }

    return node;
}

void DynamicSvg::addNodeRef(QQmlListProperty<SvgNodeRef>* nodeRefs, SvgNodeRef* nodeRef)
{
    DynamicSvg* svg = qobject_cast<DynamicSvg*>(nodeRefs->object);
    if (!svg) return;
    nodeRef->setSvg(svg);
    svg->m_nodeRefs.append(nodeRef);
}

SvgNodeRef* DynamicSvg::nodeRefAt(QQmlListProperty<SvgNodeRef>* nodeRefs, int index)
{
    DynamicSvg* svg = qobject_cast<DynamicSvg*>(nodeRefs->object);
    if (!svg) return NULL;
    return svg->m_nodeRefs.at(index);
}

void DynamicSvg::clearNodeRefs(QQmlListProperty<SvgNodeRef>* nodeRefs)
{
    DynamicSvg* svg = qobject_cast<DynamicSvg*>(nodeRefs->object);
    if (!svg) return;
    svg->m_nodeRefs.clear();
}

int DynamicSvg::nodeRefCount(QQmlListProperty<SvgNodeRef>* nodeRefs)
{
    DynamicSvg* svg = qobject_cast<DynamicSvg*>(nodeRefs->object);
    if (!svg) return 0;
    return svg->m_nodeRefs.count();
}

QSvgNode* DynamicSvg::node(const QString &id) const
{
    return m_renderer.getDocument()->namedNode(id);
}

<|endoftext|>


<commit_before>/*
 * This file is part of Clockwork.
 *
 * Copyright (c) 2013-2016 Jeremy Othieno.
 *
 * The MIT License (MIT)
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#ifndef CLOCKWORK_PRIMITIVE_HH
#define CLOCKWORK_PRIMITIVE_HH

namespace clockwork {
/**
 * An enumeration of available primitives.
 */
enum class Primitive {
	Point,
	Line,
	LineStrip,
	LineLoop,
	Triangle,
	TriangleStrip,
	TriangleFan
};
} // namespace clockwork

#endif // CLOCKWORK_PRIMITIVE_HH
<commit_msg>Document the PrimitiveTopology enumeration<commit_after>/*
 * This file is part of Clockwork.
 *
 * Copyright (c) 2013-2016 Jeremy Othieno.
 *
 * The MIT License (MIT)
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#ifndef CLOCKWORK_PRIMITIVE_HH
#define CLOCKWORK_PRIMITIVE_HH

namespace clockwork {
/**
 * An enumeration of available primitive topologies that determine how
 * consecutive vertices are organized into geometric primitives.
 */
enum class PrimitiveTopology {
	Point,
	Line,
	LineStrip,
	LineLoop,
	Triangle,
	TriangleStrip,
	TriangleFan
};
using Primitive = PrimitiveTopology;
} // namespace clockwork

#endif // CLOCKWORK_PRIMITIVE_HH
<|endoftext|>