Notes on the documentation

This description is only intended for the use of trained specialists in control and automation engineering who are familiar with the applicable national standards.
It is essential that the following notes and explanations are followed when installing and commissioning these components.

The responsible staff must ensure that the application or use of the products described satisfy all the requirements for safety, including all the relevant laws, regulations, guidelines and standards.

Disclaimer

The documentation has been prepared with care. The products described are, however, constantly under development.
For that reason the documentation is not in every case checked for consistency with performance data, standards or other characteristics.
In the event that it contains technical or editorial errors, we retain the right to make alterations at any time and without warning.
No claims for the modification of products that have already been supplied may be made on the basis of the data, diagrams and descriptions in this documentation.

Trademarks

Beckhoff®, TwinCAT®, EtherCAT®, Safety over EtherCAT®, TwinSAFE® and XFC® are registered trademarks of and licensed by Beckhoff Automation GmbH.
Other designations used in this publication may be trademarks whose use by third parties for their own purposes could violate the rights of the owners.

Patent Pending

The TwinCAT Technology is covered, including but not limited to the following patent applications and patents:
EP0851348, US6167425 with corresponding applications or registrations in various other countries.

Copyright

© Beckhoff Automation GmbH & Co. KG, Germany.
The reproduction, distribution and utilization of this document as well as the communication of its contents to others without express authorization are prohibited.
Offenders will be held liable for the payment of damages. All rights reserved in the event of the grant of a patent, utility model or design.


Overview

Description | Sample
Sample 1: Machine C# | Sample1
Sample 2: Machine Visual Basic | Sample2


ADS Return Codes

Error codes: 0x000..., 0x500..., 0x700..., 0x1000..., 0x274C...

Global Error Codes

Hex | Dec | Description | Possible Causes | Solution
0x0 | 0 | no error |  |
0x1 | 1 | Internal error |  |
0x2 | 2 | No Rtime |  |
0x3 | 3 | Allocation locked memory error |  |
0x4 | 4 | Insert mailbox error | No ADS mailbox was available to process this message. | Reduce the number of ADS calls (e.g ADS-Sum commands or Max Delay Parameter)
0x5 | 5 | Wrong receive HMSG |  |
0x6 | 6 | target port not found | ADS Server not started |
0x7 | 7 | target machine not found | Missing ADS routes |
0x8 | 8 | Unknown command ID |  |
0x9 | 9 | Bad task ID |  |
0xA | 10 | No IO |  |
0xB | 11 | Unknown ADS command |  |
0xC | 12 | Win 32 error |  |
0xD | 13 | Port not connected |  |
0xE | 14 | Invalid ADS length |  |
0xF | 15 | Invalid ADS Net ID |  |
0x10 | 16 | Low Installation level |  |
0x11 | 17 | No debug available |  |
0x12 | 18 | Port disabled |  |
0x13 | 19 | Port already connected |  |
0x14 | 20 | ADS Sync Win32 error |  |
0x15 | 21 | ADS Sync Timeout |  |
0x16 | 22 | ADS Sync AMS error |  |
0x17 | 23 | ADS Sync no index map |  |
0x18 | 24 | Invalid ADS port |  |
0x19 | 25 | No memory |  |
0x1A | 26 | TCP send error |  |
0x1B | 27 | Host unreachable |  |
0x1C | 28 | Invalid AMS fragment |  |

Router Error Codes

Hex | Dec | Description | Possible Causes | Solution
0x500 | 1280 | ROUTERERR_NOLOCKEDMEMORY | No locked memory can be allocated |
0x501 | 1281 | ROUTERERR_RESIZEMEMORY | The size of the router memory could not be changed |
0x502 | 1282 | ROUTERERR_MAILBOXFULL | The mailbox has reached the maximum number of possible messages. The current sent message was rejected | Check the connection between the communication partners
0x503 | 1283 | ROUTERERR_DEBUGBOXFULL | The mailbox has reached the maximum number of possible messages. The sent message will not be displayed in the debug monitor | Check the connection to the debug monitor
0x504 | 1284 | ROUTERERR_UNKNOWNPORTTYPE | The port type is unknown |
0x505 | 1285 | ROUTERERR_NOTINITIALIZED | Router is not initialised |
0x506 | 1286 | ROUTERERR_PORTALREADYINUSE | The desired port number is already assigned |
0x507 | 1287 | ROUTERERR_NOTREGISTERED | Port not registered |
0x508 | 1288 | ROUTERERR_NOMOREQUEUES | The maximum number of Ports reached |
0x509 | 1289 | ROUTERERR_INVALIDPORT | The port is invalid. |
0x50A | 1290 | ROUTERERR_NOTACTIVATED | TwinCAT Router not active |
0x50B | 1291 | ROUTERERR_FRAGMENTBOXFULL |  |
0x50C | 1292 | ROUTERERR_FRAGMENTTIMEOUT |  |
0x50D | 1293 | ROUTERERR_TOBEREMOVED |  |

General ADS Error Codes

Hex | Dec | Description | Possible Causes | Solution
0x700 | 1792 | error class <device error> |  |
0x701 | 1793 | Service is not supported by server |  |
0x702 | 1794 | invalid index group |  |
0x703 | 1795 | invalid index offset |  |
0x704 | 1796 | reading/writing not permitted |  |
0x705 | 1797 | parameter size not correct |  |
0x706 | 1798 | invalid parameter value(s) |  |
0x707 | 1799 | device is not in a ready state |  |
0x708 | 1800 | device is busy |  |
0x709 | 1801 | invalid context (must be in Windows) |  |
0x70A | 1802 | out of memory |  |
0x70B | 1803 | invalid parameter value(s) |  |
0x70C | 1804 | not found (files, ...) |  |
0x70D | 1805 | syntax error in command or file |  |
0x70E | 1806 | objects do not match |  |
0x70F | 1807 | object already exists |  |
0x710 | 1808 | symbol not found |  |
0x711 | 1809 | symbol version invalid | Onlinechange | Release handle and get a new one
0x712 | 1810 | server is in invalid state |  |
0x713 | 1811 | AdsTransMode not supported |  |
0x714 | 1812 | Notification handle is invalid | Onlinechange | Release handle and get a new one
0x715 | 1813 | Notification client not registered |  |
0x716 | 1814 | no more notification handles |  |
0x717 | 1815 | size for watch too big |  |
0x718 | 1816 | device not initialized |  |
0x719 | 1817 | device has a timeout |  |
0x71A | 1818 | query interface failed |  |
0x71B | 1819 | wrong interface required |  |
0x71C | 1820 | class ID is invalid |  |
0x71D | 1821 | object ID is invalid |  |
0x71E | 1822 | request is pending |  |
0x71F | 1823 | request is aborted |  |
0x720 | 1824 | signal warning |  |
0x721 | 1825 | invalid array index |  |
0x722 | 1826 | symbol not active | Onlinechange | Release handle and get a new one
0x723 | 1827 | access denied |  |
0x724 | 1828 | missing license |  | Activate license for TwinCAT 3 function
0x72c | 1836 | exception occured during system start |  | Check each device transistions
0x740 | 1856 | Error class <client error> |  |
0x741 | 1857 | invalid parameter at service |  |
0x742 | 1858 | polling list is empty |  |
0x743 | 1859 | var connection already in use |  |
0x744 | 1860 | invoke ID in use |  |
0x745 | 1861 | timeout elapsed |  | Check ADS routes of sender and receiver and your firewall setting
0x746 | 1862 | error in win32 subsystem |  |
0x747 | 1863 | Invalid client timeout value |  |
0x748 | 1864 | ads-port not opened |  |
0x750 | 1872 | internal error in ads sync |  |
0x751 | 1873 | hash table overflow |  |
0x752 | 1874 | key not found in hash |  |
0x753 | 1875 | no more symbols in cache |  |
0x754 | 1876 | invalid response received |  |
0x755 | 1877 | sync port is locked |  |

