-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcommand.h
More file actions
134 lines (116 loc) · 3.81 KB
/
command.h
File metadata and controls
134 lines (116 loc) · 3.81 KB
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
/**
* @file command.h
* @brief header-only library for handling commands to daemons
* @details This provides a wrapper to form command and arg list strings.
* Also a wrapper that stores transition states, and validates that
* a command is allowed to be sent while in the current state.
* @author David Hale <dhale@astro.caltech.edu>
*
*/
#pragma once
#include <string>
#include <vector>
#include <unordered_map>
#include <stdexcept>
/***** Sequencer **************************************************************/
/**
* @brief namespace for the observation sequencer
*
*/
namespace Sequencer {
/**
* @brief wrapper to form string from command and arglist
*/
struct Command {
std::string name;
std::vector<std::string> arglist;
std::string str() const {
std::string strung = name;
for (const auto &arg : arglist) {
strung += " " + arg;
}
return strung;
}
};
/**
* @brief command specs right now just holds min/max number of allowed args
*/
struct CommandSpec {
int min_args;
int max_args;
};
using CommandSpecMap = std::unordered_map<std::string, CommandSpec>;
/**
* @brief structure contains command and states it can transition from->to
*/
template <typename State>
struct Transition {
State from;
std::string command;
State to;
};
/**
* @brief contains the functionality of the library
* @details This pairs specs and transitions with a command and holds
* the client object used to communiate with the daemon.
*/
template <typename State>
class CommandClient {
private:
Common::DaemonClient &client;
const CommandSpecMap &specs;
State state;
const std::vector<Transition<State>> &transitions;
void validate_args( const Command &cmd ) const {
auto it = specs.find( cmd.name );
if (it == specs.end()) throw std::runtime_error("unknown command: "+cmd.name);
int nargs = cmd.arglist.size();
if (nargs < it->second.min_args || nargs > it->second.max_args) {
throw std::runtime_error("invalid arg count for "+cmd.name);
}
}
void validate_order( const Command &cmd ) const {
for (const auto &transition : transitions) {
if (transition.from == state && transition.command == cmd.name) {
return;
}
}
throw std::runtime_error("invalid command order: "+cmd.name);
}
void advance_state( const Command &cmd ) {
for (const auto &transition : transitions) {
if (transition.from == state && transition.command == cmd.name) {
state = transition.to;
return;
}
}
}
public:
CommandClient( Common::DaemonClient &client,
const CommandSpecMap &specs,
State initial_state,
const std::vector<Transition<State>> &transitions )
: client(client),
specs(specs),
state(initial_state),
transitions(transitions) { }
/**
* @brief primary interface to sending commands
* @details This validates the number of args, validates the
* transition state, that this command is allowed to be used
* in the current state, and sends the command.
* @param[in] cmd Command struct contains command and arglist
* @return return value from the client
*
*/
long send( const Command &cmd ) {
validate_args( cmd );
validate_order( cmd );
long ret = client.command( cmd.str() );
advance_state( cmd );
return ret;
}
State get_state() const { return state; }
};
}
/***** Sequencer **************************************************************/