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,462 @@
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
//Version 11 adds AlignUDPToPPS
public const double CurrentXmlVersion = 11.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;
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)
{
//ignore exception
}
}
}
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,111 @@
using System.Collections.Generic;
namespace DTS.Common.Utils
{
public class IPRange
{
/// <summary>
/// ip range, or a start and a stop
/// </summary>
public IPAddressIntForm RangeStart { get; set; }
public IPAddressIntForm RangeEnd { get; set; }
public IPRange(IPAddressIntForm start, IPAddressIntForm end)
{
RangeStart = start;
RangeEnd = end;
}
}
/// <summary>
/// just structure for ips broken into bytes
/// </summary>
public class IPAddressIntForm
{
public int ByteOne { get; }
public int ByteTwo { get; }
public int ByteThree { get; }
public int ByteFour { get; }
public IPAddressIntForm(int b1, int b2, int b3, int b4)
{
ByteOne = b1;
ByteTwo = b2;
ByteThree = b3;
ByteFour = b4;
}
public override string ToString()
{
return $"{ByteOne}.{ByteTwo}.{ByteThree}.{ByteFour}";
}
public static bool TryParse(string input, out IPAddressIntForm output)
{
var tokens = input.Split('.');
if (4 != tokens.Length)
{
output = null;
return false;
}
if (int.TryParse(tokens[0], out int one)
&& int.TryParse(tokens[1], out int two)
&& int.TryParse(tokens[2], out int three)
&& int.TryParse(tokens[3], out int four))
{
output = new IPAddressIntForm(one, two, three, four);
return true;
}
else
{
output = null;
return false;
}
}
internal class IPComparer : IComparer<IPAddressIntForm>
{
public int Compare(IPAddressIntForm x, IPAddressIntForm y)
{
if (x == y) { return 0; }
if (null == x) { return -1; }
if (null == y) { return 1; }
var ret = x.ByteOne.CompareTo(y.ByteOne);
if (ret != 0) { return ret; }
ret = x.ByteTwo.CompareTo(y.ByteTwo);
if (ret != 0) { return ret; }
ret = x.ByteThree.CompareTo(y.ByteThree);
if (ret != 0) { return ret; }
ret = x.ByteFour.CompareTo(y.ByteFour);
if (ret != 0) { return ret; }
return 0;
}
}
/// <summary>
/// gets all ips between a start and an end
/// </summary>
/// <param name="start"></param>
/// <param name="end"></param>
/// <returns></returns>
public static string[] GetAllIPs(IPAddressIntForm a, IPAddressIntForm b)
{
var list = new List<string>();
var comparer = new IPComparer();
IPAddressIntForm start = a;
IPAddressIntForm end = b;
if (comparer.Compare(a, b) > 0) { start = b; end = a; }
for( var firstByte = start.ByteOne; firstByte <= end.ByteOne; firstByte ++)
{
for( var secondByte = start.ByteTwo; secondByte <= end.ByteTwo; secondByte ++)
{
for ( var thirdByte = start.ByteThree; thirdByte <= end.ByteThree; thirdByte ++)
{
for ( var fourthByte = start.ByteFour; fourthByte <= end.ByteFour; fourthByte ++)
{
list.Add($"{firstByte}.{secondByte}.{thirdByte}.{fourthByte}");
}
}
}
}
return list.ToArray();
}
}
}

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,238 @@
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 (!AllowInternalNICIPs && 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 = 115200)
{
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 bool AllowInternalNICIPs { get; set; } = false;
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 ( !AllowInternalNICIPs && 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,870 @@
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; }
//FB 43919
List<bool> result = new List<bool>();
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++)
{
//FB 43919 If this condition is true as first iteration then break immediately and result collection should have only one false
//which means we need to remove this ip since it's not in the network
if (ipByte[i] < startBytes[i] || ipByte[i] > endBytes[i])
{
result.Add(false);
break;
}
else
{
result.Add(true);
}
}
//FB 43919 if any of the segment was true then don't remove the ip
return result.Exists(p => p);
}
catch (Exception ex)
{
APILogger.Log(ex);
}
}
//FB 43919
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) =>
{
if (hostInfos.Any())
{
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.Any())
{
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 = Array.Find(segments, p => p.Contains(DTS.Common.Constants.EventNumber));
if (!string.IsNullOrWhiteSpace(segmentWithEvent))
{
var testSegments = segmentWithEvent.Split('_');
if (Array.Exists(testSegments, 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;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,22 @@
using System;
using System.Diagnostics;
namespace DTS.Common.Utils
{
public readonly struct ValueStopwatch
{
private static readonly double TimestampToTicks = TimeSpan.TicksPerSecond / (double)Stopwatch.Frequency;
private readonly long _startTimestamp;
private ValueStopwatch(long startTimestamp) => _startTimestamp = startTimestamp;
public static ValueStopwatch StartNew() => new ValueStopwatch(Stopwatch.GetTimestamp());
public TimeSpan GetElapsedTime()
{
var end = Stopwatch.GetTimestamp();
var elapsedTicks = (long)((end - _startTimestamp) * TimestampToTicks);
return new TimeSpan(elapsedTicks);
}
}
}

View File

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