RTime Error Codes

Hex | Dec | Description | Possible Causes
0x1000 | 4096 | RTERR_INTERNAL | Internal fatal error in the TwinCAT real-time system
0x1001 | 4097 | RTERR_BADTIMERPERIODS | Timer value not vaild
0x1002 | 4098 | RTERR_INVALIDTASKPTR | Task pointer has the invalid value ZERO
0x1003 | 4099 | RTERR_INVALIDSTACKPTR | Task stack pointer has the invalid value ZERO
0x1004 | 4100 | RTERR_PRIOEXISTS | The demand task priority is already assigned
0x1005 | 4101 | RTERR_NOMORETCB | No more free TCB (Task Control Block) available. Maximum number of TCBs is 64
0x1006 | 4102 | RTERR_NOMORESEMAS | No more free semaphores available. Maximum number of semaphores is 64
0x1007 | 4103 | RTERR_NOMOREQUEUES | No more free queue available. Maximum number of queue is 64
0x1008 | 4104 | TwinCAT reserved. |
0x1009 | 4105 | TwinCAT reserved. |
0x100A | 4106 | TwinCAT reserved. |
0x100B | 4107 | TwinCAT reserved. |
0x100C | 4108 | TwinCAT reserved. |
0x100D | 4109 | RTERR_EXTIRQALREADYDEF | An external synchronisation interrupt is already applied
0x100E | 4110 | RTERR_EXTIRQNOTDEF | No external synchronsiation interrupt applied
0x100F | 4111 | RTERR_EXTIRQINSTALLFAILED | The apply of the external synchronisation interrupt failed
0x1010 | 4112 | RTERR_IRQLNOTLESSOREQUAL | Call of a service function in the wrong context
0x1017 | 4119 | RTERR_VMXNOTSUPPORTED | Intel VT-x extension is not supported.
0x1018 | 4120 | RTERR_VMXDISABLED | Intel VT-x extension is not enabled in BIOS.
0x1019 | 4121 | RTERR_VMXCONTROLSMISSING | Missing feature in Intel VT-x extension.
0x101A | 4122 | RTERR_VMXENABLEFAILS | Enabling Intel VT-x fails.

TCP Winsock Error Codes

Hex | Dec | Description | Possible Causes | Solution
0x274c | 10060 | A socket operation was attempted to an unreachable host | Host unreachable | Check network connection via ping
0x274d | 10061 | A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond. | Host unreachable | Check network connection via ping
0x2751 | 10065 | No connection could be made because the target machine actively refused it |  |
 |  | Further Winsock error codes: Win32 Error Codes |  |


TcQuickStart Silverlight for Windows Embedded Machine Sample

TwinCAT Quick Start

Example for a machine with Microsoft Silverlight for Windows Embedded

A new item in Windows Embedded CE 6.0 R3 is Silverlight for Windows Embedded. With this new technology, user interfaces of CE devices can now be written in XAML and designed with tools such as Microsoft Expression Blend. On the basis of the machine example, the creation of a Silverlight for Windows Embedded application with integration of the ADS components is described here .

Required software

- Microsoft Visual Studio 2008
- Microsoft Expression Blend 2 SP1
- TwinCAT 2.11

Required hardware

- Windows CE 6.0 R3 device (e.g. CX1020)

First steps...

1. Creating a new Silverlight 2 project:

The design of a Silverlight for Windows Embedded application is written in XAML. To this end a Silverlight 2 project is created with Microsoft Expression Blend 2 SP1 via 'File - > New Project'. The Visual Studio Solution thereby created is not needed in this example. In addition, the selection of the programming language (Visual C# or Visual Basic) can be ignored. Silverlight for Windows Embedded supports only Visual C++, which is not integrated in Expression Blend. It is therefore also not possible to use the source code generated by this tool. It is best to deactivate the integration of Visual Studio in Expression Blend in order to avoid the unnecessary automatic generation of Visual C# and Visual Basic code. To do this, select 'Clipboard only' under 'Options->Event handlers'.

2. Creating a user interface

The user interface can now be created in Expression Blend.

In the upper left area, the two outputs can be seen that are also output to the bus terminals. The bottom left shows the variable for counting the workpieces. The speed can be set on the right. The 'Steps' display corresponds to the number of cycles. In addition, a button for ending the program is created at the top right.

<UserControl
    xmlns="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 -->
        <Storyboard x:Name="timelineDeviceDown">
            <ColorAnimationUsingKeyFrames BeginTime="00:00:00"
                    Storyboard.TargetName="txtDeviceDown"
                    Storyboard.TargetProperty="(TextBlock.Foreground).(SolidColorBrush.Color)">
                <SplineColorKeyFrame KeyTime="00:00:00.4000000" Value="#FFFF0000"/>
            </ColorAnimationUsingKeyFrames>
        </Storyboard>

        <!-- Timeline Device Up -->
        <Storyboard x:Name="timelineDeviceUp">
            <ColorAnimationUsingKeyFrames BeginTime="00:00:00"
                    Storyboard.TargetName="txtDeviceUp"
                    Storyboard.TargetProperty="(TextBlock.Foreground).(SolidColorBrush.Color)">
                <SplineColorKeyFrame KeyTime="00:00:00.4000000" Value="#FFFF0000"/>
            </ColorAnimationUsingKeyFrames>
        </Storyboard>

        <!-- Timeline Engine -->
        <Storyboard x:Name="timelineEngine"/>

    </UserControl.Resources>

    <!-- Beginn der Layout Beschreibung -->
    <Grid x:Name="LayoutRoot" Background="#FF595959">

        <!-- Title Machine -->
        <TextBlock Text="Machine" Margin="80,8.438,80,0" VerticalAlignment="Top"
                FontWeight="Bold" Foreground="#FFFFFFFF" FontSize="34"/>

        <!-- Device Up / Device Down -->
        <Grid Margin="15,60,0,0" HorizontalAlignment="Left" VerticalAlignment="Top"
                Height="53" Width="132.532">
            <Rectangle Fill="{x:Null}" Stroke="#FFFFFFFF" StrokeThickness="2"
                    RadiusX="6" RadiusY="6"/>
            <TextBlock Text="Device Up" Margin="28.823,5.396,-8.823,0"
                    VerticalAlignment="Top" Foreground="#FFFFFFFF" FontSize="16"/>
            <TextBlock Text="Device Down" Margin="29.002,0,-9.002,4.994"
                    VerticalAlignment="Bottom" Foreground="#FFFFFFFF" FontSize="16"/>
            <TextBlock x:Name="txtDeviceDown" Text="ê" Margin="7.517,0,0,4.998"
                    HorizontalAlignment="Left" VerticalAlignment="Bottom" FontFamily="Wingdings"
                    FontWeight="Bold" Foreground="#FFFFFFFF" FontSize="16"/>
            <TextBlock x:Name="txtDeviceUp" Text="é" Margin="7.517,5.497,0,0"
                    HorizontalAlignment="Left" VerticalAlignment="Top" FontFamily="Wingdings"
                    FontWeight="Bold" Foreground="#FFFFFFFF" FontSize="16"/>
        </Grid>

        <!-- Counter -->
        <Grid Margin="15,0,0,71" HorizontalAlignment="Left" VerticalAlignment="Bottom"
                Height="53" Width="133">
            <Rectangle Fill="{x:Null}" Stroke="#FFFFFFFF" StrokeThickness="2"
                    RadiusX="6" RadiusY="6"/>
            <StackPanel Margin="12.991,16,0,16" HorizontalAlignment="Left"
                    Orientation="Horizontal" Width="113">
                <TextBlock Text="Count:" Width="54.824" Foreground="#FFFFFFFF"
                FontSize="16"/>
                <TextBlock x:Name="txtCount" Margin="2,0,0,0" Foreground="#FFFFFFFF"
                        FontSize="16"/>
            </StackPanel>
        </Grid>

        <!-- Speed -->
        <Grid Margin="0,0,15,71" Height="53" HorizontalAlignment="Right"
                VerticalAlignment="Bottom" Width="132.532">
            <Rectangle Fill="{x:Null}" Stroke="#FFFFFFFF" StrokeThickness="2"
                    RadiusX="6" RadiusY="6"/>
            <StackPanel Margin="12.988,0,3.012,0" Orientation="Horizontal">
                <RadioButton x:Name="radSpeedSlow" Content="slow" Margin="0,0,6,0"
                        Foreground="#FFFFFFFF" FontSize="16" Height="19.496"/>
                <RadioButton x:Name="radSpeedFast" Content="fast" Foreground="#FFFFFFFF"
                        FontSize="16" Height="19.496"/>
            </StackPanel>
        </Grid>

        <!-- Steps -->
        <Grid Margin="15,0,15,5" VerticalAlignment="Bottom"
        Height="57">
            <ProgressBar x:Name="prgSteps" Margin="0,18,0,18"
            Maximum="25"/>
            <TextBlock Text="Steps:" HorizontalAlignment="Left"
                    VerticalAlignment="Top" Foreground="#FFFFFFFF"/>
            <TextBlock Text="0%" HorizontalAlignment="Left"
                    VerticalAlignment="Bottom" Foreground="#FFFFFFFF"/>
            <TextBlock Text="100%" HorizontalAlignment="Right"
                    VerticalAlignment="Bottom" Foreground="#FFFFFFFF"/>
        </Grid>

        <!-- Close Button -->
        <Button x:Name="butClose" Content="X"
        HorizontalAlignment="Right" VerticalAlignment="Top"
                Height="20" Width="20"/>

        <!-- Beckhoff Logo -->
        <Image Source="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. Creating a new Win32 Smart Device project

A new Win32 Smart Device project must now be created in Visual Studio 2008.

4. Integrating the XAML file as a resource

The user interface designed with Expression Blend can be integrated in the new project. To do this, open the resource file (.rc). A right mouse click on the resource in the Resource View Tab and the selection of 'Add - > Resource…' opens a dialogue box via which the XAML file can be integrated.

The XAML file can be imported into the project with the aid of the dialogue.

Specify XAML as the resource type.

The standard resource ID (IDR_XAML1) can be retained in this example. However, in real projects it is a good idea to rename this.

5. Creating an AdsHelper Class

The largest part of the Ads communication can be stored in a separate class, called AdsHelper here.

AdsHelper.h

The TcAds headers are integrated in the AdsHelper headers,
#include "..\_AdditionalFiles\TcpAdsApiCe\include\TcAdsDef.h"
#include "..\_AdditionalFiles\TcpAdsApiCe\include\TcAdsAPI.h"

where the declarations are also made.
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

The headers StdAfx.h and AdsHelper.h must be integrated in the AdsHelper.cpp,
#include "StdAfx.h"
#include "AdsHelper.h"

and afterwards the global variables defined.

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

The methods AdsGetVarHandle and AdsGetVarHandleEx serve to create handles for PLC variables
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;
}

Handles for PLC variables are released again with AdsFreeVarHandle and AdsFreeVarHandleEx.
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(handle), &handle);
}

The following two methods write the PLC variable '.switch' and in doing so set the speed to slow or fast.
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 setzen
    bool 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 setzen
    bool 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 the connect method, a connection to the variables is created in the PLC.
long connect (long port, AmsAddr* pServerAddr, 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 holen

    long 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 initialisieren

    if(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;
}

When disconnecting the Ads connection, the handles of the PLC variables must be released and the port closed.
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. Processing the source code

The headers are integrated first in the SilverlightForEmbeddedMachineSample.cpp file.

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

#include "resource.h"

The declaration of the variables follows.
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);

The timer callback function checks the Ads connection and restores the connection if necessary.
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 freigegen 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;
}

The following Ads Event handler is called if a PLC variable to which a link exists changes.

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

The corresponding arrows are coloured red or not, depending on whether deviceUp, deviceDown or engine is set to TRUE.

This effect can be improved still further by the use of timelines.

    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 indicates the number of cycles. The value is output via the progress bar prgSteps. For this the data must first be converted to a byte, since the associated PLC variable is of the type byte. Since the progress bar can only transfer data of the type float, conversion to the type float subsequently takes place.
    else if (User == steps)
    {
        prgSteps->SetValue((float)*((byte*)handler->data));
    }

