VR-Link API Documentation for DIS
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Test PDU

This file contains the TestPdu class, which extends DtPdu and holds the Number and Vector data.

To derive a class from DtPdu:

  1. Create the PDU layout, a structure that is used for the network representation of the PDU. (Network representations for all of the VR-Link PDUs are in the include/packets directory.)

By using our "Net" types, such as DtNetInt32 (defined in NetTypes.h), you ensure platform independence. When you are on little endian machines, byte swapping is performed when assigning to a Net type, and when a Net type is implicitly cast to a native type.

The first field in all network representations should be a DtNetPduHeader.

If you are creating a fixed-length PDU, the structure should describe the entire layout of the PDU. Since this PDU is variable length, it is not possible to fully describe the PDU layout.

Define as much of the network representation structure as is possible with a C-style structure. The cardinality of "b" in the structure does not matter, since we will only be casting buffers to pointers to this structure, and not creating instances of the structure or relying on its size. We will not be able to access "c" at all through the structure, since it comes after the variable length array "b". We will have to use byte-arithmetic to reach "c".

  1. Extend the DtPduKind enumeration, adding our chosen value. This is the number that ends up in the PDU header's kind field.

The value you choose for PDU kind should not be one that is used by any other PDU. We recommend a value between 220 and 255.

  1. Create a type called TestPduCb. Functions of this type can be registered as callbacks to be called on receipt of TestPDUs. Forward declaration of TestPdu is necessary first.
  1. Create the TestPdu class definition.
    • Each class derived from DtPdu must have the two standard constructors possessed by all PDU classes. Your DtPdu can have additional constructors, but it must have the two standard ones.
    • PDU classes must provide a definition for the virtual function internalGetPduKind(), which returns the PDU kind value being used for this type of PDU.
    • Your class should provide the static member functions addCallback() and removeCallback() for callback management, and the static member function create(), that returns a new instance of the class.
    • Most DtPdu sub-classes provide inspector and mutator functions to examine and set the fields of your PDU, but VR-Link does not require that you do so.

In testPdu.cxx, we do the following:

The size passed to initPdu() is the size of the smallest legal TestPdu; that is, one with zero elements in the array b. The size of the PDU is therefore the size of the header plus the size of the DtNetInt32 field "a" plus the size of the DtNetFloat32 field "c".

initPdu() is a member of the base class DtPdu, and it handles allocating memory for the network representation of the PDU, and other related items.

Note
If this was a fixed-length PDU, the size would be the size of the NetTestPdu structure.
Implementing Mutator and Inspector Functions

For a fixed-length PDU, writing mutator and inspector functions is fairly straightforward. A pointer to the PDU's network representation is stored in a DtPdu member and can be obtained using DtPdu::packet(). The void* returned must be cast to a pointer to your network representation structure before fields are examined and set within your accessors. We define the function netTestPdu() to perform this cast. In our test PDU example, we can then use this function to obtain a pointer to the PDU's network representation structure.

When you write mutators for variable length PDUs, like the testPdu example, you need to call one of the following DtPdu member functions every time you change the size or layout of the PDU:

In addition, you may need to do some pointer arithmetic to access certain fields of the PDU that cannot be accessed through members of the static structure.

The insertBytes() function inserts the indicated number of blank (zeroed) bytes at the desired offset into the PDU's network representation.

The deleteBytes() function deletes the desired number of bytes, starting with the indicated offset. These functions handle reallocating the memory for the network representation if necessary, and will also make sure the size field in the header is updated to reflect the new size.

Since a represents the number of elements in the array b, we must insert or delete bytes from the PDU layout whenever we change a through its mutator.

To implement the mutator for A, the code does the following:

  1. Gets the old value of "a".
  2. Either pushes down or pulls up the rest of the PDU data depending on whether the value of "a" is increasing or decreasing.
  3. Sets the value of the "a" field.

To implement the inspector and mutator for B:

  1. The b() and setB() functions should take an index into the array, so that we know which element to inspect or set. We can do some bounds checking if we want.
  1. Either do bounds checking, then set the index'th element of the "b" array to val, or do bounds checking, then return the index'th element of the "b" array.

The inspector and mutator for the c field must do some pointer arithmetic to find where the data is stored, since it cannot be accessed through the structure.


