Project

General

Profile

RE: Cannot run multiple times my ARM/DSP application » main_dsp.cpp

Edwin McKay, 05/09/2016 12:32 PM

 
/**
* \file main.cpp
*
* \brief DSP side main routine for DSP Hello World application.
* The DSP simply sets itself up, and then waits for a
* message from the ARM, to which it replies.
*
* o 0
* | / Copyright (c) 2005-2010
* (CL)---o Critical Link, LLC
* \
* O
*/

#include <std.h>
#include <tsk.h>
#include <stdio.h>
#include <string.h>
#include <sem.h>

#include "core/cl_ipc_inbound.h"
#include "core/cl_ipc_outbound.h"

enum MsgCommand {
eReadFromARM,
eWriteToARM,
eReadFromDSP,
eWriteToDSP
};

enum MsgTypes {
eGlobalFixture,
eLibrary,
eGlobalUser,
eGlobalSerialPort,
eGlobalScannerJog,
eFeature,
eFeatureData,
eFeatureDataSource,
eFeatureExpressions,
eFeatureFixture,
eFeatureMeasurement,
eFeatureSegmentList,
eGlobalDigitalInputs,
eGlobalDigitalOutputs,
eGlobalFactory,
eDebugText
};

typedef struct {
MsgTypes type;
MsgCommand command;
} Header;

typedef struct {
Header header;
char textMessage[100]; // 100 bytes for now
} DebugText;

typedef struct {
Header header;
char fixtureName[33];
int fixtureID;
int fixtureTencoderpe;
int fixtureURR;
int fixtureHomeMethod;
int fixtureZeroMethod;
int fixtureMotion;
int fixtureDirection;
int fixtureOrientation;
double fixtureLinearTravel;
double fixtureRotaryTravel;
int motorPresent;
int motorPort;
int motorRotation;
double motorGearRatioIn;
double motorGearRationOut;
int motorType;
int motorMaxSpeed;
int motorStepsPerRevolution;
char motorAcceleration[16]; // todo
int encoderPresent;
int encoderPort;
int encoderRotation;
double encoderGearRatioIn;
double encoderGearRationOut;
int encoderResolution;
int encoderIndexPresent;
int encoderIndexInput;
int encoderIndexIsHome;
int encoderIndexIsEndOfTravel;
int encoderQuadratureMode;
int homeSensorPresent;
int homeSensorInput;
int homeSensorType;
int homeSensorDirection;
int endOfTravelSensorPresent;
int endOfTravelSensorInput;
int endOfTravelSensorType;
int endOfTravelSensorDirection;
char inputMapping[5]; // todo
char outputMapping[8]; // todo
double pulleyDiam;
int pulleyUnits;
} GlobalFixture_str ;

typedef struct {
Header header;
int SerialNumber;
double AnalogCalMinValue;
double AnalogCalMaxValue;
double AnalogCalGain;
double AnalogOffset;
int LowPowerScannerType;
int LowPowerValue;
int CalibrationUnits;
double CalibrationSmallPart;
double CalibrationLargePart;
double CalibrationmValue;
double CalibrationbValue;
char CalibrationDate[20];
} GlobalFactory_str ;

typedef struct {
Header header;
int DigInputID;
int ActivationType;
int FunctionID;
} GlobalDigitalInputs_str ;

typedef struct {
Header header;
int DigOutputID;
int Polarity;
int Latched;
int ServerID;
} GlobalDigitalOutputs_str ;

typedef struct {
Header header;
int SerialPortID;
int BaudRate;
int ParityBits;
int DataBits;
int StopBits;
int FlowControl;
int ProtocolFlag;
int DataFlag;
} GlobalSerialPort_str ;

typedef struct {
Header header;
int FeatureID;
int LibraryToFeatureKey;
char Name[16];
int AdvanceMode;
int AdvanceDelay;
double SizeOffset;
double PositionOffset;
int URRUnits;
int URRResolution;
int URRRounding;
int DataSourceExpressionType;
int DataSource_op;
int Sequence;
double ToleranceRejectPlus;
double ToleranceRejectMinus;
double ToleranceWarningPlus;
double ToleranceWarningMinus;
double ToleranceNominal;
bool ToleranceRejectEnablePlus;
bool ToleranceRejectEnableMinus;
bool ToleranceWarningEnablePlus;
bool ToleranceWarningEnableMinus;
int UseFixtureToCollectData;
int UseFixtureForAutoPositioning;
double UserSingleMasterSize;
} Feature_str ;

