using Laservall.Solidworks.Model; using Laservall.Solidworks.Server.Dto; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Laservall.Solidworks.Server.Handlers { internal sealed class BomStreamHandler { private readonly IBomDataProvider _dataProvider; private BomLoadResult _lastLoadResult; public BomStreamHandler(IBomDataProvider dataProvider) { _dataProvider = dataProvider; } public BomLoadResult LastLoadResult => _lastLoadResult; public async Task HandleStream(HttpListenerContext context, CancellationToken ct) { context.Response.ContentType = "text/event-stream; charset=utf-8"; context.Response.Headers.Set("Cache-Control", "no-cache"); context.Response.Headers.Set("Connection", "keep-alive"); context.Response.StatusCode = 200; var stream = context.Response.OutputStream; try { var progress = new Progress(async p => { try { var evt = new ProgressEventDto { Processed = p.Processed, Total = p.Total, CurrentName = p.CurrentName }; string json = JsonConvert.SerializeObject(evt); await WriteSseEvent(stream, "progress", json, ct); } catch { } }); var result = await _dataProvider.LoadBomAsync(progress, ct); _lastLoadResult = result; var response = new BomStreamResponse { Items = result.Items.Select(ToDto).ToList(), DynamicKeys = result.DynamicKeys, Settings = ToSettingsDto(result.Settings) }; string completeJson = JsonConvert.SerializeObject(response); await WriteSseEvent(stream, "complete", completeJson, ct); } catch (OperationCanceledException) { } catch (Exception ex) { try { string errorJson = JsonConvert.SerializeObject(new { error = ex.Message }); await WriteSseEvent(stream, "error", errorJson, ct); } catch { } } finally { try { context.Response.Close(); } catch { } } } private static async Task WriteSseEvent(System.IO.Stream stream, string eventType, string data, CancellationToken ct) { string message = $"event: {eventType}\ndata: {data}\n\n"; byte[] bytes = Encoding.UTF8.GetBytes(message); await stream.WriteAsync(bytes, 0, bytes.Length, ct); await stream.FlushAsync(ct); } private static BomItemDto ToDto(BomItemModel item) { return new BomItemDto { Level = item.Level, LevelDisplay = item.LevelDisplay, DrawingNo = item.DrawingNo, ConfigName = item.ConfigName, PartName = item.PartName, MaterialProp = item.MaterialProp, Material = item.Material, Classification = item.Classification, Quantity = item.Quantity, IsAssembly = item.IsAssembly, IsOutSourcing = item.IsOutSourcing, DocPath = item.DocPath, Props = item.Props ?? new Dictionary(), HasChildren = item.HasChildren, NodeId = item.NodeId, ParentNodeId = item.ParentNodeId }; } private static SettingsDto ToSettingsDto(BomSettings settings) { return new SettingsDto { Columns = settings.Columns.Select(c => new ColumnConfigDto { Name = c.Name, IsVisible = c.IsVisible, IsExport = c.IsExport, IsFixed = c.IsFixed, IsUserAdded = c.IsUserAdded }).ToList(), RemovedKeys = settings.RemovedKeys?.ToList() ?? new List() }; } } }