]> sigrok.org Git - libsigrok.git/blame - hardware/hameg-hmo/protocol.c
scpi.c: Minor cleanups, cosmetics.
[libsigrok.git] / hardware / hameg-hmo / protocol.c
CommitLineData
06a3e78a
DJ
1/*
2 * This file is part of the libsigrok project.
3 *
4 * Copyright (C) 2013 poljar (Damir Jelić) <poljarinho@gmail.com>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "protocol.h"
21
13f2b9d7
DJ
22static const char *manufacturers[] = {
23 "HAMEG",
24};
25
26static const char *hameg_scpi_dialect[] = {
27 [SCPI_CMD_GET_DIG_DATA] = ":POD%d:DATA?",
28 [SCPI_CMD_GET_TIMEBASE] = ":TIM:SCAL?",
29 [SCPI_CMD_SET_TIMEBASE] = ":TIM:SCAL %E",
30 [SCPI_CMD_GET_COUPLING] = ":CHAN%d:COUP?",
31 [SCPI_CMD_SET_COUPLING] = ":CHAN%d:COUP %s",
32 [SCPI_CMD_GET_ANALOG_DATA] = ":CHAN%d:DATA?",
33 [SCPI_CMD_GET_VERTICAL_DIV] = ":CHAN%d:SCAL?",
34 [SCPI_CMD_SET_VERTICAL_DIV] = ":CHAN%d:SCAL %E",
35 [SCPI_CMD_GET_DIG_POD_STATE] = ":POD%d:STAT?",
36 [SCPI_CMD_SET_DIG_POD_STATE] = ":POD%d:STAT %d",
37 [SCPI_CMD_GET_TRIGGER_SLOPE] = ":TRIG:A:EDGE:SLOP?",
38 [SCPI_CMD_SET_TRIGGER_SLOPE] = ":TRIG:A:EDGE:SLOP %s",
39 [SCPI_CMD_GET_TRIGGER_SOURCE] = ":TRIG:A:SOUR?",
40 [SCPI_CMD_SET_TRIGGER_SOURCE] = ":TRIG:A:SOUR %s",
41 [SCPI_CMD_GET_DIG_CHAN_STATE] = ":LOG%d:STAT?",
42 [SCPI_CMD_SET_DIG_CHAN_STATE] = ":LOG%d:STAT %d",
43 [SCPI_CMD_GET_VERTICAL_OFFSET] = ":CHAN%d:POS?",
44 [SCPI_CMD_GET_HORIZ_TRIGGERPOS] = ":TIM:POS?",
45 [SCPI_CMD_SET_HORIZ_TRIGGERPOS] = ":TIM:POS %E",
46 [SCPI_CMD_GET_ANALOG_CHAN_STATE] = ":CHAN%d:STAT?",
47 [SCPI_CMD_SET_ANALOG_CHAN_STATE] = ":CHAN%d:STAT %d",
48};
49
50static const int32_t hmo_hwcaps[] = {
51 SR_CONF_OSCILLOSCOPE,
52 SR_CONF_TRIGGER_SOURCE,
53 SR_CONF_TIMEBASE,
54 SR_CONF_NUM_TIMEBASE,
55 SR_CONF_TRIGGER_SLOPE,
56 SR_CONF_HORIZ_TRIGGERPOS,
57};
58
59static const int32_t hmo_analog_caps[] = {
60 SR_CONF_NUM_VDIV,
61 SR_CONF_COUPLING,
62 SR_CONF_VDIV,
63};
64
65static const char *hmo_coupling_options[] = {
66 "AC",
67 "ACL",
68 "DC",
69 "GND",
70 NULL,
71};
72
73static const char *scope_trigger_slopes[] = {
74 "POS",
75 "NEG",
76 NULL,
77};
78
79static const char *hmo_compact2_trigger_sources[] = {
80 "CH1",
81 "CH2",
82 "LINE",
83 "EXT",
84 "D0",
85 "D1",
86 "D2",
87 "D3",
88 "D4",
89 "D5",
90 "D6",
91 "D7",
92 NULL,
93};
94
95static const char *hmo_compact4_trigger_sources[] = {
96 "CH1",
97 "CH2",
98 "CH3",
99 "CH4",
100 "LINE",
101 "EXT",
102 "D0",
103 "D1",
104 "D2",
105 "D3",
106 "D4",
107 "D5",
108 "D6",
109 "D7",
110 NULL,
111};
112
113static const uint64_t hmo_timebases[][2] = {
114 /* nanoseconds */
115 { 2, 1000000000 },
116 { 5, 1000000000 },
117 { 10, 1000000000 },
118 { 20, 1000000000 },
119 { 50, 1000000000 },
120 { 100, 1000000000 },
121 { 200, 1000000000 },
122 { 500, 1000000000 },
123 /* microseconds */
124 { 1, 1000000 },
125 { 2, 1000000 },
126 { 5, 1000000 },
127 { 10, 1000000 },
128 { 20, 1000000 },
129 { 50, 1000000 },
130 { 100, 1000000 },
131 { 200, 1000000 },
132 { 500, 1000000 },
133 /* milliseconds */
134 { 1, 1000 },
135 { 2, 1000 },
136 { 5, 1000 },
137 { 10, 1000 },
138 { 20, 1000 },
139 { 50, 1000 },
140 { 100, 1000 },
141 { 200, 1000 },
142 { 500, 1000 },
143 /* seconds */
144 { 1, 1 },
145 { 2, 1 },
146 { 5, 1 },
147 { 10, 1 },
148 { 20, 1 },
149 { 50, 1 },
150};
151
152static const uint64_t hmo_vdivs[][2] = {
153 /* millivolts */
154 { 1, 1000 },
155 { 2, 1000 },
156 { 5, 1000 },
157 { 10, 1000 },
158 { 20, 1000 },
159 { 50, 1000 },
160 { 100, 1000 },
161 { 200, 1000 },
162 { 500, 1000 },
163 /* volts */
164 { 1, 1 },
165 { 2, 1 },
166 { 5, 1 },
167 { 10, 1 },
168};
169
170static const char *scope_analog_probe_names[] = {
171 "CH1",
172 "CH2",
173 "CH3",
174 "CH4",
175};
176
177static const char *scope_digital_probe_names[] = {
178 "D0",
179 "D1",
180 "D2",
181 "D3",
182 "D4",
183 "D5",
184 "D6",
185 "D7",
186 "D8",
187 "D9",
188 "D10",
189 "D11",
190 "D12",
191 "D13",
192 "D14",
193 "D15",
194};
195
196static struct scope_config scope_models[] = {
197 {
198 .name = {"HMO722", "HMO1022", "HMO1522", "HMO2022", NULL},
199 .analog_channels = 2,
200 .digital_channels = 8,
201 .digital_pods = 1,
202
203 .analog_names = &scope_analog_probe_names,
204 .digital_names = &scope_digital_probe_names,
205
206 .hw_caps = &hmo_hwcaps,
207 .num_hwcaps = ARRAY_SIZE(hmo_hwcaps),
208
209 .analog_hwcaps = &hmo_analog_caps,
210 .num_analog_hwcaps = ARRAY_SIZE(hmo_analog_caps),
211
212 .coupling_options = &hmo_coupling_options,
213 .trigger_sources = &hmo_compact2_trigger_sources,
214 .trigger_slopes = &scope_trigger_slopes,
215
216 .timebases = &hmo_timebases,
217 .num_timebases = ARRAY_SIZE(hmo_timebases),
218
219 .vdivs = &hmo_vdivs,
220 .num_vdivs = ARRAY_SIZE(hmo_vdivs),
221
222 .num_xdivs = 12,
223 .num_ydivs = 8,
224
225 .scpi_dialect = &hameg_scpi_dialect,
226 },
227 {
228 .name = {"HMO724", "HMO1024", "HMO1524", "HMO2024", NULL},
229 .analog_channels = 4,
230 .digital_channels = 8,
231 .digital_pods = 1,
232
233 .analog_names = &scope_analog_probe_names,
234 .digital_names = &scope_digital_probe_names,
235
236 .hw_caps = &hmo_hwcaps,
237 .num_hwcaps = ARRAY_SIZE(hmo_hwcaps),
238
239 .analog_hwcaps = &hmo_analog_caps,
240 .num_analog_hwcaps = ARRAY_SIZE(hmo_analog_caps),
241
242 .coupling_options = &hmo_coupling_options,
243 .trigger_sources = &hmo_compact4_trigger_sources,
244 .trigger_slopes = &scope_trigger_slopes,
245
246 .timebases = &hmo_timebases,
247 .num_timebases = ARRAY_SIZE(hmo_timebases),
248
249 .vdivs = &hmo_vdivs,
250 .num_vdivs = ARRAY_SIZE(hmo_vdivs),
251
252 .num_xdivs = 12,
253 .num_ydivs = 8,
254
255 .scpi_dialect = &hameg_scpi_dialect,
256 },
257};
258
259static int check_manufacturer(const char *manufacturer)
260{
261 unsigned int i;
262
263 for (i = 0; i < ARRAY_SIZE(manufacturers); ++i)
264 if (strcmp(manufacturer, manufacturers[i]) == 0)
265 return SR_OK;
266
267 return SR_ERR;
268}
269
270static void scope_state_dump(struct scope_config *config,
271 struct scope_state *state)
272{
273 unsigned int i;
274
275 for (i = 0; i < config->analog_channels; ++i) {
276 sr_info("State of analog channel %d -> %s : %s %.3eV %.3e offset", i+1,
277 state->analog_channels[i].state ? "On" : "Off",
278 (*config->coupling_options)[state->analog_channels[i].coupling],
279 state->analog_channels[i].vdiv, state->analog_channels[i].vertical_offset);
280 }
281
282 for (i = 0; i < config->digital_channels; ++i) {
283 sr_info("State of digital channel %d -> %s", i,
284 state->digital_channels[i] ? "On" : "Off");
285 }
286
287 for (i = 0; i < config->digital_pods; ++i) {
288 sr_info("State of digital POD %d -> %s", i,
289 state->digital_pods[i] ? "On" : "Off");
290 }
291
292 sr_info("Current timebase: %.2es", state->timebase);
293 sr_info("Current trigger: %s (source), %s (slope) %.2e (offset)",
294 (*config->trigger_sources)[state->trigger_source],
295 (*config->trigger_slopes)[state->trigger_slope],
296 state->horiz_triggerpos);
297}
298
299static int scope_state_get_array_option(struct sr_serial_dev_inst *serial,
300 const char *command, const char *(*array)[],
301 int *result)
302{
303 char *tmp;
304 unsigned int i;
305
306 if (sr_scpi_get_string(serial, command, &tmp) != SR_OK) {
307 if (tmp)
308 g_free(tmp);
309 return SR_ERR;
310 }
311
312 for (i = 0; (*array)[i]; ++i) {
313 if (!g_strcmp0(tmp, (*array)[i])) {
314 *result = i;
315 g_free(tmp);
316 tmp = NULL;
317 break;
318 }
319 }
320
321 if (tmp) {
322 g_free(tmp);
323 return SR_ERR;
324 }
325
326 return SR_OK;
327}
328
329static int analog_channel_state_get(struct sr_serial_dev_inst *serial,
330 struct scope_config *config,
331 struct scope_state *state)
332{
333 unsigned int i;
334 char command[MAX_COMMAND_SIZE];
335
336 for (i = 0; i < config->analog_channels; ++i) {
337 g_snprintf(command, sizeof(command),
338 (*config->scpi_dialect)[SCPI_CMD_GET_ANALOG_CHAN_STATE],
339 i + 1);
340
341 if (sr_scpi_get_bool(serial, command,
342 &state->analog_channels[i].state) != SR_OK)
343 return SR_ERR;
344
345 g_snprintf(command, sizeof(command),
346 (*config->scpi_dialect)[SCPI_CMD_GET_VERTICAL_DIV],
347 i + 1);
348
349 if (sr_scpi_get_float(serial, command,
350 &state->analog_channels[i].vdiv) != SR_OK)
351 return SR_ERR;
352
353 g_snprintf(command, sizeof(command),
354 (*config->scpi_dialect)[SCPI_CMD_GET_VERTICAL_OFFSET],
355 i + 1);
356
357 if (sr_scpi_get_float(serial, command,
358 &state->analog_channels[i].vertical_offset) != SR_OK)
359 return SR_ERR;
360
361 g_snprintf(command, sizeof(command),
362 (*config->scpi_dialect)[SCPI_CMD_GET_COUPLING],
363 i + 1);
364
365 if (scope_state_get_array_option(serial, command, config->coupling_options,
366 &state->analog_channels[i].coupling) != SR_OK)
367 return SR_ERR;
368 }
369
370 return SR_OK;
371}
372
373static int digital_channel_state_get(struct sr_serial_dev_inst *serial,
374 struct scope_config *config,
375 struct scope_state *state)
376{
377 unsigned int i;
378 char command[MAX_COMMAND_SIZE];
379
380 for (i = 0; i < config->digital_channels; ++i) {
381 g_snprintf(command, sizeof(command),
382 (*config->scpi_dialect)[SCPI_CMD_GET_DIG_CHAN_STATE],
383 i);
384
385 if (sr_scpi_get_bool(serial, command,
386 &state->digital_channels[i]) != SR_OK)
387 return SR_ERR;
388 }
389
390 for (i = 0; i < config->digital_pods; ++i) {
391 g_snprintf(command, sizeof(command),
392 (*config->scpi_dialect)[SCPI_CMD_GET_DIG_POD_STATE],
393 i + 1);
394
395 if (sr_scpi_get_bool(serial, command,
396 &state->digital_pods[i]) != SR_OK)
397 return SR_ERR;
398 }
399
400 return SR_OK;
401}
402
403SR_PRIV int scope_state_get(struct sr_dev_inst *sdi)
404{
405 struct dev_context *devc;
406 struct scope_state *state;
407 struct scope_config *config;
408
409 devc = sdi->priv;
410 config = devc->model_config;
411 state = devc->model_state;
412
413 if (analog_channel_state_get(sdi->conn, config, state) != SR_OK)
414 return SR_ERR;
415
416 if (digital_channel_state_get(sdi->conn, config, state) != SR_OK)
417 return SR_ERR;
418
419 /* TODO check if value is sensible */
420 if (sr_scpi_get_float(sdi->conn, (*config->scpi_dialect)[SCPI_CMD_GET_TIMEBASE],
421 &state->timebase) != SR_OK)
422 return SR_ERR;
423
424 if (sr_scpi_get_float(sdi->conn, (*config->scpi_dialect)[SCPI_CMD_GET_HORIZ_TRIGGERPOS],
425 &state->horiz_triggerpos) != SR_OK)
426 return SR_ERR;
427
428 if (scope_state_get_array_option(sdi->conn, (*config->scpi_dialect)[SCPI_CMD_GET_TRIGGER_SOURCE],
429 config->trigger_sources, &state->trigger_source) != SR_OK)
430 return SR_ERR;
431
432 if (scope_state_get_array_option(sdi->conn, (*config->scpi_dialect)[SCPI_CMD_GET_TRIGGER_SLOPE],
433 config->trigger_slopes, &state->trigger_slope) != SR_OK)
434 return SR_ERR;
435
436 scope_state_dump(config, state);
437
438 return SR_OK;
439}
440
441SR_PRIV struct scope_state *scope_state_new(struct scope_config *config)
442{
443 struct scope_state *state;
444
445 if (!(state = g_try_malloc0(sizeof(struct scope_state))))
446 return NULL;
447
448 if (!(state->analog_channels = g_try_malloc0_n(config->analog_channels,
449 sizeof(struct analog_channel_state))))
450 goto fail;
451
452 if (!(state->digital_channels = g_try_malloc0_n(config->digital_channels,
453 sizeof(gboolean))))
454 goto fail;
455
456 if (!(state->digital_pods = g_try_malloc0_n(config->digital_pods,
457 sizeof(gboolean))))
458 goto fail;
459
460 return state;
461
462fail:
463 if (state->analog_channels)
464 g_free(state->analog_channels);
465 if (state->digital_channels)
466 g_free(state->digital_channels);
467 if (state->digital_pods)
468 g_free(state->digital_pods);
469 g_free(state);
470
471 return NULL;
472}
473
474SR_PRIV void scope_state_free(struct scope_state *state)
475{
476 g_free(state->analog_channels);
477 g_free(state->digital_channels);
478 g_free(state->digital_pods);
479 g_free(state);
480}
481
482SR_PRIV int hmo_init_device(struct sr_dev_inst *sdi)
483{
484 char tmp[25];
485 int model_index;
486 unsigned int i;
487 unsigned int j;
488
489 struct sr_probe *probe;
490 struct dev_context *devc;
491
492 devc = sdi->priv;
493 model_index = -1;
494
495 /* Find the exact model */
496 for (i = 0; i < ARRAY_SIZE(scope_models); i++) {
497 for (j = 0; scope_models[i].name[j]; j++) {
498 if (!strcmp(sdi->model, scope_models[i].name[j])) {
499 model_index = i;
500 break;
501 }
502 }
503 if (model_index != -1)
504 break;
505 }
506
507 if (model_index == -1) {
508 sr_dbg("Unsupported HMO device");
509 return SR_ERR_NA;
510 }
511
512 if (!(devc->analog_groups = g_try_malloc0(sizeof(struct sr_probe_group) *
513 scope_models[model_index].analog_channels)))
514 return SR_ERR_MALLOC;
515
516 if (!(devc->digital_groups = g_try_malloc0(sizeof(struct sr_probe_group) *
517 scope_models[model_index].digital_pods)))
518 return SR_ERR_MALLOC;
519
520 /* Add analog channels */
521 for (i = 0; i < scope_models[model_index].analog_channels; i++) {
522 if (!(probe = sr_probe_new(i, SR_PROBE_ANALOG, TRUE,
523 (*scope_models[model_index].analog_names)[i])))
524 return SR_ERR_MALLOC;
525 sdi->probes = g_slist_append(sdi->probes, probe);
526
527 devc->analog_groups[i].name = (char *) (*scope_models[model_index].analog_names)[i];
528 devc->analog_groups[i].probes = g_slist_append(NULL, probe);
529
530 sdi->probe_groups = g_slist_append(sdi->probe_groups,
531 &devc->analog_groups[i]);
532 }
533
534 /* Add digital probe groups */
535 for (i = 0; i < scope_models[model_index].digital_pods; ++i) {
536 g_snprintf(tmp, 25, "POD%d", i);
537 devc->digital_groups[i].name = g_strdup(tmp);
538 sdi->probe_groups = g_slist_append(sdi->probe_groups,
539 &devc->digital_groups[i < 8 ? 0 : 1]);
540 }
541
542 /* Add digital channels */
543 for (i = 0; i < scope_models[model_index].digital_channels; i++) {
544 if (!(probe = sr_probe_new(i, SR_PROBE_LOGIC, TRUE,
545 (*scope_models[model_index].digital_names)[i])))
546 return SR_ERR_MALLOC;
547 sdi->probes = g_slist_append(sdi->probes, probe);
548
549 devc->digital_groups[i < 8 ? 0 : 1].probes = g_slist_append(devc->digital_groups[i < 8 ? 0 : 1].probes,
550 probe);
551 }
552
553 devc->model_config = &scope_models[model_index];
554 devc->frame_limit = 0;
555
556 if (!(devc->model_state = scope_state_new(devc->model_config)))
557 return SR_ERR_MALLOC;
558
559 return SR_OK;
560}
561
562SR_PRIV struct sr_dev_inst *hameg_probe_serial_device(const char *serial_device,
563 const char *serial_options)
564{
565 struct sr_dev_inst *sdi;
566 struct dev_context *devc;
567 struct sr_scpi_hw_info *hw_info;
568 struct sr_serial_dev_inst *serial;
569
570 sdi = NULL;
571 devc = NULL;
572 serial = NULL;
573 hw_info = NULL;
574
575 if (!(serial = sr_serial_dev_inst_new(serial_device, serial_options)))
576 goto fail;
577
578 sr_info("Probing %s.", serial_device);
579 if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
580 goto fail;
581
582 if (sr_scpi_get_hw_id(serial, &hw_info) != SR_OK) {
583 sr_info("Couldn't get IDN response");
584 goto fail;
585 }
586
587 if (check_manufacturer(hw_info->manufacturer) != SR_OK)
588 goto fail;
589
590 if (!(sdi = sr_dev_inst_new(0, SR_ST_ACTIVE,
591 hw_info->manufacturer, hw_info->model,
592 hw_info->firmware_version))) {
593 goto fail;
594 }
595 sr_scpi_hw_info_free(hw_info);
596 hw_info = NULL;
597
598 if (!(devc = g_try_malloc0(sizeof(struct dev_context))))
599 goto fail;
600
601 sdi->driver = di;
602 sdi->priv = devc;
603 sdi->inst_type = SR_INST_SERIAL;
604 sdi->conn = serial;
605
606 if (hmo_init_device(sdi) != SR_OK)
607 goto fail;
608
609 return sdi;
610
611fail:
612 if (hw_info)
613 sr_scpi_hw_info_free(hw_info);
614 if (serial)
615 sr_serial_dev_inst_free(serial);
616 if (sdi)
617 sr_dev_inst_free(sdi);
618 if (devc)
619 g_free(devc);
620
621 return NULL;
622}
623
06a3e78a
DJ
624SR_PRIV int hameg_hmo_receive_data(int fd, int revents, void *cb_data)
625{
13f2b9d7
DJ
626 struct sr_probe *probe;
627 struct sr_dev_inst *sdi;
06a3e78a 628 struct dev_context *devc;
13f2b9d7 629 struct sr_datafeed_packet packet;
06a3e78a
DJ
630
631 (void)fd;
632
633 if (!(sdi = cb_data))
634 return TRUE;
635
636 if (!(devc = sdi->priv))
637 return TRUE;
638
639 if (revents == G_IO_IN) {
13f2b9d7
DJ
640 probe = devc->current_probe->data;
641
642 switch (probe->type) {
643 case SR_PROBE_ANALOG:
644 {
645 GArray *data;
646 struct sr_datafeed_analog analog;
647
648 if (sr_scpi_get_floatv(sdi->conn, NULL, &data) != SR_OK) {
649 if (data)
650 g_array_free(data, TRUE);
651
652 return TRUE;
653 }
654
655 packet.type = SR_DF_FRAME_BEGIN;
656 sr_session_send(sdi, &packet);
657
658 analog.probes = g_slist_append(NULL, probe);
659 analog.num_samples = data->len;
660 analog.data = (float *) data->data;
661 analog.mq = SR_MQ_VOLTAGE;
662 analog.unit = SR_UNIT_VOLT;
663 analog.mqflags = 0;
664 packet.type = SR_DF_ANALOG;
665 packet.payload = &analog;
666 sr_session_send(cb_data, &packet);
667 g_slist_free(analog.probes);
668 g_array_free(data, TRUE);
669 }
670 break;
671
672 case SR_PROBE_LOGIC:
673 {
674 GArray *data;
675 struct sr_datafeed_logic logic;
676
677 if (sr_scpi_get_uint8v(sdi->conn, NULL, &data) != SR_OK) {
678 if (data)
679 g_free(data);
680 return TRUE;
681 }
682
683 packet.type = SR_DF_FRAME_BEGIN;
684 sr_session_send(sdi, &packet);
685
686 logic.length = data->len;
687 logic.unitsize = 1;
688 logic.data = data->data;
689 packet.type = SR_DF_LOGIC;
690 packet.payload = &logic;
691 sr_session_send(cb_data, &packet);
692 g_array_free(data, TRUE);
693 }
694 break;
695
696 default:
697 sr_err("Invalid probe type");
698 break;
699 }
700
701 packet.type = SR_DF_FRAME_END;
702 sr_session_send(sdi, &packet);
703
704 if (devc->current_probe->next) {
705 devc->current_probe = devc->current_probe->next;
706 hmo_request_data(sdi);
707 } else if (++devc->num_frames == devc->frame_limit) {
708 packet.type = SR_DF_END;
709 sr_session_send(sdi, &packet);
710 sdi->driver->dev_acquisition_stop(sdi, cb_data);
711 } else {
712 devc->current_probe = devc->enabled_probes;
713 hmo_request_data(sdi);
714 }
06a3e78a
DJ
715 }
716
717 return TRUE;
718}