Newer
Older
Import / projects / LGN-IP3870 / t / orig / sms.py
#! /usr/bin/env python
# -*- coding: euc-kr -*-

import modem
import time
from setting import setting
import smdl, status, config
import utils

DLL_SMS_DATA = 0x11
DLL_SMS_ERROR = 0x12
DLL_SMS_EST = 0x13
DLL_SMS_REL = 0x14
DLL_SMS_ACK = 0x15
DLL_SMS_NACK = 0x16

sms_error_code = {
	'wrong checksum': '\x01',
	'wrong message length': '\x02',
	'unknown message type': '\x03',
	'extension not supported': '\x04',
	'unspecified': '\xff'}


class TLError(Exception):
	pass

class DLLError(Exception):
	pass

class SmsModem(modem.Modem):
	def wait_response(self, pattern, timeout=2000):
		buf = ''
		if config.sms_debug:
			print 'in wait_response:', pattern
		while 1:
			buf += self.read(timeout)
			try :
				utils.check_interrupt()
			except Exception:
				#print ' >> sms mode ignore key exception.'
				pass
			if pattern == 'volt':
				value = 0
				import re
				try :
					s_value = re.search("([0-9.]+)",buf).group(1)
					arr_value = s_value.split('.')
					value = int(arr_value[0])
				except:
					value = 0
					#print 'check voltage returns 0'

				if value > 15:
					return

			if buf.find(pattern) >= 0:
				if config.sms_debug:
					print 'in wait_response:', pattern, ' OK'
				return

	# for Type approval
	def sms_write_data(self, data):
		lendata = len(data)
		wait_len = 0.0081 * lendata
		if lendata in (58, 69, 88):
			wait_len = wait_len + 0.003
		elif lendata in (94, 99, 104):
			wait_len = wait_len + 0.009
		elif lendata in (100, 105, 109, 110, 111, 116, 130, 141, 146, 147, 152, 153, 157, 158, 159):
			wait_len = wait_len + 0.015
		elif lendata > 70:
			wait_len = wait_len + 0.007
		elif lendata < 25:
			wait_len = wait_len - 0.003
		if config.sms_debug:
			print '***** wait_len: ', wait_len, 'len: ', lendata
		utils.check_interrupt()
		time.sleep(0.1)
		self.set_bit(modem.RTS)
		self.wait_for_set(modem.CTS)
		utils.check_interrupt()
		self.write(data)
		time.sleep(wait_len)
		self.clear_bit(modem.RTS)
		if config.sms_debug:
			print '******************end sms_write_data'

	def sms_read_data(self):
		if config.sms_debug:
			print '*************begin sms_read_data'
			print 'wait DCD on'
		self.got_dll_header = False
		self.extension_bit = False
		#self.wait_for_set(modem.DCD)
		utils.check_interrupt()

		message = smdl.Parser()
		message.ignore_gabage = config.sms_ignore_gabage
		buf = ''

		while not message.dll_pass:
			buf = message.read_header(self.read(13500))
			utils.check_interrupt()
		self.got_dll_header = True
		self.extension_bit = message.extension_bit

		if message.need_more() and buf:
			message.feed(buf)

		while message.need_more():
			message.feed(self.read(13500))
			utils.check_interrupt()

		if config.sms_debug:
			print 'wait DCD clear'
		self.wait_for_clear(modem.DCD)
		if config.sms_debug:
			print 'end wait DCD clear'
			print '***************end sms_read_data'
		return message

	def sms_answer(self):
		## FIXME
		#for smsrecv-test
		#첫번째 RING에서 cid 가 같이 올라오기 때문에 두번째 RING까지 기다림
		# junk data 라고 생각했던 것이 cid
		#self.wait_response('RING', 10 * 1000)
		#self.wait_response('RING', 10 * 1000)

		self.clear_bit(modem.RTS)
		self.write_command('AT+MS=V23C;B2;+SMS;X1;A\r')
		self.wait_for_clear(modem.DCD)

	def sms_call(self, number):
		self.clear_bit(modem.RTS)
		self.write_command('AT+MS=V23C;B2;+SMS;X1\r')
		cmd = "ATDT " + number + '\r'
		if config.sms_debug:
			print 'in sms_call, cmd=', cmd
		self.write(cmd)
		try:
			self.wait_response('HDX', 10 * 1000) # CONNECT V.23 HDX
		except:
			pass

		time.sleep(1)

	def hw_reset(self):
		try:
			import runtime
