RE: [PATCH] Resubmit: "sim buffering" support for AT2.

"Oded Arbel" <[email protected]>
Newsgroups gmane.comp.mobile.kannel.devel
Message-ID <[email protected]>
Another bug - sorry about that guys. all the pressure here starting to
get to me, so I'm slipping in my patches :-(

--
Oded Arbel
m-Wise Inc.
[email protected]

stilleto driven deep
clones brawl
new king crowned


> -----Original Message-----
> From: Oded Arbel 
> Sent: Sunday, March 17, 2002 9:07 PM
> To: Kannel-devel (E-mail)
> Subject: [PATCH] Resubmit: "sim buffering" support for AT2.
> 
> 
> Hi list.
> 
> Here's another resubmit ( I think the last one for today ;-) this time
> of SIM buffering for AT2. ported to latest CVS, droped all the #ifdefs
> and using configuration file instead. cleaned some stuff, and 
> generally
> I think that it looks much better now.
> To use, set sim-buffering = true in the correct group. added a
> configuration option in modem.conf to set the memory location 
> for use -
> default to "SM" which works in any modem I checked.
> 
> While the original code was in production for the last two 
> months, like
> this it's only from yesterday - but "Works for me(tm)".
> 
> hope you'll like it better now :-)
> 
> --
> Oded Arbel
> m-Wise Inc.
> [email protected]
> 
> When we seek to discover the best in others, we somehow bring out the
> best in ourselves.
> 	-- William Arthur Ward
> 
> 
> 
>
smsc_at2.sim.patch (application/octet-stream, 14 KB)
Index: gw/smsc_at2.c
===================================================================
RCS file: /home/cvs/gateway/gw/smsc_at2.c,v
retrieving revision 1.35
diff -u -r1.35 smsc_at2.c
--- gw/smsc_at2.c	17 Mar 2002 16:03:37 -0000	1.35
+++ gw/smsc_at2.c	17 Mar 2002 19:53:47 -0000
@@ -47,6 +47,7 @@
     long	sendline_sleep;
     Octstr	*keepalive_cmd;
     int		broken;
+    Octstr	*message_storage;
 } ModemDef;
  
 /* maximum data to attempt to read in one go */
@@ -70,6 +71,10 @@
 /* The number of times to attempt to send a message should sending fail */
 #define RETRY_SEND 3
  
+/* defines for use with the so-called "SIM buffering techinique" :*/
+/* once in how many seconds to poll the memory locations, if keepalive is _not_ set (will use keepalive time if set) */
+#define AT2_DEFAULT_SMS_POLL_INTERVAL	60	
+
 typedef struct PrivAT2data
 {
     List	*outgoing_queue;
@@ -93,6 +98,9 @@
     Octstr	*sms_center;
     Octstr	*name;
     Octstr	*configfile;
+    int		sms_memory_poll_interval;
+    int		sms_memory_capacity;
+    int		sms_memory_usage;
 } PrivAT2data;
 
 
@@ -107,7 +115,7 @@
 void	at2_flush_buffer(PrivAT2data *privdata);
 int	at2_init_device(PrivAT2data *privdata);
 int	at2_send_modem_command(PrivAT2data *privdata,char *cmd, time_t timeout, int greaterflag);
-int	at2_wait_modem_command(PrivAT2data *privdata, time_t timeout, int greaterflag);
+int	at2_wait_modem_command(PrivAT2data *privdata, time_t timeout, int greaterflag, int* messages_collected);
 void	at2_set_speed(PrivAT2data *privdata, int bps);
 void	at2_device_thread(void *arg);
 int	at2_shutdown_cb(SMSCConn *conn, int finish_sending);
@@ -131,6 +139,8 @@
 int	at2_detect_modem_type(PrivAT2data *privdata);
 ModemDef *at2_read_modems(PrivAT2data *privdata, Octstr *file, Octstr *id, int idnumber);
 void	at2_destroy_modem(ModemDef *modem);
