This commit is contained in:
2026-04-17 14:55:32 -04:00
commit bc3ac1d4c9
18017 changed files with 4371742 additions and 0 deletions

View File

@@ -0,0 +1,541 @@
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Threading;
namespace DTS.Common.Utils
{
/// <summary>
/// Utility class used to display spinning animation circle to indicate
/// system is busy with background process
///
/// Adapting from open source found via Google so it doesn' conform to our
/// coding formatting or commenting rules
/// </summary>
public class BusyWaitAnimation
{
// Constants =========================================================
private const double NumberOfDegreesInCircle = 360;
private const double NumberOfDegreesInHalfCircle = NumberOfDegreesInCircle / 2;
private const int DefaultInnerCircleRadius = 8;
private const int DefaultOuterCircleRadius = 10;
private const int DefaultNumberOfSpoke = 10;
private const int DefaultSpokeThickness = 4;
private readonly Color _defaultColor = Color.DarkGray;
private const int MacOsxInnerCircleRadius = 5;
private const int MacOsxOuterCircleRadius = 11;
private const int MacOsxNumberOfSpoke = 12;
private const int MacOsxSpokeThickness = 2;
private const int FireFoxInnerCircleRadius = 6;
private const int FireFoxOuterCircleRadius = 7;
private const int FireFoxNumberOfSpoke = 9;
private const int FireFoxSpokeThickness = 4;
private const int Ie7InnerCircleRadius = 8;
private const int Ie7OuterCircleRadius = 9;
private const int Ie7NumberOfSpoke = 24;
private const int Ie7SpokeThickness = 4;
// Enumeration =======================================================
public enum StylePresets
{
MacOsx,
Firefox,
Ie7,
Custom
}
// Attributes ========================================================
private readonly DispatcherTimer _timer;
private bool _isTimerActive;
private int _numberOfSpoke;
private int _spokeThickness;
private int _progressValue;
private int _outerCircleRadius;
private int _innerCircleRadius;
private PointF _centerPoint;
private Color _color;
private Color[] _colors;
private double[] _angles;
private StylePresets _stylePreset;
// Properties ========================================================
/// <summary>
/// Gets or sets the lightest color of the circle.
/// </summary>
/// <value>The lightest color of the circle.</value>
[TypeConverter("System.Drawing.ColorConverter"),
Category("LoadingCircle"),
Description("Sets the color of spoke.")]
public Color Color
{
get => _color;
set
{
_color = value;
GenerateColorsPallet();
Invalidate();
}
}
/// <summary>
/// Background color
/// </summary>
public Color BgColor
{
get;
set;
}
/// <summary>
/// Gets or sets the outer circle radius.
/// </summary>
/// <value>The outer circle radius.</value>
[Description("Gets or sets the radius of outer circle."),
Category("LoadingCircle")]
public int OuterCircleRadius
{
get
{
if (_outerCircleRadius == 0)
{ _outerCircleRadius = DefaultOuterCircleRadius;}
return _outerCircleRadius;
}
set
{
_outerCircleRadius = value;
Invalidate();
}
}
/// <summary>
/// Gets or sets the inner circle radius.
/// </summary>
/// <value>The inner circle radius.</value>
[Description("Gets or sets the radius of inner circle."),
Category("LoadingCircle")]
public int InnerCircleRadius
{
get
{
if (_innerCircleRadius == 0)
_innerCircleRadius = DefaultInnerCircleRadius;
return _innerCircleRadius;
}
set
{
_innerCircleRadius = value;
Invalidate();
}
}
/// <summary>
/// Gets or sets the number of spoke.
/// </summary>
/// <value>The number of spoke.</value>
[Description("Gets or sets the number of spoke."),
Category("LoadingCircle")]
public int NumberSpoke
{
get
{
if (_numberOfSpoke == 0)
_numberOfSpoke = DefaultNumberOfSpoke;
return _numberOfSpoke;
}
set
{
if (_numberOfSpoke != value && _numberOfSpoke > 0)
{
_numberOfSpoke = value;
GenerateColorsPallet();
GetSpokesAngles();
Invalidate();
}
}
}
/// <summary>
/// Gets or sets a value indicating whether this <see cref="T:LoadingCircle"/> is active.
/// </summary>
/// <value><c>true</c> if active; otherwise, <c>false</c>.</value>
[Description("Gets or sets the number of spoke."),
Category("LoadingCircle")]
public bool Active
{
get => _isTimerActive;
set
{
_isTimerActive = value;
ActiveTimer();
}
}
/// <summary>
/// Gets or sets the spoke thickness.
/// </summary>
/// <value>The spoke thickness.</value>
[Description("Gets or sets the thickness of a spoke."),
Category("LoadingCircle")]
public int SpokeThickness
{
get
{
if (_spokeThickness <= 0)
_spokeThickness = DefaultSpokeThickness;
return _spokeThickness;
}
set
{
_spokeThickness = value;
Invalidate();
}
}
/// <summary>
/// Gets or sets the rotation speed.
/// </summary>
/// <value>The rotation speed.</value>
[Description("Gets or sets the rotation speed. Higher the slower."),
Category("LoadingCircle")]
public long RotationSpeed
{
get => _timer.Interval.Ticks;
set
{
if (value > 0)
_timer.Interval = TimeSpan.FromMilliseconds(value);
}
}
/// <summary>
/// Quickly sets the style to one of these presets, or a custom style if desired
/// </summary>
/// <value>The style preset.</value>
[Category("LoadingCircle"),
Description("Quickly sets the style to one of these presets, or a custom style if desired"),
DefaultValue(typeof(StylePresets), "Custom")]
public StylePresets StylePreset
{
get => _stylePreset;
set
{
_stylePreset = value;
switch (_stylePreset)
{
case StylePresets.MacOsx:
SetCircleAppearance(MacOsxNumberOfSpoke,
MacOsxSpokeThickness, MacOsxInnerCircleRadius,
MacOsxOuterCircleRadius);
break;
case StylePresets.Firefox:
SetCircleAppearance(FireFoxNumberOfSpoke,
FireFoxSpokeThickness, FireFoxInnerCircleRadius,
FireFoxOuterCircleRadius);
break;
case StylePresets.Ie7:
SetCircleAppearance(Ie7NumberOfSpoke,
Ie7SpokeThickness, Ie7InnerCircleRadius,
Ie7OuterCircleRadius);
break;
case StylePresets.Custom:
SetCircleAppearance(DefaultNumberOfSpoke,
DefaultSpokeThickness,
DefaultInnerCircleRadius,
DefaultOuterCircleRadius);
break;
}
}
}
static int _xxx = 0;
/// <summary>
/// Construtor Initializes a new instance of the <see cref="T:LoadingCircle"/> class.
/// </summary>
public BusyWaitAnimation()
{
_xxx++;
_color = _defaultColor;
BgColor = Color.LightGray;
GenerateColorsPallet();
GetSpokesAngles();
GetControlCenterPoint();
_timer = new DispatcherTimer();
_timer.Tick += new EventHandler(aTimer_Tick);
ActiveTimer();
}
/// <summary>
/// Handles the Tick event of the aTimer control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
void aTimer_Tick(object sender, EventArgs e)
{
_progressValue = ++_progressValue % _numberOfSpoke;
Invalidate();
}
/// <summary>
/// Raises the <see cref="E:System.Windows.Forms.Control.Paint"></see> event.
/// </summary>
/// <param name="g">A <see cref="T:System.Windows.Forms.PaintEventArgs"></see> that contains the event data.</param>
private void OnPaint(Graphics g)
{
if (_numberOfSpoke <= 0) return;
_backgroundGraphics.Clear(BgColor);
g.SmoothingMode = SmoothingMode.HighQuality;
var intPosition = _progressValue;
for (var intCounter = 0; intCounter < _numberOfSpoke; intCounter++)
{
intPosition = intPosition % _numberOfSpoke;
DrawLine(_backgroundGraphics,
GetCoordinate(_centerPoint, _innerCircleRadius, _angles[intPosition]),
GetCoordinate(_centerPoint, _outerCircleRadius, _angles[intPosition]),
_colors[intCounter], _spokeThickness);
intPosition++;
}
g.DrawImage(_backgroundImage, new Point(_drawArea.Left, _drawArea.Top));
}
// Methods ===========================================================
/// <summary>
/// Darkens a specified color.
/// </summary>
/// <param name="objColor">Color to darken.</param>
/// <param name="intPercent">The percent of darken.</param>
/// <returns>The new color generated.</returns>
private Color Darken(Color objColor, int intPercent)
{
int intRed = objColor.R;
int intGreen = objColor.G;
int intBlue = objColor.B;
return Color.FromArgb(intPercent, Math.Min(intRed, byte.MaxValue), Math.Min(intGreen, byte.MaxValue), Math.Min(intBlue, byte.MaxValue));
}
/// <summary>
/// Generates the colors pallet.
/// </summary>
private void GenerateColorsPallet()
{
_colors = GenerateColorsPallet(_color, Active, _numberOfSpoke);
}
/// <summary>
/// Generates the colors pallet.
/// </summary>
/// <param name="color">Color of the lightest spoke.</param>
/// <param name="bhadeColor">if set to <c>true</c> the color will be shaded on X spoke.</param>
/// <param name="nbSpoke"></param>
/// <returns>An array of color used to draw the circle.</returns>
private Color[] GenerateColorsPallet(Color color, bool bhadeColor, int nbSpoke)
{
var objColors = new Color[NumberSpoke];
// Value is used to simulate a gradient feel... For each spoke, the
// color will be darken by value in intIncrement.
var bytIncrement = (byte)(byte.MaxValue / NumberSpoke);
//Reset variable in case of multiple passes
byte percentageOfDarken = 0;
for (var intCursor = 0; intCursor < NumberSpoke; intCursor++)
{
if (bhadeColor)
{
if (intCursor == 0 || intCursor < NumberSpoke - nbSpoke)
objColors[intCursor] = color;
else
{
// Increment alpha channel color
percentageOfDarken += bytIncrement;
// Ensure that we don't exceed the maximum alpha
// channel value (255)
if (percentageOfDarken > byte.MaxValue)
percentageOfDarken = byte.MaxValue;
// Determine the spoke forecolor
objColors[intCursor] = Darken(color, percentageOfDarken);
}
}
else
objColors[intCursor] = color;
}
return objColors;
}
/// <summary>
/// Gets the control center point.
/// </summary>
private void GetControlCenterPoint()
{
// m_CenterPoint = GetControlCenterPoint(this);
_centerPoint = new PointF(_drawArea.Width / 2, _drawArea.Height / 2 - 1);
}
/// <summary>
/// Draws the line with GDI+.
/// </summary>
/// <param name="graphics">The Graphics object.</param>
/// <param name="pointOne">The point one.</param>
/// <param name="pointTwo">The point two.</param>
/// <param name="color">Color of the spoke.</param>
/// <param name="lineThickness">The thickness of spoke.</param>
private void DrawLine(Graphics graphics, PointF pointOne, PointF pointTwo,
Color color, int lineThickness)
{
using (var objPen = new Pen(new SolidBrush(color), lineThickness))
{
objPen.StartCap = LineCap.Round;
objPen.EndCap = LineCap.Round;
graphics.DrawLine(objPen, pointOne, pointTwo);
}
}
/// <summary>
/// Gets the coordinate.
/// </summary>
/// <param name="circleCenter">The Circle center.</param>
/// <param name="radius">The radius.</param>
/// <param name="angle">The angle.</param>
/// <returns></returns>
private PointF GetCoordinate(PointF circleCenter, int radius, double angle)
{
var dblAngle = Math.PI * angle / NumberOfDegreesInHalfCircle;
return new PointF(circleCenter.X + radius * (float)Math.Cos(dblAngle),
circleCenter.Y + radius * (float)Math.Sin(dblAngle));
}
/// <summary>
/// Gets the spokes angles.
/// </summary>
private void GetSpokesAngles()
{
_angles = GetSpokesAngles(NumberSpoke);
}
/// <summary>
/// Gets the spoke angles.
/// </summary>
/// <param name="numberSpoke">The number spoke.</param>
/// <returns>An array of angle.</returns>
private double[] GetSpokesAngles(int numberSpoke)
{
var angles = new double[numberSpoke];
var angle = NumberOfDegreesInCircle / numberSpoke;
for (var shtCounter = 0; shtCounter < numberSpoke; shtCounter++)
angles[shtCounter] = (shtCounter == 0 ? angle : angles[shtCounter - 1] + angle);
return angles;
}
/// <summary>
/// Actives the timer.
/// </summary>
private void ActiveTimer()
{
if (_isTimerActive)
_timer.Start();
else
{
_timer.Stop();
_progressValue = 0;
}
GenerateColorsPallet();
Invalidate();
}
/// <summary>
/// Sets the circle appearance.
/// </summary>
/// <param name="numberSpoke">The number spoke.</param>
/// <param name="spokeThickness">The spoke thickness.</param>
/// <param name="innerCircleRadius">The inner circle radius.</param>
/// <param name="outerCircleRadius">The outer circle radius.</param>
public void SetCircleAppearance(int numberSpoke, int spokeThickness, int innerCircleRadius, int outerCircleRadius)
{
NumberSpoke = numberSpoke;
SpokeThickness = spokeThickness;
InnerCircleRadius = innerCircleRadius;
OuterCircleRadius = outerCircleRadius;
Invalidate();
}
#region new methods
private Rectangle _drawArea;
private Graphics _graphics;
private int _hDc = int.MinValue;
private Bitmap _backgroundImage;
private Graphics _backgroundGraphics;
private void Invalidate()
{
if (_graphics != null)
{
_graphics.DrawImageUnscaled(_backgroundImage, new Point(_drawArea.Left, _drawArea.Top));
OnPaint(_graphics);
}
}
public Graphics GDIGraphics => _graphics;
public int hDC
{
set
{
if (_graphics != null) _graphics.Dispose();
_hDc = value;
_graphics = Graphics.FromHdc(new IntPtr(value));
}
get => _hDc;
}
public void SetDrawArea(Rectangle rect)
{
_drawArea = new Rectangle(rect.X, rect.Y, rect.Width, rect.Height);
GetControlCenterPoint();
_backgroundImage = new Bitmap(rect.Width, rect.Height);
_backgroundGraphics = Graphics.FromImage(_backgroundImage);
_backgroundGraphics.SmoothingMode = SmoothingMode.HighQuality;
}
#endregion
}
}

View File