#MMW changed 2006. 01.18
			if runtime.modem:
				runtime.modem.close()
			runtime.modem = None


			runtime.dspg.modem_reset()
			time.sleep(1)


#			time.sleep(1)
			runtime.manager.modem_init()
			'''
			try:
				self.open()
			except IOError:
				time.sleep(5)
				self.open()

			try:
				self.write_command('ATZ\r')
			except IOError:
				time.sleep(5)
				self.write_command('ATZ\r')

			if setting.pcm_upstream == 0: #on
				modem_patch = config.modem_patch_upstream_on
			else:
				modem_patch = config.modem_patch_upstream_off

			for file in modem_patch:
				self.load_patch(file)
			self.reset()
			'''
#end of MMW

		except IOError:
#MMW added 2006.01.17
			self.hw_reset()
#end of MMW
			pass
			#print 'sms.py..hw_reset...IOError..self.write_command(\'ATZ\r\')'
#MMW sw reset added by MMW 2006.01.17
	def sw_reset(self):
#		time.sleep(1)
		try:
			try:
				self.write_command('ATZ\r')
			except IOError:
				time.sleep(5)
				self.write_command('ATZ\r')
				pass
			if setting.pcm_upstream == 0: #on
				modem_patch = config.modem_patch_upstream_on
			else:
				modem_patch = config.modem_patch_upstream_off

			for file in modem_patch:
				self.load_patch(file)
			self.reset()

		except IOError:
			print 'do hw reset'
			self.hw_reset()
			pass

#end of MMW

	def enter_ppp_mode(self):
		# eicho add debug 06.01.18
		print 'eicho) sms.py:: enter_ppp_mode -- '
		# eicho remove again 06.02.07
		"""
		try:
			self.write_command('AT+IBC=1\r')
			import os
			os.system('echo 1 > /proc/modem_emctl')
		except IOError:
			pass
			#print 'enter_ppp_mode ... IO ERROR!!!! '
			#print '###### CAN NOT ENTER PPP-mode ######'
		"""
		# eicho end.

	def leave_ppp_mode(self):
		# eicho add debug 06.01.18
		print 'eicho) sms.py:: leave_ppp_mode -- '
		status.phone_status = status.Disconnected
		# eicho remove again 06.02.07
		"""
		self.write_command('AT+IBC=0\r')
		import os
		os.system('echo 0 > /proc/modem_emctl')
		"""
		# eicho end.

	def load_patch(self, file):
		if config.sms_debug:
			print 'loading model patch', file
		patch = [x.strip() + '\r' for x in open(file)]
#MMW		// 2006. 01. 27
		try :
#			self.write('AT**\r')
#			self.wait_response('..') # 'Download initiated ..'
			self.write_command('AT**\r') # . for each line and OK additionally in last command
			self.write_command("".join(patch)) # . for each line and OK additionally in last command
			return True
		except IOError:
			return False
