Files
GenericTeProgramLibrary/Source/TSRealLib/MAL/Managers/BitGenSoftMeasurementManager/BitGenSoftMeasurementManager.cs
2025-03-13 12:04:22 -07:00

937 lines
30 KiB
C#

// **********************************************************************************************************
// BitGenSoftMeasurementManager.cs
// 7/28/2022
// NGI - Next Generation Interceptor
//
// Contract No. HQ0856-21-C-0003/1022000209
//
// THIS DOCUMENT DOES NOT CONTAIN TECHNOLOGY OR TECHNICAL DATA CONTROLLED UNDER EITHER THE U.S.
// INTERNATIONAL TRAFFIC IN ARMS REGULATIONS OR THE U.S. EXPORT ADMINISTRATION REGULATIONS.
//
// RAYTHEON PROPRIETARY: THIS DOCUMENT CONTAINS DATA OR INFORMATION PROPRIETARY TO RAYTHEON
// COMPANY AND IS RESTRICTED TO USE ONLY BY PERSONS AUTHORIZED BY RAYTHEON COMPANY IN WRITING TO USE IT.
// DISCLOSURE TO UNAUTHORIZED PERSONS WOULD LIKELY CAUSE SUBSTANTIAL COMPETITIVE HARM TO RAYTHEON
// COMPANY'S BUSINESS POSITION. NEITHER SAID DOCUMENT NOR ITS CONTENTS SHALL BE FURNISHED OR DISCLOSED
// TO OR COPIED OR USED BY PERSONS OUTSIDE RAYTHEON COMPANY WITHOUT THE EXPRESS WRITTEN APPROVAL OF
// RAYTHEON COMPANY.
//
// UNPUBLISHED WORK - COPYRIGHT RAYTHEON COMPANY.
//
// DESTRUCTION NOTICE: FOR CLASSIFIED DOCUMENTS FOLLOW THE PROCEDURES IN DOD 5220.22-M,
// NATIONAL INDUSTRIAL SECURITY PROGRAM OPERATING MANUAL, FEBRUARY 2006,
// INCORPORATING CHANGE 1, MARCH 28, 2013, CHAPTER 5, SECTION 7, OR DODM 5200.01-VOLUME 3,
// DOD INFORMATION SECURITY PROGRAM: PROTECTION OF CLASSIFIED INFORMATION, ENCLOSURE 3,
// SECTION 17. FOR CONTROLLED UNCLASSIFIED INFORMATION FOLLOW THE PROCEDURES IN DODM 5200.01-VOLUME 4,
// INFORMATION SECURITY PROGRAM: CONTROLLED UNCLASSIFIED INFORMATION.
//
// CONTROLLED BY: MISSILE DEFENSE AGENCY
// CONTROLLED BY: GROUND-BASED MIDCOURSE DEFENSE PROGRAM OFFICE
// CUI CATEGORY: CTI
// DISTRIBUTION/DISSEMINATION CONTROL: F
// POC: Alex Kravchenko (1118268)
// **********************************************************************************************************
using NLog;
using Raytheon.Common;
using Raytheon.Instruments;
using Raytheon.Instruments.Exceptions;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using BitGenSoftMeasurementManagerLib;
namespace MeasurementManagerLib
{
/// <summary>
/// Bit Gen Soft Measurement Manager class
/// </summary>
public class BitGenSoftMeasurementManager : IDisposable
{
private readonly Logger _logger = LogManager.GetCurrentClassLogger();
private readonly Dictionary<string, IBit> _bitNodes = new Dictionary<string, IBit>();
private readonly IInstrumentManager _instrumentManager;
private IConfigurationManager _configurationManager;
private int _checkForMessageIntervalMs = 10;
/// <summary>
/// constructor that will create a list of BIT instruments
/// </summary>
/// <param name="instrumentManager"></param>
/// <param name="instrumentNames"></param>
public BitGenSoftMeasurementManager(IInstrumentManager instrumentManager, List<string> instrumentNames)
: this(instrumentManager)
{
try
{
foreach (string instrumentName in instrumentNames)
{
var bitNode = (IBit)_instrumentManager.GetGenericInstrument(instrumentName);
if (bitNode == null)
{
_logger.Error("Error creating TCP BIT device, check your settings");
}
else
{
_bitNodes.Add(instrumentName, bitNode);
}
}
}
catch (Exception ex)
{
_logger.Error($"Unable to create BIT instrument\n{ex.Message}");
}
}
/// <summary>
/// in case instrument manager was passed from upper level
/// </summary>
/// <param name="instrumentManager"></param>
private BitGenSoftMeasurementManager(IInstrumentManager instrumentManager)
{
_instrumentManager = instrumentManager;
InitializeConfigurationManager();
}
/// <summary>
/// initializes configuration manager,
/// will use configuration path from General Instrument Manager if available
/// otherwise will try to use the instruments path
/// </summary>
private void InitializeConfigurationManager()
{
string defaultPath;
if (_instrumentManager is GeneralInstrumentManager generalinstrumentManager)
{
defaultPath = generalinstrumentManager._configLocation;
}
else
{
defaultPath = _instrumentManager._partsLocation;
}
_configurationManager = new RaytheonConfigurationManager(defaultPath);
var config = _configurationManager.GetConfiguration("BitGenSoftMeasurementManager");
_checkForMessageIntervalMs = config.GetConfigurationValue("Settings", "CheckForMessageIntervalMs", 10);
}
/// <summary>
/// initialize COE nodes based on test type
/// </summary>
/// <param name="instrumentName"></param>
/// <param name="testType"></param>
public void InitNodes()
{
foreach (var node in _bitNodes)
{
try
{
IBit bitNode = node.Value;
bitNode.Initialize();
bitNode.Open();
}
catch (Exception ex)
{
_logger.Error(ex, ex.Message);
}
}
}
/// <summary>
/// close all connections
/// </summary>
/// <exception cref="NotImplementedException"></exception>
public void Dispose()
{
foreach (var bitNode in _bitNodes)
{
bitNode.Value?.Shutdown();
}
}
/// <summary>
/// straight pass-through the message and parameters
/// </summary>
/// <param name="messageId"></param>
/// <param name="timeoutInMs"></param>
/// <param name="messageParams"></param>
/// <returns></returns>
public bool RunBIT(string messageId, uint timeoutInMs, IEnumerable<KeyValuePair<string, string>> messageParams = null)
{
if (_bitNodes.Any())
{
IBit bitNode = _bitNodes.First().Value;
return bitNode.RunBIT(messageId, timeoutInMs, messageParams);
}
else
{
_logger.Error($"Unable to locate BIT node. No nodes defined");
return false;
}
}
/// <summary>
/// straight pass-through the message and parameters
/// </summary>
/// <param name="instrumentName"></param>
/// <param name="messageId"></param>
/// <param name="timeoutInMs"></param>
/// <param name="messageParams"></param>
/// <returns></returns>
public bool RunBIT(string instrumentName, string messageId, uint timeoutInMs, IEnumerable<KeyValuePair<string, string>> messageParams = null)
{
if (_bitNodes.ContainsKey(instrumentName))
{
return _bitNodes[instrumentName].RunBIT(messageId, timeoutInMs, messageParams);
}
else
{
_logger.Error($"Unable to locate BIT node {instrumentName}");
return false;
}
}
/// <summary>
/// always runs the request on the first node,
/// </summary>
/// <param name="messageIdOut"></param>
/// <param name="messageIdIn"></param>
/// <param name="timeoutInMs"></param>
/// <param name="messageParams"></param>
/// <returns></returns>
public BitTestResults RunBITWaitForResults(string messageIdOut,
string messageIdIn,
uint timeoutInMs,
IEnumerable<KeyValuePair<string, string>> messageParams = null)
{
if (_bitNodes.Any())
{
IBit bitNode = _bitNodes.First().Value;
return bitNode.RunBITWaitForResults(messageIdOut, messageIdIn, timeoutInMs, messageParams);
}
else
{
_logger.Error($"Unable to locate BIT node. No nodes defined");
return null;
}
}
/// <summary>
/// run BIT message and wait for result from the instrument based on the name
/// </summary>
/// <param name="instrumentName"></param>
/// <param name="messageIdOut"></param>
/// <param name="messageIdIn"></param>
/// <param name="timeoutInMs"></param>
/// <param name="messageParams"></param>
/// <returns></returns>
public BitTestResults RunBITWaitForResults(string instrumentName,
string messageIdOut,
string messageIdIn,
uint timeoutInMs,
IEnumerable<KeyValuePair<string, string>> messageParams = null)
{
if (_bitNodes.ContainsKey(instrumentName))
{
return _bitNodes[instrumentName].RunBITWaitForResults(messageIdOut, messageIdIn, timeoutInMs, messageParams);
}
else
{
_logger.Error($"Unable to locate BIT node {instrumentName}");
return null;
}
}
/// <summary>
/// RunBITWaitForResults for the whole list of BIT Commands
/// runs on the first node
/// </summary>
/// <param name="commands"></param>
/// <returns></returns>
public List<BitTestResults> RunBITWaitForResultsList(List<BITCommand> commands)
{
if (_bitNodes.Any())
{
return RunBITWaitForResultsList(commands, _bitNodes.First().Value);
}
else
{
_logger.Error("Unable to locate BIT node. No nodes defined");
return null;
}
}
/// <summary>
/// RunBITWaitForResults for the whole list of BIT Commands
/// </summary>
/// <param name="instrumentName"></param>
/// <param name="commands"></param>
/// <returns></returns>
public List<BitTestResults> RunBITWaitForResultsList(string instrumentName, List<BITCommand> commands)
{
if (_bitNodes.ContainsKey(instrumentName))
{
return RunBITWaitForResultsList(commands, _bitNodes[instrumentName]);
}
else
{
_logger.Error($"Unable to locate BIT node {instrumentName}");
return null;
}
}
/// <summary>
/// Runs BIT command and returns the results as a list
/// </summary>
/// <param name="commands"></param>
/// <returns></returns>
public async Task<List<BitTestResults>> RunBITWaitForResultsListAsync(List<BITCommand> commands)
{
if (_bitNodes.Any())
{
return await RunBITWaitForResultsListAsync(commands, _bitNodes.First().Value);
}
else
{
_logger.Error("Unable to locate BIT node. No nodes defined");
return null;
}
}
/// <summary>
/// Runs BIT command for specific instrument and returns the results as a list
/// </summary>
/// <param name="instrumentName"></param>
/// <param name="commands"></param>
/// <returns></returns>
public async Task<List<BitTestResults>> RunBITWaitForResultsListAsync(string instrumentName, List<BITCommand> commands)
{
if (_bitNodes.ContainsKey(instrumentName))
{
return await RunBITWaitForResultsListAsync(commands, _bitNodes[instrumentName]);
}
else
{
_logger.Error($"Unable to locate BIT node {instrumentName}");
return null;
}
}
/// <summary>
/// asynchronously runs BIT command and waits for multiple results including parametric messages
/// </summary>
/// <param name="instrumentName">BIT instrument to run command</param>
/// <param name="bitCommandId">main command</param>
/// <param name="timeout">timeout value in ms for the main response</param>
/// <param name="commandParams">optional parameters for the main command</param>
/// <param name="parametricResponseIds">list of additional response messages with their respective timeouts to wait for</param>
/// <param name="timeout2">parametric timeout value in ms</param>
/// <param name="errorMessageId">nack response id when provided will be expected as a possible response for either main response or parametric response</param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<List<BitTestResults>> RunBITWaitForResultsAsync( string instrumentName,
string bitCommandId,
int retries,
uint timeout,
IEnumerable<KeyValuePair<string, string>> commandParams = null,
List<string> parametricResponseIds = null,
uint timeout2 = 0,
string errorMessageId = null)
{
IBit bitGenSoftManager = _bitNodes[instrumentName] ?? throw new Exception("BitGenSoftManager is null. Unable to perform operation.");
BITCommand command = BuildBITCommand(bitCommandId, retries, timeout, commandParams, parametricResponseIds, timeout2, errorMessageId);
return await RunBITWaitForResultsListAsync(new List<BITCommand> { command }, bitGenSoftManager);
}
/// <summary>
/// keep reading any of the messages from the list
/// </summary>
/// <param name="instrumentName"></param>
/// <param name="parametricResponseIds"></param>
/// <param name="timeOut"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task KeepReadingBitResultsListAsync(string instrumentName, List<string> parametricResponseIds, CancellationToken token)
{
IBit bitNode = _bitNodes[instrumentName] ?? throw new Exception("BitGenSoftManager is null. Unable to perform operation.");
await KeepReadingBitResultsListAsync(parametricResponseIds, token, bitNode);
}
/// <summary>
/// keep reading any of the messages from the list from the first node
/// </summary>
/// <param name="parametricResponseIds"></param>
/// <param name="timeOut"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task KeepReadingBitResultsListAsync(List<string> parametricResponseIds, CancellationToken token)
{
IBit bitNode = _bitNodes.First().Value;
await KeepReadingBitResultsListAsync(parametricResponseIds, token, bitNode);
}
/// <summary>
/// looking for a any of the messages from the list
/// </summary>
/// <param name="instrumentName"></param>
/// <param name="parametricResponseIds"></param>
/// <param name="timeOut"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<List<BitTestResults>> GetBitResultsListAsync(string instrumentName, List<string> parametricResponseIds, int timeOut)
{
IBit bitNode = _bitNodes[instrumentName] ?? throw new Exception("BitGenSoftManager is null. Unable to perform operation.");
return await GetBitResultsListAsync(parametricResponseIds, timeOut, bitNode);
}
/// <summary>
/// looking for a any of the messages from the list on the first node
/// </summary>
/// <param name="parametricResponseIds"></param>
/// <param name="timeOut"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<List<BitTestResults>> GetBitResultsListAsync(List<string> parametricResponseIds, int timeOut)
{
IBit bitNode = _bitNodes.First().Value;
return await GetBitResultsListAsync(parametricResponseIds, timeOut, bitNode);
}
/// <summary>
/// look for results in the node until either result was found or canceled
/// </summary>
/// <param name="token"></param>
/// <param name="responseId"></param>
/// <returns></returns>
public async Task<BitTestResults> GetBITResultsAsync(CancellationToken token, string responseId)
{
IBit bitNode = _bitNodes.First().Value;
return await GetBITResultsAsync(token, responseId, bitNode);
}
/// <summary>
/// look for results in the node until either result was found or canceled
/// </summary>
/// <param name="instrumentName"></param>
/// <param name="token"></param>
/// <param name="responseId"></param>
/// <returns></returns>
public async Task<BitTestResults> GetBITResultsAsync(string instrumentName, CancellationToken token, string responseId)
{
IBit bitNode = _bitNodes[instrumentName] ?? throw new Exception("BitGenSoftManager is null. Unable to perform operation.");
return await GetBITResultsAsync(token, responseId, bitNode);
}
/// <summary>
/// look for results in either node
/// </summary>
/// <param name="messageId"></param>
/// <returns></returns>
public BitTestResults GetBITResults(string messageId)
{
_logger.Trace($"Getting BIT result for: {messageId}.");
if (_bitNodes.Any())
{
IBit bitNode = _bitNodes.First().Value;
return bitNode.GetBITResults(messageId);
}
else
return null;
}
/// <summary>
/// look for results in either node
/// </summary>
/// <param name="instrumentName"></param>
/// <param name="messageId"></param>
/// <returns></returns>
public BitTestResults GetBITResults(string instrumentName, string messageId)
{
_logger.Trace($"{instrumentName} Getting BIT result for: {messageId}.");
return _bitNodes.ContainsKey(instrumentName) ? _bitNodes[instrumentName].GetBITResults(messageId) : null;
}
/// <summary>
/// opens a folder parse XML files for BITCommand definitions and returns a list
/// </summary>
/// <param name="dir"></param>
/// <returns></returns>
public Dictionary<string, List<BITCommand>> GetBITCommands(string directoryPath)
{
Dictionary<string, List<BITCommand>> bitCommands = new Dictionary<string, List<BITCommand>>();
try
{
string[] xmlFiles;
if (directoryPath.EndsWith(".xml", StringComparison.InvariantCultureIgnoreCase))
{
xmlFiles = new string[] {directoryPath};
}
else
{
// Get all XML files recursively in the specified directory
xmlFiles = Directory.GetFiles(directoryPath, "*.xml", SearchOption.TopDirectoryOnly);
}
foreach (string xmlFile in xmlFiles)
{
ConfigurationFile configFile = new ConfigurationFile(xmlFile);
List<string> sections = configFile.ReadAllSections();
foreach (string section in sections)
{
List<BITCommand> commands = configFile.ReadList<BITCommand>(section, "BIT_CMD");
if(bitCommands.ContainsKey(section))
{
_logger.Warn($"Found a duplicate BIT configuration section {section}, overwriting from {xmlFile} file.");
}
else
{
_logger.Trace($"Adding {section} commands from {xmlFile} file");
}
bitCommands[section] = commands;
}
}
}
catch (Exception ex)
{
_logger.Error(ex, $"Error reading BIT Command definitions in {directoryPath}");
throw;
}
return bitCommands;
}
#region Private functions
/// <summary>
/// will keep pumping messages from the receiving queue until canceled
/// </summary>
/// <param name="parametricResponseIds"></param>
/// <param name="token"></param>
/// <param name="bitNode"></param>
/// <returns></returns>
private async Task KeepReadingBitResultsListAsync(List<string> parametricResponseIds, CancellationToken token, IBit bitNode)
{
_logger.Trace($"{bitNode.Name} Start continuously reading these messages: {string.Join(", ", parametricResponseIds)}.");
Dictionary<uint, int> score = new Dictionary<uint, int>();
do
{
foreach (var responseId in parametricResponseIds)
{
BitTestResults bitTestresults = await GetBITResultsAsync(token, responseId, bitNode);
if (bitTestresults != null && !string.IsNullOrEmpty(bitTestresults.Label))
{
if(uint.TryParse(bitTestresults.Label, out uint label))
{
if(!score.ContainsKey(label))
{
score.Add(label, 1);
}
else
{
score[label]++;
}
}
}
}
//await Task.Delay(_checkForMessageIntervalMs);
} while (!token.IsCancellationRequested);
foreach (var item in score)
{
_logger.Trace($"{bitNode.Name} Dequeued the total of {item.Value} BIT messages for 0x{item.Key:X} label");
}
}
/// <summary>
///
/// </summary>
/// <param name="parametricResponseIds"></param>
/// <param name="timeOut"></param>
/// <param name="node"></param>
/// <returns></returns>
private async Task<List<BitTestResults>> GetBitResultsListAsync(List<string> parametricResponseIds, int timeOut, IBit node)
{
_logger.Trace($"{node.Name} Start continuously getting BIT results for these messages: {string.Join(", ", parametricResponseIds)}.");
List<BitTestResults> results = new List<BitTestResults>();
CancellationTokenSource tokenSource = new CancellationTokenSource();
List<Task<BitTestResults>> responseTasks = new List<Task<BitTestResults>>();
foreach (var responseId in parametricResponseIds)
{
responseTasks.Add(GetBITResultsAsync(tokenSource.Token, responseId, node));
}
Task timeoutTask = Task.Delay(timeOut);
var completedTask = await Task.WhenAny(responseTasks.Concat(new[] { timeoutTask }));
tokenSource.Cancel();
tokenSource.Dispose();
if (completedTask == timeoutTask)
{
_logger.Warn($"Timed out after {timeOut} ms while waiting on parametrized response message(s) {string.Join(", ", parametricResponseIds)}");
}
else
{
var completedResults = responseTasks.Where(t => t.Status == TaskStatus.RanToCompletion && t.Result != null).Select(t => t.Result);
results.AddRange(completedResults);
}
_logger.Trace($"{node.Name} Completed getting BIT results for these messages: {string.Join(", ", parametricResponseIds)}, found {results?.Count} results");
return results;
}
/// <summary>
/// look for results in the node until either result was found or canceled
/// </summary>
/// <param name="token"></param>
/// <param name="responseId"></param>
/// <param name="node"></param>
/// <returns></returns>
private async Task<BitTestResults> GetBITResultsAsync(CancellationToken token, string responseId, IBit node)
{
//_logger.Trace($"{node.Name} Start waiting for BIT message: {responseId}.");
BitTestResults bitTestResults = null;
do
{
await Task.Delay(_checkForMessageIntervalMs);
try
{
bitTestResults = node.GetBITResults(responseId);
}
catch (Exception ex)
{
_logger.Error(ex, ex.Message);
break;
}
} while (bitTestResults == null && !token.IsCancellationRequested);
//_logger.Trace($"{node.Name} Done waiting for BIT message: {responseId}.");
return bitTestResults;
}
/// <summary>
/// builds a command from parameters
/// </summary>
/// <param name="bitCommandId"></param>
/// <param name="retries"></param>
/// <param name="timeout"></param>
/// <param name="commandParams"></param>
/// <param name="parametricResponseIds"></param>
/// <param name="timeout2"></param>
/// <param name="errorMessageId"></param>
/// <returns></returns>
private static BITCommand BuildBITCommand(string bitCommandId,
int retries,
uint timeout,
IEnumerable<KeyValuePair<string, string>> commandParams = null,
List<string> parametricResponseIds = null,
uint timeout2 = 0,
string errorMessageId = null)
{
BITCommand bitCommand = new BITCommand
{
Command = bitCommandId,
CommandTimeout = timeout,
CommandParameters = new List<BITParameter>(),
CommandResponses = new List<BITResponseMsg>(),
ParametricResponse = retries.ToString(),
ParametricTimeout = timeout2
};
if (commandParams != null)
{
foreach (var commandParameter in commandParams)
{
bitCommand.CommandParameters.Add(new BITParameter
{
Key = commandParameter.Key,
Value = commandParameter.Value,
ParameterType = ParameterType.U
});
}
}
if(parametricResponseIds != null)
{
foreach (var response in parametricResponseIds)
{
bitCommand.CommandResponses.Add(new BITResponseMsg
{
Name = response,
ResponseType = ResponseMessageType.P
});
}
}
if(!string.IsNullOrEmpty(errorMessageId))
{
bitCommand.CommandResponses.Add(new BITResponseMsg
{
Name = errorMessageId,
ResponseType = ResponseMessageType.E
});
}
return bitCommand;
}
/// <summary>
///
/// </summary>
/// <param name="commands"></param>
/// <param name="node"></param>
/// <returns></returns>
private List<BitTestResults> RunBITWaitForResultsList(List<BITCommand> commands, IBit node)
{
List<BitTestResults> results = new List<BitTestResults>();
foreach (var command in commands)
{
string bitCommand = command.Command;
string bitResponseId = ResponseGroupForFirstBITCall(command);
uint timeout = command.CommandTimeout;
uint timeout2 = command.ParametricTimeout;
string strParamerticResponse = command.ParametricResponse;
int retries = 0;
if (int.TryParse(strParamerticResponse, out int tmpretries))
{
retries = tmpretries;
}
List<KeyValuePair<string, string>> commandParams = ConvertCommandParameters(command.CommandParameters);
int runIndex = 0;
do
{
runIndex++;
try
{
var result = node.RunBITWaitForResults(bitCommand, bitResponseId, timeout, commandParams);
results.Add(result);
}
catch (BitTimeoutException)
{
_logger.Warn($"Timeout after {timeout} ms on BIT command {bitCommand} waiting for result {bitResponseId}, retry number {runIndex}");
if (runIndex == retries)
{
throw;
}
}
} while (results == null && runIndex <= retries);
var responseGroup = ResponseGroupListForParameterBITCall(command);
if (responseGroup == null || !responseGroup.Any())
{
continue;
}
CancellationTokenSource cts = new CancellationTokenSource();
ParallelOptions options = new ParallelOptions
{
CancellationToken = cts.Token,
MaxDegreeOfParallelism = Environment.ProcessorCount
};
try
{
Parallel.ForEach(responseGroup, options, item =>
{
var totalWaitTimeMs = _checkForMessageIntervalMs;
BitTestResults parametricresults;
do
{
parametricresults = node.GetBITResults(item);
if (parametricresults != null)
{
break;
}
else
{
Thread.Sleep(_checkForMessageIntervalMs);
totalWaitTimeMs += _checkForMessageIntervalMs;
}
} while (totalWaitTimeMs < timeout2 && !cts.IsCancellationRequested);
if (parametricresults == null && !cts.IsCancellationRequested)
{
_logger.Warn($"Timed out while waiting on parametrized response message(s) for {command.Command} command");
}
if (parametricresults != null)
{
// replace earlier results (confirmation message) with parametric results
results.Add(parametricresults);
cts.Cancel();
}
});
}
catch (OperationCanceledException)
{
_logger.Trace($"Done waiting on parametrized response message(s) for {command.Command} command");
}
finally
{
cts.Dispose();
}
}
return results;
}
/// <summary>
/// runs bit command and then
/// </summary>
/// <param name="commands"></param>
/// <param name="node"></param>
/// <returns></returns>
private async Task<List<BitTestResults>> RunBITWaitForResultsListAsync(List<BITCommand> commands, IBit node)
{
List<BitTestResults> results = new List<BitTestResults>();
foreach (var command in commands)
{
string bitCommand = command.Command;
string bitResponseId = ResponseGroupForFirstBITCall(command);
uint timeout = command.CommandTimeout;
uint timeout2 = command.ParametricTimeout;
string strParamerticResponse = command.ParametricResponse;
int retries = 0;
if (int.TryParse(strParamerticResponse, out int tmpretries))
{
retries = tmpretries;
}
List<KeyValuePair<string, string>> commandParams = ConvertCommandParameters(command.CommandParameters);
int runIndex = 0;
do
{
runIndex++;
try
{
var result = await Task.Run(() => node.RunBITWaitForResults(bitCommand, bitResponseId, timeout, commandParams));
results.Add(result);
}
catch (BitTimeoutException)
{
_logger.Warn($"Timeout after {timeout} ms on BIT command {bitCommand} waiting for result {bitResponseId}, retry number {runIndex}");
if (runIndex == retries)
{
throw;
}
}
} while (results == null && runIndex <= retries);
var responseGroup = ResponseGroupListForParameterBITCall(command);
if (responseGroup == null || !responseGroup.Any())
{
continue;
}
var paramResults = await GetBitResultsListAsync(responseGroup, (int)timeout2, node);
if (paramResults != null)
{
results.AddRange(paramResults);
}
}
return results;
}
/// <summary>
/// for the initial BIT message expect to find R (main Response)
/// if any responses marked as E (error) add it to the list as alternative expected response, separated by comma
/// </summary>
/// <param name="command"></param>
/// <returns></returns>
private static string ResponseGroupForFirstBITCall(BITCommand command)
{
StringBuilder resp = new StringBuilder();
var tempMainResponseGroup = command.CommandResponses.Where(m => m.ResponseType != ResponseMessageType.E).ToList();
if (tempMainResponseGroup != null)
{
// check for Rs first and if no messages marked with Rs than check for Us
var mainResponse = tempMainResponseGroup.FirstOrDefault(m => m.ResponseType == ResponseMessageType.R);
if (mainResponse != null)
{
resp.Append(mainResponse.Name);
}
}
// append all error message ids
foreach (var item in command.CommandResponses.Where(m => m.ResponseType == ResponseMessageType.E))
{
resp.Append(',');
resp.Append(item.Name);
}
return resp.ToString();
}
/// <summary>
/// for subsequent parameter response look for either P (parameterized) or U (undefined)
/// if any responses marked as E (error) add it to the list as alternative expected response
/// </summary>
/// <param name="command"></param>
/// <returns></returns>
private static List<string> ResponseGroupListForParameterBITCall(BITCommand command)
{
List<string> resp = new List<string>();
bool parameterizedRespExpected = false;
var tempMainResponseGroup = command.CommandResponses.Where(m => m.ResponseType != ResponseMessageType.E).ToList();
if (tempMainResponseGroup != null)
{
// check for Rs first and if no messages marked with Rs than check for Us
var mainResponse = tempMainResponseGroup.FirstOrDefault(m => m.ResponseType == ResponseMessageType.P || m.ResponseType == ResponseMessageType.U);
if (mainResponse != null)
{
resp.Add(mainResponse.Name);
parameterizedRespExpected = true;
}
}
if (parameterizedRespExpected)
{
// append all error message ids
foreach (var item in command.CommandResponses.Where(m => m.ResponseType == ResponseMessageType.E))
{
resp.Add(item.Name);
}
}
return resp;
}
private static List<KeyValuePair<string, string>> ConvertCommandParameters(List<BITParameter> @in)
{
List<KeyValuePair<string, string>> parameters = new List<KeyValuePair<string, string>>();
foreach (var parameter in @in)
{
parameters.Add(new KeyValuePair<string, string>(parameter.Key, parameter.Value));
}
return parameters;
}
#endregion
}
}