MX Foundation 4
ar429_embedded_recorder.cs
/*******************************************************************************
//
// File:
// ar429_embedded_recorder.cs
//
// Copyright (c) MAX Technologies Inc. 1988-2019, All Rights Reserved.
// CONFIDENTIAL AND PROPRIETARY INFORMATION WHICH IS THE
// PROPERTY OF MAX TECHNOLOGIES INC.
//
// This example shows how to use the flash storage onboard the FlexMulti.
//
// - Format the onboard flash file(s) if necessary.
// - Automatic recording of ARINC 429 incoming RX data and write it on the flash.
// - Transmit ARINC 429 records.
// - Read the data stored on the flash.
//
// Hardware Requirements:
// - MAXT FlexMulti with loopback between first TX and RX ARINC 429 Enhanced channel.
//
*******************************************************************************/
//#define LOOPBACK
//#define LOCAL
using System;
using static MAXT.MXFoundation.mxf;
using System.Runtime.InteropServices;
using System.Text;
namespace ar429_example
{
class ar429_embedded_recorder
{
const int BUFFER_SIZE = 4096; // 4KB
const int FILE_SIZE = 256 * 1024 * 1024; // 256 MB
static void Main(string[] args)
{
UInt32 rc;
UInt64 server;
var device = new UInt64[1];
var module = new UInt64[1];
UInt64 count = 0;
var tx429 = new UInt64[1];
var rx429 = new UInt64[1];
UInt64 flash = 0;
UInt64 fileIdx = 0, fileSize;
UInt32 needUnmount = Convert.ToUInt32(false);
// Connect to services and initialize environment
#if LOCAL
rc = mxfServerConnect("0.0.0.0", "", "", Convert.ToUInt32(false), out server);
#else
rc = mxfServerConnect("192.168.0.1", "admin", "admin", Convert.ToUInt32(false), out server);
#endif
if (rc != MAXT_SUCCESS)
{
Console.Write("Failed to connect; rc=0x{0:x8}", rc);
Console.Write("\nPress a key to terminate\n");
Console.ReadKey();
return;
}
nvInfo = new MXF_NVSTORAGE_INFO();
Console.Write("\nStarting\n");
// Initialize the server
rc = mxfSystemInit(server);
// Get the device handle
if (rc == MAXT_SUCCESS)
rc = mxfSystemDeviceAllGet(server, MXF_DEVICE_ALL, 1, out count, device);
// Get first module A429 Enhanced
if (rc == MAXT_SUCCESS && count > 0)
rc = mxfDeviceModuleAllGet(device[0], MXF_MODULE_MULTI_EH, 1, out count, module);
// Get the first ARINC 429 TX channel
if (rc == MAXT_SUCCESS && count > 0)
rc = mxfModuleChannelAllGet(module[0], MXF_CLASS_A429, MXF_SCLASS_TX_CHANNEL, 1, out count, tx429);
// Get the first ARINC 429 RX channel
if (rc == MAXT_SUCCESS && count > 0)
rc = mxfModuleChannelAllGet(module[0], MXF_CLASS_A429, MXF_SCLASS_RX_CHANNEL, 1, out count, rx429);
// If A429 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);
//Activate loopback before transmission and reception
#if LOOPBACK
if (rc == MAXT_SUCCESS)
rc = mxfAttributeUint64Set(rx429[0], KMXF_A429_TX_RX_TEST_LB, VMXF_ENABLE);
#endif
// Set channel speed
if (rc == MAXT_SUCCESS)
{
rc = mxfAttributeUint64Set(rx429[0], KMXF_A429_SPEED, 100000);
if (rc == MAXT_SUCCESS)
rc = mxfAttributeUint64Set(tx429[0], KMXF_A429_SPEED, 100000);
}
/******************************************************************************/
// Initialize the flash
/******************************************************************************/
// Get flash handle
if (rc == MAXT_SUCCESS)
rc = mxfEmbeddedNVStorageHandleGet(device[0], out flash);
if (rc == MAXT_SUCCESS)
rc = mxfEmbeddedNVStorageInfoGet(flash, out nvInfo);
if (rc == MAXT_SUCCESS && nvInfo.type == MXF_EMBEDDED_NVSTORAGE_TYPE_EMMC)
{
needUnmount = Convert.ToUInt32(true);
}
// Check if a file is already created in flash
if (rc == MAXT_SUCCESS)
{
rc = mxfEmbeddedNVStorageFileCountGet(flash, out count);
if (rc == MAXT_ERROR_NVSTORAGE_NOT_FORMATTED)
{
Console.Write("Formatting...\n");
Console.Write("Done\n");
count = 0;
}
}
if (rc == MAXT_SUCCESS)
{
if (count > 0)
{
// Verify if first file is right size
rc = mxfEmbeddedNVStorageFileSizeGet(flash, 0, out fileSize);
if (rc == MAXT_SUCCESS)
{
// This file is ok, reset it
if (fileSize == FILE_SIZE / nvInfo.sectorSize)
{
fileIdx = 0;
rc = mxfEmbeddedNVStorageFileReset(flash, fileIdx);
if (rc == MAXT_SUCCESS)
Console.Write("File #{0} reset\n", fileIdx);
}
// Format and create a new file
else
{
Console.Write("Formatting...\n");
Console.Write("Done\n");
if (rc == MAXT_SUCCESS)
rc = mxfEmbeddedNVStorageFileCreate(flash, FILE_SIZE / nvInfo.sectorSize, out fileIdx);
if (rc == MAXT_SUCCESS)
Console.Write("File #{0} allocated\n", fileIdx);
}
}
}
else
{
// Allocate file
rc = mxfEmbeddedNVStorageInfoGet(flash, out nvInfo);
if (rc == MAXT_SUCCESS)
rc = mxfEmbeddedNVStorageFileCreate(flash, FILE_SIZE / nvInfo.sectorSize, out fileIdx);
if (rc == MAXT_SUCCESS)
Console.Write("File #{0} allocated\n", fileIdx);
}
}
// Enable the recording of all messages
if (rc == MAXT_SUCCESS)
{
rc = mxfA429EmbeddedNVStorageFileMsgSelectSet(rx429[0], MXF_MSG_SELECT_ONLY, 0, IntPtr.Zero);
if (rc == MAXT_SUCCESS)
rc = mxfEmbeddedNVStorageFileMsgSelectEnableSet(rx429[0], fileIdx, 0, VMXF_ENABLE);
}
if (rc == MAXT_SUCCESS)
rc = TX429PeriodicScheduling(tx429[0]);
if (rc == MAXT_SUCCESS)
rc = read429RecordsFromEmbeddedFlash(flash, fileIdx);
if (rc == MAXT_SUCCESS)
rc = mxfEmbeddedNVStorageFileMsgSelectEnableSet(rx429[0], fileIdx, 0, VMXF_DISABLE);
if (needUnmount > 0)
// Catch any previous failing function
if (rc != MAXT_SUCCESS)
{
StringBuilder buffer = new StringBuilder(256);
if (mxfSystemErrorStringGet(server, rc, (UInt32)buffer.Capacity, buffer) != MAXT_SUCCESS)
{
buffer.Clear();
buffer.Append(string.Format("ERROR # 0x{0:x8}", rc));
}
Console.Write(buffer + "\n\r");
}
// Terminate
Console.Write("\nPress enter to terminate\n");
Console.ReadKey();
return;
}
private static UInt32 TX429PeriodicScheduling(UInt64 txChannel)
{
UInt64 schedule;
var msg = new UInt64[3] { 0, 0, 0 };
UInt32 data, ssm = 0;
UInt32 rc;
var labelMsg0 = new UInt64[6] { 004, 077, 0103, 0104, 0251, 0252 };
var labelMsg1 = new UInt64[2] { 0100, 0110 };
var labelMsg2 = new UInt64[3] { 0140, 0141, 0143 };
var bufferMsg0 = new UInt64[6];
var bufferMsg1 = new UInt64[2];
var bufferMsg2 = new UInt64[3];
UInt32 rec, indexBuffer;
double bnr;
IntPtr recPtr = IntPtr.Zero;
//allocate mem for recPtr
try
{
recPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(MXF_A429_DATAREC)));
}
catch (OutOfMemoryException)
{
rc = MAXT_ERROR_MEM;
}
// Set the A429 records and create the schedule
// Create the periodic scheduler
rc = mxfTxPeriodicScheduleNew(txChannel, out schedule);
// Set scheduling values for msg0: Rate=150 ms, Phase=0 us
if (rc == MAXT_SUCCESS)
rc = mxfTxPeriodicScheduleMsgAdd(schedule, 150000, 0, out msg[0]);
// Set record for the buffers of Msg0
for (rec = 0; rec < 6 && rc == MAXT_SUCCESS; rec++)
{
rc = mxfTxPeriodicUpdateMsgBufferAlloc(txChannel, labelMsg0[rec], BUFFER_SIZE, out bufferMsg0[rec], IntPtr.Zero);
if (rc == MAXT_SUCCESS)
{
rec429.timeTag = 0;
rec429.control = 0;
rec429.repeatCount = 1;
rec429.reserved = 0;
data = 0;
switch (rec)
{
case 0:
data = 0x40000;
ssm = 0;
break;
case 1:
bnr = 100.123;
mxfA429ArwRealToBnr(128, 18, ref bnr, out data);
data >>= 10;
ssm = 3;
break;
case 2:
bnr = 400.126;
mxfA429ArwRealToBnr(512, 12, ref bnr, out data);
data >>= 10;
ssm = 3;
break;
case 3:
bnr = -25;
mxfA429ArwRealToBnr(16384, 11, ref bnr, out data);
data >>= 10;
ssm = 3;
break;
case 4:
bnr = 2222.2;
mxfA429ArwRealToBnr(4096, 16, ref bnr, out data);
data >>= 10;
ssm = 3;
break;
case 5:
bnr = 333.3;
mxfA429ArwRealToBnr(512, 10, ref bnr, out data);
data >>= 10;
ssm = 3;
break;
}
rc = mxfA429ArwCompose(labelMsg0[rec], 0, data, ssm, VMXF_A429_PARITY_ODD, out rec429.data);
if (rc == MAXT_SUCCESS)
{
Marshal.StructureToPtr(rec429, recPtr, true);
rc = mxfA429TxPeriodicUpdateMsgWrite(bufferMsg0[rec], 1, recPtr);
}
}
}
// Define the number of buffer for the list and link to it
if (rc == MAXT_SUCCESS)
rc = mxfTxPeriodicScheduleBufferListAdd(msg[0], rec, 0, bufferMsg0);
// Set scheduling values for msg1: Rate=200 ms, Phase=2000 us
if (rc == MAXT_SUCCESS)
rc = mxfTxPeriodicScheduleMsgAdd(schedule, 200000, 2000, out msg[1]);
// Set record for the buffers of Msg1
for (rec = 0; rec < 2 && rc == MAXT_SUCCESS; rec++)
{
rc = mxfTxPeriodicUpdateMsgBufferAlloc(txChannel, labelMsg1[rec], BUFFER_SIZE, out bufferMsg1[rec], IntPtr.Zero);
if (rc == MAXT_SUCCESS)
{
rec429.timeTag = 0;
rec429.control = 0;
rec429.repeatCount = 1;
rec429.reserved = 0;
data = 0;
switch (rec)
{
case 0:
bnr = 45.08;
mxfA429ArwRealToBnr(180, 13, ref bnr, out data);
data >>= 10;
ssm = 3;
break;
case 1:
bnr = 44;
mxfA429ArwRealToBnr(180, 13, ref bnr, out data);
data >>= 10;
ssm = 3;
break;
}
rc = mxfA429ArwCompose(labelMsg1[rec], 0, data, ssm, VMXF_A429_PARITY_ODD, out rec429.data);
if (rc == MAXT_SUCCESS)
{
Marshal.StructureToPtr(rec429, recPtr, true);
rc = mxfA429TxPeriodicUpdateMsgWrite(bufferMsg1[rec], 1, recPtr);
}
}
}
// Define the number of buffer for the list and link to it
if (rc == MAXT_SUCCESS)
rc = mxfTxPeriodicScheduleBufferListAdd(msg[1], rec, 0, bufferMsg1);
// Set scheduling values for msg2: Rate=75 ms, Phase=3000 us
if (rc == MAXT_SUCCESS)
rc = mxfTxPeriodicScheduleMsgAdd(schedule, 75000, 3000, out msg[2]);
// Set record for the buffers of Msg2
for (rec = 0; rec < 3 && rc == MAXT_SUCCESS; rec++)
{
rc = mxfTxPeriodicUpdateMsgBufferAlloc(txChannel, labelMsg2[rec], BUFFER_SIZE, out bufferMsg2[rec], IntPtr.Zero);
if (rc == MAXT_SUCCESS)
{
rec429.timeTag = 0;
rec429.control = 0;
rec429.repeatCount = 1;
rec429.reserved = 0;
data = 0;
switch (rec)
{
case 0:
bnr = 5;
mxfA429ArwRealToBnr(180, 13, ref bnr, out data);
data >>= 10;
ssm = 3;
break;
case 1:
bnr = -2;
mxfA429ArwRealToBnr(180, 13, ref bnr, out data);
data >>= 10;
ssm = 3;
break;
case 2:
bnr = 0;
mxfA429ArwRealToBnr(180, 13, ref bnr, out data);
data >>= 10;
ssm = 3;
break;
}
rc = mxfA429ArwCompose(labelMsg2[rec], 0, data, ssm, VMXF_A429_PARITY_ODD, out rec429.data);
if (rc == MAXT_SUCCESS)
{
Marshal.StructureToPtr(rec429, recPtr, true);
rc = mxfA429TxPeriodicUpdateMsgWrite(bufferMsg2[rec], 1, recPtr);
}
}
}
// Define the number of buffer for the list and link to it
if (rc == MAXT_SUCCESS)
rc = mxfTxPeriodicScheduleBufferListAdd(msg[2], rec, 0, bufferMsg2);
// Run the scheduler, update records
if (rc == MAXT_SUCCESS)
{
Console.Write("Running periodic transmission, please wait...\n\r");
// Run the schedule now
rc = mxfTxPeriodicScheduleRun(schedule);
}
// Wait 10 seconds
if (rc == MAXT_SUCCESS)
{
mxfSleep(10000);
rc = mxfTxPeriodicScheduleFree(schedule);
}
if (rc == MAXT_SUCCESS)
Console.Write("\n\rTransmission stopped\n\r");
for (indexBuffer = 0; indexBuffer < 6; indexBuffer++)
{
if (bufferMsg0[indexBuffer] > 0)
mxfTxPeriodicUpdateMsgBufferFree(bufferMsg0[indexBuffer]);
}
for (indexBuffer = 0; indexBuffer < 2; indexBuffer++)
{
if (bufferMsg1[indexBuffer] > 0)
mxfTxPeriodicUpdateMsgBufferFree(bufferMsg1[indexBuffer]);
}
for (indexBuffer = 0; indexBuffer < 3; indexBuffer++)
{
if (bufferMsg2[indexBuffer] > 0)
mxfTxPeriodicUpdateMsgBufferFree(bufferMsg2[indexBuffer]);
}
Marshal.FreeHGlobal(recPtr);
return rc;
}
private static UInt32 read429RecordsFromEmbeddedFlash(UInt64 flash, UInt64 fileIdx)
{
IntPtr recPtr = IntPtr.Zero, p = IntPtr.Zero;
UInt64 msgReadCnt, byteReadCnt;
UInt64 iRec, iMsg = 0, label, sdi, ssm, parity, data;
UInt32 rc = 0;
UInt64 result;
double bnr;
// Allocate host buffer
try
{
recPtr = Marshal.AllocHGlobal(BUFFER_SIZE);
}
catch (OutOfMemoryException)
{
rc = MAXT_ERROR_MEM;
}
// Read all the messages on flash in the buffer allocated previously
Console.Write("Reading from flash\n\n");
if (rc == MAXT_SUCCESS)
{
do
{
rc = mxfA429EmbeddedNVStorageFileMsgRead(flash, fileIdx, 0, BUFFER_SIZE, out msgReadCnt, out byteReadCnt, recPtr);
// Display all records written by the embedded application
for (iRec = 0, p = recPtr; iRec < msgReadCnt; iRec++, iMsg++)
{
rec429 = (MXF_A429_DATAREC)Marshal.PtrToStructure(p, typeof(MXF_A429_DATAREC));
// Extract fields from Arinc Word
mxfA429ArwDecompose(rec429.data, out label, out sdi, out data, out ssm, out parity);
switch (label)
{
case 004:
data >>= 8;
result = (data & 0xf) + (((data >> 4) & 0xf) * 10) + (((data >> 8) & 0xf) * 100);
result *= 100;
Console.Write("{0:00} - time tag={1} (arw=0x{2:x8}): label={3:000}, sdi={4}, data={5} Feet\n\r", iMsg, rec429.timeTag, rec429.data, label, sdi, result);
break;
case 077:
mxfA429ArwBnrToReal(rec429.data, 128, 18, out bnr);
Console.Write("{0:00} - time tag={1} (arw=0x{2:x8}): label={3:000}, sdi={4}, data={5:.000} MLb-in\n\r", iMsg, rec429.timeTag, rec429.data, label, sdi, bnr);
break;
case 0100:
case 0110:
case 0140:
case 0141:
case 0143:
mxfA429ArwBnrToReal(rec429.data, 180, 13, out bnr);
Console.Write("{0:00} - time tag={1} (arw=0x{2:x8}): label={3:000}, sdi={4}, data={5:.00}Deg/180\n\r", iMsg, rec429.timeTag, rec429.data, label, sdi, bnr);
break;
case 0103:
mxfA429ArwBnrToReal(rec429.data, 512, 12, out bnr);
Console.Write("{0:00} - time tag={1} (arw=0x{2:x8}): label={3:000}, sdi={4}, data={5:.00} Knots\n\r", iMsg, rec429.timeTag, rec429.data, label, sdi, bnr);
break;
case 0104:
mxfA429ArwBnrToReal(rec429.data, 16384, 11, out bnr);
Console.Write("{0:00} - time tag={1} (arw=0x{2:x8}): label={3:000}, sdi={4}, data={5:.00} Ft/Min\n\r", iMsg, rec429.timeTag, rec429.data, label, sdi, bnr);
break;
case 0251:
mxfA429ArwBnrToReal(rec429.data, 4096, 16, out bnr);
Console.Write("{0:00} - time tag={1} (arw=0x{2:x8}): label={3:000}, sdi={4}, data={5:.000}N.M.\n\r", iMsg, rec429.timeTag, rec429.data, label, sdi, bnr);
break;
case 0252:
mxfA429ArwBnrToReal(rec429.data, 512, 10, out bnr);
Console.Write("{0:00} - time tag={1} (arw=0x{2:x8}): label={3:000}, sdi={4}, data={5:.0} Min.\n\r", iMsg, rec429.timeTag, rec429.data, label, sdi, bnr);
break;
default:
Console.Write("{0:00} - time tag={1} (arw=0x{2:x8}): label={3:000}, sdi={4}, data={5:X5}\n\r", iMsg, rec429.timeTag, rec429.data, label, sdi, data);
break;
}
// Get next message pointer in array of records
}
} while (rc == MAXT_SUCCESS && msgReadCnt > 0);
}
if (recPtr != IntPtr.Zero)
Marshal.FreeHGlobal(recPtr);
return rc;
}
}
}
Updated 10/23/2023