1package com.example.mazatrol.cycles;
2
3import java.util.ArrayList;
4import java.util.List;
5
6public class ThreadingCycleSelector {
7
8
9public static final String THREAD_TYPE_METRIC = "METRIC";
10public static final String THREAD_TYPE_UNIFIED = "UNIFIED";
11public static final String THREAD_TYPE_ACME = "ACME";
12
13
14public static final String CYCLE_G32 = "G32";
15public static final String CYCLE_G33 = "G33";
16public static final String CYCLE_G76 = "G76";
17
18
19public static class ThreadingCycle {
20private String cycleCode;
21private double feedRate;
22private double depthOfCut;
23private int numberOfPasses;
24private double startDepth;
25private double endDepth;
26private double pitch;
27private String threadType;
28private double majorDiameter;
29private double minorDiameter;
30private double majorDiameterStart;
31private double majorDiameterEnd;
32private double threadAngle;
33private double retractPlane;
34
35
36public ThreadingCycle(String cycleCode, double pitch, double majorDiameter, double minorDiameter, double startDepth, double endDepth, double feedRate, double depthOfCut, double retractPlane) {
37this.cycleCode = cycleCode;
38this.pitch = pitch;
39this.majorDiameter = majorDiameter;
40this.minorDiameter = minorDiameter;
41this.startDepth = startDepth;
42this.endDepth = endDepth;
43this.feedRate = feedRate;
44this.depthOfCut = depthOfCut;
45this.retractPlane = retractPlane;
46this.numberOfPasses = 1;
47this.threadAngle = 60.0;
48this.majorDiameterStart = majorDiameter;
49this.majorDiameterEnd = majorDiameter;
50}
51
52
53public ThreadingCycle(String cycleCode, double pitch, double majorDiameter, double minorDiameter, double startDepth, double endDepth, double depthOfCut, int numberOfPasses, double threadAngle, double retractPlane, double majorDiameterStart, double majorDiameterEnd) {
54this.cycleCode = cycleCode;
55this.pitch = pitch;
56this.majorDiameter = majorDiameter;
57this.minorDiameter = minorDiameter;
58this.startDepth = startDepth;
59this.endDepth = endDepth;
60this.depthOfCut = depthOfCut;
61this.numberOfPasses = numberOfPasses;
62this.threadAngle = threadAngle;
63this.retractPlane = retractPlane;
64this.majorDiameterStart = majorDiameterStart;
65this.majorDiameterEnd = majorDiameterEnd;
66
67
68this.feedRate = pitch;
69}
70
71
72public String getCycleCode() { return cycleCode; }
73public double getFeedRate() { return feedRate; }
74public double getDepthOfCut() { return depthOfCut; }
75public int getNumberOfPasses() { return numberOfPasses; }
76public double getStartDepth() { return startDepth; }
77public double getEndDepth() { return endDepth; }
78public double getPitch() { return pitch; }
79public String getThreadType() { return threadType; }
80public double getMajorDiameter() { return majorDiameter; }
81public double getMinorDiameter() { return minorDiameter; }
82public double getMajorDiameterStart() { return majorDiameterStart; }
83public double getMajorDiameterEnd() { return majorDiameterEnd; }
84public double getThreadAngle() { return threadAngle; }
85public double getRetractPlane() { return retractPlane; }
86
87@Override
88public String toString() {
89StringBuilder sb = new StringBuilder();
90sb.append("Cycle: " + cycleCode + ", Pitch: " + pitch + ", MajorDia: " + majorDiameter + ", MinorDia: " + minorDiameter);
91sb.append(", StartDepth: " + startDepth + ", EndDepth: " + endDepth);
92if (cycleCode.equals(CYCLE_G76)) {
93sb.append(", Passes: " + numberOfPasses + ", Angle: " + threadAngle);
94} else {
95sb.append(", Feed: " + feedRate);
96}
97return sb.toString();
98}
99}
100
101
102public static class ThreadSpec {
103private double majorDiameter;
104private double pitch;
105private String threadType;
106private double threadLength;
107private double maxDepthPerPass;
108private double retractPlane;
109private double spindleSpeed;
110private double toolAngle;
111
112public ThreadSpec(double majorDiameter, double pitch, String threadType, double threadLength, double maxDepthPerPass, double retractPlane, double spindleSpeed, double toolAngle) {
113this.majorDiameter = majorDiameter;
114this.pitch = pitch;
115this.threadType = threadType;
116this.threadLength = threadLength;
117this.maxDepthPerPass = maxDepthPerPass;
118this.retractPlane = retractPlane;
119this.spindleSpeed = spindleSpeed;
120this.toolAngle = toolAngle;
121}
122
123public double getMajorDiameter() { return majorDiameter; }
124public double getPitch() { return pitch; }
125public String getThreadType() { return threadType; }
126public double getThreadLength() { return threadLength; }
127public double getMaxDepthPerPass() { return maxDepthPerPass; }
128public double getRetractPlane() { return retractPlane; }
129public double getSpindleSpeed() { return spindleSpeed; }
130public double getToolAngle() { return toolAngle; }
131}
132
133
134
135
136
137
138
139public static ThreadingCycle selectThreadingCycle(ThreadSpec spec) {
140
141if (spec == null) {
142throw new IllegalArgumentException("Thread specification cannot be null.");
143}
144if (spec.getMajorDiameter() <= 0 || spec.getPitch() <= 0 || spec.getThreadLength() <= 0 || spec.getMaxDepthPerPass() <= 0 || spec.getRetractPlane() < 0) {
145throw new IllegalArgumentException("Invalid thread parameters: dimensions must be positive.");
146}
147if (spec.getSpindleSpeed() <= 0) {
148throw new IllegalArgumentException("Spindle speed must be positive.");
149}
150if (spec.getToolAngle() <= 0 || spec.getToolAngle() > 180) {
151throw new IllegalArgumentException("Tool angle must be between 0 and 180 degrees.");
152}
153
154
155double majorDiameter = spec.getMajorDiameter();
156double pitch = spec.getPitch();
157double threadLength = spec.getThreadLength();
158double maxDepthPerPass = spec.getMaxDepthPerPass();
159double retractPlane = spec.getRetractPlane();
160double spindleSpeed = spec.getSpindleSpeed();
161double toolAngle = spec.getToolAngle();
162String threadType = spec.getThreadType() != null ? spec.getThreadType().toUpperCase() : "";
163
164
165double minorDiameter;
166double effectiveThreadAngle = toolAngle;
167
168
169if (threadType.equals(THREAD_TYPE_METRIC) || threadType.equals(THREAD_TYPE_UNIFIED)) {
170effectiveThreadAngle = 60.0;
171
172minorDiameter = majorDiameter - 1.299 * pitch;
173} else if (threadType.equals(THREAD_TYPE_ACME)) {
174effectiveThreadAngle = 29.0;
175
176minorDiameter = majorDiameter - (1.732 * pitch + 0.020);
177} else {
178
179
180minorDiameter = majorDiameter - (2.0 * pitch * Math.cos(Math.toRadians(toolAngle / 2.0)));
181}
182
183
184if (minorDiameter >= majorDiameter) {
185minorDiameter = majorDiameter - 0.1;
186}
187
188
189double totalThreadDepth = (majorDiameter - minorDiameter) / 2.0;
190
191
192
193
194double feedRate = pitch;
195
196
197String selectedCycle;
198int numberOfPasses = 0;
199double depthOfCut = 0.0;
200double calculatedFeedRate = 0.0;
201double calculatedMajorDiameterStart = majorDiameter;
202double calculatedMajorDiameterEnd = majorDiameter;
203
204
205
206
207boolean requiresMultiPass = (totalThreadDepth > maxDepthPerPass * 1.5) || (maxDepthPerPass < pitch * 1.5);
208
209if (requiresMultiPass && threadType.equals(THREAD_TYPE_METRIC) || threadType.equals(THREAD_TYPE_UNIFIED)) {
210selectedCycle = CYCLE_G76;
211
212numberOfPasses = (int) Math.ceil(totalThreadDepth / maxDepthPerPass);
213if (numberOfPasses == 0) numberOfPasses = 1;
214depthOfCut = totalThreadDepth / numberOfPasses;
215calculatedFeedRate = pitch;
216
217
218
219
220calculatedMajorDiameterStart = majorDiameter;
221calculatedMajorDiameterEnd = majorDiameter;
222
223} else {
224
225
226selectedCycle = CYCLE_G32;
227numberOfPasses = 1;
228depthOfCut = totalThreadDepth;
229calculatedFeedRate = pitch;
230calculatedMajorDiameterStart = majorDiameter;
231calculatedMajorDiameterEnd = majorDiameter;
232}
233
234
235double finalStartDepth = 0.0;
236double finalEndDepth = -threadLength;
237
238
239if (threadLength < totalThreadDepth) {
240finalEndDepth = -threadLength;
241} else {
242finalEndDepth = -totalThreadDepth;
243}
244
245
246if (finalEndDepth > 0) finalEndDepth = 0;
247
248
249
250
251
252if (selectedCycle.equals(CYCLE_G76)) {
253return new ThreadingCycle(selectedCycle, pitch, majorDiameter, minorDiameter, finalStartDepth, finalEndDepth, depthOfCut, numberOfPasses, effectiveThreadAngle, retractPlane, calculatedMajorDiameterStart, calculatedMajorDiameterEnd);
254} else {
255return new ThreadingCycle(selectedCycle, pitch, majorDiameter, minorDiameter, finalStartDepth, finalEndDepth, calculatedFeedRate, depthOfCut, retractPlane);
256}
257}
258
259
260private static double getMinorDiameter(double majorDiameter, double pitch, String threadType, double toolAngle) {
261double minorDiameter;
262if (THREAD_TYPE_METRIC.equals(threadType) || THREAD_TYPE_UNIFIED.equals(threadType)) {
263minorDiameter = majorDiameter - 1.299 * pitch;
264} else if (THREAD_TYPE_ACME.equals(threadType)) {
265minorDiameter = majorDiameter - (1.732 * pitch + 0.020);
266} else {
267minorDiameter = majorDiameter - (2.0 * pitch * Math.cos(Math.toRadians(toolAngle / 2.0)));
268}
269return Math.max(0.1, minorDiameter);
270}
271
272
273public static void main(String[] args) {
274
275ThreadSpec metricSpec = new ThreadSpec(10.0, 1.5, THREAD_TYPE_METRIC, 15.0, 1.0, 5.0, 1000.0, 60.0);
276try {
277ThreadingCycle cycle1 = selectThreadingCycle(metricSpec);
278System.out.println("Example 1 (Metric): " + cycle1);
279} catch (IllegalArgumentException e) {
280System.err.println("Error in Example 1: " + e.getMessage());
281}
282
283
284ThreadSpec unifiedSpec = new ThreadSpec(12.0, 2.0, THREAD_TYPE_UNIFIED, 10.0, 2.0, 5.0, 800.0, 60.0);
285try {
286ThreadingCycle cycle2 = selectThreadingCycle(unifiedSpec);
287System.out.println("Example 2 (Unified): " + cycle2);
288} catch (IllegalArgumentException e) {
289System.err.println("Error in Example 2: " + e.getMessage());
290}
291
292
293ThreadSpec acmeSpec = new ThreadSpec(20.0, 4.0, THREAD_TYPE_ACME, 25.0, 2.0, 5.0, 500.0, 29.0);
294try {
295ThreadingCycle cycle3 = selectThreadingCycle(acmeSpec);
296System.out.println("Example 3 (ACME): " + cycle3);
297} catch (IllegalArgumentException e) {
298System.err.println("Error in Example 3: " + e.getMessage());
299}
300
301
302ThreadSpec invalidSpec = new ThreadSpec(10.0, 0.0, THREAD_TYPE_METRIC, 10.0, 1.0, 5.0, 1000.0, 60.0);
303try {
304ThreadingCycle cycle4 = selectThreadingCycle(invalidSpec);
305System.out.println("Example 4 (Invalid): " + cycle4);
306} catch (IllegalArgumentException e) {
307System.err.println("Error in Example 4: " + e.getMessage());
308}
309}
310}