主题:多线程应用程序示范,利用挂起事件
概述:本示例展示如何利用应用程序的挂起事件,从外部工作线程与 Revit API 进行通信。
项目文件:
Command.cs
这个文件包含了继承自 IExternalCommand 接口并实现了 Execute 方法的 Command 类。
Application.cs
这个文件包含了继承自 IExternalApplication 接口并实现了其方法的 Application 类。
FaceAnalyzer.cs
这个文件处理了对一个墙面进行分析结果的显示。
SharedResults.cs
这个文件用于分析器展示结果和计算结果之间的交换。这里的所有操作都是线程安全的。
ThreadAgent.cs
这个被委托线程的主要类所包含的数据对计算很有帮助,它有一个在单独线程上运行的方法。
描述:
本示例展示了如何利用应用程序的挂起事件,从外部工作线程与 Revit API 进行通信。
说明:
1. 打开 Revit 应用程序并创建一个新文档;
2. 创造一面简单的墙;
3. 进入 3D 视图并放大,以显示墙壁最大的一面;
4. 启动外部命令“分析墙面”;
5. 当命令进入选择模式时,单击墙面上的主面;
6. 随着结果出现在墙面上,分析开始;
7. 过程需要大约12秒的时间;
8. 在此期间,您可以修改墙壁或添加门窗;
9. 每当修改墙壁时,分析将重新开始(还需12秒)。
完整的源代码请加入QQ群649037449,在群文件中下载RevitSDK.exe,解压后在文件夹中搜索本文中应用程序名称即可获得完整源码
FaceAnalyzer.cs
//
// (C) Copyright 2003-2019 by Autodesk, Inc.
//
// Permission to use, copy, modify, and distribute this software in
// object code form for any purpose and without fee is hereby granted,
// provided that the above copyright notice appears in all copies and
// that both that copyright notice and the limited warranty and
// restricted rights notice below appear in all supporting
// documentation.
//
// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
// UNINTERRUPTED OR ERROR FREE.
//
// Use, duplication, or disclosure by the U.S. Government is subject to
// restrictions set forth in FAR 52.227-19 (Commercial Computer
// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
// (Rights in Technical Data and Computer Software), as applicable.
//
using System;
using System.Threading;
using System.Collections.Generic;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.DB.Analysis;
namespace Revit.SDK.Samples.WorkThread.CS
{
/// <summary>
/// This class handles displaying results of an analysis of a wall surface.
/// </summary>
/// <remarks>
/// The analyzer only displays the results, it does not calculate them.
/// The calculation is delegated to a work-thread. The analyzer kicks
/// off the calculation and then returns to Revit. Periodically Revit
/// checks (during an Idling event) if there is more results available.
/// If there is, the analyzer grabs them from the work-thread and takes
/// care of the visualization. The thread lets the analyzer to know when
/// the calculation is finally over. When that happens, the application
/// can unregister from Idling since it does not need it anymore.
/// <para>
/// Optionally, Revit can interrupt the analysis, which it typically does
/// when the element being analyzed changes (or gets deleted). Depending
/// on that change the application requests the analysis to restart
/// or to stop completely. The analyzer makes sure the requests
/// are delivered to the work-thread.
/// </para>
/// </remarks>
///
class FaceAnalyzer
{
#region class member variables
// ActiveView
private View m_view = null;
// string representation of reference object
private String m_sreference = null;
// SpatialFieldManager
private SpatialFieldManager m_SFManager = null;
// schema Id
private int m_schemaId = -1;
// field Id
private int m_fieldId = -1;
// ThreadAgent
private ThreadAgent m_threadAgent = null;
// Results
private SharedResults m_results;
// If the result manager needs to be initialized
private bool m_needInitialization = true;
#endregion
#region class member methods
/// <summary>
/// Constructor
/// </summary>
///
public FaceAnalyzer(View view, String sref)
{
m_sreference = sref;
m_view = view;
}
/// <summary>
/// Simple convention method to get an actual reference object
/// from its stable representation we keep around.
/// </summary>
private Reference GetReference()
{
if ((m_view != null) && (m_sreference.Length > 0))
{
return Reference.ParseFromStableRepresentation(m_view.Document, m_sreference);
}
return null;
}
/// <summary>
/// Getting the face object corresponding to the reference we have stored
/// </summary>
/// <remarks>
/// The face may change during the time it takes our calculation to finish,
/// thus we always get it from the reference right when we actually need it;
/// </remarks>
private Face GetReferencedFace()
{
Reference faceref = GetReference();
if (faceref != null)
{
return m_view.Document.GetElement(faceref).GetGeometryObjectFromReference(faceref) as Face;
}
return null;
}
/// <summary>
/// Getting ready to preform an analysis for the given in view
/// </summary>
/// <remarks>
/// This initializes the Spatial Field Manager,
/// adds a field primitive corresponding to the face,
/// and registers our result schema we want to use.
/// The method clears any previous results in the view.
/// </remarks>
///
public void Initialize()
{
// create of get field manager for the view
m_SFManager = SpatialFieldManager.GetSpatialFieldManager(m_view);
if (m_SFManager == null)
{
m_SFManager = SpatialFieldManager.CreateSpatialFieldManager(m_view, 1);
}
// For the sake of simplicity, we remove any previous results
m_SFManager.Clear();
// register schema for the results
AnalysisResultSchema schema = new AnalysisResultSchema("E4623E91-8044-4721-86EA-2893642F13A9", "SDK2014-AL, Sample Schema");
m_schemaId = m_SFManager.RegisterResult(schema);
// Add a spatial field for our face reference
m_fieldId = m_SFManager.AddSpatialFieldPrimitive(GetReference());
m_needInitialization = false;
}
/// <summary>
/// Returns the Id of the element being analyzed
/// </summary>
/// <value>
/// Id of the element of which face the analysis was set up for.
/// </value>
///
public ElementId AnalyzedElementId
{
get
{
Reference faceref = GetReference();
if (faceref != null)
{
return faceref.ElementId;
}
return ElementId.InvalidElementId;
}
}
/// <summary>
/// Updating results on the surface being analyzed
/// </summary>
/// <remarks>
/// This is called periodically by the Idling event
/// until there is no more results to be updated.
/// </remarks>
/// <returns>
/// Returns True if there is still more to be processed.
/// </returns>
public bool UpdateResults()
{
// If we still need to initialize the result manager
// it means we were interrupted and we restarted.
// Therefore we know we do not have any results to pick up.
// Instead, we initialize the manager and start a new calculation.
// We will have first results from this new calculation process
// next time we get called here.
if (m_needInitialization)
{
Initialize();
return StartCalculation();
}
// We aks the Result instance if there are results available
// The methods returns True only if there has been results added
// since the last time we called that method.
IList<UV> points;
IList<ValueAtPoint> values;
if (m_results.GetResults(out points, out values))
{
FieldDomainPointsByUV fieldPoints = new FieldDomainPointsByUV(points);
FieldValues fieldValues = new FieldValues(values);
m_SFManager.UpdateSpatialFieldPrimitive(m_fieldId, fieldPoints, fieldValues, m_schemaId);
}
// if the thread is not around anymore to result more data,
// it means the analysis is finished for the FaceAnalyzer too.
return ((m_threadAgent != null) && (m_threadAgent.IsThreadAlive));
}
/// <summary>
/// Starting a work-thread to perform the calculation
/// </summary>
/// <returns>
/// True if the thread started off successfully.
/// </returns>
public bool StartCalculation()
{
// we need to get the face from the reference we track
Face theface = GetReferencedFace();
if (theface == null)
{
return false;
}
// An instance for the result exchange
m_results = new SharedResults();
// The agent does not need the face nor the reference.
// It can work with just the bounding box and a density of the grid.
// We also pass the Results as an argument. The thread will be adding calculated results
// to that object, while here in the analyzer we will read from it. Both operations are thread-safe;
m_threadAgent = new ThreadAgent(theface.GetBoundingBox(), 10, m_results);
// now we can ask the agent to start the work thread
return m_threadAgent.Start();
}
/// <summary>
/// Stopping the calculation if still in progress
/// </summary>
/// <remarks>
/// This is typically to be called when there have been changes
/// made in the model resulting in changes to the element being analyzed.
/// </remarks>
///
public void StopCalculation()
{
// We first signal we do not want more results,
// so the work-thread knows to stop if it is still around.
m_results.SetCompleted();
// If the thread is alive, we'll wait for it to finish
// It will not take longer than one calculation cycle.
if (m_threadAgent != null)
{
if (m_threadAgent.IsThreadAlive)
{
m_threadAgent.WaitToFinish();
}
m_threadAgent = null;
}
}
/// <summary>
/// Restarting the calculation.
/// </summary>
/// <remarks>
/// This is probably caused by a change in the face being analyzed,
/// typically when the DocumentChanged event indicates modification were made.
/// </remarks>
///
public void RestartCalculation()
{
// First we make sure we start the ongoing calculation
StopCalculation();
// For this might have been called during times when the document
// is in a non-modifiable state (e.g. during an undo operation)
// we cannot really start the new calculation just yet. We can only
// set a flag that a re-initialization is yet to be made,
// which will be then picked up at the next update. That will happen
// on a regular Idling event, during which the document is modifiable.
m_needInitialization = true;
}
#endregion
} // class
}
SharedResults.cs
ThreadAgent.cs
版权所有 :无锡模信建筑科技有限公司 苏ICP备2021028830号-1 BIM建模|BIM技术应用|BIM软件开发
联系地址:江苏省无锡市新吴区龙山路4号B座705 手机:18761516598