feat: Image Broadcast as you go

master
Tongg 8 months ago
parent d4c639ec84
commit f3fdfbfa1f
  1. 16
      Model/Extension/CommonExtension.cs
  2. 6
      ViewModel/Grading/DiamondSelectVM.cs
  3. 116
      ViewModel/Grading/GradingLoadingVM.cs
  4. 2
      Views/UserControl/Viewport3D.xaml
  5. 8
      Views/UserControl/Viewport3D.xaml.cs
  6. 15
      Views/UserControl/ViewportData/Helper/CommonHelper.cs
  7. 29
      Views/UserControl/ViewportData/Helper/ViewportHelperPro.cs
  8. 58
      Views/UserControl/ViewportData/ViewportData.cs

@ -42,9 +42,19 @@ public static class CommonExtension
return toStringMethod?.DeclaringType != typeof(object);
});
return hasCustomToString
? obj.ToString() // 使用自定义的ToString
: JsonConvert.SerializeObject(obj); // 未重写时序列化为JSON
try
{
string result = hasCustomToString
? obj.ToString() // 使用自定义的ToString
: JsonConvert.SerializeObject(obj);
return result;
}
catch (Exception e)
{
return obj.ToString();
}
}

@ -141,11 +141,7 @@ public class DiamondSelectVM : BaseViewModel
};
listBtn.ForEach(e => tempButtons2.Add(e));
// tempButtons2.Add(button1);
// tempButtons2.Add(button2);
// tempButtons2.Add(button3);
// tempButtons2.Add(button4);
// tempButtons2.Add(button5);
}

