| /**************************************************************************** |
| * apps/testing/monkey/monkey_recorder.c |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. The |
| * ASF licenses this file to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the |
| * License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations |
| * under the License. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <inttypes.h> |
| #include <limits.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| #include "monkey_assert.h" |
| #include "monkey_log.h" |
| #include "monkey_dev.h" |
| #include "monkey_recorder.h" |
| #include "monkey_utils.h" |
| |
| /**************************************************************************** |
| * Pre-processor Definitions |
| ****************************************************************************/ |
| |
| #define MONKEY_REC_HEAD_FMT "T%" PRIu32 ",D%X," |
| #define MONKEY_REC_TOUCH_FMT MONKEY_REC_HEAD_FMT "X%d,Y%d,PR%d\n" |
| #define MONKEY_REC_BTN_FMT MONKEY_REC_HEAD_FMT "V%" PRIu32 "\n" |
| #define MONKEY_REC_FILE_EXT ".csv" |
| #define MONKEY_REC_FILE_HEAD "rec_" |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: monkey_recorder_gen_file_path |
| ****************************************************************************/ |
| |
| static void monkey_recorder_gen_file_path(FAR char *path_buf, |
| size_t buf_size, |
| FAR const char *dir_path) |
| { |
| char localtime_str_buf[64]; |
| |
| monkey_get_localtime_str(localtime_str_buf, sizeof(localtime_str_buf)); |
| |
| snprintf(path_buf, buf_size, |
| "%s/" MONKEY_REC_FILE_HEAD "%s" MONKEY_REC_FILE_EXT, |
| dir_path, |
| localtime_str_buf); |
| } |
| |
| /**************************************************************************** |
| * Name: monkey_readline |
| ****************************************************************************/ |
| |
| static int monkey_readline(int fd, FAR char *ptr, int maxlen) |
| { |
| int n; |
| int rc; |
| char c; |
| for (n = 1; n < maxlen; n++) |
| { |
| if ((rc = read(fd, &c, 1)) == 1) |
| { |
| *ptr++ = c; |
| if (c == '\n') |
| { |
| break; |
| } |
| } |
| else if (rc == 0) |
| { |
| if (n == 1) |
| { |
| /* EOF no data read */ |
| |
| return 0; |
| } |
| else |
| { |
| /* EOF, some data read */ |
| |
| break; |
| } |
| } |
| else |
| { |
| /* error */ |
| |
| return -1; |
| } |
| } |
| |
| *ptr = 0; |
| return n; |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: monkey_recorder_create |
| ****************************************************************************/ |
| |
| FAR struct monkey_recorder_s *monkey_recorder_create(FAR const char *path, |
| enum monkey_recorder_mode_e mode) |
| { |
| char file_path[PATH_MAX]; |
| FAR const char *path_ptr; |
| int oflag; |
| int fd; |
| FAR struct monkey_recorder_s *recorder; |
| |
| MONKEY_ASSERT_NULL(path); |
| |
| recorder = calloc(1, sizeof(struct monkey_recorder_s)); |
| MONKEY_ASSERT_NULL(recorder); |
| |
| if (mode == MONKEY_RECORDER_MODE_RECORD) |
| { |
| if (!monkey_dir_check(path)) |
| { |
| goto failed; |
| } |
| |
| monkey_recorder_gen_file_path(file_path, |
| sizeof(file_path), |
| path); |
| path_ptr = file_path; |
| oflag = O_WRONLY | O_CREAT; |
| } |
| else |
| { |
| path_ptr = path; |
| oflag = O_RDONLY; |
| } |
| |
| fd = open(path_ptr, oflag, 0666); |
| if (fd < 0) |
| { |
| MONKEY_LOG_ERROR("open %s failed: %d", path_ptr, errno); |
| goto failed; |
| } |
| |
| recorder->fd = fd; |
| recorder->mode = mode; |
| |
| MONKEY_LOG_NOTICE("open %s success, fd = %d, mode = %d", |
| path_ptr, fd, mode); |
| |
| return recorder; |
| |
| failed: |
| if (fd > 0) |
| { |
| close(fd); |
| } |
| |
| if (recorder) |
| { |
| free(recorder); |
| } |
| |
| return NULL; |
| } |
| |
| /**************************************************************************** |
| * Name: monkey_recorder_delete |
| ****************************************************************************/ |
| |
| void monkey_recorder_delete(FAR struct monkey_recorder_s *recorder) |
| { |
| MONKEY_ASSERT_NULL(recorder); |
| if (recorder->fd > 0) |
| { |
| MONKEY_LOG_NOTICE("close fd: %d", recorder->fd); |
| close(recorder->fd); |
| } |
| |
| free(recorder); |
| } |
| |
| /**************************************************************************** |
| * Name: monkey_recorder_write |
| ****************************************************************************/ |
| |
| enum monkey_recorder_res_e monkey_recorder_write( |
| FAR struct monkey_recorder_s *recorder, |
| FAR const struct monkey_dev_state_s *state) |
| { |
| char buffer[128]; |
| int len = -1; |
| int ret; |
| uint32_t cur_tick; |
| |
| MONKEY_ASSERT_NULL(recorder); |
| MONKEY_ASSERT(recorder->mode == MONKEY_RECORDER_MODE_RECORD); |
| |
| cur_tick = monkey_tick_get(); |
| |
| switch (MONKEY_GET_DEV_TYPE(state->type)) |
| { |
| case MONKEY_DEV_TYPE_TOUCH: |
| len = snprintf(buffer, sizeof(buffer), MONKEY_REC_TOUCH_FMT, |
| cur_tick, |
| state->type, |
| state->data.touch.x, |
| state->data.touch.y, |
| state->data.touch.is_pressed); |
| break; |
| |
| case MONKEY_DEV_TYPE_BUTTON: |
| len = snprintf(buffer, sizeof(buffer), MONKEY_REC_BTN_FMT, |
| cur_tick, |
| state->type, |
| state->data.button.value); |
| break; |
| |
| default: |
| return MONKEY_RECORDER_RES_DEV_TYPE_ERROR; |
| } |
| |
| if (len <= 0) |
| { |
| return MONKEY_RECORDER_RES_PARSER_ERROR; |
| } |
| |
| ret = write(recorder->fd, buffer, len); |
| if (ret < 0) |
| { |
| return MONKEY_RECORDER_RES_WRITE_ERROR; |
| } |
| |
| return MONKEY_RECORDER_RES_OK; |
| } |
| |
| /**************************************************************************** |
| * Name: monkey_recorder_read |
| ****************************************************************************/ |
| |
| enum monkey_recorder_res_e monkey_recorder_read( |
| FAR struct monkey_recorder_s *recorder, |
| FAR struct monkey_dev_state_s *state, |
| FAR uint32_t *time_stamp) |
| { |
| char buffer[128]; |
| int dev_type; |
| int converted; |
| int ret; |
| |
| MONKEY_ASSERT_NULL(recorder); |
| MONKEY_ASSERT(recorder->mode == MONKEY_RECORDER_MODE_PLAYBACK); |
| |
| ret = monkey_readline(recorder->fd, buffer, sizeof(buffer)); |
| |
| if (ret < 0) |
| { |
| return MONKEY_RECORDER_RES_READ_ERROR; |
| } |
| |
| if (ret == 0) |
| { |
| return MONKEY_RECORDER_RES_END_OF_FILE; |
| } |
| |
| /* Read head to get device type */ |
| |
| converted = sscanf(buffer, MONKEY_REC_HEAD_FMT, time_stamp, &dev_type); |
| |
| if (converted != 2) |
| { |
| return MONKEY_RECORDER_RES_PARSER_ERROR; |
| } |
| |
| state->type = dev_type; |
| |
| /* Get data */ |
| |
| switch (MONKEY_GET_DEV_TYPE(state->type)) |
| { |
| case MONKEY_DEV_TYPE_TOUCH: |
| converted = sscanf(buffer, |
| MONKEY_REC_TOUCH_FMT, |
| time_stamp, |
| &dev_type, |
| &state->data.touch.x, |
| &state->data.touch.y, |
| &state->data.touch.is_pressed); |
| if (converted != 5) |
| { |
| return MONKEY_RECORDER_RES_PARSER_ERROR; |
| } |
| break; |
| |
| case MONKEY_DEV_TYPE_BUTTON: |
| converted = sscanf(buffer, |
| MONKEY_REC_BTN_FMT, |
| time_stamp, |
| &dev_type, |
| &state->data.button.value); |
| if (converted != 3) |
| { |
| return MONKEY_RECORDER_RES_PARSER_ERROR; |
| } |
| break; |
| |
| default: |
| return MONKEY_RECORDER_RES_DEV_TYPE_ERROR; |
| } |
| |
| return MONKEY_RECORDER_RES_OK; |
| } |
| |
| /**************************************************************************** |
| * Name: monkey_recorder_reset |
| ****************************************************************************/ |
| |
| enum monkey_recorder_res_e monkey_recorder_reset( |
| FAR struct monkey_recorder_s *recorder) |
| { |
| MONKEY_ASSERT_NULL(recorder); |
| lseek(recorder->fd, 0, SEEK_SET); |
| return MONKEY_RECORDER_RES_OK; |
| } |