@@ -0,0 +1,171 @@
using System;
using System.Text;
namespace DTS.Common.Utils
{
// Converts integral types to bytes but wraps the conversions so that
// outgoing they are converted to network order and incoming they are
// converted to host order.
public class ByteConvertor
{
public static void Convert(byte[] input, int offset, out byte value)
{
// Nothing to do for single bytes
value = input[offset];
}
public static void Convert(byte[] input, int offset, out ushort value)
{
value = (ushort)(0 |
input[offset + 0] << 8 |
input[offset + 1] << 0);
}
public static void Convert(byte[] input, int offset, out short value)
{
value = (short)(0 |
input[offset + 0] << 8 |
input[offset + 1] << 0);
}
public static void Convert(byte[] input, int offset, out uint value)
{
value = 0 |
(uint)input[offset + 0] << 24 |
(uint)input[offset + 1] << 16 |
(uint)input[offset + 2] << 8 |
(uint)input[offset + 3] << 0;
}
public static void Convert(byte[] input, int offset, out int value)
{
value =
input[offset + 0] << 24 |
input[offset + 1] << 16 |
input[offset + 2] << 8 |
input[offset + 3] << 0;
}
public static void Convert(byte[] input, int offset, out ulong value)
{
value = 0 |
(ulong)input[offset + 0] << 56 |
(ulong)input[offset + 1] << 48 |
(ulong)input[offset + 2] << 40 |
(ulong)input[offset + 3] << 32 |
(ulong)input[offset + 4] << 24 |
(ulong)input[offset + 5] << 16 |
(ulong)input[offset + 6] << 8 |
(ulong)input[offset + 7] << 0;
}
public static void Convert(byte[] input, int offset, out long value)
{
value = 0 |
(long)input[offset + 0] << 56 |
(long)input[offset + 1] << 48 |
(long)input[offset + 2] << 40 |
(long)input[offset + 3] << 32 |
(long)input[offset + 4] << 24 |
(long)input[offset + 5] << 16 |
(long)input[offset + 6] << 8 |
(long)input[offset + 7] << 0;
}
public static void Convert(byte[] input, int offset, out float value)
{
value = BitConverter.ToSingle(input, offset);
}
public static void Convert(byte[] input, int offset, out double value)
{
value = BitConverter.ToDouble(input, offset);
}
public static void Convert(byte[] input, int offset, out string value)
{
var sb = new StringBuilder();
for (var i = offset; i < input.Length && input[i] != 0; i++)
{
sb.Append((char)input[i]);
}
value = sb.ToString();
}
public static byte[] ToByteArray(string input)
{
var c = input.ToCharArray();
var rv = new byte[c.Length + 1];
for (var i = 0; i < c.Length; i++) rv[i] = (byte)c[i];
rv[c.Length] = 0;
return rv;
}
public static byte[] ToByteArray(byte input)
{
var rv = new byte[1];
rv[0] = input;
return rv;
}
public static byte[] ToByteArray(ushort input)
{
var rv = new byte[2];
rv[0] = (byte)((input >> 8) & 0xFF);
rv[1] = (byte)((input >> 0) & 0xFF);
return rv;
}
public static byte[] ToByteArray(short input)
{
var rv = new byte[2];
rv[0] = (byte)((input >> 8) & 0xFF);
rv[1] = (byte)((input >> 0) & 0xFF);
return rv;
}
public static byte[] ToByteArray(uint input)
{
var rv = new byte[4];
rv[0] = (byte)((input >> 24) & 0xFF);
rv[1] = (byte)((input >> 16) & 0xFF);
rv[2] = (byte)((input >> 8) & 0xFF);
rv[3] = (byte)((input >> 0) & 0xFF);
return rv;
}
public static byte[] ToByteArray(int input)
{
var rv = new byte[4];
rv[0] = (byte)((input >> 24) & 0xFF);
rv[1] = (byte)((input >> 16) & 0xFF);
rv[2] = (byte)((input >> 8) & 0xFF);
rv[3] = (byte)((input >> 0) & 0xFF);
return rv;
}
public static byte[] ToByteArray(ulong input)
{
var rv = new byte[8];
rv[0] = (byte)((input >> 56) & 0xFF);
rv[1] = (byte)((input >> 48) & 0xFF);
rv[2] = (byte)((input >> 40) & 0xFF);
rv[3] = (byte)((input >> 32) & 0xFF);
rv[4] = (byte)((input >> 24) & 0xFF);
rv[5] = (byte)((input >> 16) & 0xFF);
rv[6] = (byte)((input >> 8) & 0xFF);
rv[7] = (byte)((input >> 0) & 0xFF);
return rv;
}
public static byte[] ToByteArray(float input)
{
return BitConverter.GetBytes(input);
}
public static byte[] ToByteArray(double input)
{
return BitConverter.GetBytes(input);
}
}
}

View File

@@ -0,0 +1,494 @@
using Microsoft.Win32;
using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Text;
namespace DTS.Common.Utils
{
public static class Database
{
#region properties
/// <summary>
/// used for output from processes
/// </summary>
private static StringBuilder sb = new StringBuilder();
private static StringBuilder sbError = new StringBuilder();
/// <summary>
/// lock to prevent multiple threads operating on sb simultaneously
/// </summary>
private static readonly object PROCESS_LOCK = new object();
#endregion properties
#region methods
/// <summary>
/// used to collect output from a running process
/// </summary>
/// <param name="sendingProcess"></param>
/// <param name="outLine"></param>
private static void OutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
if (outLine.Data != null)
{
if (string.IsNullOrWhiteSpace(outLine.Data))
{
sb.Append("\r\n");
}
sb.Append(outLine.Data);
}
}
/// <summary>
/// log function optionally passed into functions
/// </summary>
/// <param name="paramlist"></param>
public delegate void LogDelegate(params object[] paramlist);
/// <summary>
/// starts a process with a given command and logs
/// </summary>
/// <param name="sqlLocalDbExeFileName"></param>
/// <param name="command"></param>
/// <param name="log"></param>
/// <returns></returns>
private static string SqlCommandProcessor(string sqlLocalDbExeFileName, string command, LogDelegate log)
{
var resultString = string.Empty;
lock (PROCESS_LOCK)
{
sb.Clear();
sbError.Clear();
var process = new Process
{
StartInfo =
{
FileName = sqlLocalDbExeFileName,
Arguments = command,
LoadUserProfile = true,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true
}
};
//* Set ONLY ONE handler here.
process.OutputDataReceived += OutputHandler;
process.ErrorDataReceived += Process_ErrorDataReceived;
//* Start process
process.Start();
//* Read one element asynchronously
process.BeginErrorReadLine();
//* Read the other one synchronously
var output = process.StandardOutput.ReadToEnd();
Console.WriteLine(output);
log?.Invoke($"Result of {command} command is: {output}");
process.WaitForExit();
if (sb.Length > 0)
{
resultString = sb.ToString();
}
if( sbError.Length > 0)
{
log?.Invoke($"Error command: {command} error: {sbError}");
resultString += sbError.ToString();
}
}
return resultString;
}
/// <summary>
/// handles any error output from running a SqlCmd.exe process
/// </summary>
private static void Process_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data != null)
{
if (string.IsNullOrWhiteSpace(e.Data))
{
sbError.Append("\r\n");
}
sbError.Append(e.Data);
}
}
/// <summary>
/// gets the path to SqlServer
/// </summary>
/// <returns></returns>
public static string GetSqlServerLocalDbPath()
{
var highestVersionInstalledPath = string.Empty;
var rk = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
var sk1 = rk.OpenSubKey("SOFTWARE\\Microsoft\\Microsoft SQL Server Local DB\\Installed Versions");
if (sk1 == null) return string.Empty;
var maxProductVersion = 0.0;
foreach (var productSubKeyName in sk1.GetSubKeyNames())
{
if (!double.TryParse(productSubKeyName, NumberStyles.Float, CultureInfo.InvariantCulture, out var thisVersion)) continue;
if (thisVersion < maxProductVersion) continue;
maxProductVersion = thisVersion;
var newKey = sk1.OpenSubKey(productSubKeyName);
if (newKey == null) continue;
var val = newKey.GetValue("InstanceAPIPath", -1, RegistryValueOptions.None).ToString();
if (val == "-1" || !val.EndsWith("SqlUserInstance.dll")) continue;
highestVersionInstalledPath = val.Substring(0, val.Length - "SqlUserInstance.dll".Length);
}
var envProgFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
Utilities.Logging.APILogger.Log($"Environment.Program Files is {envProgFiles}");
var envProgFilesNoDrive = envProgFiles.Substring(envProgFiles.LastIndexOf("\\") + 1);
Utilities.Logging.APILogger.Log($"envProgFilesNoDrive is {envProgFilesNoDrive}");
Utilities.Logging.APILogger.Log($"highestVersionInstalledPath before Replace is {highestVersionInstalledPath}");
highestVersionInstalledPath = highestVersionInstalledPath.Replace("Program Files", envProgFilesNoDrive);
Utilities.Logging.APILogger.Log($"highestVersionInstalledPath after Replace is {highestVersionInstalledPath}");
return highestVersionInstalledPath.Replace("LocalDB", "Tools");
}
/// <summary>
/// Instead of relying on the Path environment variable to have the path to the SQLCMD.EXE that
/// we need to run, earlier in the Path list than a path to an older version of SQLCMD.EXE that
/// will not work with the SqlLocalDb version that we are using, search the registry for the
/// newest version of SQLCMD.exe and return that path.
/// The path should be something like $"C:\\Program Files\\Microsoft SQL Server\\Client SDK\\ODBC\\110\\Tools\\Binn"
/// </summary>
/// <returns></returns>
public static string GetODBCToolsPath(LogDelegate log)
{
var ODBCToolsPath = string.Empty;
try
{
var rk = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
var sk1 = rk.OpenSubKey("SOFTWARE\\Microsoft\\Microsoft SQL Server");
if (sk1 == null) return string.Empty;
ODBCToolsPath = ScanRegistry(log, sk1, true);
if (string.IsNullOrWhiteSpace(ODBCToolsPath))
{
//We didn't find the patch using the previous method (ensuring that it had a CurrentVersion sub folder)
//so try to find the path without that sub folder.
ODBCToolsPath = ScanRegistry(log, sk1, false);
}
}
catch (Exception ex)
{
log?.Invoke(ex.Message);
}
finally
{
log?.Invoke($"ODBCToolsPath is {ODBCToolsPath}");
}
return ODBCToolsPath;
// This should return something like $"C:\\Program Files\\Microsoft SQL Server\\Client SDK\\ODBC\\110\\Tools\\Binn";
}
private static string ScanRegistry(LogDelegate log, RegistryKey sk1, bool checkCurrentVersion)
{
var ODBCToolsPath = string.Empty;
var envProgFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
Utilities.Logging.APILogger.Log($"Environment.Program Files is {envProgFiles}");
var envProgFilesNoDrive = envProgFiles.Substring(envProgFiles.LastIndexOf("\\") + 1);
Utilities.Logging.APILogger.Log($"envProgFilesNoDrive is {envProgFilesNoDrive}");
try
{
var maxProductVersion = 0;
var maxProductSubKeyNameInt = 0;
foreach (var productSubKeyName in sk1.GetSubKeyNames())
{
if (!double.TryParse(productSubKeyName, NumberStyles.Float, CultureInfo.InvariantCulture, out var thisVersion)) continue;
var productSubKey = sk1.OpenSubKey(productSubKeyName);
if (productSubKey == null) continue;
foreach (var folderName in productSubKey.GetSubKeyNames())
{
if (folderName == "Tools")
{
//e.g. Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\120\Tools
var toolsFolderSubkey = productSubKey.OpenSubKey(folderName);
if (toolsFolderSubkey == null) continue;
foreach (var toolsSubFolderName in toolsFolderSubkey.GetSubKeyNames())
{
if (toolsSubFolderName == "ClientSetup")
{
//e.g. Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\120\Tools\ClientSetup
var clientSetupFolderSubkey = toolsFolderSubkey.OpenSubKey(toolsSubFolderName);
if (clientSetupFolderSubkey == null) continue;
var pathValue = clientSetupFolderSubkey.GetValue("ODBCToolsPath", -1, RegistryValueOptions.None).ToString();
if (pathValue == "-1")
{
log?.Invoke($"There is no ODBCToolsPath subkey in {clientSetupFolderSubkey}");
continue;
}
Utilities.Logging.APILogger.Log($"pathValue before Replace is {pathValue}");
pathValue = pathValue.Replace("Program Files", envProgFilesNoDrive);
Utilities.Logging.APILogger.Log($"pathValue after Replace is {pathValue}");
var sqlcmdFile = Path.Combine(pathValue, "SQLCMD.EXE");
log?.Invoke($"Looking for {sqlcmdFile}");
if (!File.Exists(sqlcmdFile))
{
log?.Invoke($"No file named {sqlcmdFile} exists");
//Try without the "\Client SDK\ODBC\" folders
pathValue = pathValue.Replace("Client SDK\\ODBC\\", "");
sqlcmdFile = Path.Combine(pathValue, "SQLCMD.EXE");
log?.Invoke($"Now looking for {sqlcmdFile}");
if (!File.Exists(sqlcmdFile)) continue;
log?.Invoke($"Found {sqlcmdFile}");
}
if (checkCurrentVersion)
{
foreach (var clientSetupSubFolderName in clientSetupFolderSubkey.GetSubKeyNames())
{
if (clientSetupSubFolderName == "CurrentVersion")
{
//Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\120\Tools\ClientSetup\CurrentVersion
var currentVersionFolderSubkey = clientSetupFolderSubkey.OpenSubKey(clientSetupSubFolderName);
if (currentVersionFolderSubkey == null) continue;
var versionValue = currentVersionFolderSubkey.GetValue("CurrentVersion", -1, RegistryValueOptions.None).ToString();
if (versionValue == "-1") continue;
var majorVersionString = versionValue.Split('.')[0];
if (!Int32.TryParse(majorVersionString, NumberStyles.Integer, CultureInfo.InvariantCulture, out var majorVersionInt)) continue;
if (majorVersionInt > maxProductVersion)
{
maxProductVersion = majorVersionInt;
ODBCToolsPath = pathValue;
}
}
}
}
else
{
if (!Int32.TryParse(productSubKeyName, NumberStyles.Integer, CultureInfo.InvariantCulture, out var productSubKeyNameInt)) continue;
if (productSubKeyNameInt > maxProductSubKeyNameInt)
{
maxProductSubKeyNameInt = productSubKeyNameInt;
ODBCToolsPath = pathValue;
}
}
}
}
}
}
}
}
catch (Exception ex)
{
log?.Invoke(ex.Message);
}
finally
{
log?.Invoke($"ODBCToolsPath is {ODBCToolsPath}");
}
return ODBCToolsPath;
}
/// <summary>
/// installed, and run the command passed in.
/// </summary>
/// Get the path to the latest version of SQL Server Express LocalDB
/// <param name="command"></param>
/// <returns></returns>
private static string ProcessSqlLocalDbCommand(string command, LogDelegate log)
{
//SQL Server Express LocalDB 2014 is a Prerequisite of the DataPRO Installer,
//so it should be there unless it has been subsequently uninstalled.
var localDbPath = GetSqlServerLocalDbPath();
if (localDbPath == string.Empty)
{
//SQL Server LocalDb is not installed so display error and go away
throw new SqlServerLocalDbException(SqlServerLocalDbException.Errors.LocalDbDoesntExist);
}
var sqlLocalDbExeFileName = localDbPath + "SqlLocalDB.exe";
return SqlCommandProcessor(sqlLocalDbExeFileName, command, log);
}
private static string BatchCommandProcessor(string batchFileName,
string dbName,
string sqlDbFileName,
string sqlLogFileName,
string fullSqlcmdPath,
LogDelegate log)
{
var resultString = string.Empty;
lock (PROCESS_LOCK)
{
sb.Clear();
sbError.Clear();
var process = new Process
{
StartInfo =
{
FileName = batchFileName,
Arguments = dbName + " " + "\"" + sqlDbFileName + "\"" + " " + "\"" + sqlLogFileName + "\"" + " " + "\"" + fullSqlcmdPath + "\"",
LoadUserProfile = true,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true
}
};
//* Set ONLY ONE handler here.
process.OutputDataReceived += OutputHandler;
process.ErrorDataReceived += Process_ErrorDataReceived;
//* Start process
process.Start();
//* Read one element asynchronously
process.BeginErrorReadLine();
//* Read the other one synchronously
var output = process.StandardOutput.ReadToEnd();
Console.WriteLine(output);
log?.Invoke($"Result of attach {dbName} using {sqlDbFileName} and {sqlLogFileName} is:");
log?.Invoke(output);
process.WaitForExit();
if (sb.Length > 0)
{
resultString = sb.ToString();
}
if( sbError.Length > 0)
{
resultString += sbError.ToString();
}
}
return resultString;
}
/// <summary>
/// attaches to a given database
/// throws DbNotAttached exception
/// </summary>
/// <param name="targetDir"></param>
/// <param name="dbName"></param>
/// <param name="dbFolder"></param>
/// <param name="scriptsFolder"></param>
/// <param name="attachDBsbat"></param>
/// <param name="log"></param>
public static void AttachDatabase(string targetDir,
string dbName,
string dbFolder,
string scriptsFolder,//StringResources.ScriptsFolder
string attachDBsbat,
LogDelegate log) //StringResources.AttachDBsbat
{
const string SqlCmdExe = "sqlcmd.exe";
var dbFileName = Path.Combine(Environment.CurrentDirectory, dbFolder, dbName) + ".mdf";
var logFileName = Path.Combine(Environment.CurrentDirectory, dbFolder, dbName) + "_log.ldf";
var batchFileName = Path.Combine(targetDir, scriptsFolder, attachDBsbat);
var oDBCToolsPath = DTS.Common.Utils.Database.GetODBCToolsPath(log);
var fullSqlcmdPath = Path.Combine(oDBCToolsPath, SqlCmdExe); //e.g. $"\"C:\\Program Files\\Microsoft SQL Server\\Client SDK\\ODBC\\110\\Tools\\Binn\\sqlcmd.exe\""
var resultString = BatchCommandProcessor(batchFileName, dbName, dbFileName, logFileName, fullSqlcmdPath, log);
if (resultString.Length != 0)
{
throw new SqlServerLocalDbException(SqlServerLocalDbException.Errors.DbNotAttached);
}
}
/// <summary>
/// checks to see if database files exist
/// throws FileNotFound exception
/// </summary>
/// <param name="defaultDbName"></param>
/// <param name="dbFolder"></param>
public static void CheckLocalDatabaseFilesExist(string defaultDbName, string dbFolder)
{
var dbFileNameSource = Path.Combine(Environment.CurrentDirectory, dbFolder, defaultDbName) + ".mdf";
var logFileNameSource = Path.Combine(Environment.CurrentDirectory, dbFolder, defaultDbName) + "_log.ldf";
if (!File.Exists(dbFileNameSource))
{
throw new FileNotFoundException(dbFileNameSource);
}
if (!File.Exists(logFileNameSource))
{
throw new FileNotFoundException(logFileNameSource);
}
}
/// <summary>
/// creates an SQL local db instance
/// throws FailedToCreateInstance exception
/// </summary>
/// <param name="instance"></param>
/// <param name="log"></param>
public static void CreateInstance(string instance, LogDelegate log)
{
var resultString = ProcessSqlLocalDbCommand($"create {instance}", log);
if (resultString.Length != 0)
{
throw new SqlServerLocalDbException(SqlServerLocalDbException.Errors.FailedToCreateInstance);
}
}
/// <summary>
/// stops a given sql local db instance
/// throws LocalDbDoesntExist exception
/// </summary>
/// <param name="instance"></param>
/// <param name="log"></param>
public static void StopInstance(string instance, LogDelegate log)
{
var resultString = ProcessSqlLocalDbCommand($"stop {instance}", log);
if (resultString.Length != 0)
{
throw new SqlServerLocalDbException(SqlServerLocalDbException.Errors.FailedToStopInstance, resultString);
}
}
/// <summary>
/// deletes a given sql local db instance
/// throws FailedToDeleteInstance exception
/// </summary>
/// <param name="instance"></param>
/// <param name="log"></param>
public static void DeleteInstance(string instance, LogDelegate log)
{
var resultString = ProcessSqlLocalDbCommand($"delete {instance}", log);
if (resultString.Length != 0)
{
throw new SqlServerLocalDbException(SqlServerLocalDbException.Errors.FailedToDeleteInstance);
}
}
/// <summary>
/// starts a local db instance
/// throws FailedToStartInstance exception
/// </summary>
/// <param name="instance"></param>
/// <param name="log"></param>
public static void StartInstance(string instance, LogDelegate log)
{
var resultString = ProcessSqlLocalDbCommand($"start {instance}", log);
if (resultString.Length != 0)
{
throw new SqlServerLocalDbException(SqlServerLocalDbException.Errors.FailedToStartInstance);
}
}
#endregion methods
public class SqlServerLocalDbException
: Exception
{
public Errors Error { get; }
public SqlServerLocalDbException(Errors error)
{
Error = error;
}
public SqlServerLocalDbException(Errors error, string msg)
:base(msg)
{
Error = error;
}
public enum Errors
{
FailedToStopInstance,
LocalDbDoesntExist,//sql local db doesn't exist
FailedToDeleteInstance,
FailedToCreateInstance,
FailedToStartInstance,
DbNotAttached,
IsodbNotAttached,
DASFactoryDBNotAttached,
}
}
}
}

