Exported from Notepad++
1 // metronome.c
2 // program that runs a metronome using a resource manager, a timer and pulses
3
4 #include "metronome.h"
5
6 name_attach_t *attach;
7 Metronome_t Metronome;
8
9 char data[255];
10 int server_coid;
11
12 int main(int argc, char *argv[]) {
13 dispatch_t *dpp;
14 resmgr_io_funcs_t io_funcs;
15 resmgr_connect_funcs_t conn_funcs;
16
17 ioattr_t ioMetronome;
18 ioattr_t ioMetronomeHelp;
19
20 dispatch_context_t *ctp;
21
22 pthread_attr_t thread_attrib;
23 int id;
24
25 int server_coid;
26
27 // check command line arguments
28 if (argc != 4) {
29 fprintf(stderr,
30 "Error - Invalid Usage, use format of ./metronome beats-per-minute time-signature-top time-signature-bottom\n");
31 exit(EXIT_FAILURE);
32 }
33
34 // store command line arguments
35 Metronome.beatsPerMinute = atoi(argv[1]);
36 Metronome.timeSignatureTop = atoi(argv[2]);
37 Metronome.timeSignatureBottom = atoi(argv[3]);
38
39 if ((dpp = dispatch_create()) == NULL) {
40 fprintf(stderr, "%s: Unable to allocate dispatch context.\n", argv[0]);
41 return (EXIT_FAILURE);
42 }
43
44 iofunc_func_init(_RESMGR_CONNECT_NFUNCS, &conn_funcs, _RESMGR_IO_NFUNCS,
45 &io_funcs);
46 conn_funcs.open = io_open;
47 io_funcs.read = io_read;
48 io_funcs.write = io_write;
49
50 // override functions for ocb to attach device type
51 iofunc_funcs_t metro_ocb_funcs = {
52 _IOFUNC_NFUNCS, metro_ocb_calloc, metro_ocb_free, };
53 iofunc_mount_t metro_mount = { 0, 0, 0, 0, &metro_ocb_funcs };
54
55 // metronome
56 iofunc_attr_init(&ioMetronome.attr, S_IFCHR | 0666, NULL, NULL);
57 ioMetronome.device = METRONOME;
58 // mount functions
59 ioMetronome.attr.mount = &metro_mount;
60 // attach
61 if ((id = resmgr_attach(dpp, NULL, "/dev/local/metronome", _FTYPE_ANY, 0,
62 &conn_funcs, &io_funcs, &ioMetronome)) == -1) {
63 fprintf(stderr, "%s: Unable to attach name.\n", argv[0]);
64 return (EXIT_FAILURE);
65 }
66
67 // metronom-help
68 iofunc_attr_init(&ioMetronomeHelp.attr, S_IFCHR | 0666, NULL, NULL);
69 ioMetronomeHelp.device = METRONOME_HELP;
70 // mount functions
71 ioMetronomeHelp.attr.mount = &metro_mount;
72 // attach
73 if ((id = resmgr_attach(dpp, NULL, "/dev/local/metronome-help", _FTYPE_ANY,
74 0, &conn_funcs, &io_funcs, &ioMetronomeHelp)) == -1) {
75 fprintf(stderr, "%s: Unable to attach name.\n", argv[0]);
76 return (EXIT_FAILURE);
77 }
78
79 ctp = dispatch_context_alloc(dpp);
80
81 // create thread
82 pthread_attr_init(&thread_attrib);
83 pthread_create(NULL, &thread_attrib, &metronome_thread, &Metronome);
84
85 while (1) {
86 if ((ctp = dispatch_block(ctp))) {
87 dispatch_handler(ctp);
88 } else
89 fprintf(stderr, "ERROR \n");
90
91 }
92
93 pthread_attr_destroy(&thread_attrib);
94 name_detach(attach, 0);
95 name_close(server_coid);
96 return EXIT_SUCCESS;
97
98 }
99
100 void* metronome_thread(void *ta) {
101 struct sigevent event;
102 my_message_t msg;
103 int rcvid;
104 char *intervalString;
105 struct itimerspec itime;
106 timer_t timer_id;
107
108 if ((attach = name_attach(NULL, RESOURCE_NAME, 0)) == NULL) {
109 fprintf(stderr, "\nError - name_attach failed\n");
110 exit(EXIT_FAILURE);
111 }
112
113 event.sigev_notify = SIGEV_PULSE;
114 event.sigev_coid = ConnectAttach(ND_LOCAL_NODE, 0, attach->chid,
115 _NTO_SIDE_CHANNEL, 0);
116 event.sigev_priority = SIGEV_PULSE_PRIO_INHERIT;
117 event.sigev_code = GENERAL_PULSE;
118
119 // create timer
120 timer_create(CLOCK_REALTIME, &event, &timer_id);
121 start_interval_timer(&itime, timer_id, &Metronome);
122
123 intervalString = get_interval_string(
124 Metronome.timeSignatureTop + Metronome.timeSignatureBottom);
125
126 while (1) {
127 rcvid = MsgReceive(attach->chid, &msg, sizeof(msg),
128 NULL);
129
130 // received pulse
131 if (rcvid == 0) {
132 switch (msg.pulse.code) {
133 case GENERAL_PULSE:
134 // keep writing interval string
135 if (*intervalString == '\0') {
136 printf("\n");
137 intervalString = get_interval_string(
138 Metronome.timeSignatureTop
139 + Metronome.timeSignatureBottom);
140 } else if (*intervalString == '|') {
141 printf("%.2s", intervalString);
142 intervalString = (intervalString + 2);
143 } else {
144 printf("%c", *intervalString++);
145 }
146 break;
147 case RESET_PULSE:
148 // set new timing
149 intervalString = get_interval_string(
150 Metronome.timeSignatureTop
151 + Metronome.timeSignatureBottom);
152 printf("\n");
153 start_interval_timer(&itime, timer_id, &Metronome);
154 break;
155 case START_PULSE:
156 // start only if stopped
157 if (Metronome.timeStatus == STOPPED) {
158 Metronome.timeStatus = RUNNING;
159 start_interval_timer(&itime, timer_id, &Metronome);
160 }
161 break;
162 case PAUSE_PULSE:
163 // pause only if running
164 if (Metronome.timeStatus == RUNNING) {
165 itime.it_value.tv_sec = msg.pulse.value.sival_int;
166 timer_settime(timer_id, 0, &itime, NULL);
167 }
168 break;
169 case STOP_PULSE:
170 // stop only if running
171 if (Metronome.timeStatus == RUNNING) {
172 Metronome.timeStatus = STOPPED;
173 itime.it_value.tv_sec = 0;
174 timer_settime(timer_id, 0, &itime, NULL);
175 }
176 break;
177 case QUIT_PULSE:
178 // quit and end
179 timer_delete(timer_id);
180 name_detach(attach, 0);
181 name_close(server_coid);
182 exit(EXIT_SUCCESS);
183 }
184 } else {
185 // error received
186 fprintf(stderr, "\nerror message received\n");
187 exit(EXIT_FAILURE);
188 }
189 fflush(stdout);
190 }
191 return NULL;
192 }
193
194 int io_read(resmgr_context_t *ctp, io_read_t *msg, metro_ocb_t *mocb) {
195
196 int nb;
197
198 if (data == NULL)
199 return 0;
200
201 // corresponding response message data, /metronome or /metronome-help
202 if (mocb->ocb.attr->device == METRONOME) {
203
204 sprintf(data,
205 "\n[metronome: %d beats/min, time signature: %d/%d, sec-per-interval: %.2f, nanoSecs: %.0lf]\n",
206 Metronome.beatsPerMinute, Metronome.timeSignatureTop,
207 Metronome.timeSignatureBottom, Metronome.secondsPerInterval,
208 Metronome.nanoSeconds);
209
210 } else {
211 sprintf(data,
212 "\nMetronome Resource Manager (Resmgr)"
213 "\n\n Usage: metronome <bpm> <ts-top> <ts-bottom>"
214 "\n\n API:"
215 "\n pause[1-9]\t\t\t\t- pause the metronome for 1-9 seconds"
216 "\n quit:\t\t\t\t- quit the metronome"
217 "\n set <bpm> <ts-top> <ts-bottom>\t- set the metronome to <beatsPerMinute> ts-top/ts-bottom"
218 "\n start\t\t\t\t- start the metronome from stopped state"
219 "\n stop\t\t\t\t\t- stop the metronome; use 'start' to resume\n\n");
220
221 }
222
223 nb = strlen(data);
224
225 // test to see if we have already sent the whole message
226 if (mocb->ocb.offset == nb)
227 return 0;
228
229 // we will return which ever is smaller the size of our data or the size of the buffer
230 nb = min(nb, msg->i.nbytes);
231
232 // set the number of bytes we will return
233 _IO_SET_READ_NBYTES(ctp, nb);
234
235 // copy data into reply buffer
236 SETIOV(ctp->iov, data, nb);
237
238 // update offset into our data used to determine start position for next read
239 mocb->ocb.offset += nb;
240
241 // if we are going to send any bytes update the access time for this resource
242 if (nb > 0)
243 mocb->ocb.flags |= IOFUNC_ATTR_ATIME;
244
245 return (_RESMGR_NPARTS(1));
246 }
247
248 int io_write(resmgr_context_t *ctp, io_write_t *msg, metro_ocb_t *mocb) {
249
250 int nb = 0;
251
252 // if user attempts to write to metronome help display error message
253 if (mocb->ocb.attr->device == METRONOME_HELP) {
254 fprintf(stderr,
255 "\nError - can not write to /dev/local/metronome-help\n");
256
257 nb = msg->i.nbytes;
258 _IO_SET_WRITE_NBYTES(ctp, nb);
259 return (_RESMGR_NPARTS(0));
260 }
261
262 if (msg->i.nbytes == ctp->info.msglen - (ctp->offset + sizeof(*msg))) {
263 char *buf;
264 char *alert_msg;
265 char *timing_msg;
266 int i, small_integer = 0;
267 buf = (char*) (msg + 1);
268
269 // echo set
270 if (strstr(buf, "set") != NULL) {
271 for (i = 0; i < 4; i++) {
272 timing_msg = strsep(&buf, " ");
273 if (timing_msg == NULL) {
274 fprintf(stderr,
275 "\nError - invalid arguments for set command\n");
276 break;
277 }
278
279 if (i == 1) {
280 Metronome.beatsPerMinute = atoi(timing_msg);
281 } else if (i == 2) {
282 Metronome.timeSignatureTop = atoi(timing_msg);
283 } else if (i == 3) {
284 Metronome.timeSignatureBottom = atoi(timing_msg);
285 }
286 }
287 MsgSendPulse(server_coid, SchedGet(0, 0, NULL), RESET_PULSE,
288 small_integer);
289 // echo pause
290 } else if (strstr(buf, "pause") != NULL) {
291 for (i = 0; i < 2; i++) {
292 alert_msg = strsep(&buf, " ");
293 if (alert_msg == NULL) {
294 fprintf(stderr,
295 "\nError - invalid arguments for pause command\n");
296 break;
297 }
298 }
299 small_integer = atoi(alert_msg);
300 if (small_integer >= 1 && small_integer <= 9) {
301 MsgSendPulse(server_coid, SchedGet(0, 0, NULL), PAUSE_PULSE,
302 small_integer);
303 } else {
304 printf("\nInteger is not between 1 and 9.\n");
305 }
306 // echo start
307 } else if (strstr(buf, "start") != NULL) {
308 MsgSendPulse(server_coid, SchedGet(0, 0, NULL), START_PULSE,
309 small_integer);
310 // echo stop
311 } else if (strstr(buf, "stop") != NULL) {
312 MsgSendPulse(server_coid, SchedGet(0, 0, NULL), STOP_PULSE,
313 small_integer);
314 // echo quit
315 } else if (strstr(buf, "quit") != NULL) {
316 MsgSendPulse(server_coid, SchedGet(0, 0, NULL), QUIT_PULSE,
317 small_integer);
318 } else {
319 alert_msg = strsep(&buf, " ");
320 alert_msg[strcspn(alert_msg, "\r\n")] = 0;
321 fprintf(stderr, "\nError - \'%s\' is not a valid command\n",
322 alert_msg);
323 }
324
325 nb = msg->i.nbytes;
326 }
327
328 _IO_SET_WRITE_NBYTES(ctp, nb);
329
330 if (msg->i.nbytes > 0) {
331 mocb->ocb.flags |= IOFUNC_ATTR_MTIME | IOFUNC_ATTR_CTIME;
332 }
333
334 return (_RESMGR_NPARTS(0));
335 }
336
337 int io_open(resmgr_context_t *ctp, io_open_t *msg, RESMGR_HANDLE_T *handle,
338 void *extra) {
339
340 if ((server_coid = name_open(RESOURCE_NAME, 0)) == -1) {
341 perror("\nError - name_open failed\n");
342 return EXIT_FAILURE;
343 }
344 return (iofunc_open_default(ctp, msg, &handle->attr, extra));
345 }
346
347 char* get_interval_string(int tstPlusTsb) {
348 // return the corresponding string
349 switch (tstPlusTsb) { // tst tsb noi pattern
350 case (2 + 4): // 2 4 4 |1&2&
351 return "|1&2&";
352 break;
353 case (3 + 4): // 3 4 6 |1&2&3&
354 return "|1&2&3&";
355 break;
356 case (4 + 4): // 4 4 8 |1&2&3&4&
357 return "|1&2&3&4&";
358 break;
359 case (5 + 4): // 5 4 10 |1&2&3&4-5-
360 return "|1&2&3&4-5-";
361 break;
362 case (3 + 8): // 3 8 6 |1-2-3-
363 return "|1-2-3-";
364 break;
365 case (6 + 8): // 6 8 6 |1&a2&a
366 return "|1&a2&a";
367 break;
368 case (9 + 8): // 9 8 9 |1&a2&a3&a
369 return "|1&a2&a3&a";
370 break;
371 case (12 + 8): // 12 8 12 |1&a2&a3&a4&a
372 return "|1&a2&a3&a4&a";
373 break;
374 default:
375 return "|1&2&";
376 break;
377 }
378 }
379
380 void start_interval_timer(struct itimerspec *itime, timer_t timer_id,
381 Metronome_t *Metronome) {
382 // calculate timing
383 Metronome->beatsPerSecond = (double) 60 / Metronome->beatsPerMinute;
384 Metronome->beatsPerMeasure = Metronome->beatsPerSecond * 2;
385 Metronome->secondsPerInterval = Metronome->beatsPerMeasure
386 / Metronome->timeSignatureBottom;
387 Metronome->nanoSeconds = (Metronome->secondsPerInterval
388 - (int) Metronome->secondsPerInterval) * 1e+9;
389
390 // start timer
391 itime->it_value.tv_sec = 1;
392 itime->it_value.tv_nsec = 0;
393 itime->it_interval.tv_sec = Metronome->secondsPerInterval;
394 itime->it_interval.tv_nsec = Metronome->nanoSeconds;
395 timer_settime(timer_id, 0, itime, NULL);
396 }
397
398 metro_ocb_t* metro_ocb_calloc(resmgr_context_t *ctp, ioattr_t *mattr) {
399 metro_ocb_t *mocb;
400 mocb = calloc(1, sizeof(metro_ocb_t));
401 mocb->ocb.offset = 0;
402 return (mocb);
403 }
404
405 void metro_ocb_free(metro_ocb_t *mocb) {
406 free(mocb);
407 }
408