@ -1,5 +1,6 @@
using System.Configuration;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Windows;
@ -192,9 +193,9 @@ public class GradingLoadingVM : BaseViewModel,IDisposable
// return -1;
// }
var processImage = SOCClientService.Service.ProcessImageCollectionAsync();
Task<SocResultEntity> processImage = SOCClientService.Service.ProcessImageCollectionAsync();
//通知页面可以播放图片
LoadDefaultImages();
await processImage;
if (!("ok".Equals(processImage.Result.Status) || "S000".Equals(processImage.Result.Status)))
@ -204,7 +205,7 @@ public class GradingLoadingVM : BaseViewModel,IDisposable
MultilingualHelper.getString(StatusCodes.GetConstantNameByValue(processImage.Result.Status)));
return -1;
}
LoadImages(processImage.Result.Images);
// LoadImages(processImage.Result.Images);
}
@ -228,12 +229,12 @@ public class GradingLoadingVM : BaseViewModel,IDisposable
{
return -1;
}
StartPlayback();
}
ImageIsEnable = true;
StartPlayback();
// StartPlayback();
if (_isCancel == true)
{
SOCClientService.Service.OpenPump(false);
@ -370,6 +371,12 @@ public class GradingLoadingVM : BaseViewModel,IDisposable
parameter.PavType = _diamnondType.Split(" ")[2];
parameter.ErrorMsg = _diamnondType;
//实验室模式关闭气泵
if (Common.RunMode == 0)
{
SOCClientService.Service.OpenPump(false);
}
try
{
Logger.Info($"算法结果Json单独保存至log/result");
@ -420,21 +427,7 @@ public class GradingLoadingVM : BaseViewModel,IDisposable
}
}
}
[Log]
private async Task CompleteProgressSlowlyAsync()
{
// 缓慢完成剩余3%(总耗时保持原速度的3倍)
const int remainingSteps = 3;
int stepTime = 500; // 自定义慢速步长
int current = (int)Progress;
for (int i = 1; i <= remainingSteps; i++)
{
if (_progressCts.IsCancellationRequested) break;
UpdateProgress(current + i);
await Task.Delay(stepTime);
}
}
[Log]
private async Task CompleteProgressQuicklyAsync()
{
@ -557,11 +550,20 @@ public class GradingLoadingVM : BaseViewModel,IDisposable
}
#region 图片播放处理
[Log]
public void LoadImages(string folderPath)
public void LoadDefaultImages()
{
ImagePaths = Directory.GetFiles(folderPath, "*.bmp");
List<string> imagePaths = new List<string>();
string? savePath = ConfigurationManager.AppSettings["ImageFileBasePath"];
for (int i = 0; i < 100; i++)
{
imagePaths.Add(Path.Combine(savePath, $"image_{i}.bmp"));
}
ImagePaths = imagePaths.ToArray();
CurrentIndex = 0;
CurrentStatus = PlayStatus.Stopped;
MonitorAndPlayImagesAsync(savePath);
}
[Log]
public void LoadImages(List<string> folderPath)
@ -570,6 +572,76 @@ public class GradingLoadingVM : BaseViewModel,IDisposable
CurrentIndex = 0;
CurrentStatus = PlayStatus.Stopped;
}
[Log]
private async Task MonitorAndPlayImagesAsync(string folderPath)
{
var sw = new Stopwatch();
for (int i = 0; i < 100; i++)
{
sw.Restart();
// 等待文件就绪
var filePath = Path.Combine(folderPath, $"image_{i}.bmp");
while (!await IsFileReady(filePath))
{
await Task.Delay(50); // 每50ms检测一次文件状态
}
// 加载图片到内存
var image = await LoadImageSafely(filePath);
// 更新UI显示
await Application.Current.Dispatcher.InvokeAsync(() =>
{
CurrentImage = image;
OnPropertyChanged(nameof(CurrentImage));
});
// 强制最小显示间隔
var remainTime = Math.Max(100 - (int)sw.ElapsedMilliseconds, 0);
await Task.Delay(remainTime);
}
}
[Log]
private async Task<bool> IsFileReady(string path)
{
try
{
// 双重验证机制
if (!File.Exists(path)) return false;
// 尝试读取文件头验证完整性
using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read))
{
byte[] header = new byte[2];
await fs.ReadAsync(header, 0, 2);
return header[0] == 0x42 && header[1] == 0x4D; // BMP文件头标识
}
}
catch
{
return false;
}
}
[Log]
private async Task<BitmapImage> LoadImageSafely(string path)
{
using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read))
{
var bitmap = new BitmapImage();
await Application.Current.Dispatcher.InvokeAsync(() =>
{
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = fs;
bitmap.EndInit();
bitmap.Freeze(); // 跨线程安全
});
return bitmap;
}
}
[Log]
private async Task HandlePlayControl()
{

@ -55,7 +55,7 @@
<ContextMenu x:Name="ContextMenu">
<!-- ViewportCutXuanZ -->
<MenuItem Header="{StaticResource ViewportCutXuanZ}" x:Name="ViewportCutXuanZ" Click="MenuItem_OnClick"/>
<Separator />
<Separator x:Name="ViewportCutXuanZSeparator"/>
<!-- 菜单 -->
<MenuItem Header="{StaticResource ViewportRightMenuFront}" x:Name="ViewportRightMenuFront" Click="MenuItem_OnClick"/>
<MenuItem Header="{StaticResource ViewportRightMenuSaveViewToPNG}" x:Name="ViewportRightMenuSaveViewToPNG" Click="MenuItem_OnClick"/>

@ -85,6 +85,12 @@ public partial class Viewport3D
// Viewport3Dx.ShowFrameRate = !Viewport3Dx.ShowFrameRate;
// Viewport3Dx.ShowTriangleCountInfo = !Viewport3Dx.ShowTriangleCountInfo;
if (Common.RunMode == 0)
{
ContextMenu.Items.Remove(ViewportCutXuanZ);
ContextMenu.Items.Remove(ViewportCutXuanZSeparator);
}
//工厂模式屏蔽一些东西
string step = ViewportManager.DiamondType.Split(" ").Last();
if (Common.RunMode == 1 && step.StartsWith("S"))
@ -140,8 +146,6 @@ public partial class Viewport3D
ViewportManager.ShowMainModelLines(true);
ViewportManager.ShowMainModel3D(true);
ViewportManager.ShowMainModel3DByType(true);
}

