TwinCAT ToFile patch

From SymbitronWiki
Revision as of 15:10, 4 July 2016 by Gijsvanoort (talk | contribs) (Created page)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

After adding a ToFile block to a somewhat larger Simulink model, I got the following error:

 Error: File: C:\TwinCAT\Functions\TE1400-TargetForMatlabSimulink\Common\tctModuleClass_cpp.tlc Line: 260 Column: 50
 Ambiguous reference to System - must use array index to refer to one of multiple scopes
 Main program:
 ==> [00] C:\TwinCAT\Functions\TE1400-TargetForMatlabSimulink\Common\tctModuleClass_cpp.tlc:<NONE>(260)
     [01] C:\TwinCAT\Functions\TE1400-TargetForMatlabSimulink\Common\tctModuleFiles.tlc:<NONE>(15)
 Error: File: C:\TwinCAT\Functions\TE1400-TargetForMatlabSimulink\Common\tctModuleClass_cpp.tlc Line: 260 Column: 50
 The left-hand side of a . operator must be a valid scope identifier
 Main program:
 ==> [00] C:\TwinCAT\Functions\TE1400-TargetForMatlabSimulink\Common\tctModuleClass_cpp.tlc:<NONE>(260)
     [01] C:\TwinCAT\Functions\TE1400-TargetForMatlabSimulink\Common\tctModuleFiles.tlc:<NONE>(15)
 

The important part of the error message above is:

 Ambiguous reference to System - must use array index to refer to one of multiple scopes
 Main program:
 ==> [00] C:\TwinCAT\Functions\TE1400-TargetForMatlabSimulink\Common\tctModuleClass_cpp.tlc:<NONE>(260)

It appears that this error does not occur in very simple models, such as just a constant and a ToFile block. Also, it only occurs if a ToFile block is used. But as soon as the model gets bigger, the problem can occur. I needed to dive into the TwinCAT source and 'learn' the TLC ('Target Language Compiler') language for that. (some background: When building a Simulink Model into a TwinCAT-'executable', first a .rtw file is created by Simulink. Then the Target Language Compiler, with its associated .TLC source files, is invoked, which turns the .rtw file into .cpp/.h files. Those are then compiled with a normal C-compiler. See also this PDF, page 17.

The error message is generated during the TLC process, and the part that generates the error is (provided by TwinCAT) (I re-indented it for clarity; indentation doesn't matter in this language):

 %if(FileAccess)
   // FileWriter - Reset Count to Allow more than 100000000 lines
   %foreach blockidx = CompiledModel.System.NumBlocks                   <-- THIS LINE TRIGGERS THE ERROR
       %if (CompiledModel.System.Block[blockidx].Type == "ToFile")
           %assign FwIwork = CompiledModel.System.Block[blockidx].IWork
           %assign Fwgrpindx = CompiledModel.DWorks.DWork[FwIwork[0],FwIwork[1]].VarGroupIdx
           %assign varGroupRec = CompiledModel.VarGroups.VarGroup[Fwgrpindx[0]]
           %assign cgtIdx = varGroupRec.CGTypeIdx
           %assign memberRecord = CompiledModel.CGTypes.CGType[cgtIdx].Members.Member[Fwgrpindx[1]]
 
           m_DWork.%<memberRecord.Name>.Count = 1;
 
           %%//%<varGroupRec.Name>.%<memberRecord.Name>.Count = 1;
 
       %endif
   %endforeach
 %endif

Fortunately, TLC can be debugged. In order to do so, go to your Simulink model, open the Model Configuration Parameters window (Ctrl-E) and check the checkbox next to Code Generation|Debug|Start TLC debugger when generating code.

Debug hints: use help. Use print to show the value of a variable, use whos to show the structure of struct (called scope in TLC). Now it appears that for a small model, there is one System in the CompiledModel variable, which can be accessed as either CompiledModel.System or CompiledModel.System[0]. For a larger model, there is more than one System in the CompiledModel struct, and CompileModel.System is not enough anymore to unambiguously specify the System element needed; you need CompiledModel.System[i]. It now becomes clear that the above code only works with small models which only have one System. (By the way, the number of Systems in CompiledModel is equal to the number of non-virtual subsystems in your Simulink model).

I was going to write a fix (and send to TwinCAT) when I realized that on my own Laptop I may have a different TwinCAT version. I had a marginally newer version there, and in this version the bug was already fixed. This took me 5 hours...

  • TwinCAT v3.1.4016.12: Bug exists (Niek's TwinCAT PC)
  • TwinCAT v3.1.4018.26: Bug fixed (my own laptop)

The patched code is:

   %if(FileAccess)
   // FileWriter - Reset Count to Allow more than 100000000 lines
       %foreach sysIdx = CompiledModel.NumSystems
           %if (CompiledModel.NumSystems>1)
               %assign sys = CompiledModel.System[sysIdx]
           %else
               %assign sys = CompiledModel.System
           %endif
           %foreach blockIdx = sys.NumBlocks
               %assign block = sys.Block[blockIdx]
               %if (block.Type == "ToFile")
                   %assign FwIwork = block.IWork
                   %assign Fwgrpindx = CompiledModel.DWorks.DWork[FwIwork[0],FwIwork[1]].VarGroupIdx
                   %assign varGroupRec = CompiledModel.VarGroups.VarGroup[Fwgrpindx[0]]
                   %assign cgtIdx = varGroupRec.CGTypeIdx
                   %assign memberRecord = CompiledModel.CGTypes.CGType[cgtIdx].Members.Member[Fwgrpindx[1]]
                   m_DWork.%<memberRecord.Name>.Count = 1;
               %endif
           %endforeach
       %endforeach
   %endif