TATA SMS需求的实现

       
       基于VIA Selle1.0在FC500的机型上添加了:空短信Page
Message,重复短信Duplicate Message,类似UTK的短信订阅服务Message Service等功能.
      
以上皆为印度TATA的需求规范,其中包括允许发送内容为空的短信并存储,收到类型为Page
Message的消息时,显示回呼界面。重复短信要求跟据MsgID,OriginalAddress,ContentData为依据,收到重复短信时覆盖掉原有项,并提示重复消息.由于FC500是无卡的版本,没有UTK的功能,Message
Service实现了由列表项快速订阅相关短信服务的功能.以上实现可以在Embedded的列表中找到相关文章.

VIA Selle SMS 系列之—发送篇

上一篇文章由Duplic
Message引出了SMS的接受过程,这篇文章则由Page Message引出SMS的发送过程,同样是基于Selle1.0的。
二,短信的发送过程
这里只讲到从新建短信,添加联系人,然后发送的典型过程,不包括调用接口直接发送短信的情况,稍有不同。
1.UI层新建短信,编辑内容,选择发送:
编辑内容不为空时,此时按Send键,会调用:(或者在Edit Message的窗口中选择Options选项,也会调用:)
     SmsViewC::SetDataNewMsg(DialogC *DlgP, uint32 MsgId, uint32
Param)
      {
  EditCtrlC *EditCtrlP =
(EditCtrlC*)DlgP->GetControlByType(CTRL_TYPE_EDIT);
         StringC
StrText;
  EditCtrlP->GetText(StrText);
SmsEditMsgManagerC *EditMsgManagerP =
((SmsModelC*)mModelP)->GetEditMsgManager();
           //set StrText
to Model
          if(MsgId ==
APPMSG_SMS_NEWMSG)
         {
           EditMsgManagerP->SetEditMsgText(StrText); 

//激活 SmsEditMsgManagerC中具体的短信对象即mEditMsg,并调用默认的短信设置选项来进行初始化设置!

          }
      }
这里EditMsgManagerP是一个临时的对象,包括你在新建短信时输入的内容,与之后添加的收件人,及其相关设置。
编辑内容为空时,此时Send键不起做用,Selle默认好像是不支持空短信的,在smscust中配置设置。
const SmsCustSettingsT
gSmsCustSettings =
{
 TRUE,//FALSE didn’t allow to send blank sms
 TRUE,
  "10010"
};
判断的方法如下:   
//if can not send empty message, then
delete Send and group menu item
if(
!SmsGetCustSettings()->mSendEmptyMsg )
{
    //为空时不删除Seng等按键
}
2.UI层编辑内容后,添加联系人,选择发送:
这里以直接输入联系人电话号码为例,输入对方联系人手机号,点击发送后,会调用:
SmsViewC::SetDataNewMsgEditAddress(DialogC
*DlgP, uint32 MsgId, uint32 Param)
{
    SmsEditMsgManagerC
*EditMsgManagerP = ((SmsModelC*)mModelP)->GetEditMsgManager();
    StringC
StrAddress;
AddressEditCtrlP->GetText(StrAddress);
   
//……
省略了对多联系人及联系人列表中出现";"的处理
EditMsgManagerP->ClearEditGroupAddress();
 EditMsgManagerP->SetEditGroupAddressNumber(StrAddress);//把输入的联系人放到临时的编写短信的对象中
 EditMsgManagerP->SaveEditGroupAddress();
}
在支持空短信的处理中,我在这后面添加了从临时对象中取出短信内容,并添加类似这样的内容: "Call:123456789"
    SmsRecordC
*SmsRecordP;
  SmsRecordP = new SmsRecordC;
   SmsRecordP->CopyFrom(*(EditMsgManagerP->GetEditMsg())); //从临时对象中实例化一个SmsRecordP的对象
 StringC Content;
 SmsRecordP->GetUserData(Content);
 if(Content.GetLength()<1)
 {
    Content.Append("Call:");
    Content.Append(StrAddress);
   
EditMsgManagerP->SetEditMsgText(Content);
 }
