• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nasa / trick / 25459968639

06 May 2026 08:42PM UTC coverage: 55.916% (-0.8%) from 56.7%
25459968639

Pull #2011

github

web-flow
Merge f11412d5f into 7054e405e
Pull Request #2011: Single-file CI and code style adoption

14607 of 26123 relevant lines covered (55.92%)

466416.66 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/trick_source/sim_services/JSONVariableServer/JSONVariableServerThread.cpp
1

2
#include <iostream>
3
#include <sstream>
4
#include <stdlib.h>
5
#include <string.h>
6

7
#include "trick/JSONVariableServerThread.hh"
8
#include "trick/MemoryManager.hh"
9
#include "trick/memorymanager_c_intf.h"
10
#include "trick/exec_proto.h"
11
#include "trick/PythonPrint.hh"
12
#include "trick/tc_proto.h"
13
#include "trick/TrickConstant.hh"
14

15
Trick::JSONVariableServerThread::JSONVariableServerThread(TCDevice * listen_dev) :
×
16
 Trick::ThreadBase("JSONVarServer") {
×
17

18
    connection.disable_handshaking = TC_COMM_TRUE ;
×
19
    connection.blockio_type = TC_COMM_BLOCKIO ;
×
20

21
    tc_accept(listen_dev, &connection);
×
22

23
    // Save the hostname and port of the listen server
24
    hostname = listen_dev->hostname ;
×
25
    port = listen_dev->port ;
×
26

27
    incoming_msg = new char[MAX_CMD_LEN] ;
×
28
}
×
29

30
/*
31
@details
32
-# delete the space for incominig_msg.
33
-# These classes are always allocated... free the memory on the way out.
34
*/
35
Trick::JSONVariableServerThread::~JSONVariableServerThread() {
×
36
    delete [] incoming_msg ;
×
37
}
×
38

39
/*
40
@details
41
-# Loop until the connection is broken or timed_out
42
 -# Call select to wait up to 15 seconds for a message.
43
 -# If a message is found, read it into the incoming_msg buffer
44
 -# Call parse_request on the incoming_msg buffer
45
-# Disconnect the socket connection
46
*/
47
void * Trick::JSONVariableServerThread::thread_body() {
×
48

49
    int nbytes = -1 ;
×
50
    socklen_t sock_size = sizeof(connection.remoteServAddr) ;
×
51
    fd_set rfds;
52
    struct timeval timeout_time = { 15, 0 };
×
53
    bool timed_out = false ;
×
54

55
    while (! timed_out and nbytes != 0 ) {
×
56
        FD_ZERO(&rfds);
×
57
        FD_SET(connection.socket, &rfds);
×
58
        timeout_time.tv_sec = 15 ;
×
59
        select(connection.socket + 1, &rfds, NULL, NULL, &timeout_time);
×
60

61
        if ( FD_ISSET(connection.socket, &rfds)) {
×
62
            nbytes = recvfrom( connection.socket, incoming_msg, MAX_CMD_LEN, 0 ,
×
63
                     (struct sockaddr *)&connection.remoteServAddr, &sock_size ) ;
×
64
            //std::cout << "nbytes = " << nbytes << std::endl ;
65
            if (nbytes != 0 ) {
×
66
                //std::cout << incoming_msg << std::endl ;
67
                parse_request() ;
×
68
            }
69
        } else {
70
            timed_out = true ;
×
71
        }
72
    }
73
    tc_disconnect(&connection) ;
×
74
    //std::cout << "closed variable server connection" << std::endl ;
75

76
    return NULL ;
×
77
}
78

