399 lines
16 KiB
C++
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 )
|