Articles

Tips and tricks! Learn how to get the most out of the framework by exploring the articles here.

  • Worker PPLs - An example using the DMM HAL Demo Project

    Packed Project Libraries (PPLs) are commonly used in LabVIEW to create modular plugin architectures. However, once inheritance and dynamic loading are introduced, dependency management can quickly become difficult. This guide describes how to create a Worker PPL plugin. For this example, we will take a working source-code example and turn it into a deployable PPL plugin architecture. To make it easy to follow, this guide uses the DMM HAL Demo Project that ships with the Workers SDK, allowing you to follow along with the steps. The result is a version of the example project in which both the DMM HAL and the DMM Workers are PPLs, suitable for deploying in production in place of the source code. What you will need The DMM HAL Demo Project — one of the example projects that ships by default with the Workers SDK. This is the Source Project you will work from throughout this guide. LabVIEW Solution Builder — a free tool that builds packed libraries in the correct dependency order. Download the master branch from github.com/jovianarts/LVSolutionBuilder, then unzip it somewhere convenient.. LabVIEW 2019 or later — required by the LabVIEW Solution Builder. Important: Source and PPL modules are not interchangeable A library compiled into a PPL belongs to the PPL runtime tree. The same library in source form belongs to the development source tree. The two are not interchangeable: a PPL plugin cannot be loaded with source-based parents, and a source plugin cannot be loaded with PPL parents. Figure 1 : Source and PPL modules are not interchangeable. PPL Workers must inherit from the exact same parent libraries. Because the source code and the PPLs belong to separate trees, the workflow uses two separate projects: a Source Project, where development takes place, and a PPL Project, where the built PPLs are assembled into an application. Steps 1–3 produce the PPL runtime tree from the development source tree. Steps 4–5 work within the PPL runtime tree. Let's begin... Before starting, create a copy of the DMM HAL Demo Project example project that ships with the Workers SDK. (This can be found under the Workers tools menu > Example Projects). Confirm that the example project runs correctly from source. The code should be working before it is built. Step 1 — Organize the Source Project into libraries The Solution Builder works at the library level, so each component to be turned into a PPL must reside in its own library. Create a library named DMM Workers.lvlib and add the Worker plugins (Fluke, Keithley and Keysight DMM) to it. Create a library named DMM HAL.lvlib and add DMM HAL.lvclass to it, together with its launchers — Asynchronous Launcher - DMM HAL.vi and Launcher - DMM HAL.vi. The Source Project should now contain two libraries, each holding one layer of the architecture, as shown below in Figure 2. Figure 2 : The Source Project. DMM Workers and DMM HAL are both moved into separate libraries, and both libraries have PPL build specs. Step 2 — Create the packed library build specs Create a Packed Project Library build specification for each library, as shown in Figure 2: PPL DMM HAL — built from DMM HAL.lvlib. PPL DMM Workers — built from DMM Workers.lvlib. In each build spec, open the Source Files page and confirm that the library created in Step 1 is the source — not loose classes or VIs. Open the Additional Exclusions page and tick Exclude dependent packed libraries, as shown below. This setting prevents the builder from embedding a private copy of the HAL inside the Workers PPL. Both PPLs then resolve to one shared HAL at runtime — a single shared class identity rather than two separate ones. Figure 3 : Make sure "Exclude dependent packed libraries" is checked Step 3 — Build the PPLs with LabVIEW Solution Builder The PPLs must be built in dependency order: the HAL first, then the Workers that depend on it. ⭐ Good news! You don't need to manually do this! The Solution Builder determines this order and carries out the builds. Open SolutionBuilder.vi from the copy downloaded earlier from the LabVIEW Solution Builder GitHub repository, and point the Path field on the front panel to your Source Project. Run SolutionBuilder.vi. The Solution Builder analyses the library dependencies, builds PPL DMM HAL first, relinks it in memory, then builds PPL DMM Workers with the PPL DMM HAL as its linked dependency. After the tool completes, the Status for both build specifications should say Built. Figure 4 : Solution Builder.vi The two output PPLs — PPL DMM HAL.lvlibp and PPL DMM Workers.lvlibp — are written to the build destinations defined in their build specs. Note these locations; the files are needed in the next step. Step 4 — Create the PPL Project Create a new, separate project as shown in Figure 5 below, and add the two PPLs produced by the Solution Builder — PPL DMM HAL.lvlibp and PPL DMM Workers.lvlibp. This is the PPL Project. Everything in it works with the packed libraries, which are not the same VIs the Source Project uses. This project corresponds to the PPL runtime tree in Figure 1 above. Figure 5 : PPL Project. This project does not contain the source code, but the PPL DMM HAL and the PPL DMM Workers. Step 5 — Use the PPLs The PPL Project needs its own top-level VI to run the example. Add a VI named PPL Universal DMM User Interface.vi (as in Figure 5) to the project. It can begin as a copy of the Universal DMM User Interface.vi from the Source Project, but every Worker class and Public API VI used on its block diagram must be replaced with the equivalent Worker class or VI from the PPL libraries, as shown in Figure 6 below. The Source Project and the PPL Project use different files, and the deployed VI must reference the PPL versions throughout. Figure 6 : a copy of the Universal DMM User Interface.vi (taken from the source project) but all Public API VIs and Worker class constants are replaced with the PPL versions. With that done, you have successfully built Worker PPL plugins. Because PPL DMM Workers.lvlibp was built with PPL DMM HAL.lvlibp as its linked dependency, its Worker classes inherit from the PPL HAL. A Worker (e.g. Fluke, Keithley, etc.) can therefore be wired directly into the PPL HAL Public API VIs. Building every layer as a PPL, in dependency order, with Exclude dependent packed libraries enabled, places the whole system on a single PPL tree: one DMM HAL, one shared class identity, and any DMM Worker that inherits from it can be loaded without modification.. Step 6 — Update the Workers and rebuild You can now continue development in the Source Project. New DMM Workers can be created, and existing ones modified, in the usual way. To deploy those changes, run the Solution Builder again as in Step 3. As long as the DMM HAL source code is unchanged, the Solution Builder detects this and does not rebuild PPL DMM HAL — it rebuilds only PPL DMM Workers. The updated PPL is written to the same destination, and the PPL Project picks up the new Workers without any further changes. This is the ongoing workflow: develop in the Source Project, build with the Solution Builder, and the PPL Project always references the built PPLs. Best of luck! 🙂
  • Worker Reuse Patterns: The Four Types of Structure and Behavior Reuse in the Workers Framework

    Modern modular LabVIEW applications grow quickly, and without structured development they can become difficult to maintain, scale, and test. The Workers framework for LabVIEW was designed around a simple but important principle: Reuse is not primarily about saving time today. It is about controlling complexity tomorrow. The framework provides several structured ways to reuse functionality, each suited to a different design need. This allows you to build systems using: reusable boilerplate shared behavior reusable functional modules shared APIs with interchangeable implementations Each of these represents a different architectural reuse pattern. This article explains the four architectural reuse patterns available in the Workers framework and helps you understand when and where to use each. Let’s get started. 1️⃣ Template Workers Multiple different Workers that share the same starting boilerplate Every time you create a new Worker using the Create/Add Worker tool, the framework generates it from a template. You can also define your own templates to establish consistent starting structures for future Workers. Use a Worker template when you want to create new Workers that start with the same structure but will eventually diverge in behavior. A template provides: the same Main VI structure (MHL and/or EHL(s), default cases) the same virtual folder layout the same naming conventions and icons Each generated Worker becomes its own independent asynchronous process at run-time. Changes made later to the template do not propagate to existing Workers. Use a Worker template when: You need several Workers that begin with the same boilerplate The Workers will diverge in logic or purpose You want consistent structure and style across your application Example The framework provides two default Worker templates: Worker with EHL (EHL + MHL) Worker without EHL (MHL only) Every Worker you create begins from one of these templates and then evolves as you add custom functionality to it. 2️⃣ Multiple Instances Same Worker, multiple roles Every Worker is a class, which means you can create multiple instances of it at run-time. By either statically-linking the same Worker multiple times or dynamically loading it, you create multiple clones of the same Worker's Main VI, with each running independently. Using multiple Worker instances gives you: identical logic and behavior the same Public API differences only in Alias, configuration, or role This is the most maintainable pattern when all instances behave the same. At run-time, each Worker instance is assigned a unique alias and address, making it easy to identify and communicate with each instance in your application and from the Workers Debug Server. Because all instances use the same Worker class, you maintain one codebase. Use multiple Worker instances when: All instances should behave identically You want low maintenance overhead You need only Alias or configuration differences Example You have one Worker class: NI USB-6009 Device You want to control three separate NI USB-6009 devices, so you add multiple static instances: NI USB-6009 Device 1 NI USB-6009 Device 2 NI USB-6009 Device 3 All instances share the same implementation (i.e. logic) but represent different physical devices or logical channels. 3️⃣ Worker User Library Pre-built, fully functional Workers reused across projects The Worker User Library tool allows you to reuse entire, fully implemented Workers, not just structure or behavior, by adding a copy of a functional Worker (or library of Workers) directly into your LabVIEW projects. Workers stored in your Worker User Library: contain complete, working functionality can be integrated into new applications using their Public APIs help standardize functionality across teams and projects eliminate the need to reimplement common features become part of your organization’s internal “Worker toolbox” These Workers are often team-developed, well-tested modules that can be dropped into new projects with confidence. Use the Worker User Library when: You want to reuse functionality, not just structure A Worker already exists that solves your problem You want consistent, proven behavior across projects Your team wants a shared library of reusable Workers Example Your organization maintains reusable Workers such as: Modbus TCP Client OPC UA Tag Monitor PID Controller MySQL Data Logger Digital Output Device Once part of your Worker User Library, these modules can be added to any new application without recreating their functionality. 4️⃣ API Abstractions (e.g. HALs) One Public API shared by multiple Workers API abstraction is the most flexible form of Worker reuse. It is ideal when you need true polymorphic behavior: Multiple Workers share the same Public API but implement different functionality internally. This pattern is provided directly by the framework’s Public API Builder tool. To implement this pattern, you create: a Worker base class defining a shared Public API multiple derived Workers implementing device-specific logic Callers (and systems such as NI TestStand) interact only with the Public API defined in the base class. This is the abstraction layer. Specific implementations, i.e. the derived Workers, can then be loaded and swapped dynamically at run-time. Use this pattern when: Devices or logic vary behind a common Public API You need run-time interchangeability You want clean API polymorphism You want maximum design flexibility Example (taken from the Workers Training Course) Base Worker class: DMM HAL (shared Public API) Derived Workers: Fluke DMM (device logic) Keysight DMM (device logic) Keithley DMM (device logic) All three Workers share the same Public API that exists in the DMM HAL base class but contain their own device-specific logic. The implemented behavior is abstracted behind the Public API in the base class. 🎯 Conclusion The Workers framework for LabVIEW provides you with four flexible Worker reuse patterns, each suited to a different design need: Template Workers establish consistent starting structure. Multiple Instances reuse one implementation across multiple roles at run-time. The Worker User Library enables reuse of complete functional modules across projects. API Abstractions provides run-time flexibility through shared Public APIs and interchangeable implementations. As your applications grow, you may find yourself using different reuse patterns for different parts of the system. The key is that you should never need to duplicate code when reuse is possible. The Workers framework gives you the tools to build systems that are modular, maintainable, and scalable, whether you are developing a simple application or a large, multi-device, multi-team system. Thanks for reading — and remember: architect first. — Peter Scarfe, Creator of Workers for LabVIEW 📬 If you’d like to receive future articles like this directly in your inbox, consider joining the Workers Community website. By signing up, you’ll stay up to date with new articles, discussions, framework updates, and developments within the Workers ecosystem.
  • Using Workers 5.0 on NI Real-Time Targets - What you need to know

    At the recent GLA Summit 2025, I presented how Workers for LabVIEW (version 5.0) has evolved into an effective framework for structured, modular development on NI Real-Time (RT) targets like the CompactRIO. This article summarizes the main insights and key takeaways from my presentation. 🖥️ Modern Hardware Makes It Possible The capabilities of CompactRIO systems have drastically improved in recent years, making structured frameworks feasible and practical on RT targets. Consider these performance improvements: CPU Power: Modern cRIO CPUs are 20 to 50 times faster (DMIPS) than early models. Multicore CPUs: Current models provide dual or quad-core processors, enabling genuine parallelism for multiple real-time processes. RAM and Storage: Up to 32 times more RAM and significantly greater onboard storage capacities allow for modular and dynamically loaded applications. FPGA and Ethernet Capabilities: FPGA logic density has increased by approximately 10 times, and Ethernet throughput is substantially higher, enhancing real-time data streaming. This evolution in hardware means developers are no longer restricted to creating small, tightly coupled, monolithic applications—modular architectures are now practical and beneficial. Feature Early cRIOs (2004–2008) Modern cRIOs (2020–2025) Improvement Factor CPU Power ~500 DMIPS ~10,000–25,000 DMIPS ~20x to 50x CPU Cores Single-core only Dual-core and Quad-core available 2x to 4x RAM 64–128 MB 512 MB – 4 GB 8x to 32x Storage 128–256 MB Flash 4 – 64+ GB eMMC / SSD 16x to 256x FPGA Fabric ~200K logic gates (Virtex-II) 1–2M logic elements (Zynq-7020, Kintex-7) 5x to 10x Ethernet 10/100 Mbps Gigabit Ethernet, Time-Sensitive Networking 10x Application Size Small, monolithic apps only Large modular apps with HALs, dynamic loading 10x capability 🧩 Why Use Workers on NI Real-Time Targets? With hardware constraints largely resolved, Workers for LabVIEW becomes an ideal choice for structured RT development, providing: 1. Modern Hardware Compatibility Current cRIO hardware comfortably supports modular, message-driven architectures like Workers. There's no need for compromise on software design principles. 2. Unified Architecture for Windows and RT The Workers framework allows you to maintain consistency across platforms—develop once and deploy to both Windows and RT targets seamlessly. This dramatically reduces redundancy and simplifies development workflows. 3. Modular and Scalable Design Workers naturally encourages breaking applications into isolated, reusable modules. This scalability and modularity help manage complexity as systems grow. 4. Built-in RT-Compatible Tools Workers includes comprehensive tools designed explicitly for RT development—like automated Worker creation, API scripting, and the Workers Debug Server—greatly simplifying development and troubleshooting. 🔍 Solving Common RT Development Challenges Challenge 1: Debugging Reentrant VIs Debugging reentrant VIs on RT is challenging—typically, you can’t pause execution, probe wires, or set breakpoints. Workers resolves this by providing two types of Worker Main VIs: Main.vi (Reentrant): Standard, suitable for multiple instances. Main NR.vi (Non-Reentrant): Specifically for RT debugging, allowing standard LabVIEW debugging tools (breakpoints, probes, and stepping). Switching between these modes is quick and easy using the provided RT Worker Convert Tool, offering flexibility and control during development. Main.vi (re-entrant) and Main NR.vi (non-reentrant) Challenge 2: Limited Runtime Transparency RT targets traditionally offer minimal runtime feedback. The Workers Debug Server addresses this problem by providing live runtime monitoring and detailed message logs of applications deployed on remote RT targets, significantly enhancing visibility and debugging effectiveness. Workers Debug Server application connects to your Workers applications via TCP over your local network Challenge 3: Host-to-RT Data Streaming Complexity While Network Streams or Network Shared Variables are common, they have limitations and setup complexity. Workers simplifies host-to-RT communication through built-in TCP Server and TCP Client Workers, facilitating robust, scalable, and straightforward data streaming over TCP/IP. TCP Server and Client Workers can be used to communication between your Workers applications running anywhere on your local network ⚠️ Two Crucial Tips for RT Development Lastly, my presentation highlighted two critical recommendations to avoid common pitfalls: Disable "Separate Compiled Code from Source" Keep this flag off for all classes and VIs deployed to RT targets to avoid random deployment issues. Maintain Separate Projects for Host and RT Targets Using distinct LabVIEW projects for Windows and RT prevents class-locking and ensures compatibility with Workers development tools. 🎯 Final Thoughts Workers for LabVIEW 5.0 bridges the gap between modern software architectures and embedded RT systems. Thanks to the dramatic improvements in RT hardware, adopting a modular and structured development framework like Workers on CompactRIO targets is now not only possible—but practical and highly effective. Thanks for reading—happy wiring! — Peter Scarfe, Creator of Workers for LabVIEW
  • To Object or Not to Object, That Is the Question 🎭

    In Shakespeare's Hamlet, "to be or not to be" reflects on a choice between two fundamentally different paths. In object-oriented programming, we often face a similar existential dilemma: To object or not to object? That is the question. Here we’re talking about the decision to objectify a concept in your code—or not. It’s a question (and a very important one) that comes up frequently, especially as you build more complex applications with more advanced architectures. In LabVIEW, you can technically turn anything into an object, even simple types like integers, timestamps, or booleans. But here’s the catch: Both under-objectifying and over-objectifying your code will increase the complexity of your application. If you underuse objects, your architecture can become: Rigid Repetitive Difficult to scale However, if you overuse objects, your codebase can become: Bloated Hard to read Frustrating to debug The good news is that there’s a sweet spot in the middle, where the right use of objects makes your code cleaner, more flexible, and easier to maintain. This article explores how to find that balance, and explains how the Workers framework uses OOP to make your applications more modular, scalable, and easier to develop. 🔍 What Does “Objectifying” Something Mean? In LabVIEW, objectifying something means encapsulating it inside a class, giving it: Private state (data) Associated behavior (methods) Optional inheritance structure This enables powerful design patterns, including: ✅ Encapsulation – Keeping data and logic together ✅ Polymorphism – Swapping out different implementations with a common interface ✅ Inheritance – Sharing and reusing behavior between related components But these benefits come with trade-offs. Objectifying too much—or too early—can introduce overhead, abstraction layers, and unnecessary indirection. So while you can objectify everything, it’s often better to objectify only what adds real structure or value. ✅ When Should You Use an Object? Use an object when the thing you’re modeling: Has behavior, not just data Represents a conceptual entity (e.g. a device, task, or module) May have multiple implementations (e.g. real vs. simulated) Requires reuse, extension, or protection of internal state Benefits from being accessed through a defined interface Examples of good candidates for objects: A hardware device with IO and configuration A service process like logging, messaging, or control A Worker that encapsulates a modular, stateful thread of execution ❌ When Not to Use an Object Avoid objectifying when: You're working with simple types (scalars, booleans, strings) There's no behavior or internal logic to encapsulate You only need fast, lightweight data passing The object would add no clarity, reuse, or abstraction benefit Over-objectifying these types often leads to: More code and file overhead Slower development Frustrating debugging sessions Unnecessary complexity in small applications 🧱 How Workers for LabVIEW Uses Objects In multi-process applications, the most architecturally obvious use of objects would be to turn each process into an object—and that’s exactly what the Workers framework does—by encapsulating each process in a class called a Worker. This design choice improves the structure, modularity, and scalability of multi-process applications, and significantly improves code reuse. ✔️ What is objectified: The only thing that is objectified in the Workers framework are the Workers themselves. This alone provides the framework with the following benefits: Workers (the processes themselves): Each Worker is a stand-alone modular process that provides a stateful thread of execution, whose methods act on its own private encapsulated data. Worker base classes: Each Worker inherits from the ancestor class Worker.lvclass providing common functionality to all Workers. Developers can further extend this common functionality by adding their own base classes into a Worker's inheritance hierarchy. Dynamic Abstraction: Workers can be dynamically loaded at runtime and interacted with through API abstractions. This allows applications to run entirely on abstract interfaces, enabling simulation swapping, hardware abstraction, and plug-in extensibility, all without touching the business logic. ❌ What is not objectified: Public API Methods: Worker methods are contained within the Message Handling Loop (MHL) cases of a Worker's Queued Message Handler (QMH). They are invoked by the use of a Worker's (or Worker base class's) Local API or Public API VIs. This keeps the logic simple, direct, and intuitive. Message queue data: Message data sent between Workers is wrapped in a message-specific typedef stored in variants—not message objects—preserving the simplicity of the QMH pattern that many LabVIEW developers are familiar with. Data structures and state: Runtime data used by a Worker is stored as simple typedefs, scalars, or clusters within the class’s private data. These are not objectified unless there's a clear reason to encapsulate additional behavior (and is left up to the user). 🛠️ Example: Creating a Hardware Abstraction Layer (HAL) using Workers Let’s say you're developing a system to control a power supply, and you want the flexibility to swap between a real device and a simulation. The fact that Workers are objects allows for the easy creation of HALs in the framework. Using Workers 5.0, this is how you would achieve this: Step 1: Create a Worker Base Class (scripted) Create PowerSupply_Base.lvclass that contains the following Public API Requests: Set Voltage Get Voltage Power On/Off These are method declarations only, no behavior yet. Step 2: Create Two Workers (scripted) RealPowerSupply.lvclass : Communicates with the physical device SimulatedPowerSupply.lvclass : Returns mock values and logs actions Both classes inherit from the base class and implement the Public API behavior in their own Message Handling Loop (MHL) cases. Step 3: Load a Worker via Factory Pattern at Runtime (scripted) At startup, load either the real or simulated Worker based on a config file. The rest of your application talks only to the base class Public API, and remains unaware of which implementation is active. Without objects, this HAL implementation would require: Duplicated logic Scattered conditional code A brittle architecture that’s hard to maintain or extend 💬 Final Thoughts Object-oriented programming is a powerful tool, but with great power comes great responsibility. Whether you're just starting out with LVOOP or developing an enterprise-scale application, remember: Good architecture isn’t about turning everything into an object, it’s about objectifying what makes sense. And by objectifying the right things—and avoiding it where it's not needed—you get: ✅ Cleaner code ✅ Faster development ✅ Easier debugging ✅ Happier developers The Workers for LabVIEW framework follows this philosophy, providing you with the power of LVOOP where it makes sense, while keeping the rest of your application lean, readable and efficient. 📚 What's next? If you want to learn more about how to put the OOP features of the framework into practice, the best place to start is with the Workers for LabVIEW Training Course. This free course walks you through the core concepts, API design, debugging tools, and real-world exercises to help you build scalable applications with confidence 📖 Download the training course here: 👉 https://community.workersforlabview.io/training-course
  • “Do I need to know LVOOP to use Workers for LabVIEW?”

    One of the most common questions I am asked by LabVIEW developers discovering the Workers framework for the first time is: “Do I need to know LabVIEW Object-Oriented Programming (LVOOP) to start using Workers for LabVIEW?” The short answer? No, you don’t. While having a background in LVOOP or general OOP concepts can be helpful, Workers for LabVIEW was intentionally designed to be accessible to developers who have: Completed NI LabVIEW Core 3 Achieved CLD certification Built applications using the NI Queued Message Handler (QMH) design pattern A Framework Built for Where You Are — and Where You're Going When designing Workers, I was guided by a simple question: “What kind of framework do I wish I had access to right after finishing Core 3 — one that also helped me grow as a developer?” The result is a framework that supports advanced architectural design without requiring deep LVOOP knowledge upfront. 💡 It’s Easier Than You Might Think Many beginner and intermediate developers have told me that Workers felt familiar and intuitive from day one. Why? Because you still build applications in the style of the NI QMH template — only now, with powerful tools that handle the hard parts for you. 🔧 Development Tools Do the Heavy Lifting: Create and plug in new Workers to build your application architecture Auto-generate Local and Public APIs, including ones callable from NI TestStand Debug your system — whether running in the LabVIEW IDE, as an EXE, PPL, or on a cRIO Visualize call chains with the Call-Chain Viewer Use pre-built Workers, including: Message Pump Worker TCP Server/Client Workers 👨‍🏫 What About LVOOP? Let’s be honest — LVOOP can feel daunting at first. That’s why Workers gently introduces object-oriented principles in a way that feels natural and manageable. The only required use of LVOOP is the Worker itself — each module is implemented as a class Think of Workers as smart LabVIEW libraries — classes with enhanced capability The scripting tools handle the boilerplate code: API generation, inheritance setup, and more You’re never left on your own to figure it out — Workers supports your learning curve and offers a practical, visual, hands-on path to LVOOP understanding. 🧱 Build Your Skills Naturally Over time, you’ll become comfortable with the use of LVOOP introduced by the framework, and you’ll see how features like: API abstraction Worker inheritance and polymorphism Runtime selection of concrete Worker classes ...can lead to reusable, maintainable, scalable application design. 🚀 Final Thought You don’t need to be an expert to build something powerful. You just need the right structure — and a clear place to begin. 📚 Want to Get Started with Workers for LabVIEW? To help developers get started with the framework, the best place to start is with the Workers for LabVIEW Training Course. This free course walks you through the core concepts, API design, debugging tools, and real-world exercises to help you build scalable applications with confidence 📖 Download the training course here: 👉 https://community.workersforlabview.io/training-course
  • Reducing Technical Debt with Workers for LabVIEW

    Technical debt is an invisible burden that accumulates over time in software projects. It arises when short-term decisions lead to long-term inefficiencies, making applications harder to maintain, debug, and scale. While it might seem faster to implement quick fixes or bypass best practices, this often results in messy architectures, tightly coupled components, and unmanageable codebases—ultimately slowing development and increasing costs Common Signs of Technical Debt (and what its leads to) If you’ve worked on a LabVIEW application that feels like a fragile house of cards—where even small changes risk breaking the entire system—you’re not alone. This is a common phenomenon that can occur at any stage of an application's development, and it usually means you’ve encountered technical debt. Common signs include: ❌ Messy Architecture – No clear separation of concerns, making code harder to navigate. ❌ Quick Fixes – Temporary patches that introduce long-term instability. ❌ Entangled Code – High dependencies between modules, making modifications risky. ❌ Inconsistent Design – A lack of uniform coding practices results in unpredictable behavior. ❌ Workarounds – Short-term hacks that lead to inefficiencies. ❌ Difficult Debugging – No logging, unclear error handling, and poor system visibility. What Good Software Looks Like The key to avoiding technical debt is to design software with maintainability, scalability, and flexibility in mind. Well-structured LabVIEW applications should include: ✅ Clean Architecture – Organized layers that make code easy to extend and maintain. ✅ Scalability – Designed to handle growth without performance bottlenecks. ✅ Decoupled Components – Independent modules that interact through well-defined interfaces. ✅ Standardized APIs – Ensuring predictable communication between system parts. ✅ Abstracted Logic – Reusable and extensible components that do not break core functionality. ✅ Easy Debugging – Structured logging, error handling, and system monitoring. How Workers for LabVIEW Helps Prevent Technical Debt One of the core reasons Workers for LabVIEW was created was to help developers build applications that naturally avoid technical debt by enforcing a structured, scalable, and maintainable design. Here’s how: 1. Enforcing a Modular, Hierarchical Architecture Workers for LabVIEW uses an Actor-based approach, where each Worker is a self-contained process that operates asynchronously. Workers plug together to form a hierarchy of processes. This prevents large monolithic applications from forming and ensures that components remain loosely coupled. In this hierarchy, Workers only communicate with their direct Caller or subWorkers—never laterally across the application. This prevents tangled cross-module messaging and keeps systems maintainable. 2. Promoting Decoupled Design with API Abstractions Workers communicate using Local and Public APIs, ensuring that different parts of the application do not rely on direct references to one another. By using API abstraction layers, developers can swap or upgrade components without affecting the rest of the system. 3. Standardizing API Interactions The framework provides structured messaging systems and pre-built API templates, ensuring that communication remains consistent across projects. This reduces inconsistencies, improves readability, and avoids the debugging nightmares caused by custom, ad hoc message handling. 4. Built-In Debugging Tools The Workers Debug Server allows developers to monitor, debug, and log messages in real-time, from applications running anywhere on their local network. No more digging through scattered log files or guessing where an error occurred—debugging becomes fast, visible, and manageable, even in large systems. 5. Future-Proofing Applications Because Workers for LabVIEW enforces best practices from the beginning, applications built with the framework are naturally easier to scale and maintain. Developers can confidently introduce new features or integrate new hardware without rewriting large portions of code. Final Thoughts Avoiding technical debt starts with choosing the right architecture from the beginning. Workers for LabVIEW provides a structured, scalable foundation to help you build modular, maintainable, and flexible LabVIEW applications—without the risk of accumulating complexity over time. Want to see it in action? If you’re ready to get up and running with the framework, the best place to start is with the Workers for LabVIEW Training Course. This free course walks you through the core concepts, API design, debugging tools, and real-world exercises to help you build scalable applications with confidence 📖 Start the training course here: 👉 https://community.workersforlabview.io/training-course
  • Applying the Dependency Inversion Principle (DIP) in Workers for LabVIEW

    One of the biggest challenges in LabVIEW software development is ensuring that applications are scalable, maintainable, and adaptable over time. As requirements evolve, tightly coupled code can make modifications difficult, leading to technical debt and increased development costs. A key principle in object-oriented software design that addresses this challenge is the Dependency Inversion Principle (DIP)—one of the five SOLID principles. In this article, we’ll explore: 🔹What Dependency Inversion is and why it matters. 🔹A real-world analogy to explain DIP in simple terms. 🔹How DIP applies to Workers for LabVIEW to improve modularity and flexibility. 🔹A real-world LabVIEW example of DIP in action. What is Dependency Inversion Principle (DIP)? The Dependency Inversion Principle (DIP) states: "High-level modules should not depend on low-level modules. Both should depend on abstractions." This means that instead of tightly coupling software components together, we should introduce an abstraction layer (such as an interface or API) between them. 🔹Without DIP: High-level logic is directly dependent on specific implementations, making changes difficult. 🔹With DIP: High-level logic depends only on abstract interfaces, allowing different implementations to be swapped without breaking the system. 🔌 The Power Outlet Analogy (A Simple Explanation of DIP) Imagine designing your home's electrical system. Image courtesy: https://www.slideshare.net/MarcoManga/dependency-inversion-principle You could hardwire every appliance (TV, fridge, phone charger) directly into the wall. But what happens when you want to upgrade a device or switch to a different brand? You’d have to rewire your entire house! Instead, homes use standard power outlets, allowing any device to plug in without modifying the wiring. This works because: 🔹High-level systems (home wiring) don’t depend on low-level details (specific appliances). 🔹Appliances follow a common interface (the power plug), making them interchangeable. 🔹You can swap out devices (TVs, lamps, chargers) without rewiring your home. This is exactly how Dependency Inversion works in software! Instead of hardwiring modules to specific implementations, you create a common interface (like the power outlet) so components can be swapped without breaking the system. How Workers for LabVIEW Applies the Dependency Inversion Principle Workers for LabVIEW embraces DIP by using Public API Abstractions to decouple high-level application logic from specific implementations. Here’s how DIP is applied in Workers for LabVIEW: 🔹High-level components (Workers) depend on API abstractions, not concrete implementations. 🔹Hardware, simulation, and UI Workers can be swapped without modifying the rest of the system. 🔹Public API Abstractions act like power outlets, allowing different Workers to "plug in" seamlessly. By structuring applications this way, Workers for LabVIEW makes it easy to swap out components without refactoring the entire system. Real-World Example in LabVIEW Let’s say you’re developing a LabVIEW application that interfaces with a Power Supply device. ❌ Traditional Approach (Violates DIP) A common way to develop this might be: Your application calls specific API VIs for a USB-connected power supply. The application logic is directly tied to this specific hardware. If the hardware changes (Ethernet-based power supply, simulated device), you must rewrite large portions of your code. ✅ DIP Approach Using Workers for LabVIEW By following DIP, you can design your system differently: Your application communicates with a generic Power Supply API (Public API Abstraction). Whether it’s a USB, Ethernet, or simulated power supply, the API remains the same. Your system doesn’t need modification—just swap the implementation without changing core logic. This reduces maintenance overhead and ensures your application is future-proof. Key Benefits of Applying DIP in Workers for LabVIEW By using Public API Abstractions in Workers for LabVIEW, you: 🔹Reduce Coupling – Components depend on abstract APIs, not concrete implementations. 🔹Improve Maintainability – Swap out Workers without modifying core logic. 🔹Enhance Flexibility – Easily integrate new hardware, UI, or simulation Workers. 🔹Future-Proof Your System – Your architecture remains adaptable as requirements change. Final Thoughts Dependency Inversion Principle (DIP) is essential for building flexible and scalable LabVIEW applications. Instead of tightly coupling dependencies, use API abstractions—just like power outlets! ⚡ By applying DIP with Workers for LabVIEW, you ensure that your applications remain modular, maintainable, and adaptable, even as requirements evolve.
  • Workers for LabVIEW : Scalable, Modular, and Flexible Applications

    LabVIEW has long been a powerful tool for engineers and scientists to develop applications for data acquisition, instrument control, and industrial automation. As applications grow in complexity, the need for a structured, modular, and scalable framework becomes increasingly important. Workers for LabVIEW is a framework that addresses these needs by combining the simplicity of the LabVIEW Queued Message Handler (QMH) design pattern with the flexibility of LabVIEW Object-Oriented Programming (LVOOP). This article explores the advantages of using Workers for LabVIEW to create asynchronous, modular, and flexible professional applications. Understanding Workers for LabVIEW Workers for LabVIEW is a framework designed to help developers build modular, scalable, and maintainable applications. At its core, it introduces the concept of a Worker - a modular, asynchronous process that performs specific tasks within a hierarchical system. Workers are implemented using the LabVIEW QMH design pattern and are encapsulated as LabVIEW classes inheriting from a common base class: Worker.lvclass. This combination leverages the strengths of both procedural and object-oriented programming paradigms, allowing developers to create applications that are both easy to understand and capable of handling complex requirements. Advantages of Using Workers for LabVIEW 1. Modularity Benefit: Breaking down applications into smaller, manageable components. Encapsulation of Functionality: Each Worker encapsulates specific functionality or a set of related tasks, promoting high cohesion and low coupling. Reusable Components: Workers can be developed, tested, and maintained independently, making them reusable across multiple projects. Ease of Maintenance: Modularity simplifies debugging and updating parts of the application without affecting unrelated components. Example: A data acquisition system can have separate Workers for signal conditioning, data logging, and user interface, allowing developers to modify the data logging mechanism without impacting the user interface. 2. Asynchronous Processing Benefit: Enhancing application performance through parallel execution. Concurrent Execution: Workers operate independently, allowing multiple processes to run in parallel without blocking each other. Responsive Applications: Asynchronous processing ensures that time-consuming tasks do not freeze the user interface or delay critical operations. Efficient Resource Utilization: System resources are utilized more effectively, improving overall application performance. Example: In an industrial control system, one Worker can monitor sensor data while another processes user commands, ensuring uninterrupted responsiveness. 3. Flexible Communication via APIs Benefit: Structured and decoupled inter-process communication. Local and Public APIs: Workers communicate through well-defined local and public APIs, promoting a clear separation of concerns. Decoupled Design: Workers do not need to know the internal workings of other Workers, reducing dependencies and facilitating module replacement or upgrades. Scalable Communication: The API-based communication model supports scalability as new Workers can be added without disrupting existing communication channels. Example: A Worker responsible for data analysis can request data from a data acquisition Worker using a public API, without needing to understand how data acquisition is implemented. 4. Hierarchical Architecture Benefit: Organized structure for complex applications. Structured Hierarchy: Workers are organized in a hierarchy where each Worker can have a Caller (parent) and sub-workers (children). Controlled Access: A Worker's queue is accessible only within its immediate hierarchy, enhancing security and reducing unintended interactions. Simplified Management: The hierarchical structure makes it easier to manage complex applications by providing clear oversight of Worker relationships. Example: In a manufacturing application, a top-level Worker could oversee production line Workers, each managing different stages of the assembly process. 5. Leveraging LabVIEW Object-Oriented Programming (LVOOP) Benefit: Advanced programming features for enhanced flexibility and code reuse. Inheritance: Workers can inherit common functionality from base classes, reducing code duplication. Encapsulation: Internal data and methods are hidden from other parts of the application, reducing complexity and potential errors. Polymorphism (dynamic dispatch): Worker methods can be extended or overridden to provide specialized behavior without altering existing code. Abstraction: Enables the creation of high-level interfaces, such as Hardware Abstraction Layers (HALs), allowing interaction with hardware without concerning low-level details. Example: Creating a base class for sensor Workers, from which specific sensor types (temperature, pressure, humidity) inherit common methods while implementing sensor-specific functionality. 6. Reusability and Scalability Benefit: Efficient development and easy scaling of applications. Reusable Workers: Workers can be reused across different projects, saving development time and ensuring consistency. Multiple Instances: Workers can be instantiated multiple times within the same application, supporting scalability. Adaptability: Applications can grow in complexity by adding or combining Workers without significant architectural changes. Example: Deploying the same data logging Worker in multiple applications, ensuring consistent data handling practices. 7. Enhanced Debugging and Development Tools Benefit: Streamlining the development and maintenance process. Workers Debug Server: Facilitates debugging and monitoring of applications, even over a local network. Development Tools: The suite of scripted development tools, including quick-drop shortcuts, enhance productivity and streamline coding. Consistency: Maintains consistent coding practices across the application with scripted tools that help you create new Workers, APIs and HALs for you. Example: Using the Workers Debug Server to monitor the performance of a specific Worker in real-time, identifying performance issues or errors quickly. 8. Suitable for All Developers Benefit: Easy entry point for beginners without depriving them the power of LVOOP. Queued Message Handler (QMH): Simple development style, familiar to many LabVIEW developers, as taught in the official NI LabVIEW Core 3 course. Ease of Transition: Start developing with familiar Worker QMHs, and grow into the framework's LVOOP features without being overwhelmed. Advanced Features: The framework's native use of LVOOP allows developers to architect applications using abstraction, inheritance, and polymorphism. Example: Developers familiar with the LabVIEW QMH can quickly adapt to using Workers, reducing training time and accelerating development. Those with LVOOP knowledge can immediately start using the more advanced features of the framework. Practical Applications Industrial Automation Workers for LabVIEW is ideal for industrial automation projects where multiple processes need to run simultaneously and communicate efficiently. The modularity and hierarchical architecture make it easy to manage complex systems involving machinery control, data acquisition, and real-time monitoring. Data Acquisition and Analysis In applications requiring high-speed data acquisition and processing, Workers allow for asynchronous data collection and analysis. Developers can create dedicated Workers for different data sources, ensuring that the system remains responsive and scalable. Test and Measurement Systems Test systems often require flexibility to accommodate different devices and protocols. By utilizing LVOOP features and creating HALs, Workers for LabVIEW enables the abstraction of hardware specifics, making it easier to integrate new instruments and adapt to changing testing requirements. Conclusion Workers for LabVIEW offers a powerful framework for developing asynchronous, modular, and flexible professional applications. By combining the simplicity of the QMH design pattern with the advanced features of LVOOP, it provides a scalable solution that enhances maintainability and promotes code reuse. Whether you are developing complex industrial systems or scalable data acquisition applications, Workers for LabVIEW equips you with the tools to build robust and efficient solutions. Ready to elevate your LabVIEW applications? Explore Workers for LabVIEW and experience the benefits of modular, asynchronous, and flexible application development. You can download the Workers SDK from VIPM, link here: https://www.vipm.io/package/sc_workers/
  • The importance of plug-in architectures in modern software development

    In the rapidly evolving world of software development, adaptability and scalability are no longer just advantages—they are necessities! With the increasing complexity of systems and the demand for faster innovation, developers and organizations must seek out architectures and frameworks that provide both flexibility and future-proofing. One such architecture that has gained significant traction in recent years is the modular plug-in architecture. This approach not only addresses the growing demands for modularity and extensibility but also ensures that systems remain manageable, maintainable and scalable as they evolve. Dynamically load and unload modules into your applications at run-time using Worker API abstractions Understanding Plug-In Architectures At its core, a plug-in architecture is designed to allow a software application to extend its functionality through independent modules or "plug-ins." These plug-ins interact with the core application through well-defined interfaces, enabling developers to add or modify features without altering the underlying system. Unlike monolithic architectures, where the entire application is built as a single, tightly-coupled unit, a plug-in architecture breaks down the system into smaller, more manageable components that can be plugged into each other. This modularity is the foundation of the many advantages that plug-in architectures offer. Here are some of the advantages of developing software applications using a plug-in architecture: Modularity One of the most significant advantages of a plug-in architecture is its modularity. By breaking down the application into discrete, independent modules, developers can manage and maintain each component separately. This not only simplifies the development process but also enhances the system's overall maintainability. In a modular system, each plug-in can be developed, tested, and deployed independently of the others. This reduces the complexity of the development process and allows teams to focus on specific functionalities without worrying about how their changes might impact the rest of the system. Additionally, modularity makes it easier to locate and fix bugs since issues can be isolated to specific plug-ins rather than sifting through an entire codebase. Extensibility The ability to extend an application's functionality is crucial in today's fast-paced development environment. A plug-in architecture provides a seamless way to introduce new features or update existing ones without disrupting the core system. For example, if a new customer requires a specific feature that isn't part of the standard application, developers can create a custom plug-in to meet this need. This approach avoids the risks associated with modifying the core application and ensures that new features can be integrated smoothly. Moreover, the extensibility of plug-in architectures supports continuous innovation. As new technologies and methodologies emerge, developers can integrate them into the system as new plug-ins, keeping the application relevant and competitive without requiring a complete overhaul. Scalability Scalability is another key advantage of plug-in architectures. As an application grows in size and complexity, the ability to scale individual components becomes increasingly important. In a monolithic architecture, scaling often requires scaling the entire system, which can be resource-intensive, messy and costly. With a plug-in architecture, however, individual plug-ins can be scaled independently based on their specific needs. For instance, if a particular feature experiences high demand, the corresponding plug-in can be optimized and scaled without affecting the rest of the application. This selective scalability ensures that resources are used efficiently and that the system remains responsive under varying loads. Customizability Different clients often have different needs, and a one-size-fits-all approach rarely works in software development. Plug-in architectures offer a high degree of customizability, allowing developers to tailor the system to meet specific client requirements. By developing custom plug-ins, organizations can provide bespoke solutions that address unique technical challenges without the need for extensive modifications to the core system. This not only reduces development time and costs but also ensures that the system can adapt to a wide range of use cases. Maintainability Maintaining a large, complex application can be daunting, especially when updates or bug fixes are required. Plug-in architectures simplify maintenance by isolating components, making it easier to identify and address issues. When a bug is reported, developers can focus their efforts on the specific plug-in responsible for the issue, rather than combing through an entire monolithic codebase. This targeted approach reduces downtime and ensures that fixes can be deployed quickly and efficiently. Additionally, updates to individual plug-ins can be made without impacting the rest of the system, reducing the risk of introducing new bugs or issues during the update process. This isolated approach to maintenance ensures that the system remains stable and reliable over time. Future-Proofing The technology landscape is constantly changing, and systems that cannot adapt quickly will soon become obsolete. Plug-in architectures provide a robust framework for future-proofing applications by enabling them to evolve with new technologies and requirements. As new standards, tools, and devices emerge, developers can integrate them into the system as new plug-ins, ensuring that the application remains up-to-date and competitive. This adaptability is crucial for long-term success, as it allows organizations to respond to industry changes without the need for costly and time-consuming system overhauls. Workers for LabVIEW as a plug-in architecture Workers for LabVIEW is a framework that provides you with a modular plug-in architecture, allowing you to create scalable, maintainable and future-proof applications in LabVIEW using pluggable units known as Workers. Let's discuss how Workers provides you with the features of a plug-in architecture. Modular Development A Workers application is built out of many of modular units, plugged together in the form of a tree. This tree of linked modular units is called the "Worker call-chain hierarchy" or "Worker application tree". Every Worker in an application's Worker call-chain is plugged into another Worker. The Worker that a Worker is plugged into is known as its "Caller". A Caller will automatically detect the addition of any Workers plugged into it, and is responsible for tasks such as creating a priority message queue for the Worker, as well as handling the initialization and shutdown sequences of the Workers plugged into it. Each Worker is a modular LVOOP unit providing native encapsulation of its set of tightly-coupled VIs and typedefs together with a Queued Message Handler that exists on a Worker's Main VI. Built into each Worker is a common interface that allows Workers to be dynamically loaded by another Worker and integrated at run-time into the Workers application tree. The modularity and cloneable nature of a Worker allows each Worker to be tested as a stand-alone unit, allows libraries of pre-developed Workers to be created and stored, and allows the re-use and loading of multiple instances of the same Worker within an application. All of this helps to reduce the overall development time of your projects, both initially and also when systems require changes, upgrades or maintenance in the future. One such example of a Worker that is often reused in Workers projects is the Message Pump Worker that you can add to any Workers project through the use of the Worker User Library tool. The Message Pump Worker is a pre-developed Worker with a Public API that simply sends a message to a Caller at a user-defined interval. This Worker, through the use of its Public API, will be dynamically loaded and plugged into its Caller. As many instances of the Message Pump Worker can be loaded within a Workers application as needed. The philosophy here is: create once... re-use many. Scale your application by adding Workers to the Workers application tree Adding new Workers to the branches of your Workers application tree is easy thanks to the plug-in architecture of the Workers framework. Whenever you need to add a new asynchronous process to your application, simply create a new Worker using the Create/Add Worker tool, and decide whether you want to add the Worker to your Workers application tree as a statically-linked Worker or whether you want to plug-in the Worker dynamically at run-time. This flexibility allows you to scale your applications as needed in a local way, by adding Workers to branches of your Workers application trees without affecting the Workers in the other branches of your application. When you add a Worker to your Worker application tree, the Worker is 'plugged in' to its Caller. The Caller is then responsible for passing framework specific data into the Worker, for the initialization and shutdown of the Worker, and also for the integration of the Worker into the Workers Debug Server application. These processes are automatically handled for you by the scripting tools and by the 'behind the scenes' framework code, so that you don't have to worry about such tasks. Plug in a new Worker into your Workers application tree, and then create and integrate the Worker's Public API with the Caller. The rest is handled for you by the framework. Worker API Abstractions provide plug-in flexibility to your applications One of the new features in Workers 5.0 is the ability to create API abstractions in a Worker base class. Workers that inherit from the Worker base class can then override the methods of the base class (including Public Requests) allowing you to create both the Public API for a Worker and the implementation of the Public API separately. This feature allows you to provide an abstract 'plug-in' for a Worker, without needing to directly plug the Worker into the core codebase of the application. This allows you to future-proof your applications, where-by you can choose at a later date which Worker(s) you want to load dynamically into your application at run-time through the use of the API abstraction. This process may sound complicated, but the scripted Public API Builder tool in Workers 5.0 can both create and implement Worker API abstractions for you. By separating a Worker's Public API from its implementation, you can provide flexible upgrades to the Workers in your applications by performing maintenance and upgrades to specific Workers, or even completely exchanging Workers, without needing to modify an application's core codebase. Developing with API abstractions also makes it easy to switch in and out different Workers at run-time... making it easy to both load and unload Workers dynamically, or switch between mock Workers, simulation Workers, or Workers that interface to different but similar hardware devices... all during run-time! Conclusion In an era where agility, scalability, and maintainability are paramount, adopting a plug-in architecture offers a clear path to success. The modularity, extensibility, and flexibility provided by this approach not only streamline the development process but also ensure that applications can grow and adapt to meet future demands. Whether you're building a single process application or a large and complex multi-process application, a plug-in architecture can provide the foundation for a scalable, maintainable, and future-proof system. By embracing this approach, developers and organizations alike can position themselves to thrive in an ever-evolving technological landscape.
  • Using Recursive Public API Requests

    Purpose : To demonstrate how you can create recursive Public API Requests in a Worker base class to send messages through an application's entire Worker call-chain, both down the Worker call-chain (i.e. from Worker to subWorkers) and up the Worker call-chain (i.e. from Worker to Caller). Introduction Every Worker, through its Public Properties (i.e. a property node on a Worker class wire), has access to the Public Properties of its Caller and also access to the Public Properties of its subWorkers. This allows you to send messages in the form of a Public API Request to both a Worker's Caller and a Worker's subWorkers. And if you are sending a Public API Request that exists in a Worker base class that is inherited by all Workers in an application, then you able to send a message throughout the entire Worker call-chain by creating a recursive Public API Request in a Worker base class. Method - Top to Bottom Send Public Request from Head Worker to every Worker in application This example will show you how to send a Public Request from an application's head Worker to every Worker in a Worker's application. The Public Request is propagated throughout a Worker's application 'behind the scenes' by sending a Public Request to the same MHL case of a Worker base class in every Worker in the application. Recursive Public API Request in the MHL case of a Worker base class The image above shows how to implement a recursive Public API Request in a Worker base class, to send a Public Request to a Worker's subWorkers. The Public Request VI (position 3.) sends a message to the MHL case which is displayed, however the input to the Request VI are the subWorker handles, which need to be cast first to the Worker base class. Things to note here: A Worker's Public Properties contain the handles of its subWorkers (both statically linked and dynamically loaded). These can be cast to their specific Worker class (or Worker base class) and then wired to the subWorker's Public Request VIs. You must then cast the subWorker handles to the Worker base class. This will not result in an error if every Worker in an application inherits from the same Worker base class you are casting to. You can then use the Worker base class's Public Request VI to send a Public Request to the same MHL case that contains this VI (i.e. a recursive call). The input to the Public Request VI is not of the same Worker, but instead that of the subWorkers. You must wire the message datatype for this MHL case to the Data in input terminal of the Public Request VI so that you can propagate the same message data to the subWorkers. Through the use of overriding the MHL case of a Worker base class, you are able to catch the Public API Request in any Worker that overrides the Worker base class. Of course if you override an MHL case of a Worker base class, make sure to call the Worker base class's MHL Cases.vi in the override MHL case so that the recursive call can be propagated down through the rest of the Worker call-chain. Example Project An example project is provided which propagates a recursive Public Request from the head Worker down throughout the entire Worker call-chain. Every Worker in the application inherits from the same Worker base class where the Public API Request exists. The example project's Worker call-chain is shown below. Worker call-chain for the Recursive Public API Request example project Added to the recursive Public Request's MHL case is an invoke node that will open the front panel of a Worker's Main VI (see screenshot directly below). Therefore, when the recursive Public Request VI is called from the Head Worker, every Worker's Main VI in the application will open it's front panel. Recursive Public API Request in Worker base class will open the Main VI's front panel of every Worker in the application. Source code The LabVIEW source code for the example project described above can be found in the Workers Community Additional Example Projects repository here. You can use https://download-directory.github.io/ to download individual folders directly from the GitHub repository. Method - Bottom to Top Send Public Request from any subWorker to Head Worker This example will show you how to send a Public Request from any Worker in an application all the way up to the application's head Worker. Usually you want to use Public Reponses to send a message from a Worker to its Caller, but if the Workers in an application are coupled together through the use of a common Worker base class, then it is also possible to use Public Requests to send messages up the Worker call-chain. This is the process we use here to propagate a 'behind the scenes' Public Request up through the Worker call-chain, by sending the Public Request to an MHL case of a Worker base class in every Caller in the application. Recursive Public API Request in the MHL case of a Worker base class The image above shows what how to implement a recursive Public API Request in a Worker base class, to send a Public Request up to a Worker's Caller. The Public Request VI (position 3.) sends a message to the MHL case which is displayed, however the input to the Request VI is the Caller's handle, which needs to be cast first to the Worker base class. Things to note here: A Worker's Public Properties contain the handles of its subWorkers (both statically linked and dynamically loaded). These can be cast to their specific Worker class (or Worker base class) and then wired to the Caller's Public Request VIs. You must then cast the Caller's handle to the Worker base class. This will not result in an error if every Worker in an application inherits from the same Worker base class class you are casting to. You can then use the Worker base class's Public Request VI to send a Public Request to the same MHL case that contains this VI (i.e. a recursive call). The input to the Public Request VI is not of the same Worker, but instead that of the Caller. You must wire the message datatype for this case to the Data in input terminal of the Public Request VI so that you can propagate the same message data to the Caller. Through the use of overriding the MHL case of a Worker base class, you are able to catch the Public API Request in any Worker that overrides the Worker base class. Of course if you override an MHL case of a Worker base class, make sure to call the Worker base class's MHL Cases.vi in the override case so that the recursive call can be propagated up through the rest of the Worker call-chain. Example Project This method is demonstrated in Method 3 of the article called Sending errors up the Worker call-chain, and the example project for this article demonstrates the use of a recursive Public API Request in a Worker base class to achieve this.