View File

@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;
namespace DTS.Common.Utils
{
public static class EnumUtil
{
public static IEnumerable<T> GetValues<T>()
{
return Enum.GetValues(typeof(T)).Cast<T>();
}
public static ItemCollection GetValuesList<T>()
{
var list = new ItemCollection();
foreach (var filter in Enum.GetValues(typeof(T)))
{
list.Add(filter, Enum.GetName(typeof(T), filter));
}
return list;
}
}
}

View File

@@ -0,0 +1,460 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
// ReSharper disable UnusedMember.Local
// ReSharper disable UnusedVariable
namespace DTS.Common.Utils
{
[SuppressMessage("ReSharper", "InconsistentNaming")]
public static class FileUtils
{
[Flags]
enum MoveFileFlags
{
MOVEFILE_REPLACE_EXISTING = 0x00000001,
MOVEFILE_COPY_ALLOWED = 0x00000002,
MOVEFILE_DELAY_UNTIL_REBOOT = 0x00000004,
MOVEFILE_WRITE_THROUGH = 0x00000008,
MOVEFILE_CREATE_HARDLINK = 0x00000010,
MOVEFILE_FAIL_IF_NOT_TRACKABLE = 0x00000020
}
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern bool MoveFileEx(string lpExistingFileName, string lpNewFileName, MoveFileFlags dwFlags);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool DeleteFile(string lpFileName);
public const double DataPROPre20XmlVersion = 2.0D;
public const double DataPRO20XmlVersion = 3.0D;
public const double DataPRO21XmlVersion = 4.0D;
public const double DataPRO22XmlVersion = 5.0D;
//FB 13120 Added FilterClass and deleted CFC in version 6.0
//15390 Store the ZMO in EU in the DataPRO DB in version 7.0, adds top level field SensorChangeHistory
//13065 Sensor "First Use" Date, version 8, adds LatestCalibrationId, CalibrationId and FirstUse tags
//15727Building and Replacing Racks/Mods - version 9, adds TestId,GroupId, StandIn to hardware and Id to tests
//Version 10 adds TSR Air settings
public const double CurrentXmlVersion = 10.0D;
public static XmlElement GetImportXmlNode(string filename, string xmlDoc, out double importVersion)
{
var doc = new XmlDocument();
importVersion = 0D;
if (!string.IsNullOrWhiteSpace(xmlDoc))
{
doc.LoadXml(xmlDoc);
}
else
{
doc.Load(filename);
}
foreach (var node in doc.ChildNodes)
{
if (node is XmlDeclaration) continue;
if (!(node is XmlElement)) continue;
if ((node as XmlElement).Name != "ExportFile") continue;
var itemsToComplete = Convert.ToDouble(((XmlElement) node).GetAttribute("TotalItems"),
System.Globalization.CultureInfo.InvariantCulture);
importVersion = Convert.ToDouble(((XmlElement) node).GetAttribute("Version"),
System.Globalization.CultureInfo.InvariantCulture);
if (importVersion > CurrentXmlVersion)
{
throw new NotSupportedException("Unsupported version: " + importVersion);
}
return (XmlElement) node;
}
throw new Exception("Invalid Import XML");
}
/// <summary>
/// This API can accept a parameter “MOVEFILE_DELAY_UNTIL_REBOOT ” to remove the file only after reboot.
/// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365240(v=vs.85).aspx
/// </summary>
/// <param name="filePath">Entire path of the file we want to move</param>
public static void moveFileEx(string filePath)
{
MoveFileEx(filePath, null, MoveFileFlags.MOVEFILE_DELAY_UNTIL_REBOOT);
}
/// <summary>
/// FB16400: ISO exports error in 3rd Party Software, related to FB15801
/// By default, C# returns UTF-8 Encoding with Byte-Order Mark turned on. Need to explicitly call the overloaded constructor to turn it off:
/// https://docs.microsoft.com/en-us/dotnet/api/system.text.encoding.utf8?view=netcore-3.1
/// </summary>
/// <param name="codepage"></param>
/// <returns></returns>
public static Encoding GetEncoding(int codepage)
{
var encoding = Encoding.GetEncoding(codepage);
if (encoding is UTF8Encoding) { encoding = new UTF8Encoding(false); }
return encoding;
}
public static XmlWriter GetExportWriter(int count, double version, string software, string softwareVersion, LogDelegate logDelegate, out StringBuilder sb)
{
sb = new StringBuilder(5000000);
var xSet = new XmlWriterSettings { Indent = true, CheckCharacters = true };
var writer = XmlWriter.Create(sb, xSet);
writer.WriteStartDocument();
writer.WriteStartElement("ExportFile");
writer.WriteAttributeString("TotalItems", count.ToString(System.Globalization.CultureInfo.InvariantCulture));
writer.WriteAttributeString("Version", version.ToString(System.Globalization.CultureInfo.InvariantCulture));
writer.WriteAttributeString("Software", software);
writer.WriteAttributeString("SoftwareVersion", softwareVersion);
return writer;
}
/// <summary>
/// This API deletes an existing file.
/// https://msdn.microsoft.com/en-us/library/windows/desktop/aa363915(v=vs.85).aspx
/// </summary>
/// <param name="filePath">Entire path of the file to be deleted.</param>
/// <param name="lastError">Error if failed to delete file
/// If the function succeeds, the return value is nonzero.
/// If the function fails, the return value is zero (0).
/// To get extended error information, call GetLastError.</param>
public static bool deleteFile(string filePath, ref int lastError)
{
if (!File.Exists(filePath)) return true;
var deleted = DeleteFile(filePath);
if (!deleted)
{
lastError = Marshal.GetLastWin32Error();
}
return deleted;
}
public delegate void LogDelegate(params object[] paramlist);
public static void DeleteFileOrMove(string filepath, LogDelegate logfunction)
{
var lastError = 0;
if (!deleteFile(filepath, ref lastError))
{
logfunction("failed to delete file: ", filepath, " error: ", lastError);
moveFileEx(filepath);
}
}
/// <summary>
/// One time sql script conversion from SQLite syntax to MS SQL Server
/// all you need to know about Regex is here - http://rubular.com/r/kcqDdLJBpx
/// </summary>
/// <param name="script">SQLite script file path</param>
/// <returns></returns>
public static string ScriptFromSQLiteToSQL(string script)
{
if (string.IsNullOrEmpty(script)) return script;
const string BLOB = "BLOB";
const string NVCHAR = "NVCHAR";
const string NVARCHAR = "NVARCHAR";
const string COLLATE_NOCASE = "COLLATE NOCASE";
const string Couldnt = "Couldn't";
const string Couldnot = "Could not";
const string IDENTITY = "IDENTITY(1,1)";
const string AUTOINCREMENT = "AUTOINCREMENT";
const string varbinary_max = "varbinary(max)";
const string varchar_max = "varchar(max)";
const string max = "(max)";
const string BigInt = "bigint";
const string Integer = "integer";
const string Int = "int";
const string _2048 = "(2048)";
const string _5000 = "(5000)";
var sql = new StringBuilder();
long count = 0;
using (var inputStream = File.OpenRead(script))
{
using (var inputReader = new StreamReader(inputStream))
{
string tempLineValue;
while (null != (tempLineValue = inputReader.ReadLine()))
{
Debug.Print("Line: " + count++);
var addGo = false;
const string dropTable = "DROP TABLE IF EXISTS";
const string dropTableNew = @"if exists(SELECT * FROM sysobjects where name = '{0}') DROP TABLE [dbo].[{0}];" ;
const string insertInto = "INSERT INTO";
const string convert = "convert(varbinary(max), {0})";
if (tempLineValue.StartsWith(dropTable))
{
var tableName = tempLineValue.Replace(dropTable, string.Empty).Replace("\"", string.Empty).Replace(";", string.Empty).Trim();
tempLineValue = string.Format(dropTableNew, tableName);
addGo = true;
}
else if (tempLineValue.StartsWith(insertInto))
{
//find SQLite (or C#) date (Format: '2016-06-06 15:15:38.0630927' )
const string patternSQLiteDate = @"(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2}).\d{3,7}";
var regexSQLiteDate = new Regex(patternSQLiteDate, RegexOptions.IgnoreCase);
var matchSQLiteDate = regexSQLiteDate.Match(tempLineValue);
while (matchSQLiteDate.Success)
{
var foundSQLiteDate = matchSQLiteDate.Value;
//find SQL date in SQLite date (Format: '2016-06-06 15:15:38.063' )
const string patternSQLDate = @"(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})";
var rDate = new Regex(patternSQLDate, RegexOptions.IgnoreCase);
var mDate = rDate.Match(foundSQLiteDate);
if (mDate.Success)
{
//replace date
tempLineValue = tempLineValue.Replace(foundSQLiteDate, mDate.Value);
}
matchSQLiteDate = matchSQLiteDate.NextMatch();
}
const string patternBinary = @"[X]'\w+'";
var regexBinary = new Regex(patternBinary, RegexOptions.IgnoreCase);
var matchBinary = regexBinary.Match(tempLineValue);
while (matchBinary.Success)
{
var matchValue = matchBinary.Value;
var newValue = string.Format(convert, matchValue.Replace("X", string.Empty).Replace(",", string.Empty));
tempLineValue = tempLineValue.Replace(matchValue, newValue);
matchBinary = matchBinary.NextMatch();
}
addGo = true;
}
else
{
const string patternText = @"text\s{0,}\(\w+\)";
var regexText = new Regex(patternText, RegexOptions.IgnoreCase);
var matchText = regexText.Match(tempLineValue);
if (matchText.Success)
{
tempLineValue = tempLineValue.Replace(matchText.Value, varchar_max);
}
const string patternMaxMemory = @"\[MaxMemory\]\sinteger,";
var regexMaxMemory = new Regex(patternMaxMemory, RegexOptions.IgnoreCase);
var matchMaxMemory = regexMaxMemory.Match(tempLineValue);
if (matchMaxMemory.Success)
{
tempLineValue = tempLineValue.Replace(Integer, BigInt);
}
}
sql.AppendLine(tempLineValue);
if (addGo) sql.AppendLine("GO");
}
}
}
return sql
.Replace(AUTOINCREMENT, string.Empty)
.Replace(Couldnt, Couldnot)
.Replace(_2048, max)
.Replace(_5000, max)
.Replace(Integer, Int)
.Replace(COLLATE_NOCASE, string.Empty)
.Replace(BLOB, varbinary_max)
.Replace(BLOB.ToLower(), varbinary_max)
.Replace(NVCHAR, NVARCHAR)
.Replace(NVCHAR.ToLower(), NVARCHAR).ToString();
}
#region File List
private static List<string> _fileList = new List<string>();
public static List<string> FileList { get => _fileList; set => _fileList = value; }
private static List<string> _newFileList = new List<string>();
public static List<string> NewFileList { get => _newFileList; set => _newFileList = value; }
/// <summary>
/// recursively search directory and subdirectories and return a list of files
/// </summary>
/// <param name="path"></param>
/// <param name="pattern">*.dts</param>
/// <returns></returns>
public static void FindFiles(string path, string pattern)
{
_fileList.AddRange(FindFiles(path, pattern, SearchOption.AllDirectories));
}
/// <summary>
/// Search directory and return a list of files
/// </summary>
/// <param name="path"></param>
/// <param name="pattern">*.dts</param>
/// <returns></returns>
public static List<string> FindFilesInDirectory(string path, string pattern)
{
return FindFiles(path, pattern, SearchOption.TopDirectoryOnly);
}
private static List<string> FindFiles(string path, string pattern, SearchOption searchOption)
{
return Directory.GetFiles(path, "*" + pattern, searchOption).
Select(fn => new FileInfo(fn)).
OrderBy(f => Regex.Replace(f.Name, @"\d+", n => n.Value.PadLeft(4, '0'))).
Select(f => f.FullName).
ToList();
}
#endregion File List
[StructLayout(LayoutKind.Sequential)]
private struct RM_UNIQUE_PROCESS
{
public int dwProcessId;
public System.Runtime.InteropServices.ComTypes.FILETIME ProcessStartTime;
}
private const int RmRebootReasonNone = 0;
private const int CCH_RM_MAX_APP_NAME = 255;
private const int CCH_RM_MAX_SVC_NAME = 63;
private enum RM_APP_TYPE
{
RmUnknownApp = 0,
RmMainWindow = 1,
RmOtherWindow = 2,
RmService = 3,
RmExplorer = 4,
RmConsole = 5,
RmCritical = 1000
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct RM_PROCESS_INFO
{
public RM_UNIQUE_PROCESS Process;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_APP_NAME + 1)]
public string strAppName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_SVC_NAME + 1)]
public string strServiceShortName;
public RM_APP_TYPE ApplicationType;
public uint AppStatus;
public uint TSSessionId;
[MarshalAs(UnmanagedType.Bool)]
public bool bRestartable;
}
[DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)]
private static extern int RmRegisterResources(uint pSessionHandle,
UInt32 nFiles,
string[] rgsFilenames,
UInt32 nApplications,
[In] RM_UNIQUE_PROCESS[] rgApplications,
UInt32 nServices,
string[] rgsServiceNames);
[DllImport("rstrtmgr.dll", CharSet = CharSet.Auto)]
private static extern int RmStartSession(out uint pSessionHandle, int dwSessionFlags, string strSessionKey);
[DllImport("rstrtmgr.dll")]
private static extern int RmEndSession(uint pSessionHandle);
[DllImport("rstrtmgr.dll")]
private static extern int RmGetList(uint dwSessionHandle,
out uint pnProcInfoNeeded,
ref uint pnProcInfo,
[In, Out] RM_PROCESS_INFO[] rgAffectedApps,
ref uint lpdwRebootReasons);
/// <summary>
/// Find out what process(es) have a lock on the specified file.
/// </summary>
/// <param name="path">Path of the file.</param>
/// <returns>Processes locking the file</returns>
/// <remarks>See also:
/// http://msdn.microsoft.com/en-us/library/windows/desktop/aa373661(v=vs.85).aspx
/// http://wyupdate.googlecode.com/svn-history/r401/trunk/frmFilesInUse.cs (no copyright in code at time of viewing)
///
/// </remarks>
public static List<Process> WhoIsLocking(string path)
{
uint handle;
string key = Guid.NewGuid().ToString();
List<Process> processes = new List<Process>();
int res = RmStartSession(out handle, 0, key);
if (res != 0) throw new Exception("Could not begin restart session. Unable to determine file locker.");
try
{
const int ERROR_MORE_DATA = 234;
uint pnProcInfoNeeded = 0,
pnProcInfo = 0,
lpdwRebootReasons = RmRebootReasonNone;
string[] resources = new string[] { path }; // Just checking on one resource.
res = RmRegisterResources(handle, (uint)resources.Length, resources, 0, null, 0, null);
if (res != 0) throw new Exception("Could not register resource.");
//Note: there's a race condition here -- the first call to RmGetList() returns
// the total number of process. However, when we call RmGetList() again to get
// the actual processes this number may have increased.
res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, null, ref lpdwRebootReasons);
if (res == ERROR_MORE_DATA)
{
// Create an array to store the process results
RM_PROCESS_INFO[] processInfo = new RM_PROCESS_INFO[pnProcInfoNeeded];
pnProcInfo = pnProcInfoNeeded;
// Get the list
res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, processInfo, ref lpdwRebootReasons);
if (res == 0)
{
processes = new List<Process>((int)pnProcInfo);
// Enumerate all of the results and add them to the
// list to be returned
for (int i = 0; i < pnProcInfo; i++)
{
try
{
processes.Add(Process.GetProcessById(processInfo[i].Process.dwProcessId));
}
// catch the error -- in case the process is no longer running
catch (ArgumentException) { }
}
}
else throw new Exception("Could not list processes locking resource.");
}
else if (res != 0) throw new Exception("Could not list processes locking resource. Failed to get size of result.");
}
finally
{
RmEndSession(handle);
}
return processes;
}
}
}

