一个朋友接了个case,但是却临时有事要出差,所以就把部分的功能叫我来做。以 前一直注重于web编程,对于串口编程还是比较陌生的,还好网上的文章不少。当然比较好的文章,有直接帮助的文章还是要数cnblogs里面的文章。把这 写到博客里便于以后自己查阅,希望也能够帮到需要帮助的人。
这里主要是讲用serailPort来实现modem的来电显示。
来电显示要依赖于具体的modem,不同的modem,可能AT指令不一样,所以需要定义一个接口,将应用程序与硬件的耦合进行解耦。
public interface IModem { void Open(); void Close(); bool SupportAT { get; } bool SupportCID { get; } event EventHandler<RingEventArgs> Ring; } 这个接口定义了open,close方法,还有是否支持AT指令,是否支持来电显示,还有一个响铃事件。
RingEventArgs里面包含了来电的电话号码。在写这个IModem的实现类时,使用的是笔记本的modem,也不知道是什么型号的,就直接用modem来命名。 public class Modem : IModem { public event EventHandler<RingEventArgs> Ring; System.IO.Ports.SerialPort port = null; public Modem() { port = new System.IO.Ports.SerialPort(); port.PortName = Settings.Instance.GetValue("PortName", "COM3"); port.BaudRate = Settings.Instance.GetValue("BaudRate", 460800); port.DataBits = 8; port.Parity = Parity.None; port.StopBits = StopBits.One; port.ReadTimeout = port.WriteTimeout = 1000;//1S port.ReadBufferSize = port.WriteBufferSize = 1024;//1K port.Handshake = Handshake.None; port.ReceivedBytesThreshold = 10; port.RtsEnable = true; port.DtrEnable = true; port.NewLine = "\r"; this.port.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(port_DataReceived); } 上面就是端口的配置,不同的modem可能配置不同,需要根据实际的modem进行设置。
public void Open() { if (!this.port.IsOpen) this.port.Open(); if (!SupportAT) throw new Exception("设备不支持AT指令"); if (!SupportCID) throw new Exception("设备不支持来显"); } public void Close() { if (this.port.IsOpen) this.port.Close(); } public bool SupportAT { get { this.port.WriteLine("AT"); System.Threading.Thread.Sleep(500); string result = this.port.ReadExisting(); return result.ToUpper().Contains("OK"); } } public bool SupportCID { get { string[] commandList = Settings.Instance.SectionValues("CID"); string result = string.Empty; foreach (string command in commandList) { this.port.WriteLine(command); System.Threading.Thread.Sleep(500); result = this.port.ReadExisting(); if (result.ToUpper().Contains("OK")) { return true; } } return false; } } 这是一些方法和属性的实现。需要注意的是在发出指令后,最好能将线程堵塞一段时间,以保证有硬件足够的时间来响应,保证能够接收到完整的回复信息。这里的System.Threading.Thread.Sleep(
500 );就是这个目的。 void port_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e) { string phoneNumber = this.port.ReadExisting(); Regex r = new Regex(@"NMBR\s*=\s*[0-9]*"); Match m = r.Match(phoneNumber); string result = string.Empty; if (m != null && m.Success) { result = m.Value; result = result.Substring(result.IndexOf("=") + 1).Trim(); } if (string.IsNullOrEmpty(result)) return; if (Ring != null) { RingEventArgs eventArgs = new RingEventArgs(); eventArgs.PhoneNumber = result; foreach (EventHandler<RingEventArgs> handler in Ring.GetInvocationList()) { handler.Invoke(this, eventArgs); if (eventArgs.Handled) break; } } }
Ring 响铃事件实际上是根据DataReceived接收到的数据进行处理,比如判断是否是来电,截取来电的号码,然后事件冒泡,触发Ring事件。不同的 modem返回回来的值肯能是不一样的。上面的正则表达是可能需要再次更改。(网上查到的是 = 号的左右有空格,可是我调试的那个modem却没有)
SerialPort 是.net 2.0才出现的。如果是.net 1.1的话可能需要使用MSCOMM