• Skip to main content
  • Skip to header right navigation
  • Skip to site footer
CDP Studio logo

CDP Studio

The no-code and full-code software development tool for distributed control systems and HMI

  • Doc
  • Why CDP
    • Software developers
    • Automation engineers
    • Managers
  • Products
    • Automation Designer
    • HMI Designer
    • Maritime HMIs
  • Services
  • Use cases
  • Pricing
  • Try CDP

CDP Studio Documentation

  • Framework - CDP Core
  • Advanced: Translators and ValueMaps
  • 5.0.0

CDP Ports CDP Runtime License

Advanced: Translators and ValueMaps

Translators

Some IOServers need to know a method how to translate data between data stream and actual CDP channels (signal- or propertychannels). For that, a Translator element can be added to IOServer element.

Several pre-made Translators are available in CDP:

Translator nameDescription
TextTranslatorConverts channel values to/from the stream line by line as text.
CSVTranslatorConverts channel values to/from the stream in CSV format.
BinaryTranslatorConverts channel values to/from the stream in binary format.
JSONTranslatorConverts channel values to/from the stream in JSON format.
FormatTranslatorConverts channel values to the stream using Format pattern.
RegexTranslatorConverts channel values from the stream using Regex pattern.
CustomTranslatorYou can create your own translator in C++ and use this.

Note: One and only one Translator must be configured for a stream. In case none or multiple Translators configured, error message will be printed at runtime and TextTranslator is chosen as default.

ValueMaps

ValueMap elements can be used by Translators in order to send or receive textual values to/from the stream based on CDP channel values. Every ValueMap element will then be used to transform between one CDP value and corresponding stream text value. Unlimited number of ValueMap elements can be added.

The ValueMap element has the following properties:

PropertyDescription
ChannelValueCDP channel value (of any CDP channel type).
StreamValueCorresponding stream value (string).

For example you can use SignalChannel<bool> and send or receive strings OFF and ON (instead of CDP bool values 0 and 1) with two ValueMaps like this:

  • ChannelValue 0 <=> StreamValue OFF
  • ChannelValue 1 <=> StreamValue ON

Note: ValueMaps are type-sensitive - i.e defining ValueMap<int> for 0 does not apply to channels other than type <int> (for example value 0 for channel <short> is not mapped by this ValueMap).

TextTranslator

The TextTranslator is a stream translator that converts channel values to/from the stream, line by line, as printable text.

The TextTranslator has the following properties:

PropertyDescription
PrefixPrefix text to be added to output or ignored from input
SuffixSuffix text to be added to output or ignored from input

CSVTranslator

The CSVTranslator is a stream translator that converts channel values to/from the stream in CSV format.

The CSVTranslator has the following properties:

PropertyDescription
SeparatorSeparator for CSV values. Can be set to Semicolon (default), Comma or TabSpace.
DecimalDecimal separator for values. Can be set to Point (default) or Comma.
QuotationQuotation style to use. Can be set to None (no quoatation, default), MappedValues (only ValueMap replaced values are quoted) or All - all values are quoted. This option is available only for the output CSVTranslator.

BinaryTranslator

The BinaryTranslator converts channel values to/from the stream in binary format.

Every channel value is converted as they are represented in memory, corresponding to the channel type:

Channel typeNumber of bytes translated to/from stream
bool, char, unsigned char1 byte
short, unsigned short2 bytes
int, unsigned int, float4 bytes
int64_t, uint64_t, double8 bytes
stringnumber of bytes in string

JSONTranslator

The JSONTranslator is a stream Translator that can be used to easily send or receive JSON-formatted streams.

The JSONTranslator has the following properties:

PropertyDescription
TabSpaceNumber of spaces to use for indentation of JSON output. This option is available only for the output JSONTranslator.

Corresponding channel and group names will be used as JSON object names. Corresponding channel values will be used as JSON object values.

For more complex JSON objects, nested ChannelGroups can be added. Using them recursively, Channel and ChannelGroups hierarchy can be created that match any JSON object hierarchy.

To match JSON arrays, ChannelGroup with array element order number as ChannelGroup name has to be created.

For example, let's assume you have to send or receive the following JSON stream:

{ "Status": 1,
 "SensorData" : [
  { "Value": 1,
    "Battery": 100 },
  { "Value": 2,
    "Battery": 80 },
  { "Value": 4,
    "Battery": 90 }
  ]
}

For that you have to create channel-tree with Channels and ChannelGroups like in the figure below

Note: Channel in a ChannelGroup can be addressed (for CDP routing) using its parent group names. In the example above, the third sensor battery signal routing address in CDP will be

App.Component.Sensors.SensorData.2.Battery

CDP reserved names and symbols

When configuring Channel and ChannelGroup names, note that there are some reserved words (like "Name" etc) in CDP that you can not use as Channel or ChannelGroup name. If you have to use these names for JSON, place space at the end of Channel and ChannelGroup name (as spaces are trimmed out of name by JSONTranslator). For example, to send or receive JSON object named "Name" configure channel or group to be named "Name ".

