316 lines
8.0 KiB
C++
316 lines
8.0 KiB
C++
/*
|
|
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::Pdu> dmp)
|
|
{
|
|
auto addr_type = std::static_pointer_cast<address_type>(dmp->header());
|
|
auto failed = std::make_shared<address_pair_list>(*addr_type);
|
|
auto gotten = std::make_shared<address_pair_list>(*addr_type);
|
|
|
|
auto vectorfy = [](uint32_t value, element_length width) -> std::vector<uint8_t> {
|
|
auto ret = std::vector<uint8_t>();
|
|
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<address_list>(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<uint8_t>(success)));
|
|
}
|
|
|
|
|
|
auto reply = std::make_shared<DMP::Pdu>();
|
|
|
|
if (!gotten->properties.empty())
|
|
{
|
|
reply->setVector(GET_PROPERTY_REPLY);
|
|
reply->setHeader(dmp->header());
|
|
reply->setData(gotten);
|
|
sendDMP(reply);
|
|
}
|
|
|
|
if (!failed->properties.empty())
|
|
{
|
|
reply->setVector(GET_PROPERTY_FAIL);
|
|
reply->setData(failed);
|
|
sendDMP(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::Pdu> 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<address_type>(dmp->header());
|
|
auto failed = std::make_shared<address_pair_list>(*addr_type);
|
|
|
|
auto data = std::static_pointer_cast<address_pair_list>(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<uint8_t>(success)));
|
|
}
|
|
|
|
if (failed->properties.empty())
|
|
return;
|
|
|
|
auto reply = std::make_shared<DMP::Pdu>();
|
|
reply->setVector(SET_PROPERTY_FAIL);
|
|
reply->setHeader(dmp->header());
|
|
reply->setData(failed);
|
|
sendDMP(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<uint32_t> 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
|