283 lines
7.8 KiB
C++
283 lines
7.8 KiB
C++
#include <iostream>
|
|
#include <chrono>
|
|
#include <cmath>
|
|
#include <random>
|
|
#include <semaphore>
|
|
#include <google/protobuf/arena.h>
|
|
#include <thread>
|
|
|
|
|
|
struct StatusRequest {
|
|
};
|
|
struct SensorStatus {
|
|
uint64_t online_since;
|
|
uint32_t battery_charge;
|
|
uint32_t battery_capacity;
|
|
uint32_t max_sampling_rate;
|
|
uint32_t cur_sampling_rate;
|
|
uint32_t max_chunk_size;
|
|
uint32_t cur_chunk_size;
|
|
uint32_t n_chunk_capacity;
|
|
uint32_t n_full_data_chunks;
|
|
uint32_t n_empty_data_chunks;
|
|
};
|
|
|
|
|
|
struct SetSamplingPeriodRequest {
|
|
uint32_t new_sampling_period;
|
|
};
|
|
enum SetSamplingPeriodResult {
|
|
SAMPLING_PERIOD_OK,
|
|
SAMPLING_PERIOD_OUT_OF_RANGE
|
|
};
|
|
|
|
struct PopDataChunkRequest {
|
|
};
|
|
struct DataChunk {
|
|
uint64_t begin;
|
|
uint32_t sampling_period;
|
|
float temperature_data[];
|
|
};
|
|
|
|
template < size_t T_Capacity >
|
|
struct RandomSensor
|
|
{
|
|
private:
|
|
std::chrono::time_point<std::chrono::high_resolution_clock> online_since;
|
|
std::chrono::duration<uint64_t, std::milli> period_duration;
|
|
uint32_t chunk_size;
|
|
uint32_t next_free;
|
|
uint32_t next_full;
|
|
|
|
float bat_charge;
|
|
|
|
std::counting_semaphore<> sem_full;
|
|
std::counting_semaphore<> sem_free;
|
|
|
|
float data[ T_Capacity ];
|
|
std::thread sensor_thread;
|
|
|
|
void generate_value()
|
|
{
|
|
sem_free.acquire();
|
|
|
|
data[ next_free ] = (float) next_free / 100.0;
|
|
next_free = (next_free + 1) % T_Capacity;
|
|
|
|
sem_full.release();
|
|
}
|
|
|
|
public:
|
|
RandomSensor()
|
|
: online_since(std::chrono::high_resolution_clock::now())
|
|
, period_duration( 10 )
|
|
, chunk_size( 8192 )
|
|
, next_free( 0 )
|
|
, next_full( 0 )
|
|
, bat_charge( 8200.0 )
|
|
, sem_full( 0 )
|
|
, sem_free( T_Capacity )
|
|
, sensor_thread([&] {
|
|
while(bat_charge > 0) {
|
|
generate_value();
|
|
bat_charge -= 0.01;
|
|
std::this_thread::sleep_for( period_duration );
|
|
}
|
|
})
|
|
{}
|
|
|
|
~RandomSensor() {
|
|
sensor_thread.join();
|
|
}
|
|
|
|
uint32_t n_chunks_capacity() const {
|
|
return T_Capacity / chunk_size;
|
|
}
|
|
|
|
uint32_t n_full_chunks() const {
|
|
return (( next_free - next_full ) % T_Capacity) / chunk_size;
|
|
}
|
|
|
|
uint32_t n_empty_chunks() const {
|
|
return n_chunks_capacity() - n_full_chunks();
|
|
}
|
|
|
|
|
|
void get_status(
|
|
StatusRequest const * request,
|
|
SensorStatus * status
|
|
) {
|
|
status->online_since = std::chrono::duration_cast<std::chrono::milliseconds>(online_since.time_since_epoch()).count();
|
|
status->battery_capacity = 8200;
|
|
status->battery_charge = bat_charge;
|
|
status->max_sampling_rate = 10;
|
|
status->cur_sampling_rate = std::chrono::duration_cast<std::chrono::milliseconds>(period_duration).count();
|
|
status->cur_chunk_size = chunk_size;
|
|
status->n_chunk_capacity = n_chunks_capacity();
|
|
status->n_full_data_chunks = n_full_chunks();
|
|
status->n_empty_data_chunks = n_empty_chunks();
|
|
}
|
|
|
|
void set_sampling_period(
|
|
SetSamplingPeriodRequest const * request,
|
|
SetSamplingPeriodResult * result
|
|
) {
|
|
*result = SAMPLING_PERIOD_OK;
|
|
}
|
|
|
|
void pop_data_chunk(
|
|
PopDataChunkRequest const * request,
|
|
DataChunk * data_chunk
|
|
) {
|
|
|
|
}
|
|
};
|
|
|
|
#include <iostream>
|
|
#include <string>
|
|
#include <unistd.h>
|
|
#include <arpa/inet.h>
|
|
#include <sys/socket.h>
|
|
#include <cstring>
|
|
|
|
|
|
enum RpcTag {
|
|
GET_STATUS = 0,
|
|
SET_SAMPLING_PERIOD,
|
|
POP_DATA_CHUNK
|
|
};
|
|
|
|
struct RpcService {
|
|
private:
|
|
uint32_t port;
|
|
RandomSensor< 1048576 > & sensor;
|
|
|
|
public:
|
|
RpcService(
|
|
uint32_t port,
|
|
RandomSensor< 1048576 > & sensor
|
|
) :port(port), sensor(sensor)
|
|
{
|
|
}
|
|
|
|
void handle_client(int client_sock) {
|
|
char request_buffer[1024];
|
|
ssize_t bytes_received;
|
|
|
|
// Receive data from client
|
|
bytes_received = recv(client_sock, request_buffer, sizeof(request_buffer), 0);
|
|
if (bytes_received == -1) {
|
|
std::cerr << "Failed to receive data" << std::endl;
|
|
return;
|
|
}
|
|
|
|
printf("received %lu bytes:\n", bytes_received);
|
|
for( size_t i = 0; i < bytes_received;)
|
|
{
|
|
printf("%4lx : ", i);
|
|
for( size_t col = 0; col < 8; ++col ) {
|
|
printf("%2x ", request_buffer[i++]);
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
if (bytes_received >= 8 ) {
|
|
RpcTag * tag = (RpcTag*) request_buffer;
|
|
switch( *tag ) {
|
|
case GET_STATUS: {
|
|
printf("RPC call get_status()\n");
|
|
StatusRequest * request = (StatusRequest * ) (request_buffer + sizeof(RpcTag));
|
|
|
|
SensorStatus status;
|
|
sensor.get_status(request, &status);
|
|
|
|
send(client_sock, (char*)&status, sizeof(status), 0);
|
|
break;
|
|
}
|
|
|
|
case SET_SAMPLING_PERIOD: {
|
|
SetSamplingPeriodRequest * request = (SetSamplingPeriodRequest * ) (request_buffer + sizeof(RpcTag));
|
|
printf("RPC call set_sampling_period()\n");
|
|
|
|
SetSamplingPeriodResult result;
|
|
sensor.set_sampling_period(request, &result);
|
|
|
|
send(client_sock, (char*)&result, sizeof(result), 0);
|
|
break;
|
|
}
|
|
|
|
case POP_DATA_CHUNK: {
|
|
PopDataChunkRequest * request = (PopDataChunkRequest * ) (request_buffer + sizeof(RpcTag));
|
|
printf("RPC call pop_data_chunk()\n");
|
|
|
|
DataChunk data_chunk;
|
|
sensor.pop_data_chunk(request, &data_chunk);
|
|
|
|
send(client_sock, (char*)&data_chunk, sizeof(data_chunk), 0);
|
|
break;
|
|
}
|
|
|
|
default: {
|
|
std::string response("invalid RPC");
|
|
send(client_sock, response.c_str(), response.length(), 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int run() {
|
|
int server_sock, client_sock;
|
|
struct sockaddr_in server_addr, client_addr;
|
|
socklen_t addr_len = sizeof(client_addr);
|
|
|
|
// Create socket
|
|
server_sock = socket(AF_INET, SOCK_STREAM, 0);
|
|
if (server_sock == -1) {
|
|
std::cerr << "Failed to create socket" << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
// Set up server address
|
|
server_addr.sin_family = AF_INET;
|
|
server_addr.sin_addr.s_addr = INADDR_ANY;
|
|
server_addr.sin_port = htons(port);
|
|
|
|
// Bind socket to the address and port
|
|
if (bind(server_sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
|
|
std::cerr << "Failed to bind socket" << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
// Listen for incoming connections
|
|
if (listen(server_sock, 3) == -1) {
|
|
std::cerr << "Failed to listen on socket" << std::endl;
|
|
return 1;
|
|
}
|
|
std::cout << "Server listening on port " << port << "..." << std::endl;
|
|
|
|
// Accept and handle client connections
|
|
while (true) {
|
|
client_sock = accept(server_sock, (struct sockaddr*)&client_addr, &addr_len);
|
|
if (client_sock == -1) {
|
|
std::cerr << "Failed to accept connection" << std::endl;
|
|
continue;
|
|
}
|
|
|
|
std::cout << "Client connected!" << std::endl;
|
|
handle_client(client_sock);
|
|
|
|
// Close the client socket after handling the request
|
|
close(client_sock);
|
|
}
|
|
|
|
// Close the server socket
|
|
close(server_sock);
|
|
}
|
|
};
|
|
|
|
int main( int argc, char* argv[] ) {
|
|
RandomSensor< 1048576 > sensor;
|
|
|
|
RpcService service(8070, sensor);
|
|
return service.run();
|
|
}
|