C#实战:构建高可靠SolidWorks图纸批量转换后台服务

在制造业数字化转型浪潮中,PLM与MES系统的深度集成已成为提升生产效率的关键。作为核心资产的三维设计图纸,如何在Web环境中实现安全、高效的共享查看,是每个工程团队必须解决的痛点。本文将手把手带您构建一个基于C#的Windows服务,实现SolidWorks图纸的自动化批量转换,解决实际部署中的命名映射、错误处理和性能优化等难题。

1. 服务架构设计与环境准备

1.1 技术选型与依赖配置

构建稳定运行的转换服务需要以下基础环境:

  • SolidWorks 2020+ :确保安装时勾选API SDK组件
  • .NET 6.0 :推荐使用LTS版本以获得长期支持
  • Windows服务项目模板 :Visual Studio 2022中的Worker Service模板

关键NuGet包依赖:

<PackageReference Include="SolidWorks.Interop.sldworks" Version="2023.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="6.0.0" />
<PackageReference Include="Serilog" Version="2.12.0" />

1.2 服务生命周期设计

采用IHostedService接口构建服务主体框架:

public class ConversionService : IHostedService, IDisposable
{
    private Timer _timer;
    private readonly ILogger<ConversionService> _logger;

    public ConversionService(ILogger<ConversionService> logger)
    {
        _logger = logger;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("Starting conversion service...");
        _timer = new Timer(DoWork, null, TimeSpan.Zero, 
            TimeSpan.FromMinutes(Config.ScanInterval));
        return Task.CompletedTask;
    }
}

2. 核心转换逻辑实现

2.1 文件系统监控策略

采用混合监控模式提升效率:

  1. 定时全量扫描 :每日凌晨执行完整目录遍历
  2. FileSystemWatcher监听 :实时捕获新增/修改文件
  3. 内存缓存去重 :避免重复处理相同文件

文件过滤实现示例:

private IEnumerable<FileInfo> FilterSolidWorksFiles(DirectoryInfo dir)
{
    var extensions = new[] { ".sldprt", ".sldasm", ".slddrw" };
    return dir.EnumerateFiles("*", SearchOption.AllDirectories)
        .Where(f => extensions.Contains(f.Extension.ToLower()))
        .Where(f => f.LastWriteTime > LastScanTime);
}

2.2 SolidWorks API调用封装

安全调用SolidWorks COM API的最佳实践:

public class SwApplication : IDisposable
{
    private SldWorks _swApp;
    
    public SwApplication()
    {
        _swApp = Activator.CreateInstance(Type.GetTypeFromProgID("SldWorks.Application")) as SldWorks;
        _swApp.Visible = false;
    }

    public void ExportToScs(string inputPath, string outputPath)
    {
        try
        {
            var doc = _swApp.OpenDoc6(inputPath, (int)swDocumentTypes_e.swDocPART,
                (int)swOpenDocOptions_e.swOpenDocOptions_Silent, "", 0, 0);
                
            if (doc != null)
            {
                doc.ExportToWebPage(outputPath);
                _swApp.CloseDoc(doc.GetPathName());
            }
        }
        finally
        {
            Marshal.FinalReleaseComObject(_swApp);
        }
    }
}

3. 生产环境关键问题解决

3.1 文件名映射解决方案

针对PLM系统散列存储与原始命名不一致问题,设计多级查找策略:

查找方式 实现方法 适用场景
XML元数据 解析配套index.xml文件 标准PLM导出
数据库查询 调用PLM API获取映射关系 定制化系统
正则匹配 提取图纸特征编码 遗留系统迁移

典型XML解析实现:

public string GetOriginalName(FileInfo sldFile)
{
    var xmlFile = new FileInfo(Path.Combine(
        sldFile.DirectoryName, 
        "metadata.xml"));
        
    if (xmlFile.Exists)
    {
        var doc = XDocument.Load(xmlFile.FullName);
        return doc.Descendants("OriginalName").FirstOrDefault()?.Value;
    }
    return Path.GetFileNameWithoutExtension(sldFile.Name);
}

3.2 转换任务队列管理

使用Channel实现生产者-消费者模式:

private readonly Channel<ConversionTask> _queue = 
    Channel.CreateBounded<ConversionTask>(new BoundedChannelOptions(1000)
    {
        FullMode = BoundedChannelFullMode.Wait,
        SingleReader = true
    });

// 生产者
async Task EnqueueTaskAsync(ConversionTask task)
{
    await _queue.Writer.WriteAsync(task);
}

// 消费者
async Task ProcessQueueAsync()
{
    await foreach (var task in _queue.Reader.ReadAllAsync())
    {
        await ProcessSingleFile(task);
    }
}

4. 运维监控与性能优化

4.1 分布式日志收集方案

配置Serilog实现结构化日志:

Log.Logger = new LoggerConfiguration()
    .WriteTo.Console()
    .WriteTo.File(new CompactJsonFormatter(), 
        "logs/conversion-.json",
        rollingInterval: RollingInterval.Day)
    .WriteTo.Elasticsearch(new ElasticsearchSinkOptions(new Uri("http://elk:9200"))
    {
        AutoRegisterTemplate = true,
        IndexFormat = "sw-conversion-{0:yyyy.MM}"
    })
    .CreateLogger();

