czwartek, 30 kwietnia 2009

Wymiana danych: OPC <-> DDE <-> Excel

Ms Excel jest chyba najbardziej popularnym w Polsce arkuszem kalkulacyjnym. OPC jest popularnym standardem komunikacyjnym w automatyce przemysłowej. Jeżeli ktoś zajmuje się integracją procesu przemysłowego z aplikacjami warstwy biurowo/biznesowej, na pewno stanie w pewnym momencie przed koniecznością przeniesienia danych z procesu do Excel'a lub odwrotnie. Oczywiście sposobów rozwiązania takiego zagadnienia jest wiele, ja chciałbym pokazać rozwiązanie oparte o serwer OPC dla Excela (na przykładzie oprogramowania CommServer). W przedstawianym rozwiązaniu wykorzystywana jest technologia DDE do komunikacji pomiędzy Excel'em a OPC serwerem.

Film jest na prawdę krótki (poniżej 10 min.), co pokazuje tak naprawdę prostotę tego rozwiązania.

Zapraszam do oglądania:

środa, 29 kwietnia 2009

OPC: instalacja, uruchomienie i konfiguracja komunikacji pomiędzy dwoma komputerami

OPC jest przemysłowym standardem komunikacji między urządzeniami przemysłowymi, przez co pozwala uniezależnić oprogramowanie monitorujące i sterujące od producentów sprzętu.

Do wielu zalet technologii OPC można zaliczyć m.in.:

  • standaryzację komunikacji i wymiany danych przemysłowych,
  • dużą uniwersalność i skalowalność rozwiązań,
  • znaczne obniżenie kosztów integracji dużych systemów przemysłowych

W tym artykule chciałbym pokazać praktyczne możliwości tej technologii, na przykładzie oprogramowania CommServer. Prezentowany materiał będzie głównie w formie filmowej, co mam nadzieję ułatwi zapoznanie się z tą technologią.

Instalacja serwera OPC i podłączenie się do niego klientem OPC

Film ten na przykładzie serwera OPC: CommServer pokazuje wymagane czynności przed-instalacyjne, instalację serwera OPC, podłączenie klienta OPC do serwera, odczyt danych z serwera.

Gdyby kogoś zainteresowała tematyka z filmu i chciałby samodzielnie wykonać podobne ćwiczenie, to zapraszam do przeczytania artykułu znajdującego się tutaj: http://www.commsvr.com/Howitworks/Howto/CommServergettingstarted.aspx

OPC i komunikacja pomiędzy dwoma komputerami

Film - samouczek, który pokazuje jak przetestować komunikację klienta i serwera OPC (OPC Viewer’a i CommServer’a), zainstalowanych na dwóch różnych komputerach. Oprócz zagadnień związanych OPC, film pokazuje elementy związane z konfiguracją DCOM i elementów zabezpieczeń w systemie Windows. Ze względu na długość filmu (przekraczającą 10 min.) film został podzielony na dwie części:

Gdyby kogoś zainteresowała tematyka z filmu i chciałby samodzielnie wykonać podobne ćwiczenie, to zapraszam do przeczytania artykułu znajdującego się tutaj: http://www.commsvr.com/Howitworks/Howto/CommServer2PCtestcommunication.aspx

wtorek, 28 kwietnia 2009

Zapraszam na szkolenie: "OPC UA Address Space - From Process Architecture to Interoperability"

Na temat nowego i obiecującego standardu OPC Unified Architecture pisałem już na tym blogu wielokrotnie. Jedną z ważniejszych cech tego standardu jest możliwość pokazywania nie tylko danych z procesu, ale również wszystkich występujących w nim powiązań. Jest to możliwe dzięki zaawansowanym możliwością jakie oferuje przestrzeń adresowa OPC UA (OPC UA Address Space).

Szkolenie, na które chciałbym zaprosić, poświęcone jest przede wszystkim przestrzeni adresowej OPC UA, ale nie tylko. Będą również podstawy standardu OPC Unified Architecture.

Szkolenie będzie prowadzone w języku angielskim, a prowadzący to: Mariusz Postół i Maciej Zbrzezny.

Więcej szczegółów na temat tego szkolenia dostępnych jest pod adresem: http://www.commsvr.com/OPCWorkshops/OPCUAWorkshop.aspx.

Zapraszam zainteresowanych.