+void at2_read_sms_memory(PrivAT2data *privdata);
+int at2_check_sms_memory(PrivAT2data* privdata);
 
 /******************************************************************************
 ** For debugging purposes octstr_destroy might be a macro,
@@ -538,7 +548,7 @@
     	the next command. 10 sec should be suficient */
     	if(!privdata->pin_ready)
     	{
-   	   at2_wait_modem_command(privdata,10, 0);
+   	   at2_wait_modem_command(privdata,10, 0, NULL);
     	   if(!privdata->pin_ready) {
 	       at2_send_modem_command(privdata, "AT+CPIN?", 10, 0);
 	       if(!privdata->pin_ready) {
@@ -614,6 +624,20 @@
      ret = at2_send_modem_command(privdata, octstr_get_cstr(privdata->modem->init_string), 0, 0);
     	if(ret != 0)
     	    return -1;
+		
+    if (privdata->sms_memory_poll_interval) {
+    /* set message storage location for "SIM buffering" using the CPMS command */
+	Octstr *temp;
+	temp = octstr_create("AT+CPMS=");
+	octstr_append_char(temp, 34); // "
+	octstr_append(temp, privdata->modem->message_storage);
+	octstr_append_char(temp, 34); // "
+	ret = at2_send_modem_command(privdata, octstr_get_cstr(temp),0, 0);
+	octstr_destroy(temp);
+	if (ret != 0)
+	    return -1;
+    }
+    
     info(0, "AT2[%s]: AT SMSC successfully opened.", octstr_get_cstr(privdata->name));
     return 0;
 }
@@ -635,7 +659,7 @@
 int at2_send_modem_command(PrivAT2data *privdata,char *cmd, time_t timeout, int gt_flag)
 {
     at2_write_line(privdata,cmd);
-    return at2_wait_modem_command(privdata, timeout, gt_flag);
+    return at2_wait_modem_command(privdata, timeout, gt_flag, NULL);
 }
 
 
@@ -644,7 +668,7 @@
 ** waits for the modem to send us something
 */
 
-int at2_wait_modem_command(PrivAT2data *privdata, time_t timeout, int gt_flag)
+int at2_wait_modem_command(PrivAT2data *privdata, time_t timeout, int gt_flag, int* messages_collected)
 {
     Octstr *line = NULL;
     Octstr *line2 = NULL;
@@ -654,6 +678,7 @@
     time_t cur_time;
     Msg	*msg;
     int len;
+    int cmgr_flag = 0;
  
     time(&end_time);
     if(timeout == 0)
@@ -709,7 +734,8 @@
 	   	ret = 1;
 	   	goto end;
 	   }
-           if (-1 != octstr_search(line, octstr_imm("+CMT:"), 0))
+           if (-1 != octstr_search(line, octstr_imm("+CMT:"), 0) 
+			   || ((-1 != octstr_search(line, octstr_imm("+CMGR:"), 0)) && (cmgr_flag = 1)) )
            {
            	line2 = at2_wait_line(privdata,1,0);
  
@@ -730,14 +756,21 @@
 		    }
 		    else
 		    {
+		    	// count message even if I can't decode it
+			if (messages_collected)
+				++(*messages_collected);
 			msg = at2_pdu_decode(pdu, privdata);
                     	if(msg != NULL)
                     	{
 			    msg->sms.smsc_id = octstr_duplicate(privdata->conn->id);
                     	    bb_smscconn_receive(privdata->conn, msg);
                     	}
-                    	if(privdata->phase2plus)
-		    	    at2_write_line(privdata,"AT+CNMA");
+			
+			if (!cmgr_flag) {
+	                    	if(privdata->phase2plus)
+			    	    at2_write_line(privdata,"AT+CNMA");
+			}
+			
 			O_DESTROY(pdu);
 		    }
                 }
@@ -772,6 +805,136 @@
     return ret;
 }
 