Also, there are some reserved symbols (like . or /) that CDP also does not allow to use in object names. You can insert these special symbols using notation \xFF, that will be replaced by byte with hexadecimal value FF. For example, a dot can be inserted in name with secuence \x2E

FormatTranslator

The FormatTranslator can be used to convert channel values to output stream using format pattern.

The FormatTranslator has the following properties:

PropertyDescription
PatternPattern to form stream from channel value(s) in C++ library Boost Format() or C language printf() like format.

Pattern is a string consisting of ordinary characters that will be copied to output unmodified. Special character entities will be replaced with current channel values from the corresponding channel list.

Let's assume you have to send out payloads like this:

Temperature: 25.1 C;
Humidity: 55 %

Assuming you have two channels for temperature and humidity added to channel list, then you can use their values and FormatTranslator to form the output with:

Pattern="Temperature: %.1f C;\nHumidity: %02d %%".

Format patterns allow to compose very complex and specific payloads from channel values.

Below is a table with the most useful format patterns:

%dOutputs current value of next channel from channel list as number (of any type, even bool, float or double).
%N%Outputs current value of N-th channel from channel list as number (of any type).
%0NdOutputs current value of next channel from channel list as number (of any type), using at least N symbols (padding zeros if needed).
%.NdOutputs current value of next channel from channel list as number (of any type), using maximum N symbols (rounding decimal part).
%.NfOutputs current value of next float or double channel from channel list rounded, using maximum N digits after decimal mark
%%Outputs % (percentage mark)
\nOutputs newline
\xFFOutputs byte with hexadecimal value FF

For more information about possible format characters, please see Boost format syntax.

Note: Format pattern allows to use ValueMap. I.e. numeric value formatter (for example %d) can be used, but value can still be mapped to string - then mapped string is actually outputted (at the %d placeholder).

RegexTranslator

The RegexTranslator converts channel values from the stream using Regex match pattern.

The RegexTranslator has the following properties:

PropertyDescription
PatternPattern for converting channel values from input stream in POSIX Regex match pattern format.

Regex match pattern is a string that describes what will expected to be in the stream with special character entities that will indicate blocks, that will be read as values for channels in corresponding channel list.

Let's assume you have to receive and parse stream like this:

Temperature: 25.1 C;
Humidity: 55 %

Assuming you have corresponding temperature and humidity channels added for input , then you can use RegexTranslator with following Pattern to get values to these channels:

Pattern="Temperature: (\S+) C;\s+Humidity: (\S+) %"

Regex match patterns can detect and grab channel values from very complex payload structures.

Below is table with the most useful special characters in patterns:

(...)Indicates a match block. Anything that is matched inside parenthesis is used as a value for the next channel in the corresponding channel list.
.Matches any character
\.Matches . character (dot)
\sMatches any whitespace character (space, tab or newline)
\SMatches any non-whitespace character (any character except space, tab and newline)
\dMatches any digit character
+Indicates one or more occurrences of the preceding elements. For example, S+ causes to match one or more sequential non-whitespace characters.
*Indicates zero or more occurrences of the preceding elements. For example, S* causes to match any number (or zero) sequential non-whitespace characters.

You can learn more about Regex possibilities, at:

  • Boost Regex page
  • Wikipedia Regex page

Note: Regex match patterns also consider ValueMaps. I.e. after a pattern is matched, ValueMaps are also scanned to find any match. If matched StreamValue is found, corresponding ChannelValue is used instead to set the value of a channel.

CustomTranslator

If none of the existing Translators suits the need, a custom Translator can be created and used by programming it's behavior in C++ code.

To create a custom Translator:

  • Create new library in CDP Studio
  • Select Code mode
  • By right-clicking on library name, choose "Add New..." wizard.
  • Choose add "CDP Node Model" element, and follow the wizard, giving Translator a suitable name f.e MyTranslator.
  • Change model file (MyLibrary.MyTranslator.xml) to be derived from CDPCore.Translator (instead of CPNode), to look like this:
    <?xml version="1.0" encoding="utf-8"?>
    <Model Name="MyLibrary.MyTranslator&lt;T&gt;">
      <ModelTypeClass>C++</ModelTypeClass>
      <BaseModel>TranslatorBase&lt;T&gt;</BaseModel>
      <Template Name="T" Value="ostream; istream"/>
    
      <Attributes>
        <Attribute Name="Name" Type="string" Required="1" ReadOnly="1"/>
        <Attribute Name="Model" Value="MyLibrary.MyTranslator&lt;T&gt;" Type="string" Description="Implementation model used." Required="1" ReadOnly="1"/>
        <Attribute Name="Description" Type="string" Value="Simple CustomTranslator example." ReadOnly="1" Required="1"/>
      </Attributes>
    
    </Model>
  • Check library builder function .h and .cpp files to have function CreateNewCDPNode(const std::string& type) overriden to create MyTranslator instance. MyLibraryBuilder.h to be like:
    #ifndef MYLIBRARY_MYLIBRARYBUILDER_H
    #define MYLIBRARY_MYLIBRARYBUILDER_H
    
    #include <CDPSystem/Application/CDPBuilder.h>
    
    namespace MyLibrary {
    
    class MyLibraryBuilder : public CDPBuilder
    {
    public:
        MyLibraryBuilder(const char* libName,const char* timeStamp);
        CDP::StudioAPI::CDPNode* CreateNewCDPNode(const std::string& type) override;
    };
    
    }
    
    #endif

    MyLibraryBuilder.cpp to be like:

    #include "MyTranslator.h"
    #include "MyLibraryBuilder.h"
    
    using namespace MyLibrary;
    
    MyLibraryBuilder::MyLibraryBuilder(const char* libName, const char* timeStamp)
        : CDPBuilder(libName, timeStamp)
    {
    }
    
    CDP::StudioAPI::CDPNode *MyLibraryBuilder::CreateNewCDPNode(const std::string &type)
    {
        if (type == "MyLibrary.MyTranslator<ostream>")
          return new MyTranslator<ostream>;
        if (type == "MyLibrary.MyTranslator<istream>")
          return new MyTranslator<istream>;
        return CDPBuilder::CreateNewCDPNode(type);
    }
  • Change MyTranslator.h so that MyTranslator class is derived from ServerIO::Translator::TranslatorBase (instead of CDPNode), to look like this:
    #ifndef MYLIBRARY_MYTRANSLATOR_H
    #define MYLIBRARY_MYTRANSLATOR_H
    
    #include <CDPSystem/Base/CDPObject.h>
    #include <IO/ServerIO/Translator/TranslatorBase.h>
    
    namespace MyLibrary {
    
    template <typename STREAMTYPE>
    class MyTranslator : public ServerIO::Translator::TranslatorBase<STREAMTYPE>
    {
    public:
        void Translate(STREAMTYPE& stream, const ServerIO::Translator::TranslatorChannelGroup& channelTree) const override;
        std::string GetNodeTypeName() const override;
    };
    
    }
    
    #endif
  • Write member bodies for actual worker functions for MyTranslator<ostream>::Translate() and MyTranslator<ostream>::Translate()(). These functions will do the actual formatting and parsing. Below is a simple translator example that just composes and parses channel and channel group member values line-by-line.
    #include "MyTranslator.h"
    
    #include <StudioAPI/NodeStream.h>
    #include <Generic/CDPUtils.h>
    
    using namespace std;
    using namespace ServerIO::Translator;
    
    namespace MyLibrary {
    
    template <>
    void MyTranslator::Translate<ostream>(ostream& stream, const TranslatorChannelGroup& channelTree) const
    {
        for (auto channel : channelTree.ChildChannels())
        {
            if (channel->HasMappedValue())
                stream << channel->MappedValue();
            else
                stream << channel->StringValue();
            stream << endl;
        }
        for (auto group : channelTree.ChildGroups())
        {
            stream << endl;
            Translate(stream, *group);
        }
    }
    
    template <>
    void MyTranslator::Translate<istream>(istream& stream, const TranslatorChannelGroup& channelTree) const
    {
        string intoken;
        getline(stream, intoken);
        for (auto channel : channelTree.ChildChannels())
        {
            if (!channel->SetMappedValue(intoken))
                channel->SetValue(intoken);
            getline(stream, intoken);
        }
        for (auto group : channelTree.ChildGroups())
            Translate(stream, *group);
    }
    
    template <>
    string MyTranslator<ostream>::GetNodeTypeName() const
    {
        return "MyLibrary.MyTranslator<ostream>";
    }
    
    template <>
    string MyTranslator<istream>::GetNodeTypeName() const
    {
        return "MyLibrary.MyTranslator<istream>";
    }
    
    }

    Note: For simplicity, one of the member bodies (ComposePayload() or ParsePayload()) can be left empty, in cases where your translator is one-directional, i.e. compose- or parseonly.

  • In case the translator needs some configurable properties, also override member-function Configure() and set up properties in there.

Build the library. After successful build, the newly created MyTranslator becomes available for use in any IOServer element that accepts Translators (like MQTTIO Topics or ExternalControlIO Requests).

CDP Ports CDP Runtime License

The content of this document is confidential information not to be published without the consent of CDP Technologies AS.

CDP Technologies AS, www.cdpstudio.com

Get started with CDP Studio today

Let us help you take your great ideas and turn them into the products your customer will love.

Try CDP Studio for free
Why CDP Studio?

CDP Technologies AS
Hundsværgata 8,
P.O. Box 144
6001 Ålesund, Norway

Tel: +47 990 80 900
E-mail: info@cdptech.com

Company

About CDP

Contact us

Services

Partners

Blog

Developers

Get started

User manuals

Support

Document download

Release notes

My account

Follow CDP

  • LinkedIn
  • YouTube
  • GitHub

© Copyright 2025 CDP Technologies. Privacy and cookie policy.

Return to top