piątek, 13 lutego 2009

Implementacja serwera OPC w C#

W poprzedniej części zasygnalizowałem co należy zrobić by stworzyć w C# (na platformie .NET) klienta OPC, nie były to proste zagadnienia, niestety stworzenie serwera niesie za sobą jeszcze większe wymagania.

W tym przypadku należy stworzyć i zarejestrować odpowiedni komponent jako obiekt COM. Zagadnienie to opisane było w części .NET i COM, dlatego nie będę go teraz dalej rozwijał. Niestety to nie wystarczy, gdyż okazuje się, że nie możliwa jest implementacja w pełni zgodnego ze specyfikacją serwera OPC wykorzystując tylko rozwiązania oparte o platformę .NET.

Cóż można więc zrobić? Należy wykorzystać tak zwany serwer wrapper, czyli element pośredniczący. Server Wrapper należy przygotować tak by był to plik wykonywalny i który po rejestracji jako obiekt COM/DCOM udostępnia wszystkie interfejsy wymagane przez standard OPC i jego specyfikacje. Serwer wrapper nie musi realizować żadnej funkcjonalności poza połączeniem się do obiektu COM, który jest plikiem DLL i realizuje całą funkcjonalność serwera, ten plik DLL może już być przygotowany w C#, w oparciu o platformę .NET.

Opisywane tutaj rozwiązanie przedstawia rysunek poniżej:

Na rysunku widać jak "OPC Wrapper.exe" łączy się poprzez COM i wykorzystuje interfejsy zbliżone do tych ze specyfikacji OPC, by wykorzystać implementację serwera OPC występującego jako plik DLL (skompilowany na platformie .NET). "OPC Wrapper.exe" natomiast jest OPC serwerem, do którego dostęp mają klienci OPC i implementuje wszystkie potrzebne do tego interfejsy.

Brzmi to dość skomplikowanie, pytanie więc czy można w ten sposób zrobić OPC serwer w pełni zgodny ze specyfikacją? Okazuje się że tak, właśnie taką konstrukcję wybrało m.in. OPC Foundation do przygotowania swojego pakietu SDK wspierającego tworzenie oprogramowania zgodnego z OPC.

Kolejne pytanie może brzmieć: czy jest to rozwiązanie odpowiednio wydajne? Z doświadczenia mogę powiedzieć, że tak. A dodatkowa komunikacja odbywająca się wewnątrz komputera ma mały wpływ na wydajność całego serwera.

czwartek, 12 lutego 2009

Implementacja klienta OPC w C#

Po wcześniej przytoczonej teorii (COM , DCOM , .NET , Marshalling , .NET i COM) chyba każdy już wie od czego zacząć, by napisać klienta OPC w C#. Niestety nie obędzie się tutaj bez znajomości specyfikacji. Dodatkowo potrzebne będzie spore doświadczenie w korzystaniu z .NET Platform Invocation Services (czyli m.in marshalling'u i umiejętność konwersji definicji wywołań bibliotek kodu niezarządzalnego na definicję języka C#).

Spójrzmy na definicję przykładowej metody AddItems (z interfejsu OPC Item Management: "IOPCItemMgt"):

HRESULT AddItems(
[in] DWORD dwCount,
[in, size_is( dwCount)] OPCITEMDEF * pItemArray,
[out, size_is(,dwCount)] OPCITEMRESULT ** ppAddResults,
[out, size_is(,dwCount)] HRESULT ** ppErrors );

Ta definicja napisana w C# będzie wyglądała następująco:

int AddItems(
[In] int dwCount,
[In] IntPtr pItemArray,
[Out] out IntPtr ppAddResults,
[Out] out IntPtr ppErrors );

