Imports System.IO
Imports System.Threading
Imports UTS_Core.Compress
Imports UTS_Core.Database
Imports UTS_Core.Security
Imports UTS_Core.UTSModule
Imports UTS_Core.UTSModule.License
Imports UTS_Core.UTSModule.Service
Imports UTS_Core.UTSModule.DbConnect
Imports UTS_Core.UTSModule.DbTableModel.Manage
Imports System.Text
Public Class UpdateService
''' AUTS_DataService服务名
Private _dsName As String
''' AUTS_DataService的索引
Private _dsIndex As String
''' 数据服务的文件夹路径,不包含版本
Private _dsDirPath As String
''' 数据服务升级时存放从FTP下载的最新包文件夹路径
Private _dsPacketDirPath As String
''' 服务本地版本
Private _dsVersion As String
''' 数据服务在线状态,0离线,1在线
Private _dsOnline As Integer
''' 服务上一次本地版本
Private _lastDsVersion As String
''' 当前服务正在运行
Private _serviceRunning As Boolean
''' Log文件夹路径
Private _logDirPath As String
''' 设置文件夹路径
Private _settingsDirPath As String
''' 更新服务版本
Private _usVersion As String
''' 更新服务版本
Private _usErrMsg As String
''' 更新服务名称
Private _usName As String
''' 是否已更新过版本
Private _uploaded As Boolean
''' 当前服务正在升级
Private _serviceUpdating As Boolean
''' 等待检测服务
Private _waitCheckService As Boolean
Protected Overrides Sub OnStart(args() As String)
' 请在此处添加代码以启动您的服务。此方法应完成设置工作,
' 以使您的服务开始工作。
Thread.Sleep(3000)
InitServiceInfo()
CreateSystemFolder()
ApplicationLog.InitApplicationLogInfo(_logDirPath, My.Application.Info.ProductName)
If CheckLicense() = False Then Return
UtsDb.InitConnectParams(_license) '根据License信息,初始化数据库连接信息
UTS_Core.DebugLog.ApplicationLog.WriteInfoLog($"InitConnectParams Success.")
ApplicationLog.VendorName = _license.VendorName '写入数据前指定软件公司名
ApplicationLog.StartSaveLogToDatabase() '需要先指定数据库路径
_ftpClient = New FtpService(UtsRegistry.FtpHost, CInt(_license.FtpPort), _license.FtpUser, _license.FtpPwd)
_serviceRunning = True
ApplicationLog.WriteTraceLog($"Start Monitor!")
ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf KeepServiceConnect))
ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf UpdateMonitor))
ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf UpdateServiceInfo))
End Sub
Protected Overrides Sub OnStop()
' 在此处添加代码以执行任何必要的拆解操作,从而停止您的服务。
StopUpdateMonitor()
ApplicationLog.WriteTraceLog($"UpdateService OnStop")
ApplicationLog.StopSaveLogToDatabase()
End Sub
#Region "准备工作"
'''
''' 初始化服务信息
'''
Private Sub InitServiceInfo()
_dsOnline = 1
_dsIndex = UtsRegistry.DataServiceIndex
_dsName = UtsRegistry.DataServiceName
_dsDirPath = $"{UtsRegistry.RootPath}\{UtsRegistry.DataServiceName}"
_dsPacketDirPath = $"{UtsRegistry.RootPath}\DataServiceDownload"
_usName = My.Application.Info.ProductName
_usVersion = My.Application.Info.Version.ToString
_usErrMsg = String.Empty
_logDirPath = $"{UtsRegistry.RootPath}\Log\{My.Application.Info.ProductName}"
_settingsDirPath = $"{UtsRegistry.UpdateServiceDirPath}\Settings"
UtsRegistry.UpdateServiceVersion = _usVersion
End Sub
'''
''' 创建运行时必要文件夹
'''
Private Sub CreateSystemFolder()
Directory.CreateDirectory(_dsDirPath) '数据服务文件夹路径
Directory.CreateDirectory(_dsPacketDirPath) '数据服务下载文件夹路径
Directory.CreateDirectory(_settingsDirPath) '创建设置文件夹
Directory.CreateDirectory(_logDirPath) '创建日志文件夹
End Sub
'''
''' 关闭更新服务
'''
Private Sub StopUpdateMonitor()
_serviceRunning = False
End Sub
#End Region
#Region "更新服务操作"
'''
''' 保持与服务的定期通讯
'''
Private Sub KeepServiceConnect(obj As Object)
While _serviceRunning
Thread.Sleep(1000)
'升级状态中,取消通讯
If _serviceUpdating Then Continue While
'开始通讯
If InitTcpClient() AndAlso GetServiceLocalVersion() Then '初始化网络客户端连接,进行获取版本号通讯
ResetServiceHeartBeatFailCount()
Else
UpdateServiceHeartBeatFailCount()
End If
End While
End Sub
'''
''' 定期更新数据库中服务的状态
'''
Private Sub UpdateServiceInfo(obj As Object)
Dim Interval As Integer = 1 * 60 * 1000
While _serviceRunning
Dim sb As New StringBuilder
sb.Append($"update `{UtsDb.RemotePublicDb}`.`{DataServiceListTable.TableName}` set ")
sb.Append($"`{DataServiceListTable.ColNames.USVer}` = '{_usVersion}' ")
sb.Append($",`{DataServiceListTable.ColNames.IsOnline}` = {_dsOnline} ")
sb.Append($",`{DataServiceListTable.ColNames.ServiceLastActiveDateTime}` = current_timestamp() ")
If String.IsNullOrEmpty(_usErrMsg) = False Then
sb.Append($",`{DataServiceListTable.ColNames.USErrMsg}` = '{_usErrMsg}' ")
_usErrMsg = String.Empty
End If
sb.Append($"where `{DataServiceListTable.ColNames.ID}` = {_dsIndex}")
Try
Using db As New DbExecutor(UtsDb.RemoteDbType, UtsDb.RemoteConnString)
db.Open()
db.ExecuteNonQuery(sb.ToString)
db.Close()
End Using
Catch ex As Exception
ApplicationLog.WriteErrorLog($"Update ServiceInfo Error:{ex.Message}")
End Try
Thread.Sleep(Interval)
End While
End Sub
'''
''' 定期检查DataService是否需要更新
'''
'''
Private Sub UpdateMonitor(obj As Object)
Dim Interval As Integer = 5 * 60 * 1000
While _serviceRunning
Try
DetectionService()
Catch ex As Exception
ApplicationLog.WriteErrorLog($"DetectionService Error:{ex.Message}")
End Try
'等待任务
Dim lastTime As Date = Now
_waitCheckService = True
While (Now - lastTime).TotalMilliseconds < Interval
If _waitCheckService = False Then
Exit While
End If
Thread.Sleep(1000)
End While
End While
End Sub
Private _startServiceFailCount As Integer = 0
Private ReadOnly _startServiceFailMaxCount As Integer = 3
Private _upgradeServiceFailCount As Integer = 0
Private ReadOnly _upgradeerviceFailMaxCount As Integer = 3
Private _serviceHeartBeatFailCount As Integer = 0
Private ReadOnly _serviceHeartBeatFailMaxCount As Integer = 30
'''
''' 检测服务
'''
Private Sub DetectionService()
'服务不存在处理
If WinService.ServicesExists(_dsName) = False Then
ApplicationLog.WriteTraceLog($"{_dsName} Service Not Exists,Begin Download Packet And Install Service!")
InstallDataService()
Return
End If
'服务存在未启动
If WinService.ServicesStatus(_dsName) <> WinService.ServiceStatusEnum.Started Then '存在,则检测是否启动
ApplicationLog.WriteTraceLog($"{_dsName} Service Not Running!Status:{WinService.ServicesStatus(_dsName)},Begin StartService!")
If WinService.StartService(_dsName) = False Then
UpdateStartServiceFailCount()
Return
End If
ApplicationLog.WriteTraceLog($"Start {_dsName} Service Success!")
Return
End If
'服务启动但失联
If _dsOnline = 0 Then
If WinService.RestartService(_dsName) = False Then
UpdateStartServiceFailCount()
Return
End If
ApplicationLog.WriteTraceLog($"ReStart {_dsName} Service Success!")
Return
End If
ResetStartServiceFailCount() '重置启动失败次数
If UpgradeDataService() Then
InstallDataService()
Else
Console.WriteLine($"服务已是最新版本!")
End If
End Sub
'''
''' 数据服务正常运行时,检查数据服务更新
'''
Private Function UpgradeDataService() As Boolean
'获取云端信息
Dim packetInfo As UpdatePackageInfo = GetDataServiceInfo()
If packetInfo Is Nothing Then Return False
'获取本地旧版本服务
Dim dataSerFilePath As String = UtsRegistry.DataServiceFilePath
'确定需要更新服务
If NeedToUpdateService(_dsVersion, packetInfo.LastVersion) = False Then
Return False
End If
ApplicationLog.WriteTraceLog($"Relldy To Update DataService,LocalVer:{_dsVersion},RemoteVer:{packetInfo.LastVersion}")
'卸载服务
Try
WinService.UnInstallService(dataSerFilePath, Nothing)
ApplicationLog.WriteTraceLog($"{dataSerFilePath} UnInstallService Success!")
Catch ex As Exception
ApplicationLog.WriteTraceLog($"{dataSerFilePath} UnInstallService Fail,{ex.Message}")
Return False
End Try
'ResetTcpConnection()
Return True
End Function
'''
''' 从远程获取数据服务信息,安装或覆盖数据服务
'''
''' 服务存在状态,如果服务存在则覆盖原文件,不存在则安装服务
Private Sub InstallDataService(Optional serviceExists As Boolean = False)
_serviceUpdating = True
Dim packetInfo As UpdatePackageInfo = GetDataServiceInfo()
If packetInfo Is Nothing Then Return
ApplicationLog.WriteTraceLog($"Begin DownloadServicePacket...")
If DownloadServicePacket(packetInfo, serviceExists) = False Then
ApplicationLog.WriteTraceLog($"DownloadServicePacket Fail!")
'添加下载失败计数
UpdateUpgradeFailCount()
_serviceUpdating = False
Return
End If
ApplicationLog.WriteTraceLog($"DownloadServicePacket Success!")
_serviceUpdating = False
End Sub
'''
''' 获取数据服务的最新信息
'''
Private Function GetDataServiceInfo() As UpdatePackageInfo
Try
If DbConnector.CanConnectToRemote = False Then '判断网络连接状态
ApplicationLog.WriteWarningLog($"Can’t Connect To Database!")
Return Nothing
End If
Using db As New DbExecutor(UtsDb.RemoteDbType, UtsDb.RemoteConnString)
db.Open()
Dim tableName As String = $"{SwUpdateTable.TableName}"
Dim colNames As New List(Of String) From {
$"{SwUpdateTable.ColNamesEnum.LastVersion}",
$"{SwUpdateTable.ColNamesEnum.BinPackageMd5}",
$"{SwUpdateTable.ColNamesEnum.PackageName}"
}
Dim condition As String = $"`{SwUpdateTable.ColNamesEnum.SoftwareName}` = '{_dsName}'"
Dim dtServiceInfo As DataTable = db.ExecuteDataTable(db.CmdHelper.DbSearch(UtsDb.RemotePublicDb, colNames, tableName, condition))
If dtServiceInfo.Rows.Count <= 0 Then
ApplicationLog.WriteErrorLog($"Database Can’t Find [{_dsName}] Software!")
db.Close()
Return Nothing
End If
Dim packetInfo As New UpdatePackageInfo
packetInfo.BinPackageMd5 = dtServiceInfo.Rows(0)($"{SwUpdateTable.ColNamesEnum.BinPackageMd5}").ToString()
packetInfo.LastVersion = dtServiceInfo.Rows(0)($"{SwUpdateTable.ColNamesEnum.LastVersion}").ToString()
packetInfo.PackageName = dtServiceInfo.Rows(0)($"{SwUpdateTable.ColNamesEnum.PackageName}").ToString()
db.Close()
Return packetInfo
End Using
Catch ex As Exception
ApplicationLog.WriteErrorLog($"GetServiceRemoteVersion Error:{ex.Message}!")
Return Nothing
End Try
End Function
'''
''' 重置心跳包失败计数
'''
Private Sub ResetServiceHeartBeatFailCount()
_serviceHeartBeatFailCount = 0
End Sub
'''
''' 心跳包通讯失败后,更新心跳包失败计数
'''
Private Sub UpdateServiceHeartBeatFailCount()
_serviceHeartBeatFailCount += 1
If _serviceHeartBeatFailCount < _serviceHeartBeatFailMaxCount Then Return
ApplicationLog.WriteErrorLog($"Services NetWork FailCount [{_serviceHeartBeatFailCount}] Is greater than the limit {_serviceHeartBeatFailMaxCount}")
_dsOnline = 0
ResetServiceHeartBeatFailCount()
ResetTcpConnection()
'立刻检测服务状态
_waitCheckService = False
End Sub
'''
''' 重置启动服务失败计数
'''
Private Sub ResetStartServiceFailCount()
_startServiceFailCount = 0
End Sub
'''
''' 重启或则启动服务失败后,更新启动服务失败计数,超过计数上限则重置数据服务内容
'''
Private Sub UpdateStartServiceFailCount()
_startServiceFailCount += 1
ApplicationLog.WriteWarningLog($"StartDataService Fail,FailCount:{_startServiceFailCount}!")
If _startServiceFailCount < _startServiceFailMaxCount Then Return
ApplicationLog.WriteErrorLog($"StartDataService Fail,FailCount:{_startServiceFailCount} Is Greater Than The Limit {_startServiceFailMaxCount},Begin Reset DataService!")
ResetStartServiceFailCount()
InstallDataService(WinService.ServicesExists(_dsName))
End Sub
Private Sub ResetUpgradeFailCount()
_upgradeServiceFailCount = 0
End Sub
Private Sub UpdateUpgradeFailCount()
_upgradeServiceFailCount += 1
ApplicationLog.WriteErrorLog($"Upgrade DataService Error,Count:{_upgradeServiceFailCount}")
If _upgradeServiceFailCount >= 3 Then
_upgradeServiceFailCount = 0
_usErrMsg = "DataService unable upgrade"
ApplicationLog.WriteErrorLog($"DataService unable upgrade")
End If
End Sub
'''
''' 下载服务包,完成服务安装
'''
''' 数据库中该程序包的信息
''' 服务是否已经安装
'''
Private Function DownloadServicePacket(packetInfo As UpdatePackageInfo, Optional serviceExists As Boolean = True) As Boolean
Dim localFilePath As String = $"{_dsPacketDirPath}\{packetInfo.PackageName}"
Dim ftpFilePath As String = $"{_ftpUpdateDirPath}/{packetInfo.PackageName}"
Dim newDataServiceVerDirPath As String = $"{_dsDirPath}\{packetInfo.LastVersion}"
'检测本地包是否存在
If Directory.Exists(_dsPacketDirPath) = False Then Directory.CreateDirectory(_dsPacketDirPath)
If Directory.Exists(newDataServiceVerDirPath) = False Then Directory.CreateDirectory(newDataServiceVerDirPath)
'下载FTP文件
If File.Exists(localFilePath) = False Then
ApplicationLog.WriteTraceLog($"Begin FtpDownloadFile...")
If FtpDownloadFile(ftpFilePath, localFilePath) = False Then
ApplicationLog.WriteErrorLog($"ftpPath:{ftpFilePath},localPath:{localFilePath},FtpDownloadFile Fail!")
Return False
End If
ApplicationLog.WriteTraceLog($"ftpPath:{ftpFilePath},localPath:{localFilePath},FtpDownloadFile Success!")
End If
Thread.Sleep(2000)
'更新包文件校验
ApplicationLog.WriteTraceLog($"Begin CheckDataServicePacket...")
If CheckDataServicePacket(localFilePath, packetInfo) = False Then
'文件非法,删除下载文件
ApplicationLog.WriteErrorLog($"CheckDataServicePacket Fail,Delete {localFilePath} File!")
File.Delete(localFilePath)
Return False
End If
ApplicationLog.WriteTraceLog($"CheckDataServicePacket Success!")
Thread.Sleep(2000)
'解压更新包
ApplicationLog.WriteTraceLog($"Begin Uncompress {localFilePath} File To {newDataServiceVerDirPath}...")
Try
Compress.LoadFromZip(newDataServiceVerDirPath, localFilePath)
Catch ex As Exception
ApplicationLog.WriteErrorLog($"Uncompress Fail!{ex.Message}")
Return False
End Try
ApplicationLog.WriteTraceLog($"Uncompress Success!")
Thread.Sleep(2000)
'安装服务
If serviceExists = False Then
ApplicationLog.WriteTraceLog($"Begin InstallService {newDataServiceVerDirPath}\{_dsName}...")
Try
WinService.InstallService($"{newDataServiceVerDirPath}\{_dsName}.exe", Nothing)
Catch ex As Exception
ApplicationLog.WriteErrorLog($"InstallService Fail,{ex.Message}")
Return False
End Try
End If
ApplicationLog.WriteTraceLog($"InstallService Success!")
'启动服务
ApplicationLog.WriteTraceLog($"Begin StartDataService...")
If WinService.StartService(_dsName) = False Then
ApplicationLog.WriteErrorLog($"StartDataService Fail!")
Return False
End If
ApplicationLog.WriteTraceLog($"StartDataService Success!")
_lastDsVersion = packetInfo.LastVersion
_uploaded = True
Return True
End Function
#End Region
#Region "自动更新"
''' 初始化FTP服务类
Private _ftpClient As FtpService
''' Ftp远程升级文件夹路径
Private ReadOnly _ftpUpdateDirPath As String = $"/uts_Manager/AUTS/Service"
'''
''' 判断本地与云端版本号是否一致,不一致则代表需要更新数据服务
'''
''' 本地数据服务版本号
''' 远端数据服务版本号
'''
Private Function NeedToUpdateService(localVer As String, remoteVer As String) As Boolean
If String.IsNullOrWhiteSpace(localVer) OrElse String.IsNullOrWhiteSpace(remoteVer) Then
ApplicationLog.WriteTraceLog($"Get Version Fail,LocalVer:{localVer},RemoteVer:{remoteVer}")
Return False
End If
If String.Compare(localVer, remoteVer) = 0 Then
ApplicationLog.WriteTraceLog($"No Need To Update DataService,LocalVer:{_dsVersion},RemoteVer:{remoteVer}")
Return False
End If
Return True
End Function
'''
''' Ftp下载文件
'''
''' Ftp文件路径
''' 本地文件路径
'''
Private Function FtpDownloadFile(ftpFilePath As String, localFilePath As String) As Boolean
ApplicationLog.WriteTraceLog($"soursePath:{ftpFilePath},targetPath:{localFilePath},Begin FtpDownloadFile...")
Try
_ftpClient.FtpDownload(ftpFilePath, localFilePath)
Catch ex As Exception
ApplicationLog.WriteErrorLog("Ftp DownloadFile Fail," & ex.Message)
Return False
End Try
ApplicationLog.WriteTraceLog($"Ftp DownloadFile Success!")
Return True
End Function
'''
''' 校验数据服务包
'''
''' 本地文件路径
''' 压缩包信息
'''
Private Function CheckDataServicePacket(localFilePath As String, packetInfo As UpdatePackageInfo) As Boolean
Try
Dim localMd5 As String = Md5.GetFileMd5(localFilePath)
Dim remoteMd5 As String = packetInfo.BinPackageMd5
If String.Compare(localMd5, remoteMd5, True) <> 0 Then
ApplicationLog.WriteErrorLog($"CheckDataServicePacket Fail!localMd5:{localMd5},remoteMd5:{remoteMd5}")
Return False
End If
Catch ex As Exception
ApplicationLog.WriteErrorLog($"CheckDataServicePacket Error:{ex.Message}")
Return False
End Try
Return True
End Function
#End Region
#Region "服务通讯"
Private _tcpClient As AutsTcpClient
''' License检测
Private _license As License
''' User
Private _user As String = "Admin"
'''
''' 初始化License内容
'''
Private Function CheckLicense() As Boolean
Try
Dim licensePath As String = UtsRegistry.LicenseFilePath
UTS_Core.DebugLog.ApplicationLog.WriteInfoLog($"Begin CheckLicense,Path:{licensePath}.")
_license = New License(licensePath)
_license.CheckLicense()
UTS_Core.DebugLog.ApplicationLog.WriteInfoLog($"CheckLicense Success.")
Catch ex As Exception
UTS_Core.DebugLog.ApplicationLog.WriteErrorLog($"CheckLicense Error:{ex.Message}.")
Return False
End Try
Return True
End Function
'''
''' 获取服务版本信息
'''
Public Function GetServiceLocalVersion() As Boolean
Dim taskParam As New TaskJsonParam(TaskJsonParam.CmdNamesEnum.GetServiceVersion, _user, _usName)
taskParam.ServiceVersion = _usVersion
Return ServiceCommunicator(taskParam)
End Function
'''
''' 初始化网络套接字
''' 注册表中无端口号时会引发异常
'''
Private Function InitTcpClient() As Boolean
Static remoteIp As String = "127.0.0.1"
Static remotePort As Integer = -1 '默认55533,实际使用从注册表中获取
Try
If _tcpClient Is Nothing OrElse (_tcpClient.Connected() = False) Then
remotePort = CInt(UtsRegistry.DataServicePort)
_tcpClient = New AutsTcpClient(remoteIp, remotePort)
_tcpClient.Open()
ApplicationLog.WriteTraceLog($"InitTcpClient Success!")
End If
Catch ex As Exception
ResetTcpConnection()
If _serviceHeartBeatFailCount <= 1 Then '避免离线后大量重复数据
ApplicationLog.WriteWarningLog($"InitTcpClient Error:{ex.Message}")
End If
Return False
End Try
Return True
End Function
'''
''' 重启服务后,重置连接服务TCP标志位
'''
Private Sub ResetTcpConnection()
If _tcpClient IsNot Nothing Then
_tcpClient.Dispose()
_tcpClient = Nothing
End If
End Sub
'''
''' 与服务通讯,包含发送,接收,处理过程
'''
'''
Private Function ServiceCommunicator(taskParam As TaskJsonParam) As Boolean
Try
Dim jsonString As String = TaskJsonParam.SerializeToJson(taskParam) & vbCrLf
_tcpClient.WriteJsonString(jsonString) '发送数据
Catch ex As Exception
ApplicationLog.WriteErrorLog($"Send jsonString Error:{ex.Message}")
Return False
End Try
Dim replyString As String = _tcpClient.ReadJsonString()
Try
Dim replyParam As TaskJsonParam = CheckReplyString(replyString, taskParam.CmdName) '接收数据校验
If replyParam.CmdStatus = TaskJsonParam.CmdStatusEnum.Pass.ToString() Then
DealFunction(replyParam)
End If
Catch ex As Exception
ApplicationLog.WriteWarningLog($"CmdName:{taskParam.CmdName} Deal ReplyString Error:{ex.Message}")
Return False
End Try
Return True
End Function
'''
''' 校验回复字符串
'''
'''
'''
'''
Private Function CheckReplyString(replyString As String, cmdName As TaskJsonParam.CmdNamesEnum) As TaskJsonParam
If String.IsNullOrWhiteSpace(replyString) Then Return Nothing
Dim replyParam As TaskJsonParam
replyParam = TaskJsonParam.DeserializeFormJson(replyString)
If replyParam.CmdName <> cmdName Then Return Nothing
Return replyParam
End Function
'''
''' 处理返回值
'''
'''
Private Sub DealFunction(replyParam As TaskJsonParam)
Select Case replyParam.CmdName
Case TaskJsonParam.CmdNamesEnum.GetServiceVersion
DealServiceVersion(replyParam)
End Select
End Sub
'''
''' 获取服务版本信息
'''
'''
Private Sub DealServiceVersion(replyParam As TaskJsonParam)
_dsVersion = replyParam.ServiceVersion
_dsOnline = 1
'更新失败计数
If _uploaded = False Then Return
If String.Compare(_dsVersion, _lastDsVersion) = 0 Then
_uploaded = False
ResetUpgradeFailCount()
ApplicationLog.WriteTraceLog($"Upgrade DataService To {_lastDsVersion} Success!")
Else
_uploaded = False
UpdateUpgradeFailCount()
End If
End Sub
#End Region
End Class