[asterisk-commits] twilson: branch twilson/config_work r367118 - in /team/twilson/config_work: a...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Sun May 20 11:03:35 CDT 2012
Author: twilson
Date: Sun May 20 11:03:22 2012
New Revision: 367118
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=367118
Log:
Save work on hi-lo guessing game demo
Still some cleanup to do, but a good time to save
Modified:
team/twilson/config_work/apps/app_skel.c
team/twilson/config_work/configs/app_skel.conf.sample
Modified: team/twilson/config_work/apps/app_skel.c
URL: http://svnview.digium.com/svn/asterisk/team/twilson/config_work/apps/app_skel.c?view=diff&rev=367118&r1=367117&r2=367118
==============================================================================
--- team/twilson/config_work/apps/app_skel.c (original)
+++ team/twilson/config_work/apps/app_skel.c Sun May 20 11:03:22 2012
@@ -38,6 +38,7 @@
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+#include <math.h> /* log10 */
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
@@ -46,6 +47,7 @@
#include "asterisk/app.h"
#include "asterisk/config.h"
#include "asterisk/config_options.h"
+#include "asterisk/say.h"
#include "asterisk/astobj2.h"
#include "asterisk/acl.h"
#include "asterisk/netsock2.h"
@@ -53,112 +55,163 @@
#include "asterisk/cli.h"
/*** DOCUMENTATION
- <application name="Skel" language="en_US">
+ <application name="SkelGuessNumber" language="en_US">
<synopsis>
- Simple one line explaination.
+ An example number guessing game
</synopsis>
<syntax>
- <parameter name="dummy" required="true"/>
+ <parameter name="level" required="true"/>
<parameter name="options">
<optionlist>
- <option name="a">
- <para>Option A.</para>
+ <option name="c">
+ <para>The computer should cheat</para>
</option>
- <option name="b">
- <para>Option B.</para>
- </option>
- <option name="c">
- <para>Option C.</para>
+ <option name="n">
+ <para>How many games to play before hanging up</para>
</option>
</optionlist>
</parameter>
</syntax>
<description>
- <para>This application is a template to build other applications from.
- It shows you the basic structure to create your own Asterisk applications.</para>
+ <para>This simple number guessing application is a template to build other applications
+ from. It shows you the basic structure to create your own Asterisk applications.</para>
</description>
</application>
***/
-static char *app = "Skel";
+static char *app = "SkelGuessNumber";
enum option_flags {
- OPTION_A = (1 << 0),
- OPTION_B = (1 << 1),
- OPTION_C = (1 << 2),
+ OPTION_CHEAT = (1 << 0),
+ OPTION_NUMGAMES = (1 << 1),
};
enum option_args {
- OPTION_ARG_B = 0,
- OPTION_ARG_C = 1,
+ OPTION_ARG_NUMGAMES,
/* This *must* be the last value in this enum! */
- OPTION_ARG_ARRAY_SIZE = 2,
+ OPTION_ARG_ARRAY_SIZE,
};
AST_APP_OPTIONS(app_opts,{
- AST_APP_OPTION('a', OPTION_A),
- AST_APP_OPTION_ARG('b', OPTION_B, OPTION_ARG_B),
- AST_APP_OPTION_ARG('c', OPTION_C, OPTION_ARG_C),
+ AST_APP_OPTION('c', OPTION_CHEAT),
+ AST_APP_OPTION_ARG('n', OPTION_NUMGAMES, OPTION_ARG_NUMGAMES),
});
+/*! \brief A structure to hold global configuration-related options */
struct skel_global_config {
AST_DECLARE_STRING_FIELDS(
- AST_STRING_FIELD(foo);
- AST_STRING_FIELD(bar);
- AST_STRING_FIELD(baz);
+ AST_STRING_FIELD(prompt); /*!< The comma-separated list of sounds to prompt to enter a number */
+ AST_STRING_FIELD(wrong); /*!< The comma-separated list of sounds to indicate a wrong guess */
+ AST_STRING_FIELD(right); /*!< The comma-separated list of sounds to indicate a right guess */
+ AST_STRING_FIELD(high); /*!< The comma-separated list of sounds to indicate a high guess */
+ AST_STRING_FIELD(low); /*!< The comma-separated list of sounds to indicate a low guess */
+ AST_STRING_FIELD(lose); /*!< The comma-separated list of sounds to indicate a lost game */
);
- int blah;
- struct ast_sockaddr bindaddr;
-};
-
-struct skel_item_state {
- size_t non_config_state;
-};
-
-struct skel_item {
+ uint32_t num_games; /*!< The number of games to play before hanging up */
+ unsigned char cheat:1; /*!< Whether the computer can cheat or not */
+};
+
+/*! \brief A structure to maintain level state across reloads */
+struct skel_level_state {
+ uint32_t wins; /*!< How many wins for this level */
+ uint32_t losses; /*!< How many losses for this level */
+ double avg_guesses; /*!< The average number of guesses to win for this level */
+};
+
+/*! \brief Object to hold level config information.
+ * \note This object should hold a reference to an an object that holds state across reloads.
+ * The other fields are just examples of the kind of data that might be stored in an level.
+ */
+struct skel_level {
AST_DECLARE_STRING_FIELDS(
- AST_STRING_FIELD(name);
- AST_STRING_FIELD(description);
+ AST_STRING_FIELD(name); /*!< The name of the level */
);
- struct ast_codec_pref prefs;
- struct ast_format_cap *caps;
- struct ast_ha *permit;
- unsigned int bit1:1;
- unsigned int bit2:1;
- struct skel_item_state *state;
-};
-
-#define PVT_BUCKETS 17
-
+ uint32_t max_num; /*!< The upper value on th range of numbers to guess */
+ uint32_t max_guesses; /*!< The maximum number of guesses before losing */
+ struct skel_level_state *state; /*!< A pointer to level state that must exist across all reloads */
+};
+
+/*! \brief Information about a currently running set of games
+ * \note Because we want to be able to show true running information about the games
+ * regardless of whether or not a reload has modified what the level looks like, it
+ * is important to either copy the information we need from the level to the
+ * current_game struct, or as we do here, store a reference to the level as it is for
+ * the running game.
+ */
+struct skel_current_game {
+ uint32_t total_games; /*! The total number of games for this call to to the app */
+ uint32_t games_left; /*! How many games are left to play in this set */
+ uint32_t cheat; /*! Whether or not cheating was enabled for the game */
+ struct skel_level *level_info; /*! The level information for the running game */
+};
+
+/* Treat the levels as an array--there won't be many and this will maintain the order */
+#define LEVEL_BUCKETS 1
+
+/*! \brief A container that holds all config-related information
+ * \note This object should contain a pointer to structs for global data and containers for
+ * any levels that are configured. Objects of this type will be swapped out on reload. If an
+ * level needs to maintain state across reloads, it needs to allocate a refcounted object to
+ * hold that state and ensure that a reference is passed to that state when creating a new
+ * level for reload. */
struct skel_config {
- struct skel_global_config *general;
- struct ao2_container *items;
-};
-
+ struct skel_global_config *global;
+ struct ao2_container *levels;
+};
+
+/* Config Options API callbacks */
+
+/*! \brief Allocate a skel_config to hold a snapshot of the complete results of parsing a config
+ * \internal
+ * \returns A void pointer to a newly allocated skel_config
+ */
static void *skel_config_alloc(void);
-static void *skel_item_alloc(const char *cat);
-static int skel_item_exists(struct ao2_container *tmp_container, const char *category);
-
-static struct aco_type general_options = {
+
+/*! \brief Allocate a skel_level based on a category in a configuration file
+ * \param cat The category to base the level on
+ * \returns A void pointer to a newly allocated skel_level
+ */
+static void *skel_level_alloc(const char *cat);
+
+/*! \brief Check if a skel level exists in the specified container
+ * \note This function *does not* look for a skel_level in the active container. It is used
+ * internally by the Config Options code to check if an level has already been added to the
+ * container that will be swapped for the live container on a successul reload.
+ *
+ * \param container A non-active container to search for a level
+ * \param category The category associated with the level to check for
+ * \retval 1 The level exists in the container
+ * \retval 0 The level does not exist in the container
+ */
+static int skel_level_exists(struct ao2_container *tmp_container, const char *category);
+
+/*! \brief An aco_type structure to link the "general" and "sounds" categories to the skel_global_config type */
+static struct aco_type global_options = {
.type = ACO_GLOBAL,
- .item_offset = offsetof(struct skel_config, general),
+ .item_offset = offsetof(struct skel_config, global),
.category_match = ACO_WHITELIST,
- .category = "general",
-};
-
-static struct aco_type private_options = {
+ .category = "general|sounds",
+};
+
+/*! \brief An aco_type structure to link the everything but the "general" and "sounds" categories to the skel_level type */
+static struct aco_type level_options = {
.type = ACO_ITEM,
.category_match = ACO_BLACKLIST,
- .category = "general",
- .item_alloc = skel_item_alloc,
- .item_exists = skel_item_exists,
- .item_offset = offsetof(struct skel_config, items),
-};
-
+ .category = "general|sounds",
+ .item_alloc = skel_level_alloc,
+ .item_exists = skel_level_exists,
+ .item_offset = offsetof(struct skel_config, levels),
+};
+
+/*! \brief A global object container that will contain the skel_config that gets swapped out on reloads */
static AO2_GLOBAL_OBJ_STATIC(globals);
+/*! \brief The container of active games */
+static struct ao2_container *games;
+
+/*! \brief Register information about the configs being processed by this module */
CONFIG_INFO_STANDARD(cfg_info, "app_skel.conf", globals, skel_config_alloc,
- .types = ACO_TYPES(&general_options, &private_options),
+ .types = ACO_TYPES(&global_options, &level_options),
);
static void skel_global_config_destructor(void *obj)
@@ -167,44 +220,65 @@
ast_string_field_free_memory(global);
}
+static void skel_game_destructor(void *obj)
+{
+ struct skel_current_game *game = obj;
+ ao2_cleanup(game->level_info);
+}
+
static void skel_state_destructor(void *obj)
{
return;
}
-static void skel_item_destructor(void *obj)
-{
- struct skel_item *item = obj;
- ast_string_field_free_memory(item);
- ao2_cleanup(item->state);
-}
-
-static int skel_item_hash(const void *obj, const int flags)
-{
- const struct skel_item *item = obj;
- const char *name = (flags & OBJ_KEY) ? obj : item->name;
+static struct skel_current_game *skel_game_alloc(struct skel_level *level)
+{
+ struct skel_current_game *game;
+ if (!(game = ao2_alloc(sizeof(struct skel_current_game), skel_game_destructor))) {
+ return NULL;
+ }
+ ao2_ref(level, +1);
+ game->level_info = level;
+ return game;
+}
+
+static void skel_level_destructor(void *obj)
+{
+ struct skel_level *level = obj;
+ ast_string_field_free_memory(level);
+ ao2_cleanup(level->state);
+}
+
+static int skel_level_hash(const void *obj, const int flags)
+{
+ const struct skel_level *level = obj;
+ const char *name = (flags & OBJ_KEY) ? obj : level->name;
return ast_str_case_hash(name);
}
-static int skel_item_cmp(void *obj, void *arg, int flags)
-{
- struct skel_item *one = obj, *two = arg;
+static int skel_level_cmp(void *obj, void *arg, int flags)
+{
+ struct skel_level *one = obj, *two = arg;
const char *match = (flags & OBJ_KEY) ? arg : two->name;
return strcasecmp(one->name, match) ? 0 : (CMP_MATCH | CMP_STOP);
}
/*! \brief A custom bitfield handler
+ * \internal
* \note It is not possible to take the address of a bitfield, therefor all
* bitfields in the config struct will have to use a custom handler
+ * \param opt The opaque config option
+ * \param var The ast_variable containing the option name and value
+ * \param obj The object registerd for this option type
+ * \retval 0 Success
+ * \retval non-zero Failure
*/
static int custom_bitfield_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
{
- struct skel_item *item = obj;
-
- if (!strcasecmp(var->name, "bit1")) {
- item->bit1 = ast_true(var->value);
- } else if (!strcasecmp(var->name, "bit2")) {
- item->bit2 = ast_true(var->value);
+ struct skel_global_config *global = obj;
+
+ if (!strcasecmp(var->name, "cheat")) {
+ global->cheat = ast_true(var->value);
} else {
return -1;
}
@@ -212,22 +286,39 @@
return 0;
}
+static void play_files_helper(struct ast_channel *chan, const char *prompts)
+{
+ char *prompt, *rest = ast_strdupa(prompts);
+
+ ast_stopstream(chan);
+ while ((prompt = strsep(&rest, "&")) && !ast_stream_and_wait(chan, prompt, "")) {
+ ast_stopstream(chan);
+ }
+}
+
static int app_exec(struct ast_channel *chan, const char *data)
{
- int res = 0;
+ int win = 0;
+ uint32_t guesses;
struct ast_flags flags;
char *parse, *opts[OPTION_ARG_ARRAY_SIZE];
+ RAII_VAR(struct skel_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
+ RAII_VAR(struct skel_level *, level, NULL, ao2_cleanup);
+ RAII_VAR(struct skel_current_game *, game, NULL, ao2_cleanup);
AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(dummy);
+ AST_APP_ARG(level);
AST_APP_ARG(options);
);
+ if (!cfg) {
+ ast_log(LOG_ERROR, "Couldn't access configuratino data!\n");
+ return -1;
+ }
+
if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "%s requires an argument (dummy[,options])\n", app);
+ ast_log(LOG_WARNING, "%s requires an argument (level[,options])\n", app);
return -1;
}
-
- /* Do our thing here */
/* We need to make a copy of the input string if we are going to modify it! */
parse = ast_strdupa(data);
@@ -238,98 +329,153 @@
ast_app_parse_options(app_opts, &flags, opts, args.options);
}
- if (!ast_strlen_zero(args.dummy)) {
- ast_log(LOG_NOTICE, "Dummy value is : %s\n", args.dummy);
- }
-
- if (ast_test_flag(&flags, OPTION_A)) {
- ast_log(LOG_NOTICE, "Option A is set\n");
- }
-
- if (ast_test_flag(&flags, OPTION_B)) {
- ast_log(LOG_NOTICE, "Option B is set with : %s\n", opts[OPTION_ARG_B] ? opts[OPTION_ARG_B] : "<unspecified>");
- }
-
- if (ast_test_flag(&flags, OPTION_C)) {
- ast_log(LOG_NOTICE, "Option C is set with : %s\n", opts[OPTION_ARG_C] ? opts[OPTION_ARG_C] : "<unspecified>");
- }
-
- return res;
-}
-
-static struct skel_item *skel_state_alloc(const char *name)
-{
- struct skel_item *item;
-
- if (!(item = ao2_alloc(sizeof(*item), skel_state_destructor))) {
- return NULL;
- }
-
- return item;
-}
-
-static int skel_item_exists(struct ao2_container *tmp_container, const char *category)
-{
- RAII_VAR(struct skel_item *, item, ao2_find(tmp_container, category, OBJ_KEY), ao2_cleanup);
- return item ? 1 : 0;
+ if (ast_strlen_zero(args.level)) {
+ ast_log(LOG_ERROR, "%s requires a level argument\n", app);
+ return -1;
+ }
+
+ if (!(level = ao2_find(cfg->levels, args.level, OBJ_KEY))) {
+ ast_log(LOG_ERROR, "Unknown level: %s\n", args.level);
+ return -1;
+ }
+
+ if (!(game = skel_game_alloc(level))) {
+ return -1;
+ }
+
+ ao2_link(games, game);
+
+ /* Use app-specified values, or the options specified in [general] if they aren't passed to the app */
+ if (!ast_test_flag(&flags, OPTION_NUMGAMES) ||
+ ast_strlen_zero(opts[OPTION_ARG_NUMGAMES]) ||
+ ast_parse_arg(opts[OPTION_ARG_NUMGAMES], PARSE_UINT32, &game->total_games)) {
+ game->total_games = cfg->global->num_games;
+ }
+ game->games_left = game->total_games;
+ game->cheat = ast_test_flag(&flags, OPTION_CHEAT) || cfg->global->cheat;
+
+ for (game->games_left = game->total_games; game->games_left; game->games_left--) {
+ /* Play the prompt */
+ uint32_t num = ast_random() % level->max_num; /* random number between 0 and level->max_num */
+
+ ast_debug(1, "They should totally should guess %u\n", num);
+
+ play_files_helper(chan, cfg->global->prompt);
+ ast_say_number(chan, level->max_num, "", ast_channel_language(chan), "");
+
+ for (guesses = 0; guesses < level->max_guesses; guesses++) {
+ size_t buflen = log10(level->max_num) + 1;
+ char buf[buflen];
+ int guess;
+ buf[buflen] = '\0';
+ /* Read the number pressed */
+ ast_readstring(chan, buf, buflen - 1, 2000, 10000, "");
+ if (ast_parse_arg(buf, PARSE_INT32 | PARSE_IN_RANGE, &guess, 0, level->max_num)) {
+ continue;
+ }
+ if (guess == num && !game->cheat) {
+ /* win */
+ win = 1;
+ play_files_helper(chan, cfg->global->right);
+ guesses++;
+ break;
+ } else if (guess < num) {
+ play_files_helper(chan, cfg->global->low);
+ } else {
+ play_files_helper(chan, cfg->global->high);
+ }
+
+ if (guesses < level->max_guesses - 1) {
+ play_files_helper(chan, cfg->global->wrong);
+ }
+ }
+
+ /* Process game stats */
+ ao2_lock(level->state);
+ if (win) {
+ ++level->state->wins;
+ level->state->avg_guesses = ((level->state->wins - 1) * level->state->avg_guesses + guesses) / level->state->wins;
+ } else {
+ /* lose */
+ level->state->losses++;
+ play_files_helper(chan, cfg->global->lose);
+ }
+ ao2_unlock(level->state);
+ }
+
+ ao2_unlink(games, game);
+
+ return 0;
+}
+
+static struct skel_level *skel_state_alloc(const char *name)
+{
+ struct skel_level *level;
+
+ if (!(level = ao2_alloc(sizeof(*level), skel_state_destructor))) {
+ return NULL;
+ }
+
+ return level;
+}
+
+static int skel_level_exists(struct ao2_container *tmp_container, const char *category)
+{
+ RAII_VAR(struct skel_level *, level, ao2_find(tmp_container, category, OBJ_KEY), ao2_cleanup);
+ return level ? 1 : 0;
}
/*! \brief Look up an existing state object, or create a new one
* \internal
- * \note Since the reload code will create a new item from scratch, it
+ * \note Since the reload code will create a new level from scratch, it
* is important for any state that must persist between reloads to be
- * in a separate refcounted object. This function allows the item alloc
+ * in a separate refcounted object. This function allows the level alloc
* function to get a ref to an existing state object if it exists,
* otherwise it will return a reference to a newly allocated state object.
*/
static void *skel_find_or_create_state(const char *category)
{
RAII_VAR(struct skel_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
- RAII_VAR(struct skel_item *, item, NULL, ao2_cleanup);
- if (!cfg || !cfg->items || !(item = ao2_find(cfg->items, category, OBJ_KEY))) {
+ RAII_VAR(struct skel_level *, level, NULL, ao2_cleanup);
+ if (!cfg || !cfg->levels || !(level = ao2_find(cfg->levels, category, OBJ_KEY))) {
return skel_state_alloc(category);
}
- ao2_ref(item->state, +1);
- return item->state;
-}
-
-static void *skel_item_alloc(const char *cat)
-{
- struct skel_item *item;
-
- if (!(item = ao2_alloc(sizeof(*item), skel_item_destructor))) {
- return NULL;
- }
-
- if (ast_string_field_init(item, 128)) {
- ao2_ref(item, -1);
- return NULL;
- }
-
- if (!(item->caps = ast_format_cap_alloc_nolock())) {
- ao2_ref(item, -1);
- return NULL;
- }
-
- /* Since the item has state information that needs to persist between reloads,
- * it is important to handle that here in the item's allocation function.
+ ao2_ref(level->state, +1);
+ return level->state;
+}
+
+static void *skel_level_alloc(const char *cat)
+{
+ struct skel_level *level;
+
+ if (!(level = ao2_alloc(sizeof(*level), skel_level_destructor))) {
+ return NULL;
+ }
+
+ if (ast_string_field_init(level, 128)) {
+ ao2_ref(level, -1);
+ return NULL;
+ }
+
+ /* Since the level has state information that needs to persist between reloads,
+ * it is important to handle that here in the level's allocation function.
* If not separated out into its own object, the data would be destroyed on
* reload. */
- if (!(item->state = skel_find_or_create_state(cat))) {
- ao2_ref(item, -1);
- return NULL;
- }
-
- ast_string_field_set(item, name, cat);
-
- return item;
+ if (!(level->state = skel_find_or_create_state(cat))) {
+ ao2_ref(level, -1);
+ return NULL;
+ }
+
+ ast_string_field_set(level, name, cat);
+
+ return level;
}
static void skel_config_destructor(void *obj)
{
struct skel_config *cfg = obj;
- ao2_cleanup(cfg->general);
- ao2_cleanup(cfg->items);
+ ao2_cleanup(cfg->global);
+ ao2_cleanup(cfg->levels);
}
static void *skel_config_alloc(void)
@@ -341,15 +487,15 @@
}
/* Allocate/initialize memory */
- if (!(cfg->general = ao2_alloc(sizeof(*cfg->general), skel_global_config_destructor))) {
+ if (!(cfg->global = ao2_alloc(sizeof(*cfg->global), skel_global_config_destructor))) {
goto error;
}
- if (ast_string_field_init(cfg->general, 128)) {
+ if (ast_string_field_init(cfg->global, 128)) {
goto error;
}
- if (!(cfg->items = ao2_container_alloc(PVT_BUCKETS, skel_item_hash, skel_item_cmp))) {
+ if (!(cfg->levels = ao2_container_alloc(LEVEL_BUCKETS, skel_level_hash, skel_level_cmp))) {
goto error;
}
@@ -374,62 +520,91 @@
return NULL;
}
- if (!(cfg = ao2_global_obj_ref(globals)) || !cfg->general) {
- return NULL;
- }
-
- ast_cli(a->fd, "foo: %s\n", cfg->general->foo);
- ast_cli(a->fd, "bar: %s\n", cfg->general->bar);
- ast_cli(a->fd, "baz: %s\n", cfg->general->baz);
- ast_cli(a->fd, "blah: %s\n", AST_CLI_YESNO(cfg->general->blah));
- ast_cli(a->fd, "bindaddr: %s\n", ast_sockaddr_stringify(&cfg->general->bindaddr));
+ if (!(cfg = ao2_global_obj_ref(globals)) || !cfg->global) {
+ return NULL;
+ }
+
+ ast_cli(a->fd, "games per call: %u\n", cfg->global->num_games);
+ ast_cli(a->fd, "computer cheats: %s\n", AST_CLI_YESNO(cfg->global->cheat));
+ ast_cli(a->fd, "\n");
+ ast_cli(a->fd, "Sounds\n");
+ ast_cli(a->fd, " prompt: %s\n", cfg->global->prompt);
+ ast_cli(a->fd, " wrong guess: %s\n", cfg->global->wrong);
+ ast_cli(a->fd, " right guess: %s\n", cfg->global->right);
return CLI_SUCCESS;
}
-static char *handle_skel_show_items(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
- RAII_VAR(struct skel_config *, cfg, NULL, ao2_cleanup);
+static char *handle_skel_show_games(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
struct ao2_iterator iter;
- struct skel_item *item;
- char codec_buf[128];
+ struct skel_current_game *game;
switch(cmd) {
case CLI_INIT:
- e->command = "skel show items";
+ e->command = "skel show games";
e->usage =
- "Usage: skel show items\n"
- " List the app_skel items\n";
+ "Usage: skel show games\n"
+ " List app_skel active games\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
- if (!(cfg = ao2_global_obj_ref(globals)) || !cfg->items) {
- return NULL;
- }
-
-#define SKEL_FORMAT "%-15.15s %-25.25s %-20.20s %-5.5s %-5.5s %-5.5s %-2.2s\n"
-#define SKEL_FORMAT1 "%-15.15s %-25.25s %-20.20s %-5.5s %-5.5s %-5.5s %-2.2zu\n"
- ast_cli(a->fd, SKEL_FORMAT, "Name", "Description", "Codecs", "ACL", "Bit1", "Bit2", "N");
- iter = ao2_iterator_init(cfg->items, 0);
- while ((item = ao2_iterator_next(&iter))) {
- /* As an example of non-config-related state remaining between reloads */
- ++item->state->non_config_state;
- ast_getformatname_multiple(codec_buf, sizeof(codec_buf) - 1, item->caps);
- ast_cli(a->fd, SKEL_FORMAT1, item->name, item->description, codec_buf, AST_CLI_YESNO(item->permit != NULL), AST_CLI_YESNO(item->bit1), AST_CLI_YESNO(item->bit2), item->state->non_config_state);
- ao2_ref(item, -1);
+#define SKEL_FORMAT "%-15.15s %-15.15s %-15.15s\n"
+#define SKEL_FORMAT1 "%-15.15s %-15u %-15u\n"
+ ast_cli(a->fd, SKEL_FORMAT, "Level", "Total Games", "Games Left");
+ iter = ao2_iterator_init(games, 0);
+ while ((game = ao2_iterator_next(&iter))) {
+ ast_cli(a->fd, SKEL_FORMAT1, game->level_info->name, game->total_games, game->games_left);
+ ao2_ref(game, -1);
}
ao2_iterator_destroy(&iter);
#undef SKEL_FORMAT
#undef SKEL_FORMAT1
+ return CLI_SUCCESS;
+}
+
+static char *handle_skel_show_levels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ RAII_VAR(struct skel_config *, cfg, NULL, ao2_cleanup);
+ struct ao2_iterator iter;
+ struct skel_level *level;
+
+ switch(cmd) {
+ case CLI_INIT:
+ e->command = "skel show levels";
+ e->usage =
+ "Usage: skel show levels\n"
+ " List the app_skel levels\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (!(cfg = ao2_global_obj_ref(globals)) || !cfg->levels) {
+ return NULL;
+ }
+
+#define SKEL_FORMAT "%-15.15s %-11.11s %-12.12s %-8.8s %-8.8s %-12.12s\n"
+#define SKEL_FORMAT1 "%-15.15s %-11u %-12u %-8u %-8u %-8f\n"
+ ast_cli(a->fd, SKEL_FORMAT, "Name", "Max number", "Max Guesses", "Wins", "Losses", "Avg Guesses");
+ iter = ao2_iterator_init(cfg->levels, 0);
+ while ((level = ao2_iterator_next(&iter))) {
+ ast_cli(a->fd, SKEL_FORMAT1, level->name, level->max_num, level->max_guesses, level->state->wins, level->state->losses, level->state->avg_guesses);
+ ao2_ref(level, -1);
+ }
+ ao2_iterator_destroy(&iter);
+#undef SKEL_FORMAT
+#undef SKEL_FORMAT1
return CLI_SUCCESS;
}
static struct ast_cli_entry skel_cli[] = {
AST_CLI_DEFINE(handle_skel_show_config, "Show app_skel global config options"),
- AST_CLI_DEFINE(handle_skel_show_items, "Show app_skel private thingys"),
+ AST_CLI_DEFINE(handle_skel_show_levels, "Show app_skel levels"),
+ AST_CLI_DEFINE(handle_skel_show_games, "Show app_skel active games"),
};
static int reload_module(void)
@@ -454,20 +629,23 @@
if (aco_info_init(&cfg_info)) {
goto error;
}
-
- /* General Options */
- aco_option_register(&cfg_info, "foo", &general_options, "booya", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, foo));
- aco_option_register(&cfg_info, "bar", &general_options, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, bar));
- aco_option_register(&cfg_info, "baz", &general_options, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, baz));
- aco_option_register(&cfg_info, "blah", &general_options, NULL, OPT_BOOL_T, 1, FLDSET(struct skel_global_config, blah));
- aco_option_register(&cfg_info, "bindaddr", &general_options, "0.0.0.0:1234", OPT_SOCKADDR_T, PARSE_PORT_REQUIRE, FLDSET(struct skel_global_config, bindaddr));
-
- /* Private Options */
- aco_option_register(&cfg_info, "description", &private_options, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_item, description));
- aco_option_register(&cfg_info, "allow", &private_options, "ulaw,alaw", OPT_CODEC_T, 1, FLDSET(struct skel_item, prefs, caps));
- aco_option_register(&cfg_info, "permit", &private_options, NULL, OPT_ACL_T, 1, FLDSET(struct skel_item, permit));
- aco_option_register_custom(&cfg_info, "bit1", &private_options, "yes", custom_bitfield_handler, 0);
- aco_option_register_custom(&cfg_info, "bit2", &private_options, "no", custom_bitfield_handler, 0);
+ if (!(games = ao2_container_alloc(1, NULL, NULL))) {
+ goto error;
+ }
+
+ /* Global options */
+ aco_option_register(&cfg_info, "prompt", &global_options, "please-enter-your&number&queue-less-than", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, prompt));
+ aco_option_register(&cfg_info, "wrong_guess", &global_options, "vm-pls-try-again", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, wrong));
+ aco_option_register(&cfg_info, "right_guess", &global_options, "auth-thankyou", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, right));
+ aco_option_register(&cfg_info, "too_high", &global_options, "high", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, high));
+ aco_option_register(&cfg_info, "too_low", &global_options, "low", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, low));
+ aco_option_register(&cfg_info, "lose", &global_options, "vm-goodbye", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, lose));
+ aco_option_register(&cfg_info, "games", &global_options, "3", OPT_UINT_T, 0, FLDSET(struct skel_global_config, num_games));
+ aco_option_register_custom(&cfg_info, "cheat", &global_options, "no", custom_bitfield_handler, 0);
+
+ /* Level options */
+ aco_option_register(&cfg_info, "max_number", &level_options, NULL, OPT_UINT_T, 0, FLDSET(struct skel_level, max_num));
+ aco_option_register(&cfg_info, "max_guesses", &level_options, NULL, OPT_UINT_T, 1, FLDSET(struct skel_level, max_guesses));
if (aco_process_config(&cfg_info, 0)) {
goto error;
@@ -481,6 +659,7 @@
error:
aco_info_destroy(&cfg_info);
+ ao2_cleanup(games);
return AST_MODULE_LOAD_DECLINE;
}
Modified: team/twilson/config_work/configs/app_skel.conf.sample
URL: http://svnview.digium.com/svn/asterisk/team/twilson/config_work/configs/app_skel.conf.sample?view=diff&rev=367118&r1=367117&r2=367118
==============================================================================
--- team/twilson/config_work/configs/app_skel.conf.sample (original)
+++ team/twilson/config_work/configs/app_skel.conf.sample Sun May 20 11:03:22 2012
@@ -1,25 +1,24 @@
[general]
-foo=one
-bar=two
-baz=three
-blah=no
-bindaddr=1.2.3.4:1234
+games=3
+cheat=no
-[pvt1]
-description=first private
-allow=!all,ulaw,alaw
-permit=127.0.0.1/32
-bit1=yes
-bit2=no
+[sounds]
+;prompt=please-enter-your,number,queue-less-than
+wrong_guess=vm-pls-try-again
+right_guess=auth-thankyou
-[pvt2]
-description=second private
-allow=!all,ulaw
-bit1=no
-bit2=yes
+[easy]
+max_number=10
+max_guesses=4
-[pvt3]
+[medium]
+max_number=100
+max_guesses=6
-[pvt4]
-bit1=yes
-bit2=yes
+[hard]
+max_number=1000
+max_guesses=7
+
+[nightmare]
+max_number=1000
+max_guesses=1
More information about the asterisk-commits
mailing list