Imports System.IO.Ports Imports System.Text Imports System.Threading Imports Newtonsoft.Json Imports StackExchange.Redis Imports Telephone.PhoneBurstification Public Class Telephone 'hex 发送标志 Public G_Hexflg As Boolean = False '订阅字段 Public G_Subscribe As String = "" Public G_OldSubscribe As String = "" '发布字段 Public G_Publish As String = "" ' Public G_RedisSub, G_Redislish As RedisSubscriber 'redis消息队列 Public M_RedisQueue As Queue '事件处理线程 Public M_EventThread As Thread ' Public G_SendBuffnode As SendBuffnode '日志对象 Public G_Log As RuningLog Private Sub Telephone_Load(sender As Object, e As EventArgs) Handles MyBase.Load '设置页面标题为软件名称加版本号 Me.Text = Application.ProductName & " " & Application.ProductVersion G_Hexflg = False recvBufferli = New List(Of Byte)() G_Log = New RuningLog(RichTextBox1, Application.StartupPath & "\log") ComboBox1.Items.Clear() ComboBox1.Items.AddRange(SerialPort.GetPortNames()) GetSeting() initRedisSubscriber(G_Subscribe, G_OldSubscribe) initRedisPublisher() M_RedisQueue = New Queue() M_EventThread = New Thread(AddressOf EventThread) M_EventThread.Start() Button1.PerformClick() End Sub Public Sub EventThread() Dim msgCount As Integer '获取发短信的集合并且按时间排序 Dim sendShortMessagesLi As New List(Of CallInfoNode) Dim callLi As New List(Of CallInfoNode) Dim item As CallInfoNode While True If M_RedisQueue.Count > 0 Then '遍历队列 msgCount = M_RedisQueue.Count sendShortMessagesLi.Clear() callLi.Clear() For i As Integer = 0 To msgCount - 1 item = M_RedisQueue.Dequeue() If IsNothing(item) Then Continue For If item.Type.Trim.Equals("1") Then callLi.Add(item) ElseIf item.Type.Trim.Equals("2") Then sendShortMessagesLi.Add(item) End If Next '判断是否有短信发送 If sendShortMessagesLi.Count > 0 Then '发送短信 SendShortMessage(sendShortMessagesLi) sendShortMessagesLi.Clear() End If '判断是否有电话拨号 If callLi.Count > 0 Then '拨打电话 CallPhone(callLi) callLi.Clear() End If End If Thread.Sleep(10) End While End Sub '发送短信 Public Sub SendShortMessage(sendShortMessagesLi As List(Of CallInfoNode)) '遍历sendShortMessagesLi Dim gLogNode As LogNode For Each item In sendShortMessagesLi '获取当前时间的utc秒值 Dim nowt As Long = CLng(DateTime.UtcNow.Subtract(New DateTime(1970, 1, 1, 0, 0, 0, 0)).TotalSeconds) '判断是否超时 If item.DeadLine < nowt Then Continue For End If gLogNode = New LogNode($"开始执行{item.CallerName}发送短信:{item.PhoneNumber}", RuningLog.LogType.e_info, RuningLog.LogMode.e_consoleandRichTextBox) G_Log.AddLogNode(gLogNode) If ProcessshortmessagesData(item) Then RedisPublishMessage($"执行{item.CallerName}发送短信:{item.PhoneNumber}成功") gLogNode = New LogNode($"执行{item.CallerName}发送短信:{item.PhoneNumber}成功", RuningLog.LogType.e_info, RuningLog.LogMode.e_consoleandRichTextBox) G_Log.AddLogNode(gLogNode) Else RedisPublishMessage($"执行{item.CallerName}发送短信:{item.PhoneNumber}失败") gLogNode = New LogNode($"执行{item.CallerName}发送短信:{item.PhoneNumber}失败", RuningLog.LogType.e_Error, RuningLog.LogMode.e_consoleandRichTextBox) G_Log.AddLogNode(gLogNode) End If Next End Sub Public Function ProcessshortmessagesData(CallInfoNode As CallInfoNode) As Boolean Dim li As List(Of SendBuffnode) Dim gLogNode As LogNode Dim SendData As Byte() li = PhoneBurstification.PhoneBurstification(PhoneBurstification.PhoneBuffType.SendSMS, CallInfoNode) For Each item As SendBuffnode In li If SerialPort1.IsOpen Then G_SendBuffnode = item SendData = publicMode.StringToByte(item.SendData) SerialPort1.Write(SendData, 0, SendData.Length) SendBuffnode.SetSendStatus(item, SendBuffnode.SendStatustype.Sending) gLogNode = New LogNode($"TX:{item.SendData}", RuningLog.LogType.e_info, RuningLog.LogMode.e_both) G_Log.AddLogNode(gLogNode) Else gLogNode = New LogNode("串口未打开!!", RuningLog.LogType.e_Error, RuningLog.LogMode.e_both) gLogNode.SetLogColor(Color.Red) G_Log.AddLogNode(gLogNode) Return False End If While True If G_SendBuffnode.IsNodeReceiveTimeout2(G_SendBuffnode) Then If G_SendBuffnode.ReceiveStatus = True Then If G_SendBuffnode.CommandType = comdType.comdmsg Then Thread.Sleep(100) Else Thread.Sleep(100) End If Exit While 'Select Case G_SendBuffnode.CommandType ' Case comdType.comdCDTAM ' If G_SendBuffnode.ReceiveResult = True Then ' Exit While ' End If ' Case comdType.comdCMGF ' Exit While ' Case comdType.comdCSCA 'End Select End If Else gLogNode = New LogNode($"发送超时命令:{item.SendData}!!", RuningLog.LogType.e_Error, RuningLog.LogMode.e_fileandRichTextBox) gLogNode.SetLogColor(Color.Red) G_Log.AddLogNode(gLogNode) Return False End If End While Next Return True End Function '拨打电话 Public Sub CallPhone(callLi As List(Of CallInfoNode)) '遍历callli Dim gLogNode As LogNode For Each item In callLi '获取当前时间的utc秒值 Dim nowt As Double = CInt(DateTime.UtcNow.Subtract(New DateTime(1970, 1, 1, 0, 0, 0, 0)).TotalSeconds) '判断是否超时 If item.DeadLine < nowt Then Continue For End If gLogNode = New LogNode($"开始执行{item.CallerName}播打电话:{item.PhoneNumber}", RuningLog.LogType.e_info, RuningLog.LogMode.e_consoleandRichTextBox) G_Log.AddLogNode(gLogNode) If ProcessData(item) Then RedisPublishMessage($"执行{item.CallerName}播打电话:{item.PhoneNumber}成功") gLogNode = New LogNode($"执行{item.CallerName}播打电话:{item.PhoneNumber}成功", RuningLog.LogType.e_info, RuningLog.LogMode.e_consoleandRichTextBox) G_Log.AddLogNode(gLogNode) Else RedisPublishMessage($"执行{item.CallerName}播打电话:{item.PhoneNumber}失败") gLogNode = New LogNode($"执行{item.CallerName}播打电话:{item.PhoneNumber}失败", RuningLog.LogType.e_Error, RuningLog.LogMode.e_consoleandRichTextBox) G_Log.AddLogNode(gLogNode) End If Next End Sub Public Function ProcessData(CallInfoNode As CallInfoNode) As Boolean Dim li As List(Of SendBuffnode) Dim gLogNode As LogNode Dim SendData As Byte() li = PhoneBurstification.PhoneBurstification(PhoneBurstification.PhoneBuffType.CallPhoneAndPlay, CallInfoNode) For Each item As SendBuffnode In li 'If item.CommandType <> comdType.comd1A Then Continue For '判断串口是否打开 If SerialPort1.IsOpen Then G_SendBuffnode = item SendData = publicMode.StringToByte(item.SendData) SerialPort1.Write(SendData, 0, SendData.Length) SendBuffnode.SetSendStatus(item, SendBuffnode.SendStatustype.Sending) gLogNode = New LogNode($"TX:{item.SendData}", RuningLog.LogType.e_info, RuningLog.LogMode.e_both) G_Log.AddLogNode(gLogNode) Else gLogNode = New LogNode("串口未打开!!", RuningLog.LogType.e_Error, RuningLog.LogMode.e_both) gLogNode.SetLogColor(Color.Red) G_Log.AddLogNode(gLogNode) Return False End If While True If G_SendBuffnode.IsNodeReceiveTimeout(G_SendBuffnode) Then If G_SendBuffnode.ReceiveStatus = True Then Select Case G_SendBuffnode.CommandType Case comdType.comdCallPhone If G_SendBuffnode.ReceiveResult = True Then Thread.Sleep(1000) Exit While Else gLogNode = New LogNode($"电话已经挂断!!!", RuningLog.LogType.e_Error, RuningLog.LogMode.e_fileandRichTextBox) gLogNode.SetLogColor(Color.Red) G_Log.AddLogNode(gLogNode) Return False End If Case comdType.comdSendSMS Exit While Case comdType.comdCallPhoneAndPlay If G_SendBuffnode.ReceiveResult = True Then If G_SendBuffnode.SendCurrentCount >= 10 Then SendData = publicMode.StringToByte("AT+CHUP" & vbCrLf) SerialPort1.Write(SendData, 0, SendData.Length) gLogNode = New LogNode("主动挂掉电话!", RuningLog.LogType.e_Error, RuningLog.LogMode.e_fileandRichTextBox) gLogNode.SetLogColor(Color.Red) G_Log.AddLogNode(gLogNode) Return False Else Thread.Sleep(5000) If SerialPort1.IsOpen Then SerialPort1.Write(SendData, 0, SendData.Length) SendBuffnode.SetSendStatus(item, SendBuffnode.SendStatustype.Resend) gLogNode = New LogNode($"TX:{item.SendData}", RuningLog.LogType.e_info, RuningLog.LogMode.e_fileandRichTextBox) G_Log.AddLogNode(gLogNode) Else gLogNode = New LogNode("串口未打开!!", RuningLog.LogType.e_Error, RuningLog.LogMode.e_fileandRichTextBox) gLogNode.SetLogColor(Color.Red) G_Log.AddLogNode(gLogNode) Return False End If End If Else If G_SendBuffnode.CommandType = comdType.comdCallPhoneAndPlay AndAlso Not G_SendBuffnode.ReceiveData.Contains("VOICE CALL: END") Then Continue While End If gLogNode = New LogNode($"电话已经挂断,停止发送语音!!", RuningLog.LogType.e_Error, RuningLog.LogMode.e_fileandRichTextBox) gLogNode.SetLogColor(Color.Red) G_Log.AddLogNode(gLogNode) Return False End If Case Else Thread.Sleep(50) Exit While End Select End If Else gLogNode = New LogNode($"发送超时命令:{item.SendData}!!", RuningLog.LogType.e_Error, RuningLog.LogMode.e_fileandRichTextBox) gLogNode.SetLogColor(Color.Red) G_Log.AddLogNode(gLogNode) Return False End If '判断接收状态 Thread.Sleep(10) End While Next Return True End Function '获取系统缓存参数 Public Sub GetSeting() '刷新系统缓存 My.Settings.Reload() '获取系统缓存参数 ComboBox1.Text = My.Settings.SerialPortName ComboBox2.Text = My.Settings.BaudRate If My.Settings.THigh = 0 OrElse My.Settings.TWide = 0 Then Else Me.Height = My.Settings.THigh Me.Width = My.Settings.TWide End If G_Subscribe = My.Settings.G_Subscribe G_Publish = My.Settings.G_Publish ToolStripTextBox2.Text = G_Subscribe ToolStripTextBox1.Text = G_Publish Dim dic As Dictionary(Of Integer, (String, String, String)) dic = JsonConvert.DeserializeObject(Of Dictionary(Of Integer, (String, String, String)))(My.Settings.GTable) If IsNothing(dic) OrElse dic.Count = 0 Then Return 'JsonConvert.SerializeObject(m_Applicationconfig) End Sub '保存系统缓存参数 Public Sub SaveSeting() '刷新系统缓存 My.Settings.Reload() '获取系统缓存参数 My.Settings.SerialPortName = ComboBox1.Text My.Settings.BaudRate = ComboBox2.Text My.Settings.THigh = Me.Height My.Settings.TWide = Me.Width My.Settings.G_Subscribe = G_Subscribe My.Settings.G_Publish = G_Publish Dim dic As New Dictionary(Of Integer, (String, String, String)) My.Settings.GTable = JsonConvert.SerializeObject(dic) My.Settings.Save() End Sub Private Sub ComboBox1_DropDown(sender As Object, e As EventArgs) Handles ComboBox1.DropDown '判断串口是否打开 If SerialPort1.IsOpen Then ComboBox1.DroppedDown = False MsgBox("串口已打开,请先关闭串口") '终止下拉 '收起ComboBox1 下拉 Return End If ComboBox1.Items.Clear() ComboBox1.Items.AddRange(SerialPort.GetPortNames()) End Sub Private Sub ComboBox2_DropDown(sender As Object, e As EventArgs) Handles ComboBox2.DropDown '判断串口是否打开 If SerialPort1.IsOpen Then MsgBox("串口已打开,请先关闭串口") Return End If End Sub Private Sub CheckBox1_CheckedChanged(sender As Object, e As EventArgs) Handles CheckBox1.CheckedChanged G_Hexflg = CheckBox1.Checked End Sub Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click If Button1.Text = "打开串口" Then Try SerialPort1.PortName = ComboBox1.Text SerialPort1.BaudRate = CInt(ComboBox2.Text) SerialPort1.Open() Button1.Text = "关闭串口" Button1.ForeColor = Color.Red Catch ex As Exception MsgBox("串口打开失败") End Try Else SerialPort1.Close() Button1.Text = "打开串口" Button1.ForeColor = Color.Black End If End Sub '使用串口发送数据 Public Function SerialPortSendData(nstr As String, Optional hexflg As Boolean = False) As Boolean If SerialPort1.IsOpen Then '判断数据是否为空 If String.IsNullOrEmpty(nstr) Then MsgBox("发送数据不能为空") Return False End If If hexflg Then Dim hex As String = StrToHex(nstr) 'RuningLog.OutputLogsToTheControl(RichTextBox1, New RuningLogConfig($"串口发送: {hex}", Color.Green, 12), 1) SerialPort1.Write(hex) Else nstr = nstr & vbCrLf 'RuningLog.OutputLogsToTheControl(RichTextBox1, New RuningLogConfig($"串口发送: {nstr}", Color.Green, 12), 1) SerialPort1.Write(nstr) End If Return True Else MsgBox("串口未打开") Return False End If End Function Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click If SerialPort1.IsOpen Then SerialPortSendData(TextBox2.Text, G_Hexflg) Else MsgBox("串口未打开") End If End Sub '将字符串转Hex 数据 Private Function StrToHex(ByVal str As String) As String Dim hex As String = "" For Each c As Char In str hex += Convert.ToString(Asc(c), 16).PadLeft(2, "0"c) & " " Next Return hex End Function '将Hex 数据转字符串 Private Function HexToStr(ByVal hex As String) As String Dim str As String = "" Dim hexs() As String = hex.Split(" "c) For Each h As String In hexs If h <> "" Then str += ChrW(Convert.ToInt32(h, 16)) End If Next Return str End Function Private Sub Telephone_FormClosed(sender As Object, e As FormClosedEventArgs) Handles MyBase.FormClosed SaveSeting() RedisClose() If Not IsNothing(M_EventThread) AndAlso M_EventThread.IsAlive Then M_EventThread.Abort() End If If Not IsNothing(G_Log) Then G_Log.CloseThread() End If End Sub Private Sub ToolStripButton3_Click(sender As Object, e As EventArgs) Handles ToolStripButton3.Click RichTextBox1.Clear() End Sub Private Sub RichTextBox1_TextChanged(sender As Object, e As EventArgs) Handles RichTextBox1.TextChanged If RichTextBox1.Lines.Length > 2147000000 Then RichTextBox1.Clear() End If End Sub Private Sub ToolStripButton4_Click(sender As Object, e As EventArgs) Handles ToolStripButton4.Click If String.IsNullOrEmpty(ToolStripTextBox2.Text) Then MsgBox("请输入订阅频道") Return Else G_OldSubscribe = G_Subscribe G_Subscribe = ToolStripTextBox2.Text.Trim End If If String.IsNullOrEmpty(ToolStripTextBox1.Text) Then MsgBox("请输入发布频道") Return Else G_Publish = ToolStripTextBox1.Text.Trim End If initRedisSubscriber(G_Subscribe, G_OldSubscribe) initRedisPublisher() End Sub '订阅初始化 ''' ''' ''' ''' ''' Public Sub initRedisSubscriber(Subscribe As String, OldSubscribe As String) If String.IsNullOrEmpty(Subscribe) Then MsgBox("未设置订阅频道") Return End If If Not IsNothing(G_RedisSub) AndAlso G_RedisSub.IsConnected Then ' 取消订阅并关闭连接 G_RedisSub.UnsubscribeFromChannel(OldSubscribe) G_RedisSub.CloseConnection() End If ' 替换为你的 Redis 连接字符串 Dim connectionString = "127.0.0.1:6379" Try G_RedisSub = New RedisSubscriber(connectionString) ' 定义消息处理程序 Dim messageHandler As Action(Of RedisChannel, RedisValue) = Sub(channel, message) Dim gLogNode As LogNode Dim g_CallInfoNode As CallInfoNode Try g_CallInfoNode = JsonConvert.DeserializeObject(Of CallInfoNode)(message) Catch ex As Exception 'MsgBox($"消息解析失败:{ex.Message}") gLogNode = New LogNode(ex.Message, RuningLog.LogType.e_Error, RuningLog.LogMode.e_fileandRichTextBox) gLogNode.SetLogColor(Color.Red) G_Log.AddLogNode(gLogNode) Return End Try gLogNode = New LogNode($"Redis:{channel}: {message}", RuningLog.LogType.e_info, RuningLog.LogMode.e_fileandRichTextBox) G_Log.AddLogNode(gLogNode) M_RedisQueue.Enqueue(g_CallInfoNode) 'Console.WriteLine($"接收到来自 {channel} 的消息: {message}") End Sub ' 订阅频道 G_RedisSub.SubscribeToChannel(Subscribe, messageHandler) Catch ex As Exception MsgBox($"订阅失败:{ex.Message}") End Try '' 发布消息示例 'Console.WriteLine("发布测试消息...") 'G_RedisSub.PublishMessage("testChannel", "Hello from publisher!") '' 保持程序运行以接收更多消息 'Console.WriteLine("按任意键退出...") 'Console.ReadKey() End Sub Private Sub ToolStripButton5_Click(sender As Object, e As EventArgs) Handles ToolStripButton5.Click RedisPublishMessage("20131415926") End Sub 'redis发布初始化 Public Sub initRedisPublisher() If Not IsNothing(G_Redislish) AndAlso G_Redislish.IsConnected Then G_Redislish.CloseConnection() End If ' 替换为你的 Redis 连接字符串 Dim connectionString = "127.0.0.1:6379" Try G_Redislish = New RedisSubscriber(connectionString) Catch ex As Exception MsgBox($"redis发布初始化失败:{ex.Message}") End Try End Sub '发布消息 Public Sub RedisPublishMessage(message As String) If String.IsNullOrEmpty(G_Publish) Then MsgBox("未设置发布频道") Return End If If String.IsNullOrEmpty(message) Then MsgBox("未输入发布内容") Return End If If G_Redislish.IsConnected Then Dim gLogNode As LogNode Try G_Redislish.PublishMessage(G_Publish, message) gLogNode = New LogNode($"Redis:{G_Publish}: {message}", RuningLog.LogType.e_info, RuningLog.LogMode.e_fileandRichTextBox) G_Log.AddLogNode(gLogNode) Catch ex As Exception MsgBox($"redis发布失败:{ex.Message}") gLogNode = New LogNode(ex.Message, RuningLog.LogType.e_Error, RuningLog.LogMode.e_fileandRichTextBox) gLogNode.SetLogColor(Color.Red) G_Log.AddLogNode(gLogNode) End Try Else MsgBox("redis未连接") End If End Sub Public IsRead As Boolean = False Public listening As Boolean = False Public recvBufferli As List(Of Byte) Private Sub SerialPort1_DataReceived(sender As Object, e As SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived '获取读缓冲区数据长度 Dim gLogNode As LogNode Dim n As Integer = SerialPort1.BytesToRead Dim recvBuffer As Byte() = New Byte(n - 1) {} SerialPort1.Read(recvBuffer, 0, n) If Not IsNothing(G_SendBuffnode) Then G_SendBuffnode.ReceiveStatusStateMachine(G_SendBuffnode, recvBuffer) 'SendBuffnode.SetSendStatus(G_SendBuffnode, SendBuffnode.SendStatustype.ReceiveSuccess) End If '输出到富文本 If G_Hexflg Then 'RuningLog.OutputLogsToTheControl(RichTextBox1, New RuningLogConfig($"串口接收到数据:{ByteToString2(recvBuffer)}", Color.Olive, 12), 1) gLogNode = New LogNode($"RX:{ByteToString2(recvBuffer)}", RuningLog.LogType.e_info, RuningLog.LogMode.e_fileandRichTextBox) G_Log.AddLogNode(gLogNode) Else 'RuningLog.OutputLogsToTheControl(RichTextBox1, New RuningLogConfig($"串口接收到数据:{Encoding.Default.GetString(recvBuffer)}", Color.Olive, 12), 1) gLogNode = New LogNode($"RX:{Encoding.Default.GetString(recvBuffer)}", RuningLog.LogType.e_info, RuningLog.LogMode.e_fileandRichTextBox) G_Log.AddLogNode(gLogNode) End If 'Dim readstr As String = publicMode.ByteToString(recvBuffer) End Sub Public Shared Function ByteToString2(databuff() As Byte) Dim strData As String = String.Empty For i = 0 To databuff.Length - 1 strData &= $" {ByteToHex1(databuff(i)).PadLeft(2, "0"c)}" Next Return strData End Function Public Shared Function ByteToHex1(ByVal b As Byte) As String Return b.ToString("X2") End Function Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click 'TextBox1 '判断TextBox1 是否为空 If String.IsNullOrEmpty(TextBox1.Text) Then MsgBox("电话号码为空") Return End If '判断TextBox1 是否为数字 且符合电话号码格式 If Not IsNumeric(TextBox1.Text) OrElse TextBox1.Text.Length <> 11 Then MsgBox("电话号码格式不正确") Return End If Dim str As String = $"ATD{TextBox1.Text};" SerialPortSendData(str, False) End Sub Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click Console.WriteLine(publicMode.SwapAndPad("18144070918")) End Sub '关闭redis Public Sub RedisClose() If Not IsNothing(G_RedisSub) AndAlso G_RedisSub.IsConnected Then G_RedisSub.UnsubscribeFromChannel(G_Subscribe) G_RedisSub.CloseConnection() End If If Not IsNothing(G_Redislish) AndAlso G_Redislish.IsConnected Then G_Redislish.CloseConnection() End If End Sub End Class