912x
001825
2023-08-09

WebService i API do analizy etapów budowy

Podczas obliczania regularnych konstrukcji wprowadzanie danych często nie jest skomplikowane, ale czasochłonne. Automatyzacja wprowadzania danych pozwala zaoszczędzić cenny czas. W niniejszym przypadku należy uwzględnić kondygnacje domu jako poszczególne etapy budowy. Wpisu należy dokonać przy pomocy programu w języku C#, aby użytkownik nie musiał ręcznie wprowadzać elementów poszczególnych pięter.

Wymagania

Poniższy program działa pod następującymi warunkami:

  • Płaszczyzny kondygnacji są równoległe do płaszczyzny xy.
  • Model nie może zawierać elementów rozciągających się na kilka kondygnacji (np. powierzchnia elewacji rozciągająca się na dwie lub więcej kondygnacji).
  • Fundament i pierwsze piętro należą do kondygnacji zerowej.
  • Bryły nie są uwzględniane.
  • Używane są tylko powierzchnie płaskie.
  • Oś z jest skierowana w kierunku przyspieszenia ziemskiego („w dół”).

Podstawy teoretyczne

Ze względu na wymagania,'jasne jest, że należy zdefiniować tylko wysokości kondygnacji, przy czym współrzędna z dla elementu, takiego jak węzeł, leży powyżej wysokości dolnej kondygnacji i maksymalnie wynosi odpowiednią wysokość kondygnacji.
Dlatego użytkownik musi wybrać grupę węzłów reprezentatywnych dla wysokości kondygnacji. Współrzędne z, a tym samym wysokości kondygnacji, można określić za pomocą pętli.
Poniżej przypisano kondygnacje do wszystkich węzłów za pomocą wysokości.
W przypadku linii można cofnąć się do kondygnacji węzłów. Jeżeli linia zaczyna się na stropie, a kończy na stropie leżącym powyżej, należy ją przypisać do stropu górnego. Linia zostaje zatem przypisana do najwyższego piętra, jakie można znaleźć w węzłach.
Ponieważ pręty leżą na liniach i mogą być wcześniej przydzielane, pręt ten ma takie samo strop jak jego linia.
To samo dotyczy powierzchni. Określana jest najwyższa kondygnacja z odpowiednich węzłów.
Jeżeli wszystkie elementy stropu zostały już przypisane, etapy budowy można utworzyć z list stropów.

Aplikacja

Program wykorzystuje szablon z pakietu NuGet Dlubal.RFEMWebServiceLibrary. Tutaj dowiesz się, jak go zainstalować:

Najpierw aktywny model jest łączony w następujący sposób:


            