typedef struct {
Header header;
int FeatureToDataKey;
int LibraryToDataKey;
char Label[16];
int DisplayLine;
int SerialPortSelected;
int UseAsLimits;
} FeatureData_str ;

typedef struct {
Header header;
int FeatureToDataKey;
int LibraryToDataKey;
int TermType;
double TermConstanst;
int TermFeatureID;
int TermID;
} FeatureDataSource_str ;

typedef struct {
Header header;
int FeatureToDataKey;
int LibraryToDataKey;
int Segment ;
} FeatureSegmentList_str ;

typedef struct {
Header header;
int FeatureToDataKey;
int LibraryToDataKey;
int Type;
int EdgeCount;
int GlassMode;
int InternalRefFlag;
int LowerRefFlag;
int UpperRefFlag;
int PartSegmentIndex;
int PartModeFlag;
int SamplingMode;
int AveragingMode;
int AveragingPeriod;
int GoodScans;
int EnterDelayMode;
int EnterDelayPeriod;
char FormatString[34];
int PolledFlag;
} FeatureMeasurement_str ;

typedef struct {
Header header;
int FeatureToDataKey;
int LibraryToDataKey;
int LineOrder;
char ExpressionText[50];
} FeatureExpressions_str ;

typedef struct {
Header header;
int FeatureToDataKey;
int LibraryToDataKey;
int FixtureID;
int DisableMotorAfterMove;
int IDODWALLComputeWall;
int MeasurementMode;
int UsageMode;
int AutomaticShankDiamFlag;
double Speed;
int StepMode;
int ReturnPosition;
int NumberOfMeasurementPositions;
double TotalDistance;
double StartPosition;
double ShankDiameter;
} FeatureFixture_str ;

typedef struct {
Header header;
int ScannerJogID;
double mOffset;
double bOffset;
double LowSize;
double HighSize;
} GlobalScannerJog_str ;

typedef struct {
Header header;
char LanguageName[20];
char LanguagePrefix[5];
char Password[8];
int PowerUpLocked;
int AllowNominalChanges;
int AllowRemastering;
int AllowLibraryChanges;
int AdvancedScreenOperation;
int AutoScrollEnable;
int AudibleAlarm;
int FlipScreen180;
int ScannerDoubleDiff;
int LatchScanErrors;
int IgnoreScanErrors;
char IPAddress[20];
int ButtonsDefined;
int ButtonID_1;
int ButtonID_2;
int ButtonID_3;
} GlobalUser_str ;

typedef struct {
Header header;
int LibraryID;
char LibraryName[16];
double UserDualPointMasterSize1;
double UserDualPointMasterSize2;
double AlarmDuration;
int AlarmEnable;
int SQCBatchSize;
int SQCBatchClearAtEndOfBatch;
int SQCBatchPrintAtEndOfBatch;
int FeatureCount;
int SerialDOFAutoSendFlag;
int SerialDOFAutoSendDelay;
int SerialDOFDateTimeFlag;
int SerialDOFLabelFlag;
int SerialDOFHeaderType;
int SerialDOFRequestorType;
char SerialDOFRequestorString[5];
int SerialDOFSeparatorType;
char SerialDOFSeparatorString[5];
int SerialDOFTerminatorType;
char SerialDOFTerminatorString[5];
int SerialDOFDecimalFlag;
int SerialDOFSignFlag;
double LegacySlideCounterPerInch;
int ReportSelected1;
int ReportSelected2;
int ReportSelected3;
int ReportSelected4;
char ReportUserTitle[33];
int ReportPrinterPort;
int ReportDisableHeaderAndFooter;
int ReportDisableFormFeed;
int URRUnits;
int URRResolution;
int URRRounding;
} Library_str ;

typedef union {
Header header;
DebugText debugMsg;
Library_str library;
GlobalFixture_str globalFixture;
GlobalUser_str globalUser;
GlobalScannerJog_str globalScannerJog;
FeatureFixture_str featureFixture;
FeatureExpressions_str featureExpressions;
FeatureMeasurement_str featureMeasurement;
FeatureSegmentList_str featureSegmentList;
FeatureDataSource_str featureDataSource;
FeatureData_str featureData;
Feature_str feature;
GlobalFactory_str globalFactory;
GlobalDigitalInputs_str globalDigitalInputs;
GlobalDigitalOutputs_str globalDigitalOutputs;
GlobalSerialPort_str globalSerialPort;
} allTypes;

