添加 MX-PD-盘古 项目文件

将 MX-PD-盘古 - new 目录下的所有文件添加到主仓库
This commit is contained in:
Shi.Ji
2026-05-18 11:43:09 +08:00
parent 03632a379d
commit e31d3560bb
739 changed files with 99783 additions and 0 deletions

View File

@@ -0,0 +1,97 @@
using MaxwellFramework.Core.Common;
using MwFramework.Controls.ControlCanvas.Model;
using MwFramework.ManagerService;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MainShell.Common.Display.Models
{
public class CameraAxisDevices : ViewModelBase
{
List<HardwareDevice> _hardwareDeviceList = new List<HardwareDevice>();
HardwareDevice _selectedHardwareDevice = new HardwareDevice();
private ObservableCollection<object> _shapes = new ObservableCollection<object>();
private int _selectedIndex = 0;
private bool _cameraSwitchEnable = true;
public List<HardwareDevice> HardwareDeviceList
{
get { return _hardwareDeviceList; }
set
{
if (_hardwareDeviceList == value)
{
return;
}
_hardwareDeviceList = value;
if (_hardwareDeviceList != null && _hardwareDeviceList.Count > 0)
{
_selectedIndex = 0;
SelectedHardwareDevice = _hardwareDeviceList[_selectedIndex];
RaisePropertyChanged(nameof(SelectedIndex));
}
RaisePropertyChanged(nameof(HardwareDeviceList));
}
}
public HardwareDevice SelectedHardwareDevice
{
get { return _selectedHardwareDevice; }
set
{
if (_selectedHardwareDevice != null && _selectedHardwareDevice.Camera != null && value != null && value.Camera != null)
{
}
_selectedHardwareDevice = value; RaisePropertyChanged(nameof(SelectedHardwareDevice));
}
}
public ObservableCollection<object> Shapes
{
get { return _shapes; }
set
{
if (_shapes != value) { _shapes = value; RaisePropertyChanged(nameof(Shapes)); }
}
}
private ObservableCollection<PrimShape> _regions = new ObservableCollection<PrimShape>();
public ObservableCollection<PrimShape> Regions
{
get { return _regions; }
set
{
SetAndNotify(ref _regions, value);
}
}
public int SelectedIndex
{
get { return _selectedIndex; }
set
{
_selectedIndex = value;
if (HardwareDeviceList != null && HardwareDeviceList.Count > 0 && _selectedIndex >= 0)
{
if (_selectedIndex < HardwareDeviceList.Count)
{
SelectedHardwareDevice = HardwareDeviceList[_selectedIndex];
}
}
RaisePropertyChanged(nameof(SelectedIndex));
}
}
public bool CameraSwitchEnable
{
get { return _cameraSwitchEnable; }
set { _cameraSwitchEnable = value; RaisePropertyChanged(nameof(CameraSwitchEnable)); }
}
}
}

View File

@@ -0,0 +1,30 @@
<UserControl x:Class="MainShell.Common.Display.View.CameraAxisView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MainShell.Common.Display.View"
xmlns:control="http://www.maxwell.com/controls"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<CheckBox Panel.ZIndex="2" IsChecked="{Binding IsOpen, UpdateSourceTrigger=PropertyChanged}" Cursor="Hand" Margin="2,2" Foreground="White" HorizontalAlignment="Right" VerticalAlignment="Top">
<TextBlock Text="移动"/>
</CheckBox>
<control:CameraAxisControlA Name="cameraAxisControl"
IsShowDoubleLine="False"
IsAxisControlLDBVisible2="Visible"
Cameras="{Binding CameraAxisDevices.HardwareDeviceList}"
TabSelectedIndex ="{Binding CameraAxisDevices.SelectedIndex}"
Items="{Binding CameraAxisDevices.Shapes,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
Regions="{Binding CameraAxisDevices.Regions}" ShapeThickness="1" IsShowSolidLine="True" DrawInConcurrency="False"
>
<i:Interaction.Triggers>
<i:EventTrigger EventName="RecordPix">
<i:InvokeCommandAction Command="{Binding OnLeftMouseDownCommand}" CommandParameter="{Binding ElementName=cameraAxisControl}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</control:CameraAxisControlA>
</Grid>
</UserControl>

View File