其实这一步可以在第一步输入为空时进行操作,但在这里还要做合并号码的动作,所以就在这个地方一起处理了。
在执行完SetData的动作后,还会同时执行发送动做,因为根据输入的内容不为空,即有号码输入时:
if(Event ==
EDIT_EVENT_DATA_EMPTY) //no data in edit control, set left softkey bar to
‘search’
{
         
SoftkeybarP->SetButtonTitle(1,IDS_SMS_SEND);
         
SoftkeybarP->SetButtonMessageId(1, APPMSG_SMS_NEWMSG);
          if(
NEWMSGEDITADDRESS==EditType )
//为编辑号码状态
          {
           
SoftkeybarP->SetButtonMessageParam(1,APPMSG_SMS_PARAM_NEWMSG_SEND);
          }
}
所以按Send的同时会执行(跟踪此消息可查知,不再详细列出其消息映射关系)
3.APP层处理发送过程:
    首先会处理,APPMSG_SMS_NEWMSGAPPMSG_SMS_PARAM_NEWMSG_SEND的消息:
int
SmsControllerC::HandleNewMsgSend(uint32 MessageId, uint32 Param)
{
 //check if previous message sending
finish
 if
(!mModelP->IsPreviousMessageSendingFinish())
 {
   
GetView()->OpenPopup(APPSMS_DIALOGID_POPUP_SENDING_PREVIOUS_MESSAGE);
    return -1;
 }
 //add message to send
 if(
IsEqual(ASLSMS_RESULT_INVALID_PARAMETER, mModelP->AddEditMsgGroupToPending()) )//A
 {
   
GetView()->OpenPopup(APPSMS_DIALOGID_POPUP_ADDRESS_EMPTY);
    return 0;
 }
 //send message
 SendMessage(HasMemoryFullDialog);//B
}
其上面A处AddEditMsgGroupToPending()的做用是把编写短信时的临时对象EditMsgManagerP中的内容添加到一个代发短信列表后面,即LinkedListC
mPendingMsgList;大致的过程为:
AslSmsResultT
SmsModelC::AddEditMsgGroupToPending()
{
 int i=0;
 SavePendingMsg();//clear outbox message这里我的理解为,把PendingList中的第一条内容存储到Storage当中
    {
    SmsRecordC *SmsRecordP;
    SmsRecordP = new SmsRecordC;
    if( IsNull(SmsRecordP) )
    {
      return
ASLSMS_RESULT_OUT_OF_MEMORY;
    }
   
SmsRecordP->CopyFrom(*mEditMsgManager.GetEditMsg());
//zjj add 20090916 —begin 添加空短信时的特殊处理
#ifdef FEATURE_PAGEMESSAGE   StringC
Content;
 StringC CallContent("Call:");
 SmsRecordP->GetUserData(Content);
 if((Content.SubString(0,5).GetLength()==5)&&(IsEqual(Content.SubString(0,5),
CallContent)))
 {
   
SmsRecordP->SetTeleserviceID(AslSmsGetTeleserviceIdByType(ASLSMS_TELESERVICE_TYPE_PAGE));
//这里判断为空短信时,要把短信的TeleserviceID设置了4097,接收方跟据此标志在HandleMessageIncoming里来做特殊的显示处理
 }
#endif 
//zjj add 20090916 —end
    SmsAddressC *AddressP =
(SmsAddressC*)(*AddressArrayP)[i];
    UIASSERT( !IsNull(AddressP)
);
   
SmsRecordP->SetSmsAddress(*AddressP);
   
AddToPendingMsg(SmsRecordP);
//把当前短信添加到发PendingList的尾部,等待发送。
 }
 return ASLSMS_RESULT_SUCCESS;
}
然后是B处,SendMessage(HasMemoryFullDialog)的处理:
void SmsControllerC::SendMessage(bool
HasMemoryFullDialog,bool IsFromEditMsg,bool IsForEditAddress)
{
 //Check if is in airplane mode.
 #ifdef SYS_OPTION_RUIM
 //check if have uim card or not
 #endif
 //check if has network.
 if (
IsEqual(AslGetServiceInfo()->ServiceStatus, VAL_PSW_NO_SERVICE))
 {
   
OpenPopup(APPSMS_DIALOGID_POPUP_NO_NETWORK);
    //clear pending list
    mModelP->ClearAll();
    return;
 }
 //check storage 强调的一点是这里处理有关是否存储发送短信的相关操作,在下一篇中再介绍
 if(
!CheckStorageBeforeSendMsg(HasMemoryFullDialog,IsFromEditMsg,IsForEditAddress)
)
 {
    //clear pending list
    mModelP->ClearAll();
    return;
 }
 AslSmsResultT Result =
mModelP->SendPendingMsg() ;//具体的发送过程在这里
 APPSMS_PRINTF((char*)"SmsControllerC::SendMessage
Result = %d", Result);
 if( (Result == ASLSMS_RESULT_SUCCESS)
|| (Result == ASLSMS_RESULT_SENT_BUT_SAVE_FAILED) )
}
SendPendingMsg()的大致流程如下,省略了与主题无关的代码:
AslSmsResultT
SmsModelC::SendPendingMsg()
{
   //no message to send, then return
success
 if(mPendingMsgList.GetCount() == 0
)
 {
    return
ASLSMS_RESULT_SUCCESS;
 }
 // 设置发送频道
 if(IsEqual(1, GetSendChannelSidb()))
 {
    
APPSMS_PRINTF((char*)"SmsModelC::SendPendingMsg: set channel to TC");
   
ValSmsSetChannel(VAL_SMS_CHANNEL_TC);
 }
 else
 {
   
APPSMS_PRINTF((char*)"SmsModelC::SendPendingMsg: set channel to Default");
   
ValSmsSetChannel(VAL_SMS_CHANNEL_DEFAULT);
 }
 SmsRecordC *SmsRecordP =
(SmsRecordC*)mPendingMsgList.GetHead();//获取待发送列表中的第一条
 uint16 RecordId=0;
 uint16 MessageId=0;
 AslSmsResultT Result =
AslSmsSendMessage(*SmsRecordP, &RecordId, &MessageId,
GetSettings()->mAutoSave);//调用ASL层的函数继续处理发送过程
 if( (Result == ASLSMS_RESULT_SUCCESS)
|| (Result == ASLSMS_RESULT_SENT_BUT_SAVE_FAILED) )
 return Result;
}
4.ASL层的发送处理过程:
主要处理是根据是否为自注册短信,而选择存储或不存储此发送短信
AslSmsResultT
AslSmsSendMessage(SmsRecordC &SmsRecord, uint16 *RecordIdP, uint16
*MessageIdP, bool ToSave)
{
 AslSmsResultT result ;
#ifdef FEATURE_SMS_AUTO_REGISTER
 if(SmsRecord.GetAutoRegisterFlag())
 {
    //not
need save if is AutoRegister message.
    result = (AslSmsResultT)ValSmsSendTxtMessage(gSmsRegisterId, RecordIdP,
SmsRecord, MessageIdP, FALSE);
 }
 else
#endif
 { 
    result = (AslSmsResultT)ValSmsSendTxtMessage(gSmsRegisterId, RecordIdP,
SmsRecord, MessageIdP, ToSave);
 }
 gSmsUnReadCount = -1;//Unread sms
number should be update
 return result;
}
5.VAL层的发送过程:
ValSmsSendTxtMessage 不再详细介绍,其间是一些发送设置,最重要是继续调用ValSmsSendMessage来发送
ValSmsSendTxtMessage …
{
 if ( 0 != ValSmsSendMessage( SmsTxtMsgP ) ) //SmsTxtMsgP 为发送内容
 {
    ValSmsSendInfo[RegId].Acked =
TRUE; /* send failed, then clear the flag of ack */
    return
VAL_SMS_ERR_MSG_FORMAT;
 }
}
这里只以新建短信的发送过程为例:
int ValSmsSendMessage( ValSmsMessageT *MessageP )
{
 ValSmsTeleMsgT MsgType;
 MsgType =
MessageP->TeleMsgType;
 switch(MsgType)
 {
    case
VAL_SMS_MSG_TYPE_ORIGINATION_SUBMIT:
      i = SendSubmitMsg(MessageP);
      break;
       ……
      }
}
SendSubmitMsg函数中有更为复杂的参数设置,如跟基站交互时发送地址的格式问题等,这里就不一一介绍了(如果你对这里的设置很熟悉,欢迎跟我交流,一起研究下,现在也不是很明白:),但关键有一处,如下:
static int SendSubmitMsg(
ValSmsMessageT* MessageP )
{
 /* send ack data message to PSW
*/
 ExeMsgSend(EXE_PSW_ID,
PSW_MAILBOX_CMD, PSW_SMS_SUBMIT_DATA_MSG,
             (void *)DataMsgP,
sizeof(PswSmsUserDataMsgT));
 ——-or—————————————————————-
 /* send sms
parameter message to PSW
*/
 ExeMsgSend( EXE_PSW_ID,
               PSW_MAILBOX_CMD,
              
PSW_SMS_SUBMIT_PARMS_MSG,
  
(void*)ParmMsgP,
   sizeof(
PswSmsSubmitParmsMsgT ) );
}
把短信发送到协议层,然后就真的发出去了,至于PSW层的发送处理,唉,搞协议层暂还没纳入规划:)
6.总结:
以上实现了以空短信为例,从最上层的UI—>APP—>ASL—>VAL—>PSW层的处理过程,即短信发送的大致过程。
下一篇准备介绍有关短信是否存储的相关设置问题,哈哈,希望比这篇要短很多。

 