poniedziałek, 27 kwietnia 2009

Open XML Format SDK 2.0 - pierwsze wrażenia

Microsoft w kwietniu tego roku opublikował kolejną wersję pakietu SDK do manipulacji dokumentów w formacie OpenXML. Pakiet ten ma za zadanie ułatwić tworzenie i edycję plików w formacie OpenXML z poziomu języka programowania (np. C#), automatyzując standardowe operacje jakie zwykle implementować muszą programiści tworzący aplikacje wykorzystujące ten format. Pakiet można pobrać ze stron Microsoft'u: http://www.microsoft.com/downloads/details.aspx?FamilyID=C6E744E5-36E9-45F5-8D8C-331DF206E0D0&displaylang=en.

Gdzie szukać informacji na temat Open XML i pakietu SDK?

Pierwszy program ("Hello Word" w OpenXML)

Do projektu należy dodać następujące referencje:
  • DocumentFormat.OpenXml
  • WindowsBase
W kodzie dołączamy następujące przestrzenie nazw:
  • DocumentFormat.OpenXml;
  • DocumentFormat.OpenXml.Packaging;
  • DocumentFormat.OpenXml.Wordprocessing;
Teraz funkcja realizująca proste "Hello World" w OpenXML:
private void HelloWorld(string documentFileName)
{
  // Create a Wordprocessing document.
  using (WordprocessingDocument myDoc = WordprocessingDocument.Create(documentFileName, WordprocessingDocumentType.Document))
  {
    // Add a new main document part.
    MainDocumentPart mainPart = myDoc.AddMainDocumentPart();
    //Create Document tree for simple document.
    mainPart.Document = new Document();
    //Create Body (this element contains other elements that we want to include
    Body body = new Body();
    //Create paragraph
    Paragraph paragraph = new Paragraph();
    Run run_paragraph = new Run();
    // we want to put that text into the output document
    Text text_paragraph = new Text("Hello World!");
    //Append elements appropriately.
    run_paragraph.Append(text_paragraph);
    paragraph.Append(run_paragraph);
    body.Append(paragraph);
    mainPart.Document.Append(body);
    // Save changes to the main document part.
    mainPart.Document.Save();
  }
}
Powyższa funkcja tworzy dokument w pliku podanym przez użytkownika. Do dokumentu dodany jest paragraf który zawiera tekst "Hello World!". W ostatnim kroku dokument jest zapisywany. A jak tą funkcję wykorzystać? Poniższy przykład pyta użytkownika o plik, do którego ma być zapisany tekst, wywołuje wcześniej stworzoną funkcję i otwiera przygotowany plik przy pomocy Word (lub innej ustawionej w systemie przeglądarki plików docx).
SaveFileDialog mySaveFileDialog=new SaveFileDialog();
mySaveFileDialog.Filter = "Word 2007 file (DOCX)|*.docx";
//save dialog display:
if (mySaveFileDialog.ShowDialog() == DialogResult.OK)
{
  //call creation of HellowWord document
  HelloWorld(mySaveFileDialog.FileName);
  // let's open this deocument in Word:

  Process.Start(mySaveFileDialog.FileName);
}

Podsumowanie

Na pierwszy rzut oka, pakiet ten oferuje bardzo wygodną możliwość manipulacji plikami zgodnymi z OpenXML. Postaram się w przyszłych postach opisać bardziej zaawansowane jego możliwości.

niedziela, 26 kwietnia 2009

SyntaxHighlighter na Blogger'rze (jak uruchomić?)

Chyba każdy programista, który kiedykolwiek chciał opublikować fragmenty kodu źródłowego na jakiejś stronie stanął przed problemem jak to zrobić, by było to możliwie czytelnie (łatwo do skopiowania itp...). Oczywiście przeklejanie kodu np.z Visual Studio i próba odwojowania tamtej kolorystyki nie koniecznie przynosi pożądane efekty. Jak więc to zrobić? Rozwiązaniem może być SyntaxHighlighter.

Ten artykuł poświęcony jest właśnie sposobowi publikowania fragmentów kodu źródłowego na blogu przy użyciu SyntaxHighlighter i opisuje ja to zrobić na platformie blogowej tego bloga, czyli na na Blogger'rze.

SyntaxHighlighter, skąd wziąć, jak znaleźć informacje na jego temat.

Miejscami skąd można pobrać SyntaxHighlighter lub znaleźć informacje na jego temat są:

Dodatkowo polecam post:

http://urenjoy.blogspot.com/2008/10/publish-source-code-in-blogger.html

który opisuje jak wdrożyć to na Blogger’rze (właśnie z tego posta zaczerpnąłem część informacji tutaj podawanych).

Jak wdrożyć SyntaxHighlighter na Bloger’rze?

To dość proste, chociaż trzeba sobie odpowiedzieć na ważne pytanie: gdzie będzie hosting wykorzystanych skryptów i styli? Oczywiście można wykorzystać ten sam hosting co strona, ale w przypadku omawianego tutaj przykładu (Blog na Blogger'rze) nie można tutaj wgrać, żadnych dodatkowych skryptów, czy styli. Dlatego sensownym wydaje się hosting oferowany przez autora SyntaxHighlighter (opis tutaj: http://alexgorbatchev.com/wiki/SyntaxHighlighter:Hosting) , w moim przypadku będzie wykorzystany SyntaxHighlighter w wersji 2.0.296, dostępny tutaj: http://alexgorbatchev.com/pub/sh/2.0.296/.

Trzeba więc wykonać kilka kroków:

1. Edycja kodu HTML strony (Edycja -> układ -> edytuj kod HTML). Tutaj wklejamy następujące linie przed </head>:

<script type="text/javascript" src="http://alexgorbatchev.com/pub/sh/2.0.296/scripts/shCore.js"></script>
<script type="text/javascript" src="http://alexgorbatchev.com/pub/sh/2.0.296/scripts/shBrushBash.js"></script>
<script type="text/javascript" src="http://alexgorbatchev.com/pub/sh/2.0.296/scripts/shBrushCpp.js"></script>

<script type="text/javascript" src="http://alexgorbatchev.com/pub/sh/2.0.296/scripts/shBrushCSharp.js"></script>
<script type="text/javascript" src="http://alexgorbatchev.com/pub/sh/2.0.296/scripts/shBrushCss.js"></script>
<script type="text/javascript" src="http://alexgorbatchev.com/pub/sh/2.0.296/scripts/shBrushDelphi.js"></script>
<script type="text/javascript" src="http://alexgorbatchev.com/pub/sh/2.0.296/scripts/shBrushDiff.js"></script>
<script type="text/javascript" src="http://alexgorbatchev.com/pub/sh/2.0.296/scripts/shBrushGroovy.js"></script>
<script type="text/javascript" src="http://alexgorbatchev.com/pub/sh/2.0.296/scripts/shBrushJava.js"></script>

<script type="text/javascript" src="http://alexgorbatchev.com/pub/sh/2.0.296/scripts/shBrushJScript.js"></script>
<script type="text/javascript" src="http://alexgorbatchev.com/pub/sh/2.0.296/scripts/shBrushPhp.js"></script>
<script type="text/javascript" src="http://alexgorbatchev.com/pub/sh/2.0.296/scripts/shBrushPlain.js"></script>
<script type="text/javascript" src="http://alexgorbatchev.com/pub/sh/2.0.296/scripts/shBrushPython.js"></script>
<script type="text/javascript" src="http://alexgorbatchev.com/pub/sh/2.0.296/scripts/shBrushRuby.js"></script>
<script type="text/javascript" src="http://alexgorbatchev.com/pub/sh/2.0.296/scripts/shBrushScala.js"></script>

<script type="text/javascript" src="http://alexgorbatchev.com/pub/sh/2.0.296/scripts/shBrushSql.js"></script>
<script type="text/javascript" src="http://alexgorbatchev.com/pub/sh/2.0.296/scripts/shBrushVb.js"></script>
<script type="text/javascript" src="http://alexgorbatchev.com/pub/sh/2.0.296/scripts/shBrushXml.js"></script>
<link type="text/css" rel="stylesheet" href="http://alexgorbatchev.com/pub/sh/2.0.296/styles/shCore.css"/>
<link type="text/css" rel="stylesheet" href="http://alexgorbatchev.com/pub/sh/2.0.296/styles/shThemeDefault.css"/>
<script type="text/javascript">
 SyntaxHighlighter.config.clipboardSwf = 'http://alexgorbatchev.com/pub/sh/2.0.296/scripts/clipboard.swf';
 if(window.isBloggerMode == true)
    SyntaxHighlighter.BloggerMode();
 SyntaxHighlighter.all();
</script>

2. Kod który chcemy umieścić na stronie kodujemy tak, by nadawał się do publikacji w HTML’u (np. znaki < zamieniamy na &lt;). Można to zrobić ręcznie lub wykorzystać: http://www.string-functions.com/htmlencode.aspx.

3. na stronie gdzie chcemy opublikować kod wklejamy:

<pre name="code" class="brush: c-sharp;">
….My encoded code here…
</pre>

Dostępne typy formatowania to:

  • bash, shell
  • c-sharp, csharp
  • cpp, c
  • css
  • delphi, pas, pascal
  • diff, patch
  • groovy
  • js, jscript, javascript
  • java
  • perl, pl
  • php
  • plain, text
  • py, python
  • rails, ror, ruby
  • scala
  • sql
  • vb, vbnet
  • xml, xhtml, xslt, html, xhtml

Efekt

Po tych trzech krokach możemy już się cieszyć formatowaniem takim jak:

    private void button1_Click(object sender, EventArgs e)
    {
      for (int i = 0; i < 10; i++)
      {
        Console.Write(" {0} ", i);
      }
   }

poniedziałek, 13 kwietnia 2009

Przechowywanie i edycja ustawień aplikacji w .NET (prosto, szybko, sprawnie i elegancko)(przykłady oparte o .NET, w języku C#)

Chyba każda (nie trywialna) aplikacja potrzebuje przechowywać swoje ustawienia. Niektórzy, aby to osiągnąć tworzą bardzo skomplikowane rozwiązania, które są później trudne do zrozumienia i jeszcze trudniejsze do modyfikacji, czy utrzymania, a przecież jeśli piszemy aplikację na platformie .NET jest to takie proste.

W tym krótkim artykule pokazany zostanie właśnie bardzo prosty sposób opisujący jak można przechowywać te ustawienia i jeszcze prostszy sposób jak je zmieniać.

Do dzieła (jak dodać ustawienia?) ...

Na platformie .NET zaproponowano sposób w jaki można przechowywać ustawienia aplikacji, ustawienia te powinny być przechowywane w pliku z rozszerzeniem ".config". Jest to plik, dokument XML, którego nazwa powstaje w następujący sposób: nazwa pliku wykonywalnego aplikacji (razem z rozszerzeniem "exe") + ".config".

W Visual Studio taki plik może być dodany przy pomocy prostego kreatora. W Solution Explorer klikamy prawym przyciskiem myszy na projekcie, wybieramy Add.. -> New Item …

i pojawia się okno podobne do poniższego:

Wybieramy: "Application Configuration File" i klikamy przycisk "Add". Po tej operacji, możemy już edytować ustawienia. W tym celu wyieramy projekt (w Solution Explorer) -> Properties -> Settings.settings. Dwuklik i powinien pojawić się edytor jak poniżej:

Tutaj można zefiniować ustawienia jakie tylko chcemy. Ważne jest, że ustawienie zakresu dla każdej opcji, czyli czy ma być to ustawienie użytkownika (User), czy aplikacji (Application). Ustawienia użytkownika mogą być zmieniane przez konkretnego użytkownika i przechowywane są osobno dla każdego użytkownika aplikacji. Globalne ustawienia (ważne dla wszystkich użytkowników) przechowywane są i ustawiane globalnie dla wszystkich użytkowników jednocześnie.

A jak te ustawienia zmieniać?

Nie chciałbym wchodzić zanadto w szczegóły, ale zastanówmy się jakie są możliwości zmian tych ustawień w gotowej aplikacji.

Pierwszą możliwością jest edycja pliku .config, co sprowadza się do edycji dokumentu XML. Nie jest to zbyt dobry pomysł, użytkownik musi wiedzieć co to jest XML, jak go modyfikować, do tego dochodzi jeszcze ryzyko, że coś zepsuje.

Drugą możliwością jest przygotowanie odpowiedniego edytora (pewnego GUI dla edycji tych ustawień). Oczywiście może to być bardzo dużo pracy, a dodatkowo, w przypadku zmian ustawień (ich nazw, dodania nowego lub usunięcia istniejącego) trzeba zmienić również stworzony edytor.

Na szczęście na platformie .NET jest dostępny pewien edytor, który może automatycznie dostosować się istniejących ustawień, a jest nim ... PropertyGrid. Jak to zrobić? Bardzo prosto.

Najpier tworzymy okno które ma słyżyć edycji ustawień. Do tego okna dodajemy kontrolkę ProperyGrid i ustawiamy, aby obiektem prezentowany przez tą kontrolkę były nasze ustawienia, np. w ten sposób:

this.propertyGrid1.SelectedObject = Properties.Settings.Default;

Dodatkowo nasze okno powinno mieć przycisk "OK" lub "Save settings", służący do zapisu ustawień, czyli wykonujący kod podobny do poniżsego:

Properties.Settings.Default.Save();

Na końcu warto zwrócić uwagę, na temat klokalizacji, gdzie ustawienia są zapisywane. Nowy plik ".config" tworzony jest u zapisywany jest w katalogu, podobnym do poniższego:

"Documents and Settings\User name\Local settings\Application Data\Application name\"

Nowy plik ".config" nie przechowuje wszystkich ustawień, a jedynie ustawienia przypiusane danemu użytkownikowi.

Przykładowa aplikacja

Przykładowa aplikacja (Settings test, dostępna tutaj) pokazuje omówione elementy (prezentacja i edycja ustawień) w praktyce. Wyświetla tylko jedno okno, służące do edycji ustawień tej aplikacji W kontrolce właściwości możemy modyfikowac uistawienia należące do użytkownika, a naciśniecie przycisku "Save Settings" powoduje zapisanie ustawień.

Każdy może sobie sprawdzić czy ustawienia na pwno się zapisują, poprzez ich zmianę, wciśniecię przysisku zapisu i ponowne uruchomienie aplikacji. Pobierz przykładowy kod.

Podsumowanie

Mam nadzieję, że spodobał wam się ten prosty sposób umożliwienia użytkownikowi modyfikacji ustawień aplikacji.

Historia

sobota, 4 kwietnia 2009

OPC Unified Architecture (OPC UA), pakiety SDK (.NET, java) i inne oprogramowanie

Standard i powiązane z nim specyfikacje OPC Unified Architecture (OPC UA) powstawały przez pięć lat, w lutym tego roku zostały wreszcie opublikowane (pisałem o tym w post'cie pt. "Specyfikacje OPC Unified Architecture (OPC UA) wreszcie opublikowane jako skończone"), najważniejsze jednak jest pytanie: czy oprócz idei standardu i specyfikacji powstało już jakieś oprogramowanie?

Oczywiście TAK!

Pakiety SDK

Niemal od samego początku prac nad specyfikacjami trwały prace grupy "Early Adopters", której zadaniem było zaimplementowanie OPC UA w prawdziwym oprogramowaniu. Jako platforma, na której miało być zaimplementowane oprogramowanie wybrana została platforma .NET, a język to C#. Na początku wykorzystywano .NET 2.0 oraz Web Services Enhancements (WSE), ostatecznie użyto .NET 3.5 oraz WCF. W ten sposób powstał pakiet SDK, który w zeszłym miesiącu (2009-03-22) udostępniony został na stronach OPC Foundation jako stabilna wersja 1.00.232.4 Release Candidate. Pakiet SDK nie udostępnia jeszcze pełnej funkcjonalności opisanej w specyfikacjach OPC UA, ale jeśli chodzi o wykorzystanie podstawowego profilu udostępniającego aktualne dane procesowe (data access) , to wszystko wydaje się już być gotowe.

Aby ułatwić implementację nowej technologii, jeden z twórców specyfikacji OPC UA i pakietu SDK, rozpoczął tworzenie bloga w którym opisuje zagadnienia związane tworzeniem oprogramowania zgodnego i wykorzystującego OPC UA (pisałem o tym w post'cie pt. "Blog jednego z twórców OPC Unified Architecture (OPC UA) - Randy'iego Armstrong'a"). Przykłady które opisywane są w blogu oparte są o platformę .NET. Omówione zostały już następujące zagadnienia:

  • import opisów WSDL przy wykorzystaniu narzędzia svcutil.exe (pochodzącego z WCF),
  • tworzenie certyfikatów z wykorzystaniem narzędzia wchodzącego w skład Visual Studio, czyli "makecert", oraz obsługa tych certyfikatów w języku C#,
  • ustawienia zabezpieczeń na przykładach WCF,
  • wykorzystanie mechanizmu okrywania (OPC UA Discovery), do odnajdywania serwera.

Razem z materiałem zawartym w blogu tworzona jest przykładowa aplikacja(pod nazwą: "UA Simple WCF Applications"), która jest dostępna na stronach OPC Foundation.

Zaletą OPC Unified Architecture jest jej niezależność od konkretnej platformy, dlatego powstaje również pakiet SDK oparty o język i platformę Java. Opublikowana już została jego wersja beta (1.00.232.0).

A co z innym oprogramowaniem??

Na stronach OPC Foundation pojawiły się informacje o pierwszych produktach wspierających Unified Architecture, ich lista dostępna jest tutaj. Są tam klienci , serwery, książki, usługi (ciekawe podejście do produktu ;) ) i narzędzia uzupełniające.

Ja chciałbym tutaj przybliżyć jedno z narzędzi wspomagających tworzenie serwerów OPC UA:

UA Address Space Model Designer

Jest to narzędzie, które współtworzyłem, w pewnym uproszczeniu służy ono do modelowania rzeczywistości na potrzeby OPC Unified Architecture. Narzędzie to potrafi w wizualny sposób zaprojektować przestrzeń adresową OPC UA, którą następnie można w dość automatyczny sposób przenieść do tworzonego serwera. Narzędzie to w ściśle współpracuje z pakietem OPC UA SDK dostarczanym przez OPC Foundation i potrafi wygenerować plik XML lub kod C# opisujący modelowaną przestrzeń adresową.

Podsumowanie

W ramach podsumowania chciałbym polecić ciekawe miejsca gdzie można znaleźć informacje na temat tego nowego standardu:

czwartek, 2 kwietnia 2009

Foreach plik w pewnym katalogu rób... (CMD: FOR %A in (c:\*.*) do type %A)



Czy zdarza wam się jeszcze korzystać z linii komend w Windows?
Mnie tak.


Niniejszy post dotyczy dość banalnej sprawy, ale ponieważ od czasu do czasu zdarza mi się to wykorzystywać, więc dla mnie będzie to prosta ściągawka na przyszłość, a mam nadzieję, że może jeszcze przyda się komuś innemu.

Czarne okienko systemu Windows, czyli aplikacja CMD potrafi obsługiwać pętle. Zobaczmy przykład pętli, która ma odczytać logi IIS i wrzucić je do jednego pliku c:\logfile.log:

FOR %A in (C:\WINDOWS\system32\LogFiles\W3SVC1\*.log) do type %A >>c:\logfile.log

Jeszcze ciekawsze jest wykorzystanie pętli do wywoływania podprocedury:

echo off
FOR %%A in (c:\*.*) do CALL :Subroutine %%A
GOTO:EOF
:Subroutine
echo "nazwa pliku bez rozszerzenia: %~n1" "nazwa rozszerzenia: %~x1" "pelna sciezka: %~f1"
GOTO:EOF

Oczywiście to nie wszystko więcej informacji jest dostępnych w pomocy (cmd: help for).

środa, 1 kwietnia 2009

Podsumowanie (do artykułu: "Tworzenie napisów dla scen 3D tworzonych przy pomocy WPF")

Mam nadzieję, że czytelników zainteresowały moje dwa podejścia do tworzenia napisów na potrzeby scen 3D tworzonych w WPF. Artykuł jest dostępny również w języku angielskim: CodeProject. Zainteresowanych tematem, zapraszam do komentowania poszczególnych części, szczególnie jestem zainteresowany jakimiś sugestiami odnośnie poprawienia części związanej z "wektorami położenia".

Historia tego artykułu

  • 2009.03.04 - Powstanie angielskiej wersji artykułu (opublikowany na CodeProject, użytkownik: Maciej Zbrzezny )
  • 2009.03.09 - Powstanie polskiej wersji artykułu (opublikowany na CodeGuru.pl , użytkownik Maciej Zbrzezny)
  • 2009.03.20 - adaptacja artykuły na potrzeby bloga: http://maciej-progtech.blogspot.com , dodanie akapitu: "Napisy przestrzenne (WPF 3D), a ich wektory położenia", zmiana pewnych opisów

(To tylko jedna z części artykułu, przeczytaj pozostałe: 1, 2, 3, 4, 5, 6, 7)

Posty powiązane / Related posts