Add Heartbeat Function to Service, Add Timer to ClientSuite Service Startup check

This commit is contained in:
Jonathan Jenne
2019-02-18 17:06:41 +01:00
parent bc7605bdcf
commit 4229682da0
24 changed files with 259 additions and 85 deletions

View File

@@ -36,8 +36,14 @@ Public Class ClassConfig
End Property
' === Simple/Actual Config Properties ===
' === Service Configuration ===
Public Property ServiceIP As String = String.Empty
Public Property ServicePort As Integer = -1
Public Property HeartbeatInterval As Integer = 5000
' === Logging Configuration
Public Property LogDebug As Boolean = False
' === User Configuration ===
Public Property UserLanguage As String = "de-DE"
End Class

View File

@@ -1,7 +1,21 @@
Imports System.IO
''' <summary>
''' Helper Class to save DevExpress layouts
'''
''' Example:
'''
''' Layout 1 (for frmMain)
''' ----------------------------------------------
''' | Component 1 Component 2 |
''' | |-------------| |----------------| |
''' | | MainGrid | | DocumentGrid | |
''' | |-------------| |----------------| |
''' | |
''' |---------------------------------------------
''' </summary>
Public Class ClassLayout
Public Enum LayoutName
Public Enum GroupName
LayoutMain
End Enum
@@ -10,9 +24,19 @@ Public Class ClassLayout
DocumentManager
End Enum
Public Shared Function GetLayoutPath(LayoutName As LayoutName, Component As LayoutComponent) As String
Dim oFileName As String = $"{LayoutName.ToString}-{Component.ToString}.xml"
Return IO.Path.Combine(GetAppDataFolder(), ClassConstants.FOLDER_NAME_LAYOUT, oFileName)
''' <summary>
''' Returns a path for the chosen Devexpress layout file
''' </summary>
''' <param name="Group">The Group to which the the component belongs to. For example, a form could be a Layout</param>
''' <param name="Component">The component to which the layout belongs to</param>
''' <returns></returns>
Public Shared Function GetLayoutPath(Group As GroupName, Component As LayoutComponent) As String
Dim oFileName As String = $"{Group.ToString}-{Component.ToString}.xml"
Return Path.Combine(GetLayoutDirectory(), oFileName)
End Function
Public Shared Function GetLayoutDirectory() As String
Return Path.Combine(GetAppDataFolder(), ClassConstants.FOLDER_NAME_LAYOUT)
End Function
Private Shared Function GetAppDataFolder() As String

View File

