<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: standarddeviant</title>
    <description>The latest articles on Forem by standarddeviant (@standarddeviant).</description>
    <link>https://forem.com/standarddeviant</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1319262%2F6a14fec1-2d32-4e61-80ab-f3814655990a.png</url>
      <title>Forem: standarddeviant</title>
      <link>https://forem.com/standarddeviant</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/standarddeviant"/>
    <language>en</language>
    <item>
      <title>Firmware Architecture with StateSmith</title>
      <dc:creator>standarddeviant</dc:creator>
      <pubDate>Sun, 30 Nov 2025 13:32:50 +0000</pubDate>
      <link>https://forem.com/standarddeviant/firmware-architecture-with-statesmith-38b9</link>
      <guid>https://forem.com/standarddeviant/firmware-architecture-with-statesmith-38b9</guid>
      <description>&lt;p&gt;StateSmith is a tool that can transform UML into Code+Diagrams. Besides UML, other valid inputs are yED diagrams and draw.io diagrams.&lt;/p&gt;

&lt;p&gt;Importantly StateSmith...&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;is well tested&lt;/li&gt;
&lt;li&gt;has rich features&lt;/li&gt;
&lt;li&gt;has multi-language support&lt;/li&gt;
&lt;li&gt;is open source&lt;/li&gt;
&lt;li&gt;is light-weight&lt;/li&gt;
&lt;li&gt;is easy to use&lt;/li&gt;
&lt;li&gt;has simple integration with VSCode for visualization&lt;/li&gt;
&lt;li&gt;produces auto-generated code that is easily readable &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm an embedded engineer with almost 20 years of experience. I have encountered many buggy state machines written in C. Sadly, I have written some buggy state machines in C. I started using StateSmith on an important project written in C on a microcontroller.&lt;/p&gt;

&lt;p&gt;I call StateSmith a "career changer" because now:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I will never write another complex state machine by hand.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Paraphrasing Adam, the creator of StateSmith, the value in tools like this is that once you create the UML diagram, you get:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;source code that does exactly what you specified&lt;/li&gt;
&lt;li&gt;a visual diagram of the state machine behavior&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The visual diagram is extremely helpful when debugging an issue or changing behavior of the code.&lt;/p&gt;

&lt;p&gt;This lets us:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Fix the code by fixing the picture.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;StateSmith doesn't remove the need for care and consideration when designing system behavior, but it enables a much more rapid debug cycle by providing source code and a diagram that are effectively &lt;strong&gt;&lt;em&gt;guaranteed&lt;/em&gt;&lt;/strong&gt; to match each other.&lt;/p&gt;

&lt;p&gt;If all you need is a single state machine, then dive into the StateSmith docs and you should be all set.&lt;/p&gt;

&lt;p&gt;However...&lt;/p&gt;

&lt;p&gt;Multiple state machines that interact with each other is necessary to handle the complexity of many projects. I wanted a simple and robust way to test overall system behavior with Hardware-In-Loop testing and realized I wanted the ability to "inject" events into any state machine and get a "report" of each issued event and state change for all involved state machines.&lt;/p&gt;

&lt;p&gt;The test software only has to concern itself with syntax for two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;injection of events&lt;/li&gt;
&lt;li&gt;reporting of events and state changes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For simplicity, I use a single message type that contains three fields of information:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;event-or-state-change&lt;/li&gt;
&lt;li&gt;subsystem ID, where each state machine has its own integer ID&lt;/li&gt;
&lt;li&gt;subsystem value, an integer ID to represent individual events or state changes&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Fields 1 and 2 are just static, manually created enumerations. Field 3 is nicely handled by StateSmith infrastructure per &lt;code&gt;SomeSm_StateID_...&lt;/code&gt; and &lt;code&gt;SomeSm_EventID_...&lt;/code&gt; for a single state machine named &lt;code&gt;SomeSm&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The C version of this data type is&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="n"&gt;mtype&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="c1"&gt;// event=0 or state=1&lt;/span&gt;
  &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="n"&gt;subsys_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// manually created enum&lt;/span&gt;
  &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="n"&gt;subsys_val&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// event IDs or state IDs generated via StateSmith&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;inj_rep_t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Per the type name, this single data type can be "injected into" or "reported from" any state machine.&lt;/p&gt;