View File

@@ -0,0 +1,52 @@
using System.Windows.Controls;
using System.Windows.Media;
namespace DTS.Common.Utils
{
public class ImageButton : Button
{
private readonly Image _image;
private readonly TextBlock _textBlock;
public ImageButton()
{
var panel = new StackPanel
{
Orientation = Orientation.Vertical,
Margin = new System.Windows.Thickness(10)
};
_image = new Image {Margin = new System.Windows.Thickness(0, 0, 0, 0), Stretch = Stretch.Fill};
panel.Children.Add(_image);
_textBlock = new TextBlock();
panel.Children.Add(_textBlock);
Content = panel;
}
private ImageSource _source;
public ImageSource Source
{
get => _source;
set
{
_source = value;
_image.Source = _source;
}
}
private string _imageText;
public string ImageText
{
get => _imageText;
set
{
_imageText = value;
_textBlock.Text = _imageText;
}
}
}
}

View File

@@ -0,0 +1,50 @@
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Media;
namespace DTS.Common.Utils
{
public class MouseUtilities
{
[StructLayout(LayoutKind.Sequential)]
private struct Win32Point
{
public int X;
public int Y;
};
[DllImport("user32.dll")]
private static extern bool GetCursorPos(ref Win32Point pt);
[DllImport("user32.dll")]
private static extern bool GetPhysicalCursorPos(ref Win32Point pt);
[DllImport("user32.dll")]
private static extern bool ScreenToClient(IntPtr hwnd, ref Win32Point pt);
public static Point GetMousePosition(Visual relativeTo)
{
var mouse = new Win32Point();
GetCursorPos(ref mouse);
var presentationSource =
(System.Windows.Interop.HwndSource)PresentationSource.FromVisual(relativeTo);
var factor = 2D - presentationSource.CompositionTarget.TransformToDevice.M22;
ScreenToClient(presentationSource.Handle, ref mouse);
var transform = relativeTo.TransformToAncestor(presentationSource.RootVisual);
var offset = transform.Transform(new Point(0, 0));
if( factor > 0 )
{
offset.Y = offset.Y + offset.Y*(presentationSource.CompositionTarget.TransformToDevice.M22 - 1);
}
return new Point(mouse.X - offset.X, mouse.Y - offset.Y);
}
}
}

View File

@@ -0,0 +1,233 @@
using DTS.Common.Utilities.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace DTS.Common.Utils
{
//FB 18512 This calss contains utility methods, they are moved from Netwroking module
public static class NetworkUtils
{
/// <summary>
/// returns true if the network interface is up, false otherwise
/// stolen from FWTU
/// </summary>
/// <param name="ip"></param>
/// <returns></returns>
public static bool IsNetworkInterfaceUp(IPAddress ip)
{
foreach (var a in NetworkInterface.GetAllNetworkInterfaces())
{
try
{
var ipp = a.GetIPProperties();
if (ipp.MulticastAddresses.Count == 0)
continue; // most of VPN adapters will be skipped
if (!a.SupportsMulticast)
continue; // multicast is meaningless for this type of connection
if (OperationalStatus.Up != a.OperationalStatus)
continue; // this adapter is off or not connected
if (null == ipp.GetIPv4Properties())
continue; // IPv4 is not configured on this adapter
if (a.NetworkInterfaceType == NetworkInterfaceType.Loopback)
continue;
var addressBytes = ip.GetAddressBytes();
if (4 == addressBytes.Length && addressBytes[0] == 169 && addressBytes[1] == 254) { continue; }
if (a.GetIPProperties().UnicastAddresses.Any(i => i.Address.AddressFamily == AddressFamily.InterNetwork && IPAddress.Equals(i.Address, ip)))
{
return true;
}
}
catch (Exception)
{
continue;
}
}
return false;
}
/// <summary>
/// Indicates whether any network connection is available.
/// Filter connections below a specified speed, as well as virtual network cards.
/// </summary>
/// <param name="minimumSpeed">The minimum speed required. Passing 0 will not filter connection using speed.</param>
/// <returns>
/// <c>true</c> if a network connection is available; otherwise, <c>false</c>.
/// </returns>
private static List<NetworkInterface> GetAvailableNetworkInterfaces(long minimumSpeed = 10000000)
{
if (!NetworkInterface.GetIsNetworkAvailable())
return null;
var niList = new List<NetworkInterface>();
foreach (var ni in NetworkInterface.GetAllNetworkInterfaces())
{
// discard because of standard reasons
if ((ni.NetworkInterfaceType == NetworkInterfaceType.Loopback)
|| (ni.NetworkInterfaceType == NetworkInterfaceType.Tunnel))
continue;
// this allow to filter modems, serial, etc.
// use 10000000 as a minimum speed for most cases
if (ni.Speed < minimumSpeed)
continue;
// discard virtual cards (virtual box, virtual pc, etc.)
if (ni.Description.ToLower().Contains("virtual")
|| ni.Name.ToLower().Contains("virtual")
|| ni.Description.ToLower().Contains("bluethooth")
|| ni.Name.ToLower().Contains("bluethooth"))
continue;
// discard "Microsoft Loopback Adapter", it will not show as NetworkInterfaceType.Loopback but as Ethernet Card.
if (ni.Description.Equals("Microsoft Loopback Adapter", StringComparison.OrdinalIgnoreCase))
continue;
niList.Add(ni);
}
return niList;
}
private static string GetMACAddress(string id)
{
var macAddress = string.Empty;
var nics = GetAvailableNetworkInterfaces();
if (nics == null)
return macAddress;
foreach (var nic in nics)
{
if (nic.Id != id) continue;
var pa = BitConverter.ToString(nic.GetPhysicalAddress().GetAddressBytes());
pa = pa.Replace("-", ":");
if (string.IsNullOrEmpty(pa)) continue;
macAddress = pa;
break;
}
return macAddress;
}
private static bool InvalidNIC(NetworkInterface ni)
{
return OperationalStatus.Up != ni.OperationalStatus || !ni.SupportsMulticast;
}
public static List<HostInfo> GetAvailableHosts(bool supportMulticastOnly = false)
{
var nics = GetAvailableNetworkInterfaces();
if (nics == null) { return new List<HostInfo>(); }
var hosts = new List<HostInfo>();
foreach (NetworkInterface ni in nics)
{
if (supportMulticastOnly && InvalidNIC(ni))
{
continue;
}
//exclude wireshark network interface, it claims it can ping ips lyingly
if (ni.Description.Contains("Npcap")) { continue; }
foreach (UnicastIPAddressInformation ip in ni.GetIPProperties().UnicastAddresses)
{
var addressBytes = ip.Address.GetAddressBytes();
if (4 == addressBytes.Length && addressBytes[0] == 169 && addressBytes[1] == 254)
{
APILogger.Log($"skipping {ni.Name}\\{ni.Description} as it has a 169.254 address");
continue;//internal only
}
if (ip.Address.AddressFamily == AddressFamily.InterNetwork)
{
GetStartAndEndAddress(ip.Address, ip.IPv4Mask, out var startAddress, out var endAddress);
hosts.Add(new HostInfo { HostIpAddress = ip.Address.ToString(), HostNetworkId = ni.Id, HostMacAddress = GetMACAddress(ni.Id),
StartAddress = startAddress, EndAddress = endAddress
});
}
}
}
return hosts;
}
/// <summary>
/// given an IP address and a mask returns a ip address as a string for the first address that is accessible and a string for the last address that is accessible
/// anything between the start address and end address after doing a byte-by-byte comparison should be addressable
/// will return empty strings if something bad happens. Is designed for 4 byte addresses but isn't necessarily limited.
/// </summary>
/// <param name="ip"></param>
/// <param name="mask"></param>
/// <param name="startAddress"></param>
/// <param name="endAddress"></param>
private static void GetStartAndEndAddress( IPAddress ip, IPAddress mask, out string startAddress, out string endAddress)
{
try
{
var ipByte = ip.GetAddressBytes();
var maskBytes = mask.GetAddressBytes();
var startBytes = new byte[ipByte.Length];
var endBytes = new byte[ipByte.Length];
for (var i = 0; i < ipByte.Length; i++)
{
startBytes[i] = (byte)(ipByte[i] & maskBytes[i]);
endBytes[i] = (byte)(ipByte[i] | ~maskBytes[i]);
}
var startIP = new IPAddress(startBytes);
var endIP = new IPAddress(endBytes);
startAddress = startIP.ToString();
endAddress = endIP.ToString();
}
catch( Exception ex)
{
startAddress = string.Empty;
endAddress = string.Empty;
APILogger.Log(ex);
}
}
//FB 25658 Changed the method signature to return bool to determine the connection string has IP in it and it's not USB
/// <summary>
/// Try to parse connectionString and retrieve the IP address.
/// </summary>
/// <param name="connectionString"> DAS connectionString which might include the IP address or might be a USB connection string</param>
/// <param name="ipAddress">Return the parsed IP address from connectionString</param>
/// <returns>Returns true if was able to parse and get the IP otherwise false including the valid USB connection string </returns>
public static bool TryParseConnectionString(string connectionString, out string ipAddress)
{
ipAddress = null;
if (string.IsNullOrEmpty(connectionString))
{
return false;
}
try
{
var uri = new Uri(connectionString);
if (IPAddress.TryParse(uri.Host, out _))
{
ipAddress = uri.Host;
return true;
}
}
catch
{
var ip = connectionString.Split(':')[0];
if (IPAddress.TryParse(ip, out _))
{
ipAddress = ip;
return true;
}
}
return false;
}
}
}

View File

@@ -0,0 +1,38 @@
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace DTS.Common.Utils
{
/// <summary>
/// this class holds PNG image functions
/// </summary>
public static class PNGImageUtil
{
/// <summary>
/// renders the given element as a png to the given filepath
/// </summary>
/// <param name="view"></param>
/// <param name="fileName"></param>
public static void SaveImage(FrameworkElement view, string fileName)
{
var size = new Size(view.ActualWidth, view.ActualHeight);
var rtb = new RenderTargetBitmap((int) size.Width, (int) size.Height, 96, 96, PixelFormats.Pbgra32);
var visual = new DrawingVisual();
using (var context = visual.RenderOpen())
{
context.DrawRectangle(new VisualBrush(view), null, new Rect(new Point(), size));
context.Close();
}
rtb.Render(visual);
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(rtb));
using (var stream = new System.IO.FileStream(fileName, System.IO.FileMode.CreateNew))
{
encoder.Save(stream);
}
}
}
}

View File