79
/*
80
@details
81
-# scan the incoming message for the command and requested path
82
-# start the return message with a "{"
83
-# if both a command and request is present
84
 -# if the command is "GET"
85
  -# if the path is "/" call get_top_page
86
  -# else if the path starts with "/vars" call get_vars
87
  -# else if the path starts with "/commands" call get_commands
88
  -# else create an error message reply
89
 -# else if the command is "POST" do something at some point.
90
 -# else create an error message reply
91
-# else create an error message reply
92
-# close the return message with a "}"
93
-# create the http header for the reply message
94
-# call tc_write to send the reply message
95
*/
96
void Trick::JSONVariableServerThread::parse_request() {
×
97
    // Need to protect against buffer overflow
98
    char command[32] ;
99
    char path[512] ;
100
    std::stringstream ss ;
×
101
    std::stringstream body ;
×
102
    int ret ;
103

104
    ret = sscanf(incoming_msg, "%s %s", command , path) ;
×
105

106
    body << "{" << std::endl ;
×
107
    if ( ret == 2 ) {
×
108
        //std::cout << "command = " << command << std::endl ;
109
        //std::cout << "path = " << path << std::endl ;
110

111
        if ( ! strcmp(command, "GET") ) {
×
112
            if ( ! strcmp(path, "/") ) {
×
113
                // send the top page
114
                ret = get_top_page(body) ;
×
115
            } else if ( ! strncmp(path, "/vars", 5)) {
×
116
                // get list of variables or value
117
                ret = get_vars(body, &path[5]) ;
×
118
            } else if ( ! strncmp(path, "/commands", 9)) {
×
119
                // get list of commands
120
                ret = get_commands(body, &path[9]) ;
×
121
            } else {
122
                ret = 404 ;
×
123
                body << "    \"name\" : \"" << path << "\" ," << std::endl ;
×
124
                body << "    \"message\" : \"Not Found\"" << std::endl ;
×
125
            }
126
        } else if ( ! strcmp( command, "POST") ) {
×
127
            //TODO: Allow setting variable values with POST.
128
            ret = 405 ;
×
129
            body << "    \"verb\" : \"" << command << "\" ," << std::endl ;
×
130
            body << "    \"message\" : \"Method not allowed\"" << std::endl ;
×
131
        } else {
132
            ret = 405 ;
×
133
            body << "    \"verb\" : \"" << command << "\" ," << std::endl ;
×
134
            body << "    \"message\" : \"Method not allowed\"" << std::endl ;
×
135
        }
136

137
    } else {
138
        ret = 400 ;
×
139
        body << "    \"message\" : \"Bad Request\"" << std::endl ;
×
140
    }
141
    body << "}" << std::endl ;
×
142

143
    ss << "HTTP/1.1 " << ret ;
×
144
    switch (ret) {
×
145
        case 200:
×
146
            ss << " OK" ;
×
147
            break ;
×
148
        case 400:
×
149
            ss << " Bad Request" ;
×
150
            break ;
×
151
        case 404:
×
152
            ss << " Not Found" ;
×
153
            break ;
×
154
        case 405:
×
155
            ss << " Method Not Allowed" ;
×
156
            break ;
×
157
        default:
×
158
            ss << " Unknown" ;
×
159
            break ;
×
160
    }
161
    ss << std::endl ;
×
162
    ss << "Content-Type: application/json" << std::endl ;
×
163
    ss << "Content-Length: " << body.str().size() << std::endl << std::endl ;
×
164
    ss << body.str() ;
×
165
    //std::cout << ss.str() ;
166
    tc_write(&connection, (char *)ss.str().c_str() , ss.str().size()) ;
×
167
}
×
168

169
/*
170
@details
171
-# create top page contents and return success
172
*/
173
int Trick::JSONVariableServerThread::get_top_page( std::stringstream & body ) {
×
174
    body << "   \"commands\": \"http://" << hostname << ":" << port << "/commands\"," << std::endl ;
×
175
    body << "   \"vars\": \"http://" << hostname << ":" << port << "/vars\"" << std::endl ;
×
176
    return 200 ;
×
177
}
178

179
/*
180
@details
181
-# if the path is empty after /commands create commands top level page.
182
-# else if the path is exec_get_sim_time return create a message with the sim time.
183
-# else create an error message.
184
*/
185
int Trick::JSONVariableServerThread::get_commands( std::stringstream & body , char * path ) {
×
186
    int ret = 200 ;
×
187
    if ( path[strlen(path) - 1] == '/') {
×
188
        path[strlen(path) - 1] = '\0' ;
×
189
    }
190
    if ( path[0] == '\0' ) {
×
191
        body << "   \"exec_get_sim_time\": \"exec_get_sim_time()\"" << std::endl ;
×
192
    } else if ( ! strcmp( path , "/exec_get_sim_time" ) ) {
×
193
        body << "   \"sim_time\": \"" << exec_get_sim_time() << "\"" << std::endl ;
×
194
    } else {
195
        body << "    \"message\" : \"Not Found\"" << std::endl ;
×
196
        ret = 404 ;
×
197
    }
198
    return ret ;
×
199
}
200

