-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathNetwork.lf
222 lines (190 loc) · 8.35 KB
/
Network.lf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
/*
* This is a minimal example of an alarmclock implemeted using the
* features lingua franca supplies.
*
* This is just an extract and simplification from the main project
* which you can find here: https://github.com/revol-xut/lf-alarm-clock
* This file contains the networking implementation it is really just an
* simple socket application which parses simple http headers and respondes
* in text/plain
*
* @author Tassilo Tanneberer <tassilo.tanneberger@tu-dresden.de>
*/
target Cpp{
cmake-include: "AlarmClock.cmake",
keepalive: true
};
// there needs to be a better solution to import those files
public preamble {=
#include "shared_header.hpp"
#include <crow/app.h>
#include <crow/http_server.h>
#include <crow/http_request.h>
#include <crow/http_response.h>
#include <crow/json.h>
=}
reactor Network {
// physical event which is triggered by receiving a request
physical action new_event: Event;
physical action delete_request: std::size_t;
// variables for the receive thread
state thread: std::thread; // receive thread
state events: std::vector<Event>; // copy
input updated_events: std::vector<Event>;
output event: Event; // event which will be added to the clock
Netment which will be removed
// this reaction transforms a physical action into a logical reaction
reaction (new_event) -> event {=
if(new_event.is_present()){
event.set(new_event.get());
}
=}
reaction (delete_request) -> delete_index {=
if(delete_request.is_present()){
delete_index.set(delete_request.get());
}
=}
// main starts receive thread
reaction (startup) -> delete_request, new_event{=
thread = std::thread([&] {
crow::SimpleApp app;
// returns json of all the upcoming events
CROW_ROUTE(app, "/list") ([&]{
// function converts unix timestamp to human readable datetime string
auto unix_to_human_readable = [](unsigned int time_stamp){
using Clock = std::chrono::high_resolution_clock;
using TimePoint = std::chrono::time_point<Clock>;
const Clock::duration duration_time_stamp = std::chrono::seconds(time_stamp);
const TimePoint chrono_time_point(duration_time_stamp);
std::time_t end_time = std::chrono::system_clock::to_time_t(chrono_time_point);
std::string return_string(std::ctime(&end_time));
return return_string.substr(0, return_string.size() - 2);
};
crow::json::wvalue response;
for (const Event& event : events ){
crow::json::wvalue json_event;
json_event["date"] = std::move(unix_to_human_readable(event.time_stamp_));
json_event["message"] = event.message_;
response[std::to_string(event.time_stamp_)] = std::move(json_event);
};
return crow::response(response);
});
// adds new event by unix time stamp
CROW_ROUTE(app, "/add_event_timestamp").methods("POST"_method)
([&new_event](const crow::request& req){
auto json_body = crow::json::load(req.body);
if (!json_body) {
return crow::response(400);
}
// maybe add extra input validation
Event serialized_event {
json_body["message"].s(),
static_cast<unsigned int>(json_body["time_stamp"].u())
};
// triggers physical action
new_event.schedule(serialized_event, 0ms);
crow::json::wvalue response;
response["success"] = true;
return crow::response(response);
});
// adds new event by relativ times
CROW_ROUTE(app, "/add_event_relative").methods("POST"_method)
([&new_event](const crow::request& req){
auto relativ_time = 0l;
auto json_body = crow::json::load(req.body);
if (!json_body) {
return crow::response(400);
}
// calculates relative time in seconds
if(json_body.has("days")){
relativ_time += 24 * 60 * 60 * json_body["days"].u();
}
if(json_body.has("hours")){
relativ_time += 60 * 60 * json_body["hours"].u();
}
if(json_body.has("minutes")){
relativ_time += 60 * json_body["minutes"].u();
}
if(json_body.has("seconds")){
relativ_time += json_body["seconds"].u();
}
const auto now = std::chrono::system_clock::now();
auto current_time = std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count();
Event serialized_event {
json_body["message"].s(),
current_time + relativ_time
};
// triggers physical action
new_event.schedule(serialized_event, 0ms);
crow::json::wvalue response;
response["success"] = true;
return crow::response(response);
});
// will set the timer in the text 24 hours
CROW_ROUTE(app, "/add_event_time").methods("POST"_method)
([&new_event](const crow::request& req){
// just % doesn't work because it is the remainder operator
// and does not behave like modulo for negative numbers
auto mod = [](int a, int b) {
int r = a % b;
return r < 0 ? r + b : r;
};
auto relativ_time = 0l;
auto json_body = crow::json::load(req.body);
if (!json_body) {
return crow::response(400);
}
// use std::chrono::hh_mm_ss when C++20 is available
time_t time_struct = time(NULL);
struct tm *formatted_time = localtime(&time_struct);
// calculating time differences and turning them into seconds for the time_stamp
if(json_body.has("hour")){
relativ_time += 60 * 60 * mod(json_body["hour"].i() - formatted_time->tm_hour - 1, 24);
}
if(json_body.has("minute")){
relativ_time += 60 * mod(json_body["minute"].i() - formatted_time->tm_min - 1, 60);
}
if(json_body.has("second")){
relativ_time += mod(json_body["second"].u() - formatted_time->tm_sec - 1, 60);
}
Event serialized_event {
json_body["message"].s(),
relativ_time + time_struct
};
// triggers physical action
new_event.schedule(serialized_event, 0ms);
crow::json::wvalue response;
response["success"] = true;
return crow::response(response);
});
// request stopping playing music
// just used pidof to kill the process
CROW_ROUTE(app, "/stop") ([]{
int status = system("kill $(pidof mpg321)");
crow::json::wvalue response;
response["success"] = status;
return crow::response(response);
});
CROW_ROUTE(app, "/remove").methods("POST"_method)
([&delete_request](const crow::request& req){
auto json_body = crow::json::load(req.body);
if (!json_body) {
return crow::response(400);
}
std::size_t index = json_body["index"].u();
delete_request.schedule(index, 0s);
crow::json::wvalue response;
response["success"] = true;
return crow::response(response);
});
// start the http server
app.port(kPort).multithreaded().run();
});
=}
reaction (updated_events) {=
events = std::move(*updated_events.get());
=}
reaction ( shutdown ) {=
thread.join();
=}
}