@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace MainShell.Common.Display.View
{
/// <summary>
/// CameraAxisView.xaml 的交互逻辑
/// </summary>
public partial class CameraAxisView : UserControl
{
public CameraAxisView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,22 @@
<UserControl x:Class="MainShell.Common.Display.View.CameraView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MainShell.Common.Display.View"
xmlns:mw="http://www.maxwell.com/controls"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Border BorderThickness="1" BorderBrush ="#B0B9C4">
<mw:DesignPanel x:Name="cameraDesignPanel"
Source="{Binding DisplayImage}"
Items="{Binding Shapes}"
IsVirtualPix="Visible"
IsShowSolidLine="True"
IsCrossingLineVisible="False"
IsNonRectCrossingLineVisible="False"
IsImagePix="Collapsed" />
</Border>
</Grid>
</UserControl>

View File

@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace MainShell.Common.Display.View
{
/// <summary>
/// CameraView.xaml 的交互逻辑
/// </summary>
public partial class CameraView : UserControl
{
public CameraView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,323 @@
using MainShell.Common;
using MainShell.Common.Display.Models;
using MainShell.Log;
using MainShell.Motion;
using Maxwell.SemiFramework.DefaultConfig.Vision;
using MwFramework.Controls.Components;
using MwFramework.Controls.ControlCanvas.Model;
using MwFramework.Controls.UIControl;
using MwFramework.Device;
using MwFramework.ManagerService;
using Stylet;
using StyletIoC;
using System;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
namespace MainShell.Common.Display.ViewModel
{
public class CameraAxisViewModel : Screen
{
private const double DefaultFovX = 14.1 * 10;
private const double DefaultFovY = 10.3 * 10;
private const string SafeAxisMotionNotInjectedMessage = "SafeAxisMotion is not injected.";
private readonly object _moveLock = new object();
private CameraAxisDevices _cameraAxisDevices = new CameraAxisDevices();
private double _xPixelPos;
private double _yPixelPos;
private double _cameraXIndex;
private double _cameraYIndex;
private GetConvertValue _convertFunXDelegate;
private GetConvertValue _convertFunYDelegate;
private ObservableCollection<PrimShape> _regions = new ObservableCollection<PrimShape>();
private bool _isCameraEnable = true;
private Visibility _controlVisibility = Visibility.Visible;
private bool _isOpen;
[Inject]
public SafeAxisMotion SafeAxisMotion { get; set; }
public CameraAxisViewModel()
{
ConvertFunXDelegate = new GetConvertValue(ConvertFunX);
ConvertFunYDelegate = new GetConvertValue(ConvertFunY);
OnLeftMouseDownCommand = new DelegateCommand<object>(OnLeftMouseDown);
}
public CameraAxisDevices CameraAxisDevices
{
get { return _cameraAxisDevices; }
set { SetAndNotify(ref _cameraAxisDevices, value); }
}
public double XPixelPos
{
get { return _xPixelPos; }
set { SetAndNotify(ref _xPixelPos, value); }
}
public double YPixelPos
{
get { return _yPixelPos; }
set { SetAndNotify(ref _yPixelPos, value); }
}
public double CameraXIndex
{
get { return _cameraXIndex; }
set { SetAndNotify(ref _cameraXIndex, value); }
}
public double CameraYIndex
{
get { return _cameraYIndex; }
set { SetAndNotify(ref _cameraYIndex, value); }
}
public GetConvertValue ConvertFunXDelegate
{
get { return _convertFunXDelegate; }
set { SetAndNotify(ref _convertFunXDelegate, value); }
}
public GetConvertValue ConvertFunYDelegate
{
get { return _convertFunYDelegate; }
set { SetAndNotify(ref _convertFunYDelegate, value); }
}
public ObservableCollection<PrimShape> Regions
{
get { return _regions; }
set { SetAndNotify(ref _regions, value); }
}
public bool IsCameraEnable
{
get { return _isCameraEnable; }
set { SetAndNotify(ref _isCameraEnable, value); }
}
public Visibility ControlVisibility
{
get { return _controlVisibility; }
set { SetAndNotify(ref _controlVisibility, value); }
}
public ICommand OnLeftMouseDownCommand { get; private set; }
public bool IsOpen
{
get { return _isOpen; }
set { SetAndNotify(ref _isOpen, value); }
}
public void OnLeftMouseDown(object sender)
{
try
{
HardwareDevice selectedDevice;
if (!TryGetSelectedDevice(out selectedDevice))
{
return;
}
CameraAxisControlA controlA = sender as CameraAxisControlA;
if (controlA == null)
{
return;
}
Point pixelPos = new Point(controlA.YPixPos, controlA.XPixPos);
Task.Run(() => MoveToPixelPoint(selectedDevice, pixelPos, false));
}
catch (Exception ex)
{
LogManager.LogSysError(ex);
ShowOperationFailed(ex.Message);
}
}
public void MovePosAction()
{
try
{
HardwareDevice selectedDevice;
if (!TryGetSelectedDevice(out selectedDevice))
{
return;
}
Point pixelPos = new Point(YPixelPos, XPixelPos);
Task.Run(() => MoveToPixelPoint(selectedDevice, pixelPos, true));
}
catch (Exception ex)
{
LogManager.LogSysError(ex);
ShowOperationFailed(ex.Message);
}
}
public double ConvertFunX(double interval)
{
try
{
HardwareDevice selectedDevice;
if (!TryGetSelectedDevice(out selectedDevice))
{
return 0;
}
string name = selectedDevice.Camera.Id;
double dx;
double dy;
VisionProcess.Instance.GetCameraResolution(name, out dx, out dy);
double pixelX = interval / Math.Abs(dx);
return pixelX;
}
catch (Exception ex)
{
LogManager.LogSysError(ex);
ShowOperationFailed(ex.Message);
return 0;
}
}
public double ConvertFunY(double interval)
{
try
{
HardwareDevice selectedDevice;
if (!TryGetSelectedDevice(out selectedDevice))
{
return 0;
}
string name = selectedDevice.Camera.Id;
double dx;
double dy;
VisionProcess.Instance.GetCameraResolution(name, out dx, out dy);
double pixelY = interval / Math.Abs(dy);
return pixelY;
}
catch (Exception ex)
{
LogManager.LogSysError(ex);
ShowOperationFailed(ex.Message);
return 0;
}
}
private bool TryGetSelectedDevice(out HardwareDevice selectedDevice)
{
selectedDevice = CameraAxisDevices != null ? CameraAxisDevices.SelectedHardwareDevice : null;
if (!IsOpen || selectedDevice == null)
{
return false;
}
if (selectedDevice.Camera == null || selectedDevice.Camera.Camera == null)
{
ShowDeviceNotInitialized();
return false;
}
if (!selectedDevice.Camera.Camera.IsGrabbing)
{
LocalizedMessageBox.Show(MessageKey.VisionCameraNotGrabbing, MessageKey.TitleWarning, MessageBoxButton.OK, MessageBoxImage.Warning);
return false;
}
if (selectedDevice.AxisX == null || selectedDevice.AxisY == null)
{
ShowDeviceNotInitialized();
return false;
}
return true;
}
private void MoveToPixelPoint(HardwareDevice selectedDevice, Point pixelPos, bool requireConfirmation)
{
lock (_moveLock)
{
try
{
IAxis axisX = selectedDevice.AxisX;
IAxis axisY = selectedDevice.AxisY;
Point rulerPos = new Point(axisX.State.ActualPos, axisY.State.ActualPos);
Point newRulerPoint = Algorithm.Instance.MoveToPixelPoint(selectedDevice.Camera.Id, rulerPos, pixelPos);
if (requireConfirmation)
{
double dx = newRulerPoint.X - rulerPos.X;
double dy = newRulerPoint.Y - rulerPos.Y;
if (!ValidateMoveRange(dx, dy))
{
return;
}
if (!ConfirmMove(newRulerPoint))
{
return;
}
}
if (SafeAxisMotion == null)
{
InvalidOperationException exception = new InvalidOperationException(SafeAxisMotionNotInjectedMessage);
LogManager.LogSysError(exception);
ShowOperationFailed(SafeAxisMotionNotInjectedMessage);
return;
}
SafeAxisMotion.SafeMove(
MotionMoveRequest.ForAxis(axisX, newRulerPoint.X),
MotionMoveRequest.ForAxis(axisY, newRulerPoint.Y));
}
catch (Exception ex)
{
LogManager.LogSysError(ex);
ShowOperationFailed(ex.Message);
}
}
}
private static bool ValidateMoveRange(double dx, double dy)
{
if (Math.Abs(dx) > DefaultFovX || Math.Abs(dy) > DefaultFovY)
{
LocalizedMessageBox.Show(MessageKey.ParamOutOfRange, MessageKey.TitleWarning, MessageBoxButton.OK, MessageBoxImage.Warning);
return false;
}
return true;
}
private static bool ConfirmMove(Point newRulerPoint)
{
string message = string.Format("新点的坐标({0},{1}),是否移动?", newRulerPoint.X, newRulerPoint.Y);
return MwMessageBox.Show(message, LanguageResourceHelper.GetString(MessageKey.TitleConfirm), MessageBoxButton.OKCancel, MessageBoxImage.Question, MessageBoxResult.Cancel) == MessageBoxResult.OK;
}
private static void ShowDeviceNotInitialized()
{
LocalizedMessageBox.Show(MessageKey.DeviceNotInitialized, MessageKey.TitleWarning, MessageBoxButton.OK, MessageBoxImage.Warning);
}
private static void ShowOperationFailed(string message)
{
if (string.IsNullOrWhiteSpace(message))
{
LocalizedMessageBox.Show(MessageKey.CommonOperationFailed, MessageKey.TitleError, MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
MwMessageBox.Show(message, LanguageResourceHelper.GetString(MessageKey.TitleError), MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}

View File

@@ -0,0 +1,317 @@
using MainShell.EventArgsFolder;
using MainShell.Log;
using MainShell.Vision;
using MwFramework.Device.Model;
using MwFramework.Controls.ControlCanvas.DrawingControl;
using Stylet;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media.Imaging;
using AppCameraType = MainShell.Common.CameraType;
using System.Windows.Media;
namespace MainShell.Common.Display.ViewModel
{
public class CameraViewModel : Screen, IHandle<DisplayImageEventArgs>, IHandle<DisplayFindTemplateResultEventArgs>
{
private readonly IEventAggregator _eventAggregator;
public AppCameraType CameraType { get; }
private WriteableBitmap currentImage;
private WriteableBitmap _displayImage;
public WriteableBitmap DisplayImage
{
get { return _displayImage; }
set
{
SetAndNotify(ref _displayImage, value);
}
}
private ObservableCollection<object> _shapes = new ObservableCollection<object>();
public ObservableCollection<object> Shapes
{
get { return _shapes; }
set
{
if (_shapes != value) { _shapes = value; OnPropertyChanged(nameof(Shapes)); }
}
}
/// <summary>
/// 根据 CameraData 更新并返回 WriteableBitmap。
/// 优化要点:
/// - 重用已有 WriteableBitmap当尺寸与像素格式相同时以减少分配。
/// - 校验参数与缓冲区长度。
/// - 捕获并记录异常,失败时返回 null不抛出
/// </summary>
public WriteableBitmap UpdateBitmap(CameraData cameraData)
{
if (cameraData == null)
{
"图片数据为空".LogSysError();
return null;
}
try
{
int width = (int)cameraData.Width;
int height = (int)cameraData.Height;
int bitCnt = cameraData.Bit;
byte[] buffer = cameraData.BufferData;
if (width <= 0 || height <= 0)
{
$"CameraViewModel.UpdateBitmap: invalid size {width}x{height}".LogSysError();
return null;
}
// 选择像素格式
System.Windows.Media.PixelFormat pixelFormat = bitCnt == 1
? System.Windows.Media.PixelFormats.Gray8
: (bitCnt == 3 ? System.Windows.Media.PixelFormats.Bgr24 : System.Windows.Media.PixelFormats.Bgra32);
// 计算每行字节数stride
int bytesPerPixel = (pixelFormat.BitsPerPixel + 7) / 8;
int stride = width * bytesPerPixel;
long requiredLength = (long)stride * height;
if (buffer == null)
{
"CameraViewModel.UpdateBitmap: buffer is null".LogSysError();
return null;
}
if (buffer.Length < requiredLength)
{
($"CameraViewModel.UpdateBitmap: buffer length {buffer.Length} is less than required {requiredLength}").LogSysError();
return null;
}
// 如果现有 bitmap 可复用(尺寸和格式一致),就重用以减少 GC/分配
if (currentImage == null ||
currentImage.PixelWidth != width ||
currentImage.PixelHeight != height ||
!currentImage.Format.Equals(pixelFormat))
{
// 新建 WriteableBitmap
currentImage = new WriteableBitmap(width, height, 96, 96, pixelFormat, null);
}
// 将字节拷贝到 BackBufferUI 线程中调用)
currentImage.Lock();
try
{
// BackBuffer 为 IntPtr使用 Marshal.Copy 从 byte[] 拷贝
System.Runtime.InteropServices.Marshal.Copy(buffer, 0, currentImage.BackBuffer, (int)requiredLength);
currentImage.AddDirtyRect(new Int32Rect(0, 0, width, height));
}
finally
{
currentImage.Unlock();
}
return currentImage;
}
catch (Exception ex)
{
($"CameraViewModel.UpdateBitmap exception: {ex}").LogSysError();
return null;
}
}
public CameraViewModel(IEventAggregator eventAggregator, AppCameraType cameraType)
{
_eventAggregator = eventAggregator;
CameraType = cameraType;
_eventAggregator?.Subscribe(this);
}
public void Handle(DisplayImageEventArgs message)
{
if (message == null || message.CameraType != CameraType)
{
return;
}
CommonUti.RunOnUi(() =>
{
WriteableBitmap bmp = UpdateBitmap(message.CameraData);
// 只有在成功生成 bitmap 时才更新 DisplayImage避免把 null 赋值导致 UI 清空
if (bmp != null)
{
DisplayImage = bmp;
}
});
}
public void Handle(DisplayFindTemplateResultEventArgs message)
{
if (message == null || message.CameraType != CameraType)
{
return;
}
CommonUti.RunOnUi(() =>
{
ApplyTemplateResultShapes(message);
});
}
private void ApplyTemplateResultShapes(DisplayFindTemplateResultEventArgs message)
{
if (message.ClearShapesBeforeDisplay)
{
Shapes.Clear();
}
if (message.Result == null)
{
return;
}
AppendCenterShapes(message.Result);
AppendContourShape(message.Result.Contour);
AppendCircleShapes(message.Result.Circles);
AppendLineShapes(message.Result.Lines);
AppendRectangleShapes(message.Result.Rectangles);
}
private void AppendCenterShapes(FindTemplateResult result)
{
if (result == null || !AreFinite(result.CenterX, result.CenterY, result.Score))
{
return;
}
ShapeDrawing pointShape = new ShapeDrawing();
pointShape.X = result.CenterX;
pointShape.Y = result.CenterY;
Shapes.Add(pointShape);
TextShapeDrawing textShape = new TextShapeDrawing();
textShape.X = result.CenterX;
textShape.Y = result.CenterY;
textShape.Text = $"Score:{result.Score:F3}";
Shapes.Add(textShape);
}
private void AppendContourShape(FindTemplateContourResult contour)
{
if (contour == null || !AreFinite(contour.StartX, contour.StartY, contour.EndX, contour.EndY))
{
return;
}
RectShapeDrawing contourShape = new RectShapeDrawing();
contourShape.X = contour.StartX;
contourShape.Y = contour.StartY;
contourShape.X1 = contour.EndX;
contourShape.Y1 = contour.EndY;
Shapes.Add(contourShape);
}
private void AppendCircleShapes(IList<FindTemplateCircleResult> circles)
{
if (circles == null)
{
return;
}
foreach (FindTemplateCircleResult circle in circles)
{
if (circle == null || !AreFinite(circle.CenterX, circle.CenterY, circle.Radius))
{
continue;
}
CircleShapeDrawing circleShape = new CircleShapeDrawing();
circleShape.X = circle.CenterX;
circleShape.Y = circle.CenterY;
circleShape.CenterPoint = new Point(circle.CenterX, circle.CenterY);
circleShape.RadiusX = circle.Radius;
circleShape.RadiusY = circle.Radius;
circleShape.LineWidth = 1;
circleShape.FillColor = Brushes.Blue;
Shapes.Add(circleShape);
}
}
private void AppendLineShapes(IList<FindTemplateLineResult> lines)
{
if (lines == null)
{
return;
}
foreach (FindTemplateLineResult line in lines)
{
if (line == null || !AreFinite(line.StartX, line.StartY, line.EndX, line.EndY))
{
continue;
}
LineShapeDrawing lineShape = new LineShapeDrawing();
lineShape.X = line.StartX;
lineShape.Y = line.StartY;
lineShape.X1 = line.EndX;
lineShape.Y1 = line.EndY;
Shapes.Add(lineShape);
}
}
private void AppendRectangleShapes(IList<FindTemplateRectangleResult> rectangles)
{
if (rectangles == null)
{
return;
}
foreach (FindTemplateRectangleResult rectangle in rectangles)
{
if (rectangle == null || !AreFinite(rectangle.StartX, rectangle.StartY, rectangle.Width, rectangle.Height, rectangle.Angle))
{
continue;
}
double halfWidth = rectangle.Width / 2d;
double halfHeight = rectangle.Height / 2d;
double cosAngle = Math.Cos(rectangle.Angle);
double sinAngle = Math.Sin(rectangle.Angle);
double centerX = rectangle.StartX + (halfWidth * cosAngle) - (halfHeight * sinAngle);
double centerY = rectangle.StartY + (halfWidth * sinAngle) + (halfHeight * cosAngle);
RectShape2Drawing rectangleShape = new RectShape2Drawing();
rectangleShape.X = centerX;
rectangleShape.Y = centerY;
rectangleShape.CenterX = centerX;
rectangleShape.CenterY = centerY;
rectangleShape.HalfWidth = halfWidth;
rectangleShape.HalfHeigth = halfHeight;
rectangleShape.Angle = rectangle.Angle;
Shapes.Add(rectangleShape);
}
}
private static bool AreFinite(params double[] values)
{
if (values == null)
{
return false;
}
foreach (double value in values)
{
if (double.IsNaN(value) || double.IsInfinity(value))
{
return false;
}
}
return true;
}
}
}