937 lines
30 KiB
C#
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
|
|
}
|
|
}
|