CAM API
  • CAM API
v3.0.3
Search Results for

    Show / Hide Table of Contents

    Extension System (CAMAPI)

    For a practical guide on choosing the right entry point and implementing each one, see Extension Entry Points.

    All extension management in ENCY is performed through the interfaces defined in CAMAPI.Extensions. The central interface is IExtensionManager, which ENCY exposes via CAMAPI.ExtensionManager.dll.

    Obtaining the manager (.NET helper):

    using CAMAPI.DotnetHelper;
    
    using var managerCom = ExtensionManagerHelper.GetInstance();
    // or from an application context:
    using var managerCom = appCom.GetExtensionManager();
    

    Obtaining the manager (direct IDL):

    ICamApiApplication.GetExtensionManager(out TResultStatus, return IExtensionManager*)
    

    IExtensionManager

    Registration

    RegisterLibrary

    Registers a JSON description file into the specified storage. Returns library metadata on success.

    // .NET helper
    ComWrapper<IExtensionLibraryInfo> info = managerCom.RegisterLibrary(
        TStorageType.stCurrentUser,
        @"C:\MyPlugins\MyExtension.json");
    
    // IDL
    IExtensionManager.RegisterLibrary(
        in StorageType: TStorageType,
        in JsonFilePath: string,
        out TResultStatus,
        return IExtensionLibraryInfo*)
    

    RegisterLibraryFromFolder

    Reads a plugin folder, finds its settings.json, copies the folder into the storage's own location, then registers it. Convenient for installing a self-contained plugin folder in one call. The needReload out-flag reports whether a storage reload is required to pick it up.

    // IDL
    IExtensionManager.RegisterLibraryFromFolder(
        in StorageType: TStorageType,
        in FolderPath: string,
        out TResultStatus,
        out needReload: boolean,
        return IExtensionLibraryInfo*)
    

    UnRegisterLibrary

    Removes a library from all storages by its DLL path or JSON path.

    managerCom.UnRegisterLibrary(@"C:\MyPlugins\MyExtension.dll");
    
    // IDL
    IExtensionManager.UnRegisterLibrary(in LibraryPath: string, out TResultStatus)
    

    ReloadStorage

    Forces a re-read of a specific storage tier (e.g. after an external change to the JSON files).

    managerCom.ReloadStorage(TStorageType.stCurrentUser);
    
    // IDL
    IExtensionManager.ReloadStorage(in StorageType: TStorageType, out TResultStatus)
    

    State

    GetLibraryDisabled / SetLibraryDisabled

    Query or set the disabled flag for a library in a specific storage tier.

    bool disabled = managerCom.GetLibraryDisabled(TStorageType.stCurrentUser, libraryPath);
    managerCom.SetLibraryDisabled(TStorageType.stCurrentUser, libraryPath, disabled: true);
    

    GetLibraryDisabledFinal

    Returns the effective disabled state after all storage tiers have been merged (highest-priority tier wins).

    bool effectivelyDisabled = managerCom.GetLibraryDisabledFinal(libraryPath);
    

    GetExtensionDisabled / SetExtensionDisabled

    Same as the library variants but per individual extension type ID.

    bool disabled = managerCom.GetExtensionDisabled(TStorageType.stCurrentUser, extensionTypeId);
    managerCom.SetExtensionDisabled(TStorageType.stCurrentUser, extensionTypeId, disabled: true);
    

    GetExtensionDisabledFinal

    Effective disabled state for an individual extension after merging all storage tiers.

    bool effectivelyDisabled = managerCom.GetExtensionDisabledFinal(extensionTypeId);
    

    GetLibraryLoaded

    Returns whether a library DLL is currently loaded in memory.

    bool loaded = managerCom.GetLibraryLoaded(libraryPath);
    

    GetFieldInherited / SetFieldInherited

    Checks or marks whether a property (TStorageField) of a library or extension is inherited from a lower-priority storage tier rather than explicitly set in the current one.

    bool inherited = managerCom.GetFieldInherited(
        TStorageType.stCurrentUser, entityIdent, TStorageField.sfLibraryDisabled);
    
    managerCom.SetFieldInherited(
        TStorageType.stCurrentUser, entityIdent, TStorageField.sfAll);
    

    GetLibraryStorages

    Returns a list of TStorageType values representing the storage tiers in which the library has non-inherited (explicitly set) values.

    using var storagesCom = managerCom.GetLibraryStorages(libraryPath);
    

    AddExtension / RemoveExtension

    Explicitly tracks an extension within a storage tier so its disabled flag can be managed independently of the library-level flag.

    managerCom.AddExtension(TStorageType.stCurrentUser, libraryPath, extensionTypeId);
    managerCom.RemoveExtension(TStorageType.stCurrentUser, libraryPath, extensionTypeId);
    

    Extension settings

    Named key/value settings that belong to an extension type, stored by the extension manager instead of by the extension itself. Use them for the configuration a deployment provides — a server URL, a license key, a shared folder — where the value has to survive reinstallation and may be pinned centrally for all users.

    Settings are kept in an extension_settings.json per storage tier, next to that tier's extension list, and they are resolved as a cascade that is independent of the disabled/inherited machinery above:

    stCurrentUser → stAllUsers → stDealer → stSystem → manifest default
    

    The highest tier that defines the key wins; if no tier does, the default declared by whoever shipped the extension is used. A key does not have to be defined anywhere, so every read reports a found flag.

    Method Description
    GetExtensionSetting(extensionTypeId, key, out found, out ret) → string Effective value after the whole cascade. found is false when neither a tier nor a default defines the key
    GetExtensionSettingSourceLevel(extensionTypeId, key, out found, out fromDefault, out ret) → TStorageType Where the effective value came from. When fromDefault is true the value is the manifest default and the returned tier is meaningless
    GetExtensionSettingKeys(extensionTypeId, out ret) → IListString* Union of all keys defined for the extension across every tier and the defaults — use it to enumerate settings without knowing their names
    SetExtensionSetting(storageType, extensionTypeId, key, value, out ret) Write a value into one tier and persist that tier
    RemoveExtensionSetting(storageType, extensionTypeId, key, out ret) Drop the key from one tier, so the next tier down (or the default) becomes effective again

    There is no ExtensionManagerHelper wrapper for these — call them through Invoke:

    using var managerCom = ExtensionManagerHelper.GetInstance();
    const string extensionTypeId = "My.Company.MyExtension";
    
    // read the effective value
    var found = false;
    string url = managerCom.Invoke(m =>
    {
        var value = m.GetExtensionSetting(extensionTypeId, "serverUrl", out var wasFound, out var ret);
        if (ret.Code == TResultStatusCode.rsError)
            throw new Exception(ret.Description);
        found = wasFound;
        return value;
    });
    
    if (!found)
        throw new Exception("The 'serverUrl' setting is not configured for this installation");
    
    // remember the user's own choice
    managerCom.Invoke(m =>
    {
        m.SetExtensionSetting(TStorageType.stCurrentUser, extensionTypeId, "serverUrl", url, out var ret);
        if (ret.Code == TResultStatusCode.rsError)
            throw new Exception(ret.Description);
    });
    

    Which tier to write to. Write stCurrentUser for a choice the user made, stAllUsers when provisioning a whole machine. Writing to a tier that has no storage in this installation — stDebugMode outside a debug session — fails with rsError rather than silently doing nothing, so check the status instead of assuming the write landed.

    Secret values. A key may be marked secret, and then its value is kept in memory for the session only and never written to any tier's file. Reading and writing look exactly the same, so an extension cannot tell a secret key from a normal one — but a secret value does not survive a restart of ENCY, and the user (or the caller) has to supply it again.

    Over IPC the same five calls are available with .NET helper wrappers — see ../ipc/application.md.


    Querying Metadata

    GetExtensionTypeInfo

    Returns the IExtensionTypeInfo for a single extension by its unique type ID.

    using var typeInfoCom = managerCom.GetExtensionTypeInfo("My.Extension.Ident");
    

    GetExtensionTypeInfos

    Returns all IExtensionTypeInfo objects belonging to a group (the Group field in the JSON).

    using var listCom = managerCom.GetExtensionTypeInfos("Extension.Util.Common");
    

    GetExtensionTypeInfosFromLibrary

    Returns all extension type infos from a specific library DLL path.

    using var listCom = managerCom.GetExtensionTypeInfosFromLibrary(libraryPath);
    

    GetExtensionTypeGroups

    Returns all known group strings across all registered libraries.

    using var groupsCom = managerCom.GetExtensionTypeGroups();
    

    GetExtensionTypeGroup

    Returns the structured IExtensionTypeGroup for one group id (richer than the plain string from GetExtensionTypeGroups).

    using var groupCom = managerCom.InvokeAndWrap(m =>
        (m.GetExtensionTypeGroup("Extension.Util.Common", out var status), status));
    // IExtensionTypeGroup: Id (string), Caption (string), IsSystem (bool)
    

    IListExtensionTypeGroup is the list form (Get(index), Count(), Add, Remove, RemoveAt).

    GetLibraryInfo

    Returns IExtensionLibraryInfo for the library that contains the specified extension type ID.

    using var libInfoCom = managerCom.GetLibraryInfo("My.Extension.Ident");
    

    GetLibrariesInfo

    Returns metadata for all currently registered libraries.

    using var allLibsCom = managerCom.GetLibrariesInfo();
    

    Each IExtensionLibraryInfo carries, besides its path and UnloadMode, the descriptive fields taken from the library's settings.json: Version, Id, Author, IconPath, Tags, and SettingsPath — the path to the *.settings.json the library was registered from (all read-only strings).

    GetPendingDeletionLibraries

    Returns the libraries whose unregistration could not finish because their DLL was locked at the time (UnRegisterLibrary left a _del_ cleanup artifact on disk instead of deleting).

    using var pendingCom = managerCom.InvokeAndWrap(m => m.GetPendingDeletionLibraries());
    

    Entries whose artifact is already gone are dropped automatically, so a non-empty result means a real leftover — typically a library that will only disappear after ENCY restarts.


    Creation

    CreateExtension

    Creates and returns a new instance of an extension by its unique type ID. The extension's DLL is loaded if not already in memory.

    using var extCom    = managerCom.CreateExtension("My.Extension.Ident");
    using var solverCom = extCom.InvokeAndWrap(e => e as ICamApiTechOperationSolver);
    if (solverCom.IsNull)
        throw new Exception("Extension does not implement ICamApiTechOperationSolver");
    
    // IDL
    IExtensionManager.CreateExtension(in ExtensionTypeId: string, out TResultStatus, return IExtension*)
    

    CreateExtensionsByGroups

    Creates instances of all non-disabled extensions whose Group matches any of the semicolon-separated group strings.

    using var listCom = managerCom.CreateExtensionsByGroups("Extension.Util.Common;Extension.Util.Custom");
    
    // IDL
    IExtensionManager.CreateExtensionsByGroups(in ExtensionGroups: string, out TResultStatus, return IListExtension*)
    

    GetSingletonExtension

    Returns the single shared instance of a singleton extension (creates it on first call). Use for extensions that must exist exactly once across all callers.

    using var extCom = managerCom.GetSingletonExtension("My.Singleton.Ident");
    // Static shortcut (no manager reference needed):
    using var extCom2 = ExtensionManagerHelper.GetSingletonExtension("My.Singleton.Ident");
    

    GetExtension

    Returns an already-created extension instance by its numeric instance ID (IExtensionInstanceInfo.Id).

    using var extCom = managerCom.GetExtension(instanceId);
    // Static shortcut:
    using var extCom2 = ExtensionManagerHelper.GetExtension(instanceId);
    

    GetExtensionsByTypeId

    Returns all currently live instances of a specific extension type.

    using var listCom = managerCom.GetExtensionsByTypeId("My.Extension.Ident");
    

    Lifecycle

    FreeExtension

    Unloads a single extension instance by its numeric instance ID.

    managerCom.FreeExtension(instanceId);
    
    // IDL
    IExtensionManager.FreeExtension(in ExtensionInstanceId: hyper, out TResultStatus)
    

    FreeExtensionsByTypeId

    Unloads all live instances of a specific extension type.

    managerCom.FreeExtensionsByTypeId("My.Extension.Ident");
    

    FreeLibrary

    Unloads all extension instances from the specified library and then unloads the DLL.

    managerCom.FreeLibrary(libraryPath);
    

    FinalizeExtensions

    Unloads all extensions across all libraries. Called by ENCY during application shutdown; plugins do not normally call this directly.

    managerCom.FinalizeExtensions();
    

    Diagnostics

    Logger

    Returns the IExtensionLogger used by the extension system. Prefer accessing it through the static helper:

    using var loggerCom = ExtensionManagerHelper.Logger();
    if (!loggerCom.IsNull)
        loggerCom.Invoke(log => log.Info("My message"));
    

    ApiVersion

    Returns the version string of the CAMAPI SDK against which the extension manager was built.

    string version = managerCom.ApiVersion();
    // or static:
    string version = ExtensionManagerHelper.ApiVersion();
    

    IExtensionStorage / TStorageType

    IExtensionStorage represents a single tier of a layered configuration store. Tiers are evaluated in priority order; a value set in a higher-priority tier overrides the same value from lower tiers.

    Value Priority Scope Mutable
    stSystem Lowest All users, machine-wide No
    stDealer 2 Current user only No
    stAllUsers 3 All users, machine-wide Yes
    stCurrentUser 4 Current user only Yes
    stDebugMode 5 Current user, debug sessions Yes
    stTestMode Highest Automated test runs Yes

    Typical development workflow: register your DLL with stDebugMode so it does not affect other users and is easy to disable.

    Key storage methods (on IExtensionStorage directly):

    Method Description
    Read Load current state from the backing file or database.
    Save Persist the current in-memory state.
    RegisterLibrary(LibraryIdent) Add a library entry to this tier's tracking list.
    UnRegisterLibrary(LibraryIdent) Remove a library entry from this tier.
    GetLibraries List all library paths recorded in this tier.
    GetExtensions(LibraryIdent) List extension type IDs that have explicit settings in this tier.
    GetLibraryDisabled / SetLibraryDisabled Read or write the disabled flag for a library.
    GetExtensionDisabled / SetExtensionDisabled Read or write the disabled flag for an individual extension.
    GetLibraryFieldInherited / SetLibraryFieldInherited Query or mark a library property as inherited from a lower tier.
    GetExtensionFieldInherited / SetExtensionFieldInherited Query or mark an extension property as inherited.

    TStorageField selects which property the inheritance methods act on:

    Value Meaning
    sfAll All fields
    sfLibraryDisabled The library-level disabled flag
    sfExtensionDisabled The extension-level disabled flag

    Extension type-info kinds

    Every extension declares its kind through an IExtensionTypeInfo… marker interface (the same mechanism behind IExtensionTypeInfoUtility, IExtensionTypeInfoOperationPopup, etc. — see extension entry points). In addition to the popup and utility kinds documented there, the API also declares:

    Type-info interface Injection point
    IExtensionTypeInfoProjectReportPopup Context menu of a project report
    IExtensionTypeInfoPostprocessorPopup Context menu of a postprocessor
    IExtensionTypeInfoToolsListPopup Context menu of the tools list
    IExtensionTypeInfoNCFilesExporter Export generated NC files to an external target (pairs with IExtensionNCFilesExporter — see nc-simulation.md)
    IExtensionTypeInfoMacroBuilder Builds macros for one macro language (pairs with IExtensionMacroBuilder — see below)

    IExtensionMacroBuilder — adding a macro language

    A macro-builder extension teaches ENCY how to turn a recorded command sequence into a runnable macro in one particular language. The built-in dotnet and spr builders are exactly such extensions; the macro manager dispatches to one of them by ICamApiMacroInfo.LanguageId.

    This is the producer side. To merely record, build or run macros from a plugin, use ICamApiMacroManager instead — you do not need this interface.

    Method Description
    CheckEnvironment(languageSettings, out ret) Verify the toolchain is usable (compiler reachable, references resolvable). rsError + Description on failure
    SetupEnvironment(languageSettings, out ret) Try to repair the environment so CheckEnvironment passes; no-op for languages needing no external tooling
    AddCommand(data) Feed one recorded command (ICamApiMacroCommandData) into the builder
    AddCommandSetDriveFaceItemProperties(...) Record a drive-face item property change. Separate from AddCommand because it carries a string list (Faces), which the scalar command bag cannot hold
    AddCommandAddFixture(operationGuid, addType) Record an "add fixture" command (TFixtureAddType: 0 = Chuck, 1 = Vise, 2 = Clamp, 3 = Node)
    CreateMacro(context, mainSettings, languageSettings, out ret) → string Generate the macro source from the accumulated commands; returns its path. A new file each call — not yet runnable
    Build(id, caption, description, macroPath, out ret) → string Compile the source into something ENCY can execute. Stateless
    OpenInEditor(macroPath, context, out ret) Open the source for editing in whatever is native for the language (project folder in VS Code, host scripting IDE, …)
    Reset() Drop all accumulated commands

    IExtensionMacroBuilderContext carries a single member — CamApplication (ICamApiApplication*).

    Always Reset() around a build. The builder instance is shared and long-lived, so a failed build otherwise leaves its commands behind and they leak into the next macro until ENCY restarts.


    IExtensionLogger

    IExtensionLogger is the unified logging sink for the extension system. Obtain it via ExtensionManagerHelper.Logger() or from IExtensionManager.Logger.

    Method Level Notes
    Debug(message) Debug Detailed diagnostic output, typically suppressed in production.
    Verbose(message) Verbose Finer-grained than Info, coarser than Debug.
    Info(message) Info Normal operational messages.
    Head(message) Head Section headings or major milestones in a log sequence.
    Warning(message) Warning Recoverable anomalies.
    Error(message) Error Non-recoverable failures.
    Log(event) Any Low-level: supply a LogItem struct with event type and message directly.
    IsEventTypeActive(EventType) — Returns true if the given TLogEventType is currently being collected. Use to skip expensive message construction.
    Notify(eventType, message, title) Any Sends a visible notification to the user in the main application UI in addition to writing to the log.

    Usage example:

    using var loggerCom = ExtensionManagerHelper.Logger();
    if (loggerCom.IsNull) throw new Exception("Logger unavailable");
    
    loggerCom.Invoke(log =>
    {
        if (log.IsEventTypeActive(TLogEventType.leDebug))
            log.Debug($"Processing operation: {operation.Name}");
    
        log.Info("Toolpath calculation started");
        log.Warning("No geometry assigned — using defaults");
        log.Error("CLD receiver is null");
        log.Notify(TLogEventType.leInfo, "Export complete", "My Extension");
    });
    
    In this article