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 utilizzare i 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 di esse, 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.
Integratore
Il programma utilizza un modello dal pacchetto NuGet Dlubal.RFEMWebServiceLibrary come base. Qui puoi scoprire come installarlo:
https://github.com/Dlubal-Software/Dlubal_CSharp_ClientInnanzitutto, il modello attivo è collegato come segue:
- code.csharp#
...
- 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));
...
- /codice#
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 che l'applicazione è stata collegata correttamente, 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:
- code.csharp#
...
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("");
...
- /codice#
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:
- code.csharp#
...
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;
...
- /codice#
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 utilizzando Inverti. Inizialmente, è stato definito che la fondazione e il primo piano contano come un unico piano; questo è il motivo per cui il primo elemento nelle altezze dei solai è stato rimosso.
Successivamente, vengono trasferiti gli elementi rimanenti come linee, aste e superfici:
- code.csharp#
...
// 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("");
...
- /codice#
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. Per ogni nodo, il programma controlla dalla parte inferiore dell'edificio verso l'alto se la coordinata Z si trova all'interno dell'altezza del piano. A causa di imprecisioni numeriche, è stata sottratta una tolleranza di 1 mm:
- code.csharp#
...
// 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;
}
}
}
...
- /codice#
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 commento è utilizzato qui perché il numero del piano, ma non le coordinate z, deve essere valutato nel testo seguente.
Questo è evidente quando si guarda il loop sulle linee:
- code.csharp#
...
// 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]);
}
...
- /codice#
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:
- code.csharp#
...
// 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]);
}
...
- /codice#
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:
- code.csharp#
...
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;
}
}
...
- /codice#
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.
Conclusione
Grazie alla versatilità dell'interfaccia WebService e API, è possibile automatizzare manualmente una serie di voci complesse. 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.