@@ -18,6 +18,32 @@ Public Class ClassService
_Logger = _LogConfig.GetLogger()
End Sub
Public Function TestConnection() As ConnectionTestResult
Return TestConnection(My.Config.ServiceConnection)
End Function
Public Function TestConnection(EndpointURL As String) As ConnectionTestResult
Try
Dim oChannelFactory = GetChannelFactory(EndpointURL)
Dim oChannel = oChannelFactory.CreateChannel()
oChannel.OperationTimeout = New TimeSpan(0, 0, ClassConstants.SERVICE_OPEN_TIMEOUT)
oChannel.Open()
oChannel.Close()
Return ConnectionTestResult.Successful
Catch ex As EndpointNotFoundException
_Logger.Error(ex)
Return ConnectionTestResult.NotFound
Catch ex As Exception
_Logger.Error(ex)
Return ConnectionTestResult.Unknown
End Try
End Function
Public Async Function TestConnectionAsync() As Task(Of ConnectionTestResult)
Return Await TestConnectionAsync(My.Config.ServiceConnection)
End Function
Public Async Function TestConnectionAsync(EndpointURL As String) As Task(Of ConnectionTestResult)
Return Await Task.Factory.StartNew(Function()
Try

View File

@@ -1,4 +1,5 @@
Imports System.Threading
Imports System.ServiceModel
Imports System.Threading
Imports System.Timers
Imports DigitalData.Modules.EDMIFileOps.EDMIServiceReference
Imports DigitalData.Modules.Logging
@@ -10,28 +11,56 @@ Public Class ClassTimer
Private _Timer As Timers.Timer
Private _Channel As IEDMServiceChannel
Private Const TIMER_START_TIME = 1000
Private Const TIMER_INTERVAL = 5000
Public Event OnlineChanged As OnlineChangedEventHandler
Public Delegate Sub OnlineChangedEventHandler(sender As Object, Online As Boolean)
Public Sub Callback(sender As Object, e As ElapsedEventArgs)
_Logger.Info("Checking if Service is up...")
Public Sub New(LogConfig As LogConfig, SynchronizingObject As frmMain, HeartbeatInterval As Integer)
Try
_LogConfig = LogConfig
_Logger = LogConfig.GetLogger()
' Connect to service and send hearbeat request
_Channel = My.ChannelFactory.CreateChannel()
_Channel.Open()
My.Application.Service.Online = True
My.Application.Service.LastChecked = DateTime.Now
_Timer = New Timers.Timer(HeartbeatInterval) With {
.SynchronizingObject = SynchronizingObject,
.Enabled = True
}
AddHandler _Timer.Elapsed, AddressOf Callback
Catch ex As Exception
_Logger.Error(ex)
End Try
End Sub
Public Sub New(LogConfig As LogConfig)
_LogConfig = LogConfig
_Logger = LogConfig.GetLogger()
Public Async Sub Callback(sender As Object, e As ElapsedEventArgs)
Try
_Logger.Debug("Checking if Service is up...")
_Channel = My.ChannelFactory.CreateChannel()
_Channel.Open()
If _Channel.State = ServiceModel.CommunicationState.Faulted Then
_Channel = My.ChannelFactory.CreateChannel()
End If
_Timer = New Timers.Timer(TIMER_INTERVAL)
_Timer.SynchronizingObject = My.MainForm
AddHandler _Timer.Elapsed, AddressOf Callback
_Timer.Enabled = True
' Connect to service and send hearbeat request
Dim oResult = Await _Channel.HeartbeatAsync()
_Logger.Debug("Service is online")
SetOnlineState(True)
Catch ex As Exception
_Logger.Debug("Service is offline!")
SetOnlineState(False)
Finally
My.Application.Service.LastChecked = DateTime.Now
End Try
End Sub
Private Sub SetOnlineState(NewState As Boolean)
If My.Application.Service.Online <> NewState Then
My.Application.Service.Online = NewState
RaiseEvent OnlineChanged(Me, NewState)
End If
End Sub
End Class

View File

@@ -207,8 +207,8 @@
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
<Compile Include="MyApplication.vb" />
<Compile Include="State\ServiceState.vb" />
<Compile Include="State\UserState.vb" />
<Compile Include="State\ClassServiceState.vb" />
<Compile Include="State\ClassUserState.vb" />
<Compile Include="Strings\ControlProperties.Designer.vb">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>

View File

@@ -29,8 +29,8 @@ Namespace My
''' </summary>
Partial Class MyApplication
' User Config
Public User As New UserState()
Public Service As New ServiceState()
Public User As New ClassUserState()
Public Service As New ClassServiceState()
End Class
End Namespace

View File

@@ -0,0 +1,4 @@
Public Class ClassServiceState
Public Property Online As Boolean = True
Public Property LastChecked As DateTime = DateTime.Now
End Class

View File

@@ -3,7 +3,7 @@
''' <summary>
''' Helper Class to hold User State
''' </summary>
Public Class UserState
Public Class ClassUserState
Public UserName As String
Public MachineName As String
Public Language As String

View File

@@ -1,9 +0,0 @@
Public Class ServiceState
Public Online As Boolean
Public LastChecked As DateTime
Public Sub New()
Online = True
LastChecked = DateTime.Now
End Sub
End Class

View File

@@ -22,7 +22,7 @@
My.Config.ServicePort = Integer.Parse(oPort)
lblStatus.Text = "Verbindung wird hergestellt..."
oResult = Await _Service.TestConnectionAsync(My.ConfigManager.Config.ServiceConnection)
oResult = Await _Service.TestConnectionAsync()
If oResult = ClassService.ConnectionTestResult.Successful Then
My.ConfigManager.Save()

View File

@@ -19,10 +19,11 @@ Partial Class frmMain
'Do not modify it using the code editor.
<System.Diagnostics.DebuggerStepThrough()>
Private Sub InitializeComponent()
Me.components = New System.ComponentModel.Container()
Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(frmMain))
Dim PushTransition1 As DevExpress.Utils.Animation.PushTransition = New DevExpress.Utils.Animation.PushTransition()
Me.RibbonControl = New DevExpress.XtraBars.Ribbon.RibbonControl()
Me.MainMenu = New DevExpress.XtraBars.Ribbon.ApplicationMenu()
Me.MainMenu = New DevExpress.XtraBars.Ribbon.ApplicationMenu(Me.components)
Me.BarButtonExit = New DevExpress.XtraBars.BarButtonItem()
Me.BarButtonUserSettings = New DevExpress.XtraBars.BarButtonItem()
Me.BarButtonConnectionSettings = New DevExpress.XtraBars.BarButtonItem()
@@ -39,6 +40,7 @@ Partial Class frmMain
Me.BarButtonItem2 = New DevExpress.XtraBars.BarButtonItem()
Me.BarWorkspaceMenuItem1 = New DevExpress.XtraBars.BarWorkspaceMenuItem()
Me.WorkspaceManager1 = New DevExpress.Utils.WorkspaceManager()
Me.LabelServiceOnline = New DevExpress.XtraBars.BarStaticItem()
Me.RibbonPageCategoryEntityDesigner = New DevExpress.XtraBars.Ribbon.RibbonPageCategory()
Me.RibbonPageControlActions = New DevExpress.XtraBars.Ribbon.RibbonPage()
Me.RibbonPageGroup5 = New DevExpress.XtraBars.Ribbon.RibbonPageGroup()
@@ -50,16 +52,16 @@ Partial Class frmMain
Me.RibbonPageWorkflow = New DevExpress.XtraBars.Ribbon.RibbonPage()
Me.RibbonPageGroup4 = New DevExpress.XtraBars.Ribbon.RibbonPageGroup()
Me.RibbonStatusBar = New DevExpress.XtraBars.Ribbon.RibbonStatusBar()
Me.DocumentManager = New DevExpress.XtraBars.Docking2010.DocumentManager()
Me.TabbedView1 = New DevExpress.XtraBars.Docking2010.Views.Tabbed.TabbedView()
Me.DockManager = New DevExpress.XtraBars.Docking.DockManager()
Me.DocumentManager = New DevExpress.XtraBars.Docking2010.DocumentManager(Me.components)
Me.TabbedView1 = New DevExpress.XtraBars.Docking2010.Views.Tabbed.TabbedView(Me.components)
Me.DockManager = New DevExpress.XtraBars.Docking.DockManager(Me.components)
Me.panelContainer1 = New DevExpress.XtraBars.Docking.DockPanel()
Me.DockPanelGlobix = New DevExpress.XtraBars.Docking.DockPanel()
Me.DockPanel1_Container = New DevExpress.XtraBars.Docking.ControlContainer()
Me.Label1 = New System.Windows.Forms.Label()
Me.DockPanelProcessManager = New DevExpress.XtraBars.Docking.DockPanel()
Me.DockPanel2_Container = New DevExpress.XtraBars.Docking.ControlContainer()
Me.ProcessManagerWidget = New ClientSuite.ProcessManagerWidget()
Me.ProcessManagerWidget = New DigitalData.GUIs.ClientSuite.ProcessManagerWidget()
CType(Me.RibbonControl, System.ComponentModel.ISupportInitialize).BeginInit()
CType(Me.MainMenu, System.ComponentModel.ISupportInitialize).BeginInit()
CType(Me.DocumentManager, System.ComponentModel.ISupportInitialize).BeginInit()
@@ -76,9 +78,9 @@ Partial Class frmMain
'
Me.RibbonControl.ApplicationButtonDropDownControl = Me.MainMenu
Me.RibbonControl.ExpandCollapseItem.Id = 0
Me.RibbonControl.Items.AddRange(New DevExpress.XtraBars.BarItem() {Me.RibbonControl.ExpandCollapseItem, Me.BarButtonExit, Me.BarButtonUserSettings, Me.LabelCurrentUser, Me.LabelCurrentMachine, Me.LabelCurrentVersion, Me.BarButtonItem1, Me.BarButtonDock1, Me.SkinDropDownButtonItem1, Me.BarButtonDashboard, Me.BarButtonEntityDesigner, Me.BarButtonDeleteControl, Me.BarButtonConnectionSettings, Me.LabelCurrentLanguage, Me.BarButtonItem2, Me.BarWorkspaceMenuItem1})
Me.RibbonControl.Items.AddRange(New DevExpress.XtraBars.BarItem() {Me.RibbonControl.ExpandCollapseItem, Me.BarButtonExit, Me.BarButtonUserSettings, Me.LabelCurrentUser, Me.LabelCurrentMachine, Me.LabelCurrentVersion, Me.BarButtonItem1, Me.BarButtonDock1, Me.SkinDropDownButtonItem1, Me.BarButtonDashboard, Me.BarButtonEntityDesigner, Me.BarButtonDeleteControl, Me.BarButtonConnectionSettings, Me.LabelCurrentLanguage, Me.BarButtonItem2, Me.BarWorkspaceMenuItem1, Me.LabelServiceOnline})
Me.RibbonControl.Location = New System.Drawing.Point(0, 0)
Me.RibbonControl.MaxItemId = 18
Me.RibbonControl.MaxItemId = 19
Me.RibbonControl.Name = "RibbonControl"
Me.RibbonControl.PageCategories.AddRange(New DevExpress.XtraBars.Ribbon.RibbonPageCategory() {Me.RibbonPageCategoryEntityDesigner})
Me.RibbonControl.PageHeaderItemLinks.Add(Me.SkinDropDownButtonItem1)
@@ -200,6 +202,12 @@ Partial Class frmMain
Me.WorkspaceManager1.TargetControl = Me
Me.WorkspaceManager1.TransitionType = PushTransition1
'
'LabelServiceOnline
'
Me.LabelServiceOnline.Caption = "BarStaticItem1"
Me.LabelServiceOnline.Id = 18
Me.LabelServiceOnline.Name = "LabelServiceOnline"
'
'RibbonPageCategoryEntityDesigner
'
Me.RibbonPageCategoryEntityDesigner.AutoStretchPageHeaders = True
@@ -271,6 +279,7 @@ Partial Class frmMain
Me.RibbonStatusBar.ItemLinks.Add(Me.LabelCurrentMachine)
Me.RibbonStatusBar.ItemLinks.Add(Me.LabelCurrentVersion)
Me.RibbonStatusBar.ItemLinks.Add(Me.LabelCurrentLanguage)
Me.RibbonStatusBar.ItemLinks.Add(Me.LabelServiceOnline)
Me.RibbonStatusBar.Location = New System.Drawing.Point(0, 556)
Me.RibbonStatusBar.Name = "RibbonStatusBar"
Me.RibbonStatusBar.Ribbon = Me.RibbonControl
@@ -436,4 +445,5 @@ Partial Class frmMain
Friend WithEvents BarButtonItem2 As DevExpress.XtraBars.BarButtonItem
Friend WithEvents BarWorkspaceMenuItem1 As DevExpress.XtraBars.BarWorkspaceMenuItem
Friend WithEvents WorkspaceManager1 As DevExpress.Utils.WorkspaceManager
Friend WithEvents LabelServiceOnline As DevExpress.XtraBars.BarStaticItem
End Class

