I have a scheduler code to perform some task as per given time.
I have a table in database of name config, where different time is given to every row which have time in second to read data from a different table. I gave time in the format like.
20 , 40, 50, 60 seconds.
Code should run in such a way that when I start my program after every 20 second that column should execute which have 20 second, after every 40 second that column should execute which have 40 second and so on. But it is not executing in that way. Scheduler is not working as per given seconds, it is skipping and mismatching time
Below is a code where I am trying to read data as per time given in config table
void plac::worker_thread(void)
{
try {
std::chrono::steady_clock::time_point tick_time = std::chrono::steady_clock::now();
std::uint32_t tick_count = 1;
plac::scheduling_struct scheduling_struct;
bool executing_backlog;
bool tick_iteration_complete = true;
std::uint32_t current_iteration;
while (1)
{
executing_backlog = false;
if (not this->scheduler_backlog.empty())
{
{
std::lock_guard<std::mutex> lck(this->mtx2);
current_iteration = this->scheduler_backlog.front();
this->scheduler_backlog.pop();
}
//executing_backlog = true;
}
if (not executing_backlog and tick_iteration_complete) {
//tick_time = std::chrono::steady_clock::now();
//std::this_thread::sleep_until(tick_time + std::chrono::milliseconds(1000)); //1 second delay
tick_time = std::chrono::steady_clock::now();
std::this_thread::sleep_until(std::chrono::steady_clock::now() + std::chrono::milliseconds(1000)); //1 second delay
tick_iteration_complete = false;
if (++tick_count > this->scan_rate_max)
tick_count = 1;
}
{
std::lock_guard<std::mutex> lck(this->mtx1);
if (not executing_backlog)
{
current_iteration = this->scheduler_iteration;
if (++this->scheduler_iteration >= this->scheduler_list.size())
this->scheduler_iteration = 0;
}
scheduling_struct = this->scheduler_list.at(current_iteration);
}
if (tick_count % scheduling_struct.scan_rate == 0)
{
//std::cout << "hello" << std::endl;
if (this->_plac.find(scheduling_struct.ip) == this->_plac.end())
{
std::string error = fmt::format("No recored found in map at location {}", scheduling_struct.ip);
#ifdef _DEBUG
spdlog::error(error);
#endif // _DEBUG
LOG_ERROR << error;
continue;
}
if (not this->client.at(this->_plac.at(scheduling_struct.ip).client_location).read_progress)
{
const plac_common::config_struct config = this->_plac.at(scheduling_struct.ip).read_vector.at(scheduling_struct.config_serial_no);
if (not this->read_data(scheduling_struct))
{
spdlog::error("read data failed");
}
continue;
//this->read_data(current_iteration, client_location, config.area_type, config.area_number, config.read_location, config.read_length, config.word_length);
}
else
{
std::lock_guard<std::mutex> lck(this->mtx2);
this->scheduler_backlog.push(current_iteration);
}
}
tick_iteration_complete = true;
}
}
catch (const std::exception& ex) {
#ifdef _DEBUG
spdlog::error("Exception in worker thread. Exception : {}", ex.what());
#endif // _DEBUG
LOG_ERROR << ex.what();
}
}
OS - Windows 10 64 bit Home
Visual Studio - 15.9.19
Database - PostgreSQL
I think it maybe that if (++tick_count > this->scan_rate_max) causes that problem. When ++tick_count, it leads to change tick_count from 1 to 2. So, tick_count in if (tick_count % scheduling_struct.scan_rate == 0) is bigger than expected. I suggest that you can try to change ++tick_count to tick_count++.
if (not executing_backlog and tick_iteration_complete) {
//tick_time = std::chrono::steady_clock::now();
//std::this_thread::sleep_until(tick_time + std::chrono::milliseconds(1000)); //1 second delay
tick_time = std::chrono::steady_clock::now();
std::this_thread::sleep_until(std::chrono::steady_clock::now() + std::chrono::milliseconds(1000)); //1 second delay
tick_iteration_complete = false;
if (tick_count++ > this->scan_rate_max)
tick_count = 1;
}
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed last year.
Improve this question
I'm implementing a multiple threads download manager on Windows using C++.
The main thread starts download manage thread M, M starts several download threads D.
Each D will do the HTTP data transfer using library cpr which is a wrapper around libcurl.
After starts some D threads, M enters a loop, keep watching the download progress.
The strange thing: once the second D started, or I abort the first D by return error code(an integer other then 0, I return 1.) from the libcurl's CURLOPT_XFERINFOFUNCTION callback, M's loop will stop. (There's one debugging output inside M's loop. I notice its stop by the disappear of that output from console. Maybe it's not stop, just going into some waiting state instead...)
Both M and D thread are started by STL's std::thread.
Have been scratched by this problem a whole day. Any clue will be appreciated...
This is the M thread entrance:
void HttpDownloader1::MasterThreadFunc_()
{
int loop_count = 0;
// start the first download thread
if (!SplitDownload_(nullptr))
{
status_ = Status::ERRONEOUS;
return;
}
uint64_t prev_downloaded_bytes = recorder_->GetDownloadedBytes();
for (loop_count = 0; download_threads_.size() > 0; loop_count++)
//while (true)
{
loop_count++;
#ifdef _DEBUG
Debug_("main loop, threads: " + std::to_string(download_threads_.size()));
#endif
std::this_thread::sleep_for(std::chrono::milliseconds(500)); // 0.5s
DownloadThread* splitable_segment = nullptr;
auto it = download_threads_.begin();
while (it != download_threads_.end())
{
DownloadThread* thread = *it;
if (thread->status_ == Status::FINISH)
{
delete thread;
it = download_threads_.erase(it);
}
else
{
switch (thread->status_)
{
case Status::RUNNING:
default:
recorder_->MarkFinish(thread->begin_, thread->pos_ - 1);
// part of this segment may have been splited to other download thread
//thread->end_ = recorder_->GetSegmentEnd(thread->pos_);
break;
case Status::SUCCESSFUL:
if (recorder_->IsInitialized())
{
thread->CloseFile();
recorder_->MarkFinish(thread->begin_, thread->pos_ - 1);
}
else
{
if (!PrepareFile_(*it))
status_ = Status::ERRONEOUS;
}
splitable_segment = *it;
break;
case Status::ERRONEOUS:
if (++retry_ > kMaxHttpRetry)
{
status_ = Status::ERRONEOUS;
}
else
{
thread->CloseFile();
recorder_->MarkFailed(thread->pos_, thread->end_);
splitable_segment = *it;
}
break;
}
it++;
}
}
// break out if error occured
if (status_ == Status::ERRONEOUS)
{
break;
}
// if download completed
if (recorder_->IsFinish())
{
status_ = Status::SUCCESSFUL;
break;
}
// calculate download speed every 1 second
if ((loop_count & 1) == 1)
{
auto bytes = recorder_->GetDownloadedBytes();
bytes_per_second_ = bytes - prev_downloaded_bytes;
prev_downloaded_bytes = bytes;
}
// save progress info every 2 seconds
if ((loop_count & 3) == 3)
{
recorder_->Save();
}
// split download when any thread is available or every 4 seconds
if (splitable_segment || (loop_count & 7) == 7)
{
if (splitable_segment != nullptr)
SplitDownload_(splitable_segment);
else if (download_threads_.size() < max_threads_)
SplitDownload_(nullptr);
}
}
master_thread_.detach();
status_ = status_ != Status::ERRONEOUS ? Status::SUCCESSFUL : status_;
}
This is how M start D thread:
bool HttpDownloader1::SplitDownload_(DownloadThread* thread)
{
if (!recorder_->IsInitialized())
{
if (!thread)
thread = CreateDownloadThread_();
thread->begin_ = 0;
thread->end_ = 0;
}
else
{
int64_t begin, end;
if (recorder_->GetTask(&begin, &end))
{
// initialize this segment
if (!thread)
thread = CreateDownloadThread_();
thread->begin_ = begin;
thread->end_ = end;
thread->pos_ = thread->begin_;
if (thread->file_ == nullptr)
{
//errno_t e = fopen_s(&thread->file_, target_.GetPath().c_str(), "rb+");
thread->file_ = fopen(target_.GetPath().c_str(), "rb+");
//if (e == 0 && thread->file_)
if (thread->file_)
{
fseek(thread->file_, (long)thread->begin_, SEEK_SET);
}
else
{
thread->status_ = Status::ERRONEOUS;
return false;
}
}
}
else
{
// no more segment to download or split, remove this thread if it exists.
if (thread)
thread->status_ = Status::FINISH;
}
}
if (thread && thread->status_ != Status::FINISH)
{
thread->status_ = Status::RUNNING;
thread->thread_ = std::thread(&HttpDownloader1::DownloadThreadFunc_, this, thread);
thread->thread_.detach();
}
return true;
}
This is the D thread entrance:
void HttpDownloader1::DownloadThreadFunc_(DownloadThread* thread)
{
cpr::Response rsp;
if (thread->file_ == nullptr)
{
rsp = cpr::Get(
cpr::Url(target_.url_.c_str()),
cpr::ConnectTimeout(std::chrono::seconds(kConnectionTimeout)),
cpr::Timeout(std::chrono::seconds(kTransmitTimeout)),
cpr::VerifySsl(false),
cpr::Header{ { "Range", thread->GetRangeHeaderString().c_str() } },
cpr::CurlOption({ CURLOPT_NOPROGRESS, 1 }),
cpr::CurlOption({ CURLOPT_WRITEDATA, nullptr }),
cpr::CurlOption({ CURLOPT_WRITEFUNCTION, &HttpDownloader1::WriteCallback_ })
);
}
else
{
rsp = cpr::Get(
cpr::Url(target_.url_.c_str()),
cpr::ConnectTimeout(std::chrono::seconds(kConnectionTimeout)),
cpr::Timeout(std::chrono::seconds(kTransmitTimeout)),
cpr::VerifySsl(false),
cpr::Header{ { "Range", thread->GetRangeHeaderString().c_str() } },
cpr::CurlOption({ CURLOPT_NOPROGRESS, 0 }),
cpr::CurlOption({ CURLOPT_XFERINFODATA, thread }),
cpr::CurlOption({ CURLOPT_XFERINFOFUNCTION, &HttpDownloader1::ProgressCallback_ }),
cpr::CurlOption({ CURLOPT_WRITEDATA, thread->file_ }),
cpr::CurlOption({ CURLOPT_WRITEFUNCTION, fwrite })
);
}
if (rsp.status_code == 0)
{
thread->status_ = Status::ERRONEOUS;
Log_("thread:" + std::to_string(thread->id_) + " error: HTTP status code 0");
}
else if (rsp.status_code >= 400)
{
thread->status_ = Status::ERRONEOUS;
Log_("thread:" + std::to_string(thread->id_) + " erorr: HTTP status code " + std::to_string(rsp.status_code));
}
else if (rsp.error.code != cpr::ErrorCode::OK)
{
thread->status_ = Status::ERRONEOUS;
Log_("thread:" + std::to_string(thread->id_) + "error: " + rsp.error.message);
}
else
{
if (thread->file_ == nullptr)
thread->response_header_ = rsp.header;
thread->status_ = Status::SUCCESSFUL;
}
}
This is libcurl's progress callback:
int HttpDownloader1::ProgressCallback_(
void* clientp, std::uint64_t dltotal, std::uint64_t dlnow, std::uint64_t ultotal, std::uint64_t ulnow
) {
auto thread = (DownloadThread*)clientp;
if (dlnow > 0)
{
thread->pos_ = thread->begin_ + dlnow;
if (thread->pos_ > thread->end_)
return 1;
}
return 0;
}
Answer: Because I made a stupid mistake...
Details:
The M thread didn't got stuck(halt abnormally), it's just in a dead loop.
The loop isn't in the main loop of M, but inside the recorder_->MarkFinish(...) call, where I forgot incrementing the iterator at one of the if branch when loop a list container by while(...) {...}.
So when I placed a breakpoint at the recorder_->MarkFinish() line, it wouldn't be caught.
How I found that eventually:
I don't have much experience at multiple thread programming. After scratching my head for nearly a whole day, I thought there must be some low level details unfamiliar to me. So I post this question.
#Raymond comments brought back my confidence. I begun to add debug outputs line by line inside the M thread's main loop trying to find out what it was doing when it seemed stuck. And noticed that each time the error occurred, the outputs would disappear right after that recorder_->MarkFinish(...) call. So I went into that function and caught the carelessly code bellow:
while (it != segments_.end())
{
Segment& seg = *it;
if (seg.begin_ > end) break;
if (seg.end_ < start) continue; // οΌ
if (start <= seg.begin_ && seg.begin_ <= end)
seg.begin_ = end + 1;
if (start <= seg.end_ && seg.end_ <= end)
seg.end_ = start - 1;
if (seg.end_ < seg.begin_)
it = segments_.erase(it);
else
it++;
}
Hope this might help reducing some of the mysterious sense about the problem you meet.π
In C++ I am running a bash command. The command is "echo | openssl s_client -connect zellowork.io:443"
But if this fails I want it to timeout in 4 seconds. The typical "/usr/bin/timeout 4 /usr/bin/sh -c" before the command does not work when run from the c++ code.
So I was trying to make a function that uses popen to send out the command and then waits for up to 4 seconds for the command to complete before it returns. The difficulty that I have is that fgets is blocking and it will wait for 20 seconds (on this command) before it unblocks and fails and I can not find anyway to see if there is something to read in a stream before I call fgets. Here is my code.
ExecuteCmdReturn Utils::executeCmdWithTimeout(string cmd, int ms)
{
ExecuteCmdReturn ecr;
ecr.success = false;
ecr.outstr = "";
FILE *in;
char buff[4096];
u64_t startTime = TWTime::ticksSinceStart();
u64_t stopTime = startTime + ms;
if(!(in = popen(cmd.c_str(), "r"))){
return ecr;
}
fseek(in,0,SEEK_SET);
stringstream ss("");
long int lastPos = 0;
long int newPos = 0;
while (TWTime::ticksSinceStart() < stopTime) {
newPos = ftell(in);
if (newPos > lastPos) {
lastPos = newPos;
if (fgets(buff, sizeof(buff), in) == NULL) {
break;
} else {
ss << buff;
}
} else {
msSleep(10);
}
}
auto rc = pclose(in);
ecr.success = true;
ecr.outstr = ss.str();
return ecr;
}
Use std::async to express that you may get your result asynchronously (a std::future<ExecuteCmdReturn>)
Use std::future<T>::wait_for to timeout waiting for the result.
Here's an example:
First, a surrogate for your executeCmdWithTimeout function that randomly sleeps between 0 and 5 seconds.
int do_something_silly()
{
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> distribution(0, 5);
auto sleep_time = std::chrono::seconds(distribution(gen));
std::cout << "Sleeping for " << sleep_time.count() << " seconds\n";
std::this_thread::sleep_for(sleep_time);
return 42;
}
Then, launching the task asynchronously and timing out on it:
int main()
{
auto silly_result = std::async(std::launch::async, [](){ return do_something_silly();});
auto future_status = silly_result.wait_for(3s);
switch(future_status)
{
case std::future_status::timeout:
std::cout << "timed out\n";
break;
case std::future_status::ready:
std::cout << "finished. Result is " << silly_result.get() << std::endl;
break;
case std::future_status::deferred:
std::cout << "The function hasn't even started yet.\n";
}
}
I used a lambda here even though I didn't need to because in your situation it will be easier because it looks like you are using a member function and you'll want to capture [this].
Live Demo
In your case, main would become ExecuteCmdReturn Utils::executeCmdWithTimeout(string cmd, int ms) and do_something_silly would become a private helper, named something like executeCmdWithTimeout_impl.
If you timeout waiting for the process to complete, you optionally kill the process so that you aren't wasting any extra cycles.
If you find yourself creating many short-lived threads like this, consider thread pooling. I've had a lot of success with boost::thread_pool (and if you end up going that direction, consider using Boost.Process for handling your process creation).
I am using the Windows api Gatt Client BLE for C++, my goal is to connect two devices (but in this case I will try just one) and keep reading and writing data constantly without closing the device at any time. All my devices have one specific service that contains a read characteristic and a write one.
HOW TO TEST:
Use Visual studio 2017 (v141) with Windows SDK Version: 10.0.18362.0, create a new console (.exe) solution, change the Platform in Project -> Properties to Win32 and go to Project -> Properties -> C/C++ -> Command Line and add these options:
/std:c++17 /await
Then copy the following code in a file (you can copy all in the same .cpp file):
#pragma once
#include <SDKDDKVer.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <iostream>
#include <queue>
#include <map>
#include <mutex>
#include <condition_variable>
#include <string>
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.Web.Syndication.h>
#include "winrt/Windows.Devices.Bluetooth.h"
#include "winrt/Windows.Devices.Bluetooth.GenericAttributeProfile.h"
#include "winrt/Windows.Devices.Enumeration.h"
#include "winrt/Windows.Storage.Streams.h"
#pragma comment(lib, "windowsapp")
using namespace std;
using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::Web::Syndication;
using namespace Windows::Devices::Bluetooth;
using namespace Windows::Devices::Bluetooth::GenericAttributeProfile;
using namespace Windows::Devices::Enumeration;
using namespace Windows::Storage::Streams;
#pragma region STRUCS AND ENUMS
#define LOG_ERROR(e) cout << e << endl;
union to_guid
{
uint8_t buf[16];
guid guid;
};
const uint8_t BYTE_ORDER[] = { 3, 2, 1, 0, 5, 4, 7, 6, 8, 9, 10, 11, 12, 13, 14, 15 };
guid make_guid(const wchar_t* value)
{
to_guid to_guid;
memset(&to_guid, 0, sizeof(to_guid));
int offset = 0;
for (unsigned int i = 0; i < wcslen(value); i++) {
if (value[i] >= '0' && value[i] <= '9')
{
uint8_t digit = value[i] - '0';
to_guid.buf[BYTE_ORDER[offset / 2]] += offset % 2 == 0 ? digit << 4 : digit;
offset++;
}
else if (value[i] >= 'A' && value[i] <= 'F')
{
uint8_t digit = 10 + value[i] - 'A';
to_guid.buf[BYTE_ORDER[offset / 2]] += offset % 2 == 0 ? digit << 4 : digit;
offset++;
}
else if (value[i] >= 'a' && value[i] <= 'f')
{
uint8_t digit = 10 + value[i] - 'a';
to_guid.buf[BYTE_ORDER[offset / 2]] += offset % 2 == 0 ? digit << 4 : digit;
offset++;
}
else
{
// skip char
}
}
return to_guid.guid;
}
mutex subscribeLock;
condition_variable subscribeSignal;
mutex _mutexWrite;
condition_variable signalWrite;
struct DeviceCacheEntry {
BluetoothLEDevice device = nullptr;
GattDeviceService service = nullptr;
GattCharacteristic characteristic = nullptr;
};
map<wstring, DeviceCacheEntry> cache;
struct Subscription {
GattCharacteristic::ValueChanged_revoker revoker;
};
struct BLEDeviceData {
wstring id;
wstring name;
bool isConnectable = false;
Subscription* subscription = NULL;
};
vector<BLEDeviceData> deviceList{};
mutex deviceListLock;
condition_variable deviceListSignal;
#pragma endregion
#pragma region CACHE FUNCTIONS
//Call this function to get a device from cache or async if it wasn't found
IAsyncOperation<BluetoothLEDevice> getDevice(wchar_t* deviceId) {
if (cache.count(wstring(deviceId)) && cache[wstring(deviceId)].device)
co_return cache[wstring(deviceId)].device;
BluetoothLEDevice result = co_await BluetoothLEDevice::FromIdAsync(deviceId);
if (result == nullptr) {
LOG_ERROR("Failed to connect to device.")
co_return nullptr;
}
else {
DeviceCacheEntry d;
d.device = result;
if (!cache.count(wstring(deviceId))) {
cache.insert({ wstring(deviceId), d });
}
else {
cache[wstring(deviceId)] = d;
}
co_return cache[wstring(deviceId)].device;
}
}
//Call this function to get a service from cache or async if it wasn't found
IAsyncOperation<GattDeviceService> getService(wchar_t* deviceId, wchar_t* serviceId) {
if (cache.count(wstring(deviceId)) && cache[wstring(deviceId)].service)
co_return cache[wstring(deviceId)].service;
auto device = co_await getDevice(deviceId);
if (device == nullptr)
co_return nullptr;
GattDeviceServicesResult result = co_await device.GetGattServicesForUuidAsync(make_guid(serviceId), BluetoothCacheMode::Cached);
if (result.Status() != GattCommunicationStatus::Success) {
LOG_ERROR("Failed getting services. Status: " << (int)result.Status())
co_return nullptr;
}
else if (result.Services().Size() == 0) {
LOG_ERROR("No service found with uuid")
co_return nullptr;
}
else {
if (cache.count(wstring(deviceId))) {
cache[wstring(deviceId)].service = result.Services().GetAt(0);
}
co_return cache[wstring(deviceId)].service;
}
}
//Call this function to get a characteristic from cache or async if it wasn't found
IAsyncOperation<GattCharacteristic> getCharacteristic(wchar_t* deviceId, wchar_t* serviceId, wchar_t* characteristicId) {
try {
if (cache.count(wstring(deviceId)) && cache[wstring(deviceId)].characteristic)
co_return cache[wstring(deviceId)].characteristic;
auto service = co_await getService(deviceId, serviceId);
if (service == nullptr)
co_return nullptr;
GattCharacteristicsResult result = co_await service.GetCharacteristicsForUuidAsync(make_guid(characteristicId), BluetoothCacheMode::Cached);
if (result.Status() != GattCommunicationStatus::Success) {
LOG_ERROR("Error scanning characteristics from service. Status: " << (int)result.Status())
co_return nullptr;
}
else if (result.Characteristics().Size() == 0) {
LOG_ERROR("No characteristic found with uuid")
co_return nullptr;
}
else {
if (cache.count(wstring(deviceId))) {
cache[wstring(deviceId)].characteristic = result.Characteristics().GetAt(0);
}
co_return cache[wstring(deviceId)].characteristic;
}
}
catch (...) {
LOG_ERROR("Exception while trying to get characteristic")
}
}
#pragma endregion
#pragma region SCAN DEVICES FUNCTIONS
DeviceWatcher deviceWatcher{ nullptr };
mutex deviceWatcherLock;
DeviceWatcher::Added_revoker deviceWatcherAddedRevoker;
DeviceWatcher::Updated_revoker deviceWatcherUpdatedRevoker;
DeviceWatcher::Removed_revoker deviceWatcherRemovedRevoker;
DeviceWatcher::EnumerationCompleted_revoker deviceWatcherCompletedRevoker;
struct TestBLE {
static void ScanDevices();
static void StopDeviceScan();
};
//This function would be called when a new BLE device is detected
void DeviceWatcher_Added(DeviceWatcher sender, DeviceInformation deviceInfo) {
BLEDeviceData deviceData;
deviceData.id = wstring(deviceInfo.Id().c_str());
deviceData.name = wstring(deviceInfo.Name().c_str());
if (deviceInfo.Properties().HasKey(L"System.Devices.Aep.Bluetooth.Le.IsConnectable")) {
deviceData.isConnectable = unbox_value<bool>(deviceInfo.Properties().Lookup(L"System.Devices.Aep.Bluetooth.Le.IsConnectable"));
}
deviceList.push_back(deviceData);
}
//This function would be called when an existing BLE device is updated
void DeviceWatcher_Updated(DeviceWatcher sender, DeviceInformationUpdate deviceInfoUpdate) {
wstring deviceData = wstring(deviceInfoUpdate.Id().c_str());
for (int i = 0; i < deviceList.size(); i++) {
if (deviceList[i].id == deviceData) {
if (deviceInfoUpdate.Properties().HasKey(L"System.Devices.Aep.Bluetooth.Le.IsConnectable")) {
deviceList[i].isConnectable = unbox_value<bool>(deviceInfoUpdate.Properties().Lookup(L"System.Devices.Aep.Bluetooth.Le.IsConnectable"));
}
break;
}
}
}
void DeviceWatcher_Removed(DeviceWatcher sender, DeviceInformationUpdate deviceInfoUpdate) {
}
void DeviceWatcher_EnumerationCompleted(DeviceWatcher sender, IInspectable const&) {
TestBLE::StopDeviceScan();
TestBLE::ScanDevices();
}
//Call this function to scan async all BLE devices
void TestBLE::ScanDevices() {
try {
lock_guard lock(deviceWatcherLock);
IVector<hstring> requestedProperties = single_threaded_vector<hstring>({ L"System.Devices.Aep.DeviceAddress", L"System.Devices.Aep.IsConnected", L"System.Devices.Aep.Bluetooth.Le.IsConnectable" });
hstring aqsFilter = L"(System.Devices.Aep.ProtocolId:=\"{bb7bb05e-5972-42b5-94fc-76eaa7084d49}\")"; // list Bluetooth LE devices
deviceWatcher = DeviceInformation::CreateWatcher(aqsFilter, requestedProperties, DeviceInformationKind::AssociationEndpoint);
deviceWatcherAddedRevoker = deviceWatcher.Added(auto_revoke, &DeviceWatcher_Added);
deviceWatcherUpdatedRevoker = deviceWatcher.Updated(auto_revoke, &DeviceWatcher_Updated);
deviceWatcherRemovedRevoker = deviceWatcher.Removed(auto_revoke, &DeviceWatcher_Removed);
deviceWatcherCompletedRevoker = deviceWatcher.EnumerationCompleted(auto_revoke, &DeviceWatcher_EnumerationCompleted);
deviceWatcher.Start();
}
catch (exception e) {
LOG_ERROR(e.what())
}
}
void TestBLE::StopDeviceScan() {
scoped_lock lock(deviceListLock, deviceWatcherLock);
if (deviceWatcher != nullptr) {
deviceWatcherAddedRevoker.revoke();
deviceWatcherUpdatedRevoker.revoke();
deviceWatcherRemovedRevoker.revoke();
deviceWatcherCompletedRevoker.revoke();
deviceWatcher.Stop();
deviceWatcher = nullptr;
}
deviceListSignal.notify_one();
}
#pragma endregion
#pragma region SUBSCRIBE/READ FUNCTIONS
//On this function you can read all data from the specified characteristic
void Characteristic_ValueChanged(GattCharacteristic const& characteristic, GattValueChangedEventArgs args)
{
LOG_ERROR("Read data from device: " << to_string(characteristic.Service().Device().DeviceId()) << ", data size: " << args.CharacteristicValue().Length())
}
//Function used to subscribe async to the specific device
fire_and_forget SubscribeCharacteristicAsync(wstring deviceId, wstring serviceId, wstring characteristicId, bool* result) {
try {
auto characteristic = co_await getCharacteristic(&deviceId[0], &serviceId[0], &characteristicId[0]);
if (characteristic != nullptr) {
auto status = co_await characteristic.WriteClientCharacteristicConfigurationDescriptorAsync(GattClientCharacteristicConfigurationDescriptorValue::Notify);
if (status != GattCommunicationStatus::Success) {
LOG_ERROR("Error subscribing to characteristic. Status: " << (int)status)
}
else {
for (int i = 0; i < deviceList.size(); i++) {
if (deviceList[i].id == deviceId) {
deviceList[i].subscription = new Subscription();
deviceList[i].subscription->revoker = characteristic.ValueChanged(auto_revoke, &Characteristic_ValueChanged);
break;
}
}
if (result != 0)
*result = true;
}
}
}
catch (hresult_error& ex)
{
LOG_ERROR("SubscribeCharacteristicAsync error: " << to_string(ex.message().c_str()))
for (int i = 0; i < deviceList.size(); i++) {
if (deviceList[i].id == deviceId && deviceList[i].subscription) {
delete deviceList[i].subscription;
deviceList[i].subscription = NULL;
break;
}
}
}
subscribeSignal.notify_one();
}
//Call this function to subscribe to the specific device so you can read data from it
bool SubscribeCharacteristic(wstring deviceId, wstring serviceId, wstring characteristicId) {
unique_lock<mutex> lock(subscribeLock);
bool result = false;
SubscribeCharacteristicAsync(deviceId, serviceId, characteristicId, &result);
subscribeSignal.wait(lock);
return result;
}
#pragma endregion
#pragma region WRITE FUNCTIONS
//Function used to send data async to the specific device
fire_and_forget SendDataAsync(wchar_t* deviceId, wchar_t* serviceId, wchar_t* characteristicId, uint8_t * data, uint16_t size, bool* result) {
try {
auto characteristic = co_await getCharacteristic(deviceId, serviceId, characteristicId);
if (characteristic != nullptr) {
DataWriter writer;
writer.WriteBytes(array_view<uint8_t const>(data, data + size));
IBuffer buffer = writer.DetachBuffer();
auto status = co_await characteristic.WriteValueAsync(buffer, GattWriteOption::WriteWithoutResponse);
if (status != GattCommunicationStatus::Success) {
LOG_ERROR("Error writing value to characteristic. Status: " << (int)status)
}
else if (result != 0) {
LOG_ERROR("Data written succesfully")
*result = true;
}
}
}
catch (hresult_error& ex)
{
LOG_ERROR("SendDataAsync error: " << to_string(ex.message().c_str()))
for (int i = 0; i < deviceList.size(); i++) {
if (deviceList[i].id == deviceId && deviceList[i].subscription) {
delete deviceList[i].subscription;
deviceList[i].subscription = NULL;
break;
}
}
}
signalWrite.notify_one();
}
//Call this function to write data on the device
bool SendData(wchar_t* deviceId, wchar_t* serviceId, wchar_t* characteristicId, uint8_t * data, uint16_t size) {
bool result = false;
unique_lock<mutex> lock(_mutexWrite);
// copy data to stack so that caller can free its memory in non-blocking mode
SendDataAsync(deviceId, serviceId, characteristicId, data, size, &result);
signalWrite.wait(lock);
return result;
}
#pragma endregion
Finally copy this main function (it can be copied at the end of the same file):
int main() {
//The mac of the device that will be tested
wstring deviceMac = L"00:11:22:33:44:55";
//These are the serviceUUID, readCharacteristicUUID and writeCharacteristicUUID as I said previously
wstring serviceUUID = L"{47918888-5555-2222-1111-000000000000}";
wstring readUUID = L"{31a28888-5555-2222-1111-00000000cede}";
wstring writeUUID = L"{f55a8888-5555-222-1111-00000000957a}";
//I think it is the mac of the BLE USB Dongle because it is in all device id when they are enumerated
wstring otherMac = L"24:4b:fe:3a:1a:ba";
//The device Id that we are looking for
wstring deviceId = L"BluetoothLE#BluetoothLE" + otherMac;
deviceId += L"-";
deviceId += deviceMac;
//To start scanning just call this function
TestBLE::ScanDevices();
//Data to be written all the time
const uint16_t dataSize = 3;
uint8_t data [dataSize]= { 0x0, 0xff, 0xff };
//Wait time in miliseconds between each write
chrono::milliseconds waitTime = 100ms;
//It will be executed always
while (true) {
//Then every device and their info updated would be in this vector
for (int i = 0; i < deviceList.size(); i++) {
//If the device is connectable we will try to connect if we aren't subscribed yet or send information
if (deviceList[i].isConnectable) {
//We can do here the following code to know the structure of the device id (if otherMac variable is the BLE USB dongle mac or not)
//cout << to_string(deviceList[i].id) << endl;
if (!deviceList[i].subscription && deviceList[i].id == deviceId) {
SubscribeCharacteristic(deviceList[i].id, serviceUUID, readUUID);
}
else if (deviceList[i].subscription) {
SendData(&deviceId[0], &serviceUUID[0], &writeUUID[0], data, dataSize);
}
}
}
this_thread::sleep_for(waitTime);
}
}
You will need a BLE device with a service that contains a reading and a writing characteristic, set the corresponding values ββin the deviceMac, serviceUUID, readUUID and writeUUID variables, you can also modify the bytes that are going to be written in data and dataSize, and the time between writes in waitTime. The otherMac variable should be the mac of the BLE USB dongle device but I recommend that you check it by getting the id of the devices from deviceList inside the for loop.
When you run this code on some rare times you will get the error "Failed getting services. Status:" with result 1 (unreachable) or 3 (access denied) and in the rest of the cases it will be reading the device data correctly and after a while it will give the error "SendDataAsync error: Object has been disposed" and from there it will continue giving "SubscribeCharacteristicAsync error: Object has been disposed", so at some point it will stop being able to read data of the device. What could be the reason?
EDIT 1:
It is quite strange because with this code the data is never written correctly (the "Data written succesfully" message is not displayed) but in my completed code I have always been able to write the data, maybe the problem is still the same and it is related to the characteristic stored in the "map <wstring, DeviceCacheEntry> cache" since perhaps it is stored as a copy and when trying to access it at some point it is disposed by Windows (since it is a copy of the original that is stored in the cache) and gives the error as described in the answer to this post in the point named "UPDATE 2 - SOME WEIRDNESS"
I'm using pjsip high-level api PJSUA for doing multiple calls at one time. My sip operator support up to 3 outgoing lines for calls, so in theory it should be possible to call 3 persons at one time. The problem is, that in the operator logs i see, the message "Line limit overreached" and in pjsip, the call fails. I'm doing it like this:
Here is my class that contains methods for calling:
void Sip::InitLibrary()
{
pj::EpConfig ep_cfg;
ep_cfg.logConfig.level = 4;
ep_cfg.logConfig.consoleLevel = 4;
ep_cfg.logConfig.filename = this->log_;
ep_cfg.uaConfig.userAgent = "Ekiga";
this->ep_.libCreate();
this->ep_.libInit(ep_cfg);
}
void Sip::SetSipTransport(Sip::SipTransport transport, unsigned port)
{
TransportConfig tcfg;
tcfg.port = port;
pjsip_transport_type_e type;
switch (transport)
{
case SipTransport::TCP:
type = PJSIP_TRANSPORT_TCP;
break;
case SipTransport::UDP:
type = PJSIP_TRANSPORT_UDP;
break;
}
try {
this->transportId_ = this->ep_.transportCreate(type, tcfg);
}
catch (Error &err) {
//to log
return;
}
}
void Sip::StartLibrary()
{
this->ep_.libStart();
}
AccountInfo Sip::ConnectSipServer(string serv, string login, string pass,SipTransport transport,int port)
{
this->InitLibrary();
this->SetSipTransport(transport, port);
this->StartLibrary();
this->server_ = serv;
AccountConfig accConfig;
string uri = "<sip:" + login + "#" + serv +">";
accConfig.idUri = uri;
string regUri = "sip:" + serv;
accConfig.regConfig.registrarUri = regUri;
accConfig.sipConfig.authCreds.push_back(AuthCredInfo("digest", "*", login, 0, pass));
this->acc_ = new SipAccount();
acc_->create(accConfig, true);
{
unique_lock<mutex> locker(acc_->regLock_);
while (!acc_->regEnd_)
acc_->regDone_.wait(locker);
}
return acc_->info_;
}
bool Sip::CallNumber(string phone)
{
Call *call = new SipCall(*this->acc_);
CallOpParam prm(true);
prm.opt.audioCount = 1;
prm.opt.videoCount = 0;
call->makeCall("sip:" + phone + "#" + this->server_, prm);
SipCall *myCall = (SipCall*)call;
{
unique_lock<mutex> locker(myCall->callLock);
while (!myCall->callEnd)
myCall->callDone.wait(locker);
}
if (myCall->validPhone)
{
delete myCall;
return true;
}
delete myCall;
return false;
}
SipCall.h
SipCall(Account &acc, int call_id = PJSUA_INVALID_ID)
: Call(acc, call_id)
{
Acc = (SipAccount *)&acc;
this->callEnd = false;
this->validPhone = false;
}
~SipCall();
virtual void onCallState(OnCallStateParam &prm);
condition_variable callDone;
mutex callLock;
bool callEnd;
bool validPhone;
SipCall.cpp
void SipCall::onCallState(OnCallStateParam &prm)
{
PJ_UNUSED_ARG(prm);
CallInfo ci = getInfo();
std::cout << "*** Call: " << ci.remoteUri << " [" << ci.stateText
<< "]" << std::endl;
if (ci.state == PJSIP_INV_STATE_DISCONNECTED) {
{
unique_lock<mutex> locker(this->callLock);
this->callEnd = true;
this->callDone.notify_one();
}
}
if (ci.state == PJSIP_INV_STATE_CONFIRMED && ci.lastStatusCode == PJSIP_SC_OK)
{ //here i check that phone is valid, and just hang up the call
CallOpParam p;
this->validPhone = true;
this->hangup(p);
}
}
SipCall::~SipCall()
{
}
Since there a 3 lines avaliable im creating a three tasks to call the corresponding numbers like so:
QStringList testCalls; // initialized with numbers
struct call_result{
QString phone;
bool valid;
};
//....
QList<QFuture<call_result>> futureTestList;
while(counter < testCalls.count()) {
for(int i= 0; i < 3; i++,counter++) {
if(counter >= testCalls.count())
break;
QString number = testCalls.at(counter);
QFuture<call_result> futureResult = QtConcurrent::run(this,&sipQtThread::callNumber,number);
futureTestList.append(futureResult);
}
foreach(QFuture<call_result> future,futureTestList) {
future.waitForFinished();
call_result call = future.result();
emit phonePassed(call.phone,call.valid);
}
futureTestList.clear();
}
The correspondig task sipQtThread::callNumber is following:
sipQtThread::call_result sipQtThread::callNumber(QString phone)
{
call_result call;
pj_thread_desc initdec;
pj_thread_t* thread = 0;
pj_status_t status;
status = pj_thread_register(NULL, initdec, &thread);
if (status != PJ_SUCCESS) {
qDebug()<<"pj_thread_register faild:"<<status;
return call;
}
bool result = sip_->CallNumber(phone.toStdString());
call.phone = phone;
call.valid = result;
return call;
}
As you can see here
if (ci.state == PJSIP_INV_STATE_CONFIRMED && ci.lastStatusCode == PJSIP_SC_OK)
{ //here i check that phone is valid, and just hang up the call
CallOpParam p;
this->validPhone = true;
this->hangup(p);
}
I just check if the phone is valid, and call is starting, and the drop it. Can pjsua2 handle multiple line calls? Or even is pjsip supporting multiple outgoing lines calls? Does the thread registered by pj_thread_register needs to be somehow unregistered maybe?
I use the cassandra-c++-driver to write 100000 rows in a 100-columns table like this:
#include <cstdlib>
#include <stdio.h>
#include <cassandra.h>
#include <string>
#include <iostream>
#include <random>
#include <chrono>
#include <unistd.h>
#include <thread>
CassFuture *connect_future = NULL;
CassCluster *cluster = NULL;
CassSession *session = NULL;
std::random_device rd;
std::mt19937_64 gen(rd());
std::uniform_int_distribution<unsigned long long> dis;
int COLUMNS_COUNT = 100;
using namespace std;
void insertQ() {
auto t1 = std::chrono::high_resolution_clock::now();
for (int row = 0; row < 10000; ++row) {
string columns;
for (int i = 0; i < COLUMNS_COUNT; ++i) {
columns += "name" + to_string(i) + " , ";
}
string result = "INSERT INTO mykeyspace.users2 (user_id,";
result += columns;
result += "lname) VALUES (";
string values = to_string(dis(gen) % 50000000) + ",";
for (int i = 0; i < COLUMNS_COUNT; ++i) {
values += "'name" + to_string(dis(gen)) + "' , ";
}
values += " 'lname" + to_string(dis(gen) % 20) + "'";
result += values;
result += ");";
CassStatement *statement = cass_statement_new(result.c_str(), 0);
CassFuture *result_future = cass_session_execute(session, statement);
cass_future_wait(result_future);
if (cass_future_error_code(result_future) == CASS_OK) {
// cout << "insert ok" << endl;
}
else {
const char *message;
size_t message_length;
cass_future_error_message(result_future, &message, &message_length);
fprintf(stderr, "Unable to run query: '%.*s'\n", (int) message_length,
message);
cerr << "index : " << row << endl;
}
cass_statement_free(statement);
cass_future_free(result_future);
if (row % 1000 == 0)
{
// usleep(1000000);
// std::this_thread::sleep_for(std::chrono::seconds(1));
// cass_se
}
}
auto t2 = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1);
cout << "duration: " << duration.count() << endl;
}
int main() {
/* Setup and connect to cluster */
connect_future = NULL;
cluster = cass_cluster_new();
session = cass_session_new();
/* Add contact points */
// cass_cluster_set_contact_points(cluster, "127.0.0.1,127.0.0.2,127.0.0.3");
cass_cluster_set_contact_points(cluster, "127.0.0.1");
/* Provide the cluster object as configuration to connect the session */
connect_future = cass_session_connect(session, cluster);
if (cass_future_error_code(connect_future) == CASS_OK) {
CassFuture *close_future = NULL;
insertQ();
/* Close the session */
close_future = cass_session_close(session);
cass_future_wait(close_future);
cass_future_free(close_future);
} else {
/* Handle error */
const char *message;
size_t message_length;
cass_future_error_message(connect_future, &message, &message_length);
fprintf(stderr, "Unable to connect: '%.*s'\n", (int) message_length,
message);
}
cass_future_free(connect_future);
cass_cluster_free(cluster);
cass_session_free(session);
return 0;
}
its works and writes about 90000 rows and then falls with this error:
index : 91627
Unable to run query: 'Operation timed out - received only 0 responses.'
..
and continues, I can execute 'SELECT' queries but after this 'INSERT's fails. unitl I restart the cassandra servcice.
Whats the problem?
My system: Ubuntu 14.04 x64, 8 gig ram, cassandra 2.1.4 (from cassandra debian repositories with default configrations)
thanks.
This error is coming back from Cassandra. It indicates that less than the amount of replicas required responded to your read/write request within the period of time configured in cassandra. Since you are not specifying a consistency level, all that is required is that one node responds and it isn't within the write timeout. The most relevant configurations to look at in cassandra.yaml are:
write_request_timeout_in_ms (default 2000ms)
read_request_timeout_in_ms (default: 5000ms)
range_request_timeout_in_ms (default: 10000ms)
Since you are doing inserts, write_request_timeout_in_ms is probably the most relevant configuration.
What's likely happening is that you are overwhelming your cassandra cluster. Have you looked a cpu utilization/disk io/memory utilization on the server while running your test?
The interesting thing is that your code only ever does 1 INSERT at a time, is this correct? I would expect that this should be fine, but maybe what is happening is that this is putting intense pressure on your memory heap in cassandra and it can't flush data fast enough, so it becomes backed up while writing to disk. You should take a look at your cassandra system.log (usually in /var/log/cassandra if on linux) and see if there are any suspicious messages about long garbage collections (look for GCInspector) or memtable pressure.