#end of MMW
		# Patch는 echo, vcid setting들을 다 날려버린다.

	def send_sms_error(self, cause):
		dll_error = smdl.Maker(DLL_SMS_ERROR, sms_error_code[cause])
		self.sms_write_data(str(dll_error))

	def send_sms_rel(self):
		dll_rel = smdl.Maker(DLL_SMS_REL, '')
		self.sms_write_data(str(dll_rel))

	def send_sms_nack(self, message=chr(0xFF)):
		#FIXME
		dll_nack = smdl.Maker(DLL_SMS_NACK, message)
		dll_rel = self.send_packet(str(dll_nack))

	def send_sms_ack(self):
		dll_ack = smdl.Maker(DLL_SMS_ACK, '\x00\x00')
		if config.sms_debug:
			print 'writing DLL ACK'
		return self.send_packet(str(dll_ack))

	def send_sms_est(self):
		dll_est = smdl.Maker(DLL_SMS_EST, '')
		if config.sms_debug:
			print 'writing DLL EST'
		return self.send_packet(str(dll_est))

	def send_packet(self, packet):
		if config.sms_debug:
			print '******* begin send_packet'
		self.sms_write_data(packet)

		dll = self.get_packet()
		if dll.get_type() == DLL_SMS_ERROR:
			self.sms_write_data(packet)
			dll = self.get_packet()
		if dll.get_type() == DLL_SMS_ERROR:
			# A.2.8
			self.send_sms_rel()
			raise DLLError, "Got too many Error"
		if config.sms_debug:
			print '******* end  send_packet', dll
		return dll

	def enter_sms_mode(self):
		pass

	def leave_sms_mode(self):
# eicho change the value 06.05.08
		if setting.pcm_upstream == 1:	# PCM upstream OFF
			self.write_command('ATB3;+MS=V92,1,300,36000,300,36000;+PIG=1\r')
		else:				# PCM upstream ON
			self.write_command('ATB3;+MS=V92,1,300,36000,300,36000;+PIG=0\r')
# eicho end.

	def get_packet(self):
		ntry = 2
		while ntry > 0:
			# A.1.4
			try:
				packet = self.sms_read_data()
			except IOError:
				if self.got_dll_header:
					#이미 header를 가져왔으면 실제 데이타가 명시된 값보다
					# 더 작게 들어온 경우이므로 에러를 돌려주어야한다
					self.send_sms_error('wrong message length')
					ntry -= 1
					continue
				# A.1.8
				self.send_sms_rel()
				raise DLLError, "Timeout"
			except DLLError:
				self.send_sms_error('unknown message type')
				continue

			if not packet.checksum_valid:
				# A.2.5
				self.send_sms_error('wrong checksum')
				ntry -= 1
			else:
				return packet
		# A.1.6
		# FIXME: SC로부터 REL message가 온다
		raise DLLError, 'failed to get Packet'

	def add_segment(self, dll):
		import smtp
		smtp_segment = dll.get_payload()

		if len(smtp_segment) > 176:
			# RX 17(b)
			if config.sms_debug:
				print 'payload size:', len(smtp_segment)
			self.send_sms_nack()
			raise TLError, 'Payload size over'

		mtype = smtp.get_sms_message_type(smtp_segment)
		if mtype == 'deliver':
			payload = smtp.Deliver(smtp_segment)
		elif mtype == 'status':
			if config.sms_debug:
				print 'FIXME: Status Report'
				utils.print_hex(smtp_segment)
			payload = smtp.StatusReport(smtp_segment)
		else:
			if config.sms_debug:
				print 'FIXME: Unknown message type'
				utils.print_hex(smtp_segment)
			raise DLLError, 'Unknown message type'

		# Transfer Layer에서 메시지 오류검사
		if payload.bad_udl:
			if self.extension_bit:
				self.send_sms_error('extension not supported')
				raise TLError, "Invalid Message"
			else:
				self.send_sms_nack()
				raise TLError, "Invalid Message"
		self.payloads.append(payload)

	def sms_answer_prepare(self):
		self.enter_sms_mode()
		# modem_debug False 시에 sleep 이 없으면 answer 이 잘 되지 않음
		self.sms_answer()

	def sms_cleanup(self):
		self.hangup()
		self.leave_sms_mode()

	def sms_receive(self):
		self.payloads = []
		try:
			# modem_debug False 시에 sleep 이 없으면 answer 이 잘 되지 않음

			# sleep(1) 이하일 경우 너무 빨리 받는 warning이 뜬다
			time.sleep(1)

			dll_data = self.send_sms_est()

			if dll_data.get_type() == DLL_SMS_REL:
				raise DLLError, "Invalid Message"

			if status.sms_storage_full:
				import smtp
				message = smtp.DeliverReport('SMS Storage Full').segments[0]
				self.send_sms_nack(message)
				raise TLError, "Storage Full"

			self.add_segment(dll_data)

			while 1:
				#Roxia Begin cmlim sms_timeout 06.04.13
				dll_rel = self.send_sms_ack()
				#try:
				#	dll_rel = self.send_sms_ack()
				#except DLLError:
					#print '!!!! send_sms_ack except !!!!'
				#	break;
				#Roxia End cmlim sms_timeout 06.04.13
				if dll_rel.get_type() == DLL_SMS_DATA:
					# A.2.3 Send more than one
					self.add_segment(dll_rel)
					continue
				break
		finally:
			import traceback
			traceback.print_exc()

			self.hangup()
			self.leave_sms_mode()
		return self.payloads

	def sms_send(self, messages):
		self.enter_sms_mode()

		try:
			# SMS PABX Code 처리
			if setting.phone_extension:
				if setting.interdigit_pause:
					self.sms_call(setting.phone_extension + ',' * int(setting.interdigit_pause)+ setting.service_center1 + setting.terminal_number + '0')
				else:
					self.sms_call(setting.phone_extension + setting.service_center1 + setting.terminal_number + '0')
			else:
				self.sms_call(setting.service_center1 + setting.terminal_number + '0')

			dll_est = self.get_packet()
			if dll_est.get_type() == DLL_SMS_REL:
				raise DLLError, "No Message"
			for message in messages:
				dll_data = smdl.Maker(DLL_SMS_DATA, message)
				dll_ack = self.send_packet(str(dll_data))
				if dll_ack.get_type() != DLL_SMS_ACK:
					if dll_ack.get_type() == DLL_SMS_NACK:
						# A.1.2
						self.send_sms_rel()
						raise DLLError, 'got NACK instead of ACK'
					raise DLLError, 'failed to get ACK'
			self.send_sms_rel()
		finally:
			self.hangup()
			self.leave_sms_mode()

