Anyone interested in a pure-Java Beckhoff ADS driver for Ignition?

Hi all,

After quite a bit of thinking about it ... I've actually started working on an ADS Driver for Ignition. The connection is already working ... currently working on making the tag-information available to the Designer. As soon as that's done, the read/write stuff will follow.

The driver itself already works really well, just working on the task of integrating that into Ignition.

In order to get a feeling, if it's worth the effort of registering a company in order to be able to provide this plugin in the Ignition store, I'd love to know who would actually be willing to pay for such a plugin (and most probably more drivers that seem to be missing in Ignition).

I was going to say that I would be aiming for something round 300€/license for a pure-java ADS driver. Would that be a reasonable price-tag? If you're interested I'd also love to know how many licenses would you be needing.

(If you don't want to publicly disclose this, please send me a PM)

Chris

5 Likes

So by "Pure Java" you mean one that doesn't need the windows ADS DLL files like Kepware requires? Essentially meaning that it would work in docker containers, and anything that runs native Ignition?

Yeah exactly.

No DLLs, no required server.... Also run it on windows, Linux, macos... Everything Ignition supports :grin:

3 Likes

I think there is a decent amount of interest, but I’ll caution you that:

  1. Users overwhelmingly prefer β€œfirst party” modules, which in the case of drivers are effectively free because we include our drivers with a platform license.
  2. You will get β€œsherlocked” eventually.

Googling this term leads me to believe you think IA is going to release an ADS driver internally?

I know we are. It’s only a matter of when.

2 Likes

But hasn't this topic been open for years now without anything happening?

In order to "prefer" you need the option to chose. If there's no second option, there is only to "do" or to "do not", right? :wink:

1 Like

Yes, and on that note, you would also be able to differentiate yourself by supporting Ignition 8.1.

Any module we do theoretically release will be for Ignition 8.3.

4 Likes

Started working on our ADS protocol implementation. Got enough to read symbol and datatype entries :upside_down_face:

@Test
fun `create AdsSymbolTree`() = runBlocking {
    val symbolTree = AdsSymbolTree.create(client).getOrThrow()

    symbolTree.tree.traverseWithDepth { node, depth ->
        val name: String = node.value.name
        val type: String = node.value.type

        val indent = "   ".repeat(maxOf(0, depth - 1)) + if (depth > 0) "└─ " else ""
        println("$indent${name}${if (type.isNotEmpty()) " : $type" else ""}")
    }
}

Output:

Global_Version.stLibVersion_Tc2_Standard : ST_LibVersion
└─ Global_Version.stLibVersion_Tc2_Standard.iMajor : UINT
└─ Global_Version.stLibVersion_Tc2_Standard.iMinor : UINT
└─ Global_Version.stLibVersion_Tc2_Standard.iBuild : UINT
└─ Global_Version.stLibVersion_Tc2_Standard.iRevision : UINT
└─ Global_Version.stLibVersion_Tc2_Standard.nFlags : DWORD
└─ Global_Version.stLibVersion_Tc2_Standard.sVersion : STRING(23)
Global_Version.stLibVersion_Tc2_System : ST_LibVersion
└─ Global_Version.stLibVersion_Tc2_System.iMajor : UINT
└─ Global_Version.stLibVersion_Tc2_System.iMinor : UINT
└─ Global_Version.stLibVersion_Tc2_System.iBuild : UINT
└─ Global_Version.stLibVersion_Tc2_System.iRevision : UINT
└─ Global_Version.stLibVersion_Tc2_System.nFlags : DWORD
└─ Global_Version.stLibVersion_Tc2_System.sVersion : STRING(23)
Global_Version.stLibVersion_Tc3_Module : ST_LibVersion
└─ Global_Version.stLibVersion_Tc3_Module.iMajor : UINT
└─ Global_Version.stLibVersion_Tc3_Module.iMinor : UINT
└─ Global_Version.stLibVersion_Tc3_Module.iBuild : UINT
└─ Global_Version.stLibVersion_Tc3_Module.iRevision : UINT
└─ Global_Version.stLibVersion_Tc3_Module.nFlags : DWORD
└─ Global_Version.stLibVersion_Tc3_Module.sVersion : STRING(23)
MAIN.PLC_BOOL : BOOL
MAIN.PLC_DINT : DINT
MAIN.PLC_INT : INT
MAIN.PLC_STRING : STRING(80)
MAIN.PLC_STRUCT : TEST_STRUCT
└─ MAIN.PLC_STRUCT.FOO : INT
└─ MAIN.PLC_STRUCT.BAR : DINT
└─ MAIN.PLC_STRUCT.BAZ : STRING(80)
MAIN.PLC_STRUCT_ARRAY_1D : ARRAY [0..1] OF TEST_STRUCT
└─ MAIN.PLC_STRUCT_ARRAY_1D[0] : TEST_STRUCT
   └─ MAIN.PLC_STRUCT_ARRAY_1D[0].FOO : INT
   └─ MAIN.PLC_STRUCT_ARRAY_1D[0].BAR : DINT
   └─ MAIN.PLC_STRUCT_ARRAY_1D[0].BAZ : STRING(80)
└─ MAIN.PLC_STRUCT_ARRAY_1D[1] : TEST_STRUCT
   └─ MAIN.PLC_STRUCT_ARRAY_1D[1].FOO : INT
   └─ MAIN.PLC_STRUCT_ARRAY_1D[1].BAR : DINT
   └─ MAIN.PLC_STRUCT_ARRAY_1D[1].BAZ : STRING(80)
MAIN.PLC_STRUCT_ARRAY_2D : ARRAY [0..1,0..1] OF TEST_STRUCT
└─ MAIN.PLC_STRUCT_ARRAY_2D[0][0] : TEST_STRUCT
   └─ MAIN.PLC_STRUCT_ARRAY_2D[0][0].FOO : INT
   └─ MAIN.PLC_STRUCT_ARRAY_2D[0][0].BAR : DINT
   └─ MAIN.PLC_STRUCT_ARRAY_2D[0][0].BAZ : STRING(80)
└─ MAIN.PLC_STRUCT_ARRAY_2D[0][1] : TEST_STRUCT
   └─ MAIN.PLC_STRUCT_ARRAY_2D[0][1].FOO : INT
   └─ MAIN.PLC_STRUCT_ARRAY_2D[0][1].BAR : DINT
   └─ MAIN.PLC_STRUCT_ARRAY_2D[0][1].BAZ : STRING(80)
└─ MAIN.PLC_STRUCT_ARRAY_2D[1][0] : TEST_STRUCT
   └─ MAIN.PLC_STRUCT_ARRAY_2D[1][0].FOO : INT
   └─ MAIN.PLC_STRUCT_ARRAY_2D[1][0].BAR : DINT
   └─ MAIN.PLC_STRUCT_ARRAY_2D[1][0].BAZ : STRING(80)
└─ MAIN.PLC_STRUCT_ARRAY_2D[1][1] : TEST_STRUCT
   └─ MAIN.PLC_STRUCT_ARRAY_2D[1][1].FOO : INT
   └─ MAIN.PLC_STRUCT_ARRAY_2D[1][1].BAR : DINT
   └─ MAIN.PLC_STRUCT_ARRAY_2D[1][1].BAZ : STRING(80)
MAIN.PLC_STRUCT_ARRAY_3D : ARRAY [0..1,0..1,0..1] OF TEST_STRUCT
└─ MAIN.PLC_STRUCT_ARRAY_3D[0][0][0] : TEST_STRUCT
   └─ MAIN.PLC_STRUCT_ARRAY_3D[0][0][0].FOO : INT
   └─ MAIN.PLC_STRUCT_ARRAY_3D[0][0][0].BAR : DINT
   └─ MAIN.PLC_STRUCT_ARRAY_3D[0][0][0].BAZ : STRING(80)
└─ MAIN.PLC_STRUCT_ARRAY_3D[0][0][1] : TEST_STRUCT
   └─ MAIN.PLC_STRUCT_ARRAY_3D[0][0][1].FOO : INT
   └─ MAIN.PLC_STRUCT_ARRAY_3D[0][0][1].BAR : DINT
   └─ MAIN.PLC_STRUCT_ARRAY_3D[0][0][1].BAZ : STRING(80)
└─ MAIN.PLC_STRUCT_ARRAY_3D[0][1][0] : TEST_STRUCT
   └─ MAIN.PLC_STRUCT_ARRAY_3D[0][1][0].FOO : INT
   └─ MAIN.PLC_STRUCT_ARRAY_3D[0][1][0].BAR : DINT
   └─ MAIN.PLC_STRUCT_ARRAY_3D[0][1][0].BAZ : STRING(80)
└─ MAIN.PLC_STRUCT_ARRAY_3D[0][1][1] : TEST_STRUCT
   └─ MAIN.PLC_STRUCT_ARRAY_3D[0][1][1].FOO : INT
   └─ MAIN.PLC_STRUCT_ARRAY_3D[0][1][1].BAR : DINT
   └─ MAIN.PLC_STRUCT_ARRAY_3D[0][1][1].BAZ : STRING(80)
└─ MAIN.PLC_STRUCT_ARRAY_3D[1][0][0] : TEST_STRUCT
   └─ MAIN.PLC_STRUCT_ARRAY_3D[1][0][0].FOO : INT
   └─ MAIN.PLC_STRUCT_ARRAY_3D[1][0][0].BAR : DINT
   └─ MAIN.PLC_STRUCT_ARRAY_3D[1][0][0].BAZ : STRING(80)
└─ MAIN.PLC_STRUCT_ARRAY_3D[1][0][1] : TEST_STRUCT
   └─ MAIN.PLC_STRUCT_ARRAY_3D[1][0][1].FOO : INT
   └─ MAIN.PLC_STRUCT_ARRAY_3D[1][0][1].BAR : DINT
   └─ MAIN.PLC_STRUCT_ARRAY_3D[1][0][1].BAZ : STRING(80)
└─ MAIN.PLC_STRUCT_ARRAY_3D[1][1][0] : TEST_STRUCT
   └─ MAIN.PLC_STRUCT_ARRAY_3D[1][1][0].FOO : INT
   └─ MAIN.PLC_STRUCT_ARRAY_3D[1][1][0].BAR : DINT
   └─ MAIN.PLC_STRUCT_ARRAY_3D[1][1][0].BAZ : STRING(80)
└─ MAIN.PLC_STRUCT_ARRAY_3D[1][1][1] : TEST_STRUCT
   └─ MAIN.PLC_STRUCT_ARRAY_3D[1][1][1].FOO : INT
   └─ MAIN.PLC_STRUCT_ARRAY_3D[1][1][1].BAR : DINT
   └─ MAIN.PLC_STRUCT_ARRAY_3D[1][1][1].BAZ : STRING(80)
MAIN.PLC_UDINT : UDINT
MAIN.PLC_UINT : UINT
MAIN.PLC_ULINT : ULINT
MAIN.PLC_USINT : USINT
TwinCAT_PreventOnlineChangeGvl.PlcProfilerActive : BOOL
TwinCAT_PreventOnlineChangeGvl.PlcProfilerConfigChecksum : STRING(64)
TwinCAT_PreventOnlineChangeGvl.WriteLineIDs : BOOL
TwinCAT_SystemInfoVarList.__PlcTask : _Implicit_Task_Info
└─ TwinCAT_SystemInfoVarList.__PlcTask.dwVersion : DWORD
└─ TwinCAT_SystemInfoVarList.__PlcTask.pszName : POINTER TO STRING(80)
└─ TwinCAT_SystemInfoVarList.__PlcTask.nPriority : INT
└─ TwinCAT_SystemInfoVarList.__PlcTask.KindOf : _Implicit_KindOfTask
└─ TwinCAT_SystemInfoVarList.__PlcTask.bWatchdog : BOOL
└─ TwinCAT_SystemInfoVarList.__PlcTask.bProfilingTask : BOOL
└─ TwinCAT_SystemInfoVarList.__PlcTask.dwEventFunctionPointer : POINTER TO BYTE
└─ TwinCAT_SystemInfoVarList.__PlcTask.pszExternalEvent : POINTER TO STRING(80)
└─ TwinCAT_SystemInfoVarList.__PlcTask.dwTaskEntryFunctionPointer : POINTER TO BYTE
└─ TwinCAT_SystemInfoVarList.__PlcTask.dwWatchdogSensitivity : DWORD
└─ TwinCAT_SystemInfoVarList.__PlcTask.dwInterval : DWORD
└─ TwinCAT_SystemInfoVarList.__PlcTask.dwWatchdogTime : DWORD
└─ TwinCAT_SystemInfoVarList.__PlcTask.dwLastCycleTime : DWORD
└─ TwinCAT_SystemInfoVarList.__PlcTask.dwAverageCycleTime : DWORD
└─ TwinCAT_SystemInfoVarList.__PlcTask.dwMaxCycleTime : DWORD
└─ TwinCAT_SystemInfoVarList.__PlcTask.dwMinCycleTime : DWORD
└─ TwinCAT_SystemInfoVarList.__PlcTask.diJitter : DINT
└─ TwinCAT_SystemInfoVarList.__PlcTask.diJitterMin : DINT
└─ TwinCAT_SystemInfoVarList.__PlcTask.diJitterMax : DINT
└─ TwinCAT_SystemInfoVarList.__PlcTask.dwCycleCount : DWORD
└─ TwinCAT_SystemInfoVarList.__PlcTask.wTaskStatus : WORD
└─ TwinCAT_SystemInfoVarList.__PlcTask.wNumOfJitterDistributions : WORD
└─ TwinCAT_SystemInfoVarList.__PlcTask.pJitterDistribution : POINTER TO _Implicit_Jitter_Distribution
└─ TwinCAT_SystemInfoVarList.__PlcTask.bWithinSPSTimeSlicing : BOOL
└─ TwinCAT_SystemInfoVarList.__PlcTask.byDummy : BYTE
└─ TwinCAT_SystemInfoVarList.__PlcTask.bShouldBlock : BOOL
└─ TwinCAT_SystemInfoVarList.__PlcTask.bActive : BOOL
└─ TwinCAT_SystemInfoVarList.__PlcTask.dwIECCycleCount : DWORD
TwinCAT_SystemInfoVarList._AppInfo : PlcAppSystemInfo
└─ TwinCAT_SystemInfoVarList._AppInfo.ObjId : OTCID
└─ TwinCAT_SystemInfoVarList._AppInfo.TaskCnt : UDINT
└─ TwinCAT_SystemInfoVarList._AppInfo.OnlineChangeCnt : UDINT
└─ TwinCAT_SystemInfoVarList._AppInfo.Flags : DWORD
└─ TwinCAT_SystemInfoVarList._AppInfo.AdsPort : UINT
└─ TwinCAT_SystemInfoVarList._AppInfo.BootDataLoaded : BOOL
└─ TwinCAT_SystemInfoVarList._AppInfo.OldBootData : BOOL
└─ TwinCAT_SystemInfoVarList._AppInfo.AppTimestamp : DT
└─ TwinCAT_SystemInfoVarList._AppInfo.KeepOutputsOnBP : BOOL
└─ TwinCAT_SystemInfoVarList._AppInfo.ShutdownInProgress : BOOL
└─ TwinCAT_SystemInfoVarList._AppInfo.LicensesPending : BOOL
└─ TwinCAT_SystemInfoVarList._AppInfo.BSODOccured : BOOL
└─ TwinCAT_SystemInfoVarList._AppInfo.LoggedIn : BOOL
└─ TwinCAT_SystemInfoVarList._AppInfo.PersistentStatus : EPlcPersistentStatus
└─ TwinCAT_SystemInfoVarList._AppInfo.TComSrvPtr : ITComObjectServer
└─ TwinCAT_SystemInfoVarList._AppInfo.AppName : STRING(63)
└─ TwinCAT_SystemInfoVarList._AppInfo.ProjectName : STRING(63)
TwinCAT_SystemInfoVarList._TaskInfo : ARRAY [1..1] OF PlcTaskSystemInfo
└─ TwinCAT_SystemInfoVarList._TaskInfo[0] : PlcTaskSystemInfo
   └─ TwinCAT_SystemInfoVarList._TaskInfo[0].ObjId : OTCID
   └─ TwinCAT_SystemInfoVarList._TaskInfo[0].CycleTime : UDINT
   └─ TwinCAT_SystemInfoVarList._TaskInfo[0].Priority : UINT
   └─ TwinCAT_SystemInfoVarList._TaskInfo[0].AdsPort : UINT
   └─ TwinCAT_SystemInfoVarList._TaskInfo[0].CycleCount : UDINT
   └─ TwinCAT_SystemInfoVarList._TaskInfo[0].DcTaskTime : LINT
   └─ TwinCAT_SystemInfoVarList._TaskInfo[0].LastExecTime : UDINT
   └─ TwinCAT_SystemInfoVarList._TaskInfo[0].FirstCycle : BOOL
   └─ TwinCAT_SystemInfoVarList._TaskInfo[0].CycleTimeExceeded : BOOL
   └─ TwinCAT_SystemInfoVarList._TaskInfo[0].InCallAfterOutputUpdate : BOOL
   └─ TwinCAT_SystemInfoVarList._TaskInfo[0].RTViolation : BOOL
   └─ TwinCAT_SystemInfoVarList._TaskInfo[0].TaskName : STRING(63)
TwinCAT_SystemInfoVarList._TaskOid_PlcTask : OTCID
TwinCAT_SystemInfoVarList._TaskPouOid_PlcTask : OTCID
9 Likes

Was eagerly checking for this in the Beta... <- currently testing the new Siemens driver which so far seems AWESOME!

maybe…

A Beckhoff ADS driver is on our roadmap, but won’t be in 8.3.0 nor the handful of releases after it. Not sure on timeline, but it won’t be immediate.

We’d appreciate knowing when. This is something where we are going to hit an ultimate limitation with OPC UA …

What kind of hardware and how many tags are you subscribing to where OPC UA becomes a limitation compared to ADS?

For anyone, like me, who had no idea of what this topic is about:

The Automation Device Specification (ADS) is the communication protocol of TwinCAT. It enables the data exchange and the control of TwinCAT systems. ADS is media-independent and can communicate via serial or network connections.

ADS enables:

  • access to the process image
  • consistent data exchange
  • access to I/O tasks
  • detection of status changes
  • read-out of the PLC symbol informationn
  • access by variable name
  • sum commands
  • synchronous and asynchronous access
  • cyclic and event-based messages
1 Like

Well I've already got a fully operational driver ready. Just waiting for the founding process in Germany to be finished :wink: https://youtu.be/9eLBKtyjhB4?si=fDiQs7lvHL25Zues

5 Likes

Hi @christofer.dutz,

We have customers ready to test/purchase in France.

Will you be at ICC this year?

BR,

1 Like

I guess I'll have to pass on the ICC event. Would have been a perfect airing to my trip to apache’s community over code conference in Minneapolis, where I'm flying home on the 15th.

But please ping me on LinkedIn… then I can ping you as soon as I'm ready. I definitely think some customers testing things and getting a big discount in return is definitely something I'm planning on doing.

Chris

1 Like

Sorry about my delayed reply. Without going into deep setup details on a public form, we can always have a seperate call on this via our Account Manager.

I’d say approx 2000 max points per site and we have atleast >20 sites of varying criticality per site. Ignition is in a HA setup but not active-active as I believe only active-standby is supported in 8.2. Setting up OPC in Beckhoff is also somewhat involved, not saying it can’t be conquered but a driver from yourselves avoids this as you guys figure this all out so to speak.

Is there a published upper and lower limit on IA’s implementation of the OPC UA subsystem? Also not sure what sort of overhead Java brings to this vrs say a memory safe compiled language such as Rust or just a compiled language such as C/C++. Again not trying to start a discussion on if Java is the right tool or not… it is what is used today by IA. Simply trying to understand how much overhead a JIT language with GC has. It maybe completely negligible. Thanks Kevin.

Hey Chris,

I applaud you for your hard work. What would be the implications of IA sports their own driver a some point as I think discussed here?

Also would you have your support model sorted out for end customers? What about your support with Beckhoff? I haven't seen much progress on their Java ADS code the last I checked.

Sorry if I am asking questions you already have answers for. Naturally if IA sports Beckhoff driver these support questions are fairly moot as it will come under their existing support model. They would also sort out things with Beckhoff as necessary is the assumption, if there are issues in the Java ADS kit. Thanks.