/* Reflow Oven Controller * * Copyright (C) 2021 Mario Hüttel * * This file is part of the Reflow Oven Controller Project. * * The reflow oven controller is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * The Reflow Oven Control Firmware is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with the reflow oven controller project. * If not, see . */ /** * @addtogroup temp-profile * @{ */ #include #include #include #include #include #include struct pl_command_list_map { enum pl_command_type command; const char * const token; uint8_t expected_param_count; }; /** * @brief This list stores the command tokens and the expected number of arguments for each command */ static const struct pl_command_list_map cmd_list_map[_PL_NUM_CMDS] = { {PL_PID_CONF, "pid_conf", 6u}, {PL_SET_TEMP, "temp_set", 1u}, {PL_WAIT_FOR_TEMP, "wait_temp", 1u}, {PL_WAIT_FOR_TIME, "wait_time", 1u}, {PL_SET_RAMP, "temp_ramp", 2u}, {PL_LOUDSPEAKER_SET, "beep", 1u}, {PL_OFF, "temp_off", 0u}, {PL_CLEAR_FLAGS, "clear_flags", 0u}, }; /** * @brief Read a line in the file until a line break is detected or a comment is started. * * In case a comment is detected, it is still read form the file to ensure * the next line is read afterwards * * @param f File to read from * @param buffer Buffer to read in * @param buffsize buffer size * @return 0 if successful, -1 if disk error, */ static int read_line_until_comment(FIL *f, char *buffer, uint32_t buffsize) { char *ptr; uint32_t i; if (!f || !buffsize || !buffer) return -1000; buffer[0] = 0; /* Read the line from file */ ptr = f_gets(buffer, (int)buffsize, f); if (!ptr) { buffer[0] = 0; return -2; } /* Go through the line until we encounter a # sign or the end of line*/ for (i = 0; i < buffsize; i++) { if (buffer[i] == '\n' || buffer[i] == '#') { buffer[i] = 0; break; } else if (buffer[i] == 0) { break; } } return 0; } static const struct pl_command_list_map *string_to_command(const char *str) { uint32_t i; const struct pl_command_list_map *ret = NULL; if (!str) return NULL; for (i = 0u; i < _PL_NUM_CMDS; i++) { if (!strcmp(str, cmd_list_map[i].token)) { ret = &cmd_list_map[i]; break; } } return ret; } static int parse_line(char *line, struct pl_command *cmd) { uint8_t token_idx = 0; char *token; const char * const delim = " \t"; const struct pl_command_list_map *map = NULL; char *endptr; struct pl_command c; if (!line || !cmd) return -1000; token = strtok(line, delim); if (!token) { /* Empty line or command line */ return 1; } while (token && token_idx <= PROFILE_LANG_MAX_NUM_ARGS) { switch (token_idx) { case 0: map = string_to_command(token); c.cmd = map->command; break; default: if (!map) { /* No valid command found */ return -1; } c.params[token_idx - 1] = strtof(token, &endptr); if (endptr == token) { /* Invalid parameter */ return -2; } if (token_idx > map->expected_param_count) return -3; break; } token = strtok(NULL, delim); token_idx++; } if (!map || (token_idx - 1 < map->expected_param_count)) { return -3; } memcpy(cmd, &c, sizeof(struct pl_command)); return 0; } static SlList *copy_and_append_command_to_list(SlList *list, const struct pl_command *cmd) { struct pl_command *alloced_cmd; alloced_cmd = (struct pl_command *)malloc(sizeof(struct pl_command)); memcpy(alloced_cmd, cmd, sizeof(struct pl_command)); list = sl_list_append(list, alloced_cmd); return list; } enum pl_ret_val temp_profile_parse_from_file(const char *filename, SlList **command_list, uint32_t max_len, uint32_t *cmds_parsed) { FIL script_file; FRESULT fres; int res; enum pl_ret_val ret = PL_RET_SUCCESS; char workbuff[256]; struct pl_command temp_command; uint32_t cmd_idx; if (!filename || !command_list || !max_len || !cmds_parsed) return PL_RET_PARAM_ERR; fres = f_open(&script_file, filename, FA_READ); if (fres != FR_OK) { ret = PL_RET_DISK_ERR; goto exit; } cmd_idx = 0; *cmds_parsed = 0; do { /* read in the line */ res = read_line_until_comment(&script_file, workbuff, sizeof(workbuff)); if (res < 0) { ret = PL_RET_DISK_ERR; goto exit_close; } /* Check if list already full */ if (cmd_idx >= max_len) { ret = PL_RET_LIST_FULL; goto exit_close; } /* Parse the line */ res = parse_line(workbuff, &temp_command); if (res < 0) { ret = PL_RET_SCRIPT_ERR; goto exit_close; } else if (res == 0) { cmd_idx++; *cmds_parsed = cmd_idx; /* Append the temp_command to the list */ *command_list = copy_and_append_command_to_list(*command_list, &temp_command); } } while (!f_eof(&script_file)); exit_close: (void)f_close(&script_file); exit: return ret; } static void delete_pl_command(void *cmd) { if (cmd) free(cmd); } void temp_profile_free_command_list(SlList **list) { sl_list_free_full(*list, delete_pl_command); *list = NULL; } /** @} */