View File

@@ -13,17 +13,33 @@ Public Class frmMain
' Dieser Aufruf ist für den Designer erforderlich.
InitializeComponent()
' Assign My.MainForm before everything else
My.MainForm = Me
' Initialize Main Timer
_Timer = New ClassTimer(My.LogConfig)
' Show splashscreen
frmSplash.ShowDialog()
' Initialize Form Related Variables
My.MainForm = Me
End Sub
Private Sub SetOnlineLabel()
If My.Application.Service.Online Then
LabelServiceOnline.Caption = "Service Online"
LabelServiceOnline.ItemAppearance.Normal.ForeColor = Color.Green
Else
LabelServiceOnline.Caption = "Service Offline"
LabelServiceOnline.ItemAppearance.Normal.ForeColor = Color.Red
End If
End Sub
Private Sub HandleOnlineChanged(sender As Object, Online As Boolean)
SetOnlineLabel()
End Sub
Private Sub FrmMain_Load(sender As Object, e As EventArgs) Handles Me.Load
' Initialize Main Timer
_Timer = New ClassTimer(My.LogConfig, Me, My.Config.HeartbeatInterval)
AddHandler _Timer.OnlineChanged, AddressOf HandleOnlineChanged
SetOnlineLabel()
LabelCurrentUser.Caption = My.Application.User.UserName
LabelCurrentMachine.Caption = My.Application.User.MachineName
LabelCurrentVersion.Caption = My.Application.Info.Version.ToString
@@ -48,7 +64,6 @@ Public Class frmMain
MsgBox($"Clicked on Document {RowView.Row.Item("DocName")}")
End Sub
LoadLayout()
End Sub
@@ -57,21 +72,21 @@ Public Class frmMain
End Sub
Private Sub LoadLayout()
Dim oLayoutPathForDockManager As String = GetLayoutPath(LayoutName.LayoutMain, LayoutComponent.DockManager)
Dim oLayoutPathForDocumentManager As String = GetLayoutPath(LayoutName.LayoutMain, LayoutComponent.DocumentManager)
Dim oLayoutPathForDockManager As String = GetLayoutPath(GroupName.LayoutMain, LayoutComponent.DockManager)
Dim oLayoutPathForDocumentManager As String = GetLayoutPath(GroupName.LayoutMain, LayoutComponent.DocumentManager)
If IO.File.Exists(oLayoutPathForDockManager) Then
If File.Exists(oLayoutPathForDockManager) Then
DockManager.RestoreLayoutFromXml(oLayoutPathForDockManager)
End If
If IO.File.Exists(oLayoutPathForDocumentManager) Then
If File.Exists(oLayoutPathForDocumentManager) Then
DocumentManager.View.RestoreLayoutFromXml(oLayoutPathForDocumentManager)
End If
End Sub
Private Sub SaveLayout()
Dim oLayoutPathForDockManager As String = GetLayoutPath(LayoutName.LayoutMain, LayoutComponent.DockManager)
Dim oLayoutPathForDocumentManager As String = GetLayoutPath(LayoutName.LayoutMain, LayoutComponent.DocumentManager)
Dim oDirectory As String = Path.GetDirectoryName(oLayoutPathForDockManager)
Dim oLayoutPathForDockManager As String = GetLayoutPath(GroupName.LayoutMain, LayoutComponent.DockManager)
Dim oLayoutPathForDocumentManager As String = GetLayoutPath(GroupName.LayoutMain, LayoutComponent.DocumentManager)
Dim oDirectory As String = GetLayoutDirectory()
If Not Directory.Exists(oDirectory) Then
Directory.CreateDirectory(oDirectory)