...
#region Ustawienia aplikacji
try
{
    informacje_o_aplikacji Informacje o_aplikacji;
    try
    {
        //połączenie z aplikacją RFEM6 lub RSTAB9
        aplikacja = newKlientAplikacji(Powiązanie, Adres);

    }
    zaczep (wyjątek wyjątku)
    {
        if (zastosowanie != zero)
        {
            if (application.State != CommunicationState.Faulted)
            {
                aplikacja.Zamknij ();
            }
            else
            {
                aplikacja.Abort();
            }

            zastosowanie = zero;
        }
    }
    finally
    {
        ApplicationInfo = application.get_information();
        Console.WriteLine("Nazwa: {0}, Wersja:{1}, Typ: {2}, język: {3}", ApplicationInfo.name, ApplicationInfo.version, ApplicationInfo.type, ApplicationInfo.language_name);
    }
    #endregion

    //pobierz aktywny model
    string modelUrl = application.get_active_model ();

    //połączenie z modelem RFEM6/RSTAB9
    ModelClientmodel = newModelClient(Binding, newEndpointAddress(modelUrl));
...


Dla lepszej obsługi błędów, w kodzie źródłowym funkcji main zastosowano blok try-catch. W ramach tego bloku aplikacja jest połączona jako pierwsza, co ponownie odbywa się w bloku try-catch. Po pomyślnym połączeniu aplikacji, aktualnie aktywny model (na pierwszym planie programu RFEM 6) jest podłączany za pomocą metody get_active_model. W przypadku wystąpienia problemów zaczyna działać zewnętrzny blok try-catch.
Ponieważ wszystkie węzły, linie, pręty i powierzchnie muszą zostać przeanalizowane, sensowne jest pobranie wszystkich elementów z programu RFEM. Aby uzyskać numery odpowiednich elementów, najpierw odczytywane są numery obiektów wszystkich kategorii. W poniższym przykładzie odpowiednie obiekty można przenieść za pomocą ich numerów:


            

...
int[] nr_węzłów = 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[]pręty_nums = model.get_all_object_numbers(object_types.E_OBJECT_TYPE_MEMBER, 0);
int[] numery_powierzchni = model.get_all_object_numbers(object_types.E_OBJECT_TYPE_SURFACE, 0);

//pobranie wszystkich węzłów
Console.WriteLine("1. Pobierz wszystkie węzły:");
węzeł[] nds = nowy węzeł[liczba_węzła.Długość];
for (int i = 0; i 

Aby użytkownik miał podgląd na czas trwania procesu, co 10 elementów w konsoli wyświetlany jest "+".
Przed przeniesieniem innych elementów w kolejnym kroku określane są wysokości kondygnacji. Najpierw należy odczytać wybrane obiekty. Przekazywana tablica (tablica/wektor z elementami) typu object_location zawiera wszystkie wybrane obiekty wraz z ich typem i numerem. W ten sposób węzły można odfiltrować w pętli. Ponieważ wszystkie węzły są już dostępne, współrzędne z tych węzłów można wyznaczyć w tej samej pętli. Przez wybrane obiekty przechodzi pętla, a jeżeli element jest elementem typu węzeł, węzeł ten jest wyszukiwany w węzłach, a następnie jego współrzędna z jest wprowadzana do tablicy wysokości_podłogi. Dla każdej znalezionej współrzędnej dodawany jest jeden element do szyku:


            

...
Console.WriteLine("2. Pobierz wybrane węzły");
//pobranie wszystkich wybranych obiektów
object_location[] obj_locs = model.get_all_selected_objects();

//pobranie wszystkich wybranych numerów węzłów
podwójna[] wysokość_podłogi = new podwójna[1];
foreach (object_location obj w obj_locs)
{
	if (obj.type == object_types.E_OBJECT_TYPE_NODE)
	{
		for (int i = 0; i < nds.Length; ++i)
		{
			if (nds[i].no == obj.no)
			{
				wysokości_podłogi[wysokości_podłogi.Długość - 1] = nds[i].coordinate_3;
				Array.Resize(ref wysokość_podłogi, wysokości_podłogi.Długość + 1);
				break;
			}
		}
	}
}
Array.Resize(ref floor_heights, floor_heights.Length - 1);

//sortuj tablicę
//oś z jest ujemna, najbardziej dodatnia wartość to podłoże
Array.Sort(wysokość_podłogi);
Macierz.Reverse(wysokość_podłogi);
//grunt i pierwszy poziom stanowią jeden poziom, usuń pierwszy wpis
double[] tmp_arr = new double[wysokości_podłogi.Długość - 1];
Macierz.Copy(wysokości_podłogi, 1, tmp_arr, 0, wysokości_podłogi.Długość - 1);

wysokości_podłogi = zero;
wysokości_podłogi = tmp_arr;
...


Ponieważ współrzędne z nie są w prawidłowej kolejności, szyk jest najpierw sortowany według rozmiaru. Współrzędna z znajduje się jednak w kierunku ujemnym, dlatego sortowanie musi zostać odwrócone za pomocą opcji Odwrócić. Wstępnie ustalono, że fundament i płyta parteru stanowią jedną kondygnację, w związku z czym usuwany jest pierwszy element na kondygnacjach.
W kolejnym kroku przenoszone są pozostałe elementy, takie jak linie, pręty i powierzchnie:


            

...
//pobranie wszystkich linii
Console.WriteLine("3. Pobierz wszystkie linie:");
linia [] lns = nowa linia[liczba_linii.Długość];
for (int i = 0; i < line_nums.Length; ++i)
{
	lns[i] = model.get_line(line_nums[i]);
	if ((i % 10) == 0)
		Console.Write("+");
}
Console.WriteLine("");


//pobranie wszystkich prętów
Console.WriteLine("4. Pobierz wszystkie pręty:");
pręt [] pręty = nowy pręt [pręt_liczba.Długość];
for (int i = 0; i 

Aby posortować elementy na poszczególne kondygnacje, najpierw tworzone są dwuwymiarowe listy. Lista nds_floor_numbers ma kondygnacje jako pierwszy wymiar, a numery węzłów kondygnacji jako drugi wymiar. Po pierwsze, następująca pętla przebiega przez wszystkie węzły i posiada podpętlę, która przebiega przez wszystkie wysokości kondygnacji. Dla każdego węzła program sprawdza, od dołu w górę budynku, czy współrzędna Z leży na wysokości kondygnacji. Ze względu na niedokładności numeryczne odjęto tolerancję 1 mm:


            

...
//wykonanie pętli przez węzły i ustawienie ich podłogi
Console.WriteLine("6. Wykonaj pętlę przez węzły i pobierz ich strop");
for (int i = 0; i < nds.Length; ++i)
{
	for (int j = 0; j < wysokości_podłogi.Długość; ++j)
	{
		if (nds[i].współrzędna_3 >= wysokości_kondygnacji[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]);
			break;
		}
	}
}
...


Jeżeli węzeł znajduje się we wskazanym obszarze, numer kondygnacji jest wprowadzany jako komentarz do niego, a numer węzła zostaje dodany do listy. W tym miejscu możliwe byłoby również przeniesienie komentarza do programu RFEM (zakomentowanie). Powodem zastosowania tego komentarza jest to, że w analizowanym punkcie nie są uwzględniane współrzędne z, lecz numer kondygnacji. Jest to widoczne, gdy spojrzymy na pętlę nad liniami:


            

...
//pętla przez linie, pobranie ich węzłów i ustawienie podłogi
Console.WriteLine("7. Wykonaj pętlę przez linie i uzyskaj ich podłogę");
for (int i = 0; i < lns.Length; ++i)
{
	//pobranie węzłów linii
	int [] ln_liczba_węzłów = lns[i].definicja_węzły;
	Int32 piętro_maks. = 0;
	//pętla przez węzły linii
	for (int j = 0; j SMALLERTHAN ln_node_nums.Length; ++ j)
	{
		//pętla przez węzły
		for (int l = 0; l SMALLERTHAN nds.Length; ++l)
		{
			if (nds[l].no == ln_node_nums[j])
			{
				Int32 piętro = Int32.Parse(nds[l].comment);
				jeżeli (podłoga > maks._podłoga)
				{
					podłoga_max = podłoga;
					break;
				}
			}
		}

	}
	//podaj maksymalną kondygnację w linii
	lns[i].comment = floor_max.ToString();
	lns_floor_numbers[floor_max].Add(lns[i].no);
	//model.set_line(lns[i]);

}
...


Pętla przebiega przez wszystkie linie i posiada podpętlę, która przebiega przez wszystkie węzły linii. W ramach tej podpętli przez wszystkie węzły przebiega kolejna pętla, w której znajduje się węzeł pasujący do numeru węzła linii. Z tego węzła odczytywany jest numer kondygnacji z komentarza. Jak już wspomniano w podstawach teorii, wystarczy określić najwyższy numer kondygnacji. Numer ten zostaje następnie zapisany jako numer kondygnacji w komentarzu do linii, a numer linii zostaje wstawiony na listę.
Pręty otrzymują takie same numery kondygnacji, jak linie, na których leżą. Z tego względu wymagana jest pętla na wszystkich prętach z podpętlą na wszystkich liniach. Jeżeli numer linii odpowiada numerowi pręta, wysokość pręta zostaje oznaczona w komentarzu do wysokości kondygnacji, a numer zostaje dodany do listy:


            

...
//wykonanie pętli przez pręty, pobranie ich linii i zdefiniowanie ich podłogi
Console.WriteLine("8. Przeprowadź pętlę przez pręty i uzyskaj ich strop");
for (int i = 0; i < mems.Length; ++i)
{
	//pobranie numeru linii pręta
	int mem_ln_num = mems[i].line;

	//pętla przez linie
	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);
			break;
		}
	}

}
//wykonanie pętli przez powierzchnie, uzyskanie linii i zdefiniowanie podłogi
Console.WriteLine("9. Wykonaj pętlę przez powierzchnie i pobierz ich podłogę");
for (int i = 0; i < srfs.Length; ++i)
{
	//pobierz linie powierzchni
	int [] srf_line_nums = srfs [i].lines_lines_lines;
	Int32 piętro_maks. = 0;
	//pętla przez linie powierzchni
	for (int j = 0; j < srf_line_nums.Length; ++ j)
	{
		//pętla przez linie
		for (int l = 0; l SMALLERTHAN lns.Length; ++l)
		{
			if (lns[l].no == srf_line_nums[j])
			{
				Int32 piętro = Int32.Parse(lns[l].comment);
				jeżeli (podłoga > maks._podłoga)
				{
					podłoga_max = podłoga;
					break;
				}
			}
		}

	}
	//wprowadź maksymalną kondygnację w powierzchni
	srfs[i].comment = floor_max.ToString();
	srfs_floor_numbers[floor_max].Add(srfs[i].no);

	//model.set_surface(srfs[i]);

}
...