VIA Selle SMS 系列之—接收篇

最近一段时间都在整VIA的SMS相关需求,有Page
Message,Duplic Message,Message Service等,积累了一些经验与大家分享,是基于Selle1.0的版本。
(由于与SMS关于代码分布在PS,VAL,UI三层,并且每层都有复杂的处理过程,这里只以某些功能的实现为主线串一下相关过程,不太可能覆盖的很全面,有兴趣的同行们可以补充:)
一,短信的接收过程
短信由基站发送到手机,首先应该是协议层接收到,然后会通知VAL层,再然后到ASL层,最后到APP,UI层。
具体点:
1.协议层会发送给VAL层消息,通知有SMS到来:
The messages received from PSW have
following kinds:
VAL_SMS_DELIVER_IND_PARMS_MSG: //短信的头,包含地址,设置等信息
VAL_SMS_DELIVER_IND_DATA_MSG: 
//短信的内容,如果是空短信,内容为空,不会处理此消息
VAL_SMS_ERROR_MSG:
VAL_SMS_BCAST_IND_PARMS_MSG:
VAL_SMS_BCAST_IND_DATA_MSG:
VAL_SMS_BCAST_ERROR_MSG:
VAL_SMS_CAUSE_CODE_STATUS_MSG
这里以普通短信类型为例,Task会找到对应此消息的处理函数为:
message hangler for messages from PSW
task
bool ValProcessSmsMsg( uint32
MsgId,void* MsgBuf,uint32 MsgLen )
{
         ……
    case VAL_SMS_DELIVER_IND_DATA_MSG:
      ValSmsTstSendSpy( MsgBuf, MsgLen
);
      ValSmsProcessDeliverDataMsg( MsgBuf, MsgLen );
      break;
}
在函数ValSmsProcessDeliverDataMsg(
MsgBuf, MsgLen )中对收到的短信进行复杂的处理,看相应的代码应该可以明白,这里只说几处关键的地方:
int8 ValSmsProcessDeliverDataMsg(
void* MsgBuf, uint16 MsgLen )
{
ValSmsIncomingMsgT SmsIncomingMsg;
 /* initialize */
 RcvMsgP =
(ValSmsUserDataMsgT*)MsgBuf;
 /* fill-in the buffer with the
message received */
    datalen =
RcvMsgP->NumFields;
    for (i = 0; i < datalen;
i++)
    {
     
TxtMessage.UserData[NumUserDataRcv].Data[i] = RcvMsgP->Char[i];
    }
 /* send the message to UI */
 if (
TxtMessage.TeleSrvId==VAL_SMS_TS_ID_CATPT)  
 {
           /* For UTK
message */
 }
 else
 {
        if
(TxtMessage.TeleSrvId==VAL_SMS_TS_ID_VOICE_MAIL)/*VMN, just save into the
previous position */
    {
             /*For Voice Mail*/
     }
         else if
         {
           /* check if is
immediate message */
         }
         else
         {
               /*
other teleservice than VMN */
            if(ValSmsJudgeDupMsg(&TxtMessage,&recid,&ValSmsDupPolicy))//判断SMS重复的条件,可以是内容,地址或是MsgID
            {
             
SmsIncomingMsg.
isDuplicateSms =
TRUE;//此字段是后来加上的,可以把自己添加一些特殊信息,传送到UI
              //result =
ValSmsWriteTxtMessage( &TxtMessage, &recid );
              result =
ValSmsUpdateTxtMessage( &TxtMessage, recid );
             
//SendMsgWriteAck(VAL_SMS_SUCCESS, RcvMsgP->SeqNum);
   //return 0;        
            }       
            else
            {
             
SmsIncomingMsg.isDuplicateSms = FALSE;//zjj add 20091022
              if(!bStoreInUIM)
              {
                result =
((*CheckSaveOneMessageFunc)(&TxtMessage, &recid));            
              }
              else
              {
                result =
ValSmsWriteTxtMessage( &TxtMessage, &recid );
              }            
            }
                  if(result !=
VAL_SMS_SUCCESS)
                  {
              SendMsgWriteAck(result,
RcvMsgP->SeqNum);
             
SendMsgWriteErr(device,result);
              return -2;           
                  }
            TxtMessage.SmsMsgRecId =
recid;
            SmsIncomingMsg.recid =
recid;
         }
      }
   ProcessEvt(VAL_SMS_EVENT_INCOMING_MESSAGE,
TxtMessage.TeleSrvId, (void *)&SmsIncomingMsg);
   }
