Add High Resolution Times to CDRs for Asterisk
authorBradley Latus <brad.latus@gmail.com>
Tue, 8 Jun 2010 23:48:17 +0000 (23:48 +0000)
committerBradley Latus <brad.latus@gmail.com>
Tue, 8 Jun 2010 23:48:17 +0000 (23:48 +0000)
People expressed an interest in having access to the exact length of calls to a finer degree than seconds. See the CHANGES and UPGRADE.txt for usage also updated the sample configs to note the change.

Patch by snuffy.

(closes issue #16559)
Reported by: cianmaher
Tested by: cianmaher, snuffy

Review: https://reviewboard.asterisk.org/r/461/

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@269153 65c4cc65-6c06-0410-ace0-fbb531ad65f3

14 files changed:
CHANGES
UPGRADE.txt
addons/cdr_mysql.c
cdr/cdr_adaptive_odbc.c
cdr/cdr_odbc.c
cdr/cdr_pgsql.c
cdr/cdr_sqlite.c
cdr/cdr_tds.c
configs/cdr_custom.conf.sample
configs/cdr_odbc.conf.sample
configs/cdr_sqlite3_custom.conf.sample
configs/cdr_syslog.conf.sample
configs/cdr_tds.conf.sample
funcs/func_cdr.c

diff --git a/CHANGES b/CHANGES
index 3aac8c6..ebc722a 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -223,6 +223,8 @@ Dialplan Functions
    Returns "0" if there is a B channel associated with the call.
    Returns "1" if no B channel is associated with the call.  The call is either
    on hold or is a call waiting call.
+ * Added option to dialplan function CDR(), the 'f' option
+   allows for high resolution times for billsec and duration fields.
 
 Dialplan Variables
 ------------------
@@ -414,6 +416,9 @@ CDR
    See configs/cdr_syslog.conf.sample for more information.
  * A 'sequence' field has been added to CDRs which can be combined with
    linkedid or uniqueid to uniquely identify a CDR.
+ * Handling of billsec and duration field has changed. If your table definition
+   specifies those fields as float,double or similar they will now be logged with
+   microsecond accuracy instead of a whole integer.
 
 Calendaring for Asterisk
 ------------------------
index b02c1f4..c3139b9 100644 (file)
@@ -88,6 +88,10 @@ From 1.6.2 to 1.8:
 * When a call is redirected inside of a Dial, the app and appdata fields of the
   CDR will now be set to "AppDial" and "(Outgoing Line)" instead of being blank.
 
+* The CDR handling of billsec and duration field has changed. If your table
+  definition specifies those fields as float,double or similar they will now
+  be logged with microsecond accuracy instead of a whole integer.
+
 From 1.6.1 to 1.6.2:
 
 * SIP no longer sends the 183 progress message for early media by
index a6a527b..504163a 100644 (file)
@@ -288,6 +288,40 @@ db_reconnect:
                                        ast_str_append(&sql2, 0, ",");
                                }
 
+                               if (!strcasecmp(cdrname, "billsec") &&
+                                       (strstr(entry->type, "float") ||
+                                       strstr(entry->type, "double") ||
+                                       strstr(entry->type, "decimal") ||
+                                       strstr(entry->type, "numeric") ||
+                                       strstr(entry->type, "real"))) {
+
+                                       if (!ast_tvzero(cdr->answer)) {
+                                               snprintf(workspace, sizeof(workspace), "%lf",
+                                                       (double) (ast_tvdiff_us(cdr->end, cdr->answer) / 1000000.0));
+                                       } else {
+                                               ast_copy_string(workspace, "0", sizeof(workspace));
+                                       }
+
+                                       if (!ast_strlen_zero(workspace)) {
+                                               value = workspace;
+                                       }
+                               }
+
+                               if (!strcasecmp(cdrname, "duration") &&
+                                       (strstr(entry->type, "float") ||
+                                       strstr(entry->type, "double") ||
+                                       strstr(entry->type, "decimal") ||
+                                       strstr(entry->type, "numeric") ||
+                                       strstr(entry->type, "real"))) {
+
+                                       snprintf(workspace, sizeof(workspace), "%lf",
+                                               (double) (ast_tvdiff_us(cdr->end, cdr->start) / 1000000.0));
+
+                                       if (!ast_strlen_zero(workspace)) {
+                                               value = workspace;
+                                       }
+                               }
+
                                ast_str_make_space(&escape, (valsz = strlen(value)) * 2 + 1);
                                mysql_real_escape_string(&mysql, ast_str_buffer(escape), value, valsz);
 
