GSM Modem M95 от QUECTEL — опыт освоения
В начале
В первых строках своего поста хочу честно признаться что ранее опыта работы с GSM-модемами у меня не было, однако волею судьбы и служебной необходимостью набрел на сайт питерской конторы «Сатрон» — официального представителя QUECTEL. На сей ресурс забрел в поисках очередной железки — подходящего мне по параметрам GSM-модема и бютжетного GPS-приемника. Так получилось что информацию о продукции в виде даташитов и прочих полезных файлов можно было получить только после регистрации. Так и сделал.
Получив подтверждение через сутки (почти расстроился так долго ждать), первым делом закачал документацию на заинтересовавший меня модуль. На следующий день мне поступил звонок с неизвестного номера, приятный женский голос сообщил что звонит менеджер фирмы «Сатрон» по поводу моей заинтересованности в их продукции. Девушка на том конце телефонного контакта поинтересовалась какой у меня статус — частное лицо, либо юридическое. На этот вопрос ответил вопросом — с какой целью интересуетесь? И случилось то, чего я в принципе не ожидал, девушка объяснила что если я представляю юридическое лицо, то выбранную мною продукцию мне вышлют для испытаний при условии что будет оплачена пересылка. Естественно я согласился, хотя девушка-менеджер сама мне порекомендовала продукцию в ответ на запрошенные параметры. В пожеланиях было получить самый примитивный но предельно дешёвый GSM-модем, с возможностью слать SMS-ки и голосовой связью. В ответ мне были порекомендованы список продукции, а на запрос по модему предложен довольно свежий продукт M95 по цене «примитива». Естественно цен на продукцию я приводить не буду, это наши с «Сатрон» отношения, но замечу что модемы и другие модули были высланы в кратчайший срок и в нескольких экземплярах.
Сразу мне показалось что такой подход несколько странный, с чего это такая щедрость. Однако далее мне стало многое понятно, и дружелюбный подход фирмы, и качественная круглосуточная поддержка, и невероятное качество работы девушки-менеджера.
Дело в том что:
Так не бывает! Весь мой опыт контактов с Российскими поставщиками компонентов вопил — это подстава и очередное «впаривание» гнилого «отстоя». Однако при беглом знакомстве с документацией на модем M95 у меня зародилось странное сомнение, уж очень прорывные характеристики у наглого представителя китайского электронпрома по сравнению с именитыми монстрами, а цена… Это уже совсем отдельная история, боюсь обвинят в рекламе.
Так вот, поскольку опыта работы с модемами у меня было ровным счетом полное зеро, а испытать и проверить соответствие заявленным характеристикам было нужно, пришлось состряпать в Altium-е схему с импульсником на 4.4 вольта а сам модем обвесить необходимой периферией включая разъемы для наушников и микрофона, кнопками ON/OFF и Emergence OFF. Антенну применил AMMAP 003. Вся конструкция согласовалась по FT232LR с USB портом персоналки, причем питание портов FT232LR взято с выхода самого модема (2.8В), так что согласовывать по уровням не пришлось.
Первый старт — порт обнаружен, настраиваю порт в программе CommTrack (пока сырая, писалась сугубо под задачу) и естественно ввожу в окне консоли «AT» и жму . Замечательно! Модем ответил эхом:
AT
OK
Далее начались непонятности. Вначале как это водится решил устроить «звонок другу» (то есть себе). Однако модем устойчиво откликался «NO CARRIER». Сказалась невнимательность при изучении доки — оказывается есть разница при вызове для голосового соединения и для цифрового подключения. К номеру нужно было подцепить ";". К моему стыду я задал вопрос контактному лицу и получил ответ от инженера в весьма краткой и доходчивой форме.
Так, звоню себе на мобильник-вижу вызов. Подтверждаю вызов и пытаюсь поговорить с самим собой — тишина. Оказывается нужно настроить канал аналоговых входов и выходов. Посылаю команду модему «AT+QAUDCH=2».
Следующая попытка дозвониться оказалась удачной, и мне удалось услышать себя же по телефону и в гарнитуре. Удивило невероятно высокое качество звука и четкость речи, отсутствие каких либо эхо и других спецэффектов дешевой связи. Громкость и чувствительность микрофона регулировались в широком диапазоне, однако заводские настройки оказались наиболее удачными.
Немного поигравшись с голосовой связью SMS-ками и освоив необходимый набор команд из арсенала терминальных и GSM, приступил к установке GPRS соединения.
IP-стек и простота в использовании
Естественно установить соединение с удаленным сервером мне помог человек, уже имевший опыт с настройкой профиля соединения, и локальная задача тестирования модема на время отклика и скорости обмена данными заданной размерности была выполнена «по шаблону». Для тех кто еще не сталкивался с M95 приведу фрагмент кода программы на VB.NET.
Фрагмент кода
Private Sub RunScript() Dim sRet, sTemp As String Dim timeStart As Date Dim timeStop As Date Dim nTime As Double = 0 Dim nCount As Integer Dim tSpan As TimeSpan Dim serialStatus As Boolean = SerialPortA.IsOpen Dim seansOpen As Boolean = False Dim sBlock440 As String = "operations$öûâðëéîð÷ñéðöâéîðó2837âí2àãïëöãéïàöëäî3ïðàëîöíóïàùã34ïàùãà4óíïùöãíï4àêãùíöïùøãí4ïàùãöóåïàêãöù4ïàùãø4íïàùöã4íïàêùö6å4ê6åïöøãíàïö4ùïåàöùàïöùãà4ïöùã47ïåàêùöïù4ãïàøöãàï4ùãï4àùãöïàùöãï4àøùã6ï4àùãö4ïåàøãöï64àøã46ïà6öïàø76ö4ïàäöãàïíöãøíïuiowq3ygrowg3fowrtgowufygbow4gfowueyfgco6wt4go68gfrwkuyfcgwkgy4ckuw4ygfouwygou4fgrfouwf4gow4grfw4yofgowu4ygfou4fgwo4uygfwy4gfow4g4gfowufgou4rgyfwuyfguwygf4uyg4f6gfiu4gklejhdkwehfewhrkferhiuht43iuhiur" ' Блок переменных для фиксации временных рараметров и отказов Dim nDisconnect As Integer = 0 Dim nMinTimeA, nMinTimeB, nMaxTimeA, nMaxTimeB, nEvgTimeA, nEvgTimeB As Integer Dim nSendAttemptA, nSendAttemptB, nLostPackA, nLostPackB As Integer Dim nSendSuccA, nSendSuccB As Integer Dim nLostConnect As Integer = 0 Dim timeSummary As Double = 0 Dim timeSummaryMin As Double = 0 Dim timeSummaryMax As Double = 0 Dim timeSummaryEvg As Double = 0 _allowRun = True _allowEcho = True _ReadBuff = "" _lastClear = False _lastLine = "" nMinTimeA = 0 nMinTimeB = 0 nMaxTimeA = 0 nMaxTimeB = 0 nEvgTimeA = 0 nEvgTimeB = 0 nSendAttemptA = 0 nSendAttemptB = 0 nLostPackA = 0 nLostPackB = 0 nSendSuccA = 0 nSendSuccB = 0 'Настройка пула сообщений MessPull.ClearMessage() MessPull.AllowCMEErrorIntercept = True MessPull.AllowFuncIntercept = True MessPull.AllowSMSErrorIntercept = True MessPull.DropCME_Error() MessPull.DropSMS_Error() ' Начало операций Dim record As New StreamWriter(Application.StartupPath & "\Default.log") Me.WriteToConsole("Script started at " + DateTime.Now.ToLongTimeString + "!" + vbCrLf) record.WriteLine("Script started at " + DateTime.Now.ToLongTimeString + "!") If Not SerialPortA.IsOpen Then Try SerialPortA.Open() Catch Me.WriteToConsole("Serial Port: " + SerialPortA.PortName + " is busy! Script stopped at: " + DateTime.Now.ToLongTimeString + "!" + vbCrLf) record.WriteLine("Serial Port: " + SerialPortA.PortName + " is busy! Script stopped at: " + DateTime.Now.ToLongTimeString + "!") _allowRun = False End Try End If ' Инициаллизация модема If _allowRun Then nCount = 0 sTemp = "" MessPull.ClearMessage() While ((InStr(sTemp, "OK") = 0) And _allowRun) nCount += 1 sTemp = MessPull.ExecuteAT(SerialPortA, "AT" + vbCrLf, 3000) Me.WriteToConsole("Attempt: " + Str(nCount) + " Send: " + sTemp) End While Me.DisplayFunc() ' Включаем модем Me.WriteToConsole("Turn on the modem!" + vbCrLf) If Not MessPull.CheckAndSetup(SerialPortA, "AT+CFUN?", "CFUN", "1", , , , , "AT+CFUN=1", "Call Ready", 15000) Then Me.WriteToConsole("Modem does not turn on! Script Aborted!" + vbCrLf) _allowRun = False Else ' Малозначимые настраиваем параметры Me.WriteToConsole(MessPull.ExecuteAT(SerialPortA, "ATE1", 3000)) MessPull.CheckAndSetup(SerialPortA, "AT+CREG?", "CREG", "1", 0, ",", "OK", 1000, "AT+CREG=1", "OK", 5000) MessPull.CheckAndSetup(SerialPortA, "AT+CGREG?", "CGREG", "1", 0, ",", "OK", 1000, "AT+CGREG=1", "OK", 5000) MessPull.CheckAndSetup(SerialPortA, "AT+CRC?", "CRC", "0", 0, ",", "OK", 1000, "AT+CRC=0", "OK", 5000) MessPull.CheckAndSetup(SerialPortA, "AT+QAUDCH?", "QAUDCH", "2", 0, ",", "OK", 1000, "AT+QAUDCH=2", "OK", 5000) MessPull.CheckAndSetup(SerialPortA, "AT+QIFGCNT?", "QIFGCNT", "1", 0, ",", "OK", 1000, "AT+QIFGCNT=1", "OK", 5000) MessPull.CheckAndSetup(SerialPortA, "AT+QICSGP?", "QICSGP", "1", 0, ",", "OK", 1000, "AT+QICSGP=" + _QICSGP, "OK", 5000) End If Me.DisplayFunc() End If While _allowRun While _allowRun If (MessPull.CheckAndSetup(SerialPortA, "AT+CREG?", "CREG", "1", 1) And MessPull.CheckAndSetup(SerialPortA, "AT+CGREG?", "CGREG", "1", 1)) Then Exit While End If Me.DisplayFunc() End While Me.DisplayFunc() Me.WriteToConsole("Modem is registered on the home network at: " + DateTime.Now.ToLongTimeString + "!" + vbCrLf) record.WriteLine("Modem is registered on the home network at: " + DateTime.Now.ToLongTimeString + "!") ' Цикл установления соединения While (_allowRun And (Not seansOpen = True)) Me.DisplayFunc() Me.WriteToConsole(MessPull.ExecuteAT(SerialPortA, "AT+QISTAT", 2000) + vbCrLf) 'MessPull.WaitAnswer(SerialPortA, 5000, "", False) 'record.WriteLine("Request status at: " + DateTime.Now.TimeOfDay.ToString + "!") sRet = MessPull.GetFuncValue("STATE") Select Case sRet Case "IP INITIAL" MessPull.CheckAndSetup(SerialPortA, "AT+QISDE?", "QISDE", "0", 0, ",", "OK", 1000, "AT+QISDE = 0", 5000) MessPull.CheckAndSetup(SerialPortA, "AT+QIMUX?", "QIMUX", "0", 0, ",", "OK", 1000, "AT+QIMUX = 0", 5000) MessPull.CheckAndSetup(SerialPortA, "AT+QIMODE?", "QIMODE", "0", 0, ",", "OK", 1000, "AT+QIMODE=0", 5000) MessPull.CheckAndSetup(SerialPortA, "AT+QIDNSIP?", "QIDNSIP", "0", 0, ",", "OK", 1000, "AT+QIDNSIP=0", 5000) If Not InStr(MessPull.ExecuteAT(SerialPortA, "AT+QIREGAPP", 1000, "OK|ERROR"), "OK") > 0 Then Me.WriteToConsole("Can not register application on network! Next Attempt!" + vbCrLf) record.WriteLine("Can't Activate GPRS/CSD context! Next Attempt!") End If Case "IP START" If Not InStr(MessPull.ExecuteAT(SerialPortA, "AT+QIACT", 1000, "OK|ERROR"), "OK") > 0 Then Me.WriteToConsole("Can't Activate GPRS/CSD context! Next Attempt!" + vbCrLf) record.WriteLine("Can't Activate GPRS/CSD context! Next Attempt!") End If Case "IP GPRSACT" If Not InStr(MessPull.ExecuteAT(SerialPortA, "AT+QILOCIP", 2000, "."), ".") > 0 Then Me.WriteToConsole("Can't Get local IP addres!" + vbCrLf) record.WriteLine("Can't Get local IP addres!") End If Case "IP STATUS" If Not InStr(MessPull.ExecuteAT(SerialPortA, "AT+QIOPEN=" + _QIOPEN, 5000, "OK"), "OK") > 0 Then seansOpen = True Else Me.WriteToConsole("Can not estabilish connect!" + vbCrLf) record.WriteLine("Can not estabilish connect!") seansOpen = False End If Case "IP CLOSE" MessPull.ExecuteAT(SerialPortA, "AT+QIDEACT", 5000, "DEACT OK") Me.WriteToConsole("Deactivate GPRS/CSD context!" + vbCrLf) record.WriteLine("Deactivate GPRS/CSD context!") seansOpen = False Case "CONNECT OK" seansOpen = True Me.WriteToConsole("Connection OK!" + vbCrLf) record.WriteLine("Connection OK!") Case "PDP DEACT" Me.WriteToConsole("GPRS/CSD context was deactivated because of unknown reason. Reconnecting!" + vbCrLf) record.WriteLine("GPRS/CSD context was deactivated because of unknown reason. Reconnecting!") If Not InStr(MessPull.ExecuteAT(SerialPortA, "AT+QIACT", 1000, "OK|ERROR"), "OK") > 0 Then Me.WriteToConsole("Can't Activate GPRS/CSD context! Next Attempt!" + vbCrLf) record.WriteLine("Can't Activate GPRS/CSD context! Next Attempt!") End If Case "TCP CONNECTING" Sleep(800) Case Else Me.WriteToConsole("Connection status: " + sRet + vbCrLf) record.WriteLine("Connection status: " + sRet) End Select Me.WriteToConsole("Connection status: " + sRet + vbCrLf) Me.DisplayFunc() End While While (seansOpen And _allowRun) MessPull.ClearMessage() MessPull.ExecuteAT(SerialPortA, "AT+QISTAT") timeSummary = 0 nSendAttemptA += 1 timeStart = Now sRet = Trim(MessPull.ExecuteAT(SerialPortA, "AT+QISEND=8", 3000, ">", False)) 'Me.WriteToConsole("Time execution AT+QISEND=8" + MessPull.LastTimeRun.ToString + vbCrLf) If InStr(sRet, ">") > 0 Then sRet = MessPull.ExecuteAT(SerialPortA, "balance$", 3000, "SEND OK", False) 'Me.WriteToConsole("Time execution [balance$]" + MessPull.LastTimeRun.ToString + vbCrLf) If InStr(sRet, "SEND OK") > 0 Then _nCharWait = 40 sRet = MessPull.WaitAnswer(SerialPortA, 10000, "balance>................................", , False) If sRet = "balance>................................" Then Me.WriteToConsole(vbCrLf) timeStop = Now tSpan = timeStop.Subtract(timeStart) nTime = tSpan.TotalMilliseconds timeSummary = tSpan.TotalMilliseconds nMinTimeA = IIf(nMinTimeA = 0, nTime, IIf(nMinTimeA > nTime, nTime, nMinTimeA)) nMaxTimeA = IIf(nMaxTimeA = 0, nTime, IIf(nMaxTimeA < nTime, nTime, nMaxTimeA)) nEvgTimeA = IIf(nEvgTimeA = 0, nTime, (nTime + nEvgTimeA) / 2) nSendSuccA += 1 ' Теперь пытаемся выслать пакет nSendAttemptB += 1 timeStart = Now sRet = Trim(MessPull.ExecuteAT(SerialPortA, "AT+QISEND=440", 3000, ">", False)) 'Me.WriteToConsole("Time execution AT+QISEND=440" + MessPull.LastTimeRun.ToString + vbCrLf) If InStr(sRet, ">") > 0 Then MessPull.ClearMessage() _nCharWait = 4 sRet = MessPull.ExecuteAT(SerialPortA, sBlock440, 5000, "SEND OK", False) If InStr(sRet, "SEND OK") > 0 Then sRet = MessPull.WaitAnswer(SerialPortA, 10000, "done", , False) If InStr(sRet, "done") > 0 Then timeStop = Now tSpan = timeStop.Subtract(timeStart) nTime = tSpan.TotalMilliseconds timeSummary = timeSummary + tSpan.TotalMilliseconds nMinTimeB = IIf(nMinTimeB = 0, nTime, IIf(nMinTimeB > nTime, nTime, nMinTimeB)) nMaxTimeB = IIf(nMaxTimeB = 0, nTime, IIf(nMaxTimeB < nTime, nTime, nMaxTimeB)) nEvgTimeB = IIf(nEvgTimeB = 0, nTime, (nTime + nEvgTimeB) / 2) nSendSuccB += 1 Me.WriteToConsole(vbCrLf) Me.WriteToConsole("Cycle Ok. Time: " + timeSummary.ToString + vbCrLf) record.WriteLine("Cycle Ok. Time: " + timeSummary.ToString) timeSummaryMin = IIf(timeSummaryMin = 0, timeSummary, IIf(timeSummaryMin > timeSummary, timeSummaryMin, timeSummaryMin)) timeSummaryMax = IIf(timeSummaryMax = 0, timeSummary, IIf(timeSummaryMax < timeSummary, timeSummary, timeSummaryMax)) timeSummaryEvg = IIf(timeSummaryEvg = 0, timeSummary, (timeSummary + timeSummaryEvg) / 2) Else nLostPackB += 1 Me.WriteToConsole("Not Answer (440 byte). Time: " + nTime.ToString + vbCrLf) record.WriteLine("Not Answer (440 byte). Time: " + nTime.ToString) End If Else Me.WriteToConsole("Unsuccessfully send Data Pack (440 byte). Time: " + nTime.ToString + vbCrLf) record.WriteLine("Unsuccessfully send Data Pack (440 byte). Time: " + nTime.ToString) End If Else timeStop = Now tSpan = timeStop.Subtract(timeStart) nTime = tSpan.TotalMilliseconds Me.WriteToConsole("Error execute command AT+QISEND=440. Time: " + nTime.ToString + vbCrLf) record.WriteLine("Error execute command AT+QISEND=440. Time: " + timeSummary.ToString) End If Else timeStop = Now tSpan = timeStop.Subtract(timeStart) nTime = tSpan.TotalMilliseconds nLostPackA += 1 Me.WriteToConsole("Unsuccessfully execute command [balance$]. Time: " + nTime.ToString + vbCrLf) record.WriteLine("Unsuccessfully execute command [balance$]. Time: " + nTime.ToString) End If Else Me.WriteToConsole("Unsuccessfully send command [balance$]. Time: " + nTime.ToString + vbCrLf) record.WriteLine("Unsuccessfully send command [balance$]. Time: " + nTime.ToString) MessPull.ExecuteAT(SerialPortA, "AT", 3000, "") MessPull.ExecuteAT(SerialPortA, "AT", 3000, "") timeStop = Now tSpan = timeStop.Subtract(timeStart) nTime = tSpan.TotalMilliseconds End If Else MessPull.ExecuteAT(SerialPortA, "AT", 3000, "") Me.WriteToConsole("Error execute command AT+QISEND=8!" + vbCrLf) record.WriteLine("Error execute command AT+QISEND=8!") End If MessPull.SetFuncValue("CounterA", nSendAttemptA.ToString) MessPull.SetFuncValue("CounterB", nSendAttemptB.ToString) MessPull.SetFuncValue("AverageTimeA", nEvgTimeA.ToString) MessPull.SetFuncValue("AverageTimeB", nEvgTimeB.ToString) Me.DisplayFunc() If Not MessPull.GetFuncValue("STATE") = "CONNECT OK" Then nLostConnect += 1 seansOpen = False Me.WriteToConsole("Connection lost at: " + DateTime.Now.ToLongTimeString + vbCrLf) record.WriteLine("Connection lost at: " + DateTime.Now.ToLongTimeString) Me.WriteToConsole("Attempt to reconnect." + vbCrLf) record.WriteLine("Attempt to reconnect.") End If Sleep(2000) End While End While MessPull.ExecuteAT(SerialPortA, "AT+QICLOSE", 5000, "CLOSE OK|ERROR") MessPull.ExecuteAT(SerialPortA, "AT+QIDEACT", 5000, "DEACT OK|ERROR") Me.WriteToConsole("Connection Closed and GPRS/CSD context was deactivated at: " + DateTime.Now.ToLongTimeString + "!" + vbCrLf) record.WriteLine("Connection Closed and GPRS/CSD context was deactivated at: " + DateTime.Now.ToLongTimeString + "!") Me.WriteToConsole("---------------------------------------------------------------------------------------------" + vbCrLf) record.WriteLine("---------------------------------------------------------------------------------------------") Me.WriteToConsole("Execution repport:" + vbCrLf) record.WriteLine("Execution repport:") Me.WriteToConsole("[balance$] request MIN time (milliseconds): " + nMinTimeA.ToString + vbCrLf) record.WriteLine("[balance$] request MIN time (milliseconds): " + nMinTimeA.ToString) Me.WriteToConsole("[balance$] request MAX time (milliseconds): " + nMaxTimeA.ToString + vbCrLf) record.WriteLine("[balance$] request MAX time (milliseconds): " + nMaxTimeA.ToString) Me.WriteToConsole("[balance$] request AVERAGE time (milliseconds): " + nEvgTimeA.ToString + vbCrLf) record.WriteLine("[balance$] request AVERAGE time (milliseconds): " + nEvgTimeA.ToString) Me.WriteToConsole(vbCrLf) record.WriteLine("") Me.WriteToConsole("[440 byte pack] request MIN time (milliseconds): " + nMinTimeB.ToString + vbCrLf) record.WriteLine("[440 byte pack] request MIN time (milliseconds): " + nMinTimeB.ToString) Me.WriteToConsole("[440 byte pack] request MAX time (milliseconds): " + nMaxTimeB.ToString + vbCrLf) record.WriteLine("[440 byte pack] request MAX time (milliseconds): " + nMaxTimeB.ToString) Me.WriteToConsole("[440 byte pack] request AVERAGE time (milliseconds): " + nEvgTimeB.ToString + vbCrLf) record.WriteLine("[440 byte pack] request AVERAGE time (milliseconds): " + nEvgTimeB.ToString) Me.WriteToConsole(vbCrLf) record.WriteLine("") Me.WriteToConsole("Summary request MIN time (milliseconds): " + timeSummaryMin.ToString + vbCrLf) record.WriteLine("Summary request MIN time (milliseconds): " + timeSummaryMin.ToString) Me.WriteToConsole("Summary request MAX time (milliseconds): " + timeSummaryMax.ToString + vbCrLf) record.WriteLine("Summary request MAX time (milliseconds): " + timeSummaryMax.ToString) Me.WriteToConsole("Summary request AVERAGE time (milliseconds): " + timeSummaryEvg.ToString + vbCrLf) record.WriteLine("Summary request AVERAGE time (milliseconds): " + timeSummaryEvg.ToString) Me.WriteToConsole(vbCrLf) record.WriteLine("") Me.WriteToConsole("Total send attempt command [balance$]: " + nSendAttemptA.ToString + vbCrLf) record.WriteLine("Total send attempt command [balance$]: " + nSendAttemptA.ToString) Me.WriteToConsole("Total send attempt packet [440 byte]: " + nSendAttemptB.ToString + vbCrLf) record.WriteLine("Total send attempt packet [440 byte]: " + nSendAttemptB.ToString) Me.WriteToConsole("Total send succes [balance$]: " + nSendSuccA.ToString + vbCrLf) record.WriteLine("Total send succes [balance$]: " + nSendSuccA.ToString) Me.WriteToConsole("Total send succes [440 byte]: " + nSendSuccB.ToString + vbCrLf) record.WriteLine("Total send succes [440 byte]: " + nSendSuccB.ToString) Me.WriteToConsole(vbCrLf) record.WriteLine("") Me.WriteToConsole("Lost packet [balanse$]: " + nLostPackA.ToString + vbCrLf) record.WriteLine("Lost packet [balanse$]: " + nLostPackA.ToString) Me.WriteToConsole("Lost packet [440 byte]: " + nLostPackB.ToString + vbCrLf) record.WriteLine("Lost packet [440 byte]: " + nLostPackB.ToString) Me.WriteToConsole("---------------------------------------------------------------------------------------------" + vbCrLf) record.WriteLine("---------------------------------------------------------------------------------------------") Me.WriteToConsole("Script stopped at: " + DateTime.Now.ToLongTimeString + "!" + vbCrLf) record.WriteLine("Script stopped at: " + DateTime.Now.ToLongTimeString + "!") record.Close() 'Восстановим состояние модема If Not serialStatus Then SerialPortA.Close() End If 'Разрешим эхо в консоли _allowEcho = True End Sub
Это скучная часть тестирующей подпрограммы просто пояснит последовательность действий по установке соединения и настройке профиля на конкретном примере, в котором отсутствует адрес сервера и APN провайдера. Надеюсь с этим затруднений не будет.
Модем тестировался длительной поездкой по городу в подключенном режиме с посещением «мертвых» зон, где связь по телефону обрывается. Вот что удивительно, из более чем 860 пакетов, потеряно было только два, а IP-сессия вообще не прерывалась, не говоря уже о соединении. Модем тестировался в сравнении с довольно известной моделью BGS2 второй версии. Результаты тестов показали сокрушительный провал Cinterion BGS2 по устойчивости связи и скорости обмена в пользу M95. Надо сразу отметить что оба модема питались от USB порта ноутбука и имели одинаковые антенны AMMAP 003, однако для BGS2 требовался конденсатор по питанию 1000мФ, а M95 обошелся согласно документации 100 микрофарадами. При этом средний ток потребления M95 был на 46% ниже чем у BGS2. Весьма впечатляющие результаты, а если вспомнить что для написания тестовой программы для M95 у меня ушло два дня, а для BGS2 пришлось «потеть» четыре, то очевидные преимущества (по крайней мере для меня) налицо.
В общем мой первый опыт знакомства с продукцией QUECTEL в виде модема M95 весьма меня порадовал, я в первый раз столкнулся с продукцией которую делали именно для разработчиков, без абстрактного умничанья и соблюдения мертвых стандартов.
Да простят меня уважаемые читатели хабра за воспевание M95, но мне ничего подобного просто не встречалось в моей практике, и перелопаченные после этого восемь моделей совместимых GPRS модемов 12 класса убедили меня что лучше и главное дешевле, а также экономичнее и непритязательнее модема пока на российском рынке не представлено!
Сухой код
Для тех, кто пожелает использовать мои наработки, привожу сырой, но работоспособный код модуля с классом использовавшимся в примере приведенном выше а также полную версию кода формы.
Код модуля содержащего класс:
Imports System Imports System.Object Imports System.Collections Imports System.Threading.Thread Imports System.IO.Ports Module ScriptRun 'Public Enum AnswerStatus As Integer ' Reciv_Nothing = 0 ' Reciv_OK = 1 ' Reciv_Send = 2 ' Reciv_Func = 4 ' Reciv_Mess = 8 ' Reciv_CME_ERROR = 16 ' Reciv_ERROR = 32 ' Reciv_TimeOut = 256 'End Enum Public Class MessPull #Region "Declaration Block" Private Shared msgList As String() = {} 'массив строк сообщений, заполняемый методами класса '#Private Shared AnsModem As AnswerStatus 'статус ответа из перечисления AnswerStatus Private Shared _funcTable As New Hashtable() 'возвращаемые параметры функций формата +ФУНКЦИЯ: ПАРАМЕТР Private Shared _Recieved As Boolean 'если принята хоть одна строка - True '#Private Shared _messTable As New Hashtable() 'таблица текстовых сообщений сообщение+количество поступлений 'разрешает перехват строк значений функций, перехваченная строка исключается из массива строк сообщений, вместо этого 'строка разбирается на пару ключ-значение и помещается в Хэш-таблицу _funcVal Private Shared _funcIntercept As Boolean = True 'Разрешает перехват функций "+FUNC: VALUE" Private Shared _EchoIntegcept As Boolean = True 'Разрешает перехват эхо команды от модема (программный аналог ATE0) Private Shared _cmeIntercept As Boolean = True 'Разрешает перехват собщений об ошибке "+CME ERROR: XXX" Private Shared _smsIntercept As Boolean = True 'Разрешает перехват сообщений об ошибке "+SMS ERROR: XXX" Private Shared _cmeError As Boolean = False 'Статус - поступила "+CME ERROR: XXX" Private Shared _smsError As Boolean = False 'Статус - поступила "+SMS ERROE: XXX" Private Shared _cmeLastError As Integer = -1 'Номер последней "+CME ERROR:" Private Shared _smsLastError As Integer = -1 'Номер последней "+SMS ERROR:" Private Shared _lastCommand As String = "" 'Последняя команда переданная модему ' Блок измерения времени Private Shared startTime, stopTime As Date 'время вхождения в TimeOut-функцию и время завершения Private Shared tSpan As TimeSpan 'обработчик времени TimeOut, измеритель времени исполнения Private Shared _lastTRun As Integer 'время последнего исполнения TimeOut-функции Private Shared _timeOut As Boolean 'статус TimeOut последнего исполнения TimeOut-функции 'объект блокировки оператором SyncLock, для защиты данных от асинхронной записи Private Shared blockSync As New Object Public Shared URCMessageList() As String = {"RING", _ "MO RING", _ "MO CONNECTED", _ "Call Ready", _ "CCWV", _ "RDY", _ "NORMAL POWER DOWN", _ "UNDER_VOLTAGE WARNING", _ "UNDER_VOLTAGE POWER DOWN", _ "OVER_VOLTAGE WARNING", _ "OVER_VOLTAGE POWER DOWN"} #End Region #Region "Property definition Block" Public Shared ReadOnly Property FuncTable As Hashtable Get SyncLock blockSync Return _funcTable End SyncLock End Get End Property Public Shared ReadOnly Property MessageRecieved As Boolean Get SyncLock blockSync Return _Recieved End SyncLock End Get End Property Public Shared ReadOnly Property LastTimeRun() As Integer Get SyncLock blockSync Return _lastTRun End SyncLock End Get End Property Public Shared ReadOnly Property TimeOutState() As Boolean Get SyncLock blockSync Return _timeOut End SyncLock End Get End Property Public Shared Property AllowFuncIntercept() As Boolean Get SyncLock blockSync Return _funcIntercept End SyncLock End Get Set(value As Boolean) SyncLock blockSync _funcIntercept = value End SyncLock End Set End Property Public Shared Property AllowEchoIntercept() As Boolean Get SyncLock blockSync Return _EchoIntegcept End SyncLock End Get Set(value As Boolean) SyncLock blockSync _EchoIntegcept = value End SyncLock End Set End Property Public Shared Property AllowCMEErrorIntercept() As Boolean Get SyncLock blockSync Return _cmeIntercept End SyncLock End Get Set(value As Boolean) SyncLock blockSync _cmeIntercept = value End SyncLock End Set End Property Public Shared Property AllowSMSErrorIntercept() As Boolean Get SyncLock blockSync Return _smsIntercept End SyncLock End Get Set(value As Boolean) SyncLock blockSync _smsIntercept = value End SyncLock End Set End Property Public Shared ReadOnly Property LastCME_Error() As Boolean Get SyncLock blockSync Return _cmeLastError End SyncLock End Get End Property Public Shared ReadOnly Property LastSMS_Error() As String Get SyncLock blockSync Return _smsLastError End SyncLock End Get End Property Public Shared Property LastCommand() As String Get SyncLock blockSync Return _lastCommand End SyncLock End Get Set(value As String) SyncLock blockSync _lastCommand = value End SyncLock End Set End Property #End Region #Region "TimeOut-Function" Public Shared Function CheckAndSetup(ByRef comPort As SerialPort, ByVal sCheckCommand As String, ByVal sFunc As String, ByVal sValue As String, Optional ByVal nIndex As Integer = 0, Optional ByVal sDelim As String = ",", Optional ByVal sCheckExpect As String = "OK", Optional ByVal nCheckTimeOut As Integer = 1000, Optional ByVal sSetCommand As String = "", Optional ByVal sSetExpect As String = "OK", Optional ByVal nSetTimeOut As Integer = 2000) As Boolean '----------------------------------------------------------------------------------- 'Функция ChechAndSetup посылает AT-команду <sCheckCommand> в последовательный порт <comPort> и 'в течение определенного <nCheckTimeOut> таймаута ожидает поступления строки содержащей 'терм "OK" или "0". После этого проверяется значение перехваченной функции '<sFunc> по индексу <nIndex> на соответсвие параметру <sValue>. Если остальные параметры 'отсутсвуют, то функция возвращает True в случае соответствия параметра иначе False. 'Если в качестве <sCheckCommand> передана пустая строка или не открыт, функция завершится 'вернув False. '--------------------------------- 'При указании в качестве параметра <sSetCommand> не пустой строки, если результат сравнения 'в вышеописанном алгоритме дает False, то в последовательный порт <comPort> передается 'комманда <sSetCommand> и производится ожидание поступление терма "OK" или "0" в 'течение времени <nSetTimeOut>. Если до достижения таймаута получен ожидаемый терм, функция 'выполняет проверку значения <sFunc> по индексу <nIndex> на соответсвие параметру <sValue>. 'Функция возвращает результат проверки на соответсвие False или True. '--------------------------------- 'При возникновении таймаута функция прекращает все дальнейшие действия и всегда возврашает 'False выставляя состояние TimeOutState в True '--------------------------------- 'параметр <sDelim> определяет набор символов, который будет участвовать в парсинге 'параметра функции методом String.Split() '----------------------------------------------------------------------------------- Dim stTime As Date = Now Dim sRet As String Dim bCheck As Boolean ' startTime = stTime CheckAndSetup = False _timeOut = False If (comPort.IsOpen And (sCheckCommand.Length > 0)) Then SerialWriteLine(comPort, sCheckCommand) sRet = WaitAnswer(comPort, nCheckTimeOut, sCheckExpect, False) bCheck = CheckFuncValue(sFunc, sValue, sDelim, nIndex) If ((Not bCheck) And (Not _timeOut) And (sSetCommand.Length > 0)) Then startTime = Now SerialWriteLine(comPort, sSetCommand) sRet = WaitAnswer(comPort, nSetTimeOut, sSetExpect, False) If Not _timeOut Then CheckAndSetup = CheckFuncValue(sFunc, sValue, sDelim, nIndex) End If Else CheckAndSetup = bCheck End If End If stopTime = Now tSpan = stopTime.Subtract(stTime) _lastTRun = Int(tSpan.TotalMilliseconds) End Function Public Shared Function SendData(ByRef comPort As SerialPort, _ Optional sComm As String = "", _ Optional ByVal tOut As Integer = 1000, _ Optional sExpect As String = "OK|ERROR") As String '----------------------------------------------------------------------------------- 'Функция посылает AT-команду модему подключенному к последовательному порту comPort и ждет 'отклика в течение времени определенным параметром sComm (в миллисекундах) или до появления 'терма в принимаемых строках определенного списком термов sExpect. '------------------------------------ 'Если параметр sComm = "" или опущен, то функция пропускает действие по отсылке AT-команды 'модему, но выполняет ожидание по времени и поиск терма в принима��мых строках. '------------------------------------ 'Если параметр tOut опущен, то таймаут по умолчанию составляет 1 секунду '------------------------------------ 'Термы подлежащие распознаванию 'разделяются вертикальной чертой. В состав терма могут 'входить любые символы 'исключая вертикальную черту "|". По обнаружении терма или термов 'в(составе) 'принятых строк, функция возвращает строку содержущую выбранные строки до 'появления 'одного или нескольких термов в составе любой из строк, включая строку содержащую 'обнаруженные термы. Выбранные строки соединяются последовательно, разделяемые символами '"возврат каретки" + "перевод строки" - vbCrLf. 'По окончании заданного времени tOut, функция возвращает выбранные до этого момента 'строки, и выставляет состояние TimeOutState = True а в LastTimeRun заноситься общее 'время выполнения функции 'При нормальном завершении функции WaitAnswer, TimeOutState сбрасывается в False 'а в LastTimeRun заноситься время исполнения функции '----------------------------------------------------------------------------------- startTime = Now If sComm.Length > 0 Then SerialWrite(comPort, sComm) End If SendData = WaitAnswer(comPort, tOut, sExpect, False) stopTime = Now tSpan = stopTime.Subtract(startTime) _lastTRun = Int(tSpan.TotalMilliseconds) End Function Public Shared Function ExecuteAT(ByRef comPort As SerialPort, _ Optional sComm As String = "", _ Optional ByVal tOut As Integer = 1000, _ Optional sExpect As String = "OK|ERROR", _ Optional ByVal bSetCrLf As Boolean = True) As String '----------------------------------------------------------------------------------- 'Функция посылает AT-команду модему подключенному к последовательному порту comPort и ждет 'отклика в течение времени определенным параметром sComm (в миллисекундах) или до появления 'терма в принимаемых строках определенного списком термов sExpect. '------------------------------------ 'Если параметр sComm = "" или опущен, то функция пропускает действие по отсылке AT-команды 'модему, но выполняет ожидание по времени и поиск терма в принимаемых строках. '------------------------------------ 'Если параметр tOut опущен, то таймаут по умолчанию составляет 1 секунду '------------------------------------ 'Термы подлежащие распознаванию 'разделяются вертикальной чертой. В состав терма могут 'входить любые символы 'исключая вертикальную черту "|". По обнаружении терма или термов 'в(составе) 'принятых строк, функция возвращает строку содержущую выбранные строки до 'появления 'одного или нескольких термов в составе любой из строк, включая строку содержащую 'обнаруженные термы. Выбранные строки соединяются последовательно, разделяемые символами '"возврат каретки" + "перевод строки" - vbCrLf. 'По окончании заданного времени tOut, функция возвращает выбранные до этого момента 'строки, и выставляет состояние TimeOutState = True а в LastTimeRun заноситься общее 'время выполнения функции 'При нормальном завершении функции WaitAnswer, TimeOutState сбрасывается в False 'а в LastTimeRun заноситься время исполнения функции '----------------------------------------------------------------------------------- startTime = Now If sComm.Length > 0 Then comPort.WriteLine(Trim(sComm)) End If ExecuteAT = WaitAnswer(comPort, tOut, sExpect, False) stopTime = Now tSpan = stopTime.Subtract(startTime) _lastTRun = Int(tSpan.TotalMilliseconds) End Function Public Shared Function WaitAnswer(ByRef commPort As SerialPort, _ Optional tOut As Integer = 1000, _ Optional sExpect As String = "OK|ERROR", _ Optional ByVal bStTime As Boolean = True, _ Optional ByVal bSetCrLf As Boolean = True) As String '----------------------------------------------------------------------------------- 'Функция ожидает данных от модема подключенного к последовательному порту commPort в 'течение времени tOut в миллисекундах или до обнаружения терма в принятых строках 'объявленных в списке термов sExpect. '------------------------------------ 'Параметр bStTime определяет необходимость измерения старового времени функции, 'по(умолчанию - измерять) '------------------------------------ 'Если параметр tOut опущен, то таймаут по умолчанию составляет 1 секунду '------------------------------------ 'Термы подлежащие распознаванию 'разделяются вертикальной чертой. В состав терма могут 'входить любые символы 'исключая вертикальную черту "|". По обнаружении терма или термов 'в(составе) 'принятых строк, функция возвращает строку содержущую выбранные строки до 'появления 'одного или нескольких термов в составе любой из строк, включая строку содержащую 'обнаруженные термы. Выбранные строки соединяются последовательно, разделяемые символами '"возврат каретки" + "перевод строки" - vbCrLf. 'По окончании заданного времени tOut, функция возвращает выбранные до этого момента 'строки, и выставляет состояние TimeOutState = True а в LastTimeRun заноситься общее 'время выполнения функции 'При нормальном завершении функции WaitAnswer, TimeOutState сбрасывается в False 'а в LastTimeRun заноситься время исполнения функции '----------------------------------------------------------------------------------- Dim sTemp, sText As String Dim sTerminator As String = IIf(bSetCrLf, vbCrLf, "") Dim aExpect As String() = {} Dim bContinue As Boolean = True _timeOut = False If bStTime Then startTime = Now End If WaitAnswer = "" If sExpect.Length > 0 Then aExpect = sExpect.Split("|".ToCharArray) Else aExpect = {} End If While bContinue While (MsgCount() < 1) Sleep(5) stopTime = Now tSpan = stopTime.Subtract(startTime) If tSpan.TotalMilliseconds > tOut Then _timeOut = True _lastTRun = Int(tSpan.TotalMilliseconds) bContinue = False Exit While End If End While If bContinue Then sTemp = ExtractMess() WaitAnswer = IIf(WaitAnswer.Length > 0, WaitAnswer + sTemp + sTerminator, sTemp + sTerminator) If aExpect.Length > 0 Then For Each sText In aExpect If InStr(sTemp, sText) > 0 Then bContinue = False Exit For End If Next sText End If Else Exit While End If End While stopTime = Now tSpan = stopTime.Subtract(startTime) _lastTRun = Int(tSpan.TotalMilliseconds) End Function Public Shared Function WaitFunc(ByVal sKey As String, Optional ByVal tOut As Integer = 1000, Optional ByVal sDelim As String = ",", Optional ByVal nIndex As Integer = -1) As String '----------------------------------------------------------------------------------- 'Функция ожидает появления ключа sKey в хэш-таблице в течение времени tOut в 'миллисекундах. В случае орбнаружения ключа функция возвращает значение в зависимости 'от присутствия и значения параметра nIndex. '------------------------------------ 'Если параметр tOut опущен, то таймаут по умолчанию составляет 1 секунду '------------------------------------ 'Когда параметр nIndex передан функции, производится попытка поиска параметра по индеку 'используя в качестве разделителя группы sDelim. Если параметр nIndex опущен, функция 'возвращает всё значение ключа sKey или пустую строку в случае его отсутствия. '----------------------------------------------------------------------------------- Dim sTemp As String _timeOut = False startTime = Now WaitFunc = "" While True sTemp = GetFuncValue(sKey, sDelim, nIndex) If sTemp.Length > 0 Then WaitFunc = sTemp Exit While End If stopTime = Now tSpan = stopTime.Subtract(startTime) If tSpan.TotalMilliseconds > tOut Then _timeOut = True _lastTRun = Int(tSpan.TotalMilliseconds) Exit While End If Sleep(5) End While stopTime = Now tSpan = stopTime.Subtract(startTime) _lastTRun = Int(tSpan.TotalMilliseconds) End Function #End Region #Region "Public Methods ()" Public Shared Sub SerialWrite(ByVal comPort As SerialPort, ByVal sComm As String) If (comPort.IsOpen And (sComm.Length > 0)) Then comPort.Write(sComm) _lastCommand = Trim(sComm) End If End Sub Public Shared Sub SerialWriteLine(ByVal comPort As SerialPort, ByVal sComm As String) If (comPort.IsOpen And (sComm.Length > 0)) Then comPort.WriteLine(sComm) _lastCommand = Trim(sComm) End If End Sub Public Shared Function IsCME_Error() As Boolean IsCME_Error = _cmeError End Function Public Shared Function IsSMS_Error() As Boolean IsSMS_Error = _smsError End Function Public Shared Sub DropCME_Error() _cmeError = False _cmeLastError = -1 End Sub Public Shared Sub DropSMS_Error() _smsError = False _smsLastError = -1 End Sub Public Shared Function AppendLines(ByVal aLines As String()) As Integer '------------------------------------------------------------------------------- ' функция добавляет строки массива в массив сообщений ' возвращает общее количество элементов массива сообщений после добавления ' если в качестве аргумента указан пустой массив, то функция ничего не выполняет Dim sText As String If aLines.Length > 0 Then For Each sText In aLines AppendLines = AppendMess(sText) Next End If End Function Public Shared Function AppendMess(ByVal sText As String) As Integer '------------------------------------------------------------------------------- ' функция добавляет строку сообщения в массив сообщений ' возвращает общее количество элементов массива сообщений после добавления ' если в качестве параметра указана пустая строка, добавления сообщения не происходит Dim nCount As Integer = 0 If _funcIntercept Then sText = FuncIntercept(sText) End If If sText.Length > 0 Then SyncLock blockSync nCount = msgList.Length ReDim Preserve msgList(nCount) msgList(nCount) = Trim(sText) nCount = msgList.Length _Recieved = True End SyncLock Else SyncLock blockSync nCount = msgList.Length End SyncLock End If AppendMess = nCount End Function Public Shared Function Items() As String() '------------------------------------------------------------------------------- ' функция возвращает копию всего списка (массива строк) сообщений Dim aStr As String() SyncLock blockSync Dim nCount As Integer = UBound(msgList) ReDim aStr(nCount) Array.Copy(msgList, aStr, msgList.Length) End SyncLock Items = aStr End Function Public Shared Function MsgCount() As Integer '------------------------------------------------------------------------------- ' возвращает количество строк в массиве сообщений SyncLock blockSync MsgCount = msgList.Length End SyncLock End Function Public Shared Function ExtractMess() As String '------------------------------------------------------------------------------- ' функция извлекает самое первое сообщение из списка и возвращет его ' при этом извлекаемое сообщение удаляется из списка, а список становится ' короче на одно сообщение ' если в списке нет ни одного сообщения, возвращается пустая строка Dim nCount As Integer ExtractMess = "" SyncLock blockSync If msgList.Length > 0 Then ExtractMess = msgList(0) nCount = UBound(msgList) nCount -= 1 Array.Copy(msgList, 1, msgList, 0, msgList.Length - 1) ReDim Preserve msgList(nCount) End If If msgList.Length = 0 Then _Recieved = False End SyncLock End Function Public Shared Sub ClearMessage() '------------------------------------------------------------------------------- ' Процедура очищает список сообщений SyncLock blockSync msgList = {} _Recieved = False End SyncLock End Sub Public Shared Function GetFuncValue(ByVal sKey As String, Optional ByVal sDelim As String = ",", Optional ByVal nIndex As Integer = -1) As String Dim sVal As String = "" Dim aVal As String() = {} GetFuncValue = "" SyncLock _funcTable.SyncRoot If _funcTable.ContainsKey(sKey) Then sVal = _funcTable(sKey) End If End SyncLock If (sVal.Length > 0) And (nIndex >= 0) Then aVal = sVal.Split(sDelim.ToCharArray, StringSplitOptions.RemoveEmptyEntries) If nIndex > UBound(aVal) Then Exit Function Else GetFuncValue = aVal(nIndex) End If Else GetFuncValue = sVal End If End Function Public Shared Function CheckFuncValue(ByVal sFunc As String, ByVal sValue As String, Optional ByVal sDelim As String = ",", Optional ByVal nIndex As Integer = 0) As Boolean Dim aVal As String() CheckFuncValue = False If (sFunc.Length > 0) And (_funcTable.Contains(sFunc)) Then aVal = _funcTable(sFunc).ToString.Split(sDelim.ToCharArray, StringSplitOptions.RemoveEmptyEntries) If nIndex <= UBound(aVal) Then CheckFuncValue = IIf(Trim(sValue) = Trim(aVal(nIndex)), True, False) End If End If End Function Public Shared Sub SetFuncValue(ByVal sKey, ByVal Value) SyncLock _funcTable.SyncRoot _funcTable(sKey) = Value End SyncLock End Sub Public Shared Sub RemuveFunc(ByVal sKey) SyncLock _funcTable.SyncRoot _funcTable.Remove(sKey) End SyncLock End Sub Public Shared Sub ClearFuncList() SyncLock _funcTable.SyncRoot _funcTable.Clear() End SyncLock End Sub #End Region #Region "Private Methods" Private Shared Function FuncIntercept(ByVal sText As String) As String '------------------------------------------------------------------------------- 'Функция "перехватчик" строк типа +CREG:1. Если функция обнаруживает что 'строка содержит именно комбинацию из лидирующего "+" и финиширующего 'имя функции ":", то строка разбивается на имя функции и возвращаемый 'параметр, следующий сразу после ":". Символы "+" и ":" удаляются при 'разборе строки. '---------------- 'Функция возвращает пустую строку в случае удачного перехвата или пустой 'строки переданной в качестве параметра, иначе возвращается переданная в 'качестве параметра строка. '---------------- Dim i, n, m As Integer Dim aLine As String() Dim key As String = "" Dim val As String = "" ' Грохнем лидирующие и завершающие пробелы sText = Trim(sText) If (_EchoIntegcept And (sText = _lastCommand)) Then FuncIntercept = "" Exit Function End If ' Выставим по умолчанию возврат строки параметра FuncIntercept = sText i = InStr(sText, "+") n = InStr(sText, ":") m = InStr(sText, "^") If ((i > 0) Or (n > 0) Or (m > 0)) Then If sText.Length > 0 Then aLine = sText.Split("+^:".ToCharArray, StringSplitOptions.RemoveEmptyEntries) If aLine.Length = 0 Then Exit Function ElseIf aLine.Length = 1 Then key = aLine(0) val = "" ElseIf aLine.Length = 2 Then key = aLine(0) val = aLine(1) End If If val = "CME ERROR" Then _cmeError = True _cmeLastError = Int(val) If Not _cmeIntercept Then Exit Function End If ElseIf val = "SMS ERROR" Then _smsError = True _smsLastError = Int(val) If Not _smsIntercept Then Exit Function End If End If SyncLock _funcTable.SyncRoot _funcTable(Trim(key)) = Trim(val) End SyncLock FuncIntercept = "" End If End If End Function #End Region End Class End Module
Код событий и локальных процедур/функций формы:
#Region "Import Namespace" Imports System Imports System.Text Imports System.IO Imports System.IO.Ports Imports System.Threading Imports System.Threading.Thread Imports System.Collections Imports System.Object Imports System.ComponentModel Imports System.Runtime.InteropServices Imports System.Runtime.ConstrainedExecution #End Region Public Class CommCare #Region "Declaration" Shared SerialPortA As SerialPort Shared _continue As Boolean ' False запрещает реакцию на изменение в ComboBox с данными настройки Shared _readPort As Boolean ' False запрещает реакцию на изменение текстовых данных в консоли TextBoxM Shared _lineCount As Integer ' переменная хранения количества строк в TextBoxM, если добавлена - последняя строка передается в порт Shared _nCharWait As Integer = 0 ' переменная указывает на то, что ожидается прием _nCharWait символов не смотря vbCrLf Shared _lastLine As String = "" ' хранит последние переданные в COM порт данные Shared _lastRead As String = "" ' накопительная переменная, в которую помещаются данные принятые из COM порта Shared _ReadBuff As String = "" ' перемнная в которую помещаются данные по асинхронному запросу из другого потока Shared _lastClear As Boolean ' переменная указывающая что необходимо очистить _lastRead Shared _lockRead As Boolean ' переменная межпотоковой блокировки опустошения или пополнения _lastRead Shared _allowRun As Boolean ' переменная разрешающая продолжение исполнения скрипта, либо прерывающая (False) его исполнение Shared _allowEcho As Boolean ' переменная разрешающая вывод на консоль обработчику событий приема данных из COM порта Shared _timeOut As Boolean ' Shared _QICSGP As String ' переменная хранения настроек провайдера и сеанса связи Shared _QIOPEN As String ' переменная хранения типа соединения и адреса сервера Public hashToken As Hashtable ' публичная таблица ключевых слов AT-команд Private ScriptThread As Thread = New Thread(AddressOf RunScript) ' Перечислитель элементов массива скрипта Public Enum scNames scKeyWord scVarData scLabel scCodeData scSubData End Enum ' массив элементов скрипта Public aScript As Array = {New Hashtable, New Hashtable, New Hashtable, New ArrayList, New ArrayList} Public RunStack As Stack = New Stack Private BaudList As String() = {"4800", "9600", "14400", "19200", "38400", "57600", "115200", "128000", "230400", "460800", "614400", "921600", "1228800"} Private DataBitsList As String() = {"8", "7", "6", "5", "4"} ' API - функция чтения параметра из INI-файла Private Declare Auto Function GetPrivateProfileString Lib "kernel32" _ (ByVal lpAppName As String, _ ByVal lpKeyName As String, _ ByVal lpDefault As String, _ ByVal lpReturnedString As StringBuilder, _ ByVal nSize As Integer, _ ByVal lpFileName As String) As Int32 ' API - функция сохранения параметра в INI-файл Private Declare Auto Function WritePrivateProfileString Lib "Kernel32" _ (ByVal Section As String, _ ByVal Key As String, _ ByVal putStr As String, _ ByVal INIfile As String) As Int32 ' This delegate enables asynchronous calls for setting ' the text property on a TextBox control. Delegate Sub SetTextCallback([text] As String, bClear As Boolean) #End Region #Region "COM Collectors" ' Recollect COM port setting Private Sub Refresh_COMSetting() _continue = False ' Dim readThread As Thread = New Thread(AddressOf Read) ' Create a new SerialPort object with default settings. Call CollectParity() Call CollectBaudRate() Call DataBitsSet() Call StopBitsSet() Call FlowControlSet() _continue = True End Sub ' Collect list of available COM ports (registry info) Private Sub CollectCOMM_List() Dim listCOM As String() = IO.Ports.SerialPort.GetPortNames() Dim port As String ' Clear list of COM Ports ComboBoxCOM_List.Items.Clear() ' Get list of serial COM ports ' Fill ComboBox, names of COM For Each port In listCOM ComboBoxCOM_List.Items.Add(port) Next port ComboBoxCOM_List.SelectedIndex = 0 End Sub ' Fill ComboBox BaudRate value and set ComboBox to port value allready setted Private Sub CollectBaudRate() 'Fill ComboBox_BaudList available speed list Dim BaudRate As String Dim DefBaud As Integer Dim DefSel As Integer Dim SelBox As Integer ComboBox_BaudList.Items.Clear() DefSel = 0 DefBaud = SerialPortA.BaudRate For Each BaudRate In BaudList Try SerialPortA.BaudRate = Int(BaudRate) If Int(BaudRate) = DefBaud Then SelBox = DefSel End If DefSel = DefSel + 1 ComboBox_BaudList.Items.Add(BaudRate) Catch ex As Exception ' Do nothing End Try Next SerialPortA.BaudRate = DefBaud ComboBox_BaudList.SelectedIndex = SelBox End Sub ' Fill ComboBox DataBits value and set ComboBox to port value allready setted Private Sub DataBitsSet() Dim DefDataBits As Integer Dim sBits As Integer Dim BoxSel, nSel As Integer ComboBox_DataBits.Items.Clear() DefDataBits = SerialPortA.DataBits nSel = 0 For Each sBits In DataBitsList If Int(sBits) = DefDataBits Then BoxSel = nSel End If nSel = nSel + 1 ComboBox_DataBits.Items.Add(sBits) Next ComboBox_DataBits.SelectedIndex = BoxSel End Sub ' Fill ComboBox StopBits value and set ComboBox to port value allready setted Private Sub StopBitsSet() ComboBox_StopBits.Items.Clear() Dim s As String For Each s In [Enum].GetNames(GetType(StopBits)) ComboBox_StopBits.Items.Add(s) Next s ComboBox_StopBits.SelectedIndex = SerialPortA.StopBits End Sub ' Fill ComboBox Parity value and set ComboBox to port value allready setted Private Sub CollectParity() Dim s As String ComboBox_ParityList.Items.Clear() For Each s In [Enum].GetNames(GetType(Parity)) ComboBox_ParityList.Items.Add(s) Next s ComboBox_ParityList.SelectedIndex = SerialPortA.Parity End Sub ' Fill ComboBox Handshake value and set ComboBox to port value allready setted Private Sub FlowControlSet() Dim s As String ComboBox_FlowControl.Items.Clear() For Each s In [Enum].GetNames(GetType(Handshake)) ComboBox_FlowControl.Items.Add(s) Next s ComboBox_FlowControl.SelectedIndex = SerialPortA.Parity End Sub #End Region #Region "Thread safe sub" ' Thread-Safe write to console TextBoxM Private Sub WriteToConsole(ByVal [text] As String, Optional bClear As Boolean = False) If Me.TextBoxM.InvokeRequired Then Dim d As New SetTextCallback(AddressOf WriteToConsole) Me.Invoke(d, New Object() {[text], bClear}) Else _readPort = False If bClear Then Me.TextBoxM.Text = [text] Else Me.TextBoxM.AppendText([text]) End If Me.CB_DataRecived.Checked = MessPull.MessageRecieved End If End Sub ' Thread-Safe write to console TextBox_Recieved Private Sub WriteToRecieved(ByVal [text] As String, Optional bClear As Boolean = False) If Me.TextBox_Recieved.InvokeRequired Then Dim d As New SetTextCallback(AddressOf WriteToRecieved) Me.Invoke(d, New Object() {[text], bClear}) Else If bClear Then Me.TextBox_Recieved.Text = [text] Else Me.TextBox_Recieved.AppendText([text]) End If End If End Sub #End Region #Region "Local procedure" ' Load token list from file Private Sub LoadToken(ByVal fName As String) Dim nCntr As Integer Dim sToken As String If File.Exists(fName) Then Dim rdStream As New StreamReader(fName) hashToken = New Hashtable nCntr = 1 While Not rdStream.EndOfStream sToken = rdStream.ReadLine() hashToken.Add(sToken, nCntr) nCntr += 1 End While rdStream.Close() End If End Sub ' Write COM port configuration to file Private Sub WriteConfig(ByVal fName) WritePrivateProfileString("SERIAL1", "PortName", ComboBoxCOM_List.Text, fName) WritePrivateProfileString("SERIAL1", "BaudRate", ComboBox_BaudList.Text, fName) WritePrivateProfileString("SERIAL1", "Parity", Str(ComboBox_ParityList.SelectedIndex), fName) WritePrivateProfileString("SERIAL1", "DataBits", ComboBox_DataBits.Text, fName) WritePrivateProfileString("SERIAL1", "StopBits", Str(ComboBox_StopBits.SelectedIndex), fName) WritePrivateProfileString("SERIAL1", "Handshake", Str(ComboBox_FlowControl.SelectedIndex), fName) WritePrivateProfileString("DATE", "LastSaved", Date.Today, fName) ' Form Size & Position WritePrivateProfileString("FORMPOS", "Y", Me.Location.Y.ToString(), fName) WritePrivateProfileString("FORMPOS", "X", Me.Location.X.ToString(), fName) WritePrivateProfileString("FORMPOS", "Height", Me.Height.ToString(), fName) WritePrivateProfileString("FORMPOS", "Width", Me.Width.ToString(), fName) ' TabControl WritePrivateProfileString("INTERNAL", "TabActive", TabControlA.SelectedIndex.ToString(), fName) WritePrivateProfileString("SCRIPT", "QICSGP", TextBoxBearer.Text, fName) WritePrivateProfileString("SCRIPT", "QIOPEN", TextBoxIP.Text, fName) End Sub ' Load COM port configuration from file Private Sub LoadConfig(ByVal fName) Dim nLength As Integer Dim sBuff As StringBuilder Dim iniparam As String Dim nIndex As Integer Dim aPoint As Point sBuff = New StringBuilder(500) sBuff.Clear() If File.Exists(fName) Then nLength = GetPrivateProfileString("SERIAL1", "PortName", ComboBoxCOM_List.Text, sBuff, sBuff.Capacity, fName) iniparam = sBuff.ToString() nIndex = ComboBoxCOM_List.FindString(iniparam) If nIndex >= 0 Then ComboBoxCOM_List.SelectedIndex = nIndex SerialPortA.PortName = ComboBoxCOM_List.Text End If sBuff.Clear() ' nLength = GetPrivateProfileString("SERIAL1", "BaudRate", ComboBox_BaudList.Text, sBuff, sBuff.Capacity, fName) iniparam = sBuff.ToString() nIndex = ComboBox_BaudList.FindString(iniparam) If nIndex >= 0 Then ComboBox_BaudList.SelectedIndex = nIndex SerialPortA.BaudRate = Int(ComboBox_BaudList.Text) End If sBuff.Clear() ' nLength = GetPrivateProfileString("SERIAL1", "Parity", Str(ComboBox_ParityList.SelectedIndex), sBuff, sBuff.Capacity, fName) iniparam = sBuff.ToString() ComboBox_ParityList.SelectedIndex = Int(iniparam) SerialPortA.Parity = Int(iniparam) sBuff.Clear() ' nLength = GetPrivateProfileString("SERIAL1", "DataBits", ComboBox_DataBits.Text, sBuff, sBuff.Capacity, fName) iniparam = sBuff.ToString() nIndex = ComboBox_DataBits.FindString(iniparam) If nIndex >= 0 Then ComboBox_DataBits.SelectedIndex = nIndex SerialPortA.DataBits = Int(iniparam) End If sBuff.Clear() ' nLength = GetPrivateProfileString("SERIAL1", "StopBits", Str(ComboBox_StopBits.SelectedIndex), sBuff, sBuff.Capacity, fName) iniparam = sBuff.ToString() ComboBox_StopBits.SelectedIndex = Int(iniparam) SerialPortA.StopBits = Int(iniparam) sBuff.Clear() ' nLength = GetPrivateProfileString("SERIAL1", "Handshake", Str(ComboBox_FlowControl.SelectedIndex), sBuff, sBuff.Capacity, fName) iniparam = sBuff.ToString() ComboBox_FlowControl.SelectedIndex = Int(iniparam) SerialPortA.Handshake = Int(iniparam) ' Form size & position sBuff.Clear() nLength = GetPrivateProfileString("FORMPOS", "Y", Me.Location.Y.ToString(), sBuff, sBuff.Capacity, fName) aPoint.Y = Int(sBuff.ToString()) sBuff.Clear() nLength = GetPrivateProfileString("FORMPOS", "X", Me.Location.X.ToString(), sBuff, sBuff.Capacity, fName) aPoint.X = Int(sBuff.ToString()) Me.Location = aPoint sBuff.Clear() nLength = GetPrivateProfileString("FORMPOS", "Height", Me.Height.ToString(), sBuff, sBuff.Capacity, fName) Me.Height = Int(sBuff.ToString()) sBuff.Clear() nLength = GetPrivateProfileString("FORMPOS", "Width", Me.Width.ToString(), sBuff, sBuff.Capacity, fName) Me.Width = Int(sBuff.ToString()) ' Tab control sBuff.Clear() nLength = GetPrivateProfileString("INTERNAL", "TabActive", TabControlA.SelectedIndex.ToString(), sBuff, sBuff.Capacity, fName) nLength = Int(sBuff.ToString()) TabControlA.SelectTab(nLength) ' Script process sBuff.Clear() nLength = GetPrivateProfileString("INTERNAL", "ScriptAutoLoad", "No", sBuff, sBuff.Capacity, fName) If UCase(sBuff.ToString()) = "YES" Then AutoLoadScriptToolStripMenuItem.Checked = True sBuff.Clear() Dim scriptPathName As String = sBuff.ToString() nLength = GetPrivateProfileString("INTERNAL", "ScriptFile", "", sBuff, sBuff.Capacity, fName) scriptPathName = sBuff.ToString() Dim read As New StreamReader(scriptPathName) TextBox_Script.Text = read.ReadToEnd.ToString read.Close() End If ' Load default token nLength = GetPrivateProfileString("SCRIPT", "TokenFileName", Application.StartupPath & "\Default.tok", sBuff, sBuff.Capacity, fName) Call LoadToken(sBuff.ToString()) sBuff.Clear() nLength = GetPrivateProfileString("SCRIPT", "QICSGP", "1,""inet.bwc.ru""", sBuff, sBuff.Capacity, fName) TextBoxBearer.Text = sBuff.ToString() sBuff.Clear() nLength = GetPrivateProfileString("SCRIPT", "QIOPEN", """TCP"",""46.254.241.3"",9999""", sBuff, sBuff.Capacity, fName) TextBoxIP.Text = sBuff.ToString() End If End Sub ' Check native token Private Function CheckNativeToken(ByVal sToken) As Boolean CheckNativeToken = hashToken.Contains(sToken) End Function ' Run script operation Private Sub RunScript() Dim sRet, sTemp As String Dim timeStart As Date Dim timeStop As Date Dim nTime As Double = 0 Dim nCount As Integer Dim tSpan As TimeSpan Dim serialStatus As Boolean = SerialPortA.IsOpen Dim seansOpen As Boolean = False Dim sBlock440 As String = "operations$öûâðëéîð÷ñéðöâéîðó2837âí2àãïëöãéïàöëäî3ïðàëîöíóïàùã34ïàùãà4óíïùöãíï4àêãùíöïùøãí4ïàùãöóåïàêãöù4ïàùãø4íïàùöã4íïàêùö6å4ê6åïöøãíàïö4ùïåàöùàïöùãà4ïöùã47ïåàêùöïù4ãïàøöãàï4ùãï4àùãöïàùöãï4àøùã6ï4àùãö4ïåàøãöï64àøã46ïà6öïàø76ö4ïàäöãàïíöãøíïuiowq3ygrowg3fowrtgowufygbow4gfowueyfgco6wt4go68gfrwkuyfcgwkgy4ckuw4ygfouwygou4fgrfouwf4gow4grfw4yofgowu4ygfou4fgwo4uygfwy4gfow4g4gfowufgou4rgyfwuyfguwygf4uyg4f6gfiu4gklejhdkwehfewhrkferhiuht43iuhiur" ' Блок переменных для фиксации временных рараметров и отказов Dim nDisconnect As Integer = 0 Dim nMinTimeA, nMinTimeB, nMaxTimeA, nMaxTimeB, nEvgTimeA, nEvgTimeB As Integer Dim nSendAttemptA, nSendAttemptB, nLostPackA, nLostPackB As Integer Dim nSendSuccA, nSendSuccB As Integer Dim nLostConnect As Integer = 0 Dim timeSummary As Double = 0 Dim timeSummaryMin As Double = 0 Dim timeSummaryMax As Double = 0 Dim timeSummaryEvg As Double = 0 _allowRun = True _allowEcho = True _ReadBuff = "" _lastClear = False _lastLine = "" nMinTimeA = 0 nMinTimeB = 0 nMaxTimeA = 0 nMaxTimeB = 0 nEvgTimeA = 0 nEvgTimeB = 0 nSendAttemptA = 0 nSendAttemptB = 0 nLostPackA = 0 nLostPackB = 0 nSendSuccA = 0 nSendSuccB = 0 'Настройка пула сообщений MessPull.ClearMessage() MessPull.AllowCMEErrorIntercept = True MessPull.AllowFuncIntercept = True MessPull.AllowSMSErrorIntercept = True MessPull.DropCME_Error() MessPull.DropSMS_Error() ' Начало операций Dim record As New StreamWriter(Application.StartupPath & "\Default.log") Me.WriteToConsole("Script started at " + DateTime.Now.ToLongTimeString + "!" + vbCrLf) record.WriteLine("Script started at " + DateTime.Now.ToLongTimeString + "!") If Not SerialPortA.IsOpen Then Try SerialPortA.Open() Catch Me.WriteToConsole("Serial Port: " + SerialPortA.PortName + " is busy! Script stopped at: " + DateTime.Now.ToLongTimeString + "!" + vbCrLf) record.WriteLine("Serial Port: " + SerialPortA.PortName + " is busy! Script stopped at: " + DateTime.Now.ToLongTimeString + "!") _allowRun = False End Try End If ' Инициаллизация модема If _allowRun Then nCount = 0 sTemp = "" MessPull.ClearMessage() While ((InStr(sTemp, "OK") = 0) And _allowRun) nCount += 1 sTemp = MessPull.ExecuteAT(SerialPortA, "AT" + vbCrLf, 3000) Me.WriteToConsole("Attempt: " + Str(nCount) + " Send: " + sTemp) End While Me.DisplayFunc() ' Включаем модем Me.WriteToConsole("Turn on the modem!" + vbCrLf) If Not MessPull.CheckAndSetup(SerialPortA, "AT+CFUN?", "CFUN", "1", , , , , "AT+CFUN=1", "Call Ready", 15000) Then Me.WriteToConsole("Modem does not turn on! Script Aborted!" + vbCrLf) _allowRun = False Else ' Малозначимые настраиваем параметры Me.WriteToConsole(MessPull.ExecuteAT(SerialPortA, "ATE1", 3000)) MessPull.CheckAndSetup(SerialPortA, "AT+CREG?", "CREG", "1", 0, ",", "OK", 1000, "AT+CREG=1", "OK", 5000) MessPull.CheckAndSetup(SerialPortA, "AT+CGREG?", "CGREG", "1", 0, ",", "OK", 1000, "AT+CGREG=1", "OK", 5000) MessPull.CheckAndSetup(SerialPortA, "AT+CRC?", "CRC", "0", 0, ",", "OK", 1000, "AT+CRC=0", "OK", 5000) MessPull.CheckAndSetup(SerialPortA, "AT+QAUDCH?", "QAUDCH", "2", 0, ",", "OK", 1000, "AT+QAUDCH=2", "OK", 5000) MessPull.CheckAndSetup(SerialPortA, "AT+QIFGCNT?", "QIFGCNT", "1", 0, ",", "OK", 1000, "AT+QIFGCNT=1", "OK", 5000) MessPull.CheckAndSetup(SerialPortA, "AT+QICSGP?", "QICSGP", "1", 0, ",", "OK", 1000, "AT+QICSGP=" + _QICSGP, "OK", 5000) End If Me.DisplayFunc() End If While _allowRun While _allowRun If (MessPull.CheckAndSetup(SerialPortA, "AT+CREG?", "CREG", "1", 1) And MessPull.CheckAndSetup(SerialPortA, "AT+CGREG?", "CGREG", "1", 1)) Then Exit While End If Me.DisplayFunc() End While Me.DisplayFunc() Me.WriteToConsole("Modem is registered on the home network at: " + DateTime.Now.ToLongTimeString + "!" + vbCrLf) record.WriteLine("Modem is registered on the home network at: " + DateTime.Now.ToLongTimeString + "!") ' Цикл установления соединения While (_allowRun And (Not seansOpen = True)) Me.DisplayFunc() Me.WriteToConsole(MessPull.ExecuteAT(SerialPortA, "AT+QISTAT", 2000) + vbCrLf) 'MessPull.WaitAnswer(SerialPortA, 5000, "", False) 'record.WriteLine("Request status at: " + DateTime.Now.TimeOfDay.ToString + "!") sRet = MessPull.GetFuncValue("STATE") Select Case sRet Case "IP INITIAL" MessPull.CheckAndSetup(SerialPortA, "AT+QISDE?", "QISDE", "0", 0, ",", "OK", 1000, "AT+QISDE = 0", 5000) MessPull.CheckAndSetup(SerialPortA, "AT+QIMUX?", "QIMUX", "0", 0, ",", "OK", 1000, "AT+QIMUX = 0", 5000) MessPull.CheckAndSetup(SerialPortA, "AT+QIMODE?", "QIMODE", "0", 0, ",", "OK", 1000, "AT+QIMODE=0", 5000) MessPull.CheckAndSetup(SerialPortA, "AT+QIDNSIP?", "QIDNSIP", "0", 0, ",", "OK", 1000, "AT+QIDNSIP=0", 5000) If Not InStr(MessPull.ExecuteAT(SerialPortA, "AT+QIREGAPP", 1000, "OK|ERROR"), "OK") > 0 Then Me.WriteToConsole("Can not register application on network! Next Attempt!" + vbCrLf) record.WriteLine("Can't Activate GPRS/CSD context! Next Attempt!") End If Case "IP START" If Not InStr(MessPull.ExecuteAT(SerialPortA, "AT+QIACT", 1000, "OK|ERROR"), "OK") > 0 Then Me.WriteToConsole("Can't Activate GPRS/CSD context! Next Attempt!" + vbCrLf) record.WriteLine("Can't Activate GPRS/CSD context! Next Attempt!") End If Case "IP GPRSACT" If Not InStr(MessPull.ExecuteAT(SerialPortA, "AT+QILOCIP", 2000, "."), ".") > 0 Then Me.WriteToConsole("Can't Get local IP addres!" + vbCrLf) record.WriteLine("Can't Get local IP addres!") End If Case "IP STATUS" If Not InStr(MessPull.ExecuteAT(SerialPortA, "AT+QIOPEN=" + _QIOPEN, 5000, "OK"), "OK") > 0 Then seansOpen = True Else Me.WriteToConsole("Can not estabilish connect!" + vbCrLf) record.WriteLine("Can not estabilish connect!") seansOpen = False End If Case "IP CLOSE" MessPull.ExecuteAT(SerialPortA, "AT+QIDEACT", 5000, "DEACT OK") Me.WriteToConsole("Deactivate GPRS/CSD context!" + vbCrLf) record.WriteLine("Deactivate GPRS/CSD context!") seansOpen = False Case "CONNECT OK" seansOpen = True Me.WriteToConsole("Connection OK!" + vbCrLf) record.WriteLine("Connection OK!") Case "PDP DEACT" Me.WriteToConsole("GPRS/CSD context was deactivated because of unknown reason. Reconnecting!" + vbCrLf) record.WriteLine("GPRS/CSD context was deactivated because of unknown reason. Reconnecting!") If Not InStr(MessPull.ExecuteAT(SerialPortA, "AT+QIACT", 1000, "OK|ERROR"), "OK") > 0 Then Me.WriteToConsole("Can't Activate GPRS/CSD context! Next Attempt!" + vbCrLf) record.WriteLine("Can't Activate GPRS/CSD context! Next Attempt!") End If Case "TCP CONNECTING" Sleep(800) Case Else Me.WriteToConsole("Connection status: " + sRet + vbCrLf) record.WriteLine("Connection status: " + sRet) End Select Me.WriteToConsole("Connection status: " + sRet + vbCrLf) Me.DisplayFunc() End While While (seansOpen And _allowRun) MessPull.ClearMessage() MessPull.ExecuteAT(SerialPortA, "AT+QISTAT") timeSummary = 0 nSendAttemptA += 1 timeStart = Now sRet = Trim(MessPull.ExecuteAT(SerialPortA, "AT+QISEND=8", 3000, ">", False)) 'Me.WriteToConsole("Time execution AT+QISEND=8" + MessPull.LastTimeRun.ToString + vbCrLf) If InStr(sRet, ">") > 0 Then sRet = MessPull.ExecuteAT(SerialPortA, "balance$", 3000, "SEND OK", False) 'Me.WriteToConsole("Time execution [balance$]" + MessPull.LastTimeRun.ToString + vbCrLf) If InStr(sRet, "SEND OK") > 0 Then _nCharWait = 40 sRet = MessPull.WaitAnswer(SerialPortA, 10000, "balance>................................", , False) If sRet = "balance>................................" Then Me.WriteToConsole(vbCrLf) timeStop = Now tSpan = timeStop.Subtract(timeStart) nTime = tSpan.TotalMilliseconds timeSummary = tSpan.TotalMilliseconds nMinTimeA = IIf(nMinTimeA = 0, nTime, IIf(nMinTimeA > nTime, nTime, nMinTimeA)) nMaxTimeA = IIf(nMaxTimeA = 0, nTime, IIf(nMaxTimeA < nTime, nTime, nMaxTimeA)) nEvgTimeA = IIf(nEvgTimeA = 0, nTime, (nTime + nEvgTimeA) / 2) nSendSuccA += 1 ' Теперь пытаемся выслать пакет nSendAttemptB += 1 timeStart = Now sRet = Trim(MessPull.ExecuteAT(SerialPortA, "AT+QISEND=440", 3000, ">", False)) 'Me.WriteToConsole("Time execution AT+QISEND=440" + MessPull.LastTimeRun.ToString + vbCrLf) If InStr(sRet, ">") > 0 Then MessPull.ClearMessage() _nCharWait = 4 sRet = MessPull.ExecuteAT(SerialPortA, sBlock440, 5000, "SEND OK", False) If InStr(sRet, "SEND OK") > 0 Then sRet = MessPull.WaitAnswer(SerialPortA, 10000, "done", , False) If InStr(sRet, "done") > 0 Then timeStop = Now tSpan = timeStop.Subtract(timeStart) nTime = tSpan.TotalMilliseconds timeSummary = timeSummary + tSpan.TotalMilliseconds nMinTimeB = IIf(nMinTimeB = 0, nTime, IIf(nMinTimeB > nTime, nTime, nMinTimeB)) nMaxTimeB = IIf(nMaxTimeB = 0, nTime, IIf(nMaxTimeB < nTime, nTime, nMaxTimeB)) nEvgTimeB = IIf(nEvgTimeB = 0, nTime, (nTime + nEvgTimeB) / 2) nSendSuccB += 1 Me.WriteToConsole(vbCrLf) Me.WriteToConsole("Cycle Ok. Time: " + timeSummary.ToString + vbCrLf) record.WriteLine("Cycle Ok. Time: " + timeSummary.ToString) timeSummaryMin = IIf(timeSummaryMin = 0, timeSummary, IIf(timeSummaryMin > timeSummary, timeSummaryMin, timeSummaryMin)) timeSummaryMax = IIf(timeSummaryMax = 0, timeSummary, IIf(timeSummaryMax < timeSummary, timeSummary, timeSummaryMax)) timeSummaryEvg = IIf(timeSummaryEvg = 0, timeSummary, (timeSummary + timeSummaryEvg) / 2) Else nLostPackB += 1 Me.WriteToConsole("Not Answer (440 byte). Time: " + nTime.ToString + vbCrLf) record.WriteLine("Not Answer (440 byte). Time: " + nTime.ToString) End If Else Me.WriteToConsole("Unsuccessfully send Data Pack (440 byte). Time: " + nTime.ToString + vbCrLf) record.WriteLine("Unsuccessfully send Data Pack (440 byte). Time: " + nTime.ToString) End If Else timeStop = Now tSpan = timeStop.Subtract(timeStart) nTime = tSpan.TotalMilliseconds Me.WriteToConsole("Error execute command AT+QISEND=440. Time: " + nTime.ToString + vbCrLf) record.WriteLine("Error execute command AT+QISEND=440. Time: " + timeSummary.ToString) End If Else timeStop = Now tSpan = timeStop.Subtract(timeStart) nTime = tSpan.TotalMilliseconds nLostPackA += 1 Me.WriteToConsole("Unsuccessfully execute command [balance$]. Time: " + nTime.ToString + vbCrLf) record.WriteLine("Unsuccessfully execute command [balance$]. Time: " + nTime.ToString) End If Else Me.WriteToConsole("Unsuccessfully send command [balance$]. Time: " + nTime.ToString + vbCrLf) record.WriteLine("Unsuccessfully send command [balance$]. Time: " + nTime.ToString) MessPull.ExecuteAT(SerialPortA, "AT", 3000, "") MessPull.ExecuteAT(SerialPortA, "AT", 3000, "") timeStop = Now tSpan = timeStop.Subtract(timeStart) nTime = tSpan.TotalMilliseconds End If Else MessPull.ExecuteAT(SerialPortA, "AT", 3000, "") Me.WriteToConsole("Error execute command AT+QISEND=8!" + vbCrLf) record.WriteLine("Error execute command AT+QISEND=8!") End If MessPull.SetFuncValue("CounterA", nSendAttemptA.ToString) MessPull.SetFuncValue("CounterB", nSendAttemptB.ToString) MessPull.SetFuncValue("AverageTimeA", nEvgTimeA.ToString) MessPull.SetFuncValue("AverageTimeB", nEvgTimeB.ToString) Me.DisplayFunc() If Not MessPull.GetFuncValue("STATE") = "CONNECT OK" Then nLostConnect += 1 seansOpen = False Me.WriteToConsole("Connection lost at: " + DateTime.Now.ToLongTimeString + vbCrLf) record.WriteLine("Connection lost at: " + DateTime.Now.ToLongTimeString) Me.WriteToConsole("Attempt to reconnect." + vbCrLf) record.WriteLine("Attempt to reconnect.") End If Sleep(2000) End While End While MessPull.ExecuteAT(SerialPortA, "AT+QICLOSE", 5000, "CLOSE OK|ERROR") MessPull.ExecuteAT(SerialPortA, "AT+QIDEACT", 5000, "DEACT OK|ERROR") Me.WriteToConsole("Connection Closed and GPRS/CSD context was deactivated at: " + DateTime.Now.ToLongTimeString + "!" + vbCrLf) record.WriteLine("Connection Closed and GPRS/CSD context was deactivated at: " + DateTime.Now.ToLongTimeString + "!") Me.WriteToConsole("---------------------------------------------------------------------------------------------" + vbCrLf) record.WriteLine("---------------------------------------------------------------------------------------------") Me.WriteToConsole("Execution repport:" + vbCrLf) record.WriteLine("Execution repport:") Me.WriteToConsole("[balance$] request MIN time (milliseconds): " + nMinTimeA.ToString + vbCrLf) record.WriteLine("[balance$] request MIN time (milliseconds): " + nMinTimeA.ToString) Me.WriteToConsole("[balance$] request MAX time (milliseconds): " + nMaxTimeA.ToString + vbCrLf) record.WriteLine("[balance$] request MAX time (milliseconds): " + nMaxTimeA.ToString) Me.WriteToConsole("[balance$] request AVERAGE time (milliseconds): " + nEvgTimeA.ToString + vbCrLf) record.WriteLine("[balance$] request AVERAGE time (milliseconds): " + nEvgTimeA.ToString) Me.WriteToConsole(vbCrLf) record.WriteLine("") Me.WriteToConsole("[440 byte pack] request MIN time (milliseconds): " + nMinTimeB.ToString + vbCrLf) record.WriteLine("[440 byte pack] request MIN time (milliseconds): " + nMinTimeB.ToString) Me.WriteToConsole("[440 byte pack] request MAX time (milliseconds): " + nMaxTimeB.ToString + vbCrLf) record.WriteLine("[440 byte pack] request MAX time (milliseconds): " + nMaxTimeB.ToString) Me.WriteToConsole("[440 byte pack] request AVERAGE time (milliseconds): " + nEvgTimeB.ToString + vbCrLf) record.WriteLine("[440 byte pack] request AVERAGE time (milliseconds): " + nEvgTimeB.ToString) Me.WriteToConsole(vbCrLf) record.WriteLine("") Me.WriteToConsole("Summary request MIN time (milliseconds): " + timeSummaryMin.ToString + vbCrLf) record.WriteLine("Summary request MIN time (milliseconds): " + timeSummaryMin.ToString) Me.WriteToConsole("Summary request MAX time (milliseconds): " + timeSummaryMax.ToString + vbCrLf) record.WriteLine("Summary request MAX time (milliseconds): " + timeSummaryMax.ToString) Me.WriteToConsole("Summary request AVERAGE time (milliseconds): " + timeSummaryEvg.ToString + vbCrLf) record.WriteLine("Summary request AVERAGE time (milliseconds): " + timeSummaryEvg.ToString) Me.WriteToConsole(vbCrLf) record.WriteLine("") Me.WriteToConsole("Total send attempt command [balance$]: " + nSendAttemptA.ToString + vbCrLf) record.WriteLine("Total send attempt command [balance$]: " + nSendAttemptA.ToString) Me.WriteToConsole("Total send attempt packet [440 byte]: " + nSendAttemptB.ToString + vbCrLf) record.WriteLine("Total send attempt packet [440 byte]: " + nSendAttemptB.ToString) Me.WriteToConsole("Total send succes [balance$]: " + nSendSuccA.ToString + vbCrLf) record.WriteLine("Total send succes [balance$]: " + nSendSuccA.ToString) Me.WriteToConsole("Total send succes [440 byte]: " + nSendSuccB.ToString + vbCrLf) record.WriteLine("Total send succes [440 byte]: " + nSendSuccB.ToString) Me.WriteToConsole(vbCrLf) record.WriteLine("") Me.WriteToConsole("Lost packet [balanse$]: " + nLostPackA.ToString + vbCrLf) record.WriteLine("Lost packet [balanse$]: " + nLostPackA.ToString) Me.WriteToConsole("Lost packet [440 byte]: " + nLostPackB.ToString + vbCrLf) record.WriteLine("Lost packet [440 byte]: " + nLostPackB.ToString) Me.WriteToConsole("---------------------------------------------------------------------------------------------" + vbCrLf) record.WriteLine("---------------------------------------------------------------------------------------------") Me.WriteToConsole("Script stopped at: " + DateTime.Now.ToLongTimeString + "!" + vbCrLf) record.WriteLine("Script stopped at: " + DateTime.Now.ToLongTimeString + "!") record.Close() 'Восстановим состояние модема If Not serialStatus Then SerialPortA.Close() End If 'Разрешим эхо в консоли _allowEcho = True End Sub Private Sub InitialIpSession() Dim sTemp As String ' Select a context as foreground context AT+QIFGCNT=1 sTemp = MessPull.ExecuteAT(SerialPortA, "AT+QIFGCNT=1", 3000) If sTemp = "OK" Then Me.WriteToConsole(sTemp + vbCrLf) Else Me.WriteToConsole("AT+QIFGCNT=1 Returns ERROR. Script stopped at:" + DateTime.Now.ToLongTimeString + "!" + vbCrLf) _allowRun = False 'Exit While End If 'AT+QICSGP=1,"inet.bwc.ru" sTemp = MessPull.ExecuteAT(SerialPortA, "AT+QICSGP=1,""inet.bwc.ru""", 3000) If sTemp = "OK" Then Me.WriteToConsole(sTemp + vbCrLf) Else Me.WriteToConsole("AT+QICSGP Returns ERROR. Script stopped at:" + DateTime.Now.ToLongTimeString + "!" + vbCrLf) _allowRun = False 'Exit While End If 'AT+QIMUX=0 sTemp = MessPull.ExecuteAT(SerialPortA, "AT+QIMUX=0", 3000) If sTemp = "OK" Then Me.WriteToConsole(sTemp + vbCrLf) Else Me.WriteToConsole("AT+QIMUX=0 Returns ERROR. Script stopped at:" + DateTime.Now.ToLongTimeString + "!" + vbCrLf) _allowRun = False 'Exit While End If 'AT+QIMODE=0 sTemp = MessPull.ExecuteAT(SerialPortA, "AT+QIMUX=0", 3000) If sTemp = "OK" Then Me.WriteToConsole(sTemp + vbCrLf) Else Me.WriteToConsole("AT+QIMODE=0 Returns ERROR. Script stopped at:" + DateTime.Now.ToLongTimeString + "!" + vbCrLf) _allowRun = False 'Exit While End If 'AT+QIDNSIP=0 sTemp = MessPull.ExecuteAT(SerialPortA, "AT+QIDNSIP=0", 3000) If sTemp = "OK" Then Me.WriteToConsole(sTemp + vbCrLf) Else Me.WriteToConsole("AT+QIDNSIP=0 Returns ERROR. Script stopped at:" + DateTime.Now.ToLongTimeString + "!" + vbCrLf) _allowRun = False 'Exit While End If 'AT+QIREGAPP sTemp = MessPull.ExecuteAT(SerialPortA, "AT+QIREGAPP", 3000) If sTemp = "OK" Then Me.WriteToConsole(sTemp + vbCrLf) Else Me.WriteToConsole("AT+QIREGAPP Returns ERROR. Script stopped at:" + DateTime.Now.ToLongTimeString + "!" + vbCrLf) _allowRun = False 'Exit While End If 'AT+QIACT sTemp = MessPull.ExecuteAT(SerialPortA, "AT+QIACT", 3000) If sTemp = "OK" Then Me.WriteToConsole(sTemp + vbCrLf) Else Me.WriteToConsole("AT+QIACT Returns ERROR. Script stopped at:" + DateTime.Now.ToLongTimeString + "!" + vbCrLf) _allowRun = False 'Exit While End If 'AT+QILOCIP sTemp = MessPull.ExecuteAT(SerialPortA, "AT+QILOCIP", 3000) If sTemp = "OK" Then Me.WriteToConsole(sTemp + vbCrLf) Else Me.WriteToConsole("AT+QILOCIP Returns ERROR. Script stopped at:" + DateTime.Now.ToLongTimeString + "!" + vbCrLf) _allowRun = False 'Exit While End If End Sub 'Возвращает последнюю цифру в из строки цифровых значений разделенных запятой Private Function GetLastValue(ByVal sParam As String) As Integer Dim aLine As String() = sParam.Split(",".ToCharArray) GetLastValue = Int(aLine(UBound(aLine))) End Function ' Тестовая функция, выводит все значения возвращаемых AT-функций в консоль принытых данных Private Sub DisplayFunc() Dim enumFunc As IDictionaryEnumerator = MessPull.FuncTable.GetEnumerator Me.WriteToRecieved("Function parametr list:" + vbCrLf, True) enumFunc.Reset() While enumFunc.MoveNext Me.WriteToRecieved(enumFunc.Key + "=" + enumFunc.Value.ToString + vbCrLf) End While End Sub ' Parse string Private Function StringParse(ByVal sText As String, Optional ByVal sParser As String = " ") As String() Dim sParam As Char() = sParser.ToCharArray() StringParse = {} If sText.Length > 0 Then StringParse = sText.Split(sParam) End If End Function Private Sub LoadKeyWord() Dim htKeyWord As Hashtable Dim aKeyToken As System.Delegate() 'aKeyToken.A() htKeyWord = aScript(scNames.scKeyWord) htKeyWord.Add("IF", 0) End Sub Private Sub SendToModem(ByVal sText) If SerialPortA.IsOpen Then SerialPortA.WriteLine(sText) Else TextBoxM.AppendText("Serial port: " + SerialPortA.PortName + " Is closed. Script Aborted!") _allowRun = False End If End Sub #End Region #Region "Event Handler's" ' Procedure handler recept any change in TextBoxM. Send command string to COM-port Private Sub TextBoxM_TextChanged(sender As System.Object, e As System.EventArgs) Handles TextBoxM.TextChanged Dim LineNum As Integer LineNum = Me.TextBoxM.Lines.Length If LineNum > 1 Then If (LineNum > _lineCount) Then If _readPort Then If SerialPortA.IsOpen Then _lastLine = Me.TextBoxM.Lines(LineNum - 2) _lastLine = Trim(_lastLine) SerialPortA.WriteLine(_lastLine + vbCrLf) Else _readPort = False Me.TextBoxM.AppendText("Serial port " + SerialPortA.PortName + " serial port is closed, open it first!" + vbCrLf) End If Else _readPort = True End If _lineCount = Me.TextBoxM.Lines.Length End If Else _lineCount = Me.TextBoxM.Lines.Length End If End Sub ' Clear text in TextBoxM on button click Private Sub BtClearText_Click(sender As System.Object, e As System.EventArgs) Handles BtClearText.Click TextBoxM.Clear() TextBoxM.Refresh() _lineCount = 0 End Sub ' Change port Name on ComboBox changes Private Sub ComboBoxCOM_List_SelectedIndexChanged(sender As System.Object, e As System.EventArgs) Handles ComboBoxCOM_List.SelectedIndexChanged If _continue Then Call Refresh_COMSetting() TSLabelCOM.Text = ComboBoxCOM_List.Text SerialPortA.PortName = ComboBoxCOM_List.Text End If End Sub ' Change port Parity on ComboBox changes Private Sub ComboBox_ParityList_SelectedIndexChanged(sender As System.Object, e As System.EventArgs) Handles ComboBox_ParityList.SelectedIndexChanged If _continue Then Try SerialPortA.Parity = ComboBox_ParityList.SelectedIndex Catch ex As Exception 'None MsgBox("Can't set selected parity." + vbCrLf, MsgBoxStyle.Exclamation, AcceptButton) End Try End If End Sub ' Change port Baud Rate on ComboBox changes Private Sub ComboBox_BaudList_SelectedIndexChanged(sender As System.Object, e As System.EventArgs) Handles ComboBox_BaudList.SelectedIndexChanged If _continue Then SerialPortA.BaudRate = Int(ComboBox_BaudList.Text) TSLabelSpeed.Text = ComboBox_BaudList.Text End If End Sub ' Change port Data Bits on ComboBox changes Private Sub ComboBox_DataBits_SelectedIndexChanged(sender As System.Object, e As System.EventArgs) Handles ComboBox_DataBits.SelectedIndexChanged If _continue Then SerialPortA.DataBits = Int(ComboBox_DataBits.Text) End If End Sub ' Change port Stop Bits on ComboBox changes Private Sub ComboBox_StopBits_SelectedIndexChanged(sender As System.Object, e As System.EventArgs) Handles ComboBox_StopBits.SelectedIndexChanged If _continue Then SerialPortA.StopBits = ComboBox_StopBits.SelectedIndex End If End Sub ' Change port Handshake on ComboBox changes Private Sub ComboBox_FlowControl_SelectedIndexChanged(sender As System.Object, e As System.EventArgs) Handles ComboBox_FlowControl.SelectedIndexChanged If _continue Then SerialPortA.Handshake = ComboBox_FlowControl.SelectedIndex End If End Sub ' Check COM port setting & write text to TextBox Private Sub BTCheck_Click(sender As System.Object, e As System.EventArgs) Handles BTCheck.Click TextBox1.AppendText("Serial port name: " + SerialPortA.PortName + vbCrLf) 'TextBox1.AppendText("Serial port num: " + Str(_serialPort.PortName) + vbCrLf) TextBox1.AppendText("Serial port status: " + IIf(SerialPortA.IsOpen, "Port Open", "Port Closed") + vbCrLf) TextBox1.AppendText("Baud Rate: " + SerialPortA.BaudRate.ToString + vbCrLf) TextBox1.AppendText("Parity: " + SerialPortA.Parity.ToString + vbCrLf) TextBox1.AppendText("Parity Num: " + Str(SerialPortA.Parity) + vbCrLf) TextBox1.AppendText("Data Bits: " + SerialPortA.DataBits.ToString + vbCrLf) TextBox1.AppendText("Stop Bits: " + SerialPortA.StopBits.ToString + vbCrLf) TextBox1.AppendText("Flow Control: " + SerialPortA.Handshake.ToString + vbCrLf) TextBox1.AppendText("Read Timeout: " + SerialPortA.ReadTimeout.ToString + vbCrLf) TextBox1.AppendText("Write Timeout: " + SerialPortA.WriteTimeout.ToString + vbCrLf) TextBox1.AppendText("-------------------------" + vbCrLf + vbCrLf) End Sub ' Clear text recieved in TextBox Private Sub BTClearRecieved_Click(sender As System.Object, e As System.EventArgs) Handles BTClearRecieved.Click TextBox_Recieved.Clear() _lastRead = "" End Sub ' Event handlers on form close. Private Sub CommCare_FormClosed(sender As System.Object, e As System.Windows.Forms.FormClosedEventArgs) Handles MyBase.FormClosed If SerialPortA.IsOpen Then SerialPortA.Close() End Sub ' Open/Close serial port Private Sub BtSerial_Click(sender As System.Object, e As System.EventArgs) Handles BtSerial.Click If SerialPortA.IsOpen Then Try SerialPortA.DiscardInBuffer() SerialPortA.DiscardOutBuffer() SerialPortA.Close() Catch ex As Exception End Try TSLastCommand.Text = "Serial port: " + SerialPortA.PortName.ToString + " closed!" BtSerial.Text = "Open Port" Else Try SerialPortA.Open() SerialPortA.ReadTimeout = 1000 SerialPortA.WriteTimeout = 1000 If SerialPortA.IsOpen Then BtSerial.Text = "Close Port" TSLastCommand.Text = "Serial port: " + SerialPortA.PortName.ToString + " open successfully!" Else BtSerial.Text = "Open Port" TSLastCommand.Text = "Can't open serial port: " + SerialPortA.PortName.ToString End If Catch ex As Exception End Try End If End Sub ' Save COM port setting on menu click Private Sub SaveSettingToolStripMenuItem_Click(sender As System.Object, e As System.EventArgs) Handles SaveSettingToolStripMenuItem.Click Dim saveDialog As New SaveFileDialog Dim fName As String saveDialog.InitialDirectory = Application.StartupPath saveDialog.Filter = "INI Files (*.ini)|*.ini" saveDialog.FileName = "Setting.ini" If saveDialog.ShowDialog = Windows.Forms.DialogResult.Cancel Then Exit Sub fName = saveDialog.InitialDirectory & "\" & saveDialog.FileName Call WriteConfig(fName) End Sub ' Save at-protocol text from TextBox to file Private Sub SaveATProtocolToolStripMenuItem_Click(sender As System.Object, e As System.EventArgs) Handles SaveATProtocolToolStripMenuItem.Click Dim saveDialog As New SaveFileDialog saveDialog.InitialDirectory = Application.StartupPath saveDialog.Filter = "Log Files (*.log)|*.log|All Files (*.*)|*.*" saveDialog.FileName = "CommCare.log" If saveDialog.ShowDialog = Windows.Forms.DialogResult.Cancel Then Exit Sub Dim record As New StreamWriter(saveDialog.FileName) record.Write(TextBoxM.Text) record.Close() End Sub ' Load at-protocol text into TextBox from file Private Sub LoapATProtocolToolStripMenuItem_Click(sender As System.Object, e As System.EventArgs) Handles LoapATProtocolToolStripMenuItem.Click Dim openDialog As New OpenFileDialog openDialog.InitialDirectory = Application.StartupPath openDialog.Filter = "Log Files (*.log)|*.log|All Files (*.*)|*.*" openDialog.FileName = "CommCare.log" If openDialog.ShowDialog = Windows.Forms.DialogResult.Cancel Then Exit Sub Dim read As New StreamReader(openDialog.FileName) TextBoxM.Text = read.ReadToEnd.ToString read.Close() End Sub ' Load form Events. Proceed initialisation Private Sub CommCare_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load Dim fName, defParam, retParam As String _lastRead = "" ' Empty _lastRead _allowEcho = True ' Permit echo the received data handler CB_DataRecived.Checked = False ' Set visual data recieved check ' Collect serial port info adn declare it _continue = False ' prevent reactions to fill ComboBox SerialPortA = New SerialPort() ' Create serial port object ' Define handler to recieved data AddHandler SerialPortA.DataReceived, AddressOf DataReceviedHandler 'SerialPortA.ReadTimeout = 1000 ' set Read timeout 1000mS 'SerialPortA.WriteTimeout = 1000 ' set Write timeout 1000mS Call CollectCOMM_List() ' Collect COM port Names ' Set port name first line ComboBox SerialPortA.PortName = ComboBoxCOM_List.Text Call Refresh_COMSetting() ' Collect the remaining serial port settings ' Complete fragments of the status bar TSLabelSpeed.Text = ComboBox_BaudList.Text TSLabelCOM.Text = ComboBoxCOM_List.Text _readPort = True ' Allow the reaction to the emergence of a new line in TextBoxM ' Read default config file fName = Application.StartupPath & "\Default.ini" Call LoadConfig(fName) _continue = True ' Allow the reaction to changes in the ComboBox's WritePrivateProfileString("INTERNAL", "ScriptSaved", "No", fName) WritePrivateProfileString("INTERNAL", "ScriptSaved", "No", fName) ' TextBox_Recieved.ReadOnly = True End Sub ' Close form events. Execute finishing action Private Sub CommCare_FormClosing(sender As System.Object, e As System.Windows.Forms.FormClosingEventArgs) Handles MyBase.FormClosing Dim fName As String fName = Application.StartupPath & "\Default.ini" Call WriteConfig(fName) If Not (ScriptThread.ThreadState = Threading.ThreadState.Unstarted) Then ScriptThread.Abort() ScriptThread.Join(1000) End If End Sub ' Load COM port setting from file Private Sub OpenSettingToolStripMenuItem_Click(sender As System.Object, e As System.EventArgs) Handles OpenSettingToolStripMenuItem.Click Dim openDialog As New OpenFileDialog Dim fName As String openDialog.InitialDirectory = Application.StartupPath openDialog.Filter = "INI Files (*.ini)|*.ini" openDialog.FileName = "Setting.ini" If openDialog.ShowDialog = Windows.Forms.DialogResult.Cancel Then Exit Sub fName = openDialog.InitialDirectory & "\" & openDialog.FileName _continue = False Call LoadConfig(fName) _continue = True End Sub ' Test button Parse click. Go parse testing string Private Sub BT_Parse_Click(sender As System.Object, e As System.EventArgs) Handles BT_Parse.Click Dim words() As String Dim word As String Dim textStr As String Dim delim As Char() Dim parseString As String parseString = TB_InStr.Text textStr = TB_Delimiters.Text delim = textStr.ToCharArray() words = parseString.Split(delim, StringSplitOptions.RemoveEmptyEntries) For Each word In words TB_Console.AppendText(word + vbCrLf) Next End Sub ' Test button Clear click. Clear TextBox text. Private Sub BT_Clear_Click(sender As System.Object, e As System.EventArgs) Handles BT_Clear.Click TB_Console.Clear() End Sub ' CheckBox prevent manually changes Private Sub CB_DataRecived_CheckedChanged(sender As System.Object, e As System.EventArgs) Handles CB_DataRecived.CheckedChanged CB_DataRecived.Checked = MessPull.MessageRecieved End Sub ' Load token from file Private Sub LoadTokenToolStripMenuItem_Click(sender As System.Object, e As System.EventArgs) Handles LoadTokenToolStripMenuItem.Click Dim openDialog As New OpenFileDialog openDialog.InitialDirectory = Application.StartupPath openDialog.Filter = "Token Files (*.tok)|*.tok|All Files (*.*)|*.*" 'openDialog.FileName = "Default.tok" If openDialog.ShowDialog = Windows.Forms.DialogResult.Cancel Then Exit Sub Call LoadToken(openDialog.FileName) WritePrivateProfileString("SCRIPT", "TokenFileName", openDialog.FileName, Application.StartupPath & "\Default.ini") End Sub ' Menu File-Exit Private Sub ExitToolStripMenuItem_Click(sender As System.Object, e As System.EventArgs) Handles ExitToolStripMenuItem.Click Application.Exit() End Sub ' Script Save to file Private Sub SaveTaskScriptToolStripMenuItem_Click(sender As System.Object, e As System.EventArgs) Handles SaveTaskScriptToolStripMenuItem.Click Dim saveDialog As New SaveFileDialog saveDialog.InitialDirectory = Application.StartupPath saveDialog.Filter = """Go"" Script Files (*.gos)|*.gos|All Files (*.*)|*.*" saveDialog.FileName = "Noname.gos" If saveDialog.ShowDialog = Windows.Forms.DialogResult.Cancel Then Exit Sub Dim record As New StreamWriter(saveDialog.FileName) record.Write(TextBox_Script.Text) record.Close() WritePrivateProfileString("INTERNAL", "ScriptSaved", "Yes", Application.StartupPath & "\Default.ini") WritePrivateProfileString("INTERNAL", "ScriptFile", saveDialog.FileName, Application.StartupPath & "\Default.ini") End Sub ' Script Load from file Private Sub LoadTaskScriptToolStripMenuItem_Click(sender As System.Object, e As System.EventArgs) Handles LoadTaskScriptToolStripMenuItem.Click Dim openDialog As New OpenFileDialog openDialog.InitialDirectory = Application.StartupPath openDialog.Filter = """Go"" Script Files (*.gos)|*.gos|All Files (*.*)|*.*" 'openDialog.FileName = "CommCare.gos" If openDialog.ShowDialog = Windows.Forms.DialogResult.Cancel Then Exit Sub Dim read As New StreamReader(openDialog.FileName) TextBox_Script.Text = read.ReadToEnd.ToString read.Close() WritePrivateProfileString("INTERNAL", "ScriptLoaded", "Yes", Application.StartupPath & "\Default.ini") WritePrivateProfileString("INTERNAL", "ScriptFile", openDialog.FileName, Application.StartupPath & "\Default.ini") End Sub ' Auto load script check/uncheck Private Sub AutoLoadScriptToolStripMenuItem_Click(sender As System.Object, e As System.EventArgs) Handles AutoLoadScriptToolStripMenuItem.Click AutoLoadScriptToolStripMenuItem.Checked = True WritePrivateProfileString("INTERNAL", "ScriptAutoLoad", "Yes", Application.StartupPath & "\Default.ini") End Sub #End Region #Region "COM port Handler's" Private Sub DataReceviedHandler(sender As Object, e As SerialDataReceivedEventArgs) Dim sp As SerialPort = CType(sender, SerialPort) Dim aLine As String() Dim nAt As Integer Dim sParam As Char() Dim sTemp As String Dim indata As String = "" indata = sp.ReadExisting() ' sTemp = vbCrLf sParam = sTemp.ToCharArray() ' _lastRead = _lastRead + indata 'If _lastRead.Length > 1 Then 'If _lastRead = "> " Then 'MessPull.AppendMess(Trim(_lastRead)) '_lastRead = "" 'End If 'End If nAt = InStr(_lastRead, vbCrLf) While nAt > 0 MessPull.AppendMess(Mid(_lastRead, 1, nAt - 1)) _lastRead = Mid(_lastRead, nAt + 2) nAt = InStr(_lastRead, vbCrLf) End While If (_nCharWait > 0) And (_lastRead.Length = _nCharWait) Then MessPull.AppendMess(Trim(_lastRead)) _lastRead = "" _nCharWait = 0 ElseIf _lastRead = "> " Then MessPull.AppendMess(Trim(_lastRead)) _lastRead = "" End If 'If nAt > 0 Then 'aLine = _lastRead.Split(sParam, StringSplitOptions.RemoveEmptyEntries) 'If aLine.Length > 0 Then 'MsPull.AppendLines(aLine) '_lastRead = "" 'End If 'End If ' If _allowEcho Then Call Me.WriteToConsole(indata) End If End Sub #End Region Private Sub BTRunStop_Click(sender As System.Object, e As System.EventArgs) Handles BTRunStop.Click If _allowRun Then _allowRun = False BTRunStop.Text = "Run Script" Else _lastRead = "" CB_DataRecived.Checked = MessPull.MessageRecieved _allowRun = True BTRunStop.Text = "Stop Script" ScriptThread = New Thread(AddressOf RunScript) TimerA.Interval = 10000 TimerA.Enabled = True _timeOut = False ScriptThread.Start() End If End Sub Private Sub TimerA_Tick(sender As System.Object, e As System.EventArgs) Handles TimerA.Tick If Not _timeOut Then _timeOut = True End Sub Private Sub BTTestMsg1_Click(sender As System.Object, e As System.EventArgs) Handles BTTestMsg1.Click Dim nCount As Integer = MessPull.AppendMess(TB_InStr.Text) TSLastCommand.Text = "Message count: " + Str(nCount) End Sub Private Sub BTTestMsg2_Click(sender As System.Object, e As System.EventArgs) Handles BTTestMsg2.Click Dim sText As String = TB_InStr.Text Dim sParam As String = " ,.;:!@#$%&*()-=+|\/?<>" Dim aParam As Char() = sParam.ToCharArray() TSLastCommand.Text = "Message count: " + Str(MessPull.AppendLines(sText.Split(aParam, StringSplitOptions.RemoveEmptyEntries))) End Sub Private Sub BTTestMsg3_Click(sender As System.Object, e As System.EventArgs) Handles BTTestMsg3.Click Dim alines As String() = MessPull.Items() Dim sText As String For Each sText In alines TB_Console.AppendText(sText + vbCrLf) Next End Sub Private Sub BTTestMsg4_Click(sender As System.Object, e As System.EventArgs) Handles BTTestMsg4.Click TB_InStr.Text = MessPull.ExtractMess() TSLastCommand.Text = "Message count: " + Str(MessPull.MsgCount()) End Sub Private Sub BTTestMsgClear_Click(sender As System.Object, e As System.EventArgs) Handles BTTestMsgClear.Click MessPull.ClearMessage() End Sub Private Sub TextBoxBearer_TextChanged(sender As System.Object, e As System.EventArgs) Handles TextBoxBearer.TextChanged _QICSGP = TextBoxBearer.Text End Sub Private Sub TextBoxIP_TextChanged(sender As System.Object, e As System.EventArgs) Handles TextBoxIP.TextChanged _QIOPEN = TextBoxIP.Text End Sub End Class
Всем наилучшего! Если вы найдете в исходном коде для себя полезные фрагменты — буду рад что оказался полезен!
