4.1.4 Card Configuration Tutorial |
The Card Configuration tutorial shows a sample use case in which the card configuration is changed solely using the PDA API.
Three configuration cases are implemented and their pitfalls are described in comments:
Adding a built-in signal to the card's configuration
Adding a virtual signal calculated from built-in signals to the PDA's configuration
Adding a virtual signal calculated from an existing virtual signal and a built-in signal to the PDA's configuration
//----------------------------------------------------------------------------- // This example demonstrates how to configure a card using the PDA API. //----------------------------------------------------------------------------- using RAYLASE.PDA.API; using System.Diagnostics; using System.Net; public class Program { public static async Task<int> Main( string[] args ) { Console.WriteLine( "\n\nCard Configuration sample\n------------------\n\n" ); if ( args.Length < 1 ) { Console.WriteLine( "This tutorial requires a valid card IP address as argument" ); return -1; } var cardIP = args[0]; if ( !IPAddress.TryParse( cardIP, out _ ) ) { Console.WriteLine( "The supplied IP address argument is not a valid IP address" ); return -1; } // Default installation path of the ProcessDataAnalyzer.Service.exe var defaultServiceExePath = @$"{Environment.ExpandEnvironmentVariables( "%ProgramFiles%" )}\RAYLASE\ProcessDataAnalyzer\bin\ProcessDataAnalyzer.Service.exe"; // Check if a different path was specified in the arguments. var serviceExePath = args.Length <= 1 ? defaultServiceExePath : args[1]; // Get the API which auto-starts the PDA Server and is valid for this (Main()) scope. using var api = new RAYLASE.PDA.ClientLib.Api( serviceExePath ); // Get the current service configurations var config = await api.Configuration.GetPdaConfigAsync(); var cardConfigs = config.CardConfigurations; // The config can be saved to a service relative path as well and be // inspected/adapted manually and re-uploaded to the service // await api.Configuration.SavePdaConfigAsync( ".\servicePdaConfig.json" ); // Console.WriteLine( "Edit the servicePdaConfig.json, save and press any button to continue." ); // Console.Read(); // await api.Configuration.SetPdaConfig( ".\servicePdaConfig.json" ); // Alternatively, the configuration can be done in the PDA GUI and is reloaded on a service restart. // This example will focus on changing the configuration programmatically. //----------------------------------------------------------------------------- // Create a built-in and a virtual signal. //----------------------------------------------------------------------------- // First, let's create a physical and a virtual signal we want to record. // BuiltInSignals are physical signals on the card, VirtualSignals // represent a new signal created by the PDA and are defined through a mathematical // equation. var fpsSignal = new SignalConfig { Config = new TraceDataSignalConfig { // The new signal type. Signal = new Signal { BuiltInSignal = BuiltInSignal.BisFps }, // Enable the signal to be recorded and not only configured IsEnabled = true, // Scale offset and timing could be set as well. //ScaleOffset = new ScaleOffset { Scale = 2, Offset = 3 }, //Timing = new SignalTiming //{ // MeasurementDelay = 9, // Offset = 8, // PropagationDelay = 7, // TrackingError = 6, // TransferDelay = 5 //} } }; var velocity2D = new SignalConfig { Config = new TraceDataSignalConfig { // It is also possible to set the name directly to the signal key in which case // the key is copied to the CustomName property and a valid key is auto assigned: // Signal = new Signal { VirtualSignal = new VirtualSignal { Key = "XY Velocity" } }, // This has the drawback that the order of virtual signals is determined by the order // in which the signals are added to the configuration below. We manually assign // the ID Virtual0 to this signal. Signal = new Signal { VirtualSignal = new VirtualSignal { Key = "Virtual0" } }, // Add a name to the new virtual signal. Multiple virtual signals can be // created using the same calculation type and thus only differ in their name. CustomName = "XY Velocity", // Display and export unit name Unit = "m/s", // Interpolate values //IsUserInterpolating = true, // Configure the virtual signal to take two input signals and compute the velocity. // We use commanded X and Y here since they are also available if no scanhead // is connected to the card. VirtualSignalConfig = new VirtualSignalConfig { Calculation = CalculationType.CtVelocity2D, // For calculations, the order of input signals is from left/first to right/last. // Dividing x by y would need x as first input signal and y as second. // If this calculation would be a division rather than velocity, we would be // dividing Field0TxY by Field0TxX. InputSignals = { // Filed 0: First field // Tx: Commanded signal (Rx would be received, measured) // X: Signal in X direction new Signal { BuiltInSignal = BuiltInSignal.BisField0TxX }, new Signal { BuiltInSignal = BuiltInSignal.BisField0TxY } } }, IsEnabled = true } }; // It is possible to combine built-in and/or virtual signals in new virtual signals. // For demonstration purpose, let's multiply above velocity by the gate signal, which // will result in a velocity which will only be "active" during gate ON (otherwise the // velocity is multiplied by 0). var gateEnabledVelocity = new SignalConfig { Config = new TraceDataSignalConfig { // Make sure that this signal's ID is higher than the virtual input signal's ID. Signal = new Signal { VirtualSignal = new VirtualSignal { Key = "Virtual1" } }, CustomName = "Gate ON Velocity", Unit = "m/s", // Configure the new virtual signal. VirtualSignalConfig = new VirtualSignalConfig { Calculation = CalculationType.CtMultiplication, InputSignals = { new Signal {VirtualSignal = velocity2D.Config.Signal.VirtualSignal }, new Signal { BuiltInSignal = BuiltInSignal.BisGate } } }, IsEnabled = true } }; //----------------------------------------------------------------------------- // Add the new signals to the configuration //----------------------------------------------------------------------------- // The PDA configuration can be done for multiple devices at the same time so // a device ID must be specified to access the device specific configurations. // The device ID corresponds to the order in which the devices are connected to // the service. We have no device connected yet, but want to edit the first device // configuration since it will be created by default. var firstConnectedCard = cardConfigs[0]; firstConnectedCard.Signals.Add( fpsSignal ); // Make sure that the virtual signal key is not used already. If you want to change the signal, adopt the existing entry from the config. if ( !firstConnectedCard.Signals.Any( x => x.Config.Signal.VirtualSignal is not null && x.Config.Signal.VirtualSignal.Key == gateEnabledVelocity.Config.Signal.VirtualSignal.Key ) ) { // Since we set the signal keys to Virtual1 for gateEnabledVelocity and to Virtual0 for velocity2D, // it is ok now to switch the order in which the signals are added to the card configuration. // If the signal key would be a name, make sure that virtual signal dependencies are added to the // config first. firstConnectedCard.Signals.Add( gateEnabledVelocity ); } if ( !firstConnectedCard.Signals.Any( x => x.Config.Signal.VirtualSignal is not null && x.Config.Signal.VirtualSignal.Key == velocity2D.Config.Signal.VirtualSignal.Key ) ) { firstConnectedCard.Signals.Add( velocity2D ); } // Save the new configuration to the service // The configuration is shared between the GUI and the Service/Server and saved under // %ProgramData%\RAYLASE\ProcessDataAnalyzer\Configurations.json await api.Configuration.SetPdaConfigAsync( cardConfigs ); // Now we connect a card to the service, this could be done before configuring as well. // Starting the acquisition will get the traces of all configured built-in signals // and the PDA will process all virtual signals. // See the PDA GUI or other tutorials for acquiring and exporting data. return 0; } }