912x
001825
2023-08-09

WebService & API per l'analisi delle fasi costruttive

Quando si calcolano strutture regolari, l'immissione dei dati spesso non è complicata ma richiede molto tempo. L'automazione degli input può far risparmiare tempo prezioso. Il compito descritto in questo articolo è considerare i piani di una casa come singole fasi costruttive. I dati vengono inseriti utilizzando un programma C#in modo che l'utente non debba inserire manualmente gli elementi dei singoli piani.

Requisiti

Il seguente programma viene eseguito nelle seguenti condizioni:

  • I solai del piano sono paralleli al piano xy.
  • Il modello non deve contenere elementi che si estendono su più piani (ad esempio, una superficie di facciata che si estende su due o più piani).
  • La fondazione e il primo piano appartengono al piano zero.
  • I solidi non sono considerati.
  • Vengono utilizzate solo superfici piane.
  • L'asse z è diretto nella direzione dell'accelerazione gravitazionale ("verso il basso").

Base teorica

In base ai requisiti, è chiaro che devono essere specificate solo le altezze del piano, fornendo un elemento, come un nodo, con la sua coordinata z sopra l'altezza del piano inferiore e fino a un massimo di o uguale al altezza del piano pertinente.
Pertanto, l'utente deve selezionare una serie di nodi che sono rappresentativi per le altezze dei piani. Le coordinate z e quindi le altezze dei piani possono essere determinate per mezzo di un ciclo.
Di seguito, i piani sono assegnati a tutti i nodi utilizzando le altezze.
Per le linee, è possibile tornare ai piani dei nodi. Se una linea inizia su un soffitto e termina sul soffitto che si trova sopra, dovrebbe essere assegnata al soffitto superiore. La linea è quindi assegnata al piano più alto che può essere trovato nei tuoi nodi.
Poiché le aste giacciono su linee e poiché possono essere assegnate prima, l'asta ha lo stesso piano della sua linea.
Lo stesso vale per le superfici. Viene determinato il piano più alto dei nodi corrispondenti.
Se tutti gli elementi di un piano sono stati assegnati, le fasi costruttive possono essere create dagli elenchi dei piani.

Applicazione

Il programma utilizza un modello dal pacchetto NuGet Dlubal.RFEMWebServiceLibrary come base. Qui puoi scoprire come installarlo:

Innanzitutto, il modello attivo è collegato come segue:


            

...
#region Application Settings
try
{
    application_information ApplicationInfo;
    try
    {
        // connects to RFEM6 or RSTAB9 application
        application = new ApplicationClient(Binding, Address);

    }
    catch (Exception exception)
    {
        if (application != zero)
        {
            if (Application.State != CommunicationState.Faulted)
            {
                application.Close();
            }
            else
            {
                application.Abort();
            }

            application = null;
        }
    }
    finally
    {
        ApplicationInfo = application.get_information();
        Console.WriteLine("Name: {0}, Version:{1}, Type: {2}, language: {3} ", ApplicationInfo.name, ApplicationInfo.version, ApplicationInfo.type, ApplicationInfo.language_name);
    }
    #endregion

    // get active model
    string modelUrl = application.get_active_model();

    // connects to RFEM6/RSTAB9 model
    ModelClient model = new ModelClient(Binding, new EndpointAddress(modelURL));
...


Per una migliore gestione degli errori, un blocco try-catch viene utilizzato in tutto il codice sorgente nella funzione principale. All'interno di questo blocco, l'applicazione è collegata per prima, cosa che viene eseguita di nuovo in un blocco try-catch. Dopo aver collegato correttamente l'applicazione, il modello attualmente attivo (in primo piano in RFEM 6) è collegato tramite il metodo get_active_model. Se si verificano problemi, il blocco try-catch esterno ha effetto.
Poiché tutti i nodi, le linee, le aste e le superfici devono essere analizzati, ha senso ottenere tutti gli elementi da RFEM. Per ottenere il numero dei rispettivi elementi, vengono letti prima i numeri degli oggetti di tutte le categorie. Di seguito, i rispettivi oggetti possono quindi essere trasferiti tramite i loro numeri:


            

...
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);