index 3c52658..50577db 100644 (file)
@@ -611,6 +611,23 @@ static int odbc_log(struct ast_cdr *cdr)
                                                continue;
                                        } else {
                                                double number = 0.0;
+
+                                               if (!strcasecmp(entry->cdrname, "billsec")) {
+                                                       if (!ast_tvzero(cdr->answer)) {
+                                                               snprintf(colbuf, sizeof(colbuf), "%lf",
+                                                                                       (double) (ast_tvdiff_us(cdr->end, cdr->answer) / 1000000.0));
+                                                       } else {
+                                                               ast_copy_string(colbuf, "0", sizeof(colbuf));
+                                                       }
+                                               } else if (!strcasecmp(entry->cdrname, "duration")) {
+                                                       snprintf(colbuf, sizeof(colbuf), "%lf",
+                                                                               (double) (ast_tvdiff_us(cdr->end, cdr->start) / 1000000.0));
+
+                                                       if (!ast_strlen_zero(colbuf)) {
+                                                               colptr = colbuf;
+                                                       }
+                                               }
+
                                                if (sscanf(colptr, "%30lf", &number) != 1) {
                                                        ast_log(LOG_WARNING, "CDR variable %s is not an numeric type.\n", entry->name);
                                                        continue;
@@ -628,6 +645,23 @@ static int odbc_log(struct ast_cdr *cdr)
                                                continue;
                                        } else {
                                                double number = 0.0;
+
+                                               if (!strcasecmp(entry->cdrname, "billsec")) {
+                                                       if (!ast_tvzero(cdr->answer)) {
+                                                               snprintf(colbuf, sizeof(colbuf), "%lf",
+                                                                                       (double) (ast_tvdiff_us(cdr->end, cdr->answer) / 1000000.0));
+                                                       } else {
+                                                               ast_copy_string(colbuf, "0", sizeof(colbuf));
+                                                       }
+                                               } else if (!strcasecmp(entry->cdrname, "duration")) {
+                                                       snprintf(colbuf, sizeof(colbuf), "%lf",
+                                                                               (double) (ast_tvdiff_us(cdr->end, cdr->start) / 1000000.0));
+
+                                                       if (!ast_strlen_zero(colbuf)) {
+                                                               colptr = colbuf;
+                                                       }
+                                               }
+
                                                if (sscanf(colptr, "%30lf", &number) != 1) {
                                                        ast_log(LOG_WARNING, "CDR variable %s is not an numeric type.\n", entry->name);
                                                        continue;
index e0f830c..fde134f 100644 (file)
@@ -53,6 +53,7 @@ enum {
        CONFIG_LOGUNIQUEID =       1 << 0,
        CONFIG_USEGMTIME =         1 << 1,
        CONFIG_DISPOSITIONSTRING = 1 << 2,
+       CONFIG_HRTIME =            1 << 3,
 };
 
 static struct ast_flags config = { 0 };
@@ -96,8 +97,23 @@ static SQLHSTMT execute_cb(struct odbc_obj *obj, void *data)
        SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->dstchannel), 0, cdr->dstchannel, 0, NULL);
        SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->lastapp), 0, cdr->lastapp, 0, NULL);
        SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->lastdata), 0, cdr->lastdata, 0, NULL);