&lt;p&gt;Whether a message is "injected" or "reported" is determined by the text syntax over a text-based serial port (like UART) or implied per function calls in the firmware.&lt;/p&gt;

&lt;p&gt;Another important idea I stumbled upon is using a "content-based message router" that receives all reported events and state changes, and then potentially injects new events based on reported events and state changes.&lt;/p&gt;

&lt;p&gt;This enables modular and flexible logic for the overall system behavior. It also opens the door for flexible Hardware-In-Loop testing!&lt;/p&gt;

&lt;p&gt;A diagram of injected and reported events for HIL testing is shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2i5n28xvzdo9u4w2skl0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2i5n28xvzdo9u4w2skl0.png" alt="HIL Capable Firmware Architecture with StateSmith" width="661" height="581"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Importantly, I'm using Zephyr RTOS and chose to use &lt;code&gt;zbus&lt;/code&gt;, a pub/sub implementation built into Zephyr RTOS. My usage of &lt;code&gt;zbus&lt;/code&gt; is very simple; it just takes all reported events and routes a copy to the content-based message router and (optionally) a copy to the UART based reporting feature. This second copy to the "reporter" is extremely useful for HIL testig to verify the system is in a certain state or that certain events have happened.&lt;/p&gt;

&lt;p&gt;With this approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No state machine has to know about any other state machines

&lt;ul&gt;
&lt;li&gt;But HAL-based query functions may enable simpler state machines&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;All "integration logic" is in one place, the content-based router

&lt;ul&gt;
&lt;li&gt;This accelerates development and changing overall system behavior &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;A text message of &lt;code&gt;inj A B C&lt;/code&gt; is virtually the same as an actual hardware event 

&lt;ul&gt;
&lt;li&gt;This helps enable all kinds of automated HIL testing!&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;A "reporter" script that ingests all the &lt;code&gt;rep X Y Z&lt;/code&gt; messages is very easy to write and maintain&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;After a bit of googling and asking, I think it's accurate to say this approach is&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;the "Actor Model" leveraging a "Content-Based Message Router"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This approach is probably not appropriate for many situations. However, for a product with multiple different types of hardware that need to interact with each other, it seems to be a very efficient way to manage the necessary complexity of different hardware interactions. Good examples of different hardware it can integrate in a firmware project are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Buttons&lt;/li&gt;
&lt;li&gt;Lights&lt;/li&gt;
&lt;li&gt;Displays&lt;/li&gt;
&lt;li&gt;Speakers&lt;/li&gt;
&lt;li&gt;Motors&lt;/li&gt;
&lt;li&gt;Wireless Radio Connections, i.e. Bluetooth&lt;/li&gt;
&lt;li&gt;Battery charger chips or Power-Management ICs (PMICs)&lt;/li&gt;
&lt;li&gt;and more!&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Tying It Altogether (Update)
&lt;/h1&gt;

&lt;p&gt;In the original post, I left out some key implementation considerations regarding "injection" and "reporting" since those are pieces of project infrastructure that are custom and outside StateSmith.&lt;/p&gt;

&lt;p&gt;From the above data-flow diagram, the key pieces technically outside StateSmith are&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;INJ&lt;/code&gt;, a C function to inject events into state machines&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;REP&lt;/code&gt;, a C function to report events and new states&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;INJ&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The key requirements are &lt;code&gt;INJ&lt;/code&gt; are&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a custom enumeration of state machines as mentioned above&lt;/li&gt;
&lt;li&gt;Write a function with a switch statement that looks something like:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdint.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;"FirstSm.h"&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;"SecondSm.h"&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;"ThirdSm.h"&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;"EtcSm.h"&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;SM_ID_FirstSm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;SM_ID_SecondSm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;SM_ID_ThirdSm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;SM_ID_EtcSm&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// NOTE: allocate global objects&lt;/span&gt;
&lt;span class="n"&gt;FirstSm&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;SecondSm&lt;/span&gt; &lt;span class="n"&gt;second&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;ThirdSm&lt;/span&gt; &lt;span class="n"&gt;third&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;EtcSm&lt;/span&gt; &lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// NOTE: INJ blindly assumes that the state machines have been initialized elsewhere&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;INJ&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="n"&gt;state_machine_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="n"&gt;event_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state_machine_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;SM_ID_FirstSm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;FirstSm_dispatch_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;SM_ID_SecondSm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;SecondSm_dispatch_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;second&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;SM_ID_ThirdSm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;ThirdSm_dispatch_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;third&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;SM_ID_EtcSm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;EtcSm_dispatch_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nl"&gt;default:&lt;/span&gt;
    &lt;span class="n"&gt;LOG_WRN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"INJ received unknown state_machine_id = %d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state_machine_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now any part of the code, especially the router function can call &lt;code&gt;INJ(SM_ID_FirstSm, FirstSm_EventId_BeFirst);&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;REP&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The core &lt;code&gt;REP&lt;/code&gt; function is simpler than &lt;code&gt;INJ&lt;/code&gt;, but setting up the "router" and "reporter" takes a bit of code to set up a thread and the &lt;code&gt;zbus&lt;/code&gt; channel since I'm using Zephyr RTOS.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;zephyr/kernel.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;zephyr/zbus/zbus.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;thread_fn_router&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ptr1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ptr2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ptr3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;K_THREAD_DEFINE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;thread_id_router&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4096&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="n"&gt;thread_fn_router&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;



&lt;span class="n"&gt;ZBUS_MSG_SUBSCRIBER_DEFINE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;ZBUS_LISTENER_DEFINE_WITH_ENABLE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reporter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fn_reporter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;ZBUS_CHAN_DEFINE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;report_chan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                      &lt;span class="cm"&gt;/* Name */&lt;/span&gt;
                 &lt;span class="n"&gt;inj_rep_t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                        &lt;span class="cm"&gt;/* Message type */&lt;/span&gt;
                 &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                             &lt;span class="cm"&gt;/* Validator */&lt;/span&gt;
                 &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                             &lt;span class="cm"&gt;/* User data */&lt;/span&gt;
                 &lt;span class="n"&gt;ZBUS_OBSERVERS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reporter&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="cm"&gt;/* observers */&lt;/span&gt;
                 &lt;span class="n"&gt;ZBUS_MSG_INIT&lt;/span&gt;&lt;span class="p"&gt;(.&lt;/span&gt;&lt;span class="n"&gt;mtype&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subsys_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subsys_val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;fn_reporter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;zbus_channel&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;chan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// static uint32_t count;&lt;/span&gt;
  &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;inj_rep_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ir&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;report_chan&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;chan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;zbus_chan_const_msg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chan&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Use this&lt;/span&gt;

    &lt;span class="c1"&gt;// NOTE:&lt;/span&gt;
    &lt;span class="c1"&gt;// optionally print to reported message, ir, to UART to orchestrate&lt;/span&gt;
    &lt;span class="c1"&gt;// hardware-in-loop testing&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;thread_fn_router&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ptr1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ptr2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ptr3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;uint32_t&lt;/span&gt; &lt;span class="n"&gt;tic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;toc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;inj_rep_t&lt;/span&gt; &lt;span class="n"&gt;rep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;rc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;ARG_UNUSED&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ptr1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;ARG_UNUSED&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ptr2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;ARG_UNUSED&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ptr3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;zbus_channel&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;which_chan&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// struct acc_msg acc = {0};&lt;/span&gt;

  &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;zbus_sub_wait_msg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;which_chan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;rep&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;K_FOREVER&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;s_report_chan&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;which_chan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// NOTE: router_proc_rep function handles per-project-desires of how to&lt;/span&gt;
    &lt;span class="c1"&gt;// relay/route messages into follow-on events i.e. when the system "wakes&lt;/span&gt;
    &lt;span class="c1"&gt;// up", we send a message to "blink the led with the wake-up pattern"&lt;/span&gt;
    &lt;span class="n"&gt;router_proc_rep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rep&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;REP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inj_rep_t&lt;/span&gt; &lt;span class="n"&gt;ir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;rc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ir&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subsys_val&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// WARN: don't report 'do events'&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;rc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;zbus_chan_pub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;s_report_chan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;ir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;K_NO_WAIT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;rc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// handle publish error&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;rc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm omitting an example for &lt;code&gt;router_proc_rep&lt;/code&gt;, but the approach is similar to the inject function:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;make a switch case that handles each state machine separately&lt;/li&gt;
&lt;li&gt;handle events and state-changes in the "message router" to potentially produce new events in state machines&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  One last piece of the puzzle...
&lt;/h1&gt;

&lt;p&gt;The arrow where all the state machines effectively call &lt;code&gt;REP&lt;/code&gt; is not included in "vanilla StateSmith" at this time. Using StateSmith has two general paths:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The Easy Way&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;.csx&lt;/code&gt; Way, more complex and more capable&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I chose the The Easy Way, which has no hooks to call a function on events or state changes. So...&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;sd&lt;/code&gt; to the rescue!
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;sd&lt;/code&gt; is a modern alternative to &lt;code&gt;sed&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The main function of &lt;code&gt;sd&lt;/code&gt; is to "find and replace" content in files. I use it in a script to carefully find and replace all occurrences where an event_id is dispatched or the state_id variable changes in a StateSmith output source file. This is mildly sketchy, but it does work.&lt;/p&gt;

&lt;p&gt;To do this, I wrote a simple PowerShell script to iterate over a defined list of state machine source files in my project, that looks similar to&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;# This script will perform (2) sd operations per state-machine source file 
&lt;/span&gt;
&lt;span class="cp"&gt;# WARN: THIS SCRIPT MODIFIES THE CONTENTS OF ALL AUTO GENERATED STATE MACHINE C SOURCE FILES
&lt;/span&gt;
&lt;span class="cp"&gt;# WARN: this is NOT idempotent!!!!!
&lt;/span&gt;
&lt;span class="cp"&gt;# INFO: The (2) operations per file effectively inject zbus-based reporting for
# 1. all dispatched events via XYZ_event_dispatch(...)
# 2. all state changes via sm-&amp;gt;state_id = XYZ;
&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;smList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"FirstSm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"SecondSm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"ThirdSm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"EtcSm"&lt;/span&gt;
&lt;span class="n"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;smName&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;smList&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;Write&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Host&lt;/span&gt; &lt;span class="s"&gt;"Adding report hooks to: $smName.c"&lt;/span&gt;

  &lt;span class="cp"&gt;# operation 1-of-2 : (1) event-dispatch
&lt;/span&gt;  &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;find_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="p"&gt;(.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;_dispatch_event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;((.&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(.&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;
  &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;rep_str_1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;    &lt;span class="n"&gt;REP&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;inj_rep_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;    &lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;mtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;MTYPE_EVENT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;
  &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;rep_str_2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;".subsys_id=SM_ID_$smName, .subsys_val=event_id});"&lt;/span&gt;
  &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;rep_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"$rep_str_1$rep_str_2"&lt;/span&gt;

  &lt;span class="n"&gt;sd&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="err"&gt;`&lt;/span&gt;
    &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;find_str&lt;/span&gt; &lt;span class="err"&gt;`&lt;/span&gt;
    &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;rep_str&lt;/span&gt; &lt;span class="err"&gt;`&lt;/span&gt;
    &lt;span class="s"&gt;"sm/$smName.c"&lt;/span&gt;

  &lt;span class="cp"&gt;# operation 2-of-2 : (2) state change
&lt;/span&gt;  &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;find_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;'\&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;sm&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;state_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;
  &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;rep_str_1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;'\&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;sm&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;state_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;REP&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;inj_rep_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;
  &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;rep_str_2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"{.mtype=MTYPE_STATE, .subsys_id=SM_ID_$smName, "&lt;/span&gt;
  &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;rep_str_3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subsys_val&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;
  &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;rep_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"$rep_str_1$rep_str_2$rep_str_3"&lt;/span&gt;

  &lt;span class="n"&gt;sd&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="err"&gt;`&lt;/span&gt;
    &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;find_str&lt;/span&gt; &lt;span class="err"&gt;`&lt;/span&gt;
    &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;rep_str&lt;/span&gt; &lt;span class="err"&gt;`&lt;/span&gt;
    &lt;span class="s"&gt;"sm/$smName.c"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My understanding is that I could add these "reporting hooks" in a cleaner way with &lt;code&gt;.csx&lt;/code&gt; files.&lt;/p&gt;

&lt;p&gt;It's a workable solution for now, but in the future I plan to remove the requirement for a "very careful regex operation" from my build process.&lt;/p&gt;

&lt;p&gt;I've been rather pleased with what I've been able to achieve with this approach in about 6 months of development time. Please feel free to leave suggestions, critiques, and alternative approaches in the comments.&lt;/p&gt;

</description>
      <category>c</category>
      <category>automation</category>
      <category>architecture</category>
      <category>tooling</category>
    </item>
  </channel>
</rss>