// get all nodes
Console.WriteLine("1. Get all nodes:");
node[] nds = new node[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("");
...


Per fornire all'utente una panoramica della durata del processo, nella console viene scritto un "+" ogni 10 elementi.
Prima di trasferire altri elementi, le altezze dei piani sono determinate nel passaggio successivo. Innanzitutto, è necessario leggere gli oggetti selezionati. L'array trasferito (campo/vettore compresi gli elementi) è del tipo posizione_oggetto e contiene tutti gli oggetti selezionati con il loro tipo e numero. In questo modo, i nodi possono essere filtrati in un ciclo. Poiché tutti i nodi sono già disponibili, è possibile determinare le coordinate z di questi nodi nello stesso ciclo. C'è un ciclo che attraversa gli oggetti selezionati, e non appena un elemento è di tipo nodo, viene eseguita una ricerca tra i nodi per questo nodo, e quindi la sua coordinata z viene inserita nell'array floor_heights. L'array è esteso di un elemento per ogni coordinata trovata:


            

...
Console.WriteLine("2. Get selected nodes");
//  get all selected objects
object_location[] obj_locs = model.get_all_selected_objects();

//  get all selected node numbers
double[] floor_heights = new double[1];
foreach (object_location obj in 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)
			{
				floor_heights[floor_heights.Length - 1] = nds[i].coordinate_3;
				Array.Resize(ref floor_heights, floor_heights.Length + 1);
				break;
			}
		}
	}
}
Array.Resize(ref floor_heights, floor_heights.Length - 1);

//  sort array
//  z-axis is negative, most positive value is ground
Array.Sort(floor_heights);
Array.Reverse(floor_heights);
//  ground and first level are one, remove first entry
double[] tmp_arr = new double[floor_heights.Length - 1];
Array.Copy(floor_heights, 1, tmp_arr, 0, floor_heights.Length - 1);

floor_heights = zero;
floor_heights = tmp_arr;
...


Poiché le coordinate z non sono nell'ordine corretto, la matrice viene prima ordinata per dimensione. La coordinata z, tuttavia, è nella direzione negativa, e quindi, l'ordinamento deve essere invertito tramite Inverti. Inizialmente, è stato definito che la fondazione e il primo piano contano come un piano, ecco perché il primo elemento nelle altezze dei solai è stato rimosso.
In un passaggio successivo, vengono trasferiti gli elementi rimanenti come linee, aste e superfici:


            

...
//  get all lines
Console.WriteLine("3. Get all lines:");
line[] lns = new line[line_nums.Length];
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("");


// get all members
Console.WriteLine("4. Get all members:");
member[] mems = new member[member_nums.Length];
for (int i = 0; i < member_nums.Length; ++i)
{
	mems[i] = model.get_member(member_nums[i]);
	if ((i % 10) == 0)
		Console.Write("+");
}
Console.WriteLine("");

//  get all surfaces
Console.WriteLine("5. Get all surfaces:");
surface[] srfs = new surface[surface_nums.Length];
for (int i = 0; i < surface_nums.Length; ++i)
{
	srfs[i] = model.get_surface(surface_nums[i]);
	if ((i % 10) == 0)
		Console.Write("+");
}
Console.WriteLine("");
...


Per ordinare gli elementi nei singoli piani, vengono prima creati elenchi bidimensionali. L'elenco nds_floor_numbers ha i piani come prima dimensione e i numeri di nodo del piano come seconda dimensione. Prima di tutto, il seguente ciclo copre tutti i nodi e ha un sotto-ciclo, che copre tutte le altezze dei piani. In questo modo, viene verificato per ogni nodo dal basso verso l'alto nell'edificio se la coordinata Z si trova all'interno dell'altezza del piano. A causa di imprecisioni numeriche, è stata sottratta una tolleranza di 1 mm:


            

...
//  loop through nodes and set their floor
Console.WriteLine("6. Loop through nodes and get their floor");
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]);
			break;
		}
	}
}
...


Non appena un nodo si trova all'interno della regione specificata, il numero del piano viene inserito come commento e il numero del nodo viene aggiunto all'elenco. A questo punto, sarebbe possibile trasferire anche il commento a RFEM (commentato). Il motivo per cui viene utilizzato il commento è perché di seguito non sono le coordinate z che devono essere valutate, ma il numero del piano. Questo è evidente quando si guarda il loop sulle linee:


            

...
//  loop through lines, get their nodes and set their floor
Console.WriteLine("7. Loop through lines and get their floor");
for (int i = 0; i < lns.Length; ++i)
{
	//  get nodes of line
	int[] ln_node_nums = lns[i].definition_nodes;
	Int32 floor_max = 0;
	//  loop through nodes of line
	for (int j = 0; j SMALLERTHAN ln_node_nums.Length; ++j)
	{
		//  loop through nodes
		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 = floor;
					break;
				}
			}
		}

	}
	//  enter maxiumum floor in line
	lns[i].comment = floor_max.ToString();
	lns_floor_numbers[floor_max].Add(lns[i].no);
	//model.set_line(lns[i]);

}
...