allTypes messageA2D; // message data between DSP and ARM

using namespace MityDSP;

// Forward declarations
void init();
void debugPrint(char *buffer);
int handleInboundMessage(void *apBuffer, uint32_t anLength, void *apUserArg) ;
int handleInboundMessageBM(void *apBuffer, uint32_t anLength, void *apUserArg) ;

// Object for sending debug messages (these are received and printed to stdout by tcDspApp)
tcCL_IPCOutbound* gpDebug;
// Object for sending GPPMSGQ1 messages that the ARM will receive
tcCL_IPCOutbound* gpOutbound;
// Object for receiving DSPMSGQ0 messages that the DSP will receive
tcCL_IPCInbound* gpInbound;
// semaphore for waiting on messages
SEM_Handle msgRecvd;

#include "core/DspSerial.h"
#include "core/DspFpgaSerial.h"
#define RS232_SERIAL_BASE 0x66000080


/* *********************
void trySerialPort() {
int bytes, rv;
unsigned int my_base_addr = RS232_SERIAL_BASE; // was 0xE000001C;
unsigned int my_vector = 6;
char input[32], output[32];
tcDspFpgaSerial *my_port;

// create a serial interface
my_port = new tcDspFpgaSerial((void *)my_base_addr, my_vector,
38400, 8, 1, tcDspFpgaSerial::gnUART_NOPARITY);
// start using the port
my_port->enable(true);

// send something...
sprintf(output, "Testing..1..2..3..");
bytes = strlen(output);
rv = my_port->put((void *)output, bytes);
if (rv != bytes)
{
printf("Failed, only sent %1d of %1d bytes\n", rv, bytes);
}

// receive something...
bytes = sizeof(input);
rv = my_port->get((void *)input, bytes);
printf("Received %1d bytes of data\n", rv);

} ****************** */
extern "C" {
typedef unsigned int u_8 ;
MityDSP::tcDspFpgaSerial *Com1;
MityDSP::tcDspSerial *Com2;
bool Com1_initialized=false;
bool Com2_initialized=false;

typedef struct serialPort_str {
MityDSP::tcSerial *Com;
bool initialized;
void (*rx_func)(u_8 c, u_8 stat);
} serialPort;

void (*rx_func1)(u_8 c, u_8 stat) = NULL;
void (*rx_func2)(u_8 c, u_8 stat) = NULL;
extern "C" int StartMonitorTask1(void);
extern "C" int StartMonitorTask2(void);
}

serialPort spCom[2];

char gBuffer[200];

#define Sec2tick(x) (1000*x)
#define GGE_PRIO 9

void ser1_task() {
int count=0;
int idx=0;
static char buf[64];

while (!Com1_initialized)
TSK_sleep(Sec2tick(1));

while (1) {
//if (count == 0) {
count = strlen(gBuffer); //sprintf(buf,"%s %d\n", gBuffer, idx++);
int j=0, k=0;
for (int i=0; i < count; i++){
if ((int)gBuffer[i] < 32) {
k=sprintf(&buf[j], "*%2x!", (unsigned char)gBuffer[i]);
j = j+k;
} else {
buf[j] = gBuffer[i];
j++;
}
}
buf[j]=0;
count = j;
//}
Com1->put(buf, count, 0, 0);
TSK_sleep(1250);
/*count = Com1->get(buf, 60, 1000);
if (count && Com1_initialized && (rx_func1 != NULL)) {
idx = 0;
while(idx < count)
rx_func1(buf[idx++],0);
} else {
TSK_sleep(5);
}*/
}
}

extern "C" int StartMonitorTask1(void)
{
TSK_Handle task;
TSK_Attrs attrs;

attrs = TSK_ATTRS;
attrs.priority = GGE_PRIO;
attrs.name = "ser1_task";
task = TSK_create((Fxn)ser1_task, &attrs);
if(task)
return 1;
else
return 0;
}

