init
This commit is contained in:
541
Common/DTS.CommonCore/Utils/BusyWaitAnimation.cs
Normal file
541
Common/DTS.CommonCore/Utils/BusyWaitAnimation.cs
Normal 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
|
||||
}
|
||||
}
|
||||
171
Common/DTS.CommonCore/Utils/ByteConverter.cs
Normal file
171
Common/DTS.CommonCore/Utils/ByteConverter.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
494
Common/DTS.CommonCore/Utils/Database.cs
Normal file
494
Common/DTS.CommonCore/Utils/Database.cs
Normal 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,
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
24
Common/DTS.CommonCore/Utils/EnumUtils.cs
Normal file
24
Common/DTS.CommonCore/Utils/EnumUtils.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
460
Common/DTS.CommonCore/Utils/FileUtils.cs
Normal file
460
Common/DTS.CommonCore/Utils/FileUtils.cs
Normal file
@@ -0,0 +1,460 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml;
|
||||
|
||||
// ReSharper disable UnusedMember.Local
|
||||
// ReSharper disable UnusedVariable
|
||||
|
||||
namespace DTS.Common.Utils
|
||||
{
|
||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||
public static class FileUtils
|
||||
{
|
||||
[Flags]
|
||||
enum MoveFileFlags
|
||||
{
|
||||
MOVEFILE_REPLACE_EXISTING = 0x00000001,
|
||||
MOVEFILE_COPY_ALLOWED = 0x00000002,
|
||||
MOVEFILE_DELAY_UNTIL_REBOOT = 0x00000004,
|
||||
MOVEFILE_WRITE_THROUGH = 0x00000008,
|
||||
MOVEFILE_CREATE_HARDLINK = 0x00000010,
|
||||
MOVEFILE_FAIL_IF_NOT_TRACKABLE = 0x00000020
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
private static extern bool MoveFileEx(string lpExistingFileName, string lpNewFileName, MoveFileFlags dwFlags);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool DeleteFile(string lpFileName);
|
||||
public const double DataPROPre20XmlVersion = 2.0D;
|
||||
public const double DataPRO20XmlVersion = 3.0D;
|
||||
public const double DataPRO21XmlVersion = 4.0D;
|
||||
public const double DataPRO22XmlVersion = 5.0D;
|
||||
//FB 13120 Added FilterClass and deleted CFC in version 6.0
|
||||
//15390 Store the ZMO in EU in the DataPRO DB in version 7.0, adds top level field SensorChangeHistory
|
||||
//13065 Sensor "First Use" Date, version 8, adds LatestCalibrationId, CalibrationId and FirstUse tags
|
||||
//15727Building and Replacing Racks/Mods - version 9, adds TestId,GroupId, StandIn to hardware and Id to tests
|
||||
//Version 10 adds TSR Air settings
|
||||
public const double CurrentXmlVersion = 10.0D;
|
||||
|
||||
|
||||
public static XmlElement GetImportXmlNode(string filename, string xmlDoc, out double importVersion)
|
||||
{
|
||||
var doc = new XmlDocument();
|
||||
importVersion = 0D;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(xmlDoc))
|
||||
{
|
||||
doc.LoadXml(xmlDoc);
|
||||
}
|
||||
else
|
||||
{
|
||||
doc.Load(filename);
|
||||
}
|
||||
|
||||
foreach (var node in doc.ChildNodes)
|
||||
{
|
||||
if (node is XmlDeclaration) continue;
|
||||
if (!(node is XmlElement)) continue;
|
||||
if ((node as XmlElement).Name != "ExportFile") continue;
|
||||
|
||||
var itemsToComplete = Convert.ToDouble(((XmlElement) node).GetAttribute("TotalItems"),
|
||||
System.Globalization.CultureInfo.InvariantCulture);
|
||||
importVersion = Convert.ToDouble(((XmlElement) node).GetAttribute("Version"),
|
||||
System.Globalization.CultureInfo.InvariantCulture);
|
||||
if (importVersion > CurrentXmlVersion)
|
||||
{
|
||||
throw new NotSupportedException("Unsupported version: " + importVersion);
|
||||
}
|
||||
|
||||
return (XmlElement) node;
|
||||
}
|
||||
|
||||
throw new Exception("Invalid Import XML");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API can accept a parameter “MOVEFILE_DELAY_UNTIL_REBOOT ” to remove the file only after reboot.
|
||||
/// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365240(v=vs.85).aspx
|
||||
/// </summary>
|
||||
/// <param name="filePath">Entire path of the file we want to move</param>
|
||||
public static void moveFileEx(string filePath)
|
||||
{
|
||||
MoveFileEx(filePath, null, MoveFileFlags.MOVEFILE_DELAY_UNTIL_REBOOT);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// FB16400: ISO exports error in 3rd Party Software, related to FB15801
|
||||
/// By default, C# returns UTF-8 Encoding with Byte-Order Mark turned on. Need to explicitly call the overloaded constructor to turn it off:
|
||||
/// https://docs.microsoft.com/en-us/dotnet/api/system.text.encoding.utf8?view=netcore-3.1
|
||||
/// </summary>
|
||||
/// <param name="codepage"></param>
|
||||
/// <returns></returns>
|
||||
public static Encoding GetEncoding(int codepage)
|
||||
{
|
||||
var encoding = Encoding.GetEncoding(codepage);
|
||||
if (encoding is UTF8Encoding) { encoding = new UTF8Encoding(false); }
|
||||
return encoding;
|
||||
}
|
||||
|
||||
public static XmlWriter GetExportWriter(int count, double version, string software, string softwareVersion, LogDelegate logDelegate, out StringBuilder sb)
|
||||
{
|
||||
sb = new StringBuilder(5000000);
|
||||
|
||||
var xSet = new XmlWriterSettings { Indent = true, CheckCharacters = true };
|
||||
var writer = XmlWriter.Create(sb, xSet);
|
||||
|
||||
writer.WriteStartDocument();
|
||||
|
||||
writer.WriteStartElement("ExportFile");
|
||||
|
||||
writer.WriteAttributeString("TotalItems", count.ToString(System.Globalization.CultureInfo.InvariantCulture));
|
||||
writer.WriteAttributeString("Version", version.ToString(System.Globalization.CultureInfo.InvariantCulture));
|
||||
writer.WriteAttributeString("Software", software);
|
||||
writer.WriteAttributeString("SoftwareVersion", softwareVersion);
|
||||
return writer;
|
||||
}
|
||||
/// <summary>
|
||||
/// This API deletes an existing file.
|
||||
/// https://msdn.microsoft.com/en-us/library/windows/desktop/aa363915(v=vs.85).aspx
|
||||
/// </summary>
|
||||
/// <param name="filePath">Entire path of the file to be deleted.</param>
|
||||
/// <param name="lastError">Error if failed to delete file
|
||||
/// If the function succeeds, the return value is nonzero.
|
||||
/// If the function fails, the return value is zero (0).
|
||||
/// To get extended error information, call GetLastError.</param>
|
||||
public static bool deleteFile(string filePath, ref int lastError)
|
||||
{
|
||||
if (!File.Exists(filePath)) return true;
|
||||
|
||||
var deleted = DeleteFile(filePath);
|
||||
if (!deleted)
|
||||
{
|
||||
lastError = Marshal.GetLastWin32Error();
|
||||
}
|
||||
return deleted;
|
||||
}
|
||||
public delegate void LogDelegate(params object[] paramlist);
|
||||
public static void DeleteFileOrMove(string filepath, LogDelegate logfunction)
|
||||
{
|
||||
var lastError = 0;
|
||||
if (!deleteFile(filepath, ref lastError))
|
||||
{
|
||||
logfunction("failed to delete file: ", filepath, " error: ", lastError);
|
||||
moveFileEx(filepath);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// One time sql script conversion from SQLite syntax to MS SQL Server
|
||||
/// all you need to know about Regex is here - http://rubular.com/r/kcqDdLJBpx
|
||||
/// </summary>
|
||||
/// <param name="script">SQLite script file path</param>
|
||||
/// <returns></returns>
|
||||
public static string ScriptFromSQLiteToSQL(string script)
|
||||
{
|
||||
if (string.IsNullOrEmpty(script)) return script;
|
||||
|
||||
const string BLOB = "BLOB";
|
||||
const string NVCHAR = "NVCHAR";
|
||||
const string NVARCHAR = "NVARCHAR";
|
||||
const string COLLATE_NOCASE = "COLLATE NOCASE";
|
||||
const string Couldnt = "Couldn't";
|
||||
const string Couldnot = "Could not";
|
||||
const string IDENTITY = "IDENTITY(1,1)";
|
||||
const string AUTOINCREMENT = "AUTOINCREMENT";
|
||||
const string varbinary_max = "varbinary(max)";
|
||||
const string varchar_max = "varchar(max)";
|
||||
const string max = "(max)";
|
||||
const string BigInt = "bigint";
|
||||
const string Integer = "integer";
|
||||
const string Int = "int";
|
||||
const string _2048 = "(2048)";
|
||||
const string _5000 = "(5000)";
|
||||
|
||||
var sql = new StringBuilder();
|
||||
long count = 0;
|
||||
using (var inputStream = File.OpenRead(script))
|
||||
{
|
||||
using (var inputReader = new StreamReader(inputStream))
|
||||
{
|
||||
string tempLineValue;
|
||||
|
||||
while (null != (tempLineValue = inputReader.ReadLine()))
|
||||
{
|
||||
Debug.Print("Line: " + count++);
|
||||
var addGo = false;
|
||||
const string dropTable = "DROP TABLE IF EXISTS";
|
||||
const string dropTableNew = @"if exists(SELECT * FROM sysobjects where name = '{0}') DROP TABLE [dbo].[{0}];" ;
|
||||
const string insertInto = "INSERT INTO";
|
||||
const string convert = "convert(varbinary(max), {0})";
|
||||
|
||||
if (tempLineValue.StartsWith(dropTable))
|
||||
{
|
||||
var tableName = tempLineValue.Replace(dropTable, string.Empty).Replace("\"", string.Empty).Replace(";", string.Empty).Trim();
|
||||
tempLineValue = string.Format(dropTableNew, tableName);
|
||||
addGo = true;
|
||||
}
|
||||
else if (tempLineValue.StartsWith(insertInto))
|
||||
{
|
||||
|
||||
//find SQLite (or C#) date (Format: '2016-06-06 15:15:38.0630927' )
|
||||
const string patternSQLiteDate = @"(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2}).\d{3,7}";
|
||||
var regexSQLiteDate = new Regex(patternSQLiteDate, RegexOptions.IgnoreCase);
|
||||
var matchSQLiteDate = regexSQLiteDate.Match(tempLineValue);
|
||||
|
||||
while (matchSQLiteDate.Success)
|
||||
{
|
||||
var foundSQLiteDate = matchSQLiteDate.Value;
|
||||
|
||||
//find SQL date in SQLite date (Format: '2016-06-06 15:15:38.063' )
|
||||
const string patternSQLDate = @"(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})";
|
||||
var rDate = new Regex(patternSQLDate, RegexOptions.IgnoreCase);
|
||||
|
||||
var mDate = rDate.Match(foundSQLiteDate);
|
||||
if (mDate.Success)
|
||||
{
|
||||
//replace date
|
||||
tempLineValue = tempLineValue.Replace(foundSQLiteDate, mDate.Value);
|
||||
|
||||
}
|
||||
matchSQLiteDate = matchSQLiteDate.NextMatch();
|
||||
|
||||
}
|
||||
|
||||
const string patternBinary = @"[X]'\w+'";
|
||||
var regexBinary = new Regex(patternBinary, RegexOptions.IgnoreCase);
|
||||
var matchBinary = regexBinary.Match(tempLineValue);
|
||||
while (matchBinary.Success)
|
||||
{
|
||||
var matchValue = matchBinary.Value;
|
||||
var newValue = string.Format(convert, matchValue.Replace("X", string.Empty).Replace(",", string.Empty));
|
||||
tempLineValue = tempLineValue.Replace(matchValue, newValue);
|
||||
matchBinary = matchBinary.NextMatch();
|
||||
}
|
||||
addGo = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
const string patternText = @"text\s{0,}\(\w+\)";
|
||||
var regexText = new Regex(patternText, RegexOptions.IgnoreCase);
|
||||
var matchText = regexText.Match(tempLineValue);
|
||||
if (matchText.Success)
|
||||
{
|
||||
tempLineValue = tempLineValue.Replace(matchText.Value, varchar_max);
|
||||
}
|
||||
|
||||
const string patternMaxMemory = @"\[MaxMemory\]\sinteger,";
|
||||
var regexMaxMemory = new Regex(patternMaxMemory, RegexOptions.IgnoreCase);
|
||||
var matchMaxMemory = regexMaxMemory.Match(tempLineValue);
|
||||
if (matchMaxMemory.Success)
|
||||
{
|
||||
tempLineValue = tempLineValue.Replace(Integer, BigInt);
|
||||
}
|
||||
|
||||
}
|
||||
sql.AppendLine(tempLineValue);
|
||||
if (addGo) sql.AppendLine("GO");
|
||||
}
|
||||
}
|
||||
}
|
||||
return sql
|
||||
.Replace(AUTOINCREMENT, string.Empty)
|
||||
.Replace(Couldnt, Couldnot)
|
||||
.Replace(_2048, max)
|
||||
.Replace(_5000, max)
|
||||
.Replace(Integer, Int)
|
||||
.Replace(COLLATE_NOCASE, string.Empty)
|
||||
.Replace(BLOB, varbinary_max)
|
||||
.Replace(BLOB.ToLower(), varbinary_max)
|
||||
.Replace(NVCHAR, NVARCHAR)
|
||||
.Replace(NVCHAR.ToLower(), NVARCHAR).ToString();
|
||||
}
|
||||
|
||||
#region File List
|
||||
private static List<string> _fileList = new List<string>();
|
||||
|
||||
public static List<string> FileList { get => _fileList; set => _fileList = value; }
|
||||
|
||||
private static List<string> _newFileList = new List<string>();
|
||||
|
||||
public static List<string> NewFileList { get => _newFileList; set => _newFileList = value; }
|
||||
|
||||
/// <summary>
|
||||
/// recursively search directory and subdirectories and return a list of files
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
/// <param name="pattern">*.dts</param>
|
||||
/// <returns></returns>
|
||||
public static void FindFiles(string path, string pattern)
|
||||
{
|
||||
_fileList.AddRange(FindFiles(path, pattern, SearchOption.AllDirectories));
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// Search directory and return a list of files
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
/// <param name="pattern">*.dts</param>
|
||||
/// <returns></returns>
|
||||
public static List<string> FindFilesInDirectory(string path, string pattern)
|
||||
{
|
||||
return FindFiles(path, pattern, SearchOption.TopDirectoryOnly);
|
||||
}
|
||||
|
||||
private static List<string> FindFiles(string path, string pattern, SearchOption searchOption)
|
||||
{
|
||||
return Directory.GetFiles(path, "*" + pattern, searchOption).
|
||||
Select(fn => new FileInfo(fn)).
|
||||
OrderBy(f => Regex.Replace(f.Name, @"\d+", n => n.Value.PadLeft(4, '0'))).
|
||||
Select(f => f.FullName).
|
||||
ToList();
|
||||
}
|
||||
|
||||
#endregion File List
|
||||
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct RM_UNIQUE_PROCESS
|
||||
{
|
||||
public int dwProcessId;
|
||||
public System.Runtime.InteropServices.ComTypes.FILETIME ProcessStartTime;
|
||||
}
|
||||
|
||||
private const int RmRebootReasonNone = 0;
|
||||
private const int CCH_RM_MAX_APP_NAME = 255;
|
||||
private const int CCH_RM_MAX_SVC_NAME = 63;
|
||||
|
||||
private enum RM_APP_TYPE
|
||||
{
|
||||
RmUnknownApp = 0,
|
||||
RmMainWindow = 1,
|
||||
RmOtherWindow = 2,
|
||||
RmService = 3,
|
||||
RmExplorer = 4,
|
||||
RmConsole = 5,
|
||||
RmCritical = 1000
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
private struct RM_PROCESS_INFO
|
||||
{
|
||||
public RM_UNIQUE_PROCESS Process;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_APP_NAME + 1)]
|
||||
public string strAppName;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_SVC_NAME + 1)]
|
||||
public string strServiceShortName;
|
||||
|
||||
public RM_APP_TYPE ApplicationType;
|
||||
public uint AppStatus;
|
||||
public uint TSSessionId;
|
||||
[MarshalAs(UnmanagedType.Bool)]
|
||||
public bool bRestartable;
|
||||
}
|
||||
|
||||
[DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)]
|
||||
private static extern int RmRegisterResources(uint pSessionHandle,
|
||||
UInt32 nFiles,
|
||||
string[] rgsFilenames,
|
||||
UInt32 nApplications,
|
||||
[In] RM_UNIQUE_PROCESS[] rgApplications,
|
||||
UInt32 nServices,
|
||||
string[] rgsServiceNames);
|
||||
|
||||
[DllImport("rstrtmgr.dll", CharSet = CharSet.Auto)]
|
||||
private static extern int RmStartSession(out uint pSessionHandle, int dwSessionFlags, string strSessionKey);
|
||||
|
||||
[DllImport("rstrtmgr.dll")]
|
||||
private static extern int RmEndSession(uint pSessionHandle);
|
||||
|
||||
[DllImport("rstrtmgr.dll")]
|
||||
private static extern int RmGetList(uint dwSessionHandle,
|
||||
out uint pnProcInfoNeeded,
|
||||
ref uint pnProcInfo,
|
||||
[In, Out] RM_PROCESS_INFO[] rgAffectedApps,
|
||||
ref uint lpdwRebootReasons);
|
||||
|
||||
/// <summary>
|
||||
/// Find out what process(es) have a lock on the specified file.
|
||||
/// </summary>
|
||||
/// <param name="path">Path of the file.</param>
|
||||
/// <returns>Processes locking the file</returns>
|
||||
/// <remarks>See also:
|
||||
/// http://msdn.microsoft.com/en-us/library/windows/desktop/aa373661(v=vs.85).aspx
|
||||
/// http://wyupdate.googlecode.com/svn-history/r401/trunk/frmFilesInUse.cs (no copyright in code at time of viewing)
|
||||
///
|
||||
/// </remarks>
|
||||
public static List<Process> WhoIsLocking(string path)
|
||||
{
|
||||
uint handle;
|
||||
string key = Guid.NewGuid().ToString();
|
||||
List<Process> processes = new List<Process>();
|
||||
|
||||
int res = RmStartSession(out handle, 0, key);
|
||||
if (res != 0) throw new Exception("Could not begin restart session. Unable to determine file locker.");
|
||||
|
||||
try
|
||||
{
|
||||
const int ERROR_MORE_DATA = 234;
|
||||
uint pnProcInfoNeeded = 0,
|
||||
pnProcInfo = 0,
|
||||
lpdwRebootReasons = RmRebootReasonNone;
|
||||
|
||||
string[] resources = new string[] { path }; // Just checking on one resource.
|
||||
|
||||
res = RmRegisterResources(handle, (uint)resources.Length, resources, 0, null, 0, null);
|
||||
|
||||
if (res != 0) throw new Exception("Could not register resource.");
|
||||
|
||||
//Note: there's a race condition here -- the first call to RmGetList() returns
|
||||
// the total number of process. However, when we call RmGetList() again to get
|
||||
// the actual processes this number may have increased.
|
||||
res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, null, ref lpdwRebootReasons);
|
||||
|
||||
if (res == ERROR_MORE_DATA)
|
||||
{
|
||||
// Create an array to store the process results
|
||||
RM_PROCESS_INFO[] processInfo = new RM_PROCESS_INFO[pnProcInfoNeeded];
|
||||
pnProcInfo = pnProcInfoNeeded;
|
||||
|
||||
// Get the list
|
||||
res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, processInfo, ref lpdwRebootReasons);
|
||||
if (res == 0)
|
||||
{
|
||||
processes = new List<Process>((int)pnProcInfo);
|
||||
|
||||
// Enumerate all of the results and add them to the
|
||||
// list to be returned
|
||||
for (int i = 0; i < pnProcInfo; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
processes.Add(Process.GetProcessById(processInfo[i].Process.dwProcessId));
|
||||
}
|
||||
// catch the error -- in case the process is no longer running
|
||||
catch (ArgumentException) { }
|
||||
}
|
||||
}
|
||||
else throw new Exception("Could not list processes locking resource.");
|
||||
}
|
||||
else if (res != 0) throw new Exception("Could not list processes locking resource. Failed to get size of result.");
|
||||
}
|
||||
finally
|
||||
{
|
||||
RmEndSession(handle);
|
||||
}
|
||||
|
||||
return processes;
|
||||
}
|
||||
}
|
||||
}
|
||||
52
Common/DTS.CommonCore/Utils/ImageButton.cs
Normal file
52
Common/DTS.CommonCore/Utils/ImageButton.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
50
Common/DTS.CommonCore/Utils/MouseUtils.cs
Normal file
50
Common/DTS.CommonCore/Utils/MouseUtils.cs
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
233
Common/DTS.CommonCore/Utils/NetworkUtils.cs
Normal file
233
Common/DTS.CommonCore/Utils/NetworkUtils.cs
Normal file
@@ -0,0 +1,233 @@
|
||||
using DTS.Common.Utilities.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DTS.Common.Utils
|
||||
{
|
||||
//FB 18512 This calss contains utility methods, they are moved from Netwroking module
|
||||
public static class NetworkUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// returns true if the network interface is up, false otherwise
|
||||
/// stolen from FWTU
|
||||
/// </summary>
|
||||
/// <param name="ip"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsNetworkInterfaceUp(IPAddress ip)
|
||||
{
|
||||
foreach (var a in NetworkInterface.GetAllNetworkInterfaces())
|
||||
{
|
||||
try
|
||||
{
|
||||
var ipp = a.GetIPProperties();
|
||||
|
||||
if (ipp.MulticastAddresses.Count == 0)
|
||||
continue; // most of VPN adapters will be skipped
|
||||
if (!a.SupportsMulticast)
|
||||
continue; // multicast is meaningless for this type of connection
|
||||
if (OperationalStatus.Up != a.OperationalStatus)
|
||||
continue; // this adapter is off or not connected
|
||||
if (null == ipp.GetIPv4Properties())
|
||||
continue; // IPv4 is not configured on this adapter
|
||||
if (a.NetworkInterfaceType == NetworkInterfaceType.Loopback)
|
||||
continue;
|
||||
var addressBytes = ip.GetAddressBytes();
|
||||
if (4 == addressBytes.Length && addressBytes[0] == 169 && addressBytes[1] == 254) { continue; }
|
||||
if (a.GetIPProperties().UnicastAddresses.Any(i => i.Address.AddressFamily == AddressFamily.InterNetwork && IPAddress.Equals(i.Address, ip)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/// <summary>
|
||||
/// Indicates whether any network connection is available.
|
||||
/// Filter connections below a specified speed, as well as virtual network cards.
|
||||
/// </summary>
|
||||
/// <param name="minimumSpeed">The minimum speed required. Passing 0 will not filter connection using speed.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if a network connection is available; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
private static List<NetworkInterface> GetAvailableNetworkInterfaces(long minimumSpeed = 10000000)
|
||||
{
|
||||
if (!NetworkInterface.GetIsNetworkAvailable())
|
||||
return null;
|
||||
|
||||
var niList = new List<NetworkInterface>();
|
||||
foreach (var ni in NetworkInterface.GetAllNetworkInterfaces())
|
||||
{
|
||||
// discard because of standard reasons
|
||||
if ((ni.NetworkInterfaceType == NetworkInterfaceType.Loopback)
|
||||
|| (ni.NetworkInterfaceType == NetworkInterfaceType.Tunnel))
|
||||
continue;
|
||||
|
||||
// this allow to filter modems, serial, etc.
|
||||
// use 10000000 as a minimum speed for most cases
|
||||
if (ni.Speed < minimumSpeed)
|
||||
continue;
|
||||
|
||||
// discard virtual cards (virtual box, virtual pc, etc.)
|
||||
if (ni.Description.ToLower().Contains("virtual")
|
||||
|| ni.Name.ToLower().Contains("virtual")
|
||||
|| ni.Description.ToLower().Contains("bluethooth")
|
||||
|| ni.Name.ToLower().Contains("bluethooth"))
|
||||
continue;
|
||||
|
||||
// discard "Microsoft Loopback Adapter", it will not show as NetworkInterfaceType.Loopback but as Ethernet Card.
|
||||
if (ni.Description.Equals("Microsoft Loopback Adapter", StringComparison.OrdinalIgnoreCase))
|
||||
continue;
|
||||
|
||||
niList.Add(ni);
|
||||
}
|
||||
|
||||
return niList;
|
||||
}
|
||||
|
||||
private static string GetMACAddress(string id)
|
||||
{
|
||||
var macAddress = string.Empty;
|
||||
|
||||
var nics = GetAvailableNetworkInterfaces();
|
||||
|
||||
if (nics == null)
|
||||
return macAddress;
|
||||
|
||||
foreach (var nic in nics)
|
||||
{
|
||||
if (nic.Id != id) continue;
|
||||
var pa = BitConverter.ToString(nic.GetPhysicalAddress().GetAddressBytes());
|
||||
pa = pa.Replace("-", ":");
|
||||
if (string.IsNullOrEmpty(pa)) continue;
|
||||
macAddress = pa;
|
||||
break;
|
||||
}
|
||||
return macAddress;
|
||||
}
|
||||
|
||||
private static bool InvalidNIC(NetworkInterface ni)
|
||||
{
|
||||
return OperationalStatus.Up != ni.OperationalStatus || !ni.SupportsMulticast;
|
||||
}
|
||||
|
||||
public static List<HostInfo> GetAvailableHosts(bool supportMulticastOnly = false)
|
||||
{
|
||||
var nics = GetAvailableNetworkInterfaces();
|
||||
if (nics == null) { return new List<HostInfo>(); }
|
||||
var hosts = new List<HostInfo>();
|
||||
|
||||
foreach (NetworkInterface ni in nics)
|
||||
{
|
||||
if (supportMulticastOnly && InvalidNIC(ni))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
//exclude wireshark network interface, it claims it can ping ips lyingly
|
||||
if (ni.Description.Contains("Npcap")) { continue; }
|
||||
foreach (UnicastIPAddressInformation ip in ni.GetIPProperties().UnicastAddresses)
|
||||
{
|
||||
var addressBytes = ip.Address.GetAddressBytes();
|
||||
if (4 == addressBytes.Length && addressBytes[0] == 169 && addressBytes[1] == 254)
|
||||
{
|
||||
APILogger.Log($"skipping {ni.Name}\\{ni.Description} as it has a 169.254 address");
|
||||
continue;//internal only
|
||||
}
|
||||
if (ip.Address.AddressFamily == AddressFamily.InterNetwork)
|
||||
{
|
||||
GetStartAndEndAddress(ip.Address, ip.IPv4Mask, out var startAddress, out var endAddress);
|
||||
hosts.Add(new HostInfo { HostIpAddress = ip.Address.ToString(), HostNetworkId = ni.Id, HostMacAddress = GetMACAddress(ni.Id),
|
||||
StartAddress = startAddress, EndAddress = endAddress
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return hosts;
|
||||
}
|
||||
/// <summary>
|
||||
/// given an IP address and a mask returns a ip address as a string for the first address that is accessible and a string for the last address that is accessible
|
||||
/// anything between the start address and end address after doing a byte-by-byte comparison should be addressable
|
||||
/// will return empty strings if something bad happens. Is designed for 4 byte addresses but isn't necessarily limited.
|
||||
/// </summary>
|
||||
/// <param name="ip"></param>
|
||||
/// <param name="mask"></param>
|
||||
/// <param name="startAddress"></param>
|
||||
/// <param name="endAddress"></param>
|
||||
private static void GetStartAndEndAddress( IPAddress ip, IPAddress mask, out string startAddress, out string endAddress)
|
||||
{
|
||||
try
|
||||
{
|
||||
var ipByte = ip.GetAddressBytes();
|
||||
var maskBytes = mask.GetAddressBytes();
|
||||
|
||||
var startBytes = new byte[ipByte.Length];
|
||||
var endBytes = new byte[ipByte.Length];
|
||||
|
||||
for (var i = 0; i < ipByte.Length; i++)
|
||||
{
|
||||
startBytes[i] = (byte)(ipByte[i] & maskBytes[i]);
|
||||
endBytes[i] = (byte)(ipByte[i] | ~maskBytes[i]);
|
||||
}
|
||||
var startIP = new IPAddress(startBytes);
|
||||
var endIP = new IPAddress(endBytes);
|
||||
startAddress = startIP.ToString();
|
||||
endAddress = endIP.ToString();
|
||||
}
|
||||
catch( Exception ex)
|
||||
{
|
||||
startAddress = string.Empty;
|
||||
endAddress = string.Empty;
|
||||
APILogger.Log(ex);
|
||||
}
|
||||
}
|
||||
//FB 25658 Changed the method signature to return bool to determine the connection string has IP in it and it's not USB
|
||||
/// <summary>
|
||||
/// Try to parse connectionString and retrieve the IP address.
|
||||
/// </summary>
|
||||
/// <param name="connectionString"> DAS connectionString which might include the IP address or might be a USB connection string</param>
|
||||
/// <param name="ipAddress">Return the parsed IP address from connectionString</param>
|
||||
/// <returns>Returns true if was able to parse and get the IP otherwise false including the valid USB connection string </returns>
|
||||
public static bool TryParseConnectionString(string connectionString, out string ipAddress)
|
||||
{
|
||||
ipAddress = null;
|
||||
|
||||
if (string.IsNullOrEmpty(connectionString))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var uri = new Uri(connectionString);
|
||||
|
||||
if (IPAddress.TryParse(uri.Host, out _))
|
||||
{
|
||||
ipAddress = uri.Host;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
var ip = connectionString.Split(':')[0];
|
||||
|
||||
if (IPAddress.TryParse(ip, out _))
|
||||
{
|
||||
ipAddress = ip;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
38
Common/DTS.CommonCore/Utils/PNGImageUtil.cs
Normal file
38
Common/DTS.CommonCore/Utils/PNGImageUtil.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
853
Common/DTS.CommonCore/Utils/PingUtils.cs
Normal file
853
Common/DTS.CommonCore/Utils/PingUtils.cs
Normal file
@@ -0,0 +1,853 @@
|
||||
using DTS.Common.Interface.StatusAndProgressBar;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using DTS.Common.Utilities.Logging;
|
||||
|
||||
namespace DTS.Common.Utils
|
||||
{
|
||||
public class PingUtils
|
||||
{
|
||||
public static void EliminateBadHosts(ref List<HostInfo> list, string[] ips)
|
||||
{
|
||||
var hosts = list.ToArray();
|
||||
foreach (var host in hosts)
|
||||
{
|
||||
if (!HostMatchesAnyIPs(host, ips))
|
||||
{
|
||||
APILogger.Log($"Removing host {host.HostIpAddress} as it can't reach any ips");
|
||||
list.Remove(host);
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// returns false if the host can not access any ips provided after looking at it's ip address and netmask
|
||||
/// returns true if it can reach an ip or it can't be determined if it can reach an IP this might happen if someone used
|
||||
/// a host name instead of a xxx.yyy.zzz.aaa style address or an empty string somewhere
|
||||
/// </summary>
|
||||
/// <param name="host"></param>
|
||||
/// <param name="ips"></param>
|
||||
/// <returns></returns>
|
||||
protected static bool HostMatchesAnyIPs(HostInfo host, string[] ips)
|
||||
{
|
||||
if (ips.Length == 0) { return true; }
|
||||
if (string.Empty.Equals(host.StartAddress) || string.Empty.Equals(host.EndAddress)) { return true; }
|
||||
foreach (var ip in ips)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!IPAddress.TryParse(ip, out var ipToConnectTo))
|
||||
{
|
||||
//it's not a traditional format ip, give up trying to match it
|
||||
return true;
|
||||
}
|
||||
if (!IPAddress.TryParse(host.StartAddress, out var startAddress))
|
||||
{
|
||||
//start address is not a traditional format, give up trying to match
|
||||
return true;
|
||||
}
|
||||
if (!IPAddress.TryParse(host.EndAddress, out var endAddress))
|
||||
{
|
||||
//start address is not in traditional format, give up trying to match
|
||||
return true;
|
||||
}
|
||||
var startBytes = startAddress.GetAddressBytes();
|
||||
var endBytes = endAddress.GetAddressBytes();
|
||||
var ipByte = ipToConnectTo.GetAddressBytes();
|
||||
|
||||
for (var i = 0; i < startBytes.Length && i < endBytes.Length && i < ipByte.Length; i++)
|
||||
{
|
||||
if (ipByte[i] < startBytes[i] || ipByte[i] > endBytes[i]) { return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
APILogger.Log(ex);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//FB 18152 & 25642 This dictionary keeps the host info for each DAS
|
||||
public static ConcurrentDictionary<string, HostInfo> DasToHost { get; set; } = new ConcurrentDictionary<string, HostInfo>();
|
||||
public class PingDevice
|
||||
{
|
||||
|
||||
private const int MAX_PING_ATTEMPTS = 3;
|
||||
private const string ALPHABET = "abcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
#region Ping
|
||||
|
||||
public bool PingDevices(List<string> ipList)
|
||||
{
|
||||
//FB 18152 Get the avilable hosts
|
||||
var hostInfos = NetworkUtils.GetAvailableHosts();
|
||||
EliminateBadHosts(ref hostInfos, ipList?.ToArray());
|
||||
return PingAllDevices(hostInfos, ipList);
|
||||
}
|
||||
|
||||
#region Ping All Devices
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// ICMP ping
|
||||
/// </summary>
|
||||
/// <param name="hostIpAddress">host IP address</param>
|
||||
/// <param name="ipList">List of connected devices IP addresses </param>
|
||||
/// <returns>false - if as least one device disconnected</returns>
|
||||
private bool PingAllDevices(List<HostInfo> hostInfos, List<string> ipList)
|
||||
{
|
||||
var hostStringList = new List<string>();
|
||||
foreach (var host in hostInfos)
|
||||
{
|
||||
hostStringList.Add(host.HostIpAddress);
|
||||
}
|
||||
APILogger.Log($"trying to ping {string.Join(", ", ipList.ToArray())} using hosts: {string.Join(", ", hostStringList.ToArray())}");
|
||||
var allConnected = true;
|
||||
Parallel.ForEach(ipList, (item, state) =>
|
||||
{
|
||||
var lastHost = hostInfos.Last();
|
||||
//FB 18152 & 25642 Perform the ping on all the devices in all hosts
|
||||
foreach (var hostInfo in hostInfos)
|
||||
{
|
||||
var eachConnected = PingOneDevice(item, hostInfo.HostIpAddress);
|
||||
if (eachConnected)
|
||||
{
|
||||
//We found the host update the dictionary and break from the loop
|
||||
DasToHost[item] = hostInfo;
|
||||
allConnected = eachConnected;
|
||||
APILogger.Log($"pinging {string.Join(", ", ipList.ToArray())} worked for {hostInfo.HostIpAddress}");
|
||||
break;
|
||||
}
|
||||
if (hostInfo.Equals(lastHost))
|
||||
{
|
||||
//all the hosts are processed
|
||||
allConnected = eachConnected;
|
||||
}
|
||||
}
|
||||
if (!allConnected) state.Break();
|
||||
});
|
||||
if (allConnected) { APILogger.Log("An interface could ping"); }
|
||||
else { APILogger.Log("no intefaces could ping"); }
|
||||
return allConnected;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// TCP ping
|
||||
/// </summary>
|
||||
/// <param name="ipList">List of connected devices IP addresses </param>
|
||||
/// <returns>false - if as least one device disconnected</returns>
|
||||
private bool PingAllDevices(List<string> ipList)
|
||||
{
|
||||
var allConnected = true;
|
||||
Parallel.ForEach(ipList, (item, state) =>
|
||||
{
|
||||
allConnected = PingOneDevice(item);
|
||||
if (!allConnected) state.Break();
|
||||
});
|
||||
return allConnected;
|
||||
}
|
||||
|
||||
#endregion Ping All Devices
|
||||
|
||||
#region Ping One Device
|
||||
/// <summary>
|
||||
/// TCP ping
|
||||
/// </summary>
|
||||
/// <param name="deviceIpAddress">device IP addresses </param>
|
||||
/// <returns>false - if device disconnected</returns>
|
||||
private bool PingOneDevice(string deviceIpAddress)
|
||||
{
|
||||
for (var i = 0; i < MAX_PING_ATTEMPTS; i++)
|
||||
{
|
||||
var pingSender = new Ping();
|
||||
|
||||
var reply = pingSender.Send(deviceIpAddress);
|
||||
if (reply != null && reply.Status == IPStatus.Success) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ICMP ping
|
||||
/// </summary>
|
||||
/// <param name="deviceIp">device IP addresses </param>
|
||||
/// <param name="hostIpAddress">host IP address</param>
|
||||
/// <returns>false - if device disconnected</returns>
|
||||
private bool PingOneDevice(string deviceIp, string hostIpAddress)
|
||||
{
|
||||
for (var i = 0; i < MAX_PING_ATTEMPTS; i++)
|
||||
{
|
||||
if (PingICMP(IPAddress.Parse(hostIpAddress), IPAddress.Parse(deviceIp), Constants.PING_ICMP_TIMEOUT, Encoding.ASCII.GetBytes(ALPHABET)).Status == IPStatus.Success)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion Ping One Device
|
||||
|
||||
#region ICMP Ping
|
||||
|
||||
/// <summary>
|
||||
/// ICMP ping
|
||||
/// </summary>
|
||||
/// <param name="srcAddress">host address</param>
|
||||
/// <param name="destAddress">device address</param>
|
||||
/// <param name="timeout">timeout</param>
|
||||
/// <param name="buffer">Function will fail without buffer (used alphabet)</param>
|
||||
/// <param name="po">Ping options - not been usexd</param>
|
||||
/// <returns>PingReplyUtils class</returns>
|
||||
private static PingReplyUtils PingICMP(IPAddress srcAddress, IPAddress destAddress, int timeout = 5000, byte[] buffer = null, PingOptions po = null)
|
||||
{
|
||||
var ipSrc = string.Empty;
|
||||
var ipDest = string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
var ipSrcBytes = srcAddress?.GetAddressBytes() ?? new byte[0];
|
||||
if (4 <= ipSrcBytes.Length) { ipSrc = $"{ipSrcBytes[0]}.{ipSrcBytes[1]}.{ipSrcBytes[2]}.{ipSrcBytes[3]}"; }
|
||||
var destBytes = destAddress?.GetAddressBytes() ?? new byte[0];
|
||||
if (4 <= destBytes.Length) { ipDest = $"{destBytes[0]}.{destBytes[1]}.{destBytes[2]}.{destBytes[3]}"; }
|
||||
}
|
||||
catch (Exception ex) { APILogger.Log(ex); }
|
||||
if (destAddress == null || destAddress.AddressFamily != AddressFamily.InterNetwork ||
|
||||
destAddress.Equals(IPAddress.Any))
|
||||
{
|
||||
APILogger.Log($"Ping -S {ipSrc} {ipDest} failed - Argument exception");
|
||||
throw new ArgumentException(string.Format("PingAll failed on IP:{0}", srcAddress));
|
||||
}
|
||||
|
||||
//Defining pinvoke args
|
||||
var source = srcAddress == null ? 0 : BitConverter.ToUInt32(srcAddress.GetAddressBytes(), 0);
|
||||
var destination = BitConverter.ToUInt32(destAddress.GetAddressBytes(), 0);
|
||||
var sendbuffer = buffer ?? new byte[] { };
|
||||
var options = new Interop.Option
|
||||
{
|
||||
Ttl = (po == null ? (byte)255 : (byte)po.Ttl),
|
||||
Flags = (po == null ? (byte)0 : po.DontFragment ? (byte)0x02 : (byte)0) //0x02
|
||||
};
|
||||
var fullReplyBufferSize = Interop.ReplyMarshalLength + sendbuffer.Length;
|
||||
//Size of Reply struct and the transmitted buffer length.
|
||||
|
||||
|
||||
|
||||
var allocSpace = Marshal.AllocHGlobal(fullReplyBufferSize);
|
||||
// unmanaged allocation of reply size. TODO Maybe should be allocated on stack
|
||||
try
|
||||
{
|
||||
var start = DateTime.Now;
|
||||
var nativeCode = Interop.IcmpSendEcho2Ex(
|
||||
/* _In_ HANDLE IcmpHandle, */
|
||||
Interop.IcmpHandle,
|
||||
/* _In_opt_ HANDLE Event, */
|
||||
default(IntPtr),
|
||||
/* _In_opt_ PIO_APC_ROUTINE ApcRoutine, */
|
||||
default(IntPtr),
|
||||
/* _In_opt_ PVOID ApcContext */
|
||||
default(IntPtr),
|
||||
/* _In_ IPAddr SourceAddress, */
|
||||
source,
|
||||
/* _In_ IPAddr DestinationAddress, */
|
||||
destination,
|
||||
/* _In_ LPVOID RequestData, */
|
||||
sendbuffer,
|
||||
/* _In_ WORD RequestSize, */
|
||||
(short)sendbuffer.Length,
|
||||
/* _In_opt_ PIP_OPTION_INFORMATION RequestOptions, */
|
||||
ref options,
|
||||
/* _Out_ LPVOID ReplyBuffer, */
|
||||
allocSpace,
|
||||
/* _In_ DWORD ReplySize, */
|
||||
fullReplyBufferSize,
|
||||
/* _In_ DWORD Timeout */
|
||||
timeout);
|
||||
var duration = DateTime.Now - start;
|
||||
var reply = (Interop.Reply)Marshal.PtrToStructure(allocSpace, typeof(Interop.Reply));
|
||||
// Parse the beginning of reply memory to reply struct
|
||||
|
||||
byte[] replyBuffer = null;
|
||||
if (sendbuffer.Length != 0)
|
||||
{
|
||||
replyBuffer = new byte[sendbuffer.Length];
|
||||
Marshal.Copy(allocSpace + Interop.ReplyMarshalLength, replyBuffer, 0, sendbuffer.Length);
|
||||
//copy the rest of the reply memory to managed byte[]
|
||||
}
|
||||
|
||||
var errorCode = 0;
|
||||
if (0 != nativeCode)
|
||||
{
|
||||
errorCode = Marshal.GetLastWin32Error();
|
||||
}
|
||||
APILogger.Log($"Ping -S {ipSrc} {ipDest} result: {reply.Status} LastError: {errorCode}");
|
||||
return nativeCode == 0
|
||||
? new PingReplyUtils(nativeCode, reply.Status, new IPAddress(reply.Address), duration)
|
||||
: new PingReplyUtils(nativeCode, reply.Status, new IPAddress(reply.Address), reply.RoundTripTime,
|
||||
replyBuffer);
|
||||
}
|
||||
finally { Marshal.FreeHGlobal(allocSpace); /*free allocated space*/ }
|
||||
}
|
||||
|
||||
#endregion ICMP Ping
|
||||
|
||||
#region Support ICMP Ping
|
||||
|
||||
/// <summary>Interoperability Helper
|
||||
/// <see cref="http://msdn.microsoft.com/en-us/library/windows/desktop/bb309069(v=vs.85).aspx" />
|
||||
/// </summary>
|
||||
private static class Interop
|
||||
{
|
||||
private static IntPtr? _icmpHandle;
|
||||
private static int? _replyStructLength;
|
||||
|
||||
/// <summary>Returns the application legal icmp handle. Should be close by IcmpCloseHandle
|
||||
/// <see cref="http://msdn.microsoft.com/en-us/library/windows/desktop/aa366045(v=vs.85).aspx" />
|
||||
/// </summary>
|
||||
public static IntPtr IcmpHandle
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_icmpHandle == null)
|
||||
{
|
||||
_icmpHandle = IcmpCreateFile();
|
||||
//TODO Close Icmp Handle appropiate
|
||||
}
|
||||
|
||||
return _icmpHandle.GetValueOrDefault();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Returns the the marshaled size of the reply struct.</summary>
|
||||
public static int ReplyMarshalLength
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_replyStructLength == null)
|
||||
{
|
||||
_replyStructLength = Marshal.SizeOf(typeof(Reply));
|
||||
}
|
||||
return _replyStructLength.GetValueOrDefault();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[DllImport("Iphlpapi.dll", SetLastError = true)]
|
||||
private static extern IntPtr IcmpCreateFile();
|
||||
|
||||
[DllImport("Iphlpapi.dll", SetLastError = true)]
|
||||
private static extern bool IcmpCloseHandle(IntPtr handle);
|
||||
|
||||
[DllImport("Iphlpapi.dll", SetLastError = true)]
|
||||
public static extern uint IcmpSendEcho2Ex(IntPtr icmpHandle, IntPtr Event, IntPtr apcroutine,
|
||||
IntPtr apccontext, uint sourceAddress, uint destinationAddress, byte[] requestData,
|
||||
short requestSize, ref Option requestOptions, IntPtr replyBuffer, int replySize, int timeout);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
|
||||
public struct Option
|
||||
{
|
||||
public byte Ttl;
|
||||
public readonly byte Tos;
|
||||
public byte Flags;
|
||||
public readonly byte OptionsSize;
|
||||
public readonly IntPtr OptionsData;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
|
||||
public struct Reply
|
||||
{
|
||||
public readonly uint Address;
|
||||
public readonly int Status;
|
||||
public readonly int RoundTripTime;
|
||||
public readonly short DataSize;
|
||||
public readonly short Reserved;
|
||||
public readonly IntPtr DataPtr;
|
||||
public readonly Option Options;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Support ICMP Ping
|
||||
|
||||
#endregion Ping
|
||||
private static readonly object LogLock = new object();
|
||||
public static void PingLog(params string[] args)
|
||||
{
|
||||
var sb = new StringBuilder(200);
|
||||
sb.AppendFormat("=== {0} ", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"));
|
||||
foreach (var arg in args)
|
||||
{
|
||||
sb.Append(" ");
|
||||
sb.Append(arg);
|
||||
}
|
||||
sb.Append(Environment.NewLine);
|
||||
lock (LogLock)
|
||||
{
|
||||
try
|
||||
{
|
||||
File.AppendAllText("Logs/ping.log", sb.ToString());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
#region OldPing
|
||||
public void PingFunction(object o)
|
||||
{
|
||||
var td = o as PingThreadData;
|
||||
if (td == null) return;
|
||||
td.UpdateProgress(0D);
|
||||
if (null == td.DoneEvent)
|
||||
{
|
||||
td.DoneEvent = new ManualResetEvent(false);
|
||||
}
|
||||
if (null == td.CancelEvent)
|
||||
{
|
||||
td.CancelEvent = new ManualResetEvent(false);
|
||||
}
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendFormat("PingFunction ");
|
||||
foreach (var item in td.Entries)
|
||||
{
|
||||
sb.AppendFormat("{0}, ", item.IPAddress);
|
||||
}
|
||||
PingLog(sb.ToString());
|
||||
|
||||
if (0 == td.Entries.Length)
|
||||
{
|
||||
td.UpdateProgress(100D);
|
||||
Thread.Sleep(100);
|
||||
td.Complete();
|
||||
return;
|
||||
}
|
||||
// Kick off the background thread while we monitor/simulate progress.
|
||||
Task.Run(() => CheckOnline(td)).ConfigureAwait(false);
|
||||
// FB 25642 Consider the number of hosts as well
|
||||
var maxTime = Constants.PING_ICMP_TIMEOUT * td.Entries.Length * NetworkUtils.GetAvailableHosts().Count;
|
||||
if (maxTime > 0 && maxTime < 1000)
|
||||
{
|
||||
maxTime = 1000; //minimum of 1 second
|
||||
}
|
||||
// Tick along progress as if it will take MAXPINGTIME. If we finish early, great.
|
||||
var timeWaited = 0D;
|
||||
while (!td.DoneEvent.WaitOne(150, false) && !td.CancelEvent.WaitOne(0, false) )
|
||||
{
|
||||
timeWaited += 150D;
|
||||
if (timeWaited >= maxTime)
|
||||
{
|
||||
PingLog("WARNING timewaited >= maxpingtime: ", timeWaited.ToString(), ">=", maxTime.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
if (td.CancelEvent.WaitOne(0, false))
|
||||
{
|
||||
td.Complete();
|
||||
}
|
||||
//else if (timeWaited >= Maxpingtime)
|
||||
//{
|
||||
// PingLog("WARNING timewaited >= maxpingtime: ", timeWaited.ToString(), ">=",
|
||||
// Maxpingtime.ToString());
|
||||
// //td.CancelEvent.Set();
|
||||
// //td.Complete();
|
||||
// //td.DoneEvent.WaitOne();
|
||||
// Thread.Sleep(100);
|
||||
//}
|
||||
else
|
||||
{
|
||||
td.UpdateProgress(100D);
|
||||
Thread.Sleep(100);
|
||||
td.Complete();
|
||||
}
|
||||
}
|
||||
//used to hold the number of ips pinged completed while actively pinging
|
||||
private volatile int _pingsDone = 0;
|
||||
/// <summary>
|
||||
/// Pinging device from specific IP address function will use IMC protocol, TC Protocol will be used if host IP address is empty
|
||||
/// </summary>
|
||||
/// <param name="o">A <see cref="PingThreadData"/> object</param>
|
||||
private void CheckOnline(object o)
|
||||
{
|
||||
var td = o as PingThreadData;
|
||||
if (td != null && null == td.CancelEvent)
|
||||
{
|
||||
td.CancelEvent = new ManualResetEvent(false);
|
||||
}
|
||||
try
|
||||
{
|
||||
var maxPingTime = Convert.ToInt32(Constants.PING_ICMP_TIMEOUT);
|
||||
if (td == null) return;
|
||||
var ipList = new List<string>();
|
||||
foreach( var entry in td.Entries)
|
||||
{
|
||||
ipList.Add(entry.IPAddress);
|
||||
}
|
||||
var hostInfos = NetworkUtils.GetAvailableHosts();
|
||||
EliminateBadHosts(ref hostInfos, ipList.ToArray());
|
||||
if (!hostInfos.Any()) { return; }
|
||||
var lastHost = hostInfos.Last();
|
||||
var alphabet = Encoding.ASCII.GetBytes(ALPHABET);
|
||||
var toPing = td.Entries.Length;
|
||||
_pingsDone = 0;
|
||||
_ = Parallel.ForEach(td.Entries, item =>
|
||||
{
|
||||
if (td.CancelEvent.WaitOne(0, false))
|
||||
{
|
||||
td.UpdateEntry(item, PingProgressStates.Cancel, 0);
|
||||
return;
|
||||
}
|
||||
td.UpdateEntry(item, PingProgressStates.Pinging, 0);
|
||||
var succeded = false;
|
||||
var attempt = 0;
|
||||
|
||||
//FB 25642 Get available hosts
|
||||
|
||||
//avoid having to re-parse the ip address x thousand of times
|
||||
var lookup = new Dictionary<HostInfo, IPAddress>();
|
||||
foreach (var hostInfo in hostInfos)
|
||||
{
|
||||
lookup[hostInfo] = IPAddress.Parse(hostInfo.HostIpAddress);
|
||||
}
|
||||
while (!succeded && attempt < MAX_PING_ATTEMPTS && !td.CancelEvent.WaitOne(0, false))
|
||||
{
|
||||
if (td.CancelEvent.WaitOne(0, false))
|
||||
{
|
||||
td.UpdateEntry(item, PingProgressStates.Cancel, 0);
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
PingReplyUtils allReply = null;
|
||||
//FB 25642 we need to go through all the hosts
|
||||
foreach (var hostInfo in hostInfos)
|
||||
{
|
||||
if (!IPAddress.TryParse(item.IPAddress, out var ip))
|
||||
{
|
||||
attempt = MAX_PING_ATTEMPTS;
|
||||
continue;
|
||||
}
|
||||
//won't wpork if buffer is empty
|
||||
|
||||
var eachReply = PingICMP(lookup[hostInfo], ip, Constants.PING_ICMP_TIMEOUT, alphabet);
|
||||
if ((eachReply.Status == IPStatus.Success) && (eachReply.NativeCode != 0))
|
||||
{
|
||||
//we found the host, update the dictionary & break
|
||||
DasToHost[item.IPAddress] = hostInfo;
|
||||
allReply = eachReply;
|
||||
break;
|
||||
}
|
||||
if (hostInfo.Equals(lastHost))
|
||||
{
|
||||
//if it's the last host update the overall reply
|
||||
allReply = eachReply;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//If the default gateway of the network interface isn't set appropriately, the call to PingICMP will
|
||||
//return with a Status of IPStatus.Success. So, to differentiate this from a valid successful ping, we check NativeCode
|
||||
|
||||
if (allReply != null)
|
||||
{
|
||||
//FB 25642 allReply shows the ping was successfull or not
|
||||
if ((allReply.Status == IPStatus.Success) && (allReply.NativeCode != 0))
|
||||
{
|
||||
succeded = true;
|
||||
td.UpdateEntry(item, PingProgressStates.Ping_Good, allReply.RoundTripTime.Milliseconds);
|
||||
PingLog("ping succeeded: ", DasToHost[item.IPAddress]?.HostIpAddress, " -> ", item.IPAddress);
|
||||
}
|
||||
else
|
||||
{
|
||||
attempt++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PingLog("couldn't parse ip: ", item.IPAddress);
|
||||
attempt++;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
td.UpdateEntry(item, PingProgressStates.InvalidIPAddress, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (succeded)
|
||||
{
|
||||
_pingsDone++;
|
||||
td.UpdateProgress(100D * _pingsDone / toPing);
|
||||
}
|
||||
else if (MAX_PING_ATTEMPTS <= attempt)
|
||||
{
|
||||
_pingsDone++;
|
||||
td.UpdateEntry(item, PingProgressStates.NoReply, 0);
|
||||
td.UpdateProgress(100D * _pingsDone / toPing);
|
||||
PingLog("failed (maxpings <= attempt: ", item.IPAddress, " - ", MAX_PING_ATTEMPTS.ToString(), "<=", attempt.ToString());
|
||||
}
|
||||
|
||||
if (td.CancelEvent.WaitOne(0, false))
|
||||
{
|
||||
td.UpdateEntry(item, PingProgressStates.Cancel, 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
APILogger.Log(ex);
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_ = td.DoneEvent.Set();
|
||||
}
|
||||
}
|
||||
#endregion OldPing
|
||||
}
|
||||
}
|
||||
|
||||
#region PingReply
|
||||
[Serializable]
|
||||
public class PingReplyUtils
|
||||
{
|
||||
private Win32Exception _exception;
|
||||
|
||||
public PingReplyUtils()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
internal PingReplyUtils(uint nativeCode, int replystatus, IPAddress ipAddress, TimeSpan duration)
|
||||
{
|
||||
NativeCode = nativeCode;
|
||||
IpAddress = ipAddress;
|
||||
if (Enum.IsDefined(typeof(IPStatus), replystatus))
|
||||
{
|
||||
Status = (IPStatus)replystatus;
|
||||
}
|
||||
}
|
||||
|
||||
internal PingReplyUtils(uint nativeCode, int replystatus, IPAddress ipAddress, int roundTripTime,
|
||||
byte[] buffer)
|
||||
{
|
||||
NativeCode = nativeCode;
|
||||
IpAddress = ipAddress;
|
||||
RoundTripTime = TimeSpan.FromMilliseconds(roundTripTime);
|
||||
Buffer = buffer;
|
||||
if (Enum.IsDefined(typeof(IPStatus), replystatus))
|
||||
{
|
||||
Status = (IPStatus)replystatus;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>Native result from <code>IcmpSendEcho2Ex</code>.</summary>
|
||||
public uint NativeCode { get; } = 0;
|
||||
|
||||
public IPStatus Status { get; } = IPStatus.Unknown;
|
||||
|
||||
/// <summary>The source address of the reply.</summary>
|
||||
public IPAddress IpAddress { get; } = null;
|
||||
|
||||
public byte[] Buffer { get; } = null;
|
||||
|
||||
public TimeSpan RoundTripTime { get; } = TimeSpan.Zero;
|
||||
|
||||
/// <summary>Resolves the <code>Win32Exception</code> from native code</summary>
|
||||
public Win32Exception Exception
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Status != IPStatus.Success)
|
||||
{
|
||||
return _exception ?? (_exception = new Win32Exception((int)NativeCode, Status.ToString()));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (Status == IPStatus.Success)
|
||||
{
|
||||
return Status + " from " + IpAddress + " in " + RoundTripTime + " ms with " + Buffer.Length +
|
||||
" bytes";
|
||||
}
|
||||
if (Status != IPStatus.Unknown)
|
||||
{
|
||||
return Status + " from " + IpAddress;
|
||||
}
|
||||
|
||||
return Exception.Message + " from " + IpAddress;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion PingReply
|
||||
|
||||
#region Entry
|
||||
/// <summary>
|
||||
/// Trivial class to represent an IP address entry, its type, and a visual representation
|
||||
/// </summary>
|
||||
public class Entry
|
||||
{
|
||||
public string IPAddress { get; set; }
|
||||
public int DasType { get; set; }
|
||||
public DataRow DR { get; set; }
|
||||
public object UserData { get; set; }
|
||||
}
|
||||
#endregion Entry
|
||||
|
||||
#region HostInfo
|
||||
//FB 25642 & 25590 & 18152 This class encapsulate information for the host in one place
|
||||
public class HostInfo
|
||||
{
|
||||
public string HostIpAddress { get; set; } = string.Empty;
|
||||
public string HostMacAddress { get; set; } = string.Empty;
|
||||
public string HostNetworkId { get; set; } = string.Empty;
|
||||
|
||||
public string StartAddress { get; set; } = string.Empty;
|
||||
public string EndAddress { get; set; } = string.Empty;
|
||||
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region PingProgressStates
|
||||
/// <summary>
|
||||
/// The possible states for a given <see cref="Entry"/>.
|
||||
/// </summary>
|
||||
public enum PingProgressStates
|
||||
{
|
||||
Pinging,
|
||||
Connecting,
|
||||
Querying,
|
||||
Added,
|
||||
Updated,
|
||||
InvalidIPAddress,
|
||||
NoReply,
|
||||
Ping_Good,
|
||||
Online,
|
||||
Connected,
|
||||
NoConnection,
|
||||
Cancel,
|
||||
Ready,
|
||||
Armed,
|
||||
QueryFailed,
|
||||
QueryTimedOut,
|
||||
QueryComplete,
|
||||
UnexpectedMaxMemory,
|
||||
UnexpectedFirmwareVersion,
|
||||
Passed,
|
||||
ExpiredCalDate,
|
||||
ChannelCountMismatch,
|
||||
MissingModules,
|
||||
Canceled,
|
||||
NoMemory,
|
||||
AutoArmed,
|
||||
Realtime,
|
||||
ReadyToStream,
|
||||
Streaming,
|
||||
LostDock,
|
||||
Rebooting,
|
||||
UnexpectedFirstUseDate,
|
||||
InvalidRecordingMode,
|
||||
InvalidStreamingMode,
|
||||
StreamingNotAvailable
|
||||
}
|
||||
#endregion PingProgressStates
|
||||
|
||||
#region PingThreadData
|
||||
/// <summary>
|
||||
/// A wrapper class to represent the progress and feedback for ping activity from background threads.
|
||||
/// </summary>
|
||||
public class PingThreadData
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="Entry"/> list to act on.
|
||||
/// </summary>
|
||||
public Entry[] Entries { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional <see cref="System.Threading.ManualResetEvent"/> to tell the process to cancel.
|
||||
/// </summary>
|
||||
public ManualResetEvent CancelEvent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional <see cref="System.Threading.ManualResetEvent"/> to fire when the entire process is done.
|
||||
/// </summary>
|
||||
public ManualResetEvent DoneEvent { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// An optional delgate to be called whenever status text has changed.
|
||||
/// </summary>
|
||||
public SetStatusTextDelegate setStatusTextCallback;
|
||||
|
||||
/// <summary>
|
||||
/// An optional delgate to be called whenever progress has changed.
|
||||
/// </summary>
|
||||
public SetProgressValueDelegate progressCallback;
|
||||
|
||||
/// <summary>
|
||||
/// A delegate type used for reporting progress on an individual entry
|
||||
/// </summary>
|
||||
/// <param name="entry"><see cref="Entry"/> that is being reported on</param>
|
||||
/// <param name="state"><see cref="PingProgressStates"/> indicating the new state</param>
|
||||
public delegate void EntryProgressCallback(Entry entry, PingProgressStates state, long msRoundTrip);
|
||||
|
||||
/// <summary>
|
||||
/// An optional delegate to be called whenever an individual entry has changed.
|
||||
/// </summary>
|
||||
public EntryProgressCallback entryProgressCallback;
|
||||
|
||||
/// <summary>
|
||||
/// An optional delegate to be called when the entire process is complete. Called before <seealso cref="DoneEvent"/> is fired.
|
||||
/// </summary>
|
||||
public ActionCompleteDelegate completeCallback;
|
||||
|
||||
/// <summary>
|
||||
/// Wrapper for posting progress
|
||||
/// </summary>
|
||||
/// <param name="Percent">From 0 to 100 to indicate progress</param>
|
||||
public void UpdateProgress(double Percent)
|
||||
{
|
||||
progressCallback?.Invoke(Percent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wrapper for posting entry progress
|
||||
/// </summary>
|
||||
/// <param name="entry">The entry being updated</param>
|
||||
/// <param name="state">The new state</param>
|
||||
public void UpdateEntry(Entry entry, PingProgressStates state, long msResponseTime)
|
||||
{
|
||||
entryProgressCallback?.Invoke(entry, state, msResponseTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wrapper for posting complete
|
||||
/// </summary>
|
||||
public void Complete()
|
||||
{
|
||||
completeCallback?.Invoke();
|
||||
DoneEvent?.Set();
|
||||
}
|
||||
|
||||
public bool AvoidConnectPortion { get; set; } = false;
|
||||
|
||||
public bool PingFailed { get; set; } = false;
|
||||
}
|
||||
#endregion PingThreadData
|
||||
}
|
||||
|
||||
166
Common/DTS.CommonCore/Utils/SecureQueue.cs
Normal file
166
Common/DTS.CommonCore/Utils/SecureQueue.cs
Normal 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()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
82
Common/DTS.CommonCore/Utils/SerializableDictionary.cs
Normal file
82
Common/DTS.CommonCore/Utils/SerializableDictionary.cs
Normal 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
|
||||
}
|
||||
}
|
||||
77
Common/DTS.CommonCore/Utils/StopWatch.cs
Normal file
77
Common/DTS.CommonCore/Utils/StopWatch.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
73
Common/DTS.CommonCore/Utils/TestUtils.cs
Normal file
73
Common/DTS.CommonCore/Utils/TestUtils.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using DTS.Common.Interface;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DTS.Common.Utils
|
||||
{
|
||||
//FB 29410 POCO class to be used as parameter for MinUnixTime method
|
||||
public class TestModuleTimeStamp
|
||||
{
|
||||
public int TriggerTimestampSec { get; set; }
|
||||
public int TriggerTimestampNanoSec { get; set; }
|
||||
}
|
||||
public static class TestUtils
|
||||
{
|
||||
// FB15333: Add PTP/RTC timestamp column for CSV exports
|
||||
//FB 29410 refactor the orginal method to use IEnumerable<TestModuleTimeStamp> basemodules as input parameter to be able to use this method in different projects
|
||||
public static Tuple<double, double> MinUnixTime(IEnumerable<TestModuleTimeStamp> basemodules)
|
||||
{
|
||||
if (null != basemodules && basemodules.Count() > 0)
|
||||
{
|
||||
var utcAverage = basemodules.Average(module => (double)(module.TriggerTimestampSec + module.TriggerTimestampNanoSec / Common.Constants.NANOS_PER_SECOND));
|
||||
var utcStandardDeviation = basemodules.Select(module => (double)(module.TriggerTimestampSec + module.TriggerTimestampNanoSec / Common.Constants.NANOS_PER_SECOND)).StandardDeviation();
|
||||
utcStandardDeviation = utcStandardDeviation > Common.Constants.TEN_MILLIS_IN_SEC ? Common.Constants.TEN_MILLIS_IN_SEC : utcStandardDeviation; // select min within standard dev or 10ms, whichever is smaller
|
||||
|
||||
var minUnixTime = new Tuple<double, double>(basemodules.First().TriggerTimestampSec, basemodules.First().TriggerTimestampNanoSec);
|
||||
foreach (var module in basemodules)
|
||||
{
|
||||
var moduleTime = (double)(module.TriggerTimestampSec + module.TriggerTimestampNanoSec / Common.Constants.NANOS_PER_SECOND);
|
||||
if (module.TriggerTimestampSec <= minUnixTime.Item1 && module.TriggerTimestampNanoSec < minUnixTime.Item2 &&
|
||||
moduleTime > utcAverage - utcStandardDeviation)
|
||||
{
|
||||
minUnixTime = new Tuple<double, double>(module.TriggerTimestampSec, module.TriggerTimestampNanoSec);
|
||||
}
|
||||
}
|
||||
|
||||
return minUnixTime;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static string ParseROISuffix(string test)
|
||||
{
|
||||
var testitems = new string[] { };
|
||||
if (test.Length > 0 && (testitems = test.Split(":".ToCharArray(), StringSplitOptions.RemoveEmptyEntries)).Length > 3)
|
||||
{
|
||||
return testitems.Last();
|
||||
}
|
||||
//FB 18312 Get the ROI of particular event
|
||||
if (test.Length > 0)
|
||||
{
|
||||
var segments = test.Split(":".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
|
||||
var segmentWithEvent = segments.FirstOrDefault(p => p.Contains(DTS.Common.Constants.EventNumber));
|
||||
if (!string.IsNullOrWhiteSpace(segmentWithEvent))
|
||||
{
|
||||
var testSegments = segmentWithEvent.Split('_');
|
||||
if (testSegments.Any(p => p.Contains(DTS.Common.Constants.EventNumber.Replace("_", ""))))
|
||||
{
|
||||
//found an roi suffix
|
||||
if (!testSegments.Last().Contains(DTS.Common.Constants.EventNumber.Replace("_", "")))
|
||||
{
|
||||
return "_" + testSegments.Last();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
980
Common/DTS.CommonCore/Utils/Utils.cs
Normal file
980
Common/DTS.CommonCore/Utils/Utils.cs
Normal file
@@ -0,0 +1,980 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Xml;
|
||||
using DTS.Common.Enums;
|
||||
using DTS.Common.Enums.Hardware;
|
||||
using DTS.Common.Interface;
|
||||
using DTS.Common.Interface.TestDefinition;
|
||||
using Microsoft.Practices.ObjectBuilder2;
|
||||
|
||||
namespace DTS.Common.Utils
|
||||
{
|
||||
public static class Utils
|
||||
{
|
||||
private const string LINEAR_PATTERN = ".lin.";
|
||||
|
||||
|
||||
public delegate string GetIsoCodeDelegate(string binaryFileName);
|
||||
/// <summary>
|
||||
/// populates Test.Channels properties with details
|
||||
/// </summary>
|
||||
/// <param name="tmd">Test Metadata list</param>
|
||||
/// <param name="path">file location</param>
|
||||
/// <param name="GetIsoCode">function that returns an isocode given a filepath</param>
|
||||
public static void SetChannelInfo(ITestMetadata tmd, string path, GetIsoCodeDelegate GetIsoCode)
|
||||
{
|
||||
var fileList = new List<string>();
|
||||
|
||||
if (path.Contains("ALL") || path.Contains("ROI"))
|
||||
{
|
||||
fileList = FileUtils.FindFilesInDirectory(path, "chn");
|
||||
}
|
||||
else
|
||||
{
|
||||
FileUtils.FileList = new List<string>();
|
||||
FileUtils.FindFiles(path, "chn");
|
||||
fileList = FileUtils.FileList;
|
||||
}
|
||||
|
||||
var testId = tmd.TestRun.Id;
|
||||
tmd.TestRun.Channels = new List<ITestChannel>();
|
||||
tmd.TestRun.CalculatedChannels = new List<ITestChannel>();
|
||||
var channelCount = 0;
|
||||
foreach (var m in tmd.TestRun.Modules)
|
||||
{
|
||||
var sn = m.SerialNumber;
|
||||
foreach (var ch in m.Channels)
|
||||
{
|
||||
//ch.ChannelNumber = channelCount;
|
||||
ch.ModuleSerialNumber = sn;
|
||||
|
||||
var fullFileName = String.Empty;
|
||||
|
||||
var newStyle = fileList.Where(l => l.Contains(tmd.TestRun.DataType)).Any(f => f.Contains(testId + "Ch"));
|
||||
if (!newStyle)
|
||||
{
|
||||
|
||||
// DataPRO
|
||||
if (!string.IsNullOrEmpty(tmd.TestRun.Software))
|
||||
{
|
||||
var testFileList = fileList
|
||||
.Where(l => l.Contains(tmd.TestRun.DataType) && l.Contains(testId)).ToList();
|
||||
if (channelCount < testFileList.Count)
|
||||
{
|
||||
fullFileName = testFileList[channelCount];
|
||||
}
|
||||
}
|
||||
// SLICEWare
|
||||
else
|
||||
{
|
||||
var testFileList = fileList
|
||||
.Where(l => l.Contains(testId)).ToList();
|
||||
if (channelCount < testFileList.Count)
|
||||
{
|
||||
fullFileName = testFileList[channelCount];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#region new style
|
||||
|
||||
if (ch.Bridge == "SQUIB")
|
||||
{
|
||||
fullFileName = ch.Number % 2 == 0
|
||||
? fileList.Where(l => l.Contains(tmd.TestRun.DataType)).FirstOrDefault(f => f.Contains(testId + "Ch" + (ch.AbsoluteDisplayOrder + 1).ToString("000")))
|
||||
: fileList.Where(l => l.Contains(tmd.TestRun.DataType)).Where(f => f.Contains(testId + "Ch" + (ch.AbsoluteDisplayOrder + 1).ToString("000"))).ToList()[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
var matches = fileList.Where(l => l.Contains(tmd.TestRun.DataType)).Where(f => f.Contains(testId + "Ch" + (ch.AbsoluteDisplayOrder + 1).ToString("000")));
|
||||
//14581 Data for linear, cubic poly sensor differs in run test and view data tile
|
||||
//if we end up with 2 matches, one is the linear added version and one is the non linear version
|
||||
//determine which file we actually want, and use it.
|
||||
if (2 <= matches.Count())
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(ch.LinearizationFormula))
|
||||
{
|
||||
fullFileName = matches.Where(f => f.Contains(LINEAR_PATTERN)).FirstOrDefault();
|
||||
if (null == fullFileName)
|
||||
{
|
||||
//if there are two files and neither contains a .lin, then we've like hit a situation described in
|
||||
//MS 26913 where there was multiple downloads which resulted in the same channel information in two files
|
||||
//we can likely select either of the files in this case as they are probably identical
|
||||
fullFileName = matches.FirstOrDefault();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fullFileName = matches.Where(f => !f.Contains(LINEAR_PATTERN)).FirstOrDefault();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fullFileName = matches.FirstOrDefault();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion new style
|
||||
}
|
||||
|
||||
if (String.IsNullOrEmpty(fullFileName)) { ch.ErrorMessage = "File does not exist"; }
|
||||
else
|
||||
{
|
||||
ch.BinaryFilePath = Path.GetDirectoryName(fullFileName);
|
||||
ch.BinaryFileName = Path.GetFileName(fullFileName);
|
||||
if (string.IsNullOrEmpty(ch.IsoCode))
|
||||
{
|
||||
ch.IsoCode = GetIsoCode(ch.BinaryFilePath + $"\\" + ch.BinaryFileName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ch.ParentModule = m;
|
||||
ch.ParentTestSetup = tmd.TestSetup;
|
||||
tmd.TestRun.Channels.Add(ch);
|
||||
channelCount++;
|
||||
}
|
||||
foreach (var ch in m.CalculatedChannels)
|
||||
{
|
||||
var fullFileName = fileList.Where(l => l.Contains(tmd.TestRun.DataType)).FirstOrDefault(f => f.Contains(testId + "." + ch.Number.ToString()));
|
||||
if (string.IsNullOrEmpty(fullFileName)) { ch.ErrorMessage = "File does not exist"; }
|
||||
else
|
||||
{
|
||||
ch.ModuleSerialNumber = sn;
|
||||
ch.ParentModule = m;
|
||||
ch.ParentTestSetup = tmd.TestSetup;
|
||||
ch.BinaryFilePath = Path.GetDirectoryName(fullFileName);
|
||||
ch.BinaryFileName = Path.GetFileName(fullFileName);
|
||||
}
|
||||
tmd.TestRun.CalculatedChannels.Add(ch);
|
||||
}
|
||||
}
|
||||
foreach (var g in tmd.TestSetup.TestGraphs)
|
||||
{
|
||||
g.Channels = new List<ITestChannel>();
|
||||
foreach (var chId in g.ChannelIds)
|
||||
{
|
||||
var ch = (from c in tmd.TestRun.Channels where c.ChannelId == chId select c).FirstOrDefault();
|
||||
if (ch != null) g.Channels.Add(ch.Copy());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copied from DataPro
|
||||
/// per http://fogbugz/fogbugz/default.asp?8281 use 120%
|
||||
/// The idea here is to have a moving window no smaller than +/-1% of capacity
|
||||
/// Also make sure that the top of the signal is seen so make the visible window
|
||||
/// +/-120% of the signal
|
||||
/// this was abstrated here from two different locations for:
|
||||
/// 29416 Limit view of TSR AIR data to 1% of full scale in auto range mode
|
||||
/// </summary>
|
||||
/// <param name="capacity">plot expected maximum EU</param>
|
||||
/// <param name="dataMin">actual plot min value</param>
|
||||
/// <param name="dataMax">actual plot max value</param>
|
||||
/// <param name="maxAutoZoom">the scaler to apply to signal as the window max edge</param>
|
||||
/// <param name="minAutoZoom">the scaler to apply to expected maximum to form window min edges</param>
|
||||
/// <param name="currentYMin">min Y value to apply as plot constraint</param>
|
||||
/// <param name="currentYMax">max Y value to apply as plot constraint</param>
|
||||
/// <remarks>
|
||||
/// </remarks>
|
||||
public static void CalculateMinMaxAutoScaling(double capacity,
|
||||
double dataMin,
|
||||
double dataMax,
|
||||
double minAutoZoom,
|
||||
double maxAutoZoom,
|
||||
out double currentYMax,
|
||||
out double currentYMin
|
||||
)
|
||||
{
|
||||
capacity = Math.Abs(capacity);
|
||||
|
||||
var middleOfSignalExtrema = ((dataMax - dataMin) / 2.0D) + dataMin;
|
||||
var minZoom = capacity * minAutoZoom;
|
||||
var topOfMinZoomWindow = middleOfSignalExtrema + minZoom;
|
||||
var bottomOfMinZoomWindow = middleOfSignalExtrema - minZoom;
|
||||
|
||||
currentYMax = (dataMax + Math.Abs(dataMax * (1 - maxAutoZoom)) >= topOfMinZoomWindow) ?
|
||||
dataMax + Math.Abs(dataMax * (1 - maxAutoZoom)) : topOfMinZoomWindow;
|
||||
currentYMin = (dataMin - Math.Abs(dataMin * (1 - maxAutoZoom)) <= bottomOfMinZoomWindow) ?
|
||||
dataMin - Math.Abs(dataMin * (1 - maxAutoZoom)) : bottomOfMinZoomWindow;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// test code courtesy of AutoLab XML import/conversion code via Chad Ivan
|
||||
/// the CRC should be prepended to the ID (CRC + ID)
|
||||
/// </summary>
|
||||
/// <param name="ShortID">short ID</param>
|
||||
/// <returns>returns CRC bit for given string</returns>
|
||||
public static string GenerateEIDCRC(string ShortID)
|
||||
{
|
||||
var int64 = Int64.Parse(ShortID, System.Globalization.NumberStyles.HexNumber);
|
||||
var bytes = BitConverter.GetBytes(int64);
|
||||
var bitArray = new System.Collections.BitArray(bytes);
|
||||
var CRCbit = new System.Collections.BitArray(8);
|
||||
var CRCTemp = new System.Collections.BitArray(1);
|
||||
byte[] CRCbyte = new byte[1];
|
||||
|
||||
for (int i = 0; i < 56; i++)
|
||||
{
|
||||
CRCTemp[0] = CRCbit[0] ^ bitArray[i];
|
||||
CRCbit[0] = CRCbit[1];
|
||||
CRCbit[1] = CRCbit[2];
|
||||
CRCbit[2] = CRCbit[3] ^ CRCTemp[0];
|
||||
CRCbit[3] = CRCbit[4] ^ CRCTemp[0];
|
||||
CRCbit[4] = CRCbit[5];
|
||||
CRCbit[5] = CRCbit[6];
|
||||
CRCbit[6] = CRCbit[7];
|
||||
CRCbit[7] = CRCTemp[0];
|
||||
}
|
||||
|
||||
CRCbit.CopyTo(CRCbyte, 0);
|
||||
return BitConverter.ToString(CRCbyte); ;
|
||||
}
|
||||
/// <summary>
|
||||
/// handles indenting and formatting for xml text
|
||||
/// </summary>
|
||||
/// <param name="XML"></param>
|
||||
/// <returns></returns>
|
||||
public static string PrettyPrint(string XML)
|
||||
{
|
||||
var Result = "";
|
||||
|
||||
using (var MS = new MemoryStream())
|
||||
{
|
||||
using (var W = new XmlTextWriter(MS, Encoding.Unicode))
|
||||
{
|
||||
var D = new XmlDocument();
|
||||
try
|
||||
{
|
||||
// Load the XmlDocument with the XML.
|
||||
D.LoadXml(XML);
|
||||
|
||||
W.Formatting = Formatting.Indented;
|
||||
|
||||
// Write the XML into a formatting XmlTextWriter
|
||||
D.WriteContentTo(W);
|
||||
W.Flush();
|
||||
MS.Flush();
|
||||
|
||||
// Have to rewind the MemoryStream in order to read
|
||||
// its contents.
|
||||
MS.Position = 0;
|
||||
|
||||
// Read MemoryStream contents into a StreamReader.
|
||||
using (var SR = new StreamReader(MS))
|
||||
{// Extract the text from the StreamReader.
|
||||
var FormattedXML = SR.ReadToEnd();
|
||||
Result = FormattedXML;
|
||||
}
|
||||
}
|
||||
catch (XmlException) { }
|
||||
|
||||
MS.Close();
|
||||
W.Close();
|
||||
}
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
//https://docs.microsoft.com/en-us/dotnet/standard/base-types/how-to-display-milliseconds-in-date-and-time-values
|
||||
//FB 29410 Format the date time to string with milliseonds
|
||||
/// <summary>
|
||||
/// Format a DateTime with nanoseconds part to a date time string with milliseconds in a current culture
|
||||
/// </summary>
|
||||
/// <param name="dateTimeWithNanoSeconds">DateTime object with nano seconds part</param>
|
||||
/// <returns>Formatted date time in string</returns>
|
||||
public static string FormatTimeStamp(DateTime dateTimeWithNanoSeconds)
|
||||
{
|
||||
string fullPattern = DateTimeFormatInfo.CurrentInfo.LongTimePattern;
|
||||
|
||||
// Create a format similar to .fff but based on the current culture.
|
||||
string millisecondFormat = $"{NumberFormatInfo.CurrentInfo.NumberDecimalSeparator}fff";
|
||||
|
||||
// Append millisecond pattern to current culture's full date time pattern.
|
||||
fullPattern = Regex.Replace(fullPattern, "(:ss|:s)", $"$1{millisecondFormat}");
|
||||
|
||||
string time = dateTimeWithNanoSeconds.ToString(fullPattern);
|
||||
string date = dateTimeWithNanoSeconds.ToShortDateString();
|
||||
string dateTime = $"{date} {time}";
|
||||
|
||||
return dateTime;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// returns the next highest power of 2
|
||||
/// </summary>
|
||||
/// <param name="numSamples"></param>
|
||||
/// <returns></returns>
|
||||
public static int GetEnclosingPower2(int numSamples)
|
||||
{
|
||||
//return Convert.ToInt32(Math.Pow(2D, Math.Ceiling(Math.Log(Convert.ToDouble(numSamples)) / Math.Log(2D))));
|
||||
// 24418 use bitwise calc for better performance
|
||||
var next = (uint)numSamples;
|
||||
next--;
|
||||
next |= next >> 1;
|
||||
next |= next >> 2;
|
||||
next |= next >> 4;
|
||||
next |= next >> 8;
|
||||
next |= next >> 16;
|
||||
next++;
|
||||
return (int)next;
|
||||
}
|
||||
public static long GetEnclosingPower2(long numSamples)
|
||||
{
|
||||
//return Convert.ToInt32(Math.Pow(2D, Math.Ceiling(Math.Log(Convert.ToDouble(numSamples)) / Math.Log(2D))));
|
||||
// 24418 use bitwise calc for better performance
|
||||
var next = (ulong)numSamples;
|
||||
next--;
|
||||
next |= next >> 1;
|
||||
next |= next >> 2;
|
||||
next |= next >> 4;
|
||||
next |= next >> 8;
|
||||
next |= next >> 16;
|
||||
next |= next >> 32;
|
||||
next++;
|
||||
return (long)next;
|
||||
}
|
||||
public static string EscapeString(string str)
|
||||
{
|
||||
if (null == str) { return "#NOVALUE"; }
|
||||
var mustQuote = (str.Contains(",") || str.Contains("\"") || str.Contains("\r") || str.Contains("\n"));
|
||||
if (!mustQuote) return str;
|
||||
var sb = new StringBuilder();
|
||||
sb.Append("\"");
|
||||
foreach (var nextChar in str)
|
||||
{
|
||||
sb.Append(nextChar);
|
||||
if (nextChar == '"') sb.Append("\"");
|
||||
}
|
||||
sb.Append("\"");
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static string ReplaceLast(this string str, string find, string replace)
|
||||
{
|
||||
var place = str.LastIndexOf(find, StringComparison.Ordinal);
|
||||
return -1 == place
|
||||
? str
|
||||
: str.Remove(place, find.Length).Insert(place, replace);
|
||||
}
|
||||
public static string ReplaceFirst(this string str, string find, string replace)
|
||||
{
|
||||
var place = str.IndexOf(find, StringComparison.Ordinal);
|
||||
return -1 == place
|
||||
? str
|
||||
: str.Remove(place, find.Length).Insert(place, replace);
|
||||
}
|
||||
public static string ReplaceStrings(this string str, Dictionary<string, string> toReplace, Common.Enums.StringReplacementMode mode)
|
||||
{
|
||||
try
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case StringReplacementMode.All:
|
||||
toReplace.ForEach(pair => str = str.Replace(pair.Key, pair.Value));
|
||||
break;
|
||||
case StringReplacementMode.First:
|
||||
toReplace.ForEach(pair => str = str.ReplaceFirst(pair.Key, pair.Value));
|
||||
break;
|
||||
case StringReplacementMode.Last:
|
||||
toReplace.ForEach(pair => str = str.ReplaceLast(pair.Key, pair.Value));
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored, bad data in
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
public static void GetChildren(UIElement parent, Type targetType, ref List<UIElement> children)
|
||||
{
|
||||
var count = VisualTreeHelper.GetChildrenCount(parent);
|
||||
if (count <= 0) return;
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var child = (UIElement)VisualTreeHelper.GetChild(parent, i);
|
||||
if (child.GetType() == targetType) { children.Add(child); }
|
||||
|
||||
GetChildren(child, targetType, ref children);
|
||||
}
|
||||
}
|
||||
|
||||
public static void GetChildrenByName(UIElement parent, string targetName, ref List<FrameworkElement> children)
|
||||
{
|
||||
var count = VisualTreeHelper.GetChildrenCount(parent);
|
||||
if (count <= 0) return;
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var child = (UIElement)VisualTreeHelper.GetChild(parent, i);
|
||||
if (((string)child.GetType().GetProperty("Name").GetValue(child, null)).Contains(targetName)) { children.Add((FrameworkElement)child); }
|
||||
|
||||
GetChildrenByName(child, targetName, ref children);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds a Child of a given item in the visual tree.
|
||||
/// </summary>
|
||||
/// <param name="parent">A direct parent of the queried item.</param>
|
||||
/// <typeparam name="T">The type of the queried item.</typeparam>
|
||||
/// <param name="childName">x:Name or Name of child. </param>
|
||||
/// <returns>The first parent item that matches the submitted type parameter.
|
||||
/// If not matching item can be found,
|
||||
/// a null parent is being returned.</returns>
|
||||
public static T FindChild<T>(DependencyObject parent, string childName)
|
||||
where T : DependencyObject
|
||||
{
|
||||
// Confirm parent and childName are valid.
|
||||
if (parent == null) return null;
|
||||
|
||||
T foundChild = null;
|
||||
|
||||
var childrenCount = VisualTreeHelper.GetChildrenCount(parent);
|
||||
for (var i = 0; i < childrenCount; i++)
|
||||
{
|
||||
var child = VisualTreeHelper.GetChild(parent, i);
|
||||
// If the child is not of the request child type child
|
||||
if (!(child is T))
|
||||
{
|
||||
// recursively drill down the tree
|
||||
foundChild = FindChild<T>(child, childName);
|
||||
|
||||
// If the child is found, break so we do not overwrite the found child.
|
||||
if (foundChild != null) break;
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(childName))
|
||||
{
|
||||
// If the child's name is set for search
|
||||
if (child is FrameworkElement frameworkElement && frameworkElement.Name == childName)
|
||||
{
|
||||
// if the child's name is of the request name
|
||||
foundChild = (T)child;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// child element found.
|
||||
foundChild = (T)child;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return foundChild;
|
||||
}
|
||||
|
||||
public static T FindParent<T>(DependencyObject child)
|
||||
where T : DependencyObject
|
||||
{
|
||||
if (null == child) return null;
|
||||
T visParent = VisualTreeHelper.GetParent(child) as T;
|
||||
T logParent = LogicalTreeHelper.GetParent(child) as T;
|
||||
|
||||
return visParent ?? logParent ?? FindParent<T>(VisualTreeHelper.GetParent(child));
|
||||
}
|
||||
|
||||
public static T FindVisualChild<T>(DependencyObject obj)
|
||||
where T : DependencyObject
|
||||
{
|
||||
for (var i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
|
||||
{
|
||||
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
|
||||
if (child is T variable)
|
||||
return variable;
|
||||
var childOfChild = FindVisualChild<T>(child);
|
||||
if (childOfChild != null)
|
||||
return childOfChild;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static List<T> GetVisualChildren<T>(DependencyObject obj) where T : DependencyObject
|
||||
{
|
||||
var childList = new List<T>();
|
||||
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
|
||||
{
|
||||
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
|
||||
if (child is T)
|
||||
childList.Add(child as T);
|
||||
}
|
||||
return childList.Count > 0 ? childList : null;
|
||||
}
|
||||
|
||||
|
||||
public static string GetEnumDescription<T>(this T enumerationValue) where T : struct
|
||||
{
|
||||
var type = enumerationValue.GetType();
|
||||
if (!type.IsEnum)
|
||||
{
|
||||
throw new ArgumentException($"{nameof(enumerationValue)} must be of Enum type", nameof(enumerationValue));
|
||||
}
|
||||
var memberInfo = type.GetMember(enumerationValue.ToString());
|
||||
if (memberInfo.Length > 0)
|
||||
{
|
||||
var attrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
|
||||
|
||||
if (attrs.Length > 0)
|
||||
{
|
||||
return ((DescriptionAttribute)attrs[0]).Description;
|
||||
}
|
||||
}
|
||||
return enumerationValue.ToString();
|
||||
}
|
||||
|
||||
public static int FindValueInArray(double value, double[] valueArray)
|
||||
{
|
||||
for (var index = 0; index < valueArray.Length; index++)
|
||||
{
|
||||
if (value == valueArray[index])
|
||||
{
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static double StandardDeviation(this IEnumerable<double> values)
|
||||
{
|
||||
var avg = values.Average();
|
||||
return Math.Sqrt(values.Average(val => (val - avg) * (val - avg)));
|
||||
}
|
||||
|
||||
public static void CalculateClip(double[] data,
|
||||
out double clipPeak,
|
||||
out double clipStartSeconds,
|
||||
out double clipEndSeconds,
|
||||
double sampleRate,
|
||||
int preZeroDataPoints)
|
||||
{
|
||||
|
||||
clipPeak = 0;
|
||||
clipStartSeconds = 0;
|
||||
clipEndSeconds = 0;
|
||||
|
||||
/* From TDC */
|
||||
for (var index = 0; index < data.Length - Math.Floor(0.003 / (1 / sampleRate) + 0.5); index++)
|
||||
{
|
||||
var localclip = 10000D;
|
||||
|
||||
for (var localIndex = index; localIndex < index + Math.Floor(0.003 / (1 / sampleRate) + 0.5); localIndex++)
|
||||
{
|
||||
if (data[localIndex] < localclip)
|
||||
{
|
||||
localclip = data[localIndex];
|
||||
}
|
||||
}
|
||||
|
||||
if (!(localclip > clipPeak)) continue;
|
||||
clipPeak = localclip;
|
||||
clipStartSeconds = (index - preZeroDataPoints) * (1 / sampleRate);
|
||||
clipEndSeconds = clipStartSeconds + 0.003;
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly short[] ODDPARITY = {0, 1, 1, 0, 1, 0, 0, 1,
|
||||
1, 0, 0, 1, 0, 1, 1, 0 };
|
||||
|
||||
public static byte Math_CRC7(byte[] buffer, int length)
|
||||
{
|
||||
int i;
|
||||
int j;
|
||||
byte crc = 0xFF;
|
||||
|
||||
for (i = 0; i < length; i++)
|
||||
{
|
||||
var currentByte = buffer[i];
|
||||
for (j = 0; j < 8; j++)
|
||||
{
|
||||
crc = (byte)(crc << 1);
|
||||
if (0x00 != ((currentByte ^ crc) & 0x80))
|
||||
{
|
||||
crc = (byte)(crc ^ 0x09);
|
||||
}
|
||||
|
||||
currentByte <<= 1;
|
||||
}
|
||||
|
||||
crc = (byte)(crc & 0x7F);
|
||||
}
|
||||
|
||||
crc <<= 1;
|
||||
crc |= 0x01;
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the 16-bit CRC of the n passed in data bytes. N should be
|
||||
* even (if it is not, the last byte will not get included in the
|
||||
* computation). The initial_crc is the value to seed the computation
|
||||
* from - normally it will be set to 0.
|
||||
*
|
||||
* @param data the unsigned character buffer of which to compute the CRC16
|
||||
* @param n the number of bytes in data (must be even!)
|
||||
* @param initial_crc the CRC16 value to seed the computation with
|
||||
*
|
||||
* @return the updated CRC16 starting with initial_crc and computed over
|
||||
* n data bytes
|
||||
*/
|
||||
/* Slightly changed to handle array of bytes */
|
||||
public static ushort CalculateCRC(byte[] data, ushort initialCRC)
|
||||
{
|
||||
|
||||
ushort crc = 0;//initial_crc98765430.
|
||||
int i;
|
||||
|
||||
for (i = 0; i < data.Length; i += 2)
|
||||
{
|
||||
crc = Math_DoCRC16Step((ushort)((data[i + 1] << 8) | data[i]), crc);
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the 16-bit CRC of the n passed in data bytes. CCITT implementation
|
||||
* N should be
|
||||
* even (if it is not, the last byte will not get included in the
|
||||
* computation). The initial_crc is the value to seed the computation
|
||||
* from - normally it will be set to 0.
|
||||
*
|
||||
* @param data the unsigned character buffer of which to compute the CRC16
|
||||
* @param n the number of bytes in data (must be even!)
|
||||
* @param initial_crc the CRC16 value to seed the computation with
|
||||
*
|
||||
* @return the updated CRC16 starting with initial_crc and computed over
|
||||
* n data bytes
|
||||
*/
|
||||
/* Slightly changed to handle array of bytes */
|
||||
public static ushort CalculateCRCCCITT(byte[] data, ushort initialCRC)
|
||||
{
|
||||
|
||||
var crc = initialCRC;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < data.Length; i += 2)
|
||||
{
|
||||
crc = Math_DoCRCCCITTStep((ushort)((data[i + 1] << 8) | data[i]), crc);
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
public static ushort Math_DoCRC16Step(ushort cdata, ushort currentCRC)
|
||||
{
|
||||
cdata = (ushort)((cdata ^ (currentCRC & 0xff)) & 0xff);
|
||||
currentCRC >>= 8;
|
||||
|
||||
if (0 != (ODDPARITY[cdata & 0xf] ^ ODDPARITY[cdata >> 4]))
|
||||
currentCRC ^= 0xc001;
|
||||
|
||||
cdata <<= 6;
|
||||
currentCRC ^= cdata;
|
||||
cdata <<= 1;
|
||||
currentCRC ^= cdata;
|
||||
|
||||
return currentCRC;
|
||||
}
|
||||
|
||||
private static readonly ushort[] CRC_TABLE =
|
||||
{
|
||||
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5,
|
||||
0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b,
|
||||
0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 0x1231, 0x0210,
|
||||
0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
|
||||
0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c,
|
||||
0xf3ff, 0xe3de, 0x2462, 0x3443, 0x0420, 0x1401,
|
||||
0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b,
|
||||
0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
|
||||
0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6,
|
||||
0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738,
|
||||
0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 0x48c4, 0x58e5,
|
||||
0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
|
||||
0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969,
|
||||
0xa90a, 0xb92b, 0x5af5, 0x4ad4, 0x7ab7, 0x6a96,
|
||||
0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc,
|
||||
0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
|
||||
0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03,
|
||||
0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd,
|
||||
0xad2a, 0xbd0b, 0x8d68, 0x9d49, 0x7e97, 0x6eb6,
|
||||
0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
|
||||
0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a,
|
||||
0x9f59, 0x8f78, 0x9188, 0x81a9, 0xb1ca, 0xa1eb,
|
||||
0xd10c, 0xc12d, 0xf14e, 0xe16f, 0x1080, 0x00a1,
|
||||
0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
|
||||
0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c,
|
||||
0xe37f, 0xf35e, 0x02b1, 0x1290, 0x22f3, 0x32d2,
|
||||
0x4235, 0x5214, 0x6277, 0x7256, 0xb5ea, 0xa5cb,
|
||||
0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
|
||||
0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447,
|
||||
0x5424, 0x4405, 0xa7db, 0xb7fa, 0x8799, 0x97b8,
|
||||
0xe75f, 0xf77e, 0xc71d, 0xd73c, 0x26d3, 0x36f2,
|
||||
0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
|
||||
0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9,
|
||||
0xb98a, 0xa9ab, 0x5844, 0x4865, 0x7806, 0x6827,
|
||||
0x18c0, 0x08e1, 0x3882, 0x28a3, 0xcb7d, 0xdb5c,
|
||||
0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
|
||||
0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0,
|
||||
0x2ab3, 0x3a92, 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d,
|
||||
0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07,
|
||||
0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
|
||||
0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba,
|
||||
0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55, 0x5e74,
|
||||
0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
|
||||
};
|
||||
|
||||
public static ushort Math_DoCRCCCITTStep(ushort cdata, ushort current_crc)
|
||||
{
|
||||
return (ushort)((ushort)(current_crc << 8) ^ CRC_TABLE[(ushort)(((ushort)(current_crc >> 8) ^ cdata) & 0xFF)]);
|
||||
}
|
||||
|
||||
public static void FilterDataArray(ref double[] data, double sampleRate, double filterFrequency)
|
||||
{
|
||||
/********************************/
|
||||
/*** MCW NHTSA FILTER ROUTINE ***/
|
||||
/********************************/
|
||||
|
||||
//
|
||||
// CFILTER.C cfc filter program
|
||||
//
|
||||
// Filters data using SAE J211 algorithym (4 pole phaseless lo pass).
|
||||
// "Pads" data with 5% of the original number of points at the beginning with the first point
|
||||
// and 5% of the original number of points at the end with the last point
|
||||
// to "squelch" out filter start up spikes.
|
||||
// Previously read data file from disc, filtered and wrote new data file to disc.
|
||||
// NOW read/writes to array(s) in memory.
|
||||
// Optionally zeros baseline by subtracting the average of the first 5%
|
||||
// of the original number of points; SKIPS data which is "padded" by this routine
|
||||
// Optionally scales the data after filtering. SCALE = 1.0 skips to avoid unnecessary calc's.
|
||||
//
|
||||
// "syntax:"
|
||||
//
|
||||
// cfilter(INdata, SCALE, OUTdata, SampleRate, CFCnumb, BaseLine, NumPoints);
|
||||
//
|
||||
// INdata (pointer?): array (double) of data to be filtered
|
||||
// SCALE (double): a number multiplied by the above array after it is filtered.
|
||||
// Useful for "last minute" scaling and/or inversion
|
||||
// Outdata (pointer?): array (double) into which the results are stored.
|
||||
// INdata can = OUTdata, INdata will then be overwrittten.
|
||||
// SampleRate (double): the frequency(in samples per second) at which the INdata were sampled.
|
||||
// CFCnumb (double): SAE Channel Filter Class;
|
||||
// 60, 180, 600, or 1000; ONLY these values have been validated.
|
||||
// BaseLine (char): a single character; ONLY "Y" or "y" will zero baseline;
|
||||
// (This started as a command line style FORTRAN program and used to "ask"
|
||||
// the user. Some day this should be changed to an integer (1 or 0) switch.)
|
||||
// NumPoints (int): the number of points in the input array (INdata);
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// int cfilter(double *indata, double calib, double *out, double freq,
|
||||
// double cfc, char baseline[2], int n)
|
||||
// {
|
||||
double[] Z;
|
||||
double[] A;
|
||||
double t, pi, wd, wa, d0, d1, d2, e1, e2;
|
||||
double f1, f2, f3, f4, f5;
|
||||
int data_end, i, j, k, pad;
|
||||
double[] NewDataArray;
|
||||
var calib = 1.0;
|
||||
var CFC = filterFrequency * 0.6;
|
||||
|
||||
// The set-up of the formulas.
|
||||
|
||||
t = 1.0 / sampleRate;
|
||||
pi = 4.0 * Math.Atan(1.0);
|
||||
wd = 2.0 * pi * CFC * 2.0775;
|
||||
wa = Math.Sin(wd * (t / 2.0)) / Math.Cos(wd * (t / 2.0)); // The original formula
|
||||
d0 = wa * wa / (1 + Math.Sqrt(2.0) * wa + wa * wa);
|
||||
d1 = 2.0 * d0;
|
||||
d2 = d0;
|
||||
e1 = -2.0 * (wa * wa - 1) / (1 + Math.Sqrt(2.0) * wa + wa * wa);
|
||||
e2 = (-1 + Math.Sqrt(2.0) * wa - wa * wa) / (1 + Math.Sqrt(2.0) * wa + wa * wa);
|
||||
|
||||
data_end = data.Length - 1; // data_end is used for loop counting corresponding
|
||||
// to the number of lines that the data file has
|
||||
pad = (int)(0.05 * data.Length);
|
||||
Z = new double[pad + pad + data.Length];
|
||||
A = new double[pad + pad + data.Length];
|
||||
NewDataArray = new double[pad + pad + data.Length];
|
||||
|
||||
for (i = 0; i <= pad - 1; i++) // pad newdata at beginning
|
||||
{ // w/ first pt of indata
|
||||
NewDataArray[i] = data[i];
|
||||
}
|
||||
|
||||
for (i = pad; i <= data_end + pad - 1; i++) // Copy indata into newdata
|
||||
{ // after initial pad
|
||||
NewDataArray[i] = data[i - (pad - 1)];
|
||||
}
|
||||
|
||||
for (i = data_end + pad; i <= data_end + 2 * pad - 1; i++) // pad newdata at end
|
||||
{ // w/ last pt of indata
|
||||
NewDataArray[i] = data[data_end - 1];
|
||||
}
|
||||
|
||||
data_end = data_end + 2 * pad;
|
||||
|
||||
// Data Passed Through Filter Backward
|
||||
|
||||
for (j = data_end - 2; j >= 0; j--)
|
||||
{
|
||||
f1 = d0 * NewDataArray[j]; // Breaking
|
||||
f2 = d1 * NewDataArray[j + 1]; // down
|
||||
f3 = d2 * NewDataArray[j + 2]; // a big
|
||||
f4 = e1 * Z[j + 1]; // formula
|
||||
f5 = e2 * Z[j + 2]; // into smaller "groups".
|
||||
Z[j] = f1 + f2 + f3 + f4 + f5; // Putting the "groups" back together
|
||||
}
|
||||
Z[data_end - 1] = Z[data_end - 3];
|
||||
Z[data_end - 2] = Z[data_end - 3];
|
||||
|
||||
// Data Passed Through Filter Forward
|
||||
|
||||
for (k = 3; k <= data_end; k++)
|
||||
{
|
||||
f1 = d0 * Z[k]; // Breaking
|
||||
f2 = d1 * Z[k - 1]; // down
|
||||
f3 = d2 * Z[k - 2]; // a big
|
||||
f4 = e1 * A[k - 1]; // formula
|
||||
f5 = e2 * A[k - 2]; // into smaller "groups".
|
||||
A[k] = f1 + f2 + f3 + f4 + f5; // Putting the "groups" back together
|
||||
}
|
||||
A[1] = A[3]; // This sets the first data points to the same
|
||||
A[2] = A[3]; // value as the 3rd value. Without this, the
|
||||
|
||||
i = 0;
|
||||
for (k = pad; k <= data_end - pad; k++) // DO loop to write output file
|
||||
{
|
||||
if (calib != 1.0) //
|
||||
{ // Multiplying by the calibration factor
|
||||
A[k] = A[k] * calib; // Unless factor = 1.0
|
||||
} // AFTER the filtering has been finished
|
||||
|
||||
NewDataArray[i] = A[k];
|
||||
i++;
|
||||
}
|
||||
|
||||
Array.Copy(NewDataArray, 0, data, 0, data.Length);
|
||||
|
||||
}
|
||||
|
||||
public static bool IsLike(string text, string pattern, bool caseSensitive = false)
|
||||
{
|
||||
//Check for empties
|
||||
if (string.IsNullOrWhiteSpace(text) || string.IsNullOrWhiteSpace(pattern)) return true;
|
||||
|
||||
//Prepare for regex
|
||||
pattern = pattern.Replace(".", @"\.");
|
||||
pattern = pattern.Replace("(", @"\(");
|
||||
pattern = pattern.Replace(")", @"\)");
|
||||
pattern = pattern.Replace("?", ".");
|
||||
//pattern = pattern.Replace("*", ".*?");
|
||||
pattern = pattern.Replace(@"\", @"\\");
|
||||
pattern = pattern.Replace(" ", @"\s");
|
||||
try
|
||||
{
|
||||
return new Regex(pattern, caseSensitive ? RegexOptions.None : RegexOptions.IgnoreCase).IsMatch(text);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool? IsClockSynced(IDictionary<InputClockSource, bool> clocksyncs, ClockSyncProfile profile, HardwareTypes hwType)
|
||||
{
|
||||
if (HardwareTypes.SLICE_Pro_Distributor == hwType) { return null; }
|
||||
if ( HardwareTypes.SLICE6DB3 == hwType) { return null; }
|
||||
if ((HardwareTypes.SLICE6_Base == hwType || HardwareTypes.SLICE6DB == hwType || HardwareTypes.SLICE6DB_InDummy == hwType) && profile == ClockSyncProfile.Manual)
|
||||
{
|
||||
//18030 S6, S6DB can only do PTP so "manual" is always PTP
|
||||
profile = ClockSyncProfile.Auto_E2E;
|
||||
}
|
||||
bool? synced = null;
|
||||
switch (profile)
|
||||
{
|
||||
case ClockSyncProfile.Master_E2E:
|
||||
break;
|
||||
case ClockSyncProfile.Auto_E2E:
|
||||
case ClockSyncProfile.Slave_E2E:
|
||||
synced = clocksyncs[InputClockSource.PTP];
|
||||
break;
|
||||
case ClockSyncProfile.EXT_PPS:
|
||||
case ClockSyncProfile.Master_E2E_EXT_PPS:
|
||||
synced = clocksyncs[InputClockSource.OnePPS];
|
||||
break;
|
||||
case ClockSyncProfile.GPS:
|
||||
case ClockSyncProfile.Master_E2E_GPS:
|
||||
synced = clocksyncs[InputClockSource.GPS];
|
||||
break;
|
||||
case ClockSyncProfile.IRIG:
|
||||
case ClockSyncProfile.Master_E2E_IRIG:
|
||||
synced = clocksyncs[InputClockSource.IRIG];
|
||||
break;
|
||||
case ClockSyncProfile.GPS_EXT_PPS:
|
||||
case ClockSyncProfile.Master_E2E_GPS_EXT_PPS:
|
||||
synced = clocksyncs[InputClockSource.GPS] &&
|
||||
clocksyncs[InputClockSource.OnePPS];
|
||||
break;
|
||||
case ClockSyncProfile.IRIG_EXT_PPS:
|
||||
case ClockSyncProfile.Master_E2E_IRIG_EXT_PPS:
|
||||
synced = clocksyncs[InputClockSource.IRIG] &&
|
||||
clocksyncs[InputClockSource.OnePPS];
|
||||
break;
|
||||
}
|
||||
return synced;
|
||||
}
|
||||
|
||||
public static bool IsTmNSString(string tmns)
|
||||
{
|
||||
return tmns.StartsWith("(") &&
|
||||
tmns.EndsWith(")") &&
|
||||
tmns.TrimStart(new[] { '(' }).TrimEnd(new[] { ')' }).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).All(sval => uint.TryParse(sval, out var uval) && uval < 65536);
|
||||
}
|
||||
|
||||
const double _1 = 0.1;
|
||||
const double _2 = 0.01;
|
||||
const double _3 = 0.001;
|
||||
const double _4 = 0.0001;
|
||||
const double _5 = 0.00001;
|
||||
const double _6 = 0.000001;
|
||||
const double _7 = 0.0000001;
|
||||
public static bool EqualsDigitPrecision(this double left, double right, uint precision)
|
||||
{
|
||||
var diff = Math.Abs(left - right);
|
||||
switch(precision)
|
||||
{
|
||||
case 0:
|
||||
return (int)left == (int)right;
|
||||
case 1:
|
||||
return diff < _1;
|
||||
case 2:
|
||||
return diff < _2;
|
||||
case 3:
|
||||
return diff < _3;
|
||||
case 4:
|
||||
return diff < _4;
|
||||
case 5:
|
||||
return diff < _5;
|
||||
case 6:
|
||||
return diff < _6;
|
||||
default:
|
||||
case 7:
|
||||
return diff < _7;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
7
Common/DTS.CommonCore/Utils/XMLUtils.cs
Normal file
7
Common/DTS.CommonCore/Utils/XMLUtils.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace DTS.Common.Utils
|
||||
{
|
||||
public class XMLUtils
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user