C++ API Reference
udpDevice/udpDevice.cpp
//-
// ==========================================================================
// Copyright 2020 Autodesk, Inc. All rights reserved.
//
// Use of this software is subject to the terms of the Autodesk
// license agreement provided at the time of installation or download,
// or which otherwise accompanies this software in either electronic
// or hard copy form.
// ==========================================================================
//+
// Description:
// A simple client device node that uses UDP to transfer data.
// Sample only runs on Linux.
//
// Run Maya and execute the MEL code below. In a shell,
// run the Python code and enter 3 numbers to update the
// cube's translate.
//
/*
// MEL:
loadPlugin udpDevice;
string $node = `createNode udpDevice`;
string $cube[] = `polyCube`;
connectAttr ( $node + ".outputTranslate" ) ( $cube[0] + ".translate" );
setAttr ( $node + ".live" ) 1;
# Python: run from a Linux command line as a Python script
import socket
clientSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
while True:
data = raw_input("Type 3 numbers for translate(. to exit): ")
if data != '.':
clientSocket.sendto(data, ("localhost",7555))
else:
break
clientSocket.close()
*/
#ifdef __linux__
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#endif
#include <maya/MFnPlugin.h>
#include <maya/MTypeId.h>
#include "api_macros.h"
#include <maya/MIOStream.h>
#include <maya/MString.h>
#include <maya/MStringArray.h>
#include <maya/MPlug.h>
#include <maya/MDataBlock.h>
#include <maya/MFnNumericAttribute.h>
#include <maya/MPxClientDeviceNode.h>
class udpDeviceNode : public MPxClientDeviceNode
{
public:
udpDeviceNode();
~udpDeviceNode() override;
void postConstructor() override;
MStatus compute( const MPlug& plug, MDataBlock& data ) override;
void threadHandler( const char* serverName, const char* deviceName ) override;
void threadShutdownHandler() override;
static void* creator();
static MStatus initialize();
public:
static MObject outputTranslate;
static MObject outputTranslateX;
static MObject outputTranslateY;
static MObject outputTranslateZ;
static MTypeId id;
private:
};
MTypeId udpDeviceNode::id( 0x00081052 );
MObject udpDeviceNode::outputTranslate;
MObject udpDeviceNode::outputTranslateX;
MObject udpDeviceNode::outputTranslateY;
MObject udpDeviceNode::outputTranslateZ;
udpDeviceNode::udpDeviceNode()
{}
udpDeviceNode::~udpDeviceNode()
{
destroyMemoryPools();
}
void udpDeviceNode::postConstructor()
{
MObjectArray attrArray;
attrArray.append( udpDeviceNode::outputTranslate );
setRefreshOutputAttributes( attrArray );
// we'll be reading one character line of size 1024
createMemoryPools( 1, 1024, sizeof(char));
}
void udpDeviceNode::threadHandler( const char* serverName, const char* deviceName )
{
setDone( false );
if ( serverName != NULL && deviceName != NULL )
printf("udpThreadHandler: %s %s\n",serverName,deviceName);
#ifdef __linux__
int sock;
int bytesRead;
socklen_t addressLength;
struct sockaddr_in serverAddress , clientAddress;
serverAddress.sin_family = AF_INET;
serverAddress.sin_port = htons(7555);
serverAddress.sin_addr.s_addr = INADDR_ANY;
bzero(&(serverAddress.sin_zero),8);
addressLength = sizeof(struct sockaddr);
if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
{
return;
}
if (bind(sock,(struct sockaddr *)&serverAddress,
sizeof(struct sockaddr)) == -1)
{
return;
}
MStatus status;
MCharBuffer buffer;
char receiveBuffer[1025];
while ( !isDone() )
{
if ( ! isLive() )
continue;
// select() modifies its parameters so reset everytime
fd_set read_set;
FD_ZERO( &read_set );
FD_SET( sock, &read_set );
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 500000; // 1/2 second
if ( select( sock+1, &read_set, NULL, NULL, &tv ) == -1 )
break;
if ( ! FD_ISSET( sock, &read_set ) )
continue;
receiveBuffer[0] = 0;
bytesRead = recvfrom(sock,receiveBuffer,1024,0,
(struct sockaddr *)&clientAddress, &addressLength);
const char *receivedFromServer = inet_ntoa(clientAddress.sin_addr);
unsigned short receivedFromPort = ntohs(clientAddress.sin_port);
if ( NULL == receivedFromServer )
continue;
printf("(%s , %d) connection : %s \n",receivedFromServer,receivedFromPort, receiveBuffer);
// Simple test to make sure we are getting data from the right server
// printf("%s %s\n",serverName, receivedFromServer);
if ( 0 != strcmp( serverName, receivedFromServer ) )
continue;
// Get the storage once we have data from the server
status = acquireDataStorage(buffer);
if ( ! status )
continue;
beginThreadLoop();
{
receiveBuffer[bytesRead] = '\0';
double* doubleData = reinterpret_cast<double*>(buffer.ptr());
doubleData[0] = 0.0 ; doubleData[1] = 0.0; doubleData[2] = 0.0;
MString s( receiveBuffer );
if ( s.split( ' ', sa ) )
{
if ( sa.length() == 3 )
{
int i = 0;
for ( i = 0; i < 3; i++ )
{
doubleData[i] = ( sa[i].isDouble() ? sa[i].asDouble() : 0.0 );
}
}
}
pushThreadData( buffer );
}
endThreadLoop();
}
// Close the socket
close( sock );
#endif // __linux__
setDone( true );
}
void udpDeviceNode::threadShutdownHandler()
{
setDone( true );
}
void* udpDeviceNode::creator()
{
return new udpDeviceNode;
}
MStatus udpDeviceNode::initialize()
{
MStatus status;
outputTranslateX = numAttr.create("outputTranslateX", "otx", MFnNumericData::kDouble, 0.0, &status);
MCHECKERROR(status, "create outputTranslateX");
outputTranslateY = numAttr.create("outputTranslateY", "oty", MFnNumericData::kDouble, 0.0, &status);
MCHECKERROR(status, "create outputTranslateY");
outputTranslateZ = numAttr.create("outputTranslateZ", "otz", MFnNumericData::kDouble, 0.0, &status);
MCHECKERROR(status, "create outputTranslateZ");
outputTranslate = numAttr.create("outputTranslate", "ot", outputTranslateX, outputTranslateY,
outputTranslateZ, &status);
MCHECKERROR(status, "create outputTranslate");
ADD_ATTRIBUTE(outputTranslate);
ATTRIBUTE_AFFECTS( live, outputTranslate);
ATTRIBUTE_AFFECTS( frameRate, outputTranslate);
ATTRIBUTE_AFFECTS( serverName, outputTranslate);
ATTRIBUTE_AFFECTS( deviceName, outputTranslate);
return MS::kSuccess;
}
MStatus udpDeviceNode::compute( const MPlug& plug, MDataBlock& block )
{
MStatus status;
if( plug == outputTranslate || plug == outputTranslateX ||
plug == outputTranslateY || plug == outputTranslateZ )
{
MCharBuffer buffer;
if ( popThreadData(buffer) )
{
double* doubleData = reinterpret_cast<double*>(buffer.ptr());
MDataHandle outputTranslateHandle = block.outputValue( outputTranslate, &status );
MCHECKERROR(status, "Error in block.outputValue for outputTranslate");
double3& outputTranslate = outputTranslateHandle.asDouble3();
outputTranslate[0] = doubleData[0];
outputTranslate[1] = doubleData[1];
outputTranslate[2] = doubleData[2];
block.setClean( plug );
releaseDataStorage(buffer);
return ( MS::kSuccess );
}
else
{
return MS::kFailure;
}
}
return ( MS::kUnknownParameter );
}
MStatus initializePlugin( MObject obj )
{
MStatus status;
MFnPlugin plugin(obj, PLUGIN_COMPANY, "3.0", "Any");
status = plugin.registerNode( "udpDevice",
udpDeviceNode::id,
udpDeviceNode::creator,
udpDeviceNode::initialize,
if( !status ) {
status.perror("failed to registerNode udpDeviceNode");
}
return status;
}
MStatus uninitializePlugin( MObject obj )
{
MStatus status;
MFnPlugin plugin(obj);
status = plugin.deregisterNode( udpDeviceNode::id );
if( !status ) {
status.perror("failed to deregisterNode udpDeviceNode");
}
return status;
}