From 0e6613aa256a60571a8142e8466851a4435aecb5 Mon Sep 17 00:00:00 2001 From: Will Sobel Date: Mon, 21 Apr 2014 14:47:53 -0700 Subject: [PATCH] Added asset removal functionality. --- agent/CMakeLists.txt | 2 +- agent/adapter.cpp | 7 +- agent/agent.cpp | 129 ++++++++++++++++++++++--------- agent/agent.hpp | 6 +- agent/asset.cpp | 7 +- agent/asset.hpp | 15 ++-- agent/component.cpp | 4 +- agent/component.hpp | 2 + agent/component_event.cpp | 10 +-- agent/cutting_tool.hpp | 5 +- agent/data_item.cpp | 5 +- agent/data_item.hpp | 3 +- agent/xml_parser.cpp | 39 +++++++++- agent/xml_parser.hpp | 3 + agent/xml_printer.cpp | 6 ++ test/agent_test.cpp | 158 +++++++++++++++++++++++++++++++++++++- test/agent_test.hpp | 8 +- test/asset3.xml | 82 ++++++++++++++++++++ test/xml_parser_test.cpp | 31 ++++++++ test/xml_parser_test.hpp | 4 + test/xml_printer_test.cpp | 23 +++++- test/xml_printer_test.hpp | 2 + tools/dump_agent.rb | 2 +- 23 files changed, 486 insertions(+), 67 deletions(-) create mode 100644 test/asset3.xml diff --git a/agent/CMakeLists.txt b/agent/CMakeLists.txt index d8c50da68..0fc10b6d2 100644 --- a/agent/CMakeLists.txt +++ b/agent/CMakeLists.txt @@ -10,7 +10,7 @@ set (AGENT_VERSION_MAJOR 1) set (AGENT_VERSION_MINOR 3) set (AGENT_VERSION_PATCH 0) set (AGENT_VERSION_BUILD 0) -set (AGENT_VERSION_RC "_RC9") +set (AGENT_VERSION_RC "_RC10") if(WIN32) if(CMAKE_CL_64) diff --git a/agent/adapter.cpp b/agent/adapter.cpp index ab4fd45c2..66bdefcba 100644 --- a/agent/adapter.cpp +++ b/agent/adapter.cpp @@ -213,7 +213,12 @@ void Adapter::processData(const string& data) mAgent->updateAsset(device, assetId, list, time); return; } - + else if (key == "@REMOVE_ASSET@") + { + string assetId = value; + mAgent->removeAsset(device, assetId, time); + return; + } DataItem *dataItem; if (device != NULL) { diff --git a/agent/agent.cpp b/agent/agent.cpp index b180e8da4..4e889dcb6 100644 --- a/agent/agent.cpp +++ b/agent/agent.cpp @@ -133,7 +133,7 @@ Agent::Agent(const string& configXmlPath, int aBufferSize, int aMaxAssets, int a ss >> major >> c >> minor; if ((*device)->getAssetChanged() == NULL && (major >= 1 || (major == 1 && minor >= 2))) { - // Create availability data item and add it to the device. + // Create asset change data item and add it to the device. std::map attrs; attrs["type"] = "ASSET_CHANGED"; attrs["id"] = (*device)->getId() + "_asset_chg"; @@ -144,7 +144,21 @@ Agent::Agent(const string& configXmlPath, int aBufferSize, int aMaxAssets, int a (*device)->addDataItem(*di); (*device)->addDeviceDataItem(*di); } - } + + if ((*device)->getAssetRemoved() == NULL && (major >= 1 || (major == 1 && minor >= 3))) + { + // Create asset removed data item and add it to the device. + std::map attrs; + attrs["type"] = "ASSET_REMOVED"; + attrs["id"] = (*device)->getId() + "_asset_rem"; + attrs["category"] = "EVENT"; + + DataItem *di = new DataItem(attrs); + di->setComponent(*(*device)); + (*device)->addDataItem(*di); + (*device)->addDeviceDataItem(*di); + } +} // Reload the document for path resolution mXmlParser->loadDocument(XmlPrinter::printProbe(mInstanceId, mSlidingBufferSize, @@ -455,8 +469,7 @@ unsigned int Agent::addToBuffer(DataItem *dataItem, } bool Agent::addAsset(Device *aDevice, const string &aId, const string &aAsset, - const string &aType, - const string &aTime) + const string &aType, const string &aTime) { // Check to make sure the values are present if (aType.empty() || aAsset.empty() || aId.empty()) { @@ -470,54 +483,66 @@ bool Agent::addAsset(Device *aDevice, const string &aId, const string &aAsset, else time = aTime; + AssetPtr ptr; // Lock the asset addition to protect from multithreaded collisions. Releaes // before we add the event so we don't cause a race condition. { dlib::auto_mutex lock(*mAssetLock); - AssetPtr old = mAssetMap[aId]; - if (old.getObject() != NULL) - mAssets.remove(old); - else - mAssetCounts[aType] += 1; - - AssetPtr ptr; - if (aType == "CuttingTool") { - try { - ptr = mXmlParser->parseAsset(aId, aType, aAsset); - } - catch (runtime_error &e) { - sLogger << LERROR << "addAsset: Error parsing asset: " << aAsset << "\n" << e.what(); - return false; - } - } else { - ptr.setObject(new Asset(aId, aType, aAsset), true); - ptr->setTimestamp(time); - ptr->setDeviceUuid(aDevice->getUuid()); + try { + ptr = mXmlParser->parseAsset(aId, aType, aAsset); + } + catch (runtime_error &e) { + sLogger << LERROR << "addAsset: Error parsing asset: " << aAsset << "\n" << e.what(); + return false; + } + + AssetPtr &old = mAssetMap[aId]; + if (!ptr->isRemoved()) + { + if (old.getObject() != NULL) + mAssets.remove(&old); + else + mAssetCounts[aType] += 1; + } else if (old.getObject() == NULL) { + sLogger << LWARN << "Cannot remove non-existent asset"; + return false; } if (ptr.getObject() == NULL) { sLogger << LWARN << "Asset could not be created"; return false; } else { - if (ptr->getTimestamp().empty()) - ptr->setTimestamp(time); - if (ptr->getDeviceUuid().empty()) - ptr->setDeviceUuid(aDevice->getUuid()); + ptr->setAssetId(aId); + ptr->setTimestamp(time); + ptr->setDeviceUuid(aDevice->getUuid()); } // Check for overflow if (mAssets.size() >= mMaxAssets) { - old = mAssets.front(); + old = *mAssets.front(); mAssetCounts[old->getType()] -= 1; mAssets.pop_front(); mAssetMap.erase(old->getAssetId()); + + // Add secondary keys + AssetKeys &keys = old->getKeys(); + AssetKeys::iterator iter; + for (iter = keys.begin(); iter != keys.end(); iter++) + { + AssetIndex &index = mAssetIndices[iter->first]; + index.erase(iter->second); + } } mAssetMap[aId] = ptr; - mAssets.push_back(ptr); + if (!ptr->isRemoved()) + { + AssetPtr &newPtr = mAssetMap[aId]; + mAssets.push_back(&newPtr); + } // Add secondary keys AssetKeys &keys = ptr->getKeys(); @@ -530,7 +555,10 @@ bool Agent::addAsset(Device *aDevice, const string &aId, const string &aAsset, } // Generate an asset chnaged event. - addToBuffer(aDevice->getAssetChanged(), aType + "|" + aId, time); + if (ptr->isRemoved()) + addToBuffer(aDevice->getAssetRemoved(), aType + "|" + aId, time); + else + addToBuffer(aDevice->getAssetChanged(), aType + "|" + aId, time); return true; } @@ -552,7 +580,7 @@ bool Agent::updateAsset(Device *aDevice, const std::string &aId, AssetChangeList if (asset.getObject() == NULL) return false; - if (asset->getType() != "CuttingTool") + if (asset->getType() != "CuttingTool" && asset->getType() != "CuttingToolArchitype") return false; CuttingToolPtr tool((CuttingTool*) asset.getObject()); @@ -573,6 +601,10 @@ bool Agent::updateAsset(Device *aDevice, const std::string &aId, AssetChangeList return false; } + // Move it to the front of the queue + mAssets.remove(&asset); + mAssets.push_back(&asset); + tool->setTimestamp(aTime); tool->setDeviceUuid(aDevice->getUuid()); tool->changed(); @@ -583,6 +615,33 @@ bool Agent::updateAsset(Device *aDevice, const std::string &aId, AssetChangeList return true; } +bool Agent::removeAsset(Device *aDevice, const std::string &aId, const string &aTime) +{ + AssetPtr asset; + + string time; + if (aTime.empty()) + time = getCurrentTime(GMT_UV_SEC); + else + time = aTime; + + { + dlib::auto_mutex lock(*mAssetLock); + + asset = mAssetMap[aId]; + if (asset.getObject() == NULL) + return false; + + asset->setRemoved(true); + asset->setTimestamp(aTime); + } + + addToBuffer(aDevice->getAssetRemoved(), asset->getType() + "|" + aId, time); + + return true; +} + + /* Add values for related data items UNAVAILABLE */ void Agent::disconnected(Adapter *anAdapter, std::vector aDevices) { @@ -901,13 +960,13 @@ std::string Agent::handleAssets(std::ostream& aOut, // Return all asssets, first check if there is a type attribute string type = aQueries["type"]; + bool removed = (aQueries.count("removed") > 0 && aQueries["removed"] == "true"); - list::iterator iter; + list::iterator iter; for (iter = mAssets.begin(); iter != mAssets.end(); ++iter) { - if (type.empty() || type == (*iter)->getType()) - { - assets.push_back(*iter); + if ((type.empty() || type == (**iter)->getType()) && (removed || !(**iter)->isRemoved())) { + assets.push_back(**iter); } } } diff --git a/agent/agent.hpp b/agent/agent.hpp index 2ece6ba52..3a93df34c 100644 --- a/agent/agent.hpp +++ b/agent/agent.hpp @@ -126,7 +126,7 @@ class Agent : public server_http std::string time = "" ); - // Add an asset to the agent + // Asset management bool addAsset(Device *aDevice, const std::string &aId, const std::string &aAsset, const std::string &aType, const std::string &aTime = ""); @@ -134,6 +134,8 @@ class Agent : public server_http bool updateAsset(Device *aDevice, const std::string &aId, AssetChangeList &aList, const std::string &aTime); + bool removeAsset(Device *aDevice, const std::string &aId, const std::string &aTime); + /* Message when adapter has connected and disconnected */ void disconnected(Adapter *anAdapter, std::vector aDevices); void connected(Adapter *anAdapter, std::vector aDevices); @@ -290,7 +292,7 @@ class Agent : public server_http unsigned int mSlidingBufferSize; /* Asset storage, circ buffer stores ids */ - std::list mAssets; + std::list mAssets; AssetIndex mAssetMap; // Natural key indices for assets diff --git a/agent/asset.cpp b/agent/asset.cpp index 6155415ca..22efc0751 100644 --- a/agent/asset.cpp +++ b/agent/asset.cpp @@ -19,11 +19,10 @@ using namespace std; -Asset::Asset(const std::string &aAssetId, const std::string &aType, const std::string &aContent) +Asset::Asset(const std::string &aAssetId, const std::string &aType, const std::string &aContent, + const bool aRemoved) + : mAssetId(aAssetId), mContent(aContent), mType(aType), mRemoved(aRemoved) { - mAssetId = aAssetId; - mContent = aContent; - mType = aType; } Asset::~Asset() diff --git a/agent/asset.hpp b/agent/asset.hpp index b6db6e00b..c8753c4fc 100644 --- a/agent/asset.hpp +++ b/agent/asset.hpp @@ -35,6 +35,7 @@ class Asset : public RefCounted std::string mType; std::string mDeviceUuid; std::string mTimestamp; + bool mRemoved; AssetKeys mKeys; public: @@ -43,16 +44,19 @@ class Asset : public RefCounted mAssetId = aOther.mAssetId; mContent = aOther.mContent; mType = aOther.mType; + mRemoved = aOther.mRemoved; } - Asset(const std::string &aAssetId, const std::string &aType, const std::string &aContent); + Asset(const std::string &aAssetId, const std::string &aType, const std::string &aContent, + const bool aRemoved = false); virtual ~Asset(); - std::string &getAssetId() { return mAssetId; } + const std::string &getAssetId() const { return mAssetId; } virtual std::string &getContent() { return mContent; } - std::string &getType() { return mType; } + const std::string &getType() const { return mType; } AssetKeys &getKeys() { return mKeys; } - std::string &getDeviceUuid() { return mDeviceUuid; } - std::string &getTimestamp() { return mTimestamp; } + const std::string &getDeviceUuid() const { return mDeviceUuid; } + const std::string &getTimestamp() const { return mTimestamp; } + bool isRemoved() const { return mRemoved; } bool operator==(const Asset &aOther) { return mAssetId == aOther.mAssetId; @@ -61,6 +65,7 @@ class Asset : public RefCounted void setAssetId(const std::string &aId) { mAssetId = aId; } void setDeviceUuid(const std::string &aId) { mDeviceUuid = aId; } void setTimestamp(const std::string &aTs) { mTimestamp = aTs; } + void setRemoved(bool aRemoved) { mRemoved = aRemoved; } }; typedef std::map AssetIndex; diff --git a/agent/component.cpp b/agent/component.cpp index e790acc46..8697ebb57 100644 --- a/agent/component.cpp +++ b/agent/component.cpp @@ -129,7 +129,9 @@ void Component::addDataItem(DataItem& dataItem) mAvailability = &dataItem; else if (dataItem.getType() == "ASSET_CHANGED") mAssetChanged = &dataItem; - + else if (dataItem.getType() == "ASSET_REMOVED") + mAssetRemoved = &dataItem; + mDataItems.push_back(&dataItem); } diff --git a/agent/component.hpp b/agent/component.hpp index b75ead11e..99eec2413 100644 --- a/agent/component.hpp +++ b/agent/component.hpp @@ -98,6 +98,7 @@ class Component // Cached data items DataItem *getAvailability() const { return mAvailability; } DataItem *getAssetChanged() const { return mAssetChanged; } + DataItem *getAssetRemoved() const { return mAssetRemoved; } /* Add/get description specifications using an attribute map */ @@ -165,6 +166,7 @@ class Component Device *mDevice; DataItem *mAvailability; DataItem *mAssetChanged; + DataItem *mAssetRemoved; /* Each component keeps track of it's children in a std::list */ std::list mChildren; diff --git a/agent/component_event.cpp b/agent/component_event.cpp index d7b7dd9fa..a41437a70 100644 --- a/agent/component_event.cpp +++ b/agent/component_event.cpp @@ -177,13 +177,9 @@ AttributeList *ComponentEvent::getAttributes() getline(toParse, token, '|'); mAttributes.push_back(AttributeItem("state", token)); } - else if (mDataItem->isAssetChanged()) + else if (mDataItem->isAssetChanged() || mDataItem->isAssetRemoved()) { - istringstream toParse(mRest); - string token; - - getline(toParse, token, '|'); - mAttributes.push_back(AttributeItem("assetType", token)); + mAttributes.push_back(AttributeItem("assetType", mRest)); } mHasAttributes = true; } @@ -212,7 +208,7 @@ void ComponentEvent::convertValue(const string& value) mValue = value; } else if (mIsTimeSeries || mDataItem->isCondition() || mDataItem->isAlarm() || - mDataItem->isMessage() || mDataItem->isAssetChanged()) + mDataItem->isMessage() || mDataItem->isAssetChanged() || mDataItem->isAssetRemoved()) { string::size_type lastPipe = value.rfind('|'); diff --git a/agent/cutting_tool.hpp b/agent/cutting_tool.hpp index 7bf1154b0..a65c26a67 100644 --- a/agent/cutting_tool.hpp +++ b/agent/cutting_tool.hpp @@ -60,8 +60,9 @@ class CuttingItem : public RefCounted { class CuttingTool : public Asset { public: - CuttingTool(const std::string &aAssetId, const std::string &aType, const std::string &aContent) - : Asset(aAssetId, aType, aContent) {} + CuttingTool(const std::string &aAssetId, const std::string &aType, const std::string &aContent, + bool aRemoved = false) + : Asset(aAssetId, aType, aContent, aRemoved) {} ~CuttingTool(); void addIdentity(const std::string &aKey, const std::string &aValue); diff --git a/agent/data_item.cpp b/agent/data_item.cpp index 0c988afa4..7c9553a21 100644 --- a/agent/data_item.cpp +++ b/agent/data_item.cpp @@ -68,13 +68,16 @@ const string DataItem::SSimpleUnits[NumSimpleUnits] = DataItem::DataItem(std::map attributes) : mRepresentation(VALUE), mHasNativeScale(false), mHasSignificantDigits(false), mHasConstraints(false), mFilterValue(0.0), mFilterType(FILTER_NONE), mLastSampleValue(NAN), mDataSource(NULL), - mConversionDetermined(false), mConversionRequired(false), mHasFactor(false) { + mConversionDetermined(false), mConversionRequired(false), mHasFactor(false) +{ mId = attributes["id"]; mName = attributes["name"]; mType = attributes["type"]; mIsAlarm = (mType == "ALARM"); mIsMessage = (mType == "MESSAGE"); mIsAssetChanged = (mType == "ASSET_CHANGED"); + mIsAssetRemoved = (mType == "ASSET_REMOVED"); + mCamelType = getCamelType(mType, mPrefix); if (attributes["representation"] == "TIME_SERIES") diff --git a/agent/data_item.hpp b/agent/data_item.hpp index b553fcc45..cb8a816cc 100644 --- a/agent/data_item.hpp +++ b/agent/data_item.hpp @@ -147,6 +147,7 @@ class DataItem : public ChangeSignaler bool isAlarm() const { return mIsAlarm; } bool isMessage() const { return mIsMessage; } bool isAssetChanged() const { return mIsAssetChanged; } + bool isAssetRemoved() const { return mIsAssetRemoved; } bool isTimeSeries() const { return mRepresentation == TIME_SERIES; } bool isDiscrete() const { return mRepresentation == DISCRETE; } @@ -259,7 +260,7 @@ class DataItem : public ChangeSignaler float mNativeScale; bool mHasNativeScale; bool mThreeD; - bool mIsMessage, mIsAlarm, mIsAssetChanged; + bool mIsMessage, mIsAlarm, mIsAssetChanged, mIsAssetRemoved; /* Sig figs of data item */ unsigned int mSignificantDigits; diff --git a/agent/xml_parser.cpp b/agent/xml_parser.cpp index 4d13cdf25..b9bdfe944 100644 --- a/agent/xml_parser.cpp +++ b/agent/xml_parser.cpp @@ -571,7 +571,8 @@ AssetPtr XmlParser::parseAsset(const std::string &aAssetId, const std::string &a // all others add as plain text. xmlNodePtr node = NULL; assetNodes = xmlXPathEval(BAD_CAST path.c_str(), xpathCtx); - if (assetNodes == NULL || assetNodes->nodesetval == NULL || assetNodes->nodesetval->nodeNr == 0) + if (assetNodes == NULL || assetNodes->nodesetval == NULL || + assetNodes->nodesetval->nodeNr == 0) { // See if this is a fragment... the root node will be check when it is // parsed... @@ -584,7 +585,7 @@ AssetPtr XmlParser::parseAsset(const std::string &aAssetId, const std::string &a node = nodeset->nodeTab[0]; } - asset = handleCuttingTool(node); + asset = handleAsset(node, aAssetId, aType, aContent); // Cleanup objects... xmlXPathFreeObject(assetNodes); @@ -716,6 +717,38 @@ void XmlParser::parseCuttingToolLife(CuttingToolPtr aTool, xmlNodePtr aNode) } } +AssetPtr XmlParser::handleAsset(xmlNodePtr anAsset, const std::string &aAssetId, + const std::string &aType, const std::string &aContent) +{ + AssetPtr asset; + + // We only handle cuttng tools for now... + if (xmlStrcmp(anAsset->name, BAD_CAST "CuttingTool") == 0 || + xmlStrcmp(anAsset->name, BAD_CAST "CuttingToolArchetype") == 0) + { + asset = handleCuttingTool(anAsset); + } else { + asset.setObject(new Asset(aAssetId, (const char*) anAsset->name, aContent), true); + + for (xmlAttrPtr attr = anAsset->properties; attr != NULL; attr = attr->next) + { + if (attr->type == XML_ATTRIBUTE_NODE) { + if (xmlStrcmp(attr->name, BAD_CAST "assetId") == 0) { + asset->setAssetId((string) (const char*) attr->children->content); + } else if (xmlStrcmp(attr->name, BAD_CAST "timestamp") == 0) { + asset->setTimestamp((const char*) attr->children->content); + } else if (xmlStrcmp(attr->name, BAD_CAST "removed") == 0) { + asset->setRemoved(xmlStrncmp(attr->children->content, BAD_CAST "true", 4) == 0); + } else if (xmlStrcmp(attr->name, BAD_CAST "deviceUuid") == 0) { + asset->setDeviceUuid((const char*) attr->children->content); + } + } + } + } + + return asset; +} + CuttingToolPtr XmlParser::handleCuttingTool(xmlNodePtr anAsset) { @@ -735,6 +768,8 @@ CuttingToolPtr XmlParser::handleCuttingTool(xmlNodePtr anAsset) tool->setAssetId((string) (const char*) attr->children->content); } else if (xmlStrcmp(attr->name, BAD_CAST "timestamp") == 0) { tool->setTimestamp((const char*) attr->children->content); + } else if (xmlStrcmp(attr->name, BAD_CAST "removed") == 0) { + tool->setRemoved(xmlStrncmp(attr->children->content, BAD_CAST "true", 4) == 0); } else { tool->addIdentity((const char*) attr->name, (const char*) attr->children->content); } diff --git a/agent/xml_parser.hpp b/agent/xml_parser.hpp index 0303e622b..a17935521 100644 --- a/agent/xml_parser.hpp +++ b/agent/xml_parser.hpp @@ -100,6 +100,9 @@ class XmlParser Component *parent = NULL, Device *device = NULL); + // Asset Parser + AssetPtr handleAsset(xmlNodePtr anAsset, const std::string &aAssetId, + const std::string &aType, const std::string &aContent); // Cutting Tool Parser CuttingToolPtr handleCuttingTool(xmlNodePtr anAsset); diff --git a/agent/xml_printer.cpp b/agent/xml_printer.cpp index 622f6f76e..7771334c5 100644 --- a/agent/xml_printer.cpp +++ b/agent/xml_printer.cpp @@ -1085,6 +1085,12 @@ string XmlPrinter::printCuttingTool(CuttingToolPtr aTool) THROW_IF_XML2_ERROR(xmlTextWriterWriteAttribute(writer, BAD_CAST "assetId", BAD_CAST aTool->getAssetId().c_str())); + if (aTool->isRemoved()) + { + THROW_IF_XML2_ERROR(xmlTextWriterWriteAttribute(writer, + BAD_CAST "removed", + BAD_CAST "true")); + } set remaining; std::map::const_iterator viter; diff --git a/test/agent_test.cpp b/test/agent_test.cpp index 3ee881919..8068dd5f1 100644 --- a/test/agent_test.cpp +++ b/test/agent_test.cpp @@ -294,7 +294,7 @@ void AgentTest::testAddToBuffer() { path = "/sample"; - PARSE_XML_RESPONSE_QUERY_KV("from", "31"); + PARSE_XML_RESPONSE_QUERY_KV("from", "32"); CPPUNITTEST_ASSERT_XML_PATH_EQUAL(doc, "//m:Streams", 0); } @@ -1203,6 +1203,162 @@ void AgentTest::testAssetProbe() } } +void AgentTest::testAssetRemoval() +{ + a->enablePut(); + path = "/asset/1"; + string body = "TEST 1"; + key_value_map queries; + + queries["device"] = "LinuxCNC"; + queries["type"] = "Part"; + + CPPUNIT_ASSERT_EQUAL((unsigned int) 4, a->getMaxAssets()); + CPPUNIT_ASSERT_EQUAL((unsigned int) 0, a->getAssetCount()); + + { + PARSE_XML_RESPONSE_PUT(body, queries); + CPPUNIT_ASSERT_EQUAL((unsigned int) 1, a->getAssetCount()); + CPPUNIT_ASSERT_EQUAL(1, a->getAssetCount("Part")); + } + + { + PARSE_XML_RESPONSE; + CPPUNITTEST_ASSERT_XML_PATH_EQUAL(doc, "//m:Header@assetCount", "1"); + CPPUNITTEST_ASSERT_XML_PATH_EQUAL(doc, "//m:Part", "TEST 1"); + } + + // Make sure replace works properly + { + PARSE_XML_RESPONSE_PUT(body, queries); + CPPUNIT_ASSERT_EQUAL((unsigned int) 1, a->getAssetCount()); + CPPUNIT_ASSERT_EQUAL(1, a->getAssetCount("Part")); + } + + path = "/asset/2"; + body = "TEST 2"; + + { + PARSE_XML_RESPONSE_PUT(body, queries); + CPPUNIT_ASSERT_EQUAL((unsigned int) 2, a->getAssetCount()); + CPPUNIT_ASSERT_EQUAL(2, a->getAssetCount("Part")); + } + + { + PARSE_XML_RESPONSE; + CPPUNITTEST_ASSERT_XML_PATH_EQUAL(doc, "//m:Header@assetCount", "2"); + CPPUNITTEST_ASSERT_XML_PATH_EQUAL(doc, "//m:Part", "TEST 2"); + } + + path = "/asset/3"; + body = "TEST 3"; + + { + PARSE_XML_RESPONSE_PUT(body, queries); + CPPUNIT_ASSERT_EQUAL((unsigned int) 3, a->getAssetCount()); + CPPUNIT_ASSERT_EQUAL(3, a->getAssetCount("Part")); + } + + { + PARSE_XML_RESPONSE; + CPPUNITTEST_ASSERT_XML_PATH_EQUAL(doc, "//m:Header@assetCount", "3"); + CPPUNITTEST_ASSERT_XML_PATH_EQUAL(doc, "//m:Part", "TEST 3"); + } + + + path = "/asset/2"; + body = "TEST 2"; + + { + PARSE_XML_RESPONSE_PUT(body, queries); + CPPUNIT_ASSERT_EQUAL((unsigned int) 3, a->getAssetCount()); + CPPUNIT_ASSERT_EQUAL(3, a->getAssetCount("Part")); + } + + path = "/current"; + { + PARSE_XML_RESPONSE; + CPPUNITTEST_ASSERT_XML_PATH_EQUAL(doc, "//m:AssetRemoved", "2"); + CPPUNITTEST_ASSERT_XML_PATH_EQUAL(doc, "//m:AssetRemoved@assetType", "Part"); + } + + path = "/assets"; + { + PARSE_XML_RESPONSE; + CPPUNITTEST_ASSERT_XML_PATH_COUNT(doc, "//m:Assets/*", 2); + CPPUNITTEST_ASSERT_XML_PATH_EQUAL(doc, "//m:Header@assetCount", "3"); + CPPUNITTEST_ASSERT_XML_PATH_EQUAL(doc, "//m:Assets/*[1]", "TEST 1"); + CPPUNITTEST_ASSERT_XML_PATH_EQUAL(doc, "//m:Assets/*[2]", "TEST 3"); + } + + queries["removed"] = "true"; + { + PARSE_XML_RESPONSE; + CPPUNITTEST_ASSERT_XML_PATH_COUNT(doc, "//m:Assets/*", 3); + CPPUNITTEST_ASSERT_XML_PATH_EQUAL(doc, "//m:Header@assetCount", "3"); + CPPUNITTEST_ASSERT_XML_PATH_EQUAL(doc, "//m:Assets/*[1]", "TEST 1"); + CPPUNITTEST_ASSERT_XML_PATH_EQUAL(doc, "//m:Assets/*[2]", "TEST 2"); + CPPUNITTEST_ASSERT_XML_PATH_EQUAL(doc, "//m:Assets/*[2]@removed", "true"); + CPPUNITTEST_ASSERT_XML_PATH_EQUAL(doc, "//m:Assets/*[3]", "TEST 3"); + } +} + +void AgentTest::testAssetRemovalByAdapter() +{ + + testAddAdapter(); + + CPPUNIT_ASSERT_EQUAL((unsigned int) 4, a->getMaxAssets()); + + adapter->processData("TIME|@ASSET@|111|Part|TEST 1"); + CPPUNIT_ASSERT_EQUAL((unsigned int) 1, a->getAssetCount()); + + + adapter->processData("TIME|@ASSET@|112|Part|TEST 2"); + CPPUNIT_ASSERT_EQUAL((unsigned int) 2, a->getAssetCount()); + + adapter->processData("TIME|@ASSET@|113|Part|TEST 3"); + CPPUNIT_ASSERT_EQUAL((unsigned int) 3, a->getAssetCount()); + + path = "/current"; + { + PARSE_XML_RESPONSE; + CPPUNITTEST_ASSERT_XML_PATH_EQUAL(doc, "//m:AssetChanged", "113"); + CPPUNITTEST_ASSERT_XML_PATH_EQUAL(doc, "//m:AssetChanged@assetType", "Part"); + } + + adapter->processData("TIME|@REMOVE_ASSET@|112"); + CPPUNIT_ASSERT_EQUAL((unsigned int) 3, a->getAssetCount()); + + path = "/current"; + { + PARSE_XML_RESPONSE; + CPPUNITTEST_ASSERT_XML_PATH_EQUAL(doc, "//m:AssetRemoved", "112"); + CPPUNITTEST_ASSERT_XML_PATH_EQUAL(doc, "//m:AssetRemoved@assetType", "Part"); + } + + path = "/assets"; + { + PARSE_XML_RESPONSE; + CPPUNITTEST_ASSERT_XML_PATH_COUNT(doc, "//m:Assets/*", 2); + CPPUNITTEST_ASSERT_XML_PATH_EQUAL(doc, "//m:Header@assetCount", "3"); + CPPUNITTEST_ASSERT_XML_PATH_EQUAL(doc, "//m:Assets/*[1]", "TEST 1"); + CPPUNITTEST_ASSERT_XML_PATH_EQUAL(doc, "//m:Assets/*[2]", "TEST 3"); + } + + // TODO: When asset is removed and the content is literal, it will + // not regenerate the attributes for the asset. + queries["removed"] = "true"; + { + PARSE_XML_RESPONSE; + CPPUNITTEST_ASSERT_XML_PATH_COUNT(doc, "//m:Assets/*", 3); + CPPUNITTEST_ASSERT_XML_PATH_EQUAL(doc, "//m:Header@assetCount", "3"); + CPPUNITTEST_ASSERT_XML_PATH_EQUAL(doc, "//m:Assets/*[1]", "TEST 1"); + CPPUNITTEST_ASSERT_XML_PATH_EQUAL(doc, "//m:Assets/*[2]", "TEST 2"); + CPPUNITTEST_ASSERT_XML_PATH_EQUAL(doc, "//m:Assets/*[3]", "TEST 3"); + } +} + void AgentTest::testAssetStorageWithoutType() { a->enablePut(); diff --git a/test/agent_test.hpp b/test/agent_test.hpp index c9f44643e..7fcc02816 100644 --- a/test/agent_test.hpp +++ b/test/agent_test.hpp @@ -106,6 +106,8 @@ class AgentTest : public CppUnit::TestFixture CPPUNIT_TEST(testReferences); CPPUNIT_TEST(testDiscrete); CPPUNIT_TEST(testConditionSequence); + CPPUNIT_TEST(testAssetRemoval); + CPPUNIT_TEST(testAssetRemovalByAdapter); CPPUNIT_TEST_SUITE_END(); typedef map::kernel_1a_c map_type; @@ -184,7 +186,9 @@ class AgentTest : public CppUnit::TestFixture void testMultiLineAsset(); void testAssetProbe(); void testAssetStorageWithoutType(); - void testStreamDataObserver(); + + void testAssetRemoval(); + void testAssetRemovalByAdapter(); // Test put for data items void testPut(); @@ -192,6 +196,8 @@ class AgentTest : public CppUnit::TestFixture void testPutBlockingFrom(); // Streaming tests + void testStreamDataObserver(); + static void killThread(void *aArg); static void addThread(void *aArg); static void streamThread(void *aArg); diff --git a/test/asset3.xml b/test/asset3.xml new file mode 100644 index 000000000..22e3fdceb --- /dev/null +++ b/test/asset3.xml @@ -0,0 +1,82 @@ + + +
+ + + Cutting tool ... + ISO 13399... + + NEW + 200 + 10000 + 222 + CV50 + + 73.25 + 222.25 + 82.55 + 76.2 + 120.65 + 60.95 + 98.425 + + + + FLANGE: 1-4, ROW: 1 + + 12.7 + 2.56 + 12.7 + 0.8 + + + + FLANGE: 1-4, ROW: 2 + + 12.7 + 2.56 + 12.7 + 0.8 + + + + FLANGE: 1-4, ROW: 3 + + 12.7 + 2.56 + 12.7 + 0.8 + + + + FLANGE: 1-4, ROW: 4 + + 12.7 + 2.56 + 12.7 + 0.8 + + + + FLANGE: 1-4, ROW: 5 + + 12.7 + 2.56 + 12.7 + 0.8 + + + + FLANGE: 1-4, ROW: 6 + + 12.7 + 2.56 + 12.7 + 0.8 + + + + + + + diff --git a/test/xml_parser_test.cpp b/test/xml_parser_test.cpp index 174f0976a..15530347c 100644 --- a/test/xml_parser_test.cpp +++ b/test/xml_parser_test.cpp @@ -264,6 +264,7 @@ void XmlParserTest::testParseAsset() CPPUNIT_ASSERT_EQUAL((string) "1", tool->mIdentity["serialNumber"]); CPPUNIT_ASSERT_EQUAL((string) "KMT,Parlec", tool->mIdentity["manufacturers"]); CPPUNIT_ASSERT_EQUAL((string) "2011-05-11T13:55:22", tool->getTimestamp()); + CPPUNIT_ASSERT_EQUAL(false, tool->isRemoved()); // Top Level CPPUNIT_ASSERT_EQUAL((string) "ISO 13399...", tool->mValues["CuttingToolDefinition"]->mValue); @@ -297,6 +298,36 @@ void XmlParserTest::testParseAsset() CPPUNIT_ASSERT_EQUAL((unsigned int) 1, item->mMeasurements["CuttingEdgeLength"]->refCount()); } +void XmlParserTest::testParseOtherAsset() +{ + string document = "Data"; + AssetPtr asset = a->parseAsset("XXX", "Workpiece", document); + + CPPUNIT_ASSERT(asset.getObject() != NULL); + CPPUNIT_ASSERT_EQUAL((string) "XXX123", asset->getAssetId()); + CPPUNIT_ASSERT_EQUAL((string) "2014-04-14T01:22:33.123", asset->getTimestamp()); + CPPUNIT_ASSERT_EQUAL((string) "XXX", asset->getDeviceUuid()); + CPPUNIT_ASSERT_EQUAL((string) document, asset->getContent()); + CPPUNIT_ASSERT_EQUAL(false, asset->isRemoved()); + + document = "Data"; + asset = a->parseAsset("XXX", "Workpiece", document); + + CPPUNIT_ASSERT(asset.getObject() != NULL); + CPPUNIT_ASSERT_EQUAL(true, asset->isRemoved()); +} + +void XmlParserTest::testParseRemovedAsset() +{ + string document = getFile("asset3.xml"); + AssetPtr asset = a->parseAsset("XXX", "CuttingTool", document); + CuttingToolPtr tool = (CuttingTool*) asset.getObject(); + + CPPUNIT_ASSERT_EQUAL(true, tool->isRemoved()); +} + void XmlParserTest::testUpdateAsset() { string document = getFile("asset1.xml"); diff --git a/test/xml_parser_test.hpp b/test/xml_parser_test.hpp index 1c25ba6d1..74de49153 100644 --- a/test/xml_parser_test.hpp +++ b/test/xml_parser_test.hpp @@ -60,6 +60,8 @@ class XmlParserTest : public CppUnit::TestFixture CPPUNIT_TEST(testReferences); CPPUNIT_TEST(testExtendedAsset); CPPUNIT_TEST(testExtendedAssetFragment); + CPPUNIT_TEST(testParseOtherAsset); + CPPUNIT_TEST(testParseRemovedAsset); CPPUNIT_TEST_SUITE_END(); protected: @@ -77,6 +79,8 @@ class XmlParserTest : public CppUnit::TestFixture void testConfiguration(); void testParseAsset(); void testUpdateAsset(); + void testParseOtherAsset(); + void testParseRemovedAsset(); void testNoNamespace(); void testFilteredDataItem(); void testReferences(); diff --git a/test/xml_printer_test.cpp b/test/xml_printer_test.cpp index 7c471de09..3b0b30d96 100644 --- a/test/xml_printer_test.cpp +++ b/test/xml_printer_test.cpp @@ -667,7 +667,6 @@ void XmlPrinterTest::testAssetsStyle() } - // Helper methods @@ -722,9 +721,29 @@ void XmlPrinterTest::testPrintCuttingTool() { PARSE_XML(XmlPrinter::printAssets(123, 4, 2, assets)); CPPUNITTEST_ASSERT_XML_PATH_EQUAL(doc, "//m:Assets//m:CuttingTool@toolId", "KSSP300R4SD43L240"); - } + CPPUNITTEST_ASSERT_XML_PATH_EQUAL(doc, "//m:Assets//m:CuttingTool@removed", NULL); + } } +void XmlPrinterTest::testPrintRemovedCuttingTool() +{ + vector assets; + + string document = getFile("asset1.xml"); + AssetPtr asset = config->parseAsset("KSSP300R4SD43L240.1", "CuttingTool", document); + asset->setRemoved(true); + CuttingToolPtr tool = (CuttingTool*) asset.getObject(); + + + assets.push_back(asset); + + { + PARSE_XML(XmlPrinter::printAssets(123, 4, 2, assets)); + CPPUNITTEST_ASSERT_XML_PATH_EQUAL(doc, "//m:Assets//m:CuttingTool@removed", "true"); + } +} + + // CuttingTool tests void XmlPrinterTest::testPrintExtendedCuttingTool() { diff --git a/test/xml_printer_test.hpp b/test/xml_printer_test.hpp index 949dcbe80..29642001e 100644 --- a/test/xml_printer_test.hpp +++ b/test/xml_printer_test.hpp @@ -80,6 +80,7 @@ class XmlPrinterTest : public CppUnit::TestFixture CPPUNIT_TEST(testDevicesStyle); CPPUNIT_TEST(testErrorStyle); CPPUNIT_TEST(testAssetsStyle); + CPPUNIT_TEST(testPrintRemovedCuttingTool); CPPUNIT_TEST_SUITE_END(); protected: @@ -118,6 +119,7 @@ class XmlPrinterTest : public CppUnit::TestFixture void testPrintAssetProbe(); void testPrintCuttingTool(); void testPrintExtendedCuttingTool(); + void testPrintRemovedCuttingTool(); // Schema tests void testChangeVersion(); diff --git a/tools/dump_agent.rb b/tools/dump_agent.rb index 0c0b79e34..239576da2 100644 --- a/tools/dump_agent.rb +++ b/tools/dump_agent.rb @@ -36,7 +36,7 @@ def dump(out, xml) end out.puts event.text.to_s - # puts "#{event.name} - #{name} - #{event.text}" + puts "#{event.name} - #{name} - #{event.text}" end print '.'; STDOUT.flush nxt