Skip to content

Commit cb31b2e

Browse files
Merge pull request #3 from A9G-Data-Droid/framework
Tools should use Framework 4.8
2 parents 53defd0 + 1b25eff commit cb31b2e

File tree

5 files changed

+273
-208
lines changed

5 files changed

+273
-208
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
2+
<s:Boolean x:Key="/Default/UserDictionary/Words/=FANUC/@EntryIndexedValue">True</s:Boolean>
3+
<s:Boolean x:Key="/Default/UserDictionary/Words/=CNC/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Text;
5+
using System.Text.RegularExpressions;
6+
using System.Threading.Tasks;
7+
8+
namespace SplitFANUCProgramBackup;
9+
10+
public static class Fanuc
11+
{
12+
/// <summary>
13+
/// The left side of the OR (|) describes an "O Number", the original CNC program name structure, consisting of an "O" followed by 4 to 8 numbers
14+
/// The right side of the OR (|) describes the new FANUC 32 character alphanumeric program name. Must end in either .NC or .CNC
15+
/// </summary>
16+
public static readonly Regex ONumberPattern = new(@"^(O\d{4,8}|<\w+.c?nc>)", RegexOptions.Multiline | RegexOptions.Compiled);
17+
18+
/// <summary>
19+
/// Matches subdirectory flags dispersed throughout the backup file.
20+
/// </summary>
21+
private static readonly Regex DirectoryFlag = new("(&F=)", RegexOptions.Compiled);
22+
23+
/// <summary>
24+
/// Character found at the top and bottom of each program.
25+
/// The CNC control uses this to determine when a program begins and ends.
26+
/// </summary>
27+
public const char ProgramDelimiter = '%';
28+
private const string CncProgramFileExtension = ".CNC";
29+
private const string DefaultCncProgramName = "Unknown";
30+
private static readonly char[] SubFolderTrim = [' ', '/'];
31+
private const int MinimumProgramSize = 7;
32+
private const char LineFeed = '\n';
33+
34+
/// <summary>
35+
/// Split cnc programs found in the text file and save them as individual files.
36+
/// File subdirectories reflect those on the source CNC controller.
37+
/// </summary>
38+
/// <param name="backupFile">Full path to "ALL-PROG.TXT"</param>
39+
/// <param name="outputFolder">Destination output will be written to.</param>
40+
public static void SplitAllProgTxt(FileInfo backupFile, string outputFolder)
41+
{
42+
// Use parallel to decouple reading of the old file from writing the new ones. This allows reading at full speed.
43+
Parallel.ForEach(GetCncPrograms(backupFile.FullName, outputFolder), newFile =>
44+
{
45+
string programFileName = GetProgramNameFromHeader(newFile.ProgramText);
46+
if (programFileName.Length < 1)
47+
programFileName = DefaultCncProgramName;
48+
49+
string outputFilename =
50+
Path.Combine(outputFolder, newFile.SubFolder, programFileName + CncProgramFileExtension);
51+
try
52+
{
53+
File.WriteAllText(outputFilename, newFile.ProgramText);
54+
Console.WriteLine($"CREATED FILE: {outputFilename}");
55+
}
56+
catch (Exception err)
57+
{
58+
Console.WriteLine($"ERROR {err.HResult}: {err.Message}");
59+
Console.WriteLine($"FAILED TO CREATE FILE: {outputFilename}");
60+
}
61+
});
62+
}
63+
64+
/// <summary>
65+
/// Searches for program names in CNC program headers
66+
/// </summary>
67+
/// <param name="cncProgramText">The full text of a CNC program</param>
68+
/// <returns>The program name from the header</returns>
69+
private static string GetProgramNameFromHeader(string cncProgramText)
70+
{
71+
return ONumberPattern.Match(cncProgramText).Value;
72+
}
73+
74+
/// <summary>
75+
/// Reads a text file and attempts to split out CNC programs found within,
76+
/// while capturing each program's subdirectory as in the source CNC controller.
77+
/// </summary>
78+
/// <param name="fileName">Full path to "ALL-PROG.TXT"</param>
79+
/// <param name="outputFolder">Destination folder.</param>
80+
/// <returns>Each CNC program as a string, and any associated subdirectory</returns>
81+
private static IEnumerable<(string SubFolder, string ProgramText)> GetCncPrograms(string fileName, string outputFolder)
82+
{
83+
StringBuilder content = new();
84+
string subFolder = "";
85+
86+
// Searches for CNC programs between program name symbols
87+
var lines = File.ReadLines(fileName);
88+
foreach(string line in lines)
89+
{
90+
// Checks for subdirectory notation
91+
if (DirectoryFlag.IsMatch(line))
92+
{
93+
if (content.Length > MinimumProgramSize)
94+
{
95+
// Return file in buffer, as new subdirectory only applies to subsequent programs
96+
yield return (subFolder, CncProgramText(content));
97+
98+
// Start a new file
99+
content.Clear();
100+
}
101+
102+
// Strip out the directory flag and slashes to get just the folder name.
103+
subFolder = DirectoryFlag.Replace(line, string.Empty).Trim(SubFolderTrim);
104+
Directory.CreateDirectory(Path.Combine(outputFolder, subFolder));
105+
106+
// Don't append notation to next program
107+
continue;
108+
}
109+
110+
if (ONumberPattern.IsMatch(line))
111+
{
112+
if (content.Length > MinimumProgramSize)
113+
{ // Return the file we have in the buffer
114+
yield return (subFolder, CncProgramText(content));
115+
}
116+
117+
// Start a new file
118+
content.Clear();
119+
}
120+
121+
// Add unix style line terminators
122+
content.Append(line)
123+
.Append(LineFeed);
124+
}
125+
126+
// Once we reach the end we will have the final program in the buffer.
127+
yield return (subFolder, CncProgramText(content));
128+
}
129+
130+
private static string CncProgramText(StringBuilder content)
131+
{
132+
// Prevent IndexOutOfBounds exceptions if final program is empty
133+
if (content.Length <= MinimumProgramSize)
134+
return content.ToString();
135+
136+
// Add % to the top
137+
if (content[0] != ProgramDelimiter)
138+
{
139+
content.Insert(0, LineFeed);
140+
content.Insert(0, ProgramDelimiter);
141+
}
142+
143+
content.TrimEnd();
144+
145+
// Add % to the bottom when missing
146+
if (content[content.Length - 1] != ProgramDelimiter)
147+
{
148+
content.Append(ProgramDelimiter)
149+
.Append(LineFeed);
150+
}
151+
152+
return content.ToString();
153+
}
154+
}

0 commit comments

Comments
 (0)