912x
001825
2023-08-09

用于分析施工阶段的WebService & API

如果计算规则的结构,输入通常并不复杂,但非常耗时。 输入的自动化可以节省宝贵的时间。 本例中的任务是将房屋的楼层视为独立的施工阶段。 必须使用 C# 程序输入,这样用户就不必手动输入各个楼层的元素。

要求

下面的程序运行的条件如下:

  • 楼层平面平行于 xy 平面。
  • 模型中不能包含任何跨楼层的单元(例如,跨两层或更多楼层的幕墙)。
  • 基础及上一层为零楼层。
  • 不考虑实体。
  • 仅使用平面。
  • 坐标轴 z 轴与重力加速度的方向一致(向下)。

理论背景

根据要求,可以明确只指定楼层高度,提供一个单元(例如节点),其 z 坐标为较低楼层高度以上的最大值,相关楼层高度。
因此用户需要选择能够代表楼层高度的节点节点。 使用循环功能可以计算出 z 坐标,从而计算出楼层的高度。
下面将通过层高将楼板分配给所有节点。
对于线,可以返回到节点的楼层。 如果一条线起始于天花板,终止于上方的天花板,则应归入上部天花板。 该线将被分配到在你的节点中可以找到的最高的楼层。
由于杆件位于线上,并且可以在之前分配,所以杆件与其线具有相同的楼层。
这同样适用于面。 选择相应节点最高的楼层后,
如果分配了楼层的所有元素,则可以从楼层列表中创建施工阶段。

比例

软件以 Dlubal.RFEMWebServiceLibrary NuGet 包中的一个模板为基础。 在这里,您将了解如何安装:

首先激活模型的连接如下:


            