@@ -0,0 +1,853 @@
using DTS.Common.Interface.StatusAndProgressBar;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using DTS.Common.Utilities.Logging;
namespace DTS.Common.Utils
{
public class PingUtils
{
public static void EliminateBadHosts(ref List<HostInfo> list, string[] ips)
{
var hosts = list.ToArray();
foreach (var host in hosts)
{
if (!HostMatchesAnyIPs(host, ips))
{
APILogger.Log($"Removing host {host.HostIpAddress} as it can't reach any ips");
list.Remove(host);
}
}
}
/// <summary>
/// returns false if the host can not access any ips provided after looking at it's ip address and netmask
/// returns true if it can reach an ip or it can't be determined if it can reach an IP this might happen if someone used
/// a host name instead of a xxx.yyy.zzz.aaa style address or an empty string somewhere
/// </summary>
/// <param name="host"></param>
/// <param name="ips"></param>
/// <returns></returns>
protected static bool HostMatchesAnyIPs(HostInfo host, string[] ips)
{
if (ips.Length == 0) { return true; }
if (string.Empty.Equals(host.StartAddress) || string.Empty.Equals(host.EndAddress)) { return true; }
foreach (var ip in ips)
{
try
{
if (!IPAddress.TryParse(ip, out var ipToConnectTo))
{
//it's not a traditional format ip, give up trying to match it
return true;
}
if (!IPAddress.TryParse(host.StartAddress, out var startAddress))
{
//start address is not a traditional format, give up trying to match
return true;
}
if (!IPAddress.TryParse(host.EndAddress, out var endAddress))
{
//start address is not in traditional format, give up trying to match
return true;
}
var startBytes = startAddress.GetAddressBytes();
var endBytes = endAddress.GetAddressBytes();
var ipByte = ipToConnectTo.GetAddressBytes();
for (var i = 0; i < startBytes.Length && i < endBytes.Length && i < ipByte.Length; i++)
{
if (ipByte[i] < startBytes[i] || ipByte[i] > endBytes[i]) { return false; }
}
return true;
}
catch (Exception ex)
{
APILogger.Log(ex);
}
}
return false;
}
//FB 18152 & 25642 This dictionary keeps the host info for each DAS
public static ConcurrentDictionary<string, HostInfo> DasToHost { get; set; } = new ConcurrentDictionary<string, HostInfo>();
public class PingDevice
{
private const int MAX_PING_ATTEMPTS = 3;
private const string ALPHABET = "abcdefghijklmnopqrstuvwxyz";
#region Ping
public bool PingDevices(List<string> ipList)
{
//FB 18152 Get the avilable hosts
var hostInfos = NetworkUtils.GetAvailableHosts();
EliminateBadHosts(ref hostInfos, ipList?.ToArray());
return PingAllDevices(hostInfos, ipList);
}
#region Ping All Devices
/// <summary>
/// ICMP ping
/// </summary>
/// <param name="hostIpAddress">host IP address</param>
/// <param name="ipList">List of connected devices IP addresses </param>
/// <returns>false - if as least one device disconnected</returns>
private bool PingAllDevices(List<HostInfo> hostInfos, List<string> ipList)
{
var hostStringList = new List<string>();
foreach (var host in hostInfos)
{
hostStringList.Add(host.HostIpAddress);
}
APILogger.Log($"trying to ping {string.Join(", ", ipList.ToArray())} using hosts: {string.Join(", ", hostStringList.ToArray())}");
var allConnected = true;
Parallel.ForEach(ipList, (item, state) =>
{
var lastHost = hostInfos.Last();
//FB 18152 & 25642 Perform the ping on all the devices in all hosts
foreach (var hostInfo in hostInfos)
{
var eachConnected = PingOneDevice(item, hostInfo.HostIpAddress);
if (eachConnected)
{
//We found the host update the dictionary and break from the loop
DasToHost[item] = hostInfo;
allConnected = eachConnected;
APILogger.Log($"pinging {string.Join(", ", ipList.ToArray())} worked for {hostInfo.HostIpAddress}");
break;
}
if (hostInfo.Equals(lastHost))
{
//all the hosts are processed
allConnected = eachConnected;
}
}
if (!allConnected) state.Break();
});
if (allConnected) { APILogger.Log("An interface could ping"); }
else { APILogger.Log("no intefaces could ping"); }
return allConnected;
}
/// <summary>
/// TCP ping
/// </summary>
/// <param name="ipList">List of connected devices IP addresses </param>
/// <returns>false - if as least one device disconnected</returns>
private bool PingAllDevices(List<string> ipList)
{
var allConnected = true;
Parallel.ForEach(ipList, (item, state) =>
{
allConnected = PingOneDevice(item);
if (!allConnected) state.Break();
});
return allConnected;
}
#endregion Ping All Devices
#region Ping One Device
/// <summary>
/// TCP ping
/// </summary>
/// <param name="deviceIpAddress">device IP addresses </param>
/// <returns>false - if device disconnected</returns>
private bool PingOneDevice(string deviceIpAddress)
{
for (var i = 0; i < MAX_PING_ATTEMPTS; i++)
{
var pingSender = new Ping();
var reply = pingSender.Send(deviceIpAddress);
if (reply != null && reply.Status == IPStatus.Success) return true;
}
return false;
}
/// <summary>
/// ICMP ping
/// </summary>
/// <param name="deviceIp">device IP addresses </param>
/// <param name="hostIpAddress">host IP address</param>
/// <returns>false - if device disconnected</returns>
private bool PingOneDevice(string deviceIp, string hostIpAddress)
{
for (var i = 0; i < MAX_PING_ATTEMPTS; i++)
{
if (PingICMP(IPAddress.Parse(hostIpAddress), IPAddress.Parse(deviceIp), Constants.PING_ICMP_TIMEOUT, Encoding.ASCII.GetBytes(ALPHABET)).Status == IPStatus.Success)
return true;
}
return false;
}
#endregion Ping One Device
#region ICMP Ping
/// <summary>
/// ICMP ping
/// </summary>
/// <param name="srcAddress">host address</param>
/// <param name="destAddress">device address</param>
/// <param name="timeout">timeout</param>
/// <param name="buffer">Function will fail without buffer (used alphabet)</param>
/// <param name="po">Ping options - not been usexd</param>
/// <returns>PingReplyUtils class</returns>
private static PingReplyUtils PingICMP(IPAddress srcAddress, IPAddress destAddress, int timeout = 5000, byte[] buffer = null, PingOptions po = null)
{
var ipSrc = string.Empty;
var ipDest = string.Empty;
try
{
var ipSrcBytes = srcAddress?.GetAddressBytes() ?? new byte[0];
if (4 <= ipSrcBytes.Length) { ipSrc = $"{ipSrcBytes[0]}.{ipSrcBytes[1]}.{ipSrcBytes[2]}.{ipSrcBytes[3]}"; }
var destBytes = destAddress?.GetAddressBytes() ?? new byte[0];
if (4 <= destBytes.Length) { ipDest = $"{destBytes[0]}.{destBytes[1]}.{destBytes[2]}.{destBytes[3]}"; }
}
catch (Exception ex) { APILogger.Log(ex); }
if (destAddress == null || destAddress.AddressFamily != AddressFamily.InterNetwork ||
destAddress.Equals(IPAddress.Any))
{
APILogger.Log($"Ping -S {ipSrc} {ipDest} failed - Argument exception");
throw new ArgumentException(string.Format("PingAll failed on IP:{0}", srcAddress));
}
//Defining pinvoke args
var source = srcAddress == null ? 0 : BitConverter.ToUInt32(srcAddress.GetAddressBytes(), 0);
var destination = BitConverter.ToUInt32(destAddress.GetAddressBytes(), 0);
var sendbuffer = buffer ?? new byte[] { };
var options = new Interop.Option
{
Ttl = (po == null ? (byte)255 : (byte)po.Ttl),
Flags = (po == null ? (byte)0 : po.DontFragment ? (byte)0x02 : (byte)0) //0x02
};
var fullReplyBufferSize = Interop.ReplyMarshalLength + sendbuffer.Length;
//Size of Reply struct and the transmitted buffer length.
var allocSpace = Marshal.AllocHGlobal(fullReplyBufferSize);
// unmanaged allocation of reply size. TODO Maybe should be allocated on stack
try
{
var start = DateTime.Now;
var nativeCode = Interop.IcmpSendEcho2Ex(
/* _In_ HANDLE IcmpHandle, */
Interop.IcmpHandle,
/* _In_opt_ HANDLE Event, */
default(IntPtr),
/* _In_opt_ PIO_APC_ROUTINE ApcRoutine, */
default(IntPtr),
/* _In_opt_ PVOID ApcContext */
default(IntPtr),
/* _In_ IPAddr SourceAddress, */
source,
/* _In_ IPAddr DestinationAddress, */
destination,
/* _In_ LPVOID RequestData, */
sendbuffer,
/* _In_ WORD RequestSize, */
(short)sendbuffer.Length,
/* _In_opt_ PIP_OPTION_INFORMATION RequestOptions, */
ref options,
/* _Out_ LPVOID ReplyBuffer, */
allocSpace,
/* _In_ DWORD ReplySize, */
fullReplyBufferSize,
/* _In_ DWORD Timeout */
timeout);
var duration = DateTime.Now - start;
var reply = (Interop.Reply)Marshal.PtrToStructure(allocSpace, typeof(Interop.Reply));
// Parse the beginning of reply memory to reply struct
byte[] replyBuffer = null;
if (sendbuffer.Length != 0)
{
replyBuffer = new byte[sendbuffer.Length];
Marshal.Copy(allocSpace + Interop.ReplyMarshalLength, replyBuffer, 0, sendbuffer.Length);
//copy the rest of the reply memory to managed byte[]
}
var errorCode = 0;
if (0 != nativeCode)
{
errorCode = Marshal.GetLastWin32Error();
}
APILogger.Log($"Ping -S {ipSrc} {ipDest} result: {reply.Status} LastError: {errorCode}");
return nativeCode == 0
? new PingReplyUtils(nativeCode, reply.Status, new IPAddress(reply.Address), duration)
: new PingReplyUtils(nativeCode, reply.Status, new IPAddress(reply.Address), reply.RoundTripTime,
replyBuffer);
}
finally { Marshal.FreeHGlobal(allocSpace); /*free allocated space*/ }
}
#endregion ICMP Ping
#region Support ICMP Ping
/// <summary>Interoperability Helper
/// <see cref="http://msdn.microsoft.com/en-us/library/windows/desktop/bb309069(v=vs.85).aspx" />
/// </summary>
private static class Interop
{
private static IntPtr? _icmpHandle;
private static int? _replyStructLength;
/// <summary>Returns the application legal icmp handle. Should be close by IcmpCloseHandle
/// <see cref="http://msdn.microsoft.com/en-us/library/windows/desktop/aa366045(v=vs.85).aspx" />
/// </summary>
public static IntPtr IcmpHandle
{
get
{
if (_icmpHandle == null)
{
_icmpHandle = IcmpCreateFile();
//TODO Close Icmp Handle appropiate
}
return _icmpHandle.GetValueOrDefault();
}
}
/// <summary>Returns the the marshaled size of the reply struct.</summary>
public static int ReplyMarshalLength
{
get
{
if (_replyStructLength == null)
{
_replyStructLength = Marshal.SizeOf(typeof(Reply));
}
return _replyStructLength.GetValueOrDefault();
}
}
[DllImport("Iphlpapi.dll", SetLastError = true)]
private static extern IntPtr IcmpCreateFile();
[DllImport("Iphlpapi.dll", SetLastError = true)]
private static extern bool IcmpCloseHandle(IntPtr handle);
[DllImport("Iphlpapi.dll", SetLastError = true)]
public static extern uint IcmpSendEcho2Ex(IntPtr icmpHandle, IntPtr Event, IntPtr apcroutine,
IntPtr apccontext, uint sourceAddress, uint destinationAddress, byte[] requestData,
short requestSize, ref Option requestOptions, IntPtr replyBuffer, int replySize, int timeout);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Option
{
public byte Ttl;
public readonly byte Tos;
public byte Flags;
public readonly byte OptionsSize;
public readonly IntPtr OptionsData;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Reply
{
public readonly uint Address;
public readonly int Status;
public readonly int RoundTripTime;
public readonly short DataSize;
public readonly short Reserved;
public readonly IntPtr DataPtr;
public readonly Option Options;
}
}
#endregion Support ICMP Ping
#endregion Ping
private static readonly object LogLock = new object();
public static void PingLog(params string[] args)
{
var sb = new StringBuilder(200);
sb.AppendFormat("=== {0} ", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"));
foreach (var arg in args)
{
sb.Append(" ");
sb.Append(arg);
}
sb.Append(Environment.NewLine);
lock (LogLock)
{
try
{
File.AppendAllText("Logs/ping.log", sb.ToString());
}
catch (Exception ex)
{
Trace.WriteLine(ex);
}
}
}
#region OldPing
public void PingFunction(object o)
{
var td = o as PingThreadData;
if (td == null) return;
td.UpdateProgress(0D);
if (null == td.DoneEvent)
{
td.DoneEvent = new ManualResetEvent(false);
}
if (null == td.CancelEvent)
{
td.CancelEvent = new ManualResetEvent(false);
}
var sb = new StringBuilder();
sb.AppendFormat("PingFunction ");
foreach (var item in td.Entries)
{
sb.AppendFormat("{0}, ", item.IPAddress);
}
PingLog(sb.ToString());
if (0 == td.Entries.Length)
{
td.UpdateProgress(100D);
Thread.Sleep(100);
td.Complete();
return;
}
// Kick off the background thread while we monitor/simulate progress.
Task.Run(() => CheckOnline(td)).ConfigureAwait(false);
// FB 25642 Consider the number of hosts as well
var maxTime = Constants.PING_ICMP_TIMEOUT * td.Entries.Length * NetworkUtils.GetAvailableHosts().Count;
if (maxTime > 0 && maxTime < 1000)
{
maxTime = 1000; //minimum of 1 second
}
// Tick along progress as if it will take MAXPINGTIME. If we finish early, great.
var timeWaited = 0D;
while (!td.DoneEvent.WaitOne(150, false) && !td.CancelEvent.WaitOne(0, false) )
{
timeWaited += 150D;
if (timeWaited >= maxTime)
{
PingLog("WARNING timewaited >= maxpingtime: ", timeWaited.ToString(), ">=", maxTime.ToString());
}
}
if (td.CancelEvent.WaitOne(0, false))
{
td.Complete();
}
//else if (timeWaited >= Maxpingtime)
//{
// PingLog("WARNING timewaited >= maxpingtime: ", timeWaited.ToString(), ">=",
// Maxpingtime.ToString());
// //td.CancelEvent.Set();
// //td.Complete();
// //td.DoneEvent.WaitOne();
// Thread.Sleep(100);
//}
else
{
td.UpdateProgress(100D);
Thread.Sleep(100);
td.Complete();
}
}
//used to hold the number of ips pinged completed while actively pinging
private volatile int _pingsDone = 0;
/// <summary>
/// Pinging device from specific IP address function will use IMC protocol, TC Protocol will be used if host IP address is empty
/// </summary>
/// <param name="o">A <see cref="PingThreadData"/> object</param>
private void CheckOnline(object o)
{
var td = o as PingThreadData;
if (td != null && null == td.CancelEvent)
{
td.CancelEvent = new ManualResetEvent(false);
}
try
{
var maxPingTime = Convert.ToInt32(Constants.PING_ICMP_TIMEOUT);
if (td == null) return;
var ipList = new List<string>();
foreach( var entry in td.Entries)
{
ipList.Add(entry.IPAddress);
}
var hostInfos = NetworkUtils.GetAvailableHosts();
EliminateBadHosts(ref hostInfos, ipList.ToArray());
if (!hostInfos.Any()) { return; }
var lastHost = hostInfos.Last();
var alphabet = Encoding.ASCII.GetBytes(ALPHABET);
var toPing = td.Entries.Length;
_pingsDone = 0;
_ = Parallel.ForEach(td.Entries, item =>
{
if (td.CancelEvent.WaitOne(0, false))
{
td.UpdateEntry(item, PingProgressStates.Cancel, 0);
return;
}
td.UpdateEntry(item, PingProgressStates.Pinging, 0);
var succeded = false;
var attempt = 0;
//FB 25642 Get available hosts
//avoid having to re-parse the ip address x thousand of times
var lookup = new Dictionary<HostInfo, IPAddress>();
foreach (var hostInfo in hostInfos)
{
lookup[hostInfo] = IPAddress.Parse(hostInfo.HostIpAddress);
}
while (!succeded && attempt < MAX_PING_ATTEMPTS && !td.CancelEvent.WaitOne(0, false))
{
if (td.CancelEvent.WaitOne(0, false))
{
td.UpdateEntry(item, PingProgressStates.Cancel, 0);
return;
}
try
{
PingReplyUtils allReply = null;
//FB 25642 we need to go through all the hosts
foreach (var hostInfo in hostInfos)
{
if (!IPAddress.TryParse(item.IPAddress, out var ip))
{
attempt = MAX_PING_ATTEMPTS;
continue;
}
//won't wpork if buffer is empty
var eachReply = PingICMP(lookup[hostInfo], ip, Constants.PING_ICMP_TIMEOUT, alphabet);
if ((eachReply.Status == IPStatus.Success) && (eachReply.NativeCode != 0))
{
//we found the host, update the dictionary & break
DasToHost[item.IPAddress] = hostInfo;
allReply = eachReply;
break;
}
if (hostInfo.Equals(lastHost))
{
//if it's the last host update the overall reply
allReply = eachReply;
}
}
//If the default gateway of the network interface isn't set appropriately, the call to PingICMP will
//return with a Status of IPStatus.Success. So, to differentiate this from a valid successful ping, we check NativeCode
if (allReply != null)
{
//FB 25642 allReply shows the ping was successfull or not
if ((allReply.Status == IPStatus.Success) && (allReply.NativeCode != 0))
{
succeded = true;
td.UpdateEntry(item, PingProgressStates.Ping_Good, allReply.RoundTripTime.Milliseconds);
PingLog("ping succeeded: ", DasToHost[item.IPAddress]?.HostIpAddress, " -> ", item.IPAddress);
}
else
{
attempt++;
}
}
else
{
PingLog("couldn't parse ip: ", item.IPAddress);
attempt++;
}
}
catch (Exception)
{
td.UpdateEntry(item, PingProgressStates.InvalidIPAddress, 0);
break;
}
}
if (succeded)
{
_pingsDone++;
td.UpdateProgress(100D * _pingsDone / toPing);
}
else if (MAX_PING_ATTEMPTS <= attempt)
{
_pingsDone++;
td.UpdateEntry(item, PingProgressStates.NoReply, 0);
td.UpdateProgress(100D * _pingsDone / toPing);
PingLog("failed (maxpings <= attempt: ", item.IPAddress, " - ", MAX_PING_ATTEMPTS.ToString(), "<=", attempt.ToString());
}
if (td.CancelEvent.WaitOne(0, false))
{
td.UpdateEntry(item, PingProgressStates.Cancel, 0);
}
});
}
catch (Exception ex)
{
APILogger.Log(ex);
throw;
}
finally
{
_ = td.DoneEvent.Set();
}
}
#endregion OldPing
}
}
#region PingReply
[Serializable]
public class PingReplyUtils
{
private Win32Exception _exception;
public PingReplyUtils()
{
}
internal PingReplyUtils(uint nativeCode, int replystatus, IPAddress ipAddress, TimeSpan duration)
{
NativeCode = nativeCode;
IpAddress = ipAddress;
if (Enum.IsDefined(typeof(IPStatus), replystatus))
{
Status = (IPStatus)replystatus;
}
}
internal PingReplyUtils(uint nativeCode, int replystatus, IPAddress ipAddress, int roundTripTime,
byte[] buffer)
{
NativeCode = nativeCode;
IpAddress = ipAddress;
RoundTripTime = TimeSpan.FromMilliseconds(roundTripTime);
Buffer = buffer;
if (Enum.IsDefined(typeof(IPStatus), replystatus))
{
Status = (IPStatus)replystatus;
}
}
/// <summary>Native result from <code>IcmpSendEcho2Ex</code>.</summary>
public uint NativeCode { get; } = 0;
public IPStatus Status { get; } = IPStatus.Unknown;
/// <summary>The source address of the reply.</summary>
public IPAddress IpAddress { get; } = null;
public byte[] Buffer { get; } = null;
public TimeSpan RoundTripTime { get; } = TimeSpan.Zero;
/// <summary>Resolves the <code>Win32Exception</code> from native code</summary>
public Win32Exception Exception
{
get
{
if (Status != IPStatus.Success)
{
return _exception ?? (_exception = new Win32Exception((int)NativeCode, Status.ToString()));
}
return null;
}
}
public override string ToString()
{
if (Status == IPStatus.Success)
{
return Status + " from " + IpAddress + " in " + RoundTripTime + " ms with " + Buffer.Length +
" bytes";
}
if (Status != IPStatus.Unknown)
{
return Status + " from " + IpAddress;
}
return Exception.Message + " from " + IpAddress;
}
}
#endregion PingReply
#region Entry
/// <summary>
/// Trivial class to represent an IP address entry, its type, and a visual representation
/// </summary>
public class Entry
{
public string IPAddress { get; set; }
public int DasType { get; set; }
public DataRow DR { get; set; }
public object UserData { get; set; }
}
#endregion Entry
#region HostInfo
//FB 25642 & 25590 & 18152 This class encapsulate information for the host in one place
public class HostInfo
{
public string HostIpAddress { get; set; } = string.Empty;
public string HostMacAddress { get; set; } = string.Empty;
public string HostNetworkId { get; set; } = string.Empty;
public string StartAddress { get; set; } = string.Empty;
public string EndAddress { get; set; } = string.Empty;
}
#endregion
#region PingProgressStates
/// <summary>
/// The possible states for a given <see cref="Entry"/>.
/// </summary>
public enum PingProgressStates
{
Pinging,
Connecting,
Querying,
Added,
Updated,
InvalidIPAddress,
NoReply,
Ping_Good,
Online,
Connected,
NoConnection,
Cancel,
Ready,
Armed,
QueryFailed,
QueryTimedOut,
QueryComplete,
UnexpectedMaxMemory,
UnexpectedFirmwareVersion,
Passed,
ExpiredCalDate,
ChannelCountMismatch,
MissingModules,
Canceled,
NoMemory,
AutoArmed,
Realtime,
ReadyToStream,
Streaming,
LostDock,
Rebooting,
UnexpectedFirstUseDate,
InvalidRecordingMode,
InvalidStreamingMode,
StreamingNotAvailable
}
#endregion PingProgressStates
#region PingThreadData
/// <summary>
/// A wrapper class to represent the progress and feedback for ping activity from background threads.
/// </summary>
public class PingThreadData
{
/// <summary>
/// <see cref="Entry"/> list to act on.
/// </summary>
public Entry[] Entries { get; set; }
/// <summary>
/// Optional <see cref="System.Threading.ManualResetEvent"/> to tell the process to cancel.
/// </summary>
public ManualResetEvent CancelEvent { get; set; }
/// <summary>
/// Optional <see cref="System.Threading.ManualResetEvent"/> to fire when the entire process is done.
/// </summary>
public ManualResetEvent DoneEvent { get; set; }
/// <summary>
/// An optional delgate to be called whenever status text has changed.
/// </summary>
public SetStatusTextDelegate setStatusTextCallback;
/// <summary>
/// An optional delgate to be called whenever progress has changed.
/// </summary>
public SetProgressValueDelegate progressCallback;
/// <summary>
/// A delegate type used for reporting progress on an individual entry
/// </summary>
/// <param name="entry"><see cref="Entry"/> that is being reported on</param>
/// <param name="state"><see cref="PingProgressStates"/> indicating the new state</param>
public delegate void EntryProgressCallback(Entry entry, PingProgressStates state, long msRoundTrip);
/// <summary>
/// An optional delegate to be called whenever an individual entry has changed.
/// </summary>
public EntryProgressCallback entryProgressCallback;
/// <summary>
/// An optional delegate to be called when the entire process is complete. Called before <seealso cref="DoneEvent"/> is fired.
/// </summary>
public ActionCompleteDelegate completeCallback;
/// <summary>
/// Wrapper for posting progress
/// </summary>
/// <param name="Percent">From 0 to 100 to indicate progress</param>
public void UpdateProgress(double Percent)
{
progressCallback?.Invoke(Percent);
}
/// <summary>
/// Wrapper for posting entry progress
/// </summary>
/// <param name="entry">The entry being updated</param>
/// <param name="state">The new state</param>
public void UpdateEntry(Entry entry, PingProgressStates state, long msResponseTime)
{
entryProgressCallback?.Invoke(entry, state, msResponseTime);
}
/// <summary>
/// Wrapper for posting complete
/// </summary>
public void Complete()
{
completeCallback?.Invoke();
DoneEvent?.Set();
}
public bool AvoidConnectPortion { get; set; } = false;
public bool PingFailed { get; set; } = false;
}
#endregion PingThreadData
}

View File

@@ -0,0 +1,166 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Windows.Forms;
using DTS.Common.Utilities.Logging;
namespace DTS.Common.Utils
{
public class SecureQueue<T> : IDisposable where T : new()
{
public enum NullPolicy
{
DenyNull, // throw an exception if try to Enqueue null or zero length
SkipNull, // don't complain but don't enqueue null or zero length
AllowNull // let null's in too
}
protected Queue<T[]> ByteQueue;
protected object ByteQueueLock;
protected ManualResetEvent ByteQueueEvent;
protected NullPolicy _NullPolicy;
protected string Name;
protected Stopwatch StopWatch;
public SecureQueue(NullPolicy policy, string name)
{
StopWatch = new Stopwatch();
StopWatch.Start();
ByteQueue = new Queue<T[]>();
ByteQueueLock = new object();
ByteQueueEvent = new ManualResetEvent(false);
_NullPolicy = policy;
Name = name;
}
public void ResetEvent()
{
//latestReset = GetCaller();
ByteQueueEvent.Reset();
}
public void Enqueue(T[] newData)
{
try
{
lock (ByteQueueLock)
{
if (newData == null || newData.Length == 0)
{
switch (_NullPolicy)
{
case NullPolicy.DenyNull:
throw new ArgumentException("SecureQueue_Enqueue_Err1");
case NullPolicy.SkipNull:
return;
}
}
ByteQueue.Enqueue((T[])newData?.Clone());
if (Enums.DASFactory.DFConstantsAndEnums.ExtraCommunicationLogging)
{
APILogger.Log("Enqueing data", newData);
}
ByteQueueEvent.Set();
}
}
catch (Exception ex)
{
APILogger.LogString("SecureQueue.Enqueue: The queue(" + Name + ") threw an exception. " + ex.Message + " " + ex.StackTrace);
throw;
}
}
/// <summary>
/// Wait for TimeoutMillisec milliseconds for data to arrive in the buffer. Returns false if it times out.
/// </summary>
/// <param name="timeoutMillisec"></param>
/// <returns>true if data has arrived, false if it times out</returns>
public bool WaitForData(int timeoutMillisec)
{
try
{
var result = ByteQueueEvent.WaitOne(timeoutMillisec, false);
return result;
}
catch (Exception ex)
{
APILogger.LogString("SecureQueue.WaitForData: The queue(" + Name + ") threw an exception. " + ex.Message + " " + ex.StackTrace);
}
return false;
}
public WaitHandle QueueWaitHandle => ByteQueueEvent;
public T[] Dequeue(bool resetEvent)
{
try
{
lock (ByteQueueLock)
{
ByteQueueEvent = new ManualResetEvent(false);
if (ByteQueue.Count == 0)
{
return new T[0];
}
if (ByteQueue.Count == 1)
{
var ret = ByteQueue.Dequeue();
return ret;
}
// count how much data we have
var totalBytes = ByteQueue.Sum(arr => arr.Length);
if (totalBytes == 0)
{
APILogger.LogString("SecureQueue.Dequeue: The queue(" + Name + ") has 0 bytes!");
}
// allocate that much
var result = new T[totalBytes];
var index = 0;
// now make one large array
while (ByteQueue.Any())
{
var element = ByteQueue.Dequeue();
Array.Copy(element, 0, result, index, element.Length);
index += element.Length;
}
return result;
}
}
catch (Exception ex)
{
APILogger.LogString("SecureQueue.Dequeue: The queue(" + Name + ") threw an exception. " + ex.Message + " " + ex.StackTrace);
}
return new T[0];
}
public void Flush()
{
try
{
lock (ByteQueueLock)
{
//latestFlush = GetCaller();
ByteQueueEvent = new ManualResetEvent(false);
ByteQueue.Clear();
}
}
catch (Exception ex)
{
APILogger.LogString("SecureQueue.Flush: The queue(" + Name + ") threw an exception. " + ex.Message + " " + ex.StackTrace);
}
}
public bool IsEmpty()
{
return ByteQueue.Count == 0;
}
public void Dispose()
{
}
}
}

View File

@@ -0,0 +1,82 @@
using System.Collections.Generic;
using System.Xml.Serialization;
namespace DTS.DASLib.Utility
{
public class XmlDictionary
{
public SerializableDictionary<string, object> Dictionary { get; set; } = new SerializableDictionary<string, object>();
}
[XmlRoot("dictionary")]
public class SerializableDictionary<TKey, TValue>
: Dictionary<TKey, TValue>, IXmlSerializable
{
#region IXmlSerializable Members
public System.Xml.Schema.XmlSchema GetSchema()
{
return null;
}
public void ReadXml(System.Xml.XmlReader reader)
{
var keySerializer = new XmlSerializer(typeof(TKey));
var valueSerializer = new XmlSerializer(typeof(TValue));
var wasEmpty = reader.IsEmptyElement;
reader.Read();
if (wasEmpty)
return;
reader.ReadStartElement("items");
while (reader.NodeType != System.Xml.XmlNodeType.EndElement)
{
reader.ReadStartElement("item");
reader.ReadStartElement("key");
var key = (TKey)keySerializer.Deserialize(reader);
reader.ReadEndElement();
reader.ReadStartElement("value");
var value = (TValue)valueSerializer.Deserialize(reader);
reader.ReadEndElement();
Add(key, value);
reader.ReadEndElement();
reader.MoveToContent();
}
reader.ReadEndElement();
}
public void WriteXml(System.Xml.XmlWriter writer)
{
var keySerializer = new XmlSerializer(typeof(TKey));
var valueSerializer = new XmlSerializer(typeof(TValue));
writer.WriteStartElement("items");
foreach (var key in Keys)
{
writer.WriteStartElement("item");
writer.WriteStartElement("key");
keySerializer.Serialize(writer, key);
writer.WriteEndElement();
writer.WriteStartElement("value");
var value = this[key];
valueSerializer.Serialize(writer, value);
writer.WriteEndElement();
writer.WriteEndElement();
}
writer.WriteEndElement();
}
#endregion
}
}

View File

@@ -0,0 +1,77 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
namespace DTS.Common.Utils
{
public class StopWatchQueue
{
private readonly Stopwatch _stopWatch;
private readonly Queue<long> _watchQueue;
private readonly string _name;
public StopWatchQueue(string name)
{
_stopWatch = new Stopwatch();
_watchQueue = new Queue<long>(5000);
_name = name;
}
private static double CalculateStdDev(IEnumerable<double> values)
{
double ret = 0;
var valuesArray = values as double[] ?? values.ToArray();
if (valuesArray.Length <= 1) return ret;
//Compute the Average
var avg = valuesArray.Average();
//Perform the Sum of (value-avg)^2
var sum = valuesArray.Sum(d => Math.Pow(d - avg, 2));
//Put it all together
ret = Math.Sqrt(sum / (valuesArray.Length - 1));
return ret;
}
~StopWatchQueue()
{
DumpData();
}
public void DumpData()
{
if (_watchQueue.Count == 0)
return;
var qArray = new double[_watchQueue.Count];
for (var idx = 0; idx < qArray.Length; idx++)
qArray[idx] = Convert.ToDouble(_watchQueue.Dequeue()) / Stopwatch.Frequency * 1000D;
var qMax = qArray.Max();
var qMin = qArray.Min();
var qAvg = qArray.Average();
var qStd = CalculateStdDev(qArray);
var fName = _name + DateTime.Now.ToFileTime() + ".csv";
using (var sw = new StreamWriter(fName, false))
{
sw.WriteLine("The StopWatchQueue contains {0} entries", qArray.Length);
sw.WriteLine("Max={0} Min={1} Average={2} StdDev={3}", qMax, qMin, qAvg, qStd);
sw.WriteLine("All values in milli seconds");
for (var idx = 0; idx < qArray.Length; idx++)
sw.WriteLine("{0},{1}", idx, qArray[idx]);
}
}
public void Start()
{
_stopWatch.Start();
}
public void Stop()
{
_watchQueue.Enqueue(_stopWatch.ElapsedTicks);
_stopWatch.Reset();
}
}
}

View File

@@ -0,0 +1,73 @@
using DTS.Common.Interface;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DTS.Common.Utils
{
//FB 29410 POCO class to be used as parameter for MinUnixTime method
public class TestModuleTimeStamp
{
public int TriggerTimestampSec { get; set; }
public int TriggerTimestampNanoSec { get; set; }
}
public static class TestUtils
{
// FB15333: Add PTP/RTC timestamp column for CSV exports
//FB 29410 refactor the orginal method to use IEnumerable<TestModuleTimeStamp> basemodules as input parameter to be able to use this method in different projects
public static Tuple<double, double> MinUnixTime(IEnumerable<TestModuleTimeStamp> basemodules)
{
if (null != basemodules && basemodules.Count() > 0)
{
var utcAverage = basemodules.Average(module => (double)(module.TriggerTimestampSec + module.TriggerTimestampNanoSec / Common.Constants.NANOS_PER_SECOND));
var utcStandardDeviation = basemodules.Select(module => (double)(module.TriggerTimestampSec + module.TriggerTimestampNanoSec / Common.Constants.NANOS_PER_SECOND)).StandardDeviation();
utcStandardDeviation = utcStandardDeviation > Common.Constants.TEN_MILLIS_IN_SEC ? Common.Constants.TEN_MILLIS_IN_SEC : utcStandardDeviation; // select min within standard dev or 10ms, whichever is smaller
var minUnixTime = new Tuple<double, double>(basemodules.First().TriggerTimestampSec, basemodules.First().TriggerTimestampNanoSec);
foreach (var module in basemodules)
{
var moduleTime = (double)(module.TriggerTimestampSec + module.TriggerTimestampNanoSec / Common.Constants.NANOS_PER_SECOND);
if (module.TriggerTimestampSec <= minUnixTime.Item1 && module.TriggerTimestampNanoSec < minUnixTime.Item2 &&
moduleTime > utcAverage - utcStandardDeviation)
{
minUnixTime = new Tuple<double, double>(module.TriggerTimestampSec, module.TriggerTimestampNanoSec);
}
}
return minUnixTime;
}
return null;
}
public static string ParseROISuffix(string test)
{
var testitems = new string[] { };
if (test.Length > 0 && (testitems = test.Split(":".ToCharArray(), StringSplitOptions.RemoveEmptyEntries)).Length > 3)
{
return testitems.Last();
}
//FB 18312 Get the ROI of particular event
if (test.Length > 0)
{
var segments = test.Split(":".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
var segmentWithEvent = segments.FirstOrDefault(p => p.Contains(DTS.Common.Constants.EventNumber));
if (!string.IsNullOrWhiteSpace(segmentWithEvent))
{
var testSegments = segmentWithEvent.Split('_');
if (testSegments.Any(p => p.Contains(DTS.Common.Constants.EventNumber.Replace("_", ""))))
{
//found an roi suffix
if (!testSegments.Last().Contains(DTS.Common.Constants.EventNumber.Replace("_", "")))
{
return "_" + testSegments.Last();
}
}
}
}
return string.Empty;
}
}
}

View File

@@ -0,0 +1,980 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Media;
using System.Xml;
using DTS.Common.Enums;
using DTS.Common.Enums.Hardware;
using DTS.Common.Interface;
using DTS.Common.Interface.TestDefinition;
using Microsoft.Practices.ObjectBuilder2;
namespace DTS.Common.Utils
{
public static class Utils
{
private const string LINEAR_PATTERN = ".lin.";
public delegate string GetIsoCodeDelegate(string binaryFileName);
/// <summary>
/// populates Test.Channels properties with details
/// </summary>
/// <param name="tmd">Test Metadata list</param>
/// <param name="path">file location</param>
/// <param name="GetIsoCode">function that returns an isocode given a filepath</param>
public static void SetChannelInfo(ITestMetadata tmd, string path, GetIsoCodeDelegate GetIsoCode)
{
var fileList = new List<string>();
if (path.Contains("ALL") || path.Contains("ROI"))
{
fileList = FileUtils.FindFilesInDirectory(path, "chn");
}
else
{
FileUtils.FileList = new List<string>();
FileUtils.FindFiles(path, "chn");
fileList = FileUtils.FileList;
}
var testId = tmd.TestRun.Id;
tmd.TestRun.Channels = new List<ITestChannel>();
tmd.TestRun.CalculatedChannels = new List<ITestChannel>();
var channelCount = 0;
foreach (var m in tmd.TestRun.Modules)
{
var sn = m.SerialNumber;
foreach (var ch in m.Channels)
{
//ch.ChannelNumber = channelCount;
ch.ModuleSerialNumber = sn;
var fullFileName = String.Empty;
var newStyle = fileList.Where(l => l.Contains(tmd.TestRun.DataType)).Any(f => f.Contains(testId + "Ch"));
if (!newStyle)
{
// DataPRO
if (!string.IsNullOrEmpty(tmd.TestRun.Software))
{
var testFileList = fileList
.Where(l => l.Contains(tmd.TestRun.DataType) && l.Contains(testId)).ToList();
if (channelCount < testFileList.Count)
{
fullFileName = testFileList[channelCount];
}
}
// SLICEWare
else
{
var testFileList = fileList
.Where(l => l.Contains(testId)).ToList();
if (channelCount < testFileList.Count)
{
fullFileName = testFileList[channelCount];
}
}
}
else
{
#region new style
if (ch.Bridge == "SQUIB")
{
fullFileName = ch.Number % 2 == 0
? fileList.Where(l => l.Contains(tmd.TestRun.DataType)).FirstOrDefault(f => f.Contains(testId + "Ch" + (ch.AbsoluteDisplayOrder + 1).ToString("000")))
: fileList.Where(l => l.Contains(tmd.TestRun.DataType)).Where(f => f.Contains(testId + "Ch" + (ch.AbsoluteDisplayOrder + 1).ToString("000"))).ToList()[1];
}
else
{
var matches = fileList.Where(l => l.Contains(tmd.TestRun.DataType)).Where(f => f.Contains(testId + "Ch" + (ch.AbsoluteDisplayOrder + 1).ToString("000")));
//14581 Data for linear, cubic poly sensor differs in run test and view data tile
//if we end up with 2 matches, one is the linear added version and one is the non linear version
//determine which file we actually want, and use it.
if (2 <= matches.Count())
{
if (string.IsNullOrWhiteSpace(ch.LinearizationFormula))
{
fullFileName = matches.Where(f => f.Contains(LINEAR_PATTERN)).FirstOrDefault();
if (null == fullFileName)
{
//if there are two files and neither contains a .lin, then we've like hit a situation described in
//MS 26913 where there was multiple downloads which resulted in the same channel information in two files
//we can likely select either of the files in this case as they are probably identical
fullFileName = matches.FirstOrDefault();
}
}
else
{
fullFileName = matches.Where(f => !f.Contains(LINEAR_PATTERN)).FirstOrDefault();
}
}
else
{
fullFileName = matches.FirstOrDefault();
}
}
#endregion new style
}
if (String.IsNullOrEmpty(fullFileName)) { ch.ErrorMessage = "File does not exist"; }
else
{
ch.BinaryFilePath = Path.GetDirectoryName(fullFileName);
ch.BinaryFileName = Path.GetFileName(fullFileName);
if (string.IsNullOrEmpty(ch.IsoCode))
{
ch.IsoCode = GetIsoCode(ch.BinaryFilePath + $"\\" + ch.BinaryFileName);
}
}
ch.ParentModule = m;
ch.ParentTestSetup = tmd.TestSetup;
tmd.TestRun.Channels.Add(ch);
channelCount++;
}
foreach (var ch in m.CalculatedChannels)
{
var fullFileName = fileList.Where(l => l.Contains(tmd.TestRun.DataType)).FirstOrDefault(f => f.Contains(testId + "." + ch.Number.ToString()));
if (string.IsNullOrEmpty(fullFileName)) { ch.ErrorMessage = "File does not exist"; }
else
{
ch.ModuleSerialNumber = sn;
ch.ParentModule = m;
ch.ParentTestSetup = tmd.TestSetup;
ch.BinaryFilePath = Path.GetDirectoryName(fullFileName);
ch.BinaryFileName = Path.GetFileName(fullFileName);
}
tmd.TestRun.CalculatedChannels.Add(ch);
}
}
foreach (var g in tmd.TestSetup.TestGraphs)
{
g.Channels = new List<ITestChannel>();
foreach (var chId in g.ChannelIds)
{
var ch = (from c in tmd.TestRun.Channels where c.ChannelId == chId select c).FirstOrDefault();
if (ch != null) g.Channels.Add(ch.Copy());
}
}
}
/// <summary>
/// Copied from DataPro
/// per http://fogbugz/fogbugz/default.asp?8281 use 120%
/// The idea here is to have a moving window no smaller than +/-1% of capacity
/// Also make sure that the top of the signal is seen so make the visible window
/// +/-120% of the signal
/// this was abstrated here from two different locations for:
/// 29416 Limit view of TSR AIR data to 1% of full scale in auto range mode
/// </summary>
/// <param name="capacity">plot expected maximum EU</param>
/// <param name="dataMin">actual plot min value</param>
/// <param name="dataMax">actual plot max value</param>
/// <param name="maxAutoZoom">the scaler to apply to signal as the window max edge</param>
/// <param name="minAutoZoom">the scaler to apply to expected maximum to form window min edges</param>
/// <param name="currentYMin">min Y value to apply as plot constraint</param>
/// <param name="currentYMax">max Y value to apply as plot constraint</param>
/// <remarks>
/// </remarks>
public static void CalculateMinMaxAutoScaling(double capacity,
double dataMin,
double dataMax,
double minAutoZoom,
double maxAutoZoom,
out double currentYMax,
out double currentYMin
)
{
capacity = Math.Abs(capacity);
var middleOfSignalExtrema = ((dataMax - dataMin) / 2.0D) + dataMin;
var minZoom = capacity * minAutoZoom;
var topOfMinZoomWindow = middleOfSignalExtrema + minZoom;
var bottomOfMinZoomWindow = middleOfSignalExtrema - minZoom;
currentYMax = (dataMax + Math.Abs(dataMax * (1 - maxAutoZoom)) >= topOfMinZoomWindow) ?
dataMax + Math.Abs(dataMax * (1 - maxAutoZoom)) : topOfMinZoomWindow;
currentYMin = (dataMin - Math.Abs(dataMin * (1 - maxAutoZoom)) <= bottomOfMinZoomWindow) ?
dataMin - Math.Abs(dataMin * (1 - maxAutoZoom)) : bottomOfMinZoomWindow;
}
/// <summary>
/// test code courtesy of AutoLab XML import/conversion code via Chad Ivan
/// the CRC should be prepended to the ID (CRC + ID)
/// </summary>
/// <param name="ShortID">short ID</param>
/// <returns>returns CRC bit for given string</returns>
public static string GenerateEIDCRC(string ShortID)
{
var int64 = Int64.Parse(ShortID, System.Globalization.NumberStyles.HexNumber);
var bytes = BitConverter.GetBytes(int64);
var bitArray = new System.Collections.BitArray(bytes);
var CRCbit = new System.Collections.BitArray(8);
var CRCTemp = new System.Collections.BitArray(1);
byte[] CRCbyte = new byte[1];
for (int i = 0; i < 56; i++)
{
CRCTemp[0] = CRCbit[0] ^ bitArray[i];
CRCbit[0] = CRCbit[1];
CRCbit[1] = CRCbit[2];
CRCbit[2] = CRCbit[3] ^ CRCTemp[0];
CRCbit[3] = CRCbit[4] ^ CRCTemp[0];
CRCbit[4] = CRCbit[5];
CRCbit[5] = CRCbit[6];
CRCbit[6] = CRCbit[7];
CRCbit[7] = CRCTemp[0];
}
CRCbit.CopyTo(CRCbyte, 0);
return BitConverter.ToString(CRCbyte); ;
}
/// <summary>
/// handles indenting and formatting for xml text
/// </summary>
/// <param name="XML"></param>
/// <returns></returns>
public static string PrettyPrint(string XML)
{
var Result = "";
using (var MS = new MemoryStream())
{
using (var W = new XmlTextWriter(MS, Encoding.Unicode))
{
var D = new XmlDocument();
try
{
// Load the XmlDocument with the XML.
D.LoadXml(XML);
W.Formatting = Formatting.Indented;
// Write the XML into a formatting XmlTextWriter
D.WriteContentTo(W);
W.Flush();
MS.Flush();
// Have to rewind the MemoryStream in order to read
// its contents.
MS.Position = 0;
// Read MemoryStream contents into a StreamReader.
using (var SR = new StreamReader(MS))
{// Extract the text from the StreamReader.
var FormattedXML = SR.ReadToEnd();
Result = FormattedXML;
}
}
catch (XmlException) { }
MS.Close();
W.Close();
}
}
return Result;
}
//https://docs.microsoft.com/en-us/dotnet/standard/base-types/how-to-display-milliseconds-in-date-and-time-values
//FB 29410 Format the date time to string with milliseonds
/// <summary>
/// Format a DateTime with nanoseconds part to a date time string with milliseconds in a current culture
/// </summary>
/// <param name="dateTimeWithNanoSeconds">DateTime object with nano seconds part</param>
/// <returns>Formatted date time in string</returns>
public static string FormatTimeStamp(DateTime dateTimeWithNanoSeconds)
{
string fullPattern = DateTimeFormatInfo.CurrentInfo.LongTimePattern;
// Create a format similar to .fff but based on the current culture.
string millisecondFormat = $"{NumberFormatInfo.CurrentInfo.NumberDecimalSeparator}fff";
// Append millisecond pattern to current culture's full date time pattern.
fullPattern = Regex.Replace(fullPattern, "(:ss|:s)", $"$1{millisecondFormat}");
string time = dateTimeWithNanoSeconds.ToString(fullPattern);
string date = dateTimeWithNanoSeconds.ToShortDateString();
string dateTime = $"{date} {time}";
return dateTime;
}
/// <summary>
/// returns the next highest power of 2
/// </summary>
/// <param name="numSamples"></param>
/// <returns></returns>
public static int GetEnclosingPower2(int numSamples)
{
//return Convert.ToInt32(Math.Pow(2D, Math.Ceiling(Math.Log(Convert.ToDouble(numSamples)) / Math.Log(2D))));
// 24418 use bitwise calc for better performance
var next = (uint)numSamples;
next--;
next |= next >> 1;
next |= next >> 2;
next |= next >> 4;
next |= next >> 8;
next |= next >> 16;
next++;
return (int)next;
}
public static long GetEnclosingPower2(long numSamples)
{
//return Convert.ToInt32(Math.Pow(2D, Math.Ceiling(Math.Log(Convert.ToDouble(numSamples)) / Math.Log(2D))));
// 24418 use bitwise calc for better performance
var next = (ulong)numSamples;
next--;
next |= next >> 1;
next |= next >> 2;
next |= next >> 4;
next |= next >> 8;
next |= next >> 16;
next |= next >> 32;
next++;
return (long)next;
}
public static string EscapeString(string str)
{
if (null == str) { return "#NOVALUE"; }
var mustQuote = (str.Contains(",") || str.Contains("\"") || str.Contains("\r") || str.Contains("\n"));
if (!mustQuote) return str;
var sb = new StringBuilder();
sb.Append("\"");
foreach (var nextChar in str)
{
sb.Append(nextChar);
if (nextChar == '"') sb.Append("\"");
}
sb.Append("\"");
return sb.ToString();
}
public static string ReplaceLast(this string str, string find, string replace)
{
var place = str.LastIndexOf(find, StringComparison.Ordinal);
return -1 == place
? str
: str.Remove(place, find.Length).Insert(place, replace);
}
public static string ReplaceFirst(this string str, string find, string replace)
{
var place = str.IndexOf(find, StringComparison.Ordinal);
return -1 == place
? str
: str.Remove(place, find.Length).Insert(place, replace);
}
public static string ReplaceStrings(this string str, Dictionary<string, string> toReplace, Common.Enums.StringReplacementMode mode)
{
try
{
switch (mode)
{
case StringReplacementMode.All:
toReplace.ForEach(pair => str = str.Replace(pair.Key, pair.Value));
break;
case StringReplacementMode.First:
toReplace.ForEach(pair => str = str.ReplaceFirst(pair.Key, pair.Value));
break;
case StringReplacementMode.Last:
toReplace.ForEach(pair => str = str.ReplaceLast(pair.Key, pair.Value));
break;
}
}
catch
{
// ignored, bad data in
}
return str;
}
public static void GetChildren(UIElement parent, Type targetType, ref List<UIElement> children)
{
var count = VisualTreeHelper.GetChildrenCount(parent);
if (count <= 0) return;
for (var i = 0; i < count; i++)
{
var child = (UIElement)VisualTreeHelper.GetChild(parent, i);
if (child.GetType() == targetType) { children.Add(child); }
GetChildren(child, targetType, ref children);
}
}
public static void GetChildrenByName(UIElement parent, string targetName, ref List<FrameworkElement> children)
{
var count = VisualTreeHelper.GetChildrenCount(parent);
if (count <= 0) return;
for (var i = 0; i < count; i++)
{
var child = (UIElement)VisualTreeHelper.GetChild(parent, i);
if (((string)child.GetType().GetProperty("Name").GetValue(child, null)).Contains(targetName)) { children.Add((FrameworkElement)child); }
GetChildrenByName(child, targetName, ref children);
}
}
/// <summary>
/// Finds a Child of a given item in the visual tree.
/// </summary>
/// <param name="parent">A direct parent of the queried item.</param>
/// <typeparam name="T">The type of the queried item.</typeparam>
/// <param name="childName">x:Name or Name of child. </param>
/// <returns>The first parent item that matches the submitted type parameter.
/// If not matching item can be found,
/// a null parent is being returned.</returns>
public static T FindChild<T>(DependencyObject parent, string childName)
where T : DependencyObject
{
// Confirm parent and childName are valid.
if (parent == null) return null;
T foundChild = null;
var childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (var i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
// If the child is not of the request child type child
if (!(child is T))
{
// recursively drill down the tree
foundChild = FindChild<T>(child, childName);
// If the child is found, break so we do not overwrite the found child.
if (foundChild != null) break;
}
else if (!string.IsNullOrEmpty(childName))
{
// If the child's name is set for search
if (child is FrameworkElement frameworkElement && frameworkElement.Name == childName)
{
// if the child's name is of the request name
foundChild = (T)child;
break;
}
}
else
{
// child element found.
foundChild = (T)child;
break;
}
}
return foundChild;
}
public static T FindParent<T>(DependencyObject child)
where T : DependencyObject
{
if (null == child) return null;
T visParent = VisualTreeHelper.GetParent(child) as T;
T logParent = LogicalTreeHelper.GetParent(child) as T;
return visParent ?? logParent ?? FindParent<T>(VisualTreeHelper.GetParent(child));
}
public static T FindVisualChild<T>(DependencyObject obj)
where T : DependencyObject
{
for (var i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child is T variable)
return variable;
var childOfChild = FindVisualChild<T>(child);
if (childOfChild != null)
return childOfChild;
}
return null;
}
public static List<T> GetVisualChildren<T>(DependencyObject obj) where T : DependencyObject
{
var childList = new List<T>();
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child is T)
childList.Add(child as T);
}
return childList.Count > 0 ? childList : null;
}
public static string GetEnumDescription<T>(this T enumerationValue) where T : struct
{
var type = enumerationValue.GetType();
if (!type.IsEnum)
{
throw new ArgumentException($"{nameof(enumerationValue)} must be of Enum type", nameof(enumerationValue));
}
var memberInfo = type.GetMember(enumerationValue.ToString());
if (memberInfo.Length > 0)
{
var attrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attrs.Length > 0)
{
return ((DescriptionAttribute)attrs[0]).Description;
}
}
return enumerationValue.ToString();
}
public static int FindValueInArray(double value, double[] valueArray)
{
for (var index = 0; index < valueArray.Length; index++)
{
if (value == valueArray[index])
{
return index;
}
}
return -1;
}
public static double StandardDeviation(this IEnumerable<double> values)
{
var avg = values.Average();
return Math.Sqrt(values.Average(val => (val - avg) * (val - avg)));
}
public static void CalculateClip(double[] data,
out double clipPeak,
out double clipStartSeconds,
out double clipEndSeconds,
double sampleRate,
int preZeroDataPoints)
{
clipPeak = 0;
clipStartSeconds = 0;
clipEndSeconds = 0;
/* From TDC */
for (var index = 0; index < data.Length - Math.Floor(0.003 / (1 / sampleRate) + 0.5); index++)
{
var localclip = 10000D;
for (var localIndex = index; localIndex < index + Math.Floor(0.003 / (1 / sampleRate) + 0.5); localIndex++)
{
if (data[localIndex] < localclip)
{
localclip = data[localIndex];
}
}
if (!(localclip > clipPeak)) continue;
clipPeak = localclip;
clipStartSeconds = (index - preZeroDataPoints) * (1 / sampleRate);
clipEndSeconds = clipStartSeconds + 0.003;
}
}
private static readonly short[] ODDPARITY = {0, 1, 1, 0, 1, 0, 0, 1,
1, 0, 0, 1, 0, 1, 1, 0 };
public static byte Math_CRC7(byte[] buffer, int length)
{
int i;
int j;
byte crc = 0xFF;
for (i = 0; i < length; i++)
{
var currentByte = buffer[i];
for (j = 0; j < 8; j++)
{
crc = (byte)(crc << 1);
if (0x00 != ((currentByte ^ crc) & 0x80))
{
crc = (byte)(crc ^ 0x09);
}
currentByte <<= 1;
}
crc = (byte)(crc & 0x7F);
}
crc <<= 1;
crc |= 0x01;
return crc;
}
/**
* Computes the 16-bit CRC of the n passed in data bytes. N should be
* even (if it is not, the last byte will not get included in the
* computation). The initial_crc is the value to seed the computation
* from - normally it will be set to 0.
*
* @param data the unsigned character buffer of which to compute the CRC16
* @param n the number of bytes in data (must be even!)
* @param initial_crc the CRC16 value to seed the computation with
*
* @return the updated CRC16 starting with initial_crc and computed over
* n data bytes
*/
/* Slightly changed to handle array of bytes */
public static ushort CalculateCRC(byte[] data, ushort initialCRC)
{
ushort crc = 0;//initial_crc98765430.
int i;
for (i = 0; i < data.Length; i += 2)
{
crc = Math_DoCRC16Step((ushort)((data[i + 1] << 8) | data[i]), crc);
}
return crc;
}
/**
* Computes the 16-bit CRC of the n passed in data bytes. CCITT implementation
* N should be
* even (if it is not, the last byte will not get included in the
* computation). The initial_crc is the value to seed the computation
* from - normally it will be set to 0.
*
* @param data the unsigned character buffer of which to compute the CRC16
* @param n the number of bytes in data (must be even!)
* @param initial_crc the CRC16 value to seed the computation with
*
* @return the updated CRC16 starting with initial_crc and computed over
* n data bytes
*/
/* Slightly changed to handle array of bytes */
public static ushort CalculateCRCCCITT(byte[] data, ushort initialCRC)
{
var crc = initialCRC;
int i;
for (i = 0; i < data.Length; i += 2)
{
crc = Math_DoCRCCCITTStep((ushort)((data[i + 1] << 8) | data[i]), crc);
}
return crc;
}
public static ushort Math_DoCRC16Step(ushort cdata, ushort currentCRC)
{
cdata = (ushort)((cdata ^ (currentCRC & 0xff)) & 0xff);
currentCRC >>= 8;
if (0 != (ODDPARITY[cdata & 0xf] ^ ODDPARITY[cdata >> 4]))
currentCRC ^= 0xc001;
cdata <<= 6;
currentCRC ^= cdata;
cdata <<= 1;
currentCRC ^= cdata;
return currentCRC;
}
private static readonly ushort[] CRC_TABLE =
{
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5,
0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b,
0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 0x1231, 0x0210,
0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c,
0xf3ff, 0xe3de, 0x2462, 0x3443, 0x0420, 0x1401,
0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b,
0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6,
0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738,
0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 0x48c4, 0x58e5,
0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969,
0xa90a, 0xb92b, 0x5af5, 0x4ad4, 0x7ab7, 0x6a96,
0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc,
0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03,
0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd,
0xad2a, 0xbd0b, 0x8d68, 0x9d49, 0x7e97, 0x6eb6,
0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a,
0x9f59, 0x8f78, 0x9188, 0x81a9, 0xb1ca, 0xa1eb,
0xd10c, 0xc12d, 0xf14e, 0xe16f, 0x1080, 0x00a1,
0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c,
0xe37f, 0xf35e, 0x02b1, 0x1290, 0x22f3, 0x32d2,
0x4235, 0x5214, 0x6277, 0x7256, 0xb5ea, 0xa5cb,
0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447,
0x5424, 0x4405, 0xa7db, 0xb7fa, 0x8799, 0x97b8,
0xe75f, 0xf77e, 0xc71d, 0xd73c, 0x26d3, 0x36f2,
0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9,
0xb98a, 0xa9ab, 0x5844, 0x4865, 0x7806, 0x6827,
0x18c0, 0x08e1, 0x3882, 0x28a3, 0xcb7d, 0xdb5c,
0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0,
0x2ab3, 0x3a92, 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d,
0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07,
0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba,
0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55, 0x5e74,
0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
};
public static ushort Math_DoCRCCCITTStep(ushort cdata, ushort current_crc)
{
return (ushort)((ushort)(current_crc << 8) ^ CRC_TABLE[(ushort)(((ushort)(current_crc >> 8) ^ cdata) & 0xFF)]);
}
public static void FilterDataArray(ref double[] data, double sampleRate, double filterFrequency)
{
/********************************/
/*** MCW NHTSA FILTER ROUTINE ***/
/********************************/
//
// CFILTER.C cfc filter program
//
// Filters data using SAE J211 algorithym (4 pole phaseless lo pass).
// "Pads" data with 5% of the original number of points at the beginning with the first point
// and 5% of the original number of points at the end with the last point
// to "squelch" out filter start up spikes.
// Previously read data file from disc, filtered and wrote new data file to disc.
// NOW read/writes to array(s) in memory.
// Optionally zeros baseline by subtracting the average of the first 5%
// of the original number of points; SKIPS data which is "padded" by this routine
// Optionally scales the data after filtering. SCALE = 1.0 skips to avoid unnecessary calc's.
//
// "syntax:"
//
// cfilter(INdata, SCALE, OUTdata, SampleRate, CFCnumb, BaseLine, NumPoints);
//
// INdata (pointer?): array (double) of data to be filtered
// SCALE (double): a number multiplied by the above array after it is filtered.
// Useful for "last minute" scaling and/or inversion
// Outdata (pointer?): array (double) into which the results are stored.
// INdata can = OUTdata, INdata will then be overwrittten.
// SampleRate (double): the frequency(in samples per second) at which the INdata were sampled.
// CFCnumb (double): SAE Channel Filter Class;
// 60, 180, 600, or 1000; ONLY these values have been validated.
// BaseLine (char): a single character; ONLY "Y" or "y" will zero baseline;
// (This started as a command line style FORTRAN program and used to "ask"
// the user. Some day this should be changed to an integer (1 or 0) switch.)
// NumPoints (int): the number of points in the input array (INdata);
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// int cfilter(double *indata, double calib, double *out, double freq,
// double cfc, char baseline[2], int n)
// {
double[] Z;
double[] A;
double t, pi, wd, wa, d0, d1, d2, e1, e2;
double f1, f2, f3, f4, f5;
int data_end, i, j, k, pad;
double[] NewDataArray;
var calib = 1.0;
var CFC = filterFrequency * 0.6;
// The set-up of the formulas.
t = 1.0 / sampleRate;
pi = 4.0 * Math.Atan(1.0);
wd = 2.0 * pi * CFC * 2.0775;
wa = Math.Sin(wd * (t / 2.0)) / Math.Cos(wd * (t / 2.0)); // The original formula
d0 = wa * wa / (1 + Math.Sqrt(2.0) * wa + wa * wa);
d1 = 2.0 * d0;
d2 = d0;
e1 = -2.0 * (wa * wa - 1) / (1 + Math.Sqrt(2.0) * wa + wa * wa);
e2 = (-1 + Math.Sqrt(2.0) * wa - wa * wa) / (1 + Math.Sqrt(2.0) * wa + wa * wa);
data_end = data.Length - 1; // data_end is used for loop counting corresponding
// to the number of lines that the data file has
pad = (int)(0.05 * data.Length);
Z = new double[pad + pad + data.Length];
A = new double[pad + pad + data.Length];
NewDataArray = new double[pad + pad + data.Length];
for (i = 0; i <= pad - 1; i++) // pad newdata at beginning
{ // w/ first pt of indata
NewDataArray[i] = data[i];
}
for (i = pad; i <= data_end + pad - 1; i++) // Copy indata into newdata
{ // after initial pad
NewDataArray[i] = data[i - (pad - 1)];
}
for (i = data_end + pad; i <= data_end + 2 * pad - 1; i++) // pad newdata at end
{ // w/ last pt of indata
NewDataArray[i] = data[data_end - 1];
}
data_end = data_end + 2 * pad;
// Data Passed Through Filter Backward
for (j = data_end - 2; j >= 0; j--)
{
f1 = d0 * NewDataArray[j]; // Breaking
f2 = d1 * NewDataArray[j + 1]; // down
f3 = d2 * NewDataArray[j + 2]; // a big
f4 = e1 * Z[j + 1]; // formula
f5 = e2 * Z[j + 2]; // into smaller "groups".
Z[j] = f1 + f2 + f3 + f4 + f5; // Putting the "groups" back together
}
Z[data_end - 1] = Z[data_end - 3];
Z[data_end - 2] = Z[data_end - 3];
// Data Passed Through Filter Forward
for (k = 3; k <= data_end; k++)
{
f1 = d0 * Z[k]; // Breaking
f2 = d1 * Z[k - 1]; // down
f3 = d2 * Z[k - 2]; // a big
f4 = e1 * A[k - 1]; // formula
f5 = e2 * A[k - 2]; // into smaller "groups".
A[k] = f1 + f2 + f3 + f4 + f5; // Putting the "groups" back together
}
A[1] = A[3]; // This sets the first data points to the same
A[2] = A[3]; // value as the 3rd value. Without this, the
i = 0;
for (k = pad; k <= data_end - pad; k++) // DO loop to write output file
{
if (calib != 1.0) //
{ // Multiplying by the calibration factor
A[k] = A[k] * calib; // Unless factor = 1.0
} // AFTER the filtering has been finished
NewDataArray[i] = A[k];
i++;
}
Array.Copy(NewDataArray, 0, data, 0, data.Length);
}
public static bool IsLike(string text, string pattern, bool caseSensitive = false)
{
//Check for empties
if (string.IsNullOrWhiteSpace(text) || string.IsNullOrWhiteSpace(pattern)) return true;
//Prepare for regex
pattern = pattern.Replace(".", @"\.");
pattern = pattern.Replace("(", @"\(");
pattern = pattern.Replace(")", @"\)");
pattern = pattern.Replace("?", ".");
//pattern = pattern.Replace("*", ".*?");
pattern = pattern.Replace(@"\", @"\\");
pattern = pattern.Replace(" ", @"\s");
try
{
return new Regex(pattern, caseSensitive ? RegexOptions.None : RegexOptions.IgnoreCase).IsMatch(text);
}
catch
{
return false;
}
}
public static bool? IsClockSynced(IDictionary<InputClockSource, bool> clocksyncs, ClockSyncProfile profile, HardwareTypes hwType)
{
if (HardwareTypes.SLICE_Pro_Distributor == hwType) { return null; }
if ( HardwareTypes.SLICE6DB3 == hwType) { return null; }
if ((HardwareTypes.SLICE6_Base == hwType || HardwareTypes.SLICE6DB == hwType || HardwareTypes.SLICE6DB_InDummy == hwType) && profile == ClockSyncProfile.Manual)
{
//18030 S6, S6DB can only do PTP so "manual" is always PTP
profile = ClockSyncProfile.Auto_E2E;
}
bool? synced = null;
switch (profile)
{
case ClockSyncProfile.Master_E2E:
break;
case ClockSyncProfile.Auto_E2E:
case ClockSyncProfile.Slave_E2E:
synced = clocksyncs[InputClockSource.PTP];
break;
case ClockSyncProfile.EXT_PPS:
case ClockSyncProfile.Master_E2E_EXT_PPS:
synced = clocksyncs[InputClockSource.OnePPS];
break;
case ClockSyncProfile.GPS:
case ClockSyncProfile.Master_E2E_GPS:
synced = clocksyncs[InputClockSource.GPS];
break;
case ClockSyncProfile.IRIG:
case ClockSyncProfile.Master_E2E_IRIG:
synced = clocksyncs[InputClockSource.IRIG];
break;
case ClockSyncProfile.GPS_EXT_PPS:
case ClockSyncProfile.Master_E2E_GPS_EXT_PPS:
synced = clocksyncs[InputClockSource.GPS] &&
clocksyncs[InputClockSource.OnePPS];
break;
case ClockSyncProfile.IRIG_EXT_PPS:
case ClockSyncProfile.Master_E2E_IRIG_EXT_PPS:
synced = clocksyncs[InputClockSource.IRIG] &&
clocksyncs[InputClockSource.OnePPS];
break;
}
return synced;
}
public static bool IsTmNSString(string tmns)
{
return tmns.StartsWith("(") &&
tmns.EndsWith(")") &&
tmns.TrimStart(new[] { '(' }).TrimEnd(new[] { ')' }).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).All(sval => uint.TryParse(sval, out var uval) && uval < 65536);
}
const double _1 = 0.1;
const double _2 = 0.01;
const double _3 = 0.001;
const double _4 = 0.0001;
const double _5 = 0.00001;
const double _6 = 0.000001;
const double _7 = 0.0000001;
public static bool EqualsDigitPrecision(this double left, double right, uint precision)
{
var diff = Math.Abs(left - right);
switch(precision)
{
case 0:
return (int)left == (int)right;
case 1:
return diff < _1;
case 2:
return diff < _2;
case 3:
return diff < _3;
case 4:
return diff < _4;
case 5:
return diff < _5;
case 6:
return diff < _6;
default:
case 7:
return diff < _7;
}
}
}
}

View File

@@ -0,0 +1,7 @@
namespace DTS.Common.Utils
{
public class XMLUtils
{
}
}