
暂无个人介绍
我的博客即将入驻“云栖社区”,诚邀技术同仁一同入驻。
近几个月来,全国多个地区的居民已经开始感受到窄带物联应用带来的变化,每个居民在日常生活中经常接触到的“抄表”业务,成了窄带物联应用拓进的第一片区域。 深圳6000用户安装智能燃气表 日前,深圳电信公司宣布首单6000户的窄带物联(NB-IoT)智能燃气表放号将部署在福田区上步村管道气升级改造。根据规划,6000户智能燃气表已开始安装调测,10月交付居民使用。 上月,工信部首次颁发了物联网号段:中国移动获得148(0-9)号段(物联网业务专用号段)、1440(0-9)号段(物联网网号);中国电信获得了1410(0-9)号段(物联网网号);中国联通获得了146(0-9)号段(物联网业务专用号段)。此次三大运营商均获得物联网号段,意味着物联网商用大幕的正式开启,而深圳的这个项目,是自8月初工信部分配物联网号段以来,第一个正式放号的NB-IoT网络。 深圳“智慧燃气”项目早在2016年11月便启动,完成了从实验室测试技术方案标准制定,从选点部署到现场测试等系列工作,在近期迎来商用落地阶段。 数据自动传输取代人力采集 窄带物联在室内覆盖的功能比较强大,同时会支持大规模的设备连接,大规模的设备连接是物联网一个比较独特的属性,其设备的复杂性相对比较小,功耗和延时也比较小,一节AA电池,最多可以让设备工作数年之久。 正是因为这种特性,窄带物联被首先应用在了“抄表”上。这里所说的“抄表”,其实是指日常水表、电表、燃气表的数据采集和记录。 以深圳智慧燃气表项目为例,居民安装采用NB-IoT物联网技术的燃气表,燃气使用数据会自动传到燃气公司,这样工作人员就不用上门抄表了,一方面节省人力、建设成本,一方面也能实时监控,及时发现问题。 实际上,这意味着,居民每月定时接受水厂、电厂、燃气公司等定时“骚扰”的时代正在离我们而去。 而从抄表工作人员角度来说,他们的工作,从敲门,变成了屏幕前的数据查看、采集。 智能抄表商用步伐或将加快 日前,国内运营商联通在广州琶洲国际会展中心举行了“物联网生态大会”——中国联通物联网生态大会,会议期间,中国联通就“物联网平台+”生态战略发展规划进行了详细披露,主要包括物联网基础能力的构建和产业合作计划,并发布了物联网开放实验室落地广州的消息。 此次中国联通提供了窄带物联的良好研发测试环境,提供各种频段、多种制式以及华为、中兴、爱立信等国内外主流厂商的网络设备。同时,实验室拥有完善的端到端集成环境,提供广和通、有方、移远、大唐、SIMCOM、龙尚等主流芯片、模组及各种型号天线等物联网部件,可开展模组、终端与现网的联调测试,针对不同物联网应用特点提供最优的整体解决方案。 实验室与珠海格力、广州燃气、大唐电信、南方电网、中山乐心等合作,提供调测与集成服务,部分产品已经正式投入商用,其中,智能抄表正是一块先行而重要的业务领域。早先联通已在福建福州自来水抄表上支持窄带物联的试商用。随着广州开放实验室业务的深入开展,联通在广东推进窄带物联在智能水务、电力和燃气表上的应用步伐也会迅速加快。 记者同时了解到,目前,已有不少硬件设备、电力行业等上市公司积极与运营商展开合作,而这意味着,在国家政策利好下,窄带物联智慧抄表将会加速进入千家万户。 南方日报记者 姚翀
通常,我们把物联网设备分为三类:①无需移动性,大数据量(上行),需较宽频段,比如城市监控摄像头。②移动性强,需执行频繁切换,小数据量,比如车队追踪管理。③无需移动性,小数据量,对时延不敏感,比如智能抄表。NB-IoT正是为了应对第③种物联网设备而生。NB-IoT源起于现阶段物联网的以下几大需求: •覆盖增强(增强20dB)•支持大规模连接,100K终端/200KHz小区•超低功耗,10年电池寿命•超低成本•最小化信令开销,尤其是空口。•确保整个系统的安全性,包括核心网。•支持IP和非IP数据传送。•支持短信(可选部署)。对于现有LTE网络,并不能完全满足以上需求。即使是LTE-A,关注的主要是载波聚合、双连接和D2D等功能,并没有考虑物联网。比如,在覆盖上,以水表为例,所处位置无线环境差,与智能手机相比,高度差导致信号差4dB,同时再盖上盖子,额外增加约10dB左右损耗,所以需要增强20dB。 在大规模连接上,物联网设备太多,如果用现有的LTE网络去连接这些海量设备,会导致网络过载,即使传送的数据量小,可信令流量也够得喝上几壶。此外,NB-IoT有自己的特点,比如不再有QoS的概念,因为现阶段的NB-IoT并不打算传送时延敏感的数据包,像实时IMS一类的设备,在NB-IoT网络里不会出现。因此,3GPP另辟蹊径,在Release 13制定了NB-IoT标准来应对现阶段的物联网需求,在终端支持上也多了一个与NB-IoT对应的终端等级——cat-NB1。尽管NB-IoT和LTE紧密相关,且可集成于现有的LTE系统之上,很多地方是在LTE基础上专为物联网而优化设计,但从技术角度看,NB-IoT却是独立的新空口技术。今天,我们就来看看这一新空口技术到底有多新?1 网络1.1 核心网为了将物联网数据发送给应用,蜂窝物联网(CIoT)在EPS定义了两种优化方案:•CIoT EPS用户面功能优化(User Plane CIoT EPS optimisation)•CIoT EPS控制面功能优化(Control Plane CIoT EPS optimisation) 如上图所示,红线表示CIoT EPS控制面功能优化方案,蓝线表示CIoT EPS用户面功能优化方案。对于CIoT EPS控制面功能优化,上行数据从eNB(CIoT RAN)传送至MME,在这里传输路径分为两个分支:或者通过SGW传送到PGW再传送到应用服务器,或者通过SCEF(Service Capa- bility Exposure Function)连接到应用服务器(CIoT Services),后者仅支持非IP数据传送。下行数据传送路径一样,只是方向相反。这一方案无需建立数据无线承载,数据包直接在信令无线承载上发送。因此,这一方案极适合非频发的小数据包传送。SCEF是专门为NB-IoT设计而新引入的,它用于在控制面上传送非IP数据包,并为鉴权等网络服务提供了一个抽象的接口。对于CIoT EPS用户面功能优化,物联网数据传送方式和传统数据流量一样,在无线承载上发送数据,由SGW传送到PGW再到应用服务器。因此,这种方案在建立连接时会产生额外开销,不过,它的优势是数据包序列传送更快。这一方案支持IP数据和非IP数据传送。1.2 接入网NB-IoT的接入网构架与LTE一样。 eNB通过S1接口连接到MME/S-GW,只是接口上传送的是NB-IoT消息和数据。尽管NB-IoT没有定义切换,但在两个eNB之间依然有X2接口,X2接口使能UE在进入空闲状态后,快速启动resume流程,接入到其它eNB(resume流程将在本文后面详述)。1.3 频段NB-IoT沿用LTE定义的频段号,Release 13为NB-IoT指定了14个频段。 2 物理层 2.1 工作模式部署方式(Operation Modes)NB-IoT占用180KHz带宽,这与在LTE帧结构中一个资源块的带宽是一样的。所以,以下三种部署方式成为可能: 1)独立部署(Stand alone operation)适合用于重耕GSM频段,GSM的信道带宽为200KHz,这刚好为NB-IoT 180KHz带宽辟出空间,且两边还有10KHz的保护间隔。 2)保护带部署(Guard band operation)利用LTE边缘保护频带中未使用的180KHz带宽的资源块。3)带内部署(In-band operation)利用LTE载波中间的任何资源块。CE LevelCE Level,即覆盖增强等级(Coverage Enhancement Level)。从0到2,CE Level共三个等级,分别对应可对抗144dB、154dB、164dB的信号衰减。基站与NB-IoT终端之间会根据其所在的CE Level来选择相对应的信息重发次数。双工模式Release 13 NB-IoT仅支持FDD 半双工type-B模式。FDD意味着上行和下行在频率上分开,UE不会同时处理接收和发送。半双工设计意味着只需多一个切换器去改变发送和接收模式,比起全双工所需的元件,成本更低廉,且可降低电池能耗。 在Release 12中,定义了半双工分为type A和type B两种类型,其中type B为Cat.0所用。在type A下,UE在发送上行信号时,其前面一个子帧的下行信号中最后一个Symbol不接收,用来作为保护时隙(Guard Period, GP),而在type B下,UE在发送上行信号时,其前面的子帧和后面的子帧都不接收下行信号,使得保护时隙加长,这对于设备的要求降低,且提高了信号的可靠性。 2.2 下行链路对于下行链路,NB-IoT定义了三种物理信道:1)NPBCH,窄带物理广播信道。2)NPDCCH,窄带物理下行控制信道。3)NPDSCH,窄带物理下行共享信道。还定义了两种物理信号:1)NRS,窄带参考信号。2)NPSS和NSSS,主同步信号和辅同步信号。相比LTE,NB-IoT的下行物理信道较少,且去掉了PMCH(Physical Multicast channel,物理多播信道),原因是NB-IoT不提供多媒体广播/组播服务。下图是NB-IoT传输信道和物理信道之间的映射关系。 MIB消息在NPBCH中传输,其余信令消息和数据在NPDSCH上传输,NPDCCH负责控制UE和eNB间的数据传输。NB-IoT下行调制方式为QPSK。NB-IoT下行最多支持两个天线端口(Antenna Port),AP0和AP1。和LTE一样,NB-IoT也有PCI(Physical Cell ID,物理小区标识),称为NCellID(Narrowband physical cell ID),一共定义了504个NCellID。帧和时隙结构和LTE循环前缀(Normal CP)物理资源块一样,在频域上由12个子载波(每个子载波宽度为15KHz)组成,在时域上由7个OFDM符号组成0.5ms的时隙,这样保证了和LTE的相容性,对于带内部署方式至关重要。 每个时隙0.5ms,2个时隙就组成了一个子帧(SF),10个子帧组成一个无线帧(RF)。 这就是NB-IoT的帧结构,依然和LTE一样。NRS(窄带参考信号)NRS(窄带参考信号),也称为导频信号,主要作用是下行信道质量测量估计,用于UE端的相干检测和解调。在用于广播和下行专用信道时,所有下行子帧都要传输NRS,无论有无数据传送。NB-IoT下行最多支持两个天线端口,NRS只能在一个天线端口或两个天线端口上传输,资源的位置在时间上与LTE的CRS(Cell-Specific Reference Signal,小区特定参考信号)错开,在频率上则与之相同,这样在带内部署(In-Band Operation)时,若检测到CRS,可与NRS共同使用来做信道估测。 ▲NRS资源位置同步信号NPSS为NB-IoT UE时间和频率同步提供参考信号,与LTE不同的是,NPSS中不携带任何小区信息,NSSS带有PCI。NPSS与NSSS在资源位置上避开了LTE的控制区域,其位置图如下: ▲NPSS和NSSS资源位置NPSS的周期是10ms,NSSS的周期是20ms。NB-IoT UE在小区搜索时,会先检测NPSS,因此NPSS的设计为短的ZC(Zadoff-Chu)序列,这降低了初步信号检测和同步的复杂性。NBPBCHNBPBCH的TTI为640ms,承载MIB-NB(Narrowband Master Information Block),其余系统信息如SIB1-NB等承载于NPDSCH中。SIB1-NB为周期性出现,其余系统信息则由SIB1-NB中所带的排程信息做排程。和LTE一样,NB-PBCH端口数通过CRC mask识别,区别是NB-IOT最多只支持2端口。NB-IOT在解调MIB信息过程中确定小区天线端口数。在三种operation mode下,NB-PBCH均不使用前3个OFDM符号。In-band模式下NBPBCH假定存在4个LTE CRS端口,2个NRS端口进行速率匹配。 ▲NPBCH映射到子帧 ▲黄色小格表明NPBCH资源占用位置,洋红色表示NRS,紫色代表CRSNPDCCHNPDCCH中承载的是DCI(Downlink Control Information),包含一个或多个UE上的资源分配和其他的控制信息。UE需要首先解调NPDCCH中的DCI,然后才能够在相应的资源位置上解调属于UE自己的NPDSCH(包括广播消息,寻呼,UE的数据等)。NPDCCH包含了UL grant,以指示UE上行数据传输时所使用的资源。NPDCCH子帧设计如下图所示: ▲浅绿色和深绿色代表NPDCCH使用的RE,紫色代表LTE CRS,蓝色代表NRS。上图表示在LTE单天线端口和NB-IoT2天线端口下in-band模式的映射NPDCCH的符号起始位置:对于in-band,如果是SIB子帧,起始位置为3,非SIB子帧,起始位置包含在SIB2-NB中;对于stand-alone和Guard band,起始位置统一为0。NPDCCH有别于LTE系统中的PDCCH的是,并非每个Subframe都有NPDCCH,而是周期性出现。NPDCCH有三种搜索空间(Search Space),分别用于排程一般数据传输、Random Access相关信息传输,以及寻呼(Paging)信息传输。各个Search Space有无线资源控制(RRC)配置相对应的最大重复次数Rmax,其Search Space的出现周期大小即为相应的Rmax与RRC层配置的一参数的乘积。RRC层也可配置一偏移(Offset)以调整Search Space的开始时间。在大部分的搜索空间配置中,所占用的资源大小为一PRB,仅有少数配置为占用6个Subcarrier。一个DCI中会带有该DCI的重传次数,以及DCI传送结束后至其所排程的NPDSCH或NPUSCH所需的延迟时间,NB-IoT UE即可使用此DCI所在的Search Space的开始时间,来推算DCI的结束时间以及排程的数据的开始时间,以进行数据的传送或接收。NPDSCHNPDSCH的子帧结构和NPDCCH一样。NPDSCH是用来传送下行数据以及系统信息,NPDSCH所占用的带宽是一整个PRB大小。一个传输块(Transport Block, TB)依据所使用的调制与编码策略(MCS),可能需要使用多于一个子帧来传输,因此在NPDCCH中接收到的Downlink Assignment中会包含一个TB对应的子帧数目以及重传次数指示。2.3 上行链路对于上行链路,NB-IoT定义了两种物理信道:1)NPUSCH,窄带物理上行共享信道。2)NPRACH,窄带物理随机接入信道。还有:1)DMRS,上行解调参考信号。NB-IoT上行传输信道和物理信道之间的映射关系如下图: 除了NPRACH,所有数据都通过NPUSCH传输。时隙结构NB-IoT上行使用SC-FDMA,考虑到NB-IoT终端的低成本需求,在上行要支持单频(Single Tone)传输,子载波间隔除了原有的15KHz,还新制订了3.75KHz的子载波间隔,共48个子载波。当采用15KHz子载波间隔时,资源分配和LTE一样。当采用3.75KHz的子载波间隔时,如下图所示: 15KHz为3.75KHz的整数倍,所以对LTE系统干扰较小。由于下行的帧结构与LTE相同,为了使上行与下行相容,子载波空间为3.75KHz的帧结构中,一个时隙同样包含7个Symbol,共2ms长,刚好是LTE时隙长度的4倍。此外,NB-IoT系统中的采样频率(Sampling Rate)为1.92MHz,子载波间隔为3.75KHz的帧结构中,一个Symbol的时间长度为512Ts(Sampling Duration),加上循环前缀(Cyclic Prefix, CP)长16Ts,共528Ts。因此,一个时隙包含7个Symbol再加上保护区间(Guard Period)共3840Ts,即2ms长。NPUSCHNPUSCH用来传送上行数据以及上行控制信息。NPUSCH传输可使用单频或多频传输。 ▲单频与多频传输在NPUSCH上,定义了两种格式:format 1和format 2。NPUSCH format 1 为UL-SCH上的上行信道数据而设计,其资源块不大于1000 bits;NPUSCH format 2传送上行控制信息(UCI)。映射到传输快的最小单元叫资源单元(RU,resource unit),它由NPUSCH格式和子载波空间决定。有别于LTE系统中的资源分配的基本单位为子帧,NB-IoT根据子载波和时隙数目来作为资源分配的基本单位,如下表所示: 对于NPUSCH format 1,当子载波空间为3.75 kHz时,只支持单频传输,一个RU在频域上包含1个子载波,在时域上包含16个时隙,所以,一个RU的长度为32ms。当子载波空间为15kHz时,支持单频传输和多频传输,一个RU包含1个子载波和16个时隙,长度为8ms;当一个RU包含12个子载波时,则有2个时隙的时间长度,即1ms,此资源单位刚好是LTE系统中的一个子帧。资源单位的时间长度设计为2的幂次方,是为了更有效的运用资源,避免产生资源空隙而造成资源浪费。对于NPUSCH format 2,RU总是由1个子载波和4个时隙组成,所以,当子载波空间为3.75 kHz时,一个RU时长为8ms;当子载波空间为15kHz时,一个RU时长为2ms。对于NPUSCH format 2,调制方式为BPSK。对于NPUSCH format 1,调制方式分为以下两种情况:●包含一个子载波的RU,采用BPSK和QPSK。●其它情况下,采用QPSK。由于一个TB可能需要使用多个资源单位来传输,因此在NPDCCH中接收到的Uplink Grant中除了指示上行数据传输所使用的资源单位的子载波的索引(Index),也会包含一个TB对应的资源单位数目以及重传次数指示。NPUSCH Format 2是NB-IoT终端用来传送指示NPDSCH有无成功接收的HARQ-ACK/NACK,所使用的子载波的索引(Index)是在由对应的NPDSCH的下行分配(Downlink Assignment)中指示,重传次数则由RRC参数配置。DMRS根据NPUSCH格式,DMRS每时隙传输1个或者3个SC-FDMA符号。 ▲NPUSCH format 1。上图中,对于子载波空间为15 kHz ,一个RU占用了6个子载波。 ▲NPUSCH format 2,此格式下,RU通常只占一个子载波。NPRACH和LTE的Random Access Preamble使用ZC序列不同,NB-IoT的Random Access Preamble是单频传输(3.75KHz子载波),且使用的Symbol为一定值。一次的Random Access Preamble传送包含四个Symbol Group,一个Symbol Group是5个Symbol加上一CP,如下图: ▲Radom Access Preamble Symbol Group每个Symbol Group之间会有跳频。选择传送的Random Access Preamble即是选择起始的子载波。基站会根据各个CE Level去配置相应的NPRACH资源,其流程如下图: ▲NB-IoT Random Acces流程Random Access开始之前,NB-IoT终端会通过DL measurement(比如RSRP)来决定CE Level,并使用该CE Level指定的NPRACH资源。一旦Random Access Preamble传送失败,NB-IoT终端会在升级CE Level重新尝试,直到尝试完所有CE Level的NPRACH资源为止。3 小区接入NB-IoT的小区接入流程和LTE差不多:小区搜索取得频率和符号同步、获取SIB信息、启动随机接入流程建立RRC连接。当终端返回RRC_IDLE状态,当需要进行数据发送或收到寻呼时,也会再次启动随机接入流程。3.1 协议栈和信令承载总的来说,NB-IoT协议栈基于LTE设计,但是根据物联网的需求,去掉了一些不必要的功能,减少了协议栈处理流程的开销。因此,从协议栈的角度看,NB-IoT是新的空口协议。以无线承载(RB)为例,在LTE系统中,SRB(signalling radio bearers,信令无线承载)会部分复用,SRB0用来传输RRC消息,在逻辑信道CCCH上传输;而SRB1既用来传输RRC消息,也会包含NAS消息,其在逻辑信道DCCH上传输。LTE中还定义了SRB2,但NB-IoT没有。此外,NB-IoT还定义一种新的信令无线承载SRB1bis,SRB1bis和SRB1的配置基本一致,除了没有 PDCP,这也意味着在Control Plane CIoT EPS optimisation下只有SRB1bis,因为只有在这种模式才不需要。 ▲NB-IoT协议栈3.2 系统信息NB-IoT经过简化,去掉了一些对物联网不必要的SIB,只保留了8个: •SIBType1-NB:小区接入和选择,其它SIB调度•SIBType2-NB:无线资源分配信息•SIBType3-NB:小区重选信息•SIBType4-NB:Intra-frequency的邻近Cell相关信息•SIBType5-NB:Inter-frequency的邻近Cell相关信息•SIBType14-NB:接入禁止(Access Barring)•SIBType16-NB:GPS时间/世界标准时间信息需特别说明的是,SIB-NB是独立于LTE系统传送的,并非夹带在原LTE的SIB之中。3.3 小区重选和移动性由于NB-IoT主要为非频发小数据包流量而设计,所以RRC_CONNECTED中的切换过程并不需要,被移除了。如果需要改变服务小区,NB-IoT终端会进行RRC释放,进入RRC_IDLE状态,再重选至其他小区。在RRC_IDLE状态,小区重选定义了intra frequency和inter frequency两类小区,inter frequency指的是in-band operation下两个180 kHz载波之间的重选。NB-IoT的小区重选机制也做了适度的简化,由于NB-IoT 终端不支持紧急拨号功能,所以,当终端重选时无法找到Suitable Cell的情况下,终端不会暂时驻扎(Camp)在Acceptable Cell,而是持续搜寻直到找到Suitable Cell为止。根据3GPP TS 36.304定义,所谓Suitable Cell为可以提供正常服务的小区,而Acceptable Cell为仅能提供紧急服务的小区。3.4 随机接入过程NB-IoT的RACH过程和LTE一样,只是参数不同。基于竞争的NB-IOT随机接入过程 基于非竞争的NB-IOT随机接入过程 3.5 连接管理由于NB-IoT并不支持不同技术间的切换,所以RRC状态模式也非常简单。 RRC Connection EstablishmentRRC Connection Establishment流程和LTE一样,但内容却不相同。 很多原因都会引起RRC建立,但是,在NB-IoT中,RRCConnectionRequest中的Establishment Cause里没有delayTolerantAccess,因为NB-IOT被预先假设为容忍延迟的。另外,在Establishment Cause里,UE将说明支持单频或多频的能力。与LTE不同的是,NB-IoT新增了Suspend-Resume流程。当基站释放连接时,基站会下达指令让NB-IoT终端进入Suspend模式,该Suspend指令带有一组Resume ID,此时,终端进入Suspend模式并存储当前的AS context。 当终端需要再次进行数据传输时,只需要在RRC Connection Resume Request中携带Resume ID(如上图第四步),基站即可通过此Resume ID来识别终端,并跳过相关配置信息交换,直接进入数据传输。简而言之,在RRC_Connected至RRC_IDLE状态时,NB-IoT终端会尽可能的保留RRC_Connected下所使用的无线资源分配和相关安全性配置,减少两种状态之间切换时所需的信息交换数量,以达到省电的目的。4 Data Transfer如前文所述,NB-IoT定义了两种数据传输模式:Control Plane CIoT EPS optimisation方案和User Plane CIoT EPS optimisation方案。对于数据发起方,由终端选择决定哪一种方案。对于数据接收方,由MME参考终端习惯,选择决定哪一种方案。4.1 Control Plane CIoT EPS Optimisation对于Control Plane CIoT EPS Optimisation,终端和基站间的数据交换在RRC级上完成。对于下行,数据包附带在RRCConnectionSetup消息里;对于上行,数据包附带在RRCConnectionSetupComplete消息里。如果数据量过大,RRC不能完成全部传输,将使用DLInformationTransfer和ULInformationTransfer消息继续传送。 这两类消息中包含的是带有NAS消息的byte数组,其对应NB-IoT数据包,因此,对于基站是透明的,UE的RRC也会将它直接转发给上一层。在这种传输模式下,没有RRC connection reconfiguration流程,数据在RRC connection setup消息里传送,或者在RRC connection setup之后立即RRC connection release并启动resume流程。4.2 User Plane CIoT EPS optimisation在User Plane CIoT EPS optimisation模式下,数据通过传统的用户面传送,为了降低物联网终端的复杂性,只可以同时配置一个或两个DRB。此时,有两种情况:•当RRC连接释放时,RRC连接释放会携带携带Resume ID,并启动resume流程,如果resume成功,更新密匙安全建立后,保留了先前RRC_Connected的无线承载也随之建立。 •当RRC连接释放时,如果RRC连接释放没有携带携带Resume ID,或者resume请求失败,安全和无线承载建立过程如下图所示: 首先,通过SecurityModeCommand和SecurityModeComplete建立AS级安全。在SecurityModeCommand消息中,基站使用SRB1和DRB提供加密算法和对SRB1完整性保护。LTE中定义的所有算法都包含在NB-IoT里。当安全激活后,进入RRC connection reconfiguration流程建立DRBs。 在重配置消息中,基站为UE提供无线承载,包括RLC和逻辑信道配置。PDCP仅配置于DRBs,因为SRB采用默认值。在MAC配置中,将提供BSR、SR、DRX等配置。最后,物理配置提供将数据映射到时隙和频率的参数。 4.3 多载波配置在RRCConnectionReconfiguration消息中,可在上下行设置一个额外的载波,称为非锚定载波(non-anchor carrier)。基于多载波配置,系统可以在一个小区里同时提供多个载波服务,因此,NB-IoT的载波可以分为两类:提供NPSS、NSSS与承载NPBCH和系统信息的载波称为Anchor Carrier,其余的载波则称为Non-Anchor Carrier。当提供non-anchor载波时,UE在此载波上接收所有数据,但同步、广播和寻呼等消息只能在Anchor Carrier上接收。NB-IoT终端一律需要在Anchor Carrier上面Random Access,基站会在Random Access过程中传送Non-Anchor Carrier调度信息,以将终端卸载至Non-Anchor Carrier上进行后续数据传输,避免Anchor Carrier的无线资源吃紧。另外,单个NB-IoT终端同一时间只能在一个载波上传送数据,不允许同时在Anchor Carrier和Non-Anchor Carrier上传送数据。
NB-IoT 编辑 基于蜂窝的窄带物联网(Narrow Band Internet of Things, NB-IoT)成为万物互联网络的一个重要分支。NB-IoT构建于蜂窝网络,只消耗大约180KHz的带宽,可直接部署于GSM网络、UMTS网络或LTE网络,以降低部署成本、实现平滑升级。[1] NB-IoT是IoT领域一个新兴的技术,支持低功耗设备在广域网的蜂窝数据连接,也被叫作低功耗广域网(LPWAN)。NB-IoT支持待机时间长、对网络连接要求较高设备的高效连接。据说NB-IoT设备电池寿命可以提高至至少10年,同时还能提供非常全面的室内蜂窝数据连接覆盖。[2] 中文名 基于蜂窝网络的窄带物联网 外文名 Narrow Band Internet of Things, NB-IoT 目录 1 NB-IOT的概述 2 NB-IOT的前景与优势 3 NB-IOT的需求与发展 ▪ 转向窄带物联网 ▪ 即将步入爆发期 ▪ NB-IoT亟需开放的平台 ▪ 2016年是NB-IoT产业关键年 ▪ 助运营商开启百亿联接市场 ▪ NB-IoT规模化商用在即 4 ofo小黄车推动NB-IoT首次大规模商用 5 上海联通打造全球首个NB-IOT样板 6 华为联手沃达丰建立NB-IoT开放实验室 7 华为携手沃达丰完成首个NB-IoT商用测试 8 为物联网而生:NB-IOT开启广袤市场空间 9 电信业谋利物联网:NB-IoT终结“碎片化” ▪ 电信商押宝物联网 ▪ 巨头结盟或终结“碎片化” 10 全球NB-IOT论坛筹备会议召开 11 NB-IoT Forum成立,产业步入发展快车道 ▪ 中兴通讯成为NB-IoT Forum主要成员 12 中国电信建成下一代物联网 13 中国联通NB-IoT网络即将“试商用” NB-IOT的概述 编辑 对于物联网标准的发展,华为的推进最早。2014年5月,华为提出了窄带技术NB M2M;2015年5月融合NB OFDMA形成了NB-CIOT;7月份,NB-LTE跟NB-CIOT进一步融合形成NB-IOT;预计NB-IOT标准会在3GPP R13出现,并于2016年6月份冻结。[3] 此前,相对于爱立信、诺基亚和英特尔推动的NB-LTE,华为更注重构建NB-CIOT的生态系统,包括高通、沃达丰、德国电信、中国移动、中国联通、Bell等主流运营商、芯片商及设备系统产业链上下游均加入了该阵营。 基于蜂窝的窄带物联网(Narrow Band Internet of Things, NB-IoT)成为万物互联网络的一个重要分支。NB-IoT构建于蜂窝网络,只消耗大约180KHz的带宽,可直接部署于GSM网络、UMTS网络或LTE网络,以降低部署成本、实现平滑升级。 NB-IOT聚焦于低功耗广覆盖(LPWA)物联网(IoT)市场,是一种可在全球范围内广泛应用的新兴技术。具有覆盖广、连接多、速率低、成本低、功耗低、架构优等特点。 NB-IOT使用License频段,可采取带内、保护带或独立载波等三种部署方式,与现有网络共存。 NB-IoT应用场景(4张) NB-IOT的前景与优势 编辑 移动通信正在从人和人的连接,向人与物以及物与物的连接迈进,万物互联是必然趋势。然而当前的4G网络在物与物连接上能力不足。事实上,相比蓝牙、ZigBee等短距离通信技术,移动蜂窝网络具备广覆盖、可移动以及大连接数等特性,能够带来更加丰富的应用场景,理应成为物联网的主要连接技术。作为LTE的演进型技术,4.5G除了具有高达1Gbps的峰值速率,还意味着基于蜂窝物联网的更多连接数,支持海量M2M连接以及更低时延,将助推高清视频、VoLTE以及物联网等应用快速普及。蜂窝物联网正在开启一个前所未有的广阔市场。 对于电信运营商而言,车联网、智慧医疗、智能家居等物联网应用将产生海量连接,远远超过人与人之间的通信需求。 NB-IoT具备四大特点:一是广覆盖,将提供改进的室内覆盖,在同样的频段下,NB-IoT比现有的网络增益20dB,相当于提升了100倍覆盖区域的能力;二是具备支撑海量连接的能力,NB-IoT一个扇区能够支持10万个连接,支持低延时敏感度、超低的设备成本、低设备功耗和优化的网络架构;三是更低功耗,NB-IoT终端模块的待机时间可长达10年;四是更低的模块成本,企业预期的单个接连模块不超过5美元。[1] NB-IOT聚焦于低功耗广覆盖(LPWA)物联网(IOT)市场,是一种可在全球范围内广泛应用的新兴技术。其具有覆盖广、连接多、速率低、成本低、功耗低、架构优等特点。NB-IOT使用License频段,可采取带内、保护带或独立载波三种部署方式,与现有网络共存。[4] 因为NB-IoT自身具备的低功耗、广覆盖、低成本、大容量等优势,使其可以广泛应用于多种垂直行业,如远程抄表、资产跟踪、智能停车、智慧农业等。3GPP标准的首个版本预计在今年6月发布,到时候将有一批测试网络和小规模商用网络出现。 目前包括我国运营商在内诸多运营商在开展NB-IoT和研究。就NB-IoT的发展现状,余泉详细阐述了三个精彩观点:一是NB-IoT是蜂窝产业应对万物互联的一个重要机会。二是NB-IoT要成功必须要建立开放产业平台。三是2016年是NB-IoT产业非常关键的一年,标准、芯片、网络以及商用应用场景都会走向成熟。 NB-IoT让世界万联(4张) NB-IOT的需求与发展 编辑 随着智能城市、大数据时代的来临,无线通信将实现万物连接。很多企业预计未来全球物联网连接数将是千亿级的时代。目前已经出现了大量物与物的联接, 然而这些联接大多通过蓝牙、Wi-Fi等短距通信技术承载,但非运营商移动网络。为了满足不同物联网业务需求,根据物联网业务特征和移动通信网络特点,3GPP根据窄带业务应用场景开展了增强移动通信网络功能的技术研究以适应蓬勃发展的物联网业务需求。 我们正进入万物互联(IoT)的时代,这对于整个移动通信产业来说是一个巨大的机会。这一点在MWC2016上展露无疑。无论是运营商大咖,还是设备商巨头,纷纷展示了完整的物联网解决方案和在不同垂直行业的应用。[5] 当然,实现这一切的基础,是要有无处不在的网络联接。运营商的网络是全球覆盖最为广泛的网络,因此在接入能力上有独特的优势。然而,一个不容忽视的现实情况是,真正承载到移动网络上的物与物联接只占到联接总数的10%,大部分的物与物联接通过蓝牙、WiFi等技术来承载。 为此,产业链从几年前就开始研究利用窄带LTE技术来承载IoT联接。历经几次更名和技术演进,2015年9月,3GPP正式将这一技术命名为NB-IoT。MWC2016上,NB-IoT首次亮相,受到瞩目,运营商和设备商纷纷为其站台和背书。 华为无线网络产品线首席战略官余泉在接受《通信产业报》(网)采访时表示:“NB-IoT是蜂窝网络产业应对万物互联的一个重要机会。我们非常看好NB-IoT的商用前景,推荐将其作为物联网联接技术的首要选择。” 他向记者阐释了NB-IoT的商业和技术优势。从商业层面上来讲,截至目前,蜂窝网络覆盖了全球超过50%的地理面积,90%的人口,是一张覆盖最为完整的网络。 从技术层面上来讲,NB-IoT有4大技术优势。首先是覆盖广,相比传统GSM,一个基站可以提供10倍的面积覆盖;其次是海量连接,200KHz的带宽可以提供10万个联接;第三是低功耗,使用AA电池便可以工作十年,无需充电;第四是低成本,模组成本小于5美金。 此前,华为曾向记者算了一笔账,假设全球有500万左右物理站点,全部部署NB-IoT,每个站3个扇区、每个扇区部署200kHz、每小时每个传感器发送100个字节,那么全球站点能够联接的传感器数量高达4500亿。 据了解,NB-IoT可以广泛应用于多种垂直行业,如远程抄表、资产跟踪、智能停车、智慧农业等。随着3GPP标准的首个版本在6月份发布,将有一批测试网络和小规模商用网络出现。NB-IoT将在多个低功耗广域网技术中脱颖而出。 “NB-IoT在欧洲和乃至全球都呈现出巨大的发展机遇。到2020年IoT全部产业链价值有望达到3万亿欧元,包括全产业链上下游,如网络连接、数据处理、平台应用、商业合作等。”余泉表示,华为已经做好在2016年完成NB-IoT商用的准备。[6] 华为轮值CEO胡厚昆在2015全球移动宽带论坛上发表主题演讲时指出,移动运营商需要立即采取行动,抢占快速增长的物联网市场份额。目前,运营商虽然在可接入性方面拥有独特优势,但是许多其它技术,如ZigBee、蓝牙和内置WiFi也在迅速发展,运营商必须与垂直行业展开激烈竞争。胡厚昆强调,必须迅速制定统一的物联网标准,以推动跨行业发展。 沃达丰集团研发主管Luke Ibbetson对此表示赞同。他指出,目前80%-90%的物联网设备由低功耗的室内系统连接,余下的设备则由蜂窝网络连接。很多人认为,新出现的LPWA(低功耗广域)技术成本低,覆盖又广,将为移动运营商发展物联网带来良机。不过,Ibbetson指出:“我们目前还没有为客户开发出合适的解决方案,因此仍然面临巨大压力。” 转向窄带物联网 对于LPWA网络所用到的窄带物联网(NB-IoT),运营商业已达成共识,应使用授权频谱,采用带内、防护频带独立部署。这一新兴技术可以提供广域网络覆盖,旨在为吞吐量、成本、能耗都很低的海量物联网设备提供支撑。 2015年11月,数家全球主流运营商联合设备商、芯片厂商和相关国际组织,在香港举办NB-IoT论坛筹备会,旨在加速窄带物联网生态系统的发展,成员包括中国移动、中国联通、Etisalat、LG Uplus、意大利电信、Telefonica、沃达丰、GSMA、GTI、华为、爱立信、诺基亚、高通和英特尔。六家运营商成员还宣布,将在全球成立六个窄带物联网开放实验室,聚焦窄带物联网业务创新、行业发展、互操作性测试和产品兼容验证。 目前,运营商已经在客户中展开预标准NB-IoT技术试点工作。例如,德国电信和沃达丰已经采取行动,利用现有基站进行预部署试点,预计试商用部署在2016年下半年进行,正式商用将从2017年初开始。 沃达丰的Ibbetson表示,对3GPP标准的整合充满信心,但他也指出这一过程缓慢而艰难。“希望窄带物联网能在2016年3月份前成为独立标准,同时我们需要尽快决定使用哪个频段。” 华为也希望相关标准能尽快得到确认,这样行业才能启动大规模的物联网部署。胡厚昆指出:“华为在技术方面已经准备就绪,希望能尽快抓住窄带物联网的机遇。” 窄带物联网具有四大优势:电池寿命长(超过十年)、成本低(每个模块不足5美元)、容量大(单个小区能支持10万连接)、覆盖广(能覆盖到地下)。 Ibbetson认为:“如果产业链不能将单模块成本降到两三美元以下,实现大规模应用,NB-IoT市场就做不起来。我们需要从全局角度出发,以极低的成本将物联网模块嵌入设备中。” 胡厚昆也认为,要想刺激NB-IoT大规模发展,通信模块成本必须低于5美元。如果成本降到1美元以内,则会带来爆发式增长。 即将步入爆发期 随着网络连接、云服务、大数据分析和低成本传感器等所有核心技术的就绪,物联网已经从萌芽期步入迅速发展的阶段,大多数分析师对此都表示认可。 埃森哲亚太区高科技和电子产业主管David Sovie指出,每个CIO都应尽快制定物联网发展策略,否则将会在竞争中落败。IBM研究院物联网全球战略计划主管Wei Sun表示,IBM各行各业的大客户都在探索物联网产品和服务。 越来越多的行业已经在使用物联网技术提高效率,提升客户满意度并降低运营成本。例如,汽车零部件、家用电器及安全系统制造商博世已经将很多产品线连接起来,并从移动互联技术,尤其是车联网领域的崛起中直接获益。 在医疗领域,飞利浦已经开发了多款电子医疗应用,包括一款供慢性病患者使用的贴片。该贴片使用传感器实时收集患者健康数据,并传输到云平台,医护人员可以对数据进行监控,并适时采取医疗干预措施。 飞利浦数字加速器项目主管Alberto Prado指出,设备和系统的互操作性是数字医疗行业崛起的关键。随着协作护理模式日益盛行,未来的医疗必然将整合所有资源,并以主动预防为主。 为了迎接物联网领域的巨大机遇,整个产业不仅需要推动技术创新,还需要推动商业模式创新和跨行业协作。由于用例、应用和商业模式纷繁多样,物联网市场将比移动市场更加碎片化。 胡厚昆表示:“这将有赖于产业链上不同的利益相关者精诚合作。在物联网时代,运营商需要将关注的重点由管理技术扩展至管理整个生态系统。整个行业正处在紧要关头,运营商需要立即行动起来,抓住这一新的蓝海机遇。”[4] NB-IoT亟需开放的平台 “NB-IoT产业生态系统正在快速成长,它更需要运营商与IoT相关产业参与者精诚合作,携手共进。”谈及NB-IoT落地的挑战,余泉介绍。 就在MWC2016举办前一天,GSMA联合企业各方举办全球首届NB-IoT峰会,并在会上成立NB-IoT forum。该联盟成员包括全球主流运营商、网络设备厂家以及主要芯片模组厂家等诸多产业链企业。 余泉强调,有超过20家垂直行业企业参加了此次峰会,这是非常可喜的开端。“当然垂直行业供应商可能不是几十家,而是几千家,业界还有很多的工作要做。”余泉以智能抄表行业为例表示,目前家庭拥有水表、电表、煤气表以及暖气表等很多表,这些背后的企业很多。 如此多的参与方,会出现大量协同方面的问题,业界需要一个开放的平台加速产业的前进步伐。而且,新标准制定需要开放平台去推动。 对此,诸多运营商联合包括华为在内的电信设备商一起搭建了Open Lab。据悉,借助Open Lab,垂直行业厂家就能很轻松地在实际现网上验证自身的物联网应用、网络以及商业模式。 2016年是NB-IoT产业关键年 “NB-IoT标准预计在今年6月完成。”余泉表示,这体现NB-IoT进入了发展的关键一年。 据悉,随着3GPP标准在6月份冻结,经过市场的洗礼后,NB-IoT会在LPWA市场的多个技术竞争中脱颖而出,成为领先运营商的最佳选择。同时2016年也将成为NB-IoT的商用元年。 今年将有很多芯片厂家和模组厂家支持NB-IoT发展。在网络方面,华为计划在今年下半年推出支持NB-IoT的系统。而许多其他网络设备供应商也计划在今年实现对NB-IoT的支持。 运营商在发展NB-IoT方面表现的十分迫切。“即使产业已经尽力最大努力,促进NB-IoT快速发展,但运营商还是认为发展进度不够快,给了供应商很大压力。”余泉透露。 垂直行业也提出了他们对技术的要求:终端电池寿命要达到10年以上,安全性必须完全满足,且今年要能够商用。 用户案例是NB-IoT或者说蜂窝物联网要成功非常关键的一点。现在借助Open Lab,业界已经讨论如何去使能更多的用户案例。目前智能停车、智能水表、智能追踪等用户案例已经完成实验室验证。“预计今年下半年就会有NB-IoT商用的网络,明年将会规模部署,这是我们整个产业的大概期望。”余泉表示。 对于NB-IoT发展的挑战,余泉表示万事开头难,但蜂窝产业发展几十年,拥有开放合作的传统,才能达到今天的成就。“我相信NB-IoT产业也会重复这样的开放合作,为运营商、垂直客户带来新的商业成功,同时对整个社会,对整个的经济起到非常好的促进作用。”余泉介绍。 助运营商开启百亿联接市场 据悉,当前只有10%的IoT应用是基于蜂窝网络的,蜂窝网络具备覆盖优势和成本优势,华为已经做好了在2016年内完成商用的准备。华为方面表示,华为在IoT市场最关键的一步就是“帮助运营商开启一个百亿联接市场”。 同时,在GSMA NB-IoT Forum的倡导之下,华为与运营商共同建立开发实验室,加强企业间合作。目前,华为已与中国移动、阿联酋电信、LG Uplus、上海联通、意大利电信和沃达丰在全球成立六个NB-IoT开放实验室,专注于NB-IoT的联合创新、产业发展、集成验证,探索全新的商用案例与商业模式,并将成果整个行业。 据了解,华为与移动运营商沃达丰将联手建立NB-IoT开放实验室,以推动NB-IoT技术的发展和推广。使用预标准NB-IoT技术的NB-IoT开放实验室将研究网络解决方案验证、新应用创新、设备集成、业务模式研究以及产品合格验证等。[5] NB-IoT规模化商用在即 2016年6月16日,在韩国釜山召开的3GPPRAN全会第七十二次会议上,NB-IoT作为大会的一项重要议题,其对应的3GPP协议相关内容获得了RAN全会批准,标志着受无线产业广泛支持的NB-IoT标准核心协议的相关研究全部完成。标准化工作的成功完成也标志着NB-IoT即将进入规模商用阶段,物联网产业发展蓄势待发。 随着标准的冻结,将有更多的产业链企业加入NB-IoT阵营,这将促使NB-IoT迅速规模化商用。NB-IoT的商用也将构建全球最大的蜂窝物联网生态系统。如此一来,2016年下半年将涌现出更多的商业应用已是铁板钉钉。窄带物联网巨大的“蓝海”市场已经开启,并将在未来出现爆炸式增长。据GSMA预测,到2020年全球互联设备将会到达270亿,其中100亿为移动连接设备。[7] ofo小黄车推动NB-IoT首次大规模商用 编辑 2017年7月13日,ofo小黄车与中国电信、华为共同宣布,三家联合研发的NB-IoT(Narrow Band Internet of Things,窄带物联网)“物联网智能锁”全面启动商用。[8] 据了解,在此次三方合作中,ofo负责智能锁设备开发,中国电信负责提供NB-IoT物联网的商用网络、华为负责芯片方面的服务。此前ofo已经开始使用这款物联网智能锁,而此次将启动全面的商用。[9] 三家联手打造的支持NB-IoT技术的智能锁系统具备三大特点:首先是覆盖更广,NB-IoT信号穿墙性远远超过现有的网络,即使用户深处地下停车场,也能利用NB-IoT技术顺利开关锁,同时可通过数据传输实现“随机密码”;其次是可以连接更多设备,NB-IoT技术比传统移动通信网络连接能力高出100倍以上,也就是说,同一基站可以连接更多的ofo物联网智能锁设备,避免掉线情况;三是更低功耗,NB-IoT设备的待机时间在现有电池无需充电的情况下可使用2-3年,并改变了此前用户边骑车边发电的状况。[10-11] 上海联通打造全球首个NB-IOT样板 编辑 2010年起,上海联通率先开展了以用户感知为导向的“全业务服务体系”建设,实现企业发展的“双轮驱动模式”,以“发展”为前轮,快速扩展市场;以“服务”为后轮,纠偏平衡确保企业发展。五年时间,上海联通取得了收入翻番、利润翻番、用户规模翻番、网络规模翻番、客户满意度逐年提升的优秀成绩。[12] 然而,转型压力仍然巨大。上海市的移动通信市场已经是一个完全饱和的市场,人口红利已近消失。更多是深挖存量市场、维护现有用户,在优质的移动宽带网络下为用户提供更加丰富多彩的优质内容,培养流量使用习惯。 在集团层面,网络创新转型一直以来也是极为重要的课题,曾明确提出“网络创新转型不能再是单独的就网络说网络,必须要能支撑市场业务或者支撑模式创新”。 今年2月,上海联通运维部与集客部在对于网络创新转型进行了一系列的探讨,将方向定在了万物互联上。万物互联大幅增长对网络的压力是什么?上海联通相关负责人告诉C114,当时主要考虑的是连接数。物的连接增长没有历史数据、范围又广,无法预估,如遇突发情况,信令连接会“爆掉”。 出于这一考虑,上海联通与众多合作伙伴进行了深入的交流,当时NB-IOT的前身LTE-M(C114注:今年9月由国际电联正式命名为NB-IOT)进入其视线。LTE-M可以有效解决物联网方面的问题,且后续有着良好发展前景,上海联通最终以此为基础,携手合作伙伴在位于金桥的宁桥路机房进行部署。 然而,4.5G有什么业务可作切入?在对众多行业进行考量后,双方于4月确定先从两个业务入手,分别是智能停车和智能水表。这两个业务从芯片成熟度一直到下游合作厂商整个产业链相对比较成熟,具有良好的持续性。业务确定后就进入了马不停蹄的快速建设中,5月、6月,赶在7月亚洲移动大会·上海站之前正式上线,并在大会上隆重展示。 上海联通在宁桥路的两个停车场,共计20多个停车位,全部安装了带有4.5G NB-IOT芯片和一个地磁感应芯片的监测器,数据先传输到5楼的基站、再传到1楼的创新孵化基地,通过机房的集中管理平台实现更加智能的停车功能。与传统的停车方案相比,智能停车业务改变了需通过中继网关收集信息再反馈给基站所存在的复杂网络部署、多网络组网、高成本、大容量电池等诸多问题,可以实现整个城市一张网,便于维护和管理,与物业分离更易寻址安装等优势。 当前抄表方案存在着深度覆盖差、功耗大、成本高的挑战。而智能水表业务通过在水箱里面集成一块带有特殊芯片的电路板,不但可以实现更为精准的抄表数据传输,更可以智能监测控制水箱开关,凸显了NB-IOT技术在覆盖增强方面的优势。 拥抱万物互联 构建共赢生态 中国联通将智慧城市的试验基地扎根在上海,上海成为其探索智慧交通生活的前沿阵地。而上海联通早在2011年就开始发展物联网业务,其中最为突出的便是车联网应用。 上海联通打造了多项智慧“沃”交通的整体解决方案,从数据通信传输能力的提供者到车联网(Telematics)及相关服务的提供者,从传统业务平台的提供者到资讯平台,乃至商务平台的系统整合者,各个领域均有不少成功案例。无论是宝马的“互联驾驶”、巴士公司的“智能出租”,还是116114的“一键导航”,上海联通皆交出了一份出色答卷。 作为宝马“互联驾驶”的一级供应商,中国联通一方面为宝马公司提供基础的3G移动通信服务(MNO);另一方面,整合自身信息服务能力和宝马的其他供应商的专业能力,共同提供Telematisc服务平台系统集成(TSSP)、呼叫中心(CallCenter)和信息内容服务(Content)等整合的汽车信息化服务。这是中国运营商第一次以整体服务提供商的角色参与车厂前装车载信息服务(Telematics)项目。 据前述负责人介绍,到今年为止,上海联通物联网用户已经突破了100万(卡),其中4成是3G,主要是车载物联网;6成是2G,包括POS机、小区储物柜等。未来3-5年间,其物联网连接数量预计将有百倍规模增长。上海联通希望“网络创新可以更好地适应万物互联时代到来”,前台部门、网络建设维护部门都将参与到这个领域中来。 全球首个NB-IOT样板如何打造(3张) [12] 华为联手沃达丰建立NB-IoT开放实验室 编辑 2016年2月22日,华为与移动运营商沃达丰将联手建立NB-IoT开放实验室,以推动NB-IoT技术的发展和推广。[13] 使用预标准NB-IoT技术的NB-IoT开放实验室将研究网络解决方案验证、新应用创新、设备集成、业务模式研究以及产品合格验证等。 沃达丰集团研发总监兼NB-IoT论坛主席LukeIbbetson表示:“随着该技术在即将到来的2017年初实现商业部署,与开发商和解决方案提供商共同构建一个生态系统将变得至关重要。” NB-IoT技术将通过更加有效地连接需要较长电池寿命的对象从而扩大物联网(IoT)的应用。预计在2016年底或2017年初出现第一批由NB-IoT技术连接的设备。 沃达丰与华为将该技术融入现有位于西班牙的移动网络,然后将首个预标准NB-IoT信息发送至安装在水表中的u-blox模组。 该试验将被并入NB-IoT开放实验室联合会。[13] 华为携手沃达丰完成首个NB-IoT商用测试 编辑 沃达丰和华为都宣称,此次商用测试,将NB-IoT与现有网络基础设施融合,是世界上首个利用此技术的测试。华为还表示这次测试仅仅是NB-IoT设备一系列可能性的开始;在未来还会出现各种企业应用,例如公用事业水表、传感监测以及资产跟踪等。 华为无线网络业务部总裁汪涛(David Wang)表示:“NB-IoT技术已经得到了业界的认可。通过与沃达丰的联合创新,进一步加强了我们为客户提供创新解决方案、帮助客户满足业务需求以及引领技术和产业生态系统发展的愿景。我们将与沃达丰一起,构建一个网络连接更发达的世界。” 沃达丰集团架构和创新事业部总监Matt Beal称,此次演示证明了运营商在IoT领域的作用---M2M已经是一个成功且不断成长的市场。他说道:“沃达丰已经引领了NB-IoT的发展,授权频谱LPWA技术已经得到了业界广泛的支持。此次与华为合作完成的首次商用测试也进一步说明了这一点。一旦正式商用后,NB-IoT将为企业客户带来切实的利益,更多设备将会通过网络连接至IoT。”[2] 华为携手沃达丰完成首个NB-IoT商用测试(3张) 为物联网而生:NB-IOT开启广袤市场空间 编辑 人与人之间的通讯规模已近天花板,物与物的则刚刚进入增长快车道。随着可穿戴、车联网、智能抄表等新兴市场的开启,工业4.0、智慧城市、智慧农业等理念照进现实,万物互联的时代正加速到来。[14] 物联网(IoT)的未来充满想象空间。华为认为,到2025年全球将有1000亿个连接,其中大部分与物联网有关。 物联网对连接的要求与传统蜂窝网络有着很大不同,窄带蜂窝物联网(NB-IOT)由此应运而生。这一由电信行业推动的新兴技术拥有覆盖广、连接多、速率低、成本低、功耗少、架构优等特点,极具商用潜力。 Machina预测,NB-IOT未来将覆盖25%的物联网连接。对面临用户饱和、OTT冲击的运营商来说,NB-IOT将叩开广袤的新市场,带来三倍以上的连接增长;而对正积极转型升级的传统行业从业者而言,它在适应场景、网络性能、可管可控及可靠性等方面亦具备运营商网络的先天优势。 何为NB-IOT? 人口红利消逝和流量营收“剪刀差”下,物联网成为运营商新的收入增长源泉。以沃达丰为例,其2015财年移动用户数增幅仅3%,物联网连接数增幅则达到33.5%,相关业务收入增长亦达24.7%。但传统2G、3G、4G技术并不能充分满足物联网设备低功耗、低成本的连接需求。 NB-IOT的诞生并非偶然,寄托着电信行业对物联网市场的憧憬。其前身可以追溯至华为与沃达丰于2014年5月共同提出的NB-M2M。 由这两家公司首倡的窄带蜂窝物联网概念一经提出即得到了业界的广泛认可,随后高通、爱立信等越来越多的行业巨头加入到这一方向的标准化研究中。为了促进标准的统一有利于产业发展,最终3GPP在2015年9月RAN全会达成一致,确立NB-IoT为窄带蜂窝物联网的唯一标准,并立项为Work Item开始协议撰写,预计将于2016年6月的3GPP R13冻结。 NB-IoT在物联网应用中的优势显著,为传统蜂窝网技术及蓝牙、Wi-Fi等短距离传输技术所无法比拟。首先其覆盖更广,在同样的频段下,NB-IOT比现有网络增益20dB,覆盖面积扩大100倍。 其次是对海量连接的支撑能力,NB-IOT一个扇区能够支持10万个连接。目前全球有约500万个物理站点,假设全部部署NB-IOT、每个站点三个扇区,那么可以接入的物联网终端数将高达4500亿个。 同时NB-IOT的功耗更低,仅为2G的1/10,终端模块的待机时间可长达10年。在成本上也将更低,模块成本有望降至5美元之内。未来随着市场发展带来的规模效应和技术演进,功耗和成本还有望进一步降低。 此外,在支持大数据方面,NB-IoT连接所收集的数据可以直接上传云端,而蓝牙、Wi-Fi等技术则没有这样的便利。 NB-IOT实践与成果 尽管标准制定尚未完成,NB-IOT应用已经逐渐铺开,并在实践中得到了各方肯定。2015年世界移动通信大会(MWC 2015)期间,沃达丰与华为就联合展示了智能抄表业务。 广东联通积极响应国家“互联网+”战略,与华为针对NB-IOT展开合作,成立物联网联合创新项目组。结合联通在平台、网络和运营的强项与华为在标准、芯片和模组成本的优势,共同探索并实施可落地的物联网业务,通过真实的业务需求和场景将NB-IOT技术与之相融合,推进物联网产业发展。双方选择了社会价值较大、产业链相对成熟的智能停车等为切入点。 随着国内生活水平的提高,汽车已经进入寻常百姓家,但也带来“停车难”等问题——据调查,平均每位司机有20%的时间用在寻找停车位上,而广东作为国内发达省份之一,这一现象更为突出。 在深圳,智能停车业务已经开始推行,用户只要安装看APP即可通过收据查找附件停车位,并可支持导航等功能。与传统的停车方案相比,这一基于NB-IOT的试点改变了需要通过中继网关收集信息再反馈给基站所存在的复杂网络部署、多网络组网、高成本等诸多问题,真正实现整个城市一张网,便于维护和管理。 与智能停车类似,NB-IoT在智慧农业、智能制造等低功耗广域网领域也具有着广泛的应用前景。由于应用场景特殊带来的高技术要求,这些应用一直缺乏专有的无线技术,NB-IOT可以很好地填补这一市场空白,从而支撑物联网向更广大的领域发展。 共建生态拥抱万物互联 一项技术由纸面到商用离不开一个强大生态系统的支撑。长期以来,物联网连接技术各自为战,从芯片到系统各方采用的规范不一,造成大规模部署的瓶颈。 如今,围绕NB-IOT的生态已初步成型,并在持续扩大中,拥抱万物互联的条件开始成熟。在网络设备供应商层面,华为、爱立信等领导者均已推出了基于NB-IOT的端到端解决方案。 运营商层面,中国移动、中国联通以及沃达丰、德国电信、阿联酋电信、意大利电信、AT&T等全球顶尖运营商皆就NB-IOT发布了各自的发展计划,并展开试点。 垂直行业中,越来越多厂商开始采用NB-IOT技术来提升竞争力。比如具备智能追踪、超距告警、电子锁控制、电池监控等功能的智能拉杆箱,还有厂商推出了具备位置定位防盗功能及信息上传、跟踪功能的智能自行车。此外,在市政的路灯和垃圾管理、环境监测和畜牧养殖灌溉等领域,NB-IOT的部署亦日益增多。[14] 电信业谋利物联网:NB-IoT终结“碎片化” 编辑 低迷多时的电信运营商似乎找到了新增长点。2015年11月4日,在香港举办的MBB会议(全球移动宽带论坛)上,沃达丰电信集团呼吁全球运营商尽快商用NB-IoT(NarrowBand-IoT)技术。NB-IoT是目前主流电信运营商、设备商针对物联网市场在全球标准组织3GPP提出的最新技术。[15] 电信行业憧憬物联网市场已达10年之久,但由于传统2G、3G、4G网络并不满足物联网设备低功耗、低成本的要求,一直以来,大部分物联网设备在联接时主要使用Wi-Fi、蓝牙等免费技术,运营商很难从中获利。目前,全球联网的物联网终端约40亿个,但接入运营商移动网络的终端只有2.3亿个左右,运营商在物联网市场占比不足6%。 不过,针对物联网提出的NB-IoT有可能给运营商带来3倍增量。据华为轮值CEO胡厚昆介绍,NB-IoT能够让接入运营商网络的物联网终端在2020年达到10亿个。或许正是这个原因,NB-IoT被沃达丰主管LukeIbbeston称之为“拥有巨大潜力的商业蓝海”。 电信商押宝物联网 在全球71亿的移动用户数前,2.3亿个物联网联接确实占不了多少比重,但移动用户已经饱和,而物联网才刚刚起步,所以这是电信行业为数不多的几个值得期待的业务。 当前,多数电信运营商遭遇增长停滞难题。数据显示,2015年的财富500强中,入榜的18家电信运营商,7家出现收入下滑,全球第九大运营商西班牙电信的收入降幅高达11%。 移动通信市场饱和是运营商最主要的困境。在沃达丰的最新财报中,2015财年(2014年3月至2015年3月),沃达丰占比71%的移动业务收入出现下降,且用户数增速只有3%。相比之下,沃达丰来自物联网业务的收入则同比增长了24.7%,物联网联接数也从2014财年的1610万增长至2150万,增幅33.5%。 根据沃达丰财报数据计算,每个移动用户可以给沃达丰贡献105美元的年收入,而每个物联网联接贡献的年收入为27.8美元。但为移动用户提供网络服务,需要巨额的网络建设成本和维护成本,而物联网业务几乎都在现有网络的基础上提供,成本极低,27.8美元绝大多数都是利润。 目前,沃达丰的物联网业务分布于27个国家,但大多仍部署在传统移动网络上,功耗、联接数限制、成本等因素制约了其业务的增长速度。2014年5月,沃达丰与华为提出NBM2M技术,试图通过改进现有网络提供低成本、低功耗的物联网联接,NBM2M也是NB-IoT的前身。今年9月,国际标准组织3GPP在美国凤凰城通过了名为NB-IoT的WorkItem(WI)立项决议,根据计划,NB-IoT标准将于2016年3月的3GPPR13完成标准冻结,届时NB-IoT的规模商用也即将启动。 根据全球物联网研究机构MachinaResearch的统计数据,2015年,全球运营商物联网联接数2.3亿,给运营商贡献市场约70亿美元,这一数字在2020年将达到240亿美元。一位华为工作人员介绍,到2020年,接入运营商网络的物联网设备占比有可能达到20%,而且占比还会继续提升,基于Wi-Fi、蓝牙的联接占比将会降低。 巨头结盟或终结“碎片化” “相比于Wi-Fi、蓝牙等技术,NB-IoT最明显的优势是数据采集和能耗。”MBB的展区里,深圳大行科技产品科长罗艳鸿对21世纪经济报道记者说。 “Wi-Fi、蓝牙等技术收集的数据都是传到用户手机上,难以形成大数据,且数据准确率很低、耗电量极大,两天就得充一次电;NB-IoT联接后数据采集直接上传到云端,很精确,并且可以实现5年不充电。”罗艳鸿称,大行科技计划在50万台自行车中使用NB-IoT技术。 基于此类特性,当前大量的可穿戴设备、智能门、窗、温度计均是NB-IoT的市场。庞大的市场吸引的不只是电信玩家,诸如高通、Intel等一批芯片、传感器巨头也加入到了NB-IoT阵营,而他们的市场远远超过电信运营商。 据华为介绍,高通、Quectel、瑞士企业ublox等一批芯片企业都是华为在NB-IoT技术上的合作伙伴。除此之外,华为旗下多个产品线也启动了物联网的联合研发。去年,华为2500万美元收购了英国的芯片公司Neul。目前该公司已经推出了NB-IoT芯片。此前,华为还针对物联网推出了LiteOS操作系统。 今年7月,致力于物联网芯片的巨头Intel也加入到NB-IoT阵营。当月,Intel、爱立信、诺基亚曾宣布携手致力于面向IoT的下一代无线连接。在3GPP正式命名NB-IoT之前,NB-LTE也是技术前身之一。Intel当时宣布提供NB-LTE芯片组的路线图,并于2016年提供产品。值得一提的是,爱立信是最早布局物联网的电信企业,早在2010年时,爱立信就提出“500亿联接”概念,该概念此后被大量企业、研究机构引用。 在NB-IoT之前,物联网行业的终端、网络、芯片、操作系统、平台等各方路径不一,使得物联网“碎片化”现象严重。NB-IoT的巨头联盟或许会成为终结碎片化、统一物联网的一个契机。[15] 全球NB-IOT论坛筹备会议召开 编辑 近日,中国移动,中国联通,爱立信,阿联酋电信,GSMA,GTI,华为,英特尔,LG Uplus,诺基亚,高通,意大利电信,西班牙电信和沃达丰(排名顺序按照英文字母)等全球主流运营商,设备,芯片厂商及相关国际组织齐聚香港,共商产业大计,筹划成立NB-IOT产业论坛。此次峰会的圆满召开为下一步论坛的成立奠定了良好基础。 NB-IOT论坛旨在团结产业及生态链伙伴,促进NB-IOT市场快速上市、健康发展,将极大的促进NB-IOT产业蓬勃成长。[16] NB-IOT聚焦于低功耗广覆盖(LPWA)物联网(IoT)市场,是一种可在全球范围内广泛应用的新兴技术。具有覆盖广、连接多、速率低、成本低、功耗少、架构优等特点。 NB-IOT使用License频段,可采取带内、保护带或独立载波等三种部署方式,与现有网络共存。 NB-IOT论坛的宗旨包括: 1.加速业务应用示范和POC测试及现网验证,帮助NB-IOT解决方案更好的匹配LPWA(低功耗广域)市场需求。 2.引领行业伙伴共同构筑成熟的端到端产业链,以促进NB-IOT产业未来的快速发展及商用部署; 3.促进NB-IOT在垂直市场的应用,孵化新的商业机会点; 4.与NB-IOT所有产业伙伴合作,共同确保不同厂家的解决方案和业务的互联互通。 此次会议上,中国移动、阿联酋电信、LG Uplus、上海联通、意大利电信和沃达丰(排名顺序按照英文字母)共同宣布将在全球成立六个NB-IOT开放实验室。该实验室将专注于NB-IOT的业务创新、产业发展、互操作性测试以及产品合格验证。同时,做为NB-IOT论坛的关键组成,开放实验室还将致力于探索全新的商用案例与商业模式,并将成果分享到整个行业。 据悉,一些客户已经开始对Pre NB-IOT进行实验局测试,预计在2016年下半年预商用。2017年初将迎来商用部署。 NB-IOT论坛的筹备和开放实验室的成立标志着NB-IOT产业已经进入了全新的发展阶段,这将强有力的推动产业的快速成熟与商用进程。该论坛将接受现有国际组织的管理和指导,其范围,形式和目标已经开始制定,论坛成员可以在未来进行增补。[16] NB-IoT Forum成立,产业步入发展快车道 编辑 [西班牙,巴塞罗那,2016年2月21日] GSMA联合华为,沃达丰,爱立信,中国移动,中国联通,AT&T,德电,Etisalat, GTI, 英特尔, KDDI, KT, LG Uplus, Mediatek, 诺基亚, Oberthur Technologies, 高通,意大利电信,Telefónica,u-blox, Verizon共同发起成立了NB-IoT Forum,并于今日召开了首届全球NB-IoT峰会。[17] NB-IoT是目前在3GPP立项的应用于LPWA(低功耗广域网)市场的蜂窝网络技术,LPWA市场被认为是未来蜂窝物联网市场重要发展方向。其自身具备的低功耗、广覆盖、低成本、大容量等优势使其可广泛应用于远程抄表、智慧农业、资产跟踪等应用领域。 NB-IoT Forum旨在促进产业链健康、快速、可持续发展,扩大移动运营商网络在物联网领域创新和应用。论坛鼓励运营商和设备商等合作建立NB-IoT开放实验室,为创新及应用提供开发和测试环境,加速技术创新和市场化。此次论坛的成立标准着NB-IoT产业达到了一个新的里程碑,具备了端到端全产业链生态环境。随着更多伙伴的加入,整个产业将更加快速的向前发展。 会上GSMA、运营商、设备商、垂直行业代表分别就NB-IoT发展规划,NB-IoT Forum运作,以及行业应用发展等做主题发言和精彩讨论。与会嘉宾认为,2016年将是物联网发展具有里程碑意义的关键一年。随着3GPP标准在6月份冻结,经过市场的洗礼后,NB-IoT会在LPWA市场的多个技术竞争中脱颖而出,成为领先运营商的最佳选择。同时2016年也将成为NB-IoT的商用元年。NB-IoT Forum会发展成为横跨多个行业的最广泛的论坛组织。[17] 中兴通讯成为NB-IoT Forum主要成员 近日,中兴通讯宣布,在刚刚闭幕的巴塞罗那MWC2016展,中兴通讯正式加入并成为GSMA NB-IoT(Narrow Band-IoT) Forum主要成员。NB-IoT联盟涵盖全球主流运营商、主要的设备制造商以及芯片提供商、终端模组厂商等。作为主要的设备制造商,中兴通讯将与联盟中其他伙伴一起,共同推进基于蜂窝网的NB-IoT产业发展;协助联盟一起促进和验证NB-IoT技术,参与和推进NB-IoT相关的创新服务和应用的开发,推动NB-IoT的商用。[18] 中兴通讯一直是面向物联网的广域低功耗技术NB-IoT的主要推进者。物联网IoT(Internet of Thing)面向海量连接,在一些物联网的场景下,例如智能抄表,生态农业,智慧停车,智能小区,智能建筑等场景, 对广覆盖、低功耗、低成本终端的需求更为明确。目前广泛商用的2G/3G/4G、WLAN及其他无线技术都无法满足这些挑战,而NB-IoT,即基于LTE的窄带IoT技术,具有低功耗、广覆盖、多连接的特征,可满足物联网场景的需求。 国际标准组织3GPP计划在2016年6月冻结并发布NB-IoT标准。中兴通讯作为NB-IoT标准的主要贡献者之一,在NB-IoT技术研究和标准化工作中与同行一起积极推进,并大量投入,在空中接口核心技术例如信道设计、短码、多连接以及超低功耗方面均做出了主要贡献。 随着智能化、移动化、云化等技术的发展,多种形式的智能终端不断普及。根据预测,2020年全球智能连接数将达到1000亿。NB-IoT将会有效的解决对运营商网络提出的千亿连接的需求,使得电信运营商能够快速满足“物-物”互联的连接需求,是电信运营商发展的重要方向。中兴通讯为运营商和产业打造了基于NB-IoT的端到端解决方案,积极投入对芯片、终端、系统和物联网IoT平台的研究,助力运营商实现未来“千亿”的连接。 中兴通讯在2015年提出万物互联M-ICT的战略,提供IoT整体解决方案;在工业互联网、车联网、智能家居和智慧城市、智能抄表等领域均有完善的解决方案和应用。中兴通讯通过优化连接,构建开放IoT应用使能平台,并推出适用于物联网的SmartOS,致力于构建安全可信的生态环境,为上下游产业链的客户提供服务,帮助合作伙伴挖掘每个“BIT”的价值。中兴通讯将继续与合作伙伴一起,为行业、政企、运营商客户提供领先的IoT端到端解决方案与服务。[18] 中国电信建成下一代物联网 编辑 2017年5月17日,中国电信宣布全球首个覆盖最广的商用下一代物联网(NB-IOT)网络建成,同时领先的4G网络实现全国覆盖。 2017年6月5日,中国电信集团公司与青岛市人民政府战略合作协定签署暨新一代物联网(NB-IoT)正式商用启动协定。中国电信将发挥物联网专网网路通道优势,以4G/5G移动网路为主,在青岛率先推进NB-IoT建设,推动青岛成为全国领先的物联网应用示范城市和智慧城市标杆。[19] 2016年11月,中国电信将无锡列为首批窄带物联网试点城市。中国电信无锡分公司采用业界最先进的网络通信设备和架构,投资近2亿元,历时5个月,建设通信基站近2000个,率先实现了基于800M超低频窄带网络无锡全域覆盖。[20] 中国联通NB-IoT网络即将“试商用” 编辑 中国联通将于2017年5月15日举办“中国联通NB-IoT网络试商用发布会暨物联网生态论坛”,以迎接万物互联,抢抓百亿级市场机遇。作为智慧城市建设的积极参与者,中国联通一直致力于物联网的发展,建成了以上海为代表的目前世界上最大规模的NB-IoT商用城域网络,实现了上海城域全覆盖。[21] 和中国电信重耕800M,在800M低频上建设部署NB-IoT不同的是,中国联通采取了在900M/1800M双频上进行部署,除北京、上海等大城市,超过80%的基站拟采用基于LTE 1800M的升级部署方案。[22] 并且目前正在积极推进NB-IoT和eMTC技术试点。试点主要包括两部分:一部分是内场实验,依托于中国联通物联网开放实验室,主要推进各类物联网技术对接;另一部分是外场测试,中国联通已经在全国10多个城市同步推进测试工作。[2
1、什么是设计模式? 模式的理解 模式是一种解决问题的思路,而不是具体的做法。 设计模式的理解 在软件开发领域,设计模式是为解决开发中某一类问题而提出的一种解决方案。因此,设计模式本质上是思想,而不是代码。设计模式追求的思想上的复用,而不是代码上的复用。 2、ASP.NET MVC概述 ASP.NET MVC是一种构建Web应用程序的框架,它将一般的MVC(Model-View-Controller)模式应用于ASP.NET框架。 MVC将Web应用程序划分为三个主要的部分,以下是MSDN给出的定义: 模型(Model):模型对象是实现应用程序数据域逻辑的应用程序部件。 通常,模型对象会检索模型状态并将其存储在数据库中。 例如,Product 对象可能会从数据库中检索信息,操作该信息,然后将更新的信息写回到 SQL Server 数据库内的 Products 表中。 视图(View):视图是显示应用程序用户界面 (UI) 的组件。 通常,此 UI 是用模型数据创建的。 Products 表的编辑视图便是一个视图示例,该视图基于 Product 对象的当前状态显示文本框、下拉列表和复选框。 控制器(Controller):控制器是处理用户交互、使用模型并最终选择要呈现的视图来显示 UI 的组件。 在 MVC 应用程序中,视图仅显示信息;控制器则用于处理和响应用户输入和交互。 例如,控制器处理查询字符串值,并将这些值传递给模型,而模型可能会使用这些值来查询数据库。 3、ASP.NET MVC的发展历程 我们以一张时间轴线图开始,了解一下ASP.NET MVC的发展历程。 2007年2月,Microsoft公司的Scott Guthrie在旅途中草拟了ASP.NET MVC的内核程序。经过9个预览版本,于2009年3月13日,正式发布ASP.NETMVC1官方版本。 时隔一年,ASP.NETMVC2于2010年3月发布,部分主要特征如下: • 带有自定义模板的UI辅助程序 • 在客户端和服务端基于特性的模型验证 • 强类型的HTML辅助程序 • 改善的Visual Studio开发工具 • 支持将大型应用程序划分为域 • 支持异步控制器 • 使用Html.RenderAction支持渲染网页或网站的某一部分 • 新的辅助函数、使用工具和API增强 2011年1月,ASP.NET MVC3正式推出,部分主要特征如下: • 支持Razor视图引擎 • 支持.NET4数据注解 • 改进了模型验证 • 提供更强的控制和更大的灵活性,支持依赖项解析(Dependency Resolution)和全局操作过滤器(Global Action Filter) • 丰富的JavaScript支持,其中包括非侵入式JavaScript、jQuery验证和JSON绑定 • 支持NuGet,可以用来发布软件,管理整个平台的依赖 2012年9月,ASP.NET MVC4正式发布,新增功能主要包括: • ASP.NET Web API • 增强了默认的项目模板 • 添加使用jQuery Mobile的手机项目模板 • 支持显示模式(Display Mode) • 支持异步控制器的任务 • 捆绑和微小(minification) 2013年10月,ASP.NET MVC5与Visual Studio 2013一起发布,下面列出了一些主要特征: • One ASP.NET • 新的Web项目体验 • ASP.NETIdentity • Bootstrap模板 • 特性路由 • ASP.NET基架 • 身份验证过滤器 • 过滤器重写 4、创建ASP.NET MVC4应用程序 1、选择“文件”->“新建”->“项目”选项,如下图所示: 2、在“新建项目”对话框左侧的“已安装”->“模板”->“Visual C#”列表下,选中Web选项,选择ASP.NET MVC4 Web应用程序,将应用程序命名为MyFirstMvcProject,点击“确定”按钮,如下图所示: 3、在弹出的“新ASP.NET 项目”对话框中,选中“基本”模板,视图引擎选择“Razor”,同时将“创建单元测试项目”勾选上,点击“确定”按钮,如下图所示: 4、至此,一个新的MVC项目已经创建完成,如下图所示: 5、ASP.NET MVC应用程序结构 新的MVC项目创建完成后,会自动向这个项目中创建一些目录,下表介绍了这些目录的主要用途。 目录 用途 App_Data 用于存储想要读取/写入的数据文件 App_Start 用于保存一些功能的配置代码 Content 用于保存CSS、图像和其他站点内容 Controllers 用于保存处理URL请求的控制器类 Models 用于保存表示和操纵数据以及业务对象的类 Scripts 用于保存JavaScript库文件和脚本 Views 用于保存负责呈现输出结果的UI模板文件 6、ASP.NETMVC的约定 在默认情况下,ASP.NET MVC应用程序对约定的依赖性很强,这样就避免了开发人员配置和指定一些项,因为这些项可以根据约定来推断。这个概念通常被称为“习惯优于配置(convention over configration)”。 ASP.NET MVC对于程序结构的约定如下: 1、每个Controller类的名字以Controller结尾,保存在Controller目录中。 2、应用程序的所有视图放在单独的Views目录下。 3、控制器使用的视图是在Views主目录下的,与控制器名称相同的子目录中。 7、WebForm和ASP.NET MVC,为什么MVC更好一些? ASP.NET Webform 后台代码(behind code)—— 福音与诅咒 我们已经在项目开发中使用过ASP.NETWebform技术,大家会发现它更接近可视化设计,换句话说,开发者只需要从设计面板中拖拽控件即可完成UI,接着在behindcode中实现逻辑代码即可完成最后的Web页面功能。 所以换句话说,当你从设计面板中拖拽一个按钮时,在后台代码中就会生成一个button对象,你只需要在按钮的点击事件中实现事件响应代码即可。 public partial class WebForm1 :System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { // Developers write code here } protected void Button1_Click(object sender, EventArgs e) { // Developers write code here } } 当我们在页面中拖拽一些UI元素时,双击它们即可在后台代码中生成一系列事件响应代码,这些逻辑代码都在ASPX.CS文件中。 这个后台代码文件是ASP.NETWebForm的关键,你可以在这个文件中应用.NET的所有特性,包括事件、委托、HTTP协议以及Session等等。 但是这种behindcode模式有5个问题,下面我们将一一讲述这5个问题,并用MVC的设计思想来分别解决这些问题。 问题1:基于视图的方案来解决基于行为的需求 我们的网站最终是由用户使用的,用户访问网站肯定会有特定的目的,网站要做的就是通过让用户的交互行为来完成其想要的目的。比如当用户访问一个购物网站时,也许他的交互行为会是这样的: · 购买产品 · 打印发票 这些交互行为是通过按钮点击、右键点击和浏览器URL实现的。由于这些交互都是基于HTTP协议的,所以如果我们能将这些交互行为映射到具体的一些方法上,那么整个架构将会变得简单很多。 但是微软做不到这样,因为它要实现可视化网页编程,所以他们最终选择了基于视图的解决方案。 从上图可以看出,整个请求过程看上去很奇怪: · 用户发起一个HTTP请求,比如HTTPPOST / GET · IIS服务器将请求映射到视图 · 视图调用页面的生命周期,通过事件驱动,调用合适的交互方法 · 最后将交互的结果展现给终端用户 因为微软一开始就选择了基于视图的设计方案,所以架构本身很难向基于用户交互的设计思想靠拢。换句话说,当用户发出“购买”请求时,先是访问了视图页面“Shopping.aspx”,后台逻辑代码在“Shopping.aspx.cs”中,页面生命周期中会将页面的计算结果返回给用户。 如果利用MVC的思想,都是基于用户交互行为的话,那么请求流程将会是如下所示: 问题2:坏架构的副作用——紧耦合 当选择了一个错误的架构以后,未来将会出现很多难以解决的副作用,在ASP.NETWebForm中就出现了这个问题。尽管behindcode后台代码被分离到不同的文件中,但是ASPX.CS文件和ASPX文件却紧密的联系在一起,这将导致系统的耦合度很高,并且很难解耦合,这是一个很头疼的问题。 简单地说,我们很难将Customer.aspx.cs和CustomerDetailed.aspx简单地剥离开,后台代码已经紧紧地将其捆在一起,而且也很难复用。如果我们可以将请求先通过action,而不通过视图view,action得到的数据再由控制器决定由哪个view展示,那么请求的流程将会是这样的: 所以我们可以很方便地控制最终结果是由移动页面展示还是正常页面展示,如下代码: publicActionResult Index(string DeviceType) { if (viewType == "Mobile") { returnView("MobileView"); } else { returnView("NormalView"); } } 问题3:HTML不是唯一的返回类型 由于视图view和后台代码behindcode紧密耦合在一起,所以默认的返回类型就固定了,都是HTML类型。如果你想改变类型就必须设置Content-type和调用Response.End方法。 如果我们创建一个Action,返回的类型由Action中指定,系统就可以在同一个action中根据不同条件输出不同的返回类型。代码如下: publicActionResult Index(string viewType) { if (viewType == "JSON") { return Json(new Customer(),JsonRequestBehavior.AllowGet); } else { returnView("DisplayCustomer", new Customer()); } } 问题4:视图和数据的灵活组合 Webform是视图优先的架构,所以视图决定了展现的数据,因此视图的扩展性就很差,如果遇到复杂的数据结构,这种方式就显得力不从心了。 但是如果是行为优先的架构的话,当我们触发action时,action可以根据不同的请求选择不同的数据模型和视图结构,如下图: 在MVC中,你可以在不同的view中选择相同的数据模型,比如下面的代码,customerdata数据既可以绑定在DetailCustomer视图中,也可以绑定在Customer视图中。 publicActionResult Index(string ViewName,Customer customerdata) { if (ViewName =="Detailed") { returnView("DetailCustomer",customerdata); } else { returnView("Customer",customerdata); } } 但这在WebForm中实现起来是非常麻烦的。 问题5、将behind code当做普通的类来进行单元测试 behindcode后台代码在WebForm中是一个非常庞大的类,并且不能简单地实例化。要知道WebForm是继承于Page类的,Page类不能直接实例化,因为它有太多的依赖项。 publicpartial class WebForm1 : System.Web.UI.Page { protected void Page_Load(object sender,EventArgs e) { } public void Button1_Click(objectsender, EventArgs e) { Session["SomeSession"] ="Is this set"; } } 为什么我们想要实例化Page类呢?其中一个原因就是可以方便单元测试。比如我要测试一个按钮点击事件,用来检查Session是否设置成功。在WebForm中的代码看起来不是那么舒服: [TestMethod] publicvoid TestMethod1() { WebApplication22.WebForm1 obj = newWebApplication22.WebForm1(); obj.Button1_Click(this, newEventArgs()); } 并且运行时还会抛出一个异常: 在MVC中,这个类变成了一个普通类,我们可以在测试工程中将它实例化,并对类里面的属性方法、Session、ViewBag 、 TempData等进行单元测试。 publicclass HomeController : Controller // this class is simple { public ActionResult Index() { Session["SomeSession"] ="Is this set"; return View("SomeView"); } } 所以是否选择MVC解决方案? 从WebForm架构切换到MVC架构,我们需要做以下几件事情: · 将behindcode中的代码转移到controller类中,并将原来的方法转换成action方法。 · 中间层用数据模型和逻辑接口代替。 · 视图view只用来展现数据和页面布局。 · DAL层和其他层没有什么变化,因为它和behindcode关系不大。 所以MVC架构中,用户的请求分为下面3个步骤: · 终端用户发送请求,路由器将请求路由到合适的Controller,Controller是逻辑实体和行为Action的集合。 · Controller将请求映射到特定的Action。 · Action有两个任务,第一是获取合适的数据,第二是将这些数据和视图View绑定起来。Action创建数据模型,并将数据模型连接到指定View,输出最终的相应结果。
1、存储过程概念 2、存储过程调用 3、存储过程示例 4、带参数的存储过程 5、while在存储过程中的使用
1、索引中关于搜索的算法 2、索引的使用原则 3、索引的优缺点 4、索引的种类 5、显示索引 show index from 表名; 6、创建唯一索引 alter table 表名 add unique [唯一索引名] (列名) 7、创建全文索引 alter table 表名 add fulltext [全文索引名] (列名) 8、索引相关介绍 9、索引总结 10、全文索引的使用 select * from member where match(列名) against (‘查找列中某个值’);
1、备份 2、还原
1、存储引擎 2、事务 3、事务语法 4、事务简单原理
1、表列的添加、修改和删除 2、视图 3、字符集和校对集 4、触发器相关知识 5、
1、Web.config文件中配置数据库连接信息,如下代码: <appSettings> <!--连接MongoDB数据库连接字符串开始--> <add key="MongoIP" value="192.168.33.162" /> <add key="MongoDatabase" value="ADS5_HNXY" /> <!--MongoDB集群名称,单台MongoDB时请注释掉下面行的配置--> <!--<add key="ReplicaSetName" value="atrepl"/>--> <add key="MongoUser" value=""/> <add key="MongoPassword" value=""/> <!--连接MongoDB数据库连接字符串结束 --> </appSettings> 2、MongoDBHelper操作类,如下代码: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data; using MongoDB.Bson; using MongoDB.Driver; using MongoDB.Driver.Builders; using MongoDB.Driver.GridFS; using MongoDB.Driver.Linq; using AT.Business.IDAO; using AT.Business.DAL; using AT_DataShiftService; namespace BAL.DBHelper { /// <summary> /// MongoDB数据库操作类 /// </summary> public class MongoData { #region 属性列表 private static string ip = System.Configuration.ConfigurationManager.AppSettings["MongoIP"]; private static string dbname = System.Configuration.ConfigurationManager.AppSettings["MongoDatabase"]; private static string user = System.Configuration.ConfigurationManager.AppSettings["MongoUser"]; private static string pwd = System.Configuration.ConfigurationManager.AppSettings["MongoPassword"]; private static string myReplicaSetName = System.Configuration.ConfigurationManager.AppSettings["ReplicaSetName"]; private static ReadPreference myReadPreference = ReadPreference.SecondaryPreferred; //两个不同的表名 private const string _ADS5 = "ads5"; private const string _POWERPARAMETERS = "PowerParameters"; private static AT_System_IDAO systemidao = new AT_System_Dal(); #endregion #region MongoDB权限认证 /// <summary> /// MongoDB权限认证 /// </summary> /// <returns></returns> public static MongoDatabase getDatabase() { MongoClientSettings setting = new MongoClientSettings(); if (!string.IsNullOrEmpty(user) && !string.IsNullOrEmpty(pwd)) { //Logger.Log.Info("MongoDB开启权限访问 " + user + ":" + pwd); List<MongoCredential> lstCredential = new List<MongoCredential>(); lstCredential.Add(MongoCredential.CreateCredential(dbname, user, pwd)); setting.Credentials = lstCredential; } setting.Server = new MongoServerAddress(ip); if (!string.IsNullOrEmpty(myReplicaSetName)) { //Logger.Log.Info("MongoDB开启集群模式 " + myReplicaSetName); setting.ConnectionMode = ConnectionMode.ReplicaSet; setting.ReplicaSetName = myReplicaSetName; setting.ReadPreference = myReadPreference; } //MongoClient client = new MongoClient(QJBL.MongoDBConn); var client = new MongoClient(setting); MongoServer server = client.GetServer(); MongoDatabase database = server.GetDatabase(dbname); return database; } #endregion #region 获取MySQL中对应表具列表信息 2017-05-17 /// <summary> /// 获取MySQL中对应表具列表信息 /// </summary> /// <returns></returns> public static DataTable GetMeterList() { return systemidao.GetMeterList(); } #endregion #region 获取每个整点需要上传的表具读数 2017-05-17 /// <summary> /// 获取每个整点需要上传的表具读数 /// </summary> /// <param name="datetime"></param> /// <returns></returns> public static DataTable AT_Up_EnergyValue4WJW(string datetime) { DataTable dt = GetMeterList(); DataTable newDT = new DataTable(); newDT.Columns.Add("BuildID", typeof(string)); newDT.Columns.Add("CollectionID", typeof(string)); newDT.Columns.Add("MeterID", typeof(string)); newDT.Columns.Add("EnergyValue", typeof(double)); foreach (DataRowView drv in dt.DefaultView) { DataRow row = newDT.NewRow(); if (drv["F_MeasureClassify"].ToString() == "04" || drv["F_MeasureClassify"].ToString() == "14") { //从MongoDB中的PowerParameters中取数 row["BuildID"] = drv["BuildID"].ToString(); row["CollectionID"] = drv["CollectionID"].ToString(); row["MeterID"] = drv["MeterID"].ToString(); double rawValue = GetDataFromPowerParameters(drv["F_MeterID"].ToString(), DateTime.Parse(datetime), drv["F_ValueType"].ToString()); //这里去除的可能是负数,要做绝对值转换 double absValue = Math.Abs(rawValue); row["EnergyValue"] = double.Parse(drv["F_Ratio"].ToString()) * absValue * 0.0036; WriteLog(drv["F_MeterID"].ToString() + "\t" + datetime + "\t" + (double.Parse(drv["F_Ratio"].ToString()) * absValue * 0.0036) + "\r"); newDT.Rows.Add(row); } else { //从MongoDB中的ADS5中取数 row["BuildID"] = drv["BuildID"].ToString(); row["CollectionID"] = drv["CollectionID"].ToString(); row["MeterID"] = drv["MeterID"].ToString(); double rawValue = GetDataFromADS5(drv["F_TagName"].ToString(), DateTime.Parse(datetime)); double absValue = Math.Abs(rawValue); row["EnergyValue"] = double.Parse(drv["F_Ratio"].ToString()) * absValue; WriteLog(drv["F_TagName"].ToString() + "\t" + datetime + "\t" + double.Parse(drv["F_Ratio"].ToString()) * absValue + "\r"); newDT.Rows.Add(row); } } return newDT; } #endregion #region 当F_MeasureClassify不为‘04’和‘14’时,从ADS5表中获取表头示数 2017-08-03 /// <summary> /// F_CaclType = 0 ,从ADS5表中获取数据 /// </summary> /// <param name="TagName"></param> /// <param name="DateTime"></param> /// <returns></returns> public static double GetDataFromADS5(string TagName, DateTime DateTime) { MongoDatabase db = MongoData.getDatabase(); //获得Users集合,如果数据库中没有,先新建一个 MongoCollection col = db.GetCollection(_ADS5); var query = Query.And( Query.EQ("TagName", TagName), Query.EQ("DateTime", DateTime) ); List<BsonDocument> documents = col.FindAs<BsonDocument>(query).ToList(); //做异常处理 if (documents.Count != 0) { BsonElement element = documents[0].GetElement("Value"); return double.Parse(element.Value.ToString()); } else { //直至可以获取到上一个有数据的时刻 2017-08-03 int index = 0; do { index++; DateTime NewDateTime = DateTime.AddHours(-1 * index); query = Query.And( Query.EQ("TagName", TagName), Query.EQ("DateTime", NewDateTime) ); documents = col.FindAs<BsonDocument>(query).ToList(); } while (documents.Count == 0); BsonElement element = documents[0].GetElement("Value"); return double.Parse(element.Value.ToString()); } } #endregion #region 当F_MeasureClassify为‘04’或‘14’时,从PowerParameters表中获取表头示数 2017-08-03 /// <summary> /// 从PowerParameters表中获取数据 /// </summary> /// <param name="TagName"></param> /// <param name="DateTime"></param> /// <returns></returns> public static double GetDataFromPowerParameters(string MeterID, DateTime DateTime, string ValueType) { MongoDatabase db = MongoData.getDatabase(); //获得Users集合,如果数据库中没有,先新建一个 MongoCollection col = db.GetCollection(_POWERPARAMETERS); var query = Query.And( Query.EQ("MeterID", MeterID), Query.EQ("DateTime", DateTime) ); List<BsonDocument> documents = col.FindAs<BsonDocument>(query).ToList(); //做异常处理 if (documents.Count != 0) { BsonElement element = documents[0].GetElement(ValueType); return double.Parse(element.Value.ToString()); } else { //直至可以获取到上一个有数据的时刻 2017-08-03 int index = 0; do { index ++; DateTime NewDateTime = DateTime.AddHours(-1 * index); query = Query.And( Query.EQ("MeterID", MeterID), Query.EQ("DateTime", NewDateTime) ); documents = col.FindAs<BsonDocument>(query).ToList(); } while (documents.Count == 0); BsonElement element = documents[0].GetElement(ValueType); return double.Parse(element.Value.ToString()); } } #endregion #region 日志记录 /// <summary> /// /// </summary> /// <param name="log"></param> public static void WriteLog(string log) { string spath1 = System.AppDomain.CurrentDomain.BaseDirectory + @"\GetInterupt.Log"; string spath = System.AppDomain.CurrentDomain.BaseDirectory + @"\" + DateTime.Now.ToString("yyyy") + @"\GetInterupt" + DateTime.Now.ToString("MM") + ".Log"; if (!FileC.IsExistFile(spath)) { FileC.CreateDirectory(FileC.GetDirectoryName(spath)); FileC.CreateFile(spath); FileC.WriteText(spath1, ""); } FileC.AppendText(spath, log + "\r\n"); FileC.AppendText(spath1, log + "\r\n"); } #endregion } }
在使用网络请求时,发现根据微信官方的API的方法进行操作出现Invalid request 400错误,到底怎么回事呢? 以下是程序代码: /** * 加载电影 */ loadMovie:function(){ var page = this; wx.request({ url: 'https://api.douban.com/v2/movie/in_theaters', header:{ 'Content-Type':"application/json" }, success:function(res){ var subjects = res.data.subjects; processSubjects(subjects); page.setData({ movies: subjects, hidden:true}); } }) }, 经过一般搜索研究发现,微信开发者工具在更新到最新版本后,相应的参数配置也发生了变化,官网给出的这个配置已经不能用了, 'content-type': 'application/json' 需要改为'content-type':'application/text' 以上代码经过修改调整后,如下代码所示: /** * 加载电影 */ loadMovie:function(){ var page = this; wx.request({ url: 'https://api.douban.com/v2/movie/in_theaters', header:{ 'content-type':'application/text' }, success:function(res){ var subjects = res.data.subjects; page.processSubjects(subjects); page.setData({ movies: subjects, hidden:true}); } }) },
1、底部的tabBar 可设置的属性有color、selectedColor、borderStyle、backgroundColor、list至少2个,最多5个(其属性有pagePath、text、iconPath、selectedIconPath等) "tabBar": { "color":"#dddddd", "selectedColor":"#3cc51f", "borderStyle":"black", "backgroundColor":"#2B2B2B", "list": [ { "pagePath": "pages/movie/movie", "text": "影院热映", "iconPath": "assets/img/dy-1.png", "selectedIconPath": "assets/img/dy.png" }, { "pagePath": "pages/recommend/recommend", "text": "电影推荐", "iconPath": "assets/img/tj-1.png", "selectedIconPath": "assets/img/tj.png" }, { "pagePath": "pages/search/search", "text": "查询电影", "iconPath": "assets/img/search-1.png", "selectedIconPath": "assets/img/search.png" } ] }, 效果如下图所示: 2、滑块视图容器swiper swiper 滑块视图容器。 属性名 类型 默认值 说明 最低版本 indicator-dots Boolean false 是否显示面板指示点 indicator-color Color rgba(0, 0, 0, .3) 指示点颜色 1.1.0 indicator-active-color Color #000000 当前选中的指示点颜色 1.1.0 autoplay Boolean false 是否自动切换 current Number 0 当前所在页面的 index interval Number 5000 自动切换时间间隔 duration Number 500 滑动动画时长 circular Boolean false 是否采用衔接滑动 vertical Boolean false 滑动方向是否为纵向 bindchange EventHandle current 改变时会触发 change 事件,event.detail = {current: current, source: source} 从公共库v1.4.0开始,change事件返回detail中包含一个source字段,表示导致变更的原因,可能值如下: autoplay 自动播放导致swiper变化; touch 用户划动引起swiper变化; 其他原因将用空字符串表示。 注意:其中只可放置<swiper-item/>组件,否则会导致未定义的行为。 swiper-item 仅可放置在<swiper/>组件中,宽高自动设置为100%。 示例代码: <swiper indicator-dots="{{indicatorDots}}" autoplay="{{autoplay}}" interval="{{interval}}" duration="{{duration}}"> <block wx:for="{{imgUrls}}"> <swiper-item> <image src="{{item}}" class="slide-image" width="355" height="150"/> </swiper-item> </block> </swiper> <button bindtap="changeIndicatorDots"> indicator-dots </button> <button bindtap="changeAutoplay"> autoplay </button> <slider bindchange="intervalChange" show-value min="500" max="2000"/> interval <slider bindchange="durationChange" show-value min="1000" max="10000"/> duration Page({ data: { imgUrls: [ 'http://img02.tooopen.com/images/20150928/tooopen_sy_143912755726.jpg', 'http://img06.tooopen.com/images/20160818/tooopen_sy_175866434296.jpg', 'http://img06.tooopen.com/images/20160818/tooopen_sy_175833047715.jpg' ], indicatorDots: false, autoplay: false, interval: 5000, duration: 1000 }, changeIndicatorDots: function(e) { this.setData({ indicatorDots: !this.data.indicatorDots }) }, changeAutoplay: function(e) { this.setData({ autoplay: !this.data.autoplay }) }, intervalChange: function(e) { this.setData({ interval: e.detail.value }) }, durationChange: function(e) { this.setData({ duration: e.detail.value }) } }) 效果如下图所示: 3、豆瓣API 在浏览器中输入豆瓣电影接口地址 http://api.douban.com/v2/movie/in_theaters 然后F12,打开调试窗口,选择Console,输入var a=接口返回的json串,如下图所示: 然后回车,再输入a,再回车,即可看到已经格式化的JSON对象,如下图所示: 3、从接口获取数据进行绑定 <block wx:for="{{movies}}"> <view class="movie"> <view class="pic"> <image mode="aspectFit" src="{{item.images.medium}}"></image> </view> <view class="movie-info"> <view class="base-info"> <text>{{item.text}}</text> </view> </view> </view> <view class="hr"></view> </block> // pages/movie/movie.js Page({ /** * 页面的初始数据 */ data: { imgUrls: [ '../../assets/img/001.jpg', '../../assets/img/002.jpg', '../../assets/img/003.jpg' ], indicatorDots: true, autoplay: true, interval: 3000, duration: 1000, movies:[], hidden:false }, /** * 生命周期函数--监听页面加载 */ onLoad: function (options) { this.loadMovie(); }, /** * 生命周期函数--监听页面初次渲染完成 */ onReady: function () { }, /** * 生命周期函数--监听页面显示 */ onShow: function () { }, /** * 生命周期函数--监听页面隐藏 */ onHide: function () { }, /** * 生命周期函数--监听页面卸载 */ onUnload: function () { }, /** * 页面相关事件处理函数--监听用户下拉动作 */ onPullDownRefresh: function () { }, /** * 页面上拉触底事件的处理函数 */ onReachBottom: function () { }, /** * 用户点击右上角分享 */ onShareAppMessage: function () { }, /** * 加载电影 */ loadMovie:function(){ var page = this; wx.request({ url: 'http://api.douban.com/v2/movie/in_theaters', header:{ 'Content-Type':"application/json" }, success:function(res){ var subjects = res.data.subjects; processSubjects(subjects); page.setData({ movies: subjects, hidden:true}); } }) }, /** * */ processSubjects: function (subjects) { //循环 for (var i = 0; i < subjects.length;i++){ var subject = subjects[i]; this.processSubject(subject); } }, /** * */ processSubject:function(subject){ //名称 var title = subject.title; //导演 var directors = subject.directors; var directorStr = ""; for (var index in directors){ directorStr= directorStr+directors[index].name+" / "; } if(directorStr!=""){ directorStr = directorStr.substring(0,directorStr.length-2); } //主演 var casts = subject.casts; var castStr = ""; for(var index in casts){ castStr= castStr+casts[index].name+" / "; } if(castStr!=""){ castStr= castStr.substring(0,castStr.length-2); } //类型 var genres = subject.genres; var genresStr = ""; for(var index in genres){ genresStr = genresStr+genres[index]+" / "; } if(genresStr!=""){ genresStr= genresStr.substring(0,genresStr.length-2); } //年份 var year = subject.year; //拼接字符串 var text = "名称:"+title+"\n导演:"+directorStr+"\n主演:"+castStr+"\n类型:"+genresStr+"\n上映年份:"+year; subject.text = text; } }) 4、加载进度条 <view class="body-view"> <loading hidden="{{hidden}}" bindchange="loadingChange"> 加载中... </loading> </view> data: { imgUrls: [ '../../assets/img/001.jpg', '../../assets/img/002.jpg', '../../assets/img/003.jpg' ], indicatorDots: true, autoplay: true, interval: 3000, duration: 1000, movies:[], hidden:false }, /** * 加载电影 */ loadMovie:function(){ var page = this; wx.request({ url: 'https://api.douban.com/v2/movie/in_theaters', header:{ 'Content-Type':"application/json" }, success:function(res){ var subjects = res.data.subjects; processSubjects(subjects); page.setData({ movies: subjects, hidden:true}); } }) }, 5、如果出现请求的URL地址不在合法域名列表中的话,会出现如下问题: 解决方案:打开小程序微信公众平台设置小程序开发设置,配置服务器合法域名(必须是https),如下图所示:
1、获取位置的API 这里是获取到位置,并且打开地图定位到手机当前的经纬度坐标处 wx.getLocation({ type: 'gcj02', //返回可以用于wx.openLocation的经纬度 success: function(res) { var latitude = res.latitude var longitude = res.longitude wx.openLocation({ latitude: latitude, longitude: longitude, scale: 28 }) } }) 2、发起网络请求的API const requestTask = wx.request({ url: 'test.php', //仅为示例,并非真实的接口地址 data: { x: '' , y: '' }, header: { 'content-type': 'application/json' }, success: function(res) { console.log(res.data) } }) requestTask.abort() // 取消请求任务 3、根据经纬度坐标获取城市信息的接口地址(百度API):http://api.map.baidu.com/geocoder/v2/?ak=百度API Key&location=32.12,120.5&output=json 4、根据城市名称获取天气信息的接口地址: http://wthrcdn.etouch.cn/weather_mini?city=合肥 5、页面js方法调用,在微信API方法内调用另一个方法,要首先获取到当前的页面,如下图所示: var page = this; 6、获取返回的Json值后,可以在页面中直接.出对应节点的值,如下图所示: 首先创建数组 7、使用模板API,定义与引用 创建模板 8、利用循环获取模板并展示
1、js方法 <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>Document</title> <script src="http://pv.sohu.com/cityjson?ie=utf-8"></script> <script type="text/javascript"> document.write('IP地址:' + returnCitySN["cip"] + ', 城市编码:' + returnCitySN["cid"] + ', 地区:' + returnCitySN["cname"]); </script> </head> <body> </body> </html> 2、后台代码实现 #region IP地址限制功能 2017-07-18 /// <summary> /// /// </summary> /// <returns></returns> public bool IsIPValidate() { bool flag = false; string userip = GetLoginIp(); string[] addr = GetAddressByIp(userip); string addrs = addr[0] + addr[1]; if ("北京".Equals(addr[0]) || "北京".Equals(addr[1])) { flag = true; } return flag; } /// <summary> /// 获取远程访问用户的Ip地址 /// </summary> /// <returns>返回Ip地址</returns> protected string GetLoginIp() { string loginip = ""; //Request.ServerVariables[""]--获取服务变量集合 if (Request.ServerVariables["REMOTE_ADDR"] != null) //判断发出请求的远程主机的ip地址是否为空 { //获取发出请求的远程主机的Ip地址 loginip = Request.ServerVariables["REMOTE_ADDR"].ToString(); } //判断登记用户是否使用设置代理 else if (Request.ServerVariables["HTTP_VIA"] != null) { if (Request.ServerVariables["HTTP_X_FORWARDED_FOR"] != null) { //获取代理的服务器Ip地址 loginip = Request.ServerVariables["HTTP_X_FORWARDED_FOR"].ToString(); } else { //获取客户端IP loginip = Request.UserHostAddress; } } else { //获取客户端IP loginip = Request.UserHostAddress; } return loginip; } /// <summary> /// 根据IP获取省市 /// </summary> public string[] GetAddressByIp(string ip) { string PostUrl = "http://int.dpool.sina.com.cn/iplookup/iplookup.php?ip=" + ip; string res = GetDataByPost(PostUrl);//该条请求返回的数据为:res=1t115.193.210.0t115.194.201.255t中国t浙江t杭州t电信 string[] arr = getAreaInfoList(res); return arr; } /// <summary> /// Post请求数据 /// </summary> /// <param name="url"></param> /// <returns></returns> public string GetDataByPost(string url) { HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url); string s = "anything"; byte[] requestBytes = System.Text.Encoding.ASCII.GetBytes(s); req.Method = "POST"; req.ContentType = "application/x-www-form-urlencoded"; req.ContentLength = requestBytes.Length; Stream requestStream = req.GetRequestStream(); requestStream.Write(requestBytes, 0, requestBytes.Length); requestStream.Close(); HttpWebResponse res = (HttpWebResponse)req.GetResponse(); StreamReader sr = new StreamReader(res.GetResponseStream(), System.Text.Encoding.Default); string backstr = sr.ReadToEnd(); sr.Close(); res.Close(); return backstr; } /// <summary> /// 处理所要的数据 /// </summary> /// <param name="ip"></param> /// <returns></returns> public static string[] getAreaInfoList(string ipData) { //1t115.193.210.0t115.194.201.255t中国t浙江t杭州t电信 string[] areaArr = new string[10]; string[] newAreaArr = new string[2]; try { //取所要的数据,这里只取省市 areaArr = ipData.Split('t'); newAreaArr[0] = areaArr[4];//省 newAreaArr[1] = areaArr[5];//市 } catch (Exception e) { } return newAreaArr; } #endregion
一、使用国内镜像源: 先在这里推荐几个: 1、东软信息学院 mirrors.neusoft.edu.cn 端口:802、北京化工大学 ubuntu.buct.edu.cn/ubuntu.buct.cn 端口:803、中国科学院开源协会 mirrors.opencas.cn (mirrors.opencas.org/mirrors.opencas.ac.cn) 端口:804、上海GDG镜像服务器 sdk.gdgshanghai.com 端口:8000 5、电子科技大学 mirrors.dormforce.net 端口:80 其中,强烈推荐电子科技大学的镜像源! 二、使用方法 启动 Android SDK Manager ,打开主界面,依次选择「Tools」、「Options…」,弹出『Android SDK Manager – Settings』窗口; 在『Android SDK Manager – Settings』窗口中,在「HTTP Proxy Server」和「HTTP Proxy Port」输入框内填入mirrors.neusoft.edu.cn和80,并且选中「Force https://… sources to be fetched using http://…」复选框。设置完成后单击「Close」按钮关闭『Android SDK Manager – Settings』窗口返回到主界面;
解决中文文件名保存Excel乱码问题,主要是判断火狐或者IE浏览器,然后做对应的判断处理,核心代码如下: System.Web.HttpContext.Current.Response.ContentType = "application/vnd.ms-excel"; //设置下载的Excel文件名\ if (System.Web.HttpContext.Current.Request.ServerVariables["http_user_agent"].ToString().IndexOf("Firefox") != -1) { //火狐浏览器 System.Web.HttpContext.Current.Response.AddHeader("Content-Disposition", string.Format("attachment;filename={0}", "=?UTF-8?B?" + Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(fileName)) + "?=")); } else { //IE等浏览器 System.Web.HttpContext.Current.Response.AddHeader("Content-Disposition", string.Format("attachment;filename={0}", System.Web.HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8))); } 废话不多说,直接上类库代码,ATNPOIHelper.cs: using System; using System.Linq; using System.Web; using System.IO; using NPOI; using NPOI.SS.Util; using NPOI.HSSF.Util; using NPOI.SS.UserModel; using NPOI.HSSF.UserModel; using System.Data; using System.Collections.Generic; using System.Text; namespace AT.Utility.DotNetFile { /* 导出Excel包含的功能: 1.多表头导出最多支持到三行,表头格式说明 相邻父列头之间用’#’分隔,父列头与子列头用空格(’ ‘)分隔,相邻子列头用逗号分隔(‘,’) 两行:序号#分公司#组别#本日成功签约单数 预警,续约,流失,合计#累计成功签约单数 预警,续约,流失,合计#任务数#完成比例#排名 三行:等级#级别#上期结存 件数,重量,比例#本期调入 收购调入 件数,重量,比例#本期发出 车间投料 件数,重量,比例#本期发出 产品外销百分比 件数,重量,比例#平均值 三行时请注意:列头要重复 2.添加表头标题功能 3.添加序号功能 4.根据数据设置列宽 缺陷: 数据内容不能合并列合并行 改进思路: 添加一属性:设置要合并的列,为了实现多列合并可以这样设置{“列1,列2”,”列4”} */ /// <summary> /// 利用NPOI实现导出Excel /// </summary> public class ATNPOIHelper { #region 初始化 /// <summary> /// 声明 HSSFWorkbook 对象 /// </summary> private static HSSFWorkbook _workbook; /// <summary> /// 声明 HSSFSheet 对象 /// </summary> private static HSSFSheet _sheet; #endregion #region Excel导出 /// <summary> /// Excel导出 /// </summary> /// <param name="fileName">文件名称 如果为空或NULL,则默认“新建Excel.xls”</param> /// <param name="list"></param> /// <param name="ColMergeNum">合计:末行合计时,合并的列数</param> /// <param name="method">导出方式 1:WEB导出(默认)2:按文件路径导出</param> /// <param name="filePath">文件路径 如果WEB导出,则可以为空;如果按文件路径导出,则默认桌面路径</param> public static void Export(string fileName, IList<NPOIModel> list, int ColMergeNum, int method = 1, string filePath = null) { // 文件名称 if (!string.IsNullOrEmpty(fileName)) { if (fileName.IndexOf('.') == -1) { fileName += ".xls"; } else { fileName = fileName.Substring(1, fileName.IndexOf('.')) + ".xls"; } } else { fileName = "新建Excel.xls"; } // 文件路径 if (2 == method && string.IsNullOrEmpty(filePath)) { filePath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop); } // 调用导出处理程序 Export(list, ColMergeNum); // WEB导出 if (1 == method) { System.Web.HttpContext.Current.Response.ContentType = "application/vnd.ms-excel"; //设置下载的Excel文件名\ if (System.Web.HttpContext.Current.Request.ServerVariables["http_user_agent"].ToString().IndexOf("Firefox") != -1) { //火狐浏览器 System.Web.HttpContext.Current.Response.AddHeader("Content-Disposition", string.Format("attachment;filename={0}", "=?UTF-8?B?" + Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(fileName)) + "?=")); } else { //IE等浏览器 System.Web.HttpContext.Current.Response.AddHeader("Content-Disposition", string.Format("attachment;filename={0}", System.Web.HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8))); } using (MemoryStream ms = new MemoryStream()) { //将工作簿的内容放到内存流中 _workbook.Write(ms); //将内存流转换成字节数组发送到客户端 System.Web.HttpContext.Current.Response.BinaryWrite(ms.GetBuffer()); System.Web.HttpContext.Current.Response.End(); _sheet = null; _workbook = null; } } else if (2 == method) { using (FileStream fs = File.Open(filePath, FileMode.Append)) { _workbook.Write(fs); _sheet = null; _workbook = null; } } } /// <summary> /// 导出方法实现 /// </summary> /// <param name="list"></param> private static void Export(IList<NPOIModel> list, int ColMergeNum) { #region 变量声明 // 初始化 _workbook = new HSSFWorkbook(); // 声明 Row 对象 IRow _row; // 声明 Cell 对象 ICell _cell; // 总列数 int cols = 0; // 总行数 int rows = 0; // 行数计数器 int rowIndex = 0; // 单元格值 string drValue = null; #endregion foreach (NPOIModel model in list) { // 工作薄命名 if (model.sheetName != null) _sheet = (HSSFSheet)_workbook.CreateSheet(model.sheetName); else _sheet = (HSSFSheet)_workbook.CreateSheet(); // 获取数据源 DataTable dt = model.dataSource; // 初始化 rowIndex = 0; // 获取总行数 rows = GetRowCount(model.headerName); // 获取总列数 cols = GetColCount(model.headerName); //合计:合并表格末行N列,rows为表头行数,dt.Rows.Count为数据行数 if (ColMergeNum > 1) { CellRangeAddress region_Merge = new CellRangeAddress(rows + dt.Rows.Count, rows + dt.Rows.Count, 0, ColMergeNum - 1); _sheet.AddMergedRegion(region_Merge); } ICellStyle myBodyStyle = bodyStyle; ICellStyle myTitleStyle = titleStyle; ICellStyle myDateStyle = dateStyle; ICellStyle myBodyRightStyle = bodyRightStyle; // 循环行数 foreach (DataRow row in dt.Rows) { #region 新建表,填充表头,填充列头,样式 if (rowIndex == 65535 || rowIndex == 0) { if (rowIndex != 0) _sheet = (HSSFSheet)_workbook.CreateSheet(); // 构建行 for (int i = 0; i < rows + model.isTitle; i++) { _row = _sheet.GetRow(i); // 创建行 if (_row == null) _row = _sheet.CreateRow(i); for (int j = 0; j < cols; j++) _row.CreateCell(j).CellStyle = myBodyStyle; } // 如果存在表标题 if (model.isTitle > 0) { // 获取行 _row = _sheet.GetRow(0); // 合并单元格 CellRangeAddress region = new CellRangeAddress(0, 0, 0, (cols - 1)); _sheet.AddMergedRegion(region); // 填充值 _row.CreateCell(0).SetCellValue(model.tableTitle); // 设置样式 _row.GetCell(0).CellStyle = myTitleStyle; // 设置行高 _row.HeightInPoints = 20; } // 取得上一个实体 NPOIHeader lastRow = null; IList<NPOIHeader> hList = GetHeaders(model.headerName, rows, model.isTitle); // 创建表头 foreach (NPOIHeader m in hList) { var data = hList.Where(c => c.firstRow == m.firstRow && c.lastCol == m.firstCol - 1); if (data.Count() > 0) { lastRow = data.First(); if (m.headerName == lastRow.headerName) m.firstCol = lastRow.firstCol; } // 获取行 _row = _sheet.GetRow(m.firstRow); // 合并单元格 CellRangeAddress region = new CellRangeAddress(m.firstRow, m.lastRow, m.firstCol, m.lastCol); _sheet.AddMergedRegion(region); // 填充值 _row.CreateCell(m.firstCol).SetCellValue(m.headerName); } // 填充表头样式 for (int i = 0; i < rows + model.isTitle; i++) { _row = _sheet.GetRow(i); for (int j = 0; j < cols; j++) { _row.GetCell(j).CellStyle = myBodyStyle; //设置列宽 _sheet.SetColumnWidth(j, (model.colWidths[j] + 1) * 450); } } rowIndex = (rows + model.isTitle); } #endregion #region 填充内容 // 构建列 _row = _sheet.CreateRow(rowIndex); foreach (DataColumn column in dt.Columns) { // 添加序号列 if (1 == model.isOrderby && column.Ordinal == 0) { _cell = _row.CreateCell(0); _cell.SetCellValue(rowIndex - rows); _cell.CellStyle = myBodyStyle; } // 创建列 _cell = _row.CreateCell(column.Ordinal + model.isOrderby); // 获取值 drValue = row[column].ToString(); switch (column.DataType.ToString()) { case "System.String"://字符串类型 _cell.SetCellValue(drValue); _cell.CellStyle = myBodyStyle; break; case "System.DateTime"://日期类型 DateTime dateV; DateTime.TryParse(drValue, out dateV); _cell.SetCellValue(dateV); _cell.CellStyle = myDateStyle;//格式化显示 break; case "System.Boolean"://布尔型 bool boolV = false; bool.TryParse(drValue, out boolV); _cell.SetCellValue(boolV); _cell.CellStyle = myBodyStyle; break; case "System.Int16"://整型 case "System.Int32": case "System.Int64": case "System.Byte": int intV = 0; int.TryParse(drValue, out intV); _cell.SetCellValue(intV); _cell.CellStyle = myBodyRightStyle; break; case "System.Decimal"://浮点型 case "System.Double": double doubV = 0; double.TryParse(drValue, out doubV); _cell.SetCellValue(doubV.ToString("f2")); _cell.CellStyle = myBodyRightStyle; break; case "System.DBNull"://空值处理 _cell.SetCellValue(""); break; default: _cell.SetCellValue(""); break; } } #endregion rowIndex++; } } } #region 辅助方法 /// <summary> /// 表头解析 /// </summary> /// <remarks> /// </remarks> /// <param name="header">表头</param> /// <param name="rows">总行数</param> /// <param name="addRows">外加行</param> /// <param name="addCols">外加列</param> /// <returns></returns> private static IList<NPOIHeader> GetHeaders(string header, int rows, int addRows) { // 临时表头数组 string[] tempHeader; string[] tempHeader2; // 所跨列数 int colSpan = 0; // 所跨行数 int rowSpan = 0; // 单元格对象 NPOIHeader model = null; // 行数计数器 int rowIndex = 0; // 列数计数器 int colIndex = 0; // IList<NPOIHeader> list = new List<NPOIHeader>(); // 初步解析 string[] headers = header.Split(new string[] { "#" }, StringSplitOptions.RemoveEmptyEntries); // 表头遍历 for (int i = 0; i < headers.Length; i++) { // 行数计数器清零 rowIndex = 0; // 列数计数器清零 colIndex = 0; // 获取所跨行数 rowSpan = GetRowSpan(headers[i], rows); // 获取所跨列数 colSpan = GetColSpan(headers[i]); // 如果所跨行数与总行数相等,则不考虑是否合并单元格问题 if (rows == rowSpan) { colIndex = GetMaxCol(list); model = new NPOIHeader(headers[i], addRows, (rowSpan - 1 + addRows), colIndex, (colSpan - 1 + colIndex), addRows); list.Add(model); rowIndex += (rowSpan - 1) + addRows; } else { // 列索引 colIndex = GetMaxCol(list); // 如果所跨行数不相等,则考虑是否包含多行 tempHeader = headers[i].Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries); for (int j = 0; j < tempHeader.Length; j++) { // 如果总行数=数组长度 if (1 == GetColSpan(tempHeader[j])) { if (j == tempHeader.Length - 1 && tempHeader.Length < rows) { model = new NPOIHeader(tempHeader[j], (j + addRows), (j + addRows) + (rows - tempHeader.Length), colIndex, (colIndex + colSpan - 1), addRows); list.Add(model); } else { model = new NPOIHeader(tempHeader[j], (j + addRows), (j + addRows), colIndex, (colIndex + colSpan - 1), addRows); list.Add(model); } } else { // 如果所跨列数不相等,则考虑是否包含多列 tempHeader2 = tempHeader[j].Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries); for (int m = 0; m < tempHeader2.Length; m++) { // 列索引 colIndex = GetMaxCol(list) - colSpan + m; if (j == tempHeader.Length - 1 && tempHeader.Length < rows) { model = new NPOIHeader(tempHeader2[m], (j + addRows), (j + addRows) + (rows - tempHeader.Length), colIndex, colIndex, addRows); list.Add(model); } else { model = new NPOIHeader(tempHeader2[m], (j + addRows), (j + addRows), colIndex, colIndex, addRows); list.Add(model); } } } rowIndex += j + addRows; } } } return list; } /// <summary> /// 获取最大列 /// </summary> /// <param name="list"></param> /// <returns></returns> private static int GetMaxCol(IList<NPOIHeader> list) { int maxCol = 0; if (list.Count > 0) { foreach (NPOIHeader model in list) { if (maxCol < model.lastCol) maxCol = model.lastCol; } maxCol += 1; } return maxCol; } /// <summary> /// 获取表头行数 /// </summary> /// <param name="newHeaders">表头文字</param> /// <returns></returns> private static int GetRowCount(string newHeaders) { string[] ColumnNames = newHeaders.Split(new char[] { '@' }); int Count = 0; if (ColumnNames.Length <= 1) ColumnNames = newHeaders.Split(new char[] { '#' }); foreach (string name in ColumnNames) { int TempCount = name.Split(new char[] { ' ' }).Length; if (TempCount > Count) Count = TempCount; } return Count; } /// <summary> /// 获取表头列数 /// </summary> /// <param name="newHeaders">表头文字</param> /// <returns></returns> private static int GetColCount(string newHeaders) { string[] ColumnNames = newHeaders.Split(new char[] { '@' }); int Count = 0; if (ColumnNames.Length <= 1) ColumnNames = newHeaders.Split(new char[] { '#' }); Count = ColumnNames.Length; foreach (string name in ColumnNames) { int TempCount = name.Split(new char[] { ',' }).Length; if (TempCount > 1) Count += TempCount - 1; } return Count; } /// <summary> /// 列头跨列数 /// </summary> /// <remarks> /// </remarks> /// <param name="newHeaders">表头文字</param> /// <returns></returns> private static int GetColSpan(string newHeaders) { return newHeaders.Split(',').Count(); } /// <summary> /// 列头跨行数 /// </summary> /// <remarks> /// </remarks> /// <param name="newHeaders">列头文本</param> /// <param name="rows">表头总行数</param> /// <returns></returns> private static int GetRowSpan(string newHeaders, int rows) { int Count = newHeaders.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries).Length; // 如果总行数与当前表头所拥有行数相等 if (rows == Count) Count = 1; else if (Count < rows) Count = 1 + (rows - Count); else throw new Exception("表头格式不正确!"); return Count; } #endregion #region 单元格样式 /// <summary> /// 数据单元格样式 /// </summary> private static ICellStyle bodyStyle { get { ICellStyle style = _workbook.CreateCellStyle(); style.Alignment = HorizontalAlignment.CENTER; //居中 style.VerticalAlignment = VerticalAlignment.CENTER;//垂直居中 style.WrapText = true;//自动换行 // 边框 style.BorderBottom = BorderStyle.THIN; style.BorderLeft = BorderStyle.THIN; style.BorderRight = BorderStyle.THIN; style.BorderTop = BorderStyle.THIN; // 字体 //IFont font = _workbook.CreateFont(); //font.FontHeightInPoints = 10; //font.FontName = "宋体"; //style.SetFont(font); return style; } } /// <summary> /// 数据单元格样式 /// </summary> private static ICellStyle bodyRightStyle { get { ICellStyle style = _workbook.CreateCellStyle(); style.Alignment = HorizontalAlignment.RIGHT; //居中 style.VerticalAlignment = VerticalAlignment.CENTER;//垂直居中 style.WrapText = true;//自动换行 // 边框 style.BorderBottom = BorderStyle.THIN; style.BorderLeft = BorderStyle.THIN; style.BorderRight = BorderStyle.THIN; style.BorderTop = BorderStyle.THIN; // 字体 //IFont font = _workbook.CreateFont(); //font.FontHeightInPoints = 10; //font.FontName = "宋体"; //style.SetFont(font); return style; } } /// <summary> /// 标题单元格样式 /// </summary> private static ICellStyle titleStyle { get { ICellStyle style = _workbook.CreateCellStyle(); style.Alignment = HorizontalAlignment.CENTER; //居中 style.VerticalAlignment = VerticalAlignment.CENTER;//垂直居中 style.WrapText = true;//自动换行 //IFont font = _workbook.CreateFont(); //font.FontHeightInPoints = 14; //font.FontName = "宋体"; //font.Boldweight = (short)FontBoldWeight.BOLD; //style.SetFont(font); return style; } } /// <summary> /// 日期单元格样式 /// </summary> private static ICellStyle dateStyle { get { ICellStyle style = _workbook.CreateCellStyle(); style.Alignment = HorizontalAlignment.CENTER; //居中 style.VerticalAlignment = VerticalAlignment.CENTER;//垂直居中 style.WrapText = true;//自动换行 // 边框 style.BorderBottom = BorderStyle.THIN; style.BorderLeft = BorderStyle.THIN; style.BorderRight = BorderStyle.THIN; style.BorderTop = BorderStyle.THIN; // 字体 //IFont font = _workbook.CreateFont(); //font.FontHeightInPoints = 10; //font.FontName = "宋体"; //style.SetFont(font); IDataFormat format = _workbook.CreateDataFormat(); style.DataFormat = format.GetFormat("yyyy-MM-dd"); return style; } } #endregion #endregion } /// <summary> /// 实体类 /// </summary> public class NPOIModel { /// <summary> /// 数据源 /// </summary> public DataTable dataSource { get; private set; } /// <summary> /// 要导出的数据列数组 /// </summary> public string[] fileds { get; private set; } /// <summary> /// 工作薄名称数组 /// </summary> public string sheetName { get; private set; } /// <summary> /// 表标题 /// </summary> public string tableTitle { get; private set; } /// <summary> /// 表标题是否存在 1:存在 0:不存在 /// </summary> public int isTitle { get; private set; } /// <summary> /// 是否添加序号 /// </summary> public int isOrderby { get; private set; } /// <summary> /// 表头 /// </summary> public string headerName { get; private set; } /// <summary> /// 取得列宽 /// </summary> public int[] colWidths { get; private set; } /// <summary> /// 构造函数 /// </summary> /// <remarks> /// </remarks> /// <param name="dataSource">数据来源 DataTable</param> /// <param name="filed">要导出的字段,如果为空或NULL,则默认全部</param> /// <param name="sheetName">工作薄名称</param> /// <param name="headerName">表头名称 如果为空或NULL,则默认数据列字段 /// 相邻父列头之间用'#'分隔,父列头与子列头用空格(' ')分隔,相邻子列头用逗号分隔(',') /// 两行:序号#分公司#组别#本日成功签约单数 预警,续约,流失,合计#累计成功签约单数 预警,续约,流失,合计#任务数#完成比例#排名 /// 三行:等级#级别#上期结存 件数,重量,比例#本期调入 收购调入 件数,重量,比例#本期发出 车间投料 件数,重量,比例#本期发出 产品外销百分比 件数,重量,比例#平均值 /// 三行时请注意:列头要重复 /// </param> /// <param name="tableTitle">表标题</param> /// <param name="isOrderby">是否添加序号 0:不添加 1:添加</param> public NPOIModel(DataTable dataSource, string filed, string sheetName, string headerName, string tableTitle = null, int isOrderby = 0) { if (!string.IsNullOrEmpty(filed)) { this.fileds = filed.ToUpper().Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries); // 移除多余数据列 for (int i = dataSource.Columns.Count - 1; i >= 0; i--) { DataColumn dc = dataSource.Columns[i]; if (!this.fileds.Contains(dataSource.Columns[i].Caption.ToUpper())) { dataSource.Columns.Remove(dataSource.Columns[i]); } } // 列索引 int colIndex = 0; // 循环排序 for (int i = 0; i < dataSource.Columns.Count; i++) { // 获取索引 colIndex = GetColIndex(dataSource.Columns[i].Caption.ToUpper()); // 设置下标 dataSource.Columns[i].SetOrdinal(colIndex); } } else { this.fileds = new string[dataSource.Columns.Count]; for (int i = 0; i < dataSource.Columns.Count; i++) { this.fileds[i] = dataSource.Columns[i].ColumnName; } } this.dataSource = dataSource; if (!string.IsNullOrEmpty(sheetName)) { this.sheetName = sheetName; } if (!string.IsNullOrEmpty(headerName)) { this.headerName = headerName; } else { this.headerName = string.Join("#", this.fileds); } if (!string.IsNullOrEmpty(tableTitle)) { this.tableTitle = tableTitle; this.isTitle = 1; } // 取得数据列宽 数据列宽可以和表头列宽比较,采取最长宽度 colWidths = new int[this.dataSource.Columns.Count]; foreach (DataColumn item in this.dataSource.Columns) { colWidths[item.Ordinal] = Encoding.GetEncoding(936).GetBytes(item.ColumnName.ToString()).Length; } // 循环比较最大宽度 for (int i = 0; i < this.dataSource.Rows.Count; i++) { for (int j = 0; j < this.dataSource.Columns.Count; j++) { int intTemp = Encoding.GetEncoding(936).GetBytes(this.dataSource.Rows[i][j].ToString()).Length; if (intTemp > colWidths[j]) { colWidths[j] = intTemp; } } } if (isOrderby > 0) { this.isOrderby = isOrderby; this.headerName = "序号#" + this.headerName; } } /// <summary> /// 获取列名下标 /// </summary> /// <param name="colName">列名称</param> /// <returns></returns> private int GetColIndex(string colName) { for (int i = 0; i < this.fileds.Length; i++) { if (colName == this.fileds[i]) return i; } return 0; } } /// <summary> /// 表头构建类 /// </summary> public class NPOIHeader { /// <summary> /// 表头 /// </summary> public string headerName { get; set; } /// <summary> /// 起始行 /// </summary> public int firstRow { get; set; } /// <summary> /// 结束行 /// </summary> public int lastRow { get; set; } /// <summary> /// 起始列 /// </summary> public int firstCol { get; set; } /// <summary> /// 结束列 /// </summary> public int lastCol { get; set; } /// <summary> /// 是否跨行 /// </summary> public int isRowSpan { get; private set; } /// <summary> /// 是否跨列 /// </summary> public int isColSpan { get; private set; } /// <summary> /// 外加行 /// </summary> public int rows { get; set; } public NPOIHeader() { } /// <summary> /// 构造函数 /// </summary> /// <param name="headerName">表头</param> /// <param name="firstRow">起始行</param> /// <param name="lastRow">结束行</param> /// <param name="firstCol">起始列</param> /// <param name="lastCol">结束列</param> /// <param name="rows">外加行</param> /// <param name="cols">外加列</param> public NPOIHeader(string headerName, int firstRow, int lastRow, int firstCol, int lastCol, int rows = 0) { this.headerName = headerName; this.firstRow = firstRow; this.lastRow = lastRow; this.firstCol = firstCol; this.lastCol = lastCol; // 是否跨行判断 if (firstRow != lastRow) isRowSpan = 1; if (firstCol != lastCol) isColSpan = 1; this.rows = rows; } } } 3、导出代码示例如下: /// <summary> /// 导出测点列表表格 /// </summary> [HttpGet] [AllowAnonymous] public void ExportMeasurePointData(string TreeID, string TreeType) { DataTable dtResult = new DataTable(); DataTable dtExcel = new DataTable(); try { string sql = string.Format("EXEC P_GET_ZXJG_TagList '{0}','{1}'", TreeID, TreeType); dtResult = QuerySQL.GetDataTable(sql); dtExcel = dtResult.Copy(); dtExcel.Columns.Add("xuhao", typeof(string)); dtExcel.Columns.Add("StrValueTime", typeof(string)); dtExcel.Columns["xuhao"].SetOrdinal(0); dtExcel.Columns["StrValueTime"].SetOrdinal(2); for (int i = 0; i < dtResult.Rows.Count; i++) { dtExcel.Rows[i]["xuhao"] = (i + 1).ToString(); dtExcel.Rows[i]["StrValueTime"] = Convert.ToDateTime(dtResult.Rows[i]["F_ValueTime"]).ToString("yyyy-MM-dd HH:mm:ss"); } List<NPOIModel> list = new List<NPOIModel>(); list.Add(new NPOIModel(dtExcel, "xuhao;F_Description;StrValueTime;F_Value;F_Unit;F_AlmLow;F_AlmUp", "sheet", "序号#监测点#采集时间#当前数值#工程单位#报警下限#报警上限")); ATNPOIHelper.Export("测点列表", list, 0); } catch (Exception ex) { } }
1、Controller中的方法代码如下: 由于方法中的存储过程没有带分页参数,所以还可以有继续优化的空间。 /// <summary> /// 获取测点列表 /// </summary> /// <returns></returns> [HttpPost] public JsonResult GetMeasurePointList(string TreeID, string TreeType, int sEcho, int iDisplayStart, int iDisplayLength) { DataTable dtResult = new DataTable(); string sql = string.Format("EXEC P_GET_ZXJG_TagList '{0}','{1}'", TreeID, TreeType); dtResult = QuerySQL.GetDataTable(sql); dtResult.Columns.Add("XuHao", typeof(string)); dtResult.Columns.Add("StrValueTime", typeof(string)); for (int i = 0; i < dtResult.Rows.Count; i++) { dtResult.Rows[i]["XuHao"] = (i + 1).ToString(); dtResult.Rows[i]["StrValueTime"] = Convert.ToDateTime(dtResult.Rows[i]["F_ValueTime"]).ToString("yyyy-MM-dd HH:mm:ss"); } int iTotalRecords = 0; int iTotalDisplayRecords = 0; List<DataRow> queryList = dtResult.AsEnumerable().ToList(); iTotalRecords = queryList.Count(); queryList = queryList.Skip(iDisplayStart).Take(iDisplayLength).ToList(); iTotalDisplayRecords = queryList.Count(); var temp = from p in queryList select new { XuHao = p.Field<string>("XuHao").ToString(), F_Description = p.Field<string>("F_Description").ToString(), StrValueTime = p.Field<string>("StrValueTime").ToString(), F_Value = p.Field<decimal>("F_Value").ToString(), F_Unit = p.Field<string>("F_Unit").ToString(), F_AlmLow = p.Field<decimal>("F_AlmLow").ToString(), F_AlmUp = p.Field<decimal>("F_AlmUp").ToString() }; return Json(new { draw = sEcho, recordsFiltered = iTotalRecords, recordsTotal = iTotalDisplayRecords, data = temp.ToList() }, JsonRequestBehavior.AllowGet); } 2、cshtml视图页面中代码如下: function InitData() { var dataTable = $('#tbMeasurePointList').DataTable({ "scrollY": "hidden", "scrollCollapse": false, "dom": 'tr<"bottom"lip><"clear">', language: { lengthMenu: '',//左上角的分页大小显示。 search: '<span class="label label-success">搜索:</span>',//右上角的搜索文本,可以写html标签 loadingRecords: '数据加载中...', paginate: { //分页的样式内容。 previous: "上一页", next: "下一页", first: "", last: "" }, zeroRecords: "暂无数据",//table tbody内容为空时,tbody的内容。 //下面三者构成了总体的左下角的内容。 info: "<span class='pagesStyle'>总共<span class='recordsStyle'> _TOTAL_ 条,计 _PAGES_ </span>页,当前显示 _START_ -- _END_ 条记录 </span>",//左下角的信息显示,大写的词为关键字。初始_MAX_ 条 infoEmpty: "0条记录",//筛选为空时左下角的显示。 infoFiltered: ""//筛选之后的左下角筛选提示, }, "lengthChange": false, "ordering": false, "iDisplayLength": 10, "searching": false, destroy: true, //Cannot reinitialise DataTable,解决重新加载表格内容问题 "serverSide": true, "sAjaxSource": "@Url.Action("GetMeasurePointList", "OnlineMonitor")", "fnServerData": function (sSource, aoData, fnCallback) { aoData.push({ "name": "TreeID", "value": $("#hidTreeID").val() }); aoData.push({ "name": "TreeType", "value": $("#hidTreeType").val() }); $.ajax({ "dataType": 'json', "type": "POST", "url": sSource, "data": aoData, "success": fnCallback }); }, "aoColumns": [ { "mDataProp": "XuHao", "width": "50" }, { "mDataProp": "F_Description", "width": "400" }, { "mDataProp": "StrValueTime", "width": "200" }, { "mDataProp": "F_Value", "width": "100" }, { "mDataProp": "F_Unit", "width": "100" }, { "mDataProp": "F_AlmLow", "width": "100" }, { "mDataProp": "F_AlmUp", "width": "100"} ], "createdRow": function (row, data, index) { $(row).children('td').eq(0).attr('style', 'text-align: center;'); $(row).children('td').eq(1).attr('style', 'text-align: left;'); $(row).children('td').eq(2).attr('style', 'text-align: center;'); $(row).children('td').eq(3).attr('style', 'text-align: right;'); $(row).children('td').eq(4).attr('style', 'text-align: center;'); $(row).children('td').eq(5).attr('style', 'text-align: right;'); $(row).children('td').eq(6).attr('style', 'text-align: right;'); } }); } 3、实际显示效果如下图所示:
1、WorkFlowEngine https://workflowengine.io/downloads/
有人说,人生之难胜过逆水行舟,人的这一辈子在世,不如意的事情占十之八九,拥有和舍弃的矛盾常常困扰着我们…人生中会有许多的东西值得留恋,也有很多东西你应该学会去放弃。 1、放下压力——累与不累,取决于自己的心态 心灵的房间,不打扫就会落满灰尘。蒙尘的心,会变得灰色和迷茫。我们每天都要经历很多事情,开心的,不开心的,都在心里安家落户。心里的事情一多,就会变得杂乱无序,然后心也跟着乱起来。有些痛苦的情绪和不愉快的记忆,如果充斥在心里,就会使人委靡不振。所以,扫地除尘,能够使黯然的心变得亮堂;把事情理清楚,才能告别烦乱;把一些无谓的痛苦扔掉,快乐就有了更多更大的空间。紧紧抓住不快乐的理由,无视快乐的理由,就是你总是觉得难受的原因了。 2、放下烦扰——快乐其实很简单 所谓练习微笑,不是机械地挪动你的面部表情,而是努力地改变你的心态,调节你的心情。学会平静地接受现实,学会对自己说声顺其自然,学会坦然地面对厄运,学会积极地看待人生,学会凡事都往好处想。这样,阳光就会流进心里来,驱走恐惧,驱走黑暗,驱走所有的阴霾。快乐其实很简单,不要自己不快乐就可以了。 3、放下自卑——把自卑从你的字典里删去 不是每个人都可以成为伟人,但每个人都可以成为内心强大的人。内心的强大,能够稀释一切痛苦和哀愁;内心的强大,能够有效弥补你外在的不足;内心的强大,能够让你无所畏惧地走在大路上,感到自己的思想,高过所有的建筑和山峰!相信自己,找准自己的位置,你同样可以拥有一个有价值的人生。 4、放下懒惰——奋斗改变命运 不要一味地羡慕人家的绝活与绝招,通过恒久的努力,你也完全可以拥有。因为,把一个简单的动作练到出神入化,就是绝招;把一件平凡的小事做到炉火纯青,就是绝活。提醒自己,记住自己的提醒,上进的你,快乐的你,健康的你,善良的你,一定会有一个灿烂的人生。 5、放下消极——绝望向左,希望向右 如果你想成为一个成功的人,那么,请为“最好的自己”加油吧,让积极打败消极,让真诚打败虚伪,让宽容打败褊狭,让坚强打败脆弱,让伟大打败猥琐……只要你愿意,你完全可以一辈子都做最好的自己。没有谁能够左右胜负,除了你。 6、放下抱怨——与其抱怨,不如努力 所有的失败都是为成功做准备。抱怨和泄气,只能阻碍成功向自己走来的步伐。放下抱怨,心平气和地接受失败,无疑是智者的姿态。抱怨无法改变现状,拼搏才能带来希望。真的金子,只要自己不把自己埋没,只要一心想着闪光,就总有闪光的那一天。纵观古今中外,很多人生的奇迹,都是那些最初拿了一手坏牌的人创造的。不要总是烦恼生活。不要总以为生活辜负了你什么,其实,你跟别人拥有的一样多。 7、放下犹豫——立即行动,成功无限 认准了的事情,不要优柔寡断;选准了一个方向,就只管上路,不要回头。机遇就像闪电,只有快速果断才能将它捕获。立即行动是所有成功人士共同的特质。如果你有什么好的想法,那就立即行动吧;如果你遇到了一个好的机遇,那就立即抓住吧。立即行动,成功无限!有些人是必须忘记的,有些事是用来反省的,有些东西是不能不清理的。该放手时就放手,你才可以腾出手来,抓住原本属于你的快乐和幸福!有些事情是不能等待的,一时的犹豫,留下的将是永远的遗憾! 8、放下狭隘——心宽,天地就宽 宽容是一种美德。宽容别人,其实也是给自己的心灵让路。只有在宽容的世界里,人,才能奏出和谐的生命之歌!要想没有偏见,就要创造一个宽容的社会。要想根除偏见,就要首先根除狭隘的思想。只有远离偏见,才有人与内心的和谐,人与人的和谐,人与社会的和谐。我们不但要自己快乐,还要把自己的快乐分享给朋友、家人甚至素不相识的陌生人。因为分享快乐本身就是一种快乐,一种更高境界的快乐。
在MIS系统开发中,有时需要在主页顶部显示天气信息,本篇经验将和大家介绍一下实现过程。 1、一个可以获取各个城市天气预报情况的网站: http://wthrcdn.etouch.cn/weather_mini?city=合肥 参数city的值根据你所在的城市相应修改,获取到的气象信息如下图所示: JSON字符串如下: { "desc": "OK", "status": 1000, "data": { "wendu": "25", "ganmao": "各项气象条件适宜,无明显降温过程,发生感冒机率较低。", "forecast": [ { "fengxiang": "南风", "fengli": "3-4级", "high": "高温 27℃", "type": "晴", "low": "低温 15℃", "date": "14日星期五" }, { "fengxiang": "东风", "fengli": "微风级", "high": "高温 28℃", "type": "多云", "low": "低温 18℃", "date": "15日星期六" }, { "fengxiang": "东南风", "fengli": "3-4级", "high": "高温 25℃", "type": "中雨", "low": "低温 16℃", "date": "16日星期天" }, { "fengxiang": "西风", "fengli": "3-4级", "high": "高温 25℃", "type": "多云", "low": "低温 20℃", "date": "17日星期一" }, { "fengxiang": "西北风", "fengli": "3-4级", "high": "高温 27℃", "type": "晴", "low": "低温 15℃", "date": "18日星期二" } ], "yesterday": { "fl": "3-4级", "fx": "南风", "high": "高温 24℃", "type": "晴", "low": "低温 13℃", "date": "13日星期四" }, "aqi": "73", "city": "合肥" } } 附一个可以用于验证JSON字符串正确性的网址:http://www.bejson.com/ 2、打开json2csharp网站,网站网址为:http://json2csharp.com/,将以上JSON串复制到编辑框中,然后点击“Generate”按钮,生成实体类,如下图所示: 3、C#代码实现,核心代码如下: using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace CommonClass { public class Forecast { public string fengxiang { get; set; } public string fengli { get; set; } public string high { get; set; } public string type { get; set; } public string low { get; set; } public string date { get; set; } } public class Yesterday { public string fl { get; set; } public string fx { get; set; } public string high { get; set; } public string type { get; set; } public string low { get; set; } public string date { get; set; } } public class Data { public string wendu { get; set; } public string ganmao { get; set; } public List<Forecast> forecast { get; set; } public Yesterday yesterday { get; set; } public string aqi { get; set; } public string city { get; set; } } public class RootObject { public string desc { get; set; } public int status { get; set; } public Data data { get; set; } } } Json2Entities.cs代码如下:using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.Serialization.Json; using System.Text; using System.Web; namespace CommonClass { public class Json2Entities { /// <summary> /// 将json字符串转化为方法实体类 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="jsonString"></param> /// <returns></returns> public static T JsonToObject<T>(string jsonString) { DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(T)); MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(jsonString)); T jsonObject = (T)ser.ReadObject(ms); ms.Close(); return jsonObject; } } } //用UTF-8转码有汉字参数的url protected string UrlUtf_8(string url) { byte[] bs = Encoding.GetEncoding("UTF-8").GetBytes(url); StringBuilder sb = new StringBuilder(); for (int i = 0; i < bs.Length; i++) { if (bs[i] < 128) sb.Append((char)bs[i]); else { sb.Append("%" + bs[i++].ToString("x").PadLeft(2, '0')); sb.Append("%" + bs[i].ToString("x").PadLeft(2, '0')); } } return sb.ToString(); } //用UrlGB2312转码有汉字参数的url protected string UrlGB2312(string url) { byte[] bs = Encoding.GetEncoding("GB2312").GetBytes(url); StringBuilder sb = new StringBuilder(); for (int i = 0; i < bs.Length; i++) { if (bs[i] < 128) sb.Append((char)bs[i]); else { sb.Append("%" + bs[i++].ToString("x").PadLeft(2, '0')); sb.Append("%" + bs[i].ToString("x").PadLeft(2, '0')); } } return sb.ToString(); } /// <summary> /// /// </summary> /// <param name="url"></param> /// <returns></returns> public string GetFunction(string url) { string serviceAddress = url; //HttpWebRequest类继承于WebRequest,并没有自己的构造函数,需通过WebRequest的Creat方法 建立,并进行强制的类型转换 HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); request.ContentType = "text/html;charset=UTF-8"; request.Method = "GET"; //通过HttpWebRequest的GetResponse()方法建立HttpWebResponse,强制类型转换 HttpWebResponse response = (HttpWebResponse)request.GetResponse(); //GetResponseStream()方法获取HTTP响应的数据流,并尝试取得URL中所指定的网页内容 Stream myResponseStream = response.GetResponseStream(); //站点在响应的时候用gzip压缩过了,用using (var gStream = new GZipStream(myResponseStream, CompressionMode.Decompress)){}解压 using (var gStream = new GZipStream(myResponseStream, CompressionMode.Decompress)) { //Encoding.GetEncoding("UTF-8")转码 StreamReader myStreamReader = new StreamReader(gStream, Encoding.GetEncoding("UTF-8")); string retString = myStreamReader.ReadToEnd(); myStreamReader.Close(); response.Close(); myResponseStream.Close(); return retString; } } string url = "http://wthrcdn.etouch.cn/weather_mini?city=合肥"; string info = GetFunction(url); RootObject rootObj = new RootObject(); rootObj = Json2Entities.JsonToObject<RootObject>(info); string weather = rootObj.data.city + " " + rootObj.data.forecast[0].type + " " + rootObj.data.forecast[0].low.Replace("低温", "") + " ~ " + rootObj.data.forecast[0].high.Replace("高温", ""); 4、实现效果如下图所示:
1、http://www.cnblogs.com/Chilam007/category/916164.html
1、 制定计划,让计划成为习惯,在做具体计划之前,需要做好必要的准备工作。 2、 转变思维习惯和行为习惯。 3、 以目标为导向,关注结果。 4 、从整体和全局出发,具有全局思维,多角度看问题,注意细节,分清轻重缓急。 5、 聚焦有点,尊重,换位思考。 6 、集思广益,凝聚团队力量,善于整合团队资源,身先士卒。 7 、信任团队,用人不疑,建立团队共同价值观,共同利益和共同目标。 8 、有效沟通,沟通原则和沟通要领,做好服务,领导力、影响力。 9 、规章制度,系统化、流程化,人文关怀。 10 、承上启下,贯彻执行。 11、 任务分派,绩效考核。 12、 激励,授权,团队建设。
驱动你做一件事情的动力来源是什么?一般来说动力来源有两种。 一种是生存本能带来的驱动力,即生物性驱动力。比如每天要吃饭、睡觉、上厕所,长大了要恋爱、结婚等。这种驱动力保证我们能够生存在这个世界上,是一种原始的驱动力。 另一种是奖励或者惩罚带来的驱动力,即外在动机。比如老板承诺工作干的好有奖金,我们就努力干活。如果上班迟到会罚款,我们会早起等。这种驱动力会改变人的行为,使其多做鼓励的事情,少做禁止的事情。 如果只依靠这两种驱动力,那么你的人生会变得精彩吗?很难。为什么呢?第一种驱动力只解决了你的生存问题;而第二种驱动力则约束了你的行为。你不想上班,但不得不上班。一旦你做某件事情获得了奖励,你就会对奖励上瘾。而当你认为你得到的奖励与你的付出不成正比时,你的行为就会松懈下来。 要想保持第二种驱动力长期有效,那么就需要管理者具有相当高的水平,比如确保内部公平和外部公平,报酬要高于平均水平,考核标准、衡量因素要广。这样的的公司和领导可遇而不可求。 那么除了这两种驱动力,还有没有第三种驱动力呢?在《驱动力》一书中,作者指出了第三种驱动力,那就是内在动机,即完成某件事情带来的成就感和愉悦感。比如你玩英雄联盟,完成了5杀,比吃了一顿满汉全席还爽。再比如你去跑步,第一次不间断跑了10公里,这种超越自己的感觉肯定会非常奇妙。你修复了一个存在已久的软件故障,那么今天就会感觉特别充实。这种没有人主动给你奖励或惩罚的事情你却干的津津有味。为了练好英雄联盟中的大局观,你研习了数千盘比赛录像。为了能够跑的更远,你查阅了很多跑步相关的教程、书籍。为了修复你遇到的bug,你不惜来搜集相关资料。 如果你能驾驭这第三种驱动力,那么恭喜你,你的人生会一直前进。 那么如何驾驭这第三种驱动力呢?作为程序员,你首先要问自己一个问题:“我真的喜欢编程吗?我能确定我当程序员不仅是为了养家糊口,而是为了实现自己的人生价值吗?” 这一点非常重要,它对你否能利用第三种驱动力有着决定性作用。如果你的答案是为了养家糊口,那么很可能你成为不了一个优秀的程序员,而且在这条路上你越走越疲倦;如果你的答案是真心喜欢编程,那也很可能说明不了什么问题,除非你每天都在主动地学习和进步。 在编程的路上想要利用第三种驱动力,请先忘却它可能给你带来的奖励,而是切实的学习知识,充实自己,奖励只是你进步途中的副产品。如果你做一件事情前先考虑它有没有价值,会不会给你带来收益,那么你还是在利用第二种驱动力。你在当时的环境中很难看清楚一件事情对以后有没有价值。 举个例子,AngularJS刚面世的时候,我一个同事对其非常感兴趣,研读了很多相关的资料,并积极试用,虽然那时候它极不成熟。在那个时间点其实看不出来它会带来什么收益,说不定过几个月AngularJS框架就会销声匿迹。但是几年下来,我这个同事已经是国内AngularJS的专家了,在社区里影响力很大,还发表了关于AngularJS的原创书籍。 第三种驱动力,内在动力,其实就是自主性。当你做些不是工作范畴之内的事情时,你反而离成功更近。再拿编程来举例,很多技术大牛在上班时间外,会写一些博客,参加一些社区聚会,翻译或者写书。做这些事情都是靠内在动力驱动的。所以他们成了大牛,而按部就班的程序员依然是普通的程序员。当你自发想做一些事情时,你无疑已经领先了很多人一大步,因为这些人都在原地踏步。 第三种驱动力是一种良性循环,很容易就可以把人变得乐观,积极向上,感觉生活充满希望。善于驾驭这种能力的人会在生活和工作中都使用它,把工作和生活统一起来,不再相互冲突。比如我平时喜欢跑步,从一次只能10公里,渐渐一次可以跑15公里、20公里、42公里….直到上周花了17小时跑了72公里山路,本来计划跑100公里的,但是膝盖受伤,不得不终止。没人强迫我这样跑,我跑完了也没有任何金钱上的奖励,而且跑步本身其实既枯燥又花钱。我喜欢跑,是因为想知道自己的极限在哪里,这就是一种挑战自我的精神。我不和别人比,只和自己比。 只要能证明今天的自己比昨天的自己更优秀,那么今天就是成功的一天。这种精神也会传染到我的工作中。工作中再大的困难都不算什么,别人能做到我也能。即使现在做不到,那么也会主动的寻找差距去弥补,去提升。如果你能从早上6点跑到第二天早上8点,那么你会发现很多以前对你来说不可能的事情都不再那么困难。 我们经常听说一些名人每天工作十多个小时还坚持健身、运动,这就是第三种驱动力的力量,即内在动机。这种驱动力不仅要用在工作上,更要用在生活上。这样生活和工作更容易成为统一体,每天才能充满阳光和斗志。
管理从来都没有固定的模式,因人,因环境而异。现代知识分子,特别是在软件开发领域,对于人员的管理更是一件不容易的事情。 管理学的创始人德鲁克在上世纪就提出,对于知识分子不能像工人那样那去管理,不能依照旧的上下级的管理方式或是军队那种严格的管理方式。对于这些思想敏锐,骨子里有着像文人一样的傲气,追求自由开放的环境的人,你不能单单去管理他们,而是要领导他们,你不是作为一个经理,而是要作为导师。这时候个人的魅力比实际的权力更重要。 我个人的经验来看,不管你是Team Leader,SA 还是 PM,要管理好你的团队,以下几点非常重要: 1. 你在技术方面要有过人之处,通常team leader, SA 肯定是技术出身,且技术还很牛,而PM则不一定但是PM也不能完全不懂技术,你至少在项目管理方面要比较强。做技术的人大部分都只会佩服懂得比自己多的人。 2. 你需要经常和你的队员沟通,打成一片,想象一下一个PM,一个星期不见人,一个月没一起开过会,让人感觉高高在上,迷一样的人物,那么你的团队怎么有凝聚力,他们如何找到自己的方向和归属感。 3. 你可能是某方面的专家或是技术研究很深入,当然也有可能你没有专于某一方面,而是技术发展很全面,有这些为基础你就必需要乐于助人,你不仅仅是要管理他们,你还要帮助你的团队成员,引导他们去完成目标。你不能只是管理者,你要更像一个导师,有必要经常给他们做一些培训,技术方面的或是管理方面的,你给他们的印象应该是强人,乐于帮助他们,你还是老师会教他们很多东西,你和他们站在一起而不是高高在上。但是同时你还要注意,既然是老师,你也不能毫无威严,对于有些对工作不负责任的人你也必需要拿出你的威严来。 4. 你要真心的为你的团队成员好,为他们争取更多的利益,当然这是在不损害集体的利益的大前提下。你应该多为他们着想,他们是你的人,你不能踩着他们往上爬。你要让他们感觉到你是可以依靠的,为你努力工作是值得的。 5. 去了解他们,也去理解他们,常言到士为知己者死,女为悦已者容,你要发现他们的优点,引导他们放大他们的优点和长项,不要老盯着他们犯的错误,谁不犯错,没有错怎么成长。你要做的应该是更多的去发掘他们的长处。你更加要相信他们,不能老觉得他们什么事情都做不好,什么事情都要自己去做才放心,如果是这样,谁愿意为你卖命,你自己也会累死。 我见到一些人他们没有很多的权力,职位也不是很高,可是他们的人格魅力帮助他们获得更多的尊重和人气。一个SA说的话比PM和经理的话甚至更管用。要做到这些不容易,很多技术出身的人都想做架构师,但是架构师在一个项目里面地位虽然高,但是是没有多少实际的管理权力和行政权力的,致力于成为架构师的人更需要努力提高自己的个人修养和人格魅力,需要不断的学习和读书,增加自己的知识面。
1、Leangoohttps://www.leangoo.com/kanban/board_list2、看板在项目中的应用,可参考以下博客介绍http://blog.csdn.net/tkchen/article/details/51637643 软件开发的项目经理通常需要权衡使用什么样的高效工具、如何制定项目进程计划表等等的关键因素。敏捷工具的合适选用对开发项目起到推波助澜、事半功倍的效用! Leangoo拥有看板式的协作方式,简洁直观,能够轻松拖拽任务卡和任务列表,并在团队成员间实时同步看板变化。同时它简洁实用的功能使它比其他软件更轻量,能让团队在5分钟内协作起来,无需耗费大量的学习和使用成本。它的免费、无成员和项目数量的限制更让广大团队协作没有了后顾之忧。所以,作为一款资深敏捷团队精心打造的团队协作工具,leangoo一经上线就吸引了众多用户的支持和好评。
在学习完前面系列MySQL工具安装配置后,本篇博客将介绍利用MySQL工具之SQLYog进行SQL Server 2008 R2数据库数据导入到MySQL数据库表中。 1、导入外部数据向导,如下图所示: 2、开始新工作,点击“下一步”,如下图所示: 3、建立新的DSN,如下图所示: 4、勾选“文件数据源”,点击“下一步”,如下图所示: 5、选择“SQL Server” ,点击“下一步”,如下图所示: 6、输入数据源路径及名称,点击“下一步”,如下图所示: 7、点击完成,点击“下一步”,如下图所示: 8、输入服务器IP地址,点击“下一步”,如下图所示: 9、输入登录ID和密码,点击“下一步”,如下图所示: 10、勾选“更改默认的数据库为” ,点击“下一步”,如下图所示: 11、点击“完成”,点击“下一步”,如下图所示: 12、点击“测试数据源”,点击“下一步”,如下图所示: 13、测试成功,点击“确定”,如下图所示: 14、输入数据源凭据,点击“下一步”,如下图所示: 15、选择文件DSN,浏览到先前创建的dsn文件,然后点击“下一步”,如下图所示: 16、输入MySQL信息,点击“下一步”,如下图所示: 17、选择从数据源拷贝数据,点击“下一步”,如下图所示: 18、选择源和目的表(表结构,表名称要完全一致),然后点击“下一步”,如下图所示: 19、默认出错错误处理,点击“下一步”,如下图所示: 20、何时运行任务,点击“下一步”,如下图所示: 21、正在完成数据库导入,点击“下一步”,如下图所示: 22、向导成功完成,点击“完成”,如下图所示: 23、在SQLyog中可以看到已经导入到MySQL数据库表中的数据了,如下图所示: 至此,关于SQL Server 2008 R2中的数据库表数据导入到MySQL数据库表中的操作步骤已经详细介绍完毕了。
一、安装文件包下载 相关MySQL的安装包和工具,可以免积分到CSDN下载栏目下载,地址为:http://download.csdn.net/detail/taomanman/9642852。 二、MySQL工具之SQLYog安装配置 1、下载以上链接文件,找到SQLyog-11.3.3-0.x64包,进行解压,如下图所示: 2、下载文件后,双击安装包,如下图所示: 3、安装语言,选择简体中文,如下图所示: 4、单击“下一步”,如下图所示: 5、选择“我接受许可证协议中的条款”,单击“下一步”,如下图所示: 6、单击“下一步”,如下图所示: 7、选择安装位置,单击“安装”,如下图所示: 8、安装过程如下图所示: 9、单击“下一步”,如下图所示: 10、直至安装完成,点击“完成”,如下图所示: 11、选择UI用户界面语言“简体中文”,如下图所示: 12、双击reg文件,添加到注册表,选择“是”,如下图所示: 13、成功添加到注册表,如下图所示: 14、再次运行SQLyog,如下图所示: 15、点击连接,如下图所示: 16、输入连接名称,如下图所示: 17、点击连接,如下图所示: 18、打开表,如下图所示:
一、安装文件包下载 相关MySQL的安装包和工具,可以免积分到CSDN下载栏目下载,地址为:http://download.csdn.net/detail/taomanman/9642852。 二、Navicat for MySQL安装配置 1、下载以上链接文件,找到Navicat for MySQL 11.0.10(64bit)包,进行解压,如下图所示: 2、双击安装包,以管理员身份运行,如下图所示: 3、点击“下一步”,如下图所示: 4、选择“我同意”,点击“下一步”,如下图所示: 5、浏览选择安装文件夹,点击“下一步”,如下图所示: 6、选择开始目录,创建快捷方式,点击“下一步”,如下图所示: 7、点击“下一步”,如下图所示: 8、点击“安装”,如下图所示: 9、直至安装完毕,点击“完成”,如下图所示: 10、双击目录下“PatchNavicat.exe”,进行破解,如下图所示: 11、浏览选择到Navicat for MySQL 11.0.10目录下的navicat.exe文件,如下图所示: 12、破解成功,如下图所示: 13、回到桌面,以管理员身份运行Navicat for MySQL,如下图所示: 14、选择”文件”->”新建连接”或者直接点击“连接”,如下图所示: 15、在弹出新建连接对话框中,输入信息,并进行连接测试,如下图所示: 16、如果想要让该MySQL让局域网内可以连接,输入 grant all privileges on *.* toroot@"%" identified by 'root' with grant option; flush privileges; 17、在新窗口填写所要连接管理的数据库的信息,可以“连接测试”,或直接“确定”,如下图所示: 18、登陆成功,如下图所示: 19、连接数据后,打开数据库,才可进行更多操作,如下图所示: 20、备份数据库,一般是存为SQL文件,选中要备份的数据,右键,转存SQL文件或点“备份”,如下图所示: 21、保存文件,然后等待完成就可以了,如下图所示: 22、导入数据库,最好是清空原来的数据库,然后选中需要导入数据库,右键,选择“运行SQL文件”,从本地磁盘查找备份的SQL文件,如下图所示: 23、编码可根据实际情况选定也可不特别的指明,软件会覆盖原来的,然后点击开始,导入数据,如下图所示:
一、安装文件包下载 相关MySQL的安装包和工具,可以免积分到CSDN下载栏目下载,地址为:http://download.csdn.net/detail/taomanman/9642852。 二、MySQL安装配置 1、下载以上链接文件,找到mysql-5.6.24-win32.1432006610包,进行解压,如下图所示: 2、上面提供的MySQL是一个免安装的ZIP文件,解压到磁盘后,拷贝到C:\Program Files目录下,如下图所示: 3、环境变量配置,右键"我的电脑"->"属性"->"高级"->"环境变量",如下图所示: 4、环境变量配置 1)、为Administrator新建Path环境变量,如下图所示: 2)、向系统环境变量Path中添加MySQL的bin,注意在原先Path变量后面追加 ;C:\Program Files\mysql-5.6.24-win32\bin,如下图所示: 5、打开MySQL目录下的my-default.ini文件,如下图所示: 6、修改配置文件中的内容,将 # basedir = ..... # datadir = ..... 修改为 basedir = C:\Program Files\mysql-5.6.24-win32 datadir = C:\Program Files\mysql-5.6.24-win32\data 如下图所示: 7、以管理员身份运行CMD命令,如下图所示: 8、输入 cd C:\Program Files\mysql-5.6.24-win32\bin,如下图所示: 9、输入 mysqld -install 进行安装,如下图所示: 10、输入 net start mysql 启动服务,如下图所示: 11、继续输入 mysql -u root -p 进行首次MySQL命令行登录,首次登录密码为空,直接回车即可,如下图所示: 12、在bin目录下,输入mysql -u root后为root设置密码也为root,命令为 set password for 'root'@'localhost'=password('root'); 如下图所示: 13、以用户名root,密码root进入MySQL,输入show databases; 后显示所有数据库名,如下图所示:
一、ASP.NET MVC概述 1、什么是ASP.NET MVC? ASP.NET MVC是微软官方提供的MVC模式编写ASP.NET Web应用程序的一个框架。 是微软继ASP.NET WebForm后的又一种开发方式,而非替代方式。 2、官方网站和源码网站? 官方网站:http://www.asp.net/mvc 源码网站:http://aspnet.codeplex.com/wikipage?title=MVC 3、ASP.NET MVC的特点? 1)、分离任务 2)、可扩展 3)、强大的URL重写(路由)机制 4)、兼容ASP.NET现有的技术 5)、开源 4、ASP.NET MVC的优点? 1)、通过把项目分为model、view和controller,使得复杂项目更加容易维护。 2)、没有使用viewstate和服务器表单控件,可以更方便的控制应用程序的行为。 3)、应用程序通过controller来控制程序请求,可以提供丰富的url重写。 4)、支持测试驱动开发,英文全称Test-Driven Development,简称TDD,是一种不同于传统软件开发流程的新型的开发方法。它要求在编写某个功能的代码之前先编写测试代码,然后只编写使测试通过的功能代码,通过测试来推动整个开发的进行。这有助于编写简洁可用和高质量的代码,并加速开发过程。 5)、在团队模式下表现更加出众。 5、为什么我们需要ASP.NET MVC? 1)、关注点分离 2)、高扩展性 3)、更好的可测试性 4)、更好的URL重写 5)、更好的性能 6)、更加灵活的HTML代码控制 ... 6、Razor视图引擎? Razor视图引擎是对以代码为焦点的方法实现,其特点如下: 1)、简洁、富于表现、流畅,尽量减少编写一个视图模板所需要敲入的字符数,实现快速流畅的编程工作。不必为了明确标记出服务模块的开始和结束而中断编程,Razor解析器能从你的代码中自动推断出来。 2)、易于学习,熟悉现有的编程语言和HTML技能就能快速学会。 3)、可以在任何文本编辑器中工作。 4)、VS IDE对其增加了智能提示。 5)、支持单元测试。 7、ASP.NET MVC请求处理过程? 8、ASP.NET MVC3与MVC4 知识点? 9、ASP.NET MVC与Web Form区别? 二、ASP.NET MVC编程要点 1、@Html.DisplayNameFor(mode=>model.Name),默认会直接输出属性名称,如果属性名称想要显示中文,可在类属性定义一个System.ComponentModel下DisplayName属性,如: [DisplayName(“名称”)] public string Name {get;set;} 使用Razor语法输出属性,默认所有数据都会使用HTML编码(HtmlEncode)输出,这是出于程序安全考虑,可避免网页遭受跨网站脚本攻击(Cross-Site ScriptingAttach)。 2、@model IEnumerable<命名空间.类名> 3、@{ ViewBag.Title=”页面标题”;} 4、@Html.ActionLink(“Create New”,”Create”) 5、@Html.ActionLink(“Edit”,”Edit”,new{id=item.Id}) 用来输出超链接,第一个参数是链接显示文字,第二个参数是链接的目的Action名称,第三个参数代表路由参数id。这样MVC在输出超链接时,会加上要传给下一页的路由参数,MVC知道传入{controller}与{action}路由参数外,还给予{id}路由值。 Controller里有两个同名的方法,一个给HTTP GET方法用的,另一个是给HTTP POST方法用的。有HttpPost属性的会告知MVC框架此动作只会接收HTTP POST过来的信息,这个属性称为动作过滤器(Action Filter)或者动作选择器(Action Selector)。 // GET: /Home/Create public ActionResultCreate() { return View(); } // POST: /Home/Create [HttpPost] public ActionResultCreate(Collectorcollector) { if (ModelState.IsValid) { db.Collectors.Add(collector); db.SaveChanges(); returnRedirectToAction("Index"); } returnView(collector); } 6、Html.BeginForm()方法,该辅助方法将会输出<form>标签,而且必须以using包起来,如此便可在using程序代码最后退出后,让MVC补上</form>标签。 @using (Html.BeginForm()) {} @Html.ValidationSummary(true)用来显示当表单域发生验证失败时,显示的错误消息。 @Html.LabelFor(model=>model.Name)用来显示特定字段的显示名称,会输出包含<label>标签的域名,它与@Html.DisplayNameFor(model=>model.Name)的区别如下图所示: @Html.EditorFor用于输出表单域 @Html.ValidationMessageFor用于显示字段验证的错误消息。 在Model类中,需要必填属性上加一个System.ComponentModel.DataAnnotations命名空间下的Required属性,如下所示: [Required] public string Name {get;set;} 这是MVC引入了一种Unobtrusive的JavaScript开发风格。 跳转Action,当前Action名为Create,语法如下: return RedirectToAction(“Index”); ModelState.IsValid用来判断在模型(Model)的验证状态是否有效,如果验证都没有问题,就可以利用Entity Framework标准的方法将数据写入数据库。 @Html.HiddenFor(model=>model.ID),该辅助方法主要用来生成HTML窗体的隐藏域。 路由Routing---网址路由,主要有2个目的: 1)、比对通过浏览器传过来的HTTP请求。 2)、响应适当的网址给客户端浏览器。 客户端请求->URI Routing->Route->RouteHandler->HttpHandler routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 所有*.axd结尾的网址通常都代表着其中一个HttpHandler,如在ASP.NET WebForm中常用的WebResource.axd就是其中一个常见的HttpHandler。为了不让MVC把这些*.axd的HttpHandler视为MVC运行起来,所以必须添加这条IgnoreRoute规则,将其排除在MVC运行生命周期之外。
1、MVC5 View视图中创建带样式超链接 @Html.ActionLink("action名","controller名",new { id=item.ID },new{style="color:red",@class="css样式名"}) @Html.LabelFor(model => model.Genre, new { @class = "control-label" })@Html.ValidationMessageFor(model => model.Genre, null, new { @class = "help-inline" }) 2、Model中字段的显示设置及类型指定 添加引用 using System.ComponentModel.DataAnnotations; [Display(Name = "发布日期")] [DataType(DataType.Date)]public DateTime ReleaseDate { get; set; } Display 特性指定了显示的字段名(本例中“发布日期”替换了“ReleaseDate”)。 DataType 特性指定了数据类型,在本例中它是日期类型,因此存储在该字段的时间信息将不会显示出来。 3、 [HttpPost][ValidateAntiForgeryToken]public ActionResult Edit(Movie movie){ if (ModelState.IsValid) { db.Entry(movie).State = EntityState.Modified; db.SaveChanges(); return RedirectToAction("Index"); } return View(movie);}HttpPost特性指定只有POST请求才能调用这个Edit方法。HttpGet是默认值,无需指定。ValidateAntiForgeryToken 这个特性用来阻止伪造的请求,它和视图(Views\Movies\Edit.cshtml)中的 @Html.AntiForgeryToken() 是成对出现的。 @using (Html.BeginForm()) { @Html.AntiForgeryToken() @Html.ValidationSummary(true)}
一、风力(风速)等级表 等级 名称 陆地上地物特征 风速(米/秒) 0 无风 静,烟直上。 0.0—0.2 0 1 软风 烟能表示风向,树叶略有摇动。 0.3—1.5 2 轻风 人面感觉有风,树叶有微响,旗子开始飘动,高的草开始摇动。 1.6—3.3 3 微风 树叶及小枝摇动不息,旗子展开,高的草摇动不息。 3.4—5.4 4 和风 能吹起地面灰尘和纸张,树枝动摇,高的草呈波浪 起伏。 5.5—7.9 5 清劲风 有叶的小树摇摆,内陆的水面有小波,高的草波 浪起伏明显。 8.0—10.7 6 强风 大树枝摇动,电线呼呼有声,撑伞困难,高的草不时倾伏于地。 10.8—13.8 7 疾风 全树摇动,大树枝弯下来,迎风步行感觉不便 13.9—17.1 8 大风 可折毁小树枝,人迎风前行感觉阻力很大。 17.2—20.7 9 烈风 草房遭受破坏,屋瓦被掀起,大树枝可折断。 20.8—24.4 10 狂风 树木可被吹倒,一般建筑物遭破坏。 24.5—28.4 11 暴风 大树可被吹倒,一般建筑物遭严重破坏。 28.5—32.6 12 飓风 陆上少见,其摧毁力极大。 >32.6 二、风向计算 在实时显示风向时,采用16方位的方式,即将采集到的风向角度值经过16方位变换处理得到相应的方位,变换公示如下: K=(wd+11.25)/22.5; 其中,变量k用来表示方位,在程序设计中获取的k值为1-16的整数;变量wd为实时采集到的风向角度值。 16方位变换即将全方位360°进行16等分,则每等分为22.5°,由于采集风向数据进度为1°,所以在变换为16方位时,需要加上一个辅助值11.25,这样才能是角度值范围与方位一一对应。
一、问题 最近在一个系统的对外接口中,经过测试人员测试,发现登录接口中输入的用户名不区分大小写,经过排查找原因,原来是由于数据库忽略了大小写。 二、解决办法 Select * from Base_UserInfo where User_Account='Admin' COLLATE Chinese_PRC_CS_AI; 1、数据库设置排序规则,如下图所示: 2、SQL语句设置,如下: alter database 你的数据库名 COLLATE Chinese_PRC_CS_AI; 3、字段级设置 ALTER TABLE 你的表名ALTER COLUMN 字段名称 varchar(50) COLLATE Chinese_PRC_CS_AI;
转换函数如下代码所示: public static string GetBig(string num) { string result=null; string chNum = "零壹贰叁肆伍陆柒捌玖";//定义好大写的数字 string zheng = "元拾佰仟万拾佰仟亿拾佰仟万";//定义好数位,从小到大排 string xiaoshu = "角分";//把整数和小数分开处理 if (num.IndexOf('.') != -1)//检测用户输入中是否包含小数点 { string[] temp = num.Split('.');//把用户输入的字符 int index = temp[0].Length-1; for (int i = 0; i < temp[0].Length; i++) { result += chNum[Convert.ToInt32(temp[0][i].ToString())]; result += zheng[index]; index--; } if (temp[1].Length>0) { for (int i = 0; i < temp[1].Length; i++) { result += chNum[Convert.ToInt32(temp[1][i].ToString())]; result += xiaoshu[i]; } } } else { int index = num.Length-1; for (int i = 0; i < num.Length; i++) { result += chNum[Convert.ToInt32(num[i].ToString())]; result += zheng[index]; index--; } } return result; } 调用: GetBig("526487.25");
1、Web.Config中增加如下红色标记部分配置: <system.web> <compilation debug="true" targetFramework="4.0" /> <!--提供Ajax调用开始 --> <webServices> <protocols> <add name="HttpGet" /> <add name="HttpPost" /> </protocols> </webServices> <!--提供Ajax调用结束--> </system.web> 2、增加一个Tools类,如下代码: public class Tools { public static void WriteResult(string callback, string result) { HttpContext.Current.Response.HeaderEncoding = System.Text.Encoding.UTF8; HttpContext.Current.Response.ContentEncoding = System.Text.Encoding.UTF8; HttpContext.Current.Response.Write(callback + "(" + result + ")"); HttpContext.Current.Response.End(); } } 3、WebService文件增加头属性 [WebService(Namespace = "http://tempuri.org/")] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] [System.ComponentModel.ToolboxItem(false)] // 若要允许使用 ASP.NET AJAX 从脚本中调用此 Web 服务,请取消注释以下行。 [System.Web.Script.Services.ScriptService] [SoapDocumentService(RoutingStyle = SoapServiceRoutingStyle.RequestElement)] public class getData : System.Web.Services.WebService { #region 1、获取设备信息 [WebMethod] public void GetAllDeviceData() { string sql = @"select devid,jfid,devname from JfDevice"; DataTable dtTable = AosySql.ExecuteforDataSet(sql).Tables[0]; //获取数据库数据的方法,使用的数据库访问类,根据自身情况调整 StringBuilder sb = new StringBuilder(); if (dtTable.Rows.Count > 0) { foreach (DataRowView drv in dtTable.DefaultView) { sb.Append("{\"DevId\":\"" + drv["devid"].ToString() + "\","); sb.Append("\"DevName\":\"" + drv["devname"].ToString() + "\","); sb.Append("\"JfId\":\"" + drv["jfid"].ToString() + "\"},"); } } string result = "[" + sb.ToString().TrimEnd(',') + "]"; Tools.WriteResult(HttpContext.Current.Request["jsoncallback"], result); } } 4、将WebService发布到IIS后,在其他系统中的html页面中调用,如下代码所示: <!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml"><head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title></title> <script src="JS/jquery-1.7.2.min.js"></script> <script type="text/javascript"> $(function () { getValueFromWebService(); }); //查询webservice function webservice(url, callback) { $.ajax({ type: 'get', url: url, dataType: 'jsonp', jsonp: 'jsoncallback', success: function (result) { callback(result); } }); }; function getValueFromWebService() { webservice('http://192.168.33.158/XXXX/WebService/getData.asmx/GetAllDeviceData', function (obj) { var data = obj; //成功 $.each(data, function (key, item) { // 添加项目内容到ul列表中 $('<li>', { text: formatItem(item) }).appendTo($('#room')); }); }); } function formatItem(item) { return item.DevId + ' / ' + item.DevName + ' / ' + item.JfId; } </script></head><body> <ul id="room"> </ul></body></html>
在获取一个DataTable后,常用的操作有删除列,修改列标题,调整列顺序等,那么该如何做呢? 1、删除列 DataTable dt = XXX(); dt.Columns.Remove("Age"); dt.Columns.Remove("Sex"); 2、修改列标题 DataTable dt = XXX(); dt.Columns["Age"].ColumnName="年龄"; dt.Columns["Sex"].ColumnName="性别"; 3、调整列顺序 DataTable dt = XXX(); dt.Columns["Age"].SetOrdinal(1);
为HighCharts设置XAxis的PlotBands属性,全部代码如下: Highcharts chart = new Highcharts("HC" + divName) .InitChart(new Chart { DefaultSeriesType = CT_type, Width = _width, Height = _height, ClassName = _Title, BackgroundColor = null }).SetCredits(new Credits { Enabled = false }) .SetTitle(new Title { Text = _Title }) .SetXAxis(new XAxis { Title = new XAxisTitle { Text = "单位:" + unitName }, Categories = xCategoryList.ToArray(), Reversed = false, Opposite = false, PlotBands = new[]{ new XAxisPlotBands{ Color=ColorTranslator.FromHtml("#FCFFC5"), From=7, To=12, Label=new XAxisPlotBandsLabel {Text="上午工作时间"} }, new XAxisPlotBands{ Color=ColorTranslator.FromHtml("#FCFFC5"), From=13, To=18, Label=new XAxisPlotBandsLabel {Text="下午工作时间"} } } }) .SetYAxis(new YAxis { Title = new YAxisTitle { Text = "单位:" + SeriesUnit }, Min = 0, PlotLines = new[] { new YAxisPlotLines { Value = 0, Width = 1, Color = ColorTranslator.FromHtml("#808080") } } }) .SetTooltip(new Tooltip { Formatter = ToolTip_info, ValueDecimals = 2 }) .SetPlotOptions(new PlotOptions { Column = new PlotOptionsColumn { BorderWidth = 2, BorderColor = ColorTranslator.FromHtml("#edc240") } }) .SetLegend(new Legend { Enabled = false }) .SetSeries(new[] { new Series { Name ="", Data = new Data(yDataSeriesList.ToArray()),Color=ColorTranslator.FromHtml("#f5e2a7")} } ); return chart.ToHtmlString();
最近.NET项目上使用JAVA项目中的jsp页面,使用到跨域的信息交互。 jsp端代码,通过监听来自.NET项目所在IP地址,来请求.NET一般处理程序,实现jsp交互信息保存到.NET中: window.addEventListener('message', function(event){ // 通过origin属性判断消息来源地址 if (event.origin == 'http://192.168.11.195') { if(event.data=='1'){ var xmlStr = getXmlStr(); var sysId1= '1111222'; var userId1 = '22211112'; var menus1 = xmlStr; var callback1='?'; var url = 'http://192.168.11.195/BPMS/WebService/MessageExchange.ashx'; $.ajax({ url:url, data: {sysId:sysId1,userId:userId1,menus:menus1,callback:callback1}, dataType:'jsonp', type:'post', processData: true, success:function(data){ alert(data); }, error:function(XMLHttpRequest, textStatus, errorThrown) { alert(XMLHttpRequest.status); alert(XMLHttpRequest.readyState); } }); } } }, false); .NET页面代码,主要是通过向jsp端页面发送带标记请求信息: <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Role_InfoOuter.aspx.cs" Inherits="AT.Web.ATBase.SysUser.Role_InfoOuter" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title></title> <script src="../../Themes/Scripts/jquery-1.8.2.min.js"></script> <script type="text/javascript"> $(function () { $("#btn").click(function () { test(); }); function test() { var ifr = document.getElementById('iframe'); var targetOrigin = 'http://192.168.11.10:8080'; ifr.contentWindow.postMessage('1', targetOrigin); }; }); </script> </head> <body> <form id="form1" runat="server"> <div> <iframe id="iframe" src="http://192.168.11.10:8080/ATMxgraphBS/grapheditor/Atgraphright.jsp"></iframe> <input type="button" value="保存" id="btn" /> </div> </form> </body> </html> MessageExchange.ashx代码如下: using AT.DataBase.SqlServer; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Web; namespace AT.Web.WebService { /// <summary> /// MessageExchange 的摘要说明 /// </summary> public class MessageExchange : IHttpHandler { public void ProcessRequest(HttpContext context) { context.Response.ContentType = "text/plain"; string callback = (context.Request["callback"] ?? "").ToString(); string sysId = (context.Request["sysId"] ?? "").ToString(); string userId = (context.Request["userId"] ?? "").ToString(); string menu = (context.Request["menus"] ?? "").ToString(); string msg = SaveMessage(sysId, userId, menu); context.Response.Write(msg); context.Response.End(); } /// <summary> /// 保存业务逻辑 /// </summary> /// <param name="sysId"></param> /// <param name="userId"></param> public string SaveMessage(string sysId, string userId, string menu) { SqlServerHelper db = new SqlServerHelper("Server=192.168.11.201;Database=AT_UnifiedAuthorityDB;Uid=sa;Pwd=apesm;"); StringBuilder sb = new StringBuilder(); sb.Append("insert into Test(T1,T2,T3) values('" + sysId + "','" + userId + "','" + menu + "')"); int cnt = db.ExecuteBySql(sb); if (cnt > 0) { return "OK"; } else { return "NO"; } } public bool IsReusable { get { return false; } } } }
1、批量插入数据SQL语句 declare @i int set @i=1 while @i<=400 begin insert into GGJ_Sys_Users(UserName,Pwd,UserTrueName,Gender,Birthday,UnitType,UnitCode,UnitName,CreateUserID) values('admin'+cast(@i as varchar),'21232F297A57A5A743894A0E4A801FC3','admin'+cast(@i as varchar),'男','1985-01-12',9,'100007','行政部',1) set @i=@i+1 end 2、比较两个DataTable,取交集和差集 定义一个实体类,主要是一个构造函数和比较器,代码如下: using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace ProvincePlat.CommonClass { public class AccessedUnit { public string F_DataCenterID { get; set; } public string F_DataCenterName { get; set; } public string BuildUnitName { get; set; } public string F_Date { get; set; } public AccessedUnit(string f_DataCenterID, string f_DataCenterName, string buildUnitName, string f_Date) { this.F_DataCenterID = f_DataCenterID; this.F_DataCenterName = f_DataCenterName; this.BuildUnitName = buildUnitName; this.F_Date = f_Date; } } class AccessedUnitEquality : IEqualityComparer<AccessedUnit> { public bool Equals(AccessedUnit x, AccessedUnit y) { return x.F_DataCenterID == y.F_DataCenterID; } public int GetHashCode(AccessedUnit obj) { if (obj == null) { return 0; } else { return obj.ToString().GetHashCode(); } } } } GetAccessedUnit.ashx,逻辑代码如下: using ProvincePlat.CommonClass; using System; using System.Collections.Generic; using System.Data; using System.Linq; using System.Text; using System.Web; namespace ProvincePlat.WebPages.EnergyConsumptionPandect { /// <summary> /// GetAccessedUnit 的摘要说明 /// </summary> public class GetAccessedUnit : IHttpHandler { public void ProcessRequest(HttpContext context) { context.Response.ContentType = "text/plain"; StringBuilder jsonBuilder = new StringBuilder(); jsonBuilder.Append("["); string datestarttime = System.Configuration.ConfigurationManager.AppSettings["AccessTime"].ToString(); string days = System.Configuration.ConfigurationManager.AppSettings["PreDays"].ToString(); int day = int.Parse(days); string datetimes = DateTime.Now.AddDays(-day).ToString("yyyy-MM-dd"); string datetimee = DateTime.Now.ToString("yyyy-MM-dd"); StringBuilder sb = new StringBuilder(); string sql = string.Format("EXEC GGJ_GET_AccessedUnit '{0}','{1}'", datestarttime, datetimes); string sql2 = string.Format("EXEC GGJ_GET_AccessedUnit '{0}','{1}'", datetimes, datetimee); DataTable dt = CommonClass.QuerySQL.GetDataTable(sql); DataTable dt2 = CommonClass.QuerySQL.GetDataTable(sql2); IList<AccessedUnit> accessed = new List<AccessedUnit>(); IList<AccessedUnit> accessed2 = new List<AccessedUnit>(); foreach (DataRowView drv in dt.DefaultView) { accessed.Add(new AccessedUnit(drv["F_DataCenterID"].ToString(), drv["F_DataCenterName"].ToString(), drv["BuildUnitName"].ToString(), drv["F_Date"].ToString())); } foreach (DataRowView drv in dt2.DefaultView) { accessed2.Add(new AccessedUnit(drv["F_DataCenterID"].ToString(), drv["F_DataCenterName"].ToString(), drv["BuildUnitName"].ToString(), drv["F_Date"].ToString())); } var jiaoji = accessed.Intersect(accessed2, new AccessedUnitEquality()).ToList();//交集 var chaji = accessed.Except(accessed2, new AccessedUnitEquality()).ToList();//差集 foreach (AccessedUnit unit in jiaoji) { sb.Append("{\"id\":\"" + unit.F_DataCenterID);//单位编码 sb.Append("\"},"); } string jsonString = sb.ToString().TrimEnd(','); jsonBuilder.Append(jsonString); jsonBuilder.Append("]"); string jsonString2 = jsonBuilder.ToString(); context.Response.Write(jsonString2); context.Response.End(); } public bool IsReusable { get { return false; } } } } Ajax调用 //获取已接入单位数量 function getAccessedUnitCNT(unitcode) { var count = 0; $.ajax({ url: 'GetAccessedUnit.ashx?rd=' + Math.random(), type: 'GET', async: false,//取消默认异步为true,设置同步状态为false data: 'dname=' + escape(unitcode), datatype: 'xml', success: function (data) { var datas = eval(data); //alert(data); //alert("单位个数:" + datas.length); if (data != null && data != "") { count = datas.length; } } }); return count; } 3、BootStrap整体结构图 4、文本输入框输入完后,按回车触发按钮查询 为input控件增加onkeypress事件,执行函数名为EnterTextBox() <input type="text" tabindex="1" class="input-query" onkeypress="return EnterTextBox();" id="input_queryCondition" /> <img src="../../Images/map/search_icon.png" id="btn_query" tabindex="2" title="查询" onclick="clickSearchResult();" /> function EnterTextBox() { if (event.keyCode == 13) { event.keyCode = 9; event.returnValue = false; document.all["btn_query"].click(); } } 5、JavaScript计算两个日期的时间差 正则表达式替换Javascript日期/改为- 日期 = 日期.replace(/\//g, "\-"); 有时候我们需要知道两个日期之间差了多少天,多少小时,甚至多少分钟多少秒。下面我们用JavaScript实现一个函数,用于计算两个日期的时间差,先来看看代码: /* * 获得时间差,时间格式为 年-月-日 小时:分钟:秒 或者 年/月/日 小时:分钟:秒 * 其中,年月日为全格式,例如 : 2010-10-12 01:00:00 * 返回精度为:秒,分,小时,天*/ function GetDateDiff(startTime, endTime, diffType) { //将xxxx-xx-xx的时间格式,转换为 xxxx/xx/xx的格式 startTime = startTime.replace(/\-/g, "/"); endTime = endTime.replace(/\-/g, "/"); //将计算间隔类性字符转换为小写 diffType = diffType.toLowerCase(); var sTime = new Date(startTime); //开始时间 var eTime = new Date(endTime); //结束时间 //作为除数的数字 var divNum = 1; switch (diffType) { case "second": divNum = 1000; break; case "minute": divNum = 1000 * 60; break; case "hour": divNum = 1000 * 3600; break; case "day": divNum = 1000 * 3600 * 24; break; default: break; } return parseInt((eTime.getTime() - sTime.getTime()) / parseInt(divNum)); } var result = GetDateDiff("2010-02-26 16:00:00", "2011-07-02 21:48:40", "day"); 使用的方法很简单,比如计算天数可以这样: GetDateDiff("2010-02-26 16:00:00", "2011-07-02 21:48:40", "day"); 计算秒数则可以这样: GetDateDiff("2010-02-26 16:00:00", "2011-07-02 21:48:40", "second"); 6、
一台服务器上的QC上已经使用过一段时间,里面已经包括了域、项目、用户等信息后,此时如果服务器不满足负荷需要后,需要迁移到新的QC服务器上,这里就涉及到数据迁移问题,本篇博客结合实际操作,记录主要步骤,在这里和大家分享一下: 1、到原QC服务器上,打开SQLServer,找到如下数据库的位置,如下图所示: 2、然后到服务中停止SQL服务,如下图所示: 3、 到SQL文件位置,拷贝对应mdf和ldf文件,如下图所示: 4、拷贝C:\ProgramData\HP\ALM\repository\qc下除了Default外的文件夹目录到新服务器QC对应此目录下,如下图所示: 5、打开新服务上C:\ProgramData\HP\ALM\repository\qc中的每一个项目文件夹,打开dbid.xml文件,修改服务器的计算机名称,如下图所示: 6、停止新服务器上数据库服务,如下图所示: 7、将原服务器上备份的数据库文件,拷贝到新服务器上,进行覆盖,如下图所示: 8、再次启动SQL服务,如下图所示: 9、附加原先QC服务器中的项目数据库,如下图所示: 10、修改qcsiteadmin_db数据库,修改以下表中关于服务器及数据库连接字符串,如下图所示: 1)、td.APPSERVERS表 2)、td.DBSERVERS表 3)、td. DOMAINS表 4)、td. PERF_SERVER_GENERAL_MEASURES表 SQL语句:update td.PERF_SERVER_GENERAL_MEASURES setPSGM_SERVER_MACHINE_NAME='SERVER-QC'; 5)、td. PERF_SERVER_THREAD_TYPES表 6)、td.PROJECTS表 7)、首先暂停新服务器上的QC服务,然后选择QC数据库(项目库和qcsiteadmin_db库),然后在其库对应的SQL查询里运行 exec sp_change_users_login'update_one','TD','TD' 8)、经过上述步骤后,就完成了数据的迁移过程,可以在新服务器上打开,如下图所示: =========================================================================== 如果觉得对您有帮助,微信扫一扫打赏1元:
QC ALM 11可以关联邮箱,当提交Bug给相关人员时,其会收到邮件通知,本篇博文中使用到了Mail Direct Pro邮件转发工具。 1、 ALM系统管理员账号进入AML的站点管理,在“站点项目”列表中,选择需要配置发送邮件功能的项目,在左侧“项目详细信息”中“其它”项中勾选“自动发送邮件”,(说明:管理员账户需要事先设置邮箱号)如下图所示: 2、 点击“站点配置“,双击参数设置以下的参数值,如下图所示: 3、在“站点配置“页面,点击”设置“,点击”设置邮件协议“,选择”SMTP服务器“,如下图所示: 4、 设置邮件协议有2种情况:内网邮件服务器、外网邮件服务器,下面就这两种邮件服务器的配置进行分别说明。 1)、 内网邮件服务器: 若公司的邮件服务器是在公司内网,则直接输入内网SMTP服务器的IP即可。 2)、外网邮件服务器 若公司邮件服务器是在外网,委托在电信运营商的机房或者使用的是互联网的企业邮箱(如腾讯企业邮箱、163企业邮箱)等,则只能通过邮件转发工具来发送QC邮件,这里使用Mail Direct Pro来配置QC邮件服务器。 (1)、首先下载Mail Direct Pro邮件转发工具,并在服务器上安装,如下图所示: (2)、配置Mail Direct Pro运行环境,点击菜单栏里的工具中的“选项”,如下图所示: (3)、邮件转发工具启动和运行配置如下: 勾选“Windows启动后自动运行”勾选“程序运行后自动开始服务”,如下图所示: (4)、DNS设置,选择“指定DNS服务器地址”,输入当前服务器的IP地址,如下图所示: (5)、服务器设置 邮件发送服务器中将主机名设置为IP地址,如下图所示: (6)、SMTP转投设置 勾选“如果邮件发送失败,转投到指定SMTP服务器”。 勾选“增加”弹出SMTP服务器信息设置框,设置相关信息后并保存,如下图所示: 注意:SMTP的默认端口是25号端口,这需要和ALM的邮件服务器端口一致,如下图所示: (7)、ALM发送邮件服务器配置 点击“站点设置”选项里的“设置”,在弹出的设置邮件协议提示框中,选中SMTP服务器,输入IP地址,如下图所示: 点击“测试…”,输入收件人的邮箱地址,点击“发送”,弹出“邮件发送成功”提示框说明ALM的邮件中继转发配置成功,如下图所示: 说明:发送测试邮件时,ALM邮件服务器的端口和IP必须是和邮件中继工具设置的一致,邮件中继工具里设置的邮箱账号一定是可用可登入的,否则会弹出错误提示信息:(Fail To Test Send Email) (8)、项目管理员账号登录进入具体项目,选择页面左上角的“工具”->”自定义”,点击“自动邮件”,选择邮件自动发送的出发字段,将字段从左边“可用的缺陷字段”栏移到右边“选定的缺陷字段”栏,如下图所示: (9)、设置邮件发送到的人,常规设置是选中“测试者”和“分配给”组,也可以指定某人,设置接收邮件的过滤条件,可以对BUG进行筛选之后再发送给相关人员。例如当缺陷的“状态”发生改变时系统自动发送邮件到相关人员,如下图所示: 注意:项目组成员要能收到邮件必须在每个成员的ALM账号里配有可用邮箱(一般是公司的工作邮箱),ALM自动邮件才能发送成功,项目成员才能收到邮件。 (10)、点击自定义设置左侧栏的“报警规则”,根据实际需要将右侧规则说明里的“与警报关联的实体”和“发送电子邮件至”打钩,如下图所示: =========================================================================== 如果觉得对您有帮助,微信扫一扫打赏1元:
一旦HP-ALM安装,我们只能继续创建域,项目和用户使用后的ALM工作。下面是步骤来创建项目,域和用户。 一、创建域 1、对于创建域,第一步是进入站点管理员页面。开展QC使用URL - http://localhost:8080/qcbin/SiteAdmin.jsp。并输入被设置在安装QC管理员凭据,如下图所示: 2、登录到网站后台管理页面后,我们会看到网站项目登陆标签,如下图所示,下一步是创建一个域。 3、点击在左上角的'创建域'按钮,然后输入域名,然后单击确定,如下图所示: 4、域将被成功创建,如下图所示: 二、创建项目 一旦域被创建后,下一步是创建“项目”。创建项目需要我们通过一个向导,我们应该按照下面的步骤来创建“Projects”。 1、选择域项目需要下才被创建,然后单击"创建项目 "按钮,如下图所示: 2、创建项目向导打开。选择"创建一个空项目" ,然后单击“下一步”,如下图所示: 3、输入"项目名称" ,并选择域,然后单击"下一步",如下图所示: 4、输入数据库凭据并单击 "下一步",如下图所示: 5、选择项目管理员的用户ID,然后点击 "下一步",如下图所示: 6、选中激活复选框,启用版本控制,如果想用并点击 "创建",如下图所示: 7、当点击"创建",该项目 "XXXX"创建成功。点击"确定" ,看看在项目窗格中创建的项目,如下图所示: 三、创建用户 1、选择站点用户选项卡,如下图所示,点击"新建用户" 按钮,如下图所示: 2、新用户窗口打开。填写用户详细信息并单击 "确定",如下图所示: 3、创建用户后,给用户他们所需要的项目准入,如下图所示: 4、使用户能够访问到项目后验证 "项目用户"选项卡上的用户列表,如下图所示: =========================================================================== 如果觉得对您有帮助,微信扫一扫打赏1元:
1、一旦安装HP-ALM成功,ALM可以如下图所示,在开始菜单中启动,如下图所示: 2、安装后,我们还可以通过给将被打开,如下图所示在InternetExplorer和ALM以下URL启动ALM。网址URL:http://localhost:8080/qcbin/,如下图所示:
前面部分介绍了QC入门节QC安装的先决条件,下面我们将介绍如何安装QC。 1、下载ISO文件后将内容解压,解压缩后文件夹结构如下图所示: 2、之后打开欢迎界面,点击“下一步”按钮,如下图所示。继续单击下一步按钮,直到到达Finish(完成)屏幕。 3、经整理后,HP ALM平台配置向导将打开如下图所示: 4、许可证密钥选项卡打开,用户需要选择"ApplicationLife Cycle Management" 的许可文件,这样我们就可以访问该包的所有功能。 5、在打开的集群配置选项卡中选择单机,因为我们将在一个节点/独立的主机上安装的所有服务,而不是一个分布式系统。 6、安全选项卡打开,输入密钥的密码短语,例如,有一个最少12个字符。 7、应用服务器选项卡中打开,然后选择"JBoss应用程序服务器",并允许所有其他为默认值,如下图所示: 8、HP ALM平台服务选项卡中打开。保留空白,让Windows登录名作为用户ID启动ALM服务,如下图所示: 9、 打开Web服务器选择窗口。我们将使用JBoss内部和Web服务器。因此,我们不需要选择任何东西,但只要点击"下一步"按钮即可,如下图所示: 10、如果你想安装示例应用程序 勾选"安装Mercury Tours",然后单击 "下一步",如下图所示: 11、对于邮件服务器配置,添加SMTP或MS IIS的SMTP服务器名称或选择无。此服务器的设置可以通过电子邮件与其他质量控制用户交互或在它的基础上,设定触发的电子邮件,这里选择“无”,点击"下一步",如下图所示: 12、选择正确的数据库服务器配置。请注意,该数据库对应到服务器上已安装的那个。用户凭据应该具有权限到服务器上创建数据库。只有当所有的字段值是正确的它移动到下一个步骤,如下图所示: 13、网站管理数据库架构配置窗口打开。确保QC的所有管理员数据将被存储在一个独特的DB架构名称,如下图所示: 14、输入网站管理员的用户凭据。不要忘了记住这些凭据,以便您可以登录到QC使用这些凭据安装成功后,如下图所示: 15、文件资源库路径是所有我们创建的QC相关资产将被储存的位置,如下图所示: 16、在安装摘要窗口打开,显示所选配置的完整资料概要。如果用户希望做任何改变,这是第一阶段可以通过返回到上一个窗口进行更改,如下图所示: 17、应用配置表明,所选择的组件将与所选择的配置进行安装。如果安装成功,成功消息被抛出给用户,如下图所示: =========================================================================== 如果觉得对您有帮助,微信扫一扫打赏1元:
1、HP-QC设置 HP Quality Center环境设置是一个复杂的过程,它不是一般的安装,我们一个windows操作系统上开展。 Enterprise安装将会分发,但是,对于学习/评估目的之一,可以在独立的服务器上安装HP-ALM11.5的试用版。还应该需要考虑的先决条件精心准备才能成功安装。 2、先决条件安装QC 用于评估目的的客户端和服务器可以位于同一系统上,并在本章中,我们要做那样的安装。 3、支持Windows操作系统 仅适用于Windows操作系统的以下列表中,因此支持一要确保HP-ALM安装在以下提到的Windows操作系统: · Microsoft Windows Server 2008 Enterprise SP2 32 Bit/64 Bit · Microsoft Windows Server 2008R2 Enterprise SP1 64 Bit · Microsoft Windows Server 2012 Standard 64 Bit · Microsoft Windows Server 2012 R2 Standard 64 Bit 4、支持的数据库 确保有在进行安装HP-ALM之前安装的数据库之一。 · Oracle 10.2.0.5 · Oracle 11.2.0.3 · Oracle 11.2.0.4 · Microsoft SQL Server 2005 SP4 · Microsoft SQL Server 2008 R2SP1 · Microsoft SQL Server 2008 R2 SP2 5、支持的AppServers 确保服务器已经得到了所有已安装的下方支撑appServers运行。 · Apache 2.2 · Microsoft IIS 7.0 · Microsoft IIS 7.5 · Microsoft IIS 8.0 · Microsoft IIS 8.5 6、支持的浏览器 HP ALM只能使用Active X的浏览器进行访问,因此Internet Explorer是被支持的唯一浏览器。 · MicrosoftInternet Explorer 8 · MicrosoftInternet Explorer 9 · MicrosoftInternet Explorer 10 · MicrosoftInternet Explorer 11
本篇博客简单介绍QC的基本知识,让大家对QC有一个初步的认识。在软件开发过程中,测试是一个重要环节,以下是缺陷管理流程图: 1、什么是QC? HP质量中心,测试管理工具,现在普遍被称为应用程序生命周期管理工具(ALM),因为它不再仅仅是一个测试管理工具,它支持的软件开发生命周期的各个阶段。 HP-ALM可以帮助我们管理项目进程,交付成果,资源和项目健康的保持跟踪,标准,使产品拥有者线规了产品的当前状态。重要的是要了解历史,构建和质量中心的工作流程是非常重要的。 2、QC的历史? 质量中心的前身为Test Director。下表为我们提供了关于质量控制的版本历史记录一个更好的见解。 3、QC体系结构? HP-ALM,这是基于Java2企业版(J2EE)技术和企业范围的应用程序使用MS SQL Server或Oracle作为其后端。还有一个负载平衡器来有效地满足用户的要求。 网站管理数据库托管在一个独立的数据库服务器上,而其他与项目有关的数据都存储在单独的数据库服务器上。下面的图表显示了如何设置起来,一般会是规模较大的公司这样做。 4、HP ALM版本? HP ALM是一个商业执照的工具及惠普部署它在下面列出4种不同的版本: · HP ALM · HP ALM 要点 · HPQuality Center 企业版 · HP ALM性能中心版 5、ALM版本新功能? 上述每一个版本在ALM功能方面有其自身的局限性。根据下表可以决定哪些需要许可证为他们的项目的目的。 6、QC的工作流程? 质量中心QC的工作流程是使用一个简单的示意图如下图所示:
1、问题提出 在Web开发中,使用Echarts百度图表控件显示折线图、饼图等时,如果从数据库取出的是空数据,默认显示的是动态气泡图,看起来很凌乱,用户体验不好,那么我们如何修改呢? 2、问题解决 在初始化图表时,增加noDataLoadingOption属性配置,代码如下所示: myChart = echarts.init(document.getElementById('ElectricPie'), { noDataLoadingOption: { text: "暂无数据", effect: 'whirling', //'spin' | 'bar' | 'ring' | 'whirling' | 'dynamicLine' | 'bubble' effectOption: { backgroundColor: "#fff" }, textStyle: { fontSize: 20 } } }); effect属性的可选项如下所示: {string | Function} effect 'bubble' loading效果,可选为:'spin' | 'bar' | 'ring' | 'whirling' | 'dynamicLine' | 'bubble',支持外部装载 显示效果如下图所示:
问题:必须到场的人员没有到场 场景:会议需要通过某一项决定,这个决定必须某部门经理拍板,但是该部门经理临时有事无法出席,而派了一个部门助理参加。由于部门助理没有决定权,所以会议无法做出决定。 解决:在发送会议邀请时,建议给出必须到会的人员,如果必须到会人员无法出席会议,那么会议取消,或者要求必须到会人员指定一位全权代理。 问题:与会人员准备不充分 场景:会议上需要花大量的时间说明会议的背景,尤其是当讨论的问题比较专业的时候,往往需要花上半小时左右来介绍问题各方面的背景和情况 解决:在会议邀请中写明每一位与会人员需要准备的内容,例如:需要在会前阅读完毕的材料,需要准备的资料,需要准备会上发言,如果在会前无法准备好需要怎么办等 问题:会议主题不明确 场景:有些会议的主题一般比较明确,比如说评审、需求讨论,而另外一些会议的主题可能不是很显而易见,例如周会、月会。另外也常常会出现会议跑题的问题。 解决:建议在会议邀请中点明会议主题,主持人也需要学会在会议跑题时及时制止,如果会议在一个主题的相关问题上牵涉太久或者出现争论的情况,主持人也需要作出决定,是将此问题放到最后讨论还是继续讨论 问题:会议目的不明 场景:和会议主题不同,可能一系列的会议有着同样的主题,但是目的各不相同。就我目前的经验,会议目的应该包括以下几种:决策、讨论、汇报与确认进度等,根据会议目的不同。 解决:决策会议必须有最终决策确定,如果到最后大家仍然无法达成共识,那么要么由权威人士拍板,要么少数服从多数。讨论会不一定要有最终决策,但是如果没有最终决策,一般来说是因为信息不足,无法判断;这就需要决定下次会议的时间(至少是大体的时间)、下次会议前需要准备好的内容、这些内容分别由谁来准备、下次会议需要讨论的议题等。汇报与确认进度会中,如果没有什么问题一般来说只要做一下会议总结就可以,如果有问题那么类似于讨论会。会议目的非常重要,会议花费成本很高,如果并没有达到或者部分达到预期的目标,那么会议就是失败的。 问题:会议跑题 场景:会议中途,讨论主题中的某个细节问题时牵涉到了其它的问题,与会者就这个问题展开了讨论,几次以后,会议讨论内容和主题已经相距甚远。 解决:在会议中跑题现象经常会出现,首先在会议前必须明确会议的主题。在会议过程中,主持人应当要及时地纠正会议跑题的情况,最重要的,主持人尽量不要自己参与跑题。 问题:会议时间超出预期时间 场景:会议中遇到的问题比想象中的棘手,以至于要解决这个问题需要更多时间来讨论。这有的时候也会导致会议没有达到预期目的 解决:在会议前主持人需要预估每个问题的讨论时间,当一个问题暂时无法达成决议时,及时转到下一个问题。如果会议不能完全达成目的,至少也要达成部分目的。对于有争议的部分,可以放到会议结尾时,如果与会者还有时间,可以继续讨论这个问题。 问题:会议没有达到预期目的 场景:会议没有达到预期目的的原因有很多,例如在一个问题上花费太多时间、重要人员没有参与、人员中途离场、主持人会议主持力度不够等 解决:会议主持人应该牢记会议的目的,并且在会议过程中不断审视会议目的,以及目前所做的事情是否对达成目的。 问题:会议决议不明 场景:会议中,似乎大家都达成了共识,但是在会后好像又很难说达成了什么决议,每个人都不清楚自己要做什么。 解决:做好会议记录,在会议结束时做好会议总结,并且会议结束后以邮件或者书面形式将会议纪要和决议发送到各个与会者。 问题:会议决策落实不力 场景:会议结束后,每个人对于自己需要做什么或者需要给别人什么协助都不清楚,每个人都在被动的等待其他人的催促。 解决:会议结束后以邮件或者书面形式将会议纪要和决议发送到各个与会者,明确每人的职责以及时限。会后需要定期跟踪关于会议决议的落实情况。 问题:与会者对于会议反映冷淡 场景:会议上,大家发言极少,或者大部分成员发言极少。 解决:有时候,这是由于大家准备不足造成的,另外一些时候,则是由于人员性格造成的。会议主持人应当尽量让每个人都发言,控制经常发言的人的发言次数,增加不发言或者少发言的人的发言次数。 问题:会议陷入争执 场景:在某个问题上各方无法达成共识。 解决:如果时间充足,首先在解决问题的目的上达成一致,然后以目的为导向主持分析问题各种解决方案的优缺点,以期望双方达成共识。如果时间紧张,暂时停止讨论这个问题,先讨论以后的问题。
许多人都以为项目经理总是与“理想与光荣”相伴的,其实作为一个有志于改进中国软件开发流程的项目经理来说,他们承担的更多的是“艰辛与痛苦”。 在这里,我通过我担任项目经理期间所遇到的种种现象,来总结项目经理所必需具备的素质,当这些素质您不具备的话,就需要花费多年的努力来培养他,如果无法培养成功,那么请您转换岗位,因为项目经理不适合您,您难以在这个方面获的成功。 一、执着 可以这么说,在中国如果不执着是做不成任何事情的,因为在软件开发流程中推行各种规范和管理制度的时候,你可能遇到各种各样的阻力和障碍,如果没有应付挫折的思想和准备,你是很难推行成功的。要知道这样一个基本事实,项目管理成败的关键是:如果你不坚持,谁也不会坚持下去的。指望领导的扶持和群众的自觉是不可能的。只有坚定信念,努力打动别人,才能成功。 二、亲和力 亲和力是指你和团队相互依赖,相互信任能力的大小。亲和力是你领导团队走向成功的基础,如果一个团队的向心力不够,各自为政,那么失败就会在身边陪伴你。要团队的每个成员都信任你,你必须要做到关心下属,主动与下属沟通,为下属争取合法权利等。关心下属就是在日常工作中对下属的工作状况,发展方向进行指导,避免其走弯路;在生活中也对其身体状况进行关心,促进身体和心理健康的恢复。 多找下属沟通是消除误会的润滑剂,同时也是了解下属内心真实想法唯一捷径。做项目经理的人,在某些事情上的处理的确会与人不同,也难以令人理解。这个时候只有多与下属沟通,逐步达成共识,争取大家的理解和支持。记住,没有下属的理解和支持,你永远无法实现项目管理的规范化。这个环节很重要,我在这个方面曾经用时太少,走了许多弯路。另外就是了解下属的真实想法,经常了解一下下属的真实想法有利于我们不断改进和调整流程,使生产流程更加符合本团队的实际。切记一点,做领导的一定要多尊重下属的想法,并且与之沟通,若一味等下属找自己,那么是一般下属与之水火不容要摊牌时,才会与你沟通,这样悔之晚矣。 为下属争取合法权利是项目经理的一项重要职责。敢负责任是项目经理基本素质,如果你不经常研究工作数据保障下属的合法权益时,你就很难让你的团队保持高效率。曾经有一次,我们测试工程师的工作业绩突然下降了一半,我与之沟通后发现公司不讲效率只讲工作时间,他有一天特殊没上班,结果公司扣了一天的工资;但是他其实超额完成了月计划的120%。了解情况后,我与公司协调,顺利补回工资,生产效率就大幅上扬。 三、品德高尚 “一撇一捺是个人,世世代代学做人。”在这个世界上最难做的就是做个品德高尚的人。试想一个思想猥亵的人很难取得成功,即使靠钻营取得也只是暂时的,他不可能取得长久的成功。只有品德高尚的人才能感染周围的人,使团队具有向心力,从成功走向成功。 人有三种,一种是仗势欺人,一种是持才压人,最后一种是以德服人。仗势欺人的人自持地位高而指三道四,自然是不可能团结人,更不可能获得成功;持才压人的人自持学识高而盛气凌人,或咄咄逼人。殊不知“闻到有先后,术业有专攻”,“尺有所长,寸有所短”,难以学到更高的知识,也就难以取得更大的成功。只有以德服人的人以自己的修养和品德感染人,勇于吃亏,乐于助人,以德报怨,只有这样才能使你对立面德人都不忍心伤害你,团结到一切可以团结到的人,拥有这样的环境,你怎么可能不成功。 勇于吃亏,首先要放下私心,如果一个人始终 围着自己转的人是不可能做到的。“人不为己,天诛地灭”是八十年代后出生的人心灵普遍反应;但是要记住人首先是社会中的人,如果脱离了社会,人恐怕已不会成其为人了。因此只有当你抛弃私心,主动为人,别人才会反过来支持你,帮助你。 乐于助人,是人类的一个良好品质,就象一首歌中所唱的“人字的结构就是相互支撑”。管理流程是不可能靠项目经理一个人维持的,必须要大家支持你。但是这却需要你多帮助别人,别人才会帮助你。不管团队成员发生什么事情,你要尽你所能去帮助他,这样团队才可能继续前进。 以德报怨,可能是人最难做到的。中国人就强调“人若犯我,我必犯人”,其实在这回中不会有真正的仇敌,大家明争暗斗的结果如果过20年后再去看的时候,保准一大半的人都会觉得不值得,许多人赌得就是一口气,将自己成功的希望给湮灭了。当你能用宽容喝善良对待你对立面的人的时候,还有什么东西能阻挡你成功? “得道多助,失道寡助;多助之至,天下顺之,失道之至,亲戚叛之;以天下之所顺,攻亲戚之所叛;故君子有不战,战必胜矣。” 四、口才 良好的口才是项目经理打动项目成员的必备武器,当你拥有良好的口才将会使你无往不利。当年希特勒就是用他那天才般的口才征服了德国,使他的《我的奋斗》贯彻到每一个德国人的心中,从而成立了第三帝国。 要使自己的项目管理思想贯彻到每一个项目成员心中,就必须要做到以下的演讲原则: 1.根据项目成员的共同目标象他们制定演讲内容,只有让他们信服你才有意义; 2.调动听众的这种感官,诉之触觉、视觉、听觉,用黑板、姿势来辅助你的内容。 3.不断的总结效果,改进自己演讲宣传的接受度,如果效果不理想,尝试换一个方式来表达和描述。 4.让听众学以至用,只有他们积极反馈,才能更深入的听你的思想。 五、循序渐进 循序渐进,不急于求成是项目经理在项目管理中必需具备的品质,在中国CMM过程改进的热潮中,真正实现CMM管理的企业屈指可数,而以CMM改进过程实质性为企业带来质量提升和效益改进的公司更是寥落晨星。 为什么会出现这种情况?难道CMM真的不适应中国过情吗?不是,绝对不是。是这些企业的项目经理太心急,连CMM2还不知道怎么回事就直奔CMM3,他们忽视了事务发展的客观规律,凡事必须循序渐进。如果有一个企业在2年内通过了CMM4,我有十足的信心说,那是花钱买征;如果乐观一点,一个中小企业从CMM1走到CMM2大约要2年时间,大型企业只会更长,不会更短,因为他们需要在培训和沟通上付出更大的代价。 就以我所在公司来说,技术部原来只有10任,后来培训CVS版本管理到精通花费了1年,然后才上CVSTrac变更和过程管理,花费了3个多月,然后再实施Finabuild管理花费了3个月,最后改进CVSTrac成CVSProduce管理开发过程并统计花费了半年,其间成立了QA管理部门,并增加了项目专职管理人员,部门人数已经增加到16人,还在不断扩充中。我们的感觉管理越科学化、流程化,所需的分工就越细,人员也就越多。同事培训和做通这些人的思想工作的成本就越大。开发管理软件的成本也会随之上升。当所有人都能接受流程管理并持续改进时,大约2年光阴也就过去了。“循序渐进,循序渐进,再循序渐进。”这句巴斯德德经典名言同样适用于我们项目管理领域,他将逐步把我们带向成功。 六、持久求学 “书到用时方恨少,学至成时始知卑。”学无止境,我在生产实践中发现,整个项目管理过程改进就是“学习-培训-实施-发现问题-再学习”的循环过程,项目经理如果不学习将不能解决现实工作中出现的新问题,更不可能站在一个战略的角度来解决问题。 事实上,求学也不能没有目标,否则学到的知识太庞杂,而不能融会贯通,这样的知识对实际工作指导甚少,真正的知识是一个目标体系,严格按照流程来一步步的掌握我们所需要的知识。 最后,我总结一下中国项目经理所必需掌握的知识: 1.专业知识:数据结构、关系数据库、操作系统、软件工程、编译原理。(外国的项目经理可能不需要掌握) 2.管理知识:项目计划、项目配置管理、成本核算、风险预估、绩效考核。这是项目经理必须掌握的内容。 3.网络知识:服务器的架构、各种服务的配置。因为管理的大厦是基于软件的管理,没有一个服务管理的网络配合是不可以想象的。 4.“越过高峰,另一峰却又现”,这是中国项目经理在持续求学中会不停的挑战自我,向更高的山峰迈进。 七、敢负责任 一个人因为有责任才有生存的意义。一个人随着年龄的增长,责任感也会愈来愈重。成年时,法律也会赋予一些年少时没有的责任。同时地位逐渐提高,责任也会相对加重。 一个人惟有负责,才能产生做人的价值。所负责任愈大,价值就愈高。换句话说,有责任,生命才有意义。如果没有感受到自己该负的责任,即使年龄超过20岁,也不算是一个成年人。 因此,经理就是要负责任,如果不负责任就可以不要经理了!项目经理关系到一个项目的成败;对于公司他必须要承担及时汇报项目进度、成本核算和质量系数的责任,同时也必须保证项目组成员绩效考核,政策落实,预留人才储备等责任,是整个项目中责任最大的人,如果没有良好的心理素质和应对能力是无法担负责任的。 实际工作中项目经理主要要负责项目组的人员安排调度、工作分配、工作审核、工作跟踪、项目计划、项目汇报总结、成本核算、利润分配等职责。 八、以身作则 项目管理的一个重要工作就是定义各种规范和制定,但是这些规范和制度的执行除了靠项目经理的执着推行,口才宣传,力主培训、惩戒得当之外,关键还是在于项目经理的以身作则。如果项目经理自己都违反自己定义的条款的话,那么就别指望团队会自觉遵守这些规定。 作为一个管理者以身作则是最基本的素质,千万不要为自己违反规范和制度找各种借口,例如我我是公司只属考核,我因为某某更重要的事情而不得不违反。“只许周宫放火,不许百姓点灯”的话,是无法将规范和制度推入人心的。项目经理如果违反了规范,只有当众加重处罚,别无他法。说个真实的事例。我从事项目经理工作之前经常迟到,结果不久全技术部人隔三岔五的迟到。我当项目经理后执行晨会制度,早上到公司头半个小时总结一下昨天工作,探讨一下今天的工作,但是因为习惯,有人总是迟到。而我开始从不迟到,有一点为了赶时间我长跑去买早餐以在晨会规定时间之前到公司,被许多团队成员看见。以后就再没人迟到,直至项目结束。 因此,鉴于规范制度的权威性主要还是靠项目经理自己,只有坚持以身作则,才能将自己优秀的管理思想贯穿下去,取得开发过程改进的成功。 九、要有威信 一个项目经理说话有没有人听,必须要靠威信,这种威信是靠自身的素质,而不是狐假虎威。靠高层领导的支持来强迫团队执行项目制度过程的话,是注定会失败的。因为团队成员不信任你,表面服从,实际消极怠工,就足以让流程实质瘫痪。 做事要有信用,说一不二,不能因为朋友关心就讲情面。公是公,私是私。平时可以稀稀拉拉,关键问题决不手软,不因为朋友关系妥协,这样才能树立威信,便于工作。 威信除了必要的威信之外,最主要的还是信用,项目经理在做事没有绝对把握的时候千万不要承诺,一旦承诺就无论如何一定要实现。否则,当实现不成功而丢失信用之后,再想让团队相信你,信任你就是非常困难的事情了。 十、善于总结 项目经理要善于总结,只有不断的总结才能不停的完善自己,成功的事情总结经验,失败的事情要总结教训,总结的过程就是不断改进的过程,这也是CMM规范所必需的素质。 总结的过程要多吸取别人的意见,不要武断自己的结论。博人所长,综合起来才算趋于完美。这个原因有二:其一,项目经理不是孤立的一个人,而是必须融于团队之中,一个流程合不合理,不是由项目经理说了算,而是要由团队的成员说了算,注意倾听团队成员的真实感受,不断改进流程才能成功。中国的许多CMM改进失败,并不是项目经理知识能力不够,而是他们没有一起与团队总结,经多年经验,我们发现大多数规范,必须要有一套合理的软件支持才能成功,否则无论你的理想多先进,想靠程序员工作来提高过程质量的改进是不现实的。其二,“闻道有先后,术业有专攻”,项目经理不可能是全才,什么都懂。因此要和哪些与专攻方向不同的人一起总结。比如项目经理可能精通软件开发流程的改进,但是却不知道测试流程、网络管理流程、品质保证流程的改进,而这些流程又直接作用于软件开发流程。这个时候必须与测试人员、网管人员、质量保证人员共同探讨,找出一条切实可行的改进方案。 项目经理除了必须具备以上素质外,还必须要有珍惜时间、要有勇气、善于倾听等基本素质,我就不一一描述了,只有寄希望于大家在做项目经理的时候不断的培养完善自己,让软件开发流程不断获得改进。