#include <mil.h>
#if M_MIL_USE_WINDOWS
#include <windows.h>
#endif
#include <algorithm>
#include "ActionDevice.h"
#include "NetworkSegment.h"
#define BUFFERING_SIZE_MAX 10
void DoAction(MIL_ID MilSystem);
void DoScheduledAction(MIL_ID MilSystem);
void ProgramActionDevices(const std::vector<ActionDevice>& Devices, MIL_INT64 DeviceKey, MIL_INT64 GroupKey, MIL_INT64 GroupMask);
void ProgramMILActionContext(MIL_ID MilSystem, const std::vector<ActionDevice>& Devices, MIL_INT64 DeviceKey, MIL_INT64 GroupKey, MIL_INT64 GroupMask);
bool ControlPrecisionTimeProtocolClocks(MIL_ID MilSystem, std::vector<ActionDevice>& Devices, bool Enable);
void MFTYPE TriggerAction(TriggerFunctionArgument* Argument);
void ClearAction(MIL_ID MilSystem);
std::vector<MIL_DOUBLE> GetAcquisitionTimeMeasurments(MIL_INT BufferingSize, std::vector<ActionDevice>& Devices);
int MosMain(void)
{
MIL_ID MilApplication;
MIL_ID MilSystem ;
MIL_INT SystemType ;
MappAllocDefault(M_DEFAULT, &MilApplication, &MilSystem, M_NULL,
M_NULL, M_NULL);
MsysInquire(MilSystem, M_SYSTEM_TYPE, &SystemType);
if(SystemType != M_SYSTEM_GIGE_VISION_TYPE)
{
MosPrintf(MIL_TEXT("This example program can only be used with the Matrox Driver for ")
MIL_TEXT("GigE Vision.\n"));
MosPrintf(MIL_TEXT("Please ensure that the default system type is set accordingly in ")
MIL_TEXT("MIL Config.\n"));
MosPrintf(MIL_TEXT("-------------------------------------------------------------\n\n"));
MosPrintf(MIL_TEXT("Press <enter> to quit.\n"));
MosGetch();
MappFreeDefault(MilApplication, MilSystem, M_NULL, M_NULL, M_NULL);
return 1;
}
DoAction(MilSystem);
#if M_MIL_USE_WINDOWS
system("cls");
#endif
DoScheduledAction(MilSystem);
MappFreeDefault(MilApplication, MilSystem, M_NULL, M_NULL, M_NULL);
return 0;
}
void DoAction(MIL_ID MilSystem)
{
std::vector<NetworkSegment> NetworkSegments;
std::vector<ActionDevice> ActionDevices;
MosPrintf(MIL_TEXT("This example shows how to trigger an action signal across\n"));
MosPrintf(MIL_TEXT("multiple GigE Vision devices\n\n"));
MosPrintf(MIL_TEXT("Press <Enter> to continue.\n\n"));
MosGetch();
MosPrintf(MIL_TEXT("Detecting connected GigE Vision devices that support the Action command.\n\n"));
EnumNetworkSegments(MilSystem, M_GC_ACTION_SUPPORT, NetworkSegments);
if (NetworkSegments.size() == 0)
{
MosPrintf(MIL_TEXT("\nNo compatible devices found\n"));
MosPrintf(MIL_TEXT("Press <Enter> to continue.\n"));
MosGetch();
return;
}
MosPrintf(MIL_TEXT("\nCompatible GigE Vision devices found are:\n\n"));
for (std::vector<NetworkSegment>::size_type i = 0; i < NetworkSegments.size(); i++)
NetworkSegments[i].Print(eDevices);
if (NetworkSegments.size() > 1)
{
MosPrintf(MIL_TEXT("\nSome of the detected GigE Vision devices reside on different network\n"));
MosPrintf(MIL_TEXT("segments namely:\n\n"));
for (std::vector<NetworkSegment>::size_type i = 0; i < NetworkSegments.size(); i++)
NetworkSegments[i].Print(eAll);
MosPrintf(MIL_TEXT("\nThe action signal packet will be replicated on these segments.\n"));
MosPrintf(MIL_TEXT("Because of this the replicated action signal packet will arrive\n"));
MosPrintf(MIL_TEXT("at different times on different network segments."));
}
MosPrintf(MIL_TEXT("\nPress <Enter> to continue.\n\n"));
MosGetch();
ActionDevices = ToActionDeviceVector(NetworkSegments);
MosPrintf(MIL_TEXT("Programming action keys into compatible devices."));
MIL_INT64 DeviceKey = 0x56781234;
MIL_INT64 GroupKey = 0x24;
MIL_INT64 GroupMask = 0xFFFFFFFF;
ProgramActionDevices(ActionDevices, DeviceKey, GroupKey, GroupMask);
ProgramMILActionContext(MilSystem, ActionDevices, DeviceKey, GroupKey, 0x1);
TriggerFunctionArgument Argument;
Argument.MilSystem = MilSystem;
Argument.ActionDelay = 100;
for (std::vector<ActionDevice>::size_type i = 0; i < ActionDevices.size(); i++)
{
ActionDevices[i].Allocate(BUFFERING_SIZE_MAX);
if (i == 0)
ActionDevices[i].StartAcquisition(BUFFERING_SIZE_MAX, TriggerAction, &Argument);
else
ActionDevices[i].StartAcquisition(BUFFERING_SIZE_MAX);
}
MosPrintf(MIL_TEXT("\nAction keys programming complete.\n"));
MosPrintf(MIL_TEXT("\nPress <Enter> to trigger actions.\n\n"));
MosGetch();
TriggerAction(&Argument);
MosPrintf(MIL_TEXT("The initial action signal was sent.\n\n"));
for (std::vector<ActionDevice>::size_type i = 0; i < ActionDevices.size(); i++)
{
ActionDevices[i].StopAcquisition(M_WAIT);
MosPrintf(MIL_TEXT("%lld frames completed on %s %s (M_DEV%d)\n\n"), (long long)ActionDevices[i].ProcessedImageCount,
ActionDevices[i].Vendor.c_str(), ActionDevices[i].Model.c_str(), ActionDevices[i].DigitizerNumber);
}
MosPrintf(MIL_TEXT("Inter-device jitter measurements:\n"));
MosPrintf(MIL_TEXT("Note: the measurements can be affected by a multitude of factors such as:\n"));
MosPrintf(MIL_TEXT("1- The use of different exposure times across devices.\n"));
MosPrintf(MIL_TEXT("2- The use interrupt moderation on the host Ethernet controller(s).\n\n"));
std::vector<MIL_DOUBLE> TimeMeasurments = GetAcquisitionTimeMeasurments(BUFFERING_SIZE_MAX, ActionDevices);
for (std::vector<MIL_DOUBLE>::size_type i = 0; i < TimeMeasurments.size(); i++)
MosPrintf(MIL_TEXT("Frame: %-3d%-4.3f (usec).\n"), i + 1, TimeMeasurments[i] * 1e6);
MosPrintf(MIL_TEXT("\nPress <Enter> to continue.\n\n"));
MosGetch();
for (std::vector<ActionDevice>::size_type i = 0; i < ActionDevices.size(); i++)
ActionDevices[i].Free();
ClearAction(MilSystem);
}
void DoScheduledAction(MIL_ID MilSystem)
{
MIL_UINT32 Selection = 0;
std::vector<NetworkSegment> NetworkSegments;
std::vector<ActionDevice> ActionDevices;
MosPrintf(MIL_TEXT("This example can also show how to trigger a scheduled action signal across\n"));
MosPrintf(MIL_TEXT("multiple GigE Vision devices.\n\n"));
MosPrintf(MIL_TEXT("The scheduled action signal allows for up to sub-microsecond synchronization\n"));
MosPrintf(MIL_TEXT("between devices. It requires IEEE 1588 Precision Time Protocol (PTP) support\n"));
MosPrintf(MIL_TEXT("from your GigE Vision device.\n\n"));
MosPrintf(MIL_TEXT("Press <Enter> to continue.\n\n"));
MosGetch();
MosPrintf(MIL_TEXT("Detecting network segments with compatible GigE Vision devices.\n\n"));
EnumNetworkSegments(MilSystem, M_GC_SCHEDULED_ACTION_SUPPORT + M_GC_IEEE_1588_SUPPORT, NetworkSegments);
if (NetworkSegments.size() == 0)
{
MosPrintf(MIL_TEXT("\nNo compatible devices found\n"));
MosPrintf(MIL_TEXT("Press <Enter> to quit.\n"));
MosGetch();
return;
}
MosPrintf(MIL_TEXT("\nCompatible GigE Vision devices found are:\n\n"));
for (std::vector<NetworkSegment>::size_type i = 0; i < NetworkSegments.size(); i++)
NetworkSegments[i].Print(eDevices);
if (NetworkSegments.size() > 1)
{
MosPrintf(MIL_TEXT("\nSome of the detected GigE Vision devices reside on different network\n"));
MosPrintf(MIL_TEXT("segments namely:\n\n"));
for (std::vector<NetworkSegment>::size_type i = 0; i < NetworkSegments.size(); i++)
NetworkSegments[i].Print(eAll);
MosPrintf(MIL_TEXT("\nIEEE 1588 PTP requires that devices reside on the same PTP domain for\n"));
MosPrintf(MIL_TEXT("clock synchronization to occur. You must also ensure that the Ethernet\n"));
MosPrintf(MIL_TEXT("bandwidth of all devices on a network segment does not exceed 125 MB/s\n"));
MosPrintf(MIL_TEXT("(1 Gbps).\n\n"));
MosPrintf(MIL_TEXT("Devices residing on different PTP domains cannot be triggered using\n"));
MosPrintf(MIL_TEXT("the same action signal because they do not share a common clock.\n"));
}
MosPrintf(MIL_TEXT("\nPress <Enter> to continue.\n\n"));
MosGetch();
if (NetworkSegments.size() > 1)
{
MosPrintf(MIL_TEXT("Which network segment do you wish to use?\n\n"));
for (std::vector<NetworkSegment>::size_type i = 0; i < NetworkSegments.size(); i++)
{
MosPrintf(MIL_TEXT("%3d"), i + 1);
NetworkSegments[i].Print(eHostController);
}
MosPrintf(MIL_TEXT("\n"));
do
{
#if M_MIL_USE_WINDOWS
scanf_s("%d", &Selection);
#else
scanf("%d", (int *)&Selection);
#endif
if (Selection < 1 || Selection > NetworkSegments.size())
MosPrintf(MIL_TEXT("Invalid selection\n"));
else
break;
} while (1);
Selection--;
MosPrintf(MIL_TEXT("%s segment selected.\n\n"), NetworkSegments[Selection].HostControllerName.c_str());
}
ActionDevices = ToActionDeviceVector(NetworkSegments, Selection);
if (ControlPrecisionTimeProtocolClocks(MilSystem, ActionDevices, true))
{
MosPrintf(MIL_TEXT("\nError: Precision Time Protocol initialization failed.\n"));
MosGetch();
return;
}
MosPrintf(MIL_TEXT("Press <Enter> to continue.\n\n"));
MosGetch();
MosPrintf(MIL_TEXT("Programming Action keys into compatible devices."));
MIL_INT64 DeviceKey = 0x56781234;
MIL_INT64 GroupKey = 0x24;
MIL_INT64 GroupMask = 0xFFFFFFFF;
ProgramActionDevices(ActionDevices, DeviceKey, GroupKey, GroupMask);
ProgramMILActionContext(MilSystem, ActionDevices, DeviceKey, GroupKey, 0x1);
TriggerFunctionArgument Argument;
Argument.MilSystem = MilSystem;
for (std::vector<ActionDevice>::size_type i = 0; i < ActionDevices.size(); i++)
{
ActionDevices[i].Allocate(BUFFERING_SIZE_MAX);
if (i == 0)
ActionDevices[i].StartAcquisition(BUFFERING_SIZE_MAX, TriggerAction, &Argument);
else
ActionDevices[i].StartAcquisition(BUFFERING_SIZE_MAX);
}
MosPrintf(MIL_TEXT("\nAction keys programming complete.\n"));
MosPrintf(MIL_TEXT("\nPress <Enter> to trigger an action.\n\n"));
MosGetch();
Argument.PtpDueTime = ActionDevices[0].GetDeviceTicks() + 0.5;
TriggerAction(&Argument);
MosPrintf(MIL_TEXT("The action signal was sent.\n\n"));
for (std::vector<ActionDevice>::size_type i = 0; i < ActionDevices.size(); i++)
{
ActionDevices[i].StopAcquisition(M_WAIT);
MosPrintf(MIL_TEXT("%lld frames completed on %s %s (M_DEV%d)\n\n"), (long long)ActionDevices[i].ProcessedImageCount,
ActionDevices[i].Vendor.c_str(), ActionDevices[i].Model.c_str(), ActionDevices[i].DigitizerNumber);
}
MosPrintf(MIL_TEXT("Inter-device jitter measurements:\n\n"));
MosPrintf(MIL_TEXT("Note: the measurements can be affected by multiple factors such as:\n"));
MosPrintf(MIL_TEXT("1- The use of different exposure times across devices.\n"));
MosPrintf(MIL_TEXT("2- Network topology.\n"));
MosPrintf(MIL_TEXT("3- The use (or lack of) IEEE1588 transparent clocks and/or IEEE1588 boundary clocks.\n\n"));
std::vector<MIL_DOUBLE> TimeMeasurments = GetAcquisitionTimeMeasurments(BUFFERING_SIZE_MAX, ActionDevices);
for (std::vector<MIL_DOUBLE>::size_type i = 0; i < TimeMeasurments.size(); i++)
MosPrintf(MIL_TEXT("Frame: %-3d%-4.3f (usec).\n"), i + 1, TimeMeasurments[i] * 1e6);
ControlPrecisionTimeProtocolClocks(MilSystem, ActionDevices, false);
MosPrintf(MIL_TEXT("Press <Enter> to Quit.\n"));
MosGetch();
for (std::vector<ActionDevice>::size_type i = 0; i < ActionDevices.size(); i++)
ActionDevices[i].Free();
for (std::vector<NetworkSegment>::size_type i = 0; i < NetworkSegments.size(); i++)
{
for (std::vector<ActionDevice>::size_type j = 0; j < NetworkSegments[i].ActionDevices.size(); j++)
NetworkSegments[i].ActionDevices[j].Free();
}
ClearAction(MilSystem);
}
bool ControlPrecisionTimeProtocolClocks(MIL_ID MilSystem, std::vector<ActionDevice>& Devices, bool Enable)
{
bool Error = false;
MIL_BOOL EnablePtp = Enable ? M_TRUE : M_FALSE;
MIL_INT ReadyCount = 0, Count = 0;
std::vector<MIL_STRING> Status(Devices.size(), MIL_TEXT(""));
if (Enable)
{
MosPrintf(MIL_TEXT("Enabling IEEE 1588 Precision Time Protocol on GigE Vision devices\n\n"));
for (std::vector<ActionDevice>::size_type i = 0; i < Devices.size(); i++)
MdigControlFeature(Devices[i].MilDigitizer, M_FEATURE_VALUE, MIL_TEXT("GevIEEE1588"), M_TYPE_BOOLEAN, &EnablePtp);
MosPrintf(MIL_TEXT("Waiting for GigE Vision Precision Time Protocol enabled devices to report\n"));
MosPrintf(MIL_TEXT("readiness.\n\n"));
do
{
ReadyCount = 0;
MosSleep(50);
Count++;
MosPrintf(MIL_TEXT("."));
for (std::vector<ActionDevice>::size_type i = 0; i < Devices.size(); i++)
{
MdigInquireFeature(Devices[i].MilDigitizer, M_FEATURE_VALUE, MIL_TEXT("GevIEEE1588Status"), M_TYPE_STRING, Status[i]);
if (Status[i] == MIL_TEXT("Master") || Status[i] == MIL_TEXT("Slave"))
{
ReadyCount++;
Devices[i].IEEE1588Enabled = true;
}
}
if (Count > 500)
break;
} while (ReadyCount < (MIL_INT)Devices.size());
MIL_INT Counter = 200;
while (Counter--)
{
MosPrintf(MIL_TEXT("."));
MosSleep(50);
}
MosPrintf(MIL_TEXT("\n\nIEEE 1588 Precision Time Protocol report:\n\n"));
if (ReadyCount < (MIL_INT)Devices.size())
{
Error = true;
for (std::vector<ActionDevice>::size_type i = 0; i < Devices.size(); i++)
{
MosPrintf(MIL_TEXT("\t%s %s (M_DEV%d)\n\tStatus: %s\n\n"), Devices[i].Vendor.c_str(),
Devices[i].Model.c_str(), Devices[i].DigitizerNumber, Status[i].c_str());
}
MosPrintf(MIL_TEXT("Not all device's status report ready."));
}
else
{
MIL_STRING Accuracy = MIL_TEXT("Unknown");
for (std::vector<ActionDevice>::size_type i = 0; i < Devices.size(); i++)
{
MIL_BOOL IsPresent = M_FALSE;
MdigInquireFeature(Devices[i].MilDigitizer, M_FEATURE_PRESENT, MIL_TEXT("GevIEEE1588ClockAccuracy"), M_TYPE_BOOLEAN, &IsPresent);
if(IsPresent)
MdigInquireFeature(Devices[i].MilDigitizer, M_FEATURE_VALUE, MIL_TEXT("GevIEEE1588ClockAccuracy"), M_TYPE_STRING, Accuracy);
MosPrintf(MIL_TEXT("\t%s %s (M_DEV%d)\n\tStatus: %-12s Clock Accuracy: %-12s\n\n"), Devices[i].Vendor.c_str(),
Devices[i].Model.c_str(), Devices[i].DigitizerNumber, Status[i].c_str(), Accuracy.c_str());
}
}
}
else
{
MosPrintf(MIL_TEXT("\nDisabling IEEE 1588 Precision Time Protocol on GigE Vision devices\n\n"));
for (std::vector<ActionDevice>::size_type i = 0; i < Devices.size(); i++)
MdigControlFeature(Devices[i].MilDigitizer, M_FEATURE_VALUE, MIL_TEXT("GevIEEE1588"), M_TYPE_BOOLEAN, &EnablePtp);
}
return Error;
}
void ProgramActionDevices(const std::vector<ActionDevice>& Devices, MIL_INT64 DeviceKey, MIL_INT64 GroupKey, MIL_INT64 GroupMask)
{
for (std::vector<ActionDevice>::size_type i = 0; i < Devices.size(); i++)
{
MdigControlFeature(Devices[i].MilDigitizer, M_FEATURE_VALUE, MIL_TEXT("ActionDeviceKey"), M_TYPE_INT64, &DeviceKey);
MdigControlFeature(Devices[i].MilDigitizer, M_FEATURE_VALUE, MIL_TEXT("ActionSelector"), M_TYPE_INT64, &Devices[i].ActionNumber);
MdigControlFeature(Devices[i].MilDigitizer, M_FEATURE_VALUE, MIL_TEXT("ActionGroupKey"), M_TYPE_INT64, &GroupKey);
MdigControlFeature(Devices[i].MilDigitizer, M_FEATURE_VALUE, MIL_TEXT("ActionGroupMask"), M_TYPE_INT64, &GroupMask);
MdigControlFeature(Devices[i].MilDigitizer, M_FEATURE_VALUE, MIL_TEXT("TriggerSelector"), M_TYPE_STRING, MIL_TEXT("FrameStart"));
MdigControlFeature(Devices[i].MilDigitizer, M_FEATURE_VALUE, MIL_TEXT("TriggerMode"), M_TYPE_STRING, MIL_TEXT("Off"));
MdigControlFeature(Devices[i].MilDigitizer, M_FEATURE_VALUE, MIL_TEXT("TriggerSource"), M_TYPE_STRING, Devices[i].ActionName);
MdigControlFeature(Devices[i].MilDigitizer, M_FEATURE_VALUE, MIL_TEXT("TriggerMode"), M_TYPE_STRING, MIL_TEXT("On"));
}
}
void ProgramMILActionContext(MIL_ID MilSystem, const std::vector<ActionDevice>& Devices, MIL_INT64 DeviceKey, MIL_INT64 GroupKey, MIL_INT64 GroupMask)
{
MsysControl(MilSystem, M_GC_ACTION0 + M_GC_ACTION_DEVICE_KEY, DeviceKey);
MsysControl(MilSystem, M_GC_ACTION0 + M_GC_ACTION_GROUP_KEY, GroupKey);
MsysControl(MilSystem, M_GC_ACTION0 + M_GC_ACTION_GROUP_MASK, GroupMask);
for (std::vector<ActionDevice>::size_type i = 0; i < Devices.size(); i++)
MsysControl(MilSystem, M_GC_ACTION0 + M_GC_ACTION_ADD_DEVICE, Devices[i].MilDigitizer);
}
void MFTYPE TriggerAction(TriggerFunctionArgument* Argument)
{
if (Argument->PtpDueTime != 0.0)
MsysControl(Argument->MilSystem, M_GC_ACTION0 + M_GC_ACTION_TIME, Argument->PtpDueTime);
MsysControl(Argument->MilSystem, M_GC_ACTION0 + M_GC_ACTION_EXECUTE, M_DEFAULT);
}
void ClearAction(MIL_ID MilSystem)
{
MsysControl(MilSystem, M_GC_ACTION0 + M_GC_ACTION_CLEAR_DEVICES, M_DEFAULT);
}
std::vector<MIL_DOUBLE> GetAcquisitionTimeMeasurments(MIL_INT BufferingSize, std::vector<ActionDevice>& Devices)
{
std::vector<MIL_DOUBLE> Jitter(BufferingSize, 0.0);
MIL_DOUBLE Diff = 0.0;
for (std::vector<ActionDevice>::size_type i = 0; i < Devices.size(); i++)
{
for (std::vector<ActionDevice>::size_type j = i; j < Devices.size(); j++)
{
if (i == j)
continue;
for (MIL_INT k = 0; k < BufferingSize; k++)
{
Diff = fabs(Devices[i].DeviceTimeStamps[k] - Devices[j].DeviceTimeStamps[k]);
if (Jitter[k] < Diff)
Jitter[k] = Diff;
}
}
}
return Jitter;
}