127 lines
3.9 KiB
C++
127 lines
3.9 KiB
C++
// SPDX-License-Identifier: MIT
|
|
/**
|
|
Copyright (c) 2020 - 2022 Beckhoff Automation GmbH & Co. KG
|
|
*/
|
|
|
|
#include "AdsFile.h"
|
|
#include <iostream>
|
|
#include <list>
|
|
|
|
namespace Beckhoff
|
|
{
|
|
namespace Ads
|
|
{
|
|
static bool FindNext(const AdsDevice& route,
|
|
TcFileFindData& child,
|
|
const size_t length = 0,
|
|
const char* const path = nullptr)
|
|
{
|
|
const auto error = route.ReadWriteReqEx2(
|
|
SYSTEMSERVICE_FFILEFIND,
|
|
child.hFile,
|
|
sizeof(child),
|
|
&child,
|
|
length,
|
|
path,
|
|
nullptr);
|
|
// We reached the last child
|
|
// If there is no more file in the current path the ADS service will
|
|
// return ads error code 1804 so we can break and exit as expected.
|
|
if (error && (error != 1804)) {
|
|
throw AdsException(error);
|
|
}
|
|
// TwinCAT sends data in little endian so we have to convert it here
|
|
child.letoh();
|
|
return error == 1804;
|
|
}
|
|
|
|
static bool FindFirst(const AdsDevice& route, TcFileFindData& item, const std::string& path)
|
|
{
|
|
enum FFILEFIND : uint32_t {
|
|
GENERIC = 1 << 0,
|
|
};
|
|
|
|
item.hFile = FFILEFIND::GENERIC;
|
|
return FindNext(route, item, path.length(), path.c_str());
|
|
}
|
|
|
|
AdsFile::AdsFile(const AdsDevice& route, const std::string& filename, const uint32_t flags)
|
|
: m_Route(route),
|
|
m_Handle(route.OpenFile(filename, flags))
|
|
{}
|
|
|
|
void AdsFile::Delete(const AdsDevice& route, const std::string& filename, const uint32_t flags)
|
|
{
|
|
auto error = route.ReadWriteReqEx2(SYSTEMSERVICE_FDELETE,
|
|
flags,
|
|
0,
|
|
nullptr,
|
|
filename.length(),
|
|
filename.c_str(),
|
|
nullptr);
|
|
|
|
if (error) {
|
|
throw AdsException(error);
|
|
}
|
|
}
|
|
|
|
int AdsFile::Find(const AdsDevice& route, const std::string& basePath, const size_t maxdepth, std::ostream& os)
|
|
{
|
|
struct Path {
|
|
size_t depth;
|
|
std::string path;
|
|
};
|
|
std::list<struct Path> pendingDirs{{ 0, basePath} };
|
|
while (!pendingDirs.empty()) {
|
|
auto path = pendingDirs.front().path;
|
|
auto depth = pendingDirs.front().depth;
|
|
pendingDirs.pop_front();
|
|
|
|
TcFileFindData parent;
|
|
if (FindFirst(route, parent, path)) {
|
|
return 1804;
|
|
}
|
|
|
|
// Path exists print it and prepare traversing
|
|
os << path << '\n';
|
|
|
|
if (parent.isDirectory() && (depth < maxdepth)) {
|
|
// Finding files in a directory is a bit weird. We get only one entry per call and for
|
|
// every call we pass the last found item to get the next. The first item is special.
|
|
// To get the children of a directory we have to append '/*'to the path of the directory.
|
|
for (auto last = FindFirst(route, parent, path + "/*"); !last; last = FindNext(route, parent)) {
|
|
if (parent.isDirectory()) {
|
|
pendingDirs.push_back({depth + 1, path + '/' + parent.cFileName});
|
|
} else {
|
|
os << path << '/' << parent.cFileName << '\n';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void AdsFile::Read(const size_t size, void* data, uint32_t& bytesRead) const
|
|
{
|
|
auto error = m_Route.ReadWriteReqEx2(SYSTEMSERVICE_FREAD,
|
|
*m_Handle,
|
|
size,
|
|
data,
|
|
0,
|
|
nullptr,
|
|
&bytesRead);
|
|
|
|
if (error) {
|
|
throw AdsException(error);
|
|
}
|
|
}
|
|
|
|
void AdsFile::Write(const size_t size, const void* data) const
|
|
{
|
|
auto error = m_Route.ReadWriteReqEx2(SYSTEMSERVICE_FWRITE, *m_Handle, 0, nullptr, size, data, nullptr);
|
|
if (error) {
|
|
throw AdsException(error);
|
|
}
|
|
}
|
|
}
|
|
} |