CAM API
  • CAM API
v3.0.3
Search Results for

    Show / Hide Table of Contents

    NC Generation & Simulation — ENCY CAM API

    This document covers the interfaces that take a machining operation from geometry assignment through toolpath calculation, CLData output, NC program generation, and simulation.


    High-level flow

    Geometry model (faces / curves / holes / levels / zones)
            |
       ICamApiModelFormer  (attached to ICamApiTechOperation)
            |
       ICamApiTechOperationSolver.MakeWorkPath()
            |
       ICamApiCLDReceiver  (toolpath command stream)
            |        \
            |    IExtensionGeomCLDataConverter  (optional intercept / transform)
            |
       ICamApiProject.SaveClData()  →  *.inpcld file
            |
       ICamApiNCMaker.Generate()   →  NC program file(s)
            |
       ICamApiSimulator             (verify with machining simulation)
    

    ICamApiNCMaker — NC program generation

    ICamApiNCMaker converts a saved CLData file into one or more NC program files using a postprocessor. Obtained from ICamApiProject.NCMaker.

    Postprocessor types

    public enum TCamApiNCMakerSettingsType
    {
        ncsDotnet = 0,  // .NET-based postprocessor (XML settings file)
        ncsSppx   = 1   // SPPX postprocessor
    }
    

    ICamApiMakeCncDotnetSettings

    Settings for the .NET postprocessor.

    Member Description
    SettingsFilePath (read/write) Path to the XML settings file for the postprocessor

    ICamApiMakeCncSppxSettings

    Settings for the SPPX postprocessor.

    Member Description
    OutputFolder (read/write) Directory where the NC file is written
    NcFileName (read/write) File name of the resulting NC program

    ICamApiNCMaker methods

    Member Description
    CreateSettings(type, out status) Creates a settings object of the requested type
    Generate(clDataFileName, postProcessorFilePath, settings, out status) Runs the postprocessor and returns the list of generated file names (IListString)
    GenerateNC(clDataInfo, postProcessorFilePath, settings, out status) Modern variant — takes an ICamApiNCMakerCLDataInfo (from ICamApiProject.SaveCLDataInfo) instead of a bare path and returns a rich ICamApiNCGenerationResults

    GenerateNC and result objects

    GenerateNC is the richer counterpart to Generate: it consumes the ICamApiNCMakerCLDataInfo returned by ICamApiProject.SaveCLDataInfo (which pairs the CLData file with the exact list of operations that produced it — see project.md) and returns an ICamApiNCGenerationResults describing the outcome.

    ICamApiNCGenerationResults (all read-only):

    Member Description
    GeneratedOK true if generation finished successfully
    NCFileNames IListString of produced NC files
    Messages IListString of postprocessor messages
    TranslatedByDotNet true if a .NET postprocessor was used, false for SPPX
    PostProcessorFilePath / Settings The postprocessor and settings that were used
    CLDataFileName / CLDataInfo The CLData file and its ICamApiNCMakerCLDataInfo

    ICamApiNCMakerCLDataInfo: CLDFileName (string) + OperationsList — an ICamApiNCMakerOperationsList (Count, OperationInfo[i]). Each TCamApiNCMakerOperationInfo record carries OpID, ParentOpID (GUIDs), Name, TypeName.

    using var clInfoCom = projectCom.SaveCLDataInfo(clDataFile, operationIteratorCom);
    using var resultsCom = ncMakerCom.GenerateNC(clInfoCom, ppPath, settingsCom);
    if (resultsCom.Invoke(r => r.GeneratedOK))
    {
        using var filesCom = resultsCom.InvokeAndWrap(r => r.NCFileNames);
        // filesCom lists the generated NC programs
    }
    

    NC files exporter. An extension may implement IExtensionNCFilesExporter (TargetsCount, GetTargetCaption(i), ExportToTarget(i, ncGenerationResults)) to push the ICamApiNCGenerationResults to external targets (PLM, network folders, machine controllers).

    Full SaveClData + NCMaker workflow

    The two-step pattern: first save CLData to a temporary file, then call Generate.

    // Step 1 — save CLData from the current operation set
    var clDataFile = Path.Combine(Path.GetTempPath(), "output.inpcld");
    project.SaveClData(clDataFile, operationIterator, out var ret);
    if (ret.Code == TResultStatusCode.rsError)
        throw new Exception("Error saving CLData: " + ret.Description);
    
    // Step 2 — choose postprocessor type and create settings
    var settings = ncMaker.CreateSettings(TCamApiNCMakerSettingsType.ncsSppx, out ret)
        as ICamApiMakeCncSppxSettings
        ?? throw new Exception("Failed to create SPPX settings");
    settings.OutputFolder = Path.GetTempPath();
    settings.NcFileName   = "part.nc";
    
    // Step 3 — point to the postprocessor file and generate
    var ppPath = Path.Combine(pathsHelper.PostprocessorsFolder, "Mill", "Fanuc (30i)_Mill.sppx");
    ncMaker.Generate(clDataFile, ppPath, settings, out ret);
    if (ret.Code == TResultStatusCode.rsError)
        throw new Exception("Error generating NC: " + ret.Description);
    

    Helper wrapper version (from NCMakerHelper):

    using var settingsCom = ncMakerCom.CreateSettings(TCamApiNCMakerSettingsType.ncsSppx);
    // cast to SPPX settings and configure ...
    var filesCom = ncMakerCom.Generate(clDataFile, ppPath, settingsCom);
    

    Reference example: GCodeGeneration/ExtensionUtilityNCMakerNet/project/main/ExtensionNcMaker.cs Full workflow example: FullWorkflow/FullWorkflow3DProject/project/main/TechnologyHelper.cs (GenerateGCode method)


    ICamApiSimulator — machining simulation

    ICamApiSimulator drives the built-in material-removal simulation. Obtained from ICamApiProject.Simulator.

    Collision / gouge detection flags

    Property Type Description
    CheckGouges bool Detect tool gouging of the part
    CheckHolderCollisions bool Detect holder collisions
    CheckMachineCollisions bool Detect machine node collisions

    Break conditions

    Property Type Description
    BreakOnStopCommand bool Pause on STOP CLData command
    BreakOnEndOfOperation bool Pause after each operation completes
    BreakOnErrors bool Pause when a simulation error is detected

    Simulation speed

    Property Type Description
    SimulationSpeedPercent int Speed for smooth simulation, 0–100

    Fast (batch) simulation methods

    Method Description
    FastSimulateCurrentOperation() Simulate only the current operation
    FastSimulateUpToCurrentOperation() Simulate from the first operation up to and including the current one
    FastSimulateAllOperations() Simulate every operation in the project
    ResetSimulationResults() Clear all simulation results

    Smooth (interactive) simulation methods

    Method Description
    SmoothSimulationStart() Start continuous smooth playback from the current position
    SmoothSimulationStop() Stop smooth playback
    SmoothSimulationStepForward() Execute the next CLData command
    SmoothSimulationStepBackward() Step back one CLData command

    Additional collision level, navigation, and display state

    Beyond the three collision flags above, the simulator exposes a deepest-level check and error navigation. All members below have SimulatorHelper getters/setters (property style) unless noted as a method.

    Member (helper) Type Description
    CheckHiddenNodesCollisions bool Deepest collision level — also toggles CheckMachineCollisions+CheckHolderCollisions when enabled
    GoToNextError() / GoToPrevError() method Activate the next/previous CLData command or operation that has a simulation error
    SimulationCurrentTime double Current simulation time in minutes from the start of the process (settable to seek)
    SmoothSimulationEnabled bool Paint toolpath continuously (true) vs. advance per block (false)
    RapidScalePower int Rapid-motion scale as a power of two: 0=×1 … 7=×128
    SimulationMethod TCamApiSimulationMethod Solid / voxel-3D / voxel-5D engine
    VideoMode TCamApiVideoMode Workpiece visualization mode (Model / Rest / Cut-in)
    ResetWorkpiece() method Clear cuts/chips and restore the original stock
    DeleteChips() method Start the asynchronous "delete chips" process
    SwapPhysicMulti() method Toggle PhysicGOTO ↔ MultiGOTO toolpath representation

    Viewport display toggles

    Simple bool show/hide toggles (plus two appearance settings), all via SimulatorHelper:

    WorkpieceVisible, ToolVisible, SourceModelVisible, ToolPathVisible, Visualization3DEnabled, ChannelsVisible, GCodeViewVisible, CompensationCurveVisible, ColoredWorkpiece, MetallicWorkpiece; plus WorkpieceColor (int, packed 0xRRGGBB).

    Simulation tolerances

    Independent of the simulator, the project carries the tolerance settings the simulation uses. Obtain them via ICamApiProject.SimulationTolerances (helper SimulationTolerancesHelper).

    ICamApiSimulationTolerances: ModelTolerance() / ToolTolerance() (each an ICamApiSimulationObjectTolerance), UseApproxToolpath, UseOneWorkpieceForAllCopies. Each ICamApiSimulationObjectTolerance has UseAbsolute (absolute vs. equivalent), Absolute (double, linear units), Equivalent (int, relative).

    using var tolCom = projectCom.SimulationTolerances();
    using var modelTolCom = tolCom.ModelTolerance();
    modelTolCom.SetUseAbsolute(true);
    modelTolCom.SetAbsolute(0.01);
    

    Multi-channel simulation

    Multi-channel (mill-turn / multi-turret) machines expose per-channel state:

    Member (helper) Type Description
    ChannelCount() int Number of execution channels (always ≥ 1)
    Channel(index) ComWrapper<ICamApiMcdChannel> Channel 0..ChannelCount-1
    MultiChannelMode bool Whether multi-channel simulation mode is on

    ICamApiMcdChannel (helper McdChannelHelper) reports where that channel's simulation cursor currently sits:

    Member (helper) Type Description
    Index() int Zero-based channel index
    CurrentOperation() / SetCurrentOperation(op) ICamApiTechOperation Operation being processed in this channel
    CurrentMcdNode() / SetCurrentMcdNode(node) ICamApiMcdNode MCD node at the cursor (Caption() / Info() / IsError())
    MachineEvaluator() ICamApiMachineEvaluator Live kinematic state at the cursor — GetAbsoluteMatrix() (tool control point in the geometric CS), GetCurrentWorkpieceCSWorldMatrix() (WCS)

    The full machine-side CLData tree is read per operation via ICamApiTechOperation.McdTree() — see project.md.

    using var simulatorCom = projectCom.Simulator();
    simulatorCom.FastSimulateAllOperations();
    
    for (int c = 0; c < simulatorCom.ChannelCount(); c++)
    {
        using var channelCom = simulatorCom.Channel(c);
        using var opCom = channelCom.CurrentOperation();
        using var nodeCom = channelCom.CurrentMcdNode();
        if (!nodeCom.IsNull)
            Console.WriteLine($"ch{channelCom.Index()}: {nodeCom.Caption()} — {nodeCom.Info()}");
    }
    

    Saving the machining result

    // SaveMachiningResultToSTL(partStage, fileName, out status)
    // partStage — reserved for future use, pass null
    simulator.SaveMachiningResultToSTL(null, outputStlPath, out var ret);
    if (ret.Code == TResultStatusCode.rsError)
        throw new Exception(ret.Description);
    

    Complete simulation example

    using var simulatorCom = activeProjectCom.InvokeAndWrap(project => project.Simulator);
    simulatorCom.Invoke(simulator =>
    {
        // configure checks
        simulator.CheckGouges            = true;
        simulator.CheckHolderCollisions  = true;
        simulator.CheckMachineCollisions = true;
    
        // configure break conditions
        simulator.BreakOnStopCommand     = false;
        simulator.BreakOnEndOfOperation  = false;
        simulator.BreakOnErrors          = false;
    
        // run
        simulator.ResetSimulationResults();
        simulator.FastSimulateAllOperations();
    
        // export result
        var stlPath = Path.Combine(Path.GetTempPath(), "result.stl");
        simulator.SaveMachiningResultToSTL(null, stlPath, out var ret);
        if (ret.Code == TResultStatusCode.rsError)
            throw new Exception(ret.Description);
    });
    

    Helper wrapper version (from SimulatorHelper):

    simulatorCom.SetCheckGouges(true);
    simulatorCom.FastSimulateAllOperations();
    simulatorCom.SaveMachiningResultToSTL(null, stlPath);
    

    Reference example: FullWorkflow/FullWorkflow3DProject/project/main/SimulationHelper.cs


    ICamApiCLDReceiver — toolpath command stream

    ICamApiCLDReceiver is the sink for all toolpath commands emitted by either a ICamApiTechOperationSolver (custom operation) or intercepted by an IExtensionGeomCLDataConverter. Every method corresponds to one CLData command.

    Defined in CAMAPI.MCDFormerTypes. The helper base class CLDRecevierWrapperDefault (in CAMAPI.DotnetHelper) provides pass-through implementations of every method so that custom subclasses only override what they need.

    Structure commands

    Method Description
    BeginItem(itemType, id, caption) Open a CLData structural block (workpass, engage, retract, transition, …)
    EndItem() Close the current structural block
    AddComment(comment) Embed a comment string in the CLData stream
    AddPrint(text) Emit a print/log message
    AddInsert(insString) Insert a literal postprocessor string
    AddOpStop() Program optional stop

    Move commands

    Method Description
    CutTo(p) 3-axis linear move to point p
    CutTo5d(p, n) 5-axis linear move: position p, tool axis normal n
    CutTo6d(m) 6-axis linear move: full 4×3 transformation matrix m
    ArcTo2d(pe, pc, plane, rc, canBeFull) 2D arc: end point pe, centre pc, working plane, radius, full-circle flag
    ArcTo3d(pe, pc, nc, rc, canBeFull) 3D arc in plane defined by nc (arc-plane normal at centre)
    ArcTo5d(pe, ne, pc, nc, rc, canBeFull) 5-axis arc: end position+normal, centre position+normal, radius
    ArcTo6d(pe, pc, nc, rc, canBeFull) 6-axis arc: full matrix at end, centre point and normal, radius
    MultiArcTo6d(pm, pe) Multi-axis arc segment with mid-point matrix pm and end matrix pe
    SetGeomLCS(m) Set the current geometry local coordinate system
    SetToolContactData(isExists, cn) Provide tool contact point data
    SetCurrentPlane(plane) Set the active working plane (aplXY, aplYZ, aplZX, …)

    Feed commands

    Feed type is expressed as a flag value from TFeedTypeFlag:

    Flag Value Meaning
    affWorking 0 Standard cutting feed
    affRapid 1 Rapid traverse
    affFirst 2 First-line feed
    affEngage 4 Small approach feed
    affRetract 8 Small retraction feed
    affPlunge 16 Plunge (Z-axis) feed
    affFinish 32 Finishing pass feed
    affNext 64 Short transition feed (Waterline)
    affReturn 128 Return feed (pocketing / engraving)
    affApproach 256 Distant approach feed
    affRapid5D 512 5-axis rapid with axis limit control
    affTransitionOnSafe 1024 Transition on safe surface
    affApproachFromSafe 2048 Approach from safe surface
    affReturnToSafe 4096 Return to safe surface
    affLongNext 8192 Long Waterline transition
    affThreadPitch 16384 Thread pitch feed
    affChipBreak 32768 Chip-breaking feed
    Method Description
    OutStandardFeed(feed) Switch to a standard feed type (flag value)
    OutPercentFeed(feed, percent) Switch to a feed type scaled to percent % of the configured value
    OutFeed(feed, value, mpm) Switch to a feed type with an explicit value; mpm=true for m/min, false for mm/rev

    Spindle commands

    Method Description
    AddSpindleSpeedOnRPM(rpm, range, direction) Set spindle speed in RPM; direction=true for CW
    AddSpindleSpeedOnCSS(css, maxRpm, range, direction) Set constant surface speed with RPM cap
    AddSpindleOff() Stop the spindle
    AddSpindleOrient(orientationAngle) Orient spindle to angle (degrees)

    Compensation and auxiliary commands

    Method Description
    AddRadiusCompensation(mode, corrNumber) Radius compensation: arcOff, arcLeft, arcRight
    AddLengthCompensation(mode, corrNumber) Length compensation: alcOff, alcOn, alcOnNegative
    AddDelay(value) Dwell for value seconds
    AddCoolant(onOff, pipeNumber) Coolant on/off for pipe pipeNumber
    OutPower(power, value) Set laser/plasma power channel
    OutEffector(effectorID, value) Set binary effector state

    Extended cycles

    // Begin a canned cycle block
    receiver.BeginExtCycle(subCommands, subType, comment);
    receiver.AddExtCyclePropFlt(propCode, value);
    receiver.AddExtCyclePropInt(propCode, value);
    receiver.AddExtCyclePropBol(propCode, value);
    receiver.AddExtCyclePropStr(propCode, value);
    receiver.EndExtCycle();
    

    subCommands is a bitmask of TExtendedCycleSubCommand: aecscCycleOn = 1, aecscCycleOff = 2, aecscCycleCall = 4.

    Toolpath structure items (TCLDItemType)

    BeginItem / EndItem mark sections of the CLData tree:

    Value Meaning
    aitCLData Top-level CLData block
    aitSub Sub-program
    aitGroup Logical group
    aitWorkpass Cutting pass
    aitEngage Engage (entry) move
    aitRetract Retract (exit) move
    aitApproach Approach from safe position
    aitReturn Return to safe position
    aitTransition Inter-pass transition
    aitTransitionOnSafe Transition via safe surface
    aitApproachFromSafe Approach from safe surface
    aitReturnToSafe Return to safe surface
    aitLongNext Long transition (Waterline)

    Usage in a custom operation solver

    public void MakeWorkPath(ICamApiCLDReceiver cldFormer,
        ICamApiTechOperation techOperation,
        out TResultStatus resultStatus)
    {
        resultStatus = default;
    
        // switch to rapid feed and move to start
        cldFormer.OutStandardFeed((int)TFeedTypeFlag.affRapid);
        cldFormer.CutTo(new TST3DPoint { X = 0, Y = 0, Z = 50 });
    
        // plunge at plunge feed
        cldFormer.OutStandardFeed((int)TFeedTypeFlag.affPlunge);
        cldFormer.CutTo(new TST3DPoint { X = 0, Y = 0, Z = 0 });
    
        // cut rectangle at working feed
        cldFormer.OutStandardFeed((int)TFeedTypeFlag.affWorking);
        cldFormer.CutTo(new TST3DPoint { X = 100, Y =   0, Z = 0 });
        cldFormer.CutTo(new TST3DPoint { X = 100, Y = 100, Z = 0 });
        cldFormer.CutTo(new TST3DPoint { X =   0, Y = 100, Z = 0 });
        cldFormer.CutTo(new TST3DPoint { X =   0, Y =   0, Z = 0 });
    
        // add a 2D arc
        var pe = new TST3DPoint { X = 50, Y = 0, Z = 0 };
        var pc = new TST3DPoint { X = 25, Y = 0, Z = 0 };
        cldFormer.ArcTo2d(pe, pc, TCLDPlaneType.aplXY, 25.0, false);
    
        cldFormer.AddComment("End of pass");
    }
    

    Reference examples:

    • Operation/ExtensionOperationSimpleNet/project/main/ExtensionOperationSimpleNet.cs
    • Operation/ExtensionOperationParamsNet/project/main/ExtensionOperationParams.cs
    • CLData/ExtensionGeomCLDataConverterNet/project/main/CLDReceiverWrapperCustom.cs

    ICamApiExportToolpathReceiver — consuming an exported toolpath

    ICamApiTechOperation.ExportToolpath(receiver) (see project.md) streams the calculated toolpath into a callback receiver that your plugin implements. Unlike ICamApiCLDReceiver, which carries in-progress solver commands, ICamApiExportToolpathReceiver exposes a commands-and-children tree that describes the already-calculated toolpath in a neutral form suitable for custom post-processors or reports.

    Receiver lifecycle

    Method Description
    OpenCommand(commandCode, commandHandle, parentCommandHandle) Start a new command in the tree. commandCode is a TCLDataCommandCode value (see below); commands are linked to a parent command by handle.
    OpenCommandData() / CloseCommandData() Delimit the payload section of a command (points, normals, typed sub-commands).
    BeginPoints(pointsCS) / AddPoint(point) / EndPoints() Command-local polyline; pointsCS is a TST3DMatrix — the coordinate system of the transferred points relative to the current workpiece CS (G54). Optional SetNormal(vZ) / SetOrientation(vZ, vX) before a point sets orientation for 5D/6D moves.
    BeginChildren() / EndChildren() Nested sub-commands (enclosed structural items).
    CloseCommand() Close the currently open command.
    GetCommandReceiver(commandCode) Returns a typed ICamApiExportToolpathCommand* for the payload of the currently open command — cast to one of the sub-interfaces below to write typed data.
    GetCaptionReceiver() Returns ICamApiExportToolpathCommand_Caption* to set a command caption.

    Typed command sub-receivers

    Interface Purpose Typed methods
    ICamApiExportToolpathCommand_Caption Attach a display caption to the current command SetCaption(caption)
    ICamApiExportToolpathCommand_Comment Attach a comment string SetComment(comment)
    ICamApiExportToolpathCommand_Feedrate Record a feed change SetFeed(feedType, feedUnits, value) — feedUnits is TCamApiFeedUnits (fuMPM, fuMPR, fuConditionCode)
    ICamApiExportToolpathCommand_MultiGoto Multi-axis position with optional machine-axis values SetEndPoint, SetEndOrientation(A, B, C, D), SetMachineStateFlags, SetTime, BeginMachineAxes(axesCount) / SetMachineAxisValue(id, value) / EndMachineAxes()

    The commandCode passed to OpenCommand (and GetCommandReceiver) is a TCLDataCommandCode value from CAMAPI.MCDFormerTypes. Common codes: cmdGoTo=5005, cmdRapid=2005, cmdFrom=5003, cmdCircle=15000, cmdMultiGoto=9010, cmdPhysicGoto=9013, cmdMultiArc=9014, cmdFedrat=1009, cmdSpindl=1031, cmdCycle=1054, cmdExtendedCycle=1053, cmdThread=1036, cmdComment=1065, cmdPPrint=1044, cmdInsert=1046, cmdStop=2002, cmdOpStop=2003, cmdFini=14000, cmdLoadTl=1055, cmdSelcTl=1056, cmdPlane=99, cmdCoolnt=1030, cmdDelay=1010. Switch on it to decide which typed sub-receiver to return.

    Implementation skeleton

    public class MyExportReceiver : ICamApiExportToolpathReceiver
    {
        public void OpenCommand(int commandCode, ulong h, ulong parent) { /* record the node */ }
        public void OpenCommandData() { }
        public void CloseCommandData() { }
        public void BeginChildren() { }
        public void EndChildren() { }
        public void CloseCommand() { }
    
        public void BeginPoints() { }
        public void SetNormal(TST3DPoint vZ) { }
        public void SetOrientation(TST3DPoint vZ, TST3DPoint vX) { }
        public void AddPoint(TST3DPoint p) { /* record */ }
        public void EndPoints() { }
    
        public ICamApiExportToolpathCommand? GetCommandReceiver(int commandCode) => /* return typed sub-receiver based on commandCode */ null;
        public ICamApiExportToolpathCommand_Caption? GetCaptionReceiver()       => null;
    }
    
    // And from the extension:
    opCom.ExportToolpath(new MyExportReceiver());
    

    ICamApiModelFormer family — geometry model assignment

    An ICamApiTechOperation exposes five ICamApiModelFormer properties:

    Property Role
    ModelFormerJobAssignment The machining area (what to machine)
    ModelFormerPart Final part geometry
    ModelFormerWorkpiece Stock material geometry
    ModelFormerRestrictions Restricted (keep-out) areas
    ModelFormerFixtures Fixture bodies

    Each model former is a container of ICamApiModelItem objects. The concrete geometry is assigned by querying which specialised sub-interfaces the former supports at runtime, then calling the appropriate Add…Selected() method. All Add…Selected() methods operate on whatever geometry is currently selected in the ENCY CAM geometry tree.

    Checking supported item types

    Before adding geometry, check whether the operation supports the geometry kind:

    using var mfCom = operationCom.InvokeAndWrap(op => op.ModelFormerJobAssignment);
    mfCom.Invoke(mf =>
    {
        // faces
        if (mf is ICamApiModelFormerWithFaces withFaces)
        {
            using var items = ComWrapper.Create(withFaces.AddFacesSelected());
        }
    
        // 2D curves
        if (mf is ICamApiModelFormerWithCurve2D withCurve2D)
        {
            using var items = ComWrapper.Create(withCurve2D.AddCurves2DSelected());
        }
    
        // 5D curves
        if (mf is ICamApiModelFormerWithCurve5D withCurve5D)
        {
            using var items = ComWrapper.Create(withCurve5D.AddCurves5DSelected());
        }
    
        // depth levels
        if (mf is ICamApiModelFormerWithLevels withLevels)
        {
            if (withLevels.SupportsLevel(TModelFormerLevelType.amflTopLevel))
            {
                using var items = ComWrapper.Create(
                    withLevels.AddLevelSelected(TModelFormerLevelType.amflTopLevel));
            }
        }
    
        // machining zones
        if (mf is ICamApiModelFormerWithZones withZones)
        {
            if (withZones.SupportsJobZones())
                using var items = ComWrapper.Create(withZones.AddJobZoneSelected());
    
            if (withZones.SupportsRestrictedZones())
                using var items = ComWrapper.Create(withZones.AddRestrictedZoneSelected());
        }
    
        // holes
        if (mf is ICamApiModelFormerWithHoles withHoles)
        {
            using var items = ComWrapper.Create(withHoles.AddHolesSelected());
        }
    });
    

    The ICamApiModelFormer.SupportedItems property (ICamApiModelFormerSupportedItems) enumerates the item types that the operation declares it can receive, including captions, hints, and icon paths.

    ICamApiModelFormerWithFaces

    Assigns selected face geometry to the model former.

    using var mfCom = operationCom.ModelFormerJobAssignment();
    using var withFacesCom = mfCom.AsWithFaces();      // null wrapper if unsupported
    if (withFacesCom.IsNull)
        throw new Exception("The operation does not accept faces");
    
    using var itemsCom = withFacesCom.AddFacesSelected();
    

    AsWithFaces() is the narrowing helper — it wraps the QI so the caller stays outside Invoke, unlike the mf is ICamApiModelFormerWithFaces form shown above. Check IsNull instead of catching: an operation that does not accept faces simply yields a null wrapper.

    AddFacesSelected returns ICamApiListModelItem — the list of model items that were added.

    ICamApiModelFormerWithLevels

    Assigns top or bottom machining levels (Z-depth planes).

    bool supportsTop = withLevels.SupportsLevel(TModelFormerLevelType.amflTopLevel);
    if (supportsTop)
    {
        using var items = ComWrapper.Create(
            withLevels.AddLevelSelected(TModelFormerLevelType.amflTopLevel));
    }
    

    Each added item can be cast to ICamApiLevelModelItem to read/write the Stock offset.

    ICamApiModelFormerWithZones

    // Job zone (area to machine)
    if (withZones.SupportsJobZones())
        using var jobZoneItems = ComWrapper.Create(withZones.AddJobZoneSelected());
    
    // Restricted zone (keep-out area)
    if (withZones.SupportsRestrictedZones())
        using var restrictedItems = ComWrapper.Create(withZones.AddRestrictedZoneSelected());
    

    Each added job zone can be cast to ICamApiJobZoneModelItem to set:

    • Stock — machining stock offset
    • ContactMode — ajcmCenter, ajcmInside, ajcmOutside
    • AlternateFromSide — alternate machining direction

    ICamApiModelFormerWithCurve2D

    Assigns 2D contour curves.

    using var itemsCom = modelFormerWithCurve2DCom.AddCurves2DSelected();
    

    Each item can be cast to ICamApiCurve2DModelItem:

    Property Type Description
    Stock double Offset from curve
    IsLeft bool Machining side (left = true)
    IsInverted bool Invert curve direction
    Compensation bool Enable cutter radius compensation
    WithReturn bool Enable lead-in / lead-out moves

    ICamApiModelFormerWithCurve5D

    Assigns 5-axis drive curves.

    using var itemsCom = modelFormerWithCurve5DCom.AddCurves5DSelected();
    

    Each item can be cast to ICamApiCurve5DModelItem:

    Property Type Description
    Stock double Offset from curve
    AlternateDirection bool Reverse machining direction
    AlternateFrontSide bool Flip tool tilt side
    UseCustomVectors bool Enable custom tool axis vectors and feed zones
    InterpolationMode TModelFormerInterpolationMode aimLinear, aimSpline, aimSplineInterMatr
    FeedPoints ICamApiFeedPointList* Feed zone list (non-empty only when UseCustomVectors = true)

    Feed zones on a 5D curve

    Feed zones segment a 5D curve into regions with different feed rates:

    // enable custom control
    curveItem.UseCustomVectors = true;
    
    using var feedPoints = curveItem.FeedPoints;
    // add a zone starting at curve point 'pos', spanning 100 mm
    int idx = feedPoints.AddFeedPoint(pos, 100.0, out var ret);
    
    // configure the zone
    feedPoints.FeedType[idx]           = TModelFormerFeedType.afpWorkFeed;
    feedPoints.FeedRatePercentage[idx] = 50;     // 50 % of programmed feed
    feedPoints.FeedRateChangeType[idx] = TModelFormerFeedRateChangeType.afrcByPercent;
    

    Reference example: Technologist/SetFeedZone/project/main/CurveItem.cs

    ICamApiModelFormerWithHoles

    Assigns hole features or programmatically creates new holes.

    // from current selection
    using var holesFromSel = modelFormerWithHolesCom.AddHolesSelected();
    
    // create a hole programmatically
    var lcs = new TST3DMatrix { /* set axis vectors */ };
    using var holeCom = modelFormerWithHolesCom.CreateNewHole(lcs, 10.0 /* diameter */);
    holeCom.Invoke(h =>
    {
        h.TopLevel               = 0.0;
        h.TopLevelMode           = THoleTopLevelMode.htlmManual;
        h.BottomLevel            = -25.0;
        h.BottomLevelMode        = THoleBottomLevelMode.hblmManual;
        h.DrillTipCompensation   = THoleDrillTipCompensation.hdtAuto;
    });
    

    ICamApiModelFormerWithProbingItems — probing cycles and actions

    Exposed on operations of the "Point Probing" family. Items form a tree (group / movement / cycle), and each cycle owns an ordered list of typed probing actions.

    Obtain via opCom.ModelFormerJobAssignment() and cast:

    using var mfCom = opCom.ModelFormerJobAssignment();
    using var probingCom = mfCom.InvokeAndWrap(mf => mf as ICamApiModelFormerWithProbingItems);
    if (!probingCom.IsNull)
    {
        using var surf  = probingCom.AddSurfaceCycle();
        using var boss  = probingCom.AddBossCycle();
        using var hole  = probingCom.AddHoleCycle();
        using var grp   = probingCom.AddGroup();
        using var mov   = probingCom.AddMovement();
    }
    

    Factory helpers on ModelFormerWithProbingItemsHelper (each returns the typed cycle ComWrapper, no cast needed):

    Method Produced cycle interface
    AddSurfaceCycle() ICamApiSurfaceProbingCycle
    AddBossCycle() ICamApiBossProbingCycle
    AddHoleCycle() / AddHoleProtectedCycle() ICamApiHoleProbingCycle / ICamApiHoleProbingProtectedCycle
    AddWebCycle() ICamApiWebProbingCycle
    AddGrooveCycle() / AddGrooveProtectedCycle() ICamApiGrooveProbingCycle / ICamApiGrooveProbingProtectedCycle
    AddThreePointsWebCycle() ICamApiThreePointsWebProbingCycle
    AddExternalRectangleCycle() / AddInternalRectangleCycle() / AddInternalRectangleProtectedCycle() ICamApiExternalRectangleProbingCycle / ICamApi…InternalRectangleProbingCycle
    AddDoubleWallInternalCornerCycle() / AddDoubleWallExternalCornerCycle() ICamApiDoubleWall…CornerCycle
    AddTripleWallInternalCornerCycle() / AddTripleWallExternalCornerCycle() ICamApiTripleWall…CornerCycle
    AddNcActionItem() ICamApiNcActionProbingCycle
    AddFrameOutputCycle() ICamApiFrameOutputProbingCycle
    AddMovement() / AddGroup() ICamApiProbingModelItem (movement or group)
    AddCycleByTemplate(fileName) Cycle instantiated from a library template
    EnumerateProbingItems() IEnumerable<ComWrapper<ICamApiProbingModelItem>> (depth-first walk)
    GetSelectedItem() Currently selected item in the UI
    DeleteItem(itemCom) / Clear() Remove one item / all items
    GetTemplateLibraryCount() / GetTemplateLibrary(index) Loaded ICamApiProbingTemplateLibrary instances

    Cycle actions

    Every probing cycle carries an ordered list of typed ICamApiProbingAction entries — executed right after the cycle probes its primary feature (tool offset update, WCS set, report write, etc.). Typical access pattern:

    int count = cycleCom.Invoke(c => c.CycleActionsCount);
    for (int i = 0; i < count; i++)
    {
        using var actionCom = cycleCom.InvokeAndWrap(c => c.GetCycleAction(i));
        // cycle exposes AddSetToolOffsetAction / AddCheckBrokenToolAction / AddSetWcsAction /
        // AddCalibrateToolProbeAction / AddCalibratePartProbeAction / AddWriteToReportAction /
        // AddCustomPropGroupAction — each returns the typed action interface directly.
    }
    

    Helper classes: ProbingModelItemHelper, ProbingModelItemIteratorHelper, ProbingCycleHelper, ProbingActionHelper, ProbingTemplateHelper, plus specialised SurfaceProbingCycleHelper, BossHoleProbingCycleHelper, WebGrooveProbingCycleHelper, RectangleProbingCycleHelper, DoubleTripleWallProbingCycleHelper, NcActionFrameOutputProbingCycleHelper, CustomPropGroupProbingActionHelper.


    Other ICamApiModelFormer variants

    These are additional specialized model formers — each implemented by a subset of operation types. Query with mf is not ICamApi… x inside mfCom.Invoke(mf => { … }), then call the factory. Each Add…() helper returns the typed model item ComWrapper.

    Interface Helper Typical call Model item interface
    ICamApiModelFormerWithDriveFaces ModelFormerWithDriveFacesHelper AddDriveFacesSelected() ICamApiDriveFaceModelItem
    ICamApiModelFormerWithProjectCurves ModelFormerWithProjectCurvesHelper AddProjectCurvesSelected() ICamApiProjectCurveModelItem
    ICamApiModelFormerWithPocket ModelFormerWithPocketHelper AddPocketSelected() ICamApiPocketModelItem
    ICamApiModelFormerWithTurnGeometry ModelFormerWithTurnGeometryHelper AddTurnGeometrySelected() ICamApiTurnGeometryModelItem
    ICamApiModelFormerWithTurnMachineModel ModelFormerWithTurnMachineModelHelper SetItemMode(...) ICamApiTurnMachineModelItem
    ICamApiModelFormerWithGeom25D ModelFormerWithGeom25DHelper AddGeom25DSelected() ICamApiGeom25DModelItem
    ICamApiModelFormerWithSharpEdge ModelFormerWithSharpEdgeHelper AddSharpEdgesSelected() (face / edge items)
    ICamApiModelFormerWithChamferFaces ModelFormerWithChamferFacesHelper AddChamferFacesSelected() (face items)
    ICamApiModelFormerWithAreas ModelFormerWithAreasHelper AddArea(...) ICamApiAreaModelItem
    ICamApiModelFormerWithReferenceToPrevious ModelFormerWithReferenceToPrevious SetReferenceToPrevious(bool) —
    ICamApiModelFormerWithLatheContouringItems ModelFormerWithLatheContouringItemsHelper AddFacingItem() · AddTurningItem() ICamApiLatheContourFacingModelItem · ICamApiCommonTurningModelItem (see below)

    Lathe contouring / grooving / threading items

    Turning operations (Facing, OD/ID Turning, OD/ID Finishing, Grooving, Threading, …) assign their geometry through ICamApiModelFormerWithLatheContouringItems. Obtain it and add items with ModelFormerWithLatheContouringItemsHelper:

    using var mfCom = opCom.ModelFormerJobAssignment();
    using var latheCom = mfCom.AsWithLatheContouringItems(); // throws if the op is not a turning type
    
    using var facingCom  = latheCom.AddFacingItem();   // ICamApiLatheContourFacingModelItem (default cycle: Offset Roughing)
    using var turningCom = latheCom.AddTurningItem();  // ICamApiCommonTurningModelItem (default cycle: Roughing Advanced)
    

    Each contouring item selects its active cycle and exposes the cycle's shared property tabs:

    Member Description
    ActiveCycleType (R) Current cycle — a TLatheContourCycleType value
    SetActiveCycle(type) Switch the active cycle (fails with rsError if the container does not allow that cycle)
    ActiveCycle (R) The active cycle object as IUnknown — QI to the specific cycle interface
    Common / Leads / OverrideFeeds (R) Shared cycle tabs: ICamApiLatheCycleCommon / ICamApiLatheCycleLeads / ICamApiLatheCycleOverrideFeeds

    Facing accepts only lcctProfile, lcctOffsetLegacy, lcctOffsetRoughing; Turning also accepts lcctRoughingAdvanced, lcctRoughing, lcctZigzag. Roughing / Roughing-Advanced cycles additionally expose ICamApiLatheCycleChipBreakingDwell via QI.

    Active-cycle enums (each model-item family has its own):

    • TLatheContourCycleType: lcctProfile · lcctOffsetRoughing · lcctRoughingAdvanced · lcctRoughing · lcctZigzag · lcctOffsetLegacy · lcct4Axis
    • TLatheGrooveCycleType: gcctProfile · gcctSlotting · gcctGrooving · gcctZigzag
    • TLatheThreadCycleType: tcct76 · tcct92 · tcctExpanded
    • TLatheShapeThreadCycleType: sctct92 · sctctExpanded

    Shared value enums: TLatheMachiningSide (lmsLeft/lmsRight), TLatheLengthCorrector (llcFirst/llcSecond), TLatheCompensationType (lctNone/lctComputer/lctMachine).

    Many cycle sub-blocks have dedicated helpers (call on the QI'd cycle ComWrapper): LatheContourFacingModelItemHelper, LatheCycleGrooveHelper, LatheCycleGrooveBackOffHelper, LatheCycleZigzagToolpathHelper, LatheCycleRoughingStepHelper, LatheCycleZigzagGrooveStepHelper, LatheCycleChipBreakingDwellHelper, LatheCycleCommonToolAnglesHelper, LatheCycleOverrideFeedsHelper. Many of their properties are tagged unions read/written through GetXxxIsDistance / SetXxxIsDistance / GetXxxIsPercent / SetXxxIsPercent pairs (distance vs. percent variants).

    Primitive model formers (boxes, cylinders, casting)

    These model formers create pure parametric primitives — no geometry selection needed. All three primitive families share the same pattern: a factory method on the model former returns a typed item whose parameters are tweaked directly.

    Model former Helper Factory Item
    ICamApiModelFormerWithBoxPrimitives ModelFormerWithBoxPrimitives AddBoxPrimitive(lcs, w, h, d) · AddBoxLinkPrimitive(...) · AddGeomModelBoxPrimitive(...) · AddSimpleBoxPrimitive(...) ICamApiBoxPrimitiveModelItem family
    ICamApiModelFormerWithCylinderPrimitives ModelFormerWithCylinderPrimitives AddCylinderPrimitive(lcs, r, h) · AddHoleCylinderPrimitive(...) · AddSimpleCylinderPrimitive(...) ICamApiCylinderPrimitiveModelItem family
    ICamApiModelFormerWithCastingPrimitive ModelFormerWithCastingPrimitive AddCastingPrimitive(lcs, …) ICamApiCastingPrimitiveModelItem

    Each primitive carries a local coordinate system plus shape parameters; set them directly on the ComWrapper through Invoke (there are no per-field helpers unless listed in the primitive's *Helper.cs).

    Generic model items

    These typed ComWrapper specialisations are produced by the factories above. Each has a dedicated helper that exposes parameters as extension methods (call them on the ComWrapper directly):

    Model item interface Helper Purpose
    ICamApiFaceModelItem / ICamApiFacesArrayModelItem FaceModelItemHelper / FacesArrayModelItemHelper Single face / face array
    ICamApiMeshesArrayModelItem MeshesArrayModelItemHelper Mesh array
    ICamApiCurveModelItem / ICamApiCurvesArrayModelItem CurveModelItemHelper / CurvesArrayModelItemHelper Generic 3D curve / curve array
    ICamApiPointModelItem / ICamApiLineModelItem / ICamApiCoordinateItem PointModelItemHelper / LineModelItemHelper / CoordinateItemHelper Points, lines, coordinate frames
    ICamApiGeometryNodeBasedModelItem GeometryNodeBasedModelItemHelper Base for items that reference a geometry tree node
    ICamApiLinkModelItem LinkModelItemHelper Link item between two items
    ICamApiDriveFaceModelItem · ICamApiProjectCurveModelItem · ICamApiPocketModelItem · ICamApiTurnGeometryModelItem · ICamApiGeom25DModelItem · ICamApiTurnMachineModelItem …Helper Parameters for each specialized model item
    ICamApiAreaModelItem AreaModelItemHelper Area item (perimeter + floor level)
    ICamApiModelItemReference — Reference to another item (e.g. reuse a job zone)

    Identifying an existing item — Role and Caption

    ICamApiModelItem carries three different "what is this" fields, and picking the wrong one is the usual cause of a rebind/replace workflow failing:

    Property Granularity Use it for
    ItemType Coarse geometric shape (TModelItemType) Broad shape checks
    ItemGUID / ClassName Per-class type identity Exact implementation class
    Role Flat job-assignment classification (TModelItemRole) Which Add…Selected produced this item

    Role is the one to switch on when you walk an existing job assignment and need to recreate or rebind items:

    Value Produced by
    amirNone Not classified
    amirHole AddHolesSelected
    amirCurve2D AddCurves2DSelected
    amirCurve5D AddCurves5DSelected
    amirLevel AddLevel… — use ICamApiLevelModelItem.LevelType to tell Top from Bottom
    amirPocket AddPocketSelected
    amirDriveFace AddDriveFacesSelected
    amirJobZone AddJobZoneSelected
    amirRestrictedZone AddRestrictedZoneSelected
    amirFace AddFacesSelected
    amirProjectCurve AddProjectCurvesSelected
    amirGeom25D AddGeom25DSelected
    amirArea AddArea
    amirLinkToPreviousOperation Reference to the previous operation

    Caption is a full hierarchical geometry path for items created from geometry (curves, faces, holes, …) — e.g. "Part\Part1.igs\Face6". It is exactly the string ICamApiGeomModel.FindByFullName accepts, and the field ICamApiModelFormer.FindByFullName matches against — so it round-trips between the geometry tree and the job assignment.

    for (int i = 0; i < mfCom.Count(); i++)
    {
        using var itemCom = mfCom.Item(i);
        var role = itemCom.Invoke(it => it.Role);
        if (role == TModelItemRole.amirFace)
        {
            string geomPath = itemCom.Caption();          // "Part\Part1.igs\Face6"
            using var nodeCom = geomModelCom.FindByFullName(geomPath);
        }
    }
    

    ICamApiLevelModelItem additionally exposes read-only LevelType (TModelFormerLevelType) alongside its Stock.

    ICamApiModelFormerWithJobMode — swarf drive mode

    Mixin of the swarf operation only, obtained by QueryInterface; it exposes the UI's "Drive mode" toggle and persists as the operation's JobMode XML field.

    TModelFormerJobMode Meaning
    mfjmNone Not applicable to this operation
    mfjmByBottomEdge Drive by the bottom edge (single guide curve)
    mfjmByTwoCurves Drive between two curves (First curve + Second curve)
    using var jobModeCom = mfCom.InvokeAndWrap(mf => mf as ICamApiModelFormerWithJobMode);
    if (!jobModeCom.IsNull)
    {
        jobModeCom.SetJobMode(TModelFormerJobMode.mfjmByTwoCurves);   // do this FIRST
        // ... only now add the curves
    }
    

    Setting JobMode clears the current job assignment — assign the mode before adding curves, or the geometry you just added is discarded.

    Helper: ModelFormerWithJobModeHelper.

    Fixtures — nested nodes and selection helpers

    The fixture tree is a chain, not a flat list: a vise is component → Body → Jaw, so the jaw is reached as a child of the body, not as a second node of the component. Each ICamApiFixtureNodeItem therefore exposes its own children:

    Member Description
    NodeCount Number of child axis nodes directly under this node
    GetNode(index) Child axis node by zero-based index
    AddNode() Add a fixed child node nested under this node

    ICamApiModelFormerWithFixtures also mirrors two Fixtures-tab buttons:

    Method Description
    AddFacesSelected() Add the currently selected geometry faces to the fixtures tree, like the Add faces button — uses the current fixture selection or auto-creates a node. Returns the added items
    DeleteItemByCaption(caption) Delete the first fixture item whose Caption matches, like the Delete button. Returns true if one was found and removed

    Ready-made fixtures from the library

    Instead of assembling a clamp node by node, take a whole package (.mcp) from the fixtures library: it carries the bodies, the movable axis nodes, their travel limits and the geometry.

    Member Description
    LibraryComponentCount Number of packages in the fixtures library
    LibraryComponentFile[index] Full file path of a library package (.mcp). The caption inside the package may be localized, so match packages by file name, not by caption
    ImportComponentFromFile(fileName) Import the package — a full path, or a bare file name resolved against the fixtures library folder. Returns the new ICamApiFixtureConnectorItem, or a null wrapper when the package was not found

    ImportComponentFromFile builds the whole sub-tree in one call — connector, component, and the body and jaw axes with their captions, axis directions and travel limits — and loads the geometry into a hidden "Fixture model" folder of the geometry tree. Nothing is assembled by hand.

    using var modelFormerCom = rootOperationCom.ModelFormerFixtures();
    using var withFixturesCom = modelFormerCom.AsWithFixtures();
    
    var viseFile = "";
    for (var i = 0; i < withFixturesCom.LibraryComponentCount(); i++)
    {
        var file = withFixturesCom.LibraryComponentFile(i);
        if (Path.GetFileNameWithoutExtension(file) == "Vise 66x82")
            viseFile = file;
    }
    
    using var viseCom = withFixturesCom.ImportComponentFromFile(viseFile);
    if (viseCom.IsNull)
        throw new Exception("Cannot import the vise from the fixtures library");
    
    viseCom.SetConnectorIndex(baseTableConnectorIndex);
    
    // an imported component sits at the connector origin — move it into place
    using var componentCom = viseCom.GetComponent();
    componentCom.SetSetupLCS(viseLcs);
    
    // nested chain: the jaw is a child of the body
    using var bodyCom = componentCom.GetNode(0);
    using var jawCom = bodyCom.GetNode(0);
    jawCom.SetPosition(63.25);
    

    ICamApiModelFormer — base operations

    Method / Property Description
    SupportedItems Enumerate item types the operation declares support for
    MakeSupportedItems(callback) Register a callback that fills supported items programmatically
    FillItemsBySupportedItems() Apply the supported items list (returns true if items were added)
    Count / Item[i] Enumerate all model items currently in the former
    FindItem(itemID) Look up an item by string ID
    SearchInsertItem(type, id) Find or create an item of a given type
    AddItem(id, type, typeName) Add a new item entry
    DeleteItem(item) Remove an item
    DeleteItemById(id) Remove an item by ID
    GetFaceList(lcs) Retrieve all faces as ICamApiFaceList in the given coordinate system
    GetBoundingBox(lcs) Get the bounding box of all items
    AddSupportedItemsSelected(type, id) Add currently selected geometry for a specific supported-item type

    Reference example: FullWorkflow/FullWorkflow3DProject/project/main/TechnologyHelper.cs


    ICamApiTechOperationSolver — custom operation implementation

    Implement ICamApiTechOperationSolver together with IExtension to create a custom operation type.

    public class MyOperationSolver : IExtension, ICamApiTechOperationSolver
    {
        public IExtensionInfo? Info { get; set; }
        private ComWrapper<ICamApiTechOperation>? _operationCom;
    
        // --- lifecycle ---
    
        public void InitSolver(ICamApiTechOperationSolverInitializeContext context,
            out TResultStatus resultStatus)
        {
            resultStatus = default;
            _operationCom = ComWrapper.Create(context.TechOperation);
            // context.UpdateHandler is available for progress reporting
        }
    
        public void FinalizeSolver()
        {
            _operationCom?.Dispose();
            _operationCom = null;
        }
    
        // --- optional dynamic parameters ---
    
        public bool GetPropIterator(string pageId,
            out IST_CustomPropIterator? iterator,
            out TResultStatus resultStatus)
        {
            resultStatus = default;
            iterator = null;
            // Return false if the operation uses no custom property pages.
            // Return true and a populated IST_SimplePropIterator to expose
            // parameters in the operation properties panel.
            return false;
        }
    
        public void OnPropFilterChanged(string parameterName, string value)
        {
            // Called when a parameter changes; can be used to update
            // visibility/availability of other parameters.
        }
    
        // --- toolpath calculation ---
    
        public void MakeWorkPath(ICamApiCLDReceiver cldFormer,
            ICamApiTechOperation techOperation,
            out TResultStatus resultStatus)
        {
            resultStatus = default;
            try
            {
                // read parameters from XMLProp
                var xmlProp = techOperation.XMLProp;
                var depth   = xmlProp.Flt["MyParams.Depth"];
                var passes  = xmlProp.Int["MyParams.Passes"];
    
                // emit toolpath commands
                cldFormer.OutStandardFeed((int)TFeedTypeFlag.affRapid);
                cldFormer.CutTo(new TST3DPoint { X = 0, Y = 0, Z = 10 });
                // ... additional moves
            }
            catch (Exception e)
            {
                resultStatus.Code        = TResultStatusCode.rsError;
                resultStatus.Description = e.Message;
            }
        }
    }
    

    Reading XMLProp parameters

    Operation parameters are stored in an IST_XMLPropPointer tree accessible via ICamApiTechOperation.XMLProp. The property path uses dot notation for nesting:

    var xmlProp = techOperation.XMLProp;
    
    // read values
    double depth   = xmlProp.Flt["ToolpathParams.ZLayers.ZStart"];
    int    count   = xmlProp.Int["ToolpathParams.ZLayers.Count"];
    string mode    = xmlProp.Str["DrillingType"];
    bool   exists  = xmlProp.PropExists["ToolpathParams.ZLayers.ZStart"];
    
    // write values
    xmlProp.Flt["ToolpathParams.ZLayers.ZStep"] = -5.0;
    xmlProp.Str["DrillingType"] = "HolePocketing";
    

    Exposing custom parameters (GetPropIterator)

    To add a properties page to the operation panel, create and populate an IST_SimplePropIterator inside GetPropIterator. The ExtensionOperationParamsNet example shows the full pattern using OperationCustomPropsHelper as a convenience wrapper:

    public bool GetPropIterator(string pageId,
        out IST_CustomPropIterator? iterator,
        out TResultStatus resultStatus)
    {
        resultStatus = default;
        iterator = null;
        try
        {
            using var propHelpersCom =
                SystemExtensionFactory.GetSingletonExtension<IST_CustomPropHelpers>(
                    "Extension.CustomPropHelpers");
    
            _propIteratorCom?.Dispose();
            _propIteratorCom = propHelpersCom.InvokeAndWrap(h => h.CreateSimplePropIterator());
    
            // add a numeric parameter
            using var doublePropCom = propHelpersCom.InvokeAndWrap(h => h.CreateDoubleProp("Depth"));
            doublePropCom.Invoke(prop =>
            {
                prop.PropID      = "CustomOperationPropertiesArray(MyParams.Depth)";
                prop.ValueGetter = new DoubleValueGetter(() =>
                    _operationCom!.Invoke(op => op.XMLProp.Flt[prop.PropID]));
                prop.ValueSetter = new DoubleValueSetter(v =>
                    _operationCom!.Invoke(op => op.XMLProp.Flt[prop.PropID] = v));
                _propIteratorCom!.Invoke(it => it.AddNewProp(prop, -1));
            });
    
            _propIteratorCom.Invoke(it => it.MoveToRoot());
            iterator = _propIteratorCom.Invoke(it => (IST_CustomPropIterator)it);
            return true;
        }
        catch (Exception e)
        {
            resultStatus.Code        = TResultStatusCode.rsError;
            resultStatus.Description = e.Message;
            return false;
        }
    }
    

    Reference examples:

    • Operation/ExtensionOperationSimpleNet/project/main/ExtensionOperationSimpleNet.cs — minimal solver, XMLProp parameters
    • Operation/ExtensionOperationParamsNet/project/main/ExtensionOperationParams.cs — solver with full dynamic property page

    IExtensionGeomCLDataConverter — intercepting the CLData stream

    IExtensionGeomCLDataConverter allows a plugin to intercept and modify every CLData command that flows from the operation solver to the postprocessor. It is registered alongside IExtension in the extension factory.

    The interface has two methods:

    Method Description
    GetCLDReceiverWrapper(operation, receiver, out ret) Return a custom ICamApiCLDReceiver that wraps the original receiver. All commands will now pass through the wrapper.
    FinalizeConverter() Release all COM objects held by the converter. Called when the operation finishes.

    Implementation pattern

    1. Subclass CLDRecevierWrapperDefault and override only the methods of interest.
    2. In GetCLDReceiverWrapper, construct the subclass wrapping the provided receiver.
    3. In FinalizeConverter, dispose the wrapper.
    public class ExtensionGeomClDataConverter : IExtension, IExtensionGeomCLDataConverter
    {
        public IExtensionInfo? Info { get; set; }
        private ICamApiCLDReceiver? _wrapper;
    
        public ICamApiCLDReceiver? GetCLDReceiverWrapper(
            ICamApiTechOperation operation,
            ICamApiCLDReceiver receiver,
            out TResultStatus ret)
        {
            ret = default;
            _wrapper = new MyReceiverWrapper(receiver);
            return _wrapper;
        }
    
        public void FinalizeConverter()
        {
            if (_wrapper is IDisposable d)
            {
                d.Dispose();
                _wrapper = null;
            }
        }
    }
    
    // Shift all working-feed CutTo points upward by 20 mm
    public class MyReceiverWrapper : CLDRecevierWrapperDefault
    {
        private int _currentFeed = (int)TFeedTypeFlag.affWorking;
    
        public MyReceiverWrapper(ICamApiCLDReceiver receiver) : base(receiver) { }
    
        public override void OutStandardFeed(int feed)
        {
            _currentFeed = feed;
            base.OutStandardFeed(feed);
        }
    
        public override void CutTo(TST3DPoint p)
        {
            if (_currentFeed == (int)TFeedTypeFlag.affWorking)
                p.Z += 20.0;
            base.CutTo(p);
        }
    }
    

    Reference example: CLData/ExtensionGeomCLDataConverterNet/project/main/ (ExtensionGeomCLDataConverter.cs + CLDReceiverWrapperCustom.cs)


    ICAMAPIProjectEtalon — test reference data

    ICAMAPIProjectEtalon stores a tree of typed data nodes for use in automated tests. It is not part of the production machining workflow.

    Interface Purpose
    ICAMAPIProjectEtalon Load/save etalon from/to file; exposes Version and FileName
    ICAMAPIProjectEtalonReceiver Sink for writing etalon nodes (BeginEtalonNode / EndEtalonNode)
    ICAMAPIEtalizableObject Any object that can serialise itself to an ICAMAPIProjectEtalonReceiver
    ICAMAPIProjectEtalonComparer Compare two etalons; configure ignored node types via IgnoredNodeTypesList
    ICAMAPIProjectEtalonFormer Factory: BeginEtalon() / EndEtalon(), CreateEtalonComparer(), SetFlag / GetFlag

    Typical test usage:

    // Record reference data
    etalonFormer.BeginEtalon();
    etalizableObject.SaveToEtalonReceiver(etalonFormer as ICAMAPIProjectEtalonReceiver, out _);
    var referenceEtalon = etalonFormer.EndEtalon();
    referenceEtalon.SaveToFile("reference.etalon");
    
    // Compare in a later test run
    var currentEtalon = /* produce current etalon */;
    var comparer = etalonFormer.CreateEtalonComparer();
    comparer.IgnoredNodeTypesList = "Timestamp,Version";
    bool equal = comparer.CompareEtalons(referenceEtalon, currentEtalon);
    

    IDL reference

    Interface Library GUID
    ICamApiNCMaker CAMAPI_NCMaker 46c43f46-79e9-43bb-abbd-b74f8620aca6
    ICamApiMakeCncDotnetSettings CAMAPI_NCMaker 6d8c69c9-9d81-4d5b-8929-9c6b14863c7d
    ICamApiMakeCncSppxSettings CAMAPI_NCMaker 5d1ea41a-0b6e-4be3-ba5d-1352b8d0df52
    ICamApiSimulator CAMAPI_Simulator af46b3b9-0dc7-4780-96d2-1b146e058330
    ICamApiCLDReceiver CAMAPI_MCDFormerTypes d947e098-92b6-4bac-9ca5-e3914cbf7904
    ICamApiModelFormer CAMAPI_ModelFormerTypes 5e407027-cd32-4702-b8da-b899a8b2ee38
    ICamApiTechOperation CAMAPI_TechOperation c0384d7a-9e7c-4867-87aa-42642af3186c
    ICamApiTechOperationSolver CAMAPI_TechOperation 4559efd1-f631-451f-b5e7-d6f6a66f7acb
    IExtensionGeomCLDataConverter CAMAPI_TechOperation 369770ef-e71b-4fc0-b3b4-a8b55035de45
    ICAMAPIProjectEtalon CAMAPI_EtalonProject E5493D50-5FBD-4931-B46E-8310A3B4AEA1
    In this article