399 lines
16 KiB
C++

//======================================================================================================
// Copyright 2016, NaturalPoint Inc.
//======================================================================================================
#pragma once
// STL
// Private STL members are OK in exported class since they do not cross the DLL boundary
#pragma warning( push )
#pragma warning( disable : 4251 )
#pragma warning( disable : 4275 )
#include <vector>
#include <map>
#include <list>
#include <memory>
#include <chrono>
#include <ctime>
// OptiTrack PluginDevice API
#include "AnalogSystemBuildConfig.h"
#include "IDevice.h"
#include "AnalogFrame.h"
#include "AnalogChannelDescriptor.h"
#include "PropertySet.h"
#define ANALOG_DEVICEBUFFERSIZE 1000
#define ANALOG_ERRORMESSAGESIZE 4096
#define ANALOG_MAXSAMPLESPERMOCAPFRAME 16
namespace MoCapCore
{
class cPropertyCollectionDefinition;
}
namespace AnalogSystem
{
class AnalogChannelDescriptor;
class IDeviceManager;
class AnalogFrameGroup;
/// <summary>
/// Device sync mode
/// </summary>
enum DeviceSyncMode
{
DeviceSync_FreeRun = 0, // Free running
DeviceSync_Triggered // Waits for a trigger signal in data packet before starting
};
/// <summary>
/// Device Type (Devices can register themselves as a known type (e.g. Force Plate)
/// which can help Motive in interpreting the device in the UI, used derived node classes, etc.
/// </summary>
enum DeviceType
{
DeviceType_None = 0, // No particular type
DeviceType_NIDAQ, // NIDAQ card
DeviceType_ForcePlate, // Force plate
DeviceType_EyeTracker, // Eye Tracker
DeviceType_Pointer, // Pointer
DeviceType_Glove // Glove
};
/// <summary>
/// Frame Flags
/// </summary>
enum FrameFlag
{
FrameFlag_SyncBit = 0x01, // this frame contained a sync bit from the device
FrameFlag_DropFrame = 0x02, // this frame was dropped by the device and is padded with 0's
FrameFlag_Synthetic = 0x04 // this frame contains synthetic (padded or device filled) data
};
/// <summary>
/// Device frame buffer state
/// </summary>
enum DeviceBufferState
{
Buffer_Ready = 0,
Buffer_NeedSync = -1,
Buffer_Behind = -2,
Buffer_Ahead = -3,
Buffer_Empty = -4,
Buffer_MocapFrameOutOfRange = -5
};
/// <summary>
/// Device data state (not ready, ready, data flowing)
/// </summary>
enum DeviceDataState
{
DeviceState_NotReady = 0, // Device not ready (channels not configured, not calibrated, etc)
DeviceState_Ready, // Device ready for capture (channels enabled and correctly configured, calibrated, etc)
DeviceState_ReceivingData // Device actually actively receiving data
};
/// <summary>
/// Device sync state
/// </summary>
enum DeviceSyncStatus
{
DeviceSyncStatus_NeedSync = 0,
DeviceSyncStatus_Resyncing,
DeviceSyncStatus_ReadyForSync,
DeviceSyncStatus_Synced
};
/// <summary>
/// cPluginDeviceBase is the base implementation class for a Motive Plugin Device/
///
/// Implementors should derive from the template PluginDevice subclass
//
/// A PluginDevice provides
/// - Device operation callbacks, to be overridden by device implementor
/// - Device channel and property management, for exposing device channel information
/// and custom properties to the Motive UI. Also provides property change event
/// communication between Motive and Device
/// - Data management, providing a thread-safe user-typed data buffer for the device to
/// quickly write to, and for Motive to read from.
/// </summary>
/// <seealso cref="IDevice" />
class ANALOGSYSTEM_API cPluginDeviceBase : public IDevice, public cPropertySet
{
public:
cPluginDeviceBase();
virtual ~cPluginDeviceBase();
///////////////////////////////////////////////////////////////////////
//
// Plugin Device Interface (override in derived plugin device)
//
///<summary>Perform device specific configuration, allocates channel buffer. (Call base when implementing)</summary>
bool Configure() override;
/// <summary>Deconfigure this instance</summary>
bool Deconfigure() override;
/// <summary>Zero the device</summary>
void Zero() override;
/// <summary>Prepare live clocked devices for sync</summary>
bool PrepareForSync() override;
/// <summary>Perform any device-specific pre-capture operations</summary>
bool PreCapture() override;
/// <summary>Start device sampling (Call base when implementing)</summary>
bool StartCapture() override;
/// <summary>Stop device sampling (Call base when implementing)</summary>
bool StopCapture() override;
/// <summary>Perform any device-specific post-capture operations</summary>
bool PostCapture() override;
/// <summary>Return whether device is actively capturing data</summary>
bool IsCapturing() const override;
/// <summary>Return whether device is actively capturing data</summary>
bool IsReady() const;
/// <summary>Gives device a chance to validate a property value before applying it</summary>
bool ValidatePropertyChange( const char* propertyName, const IPropVal& val ) override;
/// <summary>Handle device specific property change events</summary>
void OnPropertyChanged( const char* propertyName ) override;
/// <summary>Handle device channel property change events</summary>
void OnChannelPropertyChanged( const char* channelName, const char* propertyName ) override;
/// <summary>Generic device-defined message passed from host (e.g. Motive) to device</summary>
int MessageFromHost( const char* ) const override;
///////////////////////////////////////////////////////////////////////
//
// Plugin Device Base Implementation (do not override)
//
// channels
/// <summary>Retrieves the ChannelDescriptor at the specified index</summary>
AnalogChannelDescriptor* ChannelDescriptor( int index ) override;
/// <summary>Retrieves the ChannelDescriptor at the specified channel name</summary>
AnalogChannelDescriptor* ChannelDescriptor( const char* channelName ) override;
/// <summary>Retrieves the number of Channels</summary>
int ChannelDescriptorCount() const override;
/// <summary>Retrieves the number of enabled channels</summary>
int ActiveChannelCount() const override;
/// <summary>Retrieves the ChannelDescriptor at the specified active channel index</summary>
AnalogChannelDescriptor* ActiveChannelDescriptor( int index ) override;
/// <summary>Retrieves the ChannelID at the specified index</summary>
int ChannelID( int index ) const override;
/// <summary>Retrieves the ChannelID associated with the specified channel name</summary>
int ChannelID( const char* channelName ) const;
// data
/// <summary>Called by Device before updating the frame buffer to lock the buffer and provide a slot</summary>
/// <param name="DeviceFrameID">The device frame ID.</param>
/// <returns>Slot to write data to</returns>
AnalogFrameBase* BeginFrameUpdate( long DeviceFrameID, bool wait = true ) override;
/// <summary>Called by device when frame update is done, unlocking the buffer and updating the indexer</summary>
void EndFrameUpdate() override;
/// <summary>Gets a frame of data from the device buffer at the specified buffer index</summary>
const AnalogFrameBase* GetFrameByBufferIndex( int index ) const override;
/// <summary>Gets a frame of data from the device buffer at the specified frame ID</summary>
const AnalogFrameBase* GetFrameByDeviceFrameID( long frameID ) const override;
/// <summary>Gets a newly allocated mocap aligned group of device frames. Caller is responsible for relasing framegroup</summary>
/// <param name="mocapFrameID">The mocap frame ID</param>
/// <param name="resultCode">0 if successful, error code otherwise</param>
/// <returns>Collection of analog frames</returns>
AnalogFrameGroup* GetFrameGroupByMocapFrameID( long mocapFrameID, DeviceBufferState& resultCode, bool recording = false );
/// <summary>Gets a mocap aligned frame group</summary>
/// <param name="frameGroup">caller provided frame group</param>
/// <param name="mocapFrameID">The mocap frame ID</param>
/// <returns>0 if successful, error code otherwise</returns>
DeviceBufferState GetFrameGroupByMocapFrameID( AnalogFrameGroup& frameGroup, long mocapFrameID, bool recording = false );
/// <summary>Gets the buffer index of the most recently added frame</summary>
int GetMostRecentFrameIndex() const override;
/// <summary>Gets the frame ID of the most recently added frame</summary>
long GetMostRecentFrameID() const override;
/// <summary>Gets the frame ID of the oldest frame in the buffer</summary>
long GetOldestFrameID();
/// <summary>Updates the software drift correction based on the most recent mocap and device frames</summary>
/// <param name="mocapFrameID">Most recent mocap frame ID</param>
void UpdateDriftCorrection( long mocapFrameID ) override;
/// <summary>Resets the drift correction</summary>
void ResetDriftCorrection();
/// <summary>Resets all sync counters (frame id, timestamp)</summary>
void ResetSyncCounters();
/// <summary>Gets the most recent Frame ID</summary>
long FrameCounter() const;
/// <summary>Sets the most recent Frame ID</summary>
void SetFrameCounter( long val );
/// <summary>Sets the DeviceManager (Device to Motive communication interface)</summary>
void SetDeviceManager( IDeviceManager* pDeviceManager ) override;
/// <summary>Gets the DeviceManager</summary>
IDeviceManager* DeviceManager() override;
/// <summary>Logs an error message</summary>
void LogError( int errorType, const char* fmt, ... );
/// <summary>Gets the last logged error message</summary>
const char* LastError() const override;
/// <summary>Gets the size (in bytes) of a channel</summary>
int DataSize() const;
/// <summary>Returns the number of device frame sampled per mocap frame</summary>
int DeviceFramesPerMocapFrame() const;
/// <summary>Return the sync aligned device frame IDs for the specified mocap frame ID</summary>
/// <param name="requestedMocapFrameID">Mocap frame</param>
/// <param name="deviceStart">Device frame start</param>
/// <param name="deviceStop">Device frame stop</param>
/// <returns>true if successful, false otherwise</returns>
bool MocapFrameIDToDeviceFrameIDs( long requestedMocapFrameID, long& deviceStart, long& deviceStop ) const override;
bool HaveSyncFrames() const;
void UpdateDeviceSyncStatus();
uint64_t LastArrivedSampleTimestamp() const;
// IProfileSubscriber Interface
std::wstring ProfileTypeName() const override;
std::wstring ProfileLabel() const override;
std::wstring ProfileDescription() const override;
void SaveToProfile( tinyxml2wc::XMLElement& saveTo ) const override;
bool LoadFromProfile( const tinyxml2wc::XMLElement& source ) override;
// deprecated
void AddFrameToBuffer( AnalogFrameBase* pFrame );
virtual void RegisterFactory( int channelCount ) { };
// PluginDevice Standard Properties. Plugins can add UI exposed custom properties via AddProperty()
static const char* kEnabledPropName;
static const char* kNamePropName;
static const char* kDisplayNamePropName;
static const char* kModelPropName;
static const char* kSerialPropName;
static const char* kChannelCountPropName;
static const char* kRatePropName;
static const char* kMocapRateMultiplePropName;
static const char* kUseExternalClockPropName;
static const char* kDeviceTypePropName;
static const char* kExternalClockTerminalPropName;
static const char* kExternalTriggerTerminalPropName;
static const char* kOrderPropName;
static const char* kMocapRatePropName;
static const char* kSyncStatusPropName;
static const char* kSyncModePropName;
static const char* kMocapSyncFramePropName;
static const char* kSyncFramePropName;
static const char* kNeedDeviceSyncFramePropName;
static const char* kNeedMocapSyncFramePropName;
static const char* kDriftCorrectionPropName;
static const char* kUseDriftCorrectionPropName;
static const char* kExternalClockChannelPropName;
static const char* kMasterSerialPropName;
static const char* kSlaveIndexPropName;
static const char* kNoiseAveragePropName;
static const char* kZeroPropName;
static const char* kDataStatePropName;
static const char* kAppRunModePropName;
static const char* kGroupNamePropName;
static const char* kAssetPropName;
static const char* kConnectedPropName;
static const char* kDummySerialPropName;
static const char* kUserDataPropName;
static const char* kCorner0PropName;
static const char* kCorner1PropName;
static const char* kCorner2PropName;
static const char* kCorner3PropName;
static const char* kScalePropName;
static const char* kCalOffsetPropName;
static const char* kCalSquareRotationPropName;
protected:
/// <summary>Adds a channel definition to the device</summary>
/// <param name="channelName">Name of the channel.</param>
/// <param name="dataType">Type of the data.</param>
/// <returns>ID of the new channel</returns>
int AddChannelDescriptor( char* channelName, int dataType = (int) ChannelType_Float );
// allocate a new frame (implemented in derived template class)
virtual AnalogFrameBase* AllocateFrame( int channelCount ) = 0;
/// <summary>Size (in bytes) of a data channel</summary>
int mDataSize;
bool mIsReady;
bool mIsCapturing;
long mRecordingDeviceSyncFrame = -1;
long mLiveDeviceSyncFrame = -1;
private:
// properties
std::shared_ptr<MoCapCore::cPropertyCollectionDefinition> CreatePropertyDefinitions();
// channels
std::vector<AnalogChannelDescriptor*> mChannelDescriptors;
std::vector<int> mActiveChannelMap;
// data
AnalogFrameBase* GetNextSlot( long DeviceFrameID );
bool LockBuffer( bool wait = true ) const;
void UnlockBuffer() const;
void ReleaseBuffer();
std::vector<AnalogFrameBase*> mFrameBuffer;
mutable void* mFrameBufferLock;
int mFrameBufferSize;
int mLastAddedIndex;
std::map<long, int> mAnalogFrameIDToBufferIndex;
long mFrameCounter;
long mDriftCorrection;
// helpers
IDeviceManager* mDeviceManager;
char mLastErrorMsg[ANALOG_ERRORMESSAGESIZE];
void* mMessageLock;
std::wstring mDeviceName;
static const std::wstring kPropertyDefinitionName;
static Core::cUID sPropertyDefinitionsID;
std::chrono::time_point<std::chrono::high_resolution_clock> mLastTimestamp;
bool mCached_NeedDeviceSyncFrame = true;
};
}
#pragma warning( pop )