Beispiel Maschine mit Microsoft Silverlight for Windows Embedded

Eine Neuerung in Windows Embedded CE 6.0 R3 ist Silverlight for Windows Embedded. Mit dieser neuen Technologie können Bedienoberflächen von CE Geräten nun in XAML beschrieben werden und mit Tools wie Microsoft Expression Blend gestaltet werden. Anhand des Maschine Beispiels wird hier die Erstellung einer Silverlight for Windows Embedded Applikation mit Einbindung der ADS Komponente beschrieben.

Zielplattform

Implementierung

Erforderliche Software

Erforderliche Hardware

Die ersten Schritte...

1. Neues Silverlight 2 Projekt erstellen:
Das Design einer Silverlight for Windows Embedded Applikation wird in XAML beschrieben. Dazu wird mit Microsoft Expression Blend 2 SP1 über 'File -> New Projekt' ein Silverlight 2 Projekt erstellt. Die dabei erstellte Visual Studio Solution wird in diesem Beispiel nicht benötigt. Zudem kann die Auswahl der Programmiersprache (Visual C# oder Visual Basic) außer Acht gelassen werden. Silverlight for Windows Embedded unterstützt nur Visual C++, welches nicht in Expression Blend integriert ist. Es ist daher auch nicht möglich den Quellcode zu verwenden, der von diesem Tool generiert wird. Daektivieren Sie die Visual Studio Integration in Expression Blend um eine unnötige automatische Generation von Visual C# und Visual Basic Code zu vermieden. Wählen Sie dazu: 'Options->Event handlers' 'Clipboard only'.

Beispiel Maschine mit Microsoft Silverlight for Windows Embedded 1:



2. Bedienoberfläche erstellen
In Expression Blend kann nun die Bedienoberfläche erstellt werden.

Beispiel Maschine mit Microsoft Silverlight for Windows Embedded 2:


Im oberen linken Bereich sind die beiden Ausgänge zu sehen, die auch auf den Busklemmen ausgegeben werden. Unten links ist die Variable abgebildet, welche die Werkstücke zählt. Rechts kann die Geschwindigkeit eingestellt werden. Die Anzeige 'Steps' entspricht der Anzahl der Takte. Oben rechts wurde zudem noch ein Button zum Beenden des Programms erstellt.

<UserControlxmlns="http: schemas.microsoft.com winfx 2006 xaml presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="319" Height="255">
    <!-- Timelines -->
    <UserControl.Resources>
        <!-- Timeline Device Down -->
        <Storyboardx:Name="timelineDeviceDown">
            <ColorAnimationUsingKeyFramesBeginTime="00:00:00" Storyboard.TargetName="txtDeviceDown" Storyboard.TargetProperty="(TextBlock.Foreground).(SolidColorBrush.Color)">
                <SplineColorKeyFrameKeyTime="00:00:00.4000000" Value="#FFFF0000"/>
            </ColorAnimationUsingKeyFrames>
        </Storyboard>
        <!-- Timeline Device Up -->
        <Storyboardx:Name="timelineDeviceUp">
            <ColorAnimationUsingKeyFramesBeginTime="00:00:00" Storyboard.TargetName="txtDeviceUp" Storyboard.TargetProperty="(TextBlock.Foreground).(SolidColorBrush.Color)">
                <SplineColorKeyFrameKeyTime="00:00:00.4000000" Value="#FFFF0000"/>
            </ColorAnimationUsingKeyFrames>
        </Storyboard>
        <!-- Timeline Engine -->
        <Storyboardx:Name="timelineEngine"/>
    </UserControl.Resources>
    <!-- Beginn der Layout Beschreibung -->
    <Gridx:Name="LayoutRoot" Background="#FF595959">
        <!-- Title Machine -->
        <TextBlockText="Machine" Margin="80,8.438,80,0" VerticalAlignment="Top" FontWeight="Bold" Foreground="#FFFFFFFF" FontSize="34"/>
        <!-- Device Up / Device Down -->
        <GridMargin="15,60,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Height="53" Width="132.532">
            <RectangleFill="{x:Null}" Stroke="#FFFFFFFF" StrokeThickness="2" RadiusX="6" RadiusY="6"/>
            <TextBlockText="Device Up" Margin="28.823,5.396,-8.823,0" VerticalAlignment="Top" Foreground="#FFFFFFFF" FontSize="16"/>
            <TextBlockText="Device Down" Margin="29.002,0,-9.002,4.994" VerticalAlignment="Bottom" Foreground="#FFFFFFFF" FontSize="16"/>
            <TextBlockx:Name="txtDeviceDown" Text="ê" Margin="7.517,0,0,4.998" HorizontalAlignment="Left" VerticalAlignment="Bottom" FontFamily="Wingdings" FontWeight="Bold" Foreground="#FFFFFFFF" FontSize="16"/>
            <TextBlockx:Name="txtDeviceUp" Text="é" Margin="7.517,5.497,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" FontFamily="Wingdings" FontWeight="Bold" Foreground="#FFFFFFFF" FontSize="16"/>
        </Grid>
        <!-- Counter -->
        <GridMargin="15,0,0,71" HorizontalAlignment="Left" VerticalAlignment="Bottom" Height="53" Width="133">
            <RectangleFill="{x:Null}" Stroke="#FFFFFFFF" StrokeThickness="2" RadiusX="6" RadiusY="6"/>
            <StackPanelMargin="12.991,16,0,16" HorizontalAlignment="Left" Orientation="Horizontal" Width="113">
                <TextBlockText="Count:" Width="54.824" Foreground="#FFFFFFFF" FontSize="16"/>
                <TextBlockx:Name="txtCount" Margin="2,0,0,0" Foreground="#FFFFFFFF" FontSize="16"/>
            </StackPanel>
        </Grid>
        <!-- Speed -->
        <GridMargin="0,0,15,71" Height="53" HorizontalAlignment="Right" VerticalAlignment="Bottom" Width="132.532">
            <RectangleFill="{x:Null}" Stroke="#FFFFFFFF" StrokeThickness="2" RadiusX="6" RadiusY="6"/>
            <StackPanelMargin="12.988,0,3.012,0" Orientation="Horizontal">
                <RadioButtonx:Name="radSpeedSlow" Content="slow" Margin="0,0,6,0" Foreground="#FFFFFFFF" FontSize="16" Height="19.496"/>
                <RadioButtonx:Name="radSpeedFast" Content="fast" Foreground="#FFFFFFFF" FontSize="16" Height="19.496"/>
            </StackPanel>
        </Grid>
        <!-- Steps -->
        <GridMargin="15,0,15,5" VerticalAlignment="Bottom" Height="57">
            <ProgressBarx:Name="prgSteps" Margin="0,18,0,18" Maximum="25"/>
            <TextBlockText="Steps:" HorizontalAlignment="Left" VerticalAlignment="Top" Foreground="#FFFFFFFF"/>
            <TextBlockText="0%" HorizontalAlignment="Left" VerticalAlignment="Bottom" Foreground="#FFFFFFFF"/>
            <TextBlockText="100%" HorizontalAlignment="Right" VerticalAlignment="Bottom" Foreground="#FFFFFFFF"/>
        </Grid>
        <!-- Close Button -->
        <Buttonx:Name="butClose" Content="X" HorizontalAlignment="Right" VerticalAlignment="Top" Height="20" Width="20"/>
        <!-- Beckhoff Logo -->
        <ImageSource="beckhoff_logo_white.jpg" Margin="0,64.502,15,0" HorizontalAlignment="Right" VerticalAlignment="Top" Height="43.151" Width="133.745" Stretch="Fill"/>
    </Grid>
</UserControl>



3. Neues Win32 Smart Device Projekt erstellen
In Visual Studio 2008 muss nun ein neues Win32 Smart Device Projekt erstellt werden.

Beispiel Maschine mit Microsoft Silverlight for Windows Embedded 3:

Sollte das Beckhoff HMI 600 SDK noch nicht auf dem Rechner installiert sein, so installieren Sie dies vor der Erstellung eines neuen Visual Studio Projektes.

Beispiel Maschine mit Microsoft Silverlight for Windows Embedded 4:


Das Platform SDK dieses Projektes ist das Beckhoff HMI 600 SDK.

Beispiel Maschine mit Microsoft Silverlight for Windows Embedded 5:


Als Servertyp wird Executable (EXE) ausgewählt.

Beispiel Maschine mit Microsoft Silverlight for Windows Embedded 6:



4. XAML-Datei als Resource einbinden
Die mit Expression Blend gestaltete Bedienoberfläche kann in das neue Projekt eingebunden werden. Öffnen Sie dazu die Resource-Datei (.rc). Mit einem Rechts-Klick auf die Resource im Resource View Tab und der Auswahl von 'Add -> Resource...' öffnet sich ein Dialog über den die XAML-Datei eingebunden werden kann.

Beispiel Maschine mit Microsoft Silverlight for Windows Embedded 7:


Mit Hilfe des Dialoges kann die XAML-Datei ins Projekt importiert werden.

Beispiel Maschine mit Microsoft Silverlight for Windows Embedded 8:


XAML als Resourcetyp angeben.

Beispiel Maschine mit Microsoft Silverlight for Windows Embedded 9:


Die Standard ID (IDR_XAML1) der Resource kann in diesem Beispiel beibehalten werden. In eigenen Projekten ist es jedoch sinnvoll diese umzubenennen.

Beispiel Maschine mit Microsoft Silverlight for Windows Embedded 10:



5. AdsHelper Class erstellen
Der größte Teil der Ads-Kommunikation kann in einer separaten Klasse, hier AdsHelper genannt, ausgelagert werden.

AdsHelper.h
In den AdsHelper-Header werden die TcAds-Headers eingebunden. Dabei ist auf die richtige Pfadangabe zu achten. Zudem sind die Headers abhängig vom Prozessortyp. Im Beispielcode sind die X86-Headers angefügt.

#include"..\_AdditionalFiles\TcpAdsApiCe\include\TcAdsDef.h"#include"..\_AdditionalFiles\TcpAdsApiCe\include\TcAdsAPI.h"


Des Weiteren werden im Header die Deklarationen vorgenommen.

typedef enum E_NOTIFICATION_IDENT
{
    engine = 0,
    deviceUp = 1,
    deviceDown = 2,
    steps,
    count,
    switchSpeed    
};

long AdsGetVarHandle(AmsAddr* pServerAddr, const char* szVarname, long* pHandle);
long AdsFreeVarHandle(AmsAddr* pServerAddr, long handle);
long AdsGerVarHandleEx(long port, AmsAddr* pServerAddr, const char* szVarname, long* pHandle);
long AdsFreeVarHandleEx(long port, AmsAddr* pServerAddr, long handle);

long SpeedSlowEx(long port, AmsAddr* pServerAddr);
long SpeedFastEx(long port, AmsAddr* pServerAddr);

long connect(long port, AmsAddr* pServerAddr);
long disconnect(long port, AmsAddr* pServerAddr);



AdsHelper.cpp
Die Header StdAfx.h und AdsHelper.h müssen in die AdsHelper.cpp eingebunden werden,

#include"StdAfx.h"#include"AdsHelper.h"

und anschließend werden die globalen Variablen definiert.

long hEngine, hDeviceUp, hDeviceDown, hSteps, hCount, hSwitch;
unsigned long hEngineNotification, hDeviceUpNotification, hDeviceDownNotification, 
          hStepsNotification, hCountNotification, hSwitchNotification;

Die Methoden AdsGetVarHandle und AdsGetVarHandleEx dienen dazu, Handles zu SPS-Variablen zu erstellen

long AdsGetVarHandle(AmsAddr* pServerAddr, const char* szVarname, long* pHandle)
{
    if (pHandle == NULL || pServerAddr == NULL)
    return E_POINTER;
    
    unsigned long read = 0;
    long nErr = 
    AdsSyncReadWriteReqEx(pServerAddrm, ADSIGRP_SYM_HNDBYNAME, 0x0,
    sizeof(long), pHandle,strlen(szVarname), (char*)szVarname, &read);
    
    return nErr;
}

long AdsGetVarHandleEx(long port, AmsAddr* pServerAddr, const char* szVarname, long* pHandle)
{
    if(pHandle == NULL || pServerAddr == NULL)
    return E_POINTER;
    
    unsigned long read = 0;
    
    long nErr = 
    AdsSyncReadWriteReyEx2(port, pServerAddr, ADSIGRP_SYM_HNDBYNAME, 0x0,
    sizeof(long), pHandle, strlen(szVarname), (char*)szVarname, &read);
    
    return nErr;
}


Mit AdsFreeVarHandle und AdsFreeVarHandleEx werden Handles zu SPS-Variablen wieder freigegeben.

long AdsFreeVarHandle(AmsAddr* pServerAddr, long handle)
{
    return AdsSyncWriteReq(pServerAddr, ADSIGRP_SYM_RELEASEHND, 0, 
               sizeof(handle), &handle);
}

long AdsFreeVarHandleEx(long port, AmsAddr* pServerAddr, long* pHandle)
{
    return AdsSyncWriteReqEx(port, pServerAddr, ADSIGRP_SYM_REALEASEHND, 0, 
                 sizeof(pHandle), &pHandle);
}


Die folgenden beiden Methoden schreiben die SPS-Variable ".switch" und setzen dadurch die Geschwindigkeit auf langsam bzw. schnell.

long SpeedSlowEx(long port, AmsAddr* pServerAddr)
{
    // Handle der SPS-Variable ".switch" erstellen.long handleSpeedSlow = 0;
    long adserror = AdsGetVarHandleEx(port, pServerAddr, ".switch", &handleSpeedSlow);    
    
    // Die SPS-Variable ".switch" auf FALSE setzenbool datafalse = false;
    adserror = AdsSyncWriteReqEx(port, pServerAddr, ADSIGRP_SYM_VALBYHND, 
                 handleSpeedSlow, 0x1, &datafalse);
    
    // Handle der SPS-Variable ".switch" freigeben
    adserror = AdsFreeVarHandleEx(port, pServerAddr, handleSpeedSlow);
    return adserror;
}

long SpeedFastEx(long port, AmsAddr* pServerAddr)
{    
    // Handle der SPS-Variable ".switch" erstellen.long handleSpeedFast = 0;
    long adserror = AdsGetVarHandleEx(port, pServerAddr, ".switch", &handleSpeedFast);    
    
    // Die SPS-Variable ".switch" auf TRUE setzenbool datatrue = true;
    adserror = AdsSyncWriteReqEx(port, pServerAddr, ADSIGRP_SYM_VALBYHND, 
                 handleSpeedFast, 0x1, &datatrue);    
    
    // Handle der SPS-Variable ".switch" freigeben
    adserror = AdsFreeVarHandleEx(port, pServerAddr, handleSpeedFast);
    return adserror;
}


In der connect-Methode wird eine Verbindung zu den Variablen in der SPS erzeugt.

long connect (long port, AmsAddr* addr, PAdsNotificationFuncEx Callback)
{
    // Attribute der Notification festlegen
    AdsNotificationAttrib attr;
    attr.cbLength = 2;
    attr.nTransMode = ADSTRANS_SERVERCYCLE;
    attr.nMaxDelay = 100000000; // = 1 sec
    attr.nCycleTime = 100000; // = 0,5 sec// Handles der SPS-Variablen holenlong adserr = AdsGetVarHandleEx(port, addr, ".engine", &hEngine);
    
    if(adserr == 0)
    adserr = AdsGetVarHandleEx(port, addr, ".deviceUp", &hDeviceUp);
    
    if(adserr == 0)
    adserr = AdsGetVarHandleEx(port, addr, ".deviceDown", &hDeviceDown);
    
    if(adserr == 0)
    adserr = AdsGetVarHandleEx(port, addr, ".steps", &hSteps);
    
    if(adserr == 0)
    adserr = AdsGetVarHandleEx(port, addr, ".count", &hCount);
    
    if(adserr == 0)
    adserr = AdsGetVarHandleEx(port, addr, ".switch", &hSwitch);
    
    
    // Überwachung der SPS-Variablen initialisierenif(adserr == 0)
    {
    attr.cbLength = 1;
    adserr = AdsSyncAddDeviceNotificationReqEx(port, addr, ADSIGRP_SYM_VALBYHND, hEngine, 
                           &attr, Callback, engine, &hEngineNotification);
    }
    if(adserr == 0)
    {
    attr.cbLength = 1;
    adserr = AdsSyncAddDeviceNotificationReqEx(port, addr, ADSIGRP_SYM_VALBYHND, hDeviceUp, 
                           &attr, Callback, deviceUp, &hDeviceUpNotification);
    }
    if(adserr == 0)
    {
    attr.cbLength = 1;
    adserr = AdsSyncAddDeviceNotificationReqEx(port, addr, ADSIGRP_SYM_VALBYHND, hDeviceDown, 
                           &attr, Callback, deviceDown, &hDeviceDownNotification);
    }
    if(adserr == 0)
    {
    attr.cbLength = 1
    adserr = AdsSyncAddDeviceNotificationReqEx(port, addr, ADSIGRP_SYM_VALBYHND, hSteps, 
                           &attr, Callback, steps, &hStepsNotification);
    }
    if(adserr == 0)
    {
    attr.cbLength = 2;
    adserr = AdsSyncAddDeviceNotificationReqEx(port, addr, ADSIGRP_SYM_VALBYHND, hCount, 
                           &attr, Callback, count, &hCountNotification);
    }
    if(adserr == 0)
    {
    attr.cbLength = 1;
    adserr = AdsSyncAddDeviceNotificationReqEx(port, addr, ADSIGRP_SYM_VALBYHND, hSwitch, 
                           &attr, Callback, switchSpeed, &hSwitchNotification);
    }
    
    return adserr;
}


Beim Trennen der Ads-Verbindung müssen die Handles der SPS-Variablen freigegeben und der Port geschlossen werden.

long disconnect(long port, AmsAddr* addr)
{
    // Handles der SPS-Variablen freigeben
    AdsFreeVarHandleEx(port, addr, hEngine);
    AdsFreeVarHandleEx(port, addr, hDeviceUp);
    AdsFreeVarHandleEx(port, addr, hDeviceDown);
    AdsFreeVarHandleEx(port, addr, hSteps);
    AdsFreeVarHandleEx(port, addr, hCount);
    AdsFreeVarHandleEx(port, addr, hSwitch);
    
    // Notifications löschen
    AdsSyncDelDeviceNotificationReqEx(port, addr, hEngineNotification);
    AdsSyncDelDeviceNotificationReqEx(port, addr, hDeviceUpNotification);
    AdsSyncDelDeviceNotificationReqEx(port, addr, hDeviceDownNotification);
    AdsSyncDelDeviceNotificationReqEx(port, addr, hStepsNotification);
    AdsSyncDelDeviceNotificationReqEx(port, addr, hCountNotification);
    AdsSyncDelDeviceNotificationReqEx(port, addr, hSwitchNotification);
    
    // Kommunikationsport schließen
    AdsPortCloseEx(port);
    
    return 0;
}



6. Quellcode bearbeiten

In der SilverlightForEmbeddedMachineSample.cpp-Datei werden als erstes die Headers eingebunden.

#include"pwinuser.h"#include"xamlruntime.h"#include"xrdelegate.h"#include"xrptr.h"    #include"resource.h"


Anschließend folgt die Variablendeklaration.

IXRDelegate<XRMouseButtonEventArgs>* clickdelegate;

UINT exitcode;    

IXRVisualHostPtr   vhost;
IXRButtonBasePtr   butClose;
IXRRadioButtonPtr  radSpeedSlow;
IXRRadioButtonPtr  radSpeedFast;
IXRTextBlockPtr    txtDeviceDown;
IXRTextBlockPtr    txtDeviceUp;
IXRTextBlockPtr    txtCount;
IXRProgressBarPtr  prgSteps;

IXRStoryboardPtr   timelineDeviceDown;
IXRStoryboardPtr   timelineDeviceUp;
IXRStoryboardPtr   timelineEngine;

long port;
AmsAddr addr;

unsigned long TimerID;
DWORD EventID;
CRITICAL_SECTION cs;
long event_cnt;
long event_cntold;

void __stdcall Callback(AmsAddr* addr, AdsNotificationHeader* handler, unsigned long User);



Die Timer-Callback-Funktion dient zur Überprüfung der Ads-Verbindung und stellt bei Bedarf die Verbindung wieder her.

VOID CALLBACK MyTimerProc(
    HWND hwnd,     // handle to window for timer messages
    UINT message,  // WM_TIMER message
    UINT idTimer,  // timer identifier
    DWORD dwTimer) // current system time
{
    if (event_cnt = event_cntold)
    {
    // Handels werden freigegeben und der Port geschlossen
    disconnect(port, &addr);
    
    // Kommunikationsport auf dem ADS Router öffnen
    port = AdsPortOpenEx();
        
    addr.port = 0x321;
    long adserror = -1;
    
    // Neue Verbindung zur SPS herstellen.while(adserror != 0)
    {
        adserror = connect(port, &addr, Callback);
        Sleep(1000);
    }
    }
    
    event_cntold = event_cnt;
}



Der folgende Ads-Event-Handler wird aufgerufen wenn sich eine SPS-Variable ändert, zu der eine Verknüpfung besteht.

// ADS-State Callback-Functionvoid __stdcall Callback(AmsAddr* addr, AdsNotificationHeader* handler, unsigned long User)
{
    event_cnt++;    


Abhängig davon ob deviceUp, deviceDown oder engine auf TRUE gesetzt ist werden die entsprechenden Pfeile rot eingefärbt oder nicht.

Durch Verwendung von Timelines kann dieser Effekt noch verbessert werden.

if (User == deviceUp)
    {
    if (*(bool*)handler->data == true)
    {
        timelineDeviceDown->Stop();
        timelineEngine->Stop();
        timelineDeviceUp->Begin();
    }
    }
    else if (User == deviceDown)
    {
     if(*bool*)handler-data == true)
     {
         timelineDeviceUp->Stop();
         timelineEngine->Stop();
         timelineDeviceDown->Begin();
     }
    }
    else if (User == engine)
    {
    if (*(bool*)handler->data == true)
    {
        timelineDeviceDown->Stop();
        timelineDeviceUp->Stop();
        timelineEngine->Begin();
    }
    }


steps gibt die Anzahl der Takte an. Der Wert wird über die Progressbar prgSteps ausgegeben. Hierzu müssen die Daten zunächst in ein Byte konvertiert werden, da die dazugehörige SPS Variable vom Typ Byte ist. Da der Progressbar nur Daten vom Typ float übergeben werden können erfolgt anschließend ein Konvertierung zum Typ float.

else if (User == steps)
    {
    prgSteps->SetValue((float)*((byte*)handler->data));
    }


Wie auch schon bei Steps müssen bei count die Daten zunächst in ihren ursprünglichen Datentyp konvertiert werden, bevor sie in einen Text umgewandelt und dem Textblock txtCount übergeben werden können.

else if (User == count)
    {
    WCHAR text[6];
    wsprintf(text, L"%d", *(unsigned short*)handler->data);
    txtCount->SetText(text);
    }


Die Ausgabe des Geschwindigkeitstyps erfolgt über RadioButtons. Je nach Geschwindigkeit wird der entsprechende RadioButton markiert.

else if (User == switchSpeed)
    {
    if (*(bool*)handler->data == true)
    {
        radSpeedFast->SetIsChecked(XRThreeState_Checked);
    }
    else
    {
        radSpeedSlow->SetIsChecked(XRThreeState_Checked);
    }
    }
}