In the case of count, as with steps, the data must first be converted to their original data type before they can be converted into a text and transferred to the text block txtCount.
    else if (User == count)
    {
        WCHAR text[6];
        wsprintf(text, L"%d", *(unsigned short*)handler->data);
        txtCount->SetText(text);
    }

The speed type is output via radio buttons. The appropriate radio button is marked depending on the speed.
    else if (User == switchSpeed)
    {
        if (*(bool*)handler->data == true)
        {
            radSpeedFast->SetIsChecked(XRThreeState_Checked);
        }
        else
        {
            radSpeedSlow->SetIsChecked(XRThreeState_Checked);
        }
    }
}

The OnClick event is triggered by various instances and the initiating instance can be distinguished from the name

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 freigegen 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 the WinMain method, the XMAL runtime must be initialised first. If XamlRuntimeInitialize is successful, then Silverlight for Windows Embedded Runtime is started in the application.

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

Each Silverlight for Windows Embedded application has its own "applications" object, via which access to global characteristics is possible.
The GetXRApplicationInstance API is used in order to access this object.
    HRESULT retcode;

    // Load an dinit XAML resource
    IXRApplicationPtr app;

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

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

After the initialisation of the application object, the main window can be created and the administration of the object can be handed over to Silverlight for Windows Embedded.
    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;

The object within a Silverlight for Windows Embedded application is organised in object trees. In order to access this object, a pointer to the root element is required.
    IXRFrameworkElementPtr root;

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

Creation of instances of the controls and timelines.
    // Get controls by name
    if (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 name
    if (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;

Creating the "RadioButtonGroup" and assigning the two radio buttons from this group.
    WCHAR groupName[17];
    wsprintf(groupName, L"RadioButtonGroup");
    radSpeedFast->SetGroupName(groupName);
    radSpeedSlow->SetGroupName(groupName);

A redirecting object is needed to link the EventHandler with the buttons.
    BtnEventHandler handler;

    // Set the event handler for the buttons
    if (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;

Integrating the Ads components.
    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. Features

A connection to xamlruntime.lib and TcAdsDllCe.lib must be made in the project features.

Download Silverlight for Windows Embedded example

Sample Silverlight for Windows Embedded

Note:
The sample uses the X86 version of the TcAdsDllCe.lib. To use the sample with ARM devices you have to change this library with the corresponding ARM version.


TwinCAT Quick Start - Beispiel Maschine mit Silverlight JavaScript

TwinCAT Quick Start

Sample Machine with Microsoft Silverlight and JavaScript

Microsoft Silverlight is a web-presentation-technology.

Required Software:

- runtime:
- Microsoft Silverlight 1.0 / 1.1

- developer tools:
- Microsoft Visual Studio 2008 Beta 2
- Microsoft Silverlight Tools Alpha Refresh for Visual Studio (July 2007)
or
- Microsoft Visual Studio 2005
- Microsoft Silverlight 1.0 Software Development Kit
For this sample Microsoft Visual Studio 2005 was used.

- designer tools:
- Expression Blend 2

- others:
- TwinCAT 2.10
- Browser (for example Internet Explorer 7 or Mozilla Firefox 2)
- Microsoft .NET Framework Version 3.0

The first steps...

Step for step you learn to develop a Silverlight-Application and include the TwinCAT ADS Web Service on a sample.

1. Create new Project:

Start the Microsoft Expression Blend 2 and create a new user interface over 'Menu → File → new Project...' . If the Dialog 'Create New Project' open, now you can edit the type, the name, the location and the language. In this sample choose the type 'Silverlight Application (JavaScript)' and the name 'Machine'.

2. Create user interface:

A few controls are available for the development of the user interfaces in Silverlight applications. You can create a RadioButton with a Textbox and two Ellipses, whitch are grouped into a Canvas. The Progressbar can be created with three Rectangles which are grouped into a separate Canvas again.
The properties of the interface are described in the Page.xaml file.
Note that you can not set the object size to 'Auto'. This may lead to errors later.

You can see the two outputs in the upper left corner. The variable, which counts the work pieces, is placed below. You can change the engine speed with the two RadioButtons on the right side. The displayed steps are equivalent to the number of clocks that are returned to output 1.

3. Add XMLHTTP.JS

In Visual Studio over 'Project → Add Existing Item...' add the file 'XMLHTTP.JS'. This file contains general methods for reading and writing of PLC variables and converting data types.

4. Edit source code

Open the file 'Default.html' in Visual Studio and include the 'XMLHTTP.JS' file into the header.

<script type="text/javascript" src="xmlhttp.js"></script>

Add a JavaScript-range in the HEAD-field of the HTML page. You find the following source code in it:

The global variables has to be declared first.

<script type="text/javascript">

    //enter URL to webservice here:
    var url = "http://localhost/TcAdsWebService/TcAdsWebService.dll";
    //enter netId here:
    var netId = "172.16.2.63.1.1";
    //enter the port here:
    var port = 811;
    //send soap request every x seconds:
    var refresh = 1000;

    var inuse = false;

    var b64s, success, errors, req;

    var vUp, vDown, vProgressbar, vCount, vFast, vSlow;

...

You have to adapt the url of the TcAdsWebService, the netID and the port.

The GUI objects can not be accessed directly. In order to do this the objects will be assigned to already declared variables after the start of the application.

function Load(sender, eventArgs)
{
    vUp = sender.findName("pathUp");
    vDown = sender.findName("pathDown");
    vProgressbar = sender.findName("recProgressbar");
    vCount = sender.findName("txbCount");
    vFast = sender.findName("ellPointFast");
    vSlow = sender.findName("ellPointSlow");
}

The loading method contains the assignation of the variables. For executing the Load method Loaded="Load" has to be added to the Canvas on top of the Page.xaml.

<Canvas
    xmlns="http://schemas.microsoft.com/client/2007"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Width="368" Height="256"
    x:Name="Page"
    Loaded="Load"
>
...

function loop(x)
{
    Read(netId, port, '16416', '0', '86'); //send soap read request via xmlhttprequest
    window.setTimeout("loop("+x+")", x);
}

Read PLC variable

Read(netId, nPort, indexGroup, indexOffset, cbLen)
  • netId: String indicating the AMS-Net-Id of the PLC
  • nPort: Port-number of the runtime-system
  • indexGroup: IndexGroup of the variables to read
  • indexOffset: First byte to read
  • ncbLen: Number of bytes to read

function init()
{
    b64s = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
    success = 0;
    errors = 0;
    loop(refresh); //send request every x seconds
}

Add onload="init()" into <body>, so that the method will be executed during loading.

<body onload="init()" >

The next function reads values and displays them. It is important that the address of the variable in Machine.pro is correctly. See also: Declaration of variables...

function processReqChange()
{
    /*
    readyStates:
    0 = uninitialized
    1 = loading
    2 = loaded
    3 = interactive
    4 = complete
    */

    if (req.readyState == 4)
    {
        // only if "OK"
        if (req.status == 200)
        {
            response = req.responseXML.documentElement;

            inuse = false;

            try //check if there was an error in the request
            {
                errortext = response.getElementsByTagName('faultstring')[0].firstChild.data;
                try
                {
                    errorcode = response.getElementsByTagName('errorcode')[0].firstChild.data;
                }
                catch (e){errorcode="-";}
                    alert(errortext + " ("+errorcode+")");
                return;
            }
            catch (e)
            {
                errorcode=0;
            }

            var data;
            try //if the server returns a <ppData> element decode it, otherwise (write request) do nothing
            {
                data = response.getElementsByTagName('ppData')[0].firstChild.data;
                mode = "read";
            }
            catch (e)
            {
                data = "";
                mode = "";
            }

            if (mode=="read")
            {
                try
                {
                    data = b64t2d(data); //decode result string

                    steps = toInt(data.substr(1, 2));

                    bool = toInt(data.substr(5,2));
                    bool2 = toInt(data.substr(4,2));

                    count = toInt(data.substr(3, 2));

                    speed = toInt(data.substr(6, 2));
                }
                catch (e)
                {
                    alert("Parsing Failed:" + e);
                    return;
                }

                vProgressbar.Width = 306.321/100*steps*4;

                if (bool2 != "1")
                    { vCount.Text = count.toString();}

                if (bool == "1")
                    { vUp.Opacity=1.0;
                      vDown.Opacity=0.0; }
                else if (bool2 == "1")
                    { vUp.Opacity=0.0;
                      vDown.Opacity=1.0; }
                else
                    { vUp.Opacity=0.0;
                      vDown.Opacity=0.0; }

                if (speed == "0")
                   { vFast.Opacity=1.0;
                     vSlow.Opacity=0.0; }
                else
                   { vSlow.Opacity=1.0;
                     vFast.Opacity=0.0; }

            }
        }
        else alert(req.statusText+" "+req.status); //cannot retrieve xml data
    }
}

With the last two methods the PLC variable, which controls the speed of the machine, is set to zero or one.

function Fast_MouseLeftButtonDown(sender, eventArgs)
{
    Write(netId, port, '16416', '6', '2', '0', 'int');
}
function Slow_MouseLeftButtonDown(sender, eventArgs)
{
    Write(netId, port, '16416', '6', '2', '1', 'int');
}

Write SPS Variables

Write(netId, port, indexGroup, indexOffset, cbLen, pwrData, type)
  • netId: String indicating the AMS.Net.Id of the PLC
  • nPort: Port-number of the runtime-system
  • indexGroup: IndexGroup of the variable
  • indexOffset: First byte to write to
  • ncbLen: Number of bytes to write
  • pwrData: Array containing the data to write
  • type: "bool", "int" or "string"

It has to be changed to 'Expression Blend 2' as it has been done with the 'Load' function for declaring the according rows as 'Click' event for both buttons.

<Canvas x:Name="canvasFast" MouseLeftButtonDown="Fast_MouseLeftButtonDown" ...
<Canvas x:Name="canvasSlow" MouseLeftButtonDown="Slow_MouseLeftButtonDown" ...

5. Testing:

By debugging the application you will recognize that it will not work as expected. To prevent this got to 'Build -> Publish'.

Choose the 'Target location' in the dialog window and then 'All project files' under 'Copy'.

If the statusbar says 'Publish succeded', you can run and test the application in the browser.

Download:

Silverlight-Sample


TwinCAT Quick Start example: Machine with Microsoft Expression Blend in Microsoft Windows Vista Media Center

TwinCAT Quick Start

Example: Machine with Microsoft Expression Blend in Microsoft Windows Vista Media Center

Microsoft Expression Blend is a program for creating program interfaces for C# and Visual Basic. In this example an interface created with the program is linked with the Machine example and subsequently integrated in the Microsoft Windows Vista Media Center. The programming language C# was used.

Required software:

- Microsoft .NET Framework Version 3.0, for more information click here
- Microsoft Expression Blend, for further information click here
- Microsoft Visual Studio 2005
- Microsoft Windows Vista Media Center
- Microsoft Windows SDK for .Net Framework 3.0, for more information click here
- Microsoft Visual Studio 2005 extensions for .Net Framework 3.0 (November 2006 CTP), for more information click here
- TwinCAT 2.10
- Notepad or other text editor

First steps ...

Step by step familiarisation with the development of a program with Microsoft Visual Studio and Microsoft Expression Blend, integration of the TwinCAT ADS .NET component based on an example, and integration in the Microsoft Windows Vista Media Center.

1. Create new project:

Start Microsoft Visual Studio and create new XAML browser application. Proceed via the menu 'File -> New -> Project...' . The dialog box 'New Project' opens.

First select the project type: 'Project types -> Visual C# -> Net Framework 3.0'. The project type templates appear on the right. Select 'XAML Browser Application'. Enter a name for your project (in this case Machine) and specify the location.

2. Creating a user interface

Now change to Microsoft Expression Blend and open the project you just created in order to create the user interface.

In the upper left you see the two outputs that are also output to the Bus Terminals. The bottom left shows the variable for counting the workpieces. The cycle speed of the motor can be changed via the 'Speed' field on the right. The 'Steps' display shows the number of cycles that are output on output 1.

To make the user interfaces constantly adapt their size, copy the upper grid and insert a view box instead of the grid. Now insert the grid into the view box. Now set the size of the page, the view box and the grid to 'Auto'. This may result in shifting of elements, in which case you have to reposition them. Please ensure that the size of the page, the view box and the grid is not inadvertently reset to "fixed".

3. Adding a reference

Once the interface has been created, add a reference called 'TwinCAT.Ads.dll'. This can be done in Visual Studio or Expression Blend. In both cases proceed via the menu 'Project --> Add Reference'.

4. Security activation

Select 'Project -> <Project Name> Properties....' from the menu . .

A tab opens, in which you can specify the project properties. Select 'Security' and then 'this is a full trust application'.

5. Edit source text

You can now start creating the source text in C#.
The required namespaces 'System.IO' and 'TwinCAT.Ads' are added in the top row of the source text.

using  System.IO;
using  TwinCAT.Ads;

The next step involves the declarations.

private TcAdsClient tcClient;
private AdsStream dataStream;
private BinaryReader binReader;
private int hEngine;
private int hDeviceUp;
private int hDeviceDown;
private int hSteps;
private int hCount;
private int hSwitchNotify;
private int hSwitchWrite;

The first method is the 'Load' method. It is used to generate instances of different classes and create a link to port 801.

//-----------------------------------------------------
// Wird als erstes beim Starten des Programms aufgerufen
// Is activated first when the program is started
//-----------------------------------------------------
private void Load(object sender, EventArgs e)
{
    try
    {
    // Eine neue Instanz der Klasse AdsStream erzeugen
    // Create an new instance of the AdsStream class
    dataStream = new AdsStream(7);

    // Eine neue Instanz der Klasse BinaryReader erzeugen
    // Create a new instance of the BinaryReader class
    binReader = new BinaryReader(dataStream);

    // Eine neue Instanz der Klasse TcAdsClient erzeugen
    // Create an new instance of the TcAdsClient class
    tcClient = new TcAdsClient();

    // Verbinden mit lokaler SPS - Laufzeit 1 - Port 801
    // Connecting to local PLC - Runtime 1 - Port 801
    tcClient.Connect(801);
    }
    catch
    {
    MessageBox.Show("Fehler beim Laden");
    }

    //...

The variables in the 'Load' method are then linked and linked with a method (that still has to be written), which is called when a variable changes.

    try
    {
        // Initialisieren der Überwachung der SPS-Variablen
        // Initalizing the monitoring of the PLC variables
        hEngine = tcClient.AddDeviceNotification(".engine", dataStream, 0, 1, AdsTransMode.OnChange, 10, 0, null);
        hDeviceUp = tcClient.AddDeviceNotification(".deviceUp", dataStream, 1, 1, AdsTransMode.OnChange, 10, 0, null);
        hDeviceDown = tcClient.AddDeviceNotification(".deviceDown", dataStream, 2, 1, AdsTransMode.OnChange, 10, 0, null);
        hSteps = tcClient.AddDeviceNotification(".steps", dataStream, 3, 1, AdsTransMode.OnChange, 10, 0, null);
        hCount = tcClient.AddDeviceNotification(".count", dataStream, 4, 2, AdsTransMode.OnChange, 10, 0, null);
        hSwitchNotify = tcClient.AddDeviceNotification(".switch", dataStream, 6, 1,  AdsTransMode.OnChange, 10, 0, null);

        // Holen des Handles von 'switch' - wird für das Schreiben des Wertes benötigt
        // Getting the handle for 'switch' - needed for writing the value
        hSwitchWrite = tcClient.CreateVariableHandle(".switch");

        // Erstellen eines Events für Änderungen an den SPS-Variablen-Werten
        // Creating an event for changes of the PLC variable value
        tcClient.AdsNotification += new AdsNotificationEventHandler(tcClient_OnNotification);
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

6. Definition

Linking PLC variables:
The method AddDeviceNotification was used for linking the variables.
 public int AddDeviceNotification(string variableName, AdsStream dataStream, int  offset, int length, AdsTransMode transMode, int cycleTime, int maxDelay, object userData);
  • variableName: Name of the PLC variable.
  • dataStream: Data stream receiving the data.
  • offset: Interval in the data stream.
  • length: Length in the data stream.
  • transMode: Event if the variable changes.
  • cycletime: Time (in ms) after which the PLC server checks whether the variable has changed.
  • maxDelay: Latest time (in ms) after which the event has finished.
  • userData: Object that can be used for storing certain data.

The method CreateVariableHandle was used for linking the variable 'hSwitchWrite'.

int TcAdsClient.CreateVariableHandle(string variableName);

  • variableName: Name of the PLC variable.

7. Write method:

A method that does not exist yet was referred to above. This method ('tcClient_OnNotification') is written next. The method is called if one of the PLC variables has changed.

//------------------------------------------------
// wird bei Änderung einer SPS-Variablen aufgerufen
// is activated when a PLC variable changes
//------------------------------------------------
private void tcClient_OnNotification(object sender, AdsNotificationEventArgs e)
{
    try
    {
        // Setzen der Position von e.DataStream auf die des aktuellen benötigten Wertes
        // Setting the position of e.DataStream to the position of the current needed value
        e.DataStream.Position = e.Offset;

        // Ermittlung welche Variable sich geändert hat
        // Detecting which variable has changed
        if(e.NotificationHandle == hDeviceUp)
        {
            // Die Farben der Grafiken entsprechened der Variablen anpassen
            // Adapt colors of graphice according to the variables
            if (binReader.ReadBoolean() == true)
            {
                DeviceUp_LED.Foreground = new SolidColorBrush(Colors.Red);
            }
            else
            {
                DeviceUp_LED.Foreground = new SolidColorBrush(Colors.White);
            }
        }
        else if(e.NotificationHandle == hDeviceDown)
        {
            if (binReader.ReadBoolean() == true)
            {
                DeviceDown_LED.Foreground = new SolidColorBrush(Colors.Red);
            }
            else
            {
                DeviceDown_LED.Foreground = new SolidColorBrush(Colors.White);
            }
        }
        else if(e.NotificationHandle == hSteps)
        {
            // Einstellen der ProgressBar auf den aktuellen Schritt
            // Setting the ProgressBar to the current step
            prgSteps.Value = binReader.ReadByte();
        }
        else if(e.NotificationHandle == hCount)
        {
            // Anzeigen des 'Zähler'-Wertes
            // Displaying the 'count' value
            lblCount.Text = binReader.ReadUInt16().ToString();
        }
        else if(e.NotificationHandle == hSwitchNotify)
        {
            // Markieren des korrekten RadioButtons
            // Checking the correct RadioButton
            if (binReader.ReadBoolean() == true)
            {
                optSpeedFast.IsChecked = true;
            }
            else
            {
                optSpeedSlow.IsChecked = true;
            }
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}
Lastly, we require two methods for setting the machine to fast or slow. They are used to switch a virtual switch by writing a value to the PLC variable 'switch'.
//------------------------------------------------------
// wird aufgerufen, wenn das Feld 'schnell' markiert wird
// is activated when the 'fast' field is marked
//------------------------------------------------------
private void optSpeedFast_Click(object sender, EventArgs e)
{
    try
    {
        tcClient.WriteAny(hSwitchWrite, true);
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

//------------------------------------------------------
// wird aufgerufen, wenn das Feld 'langsam' markiert wird
// is activated when the 'slow' field is marked
//------------------------------------------------------
private void optSpeedSlow_Click(object sender, EventArgs e)
{
    try
    {
        tcClient.WriteAny(hSwitchWrite, false);
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

8. Delete notifications and handles:

In the Close event of the window the links are activated again with the method DeleteDeviceNotification().

//------------------------------------------------------
// wird beim Beenden des Programms aufgerufen
// is activated when ending the program
//------------------------------------------------------
private void Close(object sender, EventArgs e)
{
    try
    {
        // Löschen der Notifications und Handles
        // Deleting of the notification and handles
        tcClient.DeleteDeviceNotification(hEngine);
        tcClient.DeleteDeviceNotification(hDeviceUp);
        tcClient.DeleteDeviceNotification(hDeviceDown);
        tcClient.DeleteDeviceNotification(hSteps);
        tcClient.DeleteDeviceNotification(hCount);
        tcClient.DeleteDeviceNotification(hSwitchNotify);

        tcClient.DeleteVariableHandle(hSwitchWrite);
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
    tcClient.Dispose();
}

Last but not least we need to ensure that the methods are called for the right event. To this end open Expression Blend, select Page, change to Events in Properties and enter 'Load' under 'Loaded' and 'Close' under 'Unloaded'. Proceed accordingly for the two radio buttons, except that the 'Click' event should be selected and the method 'optSpeedFast_Click' or 'optSpeedSlow_Click'.
The Machine_Final.pro PLC machine program must run in runtime system 1, and the program can be tested in Internet Explorer 7.

9. Integration in Vista Media Center

Once you have tested your project sufficiently and no errors were detected, you can integrate it in the Microsoft Windows Vista Media Center.
In Visual Studio call up the project properties again, but this time select 'Publish'. Click on 'Publish Now'. This creates an xbap file which you subsequently call up in the Media Center. This step is always required whenever the program has been modified and the change is to be transferred to the Media Center.

Now open a text editor (e.g. Notepad) and enter:
<application
        URL = "C:\Users\<User>\Documents\Visual Studio 2005\Projects\Machine\Machine\Publish\Machine.xbap">
</application>

Save this under: 'C:\Users\<User>\AppData\Roaming\Media Center Programs\Machine.mcl'. If you now start your Media Center you will find your program under 'Online Media -> program library -> programs by name -> Machine'.
This is the simplest form of integration in the Microsoft Windows Vista Media Center. Further information on integration in the Media Center can be found here.

10. Download Expression Blend example:

Expression example


Example: Machine with Microsoft Expression Blend in Microsoft Windows Vista Media Center

Microsoft Expression Blend is a program for creating program interfaces for C# and Visual Basic. In this example an interface created with the program is linked with the Machine example and subsequently integrated in the Microsoft Windows Vista Media Center. The programming language Visual Basic was used.

Required Software:

- Microsoft .NET Framework Version 3.0, further information here
- Microsoft Expression Blend, further information here
- Microsoft Visual Studio 2005
- Microsoft Windows Vista Media Center
- Microsoft Windows SDK for .Net Framework 3.0, further information here
- Microsoft Visual Studio 2005 extensions for .Net Framework 3.0 (November 2006 CTP), further information here
- TwinCAT 2.10
- Notepad or another text editor

First steps ...

Step by step familiarisation with the development of a program with Microsoft Visual Studio and Microsoft Expression Blend, integration of the TwinCAT ADS .NET component based on an example, and integration in the Microsoft Windows Vista Media Center.

1. Create new project:

Start Microsoft Visual Studio and create new XAML browser application. Proceed via the menu 'File -> New -> Project...' . The dialog box 'New Project' opens.

First select the project type: 'Project types -> Visual Basic -> Net Framework 3.0'. The project type templates appear on the right. Select 'XAML Browser Application'. Enter a name for your project (in this case Machine) and specify the location.

2. Creating a user interface

Now change to Microsoft Expression Blend and open the project you just created in order to create the user interface.

In the upper left you see the two outputs that are also output to the Bus Terminals. The bottom left shows the variable for counting the workpieces. The cycle speed of the motor can be changed via the 'Speed' field on the right. The 'Steps' display shows the number of cycles that are output on output 1.

To make the user interfaces constantly adapt their size, copy the upper grid and insert a view box instead of the grid. Now insert the grid into the view box. Now set the size of the page, the view box and the grid to 'Auto'. This may result in shifting of elements, in which case you have to reposition them. Please ensure that the size of the page, the view box and the grid is not inadvertently reset to "fixed".

3. Adding a reference

Once the interface has been created, add a reference called 'TwinCAT.Ads.dll'. This can be done in Visual Studio or Expression Blend. In both cases proceed via the menu 'Project --> Add Reference'.

4. Security activation

Select 'Project -> <Project Name> Properties....' from the menu . .

A tab opens, in which you can specify the project properties. Select 'Security' and then 'this is a full trust application'.

5. Edit source text

You can now start creating the source text in C#.
The required namespaces 'System.IO' and 'TwinCAT.Ads' are added in the top row of the source text.

Imports  System.IO
Imports  TwinCAT.Ads

The next step involves the declarations.

Private hEngine As Integer
Private hDeviceUp As Integer
Private hDeviceDown As Integer
Private hSteps As Integer
Private hCount As Integer
Private hSwitchNotify As Integer
Private hSwitchWrite As Integer

Private tcClient As TwinCAT.Ads.TcAdsClient
Private dataStream As TwinCAT.Ads.AdsStream
Private binReader As System.IO.BinaryReader

The first method is the 'Load' method. It is used to generate instances of different classes and create a link to port 801.

Private Sub Page1_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
        Try
            ' Eine neue Instanz der Klasse AdsStream erzeugen
            ' Create a new instance of the AdsStream class
            dataStream = New AdsStream(7)

            ' Eine neue Instanz der Klasse BinaryReader erzeugen
            ' Create a new instance of the BinaryReader class
            binReader = New BinaryReader(dataStream)

            ' Eine neue Instanz der Klasse TcAdsClient erzeugen
            ' Create a new instance of the TcAdsClient class
            tcClient = New TwinCAT.Ads.TcAdsClient()

            ' Verbinden mit lokaler SPS - Laufzeit 1 - Port 801
            ' Connecting to local PLC - Runtime 1 - Port 801
            tcClient.Connect(801)
        Catch
            MessageBox.Show("Error while loading")
        End Try
...

The variables in the 'Load' method are then linked and linked with a method (that still has to be written), which is called when a variable changes.

    Try
            ' Initialisieren der Überwachung der SPS-Variablen
            ' Initializing the monitoring of the PLC variables
            hEngine = tcClient.AddDeviceNotification(".engine", dataStream, 0, 1, AdsTransMode.OnChange, 10, 0, DBNull.Value)
            hDeviceUp = tcClient.AddDeviceNotification(".deviceUp", dataStream, 1, 1, AdsTransMode.OnChange, 10, 0, DBNull.Value)
            hDeviceDown = tcClient.AddDeviceNotification(".deviceDown", dataStream, 2, 1, AdsTransMode.OnChange, 10, 0, DBNull.Value)
            hSteps = tcClient.AddDeviceNotification(".steps", dataStream, 0, 1, AdsTransMode.OnChange, 10, 0, DBNull.Value)
            hCount = tcClient.AddDeviceNotification(".count", dataStream, 4, 2, AdsTransMode.OnChange, 10, 0, DBNull.Value)
            hSwitchNotify = tcClient.AddDeviceNotification(".switch", dataStream, 6, 1, AdsTransMode.OnChange, 10, 0, DBNull.Value)

            ' Holen des Handles von "switch" - wird für das Schreiben des Wertes benötigt
            ' Getting the handle for "switch" - needed for writing the value
            hSwitchWrite = tcClient.CreateVariableHandle(".switch")

            ' Erstellen eines Events für Änderungen an den SPS-Variablen-Werten
            ' Creating an event for changes of the PLC variable values
            AddHandler tcClient.AdsNotification, AddressOf tcClient_OnNotification
        Catch
            MessageBox.Show("Error when connecting")
        End Try
    End Sub

6. Definition

Linking PLC variables:
The method AddDeviceNotification was used for linking the variables.
 public int AddDeviceNotification(string variableName, AdsStream dataStream, int  offset, int length, AdsTransMode transMode, int cycleTime, int maxDelay, object userData);
  • variableName: Name of the PLC variable.
  • dataStream: Data stream receiving the data.
  • offset: Interval in the data stream.
  • length: Length in the data stream.
  • transMode: Event if the variable changes.
  • cycletime: Time (in ms) after which the PLC server checks whether the variable has changed.
  • maxDelay: Latest time (in ms) after which the event has finished.
  • userData: Object that can be used for storing certain data.

The method CreateVariableHandle was used for linking the variable 'hSwitchWrite'.

int TcAdsClient.CreateVariableHandle(string variableName);

  • variableName: Name of the PLC variable.

7. Write method:

A method that does not exist yet was referred to above. This method ('tcClient_OnNotification') is written next. The method is called if one of the PLC variables has changed.

'------------------------------------------------
' wird bei Änderung einer SPS-Variablen aufgerufen
' is activated when a PLC variable changes
'------------------------------------------------
Private Sub tcClient_OnNotification(ByVal sender As Object, ByVal e As AdsNotificationEventArgs)
        Try
            ' Setzen der Position von e.DataStream auf die des aktuellen benötigten Wertes
            ' Setting the position of e.DataStream to the position of the current needed value
            e.DataStream.Position = e.Offset

            ' Ermittlung welche Variable sich geändert hat
            ' Detecting which variable has changed
            If (e.NotificationHandle = hDeviceUp) Then
                'Die Farben der Grafiken entsprechened der Variablen anpassen
                'Adapt colors of graphics according to the variables
                If (binReader.ReadBoolean() = True) Then
                    DeviceUp_LED.Foreground = New SolidColorBrush(Colors.Red)
                Else
                    DeviceUp_LED.Foreground = New SolidColorBrush(Colors.White)
                End If
            ElseIf (e.NotificationHandle = hDeviceDown) Then
                If (binReader.ReadBoolean() = True) Then
                    DeviceDown_LED.Foreground = New SolidColorBrush(Colors.Red)
                Else
                    DeviceDown_LED.Foreground = New SolidColorBrush(Colors.White)
                End If
            ElseIf (e.NotificationHandle = hSteps) Then
                ' Einstellen der ProgressBar auf den aktuellen Schritt
                ' Setting the ProgressBar to the current step
                prgSteps.Value = (binReader.ReadByte() * 4)
            ElseIf (e.NotificationHandle = hCount) Then
                ' Anzeigen des "count"-Werts
                ' Displaying the "count" value
                lblCount.Content = binReader.ReadUInt16().ToString()
            ElseIf (e.NotificationHandle = hSwitchNotify) Then
                ' Markieren des korrekten RadioButtons
                ' Checking the correct RadioButton
                If (binReader.ReadBoolean() = True) Then
                    optSpeedFast.IsChecked = True
                Else
                    optSpeedSlow.IsChecked = True
                End If
            End If
        Catch
            MessageBox.Show("Error")
        End Try
    End Sub
Lastly, we require two methods for setting the machine to fast or slow. They are used to switch a virtual switch by writing a value to the PLC variable 'switch'.
'------------------------------------------------------
' wird aufgerufen, wenn das Feld 'schnell' markiert wird
' is activated when the 'fast' field is marked
'------------------------------------------------------
Private Sub optSpeedFast_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles optSpeedFast.Click
        Try
            tcClient.WriteAny(hSwitchWrite, True)
        Catch
            MessageBox.Show("Error")
        End Try
End Sub
'------------------------------------------------------
' wird aufgerufen, wenn das Feld 'langsam' markiert wird
' is activated when the 'slow' field is marked
'------------------------------------------------------
Private Sub optSpeedSlow_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles optSpeedSlow.Click
        Try
            tcClient.WriteAny(hSwitchWrite, False)
        Catch
            MessageBox.Show("Error")
        End Try
End Sub

8. Delete notifications and handles:

In the Close event of the window the links are activated again with the method DeleteDeviceNotification().

//------------------------------------------------------
// wird beim Beenden des Programms aufgerufen
// is activated when ending the program
//------------------------------------------------------
Private Sub Page1_Unloaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Unloaded
        Try
            ' Löschen der Notifications und Handles
            ' Deleting of the notifications and handles
            tcClient.DeleteDeviceNotification(hEngine)
            tcClient.DeleteDeviceNotification(hDeviceUp)
            tcClient.DeleteDeviceNotification(hDeviceDown)
            tcClient.DeleteDeviceNotification(hSteps)
            tcClient.DeleteDeviceNotification(hCount)
            tcClient.DeleteDeviceNotification(hSwitchNotify)

            tcClient.DeleteVariableHandle(hSwitchWrite)
        Catch
            MessageBox.Show("Error")
        End Try
        tcClient.Dispose()
End Sub

The Machine_Final.pro PLC machine program must run in runtime system 1, and the program can be tested in Internet Explorer 7.

9. Integration in Vista Media Center

Once you have tested your project sufficiently and no errors were detected, you can integrate it in the Microsoft Windows Vista Media Center.
In Visual Studio call up the project properties again, but this time select 'Publish'. Click on 'Publish Now'. This creates an xbap file which you subsequently call up in the Media Center. This step is always required whenever the program has been modified and the change is to be transferred to the Media Center.

Now open a text editor (e.g. Notepad) and enter:

<application
        URL = "C:\Users\<User>\Documents\Visual Studio 2005\Projects\Machine\Machine\Publish\Machine.xbap">
</application>

Save this under: 'C:\Users\<User>\AppData\Roaming\Media Center Programs\Machine.mcl'. If you now start your Media Center you will find your program under 'Online Media -> program library -> programs by name -> Machine'.
This is the simplest form of integration in the Microsoft Windows Vista Media Center. Further information on integration in the Media Center can be found here.

10. Download Expression Blend example:

Expression example