void ser2_task() {
int count;
int idx=0;
static int flipper;
static char buf[64];
char workingBuffer[100]; // just some junk

while (!Com2_initialized)
TSK_sleep(Sec2tick(1));

while (1) {
/*count = sprintf(buf, "hello world #2 %d\n", idx++);
Com2->put(buf, count, 0, 0);
TSK_sleep(1250);*/
/**/
count = Com2->get(buf, 64, 500);
if (count && Com2_initialized && Com1_initialized ) {
// copy the data to a global buffer and...
sprintf(gBuffer, "%s", buf);
unsigned char command = gBuffer[0];
if (command == 208 && gBuffer[1] == '0' && gBuffer[2] == '2') { // was a byte request..for 02
// send back some sort of string
flipper++;
count = sprintf(workingBuffer, "%c02%02d",0xe0, flipper);
if (flipper > 5) flipper = 0;
sprintf(gBuffer, "%s!!!!", workingBuffer);
Com2->put(workingBuffer,count,0,0);
} else {
command = command + 0x10;
count = sprintf(workingBuffer, "%c%c%c%02d",command, gBuffer[1], gBuffer[2], flipper);
Com2->put(workingBuffer,count,0,0);
}
//} else {
gBuffer[count] = NULL;
} else if (count == 0) {
sprintf(gBuffer, "empty string %d ", idx++);
}
if (count && Com2_initialized && (rx_func2 != NULL)) {
idx = 0;
while(idx < count)
rx_func2(buf[idx++],0);
} else {
TSK_sleep(5);
}
/* */
}
}