Oczywiście postępując w ten sposób i deklarując parametry jako IntPtr, tracimy wiele informacji. Kolejna sprawa, to standardowe mapowanie błędów zwracanych przez obiekt COM (jako HRESULT), które są konwertowane przez mechanizm marszalingu .NET (.NET marshaller) na wyjątek: COMException. Dodatkowo niektóre metody dostępne poprzez COM zwracają kody wyników: zwykle S_OK (ale czasami inne, np. S_FALSE). Z domyślnym mapowaniem, ta informacja może zostać zagubiona. Aby ominąć te problemy można zadeklarować funkcję interfejsu korzystając ze specjalnego atrybutu PreserveSig . Spójrzcie na kod poniżej:

 [ComVisible(true), ComImport,
Guid("39c13a54-011e-11d0-9675-0020afd8adb3"),
InterfaceType( ComInterfaceType.InterfaceIsIUnknown )]
internal interface IOPCItemMgt
{
[PreserveSig]
int AddItems(
[In] int dwCount,
[In] IntPtr pItemArray,
[Out] out IntPtr ppAddResults,
[Out] out IntPtr ppErrors );
...

W tej części będzie to już wszystko. Dla tych, którzy chcą rozszerzyć opisane tutaj zagadnienia, polecam artykuł: http://www.codeproject.com/KB/COM/opcdotnet.aspx

środa, 11 lutego 2009

Specyfikacje OPC Unified Architecture (OPC UA) wreszcie opublikowane jako skończone

Ostatnio (9 lutego 2008) na stronach OPC Foundation, wreszcie pojawiły się ostateczne wersje specyfikacji OPC Unified Architecture (OPC UA). Aktualnie opublikowane zostały części od 1 do 8, specyfikacje mają status „release”, czyli już nie powinny się zmienić lub mogą zmienić się nieznacznie):

Prace nad specyfikacją trwały od 2004 i było w nie zaangażowanych wiele film (w tym jedna z Polski: CAS). Fakt, że te specyfikacje otrzymały wreszcie status zakończonych otwiera drogę dla producentów oprogramowania zgodnego z nową technologią. Wraz ze specyfikacjami opublikowana została kolejna (stabilna) wersja pakietu SDK ułatwiająca tworzenie zgodne z technologią OPC Unified Architecture.

Pracę nad tą technologią nadal trwają (aktualnie zakończona została pierwsza faza prac) i m.in. do opublikowania zostały:

Part 9 - Alarms and Conditions

Part 10 - Programs

Part 11 - Historical Access

Part 12 – Discovery

Ponadto w grupach roboczych trwają prace nad wykorzystaniem tej technologii, jako standardowego sposobu dostępu do urządzeń (grupa: DI – Device interface) oraz analizatorów (grupa ADI – Analyser Device Interface).

OPC Foundation, gratulacje zakończenia pierwszej fazy prac nad OPC UA i powodzenia w dalszych pracach. Dla osób pragnących rozszerzyć swoją wiedzę na temat OPC UA polecam artykuły:

wtorek, 10 lutego 2009

.NET i COM

Wcześniej opisałem już wspomniane w tytule technologie i wskazałem problemy i potencjalne rozwiązania używane podczas tworzenia oprogramowania, w którym należy wykorzystać komunikację pomiędzy kodem zarządzalny i niezarządzalnym. W przypadku interakcji komponentu COM i kodu powstałego w oparciu o technologię .NET należy poruszyć jeszcze dwie sprawy:

  • jak dostać się do obiektu COM z poziomu .NET Framework
  • jak udostępnić obiekt .NET Framework poprzez COM

Z pierwszą sytuacją mamy do czynienia, gdy chcemy napisać aplikację, która realizuje funkcjonalność klienta COM (np. klient OPC). Jest to prosty przypadek wystarczy zaimportować odpowiedni interfejs dostępny poprzez COM.

W tym celu definicję odpowiedniego interfejsu w języku C# poprzedzamy odpowiednimi atrybutami:

 [ComVisible(true), ComImport,
Guid("/*odpowiedni guid*/"),
InterfaceType( ComInterfaceType.InterfaceIsIUnknown )]

Interfejs powinien mieć definicję wszystkich potrzebnych funkcji. Zwykle informacje na temat, co to za funkcje znajdziemy w odpowiedniej specyfikacji. Do ich definicji następnie można już wykorzystać zasady marshallingu opisanego w poprzedniej części.

Oprócz definicji interfejsów należy zaimportować jeszcze pewne funkcje, np.:

