feat: типы отверстий — цековка и зенковка (hole_counterbore, hole_countersink) через API7
- HoleCore отрефакторен: параметр holeType + Action<IHole3D> configure (настройка HoleParameters) - CounterboreHoleAsync (ksHTCounterbore + ISpotfacingHoleParameters: SpotfacingDiameter/Depth) - CountersinkHoleAsync (ksHTCountersinking + ICountersinkHoleParameters: ksCTDiameterAngle, Diameter/Angle) - переиспользуется размещение/подбор направления/откат из базового hole - инструменты hole_counterbore, hole_countersink; интеграционные тесты (объём ±10% / больше базы) - 69 инструментов, 118 тестов; спек docs/superpowers/specs/2026-05-27-hole-types-design.md
This commit is contained in:
@@ -28,9 +28,49 @@ public sealed class HoleService
|
||||
/// diameter — диаметр (мм). throughAll — сквозное (depth игнорируется), иначе на глубину depth (мм).
|
||||
/// </summary>
|
||||
public Task HoleAsync(double x, double y, double z, double diameter, double depth, bool throughAll, CancellationToken ct = default)
|
||||
=> _dispatcher.InvokeAsync(() => HoleCore(x, y, z, diameter, depth, throughAll), ct);
|
||||
=> _dispatcher.InvokeAsync(() => HoleCore(x, y, z, diameter, depth, throughAll, ksHoleTypeEnum.ksHTBase, null), ct);
|
||||
|
||||
private void HoleCore(double x, double y, double z, double diameter, double depth, bool throughAll)
|
||||
/// <summary>Отверстие с цековкой (цилиндрическим уширением сверху, под винт с цилиндрической
|
||||
/// головкой): основное отверстие diameter + цековка spotDiameter×spotDepth.</summary>
|
||||
public Task CounterboreHoleAsync(double x, double y, double z, double diameter, double depth, bool throughAll,
|
||||
double spotDiameter, double spotDepth, CancellationToken ct = default)
|
||||
=> _dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
if (!double.IsFinite(spotDiameter) || spotDiameter <= diameter)
|
||||
throw new ArgumentOutOfRangeException(nameof(spotDiameter), "Диаметр цековки должен быть больше диаметра отверстия.");
|
||||
if (!double.IsFinite(spotDepth) || spotDepth <= 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(spotDepth), "Глубина цековки должна быть > 0.");
|
||||
HoleCore(x, y, z, diameter, depth, throughAll, ksHoleTypeEnum.ksHTCounterbore, hole =>
|
||||
{
|
||||
var p = hole.HoleParameters as ISpotfacingHoleParameters
|
||||
?? throw new InvalidOperationException("HoleParameters не приводится к ISpotfacingHoleParameters.");
|
||||
p.SpotfacingDiameter = spotDiameter;
|
||||
p.SpotfacingDepth = spotDepth;
|
||||
});
|
||||
}, ct);
|
||||
|
||||
/// <summary>Отверстие с зенковкой (коническим уширением сверху, под винт с потайной головкой):
|
||||
/// основное отверстие diameter + зенковка sinkDiameter под углом sinkAngle (градусы, конус).</summary>
|
||||
public Task CountersinkHoleAsync(double x, double y, double z, double diameter, double depth, bool throughAll,
|
||||
double sinkDiameter, double sinkAngle, CancellationToken ct = default)
|
||||
=> _dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
if (!double.IsFinite(sinkDiameter) || sinkDiameter <= diameter)
|
||||
throw new ArgumentOutOfRangeException(nameof(sinkDiameter), "Диаметр зенковки должен быть больше диаметра отверстия.");
|
||||
if (!double.IsFinite(sinkAngle) || sinkAngle <= 0 || sinkAngle >= 180)
|
||||
throw new ArgumentOutOfRangeException(nameof(sinkAngle), "Угол зенковки должен быть в (0; 180) градусов.");
|
||||
HoleCore(x, y, z, diameter, depth, throughAll, ksHoleTypeEnum.ksHTCountersinking, hole =>
|
||||
{
|
||||
var p = hole.HoleParameters as ICountersinkHoleParameters
|
||||
?? throw new InvalidOperationException("HoleParameters не приводится к ICountersinkHoleParameters.");
|
||||
p.CountersinkType = ksCountersinkTypeEnum.ksCTDiameterAngle; // задаём диаметр + угол
|
||||
p.CountersinkDiameter = sinkDiameter;
|
||||
p.CountersinkAngle = sinkAngle;
|
||||
});
|
||||
}, ct);
|
||||
|
||||
private void HoleCore(double x, double y, double z, double diameter, double depth, bool throughAll,
|
||||
ksHoleTypeEnum holeType, Action<IHole3D>? configure)
|
||||
{
|
||||
if (!double.IsFinite(x) || !double.IsFinite(y) || !double.IsFinite(z))
|
||||
throw new ArgumentException("Координаты точки отверстия должны быть конечными числами.");
|
||||
@@ -68,13 +108,14 @@ public sealed class HoleService
|
||||
{
|
||||
hole = container.Holes3D.Add()
|
||||
?? throw new InvalidOperationException("Holes3D.Add() вернул null.");
|
||||
hole.HoleType = ksHoleTypeEnum.ksHTBase;
|
||||
hole.HoleType = holeType;
|
||||
hole.Diameter = diameter;
|
||||
hole.DepthType = throughAll ? ksDepthTypeEnum.ksDTReachThrough : ksDepthTypeEnum.ksDTValue;
|
||||
if (!throughAll) hole.Depth = depth;
|
||||
hole.EndFaceType = ksEndFaceTypeEnum.ksEFFlat;
|
||||
hole.Axis = false;
|
||||
hole.ShowThread = false;
|
||||
configure?.Invoke(hole); // параметры цековки/зенковки (после установки HoleType)
|
||||
|
||||
var disp = (IHoleDisposal)hole;
|
||||
disp.BaseSurface = (IModelObject)face;
|
||||
|
||||
@@ -212,6 +212,38 @@ public sealed class FeatureTools(KompasSession session, PartModeler modeler, Hol
|
||||
return $"Уклон {angle}° создан на {count} гранях (нейтральная плоскость {neutralPlane}), id={id}.";
|
||||
}
|
||||
|
||||
[McpServerTool(Name = "hole_counterbore")]
|
||||
[Description("Отверстие с ЦЕКОВКОЙ (цилиндрическое уширение сверху — под винт с цилиндрической головкой) на грани в точке (x,y,z — центр). diameter — диаметр отверстия (мм). depth/throughAll — как у hole. spotfaceDiameter — диаметр цековки (мм, больше diameter), spotfaceDepth — глубина цековки (мм). Направление в тело — авто.")]
|
||||
public async Task<string> HoleCounterbore(
|
||||
double x, double y, double z,
|
||||
[Description("Диаметр отверстия, мм")] double diameter,
|
||||
[Description("Диаметр цековки, мм (> diameter)")] double spotfaceDiameter,
|
||||
[Description("Глубина цековки, мм")] double spotfaceDepth,
|
||||
[Description("Глубина отверстия, мм (если throughAll=false)")] double depth = 0,
|
||||
[Description("Сквозное отверстие")] bool throughAll = false)
|
||||
{
|
||||
await session.ConnectAsync();
|
||||
await holes.CounterboreHoleAsync(x, y, z, diameter, depth, throughAll, spotfaceDiameter, spotfaceDepth);
|
||||
var kind = throughAll ? "сквозное" : $"глубина {depth} мм";
|
||||
return $"Отверстие с цековкой Ø{diameter} (цековка Ø{spotfaceDiameter}×{spotfaceDepth}, {kind}) создано в точке ({x}, {y}, {z}).";
|
||||
}
|
||||
|
||||
[McpServerTool(Name = "hole_countersink")]
|
||||
[Description("Отверстие с ЗЕНКОВКОЙ (коническое уширение сверху — под винт с потайной головкой) на грани в точке (x,y,z — центр). diameter — диаметр отверстия (мм). depth/throughAll — как у hole. sinkDiameter — диаметр зенковки (мм, больше diameter), sinkAngle — угол конуса зенковки (градусы, 0..180; типично 90). Направление в тело — авто.")]
|
||||
public async Task<string> HoleCountersink(
|
||||
double x, double y, double z,
|
||||
[Description("Диаметр отверстия, мм")] double diameter,
|
||||
[Description("Диаметр зенковки, мм (> diameter)")] double sinkDiameter,
|
||||
[Description("Угол конуса зенковки, градусы (0..180, типично 90)")] double sinkAngle,
|
||||
[Description("Глубина отверстия, мм (если throughAll=false)")] double depth = 0,
|
||||
[Description("Сквозное отверстие")] bool throughAll = false)
|
||||
{
|
||||
await session.ConnectAsync();
|
||||
await holes.CountersinkHoleAsync(x, y, z, diameter, depth, throughAll, sinkDiameter, sinkAngle);
|
||||
var kind = throughAll ? "сквозное" : $"глубина {depth} мм";
|
||||
return $"Отверстие с зенковкой Ø{diameter} (зенковка Ø{sinkDiameter}, угол {sinkAngle}°, {kind}) создано в точке ({x}, {y}, {z}).";
|
||||
}
|
||||
|
||||
[McpServerTool(Name = "rebuild")]
|
||||
[Description("Перестроить активный документ.")]
|
||||
public async Task<string> Rebuild()
|
||||
|
||||
Reference in New Issue
Block a user