extern "C" int StartMonitorTask2(void)
{
TSK_Handle task;
TSK_Attrs attrs;

attrs = TSK_ATTRS;
attrs.priority = GGE_PRIO;
attrs.name = "ser1_task";
task = TSK_create((Fxn)ser2_task, &attrs);
if(task)
return 1;
else
return 0;
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// This routine actually creates the comm port. It is not to be called
// directly by users.
///////////////////////////////////////////////////////////////////////////////

extern "C" void InitSerPort(serialPort *xPort, int style, int baud, u_8 dbits, u_8 parity,
u_8 sbits, u_8 hnd_shk,
void (*rx_func)(u_8 c, u_8 stat))
{
// PORT tmp;

MityDSP::tcDspSerial::tsConfig Cfg;

int DBitsCfg,SbitsCfg,PrtyCfg;

DBitsCfg = dbits;

switch(parity)
{
case 'e':
case 'E':
PrtyCfg = MityDSP::tcDspFpgaSerial::gnUART_EVENPARITY;
Cfg.parity = MityDSP::tcDspSerial::tsConfig::even;
break;
case 'o':
case 'O':
PrtyCfg = MityDSP::tcDspFpgaSerial::gnUART_ODDPARITY;
Cfg.parity = MityDSP::tcDspSerial::tsConfig::odd;
break;
case 'n':
case 'N':
PrtyCfg = MityDSP::tcDspFpgaSerial::gnUART_NOPARITY;
Cfg.parity = MityDSP::tcDspSerial::tsConfig::none;

break;
}

SbitsCfg = sbits;

if(style == 1)
{
xPort->Com = new MityDSP::tcDspFpgaSerial((void *)RS232_SERIAL_BASE,
baud,
DBitsCfg,
SbitsCfg,
PrtyCfg,
false);

xPort->rx_func = rx_func;
}
else
{
xPort->Com = MityDSP::tcDspSerial::GetInstance(MityDSP::tcDspSerial::UART0);
Cfg.baud = baud;
Cfg.databits = DBitsCfg;
Cfg.stopbits = SbitsCfg;
Cfg.hw_flowcontrol = false;
Com2->configure(&Cfg);

xPort->rx_func = rx_func;
StartMonitorTask2();
}
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// This is the main initialization call to create the comm ports. It should
// be called before interrupts are enabled
///////////////////////////////////////////////////////////////////////////////

extern "C" void InitSerPortX(serialPort *xPort, int style, int baud)
{
xPort->Com = 0;
xPort->initialized = false;
InitSerPort(xPort, style, baud, 8, 'n', 1, 0, 0);
xPort->initialized = true;
}



extern "C" void InitSerPortndc(u_8 port, u_8 io, int baud, u_8 dbits, u_8 parity,
u_8 sbits, u_8 hnd_shk,
void (*rx_func)(u_8 c, u_8 stat))
{
// PORT tmp;

MityDSP::tcDspSerial::tsConfig Cfg;

int DBitsCfg,SbitsCfg,PrtyCfg;

DBitsCfg = dbits;

switch(parity)
{
case 'e':
case 'E':
PrtyCfg = MityDSP::tcDspFpgaSerial::gnUART_EVENPARITY;
Cfg.parity = MityDSP::tcDspSerial::tsConfig::even;
break;
case 'o':
case 'O':
PrtyCfg = MityDSP::tcDspFpgaSerial::gnUART_ODDPARITY;
Cfg.parity = MityDSP::tcDspSerial::tsConfig::odd;
break;
case 'n':
case 'N':
PrtyCfg = MityDSP::tcDspFpgaSerial::gnUART_NOPARITY;
Cfg.parity = MityDSP::tcDspSerial::tsConfig::none;

break;
}

SbitsCfg = sbits;

if(port == 1)
{
Com1 = new MityDSP::tcDspFpgaSerial((void *)RS232_SERIAL_BASE,
baud,
DBitsCfg,
SbitsCfg,
PrtyCfg,
false);

rx_func1 = rx_func;
StartMonitorTask1();
}
else
{
Com2 = MityDSP::tcDspSerial::GetInstance(MityDSP::tcDspSerial::UART0);
Cfg.baud = baud;
Cfg.databits = DBitsCfg;
Cfg.stopbits = SbitsCfg;
Cfg.hw_flowcontrol = false;
Com2->configure(&Cfg);

rx_func2 = rx_func;
StartMonitorTask2();
}
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// This is the main initialization call to create the comm ports. It should
// be called before interrupts are enabled
///////////////////////////////////////////////////////////////////////////////

extern "C" void InitSerPorts(int baud1, int baud2)
{
Com1 = 0;
Com2 = 0;

Com1_initialized = false;
Com2_initialized = false;
InitSerPortndc(1, 0, baud1, 8, 'n', 1, 0, 0);
InitSerPortndc(2, 0, baud2, 8, 'n', 1, 0, 0);
Com1_initialized = true;
Com2_initialized = true;

}

/**
* Main routine.
*/
int main(int argc, char* argv[])
{

int bWaitForMe = argc;
int iCounter = 0;
int iCounter2 = 0;
while (bWaitForMe) {
iCounter++; // wait for the debugger here....
if (iCounter > 10000) {
iCounter2++;
iCounter = 0;
}
}

// initialize the DSPLink system
tcCL_IPCInit::GetInstance();

msgRecvd = SEM_create(0, NULL);

// Launch an initialization task
TSK_Attrs* lpAttrs = new TSK_Attrs;
*lpAttrs = TSK_ATTRS;
lpAttrs->name = "Initialize";
lpAttrs->stacksize = 8192*2;
lpAttrs->priority = 5;
TSK_create((Fxn)init,lpAttrs);

// according to loc_init.c:loc_init it appears the first port is the 232 and the second one is the 422
// InitSerPorts(baud232, baud422);
// This will create a task for each serial port.
/*
* InitSerPortX(&spCom[0], 1, 38400); //. TBD use enum for style of serial port either FPGA or plain DSP
StartMonitorTask1();
InitSerPortX(&spCom[1], 2, 38400);
StartMonitorTask2();*/

// we're done exit and leave the tasks going...
return 0;
}

/**
* Initialize the inbound and outbound IPC objects.
*
* \return None.
*/
void init()
{
// Message to ARM core.
char lpReturnMessage[] = "DSP Initialization finished.";
// Buffer for return message
char* lpMessageBuffer = NULL;

// Create the outbound debug link
gpDebug = new tcCL_IPCOutbound("debug");

// Create the inbound link for messages to the DSP
gpInbound = new tcCL_IPCInbound();

gpInbound->Open("DSPMSGQ0", 8);

// Create the outbound controller for sending messages to the ARM
gpOutbound = new tcCL_IPCOutbound("GPPMSGQ1");

if (NULL != gpInbound)
{
// Register a callback function to handle messages from the ARM
gpInbound->RegisterCallback(handleInboundMessageBM, (void*)NULL);
}
// Now that initialization is complete, let the ARM know with a message

// Obtain a dsplink buffer for the return message
lpMessageBuffer = (char*)gpOutbound->GetBuffer(sizeof(messageA2D) + 1);
// Make sure we received a valid buffer
if (NULL != lpMessageBuffer)
{

// create a debug message for the first one...
sprintf(messageA2D.debugMsg.textMessage, "%s", lpReturnMessage);
messageA2D.debugMsg.header.type = eDebugText;
// Copy our message to the buffer
memcpy(lpMessageBuffer, &messageA2D, sizeof(messageA2D));

// Send first message to the ARM
gpOutbound->SendMessage(lpMessageBuffer);
}

// now wait on msgs from the ARM
bool keepGoing=true;
while (keepGoing) {
SEM_pend(msgRecvd, SYS_FOREVER);
// msg rcv'd... now what...
}
}

/**
* Callback function that handles messages from the ARM.
*
* \param apBuffer Pointer to message buffer.
* \param anLength Length of message.
* \param apUserArg Pointer to user defined argument
*
* \return 0 on sucess.
*/
int handleInboundMessage(void* apBuffer, uint32_t anLength, void* apUserArg)
{
int retval = 0;
// The return message to the ARM
char lpReturnMessage[] = "Hello World. DSP Received Message = \'";
// Buffer for return message
char* lpMessageBuffer = NULL;
// Obtain a dsplink buffer for the return message
lpMessageBuffer = (char*)gpOutbound->GetBuffer(strlen(lpReturnMessage) + strlen((const char*)apBuffer) + 2);
// Make sure we received a valid buffer
if (NULL != lpMessageBuffer)
{
// Copy our message to the buffer
strcpy(lpMessageBuffer, lpReturnMessage);
// Append the received message to the buffer
strcpy((char*)&lpMessageBuffer[strlen(lpReturnMessage)], (const char *)apBuffer);
// Null terminate the string
lpMessageBuffer[strlen(lpMessageBuffer)+1] = 0;
// Append the closing quotation
lpMessageBuffer[strlen(lpMessageBuffer)] = '\'';
// Send the message back to the ARM
retval = gpOutbound->SendMessage(lpMessageBuffer);
}
else
{
retval = -1;
}
return retval;
}

/**
* Callback function that handles messages from the ARM.
*
* \param apBuffer Pointer to message buffer.
* \param anLength Length of message.
* \param apUserArg Pointer to user defined argument
*
* \return 0 on sucess.
*/
int handleInboundMessageBM(void* apBuffer, uint32_t anLength, void* apUserArg)
{
int retval = 0;
// Buffer for return message
char* lpMessageBuffer = NULL;

// Obtain a dsplink buffer for the return message
lpMessageBuffer = (char*)gpOutbound->GetBuffer(sizeof(messageA2D));

// Make sure we received a valid buffer
if (NULL != lpMessageBuffer)
{
// Process message from ARM here...
// extract msg type from message
memcpy(&messageA2D, apBuffer, anLength);
switch (messageA2D.header.type) {
case eLibrary:
// for fun just increment some values and send back to ARM
messageA2D.library.FeatureCount++;
messageA2D.library.SQCBatchSize++;
messageA2D.library.header.command = eWriteToARM;
break;
};
// Copy our message to the buffer
memcpy(lpMessageBuffer, &messageA2D, sizeof(messageA2D));

// Send the message back to the ARM
retval = gpOutbound->SendMessage(lpMessageBuffer);
SEM_post(msgRecvd); // trigger that we have recv'd and responded to the ARM Message.
gpOutbound->ReturnBuffer(apBuffer);
}
else
{
retval = -1;
}

return retval;
}

/**
* Function for sending debug messages to the ARM.
*
* \param buffer Null terminated string to be printed.
*
* \return None.
*/
void debugPrint(char* pMsg)
{
// The length of the message to be sent
int len = strlen(pMsg);
// Pointer to dsplink buffer where to write the message
char* pBuffer;
// Make sure the debug IPC outbound object has been initialized
if (gpDebug == NULL)
return;
// Get a buffer for the message
pBuffer = (char *)gpDebug->GetBuffer(len+1);
// Check that the buffer is valid
if (pBuffer)
{
// Copy the message to the buffer
strcpy(pBuffer, pMsg);
// Send the message
gpDebug->SendMessage(pBuffer);
}
}

(3-3/3)