ProcessEvt会调用注册消息VAL_SMS_EVENT_INCOMING_MESSAGE对应的asl层的回调函数:
注册的地方在:
/*!
\brief initialize SMS Data: register
callback function, register teleservice id
bool AslSmsInitialize (void)
{
 gSmsRegisterId = ValSmsRegister(SMSMessageListener);
}
2.接到收此消息后,轮到ASL层的处理;
/*
 SMSMessageListener: to process event
from VAL
*/
static void SMSMessageListener( RegIdT RegId, ValSmsEventIdT
Event, void *MsgP)
{
    ……
 switch (Event)
 {
 case VAL_SMS_EVENT_INCOMING_MESSAGE:
    SmsListenerIncomingMessage(MsgP);
    break;
 case
VAL_SMS_EVENT_SEND_STATUS_MESSAGE:
    SmsListenerSendStatus(MsgP);
    break;
       ……
 }
}
static void SmsListenerIncomingMessage(void *MsgP)
{
 UiGetMailServer()->PostMail(APPMSG_SMS_MESSAGE_LISTEN_INCOMING,
WIN_HANDLE_NULL, (uint32)MsgP, sizeof(ValSmsIncomingMsgT), BY_ADDRESS);
}
void AslSmsListenerIncomingMessage(void *MsgP)
{
     ……
 ValSmsIncomingMsgT *SmsIncomingMsgP =
(ValSmsIncomingMsgT*)MsgP;//这样就收到了在VAL层自己添加的信息
 VsmsRecordId =
SmsIncomingMsgP->recid;
 bool duplicateSms=SmsIncomingMsgP->isDuplicateSms;
    ……
     
UiGetMailServer()->PostMail(APPMSG_SMS_MESSAGE_INCOMING, WIN_HANDLE_NULL,
(uint32)VsmsRecordId, (uint32)
duplicateSms, BY_VALUE); //这里原来第二个参数为空,但也可以添加想传到UI层的自定义数据。
}
以上为SMS在ASL层的流程,从代码可知其意,过渡到App,UI之上。
3.ASL层发送消息,映射函数到APP层接着处理:
ON_MSG_VOID_WORDLONG(APPMSG_SMS_MESSAGE_INCOMING,SmsAppC::OnMessageIncoming)//这里要注意的是,由于自己添加了一个参数,所以映身类型从ON_MSG_VOID_WORD改为了ON_MSG_VOID_WORDLONG即传递了两个参数。
void SmsAppC::OnMessageIncoming(uint32
Param,uint32 ParamB)
{
 mControllerP->HandleMessageIncoming(Param,ParamB);
}
下面这个函数,负责处理SMS在UI上的显示,如是直显短信就直接显示内容,是普通短信则打开提示窗口,是重复短信则跳出来我加上去的窗口
void
SmsControllerC::HandleMessageIncoming(uint32 Param,uint32 ParamB)
{
 ……
 if(ParamB) //if ParamB is True ,it is
duplicate Sms
   {
      SmsViewMsgManagerC
*ViewMsgManagerP = ((SmsModelC*)mModelP)->GetViewMsgManager();
      UIASSERT(
!IsNull(ViewMsgManagerP) );
     
ViewMsgManagerP->SetViewMsg(NewMsg);
//这里把Controller层的新短信数据,传到UI层可以读取并显示
     
if(!IsTrue(GetView()->IsDialogOpened(APPSMS_DIALOGID_DISPLAY_DUPLICATE_MSG))
)
      {
        
GetView()->OpenDialog(APPSMS_DIALOGID_DISPLAY_DUPLICATE_MSG);
//打开自定义的重复窗口
      }
      else
      {
        
GetView()->UpdateView(APPSMS_DIALOGID_DISPLAY_DUPLICATE_MSG);
      }
      //update menu info
     
GetView()->UpdateSmsListInfoOnMsgModify();
      //start alert
     
GetView()->StartAlert();
     
GetView()->UpdateAlert();
      return;
   }
 ……
}
4.总结:
以上实现了在VAL判断是否是重复短信,把结果放在一参数中,传到UI,跟据此参数来显示不同内容的大致方法,即短信接收的大致流程。
本来想写成一篇,看来这篇简介己经够长了,所以拆分成了一个个之一了
🙂
下次介绍Page Message的实现,亦即短信发送的大致过程。

 

