Skip to Content

category

Category: Industrial Automation

Installing TwinCAT 3 XAE on Windows 10 x86 VM

Installing TwinCAT ain’t always issue free download->install->restart->run. Such was my case with following clean setup on Virtual Machine:

  • Windows 10 Enterprise 32bit
  • TwinCAT 3.1 Build 4020

where I received this nice error message: “the installation of microsoft visual studio 2013 integrated shell has failed

First thought, restart and try again, usually worked on another errors on different configurations, but not this time.

Visual Studio 2013 Integrated Shell

Download this small installer here and run. It’ll probably end up with “Windows Program Compatibility mode is on. Turn it off and then try Setup again.” such as in my case. To save you googleing, copy installer to another location and rename to VS_ISOSHELL, that should solve the problem.

Next time you run TwinCAT XAE installer, it should detect existing VS2013 shell, as it would with any other installed VS > 2010, and offer to integrate it.

Existing VS shell integration

When installing TwinCAT XAE on system with existing VS installation, be aware that it will most probably mess up your VS window settings.

Reboot

Some time has passed since the last post… actually a long time.

Quick DajSiePoznac summary

Let’s be honest, it was an utter failure. At some point, due to participating in too many projects I faced hard choice regarding my spare time, bloggers fame and glory or sleep, I choose the letter, yet had none of the above. Pure magic. When it comes to visitors, one is sure, bots love my writing. They visit and comment everyday, day and night (if only those comments made any sense). Beside them… well… even saying friends and family is exaggeration.

What’s next?

Since I don’t have enough time to finish library I started to write for DajSiePoznac, I’ll focus on things I use, and will use in following months. Which may be interesting for some (doubt that). By coincidence I will have to learn some new stuff, and learn it fast. Google based crash course you may say, and what’s better way to understand stuff, than explaining it to another?

Real-Time, TwinCAT 3 and LIN

So far I wrote CAN/LIN communication software in C#, and lately in F#. On a horizon, I have project which requires hard RT, and thus .NET is not an option (damn you Garbage Collector!!!).

CLR GC is a poor choice for hard RT

Before you argue, if an actual hard real-time is needed for LIN communication, or it can be worked around for performance, ask yourself, would you’d like to end up with premium car with incorrectly calibrated seat-heater, just because GC kicked-in. I thought so.

I’m kind of excited when I think of it.

  • new programming language and environment (so far I only know how to do pretty basic, PASCAL-like stuff in  TwinCAT 2)
  • new hardware (never worked with Beckhoff EL6001  Serial Interface Terminal before)
  • tight timing for operations

how bad can it get?

Alternatives

Haskell

So far I considered few alternatives, Haskell EDSLs Ivory and Atom, as funprog is jazzy, but no one I know could confirm it’s actually suited for my application, it would mean writing additional wrappers for manufacturer provided drivers, for Kvaser LIN Leaf or Vector VN1611, plus I already have to failed attempts to learn Haskell.

Java

There seems to be something called RTSJ (Real-Time Specification for Java), which is claimed to be used in South Korean T-50 jet trainer. But it’s more like curiosity for me. I just don’t trust anything that’s named Java, JVM or based on it.

C++

No introduction required, best candidate. But where’s the fun? The journey into unknown?

TL;DR

DajSiePoznac was a failure. Expect “LIN/TwinCAT for dummies by dummy”, unless again I’ll have to pick between sleep and blog.

Setting up development environment

In order to run software communicating with TwinCAT one needs actual runtime. If you can get a hold of actual PLC that’s great! In other cases you’ll need to setup it on your computer, in this post we’ll focus on setting in up on VM. VirtualBox to be precise.

Development environment

I think it’s safe to assume that today’s development machines consist mostly of x64 OS, thus our final setup will be:

  • x64 Windows host
  • x32 Windows VM
  • TwinCat 2

Note, that it’s not possible  to run TwinCAT 2 runtime on x64 host, we’ll need TwinCAT engineering for that. I assume you’re familiar with basic VirtualBox VM setup.

Downloading TwinCAT

Go to beckhoff download page and download TwinCAT 2.11 R3 and TwinCAT 2.11 x64 Engineering. After installation TwinCAT 2.11 R3 works for 30 days, but later on you can reinstall. For our purposes we’ll go with installation as Next-clicking until it’s finished. Remember TwinCAT 2.11 R3 is for VM and TwinCAT 2.11 x64 Engineering is for host.

Configuring network

For your VM select Host-only adapter.

Adding route

After installing TwinCAT and configuring network you should setup route between host and VM TwinCAT instalations. To do so:

  • click on TwinCAT tray icon
  • then “System Manager”
  • “Choose target”
  • “Search (Ethernet)…”
  • “Broadcast Search”
  • wait till search finishes, you should see 2 runtimes available, host and VM
  • Choose “IP Address” radio button for “Address info:”
  • click “Add route”
  • popup will appear, you should provide credentials for VM user with administrative privileges and confirm by clicking OK.
  • close “Add route dialog”
  • on a treeview select VM and click OK

After doing so you should be able to put VM TwinCAT in run mode, and upload program from TwinCAT PLC Control (TwinCAT tray icon –> “PLC Control”)

Adding route manually

if you’d want to add route manually, for whatever may your reason be:

  • click on TwinCAT tray icon and select properties
  • go to AMS Router tab
  • click Add
  • provide remote Machine name, Ams Net Id, and IP address  or click browse and search through your local network (note: that your probably won’t be able to read remote machine Ams Net Id via “Browse” due to 0x80070005 Access Denied exception)
  • repeat for both host and VM

Reading Ams Net Id of remote machine via Browse

In order to read remote machine AmsNetId, you’ll need to do some more configuration, at least that was my case, though you’ll probably won’t need read it anyway.

  1. make sure user accounts on VM and host have same login and password
  2. enable DCOM access rights (VM + host)
  3. enable WMI namespace access rights (VM + host)
  4. verify WMI service is running (VM + host)
  5. enable local security policy (VM + host)
DCOM access rights
  1. run dcomcnfg from Start –> Run
  2. enter “Component Services” –> “Computers”
  3. right click on “My Computer” and select “Properties”
  4. go to “Com Security” tab
  5. for both “Edit Limits” and “Edit Default” under “Access Permissions”
    1. click “Add”
    2. find user you’ll be connecting
    3. check “Local Address” and “Remote Address” boxes
    4. “Apply”
  6. click “OK”
WMI namespace access rights
  1. run wmimgmt.msc from Start –> Run
  2. right click “WMI Control” and select “Properties”
  3. go to “Security” tab
  4. click “Security” and then “Advanced”
  5. find user you’ll be connecting and lick “Edit”
  6. check “Execute Methods”, “Enable Account” and “Remote Enable”
  7. click “Apply” and then “OK”
verify WMI service is running
  1. run services.msc from Start –> Run
  2. scroll to “Windows Management Instrumentation”
  3. right click on it and verify if started and set to “Automatic”
enable local security policy
  1. run secpol.msc from Start –> Run
  2. select “Local Policies” –> “Security Options”
  3. scroll to “Network access: Sharing and security model for local accounts”
  4. right click and select “Properties”
  5. select “Classic – local users authenticate as themselves”
  6. confirm with “OK”
  7. restart

Running TwinCAT program

You can pickup sample program from #DSP project or write your own, whichever you choose, to run it:

  • Online –> Choose Run-Time System
  • Select available port from VM and click OK
  • Online –> Login (F11) to login with program
  • Online –> Run (F5) to run it

Obstacles when communicating with TwinCAT from F# (plus Workarounds)

I took a detour from main goal of project, which is type provider, in order to provide with lightweight fsharpish library for ADS communication. To my surprise a lot of drawbacks popped out, here you may find a descriptions of them, plus workarounds for some.

Marshalling structures with BOOL arrays.

On one hand, according to Infosys BOOL type has size of 1 byte (where 1 is true, 0 is false, while any other value is incorrect as it can’t be interpreted correctly), same 1 byte size for bool is in .NET. On the other hand, we have Win32 BOOL, which is 4 byte size (more detailed on SO).  Which is scenario we can easily handle in C#, but not in F#.

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct BoolArrSample
{
  [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10, ArraySubType = UnmanagedType.I1)]
  public readonly bool[] values;
}
.class public sequential ansi sealed beforefieldinit Structs.BoolArrSample
  extends [mscorlib]System.ValueType
{
  .pack 1
  .size 0

  .field public initonly marshal(fixed array[10] int8( bool[] values
}
[<Struct;StructLayout(LayoutKind.Sequential, Pack = 1)>]
type BoolArrSample =
  [<MarshalAs(UnmanagedType.ByValArray, SizeConst = 10, ArraySubType = UnmanagedType.I1)>]
  val  values: BOOL[]
.class public sequential ansi sealed serializable NuSoft.Ads.Experimental.Samples.BoolArrSample
  extends [mscorlib]System.ValueType
  implements class [mscorlib]System.IEquatable`1<valuetype NuSoft.Ads.Experimental.Samples.BoolArrSample>,
             [mscorlib]System.Collections.IStructuralEquatable,
             class [mscorlib]System.IComparable`1<valuetype NuSoft.Ads.Experimental.Samples.BoolArrSample>,
             [mscorlib]System.IComparable,
             [mscorlib]System.Collections.IStructuralComparable
{
  .custom instance void [FSharp.Core]Microsoft.FSharp.Core.StructAttribute::.ctor() = (
    01 00 00 00
  )
  .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = (
    01 00 03 00 00 00 00 00
  )
  .pack 1
  .size 0

  .field assembly marshal(fixed array[10]( bool[] values@

  .property instance bool[] values()
  {
    .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags,  int32) = (
      01 00 04 00 00 00 00 00 00 00 00 00
    )
    .get instance bool[] NuSoft.Ads.Experimental.Samples.BoolArrSample::get_values()
    {
      IL_0000: ldarg.0
      IL_0001: ldfld bool[] NuSoft.Ads.Experimental.Samples.BoolArrSample::values@
      IL_0006: ret
    }
  }
}

as you may notice, there’s no ArraySubType value in F# sample IL output. If you look at a visual fsharp sources you’ll notice that ArraySubType is disregarded. Click here for open issue.

Workaround for marhsalling byte arrays in  F#

Other than fixing the issue, one can use Struct array instead of  bool array, though it’s not much convenient.

[<AutoOpen>]
module Workarounds =
  open System.Runtime.InteropServices
  
  [<Struct;StructLayout(LayoutKind.Sequential, Pack=1)>]
  type BOOLSTRUCT = 
    val value: BOOL

    new(value) = {
      value=value
    }
    
  let inline BOOL (x: BOOL) = new BOOLSTRUCT(x)
  let inline BOOLSTRUCT (x: BOOLSTRUCT) = x.value

Marshalling TwinCAT TOD, DATETIME, TIME, DATE types

In TwinCAT time and date types are all 4 bytes, closest out-of-the-box equivalents in .Net world are DateTime for DATE_AND_TIME and DATE, and  TimeSpan for TIME_OF_DAY and TIME, problem is both of them are 8 bytes long.

Marshalling DateTime/TimeSpan values

If you read trough Infosys, you’ll find Reading and writing of TIME/DATE variables, it utilizes AdsStream and AdsBinaryReader/AdsBinaryWriter, but personally prefer to use other methods provided by TcAdsClient.dll.

[<AutoOpen>]
module Ext =
  type TcAdsClient with 
        member self.ReadDateTime(variableHandle) = 
          self.ReadAny(variableHandle, typedefof<uint32>) 
          :?> uint32
          |> int64 
          |> TwinCAT.Ads.Internal.PlcOpenDateConverterBase.ToDateTime

        member self.ReadTimeSpan(variableHandle) = 
          self.ReadAny(variableHandle, typedefof<uint32>) 
          :?> uint32
          |> int64 
          |> TwinCAT.Ads.Internal.PlcOpenTimeConverter.ToTimeSpan

        member self.WriteAny(variableHandle, value) = 
          let v = 
            value 
            |> TwinCAT.Ads.Internal.PlcOpenDateConverterBase.ToTicks
            |> uint32
          self.WriteAny(variableHandle, v) 
        
        member self.WriteAny(variableHandle, value) = 
          let v = 
            value 
            |> TwinCAT.Ads.Internal.PlcOpenTimeConverter.ToTicks
            |> uint32
          self.WriteAny(variableHandle, v) 

you can always use code from Infosys as an extension methods as well.

Marshalling DateTime/TimeSpan structure fields

When it comes to workaround for structure fields there is no workaround that I would like, at least not among those that I know to work. I had few approaches to solutions that do not work, and as I learned couldn’t even possible work. May say one learns by failing. Let’s start with one notable failure, as it also comes with F# missing functionality.

Custom marshallers

At first I thought that it would be possible to marshal one filed with usage of custom marshaller, well it doesn’t work that way.

I may be wrong at this, but Custom marshaller is not an option even if we invoked native method from tcadsdll.dll, as the signature

[DllImport("tcadsdll.dll", CharSet=CharSet.None, ExactSpelling=false)]
public static extern unsafe AdsErrorCode AdsSyncReadReq(byte* addr, uint indexGroup, uint indexOffset, int length, void* data);

expects void pointer not actual structure

Workaround

[<Struct;StructLayout(LayoutKind.Sequential, Pack = 1)>]
type DateTimeArraySample =
  [<MarshalAs(UnmanagedType.ByValArray, SizeConst = 10, ArraySubType = UnmanagedType.U4)>]
  val values: uint32[]

  member self.Values =
    self.values 
    |> Array.map (int64 >> TwinCAT.Ads.Internal.PlcOpenDateConverterBase.ToDateTime)

what I do not like about is obfuscating structure with additional properties, and even if we’d use extension properties/methods, it still involves duplicating every single DT/TOD/D/T fields.

Note on custom marshallers in F#

Putting it simply, they cannot be used ATM, SO question. Still not sure why, but actual code for read and write IL, is present in sources, yet when parsing MarshalAsAttribute exception is thrown explicitly, though it doesn’t seem that hard to fix it, I have it working on modified sources. If I only knew how to test it properly…

Marshalling records – dead end

It turns out idea to use records in ADS client instead of structs wasn’t a good idea. I expected some issues, mostly with lack of parameterless constructor, but things turned out worse than expected.

let’s consider simple struct example

[<StructLayout(LayoutKind.Sequential, Pack=1)>]
type SimpleStruct =
  struct
    val byteVar: BYTE
  end
  
[<StructLayout(LayoutKind.Sequential, Pack=1)>]
type ContainingStruct =
  struct
    [<field: MarshalAs(UnmanagedType.Struct)>]
    val nested: SimpleStruct
  end
[<StructLayout(LayoutKind.Sequential, Pack=1)>]
type ContainingStructArray =
  struct
    [<field: MarshalAs(UnmanagedType.ByValArray, SizeConst=10,ArraySubType=UnmanagedType.Struct)>]
    val nested: SimpleStruct array
  end

Assert.Equal(1, typedefof<SimpleStruct> |> Marshal.SizeOf)
Assert.Equal(1, typedefof<ContainingStruct> |> Marshal.SizeOf)
Assert.Equal(10, typedefof<ContainingStructArray> |> Marshal.SizeOf)

all passes like a charm, now let’s rewrite our structs to records

[<StructLayout(LayoutKind.Sequential, Pack=1)>]
type SimpleRecord = {
  byteVar: BYTE
}
  
[<StructLayout(LayoutKind.Sequential, Pack=1)>]
type ContainingRecord = {
  [<field: MarshalAs(UnmanagedType.Struct)>]
  nested: SimpleRecord
}
[<StructLayout(LayoutKind.Sequential, Pack=1)>]
type ContainingRecordArray = {
  
  [<field: MarshalAs(UnmanagedType.ByValArray, SizeConst=10,ArraySubType=UnmanagedType.Struct)>]
  nested: SimpleRecord array
}

Assert.Equal(1, typedefof<SimpleRecord> |> Marshal.SizeOf) //success
Assert.Equal(1, typedefof<ContainingRecord> |> Marshal.SizeOf) //success
Assert.Equal(10, typedefof<ContainingRecordArray> |> Marshal.SizeOf) //failure, actual size is 40

note that it only doesn’t work on array of structs, not sure how it works under the hood, but somehow it always defaults to 4 bytes, as if we used sizeof<SimpleRecord>, thus array of 10 items, each of them 4 bytes.

Solution?

I’m aware of none yet, beside writing custom code to serialize/deserialize records to/from byte arrays. Which itself would be that bad idea (if we ignore performance) as I could handle endianness conversions if by any chance communication with PLC would occur from big endian machine. 

F# ADS Toolkit | Tools review

Why F#?

For the development of ADS Toolkit I have considered two .NET based languages, F# due to Type Providers, and Nemrele due to powerful metaprogramming capabilities. Unfortunately Nemerle, which is great language, I strongly encourage you to try it, lacks mature support in Visual Studio.

Let’s focus on few F# features that’ll be useful for this particular project.

Type aliases

As mentioned in previous post, TwinCAT implements IEC 61131-3 specification, thus data types naming is different from the one we know from .NET world.  Type aliases are a little thing, yet they make communication a lot simpler, due to allowing both parties (.NET Dev and PLC Dev) usage of same naming convention. Besides, it’s easier, and faster, to say double word instead of  32bit unsigned integer.

Units of Measure and strong typing

Yet another little thing, but it makes values less abstract, plus it makes types safe, if by any chance we’ll by trying to add, or compare,  A (current) to V (voltage) we’ll end up with compile time error. Also, typical solution involves precise measurement thus F# lack of dynamic casting between numeric is another plus.

Type providers

An F# type provider is a component that provides types, properties, and methods for use in your program. – MSDN

I want to skip as much manual work as possible when creating software, call me lazy, thus major part of this project is providing types, required to communicate with PLC, based on *.tpy file generated by TwinCAT.

F# itself

On a first few projects in area of Industrial Automation I used C# exclusively, but recently, say one year ago, I decided to take my chances and try F# for the part of solution responsible for communication with PLC, LIN (Local Interconnect Network – an Automotive network) systems and third party web service. Since that time I cannot imagine myself using non-functional language for such appliances. Time required for development was at least cut 40%, moreover I wasn’t distracted by expressive C-like syntax. As a side note, code metrics is way shorter, yet it’s not a valid criterion to judge software.

Testing

AppVeyor

I always wanted to give those fancy hosted CI build servers a shot. I picked AppVeyor as it’s free for OSS projects (same as Travis-CI), comes with nice badges (again, same as Travis-CI) and is Windows based. Later, if I get to the point at which I replace Beckhoff’s TwinCAT.Ads.dll with own, fully managed .NET client, I’ll be testing it on mono/linux.

FoQ, AutoFixture, xUnit, FsUnit

I admit, I used none of them before, only nUnit, MSTest and JustMock, no wonder I want to try something different, although JustMock has some nice features. DSP is great moment to try new tools, and I’m curious if xUnit is as much extensible as it is described, if AutoFixture saves as much time as its authors boast, and moreover, how all of them play together.

Why I’ll need mocking

As said earlier, I’ll be using AppVeyor, hosted CI build server. It means I can forget about testing against actual TwinCAT instance, whether VM or actual PLC, adding routes to running TwinCAT instance can sometimes be pain in the ass, and I don’t want to check how it can be done on AppVeyor, it probably can’t be done anyway. I’m going to wrap TcAdsClient in  my class (and mock it for test). It’s always good idea to create wrapper for TcAdsClient anyway, as AdsException does not contain information regarding on which particular symbolic/handle operation has failed.

F# ADS Toolkit

Blog registered for “Daj się poznać” contest so I can start countdown until it begins as well as work on project.

To begin with let me explain what I’ll be working on and why I find it useful.

Background

I won’t go into detail what ADS (Automation Device System) is, you can read that at Infosys. What matters is, that it is a protocol that allows you to communicate with TwinCAT. Note that I’ll only focus on access via symbolic variable names.

If we were to read/write, let’s say uint16 variable, it would look like

use client = new TcAdsClient()

//connecto do ADS device
client.Connect(amsNetId,801)

//create variable handle
let myuintHandle = client.CreateVariableHandle ".myuint"

//read value and cast to short
let myuintValue = client.ReadAny(myuintHandle, Operators.typedefof<uint16>) :?> uint16

//write incremented value
client.WriteAny(myuintHandle,myuintValue + 1us)

(* some other logic *)

//remove handle
client.DeleteVariableHandle myuintHandle

//dispose client
client.Dispose()

If you look at highlighted lines, you’ll notice that this code is prone to errors. First of all we pass symbolic name as a string, if we make typo, in runtime we end up with “0x710 symbol not found” exception. Sure we can make it more convenient by wrapping it into generic read methods, but still we have to pay a lot of attention to naming and types. Yep, type names in TwinCAT follow IEC61131-3 specification . Our .NET uint16 is WORD/UINT in TwinCAT, for other types refer to Infosys.

Another tricky thing we can encounter are structures. Sure we can marshal whole structures, but as always, layout is important.

STRUCT
	lrealVal: LREAL := 1.23;
	dintVal1: DINT := 120000;
END_STRUCT
END_TYPE

TYPE TComplexStruct :
STRUCT
	intVal : INT:=1200;
	dintArr: ARRAY[0..3] OF DINT:= 1,2,3,4;
	boolVal: BOOL := FALSE;
	byteVal: BYTE:=10;
	stringVal : STRING(5) := 'hallo';
	simpleStruct1: TSimpleStruct;
END_STRUCT
END_TYPE
[<StructLayout(LayoutKind.Sequential, Pack=1)>]
type SimpleStruct =
  struct
    val lrealVal: double
    val dintVal1: int
  end

[<StructLayout(LayoutKind.Sequential, Pack=1)>]
type ComplexStruct =
  struct
    val intVal: int16
    [<MarshalAs(UnmanagedType.ByValArray, SizeConst=4)>]
    val dintArr: int array
    [<MarshalAs(UnmanagedType.I1)>]
    val boolVal: bool
    val byteVal: byte
    [<MarshalAs(UnmanagedType.ByValTStr, SizeConst=6)>]
    val stringVal: string
    val simpleStruct1: SimpleStruct
  end

Beside basically repeating same code on different sides, imagine situation where PLC developer slightly refactors his code and changes order of fields, no exceptions thrown but invalid values as a result.

Notice highlighted lines, when declaring .NET counterparts, you have to declare length 1 byte longer then TwinCAT due to string null terminator. That’s yet another thing you have to keep in mind when doing things manually.

I could go on with things we can mess up and end up debugging if we do things manually, but above are main problems I aim to address with this project.

First milestone – Convenient syntax and base classes

As a first milestone we should end up with set of base classes that we can extend manually to suit particular project we work on, and use it in convenient way

Second milestone – Type provider based on *.tpy file

Second milestone is to generate ADS client based on *.tpy file, it is generated when TwinCAT project is compiled, at least that’s default setting. It contains custom types definitions and variables. Format is XML based.