Il ciclo passa attraverso tutte le linee e ha un sotto-ciclo, che attraversa tutti i nodi della linea. All'interno di questo sotto-ciclo, c'è un altro ciclo che attraversa tutti i nodi per trovare il nodo che corrisponde al numero di nodo dalla linea. Da questo nodo, viene letto il numero del piano dal commento. Come già accennato nelle basi teoriche, è sufficiente determinare il numero di piani più alto. Questo numero viene quindi memorizzato come numero del piano nel commento della linea e il numero della linea viene inserito nell'elenco.
Le aste ottengono gli stessi numeri dei piani delle linee su cui giacciono. Pertanto, è necessario un ciclo su tutte le aste con un sotto-ciclo su tutte le linee. Non appena il numero della linea corrisponde al numero dell'asta, l'asta ottiene la sua altezza del piano come commento e il numero viene aggiunto all'elenco:


            

...
//  loop through members, get their line and set their floor
Console.WriteLine("8. Loop through members and get their floor");
for (int i = 0; i < mems.Length; ++i)
{
	//  get line number of member
	int mem_ln_num = mems[i].line;

	//  loop through lines
	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;
		}
	}

}
//  loop through surfaces, get their lines and set their floor
Console.WriteLine("9. Loop through surfaces and get their floor");
for (int i = 0; i < srfs.Length; ++i)
{
	//  get lines of surface
	int[] srf_line_nums = srfs[i].boundary_lines;
	Int32 floor_max = 0;
	//  loop through lines of surface
	for (int j = 0; j < srf_line_nums.Length; ++j)
	{
		//  loop through lines
		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 = floor;
					break;
				}
			}
		}

	}
	//  enter maxiumum floor in surface
	srfs[i].comment = floor_max.ToString();
	srfs_floor_numbers[floor_max].Add(srfs[i].no);

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

}
...


La procedura nelle superfici è simile a quella nelle linee. C'è un anello sopra le superfici con il sotto-ciclo sopra le loro linee di contorno. Il piano più alto dalla linea di contorno diventa il numero del piano della superficie.
Dopo aver ordinato tutti gli elementi nei piani, è necessario creare le fasi costruttive. Per ogni piano viene creata una fase costruttiva, quindi viene eseguito un ciclo sulle altezze del piano:


            

...
Console.WriteLine("10. Set construction stages");
try
{
	model.begin_modification("set construction stages");
	//  create construction stages
	for (int i = 0; i < floor_heights.Length; ++i)
	{
		construction_stage cstg = new construction_stage();

		cstg.no = i + 1;
		cstg.continue_on_construction_stage = i;
		cstg.continue_on_construction_stageSpecified = 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 = "floor " + i;
		cstg.user_defined_name_enabled = true;
		cstg.user_defined_name_enabledSpecified = true;

		model.set_construction_stage(cstg);

	}
}
catch (Exception exception)
{
	model.cancel_modification();
	Console.WriteLine("Succede qualcosa durante la creazione della geometria" + eccezione.Message);
	throw;
}
finally
{
	try
	{
		model.finish_modification();
	}
	catch (Exception exception)
	{
		Console.WriteLine("Something happen when finishing creation of geometry" + exception.Message);
		throw;
	}
}
...


Per la creazione di elementi in RFEM, viene utilizzato anche un blocco begin_modification/finish_modification per massimizzare la velocità di trasferimento. Se sorgono problemi, un ulteriore blocco try-catch fornisce protezione, che richiama cancel_modification in caso di interruzione, terminando così correttamente l'elaborazione. Una nota importante sul trasferimento è che gli elementi da trasferire come continue_on_construction_stage possono essere trasferiti solo se la proprietà "Specificata" corrispondente è stata impostata su "true" (continue_on_construction_stageSpecified). Non tutti gli elementi hanno una tale proprietà "Specificata", ma dove è disponibile, deve essere presa in considerazione.

Conclusioni

Grazie alla versatilità dell'interfaccia WebService e API, è possibile automatizzare una serie di voci che richiederebbero molto tempo se eseguite manualmente. Nel presente esempio, l'input per l'add-on Analisi delle fasi costruttive è automatizzato, il quale funge anche da segnaposto per molti altri add-on e opzioni. Quindi, ci sono solo pochi limiti alla tua creatività per soluzioni che fanno risparmiare tempo, e seguiranno altri esempi.


Autore

Il signor Günthel fornisce supporto tecnico per i clienti di Dlubal Software e si prende cura delle loro richieste.

Download