View File

@@ -49,6 +49,12 @@ Public NotInheritable Class frmSplash
End Function
#Region "Worker"
Private Enum WorkerResult
AllGood
ServiceOffline
End Enum
Private Sub StartWorker()
AddHandler _Worker.DoWork, AddressOf bw_DoWork
AddHandler _Worker.ProgressChanged, AddressOf bw_ProgressChanged
@@ -58,17 +64,25 @@ Public NotInheritable Class frmSplash
_Worker.RunWorkerAsync()
End Sub
Private Sub bw_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs)
Private Async Sub bw_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs)
Dim oService As New ClassService(My.LogConfig)
_Worker.ReportProgress(SetProgress(1), "Connecting to service..")
Dim oChannelFactory As ChannelFactory(Of IEDMServiceChannel)
Dim oChannel As IEDMServiceChannel
Dim oConnectionSuccessful = False
oChannelFactory = oService.GetChannelFactory()
oChannel = oChannelFactory.CreateChannel()
e.Result = WorkerResult.AllGood
_ChannelFactory = oService.GetChannelFactory()
_Channel = _ChannelFactory.CreateChannel()
'Dim oServiceOnline = Await oService.TestConnectionAsync()
Dim oServiceState = oService.TestConnection()
If oServiceState <> ClassService.ConnectionTestResult.Successful Then
e.Result = WorkerResult.ServiceOffline
Return
End If
Thread.Sleep(SLEEP_TIME)
@@ -92,11 +106,25 @@ Public NotInheritable Class frmSplash
Private Sub bw_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs)
' Bei Fehler MsgBox anzeigen und Programm beenden
If e.Error IsNot Nothing Then
'ClassLogger.Add("Unexpected error in Initializing application....")
MsgBox(e.Error.Message, MsgBoxStyle.Critical, "Critical Error")
MsgBox(e.Error.Message, MsgBoxStyle.Critical, "Unhandled Error")
Application.Exit()
ElseIf e.Result <> WorkerResult.AllGood Then
Dim oErrorMessage
Select Case e.Result
Case WorkerResult.ServiceOffline
oErrorMessage = "Service is offline!"
Case Else
oErrorMessage = "Unknown Error"
End Select
MsgBox($"Application could not be started{vbNewLine}Reason: {oErrorMessage}", MsgBoxStyle.Critical, "Critical Error")
Application.Exit()
End If
My.ChannelFactory = _ChannelFactory
My.Channel = _Channel
' Wenn kein Fehler, Splashscreen schließen
Me.Close()
End Sub