22 Nov 2024
Signaling a Heartbeat for Long-Running APIs with AutoCAD Civil 3D Design Automation
Long-running operations, such as exporting Civil 3D corridors to solids, can cause workflows to appear unresponsive, especially in cloud-based or asynchronous environments. To address this, AutoCAD provides a heartbeat signal mechanism to indicate the application is still alive during lengthy tasks.
This post demonstrates how to implement a heartbeat mechanism while performing a complex operation: exporting corridors to solids using Civil 3D's Design Automation (DA) environment.
Thanks for Albert from Engineering to provide this suggestion.
Why Use a Heartbeat?
Design Automation (DA) environments often impose timeouts to ensure system stability. Without interaction during a long-running operation, DA might terminate the process, mistaking it for a hang. By signaling a heartbeat, you can inform DA that the process is active and prevent unintended termination.
Code Walkthrough
Below is the complete implementation of a corridor export command, including heartbeat signaling.
Key Features
- Heartbeat Signaling: Signals activity during long-running operations using
AcApHeartSendBeat()
. - Corridor Export: Exports corridors to solids while reporting progress and writing results to a file.
- Error Handling: Provides robust error and transaction management.
The Heartbeat Manager
The HeartbeatManager
encapsulates the logic for sending heartbeat signals at regular intervals. It uses a background task to send the signal and gracefully handles cancellation when the process ends.
[DllImport("accore.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl,
EntryPoint = "?sendBeat@AcApHeart@@YAXXZ")]
private static extern void AcApHeartSendBeat();
private class HeartbeatManager : IDisposable
{
private readonly CancellationTokenSource _cts;
private readonly Task _heartbeatTask;
public HeartbeatManager()
{
_cts = new CancellationTokenSource();
_heartbeatTask = StartHeartbeat(_cts.Token);
}
private static Task StartHeartbeat(CancellationToken token)
{
return Task.Run(async () =>
{
while (!token.IsCancellationRequested)
{
AcApHeartSendBeat();
await Task.Delay(100, token);
}
}, token);
}
public void Dispose()
{
_cts.Cancel();
_heartbeatTask.Wait(1000);
_cts.Dispose();
}
}
Exporting Corridors to Solids
The ExportCorridor
command demonstrates how to:
- Access Civil 3D corridors from the active document.
- Export these corridors to solids using
ExportSolids
. - Write the results (handles of solids) to a text file.
Here’s the core command logic:
[CommandMethod("ExportCorridor")]
public void ExportCorridor()
{
var doc = Application.DocumentManager.MdiActiveDocument;
if (doc == null) return;
using var heartbeat = new HeartbeatManager();
try
{
ExportCorridorInternal(doc.Database, doc.Editor);
}
catch (Exception ex)
{
doc.Editor.WriteMessage($"\nError during corridor export: {ex.Message}");
}
}
The Export Logic
The method ExportCorridorInternal
performs the export by iterating through corridors, creating solids, and writing their handles to a file. It uses the ExportCorridorSolidsParams
class to define export options.
private static void ExportCorridorInternal(Database database, Editor editor)
{
var civilDoc = CivilApplication.ActiveDocument ?? throw new InvalidOperationException("No active Civil 3D document found.");
var corridors = civilDoc.CorridorCollection;
var exportOptions = CreateExportOptions();
using (var transaction = database.TransactionManager.StartOpenCloseTransaction())
{
foreach (ObjectId corridorId in corridors)
{
var corridor = transaction.GetObject(corridorId, OpenMode.ForRead) as Corridor;
if (corridor == null) continue;
ObjectIdCollection solidIds = corridor.ExportSolids(exportOptions, null);
File.Create(@"solids.txt").Close();
using (var file = new StreamWriter(@"solids.txt", true))
{
foreach (var solidId in solidIds.Cast<ObjectId>())
{
var solid = transaction.GetObject(solidId, OpenMode.ForRead) as Solid3d;
if (solid != null)
{
file.WriteLine(solidId.Handle.ToString());
}
}
}
editor.WriteMessage($"\nExported {solidIds?.Count ?? 0} solids.");
}
}
}
Export Options
The CreateExportOptions
method defines the parameters for the export operation, including what corridor components to export.
private static ExportCorridorSolidsParams CreateExportOptions()
{
return new ExportCorridorSolidsParams
{
CreateSolidForShape = true,
ExportShapes = true,
ExportLinks = false,
SweepSolidForShape = false
};
}
Example Use Case
-
Source Drawing: The command assumes a Civil 3D corridor exists in the drawing.
Example file:Corridor-5c.dwg
(from Civil 3D tutorials). -
Command Execution: Run the command
ExportCorridor
in the Civil 3D environment. -
Output: A file named
solids.txt
will be created, listing the handles of exported solids. -
Heartbeat: The system remains responsive throughout the process, signaling activity every 100ms.
Example Use Case
-
Source Drawing: The command assumes a Civil 3D corridor exists in the drawing.
Example file:Corridor-5c.dwg
(from Civil 3D tutorials). -
Command Execution: Run the command
ExportCorridor
in the Civil 3D environment. -
Output: A file named
solids.txt
will be created, listing the handles of exported solids. -
Heartbeat: The system remains responsive throughout the process, signaling activity every 100ms.
The heartbeat mechanism ensures smooth execution of long-running tasks in Design Automation environments. This example demonstrates how to integrate it with Civil 3D or AutoCAD Design Automation API for corridor-to-solid export tasks.
With this approach, your applications can handle complex workflows while maintaining stability and preventing timeouts.
Try it out and let us know how it works for you!