+/*
+ * at2_read_sms_memory
+ * checks whether any messages are buffered in message storage and extract them.
+ */
+void at2_read_sms_memory(PrivAT2data* privdata)
+{
+	char cmd[20];
+
+	// get memory status
+	if (at2_check_sms_memory(privdata) == -1) 
+	{
+		debug("bb.smsc.at2",0,"AT2[%s]: memory check error",octstr_get_cstr(privdata->device));
+		return;
+	}
+
+	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 recieved 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->device),privdata->sms_memory_usage);
+		
+		for (i = 1; i <= privdata->sms_memory_capacity && 
+			message_count < privdata->sms_memory_usage; ++i) 
+		{ // loop till end of memory or collected enouch messages
+			int old_message_count = message_count;
+			sprintf(cmd,"AT+CMGR=%d",i);
+			/* read one message from memory */
+			at2_write_line(privdata,cmd);
+			if (at2_wait_modem_command(privdata,0,0,&message_count) != 0)
+			{
+			   	debug("bb.smsc.at2",0,"AT2[%s]: failed to get message %d.",octstr_get_cstr(privdata->device),i);
+				continue; /* failed to read the message - skip to next message */
+			}
+
+			if (old_message_count == message_count) { /* no need to delete if no message collected */
+				debug("bb.smsc.at2",0,"AT2[%s]: not deleted.",octstr_get_cstr(privdata->device));
+				continue;
+			}
+			
+			sprintf(cmd,"AT+CMGD=%d",i); /* delete the message we just read */
+			if (at2_send_modem_command(privdata,cmd,7,0) != 0)
+			{  /* 3 seconds is not enough with some modems if the message is large - so we'll give it 7 seconds */
+			   	debug("bb.smsc.at2",0,"AT2[%s]: failed to delete message %d.",octstr_get_cstr(privdata->device),i);
+				continue; /* failed to delete the message, we'll just ignore it for now - this is bad, since
+							if the message really didn't get deleted - we'll see it next time around. */
+			}
+		}
+	}
+	//at2_send_modem_command(privdata, ModemTypes[privdata->modemid].init1, 0, 0);
+}
+
+/*
+ * at_check_sms_memory()
+ * Memory capacity and usage check
+ */
+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; // return code
+	Octstr* search_cpms = NULL;
+
+	if ((ret = at2_send_modem_command(privdata, "AT+CPMS?",0,0)) != 0) //MEM_SELECT_COMMAND
+	{ // select memory type and get report
+		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
+		
+		pos = octstr_search(privdata->lines,octstr_imm(","),pos) +1 ; // skip memory indication
+		while (index < 4 && 
+			pos < octstr_len(privdata->lines) && 
+			(pos = octstr_parse_long(&values[index],privdata->lines,pos,10)) != -1)
+		{ // find all the values
+			++pos; // skip number seperator;
+			++index; // increment array index
+			if (index == 2)
+				pos = octstr_search(privdata->lines,octstr_imm(","),pos)+1; // skip second memory indication
+		}
+		
+		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->device), 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];
+		*/
+		
+		ret = 0; // everything's cool
+		
+		//  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->device));
+		ret = -1; // didn't get a +CPMS response - this is clearly an error
+	}
+
+	O_DESTROY(search_cpms);
+	return ret; 
+}
 
 /******************************************************************************
 ** at2_set_speed
@@ -844,7 +1007,7 @@
     PrivAT2data	*privdata = conn->data;
 
     int l, wait=0;
-    long idle_timeout;
+    long idle_timeout, memory_poll_timeout = 0;
    
     conn->status = SMSCCONN_CONNECTING;
     
@@ -918,7 +1081,7 @@
 	    at2_send_messages(privdata);
 	    idle_timeout = time(NULL);
 	} else
-	    at2_wait_modem_command(privdata,1,0);
+	    at2_wait_modem_command(privdata,1,0,NULL);
 
 	if(privdata->keepalive && 
 	   idle_timeout + privdata->keepalive < time(NULL)) {
@@ -930,6 +1093,12 @@
 	    }
 	    idle_timeout = time(NULL);
 	}
+		 
+	if (privdata->sms_memory_poll_interval &&
+	    memory_poll_timeout + privdata->sms_memory_poll_interval < time(NULL)) {
+	    at2_read_sms_memory(privdata);
+	    memory_poll_timeout = time(NULL);
+	}
    }
     at2_close_device(privdata);
     conn->status = SMSCCONN_DISCONNECTED;
@@ -1034,6 +1203,14 @@
 
     privdata->keepalive = 0;
     cfg_get_integer(&privdata->keepalive, cfg, octstr_imm("keepalive"));
+	
+    cfg_get_bool(&privdata->sms_memory_poll_interval, cfg, octstr_imm("sim-buffering"));
+    if (privdata->sms_memory_poll_interval) {
+    	if (privdata->keepalive)
+    		privdata->sms_memory_poll_interval = privdata->keepalive;
+    	else
+    		privdata->sms_memory_poll_interval = AT2_DEFAULT_SMS_POLL_INTERVAL;
+    }
 
     cfg_get_bool(&privdata->retry, cfg, octstr_imm("retry"));
     privdata->my_number = cfg_get(cfg, octstr_imm("my-number"));
@@ -1134,10 +1311,17 @@
 
     buffer = octstr_duplicate(line);
     /* find the beginning of a message from the modem*/ 
