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

4 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.

3 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
7 Likes