Der OnClick Event wird von den verschiedenen Instanzen angetriggert und über den Namen kann unterschieden werden, welche Instanz der Auslöser war.

class BtnEventHandler
{
public:

    HRESULT OnClick(IXRDependencyObject* source, XRMouseButtonEventArgs* args)
    {
    BSTR name;
    HRESULT hr = NULL;
    source->GetName(&name);
    
    short state = 0;
    
    long adserror = 0;
    
    if (wcscmp(name, L"butClose") == 0)
    {
        // Machine Dialog schließen
        vhost->EndDialog(exitcode);
    }
    if (wcsmp(name, L"radSpeedSlow") == 0)
    {
        // Aufruf der Methode SpeedSlowEx um die Geschwindigkeit auf langsam zu setzen.
        adserror = SpeedSlowEx(port, &addr);
    }
    if (wscmp(name, L"radSpeedFast") == 0)
    {
        // Aufruf der Methode SpeedFastEx um die Geschwindigkeit auf schnell zu setzen.
        adserror = SpeedFastEx(port, &addr);
    }
    
    if (adserror != NULL)
    {
        // Die Handels werden freigegeben und der Port geschlossen
        disconnect(port, &addr);
        
        // Der Kommunikationsport auf dem ADS-Router wird geöffnet
        port = AdsPortOpenEx();
        
        addr.port = 0x321;
        
        // Neu Verbindung zur SPS herstellen.
        adserror = connect(port, &addr, Callback);
        
        Sleep(1000);
    }
    
    SysFreeString(name);
    return S_OK;
    }    
};



