using Microsoft.Win32; using Newtonsoft.Json.Linq; using Serein.Library; using Serein.Library.Api; using Serein.Workbench.Api; using Serein.Workbench.ViewModels; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text; using System.Threading.Tasks; using System.Windows.Input; namespace Serein.Workbench.Services { #region 工作台事件 public delegate void PreviewlMethodInfoHandler(PreviewlMethodInfoEventArgs eventArgs); #endregion #region 工作台事件参数 public class PreviewlMethodInfoEventArgs(MethodDetailsInfo mdInfo) : EventArgs { /// /// 方法信息 /// public MethodDetailsInfo MethodDetailsInfo { get; } = mdInfo; } #endregion /// /// 工作台事件管理 /// internal interface IWorkbenchEventService { /// /// 预览了某个方法信息(待创建) /// event PreviewlMethodInfoHandler OnPreviewlMethodInfo; /// /// 预览依赖方法信息 /// void PreviewLibraryMethodInfo(MethodDetailsInfo mdInfo); } /// /// 工作台事件的实现类 /// internal class WorkbenchEventService : IWorkbenchEventService { private readonly IFlowEnvironment flowEnvironment; private readonly IFlowEEForwardingService flowEEForwardingService; private readonly IKeyEventService keyEventService; private readonly FlowNodeService flowNodeService; /// /// 管理工作台的事件 /// /// /// /// /// public WorkbenchEventService(IFlowEnvironment flowEnvironment, IFlowEEForwardingService flowEEForwardingService, IKeyEventService keyEventService, FlowNodeService flowNodeService) { this.flowEnvironment = flowEnvironment; this.flowEEForwardingService = flowEEForwardingService; this.keyEventService = keyEventService; this.flowNodeService = flowNodeService; InitEvents(); } private void InitEvents() { flowEEForwardingService.ProjectLoaded += FlowEEForwardingService_OnProjectLoaded; flowEEForwardingService.ProjectSaving += SaveProjectToLocalFile; flowEEForwardingService.EnvOutput += FlowEEForwardingService_OnEnvOut; keyEventService.OnKeyDown += KeyEventService_OnKeyDown; ; } private void FlowEEForwardingService_OnProjectLoaded(ProjectLoadedEventArgs eventArgs) { var edit = App.GetService().FlowEditViewModel; App.UIContextOperation.Invoke(async () => { foreach (var item in flowNodeService.FlowCanvass) { await Task.Delay(50); flowNodeService.CurrentSelectCanvas = item; var tab = edit.CanvasTabs.First(tab => tab.Content == item); edit.SelectedTab = tab; } }); } private void KeyEventService_OnKeyDown(System.Windows.Input.Key key) { } private void FlowEEForwardingService_OnEnvOut(InfoType type, string value) { LogWindow.Instance.AppendText($"{DateTime.Now} [{type}] : {value}{Environment.NewLine}"); } /// /// 预览了某个方法信息(待创建) /// public event PreviewlMethodInfoHandler? OnPreviewlMethodInfo; /// /// 预览依赖方法信息 /// public void PreviewLibraryMethodInfo(MethodDetailsInfo mdInfo) { OnPreviewlMethodInfo?.Invoke(new PreviewlMethodInfoEventArgs(mdInfo)); } /// /// 需要放置节点控件 /// public void PlateNodeControl() { } /// /// 保存项目数据到本地文件 /// /// private void SaveProjectToLocalFile(ProjectSavingEventArgs e) { var project = e.ProjectData; #region 获取保存路径 // 创建一个新的保存文件对话框 SaveFileDialog saveFileDialog = new() { Filter = "DynamicNodeFlow Files (*.dnf)|*.dnf", DefaultExt = "dnf", FileName = "project.dnf" // FileName = System.IO.Path.GetFileName(App.FileDataPath) }; // 显示保存文件对话框 bool? result = saveFileDialog.ShowDialog(); // 如果用户选择了文件并点击了保存按钮 if (result == false) { SereinEnv.WriteLine(InfoType.ERROR, "取消保存文件"); return; } var savePath = saveFileDialog.FileName; string? librarySavePath = System.IO.Path.GetDirectoryName(savePath); if (string.IsNullOrEmpty(librarySavePath)) { SereinEnv.WriteLine(InfoType.ERROR, "保存项目DLL时返回了意外的文件保存路径"); return; } #endregion #region 将Dll输出到指定路径 Uri saveProjectFileUri = new Uri(savePath); SereinEnv.WriteLine(InfoType.INFO, "项目文件保存路径:" + savePath); for (int index = 0; index < project.Librarys.Length; index++) { FlowLibraryInfo? library = project.Librarys[index]; string sourceFilePath = new Uri(library.FilePath).LocalPath; // 源文件夹 string targetDir = System.IO.Path.Combine(librarySavePath, library.AssemblyName); // 目标文件夹 if (!Path.Exists(targetDir)) { Directory.CreateDirectory(targetDir); } string targetFilePath = System.IO.Path.Combine(targetDir, library.FileName); // 目标文件夹 try { if (File.Exists(sourceFilePath)) { if (!File.Exists(targetFilePath)) { SereinEnv.WriteLine(InfoType.INFO, $"源文件路径 : {sourceFilePath}"); SereinEnv.WriteLine(InfoType.INFO, $"目标路径 : {targetFilePath}"); File.Copy(sourceFilePath, targetFilePath, true); } else { SereinEnv.WriteLine(InfoType.WARN, $"目标路径已有类库文件: {targetFilePath}"); } } else { SereinEnv.WriteLine(InfoType.WARN, $"源文件不存在 : {targetFilePath}"); } } catch (IOException ex) { SereinEnv.WriteLine(InfoType.ERROR, ex.Message); } var dirName = System.IO.Path.GetDirectoryName(targetFilePath); if (!string.IsNullOrEmpty(dirName)) { var tmpUri2 = new Uri(targetFilePath); var relativePath = saveProjectFileUri.MakeRelativeUri(tmpUri2).ToString(); // 转为类库的相对文件路径 //string relativePath = System.IO.Path.GetRelativePath(savePath, targetPath); project.Librarys[index].FilePath = relativePath; } } #endregion #region 输出项目保存文件 JObject projectJsonData = JObject.FromObject(project); File.WriteAllText(savePath, projectJsonData.ToString()); #endregion } } } #region 抽取重复文件(例如net运行时) /* 1. 扫描目录并计算哈希 string[] directories = new[] { "path1", "path2" }; var fileHashMap = new Dictionary>(); // hash -> List foreach (var dir in directories) { foreach (var file in Directory.EnumerateFiles(dir, "*.*", SearchOption.AllDirectories)) { using var stream = File.OpenRead(file); using var sha = SHA256.Create(); var hash = Convert.ToHexString(sha.ComputeHash(stream)); if (!fileHashMap.ContainsKey(hash)) fileHashMap[hash] = new List(); fileHashMap[hash].Add(file); } } 2. 将重复文件压缩并保存 string archiveDir = "compressed_output"; Directory.CreateDirectory(archiveDir); var manifest = new List(); foreach (var kvp in fileHashMap.Where(kvp => kvp.Value.Count > 1)) { var hash = kvp.Key; var originalFile = kvp.Value[0]; var archivePath = Path.Combine(archiveDir, $"{hash}.gz"); using (var input = File.OpenRead(originalFile)) using (var output = File.Create(archivePath)) using (var gzip = new GZipStream(output, CompressionLevel.Optimal)) { input.CopyTo(gzip); } manifest.Add(new FileRecord { Hash = hash, ArchiveFile = $"{hash}.gz", OriginalPaths = kvp.Value }); } 3. 生成清单文件(JSON) public class FileRecord { public string Hash { get; set; } public string ArchiveFile { get; set; } public List OriginalPaths { get; set; } } File.WriteAllText("manifest.json", JsonSerializer.Serialize(manifest, new JsonSerializerOptions { WriteIndented = true })); 4. 根据清单还原原始文件结构 var manifestJson = File.ReadAllText("manifest.json"); var manifest = JsonSerializer.Deserialize>(manifestJson); foreach (var record in manifest) { var archivePath = Path.Combine("compressed_output", record.ArchiveFile); foreach (var path in record.OriginalPaths) { Directory.CreateDirectory(Path.GetDirectoryName(path)!); using var input = File.OpenRead(archivePath); using var gzip = new GZipStream(input, CompressionMode.Decompress); using var output = File.Create(path); gzip.CopyTo(output); } } */ #endregion