Postępowanie w przypadku powierzchni jest podobne jak w przypadku linii. Nad powierzchniami poprowadzona jest pętla, a podpętla nad ich liniami granicznymi. Najwyższa kondygnacja za linią graniczną staje się numerem kondygnacji powierzchni.
Po posortowaniu wszystkich elementów na kondygnacje należy utworzyć etapy budowy. Dla każdej kondygnacji tworzony jest etap budowy, a zatem pętla jest wykonywana nad wysokościami kondygnacji:


            

...
Console.WriteLine("10. Zdefiniuj etapy budowy");
try
{
	model.begin_modification("ustaw etapy budowy");
	//utwórz etapy budowy
	for (int i = 0; i < wysokości_podłogi.Długość; ++i)
	{
		etap_budowy cstg = nowy etap_budowy ();

		cstg.no = i + 1;
		cstg.continue_on_construction_stage = i;
		cstg.continue_on_construction_stageSpecified = prawda;

		cstg.are_members_enabled_to_modify = prawda;
		cstg.are_members_enabled_to_modifySpecified = prawda;
		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 = prawda;
		cstg.added_surfaces = liczba_podłog_srfs[i].ToArray();
		cstg.active_surfaces = cstg.added_surfaces;

		cstg.name = "podłoga " + i;
		cstg.nazwa_zdefiniowanej_użytkownika_włączone = prawda;
		cstg.nazwa_zdefiniowanej_użytkownika_enabledSpecified = prawda;

		model.set_construction_stage(cstg);

	}
}
zaczep (wyjątek wyjątku)
{
	model.cancel_modification();
	Console.WriteLine("Coś się stało podczas tworzenia geometrii" + wyjątek.Message);
	rzut;
}
finally
{
	try
	{
		model.finish_modification();
	}
	zaczep (wyjątek wyjątku)
	{
		Console.WriteLine("Coś się stało podczas kończenia tworzenia geometrii" + wyjątek.Message);
		rzut;
	}
}
...


