znlgis 博客

GIS开发与技术分享

第十五章:插件系统与扩展开发

15.1 插件系统概述

15.1.1 Weikio Plugin Framework

LightCAD的插件系统基于Weikio Plugin Framework构建,这是一个.NET平台的轻量级插件加载框架,支持运行时动态加载和卸载程序集。

15.1.2 内置行业插件

LightCAD预置了五个行业插件:

插件 文件 大小 功能领域
QdArch QdArch.dll 591KB 建筑设计
QdElectric QdElectric.dll 45KB 电气设计
QdHvac QdHvac.dll 95KB 暖通设计
QdStruct QdStruct.dll 173KB 结构设计
QdWater QdWater.dll 201KB 给排水设计

15.2 插件加载机制

15.2.1 插件管理器

public class PluginManager
{
    private static List<IPlugin> loadedPlugins = new();
    private static string pluginDirectory;

    /// <summary>
    /// 加载指定目录下的所有插件
    /// </summary>
    public static void LoadPlugins(string directory)
    {
        pluginDirectory = directory;

        if (!Directory.Exists(directory))
        {
            Directory.CreateDirectory(directory);
            return;
        }

        var pluginFiles = Directory.GetFiles(directory, "*.dll");

        foreach (var pluginFile in pluginFiles)
        {
            try
            {
                LoadPlugin(pluginFile);
            }
            catch (Exception ex)
            {
                Console.WriteLine(
                    $"加载插件失败 {Path.GetFileName(pluginFile)}: {ex.Message}");
            }
        }
    }

    /// <summary>
    /// 加载单个插件
    /// </summary>
    private static void LoadPlugin(string pluginPath)
    {
        // 使用Weikio Plugin Framework加载
        var assembly = Assembly.LoadFrom(pluginPath);

        // 查找实现IPlugin接口的类型
        var pluginTypes = assembly.GetTypes()
            .Where(t => typeof(IPlugin).IsAssignableFrom(t)
                && !t.IsAbstract && !t.IsInterface);

        foreach (var pluginType in pluginTypes)
        {
            var plugin = (IPlugin)Activator.CreateInstance(pluginType);
            plugin.Initialize();
            loadedPlugins.Add(plugin);

            Console.WriteLine($"已加载插件:{plugin.Name} v{plugin.Version}");
        }
    }

    /// <summary>
    /// 获取所有已加载的插件
    /// </summary>
    public static IReadOnlyList<IPlugin> LoadedPlugins => loadedPlugins;

    /// <summary>
    /// 卸载所有插件
    /// </summary>
    public static void UnloadAll()
    {
        foreach (var plugin in loadedPlugins)
        {
            plugin.Shutdown();
        }
        loadedPlugins.Clear();
    }
}

15.2.2 插件接口

/// <summary>
/// 插件基础接口
/// </summary>
public interface IPlugin
{
    /// <summary>
    /// 插件名称
    /// </summary>
    string Name { get; }

    /// <summary>
    /// 插件版本
    /// </summary>
    string Version { get; }

    /// <summary>
    /// 插件描述
    /// </summary>
    string Description { get; }

    /// <summary>
    /// 插件作者
    /// </summary>
    string Author { get; }

    /// <summary>
    /// 初始化插件
    /// </summary>
    void Initialize();

    /// <summary>
    /// 关闭插件
    /// </summary>
    void Shutdown();
}

/// <summary>
/// 带命令注册能力的插件
/// </summary>
public interface ICommandPlugin : IPlugin
{
    /// <summary>
    /// 注册插件命令
    /// </summary>
    void RegisterCommands(CommandRegistry registry);
}

/// <summary>
/// 带UI扩展能力的插件
/// </summary>
public interface IUIPlugin : IPlugin
{
    /// <summary>
    /// 注册菜单项
    /// </summary>
    void RegisterMenuItems(MenuRegistry registry);

    /// <summary>
    /// 注册工具栏按钮
    /// </summary>
    void RegisterToolbarItems(ToolbarRegistry registry);

    /// <summary>
    /// 注册面板
    /// </summary>
    void RegisterPanels(PanelRegistry registry);
}

