TcGen Agent brings AI assistance into Visual Studio and TwinCAT XAE Shell. It can explain the open project, draft Structured Text, and prepare changes that are applied through the local TwinCAT engineering tools after review.
TwinCAT stays on the engineering workstation. Agile Automation's cloud service handles login, usage limits, model selection, and AI reasoning, while the local bridge reads the project and applies approved updates. TcGen Agent supports TwinCAT 3.1 Build 4024 and 4026+ shell generations.
When you ask for help, TcGen Agent shares the relevant project context with Agile Automation's AI service so the answer is based on your actual TwinCAT solution, not a blank prompt. A local bridge handles project reads and approved writes on the engineering workstation.
TcGen Agent can draft Structured Text and, after approval, update DUTs, functions, interfaces, function blocks, methods, and programs through the standard TwinCAT automation interface.
You choose how project writes are handled: ask every time, approve automatically, or read-only. In ask mode, the generated Structured Text can be reviewed and edited before it is applied.
The local bridge can read source-controlled TwinCAT project files such as .plcproj, .TcPOU, .TcDUT, .TcGVL, and .xti, then give the AI the code it needs to explain the project accurately.
Ask TcGen Agent to produce Structured Text for new blocks, methods, interfaces, data types, and example programs while keeping the generated code visible to the developer.
TcGen Agent can recommend a change, create new TwinCAT objects, or apply updates to an existing solution after approval.
TcGen Agent works from the active TwinCAT solution, so recommendations can refer to real function blocks, DUTs, methods, actions, and project structure.
The agent can turn existing Structured Text into readable documentation without losing the engineering detail.
When the agent generates code, the engineer can review the proposed Structured Text before it is written to TwinCAT.
In this anonymized example, TcGen Agent reviewed an existing filling line project and returned prioritized, project-aware recommendations instead of a generic checklist.
Now I have a thorough understanding of the codebase. Here are my architectural recommendations:
Problem: The exact same block of ~30 lines resetting all drive service flags (brake release, homing, manual) is duplicated in 3 places:
This is a maintenance nightmare — adding a new axis means updating all 3 locations.
Recommendation: Extract into a single function:
FUNCTION F_ResetAllDriveServiceFlags
VAR_IN_OUT
iq_stRuntime : ST_RuntimeInterface;
END_VAR
// Single source of truth for all drive resetsThen call F_ResetAllDriveServiceFlags(stRuntime) from all 3 places.
Problem: SR_LineController's declaration section contains ~80+ local variables spanning safety, alarming, format change, machine mode, transport mode, controller, machine signals, motion, light grid, IPC, and more. This is a flat, unstructured declaration that mixes concerns.
Recommendation: Group related variables into local structs:
VAR
stSafety : ST_LineController_Safety;
stAlarming : ST_LineController_Alarming;
stFormat : ST_LineController_FormatChange;
stMode : ST_LineController_MachineMode;
...
END_VARThis improves readability and makes it easier to refactor sub-functionality into separate function blocks later.
Problem: Many SR_LineController actions are implemented in NWL (Network/Ladder) format — InputProcessing_active, ControllerLogic_active, OperationMode_active, ServiceMode_active, OutputProcessing_active, etc. This is:
Recommendation: Convert all NWL actions to Structured Text (ST). For example, ControllerLogic_active (3 AND gates with latches) is trivial in ST:
xModeReady := xActOperation OR xActService;
xActOperation := xInputOperation AND xModeReady;
xActService := xInputService AND xModeReady;Problem: The format change detection in GlobalServices_active compares 8+ hardware cam data values against stored "temp" variables. This is:
Recommendation: Create a dedicated FB_FormatChange function block:
FUNCTION_BLOCK FB_FormatChange
VAR_INPUT
i_astCamData : ARRAY [*] OF ST_CamSnapshot;
END_VAR
VAR_OUTPUT
q_xFormatChanged : BOOL;
END_VARWith a method to register cam data sources and compare with configurable tolerance.
Problem: The project has EM_01 (Global), EM_02 (Transport), EM_03 (Filling), EM_04 (PreSealing) — but no EM for final sealing/crimping. The SR_LineController declaration has temp variables for rSealCrimpingStartPositionTemp and rSealCrimpingCrimpPositionTemp that are declared but never used in the format change logic (commented out or missing). This suggests sealing was planned but not yet implemented.
Recommendation: Either implement EM_FinalSealing or remove the unused variables to avoid confusion.
Problem: Only stRuntime and stController are PERSISTENT. The AxisRestoreStore GVL (RETAIN PERSISTENT) stores axis restore positions separately. This split means:
Recommendation: Move axis restore positions into stRuntime as a nested struct, or at minimum document the persistence strategy clearly.
Problem: In OutputProcessing_active, EXUNIT.FC_LineConfiguration is called with hardcoded literals:
i_uiMachineType := 50;
i_xVials := TRUE;
i_xInletTurntable := TRUE;
i_uiObjectsPerHolderMax := 6;
i_uiObjectsPerHolderMin := 3;
...Recommendation: Move these to a configuration GVL or the ST_LineConfig struct so they can be changed without modifying the program code.
Problem: Error quit processing happens in:
The TON_VendorErrorQuit and TP_VendorErrorQuit in SR_LineController suggest a vendor-specific workaround that's mixed with the main logic.
Recommendation: Encapsulate all error quit handling into a single FB_ErrorQuit function block with configurable pulse timing and vendor-specific extensions.
Problem: In GlobalServices_active:
FOR i:= 1 TO EXUNIT.LineConfig.g_c_uiStandardObjectCount DO
stController.arProcessCorrection[i] := stRuntime.stTransport.stRotor.stVal.astNetWeightIpc[i].rCorrection;
END_FORThis copies the entire array every cycle, even when nothing changed.
Recommendation: Use MEMCPY (from Tc2_Utilities) or only update on change detection.
Problem: The project has no test infrastructure. With 226 files and complex SFC state machines, manual testing is the only option.
Recommendation: Consider adding:
Would you like me to implement any of these changes? I'd recommend starting with #1 (drive reset duplication) and #4 (format change FB) as they provide immediate maintainability benefits with low risk.
FB_Ampel is a TwinCAT 3 function block that simulates a standard traffic light sequence. It cycles through Red, RedYellow, Green, Yellow, and back to Red with fully configurable phase durations. It is designed for HMI lamp widgets, physical signal towers, or status indicators on automated machinery.
TYPE E_AmpelState :
(
eRed := 0,
eRedYellow := 1,
eGreen := 2,
eYellow := 3
);
END_TYPEThe default total cycle time is 11 seconds.
PROGRAM MAIN
VAR
fbTrafficLight : FB_Ampel;
END_VAR
fbTrafficLight(xEnable := TRUE);fbTrafficLight(
xEnable := TRUE,
tRedDuration := T#10s,
tRedYellowDuration := T#2s,
tGreenDuration := T#8s,
tYellowDuration := T#2s
);HmiLampRed := fbTrafficLight.xRed;
HmiLampYellow := fbTrafficLight.xYellow;
HmiLampGreen := fbTrafficLight.xGreen;
HmiLightActive := fbTrafficLight.xCycleActive;fbTrafficLight(
xEnable := gxMachineAuto AND NOT gxAlarmActive
);A TwinCAT write request is waiting for approval in the TwinCAT shell.
Approve or deny this write from the local dashboard or from the TwinCAT XAE Shell approval window.
TYPE E_StepState :
(
eStepIdle := 0,
eStepRunning := 1,
eStepComplete := 2,
eStepError := 3
);
END_TYPE
FUNCTION_BLOCK FB_Stepchain
VAR_INPUT
xEnable : BOOL; (* Enable the step chain execution *)
xReset : BOOL; (* Reset the step chain to idle *)
END_VAR
VAR_OUTPUT
nStep : UINT; (* Current active step number *)
eState : E_StepState; (* Current state of the step chain *)
xStepActive : BOOL; (* TRUE when a step is executing *)
xChainComplete : BOOL; (* TRUE when all steps have completed *)
END_VAR
VAR
nMaxSteps : UINT := 10;
rTrigEnable : R_TRIG;
rTrigReset : R_TRIG;
rTrigAdvance : R_TRIG;
xAdvanceRequested : BOOL;
END_VAR
rTrigEnable(CLK := xEnable);
rTrigReset(CLK := xReset);
IF rTrigReset.Q OR (xReset AND NOT xEnable) THEN
nStep := 0;
eState := eStepIdle;
xChainComplete := FALSE;
xAdvanceRequested := FALSE;
RETURN;
END_IF
IF rTrigEnable.Q THEN
nStep := 1;
eState := eStepRunning;
xChainComplete := FALSE;
END_IF
IF eState = eStepRunning THEN
rTrigAdvance(CLK := xAdvanceRequested);
IF rTrigAdvance.Q THEN
IF nStep >= nMaxSteps THEN
nStep := 0;
eState := eStepComplete;
xChainComplete := TRUE;
xAdvanceRequested := FALSE;
ELSE
nStep := nStep + 1;
xAdvanceRequested := FALSE;
END_IF
END_IF;
END_IF
xStepActive := (eState = eStepRunning) AND (nStep > 0);
METHOD m_xAdvance : BOOL
VAR_INPUT
xExecute : BOOL;
END_VAR
IF xExecute THEN
xAdvanceRequested := TRUE;
m_xAdvance := TRUE;
ELSE
m_xAdvance := FALSE;
END_IF
METHOD m_xIsActiveStep : BOOL
VAR_INPUT
nStepNumber : UINT;
END_VAR
m_xIsActiveStep := (eState = eStepRunning) AND (nStep = nStepNumber);Solo engineer, low-friction trial
A practical entry point for one engineer evaluating project-aware AI in TwinCAT.
Small machine builder, up to 3 users
For small teams that want shared access without enterprise procurement overhead.
Serious TwinCAT team, up to 10 users
For engineering groups using TcGen Agent across active machine software work.
Larger OEMs, self-hosted, privacy review, procurement
For larger organizations that need deployment, privacy, and procurement alignment.
Tell us what you are building, where the current blocker is, and what the target architecture should support.