This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
This is a TwinCAT 3 PLC application — a component-based automation framework for Beckhoff TwinCAT 3 real-time controllers, written in IEC 61131-3 Structured Text (ST). It serves as an educational sample demonstrating professional PLC development patterns.
This project requires TwinCAT 3 XAE IDE (Visual Studio extension, Windows-only).
- Open solution:
ApplicationDevelopment/ApplicationDevelopment.sln - Build: Right-click solution → Build in TwinCAT XAE IDE
- Target: TwinCAT RT or TwinCAT OS in Debug/Release for x86, x64, ARMV7-A, ARMV7-M, or ARMV8-A
Tests run inside the TwinCAT PLC runtime — there is no CLI test runner.
- Run all tests: Download and run the project on a TwinCAT controller or simulator;
UnitTests/MAIN.TcPOUorchestrates all test calls - Run a single test: Comment out all other test function calls in
MAIN.TcPOU, leaving only the target test (e.g.,Component_TEST()) - Test results: Inspect the global
GlobalTestSuite.TestSuiteobject at runtime via the TwinCAT online view
ApplicationDevelopment_Sample/
├── ApplicationBase/ # Reusable framework library (tspproj)
│ └── ApplicationBase/ # Framework source (components, modules, utilities)
└── ApplicationDevelopment/ # Applications and unit tests (sln + tsproj)
├── UnitTests/ # PLC project with all test POUs and mockups
├── Template/ # Minimal pre-wired starting point for new projects
└── VFFS/ # Full demo application (Vertical Form Fill Seal machine)
Four projects:
ApplicationBase— the reusable framework (library project)UnitTests(insideApplicationDevelopment) — exercises the framework with 26+ test modulesTemplate(insideApplicationDevelopment) — minimal pre-wired starting point for new applicationsVFFS(insideApplicationDevelopment) — complete demo application modelling a packaging machine
The framework uses a hierarchical, interface-driven component model:
Component— base unit; implementsI_Component. All reusable building blocks extend this.CyclicComponent— extendsComponentwithI_Cyclic; overrideCyclicLogic()for per-scan work.Module— aggregates components and sub-modules; implementsI_Module,I_Cyclic,I_Initializable. Owns aComponentCollectionandModuleCollection.CyclicRunner<MaxSize>— manages cyclic dispatch of registeredI_Cyclicobjects within a module.Initializer— manages ordered initialization sequences ofI_Initializableobjects.Statemachine<InitialState>— generic state machine FB extendingCyclicComponent.
| Folder | Purpose |
|---|---|
Component/ |
Abstract Component and CyclicComponent base FBs |
Module/ |
Module, EquipmentModule, MachineModule, CyclicRunner, Initializer |
Components/Digital/ |
DigitalInput_NC, DigitalInput_NO, DigitalOutput |
Components/Analog/ |
Analog I/O components |
Components/Cylinder/ |
Pneumatic/hydraulic cylinder control |
Components/ADS/ |
ADS (Automation Device Specification) communication |
Statemachine/ |
Generic state machine with mode control |
Modes/ |
ModeControl operating mode management |
Tracing/ |
Event logging and diagnostics |
Utilities/AnyBuffer/ |
Generic queue/buffer (Push/Pop/Peek, left/right ends) |
Utilities/Force/ |
Variable forcing utilities |
Visitors/ |
Tree-traversal implementations (visitor pattern over component/module trees) |
_Interface/ |
All I_* interface definitions |
Operations on the component/module hierarchy (reset, mode change, name enumeration) are implemented as visitors that implement VisitComponent() / VisitModule(). This avoids modifying the visited objects.
ApplicationBase/ApplicationBaseParameter.TcGVL:
MaxCountInCollections: 50 — upper bound for all collections, runners, and initializersEnableAdsLogger: TRUETraceLevel: VerboseClearingTimeout: 2 s
| Convention | Pattern | Example |
|---|---|---|
| Interfaces | I_ prefix |
I_Component, I_Cyclic |
| Structs | ST_ prefix |
ST_Recipe |
| Private variables | _ prefix |
_Name, _IsInitialized |
| Test functions | _TEST suffix |
Component_TEST, Module_TEST |
| Global variable lists | TcGVL files |
ApplicationBaseParameter.TcGVL |
// Each test function guards execution:
IF NOT TestSuite.Test(__POUNAME()).ExecuteTest() THEN RETURN; END_IF
// Assertions:
TestSuite.AssertEqual(actual, expected, 'message');Mockup objects for components and modules live in UnitTests/_Mockups/.
- Task name:
UnitTests, Priority: 20, Cycle time: 100 ms, AMS Port: 350