Multi-File Configurations and Merging
Real projects split configuration across files: the platform team ships defaults, the project team adds its messages, a transformer tool contributes derived values.
The definition decides what is allowed via configurationFileMode.
This chapter explains the four modes, entity merging and priorities.
The Four File Modes
| Mode | Multiple files | Entities merge | Conflict handling |
|---|---|---|---|
|
no |
- |
second file for the definition is an error |
|
yes |
no |
files are validated independently |
|
yes |
yes |
same value twice is fine, different values are an error |
|
yes |
yes |
higher |
Merging happens per CfgSpace, files in different CfgSpaces never merge.
Mode selection:
-
One owner, one file:
SingleFileSimplest to reason about. -
Several files, but each entity fully owned by one file:
MultipleFiles -
Entities assembled from several contributions (platform plus project):
MultipleFilesEntitiesMergeable -
Layered overrides (defaults, user configuration, generated values, mixins):
MultipleFilesEntitiesMergeableWithPriority
In the MultipleFiles mode the multiplicity of a top level entity counts across the files of a CfgSpace, so a 1..1 entity sits in exactly one of them.
Everything below the top level is checked per file, since each entity lives in one file.
The mergeable modes check presence on the merged result at every level. The user manual’s Working with Multiple Files shows both levels from the configuration side.
|
The |
Entity Merging
In the mergeable modes, entities with the same identity (name or id) and the same type are merged across the files of a CfgSpace.
The CanStack example splits one CAN channel between two teams:
definitionName: "CanStack",
definitionVersion: "1.0",
configurationFileMode: "MultipleFilesEntitiesMergeable",
The platform configuration file owns the channel and its base timing:
{
$definition: "CanStack@1.0",
channels: [
{
name: "Body",
baudRate: 500,
messages: [
{
name: "EcuStatus",
canId: "0x1A0",
cycleTime: 100
}
]
}
]
}
The project configuration file adds its own message to the same channel entity, matched by name: "Body":
{
$definition: "CanStack@1.0",
channels: [
{
name: "Body",
// baudRate is contributed by the platform file.
// Setting a different value here would be a merge conflict.
busOffRecovery: false,
messages: [
{
name: "DoorState",
canId: "0x2B4",
dlc: 2,
cycleTime: 50
}
]
}
]
}
The merged model contains one Body channel with baudRate, busOffRecovery, and both messages.
cfu cfg export-merged writes this merged view as a single file:
cfu cfg export-merged -o merged.json .
Or you can inspect it with:
cfu cfg inspect
Entity Fragments
A configuration file may hold the content of one nested entity instead of the structure down from the definition root.
Such a fragment declares its anchor with a top-level $path / $type pair, and both keys are rejected outside the mergeable modes:
{
$definition: "CanStack@1.0",
$path: "/CanStack/Body",
$type: "CanStack::Channel",
messages: [
{
name: "LightRequest",
canId: "0x3C8",
dlc: 1,
cycleTime: 20
}
]
}
$path is the absolute entity path, same as an absolute reference.
$type is a type path of the entity to configure.
It could be its own type or one it inherits from.
A fragment never creates its own anchor entity, so another file must declare the place of the entity.
Mixins
A mergeable mode also gives configuration authors mixins: a block of content written once in the top level $mixins list and merged into every element its selector matches.
A named mixin ($mixin) reaches the elements that ask for it via $applyMixins, leave the name out and it reaches every element of its $type.
Nothing in the definition declares or restricts mixins, so a definition in a mergeable mode has to tolerate that any of its entity and struct types may be configured class-wide rather than element by element. It is also how the definition ships its own default or preconfig values, see Default and Preconfiguration Files. The configuration side is documented in Reusing Content with Mixins.
Merge Conflicts
Without priorities, all files are equal. If two files set the same parameter of the same entity to different values, that is a merge conflict error. Setting it to the same value in both files is accepted but useless.
Multiplicity Across Merged Files
In mergeable modes, min/max multiplicity counts are checked on the merged result, not per file.
The platform file alone satisfies baudRate’s `1..1 even though the project file never mentions it.
The corresponding diagnostics say so explicitly, for example "too few elements across merged files".
Priority Merging
MultipleFilesEntitiesMergeableWithPriority adds a file level priority.
Each configuration file declares its $fileType, when two files disagree on a value, the higher priority wins silently, and only equal priorities conflict.
The Nm definition declares the mode:
definitionName: "Nm",
definitionVersion: "1.0",
configurationFileMode: "MultipleFilesEntitiesMergeableWithPriority",
The platform configuration file ships the timing at normal priority:
{
$definition: "Nm@1.0",
$fileType: "UserConfig",
nmNodes: [
{
name: "Gateway",
timeoutTime: 2000,
repeatMessageTime: 1600
},
{
name: "Diagnostic",
timeoutTime: 5000
}
]
}
The project overrides one value with a higher priority file:
{
$definition: "Nm@1.0",
$fileType: "UserConfigPriorityHigh",
nmNodes: [
{
name: "Gateway",
timeoutTime: 1500
}
]
}
The merged Gateway node has timeoutTime: 1500 (project wins) and repeatMessageTime: 1600 (only the platform file sets it).
File Types and Their Priority
From lowest to highest priority:
-
DefaultValues -
DefaultValuesProject -
GeneratedBelowUserConfig -
UserConfigPriorityLow -
UserConfig(the default when$fileTypeis omitted) -
UserConfigPriorityHigh -
GeneratedAboveUserConfig -
PreConfigurationProject -
PreConfiguration -
UserOverride -
PreConfigurationNoOverride
The names encode the intended layering: generated content can sit below or above user configuration, preconfiguration outranks users, and PreConfigurationNoOverride is absolute.
The DefaultValues* types must live in .default.def.json5 files and the PreConfiguration* types in .preconfig.def.json5 files, so their role is visible in the file name.
The user facing types use the normal .cfg.json5 suffix.
Default and Preconfiguration Files
.default.def.json(5) and .preconfig.def.json(5) files are how a definition ships configuration values of its own.
They are part of the definition and not changeable by the configuration author.
Despite the .def suffix they are written in configuration syntax, and their $fileType decides the layer, a DefaultValues* type below the user configuration and a
PreConfiguration* type above it.
Most of the time the content is an unnamed mixin, which applies values to every element of a type instead of to one the file would have to name:
{
$definition: "SensorLoop@1.0",
$fileType: "DefaultValues",
$mixins: [
{
$type: "SensorLoop::Sensor",
sampleRate: 100
}
]
}
This also ties the feature to the file mode: $mixins needs a mergeable mode, so a SingleFile definition can’t ship default or preconfig data.
A definition default on a parameter and a default file are different mechanisms, and both have their place.
The parameter default applies wherever the value is absent, shows up in hover and costs nothing.
A default file contributes a real value into the merge, which the origin export attributes to that file and a project can override with an ordinary configuration file.
Use the parameter default for a per-value fallback and a default file for a coherent block of project-level values.
Default and preconfig files never activate the definition in a workspace: per CfgSpace, their content participates in merging only when at least one .cfg.json(5) declares the
definition.