scrum

Kniberg的《Scrum与Xp》详细讲述了他如何实施敏捷的软件团队的过程及经验,比较普通的教科书式的敏捷教材,这本小书要生动,实用很多,受此启发,写了一篇简短的文章,关于公司所在VIA团队采用Scrum的实施方案,相信能够提高VIA团队的生产力。 

VIA组 实施Scrum可行性的方案

现在在公司做基于VIA的方案,VIA组包括UI,驱动两部分。接到项目后大致的流程是,首先从UI组找一个做软件PM主跟项目,然后项目PM会安排项目日期,包括硬件,驱动,软件。在确定项目日期后,软件PM开始着手来准备软件的基版,在VSS中上传软件代码,再然后开始软件的开发迭代。驱动更新,UI的更新都提交到VSS中,直到最后发布版本。
现在突出的问题是:软件的进度无法保障,软件的质量很难控制到位。
具体的原因分析为:1. 用户不断变化的需求
                  2.
VIA方案版本的不断更新
                  3. 工程师资源紧张
                  4.
项目前期的评估没有与工程师进行讨论,对工作量及可能遇到的难点估计不足
现在公司VIA的项目有个很大的特点是,开发过程的重点在用户需求及Bug的修改上,因为用的是VIA给的参考设计来做基版,基本功能都以实现,所以项目软件的开发过程可分为两个部分,需求的开发,Bug的修改。这两部分也是我们的主要工作内容。
用敏捷的项目管理方法来实施这部分软件的开发工作,对项目的进度,质量及资源的分配将会更加可控与合理。具体的方案如下:
1.划分BackLog:在得到用户需求与VIA给的Base版软件的基础上,进行分析,哪些是己实现的功能,哪些是需要我们添加的功能,哪些是需要VIA支持才能完成的功能。把这些都详细分成一个个条目。 如:
        条目1.自己完成添加短信防火墙;
     条目2.在VIA的协助下完成阿拉伯输入法的添加;
     条目3.发短信后的UI提示不正确的Bug;