/// <summary>
/// 带元素类型扩展能力的插件
/// </summary>
public interface IElementPlugin : IPlugin
{
    /// <summary>
    /// 注册自定义元素类型
    /// </summary>
    void RegisterElementTypes(ElementTypeRegistry registry);
}

15.3 开发自定义插件

15.3.1 创建插件项目

# 创建类库项目
dotnet new classlib -n MyLightCADPlugin -f net8.0-windows

# 添加LightCAD核心引用
cd MyLightCADPlugin
dotnet add reference ../LightCAD.Core/LightCAD.Core.csproj
dotnet add reference ../LightCAD.Runtime/LightCAD.Runtime.csproj

15.3.2 实现插件

// MyPlugin.cs
public class MyPlugin : IPlugin, ICommandPlugin, IElementPlugin
{
    public string Name => "我的自定义插件";
    public string Version => "1.0.0";
    public string Description => "LightCAD自定义插件示例";
    public string Author => "开发者";

    public void Initialize()
    {
        Console.WriteLine("我的插件已初始化");
    }

    public void Shutdown()
    {
        Console.WriteLine("我的插件已关闭");
    }

    public void RegisterCommands(CommandRegistry registry)
    {
        registry.Register(new CommandInfo
        {
            Name = "MYCOMMAND",
            DisplayName = "我的命令",
            Description = "执行自定义操作",
            Action = ExecuteMyCommand
        });

        registry.Register(new CommandInfo
        {
            Name = "DRAWSTAR",
            DisplayName = "绘制星形",
            Description = "绘制一个正五角星",
            Action = DrawStar
        });
    }

    public void RegisterElementTypes(ElementTypeRegistry registry)
    {
        registry.Register(new ElementType
        {
            Guid = new LcGuid("my-plugin-star-element"),
            Name = "Star",
            DisplayName = "五角星",
            Category = "自定义图形",
            Is3D = false,
            Creator = () => new StarElement()
        });
    }

    private void ExecuteMyCommand()
    {
        // 自定义命令逻辑
        MessageBox.Show("执行自定义命令!");
    }

    private void DrawStar()
    {
        // 绘制五角星的逻辑
        var center = InputManager.GetPoint("指定五角星中心:");
        var radius = InputManager.GetDistance("指定外径:");

        var star = CreateStarPolyline(center, radius, 5);
        DocumentManager.Current.Entities.Add(star);
    }

    private LcPolyline CreateStarPolyline(Point2d center,
                                           double outerRadius, int points)
    {
        var polyline = new LcPolyline { IsClosed = true };
        var innerRadius = outerRadius * 0.382;

        for (int i = 0; i < points * 2; i++)
        {
            var angle = Math.PI / 2 + Math.PI * i / points;
            var r = (i % 2 == 0) ? outerRadius : innerRadius;
            polyline.AddVertex(new Point2d(
                center.X + r * Math.Cos(angle),
                center.Y + r * Math.Sin(angle)
            ));
        }

        return polyline;
    }
}

15.3.3 自定义元素类型

/// <summary>
/// 自定义五角星元素
/// </summary>
public class StarElement : LcEntity, IUpdateObject
{
    public Point2d Center { get; set; }
    public double OuterRadius { get; set; } = 50;
    public double InnerRadius { get; set; } = 19.1;
    public int Points { get; set; } = 5;
    public double Rotation { get; set; } = 0;

    public override string TypeName => "Star";

    public override BoundingBox2d Bounds
    {
        get
        {
            return new BoundingBox2d
            {
                Min = new Point2d(
                    Center.X - OuterRadius, Center.Y - OuterRadius),
                Max = new Point2d(
                    Center.X + OuterRadius, Center.Y + OuterRadius)
            };
        }
    }

    public override void TransformBy(Matrix4d matrix)
    {
        var p3d = new Point3d(Center.X, Center.Y, 0);
        var transformed = matrix.Transform(p3d);
        Center = new Point2d(transformed.X, transformed.Y);
    }

    public override LcEntity Clone()
    {
        return new StarElement
        {
            Center = Center,
            OuterRadius = OuterRadius,
            InnerRadius = InnerRadius,
            Points = Points,
            Rotation = Rotation,
            Color = Color,
            LayerName = LayerName
        };
    }

    public override double DistanceTo(Point2d point)
    {
        return point.DistanceTo(Center) - OuterRadius;
    }