关键监控指标:

  • 转换成功率 :成功数/总数 × 100%
  • 平均处理时间 :从开始到完成的耗时
  • 资源占用率 :CPU/内存使用峰值

4.2 性能调优技巧

  1. 实例池优化

    public class SwInstancePool : IDisposable
    {
        private readonly ConcurrentBag<SldWorks> _instances = new();
        private readonly int _maxInstances = 4;
    
        public SldWorks GetInstance()
        {
            if (_instances.TryTake(out var instance))
                return instance;
                
            return Activator.CreateInstance(
                Type.GetTypeFromProgID("SldWorks.Application")) as SldWorks;
        }
    }
    
  2. 并行处理控制

    var options = new ParallelOptions 
    { 
        MaxDegreeOfParallelism = Environment.ProcessorCount - 1 
    };
    
    Parallel.ForEach(files, options, file => 
    {
        ProcessFile(file);
    });
    
  3. 内存泄漏预防

    • 使用Marshal.ReleaseComObject释放COM对象
    • 定期重启服务(每日凌晨)
    • 监控SolidWorks进程资源占用

5. 异常处理与容错机制

5.1 错误分类处理策略

错误类型 处理方式 重试策略
文件锁定 延迟重试 指数退避
API超时 实例重启 最大3次
内存不足 暂停处理 人工介入
权限不足 终止任务 立即告警

典型错误处理实现:

public async Task SafeConvertAsync(string inputPath, string outputPath)
{
    int retryCount = 0;
    while (retryCount < 3)
    {
        try
        {
            using var swApp = new SwApplication();
            swApp.ExportToScs(inputPath, outputPath);
            return;
        }
        catch (COMException ex) when (ex.HResult == -2147221164)
        {
            retryCount++;
            await Task.Delay(1000 * retryCount);
        }
    }
    throw new ConversionException($"Failed after {retryCount} attempts");
}

5.2 死信队列设计

对于持续失败的任务,转入死信队列供后续分析:

public class DeadLetterService
{
    private readonly string _storePath = "deadletters";
    
    public void StoreFailedTask(ConversionTask task, Exception ex)
    {
        var record = new
        {
            Timestamp = DateTime.UtcNow,
            Task = task,
            Error = ex.ToString()
        };
        
        var fileName = $"{DateTime.Now:yyyyMMddHHmmss}-{Guid.NewGuid()}.json";
        File.WriteAllText(Path.Combine(_storePath, fileName), 
            JsonSerializer.Serialize(record));
    }
}

6. 部署与持续集成

6.1 Windows服务安装脚本

使用SC命令创建服务:

$serviceName = "SolidWorksConverter"
$displayName = "SolidWorks图纸转换服务"
$binPath = "C:\Services\SWConverter\SolidWorksConverter.exe"

sc.exe create $serviceName binPath= `"$binPath`" start= auto DisplayName= `"$displayName`"
sc.exe description $serviceName "自动将SolidWorks图纸转换为Web可预览格式"
sc.exe start $serviceName

6.2 CI/CD管道配置

示例Azure Pipelines配置:

steps:
- task: DotNetCoreCLI@2
  inputs:
    command: 'publish'
    publishWebProjects: false
    arguments: '--configuration Release --output $(Build.ArtifactStagingDirectory)'
    zipAfterPublish: true

- task: WindowsMachineFileCopy@2
  inputs:
    sourceFolder: '$(Build.ArtifactStagingDirectory)'
    targetFolder: '\\prod-server\SWConverter'
    machineNames: 'sw-converter-01'
    adminUserName: '$(deployUser)'
    adminPassword: '$(deployPassword)'

7. 浏览器端集成方案

7.1 Web查看器选型对比

方案 优点 缺点 适用场景
SolidWorks Web Viewer 官方支持,功能完整 需要授权许可 企业内网
Three.js自定义渲染 高度定制化 开发成本高 互联网产品
Autodesk Forge 云服务支持 按量计费 混合云部署

7.2 安全访问控制

JWT令牌验证示例:

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new()
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = Configuration["Jwt:Issuer"],
            ValidAudience = Configuration["Jwt:Audience"],
            IssuerSigningKey = new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
        };
    });

8. 实战经验分享

在多个制造业客户现场部署时,我们发现几个关键点:

  1. SolidWorks版本兼容性 :不同年份版本的API存在细微差异,建议在服务启动时检查版本:

    var version = _swApp.RevisionNumber();
    if (version < 20200000)
        throw new NotSupportedException("Requires SolidWorks 2020+");
    
  2. 大文件处理 :超过500MB的装配体需要特殊处理:

    • 增加超时时间至30分钟
    • 单独分配SolidWorks实例
    • 优先安排在非高峰期处理
  3. 临时文件清理 :定期清理SolidWorks生成的临时文件:

    Get-ChildItem -Path $env:TEMP -Filter "swx*" | Remove-Item -Force -Recurse
    
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