Na Start
Bim Vision to Polska przeglądarka plików IFC firmy DataComp. Pisałem już o niej w artykule Format IFC, czyli wprowadzenie do BIM, więc gorąco zachęcam do zapoznania się z artykułem. Wiele firm obecnie używa różnych przeglądarek BIM, my mając patriotyczne podejście do gospodarki w pierwszym artykule pokażemy, w jaki sposób stworzyć własną wtyczkę do BIM VISION. Pisanie wtyczki odbędzie się w środowisku .Net w popularnym programie wśród programistów Visual Studio 2019 Community. Językiem wykorzystanym przy tworzeniu plugina będzie C#.
Przygotowanie
Krok pierwszy, absolutnie podstawowy, to instalacja programów Visual Studio oraz Bim Vision. Podejrzewam, że masz już oba narzędzia, więc ściągamy SDK dla BimVision ze strony: https://bimvision.eu/pobierz-pl/sdk/
Kolejnym krokiem jest wypakowanie pobranej paczki *.zip w dowolne miejsce w komputerze. Nie muszę przypominać, że powinniśmy znać lokalizację wypakowanych plików :), ja używam pulpitu dla projektów, nad którymi pracuję, dlatego stworzę tu katalog „BimVision Tutorial”.
Struktura wypakowanych plików wygląda nastęująco:
- bim_vision_exe – zawiera najnowszą wersję aplikacji
- doc – dokumentacja biblioteki
- examples – przykłady w c++
- examples .NET – przykłady w c#
- sdk – biblioteka w c++
- sdk .NET – biblioteka w C#
- setup – template do instalatora „Inno Setup” użyjemy dla projektów komercyjnych
- readme.txt – producent prosi o zapoznanie się z tym plikiem.
Rozpoczynamy
Uruchamiamy Visual Studio i tworzymy nowy projekt w katalogu, gdzie wypakowaliśmy nasz SDK – najlepiej jest później kompilować. Tworzymy projekt w oparciu o framework 4.7 lub wyżej i wybieramy typ projektu jako „Class library”.
Pierwszy krok to zaczytanie biblioteki Bim vision oraz zmiana nazwy klasy głównej. W celu załadowania klasy SDK klikamy w eksploratorze projektu na nasz projekt prawym i wybieramy ADD->Exsiting item.
Teraz ładujemy bibliotekę jako link w taki sposób:
Teraz możemy dodać klasę „Plugin”, po której będziemy dziedziczyli.
using BIMVision; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace first_ext_bv { public class PoradnikInzyniera : Plugin { public override void GetPluginInfo(ref PluginInfo info) { throw new NotImplementedException(); } public override byte[] GetPluginKey() { throw new NotImplementedException(); } public override void OnCallLimit() { throw new NotImplementedException(); } public override void OnLoad(PLUGIN_ID pid, bool registered, IntPtr viewerHwnd) { throw new NotImplementedException(); } } }
Interfejs wymaga konfiguracji. Oto przykładowa konfiguracja naszej wtyczki.
public class PoradnikInzyniera : Plugin { // Api wrapper - obiekt do połączenia z BIM VISION. private ApiWrapper api; public override void GetPluginInfo(ref PluginInfo info) { info.name = "Pierwszy plugin"; info.producer = "Michał Wrochna"; info.www = "www.poradnikinzyniera.pl"; info.email = "michal.wrochna@poradnikinzyniera.pl"; info.description = "Przykład pierwszego pluginu"; } public override byte[] GetPluginKey() { return null; } public override void OnCallLimit() { //Metoda przekroczenia limitu 100 operacji dla wersji demo } public override void OnLoad(PLUGIN_ID pid, bool registered, IntPtr viewerHwnd) { api = new ApiWrapper(pid); } }
Konfiguracja projektu
Wygląda na łatwiznę. Teraz przygotujemy konfigurację projektu. W tym celu otwieramy właściwości projektu z okna inspektora PPM->Properties.
Po pierwsze przechodzimy na zakładkę Build i ustawiamy platformę (x86 lub x64) oraz ścieżkę do kompilacji projektu. Jeżeli projekt znajduje się w tym samym katalogu co nasze SDK – ścieżka może być relatywna, jeżeli nie, ustawiamy ścieżkę lokalną. Ja na potrzeby poradnika użyję procesora 32 bit i podam ścieżkę do folderu z plugin-ami x86 Bim Vision.
Przejdziemy do katalogu z plugin-ami (uwaga na właściwą wersję x86 i x64) i utworzymy nowy folder, w którym będziemy kompilować nasz projekt. Żeby wszystko działało, utworzymy nowy plik o tej samej nazwie co katalog z rozszerzeniem *.plg. Ważne: plik ma być pusty, wychodzimy więc z nowego pliku tekstowego. Nazwa folderu musi się pokrywać z nazwą biblioteki, którą tworzymy (pliku .dll).
Nasza konfiguracja wygląda w ten sposób:
Ustawiona platforma kompilacji
Proszę zwrócić uwagę na „Platform Target” i „Output path”.
Jeżeli Visual Studio nie widzi programu, można ustawić ścieżkę przez przycisk „Browse…”. Pamiętajmy, że wersja Bim Vision (x86, x64), jaką uruchamiamy podczas kompilacji, musi się pokrywać z platformą ustawioną powyżej.
Ostatnim etapem konfiguracji jest uruchomienie managera pakietów nuget i instalacja pakietu „UnmanagedExports”.
Teraz możemy odpalić debugger i zobaczyć nasz pierwszy plugin.
Wszystko wygląda w porządku.
Tworzymy przykładowe okienko
Kończąc pierwszą część stworzymy jeszcze proste okienko, które wyświetli ID zaznaczonego elementu. Dlatego, że tworzymy bibliotekę, do referencji naszego projektu musimy dodać bibliotekę System.Windows.Forms. Następnie tworzymy z pozycji inspektora (PPM->Add->Windows Form…) nowe okno o nazwie GetID.
Wstawiamy label i textbox. Nie konfigurujemy nic, ponieważ nie ma takiej potrzeby. Nie zmieniamy też nazw, ponieważ to tylko projekt poglądowy.
Przechodzimy teraz do kodu. Po pierwsze do konstruktora okna przekazujemy nasz API Wrapper. Dzięki temu będziemy mieli dostęp do API z poziomu okienka. Możemy to zrobić również przez stworzenia instancji typu property. Będzie wtedy bardziej elegancko. Zresztą pokaże obie opcje.
//Tak - elegancko public ApiWrapper api { get; set; } //lub tak - też dobrze ApiWrapper api; public GetID(ApiWrapper api) { this.api = api; InitializeComponent(); }
Zaletą przekazania do konstruktora będzie dostęp do API od utworzenia instancji naszego okna.
Pobieranie danych z modelu
Teraz obsłużymy zdarzenie „onSelectionChange” w naszym API. Zdarzenie wywołuje się zawsze, gdy zaznaczymy\odznaczymy element w modelu IFC.
My potrzebujemy pobrać wszystkie adresy GUID zaznaczonych elementów. Stworzymy więc pustą tablicę OBJECT_ID[] o nazwie Selected_IDs. Na koniec pobieramy wszystkie ID metodą GetSelected();
using BIMVision; 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; namespace first_ext_bv { public partial class GetID : Form { OBJECT_ID[] Selected_IDs; ApiWrapper api; public GetID(ApiWrapper api) { this.api = api; InitializeComponent(); this.api.OnSelectionChange(OnSelectionChange); FormClosing += GetID_FormClosing; } private void GetID_FormClosing(object sender, FormClosingEventArgs e) { e.Cancel = true; this.Hide(); } void OnSelectionChange() { Selected_IDs = this.api.GetSelected(); } } }
Wracamy do klasy głównej pluginu. Przedtem dodamy obsługę zamknięcia okna. Chcemy, żeby okno nie zostało „zabijane”, a jedynie chowane. Dodajemy więc wpis tj. w linii 24 oraz tj. w liniach 27-31. Teraz zaprogramujemy wstążkę i jakiś przycisk. Ponieważ nie mamy wyzwalacza naszego okna i nie możemy wywołać go z poziomu programu Bim Vision.
Tworzenie nowego przycisku
Na początek tworzymy w dowolnym programie ikonę o rozmiarze 32×32 px. Ja na potrzeby przykładu pobiorę ją z darmowego repozytorium, aby pobrać kliknij tu. W folderze głównym naszej kompilacji tworzymy katalog „icons” i wgrywam tam pobrany plik.
Teraz przechodzimy do kodu i dodajemy przycisk.
using BIMVision; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; namespace first_ext_bv { public class PoradnikInzyniera : Plugin { GetID getIDWindow; // Nowa instancja naszego okna List<int> buttons; // lista przycisków string DLL_DIR = ""; // Api wrapper - obiekt do połączenia z BIM VISION. private ApiWrapper api; public override void GetPluginInfo(ref PluginInfo info) { info.name = "Pierwszy plugin"; info.producer = "Michał Wrochna"; info.www = "www.poradnikinzyniera.pl"; info.email = "michal.wrochna@poradnikinzyniera.pl"; info.description = "Przykład pierwszego pluginu"; } public override byte[] GetPluginKey() { return null; } public override void OnCallLimit() { //Metoda przekroczenia limitu 100 operacji dla wersji demo } public override void OnLoad(PLUGIN_ID pid, bool registered, IntPtr viewerHwnd) { api = new ApiWrapper(pid); //Tworzy kolekcję przycisków buttons = new List<int>(); //Pobieramy lokalizację naszego pluginu DLL_DIR = api.GetDLLDirectory(); //Stworzenie nowej zakładki var PI_TabID = api.CreateTab("Poradnik inżyniera"); //Stworzenie nowej grupy w zakładnce var Tools_ID = api.CreateGroup(PI_TabID, "Narzędzia"); //Stworzenie przycisku w grupie CreateButton(Tools_ID, "Uruchom okno", "To jest opis przycisku", @"\icons\button1.png", onClickUruchom_Okno); } void CreateButton(int group, string name, string tooltip, string image, Api.callback_fun Callback) { var button1 = api.CreateButton(group, Callback); //Tworzy przycisk api.SetButtonText(button1, name, tooltip); //Tworzy nazwę i dymek api.SetButtonImage(button1, DLL_DIR + image); //Tworzy ikonę buttons.Add(button1); } //Funkcja obsługująca zdarzenie kliknięcia w przycisk void onClickUruchom_Okno() { if(getIDWindow == null) { getIDWindow = new GetID(api); // tworzymy nowe okno. getIDWindow.Show(); //pokazujemy okno } else { getIDWindow.Show(); //pokazujemy okno } } } }
W linii 13 definiujemy zmienną przechowującą nasze okno, w kolejnej definiujemy kolekcję przycisków, a w następnej definiujemy zmienną, która będzie przechowywać ścieżkę globalną do wtyczki.
W linii 45 pobieramy ścieżkę do naszej biblioteki. Ścieżka ta pomoże lokalizować pliki zewnętrzne. W linii 47 tworzymy zakładkę na wstążce, dalej tworzymy nową grupę a na koniec w linii 51 uruchamiamy metodę, która tworzy nowy przycisk.
Metoda, która tworzy przycisk zawiera parametry:
- group – typ integer – ID grupy którą utworzyliśmy,
- name – typ string – nazwa tworzonego przycisku,
- tooltip – typ string – dymek z opisem funkcji,
- image – typ string – relatywna ścieżka do pliku ikony,
- Callback – typ void – metoda handler wywoływana po kliknięciu w przycisk
Ciało metody zawiera funkcję utworzenia przycisku, ustalenia opisów i ikony. Na koniec id przycisku zapisywane jest do kolekcji.
Powrót do okna
Teraz, gdy nasze okno się już wyświetla, możemy pobrać dane z zaznaczonych obiektów do textboxa. W tym celu do metody „OnSelectionChange” dodajemy kod.
void OnSelectionChange() { Selected_IDs = this.api.GetSelected(); var idsToTxt = ""; //tekst do wyświetlenia if (Selected_IDs != null) idsToTxt = Selected_IDs.Select(x => x.id.ToString() + "; ").Aggregate((y, z) => y + z); //Przekształcenie id's na tekst textBox1.Text = idsToTxt; }
Uruchomienie
Nadeszła wreszcie chwila pierwszego uruchomienia. Sprawdzimy czy wszystko działa jak należy. Na początek przechodzimy na utworzoną przez nas zakładkę, po kliknięciu wyświetla się nasza grupa, a w niej nasz przycisk. Klikamy w niego i jeżeli nic się nie wysypuje, możemy załadować model.
Jak widać nasz pierwszy plugin działa wzorowo. Na tym więc zakończymy ten artykuł.
Podsumowanie
W niniejszym artykule pokazałem, w jaki sposób prosto możemy wyciągać dane z modelu. Tworzenie wtyczek do popularnych programów automatyzuje pracę. Nie trzeba więc zatrudniać dodatkowych osób do analizy danych. Możliwości są nieograniczone, możemy zaczytywać dane do excela, wysyłać przez sieć, tworzyć bazę danych, linkować pliki, stworzyć dokumentację powykonawczą, jak i wykonawczą w wersji cyfrowej. To jest przyszłość, a pomysłów jest cała masa. Zapraszam do pisania w komentarzach, jak Wy wykorzystalibyście w Waszej firmie tę umiejętność.