Mads
Multi-Agent Distributed System
Loading...
Searching...
No Matches
image.hpp
Go to the documentation of this file.
1/*
2 ___ _
3 |_ _|_ __ ___ __ _ __ _ ___ ___| | __ _ ___ ___
4 | || '_ ` _ \ / _` |/ _` |/ _ \ / __| |/ _` / __/ __|
5 | || | | | | | (_| | (_| | __/ | (__| | (_| \__ \__ \
6 |___|_| |_| |_|\__,_|\__, |\___| \___|_|\__,_|___/___/
7 |___/
8
9This class is a pure frontend class: it is expected to stream an image as a
10binary blob
11
12Author(s): Paolo Bosetti
13*/
14
15#ifndef IMAGE_HPP
16#define IMAGE_HPP
17
18
19// #include "mads.hpp"
20#include "agent.hpp"
21#include <fcntl.h>
22#ifdef __APPLE__
23#include <sys/event.h>
24#elif __linux__
25#include <errno.h>
26#include <sys/inotify.h>
27#define EVENT_BUF_LEN sizeof(struct inotify_event) + NAME_MAX + 1
28#else
29#error "Unsupported platform"
30#endif
31
32using json = nlohmann::json;
33
34namespace Mads {
35
36typedef struct {
37 unsigned int id;
38 char topic[1024];
39 char path[1024];
41
48class Image : public Agent {
49public:
56 Image(std::string name, std::string settings_path)
57 : Agent(name, settings_path) {
58#ifdef __linux__
59 if (_infd < 0)
60 throw runtime_error("Could not init inotify system: " +
61 string(strerror(errno)));
62#endif
63 }
64
66 for (auto fd : _fds)
67 close(fd);
68 for (auto data : _image_data)
69 free(data);
70#ifdef __APPLE__
71 free(_events);
72 close(_kq);
73#elif __linux__
74 for (auto &[fd, fn] : _inwds) {
75 inotify_rm_watch(_infd, fd);
76 }
77 close(_infd);
78#endif
79 }
80
81 void info(ostream &out = cout) override {
82 Agent::info(out);
83 out << " Watched files: " << style::bold;
84 for (auto &[k, v] : _watch_list) {
85 out << k << ": " << v << "; ";
86 }
87 out << style::reset << endl;
88 }
89
95 string topic, path, type;
96
97#ifdef __APPLE__
98 struct kevent change;
99 image_data_t *data;
100 if (kevent(_kq, NULL, 0, &change, 1, NULL) == -1) {
101 exit(1);
102 }
103 data = (image_data_t *)change.udata;
104 topic = data->topic;
105 path = data->path;
106 type = path.substr(path.find_last_of(".") + 1);
107 cout << "Change detected to image '" << topic << "' at " << path
108 << " (type " << type << ")" << endl;
109
110#elif __linux__
111 ssize_t length;
112 char buffer[EVENT_BUF_LEN];
113 length = read(_infd, buffer, EVENT_BUF_LEN);
114 if (length < 0 && errno == EAGAIN) {
115 return;
116 }
117 if (length < 0 && errno != EAGAIN) {
118 throw runtime_error("Error watching file changes: " +
119 string(strerror(errno)));
120 }
121 struct inotify_event *event = (struct inotify_event *)&buffer;
122 if (event->mask & IN_MODIFY) {
123 topic = _inwds[event->wd];
124 path = _watch_list[_inwds[event->wd]];
125 type = path.substr(path.find_last_of(".") + 1);
126 cout << "Change detected to topic '" << topic << "' at " << path
127 << " (type " << type << ")" << endl;
128 } else {
129 throw runtime_error("Unexpected event: "s + to_string(event->mask) +
130 " for " + string(event->name));
131 }
132
133#endif
134 json meta{{"format", type}};
135 ifstream fin(path);
136 // get pointer to associated buffer object
137 filebuf *pbuf = fin.rdbuf();
138 // get file size using buffer's members
139 size_t size = pbuf->pubseekoff(0, fin.end, fin.in);
140 pbuf->pubseekpos(0, fin.in);
141 // allocate memory to contain file data
142 char *filebuffer = new char[size];
143 // get file data
144 pbuf->sgetn(filebuffer, size);
145 publish(filebuffer, size, meta, topic);
146 fin.close();
147 delete[] filebuffer;
148 }
149
150private:
157 void load_settings() override {
158 auto cfg = _config[_name];
159 string fname, topic;
160 unsigned int count = 0;
161
162#ifdef __APPLE__
163 unsigned int i = 0;
164 if (!cfg["watch_list"].is_table()) {
165 throw std::runtime_error("No watch_list found in settings");
166 }
167 auto wl = cfg["watch_list"].as_table();
168 unsigned int n = wl->size();
169 _events = (struct kevent *)calloc(n, sizeof(struct kevent));
170 wl->for_each([&](const auto &k, const auto &v) {
171 fname = v.value_or("undefined");
172 _fds.push_back(open(fname.c_str(), O_CREAT | O_RDONLY));
173 if (_fds.back() >= 0) {
174 _watch_list[static_cast<string>(k)] = fname;
175 } else {
176 throw std::runtime_error("File " + fname +
177 " not found (and cannot be created)");
178 }
179 _image_data.push_back((image_data_t *)malloc(sizeof(image_data_t)));
180 _image_data.back()->id = count++;
181 strcpy(_image_data.back()->topic, static_cast<string>(k).c_str());
182 strcpy(_image_data.back()->path, fname.c_str());
183 EV_SET(&_events[i++], _fds.back(), EVFILT_VNODE,
184 EV_ADD | EV_CLEAR | EV_ENABLE, NOTE_WRITE, 0,
185 (void *)(_image_data.back()));
186 });
187 kevent(_kq, _events, n, NULL, 0, NULL);
188
189#elif __linux__
190 cfg["watch_list"].as_table()->for_each([&](const auto &k, const auto &v) {
191 fname = v.value_or("undefined");
192 topic = static_cast<string>(k);
193 _watch_list[topic] = fname;
194 _image_data.push_back((image_data_t *)malloc(sizeof(image_data_t)));
195 _image_data.back()->id = count++;
196 strcpy(_image_data.back()->topic, topic.c_str());
197 strcpy(_image_data.back()->path, fname.c_str());
198 count = inotify_add_watch(_infd, fname.c_str(), IN_MODIFY);
199 if (count < 0) {
200 throw runtime_error("Inotify add watch error: " +
201 string(strerror(errno)));
202 }
203 _inwds[count] = static_cast<string>(k);
204 });
205#endif
206 }
207
208 map<string, string> _watch_list;
209 vector<int> _fds;
210 vector<image_data_t *> _image_data;
211#ifdef __APPLE__
212 struct kevent *_events = NULL;
213 int _kq = kqueue();
214#elif __linux__
215 int _infd = inotify_init1(IN_NONBLOCK);
216 map<int, string> _inwds; /* a map fd->topic */
217#endif
218};
219
220} // namespace Mads
221
222#endif // IMAGE_HPP
nlohmann::json json
Definition bridge.hpp:24
toml::table _config
Definition agent.hpp:1289
std::string name()
Returns the name of the agent.
std::string _name
Definition agent.hpp:1286
virtual void info(std::ostream &out=std::cout)
Prints information about the agent.
void publish(nlohmann::json payload, std::string topic="")
Publishes a message with the given JSON payload.
The RFID class represents metadata for an agent.
Definition image.hpp:48
void info(ostream &out=cout) override
Prints information about the agent.
Definition image.hpp:81
void publish_change()
Blocks until a write event happens on one of the watched files, then publishes the binary object.
Definition image.hpp:94
Image(std::string name, std::string settings_path)
Constructs a Image object with the given name and settings path.
Definition image.hpp:56
Definition agent.hpp:67
nlohmann::json json
unsigned int id
Definition image.hpp:37