OpenLCP/protocol/esta/acn/dmp/device.cpp

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);
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::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);
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<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