CoCreateInstanceEx z biblioteki ole32.dll:

      [DllImport("ole32.dll")]
      private static extern void CoCreateInstanceEx(
          ref Guid         clsid,
          [MarshalAs(UnmanagedType.IUnknown)]
          object           punkOuter,
          uint             dwClsCtx,
          [In]
          ref COSERVERINFO pServerInfo,
          uint             dwCount,
          [In, Out]
          MULTI_QI[]       pResults);

Ewentualnie można również wykorzystać metodę CreateComInstanceFrom z klasy System.Activator.

Odrobinę trudniejsze jest udostępnienie komponentu napisanego w C# poprzez mechanizm COM (z takim przypadkiem mamy do czynienia chcąc napisać serwer OPC). W tym celu należy:

  1. Wybrać typy i procedury które mają być dostępne poprzez COM. Wszystkie one muszą być publiczne, typy (klasy) muszą mieć publiczny domyślny konstruktor (który jest jedynym konstruktorem, który może być wywołany poprzez COM).
  2. Ustawić odpowiednie atrybuty, które rozszerzą przenośność komponentu.
  3. Zarejestrować assembly wśród komponentów typu COM. Czynność ta może być wykonana w momencie tworzenia komponentu, instalacji lub później przy użyciu narzędzia Regasm.exe