@ -7,6 +7,7 @@ using SharpDX;
using SparkClient.Model.Attributes;
using SparkClient.Views.UserControl.ViewportData.Entity;
using SparkClient.Views.UserControl.ViewportData.Enum;
using SparkDotNetCore.DiamondScanner.Entity.Child;
namespace SparkClient.Views.UserControl.ViewportData.Helper;
@ -27,11 +28,11 @@ public class CommonHelper
public static Viewport3DTriangleEntity CreateByJsonStr(string json)
{
// 解析 JSON 数据
var jsonObject = JsonConvert.DeserializeObject<dynamic>(json);
if (jsonObject == null) throw new ArgumentException("Json object is null");
var facet = JsonConvert.DeserializeObject<Facet>(json);
if (facet == null) throw new ArgumentException("Json object is null");
// 提取坐标
var coords = jsonObject.coords.ToObject<dynamic[]>();
if (coords.Length < 3)
var coords = facet.coords;
if (coords.Count < 3)
{
throw new ArgumentException("The input JSON does not have enough points to form a triangle.");
}
@ -45,9 +46,9 @@ public class CommonHelper
// var point3 = new Vector3((float)coords[2].x, (float)coords[2].y, (float)coords[2].z);
// 提取 PlaneCode 和 PlaneType
string planeCode = jsonObject.facet_id ?? (string)jsonObject.facet_id;
PlaneType planeType = (PlaneType)(int)jsonObject.facet_type;
double theta = jsonObject.theta ?? (double)jsonObject.theta;
string planeCode = facet.facet_id;
PlaneType planeType = (PlaneType)int.Parse(facet.facet_type);
double theta = facet.theta;
// 生成 TriangleCode
var triangleCode = GenerateTriangleCode(point1, point2, point3);

@ -14,13 +14,9 @@ using SparkClient.Views.UserControl.ViewportData.Enum;
using SparkClient.Views.UserControl.ViewportData.Entity;
using Color = SharpDX.Color;
using GeometryModel3D = HelixToolkit.Wpf.SharpDX.GeometryModel3D;
using MeshGeometry3D = HelixToolkit.Wpf.SharpDX.MeshGeometry3D;
using Point = System.Windows.Point;
using PerspectiveCamera = HelixToolkit.Wpf.SharpDX.PerspectiveCamera;
using System.Windows.Controls;
using HelixToolkit.Wpf;
using MeshBuilder = HelixToolkit.Wpf.SharpDX.MeshBuilder;
using OrthographicCamera = HelixToolkit.Wpf.SharpDX.OrthographicCamera;
using System.Windows.Input;
using SparkClient.Model.Attributes;
using SparkClient.Model.Extension;
@ -51,6 +47,7 @@ public class ViewportHelperPro
/// </summary>
/// <param name="viewport"></param>
/// <param name="entities"></param>
/// <param name="color"></param>
[Log]
public static MeshGeometryModel3D GenerateModelByEntity(Viewport3DX viewport, List<Viewport3DTriangleEntity> entities, Color4? color = null)
{
@ -112,7 +109,6 @@ public class ViewportHelperPro
/// 通过三角形实体集合生成面模型(只生成不添加)
/// </summary>
/// <param name="entities"></param>
/// <param name="color"></param>
/// <returns></returns>
[Log]
public static List<MeshGeometryModel3D> GenerateModelByEntityGroupByType(List<Viewport3DTriangleEntity> entities)
@ -185,7 +181,7 @@ public class ViewportHelperPro
}
[Log]
public static async Task ExportModelsToVideo(Viewport3DX viewport, string filePath)
public static async Task ExportModelsToVideo(Viewport3DX? viewport, string filePath)
{
if (viewport == null)
viewport = ViewportManager.GetViewport3D();
@ -203,7 +199,7 @@ public class ViewportHelperPro
/// <param name="viewport"></param>
/// <param name="filePath"></param>
[Log]
public static void ExportModelsToStl(Viewport3DX viewport, string filePath)
public static void ExportModelsToStl(Viewport3DX? viewport, string filePath)
{
if (viewport == null)
viewport = ViewportManager.GetViewport3D();
@ -214,7 +210,7 @@ public class ViewportHelperPro
foreach (var model in viewport.Items.OfType<MeshGeometryModel3D>())
{
if (model.Geometry is HelixToolkit.Wpf.SharpDX.Geometry3D geometry)
if (model.Geometry != null && model.Geometry is HelixToolkit.Wpf.SharpDX.Geometry3D geometry)
{
var positions = geometry.Positions;
var indices = geometry.Indices;
@ -252,7 +248,6 @@ public class ViewportHelperPro
/// <summary>
/// 导出模型
/// </summary>
/// <param name="viewport"></param>
/// <param name="filePath"></param>
[Log]
public static async Task ExportModelsToStlASync(string filePath)
@ -379,7 +374,7 @@ public class ViewportHelperPro
{ culetLineZ, culetLineX };
result.Add(DisplayLineModel3D(centerLines, Color.Red, 0.8f));
result.Add(DisplayLineModel3D(tableLines, Color.Green, 1f));
result.Add(DisplayLineModel3D(tableLines, Color.Green));
result.Add(DisplayLineModel3D(culetLines, Color.Blue, 0.8f));
return result;
}
@ -389,6 +384,8 @@ public class ViewportHelperPro
/// </summary>
/// <param name="viewport"></param>
/// <param name="entities"></param>
/// <param name="color"></param>
/// <param name="thickness"></param>
[Log]
public static List<LineGeometryModel3D> GentrateLineByEntity(Viewport3DX viewport, List<Viewport3DTriangleEntity> entities, Color4? color = null, double thickness = 1.0)
{
@ -599,11 +596,11 @@ public class ViewportHelperPro
public static List<GeometryModel3D> GenerateLineTextModels(List<Viewport3DTriangleEntity> entities, string valKey = "")
{
var result = new List<GeometryModel3D>();
string steps = ViewportManager.DiamondType.Split(" ").Last();
// string steps = ViewportManager.DiamondType.Split(" ").Last();
if (Common.RunMode == 0)
{
Logger.Info("【面文本生成】开始生成面相关文本信息");
var selFacet = entities;
// var selFacet = entities;
var selFacetType = entities.First().PlaneType;
@ -729,7 +726,7 @@ public class ViewportHelperPro
var facetIndex = int.Parse(dic.Key.Split("_")[1]) - 1;
if ("GIRDLE_VALLEY".Equals(gridleResult.Key))
{
var showLine =GetLeftOrRightLineSegment(facetPoints, facetIndex >= 25 && facetIndex <= 57 ? true : false);;
var showLine =GetLeftOrRightLineSegment(facetPoints, facetIndex >= 25 && facetIndex <= 57 );
if (showLine == null) continue;
result.Add(DisplayLineModel3D(new List<Tuple<Vector3, Vector3>>() { showLine }, new Color4(1f, 0, 0, 1f), 2f));
}
@ -752,7 +749,7 @@ public class ViewportHelperPro
return result;
}
var valueFloat = ValueFormat(paramValue.ToString(), gridleResult.Key, false);
var valueFloat = ValueFormat(paramValue.ToString(), gridleResult.Key);
result.Add(DisplayText3D($"{valueFloat}", facetTextPoint));
}
}
@ -796,7 +793,7 @@ public class ViewportHelperPro
//高亮的线
// var showLine = GetLeftParallelLineSegment(facetPoints);
var facetIndex = int.Parse(dic.Key.Split("_")[1]) - 1;
var showLine =GetLeftOrRightLineSegment(facetPoints, facetIndex >= 25 && facetIndex <= 57 ? true : false);;
var showLine =GetLeftOrRightLineSegment(facetPoints, facetIndex >= 25 && facetIndex <= 57);;
if (showLine == null) continue;
//文字显示位置
@ -2311,7 +2308,7 @@ public class ViewportHelperPro
return resultSegment; // 返回符合条件的线段
}
[Log]
private static Tuple<Vector3, Vector3> GetLeftOrRightLineSegment(List<Vector3> facetPoints,
private static Tuple<Vector3, Vector3>? GetLeftOrRightLineSegment(List<Vector3> facetPoints,
bool isLeft = true)
{
Tuple<Vector3, Vector3> resultSegment = null;

@ -1,11 +1,14 @@
using System.Text.Json.Nodes;
using Newtonsoft.Json;
using SharpDX;
using SparkClient.Model.Attributes;
using SparkClient.Model.Entity.ApiEntity;
using SparkClient.Model.Extension;
using SparkClient.Model.Helper;
using SparkClient.Views.UserControl.ViewportData.Entity;
using SparkClient.Views.UserControl.ViewportData.Enum;
using SparkClient.Views.UserControl.ViewportData.Helper;
using SparkDotNetCore.DiamondScanner.Entity;
namespace SparkClient.Views.UserControl.ViewportData;
@ -53,27 +56,31 @@ public class ViewportData
[Log]
public void LoadData()
{
JsonNode? json = null;
var settings = new JsonSerializerSettings
{
ContractResolver = new DefaultValueContractResolver(),
NullValueHandling = NullValueHandling.Ignore
};
DiaResult? diamondData = null;
try
{
json = JsonNode.Parse(DiamondData);
diamondData = JsonConvert.DeserializeObject<AlgorithmResultEntity>(DiamondData.ToSafeString(),settings);
}
catch
{
throw new Exception("DiamondData is invalid");
}
if(json == null) throw new Exception("DiamondData is invalid");
if(diamondData == null) throw new Exception("DiamondData is invalid");
try
{
List<Viewport3DTriangleEntity> facets = new List<Viewport3DTriangleEntity>();
if (json.AsObject()["facets"] != null)
if (diamondData.Facets != null)
{
JsonArray jsonArray = json.AsObject()["facets"].AsArray();
foreach (var item in jsonArray)
foreach (var item in diamondData.Facets)
{
var value = CommonHelper.CreateByJsonStr(item.ToString());
var value = CommonHelper.CreateByJsonStr(JsonConvert.SerializeObject(item, settings));
facets.Add(value);
}
}
@ -82,24 +89,27 @@ public class ViewportData
throw new Exception("facets is invalid");
}
if (json.AsObject()["positive_direction"] != null)
{
JsonObject positive = json.AsObject()["positive_direction"].AsObject();
ViewportManager.PositiveDirection.X = float.Parse(positive["x"]?.ToString() ?? "1.0");
ViewportManager.PositiveDirection.Y = float.Parse(positive["z"]?.ToString() ?? "0");
ViewportManager.PositiveDirection.Z = float.Parse(positive["y"]?.ToString() ?? "0");
}
else
{
ViewportManager.PositiveDirection.X = 1.0f;
ViewportManager.PositiveDirection.Y = 0f;
ViewportManager.PositiveDirection.Z = 0;
}
// if (json.AsObject()["positive_direction"] != null)
// {
// JsonObject positive = json.AsObject()["positive_direction"].AsObject();
// ViewportManager.PositiveDirection.X = float.Parse(positive["x"]?.ToString() ?? "1.0");
// ViewportManager.PositiveDirection.Y = float.Parse(positive["z"]?.ToString() ?? "0");
// ViewportManager.PositiveDirection.Z = float.Parse(positive["y"]?.ToString() ?? "0");
// }
// else
// {
// ViewportManager.PositiveDirection.X = 1.0f;
// ViewportManager.PositiveDirection.Y = 0f;
// ViewportManager.PositiveDirection.Z = 0;
// }
ViewportManager.PositiveDirection.X = 1.0f;
ViewportManager.PositiveDirection.Y = 0f;
ViewportManager.PositiveDirection.Z = 0;
ViewportManager.DiamondCode = DiamondCode;
ViewportManager.DiamondType = json.AsObject()["error_msg"].ToSafeString();
var measurements = json.AsObject()["measurements"];
ViewportManager.DiamondData = measurements==null?new JsonObject():measurements.AsObject();
ViewportManager.DiamondType = diamondData.ErrorMsg.ToSafeString();
ViewportManager.DiamondData = JsonObject.Parse(JsonConvert.SerializeObject(diamondData.Measurements)).AsObject();
var midZ = facets.SelectMany(e => new[] { e.Point1.X, e.Point2.X, e.Point3.X }).OrderBy(z => Math.Abs(z))
.First();
List<Viewport3DTriangleEntity> facetsFinal = new List<Viewport3DTriangleEntity>();
@ -150,7 +160,7 @@ public class ViewportData
facetsFinal.Add(item);
}
if (json["status"].ToString().Equals("P021"))
if (diamondData.Status.Equals("P021"))
{
ViewportManager.isUglyDiamond = true;
}

Loading…
Cancel
Save