Compose String — Control Systems/Strings
Control_Systems/Strings/Compose_String · 2 input / 1 output port(s) at insert · exports to Python, MATLAB, Java, Rust, C, C++
Description#
The block's own DESCRIPTION_HTML, rendered verbatim — the same text the config dialog's info panel and the library navigator show. Fix a wrong sentence in the block's .cpp (R-D9), never here.
Compose String
Control Systems / Strings
Builds text from a format and its inputs, as C's and MATLAB's
sprintf do: y = sprintf(Format, u1, u2, …). With the
Format "x=%d, v=%.2f" and the inputs 7 and 3.14159, the output is
x=7, v=3.14.
Ports
- Input 1 (
i32,ICoreInt32, for the default Format's%d) – the value for the first conversion, one value [1,1]. - Input 2 – the value for the default Format's
%f, one number [1,1]. - Output – the text (
str,ICoreString), one value, cut at 255 bytes.
There is one input per conversion, in order; the number of inputs is yours to
set and must equal the number of conversions in the Format. What each input
carries is decided by its conversion, exactly as in Simulink:
%d and %i take a signed integer of at most 32 bits
(i8, i16, i32) or a bool;
%u, %x, %X and %o an
unsigned one (u8, u16, u32) or a
bool; %f, %e, %E,
%g and %G a floating-point number (f64,
f32); %s text (str,
ICoreString). Anything else is refused at the port. A double does
not go into %d: convert it with Data Type Conversion first, as in
Simulink.
Parameters
- Format – the format, written with its quotes as in
Simulink:
"%d %f"(the default). A doubled quote inside is one quote. Text is copied as it is;%%is a percent sign; the escapes\n,\t,\r,\a,\b,\f,\vand\\are the control characters they name. A conversion is%, then optional flags (-left-justify,+always show the sign,a space for a positive sign,0pad with zeros), an optional width, an optional.precision, and one ofd i u x X o f e E g G s. Between 1 and 128 conversions. - Sampling Time (s) – zero or less inherits the solver's rate; a positive value runs the block at that period.
What is not offered
Each of these stops the run with a message naming it: %c,
%a, %p, %n and a * width or
precision (Simulink refuses them too); length modifiers such as
%ld and %hd, which Simulink accepts; the
# flag; 0 together with -, with text, or
with an integer precision; + or a space on an unsigned or text
conversion; an integer precision of 0; and the escapes \x.. and
octal \... These are left out because the six target languages do
not agree on what they mean, and a block whose exported code disagreed with it
would be worse than one that says no.
Not-a-number and infinity
A floating-point conversion writes NaN, Inf or
-Inf – +Inf under the + flag
– and applies no width, precision or zero padding to them. That is
Simulink's own rule, measured; C's own printf would write nan and
pad it.
Code export
C, C++, Python, MATLAB, Java and
Rust all write the same text. The first four use their own
printf for each conversion; Java and Rust have no equivalent that
agrees with C on floating point, so their generated code rounds each value
exactly and lays it out by the same rules. The Format is read when the code is
generated, not when it runs.
VHDL, Verilog and SystemVerilog do not carry text, and PLC Structured Text is not supported either: the language has a string type, but the check that runs an exported core cannot read one back. An export to any of these four stops and names this block and the reason.
Simulink bridge
Import and export, mapped to simulink/String/Compose String. "Format"
crosses unchanged, quotes and all; on import the number of inputs and what each
one carries are read off it, since Simulink derives both from the Format as
well. The output data type is always string and is written as a
fixed setting. The Simulink block has no sampling-time setting of its own, so a
positive Sampling Time (s) stays on this side and is reported rather
than written.
Notes
- Algebraic, with no state: the output depends only on the current inputs.
- Width and precision on
%scount bytes of UTF-8 here and in C, and characters in Simulink and in Java, Python and MATLAB – the same number for plain English text.
Code facts#
| Fact | Value |
|---|---|
| registered type | Control_Systems/Strings/Compose_String |
| family | Control_Systems/Strings |
| solver environment class | ICoreBlock_0_Control_Systems_1_Strings_2_Compose_String |
| source | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Strings/Compose_String/ICoreBlock_0_Control_Systems_1_Strings_2_Compose_String.cpp |
| header | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Strings/Compose_String/ICoreBlock_0_Control_Systems_1_Strings_2_Compose_String.h |
| default size on canvas | 100 × 70 px |
| ports at insert | 2 in, 1 out |
| code generators implemented | Python, MATLAB, Java, Rust, C, C++ |
Ports#
| # | Direction | Signal type | Description label |
|---|---|---|---|
| 1 | in | ICoreInt32 | — |
| 2 | in | ICoreDouble | — |
| 3 | out | ICoreString | — |
Ports the constructor creates. A block whose port list changes with its configuration adds or removes ports at load time; the count above is the one a freshly inserted block has.
Configuration variables#
| Config variable | Default | Simulink parameter |
|---|---|---|
Format | "%d %f" | Format |
Every block also carries Sampling Time (s) from ICoreBlockSolverEnvironment: zero or less inherits the solver's rate, a positive value runs the block at that period.
Simulink bridge#
| support | Support::Both |
| Simulink path | simulink/String/Compose String |
| port-count rule | PortsParam::ComposeStringFormat |
SampleTime parameter | no — the counterpart defines none; the rate stays on the ICore side |
| ICore config | Simulink parameter | Value translation |
|---|---|---|
Format | Format | passes through |
Caveat (shown to the user): "Format crosses verbatim, quotes included
Catalog contract: src/ICoreBlocks/ICoreCoder/ICoreCommandSystem/SimulinkBridge/ICoreSimulinkBlockCatalog.h
Description vs code#
The lists agree. check_block_descriptions.py finds no disagreement between the description's Ports, Parameters, Code export and Simulink bridge lists and the code's.
The verdict above is
tools/docs/check_block_descriptions.py(P7.1), which compares LISTS. It cannot read a sentence: "stateless" on a block with a state, an initial-value semantic the recursion does not implement, a "not synthesizable" caveat the HDL banner contradicts. That is the agent audit (P7.3) on BLOCK_DESCRIPTION_AUDIT.md, and this tool's green is not a substitute for one.
File banner (developer view)#
The top comment of the block's .cpp — the maths, the realization and the export strategy, addressed to whoever changes it. It must not contradict the description above (P7.5).
Compose String -- sprintf(Format, u1, u2, ...), one input per conversion The Format is parsed ONCE per use into literal pieces and conversions (parseFormat below), and everything else is driven by that parse: the input count verify checks, each input's type and accepted set, the live answer, and every generator -- which walks the pieces at EXPORT time and emits straight-line code, so no target has to interpret a format at run time. The measured Simulink rules the parse enforces are in the header.
The output is a String signal, so the value leaves through the PORT (D2), and it is cut at 255 bytes, what the exported C buffer holds beside its terminator.
Sample results#
This block carries a ICoreString signal, whose value is text rather than a number and is not something a plot has an axis for. The samples are in the table below, exactly as the run recorded them.
| t | in ICoreDouble-Out-0 | out ICoreString-Out-0 |
|---|---|---|
| 0 | -2 | 0 -2.000000 |
| 0.4 | 0.5 | 0 0.500000 |
| 0.8 | -2 | 0 -2.000000 |
| 1.2 | 0.5 | 0 0.500000 |
| 1.6 | -2 | 0 -2.000000 |
| 2 | 0.5 | 0 0.500000 |
| 2.4 | -2 | 0 -2.000000 |
| 2.8 | 0.5 | 0 0.500000 |
| 3.2 | -2 | 0 -2.000000 |
| 3.6 | 0.5 | 0 0.500000 |
| 4 | -2 | 0 -2.000000 |
| 4.4 | 0.5 | 0 0.500000 |
| 4.8 | -2 | 0 -2.000000 |
| 5.2 | 0.5 | 0 0.500000 |
Every 4th of 60 samples, from the table stimulus.
The same rig also ran:
| Stimulus | What it is | Output range |
|---|---|---|
impulse | Impulse: one sample of 1 at k = 5, 0 elsewhere (Repeating Sequence Stair) | — |
ramp | Ramp: slope 1 from t = 0 | — |
sine | Sine Wave: amplitude 1, 2 rad/s, no phase, no bias | — |
step | Step: 0 -> 1 at t = 1 s | — |
Plotted: table — Repeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sample
Category static · sample time 0.1 · 60 steps · commit 93133d604 · produced by docsSample --out <folder> --blocks Gain_Scheduled_Lead_Lag Controller_1D Controller_Blend_1D Controller_2D Controller_3D Observer_Form_1D Self_Conditioned_1D Line_Of_Sight_Access Orbit_Propagator_Kepler Attitude_Dynamics Attitude_Profile_Nadir_Pointing Attitude_Profile_Geographic_Pointing Multitaper_PSD Cross_Power_Spectral_Density Transfer_Function_Estimate Envelope_Spectrum Compose_String Scan_String --steps 60 · data docs/generated/samples/Control_Systems__Strings__Compose_String.json · the SVG is generated from those numbers by tools/docs/plot_svg.py, so it is a run and not a drawing (R-D10).