Application Structure
A Zeugwerk Application is always held inside a folder called per default ZApp and is integrateable into existing applications (not using the Zeugwerk Framework so far) without interfering with the existing code.
The foundation of the PLC template is its application context. Its main purpose is fixing the contact points for communication and data and declaring a program where all the units are running. It also provides auxiliary functions for the PLC like logging, alarming, fieldbus handling (EtherCAT Master and Slave) and declaring global variables.
Folder layout
Below ZApp, a few top-level items are always present:
ZApp/
├── ZGlobal.TcGVL Global variables: Com (runtime) + Data (persistent)
├── ZModuleProgram.TcPOU Main PROGRAM: application context + all units
├── ZModuleTask.TcTTO PLC task calling ZModuleProgram cyclically
│
├── App/ The application function block itself
│ ├── ZApp.TcPOU Extends ZApplication.Application
│ ├── _States/ ZAppSequenceBoot, ZAppSequenceBootFault, ZAppSequenceRunning
│ └── _SubObjects/ ZAppSequence (abstract base for the sequences above)
│
├── Com/ Communication struct definitions (see Global Data & Communication)
├── Data/ Persistent data struct definitions (see Global Data & Communication)
│
└── Unit/
└── <UnitName>/ One folder per unit, see the Units concept pages
Communication and data are kept outside of logic code in the global variable list ZGlobal, separating internal behavior from interfaces to IT.
The structure of ZGlobal.Com and ZGlobal.Data, and how they are used for HMI/ADS communication, is covered in Global Data & Communication.
ZModuleProgram
The PLC runs in a task which is automatically created when you are using the Zeugwerk template. This task is called ZModuleTask and runs the PRG ZModuleProgram. This program holds all created instances of your units and the application context:
PROGRAM ZModuleProgram
VAR_INPUT
App : ZApp(displayName:='My-Application', com:=ZGlobal.Com.App);
Datetime : ZAux.DateTimeUM;
LoggerHmi : ZApplication.Messaging(parent:=App, datetime:=datetime, com:=ZGlobal.Com.Messages);
LoggerFile : ZAux.LoggerFile7FF(parent:=App, datetime:=datetime, filePath:='C:\temp\msg.log', target:='192.168.0.80.1.1');
Logger : ZAux.Logging;
Alarming : ZApplication.Alarming(App, datetime := Datetime, com := ZGlobal.Com.Alarming);
MyUnit1 : MyUnit(displayName:='My-Unit-1',
parent:=App,
configdata:=ZGlobal.Data.Config.MyUnit1,
machinedata:=ZGlobal.Data.Machine.MyUnit1,
calibrationdata:=ZGlobal.Data.Calibration.MyUnit1,
com:=ZGlobal.Com.Unit.MyUnit1);
END_VAR
App: the Application instance. ItsCyclicmethod, called in the body ofZModuleProgram, drives all objects in a Zeugwerk Application. This object has sequences for booting and running the application:- The Booting sequence makes sure that all instantiated objects (with the root parent
App) get ready, transitioning from their Booting state to their Idle state. - The Running sequence continuously calls the
Cyclicmethod of all instantiated objects (with the root parentApp), ensuring their operation.
In a concrete application,
Appis a small function block extendingApplication, typically calledZApp, that wires the three application-level sequences in its constructor:FUNCTION_BLOCK ZApp EXTENDS ZApplication.Application VAR _boot : ZAppSequenceBoot; _bootFault : ZAppSequenceBootFault; _running : ZAppSequenceRunning; END_VAR// FB_init SetBootSequence(_boot); SetBootFaultSequence(_bootFault); SetRunningSequence(_running);ZAppSequenceBootis a good place for one-time startup logic that does not belong to a specific unit, for example setting default machine data or wiring additional log appenders:ZGlobal.Data.Machine.MyUnit1.Lowspeed := 0.7; ZGlobal.Data.Machine.MyUnit1.Highspeed := 1.50; ZModuleProgram.LoggerFile.MinimumLevel := ZCore.LogLevel.Trace; ZModuleProgram.Logger.AddLogger(ZModuleProgram.LoggerFile);- The Booting sequence makes sure that all instantiated objects (with the root parent
Datetime: an instance of the date-time function block, which can be synced to an internal or external clock and can be used all over the application.Logging is an integral part of the Zeugwerk Framework, so the next instances refer to the logging mechanism. Essentially, Logging provides a thread-safe generic implementation for logging. Adding various Appenders to this object makes it possible to write messages into files, or to an HMI or a similar program that utilizes ADS. Decorators may be used to provide more context to such messages. Find a detailed explanation on logging in the Logging Tutorial.
Alarming is similar to Logging in that it allows sending messages in a thread-safe way, but the messages sent with Alarming are meant for operators, not developers, and may require an operator to acknowledge them before the application continues. Every unit forwards its alarms to this single application-wide instance through an internal
AlarmingDecorator, which prefixes each message with the unit's name; an unacknowledged critical alarm also blocks that unit from starting new sequences.Units: last but not least, all units of the application are instantiated. The communication and data structures of every unit are initialized by reference from
ZGlobal. Every unit has its own specialized structs with all necessary data types for that unit, but the location where all this data is stored is the same for every Zeugwerk Application, providing a clear interface to external applications that are connected to the PLC with ADS or a similar protocol. See the Units concept pages for what happens inside a unit, and Multi-Unit Applications for how several units are organized together.
What else lives in ZModuleProgram
Besides units, other function blocks are usually instantiated in ZModuleProgram when they need to be shared by multiple unit instances, for example a shared fieldbus master or a mutex. This pattern, together with how application-specific safety logic is typically built, is covered in Shared Equipment.