-    pos = octstr_search(buffer, octstr_imm("+CMT:"), 0);
-    if(pos == -1) 
+    if ( -1 != (pos = octstr_search(buffer, octstr_imm("+CMT:"), 0)) )
+    	pos +=5;
+    else if ( -1 != (pos = octstr_search(buffer, octstr_imm("+CMGR:"), 0)) ) {
+	pos += 6;
+	if ( -1 != (pos = octstr_search(buffer,octstr_imm(","),pos)) ) // skip status field in +CMGR response
+	    pos++;
+	else
+	    goto nomsg;
+    }
+    else
 	goto nomsg;
-    pos += 5;
 
     tmp = octstr_search(buffer, octstr_imm(","), pos);
     if(! privdata->modem->broken && tmp == -1)
@@ -1480,7 +1664,7 @@
 	    at2_write(privdata,command);
 	    at2_write_ctrlz(privdata);
         /* wait 20 secs for modem command */
-	    ret = at2_wait_modem_command(privdata, 20, 0);
+	    ret = at2_wait_modem_command(privdata, 20, 0, NULL);
 	    debug("bb.at", 0, "AT2[%s]: send command status: %d", 
 		  octstr_get_cstr(privdata->name), ret);
 	    if(ret != 0) /* OK only */
@@ -1498,6 +1682,7 @@
 	    bb_smscconn_send_failed(privdata->conn,msg,SMSCCONN_FAILED_MALFORMED);
 	}
     }
+    O_DESTROY(dlrmsgid);
 }
 
 
@@ -2030,6 +2215,10 @@
 	if(modem->keepalive_cmd == NULL)
 	    modem->keepalive_cmd = octstr_create("AT");
 
+        modem->message_storage = cfg_get(grp,octstr_imm("message-storage"));
+	if (modem->message_storage == NULL)
+	    modem->message_storage = octstr_create("SM");
+				    
 	cfg_get_bool(&modem->broken, grp, octstr_imm("broken"));
 
 	cfg_destroy(cfg);
@@ -2050,6 +2239,7 @@
 	O_DESTROY(modem->init_string);
 	O_DESTROY(modem->enable_hwhs);
 	O_DESTROY(modem->keepalive_cmd);
+	O_DESTROY(modem->message_storage);
 	gw_free(modem);
     }
 }
Index: gwlib/cfg.def
===================================================================
RCS file: /home/cvs/gateway/gwlib/cfg.def,v
retrieving revision 1.45
diff -u -r1.45 cfg.def
--- gwlib/cfg.def	17 Mar 2002 16:03:37 -0000	1.45
+++ gwlib/cfg.def	17 Mar 2002 19:53:47 -0000
@@ -161,6 +161,7 @@
     OCTSTR(smsc-username)
     OCTSTR(smsc-password)
     OCTSTR(keepalive)
+    OCTSTR(sim-buffering)
     OCTSTR(modemtype)
     OCTSTR(device)
     OCTSTR(speed)
@@ -309,6 +310,7 @@
     OCTSTR(sendline-sleep)
     OCTSTR(keepalive-cmd)
     OCTSTR(broken)
+    OCTSTR(message-storage)
 )
 
 #undef OCTSTR
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.