In der WinMain-Methode muss als erstes die XMAL-Runtime initialisiert werden. Ist XamlRuntimeInitialize erfolgreich, so wird die Silverlight for Windows Embedded Runtime in der Applikation gestartet.

int WINAPI WinMain(HINSTANCE hInstance,
          HINSTANCE hPrevInstance,
          LPTSTR lpCmdLine,
          int nCmdShow)
{
    // Initialisierung der XAML Runtimeif (!XamlRuntimeInitialize())
    return -1;


Jede Silverlight for Windows Embedded Applikation hat ein einzelnes "Applikations"-Objekt über welches der Zugriff auf globale Eigenschaften möglich ist.
Um auf dieses Objekt zuzugreifen wird die GetXRApplicationInstance API verwendet.

    HRESULT retcode;

    // Load an dinit XAML resource
    IXRApplicationPtr app;

    if (FAILED (retcode=GetXRApplicationInstance(&app)))
    return -1;
    
    if (FAILED (retcode=app->AddResourceModule(hInstance)))
    return -1;


Nach der Initializierung des Applikation Objekts kann das Hauptfenster erstellt und Silverlight for Windows Embedded die Verwaltung des Objektes übergeben werden.

    XRWindowCreateParams wp;

    ZeroMemory(&wp, sizeof(XRWindowCreateParams));

    // Set window styles
    wp.Style = WS_BORDER;
    wp.pTitle = L"Silverlight for Windows Embedded Machine Sample";
    wp.Left = 0;
    wp.Top = 0;
    wp.AllowsMultipleThreadAccess = true;

    XRXamlSource xamlsrc;

    xamlsrc.SetResource(hInstance, TEXT("XAML"), MAKEINTRESOURCE(IDR_XAML1));

    if (FAILED(retcode=app->CreateHostFromXaml(&xamlsrc, &wp, &vhost)))
    return -1;


Das Objekt innerhalb einer Silverlight for Windows Embedded Applikation ist in Objektbäumen organisiert. Um auf dieses Objekt zuzugreifen wird ein Pointer zum Wurzelelement benötigt.

    IXRFrameworkElementPtr root;

    if (FAILED (retocde=app->CreateHostFromXaml(&xamlsrc, &wp, &vhost)))
    return -1;


Erzeugen von Instanzen der Oberflächenelemente und der Timelines.

// Get controls by nameif (FAILED(retcode=root->FindName(TEXT("butClose"), &butClose))) 
    return -1;

    if (FAILED(retcode=root->FindName(TEXT("radSpeedSlow", &radSpeedSlow)))
    return -1;
    
    if (FAILED(retcode=root->FindName(TEXT("radSpeedFast", &radSpeedFast)))
    return -1;
    
    if (FAILED(retcode=root->FindName(TEXT("txtDeviceDown", &txtDeviceDown)))
    return -1;
    
    if (FAILED(retcode=root->FindName(TEXT("txtDeviceUp", &txtDeviceUp)))
    return -1;
    
    if (FAILED(retcode=root->FindName(TEXT("txtCount", &txtCount)))
    return -1;
    
    if (FAILED(retcode=root->FindName(TEXT("prgSteps", &prgSteps)))
    return -1;
    
    // Get timelines by nameif (FAILED (retcode=root->FindName(TEXT("timelineDeviceDown"), &timelineDeviceDown))) 
    return -1;  
       
    if (FAILED (retcode=root->FindName(TEXT("timelineDeviceUp"), &timelineDeviceUp))) 
    return -1;
    
    if (FAILED (retcode=root->FindName(TEXT("timelineEngine"), &timelineEngine))) 
    return -1;


Die Gruppe "RadioButtonGroup" erstellen und die beiden RadioButtons dieser Gruppe zuweisen.

    WCHAR groupName[17];
    wsprintf(groupName, L"RadioButtonGroup");
    radSpeedFast->SetGroupName(groupName);
    radSpeedSlow->SetGroupName(groupName);


Zum Verbinden des EventHandlers mit den Buttons wird ein weiterleitendes Objekt benötigt.

    BtnEventHandler handler;

    // Set the event handler for the buttonsif (FAILED(retcode=CreateDelegate(&handler, &BtnEventHandler::OnClick, &clickdelegate)))
    return -1;

    if (FAILED(retcode=butClose->btnAddClickEventHandler(clickdelegate)))
    return -1;
    
    if (FAILED(retcode=radSpeedSlow->AddClickEventHandler(clickdelegate)))
    return -1;
    
    if (FAILED(retcode=radSpeedFast->AddClickEventHandler(clickdelegate)))
    return -1;


Einbinden der Ads-Komponenten.

long adserror = -1;
    port = AdsPortOpenEx();

    AdsGetLocalAddressEx(port, &addr);

    // connect to the PLC and register callbacks
    addr.port = 0x321;
    adserror = connect(port, &addr, Callback);
    
    event_cnt = 0;
    event_cntold = -1;
    
    
    // init timer for reconnect
    SetTimer(NULL, NULL, 5000, MyTimerProc);
    
    if (FAILED(retcode=vhost->StartDialoge(&exitcode)))
    return -1;
    
    // cleanup
    disconnect(port, &addr);
    clickdelegate->Release();
    
    return 0;
}



7. Eigenschaften

In den Projekteigenschaften muss eine Verbindung zur xamlruntime.lib und TcAdsDllCe.lib hergestellt werden.

Beispiel Maschine mit Microsoft Silverlight for Windows Embedded 11:

Download Silverlight for Windows Embedded Beispiel

Sample Silverlight for Windows Embedded

Beispiel Maschine mit Microsoft Silverlight for Windows Embedded 12:

Sample für ARM-Geräte

Im Sample wird die X86 Version der TcAdsDllCe.lib verwendet. Um das Sample für ARM-Geräte bauen zu können, muss diese Bibliothek vorher gegen die entsprechende ARM-Version ausgetauscht werden.