    /// <summary>
    /// 获取五角星的顶点
    /// </summary>
    public List<Point2d> GetVertices()
    {
        var vertices = new List<Point2d>();
        for (int i = 0; i < Points * 2; i++)
        {
            var angle = Rotation + Math.PI / 2 + Math.PI * i / Points;
            var r = (i % 2 == 0) ? OuterRadius : InnerRadius;
            vertices.Add(new Point2d(
                Center.X + r * Math.Cos(angle),
                Center.Y + r * Math.Sin(angle)
            ));
        }
        return vertices;
    }

    public bool NeedsUpdate { get; private set; }
    public void OnUpdate(UpdateContext context)
    {
        NeedsUpdate = false;
    }
}

15.4 插件部署

15.4.1 编译和部署

# 编译插件
dotnet build -c Release

# 将编译输出复制到LightCAD的Plugins目录
copy bin/Release/net8.0-windows/MyLightCADPlugin.dll \
     ../LightCAD.WinForm/bin/Debug/net8.0-windows/Plugins/

15.4.2 插件清单文件

为了更好地管理插件信息,可以创建清单文件:

{
    "name": "MyLightCADPlugin",
    "version": "1.0.0",
    "author": "开发者",
    "description": "LightCAD自定义插件示例",
    "assembly": "MyLightCADPlugin.dll",
    "dependencies": [],
    "minimumLightCADVersion": "1.0.0"
}

15.5 ComponentActionLoader

15.5.1 操作加载器

ComponentActionLoader是LightCAD中专门用于加载建模操作的插件加载器:

public class ComponentActionLoader
{
    private Dictionary<string, IComponentAction> actions = new();

    /// <summary>
    /// 从程序集加载操作
    /// </summary>
    public void LoadActionsFromAssembly(Assembly assembly)
    {
        var actionTypes = assembly.GetTypes()
            .Where(t => typeof(IComponentAction).IsAssignableFrom(t)
                && !t.IsAbstract);

        foreach (var type in actionTypes)
        {
            var action = (IComponentAction)Activator.CreateInstance(type);
            actions[action.Name] = action;
        }
    }

    /// <summary>
    /// 获取操作
    /// </summary>
    public IComponentAction GetAction(string name)
    {
        return actions.TryGetValue(name, out var action) ? action : null;
    }

    /// <summary>
    /// 获取所有操作
    /// </summary>
    public IEnumerable<IComponentAction> GetAllActions()
    {
        return actions.Values;
    }

    /// <summary>
    /// 获取某个类别的操作
    /// </summary>
    public IEnumerable<IComponentAction> GetActionsByCategory(string category)
    {
        return actions.Values.Where(a => a.Category == category);
    }
}

public interface IComponentAction
{
    string Name { get; }
    string DisplayName { get; }
    string Category { get; }
    LcSolid3d Execute(ActionContext context);
}

15.6 扩展点总结

15.6.1 LightCAD可扩展的维度

扩展维度 接口/机制 说明
元素类型 IElementPlugin / ElementTypeRegistry 定义新的图元类型
命令 ICommandPlugin / CommandRegistry 添加新的用户命令
建模操作 IComponentAction / ComponentActionLoader 添加3D建模操作
UI菜单 IUIPlugin / MenuRegistry 扩展菜单系统
UI工具栏 IUIPlugin / ToolbarRegistry 添加工具栏按钮
UI面板 IUIPlugin / PanelRegistry 添加侧边面板
文件格式 IFormatPlugin / FormatConverter 支持新的文件格式
捕捉类型 ISnapProvider / SnapManager 自定义捕捉行为

15.7 本章小结

本章详细介绍了LightCAD的插件系统和扩展开发方法。LightCAD基于Weikio Plugin Framework提供了灵活的插件加载机制,支持运行时动态加载插件。通过IPlugin及其扩展接口(ICommandPlugin、IElementPlugin、IUIPlugin),开发者可以在多个维度上扩展LightCAD的功能。内置的五个行业插件展示了实际的插件应用场景。ComponentActionLoader专门用于加载3D建模操作,是建模扩展的核心入口。


上一章第十四章:文件格式与数据交换

下一章第十六章:命令与操作系统