class SmsQueue:
	def __init__(self):
		self.segments = []
		self.messages = []

	def add(self, segments):
		for s in segments:
			if s.message_total_number == 1:
				if (s.bMMSNoti):
					self.messages.append(s)
				else:
					s.contents = self.content_refine(s.contents)
					self.messages.append(s)
			else:
				self.segments.append(s)
		self.sort()
		try:
			self.merge()
		except:
			import traceback
			traceback.print_stack()
			traceback.print_exc()

	def get_message(self):
		message = []
		for m in self.messages:
			import smtp
			sm = smtp.SmsMessage()
			if isinstance(m, smtp.StatusReport):
				if config.sms_debug:
					print 'call set_from_status_report', m
				sm.set_from_status_report(m)
			elif isinstance(m, smtp.Deliver):
				sm.set_from_deliver(m)
			message.append(sm)
		self.messages = []
		return message

	def sort(self):
		def segment_sort(x,y):
			if x.message_ref == y.message_ref:
				return cmp(x.message_seq, y.message_seq)
			if x.message_ref > y.message_ref:
				return 1
			return -1
		self.segments.sort(segment_sort)

	def merge(self):
		if config.sms_debug:
			for i, seg in enumerate(self.segments):
				print 'index=', i, 'seg num =', seg.message_seq, 'total num=', seg.message_total_number, 'ref =', seg.message_ref
		i = 0
		while i < len(self.segments):
			first_seg = self.segments[i]
			if config.sms_debug:
				print 'total message number', first_seg.message_total_number
			ref = first_seg.message_ref
			j = i + 1
			while j < len(self.segments) and self.segments[j].message_ref == ref:
				j += 1
			num_seg = j - i
			if num_seg < first_seg.message_total_number:
				if config.sms_debug:
					print 'Message not sufficent'
				i = j
				continue
			segments = self.segments[i:j]
			del self.segments[i:j]
			i = j

			if len(segments) > first_seg.message_total_number:
				new_segs = []
				msg_seq = 1
				for k, s in enumerate(segments):
					if s.message_seq == msg_seq:
						new_segs.append(s)
						msg_seq += 1
					elif s.message_seq > msg_seq:
						if config.sms_debug:
							print 'bad message seq: discarding'
						continue
				segments = new_segs

			# sanity check
			for k in range(len(segments)):
				if config.sms_debug:
					print 'seq:', segments[k].message_seq
				if segments[k].message_seq != (k + 1):
					if config.sms_debug:
						print '********message seq is not valid, discarding'
					return
			self.messages.append(self.join_segments(segments))

	def join_segments(self, segments):
		contents = []
		for seg in segments:
			for dtype, data in seg.contents:
				if dtype == 'Text':
					if contents and contents[-1][0] == 'Text':
						contents[-1] = 'Text', contents[-1][1] + data
						continue
				contents.append((dtype, data))
		if (segments[0].bMMSNoti):
			segments[0].contents = contents
		else:
			segments[0].contents = self.content_refine(contents)
		return segments[0]

	def content_refine(self, contents):
		raw_data = ''
		raw_type = ''
		for i, (type, data) in enumerate(contents):
			if type == 'Text':
				import gsm7
				contents[i] = 'Text', gsm7.gsm7bit_decode(data)
			elif type in ('Large Picture', 'Small Picture', 'Variable Picture'):
				contents[i] = 'Bitmap', data.get_bitmap()
			elif type in ('CLI Icon', 'Operator Logo'):
				contents[i] = 'Bitmap', data.get_bitmap()
			elif type in ('Ring','User Defined Sound', 'Predefined Sound'):
				contents[i] = 'Melody', data.get_melody()
			elif type == 'Raw':
				ctype, cdata = data
				if raw_type and ctype != raw_type:
					if config.sms_debug:
						print 'invalid smartmessage output'
						print 'contents', contents
					continue
				raw_type = ctype
				raw_data += cdata
				contents[i] = None
		if raw_type:
			import smartmsg
			for cdata in contents:
				if cdata != None:
					if config.sms_debug:
						print 'invalid smartmessage output'
						print 'contents', contents
					return []
			if raw_type == 'Multipart':
				multi = smartmsg.Multipart(raw_data)
				contents = multi.contents
			else:
				contents = [smartmsg.parse_content(raw_type, raw_data)]
			if config.sms_debug:
				print 'contents in', self, contents
			return self.content_refine(contents)
		if config.sms_debug:
			print 'contents in', self, contents
		return contents

sms_queue = SmsQueue()

def recv(modem):
	payloads = modem.sms_receive()
	sms_queue.add(payloads)
	message = sms_queue.get_message()
	if config.sms_debug:
		print '######## recv msg len:', len(message)
	return message

def send_email(text, **kw):
	import smtp
	message = smtp.SmsMessage('0', 'email', [('Text', text)])
	message.send()

if __name__ == '__main__':
	def handle_modem_event(*ev):
		print 'modem event', ev
	m = SmsModem(handle_modem_event)

	print repr(smdl.Maker(DLL_SMS_EST, ''))

	# italy
	m.write_command('AT+GCI=59\r')

	# korean
	#m.write_command('AT+GCI=61\r')

	m.write_command('AT-STE=7\r')


	m.load_patch('Patch_EIS_07_01_RAM.S37')
	if 1:
		m.sms_send('123456789012')
	#	try:
	#		m.sms_send('Test')
	#	except:
	#		print 'except occurred '
	#		m.leave_sms_mode()
	else:
		m.sms_receive()

	#m.hangup()
	print 'all ok...'