W przypadku tworzenia elementów w programie RFEM, dodatkowo wykorzystywany jest blok Begin_modification/Finish_modification, aby zmaksymalizować prędkość transferu. W przypadku wystąpienia problemów, dodatkowy blok try-catch zapewnia ochronę, która wywołuje anulowanie_modyfikacji w przypadku zerwania połączenia, co powoduje prawidłowe zakończenie przetwarzania. Ważną informacją dotyczącą przenoszenia danych jest to, że elementy, które mają zostać przeniesione, takie jak Further_on_construction_stage, mogą być przenoszone tylko wtedy, gdy odpowiadająca im właściwość „Specified” ma wartość „true” (continue_on_construction_stageSpecified). Nie wszystkie elementy mają taką właściwość "Określone", ale jeśli jest ona dostępna, należy ją uwzględnić.

Podsumowanie

Ze względu na wszechstronność interfejsu WebService i API, możliwe jest zautomatyzowanie wielu wpisów, które ręcznie byłyby czasochłonne. W omawianym przykładzie automatycznie wprowadzane są dane dla rozszerzenia Analiza etapów budowy, które służy również jako obiekt zastępczy dla wielu innych rozszerzeń i opcji. Ogranicza Państwa kreatywność w zakresie rozwiązań oszczędzających czas, a przykłady pojawią się więcej.


Autor

Pan Günthel zapewnia wsparcie techniczne klientom firmy Dlubal Software i zajmuje się ich zapytaniami.

Pobrane