Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
5758e0315f |
@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.0)
|
|||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
add_compile_options(-Wno-psabi)
|
add_compile_options(-Wno-psabi)
|
||||||
|
|
||||||
project(inverter-tools VERSION 1.4.0)
|
project(inverter-tools VERSION 1.5.0)
|
||||||
|
|
||||||
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
|
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
|
||||||
set(CMAKE_INSTALL_PREFIX /usr/local/bin)
|
set(CMAKE_INSTALL_PREFIX /usr/local/bin)
|
||||||
@ -29,24 +29,30 @@ find_library(LIBSERIALPORT_LIBRARY serialport)
|
|||||||
find_path(LIBSERIALPORT_INCLUDE_DIR libserialport.h)
|
find_path(LIBSERIALPORT_INCLUDE_DIR libserialport.h)
|
||||||
|
|
||||||
|
|
||||||
|
# shared list of sources
|
||||||
|
set(sources
|
||||||
|
src/common.cc
|
||||||
|
src/logging.cc
|
||||||
|
src/util.cc
|
||||||
|
src/p18/defines.cc
|
||||||
|
src/p18/client.cc
|
||||||
|
src/p18/functions.cc
|
||||||
|
src/p18/response.cc
|
||||||
|
src/p18/commands.cc
|
||||||
|
src/formatter/formatter.cc
|
||||||
|
src/voltronic/crc.cc
|
||||||
|
src/voltronic/usb_device.cc
|
||||||
|
src/voltronic/device.cc
|
||||||
|
src/voltronic/time.cc
|
||||||
|
src/voltronic/serial_device.cc
|
||||||
|
src/voltronic/pseudo_device.cc
|
||||||
|
src/voltronic/shared_buf.cc)
|
||||||
|
|
||||||
add_executable(inverterctl
|
add_executable(inverterctl
|
||||||
src/inverterctl.cc
|
src/inverterctl.cc
|
||||||
src/p18/defines.cc
|
${sources})
|
||||||
src/p18/client.cc
|
|
||||||
src/p18/functions.cc
|
|
||||||
src/p18/response.cc
|
|
||||||
src/util.cc
|
|
||||||
src/p18/commands.cc
|
|
||||||
src/common.cc
|
|
||||||
src/formatter/formatter.cc
|
|
||||||
src/voltronic/crc.cc
|
|
||||||
src/voltronic/usb_device.cc
|
|
||||||
src/voltronic/device.cc
|
|
||||||
src/voltronic/time.cc
|
|
||||||
src/voltronic/serial_device.cc
|
|
||||||
src/voltronic/pseudo_device.cc)
|
|
||||||
target_include_directories(inverterctl PRIVATE .)
|
target_include_directories(inverterctl PRIVATE .)
|
||||||
target_link_libraries(inverterctl m ${HIDAPI_LIBRARY} ${LIBSERIALPORT_LIBRARY})
|
target_link_libraries(inverterctl m pthread ${HIDAPI_LIBRARY} ${LIBSERIALPORT_LIBRARY})
|
||||||
target_compile_definitions(inverterctl PUBLIC INVERTERCTL)
|
target_compile_definitions(inverterctl PUBLIC INVERTERCTL)
|
||||||
target_include_directories(inverterctl PRIVATE
|
target_include_directories(inverterctl PRIVATE
|
||||||
${HIDAPI_INCLUDE_DIR}
|
${HIDAPI_INCLUDE_DIR}
|
||||||
@ -59,23 +65,10 @@ install(TARGETS inverterctl
|
|||||||
|
|
||||||
add_executable(inverterd
|
add_executable(inverterd
|
||||||
src/inverterd.cc
|
src/inverterd.cc
|
||||||
src/common.cc
|
${sources}
|
||||||
src/util.cc
|
|
||||||
src/server/server.cc
|
src/server/server.cc
|
||||||
src/server/connection.cc
|
src/server/connection.cc
|
||||||
src/server/signal.cc
|
src/server/signal.cc)
|
||||||
src/p18/commands.cc
|
|
||||||
src/p18/defines.cc
|
|
||||||
src/p18/client.cc
|
|
||||||
src/p18/functions.cc
|
|
||||||
src/p18/response.cc
|
|
||||||
src/formatter/formatter.cc
|
|
||||||
src/voltronic/crc.cc
|
|
||||||
src/voltronic/usb_device.cc
|
|
||||||
src/voltronic/device.cc
|
|
||||||
src/voltronic/time.cc
|
|
||||||
src/voltronic/serial_device.cc
|
|
||||||
src/voltronic/pseudo_device.cc)
|
|
||||||
target_include_directories(inverterd PRIVATE .)
|
target_include_directories(inverterd PRIVATE .)
|
||||||
target_compile_definitions(inverterd PUBLIC INVERTERD)
|
target_compile_definitions(inverterd PUBLIC INVERTERD)
|
||||||
target_link_libraries(inverterd
|
target_link_libraries(inverterd
|
||||||
|
@ -41,6 +41,10 @@ for all possible options and commands.
|
|||||||
- [inverter-bot](https://github.com/gch1p/inverter-bot) - Telegram bot that uses inverterd
|
- [inverter-bot](https://github.com/gch1p/inverter-bot) - Telegram bot that uses inverterd
|
||||||
for querying data.
|
for querying data.
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
|
- Implement proper logging with levels.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
BSD-3-Clause
|
BSD-3-Clause
|
@ -457,6 +457,7 @@ int main(int argc, char *argv[]) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dev->setWorkerType(voltronic::WorkerType::OneShot);
|
||||||
dev->setVerbose(verbose);
|
dev->setVerbose(verbose);
|
||||||
dev->setTimeout(timeout);
|
dev->setTimeout(timeout);
|
||||||
|
|
||||||
|
@ -6,8 +6,9 @@
|
|||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <ios>
|
#include <ios>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
#include "numeric_types.h"
|
#include "types.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "voltronic/device.h"
|
#include "voltronic/device.h"
|
||||||
#include "voltronic/exceptions.h"
|
#include "voltronic/exceptions.h"
|
||||||
|
17
src/logging.cc
Normal file
17
src/logging.cc
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
#include "logging.h"
|
||||||
|
|
||||||
|
std::mutex custom_log::mutex_;
|
||||||
|
|
||||||
|
custom_log::custom_log(std::ostream& os, const std::string& func)
|
||||||
|
: os_(os)
|
||||||
|
{
|
||||||
|
mutex_.lock();
|
||||||
|
os_ << func << ": ";
|
||||||
|
}
|
||||||
|
|
||||||
|
custom_log::~custom_log() {
|
||||||
|
os_ << std::endl;
|
||||||
|
mutex_.unlock();
|
||||||
|
}
|
@ -6,26 +6,24 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
#include <mutex>
|
||||||
|
#include "./types.h"
|
||||||
|
|
||||||
class custom_log
|
class custom_log
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
std::ostream& os_;
|
std::ostream& os_;
|
||||||
|
static std::mutex mutex_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
custom_log(std::ostream& os, const std::string& func) : os_(os) {
|
custom_log(std::ostream& os, const std::string& func);
|
||||||
os_ << func << ": ";
|
~custom_log();
|
||||||
}
|
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
custom_log &operator<<(const T &v) {
|
custom_log& operator<<(const T& v) {
|
||||||
os_ << v;
|
os_ << v;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
~custom_log() {
|
|
||||||
os_ << std::endl;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
inline std::string method_name(const std::string& function, const std::string& pretty) {
|
inline std::string method_name(const std::string& function, const std::string& pretty) {
|
||||||
@ -33,7 +31,7 @@ inline std::string method_name(const std::string& function, const std::string& p
|
|||||||
size_t begin = pretty.rfind(" ", locFunName) + 1;
|
size_t begin = pretty.rfind(" ", locFunName) + 1;
|
||||||
size_t end = pretty.find("(", locFunName + function.length());
|
size_t end = pretty.find("(", locFunName + function.length());
|
||||||
return pretty.substr(begin, end - begin) + "()";
|
return pretty.substr(begin, end - begin) + "()";
|
||||||
}
|
}
|
||||||
|
|
||||||
#define __METHOD_NAME__ method_name(__FUNCTION__, __PRETTY_FUNCTION__)
|
#define __METHOD_NAME__ method_name(__FUNCTION__, __PRETTY_FUNCTION__)
|
||||||
|
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
// SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
|
|
||||||
#ifndef INVERTER_TOOLS_NUMERIC_TYPES_H
|
|
||||||
#define INVERTER_TOOLS_NUMERIC_TYPES_H
|
|
||||||
|
|
||||||
typedef uint8_t u8;
|
|
||||||
typedef uint16_t u16;
|
|
||||||
typedef uint32_t u32;
|
|
||||||
typedef uint64_t u64;
|
|
||||||
|
|
||||||
#endif //INVERTER_TOOLS_NUMERIC_TYPES_H
|
|
@ -115,9 +115,9 @@ std::shared_ptr<response_type::BaseResponse> Client::execute(p18::CommandType co
|
|||||||
std::pair<std::shared_ptr<char>, size_t> Client::runOnDevice(std::string& raw) {
|
std::pair<std::shared_ptr<char>, size_t> Client::runOnDevice(std::string& raw) {
|
||||||
size_t bufSize = 256;
|
size_t bufSize = 256;
|
||||||
std::shared_ptr<char> buf(new char[bufSize]);
|
std::shared_ptr<char> buf(new char[bufSize]);
|
||||||
size_t responseSize = device_->run(
|
size_t responseSize = device_->enqueue(
|
||||||
(const u8*)raw.c_str(), raw.size(),
|
(const u8*) raw.c_str(), raw.size(),
|
||||||
(u8*)buf.get(), bufSize);
|
(u8*) buf.get(), bufSize);
|
||||||
|
|
||||||
return std::pair<std::shared_ptr<char>, size_t>(buf, responseSize);
|
return std::pair<std::shared_ptr<char>, size_t>(buf, responseSize);
|
||||||
}
|
}
|
||||||
|
@ -5,14 +5,12 @@
|
|||||||
#include <ios>
|
#include <ios>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
#include "connection.h"
|
#include "connection.h"
|
||||||
#include "../p18/commands.h"
|
#include "../p18/commands.h"
|
||||||
#include "../p18/response.h"
|
|
||||||
#include "../logging.h"
|
#include "../logging.h"
|
||||||
#include "../common.h"
|
#include "../common.h"
|
||||||
#include "hexdump/hexdump.h"
|
|
||||||
#include "signal.h"
|
|
||||||
|
|
||||||
#define CHECK_ARGUMENTS_LENGTH(__size__) \
|
#define CHECK_ARGUMENTS_LENGTH(__size__) \
|
||||||
if (arguments.size() != (__size__)) { \
|
if (arguments.size() != (__size__)) { \
|
||||||
@ -239,8 +237,13 @@ Response Connection::processRequest(char* buf) {
|
|||||||
|
|
||||||
resp.type = ResponseType::Error;
|
resp.type = ResponseType::Error;
|
||||||
|
|
||||||
auto err = p18::response_type::ErrorResponse(e.what());
|
try {
|
||||||
resp.buf << *(err.format(options_.format));
|
auto err = p18::response_type::ErrorResponse(e.what());
|
||||||
|
resp.buf << *(err.format(options_.format));
|
||||||
|
} catch (nlohmann::detail::exception& e) {
|
||||||
|
myerr << e.what();
|
||||||
|
resp.buf << "error while formatting json: " << e.what();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp;
|
return resp;
|
||||||
|
@ -8,13 +8,13 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
#include <stdexcept>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "../voltronic/exceptions.h"
|
#include "../voltronic/exceptions.h"
|
||||||
#include "../p18/exceptions.h"
|
#include "../p18/exceptions.h"
|
||||||
#include "../voltronic/time.h"
|
#include "../voltronic/time.h"
|
||||||
#include "../logging.h"
|
#include "../logging.h"
|
||||||
//#include "hexdump/hexdump.h"
|
|
||||||
#include "server.h"
|
#include "server.h"
|
||||||
#include "connection.h"
|
#include "connection.h"
|
||||||
#include "signal.h"
|
#include "signal.h"
|
||||||
@ -31,9 +31,20 @@ Server::Server(std::shared_ptr<voltronic::Device> device)
|
|||||||
, deviceErrorCounter_(0)
|
, deviceErrorCounter_(0)
|
||||||
, verbose_(false)
|
, verbose_(false)
|
||||||
, device_(std::move(device)) {
|
, device_(std::move(device)) {
|
||||||
|
|
||||||
client_.setDevice(device_);
|
client_.setDevice(device_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Server::~Server() {
|
||||||
|
if (sock_ > 0)
|
||||||
|
close(sock_);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common stuff, getters, setters
|
||||||
|
*/
|
||||||
|
|
||||||
void Server::setVerbose(bool verbose) {
|
void Server::setVerbose(bool verbose) {
|
||||||
verbose_ = verbose;
|
verbose_ = verbose;
|
||||||
device_->setVerbose(verbose);
|
device_->setVerbose(verbose);
|
||||||
@ -51,10 +62,10 @@ void Server::setDeviceErrorLimit(u32 deviceErrorLimit) {
|
|||||||
deviceErrorLimit_ = deviceErrorLimit;
|
deviceErrorLimit_ = deviceErrorLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
Server::~Server() {
|
|
||||||
if (sock_ > 0)
|
/**
|
||||||
close(sock_);
|
* TCP Server
|
||||||
}
|
*/
|
||||||
|
|
||||||
void Server::start(std::string& host, int port) {
|
void Server::start(std::string& host, int port) {
|
||||||
host_ = host;
|
host_ = host;
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
|
||||||
#include "connection.h"
|
#include "connection.h"
|
||||||
#include "../numeric_types.h"
|
#include "../types.h"
|
||||||
#include "../formatter/formatter.h"
|
#include "../formatter/formatter.h"
|
||||||
#include "../p18/client.h"
|
#include "../p18/client.h"
|
||||||
#include "../p18/types.h"
|
#include "../p18/types.h"
|
||||||
@ -22,8 +22,6 @@
|
|||||||
|
|
||||||
namespace server {
|
namespace server {
|
||||||
|
|
||||||
typedef std::lock_guard<std::mutex> LockGuard;
|
|
||||||
|
|
||||||
class Connection;
|
class Connection;
|
||||||
|
|
||||||
struct CachedResponse {
|
struct CachedResponse {
|
||||||
@ -69,6 +67,7 @@ public:
|
|||||||
void setDeviceErrorLimit(u32 deviceErrorLimit);
|
void setDeviceErrorLimit(u32 deviceErrorLimit);
|
||||||
|
|
||||||
void start(std::string& host, int port);
|
void start(std::string& host, int port);
|
||||||
|
void createDeviceThread();
|
||||||
|
|
||||||
bool verbose() const { return verbose_; }
|
bool verbose() const { return verbose_; }
|
||||||
void addConnection(Connection* conn);
|
void addConnection(Connection* conn);
|
||||||
|
16
src/types.h
Normal file
16
src/types.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
#ifndef INVERTER_TOOLS_TYPES_H
|
||||||
|
#define INVERTER_TOOLS_TYPES_H
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
typedef uint8_t u8;
|
||||||
|
typedef uint16_t u16;
|
||||||
|
typedef uint32_t u32;
|
||||||
|
typedef uint64_t u64;
|
||||||
|
|
||||||
|
typedef std::lock_guard<std::mutex> LockGuard;
|
||||||
|
typedef std::unique_lock<std::mutex> UniqueLock;
|
||||||
|
|
||||||
|
#endif //INVERTER_TOOLS_TYPES_H
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include "../numeric_types.h"
|
#include "../types.h"
|
||||||
|
|
||||||
namespace voltronic {
|
namespace voltronic {
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <sstream>
|
#include <chrono>
|
||||||
|
|
||||||
#include "crc.h"
|
#include "crc.h"
|
||||||
#include "device.h"
|
#include "device.h"
|
||||||
@ -13,11 +13,38 @@
|
|||||||
#include "hexdump/hexdump.h"
|
#include "hexdump/hexdump.h"
|
||||||
#include "../logging.h"
|
#include "../logging.h"
|
||||||
|
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
namespace voltronic {
|
namespace voltronic {
|
||||||
|
|
||||||
Device::Device() :
|
Device::Device()
|
||||||
flags_(FLAG_WRITE_CRC | FLAG_READ_CRC | FLAG_VERIFY_CRC),
|
: flags_(FLAG_WRITE_CRC | FLAG_READ_CRC | FLAG_VERIFY_CRC)
|
||||||
timeout_(TIMEOUT) {}
|
, timeout_(TIMEOUT)
|
||||||
|
, timeStarted_(0)
|
||||||
|
, verbose_(false)
|
||||||
|
, workerType_(WorkerType::Normal)
|
||||||
|
{
|
||||||
|
thread_ = std::thread(&Device::threadLoop, this);
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
// try to set the highest priority for this thread (requires root privs)
|
||||||
|
struct sched_param sp;
|
||||||
|
sp.sched_priority = sched_get_priority_max(SCHED_FIFO);
|
||||||
|
if (sp.sched_priority == -1) {
|
||||||
|
myerr << "sched_get_priority_max: " << std::string(strerror(errno));
|
||||||
|
} else {
|
||||||
|
int res = pthread_setschedparam(thread_.native_handle(), SCHED_FIFO, &sp);
|
||||||
|
if (res)
|
||||||
|
myerr << "pthread_setschedparam: error " << std::to_string(res);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
thread_.detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
Device::~Device() {
|
||||||
|
workerType_ = WorkerType::Dead;
|
||||||
|
}
|
||||||
|
|
||||||
void Device::setFlags(int flags) {
|
void Device::setFlags(int flags) {
|
||||||
flags_ = flags;
|
flags_ = flags;
|
||||||
@ -27,6 +54,10 @@ int Device::getFlags() const {
|
|||||||
return flags_;
|
return flags_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Device::setWorkerType(WorkerType wt) {
|
||||||
|
workerType_ = wt;
|
||||||
|
}
|
||||||
|
|
||||||
void Device::setVerbose(bool verbose) {
|
void Device::setVerbose(bool verbose) {
|
||||||
verbose_ = verbose;
|
verbose_ = verbose;
|
||||||
}
|
}
|
||||||
@ -50,10 +81,130 @@ u64 Device::getTimeLeft() const {
|
|||||||
return timeout_ - elapsed;
|
return timeout_ - elapsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t Device::enqueue(const u8* inbuf, size_t inbufSize, u8* outbuf, size_t outbufSize) {
|
||||||
|
if (verbose_)
|
||||||
|
mylog << "waiting to accept new task...";
|
||||||
|
|
||||||
|
mutex_.lock();
|
||||||
|
shio_.resetWith(inbuf, inbufSize, outbuf, outbufSize);
|
||||||
|
mutex_.unlock();
|
||||||
|
|
||||||
|
cv_.notify_all();
|
||||||
|
|
||||||
|
if (verbose_)
|
||||||
|
mylog << "notify the worker thread";
|
||||||
|
|
||||||
|
UniqueLock lock(mutex_);
|
||||||
|
cv_.wait(lock, [this]{
|
||||||
|
return shio_.state == SharedIOBufferState::Done;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (verbose_)
|
||||||
|
mylog << "worker thread done it's job";
|
||||||
|
|
||||||
|
switch (shio_.errorType) {
|
||||||
|
case ErrorType::DeviceError: throw DeviceError(shio_.errorMessage);
|
||||||
|
case ErrorType::TimeoutError: throw TimeoutError(shio_.errorMessage);
|
||||||
|
case ErrorType::InvalidDataError: throw InvalidDataError(shio_.errorMessage);
|
||||||
|
case ErrorType::OverflowError: throw OverflowError(shio_.errorMessage);
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return shio_.dataSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
// all code below runs in a separate thread
|
||||||
|
// ----------------------------------------
|
||||||
|
|
||||||
|
void Device::threadLoop() {
|
||||||
|
while (workerType_ != Dead) {
|
||||||
|
if (verbose_)
|
||||||
|
mylog << "waiting for new task...";
|
||||||
|
|
||||||
|
// wait for new task
|
||||||
|
UniqueLock lock(mutex_);
|
||||||
|
auto pred = [this]{
|
||||||
|
return shio_.state == SharedIOBufferState::Ready;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (workerType_ == OneShot) {
|
||||||
|
cv_.wait(lock, pred);
|
||||||
|
} else {
|
||||||
|
cv_.wait_for(lock, 1s, pred);
|
||||||
|
if (!pred())
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (verbose_)
|
||||||
|
mylog << "got something";
|
||||||
|
|
||||||
|
shio_.state = SharedIOBufferState::InProgress;
|
||||||
|
|
||||||
|
try {
|
||||||
|
shio_.setResult(run(shio_.inputBuffer,
|
||||||
|
shio_.inputBufferSize,
|
||||||
|
shio_.outputBuffer,
|
||||||
|
shio_.outputBufferSize));
|
||||||
|
}
|
||||||
|
catch (DeviceError& e) {
|
||||||
|
shio_.setResult(ErrorType::DeviceError, e.what());
|
||||||
|
}
|
||||||
|
catch (TimeoutError& e) {
|
||||||
|
shio_.setResult(ErrorType::TimeoutError, e.what());
|
||||||
|
}
|
||||||
|
catch (InvalidDataError& e) {
|
||||||
|
shio_.setResult(ErrorType::InvalidDataError, e.what());
|
||||||
|
}
|
||||||
|
catch (OverflowError& e) {
|
||||||
|
shio_.setResult(ErrorType::OverflowError, e.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verbose_)
|
||||||
|
mylog << "unlocking";
|
||||||
|
|
||||||
|
lock.unlock();
|
||||||
|
cv_.notify_all();
|
||||||
|
|
||||||
|
if (workerType_ == OneShot)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
size_t Device::run(const u8* inbuf, size_t inbufSize, u8* outbuf, size_t outbufSize) {
|
size_t Device::run(const u8* inbuf, size_t inbufSize, u8* outbuf, size_t outbufSize) {
|
||||||
timeStarted_ = timestamp();
|
timeStarted_ = timestamp();
|
||||||
|
|
||||||
send(inbuf, inbufSize);
|
// ------------------------------
|
||||||
|
// add CRC and send to the device
|
||||||
|
// ------------------------------
|
||||||
|
|
||||||
|
size_t dataLen;
|
||||||
|
std::shared_ptr<u8> data;
|
||||||
|
|
||||||
|
if ((flags_ & FLAG_WRITE_CRC) == FLAG_WRITE_CRC) {
|
||||||
|
const CRC crc = crc_calculate(inbuf, inbufSize);
|
||||||
|
dataLen = inbufSize + sizeof(u16) + 1;
|
||||||
|
data = std::unique_ptr<u8>(new u8[dataLen]);
|
||||||
|
crc_write(crc, &data.get()[inbufSize]);
|
||||||
|
} else {
|
||||||
|
dataLen = inbufSize + 1;
|
||||||
|
data = std::unique_ptr<u8>(new u8[dataLen]);
|
||||||
|
}
|
||||||
|
|
||||||
|
u8* dataPtr = data.get();
|
||||||
|
memcpy((void*)dataPtr, inbuf, inbufSize);
|
||||||
|
dataPtr[dataLen - 1] = '\r';
|
||||||
|
if (verbose_) {
|
||||||
|
myerr << "writing " << dataLen << (dataLen > 1 ? " bytes" : " byte");
|
||||||
|
std::cerr << hexdump(dataPtr, dataLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
writeAll(dataPtr, dataLen);
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------
|
||||||
|
// check for timeout
|
||||||
|
// -----------------
|
||||||
|
|
||||||
if (!getTimeLeft()) {
|
if (!getTimeLeft()) {
|
||||||
// FIXME
|
// FIXME
|
||||||
@ -63,37 +214,42 @@ size_t Device::run(const u8* inbuf, size_t inbufSize, u8* outbuf, size_t outbufS
|
|||||||
throw TimeoutError("sending already took " + std::to_string(getElapsedTime()) + " ms");
|
throw TimeoutError("sending already took " + std::to_string(getElapsedTime()) + " ms");
|
||||||
}
|
}
|
||||||
|
|
||||||
return recv(outbuf, outbufSize);
|
// ------------------------------
|
||||||
}
|
// read from device and check CRC
|
||||||
|
// ------------------------------
|
||||||
|
|
||||||
void Device::send(const u8* buf, size_t bufSize) {
|
size_t bytesRead = readAll(outbuf, outbufSize);
|
||||||
size_t dataLen;
|
|
||||||
std::shared_ptr<u8> data;
|
|
||||||
|
|
||||||
if ((flags_ & FLAG_WRITE_CRC) == FLAG_WRITE_CRC) {
|
|
||||||
const CRC crc = crc_calculate(buf, bufSize);
|
|
||||||
dataLen = bufSize + sizeof(u16) + 1;
|
|
||||||
data = std::unique_ptr<u8>(new u8[dataLen]);
|
|
||||||
crc_write(crc, &data.get()[bufSize]);
|
|
||||||
} else {
|
|
||||||
dataLen = bufSize + 1;
|
|
||||||
data = std::unique_ptr<u8>(new u8[dataLen]);
|
|
||||||
}
|
|
||||||
|
|
||||||
u8* dataPtr = data.get();
|
|
||||||
memcpy((void*)dataPtr, buf, bufSize);
|
|
||||||
|
|
||||||
dataPtr[dataLen - 1] = '\r';
|
|
||||||
|
|
||||||
if (verbose_) {
|
if (verbose_) {
|
||||||
myerr << "writing " << dataLen << (dataLen > 1 ? " bytes" : " byte");
|
myerr << "got " << bytesRead << (bytesRead > 1 ? " bytes" : " byte");
|
||||||
std::cerr << hexdump(dataPtr, dataLen);
|
std::cerr << hexdump(outbuf, bytesRead);
|
||||||
}
|
}
|
||||||
|
|
||||||
writeLoop(dataPtr, dataLen);
|
bool crcNeeded = (flags_ & FLAG_READ_CRC) == FLAG_READ_CRC;
|
||||||
|
size_t minSize = crcNeeded ? sizeof(u16) + 1 : 1;
|
||||||
|
|
||||||
|
if (bytesRead < minSize)
|
||||||
|
throw InvalidDataError("response is too small");
|
||||||
|
|
||||||
|
const size_t dataSize = bytesRead - minSize;
|
||||||
|
|
||||||
|
if (crcNeeded) {
|
||||||
|
const CRC crcActual = crc_read(&outbuf[dataSize]);
|
||||||
|
const CRC crcExpected = crc_calculate(outbuf, dataSize);
|
||||||
|
|
||||||
|
if ((flags_ & FLAG_VERIFY_CRC) == FLAG_VERIFY_CRC && crcActual == crcExpected)
|
||||||
|
return dataSize;
|
||||||
|
|
||||||
|
std::ostringstream error;
|
||||||
|
error << std::hex;
|
||||||
|
error << "crc is invalid: expected 0x" << crcExpected << ", got 0x" << crcActual;
|
||||||
|
throw InvalidDataError(error.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return dataSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Device::writeLoop(const u8* data, size_t dataSize) {
|
void Device::writeAll(const u8* data, size_t dataSize) {
|
||||||
int bytesLeft = static_cast<int>(dataSize);
|
int bytesLeft = static_cast<int>(dataSize);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
@ -112,42 +268,7 @@ void Device::writeLoop(const u8* data, size_t dataSize) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Device::recv(u8* buf, size_t bufSize) {
|
size_t Device::readAll(u8 *buf, size_t bufSize) {
|
||||||
size_t bytesRead = readLoop(buf, bufSize);
|
|
||||||
|
|
||||||
if (verbose_) {
|
|
||||||
myerr << "got " << bytesRead << (bytesRead > 1 ? " bytes" : " byte");
|
|
||||||
std::cerr << hexdump(buf, bytesRead);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool crcNeeded = (flags_ & FLAG_READ_CRC) == FLAG_READ_CRC;
|
|
||||||
size_t minSize = crcNeeded ? sizeof(u16) + 1 : 1;
|
|
||||||
|
|
||||||
if (bytesRead < minSize)
|
|
||||||
throw InvalidDataError("response is too small");
|
|
||||||
|
|
||||||
const size_t dataSize = bytesRead - minSize;
|
|
||||||
|
|
||||||
if (crcNeeded) {
|
|
||||||
const CRC crcActual = crc_read(&buf[dataSize]);
|
|
||||||
const CRC crcExpected = crc_calculate(buf, dataSize);
|
|
||||||
|
|
||||||
// buf[dataSize] = 0;
|
|
||||||
|
|
||||||
if ((flags_ & FLAG_VERIFY_CRC) == FLAG_VERIFY_CRC && crcActual == crcExpected)
|
|
||||||
return dataSize;
|
|
||||||
|
|
||||||
std::ostringstream error;
|
|
||||||
error << std::hex;
|
|
||||||
error << "crc is invalid: expected 0x" << crcExpected << ", got 0x" << crcActual;
|
|
||||||
throw InvalidDataError(error.str());
|
|
||||||
}
|
|
||||||
|
|
||||||
// buf[dataSize] = 0;
|
|
||||||
return dataSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t Device::readLoop(u8 *buf, size_t bufSize) {
|
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
|
|
||||||
while(true) {
|
while(true) {
|
||||||
@ -170,7 +291,7 @@ size_t Device::readLoop(u8 *buf, size_t bufSize) {
|
|||||||
throw TimeoutError("data reading already took " + std::to_string(getElapsedTime()) + " ms");
|
throw TimeoutError("data reading already took " + std::to_string(getElapsedTime()) + " ms");
|
||||||
|
|
||||||
if (bufSize <= 0)
|
if (bufSize <= 0)
|
||||||
throw std::overflow_error("input buffer is not large enough");
|
throw OverflowError("input buffer is not large enough");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,10 +5,15 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <atomic>
|
||||||
|
#include <thread>
|
||||||
|
#include <mutex>
|
||||||
|
#include <condition_variable>
|
||||||
#include <hidapi/hidapi.h>
|
#include <hidapi/hidapi.h>
|
||||||
#include <libserialport.h>
|
#include <libserialport.h>
|
||||||
|
|
||||||
#include "../numeric_types.h"
|
#include "exceptions.h"
|
||||||
|
#include "../types.h"
|
||||||
|
|
||||||
namespace voltronic {
|
namespace voltronic {
|
||||||
|
|
||||||
@ -18,10 +23,40 @@ enum {
|
|||||||
FLAG_VERIFY_CRC = 4,
|
FLAG_VERIFY_CRC = 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class SharedIOBufferState {
|
||||||
|
Ready,
|
||||||
|
InProgress,
|
||||||
|
Done,
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
struct SharedIOBuffer {
|
||||||
* Common device
|
// helper methods
|
||||||
*/
|
void resetWith(const u8* inbuf, size_t inbufSize, u8* outbuf, size_t outbufSize);
|
||||||
|
void setResult(size_t dataSize);
|
||||||
|
void setResult(ErrorType type, std::string message);
|
||||||
|
|
||||||
|
// input
|
||||||
|
const u8* inputBuffer;
|
||||||
|
u8* outputBuffer;
|
||||||
|
size_t inputBufferSize;
|
||||||
|
size_t outputBufferSize;
|
||||||
|
|
||||||
|
// output
|
||||||
|
SharedIOBufferState state = SharedIOBufferState::Done;
|
||||||
|
size_t dataSize;
|
||||||
|
ErrorType errorType = ErrorType::None;
|
||||||
|
std::string errorMessage;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum WorkerType {
|
||||||
|
OneShot,
|
||||||
|
Normal,
|
||||||
|
Dead,
|
||||||
|
};
|
||||||
|
|
||||||
|
// -------------
|
||||||
|
// Common device
|
||||||
|
// -------------
|
||||||
|
|
||||||
class Device {
|
class Device {
|
||||||
protected:
|
protected:
|
||||||
@ -30,11 +65,15 @@ protected:
|
|||||||
u64 timeStarted_;
|
u64 timeStarted_;
|
||||||
bool verbose_;
|
bool verbose_;
|
||||||
|
|
||||||
void send(const u8* buf, size_t bufSize);
|
SharedIOBuffer shio_;
|
||||||
size_t recv(u8* buf, size_t bufSize);
|
std::thread thread_;
|
||||||
|
// std::mutex enqueueMutex_;
|
||||||
|
std::mutex mutex_;
|
||||||
|
std::condition_variable cv_;
|
||||||
|
WorkerType workerType_;
|
||||||
|
|
||||||
void writeLoop(const u8* data, size_t dataSize);
|
void writeAll(const u8* data, size_t dataSize);
|
||||||
size_t readLoop(u8* buf, size_t bufSize);
|
size_t readAll(u8* buf, size_t bufSize);
|
||||||
|
|
||||||
u64 getElapsedTime() const;
|
u64 getElapsedTime() const;
|
||||||
u64 getTimeLeft() const;
|
u64 getTimeLeft() const;
|
||||||
@ -43,23 +82,26 @@ public:
|
|||||||
static const u64 TIMEOUT = 1000;
|
static const u64 TIMEOUT = 1000;
|
||||||
|
|
||||||
Device();
|
Device();
|
||||||
|
~Device();
|
||||||
virtual size_t read(u8* buf, size_t bufSize) = 0;
|
|
||||||
virtual size_t write(const u8* data, size_t dataSize) = 0;
|
|
||||||
|
|
||||||
void setTimeout(u64 timeout);
|
|
||||||
size_t run(const u8* inbuf, size_t inbufSize, u8* outbuf, size_t outbufSize);
|
|
||||||
|
|
||||||
void setFlags(int flags);
|
void setFlags(int flags);
|
||||||
int getFlags() const;
|
int getFlags() const;
|
||||||
|
void setWorkerType(WorkerType wt);
|
||||||
void setVerbose(bool verbose);
|
void setVerbose(bool verbose);
|
||||||
|
void setTimeout(u64 timeout);
|
||||||
|
|
||||||
|
size_t enqueue(const u8* inbuf, size_t inbufSize, u8* outbuf, size_t outbufSize);
|
||||||
|
size_t run(const u8* inbuf, size_t inbufSize, u8* outbuf, size_t outbufSize);
|
||||||
|
void threadLoop();
|
||||||
|
|
||||||
|
virtual size_t read(u8* buf, size_t bufSize) = 0;
|
||||||
|
virtual size_t write(const u8* data, size_t dataSize) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
// ----------
|
||||||
* USB device
|
// USB device
|
||||||
*/
|
// ----------
|
||||||
|
|
||||||
class USBDevice : public Device {
|
class USBDevice : public Device {
|
||||||
private:
|
private:
|
||||||
@ -79,9 +121,9 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
// -------------
|
||||||
* Serial device
|
// Serial device
|
||||||
*/
|
// -------------
|
||||||
|
|
||||||
typedef unsigned SerialBaudRate;
|
typedef unsigned SerialBaudRate;
|
||||||
|
|
||||||
@ -149,7 +191,10 @@ public:
|
|||||||
explicit SerialPortConfiguration(SerialDevice& device);
|
explicit SerialPortConfiguration(SerialDevice& device);
|
||||||
~SerialPortConfiguration();
|
~SerialPortConfiguration();
|
||||||
|
|
||||||
void setConfiguration(SerialBaudRate baudRate, SerialDataBits dataBits, SerialStopBits stopBits, SerialParity parity);
|
void setConfiguration(SerialBaudRate baudRate,
|
||||||
|
SerialDataBits dataBits,
|
||||||
|
SerialStopBits stopBits,
|
||||||
|
SerialParity parity);
|
||||||
};
|
};
|
||||||
|
|
||||||
bool is_serial_baud_rate_valid(SerialBaudRate baudRate);
|
bool is_serial_baud_rate_valid(SerialBaudRate baudRate);
|
||||||
@ -158,9 +203,9 @@ bool is_serial_stop_bits_valid(SerialStopBits stopBits);
|
|||||||
bool is_serial_parity_valid(SerialParity parity);
|
bool is_serial_parity_valid(SerialParity parity);
|
||||||
|
|
||||||
|
|
||||||
/**
|
// -------------
|
||||||
* Pseudo device
|
// Pseudo device
|
||||||
*/
|
// -------------
|
||||||
|
|
||||||
class PseudoDevice : public Device {
|
class PseudoDevice : public Device {
|
||||||
public:
|
public:
|
||||||
|
@ -7,6 +7,10 @@
|
|||||||
|
|
||||||
namespace voltronic {
|
namespace voltronic {
|
||||||
|
|
||||||
|
// ----------
|
||||||
|
// exceptions
|
||||||
|
// ----------
|
||||||
|
|
||||||
class DeviceError : public std::runtime_error {
|
class DeviceError : public std::runtime_error {
|
||||||
public:
|
public:
|
||||||
using std::runtime_error::runtime_error;
|
using std::runtime_error::runtime_error;
|
||||||
@ -22,6 +26,24 @@ public:
|
|||||||
using std::runtime_error::runtime_error;
|
using std::runtime_error::runtime_error;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class OverflowError : public std::overflow_error {
|
||||||
|
public:
|
||||||
|
using std::overflow_error::overflow_error;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------
|
||||||
|
// exception types
|
||||||
|
// ---------------
|
||||||
|
|
||||||
|
enum class ErrorType {
|
||||||
|
None = 0,
|
||||||
|
DeviceError,
|
||||||
|
TimeoutError,
|
||||||
|
InvalidDataError,
|
||||||
|
OverflowError,
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif //INVERTER_TOOLS_VOLTRONIC_EXCEPTIONS_H
|
#endif //INVERTER_TOOLS_VOLTRONIC_EXCEPTIONS_H
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include "crc.h"
|
#include "crc.h"
|
||||||
#include "hexdump/hexdump.h"
|
#include "hexdump/hexdump.h"
|
||||||
#include "../logging.h"
|
#include "../logging.h"
|
||||||
|
#include "exceptions.h"
|
||||||
|
|
||||||
namespace voltronic {
|
namespace voltronic {
|
||||||
|
|
||||||
@ -26,7 +27,7 @@ static const char* response = "^D1060000,000,2300,500,0115,0018,002,500,000,000,
|
|||||||
// set response
|
// set response
|
||||||
//static const char* response = "^1";
|
//static const char* response = "^1";
|
||||||
|
|
||||||
// TODO: maybe move size and crc stuff to readLoop()?
|
// TODO: maybe move size and crc stuff to readAll()?
|
||||||
size_t PseudoDevice::read(u8* buf, size_t bufSize) {
|
size_t PseudoDevice::read(u8* buf, size_t bufSize) {
|
||||||
size_t pseudoResponseSize = strlen(response);
|
size_t pseudoResponseSize = strlen(response);
|
||||||
|
|
||||||
@ -37,7 +38,7 @@ size_t PseudoDevice::read(u8* buf, size_t bufSize) {
|
|||||||
if (responseSize + 1 > bufSize) {
|
if (responseSize + 1 > bufSize) {
|
||||||
std::ostringstream error;
|
std::ostringstream error;
|
||||||
error << "buffer is not large enough (" << (responseSize + 1) << " > " << bufSize << ")";
|
error << "buffer is not large enough (" << (responseSize + 1) << " > " << bufSize << ")";
|
||||||
throw std::overflow_error(error.str());
|
throw OverflowError(error.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(buf, response, responseSize);
|
memcpy(buf, response, responseSize);
|
||||||
|
38
src/voltronic/shared_buf.cc
Normal file
38
src/voltronic/shared_buf.cc
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
#include "device.h"
|
||||||
|
|
||||||
|
namespace voltronic {
|
||||||
|
|
||||||
|
void SharedIOBuffer::resetWith(const u8* inbuf,
|
||||||
|
size_t inbufSize,
|
||||||
|
u8* outbuf,
|
||||||
|
size_t outbufSize) {
|
||||||
|
// set input
|
||||||
|
inputBuffer = inbuf;
|
||||||
|
inputBufferSize = inbufSize;
|
||||||
|
|
||||||
|
outputBuffer = outbuf;
|
||||||
|
outputBufferSize = outbufSize;
|
||||||
|
|
||||||
|
// clean output
|
||||||
|
errorType = ErrorType::None;
|
||||||
|
errorMessage.erase();
|
||||||
|
dataSize = 0;
|
||||||
|
|
||||||
|
// mark as ready
|
||||||
|
state = SharedIOBufferState::Ready;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SharedIOBuffer::setResult(ErrorType type, std::string message) {
|
||||||
|
errorType = type;
|
||||||
|
errorMessage = std::move(message);
|
||||||
|
state = SharedIOBufferState::Done;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SharedIOBuffer::setResult(size_t _dataSize) {
|
||||||
|
dataSize = _dataSize;
|
||||||
|
state = SharedIOBufferState::Done;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -3,7 +3,7 @@
|
|||||||
#ifndef INVERTER_TOOLS_VOLTRONIC_TIME_H
|
#ifndef INVERTER_TOOLS_VOLTRONIC_TIME_H
|
||||||
#define INVERTER_TOOLS_VOLTRONIC_TIME_H
|
#define INVERTER_TOOLS_VOLTRONIC_TIME_H
|
||||||
|
|
||||||
#include "../numeric_types.h"
|
#include "../types.h"
|
||||||
|
|
||||||
namespace voltronic {
|
namespace voltronic {
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user