...
#region 应用程序设置
尝试
{
    application_information
    尝试
    {
        //连接到 RFEM6 或 RSTAB9 的应用程序
        application = new ApplicationClient(Binding, Address);

    }
    catch(Exception异常)
    {
        if (应用程序 != 零)
        {
            if (application.State != CommunicationState.Faulted)
            {
                application.Close();
            }
            其他
            {
                application.Abort();
            }

            application = 0;
        }
    }
    最终
    {
        ApplicationInfo = application.get_information();
        Console.WriteLine("名称: {0},版本:{1},类型: {2}, 语言: {3} ", ApplicationInfo.name, ApplicationInfo.version, ApplicationInfo.type, ApplicationInfo.language_name);
    }
    #结束区域

    //获取活动模型
    string modelUrl = application.get_active_model();

    //连接到 RFEM6/RSTAB9 模型
    ModelClient model = new ModelClient(Binding, new EndpointAddress(modelUrl));
...


为了更好地处理错误,在整个源代码中的 main 函数中使用了 ry-catch 块。 在该块中,应用程序首先被连接,然后在 ry-catch 块中进行连接。 成功连接应用程序后,当前活动的模型(在 RFEM 6 前景中)通过 get_active_model 方法连接。 如果出现任何问题,则外部的 ry-catch 块才会生效。
因为必须要分析所有的节点、线、杆件和面,所以从 RFEM 中获取所有的单元是有意义的。 要获得相应元素的编号,请先读出所有类别的对象编号。 以下是可以通过对象编号分别导入对象:


            

...
int[] node_nums = model.get_all_object_numbers(object_types.E_OBJECT_TYPE_NODE, 0);
int[] line_nums = model.get_all_object_numbers(object_types.E_OBJECT_TYPE_LINE, 0);
int[] member_nums = model.get_all_object_numbers(object_types.E_OBJECT_TYPE_MEMBER, 0);
int[] surface_nums = model.get_all_object_numbers(object_types.E_OBJECT_TYPE_SURFACE, 0);

//得到所有的节点
Console.WriteLine("1. 获取所有节点:");
节点[] nds = 新建节点[node_nums.Length];
for (int i = 0; i < node_nums.Length; ++i)
{
	nds[i] = model.get_node(node_nums[i]);
	if ((i%10)==0)
		Console.Write("+");

}
Console.WriteLine("");
...


在控制台中每隔 10 个单元旁会标注一个“+”号,以便让用户清楚地了解过程所需的时间。
在导入其他元素之前,需要先确定楼层高度。 首先,您必须读出所选对象。 传输的对象位置类型的数组(带元素的数组/向量)包含了所有选定的对象及其类型和编号。 用户可以不勾选该节点,将节点过滤掉。 因为所有节点都已经可用,所以可以在同一个循环中确定这些节点的z坐标。 只要选择的对象是节点类型,就会在节点中搜索该节点,然后将其 z 坐标输入到 floor_heights 数组中。 每找到一个坐标,就向数组添加一个元素:


            

...
Console.WriteLine("2. 获取选择的节点");
//获取所有被选择的对象
object_location[] obj_locs = model.get_all_selected_objects();

//得到所有选择的节点编号
double[] floor_heights = new double[1];
foreach(obj_location中的object_location obj)
{
	if (obj.type == object_types.E_OBJECT_TYPE_NODE)
	{
		for (int i = 0; i < nds.Length; ++i)
		{
			if (nds[i].no == obj.no)
			{
				floor_heights[floor_heights.Length - 1] = nds[i].coordinate_3;
				Array.Resize(ref floor_heights, floor_heights.Length + 1);
				打断;
			}
		}
	}
}
Array.Resize(ref floor_heights, floor_heights.Length - 1);

//排序数组
//z轴为负,最大的正值为地面
Array.Sort(floor_heights);
Array.Rverse(floor_heights);
//第一层和第一层是一个,删除第一个条目
double[] tmp_arr = new double[floor_heights.Length - 1];
Array.Copy(floor_heights, 1, tmp_arr, 0, floor_heights.Length - 1);

floor_heights = 0;
floor_heights = tmp_arr;
...


因为 z 坐标的顺序是错误的,所以首先按大小对数组进行排序。 但是 z 坐标是负方向,因此必须使用反转来反转排序。 最初,确定基础和第一层楼板计为一层,因此删除了层高的第一个元素。
下一步将导入剩余的单元,例如线、杆件和面:


            

...
//得到所有的线
Console.WriteLine("3. 获取所有线:");
line[] lns = new line[line_nums.Length];
for (int i = 0; i < line_nums.Length; ++i)
{
	lns[i] = model.get_line(line_nums[i]);
	如果 ((i % 10) == 0)
		Console.Write("+");
}
Console.WriteLine("");


//获取所有杆件
Console.WriteLine("4. 获取所有杆件:");
杆件[] mems = 新杆件[member_nums.Length];
for (int i = 0; i < member_nums.Length; ++i)
{
	mems[i] = model.get_member(member_nums[i]);
	如果 ((i % 10) == 0)
		Console.Write("+");
}
Console.WriteLine("");

//得到所有的面
Console.WriteLine("5. 获取所有面:");
面[] srfs = 新建面[surface_nums.Length];
for (int i = 0; i < surface_nums.Length; ++i)
{
	srfs[i] = model.get_surface(surface_nums[i]);
	如果 ((i % 10) == 0)
		Console.Write("+");
}
Console.WriteLine("");
...


首先需要创建二维列表, 在 nds_floor_numbers 列表中,楼层是第一维,楼层的节点编号是第二维。 首先,下面的循环遍历所有节点,还有一个子循环遍历所有楼层高度。 对于每个节点,程序从建筑物底部向上检查 Z 坐标是否位于楼层高度内。 由于数值不准确,程序中给出的数值减去1 mm的公差:


            

...
//循环通过节点并设置它们的地板
Console.WriteLine("6. 循环通过节点并获取它们的楼层");
for (int i = 0; i < nds.Length; ++i)
{
	for (int j = 0; j < floor_heights.Length; ++j)
	{
		if (nds[i].coordinate_3 >= floor_heights[j] - 0.001)
		{
			nds[i].comment = j.ToString();
			Console.WriteLine("node " + nds[i] + " floor " + j + ";" + nds[i].coordinate_3 + ";" + (floor_heights[j] - 0.001));
			nds_floor_numbers[j].Add(nds[i].no);
			model.set_node(nds[i]);
			打断;
		}
	}
}
...


只要一个节点位于指定区域内,楼层编号就会被输入作为注释,然后该节点编号就会被添加到列表中。 这时,注释可以继续传递给 RFEM(注释掉)。 给出注释是因为下面计算的不是 z 坐标,而是楼层编号。 当在线上查看循环时,这一点很明显:


            

...
//循环访问线,获取线的节点并设置线的楼层
Console.WriteLine("7. 循环线并得到它们的地板");
for (int i = 0; i < lns.Length; ++i)
{
	//得到线的节点
	int[] ln_node_nums = lns[i].definition_nodes;
	Int32 floor_max = 0;
	//循环通过线的节点
	for (int j = 0; j SMALLERTHAN ln_node_nums.Length; ++j)
	{
		//循环通过节点
		for (int l = 0; l SMALLERTHAN nds.Length; ++l)
		{
			if (nds[l].no == ln_node_nums[j])
			{
				Int32 floor = Int32. Parse(nds[l].comment);
				if (floor > floor_max)
				{
					floor_max =楼板;
					打断;
				}
			}
		}

	}
	//输入线的最大楼层
	lns[i].comment = floor_max.ToString();
	lns_floor_numbers[floor_max].Add(lns[i].no);
	model.set_line(lns[i]);

}
...


小环通过线的所有节点, 在这个子循环中,还有另一个循环通过所有节点查找在线上与节点编号匹配的节点。 并从该节点读取注释中的楼层编号。 正如在理论基础中已经提到的,确定最高楼层的编号就足够了。 该编号将被保存为该线的备注中的楼层编号,该编号将被添加到列表中。
杆件的楼层编号与其所在线的楼层编号相同。 因此,需要一个覆盖所有杆件的大环,以及覆盖所有线的子环。 一旦线编号与杆件的编号相匹配,杆件将获取其楼层高度的注释,并将该编号添加到列表中:


            

...
//循环通过杆件,得到他们的线并设置他们的地板
Console.WriteLine("8. 循环遍历杆件并获取其楼层");
for (int i = 0; i < mems.Length; ++i)
{
	//获取杆件的线编号
	int mem_ln_num = mems[i].line;

	//循环线
	for (int j = 0; j < lns.Length; ++j)
	{
		if (lns[j].no == mem_ln_num)
		{
			mems[i].comment = lns[j].comment;
			mems_floor_numbers[Int32.Parse(lns[j].comment)].Add(mems[i].no);
			打断;
		}
	}

}
//循环通过面,得到它们的线并设置它们的地板
Console.WriteLine("9. 循环通过面并获取它们的地板");
for (int i = 0; i < srfs.Length; ++i)
{
	//得到面的线
	int[] srf_line_nums = srfs[i].boundary_lines;
	Int32 floor_max = 0;
	//循环通过面的线
	for (int j = 0; j < srf_line_nums.Length; ++j)
	{
		//循环线
		for (int l = 0; l SMALLERTHAN lns.Length; ++l)
		{
			if (lns[l].no == srf_line_nums[j])
			{
				Int32 floor = Int32.Parse(lns[l].comment);
				if (floor > floor_max)
				{
					floor_max =楼板;
					打断;
				}
			}
		}

	}
	//输入面的最大楼层
	srfs[i].comment = floor_max.ToString();
	srfs_floor_numbers[floor_max].Add(srfs[i].no);

	model.set_surface(srfs[i]);

}
...


使用方法与线相似。 面上方为子环,子环位于面的边界线上。 从边界线开始最高的楼层成为面的楼层编号。
在将所有元素分类到楼层中后,接下来要创建施工阶段。 每层都创建一个施工阶段,因此计算出的循环箭头如下:


            

...
Console.WriteLine("10. 设置施工阶段");
尝试
{
	model.begin_modification("设置施工阶段");
	//创建施工阶段
	for (int i = 0; i < floor_heights.Length; ++i)
	{
		construction_stage cstg = new construction_stage();

		cstg.编号 = i + 1;
		cstg.continue_on_construction_stage = i;
		cstg.continue_on_construction_specified = true;

		cstg.are_members_enabled_to_modify = true;
		cstg.are_members_enabled_to_modifySpecified = true;
		cstg.added_members = mems_floor_numbers[i].ToArray();
		cstg.active_members = cstg.added_members;

		cstg.are_surfaces_enabled_to_modify = true;
		cstg.are_surfaces_enabled_to_modifySpecified = true;
		cstg.added_surfaces = srfs_floor_numbers[i].ToArray();
		cstg.active_surfaces = cstg.added_surfaces;

		cstg.name = "楼板 " + i;
		cstg.user_defined_name_enabled = true;
		cstg.user_defined_name_enabledSpecified = true;

		model.set_construction_stage(cstg);

	}
}
catch(Exception异常)
{
	model.cancel_modification();
	Console.WriteLine("创建几何图形时发生错误" + exception.Message);
	抛出;
}
最终
{
	尝试
	{
		model.finish_modification();
	}
	catch(Exception异常)
	{
		Console.WriteLine("当几何图形创建完成时发生错误" + exception.Message);
		抛出;
	}
}
...


在 RFEM 中创建单元时,额外使用 begin_modification/finish_modification 块,以便最大限度地提高传输速度。 如果出现问题,附加的 ry-catch 块会提供保护,在中断的情况下调用 cancel_modification,从而正确终止处理。 关于迁移需要注意的是,要迁移的单元(例如 continue_on_on_construction_stage)只有在相应的“特定”属性为“真”时 (continue_on_construction_stageSpecified)才会被迁移。 不是所有的单元都有这种 "特定的" 属性,但是如果有这种属性,那么必须加以考虑。

小结

由于 WebService 和 API 接口的多功能性,可以自动完成一些手动完成时非常耗时的输入工作。 在本示例中,“施工阶段分析”模块的输入是自动进行的, 在这里您可以尽情发挥创意,寻找省时的解决方案,下面将介绍更多的示例。


作者

Günthel 先生为Dlubal 软件客户提供技术支持。

下载