using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Linq; using System.Text; using JmSetupSheetReceiver.Models; using Newtonsoft.Json; namespace JmSetupSheetReceiver { /// /// 最近一次成功还原的程序单对象,便于调试器监视与控制台命令操作。 /// internal static class SetupSheetStore { private static readonly object Gate = new object(); private static SetupsheetData _latest; private static string _latestJsonPath; private static string _latestTxtPath; private static DateTime _latestUtc; public static SetupsheetData Latest { get { lock (Gate) { return _latest; } } } public static string LatestJsonPath { get { lock (Gate) { return _latestJsonPath; } } } public static string LatestTxtPath { get { lock (Gate) { return _latestTxtPath; } } } public static DateTime LatestUtc { get { lock (Gate) { return _latestUtc; } } } public static void SetLatest(SetupsheetData data, string jsonPath, string txtPath = null) { lock (Gate) { _latest = data; _latestJsonPath = jsonPath; _latestTxtPath = txtPath; _latestUtc = DateTime.UtcNow; } } } /// /// 对已还原对象的查看 / 导出操作(控制台与学习演示用)。 /// internal static class SetupSheetOps { public static readonly JsonSerializerSettings JsonSettings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.Indented, DateParseHandling = DateParseHandling.None }; public static SetupsheetData Deserialize(string json) { return JsonConvert.DeserializeObject(json, JsonSettings); } public static string Serialize(SetupsheetData data) { return JsonConvert.SerializeObject(data, JsonSettings); } /// /// 生成带中文字段名的分层明文(预览图只写字节摘要,不输出 Base64)。 /// public static string ToPlainText(SetupsheetData data) { var sb = new StringBuilder(); if (data == null) { sb.AppendLine("(无对象)"); return sb.ToString(); } sb.AppendLine("[程序单]"); Line(sb, 1, "串联坐标名称", "NcWpName", data.NcWpName); Line(sb, 1, "精公火花位", "ThicknessF", data.ThicknessF); Line(sb, 1, "中公火花位", "ThicknessM", data.ThicknessM); Line(sb, 1, "粗公火花位", "ThicknessR", data.ThicknessR); Line(sb, 1, "精公数量", "NumberF", data.NumberF); Line(sb, 1, "中公数量", "NumberM", data.NumberM); Line(sb, 1, "粗公数量", "NumberR", data.NumberR); Line(sb, 1, "精公自动换刀NC", "AutoToolChangNcF", data.AutoToolChangNcF); Line(sb, 1, "中公自动换刀NC", "AutoToolChangNcM", data.AutoToolChangNcM); Line(sb, 1, "粗公自动换刀NC", "AutoToolChangNcR", data.AutoToolChangNcR); Line(sb, 1, "电极头检测尺寸", "DetectionSize", data.DetectionSize); Line(sb, 1, "电极基准面Z值", "ReferencePlaneZval", data.ReferencePlaneZval); Line(sb, 1, "程序单生成时间", "CreatTime", data.CreatTime); Line(sb, 1, "工件号", "PieceNumber", data.PieceNumber); Line(sb, 1, "精公二维码", "CellBarCodeF", data.CellBarCodeF); Line(sb, 1, "中公二维码", "CellBarCodeM", data.CellBarCodeM); Line(sb, 1, "粗公二维码", "CellBarCodeR", data.CellBarCodeR); Line(sb, 1, "分中Z轴取数方式", "ZType", data.ZType); Line(sb, 1, "是否旋转工件", "NcRotate", data.NcRotate); Line(sb, 1, "旋转角度", "NcAngle", data.NcAngle); Line(sb, 1, "是否镜像工件", "NcMirror", data.NcMirror); Line(sb, 1, "镜像参考平面", "NcAxis", data.NcAxis); if (data.ProjectData != null) { var pd = data.ProjectData; sb.AppendLine(" [项目 ProjectData]"); Line(sb, 2, "项目名称", "Project", pd.Project); Line(sb, 2, "项目路径", "PDSession", pd.PDSession); Line(sb, 2, "NC输出路径", "NcprogramPath", pd.NcprogramPath); Line(sb, 2, "项目总时长", "TotalTime", pd.TotalTime); Line(sb, 2, "项目切削时长", "CutTime", pd.CutTime); Line(sb, 2, "PD总时长(一般不用)", "PDTotalTime", pd.PDTotalTime); Line(sb, 2, "PD切削时长(一般不用)", "PDCutTime", pd.PDCutTime); } else { sb.AppendLine(" [项目 ProjectData]: (null)"); } if (data.PositionDataList == null || data.PositionDataList.Count == 0) { sb.AppendLine(" [工位列表]: (空)"); } else { for (int i = 0; i < data.PositionDataList.Count; i++) { AppendPosition(sb, i, data.PositionDataList[i]); } } AppendExtension(sb, 1, data.ExtensionData); return sb.ToString(); } private static void AppendPosition(StringBuilder sb, int index, PositionData pos) { if (pos == null) { sb.AppendLine(string.Format(" [工位 {0}]: (null)", index)); return; } sb.AppendLine(string.Format(" [工位 {0}] {1}", index, pos.PositionName ?? "(unnamed)")); Line(sb, 2, "工位名称", "PositionName", pos.PositionName); Line(sb, 2, "毛坯最大Z", "StartBlockMaxZ", pos.StartBlockMaxZ); Line(sb, 2, "工位总时长", "PositionTotalTime", pos.PositionTotalTime); Line(sb, 2, "工位切削时长", "PositionCutTime", pos.PositionCutTime); Line(sb, 2, "基准角", "DatumMark", pos.DatumMark); Line(sb, 2, "订料尺寸", "BlockSizeString", pos.BlockSizeString); Line(sb, 2, "模型尺寸", "ModelSizeByPosition", pos.ModelSizeByPosition); Line(sb, 2, "毛料尺寸", "BlockSizeByPosition", pos.BlockSizeByPosition); if (pos.ConfigSetupSheet != null) { var cfg = pos.ConfigSetupSheet; sb.AppendLine(" [界面配置 ConfigSetupSheet]"); Line(sb, 3, "客户", "Customers", cfg.Customers); Line(sb, 3, "机床", "MachineNo", cfg.MachineNo); Line(sb, 3, "材质", "Material", cfg.Material); Line(sb, 3, "模号", "MouldNo", cfg.MouldNo); Line(sb, 3, "工件名称", "PieceName", cfg.PieceName); Line(sb, 3, "员工", "EmployeeName", cfg.EmployeeName); Line(sb, 3, "装夹", "ClampType", cfg.ClampType); AppendExtension(sb, 3, cfg.ExtensionData); } if (pos.ImageBytesList == null || pos.ImageBytesList.Count == 0) { sb.AppendLine(" 预览图(ImageBytesList): 0 张"); } else { sb.AppendLine(string.Format(" 预览图(ImageBytesList): 共 {0} 张", pos.ImageBytesList.Count)); for (int ii = 0; ii < pos.ImageBytesList.Count; ii++) { byte[] bytes = pos.ImageBytesList[ii]; int len = bytes == null ? 0 : bytes.Length; sb.AppendLine(string.Format(" 第 {0} 张: {1} 字节", ii, len)); } } if (pos.ToolPathList == null || pos.ToolPathList.Count == 0) { sb.AppendLine(" [刀路列表]: (空)"); } else { for (int ti = 0; ti < pos.ToolPathList.Count; ti++) { AppendToolPath(sb, ti, pos.ToolPathList[ti]); } } AppendExtension(sb, 2, pos.ExtensionData); } private static void AppendToolPath(StringBuilder sb, int index, ToolPathData tp) { if (tp == null) { sb.AppendLine(string.Format(" [刀路 {0}]: (null)", index)); return; } sb.AppendLine(string.Format(" [刀路 {0}] {1}", index, tp.Toolpath ?? "(unnamed)")); Line(sb, 3, "串联坐标", "NcWpName", tp.NcWpName); Line(sb, 3, "NC名称", "Ncprogram", tp.Ncprogram); Line(sb, 3, "刀路名称", "Toolpath", tp.Toolpath); Line(sb, 3, "刀具名称", "ToolID", tp.ToolID); Line(sb, 3, "刀尖类型", "ToolType", tp.ToolType); Line(sb, 3, "刀具直径", "Dia", tp.Dia); Line(sb, 3, "刀尖圆角", "Tip", tp.Tip); Line(sb, 3, "伸出长度", "Overhang", tp.Overhang); Line(sb, 3, "刀路Z最小", "TPZMin", tp.TPZMin); Line(sb, 3, "旧版转速", "Spindle", tp.Spindle); Line(sb, 3, "旧版进给", "Cut", tp.Cut); Line(sb, 3, "公差", "Tol", tp.Tol); Line(sb, 3, "总时间", "Totaltime", tp.Totaltime); Line(sb, 3, "切削时间", "CutTime", tp.CutTime); Line(sb, 3, "径向余量", "Thickness", tp.Thickness); Line(sb, 3, "轴向余量", "AxialThickness", tp.AxialThickness); Line(sb, 3, "碰撞检测", "CollChecked", tp.CollChecked); Line(sb, 3, "刀号", "ToolNumber", tp.ToolNumber); Line(sb, 3, "NC描述", "Description", tp.Description); Line(sb, 3, "刃长", "Len", tp.Len); Line(sb, 3, "锥度直径", "TaperDiameter", tp.TaperDiameter); Line(sb, 3, "刀具锥度", "TaperAngle", tp.TaperAngle); Line(sb, 3, "行距", "Stepover", tp.Stepover); Line(sb, 3, "步距", "Stepdown", tp.Stepdown); Line(sb, 3, "NC备注", "NcprogramNotes", tp.NcprogramNotes); Line(sb, 3, "刀具描述", "ToolDescription", tp.ToolDescription); Line(sb, 3, "进给(FeedRateRapid)", "FeedRateRapid", tp.FeedRateRapid); Line(sb, 3, "刀头最大", "THUpper", tp.THUpper); Line(sb, 3, "刀柄最大", "TSUpper", tp.TSUpper); Line(sb, 3, "刀头最小", "THLower", tp.THLower); Line(sb, 3, "刀柄最小", "TSLower", tp.TSLower); Line(sb, 3, "刀路坐标", "WKPLName", tp.WKPLName); Line(sb, 3, "刀头名称", "HolderName", tp.HolderName); Line(sb, 3, "刀具格式名", "Tool", tp.Tool); Line(sb, 3, "刀具备注", "ToolNote", tp.ToolNote); Line(sb, 3, "加工深度", "Depth", tp.Depth); Line(sb, 3, "刀路备注", "ToolpathNotes", tp.ToolpathNotes); Line(sb, 3, "NC路径", "NcprogramPath", tp.NcprogramPath); Line(sb, 3, "上圆角", "UpperTip", tp.UpperTip); Line(sb, 3, "刀柄名称", "ShankName", tp.ShankName); Line(sb, 3, "加长杆名称", "ExtensionName", tp.ExtensionName); Line(sb, 3, "加长杆长度", "ExtensionLength", tp.ExtensionLength); Line(sb, 3, "刀柄长度", "ShankLength", tp.ShankLength); Line(sb, 3, "刀头加长杆刀柄", "HolderExtensioShank", tp.HolderExtensioShank); Line(sb, 3, "刀头加长杆", "HolderExtensio", tp.HolderExtensio); Line(sb, 3, "安全高度", "SafeZ", tp.SafeZ); Line(sb, 3, "半径补偿编码", "CompCode", tp.CompCode); Line(sb, 3, "启用半径补偿", "RadiusCompNumber_Active", tp.RadiusCompNumber_Active); Line(sb, 3, "半径补偿号", "RadiusCompNumber_Value", tp.RadiusCompNumber_Value); Line(sb, 3, "夹持名称", "OldHolderName", tp.OldHolderName); Line(sb, 3, "刀具半径补偿", "CncCutterCompensationActive", tp.CncCutterCompensationActive); Line(sb, 3, "NC文件大小", "NcFileSize", tp.NcFileSize); Line(sb, 3, "NC输出坐标系", "NcWorkplane", tp.NcWorkplane); Line(sb, 3, "切削长度", "CutLength", tp.CutLength); Line(sb, 3, "标题", "Title", tp.Title); Line(sb, 3, "刀具寿命", "ToolLife", tp.ToolLife); Line(sb, 3, "TPXmax", "TPXmax", tp.TPXmax); Line(sb, 3, "TPXmin", "TPXmin", tp.TPXmin); Line(sb, 3, "TPYmax", "TPYmax", tp.TPYmax); Line(sb, 3, "TPYmin", "TPYmin", tp.TPYmin); Line(sb, 3, "TPZmax", "TPZmax", tp.TPZmax); Line(sb, 3, "TPZmin", "TPZmin", tp.TPZmin); Line(sb, 3, "标距长度", "GaugeLength", tp.GaugeLength); Line(sb, 3, "策略类型", "Strategy", tp.Strategy); Line(sb, 3, "钻孔径向余量", "DrillThickness", tp.DrillThickness); Line(sb, 3, "钻孔轴向余量", "DrillAxialThickness", tp.DrillAxialThickness); Line(sb, 3, "刀路径向余量", "TpThickness", tp.TpThickness); Line(sb, 3, "刀路轴向余量", "TpAxialThickness", tp.TpAxialThickness); Line(sb, 3, "钻孔深度类型", "DrillDepthType", tp.DrillDepthType); Line(sb, 3, "钻孔深度用户定义", "DrillDepthUserDefined", tp.DrillDepthUserDefined); Line(sb, 3, "转速", "Rpm", tp.Rpm); Line(sb, 3, "进给", "Frate", tp.Frate); Line(sb, 3, "钻孔循环类型", "DrillType", tp.DrillType); Line(sb, 3, "啄孔深度", "PeckDepth", tp.PeckDepth); Line(sb, 3, "不合并NC", "NoMerge", tp.NoMerge); if (tp.ToolHolders != null && tp.ToolHolders.Count > 0) { sb.AppendLine(string.Format(" 刀头几何(ToolHolders): {0} 段", tp.ToolHolders.Count)); for (int i = 0; i < tp.ToolHolders.Count; i++) AppendHolder(sb, "刀头", i, tp.ToolHolders[i]); } if (tp.ToolShanks != null && tp.ToolShanks.Count > 0) { sb.AppendLine(string.Format(" 刀柄几何(ToolShanks): {0} 段", tp.ToolShanks.Count)); for (int i = 0; i < tp.ToolShanks.Count; i++) AppendHolder(sb, "刀柄", i, tp.ToolShanks[i]); } if (tp.TpSizeList != null && tp.TpSizeList.Count > 0) { sb.AppendLine(string.Format(" 刀路尺寸列表(TpSizeList): {0}", tp.TpSizeList.Count)); for (int i = 0; i < tp.TpSizeList.Count; i++) Line(sb, 4, "尺寸[" + i + "]", "TpSizeList", tp.TpSizeList[i]); } if (tp.NcCodeList != null && tp.NcCodeList.Count > 0) { Line(sb, 3, "串联二维码", "NcCodeList", string.Join("; ", tp.NcCodeList)); } AppendExtension(sb, 3, tp.ExtensionData); } private static void AppendHolder(StringBuilder sb, string kind, int index, HolderOrShank h) { if (h == null) { sb.AppendLine(string.Format(" {0}[{1}]: (null)", kind, index)); return; } sb.AppendLine(string.Format(" {0}[{1}]: 上径={2} 下径={3} 长={4}", kind, index, h.UpperDiameter, h.LowerDiameter, h.Length)); } private static void AppendExtension(StringBuilder sb, int indent, IDictionary ext) { if (ext == null || ext.Count == 0) return; string pad = new string(' ', indent * 2); sb.AppendLine(pad + "[扩展字段 ExtensionData]"); foreach (var kv in ext) { string val = kv.Value == null ? "(null)" : kv.Value.ToString(Newtonsoft.Json.Formatting.None); if (val.Length > 200) val = val.Substring(0, 200) + "...(截断)"; sb.AppendLine(string.Format("{0} {1}: {2}", pad, kv.Key, val)); } } private static void Line(StringBuilder sb, int indent, string cn, string en, object value) { string pad = new string(' ', indent * 2); string text; if (value == null) text = "(null)"; else if (value is string) text = (string)value; else text = Convert.ToString(value, System.Globalization.CultureInfo.InvariantCulture); sb.AppendLine(string.Format("{0}{1}({2}): {3}", pad, cn, en, text)); } public static void PrintPlainText(SetupsheetData data) { Console.WriteLine(ToPlainText(data)); } public static string SavePlainText(SetupsheetData data, string txtPath) { string text = ToPlainText(data); File.WriteAllText(txtPath, text, new UTF8Encoding(false)); return txtPath; } public static void PrintSummary(SetupsheetData data) { if (data == null) { Console.WriteLine(" (无对象)"); return; } string project = data.ProjectData != null ? data.ProjectData.Project : null; string session = data.ProjectData != null ? data.ProjectData.PDSession : null; Console.WriteLine(" ---- SetupsheetData 对象摘要 ----"); Console.WriteLine(" PieceNumber : {0}", data.PieceNumber ?? "(null)"); Console.WriteLine(" Project : {0}", project ?? "(null)"); Console.WriteLine(" PDSession : {0}", session ?? "(null)"); Console.WriteLine(" CreatTime : {0}", data.CreatTime ?? "(null)"); Console.WriteLine(" NcWpName : {0}", data.NcWpName ?? "(null)"); if (data.ProjectData != null) { Console.WriteLine(" TotalTime : {0}", data.ProjectData.TotalTime); Console.WriteLine(" CutTime : {0}", data.ProjectData.CutTime); } Console.WriteLine(" 工位数 : {0}", data.PositionCount); if (data.PositionDataList != null) { for (int i = 0; i < data.PositionDataList.Count; i++) { var pos = data.PositionDataList[i]; if (pos == null) { Console.WriteLine(" [{0}] (null)", i); continue; } int img = pos.ImageBytesList == null ? 0 : pos.ImageBytesList.Count(b => b != null && b.Length > 0); Console.WriteLine(" [{0}] {1} 刀路={2} 预览图={3} 订料={4}", i, pos.PositionName ?? "(unnamed)", pos.ToolPathCount, img, pos.BlockSizeString ?? ""); if (pos.ModelSizeByPosition != null) Console.WriteLine(" 模型尺寸: {0}", pos.ModelSizeByPosition); if (pos.ConfigSetupSheet != null) { var cfg = pos.ConfigSetupSheet; Console.WriteLine(" 客户/机床/材质: {0} / {1} / {2}", cfg.Customers ?? "-", cfg.MachineNo ?? "-", cfg.Material ?? "-"); } } } int tpCount = data.EnumerateToolPaths().Count(); Console.WriteLine(" 刀路合计 : {0}", tpCount); Console.WriteLine(" --------------------------------"); } public static void PrintToolPaths(SetupsheetData data) { if (data == null) { Console.WriteLine(" (无对象)"); return; } int n = 0; if (data.PositionDataList != null) { for (int pi = 0; pi < data.PositionDataList.Count; pi++) { var pos = data.PositionDataList[pi]; if (pos == null || pos.ToolPathList == null) continue; for (int ti = 0; ti < pos.ToolPathList.Count; ti++) { var tp = pos.ToolPathList[ti]; if (tp == null) continue; n++; Console.WriteLine(" #{0} 工位[{1}]{2} {3}", n, pi, pos.PositionName ?? "", tp); Console.WriteLine(" 刀号={0} Tip={1} Overhang={2} Tol={3} 余量={4}/{5} 时间={6}", tp.ToolNumber, tp.Tip, tp.Overhang, tp.Tol, tp.Thickness, tp.AxialThickness, tp.Totaltime); } } } if (n == 0) Console.WriteLine(" (无刀路)"); } public static ToolPathData FindToolPath(SetupsheetData data, string keyword) { if (data == null || string.IsNullOrWhiteSpace(keyword)) return null; keyword = keyword.Trim(); foreach (var tp in data.EnumerateToolPaths()) { if (string.Equals(tp.Toolpath, keyword, StringComparison.OrdinalIgnoreCase) || string.Equals(tp.Ncprogram, keyword, StringComparison.OrdinalIgnoreCase) || string.Equals(tp.ToolID, keyword, StringComparison.OrdinalIgnoreCase) || string.Equals(tp.Tool, keyword, StringComparison.OrdinalIgnoreCase) || (!string.IsNullOrEmpty(tp.Toolpath) && tp.Toolpath.IndexOf(keyword, StringComparison.OrdinalIgnoreCase) >= 0)) { return tp; } } return null; } public static void PrintToolPathDetail(ToolPathData tp) { if (tp == null) { Console.WriteLine(" (未找到)"); return; } Console.WriteLine(SerializeOne(tp)); } public static int SavePreviewImages(SetupsheetData data, string outputDir) { if (data == null || data.PositionDataList == null) return 0; Directory.CreateDirectory(outputDir); int saved = 0; for (int pi = 0; pi < data.PositionDataList.Count; pi++) { var pos = data.PositionDataList[pi]; if (pos == null || pos.ImageBytesList == null) continue; for (int ii = 0; ii < pos.ImageBytesList.Count; ii++) { byte[] bytes = pos.ImageBytesList[ii]; if (bytes == null || bytes.Length == 0) continue; string name = string.Format("pos{0}_{1}_img{2}.png", pi, SanitizeFileName(pos.PositionName ?? "pos"), ii); string path = Path.Combine(outputDir, name); try { using (var ms = new MemoryStream(bytes)) using (Image img = Image.FromStream(ms)) { img.Save(path, System.Drawing.Imaging.ImageFormat.Png); } saved++; Console.WriteLine(" 已保存预览图: {0}", path); } catch (Exception ex) { string bin = Path.ChangeExtension(path, ".bin"); File.WriteAllBytes(bin, bytes); saved++; Console.WriteLine(" 非位图,已写原始字节: {0} ({1})", bin, ex.Message); } } } return saved; } public static string SanitizeFileName(string name) { if (string.IsNullOrEmpty(name)) return "x"; foreach (char c in Path.GetInvalidFileNameChars()) name = name.Replace(c, '_'); return name; } private static string SerializeOne(object obj) { return JsonConvert.SerializeObject(obj, JsonSettings); } public static void PrintHelp() { Console.WriteLine(); Console.WriteLine("控制台命令(对象还原后可用):"); Console.WriteLine(" help 显示本帮助"); Console.WriteLine(" summary 打印 Latest 对象摘要"); Console.WriteLine(" text 打印完整明文(中英对照),并可再落盘 .txt"); Console.WriteLine(" tools 列出全部刀路"); Console.WriteLine(" tool <关键字> 按刀路名/NC名/刀具名查找并打印 JSON"); Console.WriteLine(" positions 列出工位"); Console.WriteLine(" images 将预览图导出到 Received\\images_xxx\\"); Console.WriteLine(" dump 将对象重新序列化为 Received\\latest-object.json"); Console.WriteLine(" path 显示最近落盘的 JSON / 明文路径"); Console.WriteLine(" load [路径] 从 JSON 文件再次还原"); Console.WriteLine(" quit / exit 退出程序"); Console.WriteLine(); } } }