-       SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->duration, 0, NULL);
-       SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->billsec, 0, NULL);
+
+       if (ast_test_flag(&config, CONFIG_HRTIME)) {
+               double hrbillsec = 0.0;
+               double hrduration;
+
+               if (!ast_tvzero(cdr->answer)) {
+                       hrbillsec = (double) ast_tvdiff_us(cdr->end, cdr->answer) / 1000000.0;
+               }
+               hrduration = (double) ast_tvdiff_us(cdr->end, cdr->start) / 1000000.0;
+
+               SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_DOUBLE, SQL_FLOAT, 0, 0, &hrbillsec, 0, NULL);
+               SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_DOUBLE, SQL_FLOAT, 0, 0, &hrduration, 0, NULL);
+       } else {
+               SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->duration, 0, NULL);
+               SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->billsec, 0, NULL);
+       }
+
        if (ast_test_flag(&config, CONFIG_DISPOSITIONSTRING))
                SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ast_cdr_disp2str(cdr->disposition)) + 1, 0, ast_cdr_disp2str(cdr->disposition), 0, NULL);
        else
@@ -203,6 +219,14 @@ static int odbc_load_module(int reload)
                        ast_debug(1, "cdr_odbc: Logging in local time\n");
                }
 
+               if (((tmp = ast_variable_retrieve(cfg, "global", "hrtime"))) && ast_true(tmp)) {
+                       ast_set_flag(&config, CONFIG_HRTIME);
+                       ast_debug(1, "cdr_odbc: Logging billsec and duration fields as floats\n");
+               } else {
+                       ast_clear_flag(&config, CONFIG_HRTIME);
+                       ast_debug(1, "cdr_odbc: Logging billsec and duration fields as integers\n");
+               }
+
                if ((tmp = ast_variable_retrieve(cfg, "global", "table")) == NULL) {
                        ast_log(LOG_WARNING, "cdr_odbc: table not specified.  Assuming cdr\n");
                        tmp = "cdr";
index eae0926..53e957c 100644 (file)
@@ -211,12 +211,12 @@ static int pgsql_log(struct ast_cdr *cdr)
                                } else if (strncmp(cur->type, "float", 5) == 0) {
                                        struct timeval *when = cur->name[0] == 'd' ? &cdr->start : &cdr->answer;
                                        LENGTHEN_BUF2(31);
-                                       ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->end.tv_sec - when->tv_sec + cdr->end.tv_usec / 1000000.0 - when->tv_usec / 1000000.0);
+                                       ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double) (ast_tvdiff_us(cdr->end, *when) / 1000000.0));
                                } else {
                                        /* Char field, probably */
                                        struct timeval *when = cur->name[0] == 'd' ? &cdr->start : &cdr->answer;
                                        LENGTHEN_BUF2(31);
-                                       ast_str_append(&sql2, 0, "%s'%f'", first ? "" : ",", (double)cdr->end.tv_sec - when->tv_sec + cdr->end.tv_usec / 1000000.0 - when->tv_usec / 1000000.0);
+                                       ast_str_append(&sql2, 0, "%s'%f'", first ? "" : ",", (double) (ast_tvdiff_us(cdr->end, *when) / 1000000.0));
                                }
                        } else if (strcmp(cur->name, "disposition") == 0 || strcmp(cur->name, "amaflags") == 0) {
                                if (strncmp(cur->type, "int", 3) == 0) {
index 5a3a4b2..d871e8a 100644 (file)
@@ -49,8 +49,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/utils.h"
 #include "asterisk/paths.h"
 
-#define LOG_UNIQUEID   0
-#define LOG_USERFIELD  0
+#define LOG_UNIQUEID    0
+#define LOG_USERFIELD   0
+#define LOG_HRTIME      0
 
 /* When you change the DATE_FORMAT, be sure to change the CHAR(19) below to something else */
 #define DATE_FORMAT "%Y-%m-%d %T"
@@ -74,8 +75,13 @@ static const char sql_create_table[] = "CREATE TABLE cdr ("
 "      start           CHAR(19),"
 "      answer          CHAR(19),"
 "      end             CHAR(19),"
+#if LOG_HRTIME
+"      duration        FLOAT,"
+"      billsec         FLOAT,"
+#else
 "      duration        INTEGER,"
 "      billsec         INTEGER,"
+#endif
 "      disposition     INTEGER,"
 "      amaflags        INTEGER,"
 "      accountcode     VARCHAR(20)"
@@ -101,6 +107,10 @@ static int sqlite_log(struct ast_cdr *cdr)
        char *zErr = 0;
        char startstr[80], answerstr[80], endstr[80];
        int count;
+#if LOG_HRTIME
+       double hrbillsec = 0.0;
+       double hrduration;
+#endif
 
        ast_mutex_lock(&sqlite_lock);
 
@@ -108,6 +118,13 @@ static int sqlite_log(struct ast_cdr *cdr)
        format_date(answerstr, sizeof(answerstr), &cdr->answer);
        format_date(endstr, sizeof(endstr), &cdr->end);
 
+#if LOG_HRTIME
+       if (!ast_tvzero(cdr->answer)) {
+               hrbillsec = (double) ast_tvdiff_us(cdr->end, cdr->answer) / 1000000.0;
+       }
+       hrduration = (double) ast_tvdiff_us(cdr->end, cdr->start) / 1000000.0;
+#endif
+
        for(count=0; count<5; count++) {
                res = sqlite_exec_printf(db,
                        "INSERT INTO cdr ("
@@ -126,7 +143,11 @@ static int sqlite_log(struct ast_cdr *cdr)
                                "'%q', '%q', '%q', '%q', "
                                "'%q', '%q', '%q', '%q', "
                                "'%q', '%q', '%q', "
+#if LOG_HRTIME
+                               "%f, %f, %d, %d, "
+#else
                                "%d, %d, %d, %d, "
+#endif
                                "'%q'"
 #                              if LOG_UNIQUEID
                                ",'%q'"
@@ -138,7 +159,11 @@ static int sqlite_log(struct ast_cdr *cdr)
                                cdr->clid, cdr->src, cdr->dst, cdr->dcontext,
                                cdr->channel, cdr->dstchannel, cdr->lastapp, cdr->lastdata,
                                startstr, answerstr, endstr,
+#if LOG_HRTIME
+                               hrduration, hrbillsec, cdr->disposition, cdr->amaflags,
+#else
                                cdr->duration, cdr->billsec, cdr->disposition, cdr->amaflags,
+#endif
                                cdr->accountcode
 #                              if LOG_UNIQUEID
                                ,cdr->uniqueid
index c615400..ab0f065 100644 (file)
@@ -87,6 +87,7 @@ struct cdr_tds_config {
                AST_STRING_FIELD(table);
                AST_STRING_FIELD(charset);
                AST_STRING_FIELD(language);
+               AST_STRING_FIELD(hrtime);
        );
        DBPROCESS *dbproc;
        unsigned int connected:1;
@@ -149,7 +150,36 @@ retry:
        }
 
        if (settings->has_userfield) {
-               erc = dbfcmd(settings->dbproc,
+               if (settings->hrtime) {
+                       double hrbillsec = 0.0;
+                       double hrduration;
+
+                       if (!ast_tvzero(cdr->answer)) {
+                               hrbillsec = (double)(ast_tvdiff_us(cdr->end, cdr->answer) / 1000000.0);
+                       }
+                       hrduration = (double)(ast_tvdiff_us(cdr->end, cdr->start) / 1000000.0);
+
+                       erc = dbfcmd(settings->dbproc,
+                                        "INSERT INTO %s "
+                                        "("
+                                        "accountcode, src, dst, dcontext, clid, channel, "
+                                        "dstchannel, lastapp, lastdata, start, answer, [end], duration, "
+                                        "billsec, disposition, amaflags, uniqueid, userfield"
+                                        ") "
+                                        "VALUES "
+                                        "("
+                                        "'%s', '%s', '%s', '%s', '%s', '%s', "
+                                        "'%s', '%s', '%s', %s, %s, %s, %lf, "
+                                        "%lf, '%s', '%s', '%s', '%s'"
+                                        ")",
+                                        settings->table,
+                                        accountcode, src, dst, dcontext, clid, channel,
+                                        dstchannel, lastapp, lastdata, start, answer, end, hrduration,
+                                        hrbillsec, ast_cdr_disp2str(cdr->disposition), ast_cdr_flags2str(cdr->amaflags), uniqueid,
+                                        userfield
+                       );
+               } else {
+                       erc = dbfcmd(settings->dbproc,
                                         "INSERT INTO %s "
                                         "("
                                         "accountcode, src, dst, dcontext, clid, channel, "
@@ -168,8 +198,37 @@ retry:
                                         cdr->billsec, ast_cdr_disp2str(cdr->disposition), ast_cdr_flags2str(cdr->amaflags), uniqueid,
                                         userfield
                        );
+               }
        } else {
-               erc = dbfcmd(settings->dbproc,
+               if (settings->hrtime) {
+                       double hrbillsec = 0.0;
+                       double hrduration;
+
+                       if (!ast_tvzero(cdr->answer)) {
+                               hrbillsec = (double)(ast_tvdiff_us(cdr->end, cdr->answer) / 1000000.0);
+                       }
+                       hrduration = (double)(ast_tvdiff_us(cdr->end, cdr->start) / 1000000.0);
+
+                       erc = dbfcmd(settings->dbproc,
+                                        "INSERT INTO %s "
+                                        "("
+                                        "accountcode, src, dst, dcontext, clid, channel, "
+                                        "dstchannel, lastapp, lastdata, start, answer, [end], duration, "
+                                        "billsec, disposition, amaflags, uniqueid"
+                                        ") "
+                                        "VALUES "
+                                        "("
+                                        "'%s', '%s', '%s', '%s', '%s', '%s', "
+                                        "'%s', '%s', '%s', %s, %s, %s, %lf, "
+                                        "%lf, '%s', '%s', '%s'"
+                                        ")",
+                                        settings->table,
+                                        accountcode, src, dst, dcontext, clid, channel,
+                                        dstchannel, lastapp, lastdata, start, answer, end, hrduration,
+                                        hrbillsec, ast_cdr_disp2str(cdr->disposition), ast_cdr_flags2str(cdr->amaflags), uniqueid
+                       );
+               } else {
+                       erc = dbfcmd(settings->dbproc,
                                         "INSERT INTO %s "
                                         "("
                                         "accountcode, src, dst, dcontext, clid, channel, "
@@ -187,6 +246,7 @@ retry:
                                         dstchannel, lastapp, lastdata, start, answer, end, cdr->duration,
                                         cdr->billsec, ast_cdr_disp2str(cdr->disposition), ast_cdr_flags2str(cdr->amaflags), uniqueid
                        );
+               }
        }
 
        if (erc == FAIL) {
@@ -502,6 +562,13 @@ static int tds_load_module(int reload)
                ast_string_field_set(settings, table, "cdr");
        }
 
+       ptr = ast_variable_retrieve(cfg, "global", "hrtime");
+       if (ptr && ast_true(ptr)) {
+               ast_string_field_set(settings, hrtime, ptr);
+       } else {
+               ast_log(LOG_NOTICE, "High Resolution Time not found, using integers for billsec and duration fields by default.\n");
+       }
+
        mssql_disconnect();
 
        if (mssql_connect()) {
index ce3248b..bec9e33 100644 (file)
@@ -8,5 +8,7 @@
 ;
 ;[mappings]
 ;Master.csv => ${CSV_QUOTE(${CDR(clid)})},${CSV_QUOTE(${CDR(src)})},${CSV_QUOTE(${CDR(dst)})},${CSV_QUOTE(${CDR(dcontext)})},${CSV_QUOTE(${CDR(channel)})},${CSV_QUOTE(${CDR(dstchannel)})},${CSV_QUOTE(${CDR(lastapp)})},${CSV_QUOTE(${CDR(lastdata)})},${CSV_QUOTE(${CDR(start)})},${CSV_QUOTE(${CDR(answer)})},${CSV_QUOTE(${CDR(end)})},${CSV_QUOTE(${CDR(duration)})},${CSV_QUOTE(${CDR(billsec)})},${CSV_QUOTE(${CDR(disposition)})},${CSV_QUOTE(${CDR(amaflags)})},${CSV_QUOTE(${CDR(accountcode)})},${CSV_QUOTE(${CDR(uniqueid)})},${CSV_QUOTE(${CDR(userfield)})},${CDR(sequence)}
+;
+; High Resolution Time for billsec and duration fields
+;Master.csv => ${CSV_QUOTE(${CDR(clid)})},${CSV_QUOTE(${CDR(src)})},${CSV_QUOTE(${CDR(dst)})},${CSV_QUOTE(${CDR(dcontext)})},${CSV_QUOTE(${CDR(channel)})},${CSV_QUOTE(${CDR(dstchannel)})},${CSV_QUOTE(${CDR(lastapp)})},${CSV_QUOTE(${CDR(lastdata)})},${CSV_QUOTE(${CDR(start)})},${CSV_QUOTE(${CDR(answer)})},${CSV_QUOTE(${CDR(end)})},${CSV_QUOTE(${CDR(duration,f)})},${CSV_QUOTE(${CDR(billsec,f)})},${CSV_QUOTE(${CDR(disposition)})},${CSV_QUOTE(${CDR(amaflags)})},${CSV_QUOTE(${CDR(accountcode)})},${CSV_QUOTE(${CDR(uniqueid)})},${CSV_QUOTE(${CDR(userfield)})},${CDR(sequence)}
 ;Simple.csv => ${CSV_QUOTE(${EPOCH})},${CSV_QUOTE(${CDR(src)})},${CSV_QUOTE(${CDR(dst)})}
-
index e9d21d6..93bd6ff 100644 (file)
@@ -8,3 +8,4 @@
 ;dispositionstring=yes
 ;table=cdr             ;"cdr" is default table name
 ;usegmtime=no             ; set to "yes" to log in GMT
+;hrtime=yes            ;Enables microsecond accuracy with the billsec and duration fields
index 55872b3..db1ad79 100644 (file)
@@ -5,3 +5,6 @@
 table  => cdr
 columns        => calldate, clid, dcontext, channel, dstchannel, lastapp, lastdata, duration, billsec, disposition, amaflags, accountcode, uniqueid, userfield, test
 values => '${CDR(start)}','${CDR(clid)}','${CDR(dcontext)}','${CDR(channel)}','${CDR(dstchannel)}','${CDR(lastapp)}','${CDR(lastdata)}','${CDR(duration)}','${CDR(billsec)}','${CDR(disposition)}','${CDR(amaflags)}','${CDR(accountcode)}','${CDR(uniqueid)}','${CDR(userfield)}','${CDR(test)}'
+
+;Enable High Resolution Times for billsec and duration fields
+;values        => '${CDR(start)}','${CDR(clid)}','${CDR(dcontext)}','${CDR(channel)}','${CDR(dstchannel)}','${CDR(lastapp)}','${CDR(lastdata)}','${CDR(duration,f)}','${CDR(billsec,f)}','${CDR(disposition)}','${CDR(amaflags)}','${CDR(accountcode)}','${CDR(uniqueid)}','${CDR(userfield)}','${CDR(test)}'
index 70c9568..3a619be 100644 (file)
@@ -73,6 +73,8 @@
 
 ;template = "${CDR(clid)}","${CDR(src)}","${CDR(dst)}","${CDR(dcontext)}","${CDR(channel)}","${CDR(dstchannel)}","${CDR(lastapp)}","${CDR(lastdata)}","${CDR(start)}","${CDR(answer)}","${CDR(end)}","${CDR(duration)}","${CDR(billsec)}","${CDR(disposition)}","${CDR(amaflags)}","${CDR(accountcode)}","${CDR(uniqueid)}","${CDR(userfield)}"
 
+; High Resolution Time for billsec and duration fields
+;template = "${CDR(clid)}","${CDR(src)}","${CDR(dst)}","${CDR(dcontext)}","${CDR(channel)}","${CDR(dstchannel)}","${CDR(lastapp)}","${CDR(lastdata)}","${CDR(start)}","${CDR(answer)}","${CDR(end)}","${CDR(duration,f)}","${CDR(billsec,f)}","${CDR(disposition)}","${CDR(amaflags)}","${CDR(accountcode)}","${CDR(uniqueid)}","${CDR(userfield)}"
 ;[cdr-simple]
 
 ; Since we don't specify a facility or priority for this logging location, the
index a2a0adc..309599e 100644 (file)
 ; Default value:  iso_1
 
 ;charset=BIG5
+
+; High Resolution Times
+;
+; The 'hrtime' setting is used to store high resolution (sub second) times for
+; billsec and duration fields.
+;
+; Accepted value: true or false
+; Default value: false
+
+;hrtime=false
+
index 2111872..3d0c846 100644 (file)
@@ -106,6 +106,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                        </parameter>
                        <parameter name="options" required="false">
                                <optionlist>
+                                       <option name="f">
+                                               <para>Returns billsec or duration fields as floating point values.</para>
+                                       </option>
                                        <option name="l">
                                                <para>Uses the most recent CDR on a channel with multiple records</para>
                                        </option>
@@ -174,9 +177,11 @@ enum cdr_option_flags {
        OPT_UNPARSED = (1 << 1),
        OPT_LAST = (1 << 2),
        OPT_SKIPLOCKED = (1 << 3),
+       OPT_FLOAT = (1 << 4),
 };
 
 AST_APP_OPTIONS(cdr_func_options, {
+       AST_APP_OPTION('f', OPT_FLOAT),
        AST_APP_OPTION('l', OPT_LAST),
        AST_APP_OPTION('r', OPT_RECURSIVE),
        AST_APP_OPTION('s', OPT_SKIPLOCKED),
@@ -213,9 +218,38 @@ static int cdr_read(struct ast_channel *chan, const char *cmd, char *parse,
                while (ast_test_flag(cdr, AST_CDR_FLAG_LOCKED) && cdr->next)
                        cdr = cdr->next;
 
-       ast_cdr_getvar(cdr, args.variable, &ret, buf, len,
-                      ast_test_flag(&flags, OPT_RECURSIVE),
-                          ast_test_flag(&flags, OPT_UNPARSED));
+       if (!strcasecmp("billsec", args.variable) && ast_test_flag(&flags, OPT_FLOAT)) {
+               if (!ast_tvzero(cdr->answer)) {
+                       double hrtime;
+
+                       if(!ast_tvzero(cdr->end))
+                               hrtime = (double)(ast_tvdiff_us(cdr->end, cdr->answer) / 1000000.0);
+                       else
+                               hrtime = (double)(ast_tvdiff_us(ast_tvnow(), cdr->answer) / 1000000.0);
+
+                       snprintf(buf, len, "%lf", hrtime);
+               } else {
+                       snprintf(buf, len, "%lf", 0.0);
+               }
+               ret = buf;
+       } else if (!strcasecmp("duration", args.variable) && ast_test_flag(&flags, OPT_FLOAT)) {
+                       double hrtime;
+
+                       if(!ast_tvzero(cdr->end))
+                               hrtime = (double)(ast_tvdiff_us(cdr->end, cdr->start) / 1000000.0);
+                       else
+                               hrtime = (double)(ast_tvdiff_us(ast_tvnow(), cdr->start) / 1000000.0);
+
+                       snprintf(buf, len, "%lf", hrtime);
+
+               if (!ast_strlen_zero(buf)) {
+                       ret = buf;
+               }
+       } else {
+               ast_cdr_getvar(cdr, args.variable, &ret, buf, len,
+                              ast_test_flag(&flags, OPT_RECURSIVE),
+                                  ast_test_flag(&flags, OPT_UNPARSED));
+       }
 
        return ret ? 0 : -1;
 }