201
/*
202
@details
203
-# if the path is empty after /vars
204
 -# loop through all allocations in the memory manager
205
 -# if the allocation has a name create an item in the reply message for that allocation.
206
-# else
207
 -# convert "/" characters to "." in the path and strip trailing "/" characters.
208
 -# add converted variable name to the reply message.
209
 -# call ref_attributes to find the variable.
210
  -# if the variable exists
211
   -# if the variable type is a structure.
212
    -# loop through the attributes and add each of the struct/class variables as links in the reply message.
213
   -# else add the variables value and units to the reply message.
214
   -# add the description text to the reply message
215
   -# add the type information to the reply message
216
   -# add the type io specification to the reply message
217
   -# if the variable has dimensions add the dimension information to the reply message.
218
 -# else add an error message to the reply message
219
*/
220
int Trick::JSONVariableServerThread::get_vars( std::stringstream & body , char * path ) {
×
221

222
    int ret = 200 ;
×
223

224
    //std::cout << "get_vars " << path << std::endl ;
225
    if ( path[0] == '\0' ) {
×
226
        Trick::ALLOC_INFO_MAP_ITER aim_it ;
×
227
        bool first_item = true ;
×
228
        for ( aim_it = trick_MM->alloc_info_map_begin() ; aim_it != trick_MM->alloc_info_map_end() ; ++aim_it ) {
×
229
            ALLOC_INFO * alloc_info = (*aim_it).second ;
×
230
            if ( alloc_info->name != NULL and alloc_info->user_type_name != NULL ) {
×
231
                if ( first_item == true ) {
×
232
                    first_item = false ;
×
233
                } else {
234
                    body << " ," << std::endl ;
×
235
                }
236
                body << "    \"" << alloc_info->name << "\" : \"" << alloc_info->user_type_name << "\"" ;
×
237
            }
238
        }
239
        body << std::endl ;
×
240
    } else {
241

242
        //TODO: More path character translations for characters like "[]"
243

244
        if ( path[strlen(path) - 1] == '/') {
×
245
            path[strlen(path) - 1] = '\0' ;
×
246
        }
247
        for ( unsigned int ii = 1 ; ii < strlen(path) ; ii++ ) {
×
248
            if ( path[ii] == '/') {
×
249
                path[ii] = '.' ;
×
250
            }
251
        }
252

253
        body << "    \"name\" : \"" << &path[1] << "\" ," << std::endl ;
×
254
        //std::cout << "getting var " << path << std::endl ;
255
        // skip leading "/"
256
        REF2 * ref = ref_attributes( &path[1] ) ;
×
257
        if ( ref != NULL and ref->attr != NULL ) {
×
258
            ret = 200 ;
×
259
            //std::cout << "num_index " << ref->num_index << std::endl ;
260
            if ( ref->attr->type == TRICK_STRUCTURED ) {
×
261
                ATTRIBUTES * attr = (ATTRIBUTES *)ref->attr->attr ;
×
262
                body << "    \"links\" : [" << std::endl ;
×
263
                for ( int ii = 0 ; attr[ii].name[0] != '\0' ; ii++ ) {
×
264
                    if ( ii != 0 ) {
×
265
                        body << " ," << std::endl ;
×
266
                    }
267
                    body << "                \"" << attr[ii].name << "\"" ;
×
268
                }
269
                body << std::endl << "              ] ," << std::endl ;
×
270
            } else {
271
                body << "    \"value\" : " ;
×
272
                Trick::PythonPrint::write_rvalue(body, ref->address, ref->attr, ref->num_index, 0, false, true, nullptr);
×
273
                body << " ," << std::endl ;
×
274
                if ( ref->attr->units != NULL ) {
×
275
                    body << "    \"units\" : \"" << ref->attr->units << "\" ," << std::endl ;
×
276
                }
277
            }
278
            if ( ref->attr->des != NULL ) {
×
279
                body << "    \"description\" : \"" << ref->attr->des << "\" ," << std::endl ;
×
280
            }
281
            body << "    \"type_name\" : " ;
×
282
            if ( ref->attr->type_name != NULL ) {
×
283
                body << "\"" << ref->attr->type_name << "\" ," << std::endl ;
×
284
            } else {
285
                body << "\"(null)\" ," << std::endl ;
×
286
            }
287
            body << "    \"type\" : " << ref->attr->type << " ," << std::endl ;
×
288
            body << "    \"io\" : " << ref->attr->io ;
×
289
            if ( (ref->attr->num_index - ref->num_index) > 0 ) {
×
290
                body << " ," << std::endl ;
×
291
                body << "    \"num_index\" : " << ref->attr->num_index - ref->num_index << " ," << std::endl ;
×
292
                body << "    \"index\" : [" << std::endl ;
×
293
                for ( int ii = ref->num_index , jj = 0 ; ii < ref->attr->num_index ; ii++ , jj++ ) {
×
294
                    if ( jj != 0 ) {
×
295
                        body << " ," << std::endl ;
×
296
                    }
297
                    body << "                {" << std::endl ;
×
298
                    body << "                    \"size\" : " << ref->attr->index[ii].size << " ," << std::endl ;
×
299
                    body << "                    \"start\" : " << ref->attr->index[ii].start << std::endl ;
×
300
                    body << "                }" ;
×
301
                }
302
                body << std::endl << "              ]" << std::endl ;
×
303
            } else {
304
                body << std::endl ;
×
305
            }
306
            free(ref) ;
×
307
        } else {
×
308
            body << "    \"message\" : \"Not Found\"" << std::endl ;
×
309
            ret = 404 ;
×
310
        }
311
    }
312
    return ret ;
×
313
}
314

STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc