Skip to content

Commit

Permalink
Merge pull request #413 from mtconnect/main-dev
Browse files Browse the repository at this point in the history
  • Loading branch information
wsobel authored Feb 17, 2024
2 parents 01b97ae + 0bf7711 commit f5df367
Show file tree
Hide file tree
Showing 23 changed files with 638 additions and 67 deletions.
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# The version number.
set(AGENT_VERSION_MAJOR 2)
set(AGENT_VERSION_MINOR 2)
set(AGENT_VERSION_MINOR 3)
set(AGENT_VERSION_PATCH 0)
set(AGENT_VERSION_BUILD 17)
set(AGENT_VERSION_BUILD 1)
set(AGENT_VERSION_RC "")

# This minimum version is to support Visual Studio 2019 and C++ feature checking and FetchContent
Expand Down
188 changes: 182 additions & 6 deletions README.md
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

MTConnect C++ Agent Version 2.2
MTConnect C++ Agent Version 2.3
--------
[![Build MTConnect C++ Agent](https://github.com/mtconnect/cppagent/actions/workflows/build.yml/badge.svg)](https://github.com/mtconnect/cppagent/actions/workflows/build.yml)

Expand Down Expand Up @@ -958,6 +958,171 @@ Sinks {
* `SuppressIPAddress` - Suppress the Adapter IP Address and port when creating the Agent Device ids and names.
*Default*: false
* `AdapterIdentity` - Adapter Identity name used to prefix dataitems within the Agent device ids and names.

*Default*:
* If `SuppressIPAddress` == false:\
`AdapterIdentity` = ```_ {IP}_{PORT}```\
example:`_localhost_7878`

* If `SuppressIPAddress` == true:\
`AdapterIdentity` = ```_ sha1digest({IP}_{PORT})```\
example: `__71020ed1ed`

#### MQTT Adapter/Source

* `MqttHost` - IP Address or name of the MQTT Broker

*Default*: 127.0.0.1

* `MqttPort` - Port number of MQTT Broker

*Default*: 1883

* `topics` - list of topics to subscribe to. Note : Only raw SHDR strings supported at this time

*Required*

* `MqttClientId` - Port number of MQTT Broker

*Default*: Auto-generated

> **⚠️Note:** Mqtt Sinks and Mqtt Adapters create separate connections to their respective brokers, but currently use the same client ID by default. Because of this, when using a single broker for source and sink, best practice is to explicitly specify their respective `MqttClientId`
>

Example mqtt adapter block:
```json
mydevice {
Protocol = mqtt
MqttHost = localhost
MqttPort = 1883
MqttClientId = myUniqueID
Topics = /ingest
}
```

### MQTT JSON Ingress Protocol Version 2.0

In general the data format will be {"timestamp": "YYYY-MM-DDThh:mm:ssZ","dataItemId":"value", "dataItemId":{"key1":"value1", ..., "keyn":"valuen}}

**NOTE**: See the standard for the complete description of the fields for the data item representations below.

A simple set of events and samples will look something like this:

```json
{
"timestamp": "2023-11-06T12:12:44Z", //Time Stamp
"tempId": 22.6, //Temperature
"positionId": 1002.345, //X axis position
"executionId": "ACTIVE" //Execution state
}
```

A `CONDITION` requires the key to be the dataItemId and requires the 6 fields as shown in the example below

```json
{
"timestamp": "2023-11-06T12:12:44Z",
"dataItemId": {
"level": "fault",
"conditionId":"ac324",
"nativeSeverity": "1000",
"qualifier": "HIGH",
"nativeCode": "ABC",
"message": "something went wrong"
}
}
```
A `MESSAGE` requires the key to be the dataItemId and requires the nativeCode field as shown in the example below

```json
{
"timestamp": "2023-11-06T12:12:44Z",
"messsageId": {
"nativeCode": "ABC",
"message": "something went wrong"
}
}
```

The `TimeSeries` `REPRESENTATION` requires the key to be the dataItemId and requires 2 fields "count" and "values" and 1 to n comma delimited values.
**NOTE**: The "frequency" field is optional.

```json
{
"timestamp": "2023-11-06T12:12:44Z",
"timeSeries1": {
"count": 10,
"frequency": 100,
"values": [1,2,3,4,5,6,7,8,9,10]
}
}
```
The `DataSet` `REPRESENTATION` requires the the dataItemId as the key and the "values" field. It may also have the optional "resetTriggered" field.

```json
{
{
"timestamp": "2023-11-09T11:20:00Z",
"dataSetId": {
"key1": 123,
"key2": 456,
"key3": 789
}
}
```

Example with the optional "resetTriggered" filed:

```json
{
"timestamp": "2023-11-09T11:20:00Z",
"cncregisterset1": {
"resetTriggered": "NEW",
"value": {"r1":"v1", "r2":"v2", "r3":"v3" }
}
}
```

The `Table` `REPRESENTATION` requires the the dataItemId as the key and the "values" field. It may also have the optional "resetTriggered" field.

```json

{
"timestamp":"2023-11-06T12:12:44Z",
"tableId":{
"row1":{
"cell1":"Some Text",
"cell2":3243
},
"row2": {
"cell1":"Some Other Text",
"cell2":243
}
}
}
```

Example with the optional resetTriggered field:

```json
{
"timestamp": "2023-11-09T11:20:00Z",
"a1": {
"resetTriggered": "NEW",
"value": {
"r1": {
"k1": 123.45,
"k3": 6789
},
"r2": null
}
}
}
```



* `AdapterIdentity` - Adapter Identity name used to prefix dataitems within the Agent device ids and names.

Expand Down Expand Up @@ -1080,9 +1245,20 @@ Conditions require six (6) fields as follows:

<timestamp>|<data_item_name>|<level>|<native_code>|<native_severity>|<qualifier>|<message>

Condition id and native code are set to the same value given as <native_code>

<timestamp>|<data_item_name>|<level>|<native_code>:<condition_id>|<native_severity>|<qualifier>|<message>

Condition id is set to condition_id and native code is set to native_code

<timestamp>|<data_item_name>|<level>|<condition_id>|<native_severity>|<qualifier>|<message>

Condition id is set to condition_id and native code is not set


For a complete description of these fields, see the standard. An example line will look like this:

2014-09-29T23:59:33.460470Z|htemp|WARNING|HTEMP|1|HIGH|Oil Temperature High
2014-09-29T23:59:33.460470Z|htemp|WARNING|HTEMP-1-HIGH|HTEMP|1|HIGH|Oil Temperature High

The next special format is the Message. There is one additional field, native_code, which needs to be included:

Expand Down Expand Up @@ -1551,7 +1727,7 @@ to instruct conan to not parallelize the builds. Some of the modules that includ

### Build the agent

conan create cppagent -pr cppagent/conan/profile/gcc --build=missing
conan create cppagent -pr cppagent/conan/profiles/gcc --build=missing

## Building on Mac OS

Expand All @@ -1572,11 +1748,11 @@ Install brew and xcode command line tools

### Build the agent

conan create cppagent -pr cppagent/conan/profile/macos --build=missing
conan create cppagent -pr cppagent/conan/profiles/macos --build=missing

### Generate an xcode project for debugging

conan build . -pr conan/profile/xcode -s build_type=Debug --build=missing -o development=True
conan build . -pr conan/profiles/xcode -s build_type=Debug --build=missing -o development=True

## Building on Fedora Alpine

Expand All @@ -1596,7 +1772,7 @@ Install brew and xcode command line tools

### Build the agent

conan create cppagent -pr cppagent/conan/profile/gcc --build=missing
conan create cppagent -pr cppagent/conan/profiles/gcc --build=missing

## For some examples, see the CI/CD workflows in `.github/workflows/build.yml`

Expand Down
16 changes: 11 additions & 5 deletions src/mtconnect/agent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1301,17 +1301,20 @@ namespace mtconnect {
// Validation methods
// -----------------------------------------------

string Agent::devicesAndPath(const std::optional<string> &path, const DevicePtr device) const
string Agent::devicesAndPath(const std::optional<string> &path, const DevicePtr device, const std::optional<std::string> &deviceType) const
{
string dataPath;

if (device)
if (device || deviceType)
{
string prefix;
if (device->getName() == "Agent")
if ((device && device->getName() == "Agent") ||
(deviceType && *deviceType == "Agent"))
prefix = "//Devices/Agent";
else
else if (device)
prefix = "//Devices/Device[@uuid=\"" + *device->getUuid() + "\"]";
else if (deviceType)
prefix = "//Devices/Device";

if (path)
{
Expand All @@ -1331,7 +1334,10 @@ namespace mtconnect {
}
else
{
dataPath = path ? *path : "//Devices/Device|//Devices/Agent";
if (path)
dataPath = *path;
else
dataPath = "//Devices/Device|//Devices/Agent";
}

return dataPath;
Expand Down
14 changes: 11 additions & 3 deletions src/mtconnect/agent.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,10 @@ namespace mtconnect {
/// @return The MTConnect schema version as a string
const auto &getSchemaVersion() const { return m_schemaVersion; }

/// @brief Get the integer schema version based on configuration.
/// @returns the schema version as an integer [major * 100 + minor] as a 32bit integer.
const auto getIntSchemaVersion() const { return m_intSchemaVersion; }

/// @brief Find a device by name
/// @param[in] name The name of the device to find
/// @return A shared pointer to the device
Expand Down Expand Up @@ -421,9 +425,11 @@ namespace mtconnect {
///
/// @param[in] path Optional path to prefix
/// @param[in] device Optional device if one device is specified
/// @param[in] deviceType optional Agent or Device selector
/// @return The rewritten path properly prefixed
std::string devicesAndPath(const std::optional<std::string> &path,
const DevicePtr device) const;
const DevicePtr device,
const std::optional<std::string> &deviceType = std::nullopt) const;

/// @brief Creates unique ids for the device model and maps to the originals
///
Expand Down Expand Up @@ -575,6 +581,7 @@ namespace mtconnect {
fun(ldi);
}
}
int32_t getSchemaVersion() const override { return m_agent->getIntSchemaVersion(); }
void deliverObservation(observation::ObservationPtr obs) override
{
m_agent->receiveObservation(obs);
Expand Down Expand Up @@ -636,9 +643,10 @@ namespace mtconnect {
const PrinterMap &getPrinters() const override { return m_agent->getPrinters(); }

void getDataItemsForPath(const DevicePtr device, const std::optional<std::string> &path,
FilterSet &filter) const override
FilterSet &filter,
const std::optional<std::string> &deviceType) const override
{
std::string dataPath = m_agent->devicesAndPath(path, device);
std::string dataPath = m_agent->devicesAndPath(path, device, deviceType);
const auto &parser = m_agent->getXmlParser();
parser->getDataItems(filter, dataPath);
}
Expand Down
12 changes: 10 additions & 2 deletions src/mtconnect/observation/observation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -333,14 +333,22 @@ namespace mtconnect {
auto cond = make_shared<Condition>(name, props);
if (cond)
{
auto code = cond->m_properties.find("nativeCode");
if (code != cond->m_properties.end())
if (auto code = cond->m_properties.find("conditionId");
code != cond->m_properties.end())
{
cond->m_code = std::get<string>(code->second);
}
else if (auto code = cond->m_properties.find("nativeCode");
code != cond->m_properties.end())
{
cond->m_code = std::get<string>(code->second);
}
}
return cond;
});
factory->addRequirements(Requirements {{"type", ValueType::USTRING, true},
{"nativeCode", false},
{"conditionId", false},
{"nativeSeverity", false},
{"qualifier", ValueType::USTRING, false},
{"statistic", ValueType::USTRING, false},
Expand Down
1 change: 1 addition & 0 deletions src/mtconnect/observation/observation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ namespace mtconnect::observation {
m_level = NORMAL;
m_code.clear();
m_properties.erase("nativeCode");
m_properties.erase("conditionId");
m_properties.erase("nativeSeverity");
m_properties.erase("qualifier");
m_properties.erase("statistic");
Expand Down
3 changes: 3 additions & 0 deletions src/mtconnect/pipeline/pipeline_contract.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ namespace mtconnect {
/// @param[in] name name or id of the data item
/// @return shared pointer to the data item if found
virtual DataItemPtr findDataItem(const std::string &device, const std::string &name) = 0;
/// @brief get the current schema version as an integer
/// @returns the schema version as an integer [major * 100 + minor] as a 32bit integer.
virtual int32_t getSchemaVersion() const = 0;
/// @brief iterate through all the data items calling `fun` for each
/// @param[in] fun The function or lambda to call
virtual void eachDataItem(EachDataItem fun) = 0;
Expand Down
Loading

0 comments on commit f5df367

Please sign in to comment.