Ciekawy artykuł na ten temat (rozwijające zagadnienia wspomniane w powyższych krokach) dostępny na stronach MSDN to: "Exposing .NET Framework Components to COM" (http://msdn.microsoft.com/en-us/library/zsfww439(VS.80).aspx).

poniedziałek, 9 lutego 2009

.NET Platform Invocation Services

Wiele tradycyjnie tworzonych bibliotek (w tym biblioteki wchodzące w skład dostarczanego przez OPC Foundation: "OPC Core Components") stworzone zostały w tradycyjnych językach programowania jak C lub C++ i skompilowane są do kodu maszynowego. Aby wykorzystać stworzoną w ten sposób bibliotekę potrzebne są odpowiednie pliki nagłówkowe (zwykle dostarczone przez producenta biblioteki), które dodawane są do kodu źródłowego tworzonej aplikacji i które powodują wywołanie odpowiednich procedur z biblioteki. Wykorzystanie takich bibliotek w programie napisanym w języku C++ jest dość proste i wystarczy bazować na dołączonych plikach. Inaczej wygląda sprawa wykorzystywania takich biblioteki w zarządzanym kodzie, jakim są programy napisane w języku C#. W takim przypadku nie wystarczy dołączyć odpowiednich plików nagłówkowych (trzeba ew. napisać własne odpowiedniki pewnych plików), ponadto trzeba wykonać pewne dodatkowe zabiegi zapewniające współpracę kodu zarządzanego i niezarządzanego. Problemy te rozwiązuje się przy pomocy .NET Platform Invocation Services, która to usługa pozwala wywoływanie procedur z bibliotek DLL lub komponentów COM (które nie są skompilowane do kodu zarządzanego). Dla każdej wykorzystywanej funkcji z biblioteki należy napisać odpowiednią deklarację. Przykładowo dla funkcji systemu Windows odczytującej wartość licznika procesora, która oryginalnie jest w postaci:

BOOL QueryPerformanceCounter( LARGE_INTEGER *lpPerformanceCount );

napiszemy:

[DllImport("kernel32")] public static extern bool QueryPerformanceCounter(ref Int64 lpPerformanceCount);

Przy pisaniu nowych deklaracji można zauważyć wiele prawidłowości np. *zmienna najczęściej zamieniamy to na IntPtr zmienna (wyjątek stanowi char *zmienna – gdyż to zamieniamy na string lub StringBuilder), **zmienna – zamieniamy na ref IntPtr. Klasa IntPtr służy najczęściej do wskazywania miejsca w niezarządzanym obszarze pamięci. Jeżeli istnieje potrzeba to można zaalokować sobie pamięć w obszarze niezarządzanym, robi się to funkcją Marshal.AllocCoTaskMem(int length) (wykorzystywane jest to np. przy tworzeniu pakietów do wysłania). W zaalokowanym obszarze można pisać i czytać (np. Marshal.WriteByte(pointer, byte)) W przypadku gdyby zaistniał problem przeniesienia struktury lub klasy z obszaru niezarządzanego do zarządzanego, można wykonać to funkcją PtrToStructure(pointer,structure). Ponieważ wykorzystywane tutaj mechanizmy bazują na klasach z przestrzeni nazw: "Marshall", dlatego często do określenia takiego podejścia jest używany termin marszaling (ang. "marshalling").

piątek, 6 lutego 2009

Platforma .NET Framework

Platforma .NET jest dość nową technologią opracowaną przez Microsoft i opiera się na mechanizmie opartym o uruchamianie skompilowanego kodu w czymś podobnym do maszyny wirtualnej. Microsoft nazywa środowisko uruchomieniowe: Common Language Runtime (w skrócie CLR). Środowisko CLR kompiluje i wykonuje zapisany w standardowym języku pośrednim Microsoft (MSIL) kod aplikacji zwany kodem zarządzanym (ang. managed code), zapewniając wszystkie podstawowe funkcje konieczne do działania aplikacji.

Mhmm, jakkolwiek skomplikowanie to brzmi, oznacza to, że sposób w jaki uruchamiane są aplikacje bazujące na platformie .NET jest odmienne od tego, jak uruchamiane są aplikacje kompilowane do kodu maszynowego.

Odmienny sposób uruchamiania aplikacji bazujących na .NET Framework niesie za sobą wiele zalet. Programista może przenieść na system operacyjny problemy związane z zarządzaniem pamięcią (niepotrzebne obiekty, zajmujące pamięć, usunie automatycznie tzw. Garbage Collector). .NET Framework zapewnia bogatą bibliotekę komponentów, ułatwiające pisanie aplikacji. Docelowy plik wykonywalny jest zwykle mniejszy od pliku wygenerowanego tradycyjnym sposobem. Języki wykorzystywane do pisania programów na platformę .NET (np. coraz bardziej popularny C#) zostały tak opracowane, by wiele błędów można było wykryć już na poziomie kompilacji. System operacyjny może odizolować uruchomioną aplikację (dla aplikacji tworzona jest tzw. domena aplikacji, dla której można konfigurować odpowiednie zabezpieczenia, poziom izolacji, itp...). Ciekawy jest też nowy sposób zdalnego wywoływania procedur, Microsoft nazwał go ".NET Remoting" oraz jego następca "Windows Communication Foundation" (WCF) . Przewidziane jest kilka sposobów przekazywania danych, m.in: przesyłane binarne (strumienie oparte o protokół TCP), strumienie XML, binarne potoki (zwane w języku ang. "named pipes").

Poza wspomnianymi zaletami niestety pojawiają się również pewne problemy, zwłaszcza w przypadku komunikacji na granicy "kod niezarządzalny" (ang. "unmanaged code" - czyli aplikacje lub biblioteki powstałe w tradycyjny sposób i skompilowane do kodu maszynowaego), a "kod zarządzalny" (ang. "managed code", czyli skompilowany do MSIL). W tym przypadku dostępne są następujące możliwości:

  • wykorzystanie technologii COM,
  • zaimportowanie odpowiedniego pliku DLL i wykorzystanie tzw. .NET Platform Invocation Services

W kolejnych częściach opiszę jak można te dwie możliwości wykorzystać.

czwartek, 5 lutego 2009

Technologie, na których bazuje OPC Classic. Część 2: Technologia DCOM.

Technologia DCOM jest rozszerzeniem technologii COM (opisanej wcześniej), umożliwiającym wymianę danych za pośrednictwem sieci, podczas gdy COM dotyczył jedynie komunikacji na lokalnym komputerze. DCOM zastępuje protokołem sieciowym lokalną komunikacje między procesami, korzystając z technologii DCE RPC (Distributed Computing Environment / Remote Procedure Call). W rzeczywistości z punktu widzenia klienta bądź serwera nie ma różnicy, czy druga strona znajduje się na tej samej maszynie czy zdalnie.
Komunikacja realizowana jest to za pośrednictwem tzw. RPC stubs oraz obiektów Proxy, istniejących po obu stronach. Przykładowa komunikacja wygląda następująco:
  1. Klient wywołuje lokalnie pewną funkcję.
  2. Proxy przejmuje wywołanie i tworzy wiadomość do przesłania przez sieć.
  3. Wiadomość jest przesyłana.
  4. System operacyjny serwera otrzymuje wiadomość i przekazuje ją do stub'a serwera. Stub rozpakowuje wiadomość i lokalnie wywołuje funkcję na serwerze.
Aby umożliwić interakcję zdalnych aplikacji za pośrednictwem DCOM należy skonfigurować DCOM na każdym komputerze, np. przy pomocy programu DCOMCNFG.

środa, 4 lutego 2009

Technologie, na których bazuje OPC Classic. Część 1: Technologia COM.

OPC Classic, czyli OPC bazujące na aktualnie wykorzystywanych i opublikowanych specyfikacjach, do transportu i wymiany danych wykorzystuje technologię DCOM. DCOM jest technologią opracowaną przez Microsoft, która służy do zdalnego wywoływania procedur pomiędzy różnymi komputerami. DCOM jako skrót oznacza Distributed COM (Distributed Component Object Model).

COM, jest opracowaną przez Microsoft technologią umożliwiającą efektywną komunikację między aplikacjami. COM definiuje komponenty programowe niezależne od języka programowania, co umożliwia włączanie do tworzonych aplikacji elementów należących do innych programów i wymianę danych między poszczególnymi obiektami za pomocą tzw. interfejsów.

W następnej części opisana zostanie technologia DCOM.

wtorek, 3 lutego 2009

OPC, DCOM, .NET i C# w jednym stali domu

Przeglądałem ostatnio zapytania, które są wpisywane do wyszukiwarki Google, a powodują, że ktoś trafia na tego bloga. Jeden z tematów to "C# i DCOM". Chciałby wyjść na przeciw zainteresowaniom czytelników i pokazać jak można połączyć te dwa elementy. Ponieważ na co dzień zajmuję się technologią OPC, więc właśnie o tej technologii dotyczyć będzie ten artykuł ten artykuł.

Artykuł omawia różnice pomiędzy kodem zarządzalnym i niezarządzalnym oraz sposoby komunikacji między nimi. Tekst jest podzielony na części i po teoretycznym wstępie przedstawione są przykłady wywołań między-platformowych. Artykuł opisuje również możliwości tworzenia i obsługi elementów COM/DCOM z poziomu platformy .NET, a kończy się przykładami opisującymi możliwości implementacji technologii OPC na platformie .NET.

W celu ułatwienia czytania, artykuł został podzielony na następujące części:

  1. Technologie, na których bazuje OPC Classic. Część 1: Technologia COM.
  2. Technologie, na których bazuje OPC Classic. Część 2: Technologia DCOM.
  3. Platforma .NET Framework
  4. .NET Platform Invocation Services
  5. .NET i COM
  6. Implementacja klienta OPC w C#
  7. Implementacja serwera OPC w C#
  8. Podsumowanie

Zapraszam do lektury.

poniedziałek, 2 lutego 2009

Blog jednego z twórców OPC Unified Architecture (OPC UA) - Randy'iego Armstrong'a

Niedawno jeden z twórców standardu OPC Unified Architecture i pakietu SDK poświęconego tej technologii: Randy Armstrong, rozpoczął tworzenie bloga kierowanego do inżynierów i programistów chcących wykorzystywać technologię OPC Unified Architecture (OPC UA). Blog jest dostępny pod adresem:

http://lists.opcfoundation.org/RandyBlog/default.aspx

Zgodnie z tym co napisał w swym pierwszym post'cie, Randy Armstrong deklaruje, że jego blog będzie poświęcony podstawom implementacji technologii OPC UA. Jego posty będą pokrywały rożne techniczne tematy i zapewnią lepszy start prac nad oprogramowaniem wspierającym technologię OPC UA, bez konieczności przeczytania wszystkich specyfikacji "od deski do deski". Ponadto uzupełnieniem bloga będzie przykładowy kod, który nie będzie oparty o pakiet OPC UA SDK (dostarczany przez OPC Foundation), a będzie dostępny dla wszystkich członków OPC Foundation.

Zachęcam wszystkich do lektury.

Posty powiązane / Related posts