MX Foundation 4
csdb_aperiodic.cs
/*******************************************************************************
//
// File:
// csdb_aperiodic.cs
//
// Copyright (c) MAX Technologies Inc. 1988-2019, All Rights Reserved.
// CONFIDENTIAL AND PROPRIETARY INFORMATION WHICH IS THE
// PROPERTY OF MAX TECHNOLOGIES INC.
//
// This demo shows how to set a basic periodic transmission
// with the scheduler and update the CSDB data.
//
// Hardware Requirements:
// - MAXT Flex with loopback between first TX and RX CSDB Enhanced channels.
//
*******************************************************************************/
#define LOOPBACK
//#define LOCAL
using System;
using static MAXT.MXFoundation.mxf;
using System.Runtime.InteropServices;
using System.Text;
namespace csdb_example
{
class csdb_aperiodic
{
const int BUFFER_SIZE = 4096; // 4KB
const int MAX_TX_RECORDS_TO_TRANSMIT = 8;
const int BLOCKCOUNT = 6;
public static void Main(string[] args)
{
UInt32 rc;
UInt64 server;
UInt64 device = 0;
var module = new UInt64[1];
UInt64 count = 0;
var rxChannel = new UInt64[1];
var txChannel = new UInt64[1];
UInt64 rxBuffer = 0;
UInt64 txBuffer = 0;
IntPtr txHostBuffer = IntPtr.Zero;
IntPtr rxHostBuffer = IntPtr.Zero;
UInt64 dev, mod, port;
MXF_SYSTEM_INIT_ATTRIBUTE_UINT64_HANDLER _initHandler = initHandler;
// Connects to services and initialize environment
#if LOCAL
rc = mxfServerConnect("0.0.0.0", "", "", Convert.ToUInt64(false), out server);
#else
rc = mxfServerConnect("192.168.0.1", "admin", "admin", Convert.ToUInt64(false), out server);
#endif
if (rc != MAXT_SUCCESS)
{
Console.Write("Failed to connect; rc=0x{0:x8}", rc);
Console.WriteLine();
Console.WriteLine("Press a key to terminate");
Console.Read();
return;
}
// Initialize the server
Console.WriteLine();
Console.WriteLine("Starting");
// initialize init callback handler to set IPM-ASYNC-EH first TX and RX channel to CSDB
if (rc == MAXT_SUCCESS)
rc = mxfSystemInit(server);
// Get the first device handle
if (rc == MAXT_SUCCESS)
rc = mxfSystemDeviceGet(server, 0, out device);
if (rc == MAXT_SUCCESS)
rc = mxfDeviceModuleAllGet(device, MXF_MODULE_ASYNC_EH, 1, out count, module);
// Obtain the first CSDB Protocol RX channel (RX logical #0)
if ((rc == MAXT_SUCCESS) && (count != 0))
rc = mxfModuleChannelAllGet(module[0], MXF_CLASS_CSDB, MXF_SCLASS_RX_CHANNEL, 1, out count, rxChannel);
// Obtain the first CSDB Protocol TX channel (TX logical #0)
if ((rc == MAXT_SUCCESS) && (count != 0))
rc = mxfModuleChannelAllGet(module[0], MXF_CLASS_CSDB, MXF_SCLASS_TX_CHANNEL, 1, out count, txChannel);
// If module or channel not found, return an error
if ((rc == MAXT_SUCCESS) && (count == 0))
rc = MAXT_ERROR_NOT_FOUND;
// Set timebase to 64-bit microseconds
if (rc == MAXT_SUCCESS)
rc = mxfSystemTimeBaseSet(server, MXF_TIMEBASE_DEVICE_USEC);
// Get the physical port location
if (rc == MAXT_SUCCESS)
{
rc = mxfChannelLocationGet(rxChannel[0], out dev, out mod, out port);
if (rc == MAXT_SUCCESS)
Console.WriteLine("Acquisition Channel (RX) location={0}.{1}.{2}", dev, mod, port);
}
if (rc == MAXT_SUCCESS)
{
rc = mxfChannelLocationGet(txChannel[0], out dev, out mod, out port);
if (rc == MAXT_SUCCESS)
Console.WriteLine("Transmitter Channel (TX) location={0}.{1}.{2}", dev, mod, port);
}
//Activate loopback before transmission and reception
#if LOOPBACK
if (rc == MAXT_SUCCESS)
rc = mxfAttributeUint64Set(rxChannel[0], KMXF_CSDB_TX_RX_TEST_LB, VMXF_ENABLE);
#endif
// Set block count
if (rc == MAXT_SUCCESS)
rc = mxfAttributeUint64Set(rxChannel[0], KMXF_CSDB_BLOCKCOUNT, BLOCKCOUNT);
if (rc == MAXT_SUCCESS)
rc = mxfAttributeUint64Set(txChannel[0], KMXF_CSDB_BLOCKCOUNT, BLOCKCOUNT);
// Allocate RX acquisition buffer
if (rc == MAXT_SUCCESS)
rc = mxfRxAcqBufferAlloc(rxChannel[0], BUFFER_SIZE, out rxBuffer, IntPtr.Zero);
// Allocate Aperiodic TX static buffers for HIGH priority queue
if (rc == MAXT_SUCCESS)
rc = mxfTxAperiodicBufferAlloc(txChannel[0], MXF_TXAPERIODIC_PRIORITY_HIGH, BUFFER_SIZE, out txBuffer, IntPtr.Zero);
// Allocate host buffer
if (rc == MAXT_SUCCESS)
{
try
{
txHostBuffer = Marshal.AllocHGlobal(BUFFER_SIZE);
}
catch (OutOfMemoryException)
{
rc = MAXT_ERROR_MEM;
}
try
{
rxHostBuffer = Marshal.AllocHGlobal(BUFFER_SIZE);
}
catch (OutOfMemoryException)
{
rc = MAXT_ERROR_MEM;
}
}
// Start the acquisition process
if (rc == MAXT_SUCCESS)
rc = mxfRxAcqModeSet(rxBuffer, MXF_RXACQ_MODE_LINEAR);
if (rc == MAXT_SUCCESS)
{
rc = mxfRxAcqStart(rxBuffer, MXF_RXACQ_FLAG_DEFAULT, 0, 0);
if (rc == MAXT_SUCCESS)
{
Console.WriteLine();
Console.WriteLine("Acquisition started");
}
}
if (rc == MAXT_SUCCESS)
{
// Start CSDB data transmission
rc = StartAperiodicTransmissionDefault(txBuffer, txHostBuffer);
if (rc == MAXT_SUCCESS)
rc = ReadAcquisitionData(rxBuffer, rxHostBuffer);
if (rc == MAXT_SUCCESS)
rc = StartAperiodicTransmissionAbsolute(device, txBuffer, txHostBuffer);
if (rc == MAXT_SUCCESS)
rc = ReadAcquisitionData(rxBuffer, rxHostBuffer);
if (rc == MAXT_SUCCESS)
rc = StartAperiodicTransmissionRecordAbsolute(device, txBuffer, txHostBuffer);
if (rc == MAXT_SUCCESS)
rc = ReadAcquisitionData(rxBuffer, rxHostBuffer);
if (rc == MAXT_SUCCESS)
rc = StartAperiodicTransmissionRecordRelative(txBuffer, txHostBuffer);
if (rc == MAXT_SUCCESS)
rc = ReadAcquisitionData(rxBuffer, rxHostBuffer);
}
// Stop and flush unread data
if (rc == MAXT_SUCCESS)
rc = mxfRxAcqStop(rxBuffer);
if (rc == MAXT_SUCCESS)
{
rc = mxfRxAcqClear(rxBuffer);
if (rc == MAXT_SUCCESS)
{
Console.WriteLine();
Console.WriteLine("Acquisition stopped");
}
}
// Catch any previous error
if (rc != MAXT_SUCCESS)
{
StringBuilder buffer = new StringBuilder(256);
if (mxfSystemErrorStringGet(server, rc, 256, buffer) != MAXT_SUCCESS)
buffer.Append(string.Format("ERROR # 0x{0:x8}", rc));
Console.WriteLine(buffer);
}
Console.WriteLine();
Console.WriteLine("Terminating");
// Free all buffers and terminate
if (rxBuffer != 0)
mxfRxAcqBufferFree(rxBuffer);
if (txBuffer != 0)
if (txHostBuffer != IntPtr.Zero)
Marshal.FreeHGlobal(txHostBuffer);
if (rxHostBuffer != IntPtr.Zero)
Marshal.FreeHGlobal(rxHostBuffer);
Console.WriteLine();
Console.WriteLine("Press enter to terminate");
Console.Read();
return;
}
/***************************************************************************************************************/
// ReadAcquisitionData
/***************************************************************************************************************/
private static UInt32 ReadAcquisitionData(UInt64 rxBuffer, IntPtr rxHostBuffer)
{
IntPtr recPtr = rxHostBuffer;
UInt64 status, msgsCount, bytesCount;
UInt64 j;
UInt32 rc, data;
{
data = new byte[12]
};
// Read and display records
rc = mxfCSDBRxAcqRead(rxBuffer, 0, BUFFER_SIZE, out status, out msgsCount, out bytesCount, rxHostBuffer);
if (rc == MAXT_SUCCESS)
{
Console.WriteLine("Read {0} messages", msgsCount);
for (j = 0; (rc == MAXT_SUCCESS) && (j < msgsCount); j++)
{
recCsdb = (MXF_CSDB_DATAREC)Marshal.PtrToStructure(recPtr, typeof(MXF_CSDB_DATAREC));
Console.Write("{0:D2}: Timetag {1} - ", j, recCsdb.timeTag);
for (data = 0; data < 6; data++)
Console.Write("{0:x2}", recCsdb.data[data]);
Console.WriteLine();
rc = mxfCSDBNextDataRecordPtrGet(recPtr, out recPtr);
}
}
if (rc != MAXT_SUCCESS)
Console.WriteLine("Acquisition read failed; rc=0x{0:x8}", rc);
return rc;
}
/***************************************************************************************************************/
// WaitQueueEmpty
/***************************************************************************************************************/
private static UInt32 WaitQueueEmpty(UInt64 txBuffer)
{
UInt64 msgCount, usedBytes, freeBytes;
UInt32 rc;
// Wait till all records are sent on the interface
do
{
rc = mxfTxAperiodicBufferStatusGet(txBuffer, out msgCount, out usedBytes, out freeBytes);
if (rc != MAXT_SUCCESS)
return rc;
} while (msgCount != 0);
// Make sure they are received
mxfSleep(100);
return MAXT_SUCCESS;
}
/***************************************************************************************************************/
// StartAperiodicTransmissionDefault
/***************************************************************************************************************/
private static UInt32 StartAperiodicTransmissionDefault(UInt64 txBuffer, IntPtr txHostBuffer)
{
IntPtr recPtr = txHostBuffer;
UInt64 dataIdx;
UInt32 record;
UInt64 delay100ms = 100000;
UInt32 rc = MAXT_SUCCESS;
{
data = new byte[12]
};
// In the example below the record are sent back-to-back 100 ms in the future.
Console.WriteLine();
Console.WriteLine("Aperiodic transmission (Relative Start Time-Default)");
// Set the record
for (record = 0; (record < MAX_TX_RECORDS_TO_TRANSMIT) && (rc == MAXT_SUCCESS); record++)
{
if (record == 0)
{
// Set sync block
rec.timeTag = 0;
rec.control = 0;
rec.repeatCount = 1;
rec.reserved = 0;
for (dataIdx = 0; dataIdx < BLOCKCOUNT; dataIdx++)
rec.data[dataIdx] = 0xA5;
}
else
{
// Set each record: LABEL=5, SI=1
rec.timeTag = 0;
rec.control = 0;
rec.repeatCount = 1;
rec.reserved = 0;
rec.data[0] = 5;
rec.data[1] = 1;
for (dataIdx = 2; dataIdx < BLOCKCOUNT; dataIdx++)
rec.data[dataIdx] = (byte)(0x11 * record);
}
Marshal.StructureToPtr(rec, recPtr, false);
rc = mxfCSDBNextDataRecordPtrGet(recPtr, out recPtr);
}
// Transmit the array of records
rc = mxfCSDBTxAperiodicWrite(txBuffer, MXF_TXAPERIODIC_FLAG_DEFAULT, delay100ms, MAX_TX_RECORDS_TO_TRANSMIT, txHostBuffer);
// Wait TX completion
if (rc == MAXT_SUCCESS)
rc = WaitQueueEmpty(txBuffer);
return rc;
}
/***************************************************************************************************************/
// StartAperiodicTransmissionAbsolute
/***************************************************************************************************************/
private static UInt32 StartAperiodicTransmissionAbsolute(UInt64 device, UInt64 txBuffer, IntPtr txHostBuffer)
{
IntPtr recPtr = txHostBuffer;
UInt64 dataIdx;
UInt32 record, rc;
UInt32 delay100ms = 100000;
UInt64 currentTime;
{
data = new byte[12]
};
// Get the current time
rc = mxfDeviceTimerGet(device, out currentTime);
if (rc != MAXT_SUCCESS)
return rc;
// In the example below the option MXF_TXAPERIODIC_FLAG_ABSOLUTE_START_TIME
// is used. In this case the records are sent freely (no time clock control)
// with a start transmission time based on the device clock + 100ms in the futur.
Console.WriteLine();
Console.WriteLine("Aperiodic transmission (Absolute)");
for (record = 0; (record < MAX_TX_RECORDS_TO_TRANSMIT) && (rc == MAXT_SUCCESS); record++)
{
if (record == 0)
{
// Set sync block
rec.timeTag = 0;
rec.control = 0;
rec.repeatCount = 1;
rec.reserved = 0;
for (dataIdx = 0; dataIdx < BLOCKCOUNT; dataIdx++)
rec.data[dataIdx] = 0xA5;
}
else
{
// Set each record: LABEL=6, SI=2
rec.timeTag = 0;
rec.control = 0;
rec.repeatCount = 1;
rec.reserved = 0;
rec.data[0] = 6;
rec.data[1] = 2;
for (dataIdx = 2; dataIdx < BLOCKCOUNT; dataIdx++)
rec.data[dataIdx] = (byte)record;
}
Marshal.StructureToPtr(rec, recPtr, false);
rc = mxfCSDBNextDataRecordPtrGet(recPtr, out recPtr);
}
// Transmit the array of records
rc = mxfCSDBTxAperiodicWrite(txBuffer, MXF_TXAPERIODIC_FLAG_ABSOLUTE_START_TIME, currentTime + delay100ms, MAX_TX_RECORDS_TO_TRANSMIT, txHostBuffer);
// Wait TX completion
if (rc == MAXT_SUCCESS)
rc = WaitQueueEmpty(txBuffer);
return rc;
}
/***************************************************************************************************************/
// StartAperiodicTransmissionRecordAbsolute
/***************************************************************************************************************/
private static UInt32 StartAperiodicTransmissionRecordAbsolute(UInt64 device, UInt64 txBuffer, IntPtr txHostBuffer)
{
IntPtr recPtr = txHostBuffer;
UInt64 dataIdx;
UInt32 record;
UInt64 currentTime;
UInt32 rc;
{
data = new byte[12]
};
// In the example below the timetag in the record is set
// and the option MXF_TXAPERIODIC_FLAG_USE_RECORD_ABSOLUTE_TIME
// In this case the records are sent based on the absolute clock
// timing specified in the timetag field for each records.
Console.WriteLine();
Console.WriteLine("Aperiodic transmission (Absolute with timetag)");
// Get the current time
rc = mxfDeviceTimerGet(device, out currentTime);
if (rc != MAXT_SUCCESS)
return rc;
currentTime += 100000;
// Set the record
for (record = 0; (record < MAX_TX_RECORDS_TO_TRANSMIT) && (rc == MAXT_SUCCESS); record++)
{
// The scheduling timetag is as follow:
// clock current time + 1 ms
// Each record are sent with a timetag a 10000 uS (10 ms) interval.
if (record == 0)
{
// Set sync block
rec.timeTag = currentTime + ((record + 1) * 10000);
rec.control = 0;
rec.repeatCount = 1;
rec.reserved = 0;
for (dataIdx = 0; dataIdx < BLOCKCOUNT; dataIdx++)
rec.data[dataIdx] = 0xA5;
}
else
{
// Set each records: LABEL=7, SI=1
rec.timeTag = currentTime + ((record + 1) * 10000);
rec.control = 0;
rec.repeatCount = 1;
rec.reserved = 0;
rec.data[0] = 7;
rec.data[1] = 1;
for (dataIdx = 2; dataIdx < BLOCKCOUNT; dataIdx++)
rec.data[dataIdx] = (byte)(2 * record);
}
Marshal.StructureToPtr(rec, recPtr, false);
rc = mxfCSDBNextDataRecordPtrGet(recPtr, out recPtr);
}
// Transmit the array of records
rc = mxfCSDBTxAperiodicWrite(txBuffer, MXF_TXAPERIODIC_FLAG_USE_RECORD_ABSOLUTE_TIME, 0, MAX_TX_RECORDS_TO_TRANSMIT, txHostBuffer);
// Wait TX completion
if (rc == MAXT_SUCCESS)
rc = WaitQueueEmpty(txBuffer);
return rc;
}
/***************************************************************************************************************/
// StartAperiodicTransmissionRecordRelative
/***************************************************************************************************************/
private static UInt32 StartAperiodicTransmissionRecordRelative(UInt64 txBuffer, IntPtr txHostBuffer)
{
IntPtr recPtr = txHostBuffer;
UInt64 dataIdx;
UInt32 record;
UInt64 currentTime;
UInt32 rc = MAXT_SUCCESS;
{
data = new byte[12]
};
// In the example below the timetag in the record is set
// and the option MXF_TXAPERIODIC_FLAG_USE_RECORD_RELATIVE_TIME.
// The relative time between each records is set in the
// timetag field of the record array.
Console.WriteLine();
Console.WriteLine("Aperiodic transmission (Record Relative)");
// Set the record
for (record = 0, currentTime = 100000; (record < MAX_TX_RECORDS_TO_TRANSMIT) && (rc == MAXT_SUCCESS); record++)
{
// The scheduling timetag is as follow:
// clock current time + 1 ms
// Each record are sent with a timetag a 10000 uS (10 ms) interval.
if (record == 0)
{
// Set sync block
rec.timeTag = currentTime + ((record + 1) * 10000);
rec.control = 0;
rec.repeatCount = 1;
rec.reserved = 0;
for (dataIdx = 0; dataIdx < BLOCKCOUNT; dataIdx++)
rec.data[dataIdx] = 0xA5;
}
else
{
// Set each records: LABEL=8, SI=1
rec.timeTag = currentTime + ((record + 1) * 10000);
rec.control = 0;
rec.repeatCount = 1;
rec.reserved = 0;
rec.data[0] = 8;
rec.data[1] = 1;
for (dataIdx = 2; dataIdx < BLOCKCOUNT; dataIdx++)
rec.data[dataIdx] = (byte)(3 * record);
}
Marshal.StructureToPtr(rec, recPtr, false);
rc = mxfCSDBNextDataRecordPtrGet(recPtr, out recPtr);
}
// Transmit the array of records
rc = mxfCSDBTxAperiodicWrite(txBuffer, MXF_TXAPERIODIC_FLAG_USE_RECORD_RELATIVE_TIME, 0, MAX_TX_RECORDS_TO_TRANSMIT, txHostBuffer);
// Wait TX completion
if (rc == MAXT_SUCCESS)
rc = WaitQueueEmpty(txBuffer);
return rc;
}
private static UInt32 initHandler(UInt64 server, UInt64 deviceIndex, UInt64 moduleIndex, UInt64 channelIndex, UInt64 attrib, ref UInt64 value)
{
UInt64 device;
MXF_DEVICE_INFO deviceInfo = new MXF_DEVICE_INFO();
UInt32 rc;
if (attrib == KMXF_CHANNEL_CLASS)
{
rc = mxfSystemDeviceGet(server, deviceIndex, out device);
if (rc == MAXT_SUCCESS)
rc = mxfDeviceInfoGet(device, out deviceInfo);
if ((rc == MAXT_SUCCESS) && (deviceInfo.modules[moduleIndex].type == MXF_MODULE_ASYNC_EH))
{
// Sets IPM-ASYNC-EH first TX and RX channel to CSDB
if ((channelIndex == 0) || (channelIndex == deviceInfo.modules[moduleIndex].txCount))
{
value = MXF_CLASS_CSDB;
return Convert.ToUInt32(true);
}
}
}
return Convert.ToUInt32(false);
}
}
}
Updated 10/23/2023