Please, consider my proposal for enhancing the smsc_at functionality:
The method currently implemented for reading SMSes from memory is way
too slow and inefficient, compared to usage of the standard CMGL command.
What at2_read_sms_memory tries to achieve in the current version is just
what AT+CMGL command already achieves - in much less time.
Therefore, instead of slowly traversing through the memory in search for
present SMSes, AT+CMGL command is issued.
It responds in almost identical format as AT+CMGR, with just an extra
parameter - the index of the actual SMS being read.
There are as many response lines as there are SMSes in memory.
During the AT+CMGL response, a private List of SMS indices is being
built, so those SMSes can be deleted once the AT+CMGL response is over.
Moreover, there is no need for at2_check_memory, nor for the private
fields sms_memory_capacity and sms_memory_usage in the PrivAT2data.
It should be noted that once this patch is applied, the smsc_at becomes
quite useful in a very safe use case:
AT+CNMI is not issued, so there is no concern whether the SMS modem will
respond to the incoming SMS notification (AT+CMTx) in a timely fashion.
Instead of that, sim-buffering is set to true, with a 1 second keepalive
interval.
The use case described above has but one drawback: logs tend to become
rather massive because of numerous DEBUG lines coming from communication
with the SMS modem.
Implementation of this use case with the current version of smsc_at
brought us to delays in SMS responses of up to 18 minutes. With the
improvement we propose here, the SMS response delay depends only on the
GSM network throughput (6 SMS/minute max, AFAIK).
Regards,
Andrija
? gateway/gw/smsc/.smsc_at.c.swp
Index: gateway/gw/smsc/smsc_at.c
===================================================================
RCS file: /home/cvs/gateway/gw/smsc/smsc_at.c,v
retrieving revision 1.31
diff -u -b -B -a -u -r1.31 smsc_at.c
--- gateway/gw/smsc/smsc_at.c 23 May 2006 13:27:31 -0000 1.31
+++ gateway/gw/smsc/smsc_at.c 27 May 2006 11:31:54 -0000
@@ -621,7 +621,7 @@
time_t cur_time;
Msg *msg;
int len;
- int cmgr_flag = 0;
+ int cmgr_flag = 0, cmgl_flag=0;
time(&end_time);
if (timeout == 0)
@@ -683,11 +683,12 @@
}
if (octstr_search(line, octstr_imm("+CMT:"), 0) != -1 ||
octstr_search(line, octstr_imm("+CDS:"), 0) != -1 ||
+ ((octstr_search(line, octstr_imm("+CMGL:"), 0) != -1) &&
(cmgl_flag = 1)) ||
((octstr_search(line, octstr_imm("+CMGR:"), 0) != -1) &&
(cmgr_flag = 1)) ) {
- line2 = at2_wait_line(privdata, 1, 0);
+ line2 = at2_wait_line(privdata, 30, 0);
if (line2 == NULL) {
- error(0, "AT2[%s]: got +CMT but waiting for next line
timed out",
+ error(0, "AT2[%s]: got an incoming message but waiting for
next line timed out",
octstr_get_cstr(privdata->name));
} else {
octstr_append_cstr(line, "\n");
@@ -695,7 +696,7 @@
O_DESTROY(line2);
at2_pdu_extract(privdata, &pdu, line, smsc_number);
if (pdu == NULL) {
- error(0, "AT2[%s]: got +CMT but pdu_extract failed",
+ error(0, "AT2[%s]: got an incoming message but
pdu_extract failed",
octstr_get_cstr(privdata->name));
} else {
/* count message even if I can't decode it */
@@ -711,7 +712,7 @@
octstr_get_cstr(privdata->name));
}
- if (!cmgr_flag) {
+ if (!(cmgr_flag|cmgl_flag)) {
if (privdata->phase2plus)
at2_write_line(privdata, "AT+CNMA");
}
@@ -719,6 +720,8 @@
O_DESTROY(pdu);
}
}
+ time(&end_time);
+ end_time += 3;
continue;
}
if ((octstr_search(line, octstr_imm("+CMGS:"),0) != -1) &&
(output)) {
@@ -900,135 +903,23 @@
static int at2_read_sms_memory(PrivAT2data* privdata)
{
- /* get memory status */
- if (at2_check_sms_memory(privdata) == -1) {
- debug("bb.smsc.at2", 0, "AT2[%s]: memory check error",
octstr_get_cstr(privdata->name));
- return -1;
- }
-
- if (privdata->sms_memory_usage) {
- /*
- * that is - greater then 0, meaning there are some messages to fetch
- * now - I used to just loop over the first input_mem_sms_used
locations,
- * but it doesn't hold, since under load, messages may be received
while
- * we're in the loop, and get stored in locations towards the end of
the list,
- * thus creating 'holes' in the memory.
- *
- * There are two ways we can fix this :
- * (a) Just read the last message location, delete it and return.
- * It's not a complete solution since holes can still be created
if messages
- * are received between the memory check and the delete command,
- * and anyway - it will slow us down and won't hold well under
pressure
- * (b) Just scan the entire memory each call, bottom to top.
- * This will be slow too, but it'll be reliable.
- *
- * We can massivly improve performance by stopping after
input_mem_sms_used messages
- * have been read, but send_modem_command returns 0 for no message as
well as for a
- * message read, and the only other way to implement it is by doing
memory_check
- * after each read and stoping when input_mem_sms_used get to 0. This
is slow
- * (modem commands take time) so we improve speed only if there are
less then 10
- * messages in memory.
- *
- * I implemented the alternative - changed at2_wait_modem_command to
return the
- * number of messages it collected.
- */
- int i;
- int message_count = 0; /* cound number of messages collected */
-
- debug("bb.smsc.at2", 0, "AT2[%s]: %d messages waiting in memory",
- octstr_get_cstr(privdata->name), privdata->sms_memory_usage);
-
- /*
- * loop till end of memory or collected enouch messages
- */
- for (i = 1; i <= privdata->sms_memory_capacity &&
- message_count < privdata->sms_memory_usage; ++i) {
-
- /* if (meanwhile) there are pending CMTI notifications, process
these first
- * to not let CMTI and sim buffering sit in each others way */
- while (gwlist_len(privdata->pending_incoming_messages) > 0) {
- at2_read_pending_incoming_messages(privdata);
- }
- /* read the message and delete it */
- message_count += at2_read_delete_message(privdata, i);
+ at2_send_modem_command(privdata,"AT+CMGL=0",0,0);
+ while(gwlist_len(privdata->smses_for_deletion))
+ {
+ char command[40]={0};
+
sprintf(command,"AT+CMGD=%ld",(long)gwlist_extract_first(privdata->smses_for_deletion));
+ at2_send_modem_command(privdata,command,7,0);
}
+ at2_send_modem_command(privdata,"AT+CMGL=1",0,0);
+ while(gwlist_len(privdata->smses_for_deletion))
+ {
+ char command[40]={0};
+
sprintf(command,"AT+CMGD=%ld",(long)gwlist_extract_first(privdata->smses_for_deletion));
+ at2_send_modem_command(privdata,command,7,0);
}
-
- /*
- at2_send_modem_command(privdata, ModemTypes[privdata->modemid].init1, 0,
0);
- */
return 0;
}
-
-static int at2_check_sms_memory(PrivAT2data *privdata)
-{
- long values[4]; /* array to put response data in */
- int pos; /* position of parser in data stream */
- int ret;
- Octstr *search_cpms = NULL;
-
- /* select memory type and get report */
- if ((ret = at2_send_modem_command(privdata, "AT+CPMS?", 0, 0)) != 0) {
- debug("bb.smsc.at2.memory_check", 0, "failed to send mem select
command to modem %d", ret);
- return -1;
- }
-
- search_cpms = octstr_create("+CPMS:");
-
- if ((pos = octstr_search(privdata->lines, search_cpms, 0)) != -1) {
- /* got back a +CPMS response */
- int index = 0; /* index in values array */
- pos += 6; /* position of parser in the stream - start after header */
-
- /* skip memory indication */
- pos = octstr_search(privdata->lines, octstr_imm(","), pos) + 1;
-
- /* find all the values */
- while (index < 4 && pos < octstr_len(privdata->lines) &&
- (pos = octstr_parse_long(&values[index], privdata->lines, pos,
10)) != -1) {
- ++pos; /* skip number seperator */
- ++index; /* increment array index */
- if (index == 2)
- /* skip second memory indication */
- pos = octstr_search(privdata->lines, octstr_imm(","), pos) +
1;
- }
-
- if (index < 4) {
- /* didn't get all memory data - I don't why, so I'll bail */
- debug("bb.smsc.at2", 0, "AT2[%s]: couldn't parse all memory
locations : %d:'%s'.",
- octstr_get_cstr(privdata->name), index,
- &(octstr_get_cstr(privdata->lines)[pos]));
- O_DESTROY(search_cpms);
- return -1;
- }
-
- privdata->sms_memory_usage = values[0];
- privdata->sms_memory_capacity = values[1];
- /*
- privdata->output_mem_sms_used = values[2];
- privdata->output_mem_sms_capacity = values[3];
- */
-
- /* everything's cool */
- ret = 0;
-
- /* clear the buffer */
- O_DESTROY(privdata->lines);
-
- } else {
- debug("bb.smsc.at2", 0, "AT2[%s]: no correct header for CPMS
response.",
- octstr_get_cstr(privdata->name));
-
- /* didn't get a +CPMS response - this is clearly an error */
- ret = -1;
- }
-
- O_DESTROY(search_cpms);
- return ret;
-}
-
-
static void at2_set_speed(PrivAT2data *privdata, int bps)
{
struct termios tios;
@@ -1226,6 +1117,7 @@
octstr_destroy(privdata->configfile);
gw_prioqueue_destroy(privdata->outgoing_queue, NULL);
gwlist_destroy(privdata->pending_incoming_messages, octstr_destroy_item);
+ gwlist_destroy(privdata->smses_for_deletion, NULL);
gw_free(conn->data);
conn->data = NULL;
mutex_lock(conn->flow_mutex);
@@ -1315,6 +1207,7 @@
privdata = gw_malloc(sizeof(PrivAT2data));
privdata->outgoing_queue = gw_prioqueue_create(sms_priority_compare);
privdata->pending_incoming_messages = gwlist_create();
+ privdata->smses_for_deletion = gwlist_create();
privdata->configfile = cfg_get_configfile(cfg);
@@ -1459,6 +1352,19 @@
pos++;
else
goto nomsg;
+ } else if ((pos = octstr_search(buffer, octstr_imm("+CMGL:"), 0)) !=
-1) {
+ long index;
+ pos = octstr_parse_long(&index, buffer, pos+6, 10);
+ if(pos == -1) {
+ error(0, "AT2[%s]: Failed to extract index from CMGL
response",octstr_get_cstr(privdata->name));
+ goto nomsg;
+ }
+ gwlist_append(privdata->smses_for_deletion,(void*)index);
+ /* skip status field in +CMGL response */
+ if ((pos = octstr_search(buffer, octstr_imm(","), pos + 1)) != -1)
+ pos++;
+ else
+ goto nomsg;
} else
goto nomsg;
Index: gateway/gw/smsc/smsc_at.h
===================================================================
RCS file: /home/cvs/gateway/gw/smsc/smsc_at.h,v
retrieving revision 1.11
diff -u -b -B -a -u -r1.11 smsc_at.h
--- gateway/gw/smsc/smsc_at.h 24 Apr 2006 21:32:01 -0000 1.11
+++ gateway/gw/smsc/smsc_at.h 27 May 2006 11:31:54 -0000
@@ -136,13 +136,12 @@
Octstr *name;
Octstr *configfile;
int sms_memory_poll_interval;
- int sms_memory_capacity;
- int sms_memory_usage;
List *pending_incoming_messages;
int max_error_count;
Octstr *rawtcp_host;
int rawtcp_port;
int is_serial; /* false if device is rawtcp */
+ List *smses_for_deletion;
} PrivAT2data;
@@ -347,11 +346,6 @@
static int at2_read_sms_memory(PrivAT2data *privdata);
/*
- * Memory capacity and usage check
- */
-static int at2_check_sms_memory(PrivAT2data *privdata);
-
-/*
* This silly thing here will just translate a "swapped nibble"
* pseodo Hex encoding (from PDU) into something that people can
* actually understand.