/*********************************************************************
** Copyright (c) 1992-2010 VT MAK
** All rights reserved.
*********************************************************************/
#pragma once
#if DtDIS
#include <vl/pdu.h>
typedef struct NetTestPdu
{
class TestPdu;
typedef void (*TestPduCb)(TestPdu* pdu, void* usr);
class TestPdu : public DtPdu
{
public:
TestPdu(const NetTestPdu *initial,
DtBufferPtr buffer = DtUSE_INTERNAL_BUFFER, DtPduFactory* pduFactory = 0);
virtual ~TestPdu();
TestPdu(const TestPdu& orig);
TestPdu& operator=(const TestPdu& orig);
virtual void printData() const;
virtual void setA(int val);
virtual int a() const;
virtual void setB(int index, int val);
virtual int b(int index) const;
virtual void setC(float val);
virtual float c() const;
virtual NetTestPdu* netTestPdu();
virtual const NetTestPdu* netTestPdu() const;
public:
static DtPdu* create(const DtNetPacket *initial,
DtBufferPtr buffer = DtUSE_INTERNAL_BUFFER, DtPduFactory* pduFactory = 0);
static void addCallback(DtExerciseConn *conn, TestPduCb cb, void *usr);
static void removeCallback(DtExerciseConn *conn, TestPduCb cb, void *usr);
protected:
virtual DtPduKind internalGetPduKind() const;
};
#endif

/*********************************************************************
** Copyright (c) 1992-2010 VT MAK
** All rights reserved.
*********************************************************************/
#if DtDIS
#include "testPdu.h"
#include "vl/pduFactory.h"
#include <iostream>
// Blank constructor.
{
// Must call initPdu, passing the minimal size of this type of PDU, and the
// buffer pointer passed to us. Here, minimal size occurs when "b" has 0
// elements. The size of the PDU is therefore the size of the header plus
// the size of the DtNetInt32 field "a" plus the size of the DtNetFloat32
// field "c".
int minimalSize = sizeof(DtNetPduHeader) + sizeof(DtNetInt32) +
sizeof(DtFloat32);
initPdu(minimalSize, buffer);
}
{
}
// From-network-representation constructor.
TestPdu::TestPdu(const NetTestPdu *initial, DtBufferPtr buffer, DtPduFactory* pduFactory)
{
// Must call initPdu, passing the network representation and the buffer
// that were passed to us.
initPdu(initial, buffer, pduFactory);
}
// Copy constructor.
TestPdu::TestPdu(const TestPdu& orig) :
DtPdu(orig)
{
}
// Assignment operator.
{
// Guard against assignment to yourself.
if (this == &orig)
{
return *this;
}
return *this;
}
void TestPdu::printData() const
{
std::cout << "A: " << a() << "\n"
<< "B: \n";
// Remember that a() indicates the length of array "b".
for (int index = 0; index < a(); index++)
{
std::cout << b(index) << '\n';
}
std::cout << "C: "<< c() << std::endl;
}
void TestPdu::setA(int val)
{
// Since "a" represents the number of elements in the "b" array, we must
// insert or delete bytes from the PDU layout whenever we change "a"
// through this mutator.
// Get the old value of "a".
int oldA = a();
if (val > oldA)
{
// Insert space for (val - oldA) 32-bit integers after the current last
// element of the "b" array. insertBytes will push the rest of the PDU
// data (in this case just "c") down past the end of the byte being
// added.
insertBytes(&(netTestPdu()->b[oldA]),
(val - oldA) * sizeof(DtNetInt32));
}
else if (val < oldA)
{
// Delete space used by the last (oldA - val) 32-bit integers.
// deleteBytes pulls up the rest of the PDU data (in this cast just "c",
// so that it occupies the space vacated by the deleted bytes.
deleteBytes(&(netTestPdu()->b[val]), (oldA - val) * sizeof(DtNetInt32));
}
// Now set the value of the "a" field.
netTestPdu()->a = val;
}
int TestPdu::a() const
{
return netTestPdu()->a;
}
void TestPdu::setB(int index, int val)
{
// Do bounds checking, then set the index'th element of the "b" array to
// val.
if (index < 0 || index > a())
{
std::cout << "TestPdu::setB(): index out of range.\n";
return;
}
netTestPdu()->b[index] = val;
}
int TestPdu::b(int index) const
{
// Do bounds checking, then return the index'th element of the "b" array.
if (index < 0 || index > a())
{
std::cout << "TestPdu::b(): index out of range.\n";
return 0;
}
return netTestPdu()->b[index];
}
void TestPdu::setC(float val)
{
// We can find "c" after the last element of "b".
DtNetFloat32* netC = (DtNetFloat32*) &(netTestPdu()->b[a()]);
// val will be implicitly cast to a DtNetFloat32.
*netC = val;
}
float TestPdu::c() const
{
// We can find "c" after the last element of "b".
DtNetFloat32* netC = (DtNetFloat32*) &(netTestPdu()->b[a()]);
// netC will be implicitly cast to a native float.
return *netC;
}
{
return (NetTestPdu*) packet();
}
{
return (const NetTestPdu*) packet();
}
DtPdu* TestPdu::create(const DtNetPacket *initial, DtBufferPtr buffer, DtPduFactory* pduFactory)
{
// Return a new'ed instance of this class.
return new TestPdu((NetTestPdu *) initial, buffer, pduFactory);
}
void TestPdu::addCallback(DtExerciseConn *conn, TestPduCb cb, void *usr)
{
// Register this class' create function with the DtExerciseConn's
// pduFactory so that the DtExerciseConn will know to create an instance of
// this class to represent incoming PDUs of the appropriate kind. If this
// is not done here, the user of this class will have to explicitly register
// the class with the PDU factory himself.
conn->pduFactory()->addCreator(TestPduKind, TestPdu::create);
// Cast the TestPduCb to the more general DtPduCallbackFcn and pass it along
// with the PDU kind to the DtExerciseConn who actually maintains the
// callback lists for all PDU kinds.
conn->addPduCallback(TestPduKind, (DtPduCallbackFcn) cb, usr);
}
{
// Cast the TestPduCb to the more general DtPduCallbackFcn and pass it along
// with the PDU kind to the DtExerciseConn who actually maintains the
// callback lists for all PDU kinds.
conn->removePduCallback(TestPduKind, (DtPduCallbackFcn) cb, usr);
}
{
// We defined TestPduKind in testPdu.h as DtPduKind(220).
return TestPduKind;
}
#endif

Document ID: Generated on Wed Jan 7 13:31:29 EST 2015 from SVN revision 149162
Copyright © 2005-2014 VT MÄK. All Rights Reserved (www.mak.com)