问题描述:
如何通过 SAP2000 API 启动或连接远程计算机上的 SAP2000 程序?
图 1. API 帮助文档
解答:
当需要处理大量荷载工况(如:基于性能化设计的地震作用、桥梁设计的移动荷载等)且有多台计算机可同时进行分析时,此功能非常实用。通过 API 脚本或插件可在多台远程计算机上同时启动 SAP2000 并运行分析,各台计算机上的分析结果可自动合并至主计算机,无需人工干预。此外,该功能还可用于分布式处理,执行大规模参数研究或蒙特卡洛模拟。用户可参考图 1 所示的帮助文档,具体要求及操作步骤整理如下:
1. 硬件包括两台计算器,都安装有 SAP2000 程序,推荐 V26 以下版本。
☆ Main
Computer:主计算机(你的主要电脑)。
☆ Remote
Computer:任意你有权限访问(物理或网络)的远程计算机,用于运行 SAP2000。
2. 在远程计算机上,以管理员身份运行 CSiAPIService.exe 文件,如图 2 所示。
图 2. CSiAPIService.exe 文件路径及运行界面
3. 使用 Test-NetConnection 确认远程计算机端口开放。如图 3 所示,在主计算机上打开
PowerShell,运行以下命令测试 TCP 端口连通性:
Test-NetConnection
-ComputerName IPV4地址 -Port
11650
如果 TcpTestSucceeded 为
False,请检查远程计算机的防火墙设置,确保端口 11650 的入站规则已打开。
图 3. 端口测试
4. 在主计算机(Main Computer)上编写脚本/程序或插件,通过以下 API 函数远程启动或连接 SAP2000。具体如下:
创建远程实例(CreateObject)
-
cHelper.CreateObjectHost(hostName,
fullPath)
-
cHelper.CreateObjectHostPort(hostName,
portNumber, fullPath)
-
cHelper.CreateObjectProgIDHost(hostName,
progID)
-
cHelper.CreateObjectProgIDHostPort(hostName,
portNumber, progID)
连接已在远程运行的实例(GetObject)
-
cHelper.GetObjectHost(hostName,
progID)
-
cHelper.GetObjectHostPort(hostName,
portNumber, progID)
其中:
hostName:远程计算机 IP 地址,步骤:win+R -> cmd -> ipconfig -> IPv4 地址,如图 4 所示。
portNumber:可选,指定 TCP 端口。
fullPath / progID:安装路径或进程 ID 号,与本地 CreateObject()/GetObject() 相同。
图 4. IPv4 地址查询
5. 以 CreateObjectHost() 函数为例,成功在远程计算机创建实例后,返回的 cOAPI 对象上的所有 API 调用均会在远程计算机执行,包括:打开模型、修改模型、运行分析与设计、读取结果、将结果合并回主计算机上。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using SAP2000v1;
namespace RemoteAPI
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
// 使用 Remote API 连接到远程 SAP2000 实例
private void Btn_OpenRemoteAPI_Click(object sender, EventArgs e)
{
cHelper myHelper;
myHelper = new Helper();
int ret = 0;
var RemoteServe = "192.168.0.***"; // 远程计算机的 IP 地址 win+R -> cmd -> ipconfig -> IPv4 地址
var ProgramPath = @"C:\Program Files\Computers and Structures\SAP2000 25\SAP2000.exe"; //远程计算机上 SAP2000 的安装路径
//full path to the model
//set it to the desired path of your model
string ModelDirectory = @"C:\CSiAPIexample";
try
{
System.IO.Directory.CreateDirectory(ModelDirectory);
}
catch (Exception ex)
{
Console.WriteLine("Could not create directory: " + ModelDirectory);
return;
}
string ModelName = "API_1-001.sdb"; string ModelPath = ModelDirectory + System.IO.Path.DirectorySeparatorChar + ModelName;
var mySapObject = myHelper.CreateObjectHost(RemoteServe, ProgramPath);
mySapObject.ApplicationStart(); //启动SAP2000
// 判断是否成功连接到 SAP2000
if (mySapObject == null)
{
MessageBox.Show("Failed to connect to SAP2000.");
return;
}
var mySapModel = mySapObject.SapModel;
mySapModel.InitializeNewModel(eUnits.N_mm_C); //初始化模型
mySapModel.File.NewBlank(); //建立空白模型
//initialize model
ret = mySapModel.InitializeNewModel((eUnits.kip_in_F));
//create new blank model
ret = mySapModel.File.NewBlank();
//define material property
ret = mySapModel.PropMaterial.SetMaterial("CONC", eMatType.Concrete, -1, "", "");
//assign isotropic mechanical properties to material
ret = mySapModel.PropMaterial.SetMPIsotropic("CONC", 3600, 0.2, 0.0000055, 0);
//define rectangular frame section property
ret = mySapModel.PropFrame.SetRectangle("R1", "CONC", 12, 12, 1, "", "");
//define frame section property modifiers
double[] ModValue = new double[8];
int i;
for (i = 0; i <= 7; i++) { ModValue[i] = 1; } ModValue[0] = 1000; ModValue[1] = 0; ModValue[2] = 0; ret = mySapModel.PropFrame.SetModifiers("R1", ref ModValue); //switch to k-ft units ret = mySapModel.SetPresentUnits(eUnits.kip_ft_F); //add frame object by coordinates string[] FrameName = new string[3]; string temp_string1 = FrameName[0]; string temp_string2 = FrameName[0]; ret = mySapModel.FrameObj.AddByCoord(0, 0, 0, 0, 0, 10, ref temp_string1, "R1", "1", "Global"); FrameName[0] = temp_string1; ret = mySapModel.FrameObj.AddByCoord(0, 0, 10, 8, 0, 16, ref temp_string1, "R1", "2", "Global"); FrameName[1] = temp_string1; ret = mySapModel.FrameObj.AddByCoord(-4, 0, 10, 0, 0, 10, ref temp_string1, "R1", "3", "Global"); FrameName[2] = temp_string1; //assign point object restraint at base string[] PointName = new string[2]; bool[] Restraint = new bool[6]; for (i = 0; i <= 3; i++) { Restraint[i] = true; } for (i = 4; i <= 5; i++) { Restraint[i] = false; } ret = mySapModel.FrameObj.GetPoints(FrameName[0], ref temp_string1, ref temp_string2); PointName[0] = temp_string1; PointName[1] = temp_string2; ret = mySapModel.PointObj.SetRestraint(PointName[0], ref Restraint, 0); //assign point object restraint at top for (i = 0; i <= 1; i++) { Restraint[i] = true; } for (i = 2; i <= 5; i++) { Restraint[i] = false; } ret = mySapModel.FrameObj.GetPoints(FrameName[1], ref temp_string1, ref temp_string2); PointName[0] = temp_string1; PointName[1] = temp_string2; ret = mySapModel.PointObj.SetRestraint(PointName[1], ref Restraint, 0); //refresh view, update (initialize) zoom bool temp_bool = false; ret = mySapModel.View.RefreshView(0, temp_bool); //add load patterns temp_bool = true; ret = mySapModel.LoadPatterns.Add("1", eLoadPatternType.Other, 1, temp_bool); ret = mySapModel.LoadPatterns.Add("2", eLoadPatternType.Other, 0, temp_bool); ret = mySapModel.LoadPatterns.Add("3", eLoadPatternType.Other, 0, temp_bool); ret = mySapModel.LoadPatterns.Add("4", eLoadPatternType.Other, 0, temp_bool); ret = mySapModel.LoadPatterns.Add("5", eLoadPatternType.Other, 0, temp_bool); ret = mySapModel.LoadPatterns.Add("6", eLoadPatternType.Other, 0, temp_bool); ret = mySapModel.LoadPatterns.Add("7", eLoadPatternType.Other, 0, temp_bool); //assign loading for load pattern 2 ret = mySapModel.FrameObj.GetPoints(FrameName[2], ref temp_string1, ref temp_string2); PointName[0] = temp_string1; PointName[1] = temp_string2; double[] PointLoadValue = new double[6]; PointLoadValue[2] = -10; ret = mySapModel.PointObj.SetLoadForce(PointName[0], "2", ref PointLoadValue, false, "Global", 0); ret = mySapModel.FrameObj.SetLoadDistributed(FrameName[2], "2", 1, 10, 0, 1, 1.8, 1.8, "Global", true, true, 0); //assign loading for load pattern 3 ret = mySapModel.FrameObj.GetPoints(FrameName[2], ref temp_string1, ref temp_string2); PointName[0] = temp_string1; PointName[1] = temp_string2; PointLoadValue = new double[6]; PointLoadValue[2] = -17.2; PointLoadValue[4] = -54.4; ret = mySapModel.PointObj.SetLoadForce(PointName[1], "3", ref PointLoadValue, false, "Global", 0); //assign loading for load pattern 4 ret = mySapModel.FrameObj.SetLoadDistributed(FrameName[1], "4", 1, 11, 0, 1, 2, 2, "Global", true, true, 0); //assign loading for load pattern 5 ret = mySapModel.FrameObj.SetLoadDistributed(FrameName[0], "5", 1, 2, 0, 1, 2, 2, "Local", true, true, 0); ret = mySapModel.FrameObj.SetLoadDistributed(FrameName[1], "5", 1, 2, 0, 1, -2, -2, "Local", true, true, 0); //assign loading for load pattern 6 ret = mySapModel.FrameObj.SetLoadDistributed(FrameName[0], "6", 1, 2, 0, 1, 0.9984, 0.3744, "Local", true, true, 0); ret = mySapModel.FrameObj.SetLoadDistributed(FrameName[1], "6", 1, 2, 0, 1, -0.3744, 0, "Local", true, true, 0); //assign loading for load pattern 7 ret = mySapModel.FrameObj.SetLoadPoint(FrameName[1], "7", 1, 2, 0.5, -15, "Local", true, true, 0); //switch to k-in units ret = mySapModel.SetPresentUnits(eUnits.kip_in_F); //save model ret = mySapModel.File.Save(ModelPath); //run model (this will create the analysis model) ret = mySapModel.Analyze.RunAnalysis(); //initialize for SAP2000 results double[] SapResult = new double[7]; ret = mySapModel.FrameObj.GetPoints(FrameName[1], ref temp_string1, ref temp_string2); PointName[0] = temp_string1; PointName[1] = temp_string2; //get SAP2000 results for load patterns 1 through 7 int NumberResults = 0; string[] Obj = new string[1]; string[] Elm = new string[1]; string[] LoadCase = new string[1]; string[] StepType = new string[1]; double[] StepNum = new double[1]; double[] U1 = new double[1]; double[] U2 = new double[1]; double[] U3 = new double[1]; double[] R1 = new double[1]; double[] R2 = new double[1]; double[] R3 = new double[1]; for (i = 0; i <= 6; i++) { ret = mySapModel.Results.Setup.DeselectAllCasesAndCombosForOutput(); ret = mySapModel.Results.Setup.SetCaseSelectedForOutput(Convert.ToString(i + 1), true); if (i <= 3) { ret = mySapModel.Results.JointDispl(PointName[1], eItemTypeElm.ObjectElm, ref NumberResults, ref Obj, ref Elm, ref LoadCase, ref StepType, ref StepNum, ref U1, ref U2, ref U3, ref R1, ref R2, ref R3); SapResult[i] = U3[0]; } else { ret = mySapModel.Results.JointDispl(PointName[0], eItemTypeElm.ObjectElm, ref NumberResults, ref Obj, ref Elm, ref LoadCase, ref StepType, ref StepNum, ref U1, ref U2, ref U3, ref R1, ref R2, ref R3); SapResult[i] = U1[0]; } } //close SAP2000 //mySapObject.ApplicationExit(false); //mySapModel = null; //mySapObject = null; //fill SAP2000 result strings string[] SapResultString = new string[7]; for (i = 0; i <= 6; i++) { SapResultString[i] = string.Format("{0:0.00000}", SapResult[i]); ret = (string.Compare(SapResultString[i], 1, "-", 1, 1, true)); if (ret != 0) { SapResultString[i] = " " + SapResultString[i]; } } //fill independent results double[] IndResult = new double[7]; string[] IndResultString = new string[7]; IndResult[0] = -0.02639; IndResult[1] = 0.06296; IndResult[2] = 0.06296; IndResult[3] = -0.2963; IndResult[4] = 0.3125; IndResult[5] = 0.11556; IndResult[6] = 0.00651; for (i = 0; i <= 6; i++) { IndResultString[i] = string.Format("{0:0.00000}", IndResult[i]); ret = (string.Compare(IndResultString[i], 1, "-", 1, 1, true)); if (ret != 0) { IndResultString[i] = " " + IndResultString[i]; } } //fill percent difference double[] PercentDiff = new double[7]; string[] PercentDiffString = new string[7]; for (i = 0; i <= 6; i++) { PercentDiff[i] = (SapResult[i] / IndResult[i]) - 1; PercentDiffString[i] = string.Format("{0:0%}", PercentDiff[i]); ret = (string.Compare(PercentDiffString[i], 1, "-", 1, 1, true)); if (ret != 0) { PercentDiffString[i] = " " + PercentDiffString[i]; } } //display message box comparing results string msg = ""; msg = msg + "LC Sap2000 Independent %Diff\r\n"; for (i = 0; i <= 5; i++) { msg = msg + string.Format("{0:0}", i + 1) + " " + SapResultString[i] + " " + IndResultString[i] + " " + PercentDiffString[i] + "\r\n"; } msg = msg + string.Format("{0:0}", i + 1) + " " + SapResultString[i] + " " + IndResultString[i] + " " + PercentDiffString[i]; Console.WriteLine(msg); Console.Read(); MessageBox.Show(this, "SAP2000 Remote API Example", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); } } }