这次拆分Backlog按功能来划分,有些功能会很大,有些则会很小,把这些故事点都罗列出来,进步一下的时间评估,及资源的分配。
2.制定Spring计划:每个Spring是一个阶段的工作,如2个星期,每个Spring结束都会给出一个演示版本,VIA这里可以表示为对外发布一版测试版。长度2周为好,时间过短会造成频繁发布版本的麻烦,时间太长不符合VIA项目的特点,因为VIA的重点是需求的实现及Bug的修改,而不是完全从头开发。由于我们人数有限,所以只能划分一个Spring了。然后由工程师来一起评估每个故事点所需要的时间,其Own是谁,之后把所有信息帖在如图的一张画板上,这样项目的进度,当前的问题,责任人都非常清楚。右边的是燃进图,记录了当前项目的总体状态,便于以后对Spring的评估与分析。Unplanned
Items 可以修改Bug产生的Bug,或测试新发现的Bug,数量较少时可以由工程师自行解决,较多时则需要重新制定Spring来完成,如此进行迭代。
 
看板的好处还有,项目经理可以明确知道每个项目当前的状态,就不用一直骚扰我们工程师了:)
3.对Spring进行验收。每2周对外发布测试版本,需要的是完整的测试版本,可以交给测试部及开发部区域测试的可验证版本。当所有的Spring结束后,测试没有问题,交给客户进行测试验收。
以上只是大致的流程,细节还有很多,最突出的是一个人同时有很多项目,这样就要按个人来重新划分故事点,按优先级对项目进行评估,与项目经理,部门经理进行评估,同样给出项目进度的看板来。
实施Srcum后,项目的进度可以有较为明确的日期,也可以尽早的暴露问题,工程师也可以有了自己较为明确的目标。项目进度可控,就不会产生为赶工期而牺牲了软件质量,测试力度也会尽可能的充分。
总的来说,相信VIA组实施Scrum后,会有助于提高生产力。

 

WindowsMobile 6.5 and Windows7

虽然VIA的系统己不知Down过多少次,但还是第一次给我的WindowsMobile刷机,现在用的是最新版6.5rom,感觉不错,跟家里的Windows7一样,简洁漂亮实用。手机是比较老838的机器,刷系统还是比较麻烦的,要用WM5先降IPL与SPL,然后解网络锁,解CID锁,再然后才刷成功,否则刷后仍会是之前的版本。