Skip to content

Commit 0d59dcf

Browse files
author
hpcdgrie
committed
Added dummy client that creates gui sliders for observed variables
1 parent 0360fa9 commit 0d59dcf

3 files changed

Lines changed: 284 additions & 0 deletions

File tree

src/OpenCOVER/DataClient/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11

22
SET(HEADERS
33
DataClient.h
4+
DummyClient.h
45
MultiDimensionalArray.h
56
ObserverHandle.h
67
)
78

89
SET(SOURCES
910
DataClient.cpp
11+
DummyClient.cpp
1012
MultiDimensionalArray.cpp
1113
ObserverHandle.cpp
1214
)
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
#include "DummyClient.h"
2+
#include <cover/coVRPluginSupport.h>
3+
4+
#include <iostream>
5+
#include <algorithm>
6+
7+
namespace opencover { namespace dataclient {
8+
9+
DummyClient::DummyClient(const std::string& name)
10+
: ui::Owner(name, cover->ui)
11+
, m_name(name)
12+
{
13+
// Create the main menu for the dummy client
14+
m_menu = std::make_unique<ui::Menu>(m_name, this);
15+
16+
// Create connect/disconnect button
17+
m_connectButton = std::make_unique<ui::Button>(m_menu.get(), "Connect");
18+
m_connectButton->setState(false);
19+
m_connectButton->setCallback([this](bool state) {
20+
if (state) {
21+
connect();
22+
} else {
23+
disconnect();
24+
}
25+
});
26+
}
27+
28+
DummyClient::~DummyClient()
29+
{
30+
disconnect();
31+
}
32+
33+
void DummyClient::connect()
34+
{
35+
if (m_connected)
36+
return;
37+
38+
m_connected = true;
39+
m_connectButton->setState(true);
40+
m_connectButton->setText("Disconnect");
41+
statusChanged();
42+
std::cout << "DummyClient '" << m_name << "' connected" << std::endl;
43+
}
44+
45+
void DummyClient::disconnect()
46+
{
47+
if (!m_connected)
48+
return;
49+
50+
m_connected = false;
51+
m_connectButton->setState(false);
52+
m_connectButton->setText("Connect");
53+
statusChanged();
54+
std::cout << "DummyClient '" << m_name << "' disconnected" << std::endl;
55+
}
56+
57+
bool DummyClient::isConnected() const
58+
{
59+
return m_connected;
60+
}
61+
62+
ObserverHandle DummyClient::observeNode(const std::string& name)
63+
{
64+
// Check if node already exists
65+
for (auto& pair : m_nodes) {
66+
if (pair.second && pair.second->name == name) {
67+
std::cout << "DummyClient: Node '" << name << "' already observed" << std::endl;
68+
return ObserverHandle(pair.first, this);
69+
}
70+
}
71+
72+
// Create new node with default type (double) and as scalar
73+
size_t nodeId = m_nextNodeId++;
74+
auto node = std::make_unique<DummyNode>();
75+
node->name = name;
76+
77+
// Create slider for this node
78+
createSliderForNode(node.get());
79+
80+
m_nodes[nodeId] = std::move(node);
81+
82+
std::cout << "DummyClient: Observing node '" << name << "' with ID " << nodeId << std::endl;
83+
84+
return ObserverHandle(nodeId, this);
85+
}
86+
87+
void DummyClient::createSliderForNode(DummyNode* node)
88+
{
89+
if (!node || node->slider)
90+
return;
91+
92+
// Create a slider for this node
93+
node->slider = new ui::Slider(m_menu.get(), node->name);
94+
node->slider->setBounds(0.0, 360.0);
95+
96+
// Set callback to update the node value when slider changes
97+
node->slider->setCallback([node](double value, bool released) {
98+
node->values.push_back(value);
99+
});
100+
}
101+
102+
double DummyClient::getNumericScalar(const std::string& name, double* timestep)
103+
{
104+
auto* node = findNode(name);
105+
if (!node) {
106+
std::cerr << "DummyClient: Node '" << name << "' not found" << std::endl;
107+
return 0.0;
108+
}
109+
110+
if (timestep) {
111+
*timestep = 0.0; // Dummy client doesn't track real timestamps
112+
}
113+
auto v = node->values.front();
114+
if(node->values.size() > 1)
115+
node->values.pop_front();
116+
return v;
117+
}
118+
119+
double DummyClient::getNumericScalar(const ObserverHandle& handle, double* timestep)
120+
{
121+
DummyNode *node = nullptr;
122+
for(const auto& pair : m_nodes) {
123+
if (handle == pair.first) {
124+
node = pair.second.get();
125+
break;
126+
}
127+
}
128+
if(!node)
129+
return 0.0;
130+
131+
if (timestep) {
132+
*timestep = 0.0; // Dummy client doesn't track real timestamps
133+
}
134+
auto v = node->values.front();
135+
if(node->values.size() > 1)
136+
node->values.pop_front();
137+
return v;
138+
}
139+
140+
size_t DummyClient::numNodeUpdates(const std::string& name)
141+
{
142+
auto* node = findNode(name);
143+
if (!node) {
144+
return 0;
145+
}
146+
return node->values.size();
147+
}
148+
149+
std::unique_ptr<detail::MultiDimensionalArrayBase>
150+
DummyClient::getArrayImpl(std::type_index type, const std::string& name)
151+
{
152+
if(type != std::type_index(typeid(double)))
153+
return nullptr;
154+
auto node = findNode(name);
155+
if (!node)
156+
return nullptr;
157+
auto value = std::make_unique<dataclient::MultiDimensionalArray<double>>();
158+
value->dimensions = {1};
159+
value->data = {getNumericScalar(name)};
160+
return value;
161+
}
162+
163+
std::vector<std::string> DummyClient::getNodesWith(std::type_index type, bool isScalar) const
164+
{
165+
std::vector<std::string> result;
166+
if(type != std::type_index(typeid(double)))
167+
return result; // Only double type supported in dummy client
168+
for (const auto& pair : m_nodes) {
169+
result.push_back(pair.second->name);
170+
}
171+
return result;
172+
}
173+
174+
std::vector<std::string> DummyClient::getNodesWith(bool isArithmetic, bool isScalar) const
175+
{
176+
std::vector<std::string> result;
177+
if(!isScalar)
178+
return result; // Only scalars supported in dummy client
179+
for (const auto& pair : m_nodes) {
180+
result.push_back(pair.second->name);
181+
}
182+
return result;
183+
}
184+
185+
DummyNode* DummyClient::findNode(const std::string& name)
186+
{
187+
for (auto& pair : m_nodes) {
188+
if (pair.second && pair.second->name == name) {
189+
return pair.second.get();
190+
}
191+
}
192+
return nullptr;
193+
}
194+
195+
DummyNode* DummyClient::findNode(size_t id)
196+
{
197+
auto it = m_nodes.find(id);
198+
if (it != m_nodes.end()) {
199+
return it->second.get();
200+
}
201+
return nullptr;
202+
}
203+
204+
void DummyClient::unregisterNode(size_t id)
205+
{
206+
auto it = m_nodes.find(id);
207+
if (it != m_nodes.end()) {
208+
std::cout << "DummyClient: Unregistering node '" << it->second->name << "'" << std::endl;
209+
m_nodes.erase(it);
210+
}
211+
}
212+
213+
}} // namespace opencover::dataclient
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#ifndef OPENCOVER_DUMMYCLIENT_H
2+
#define OPENCOVER_DUMMYCLIENT_H
3+
4+
#include "DataClient.h"
5+
#include "export.h"
6+
7+
#include <cover/ui/Button.h>
8+
#include <cover/ui/Menu.h>
9+
#include <cover/ui/Slider.h>
10+
#include <cover/ui/Owner.h>
11+
12+
#include <map>
13+
#include <memory>
14+
#include <string>
15+
#include <vector>
16+
17+
namespace opencover { namespace dataclient {
18+
19+
struct DummyNode {
20+
std::string name;
21+
ui::Slider* slider = nullptr;
22+
std::map<size_t, opencover::dataclient::Client**> subscribers;
23+
std::deque<double> values{0.0};
24+
std::set<size_t> updatedSubscribers;
25+
bool operator==(const DummyNode &other) const{return other.name == name;}
26+
bool operator==(const std::string &name) const{return name == this->name;}
27+
};
28+
29+
class DATACLIENTEXPORT DummyClient : public Client , public opencover::ui::Owner{
30+
public:
31+
DummyClient(const std::string& name = "DummyClient");
32+
~DummyClient() override;
33+
34+
// Connection lifecycle - for dummy, these are no-ops
35+
void connect() override;
36+
void disconnect() override;
37+
bool isConnected() const override;
38+
39+
// Register nodes to get updates via UI sliders
40+
[[nodiscard]] ObserverHandle observeNode(const std::string& name) override;
41+
42+
// Pull-style access
43+
double getNumericScalar(const std::string& name, double* timestep = nullptr) override;
44+
double getNumericScalar(const ObserverHandle& handle, double* timestep = nullptr) override;
45+
46+
size_t numNodeUpdates(const std::string& name) override;
47+
48+
private:
49+
std::unique_ptr<detail::MultiDimensionalArrayBase> getArrayImpl(std::type_index type, const std::string& name) override;
50+
std::vector<std::string> getNodesWith(std::type_index type, bool isScalar) const override;
51+
std::vector<std::string> getNodesWith(bool isArithmetic, bool isScalar) const override;
52+
53+
// Helper methods
54+
DummyNode* findNode(const std::string& name);
55+
DummyNode* findNode(size_t id);
56+
void createSliderForNode(DummyNode* node);
57+
void unregisterNode(size_t id);
58+
59+
std::string m_name;
60+
std::unique_ptr<ui::Menu> m_menu;
61+
std::unique_ptr<ui::Button> m_connectButton;
62+
std::map<size_t, std::unique_ptr<DummyNode>> m_nodes;
63+
size_t m_nextNodeId = 1;
64+
bool m_connected = false;
65+
};
66+
67+
}} // namespace opencover::dataclient
68+
69+
#endif // OPENCOVER_DUMMYCLIENT_H

0 commit comments

Comments
 (0)