上一节我们讲述了信息共享系统的服务端,今日我们讲一下信息共享系统的客户端。
为了不展示用户信息,我就把该不展示的地方给抹了。
OK,就是这个界面,这个界面号称客户端,可以部署多份,假如污一点,这个系统用在洗浴中心。那么可以在前台部署一份,可以在二楼休息室部署一份,可以在三楼餐厅部署一份,可以在四楼舞厅部署一份。当消息从管理后台发出以后,这些地方都可以同时收到消息,并进行语音播。如果四楼存在黄色交易,就可以很快收到通知,紧急撤离。下来我们就模拟一下警察突击洗浴中心,很刺激。
假如老板在后台发布消息如下
那么四个楼层都可以收到消息,并语音播报。
这样其实老板也不用挨个打电话通知了,一下子大家就都听见了。该跑的就跑了,哈哈。
接下来我们看一下代码,如下。
private async void FrmMessageDisplay_Load(object sender, EventArgs e) { var messageServer = await MessageManageBiz.Instance.GetMessageServer(); if (messageServer == null) { this.ShowMessage("消息服务器地址信息未配置,请先进行配置!"); this.Close(); return; } UnitID = (await UnitManageBiz.Instance.GetUnitInfoExactByName(ownUnitName)).GetValueOrDefault(0); if (UnitID <= 0) { this.ShowMessage("部门信息配置不正确,请重新配置!"); this.Close(); return; } this.ServerURI = string.Join(":", messageServer.IPAddress, messageServer.Port); this.ServerURI = string.Concat("http://", ServerURI); await ConnectAsync(); }
首先form加载的时候,检查服务配置,以及当前部门配置是否正确,这个部门是在后台维护的即刚才讲到的前台,歌舞厅等。
取到配置以后,就是连接。
private async Task ConnectAsync() { Connection = new HubConnection(ServerURI); Connection.Closed += Connection_Closed; HubProxy = Connection.CreateHubProxy("MessageHub"); HubProxy.On<string, string, string, List<string>>("AddMessage", (title, message, currentUnitName, units) => this.Invoke((Action)(() => { if (units.Contains(ownUnitName)) { this.timer_Flow.Enabled = false; this.ClearListViewBackColor(); this.lv_MessageTitle.Items.Insert(0, string.Concat(currentUnitName ?? string.Empty, "(", title, ")"), 0).Tag = message; this.lv_MessageTitle.Items[0].Selected = true; this.lv_MessageTitle.Items[0].BackColor = Color.AliceBlue; this.txt_Message.Text = message; try { Application.DoEvents(); ClsMCI cm = new ClsMCI(); cm.FileName = string.Concat(AppDomain.CurrentDomain.BaseDirectory, "Resources\\1.mp3"); cm.play(); Thread.Sleep(3000); //voice.Speak(string.Concat(currentUnitName, ",通知,", message)); //voice.WaitUntilDone(120000); this.timer_Flow.Enabled = true; speechSync.Speak(string.Concat(currentUnitName, ",通知,", message)); } catch { this.timer_Flow.Enabled = true; } } } ))); try { Connection.Headers.Add("UnitName", Uri.EscapeDataString(ownUnitName)); await Connection.Start(); this.InitDefaultMessage(); this.toolStripStatusLab_Status.Text = "连接成功."; this.toolStripStatusLab_Status.ForeColor = Color.Green; this.toolStripSplitBtn_Reconnect.Visible = false; } catch (Exception) { MessageBox.Show("连接失败!请检查网络是否畅通,配置是否正确!"); this.toolStripSplitBtn_Reconnect.Visible = true; } }
监听到消息来了以后,如果发送消息的部门中包含当前部门,就将消息插入列表,然后播放MP3,MP3其实是一个提示作用,比如地铁快到站了,先有个提示音,再提示前方到达大雁塔站,下一站体育场站。因为这个MP3大概三秒,所以我们sleep 3s。
播放完MP3之后,开始文字转语音,直接用微软TTS。这里解释一下,如果是win10,则自带文字转语音识别功能,而且发音停顿非常好。如果是win7,则需要安装TTS Repair,否则不能播放。
voice.Speak(string.Concat(currentUnitName, ",通知,", message));
上面这句是针对Win7的,而win10直接使用SpeechSynthesizer类即可,该类在SpeechLib命名空间下。
OK,在这里需要强调的是如果存在中文,最好转义一下
Connection.Headers.Add("UnitName", Uri.EscapeDataString(ownUnitName));
那么有的人想如果消息多了的话,怎么办?如果客户端断电以后,再启动起来客户端还能否显示没电期间的消息。当然,这点容错机制还是有的。
private async Task InitDefaultMessage() { var messageList = await MessageManageBiz.Instance.GetDisplyMessageByUnit(UnitID); this.txt_Message.Clear(); if (messageList == null || messageList.Count() == 0) return; foreach (var message in messageList) { lv_MessageTitle.Items.Add(string.Concat(message.PublishUnitName ?? string.Empty, "(", message.MessageTitle, ")"), 0).Tag = message.MessageContent; } lv_MessageTitle.Items[0].Selected = true; }
首先刚启动起来我们就会抓取今天发给我们的消息并将其放到我们的列表中。
SELECT TOP(20) A.Title AS MessageTitle, A.Content AS MessageContent, D.Name AS PublishUnitName FROM dbo.Message A WITH(NOLOCK) INNER JOIN dbo.MessageSend B WITH(NOLOCK) ON A.ID = B.MessageID LEFT JOIN dbo.Customer C WITH(NOLOCK) ON A.InUser = C.UserNo LEFT JOIN dbo.Unit D WITH(NOLOCK) ON C.UnitID = D.ID WHERE B.UnitID = @UnitID AND CONVERT(VARCHAR(100),GETDATE(),23) = CONVERT(VARCHAR(100),A.InDate,23) ORDER BY A.InDate DESC
那么消息如果太多怎么办,首先语音播报只是一次,不然一天一直循环播放,人都听的烦死了。所以语音播放只是一次,但是我们的消息会一直轮播,怎么轮播,当然是用timer,每隔30s轮播一次。
private async void timer_Flow_Tick(object sender, EventArgs e) { if (now.Date != DateTime.Now.Date) { now = DateTime.Now; timer_Flow.Enabled = false; lv_MessageTitle.Items.Clear(); await InitDefaultMessage(); timer_Flow.Enabled = true; } if (this.lv_MessageTitle.Items.Count <= 0) return; if (lv_MessageTitle.SelectedItems.Count == 0) { lv_MessageTitle.Items[0].Selected = true; return; } lv_MessageTitle.BeginUpdate(); this.ClearListViewBackColor(); for (int i = 0; i < lv_MessageTitle.Items.Count; i++) { if (lv_MessageTitle.Items[i].Selected) { if (i == lv_MessageTitle.Items.Count - 1) //最后一条 { lv_MessageTitle.Items[0].Selected = true; lv_MessageTitle.Items[0].BackColor = Color.AliceBlue; break; } else { lv_MessageTitle.Items[i + 1].Selected = true; lv_MessageTitle.Items[i + 1].BackColor = Color.AliceBlue; break; } } } lv_MessageTitle.EndUpdate(); }
如果timer中检测到日期有变化,比如之前是12-8号,但是现在发现到了12-9号,那么就会清除列表,并加载今天的消息。如果今天的消息只有一条,则不进行轮播。如果可以轮播,则将当前的轮播消息的背景色变一下。注意这里在更新ListView的,为了避免UI频繁重绘,请使用BeginUpdate和EndUpdate。
OK,关于客户端的解说就到这里,下一篇,我们看看后台管理系统。
下一篇 .NET OCR 图片文字识别