/* dmp/device.cpp Copyright (c) 2021 Kevin Matz (kevin.matz@gmail.com) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "device.h" namespace ACN::DMP { /** * @brief Device::Device * @param uuid * @param fctn */ Device::Device(UUID::uuid uuid, std::string fctn) : DMP::Component(uuid, fctn) { } /** * @brief Device::~Device */ Device::~Device() { } /** * @brief Device::rxDmpGetProperty * @param dmp */ void Device::rxDmpGetProperty(PDU::Message dmp) { auto addr_type = std::static_pointer_cast(dmp->header()); auto failed = std::make_shared(*addr_type); auto gotten = std::make_shared(*addr_type); auto vectorfy = [](uint32_t value, element_length width) -> std::vector { auto ret = std::vector(); switch (width) { case ONE: ret.push_back(value); break; case TWO: { ret.push_back(value << 8); ret.push_back(value); break; } case FOUR: { ret.push_back(value << 24); ret.push_back(value << 16); ret.push_back(value << 8); ret.push_back(value); break; } default: break; } return ret; }; auto ranges = std::static_pointer_cast(dmp->data()); for (const auto & range : ranges->addresses) { failure_reason success; auto result = getProperty(range, &success); if (success == SUCCESS) gotten->properties.push_back(address_data_pair(range, vectorfy(result, range.elementLength()))); else failed->properties.push_back(address_data_pair(range, std::vector(success))); } auto reply = std::make_shared(); if (!gotten->properties.empty()) { reply->setVector(GET_PROPERTY_REPLY); reply->setHeader(dmp->header()); reply->setData(gotten); sendMessage(reply); } if (!failed->properties.empty()) { reply->setVector(GET_PROPERTY_FAIL); reply->setData(failed); sendMessage(reply); } } /** * @brief Device::getProperty * @param range * @param failure * @return */ uint32_t Device::getProperty(const Range range, failure_reason *failure) { *failure = SUCCESS; switch (range.type()) { case SINGLE: case RANGE: { auto address = range.address; if (range.isRelative()) address += last_address_used; last_address_used = address; uint32_t ret; try { ret = properties_.at(address); } catch (std::out_of_range const&) { *failure = NOT_PROPERTY; return 0; } return ret; } default: *failure = NONSPECIFIC; return 0; } return 0; } /** * @brief Device::rxDmpSetProperty * @param dmp */ void Device::rxDmpSetProperty(PDU::Message dmp) { /// \cite DMP 7.2 Set Property Fail Response to Set Property /// Set Property Fail shall be sent in response to a Set Property that was not completely /// successful. The message provides the property address or addresses at which the Set /// Property failed and indicates the failure reason for each property. auto addr_type = std::static_pointer_cast(dmp->header()); auto failed = std::make_shared(*addr_type); auto data = std::static_pointer_cast(dmp->data()); for(const auto & property : data->properties) { failure_reason success; setProperty(property, &success); if (success != SUCCESS) failed->properties.push_back(address_data_pair(property.first, std::vector(success))); } if (failed->properties.empty()) return; auto reply = std::make_shared(); reply->setVector(SET_PROPERTY_FAIL); reply->setHeader(dmp->header()); reply->setData(failed); sendMessage(reply); } /** * @brief Device::setProperty * @param addr_data * @param failure */ void Device::setProperty(const address_data_pair addr_data, failure_reason *failure) { *failure = SUCCESS; const auto & [range, data] = addr_data; std::vector addresses; switch (range.type()) { case SINGLE: { auto address = range.isAbsolute() ? range.address : range.address + last_address_used; last_address_used = address; addresses.push_back(address); break; } case RANGE: case ARRAY: case SERIES: { for (uint32_t idx = 0; idx < range.count; idx++) { auto address = range.address + (range.count * range.incriment); address = range.isAbsolute() ? address : address + last_address_used; last_address_used = address; addresses.push_back(address); } break; } } auto value = [addr_data, failure](uint32_t offset) -> uint32_t { const auto & [range, data] = addr_data; uint32_t ret = 0; switch (range.elementLength()) { case ONE: { try { ret = data.at(offset); } catch (std::out_of_range const&) { *failure = DATA_ERROR; return ret; } break; } case TWO: { try { ret = data.at(offset) << 8; ret |= data.at(offset + 1); } catch (std::out_of_range const&) { *failure = DATA_ERROR; return ret; } break; } case FOUR: { try { ret = data.at(offset) << 24; ret |= data.at(offset + 1) << 16; ret |= data.at(offset + 2) << 8; ret |= data.at(offset + 3); } catch (std::out_of_range const&) { *failure = DATA_ERROR; return ret; } break; } default: break; } return ret; }; switch (range.type()) { case SINGLE: case RANGE: { auto data = value(0); if (*failure != SUCCESS) return; for (const auto & address : addresses) { try { properties_.at(address) = data; } catch (std::out_of_range const&) { *failure = NOT_PROPERTY; return; } } break; } case ARRAY: { for (size_t idx = 0; idx < addresses.size(); idx++) { size_t offset = 0; switch (range.elementLength()) { case ONE: offset = idx; break; case TWO: offset = idx * 2; break; case FOUR: offset = idx * 4; default: break; } auto data = value(offset); if (*failure != SUCCESS) return; try { properties_.at(addresses[idx]) = data; } catch (std::out_of_range const&) { *failure = NOT_PROPERTY; return; } } } case SERIES: default: break; } } } // namespace ACN::DMP