mirror of
https://gitlab.com/fabinfra/fabaccess/borepin.git
synced 2025-03-12 14:51:44 +01:00
Merge branch 'fix/reconnect' into 'main'
Fix/reconnect See merge request fabinfra/fabaccess/borepin!74
This commit is contained in:
commit
28687274c0
@ -53,5 +53,7 @@
|
||||
|
||||
<Capabilities>
|
||||
<Capability Name="internetClient" />
|
||||
<Capability Name="privateNetworkClientServer"/>
|
||||
<Capability Name="allJoyn"/>
|
||||
</Capabilities>
|
||||
</Package>
|
@ -12,6 +12,7 @@ using Borepin.PageModel.AddServerProcess;
|
||||
using System;
|
||||
using Borepin.Service.Storage;
|
||||
using NLog;
|
||||
using Borepin.Service.ErrorMessage;
|
||||
|
||||
namespace Borepin
|
||||
{
|
||||
@ -69,6 +70,7 @@ namespace Borepin
|
||||
|
||||
#region Register Service
|
||||
containerRegistry.RegisterSingleton<ILoginStorageService, LoginStorageService>();
|
||||
containerRegistry.RegisterSingleton<IErrorMessageService, ErrorMessageService>();
|
||||
|
||||
// NEED PLATFORM SPECIFIC SERVICE
|
||||
// IPreferenceStorageService
|
||||
|
@ -1,5 +1,6 @@
|
||||
using Borepin.Service;
|
||||
using FabAccessAPI;
|
||||
using NLog;
|
||||
using Prism.Navigation;
|
||||
using Prism.Services;
|
||||
using System;
|
||||
@ -30,49 +31,29 @@ namespace Borepin.Base
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
public async void OnConnectionStatusChanged(object sender, ConnectionStatusChange args)
|
||||
public async void OnConnectionStatusChanged(object sender, ConnectionStatusChanged args)
|
||||
{
|
||||
switch(args)
|
||||
{
|
||||
case ConnectionStatusChange.Connected:
|
||||
case ConnectionStatusChanged.Connected:
|
||||
IsConnected = true;
|
||||
IsConnecting = false;
|
||||
try
|
||||
{
|
||||
await LoadAPIData().ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
catch(Exception exception)
|
||||
{
|
||||
IsConnected = false;
|
||||
await _API.Disconnect().ConfigureAwait(false);
|
||||
_API.UnbindAllEvents();
|
||||
Log.Warn("Load API Data failed", exception);
|
||||
}
|
||||
break;
|
||||
case ConnectionStatusChange.Reconnected:
|
||||
try
|
||||
{
|
||||
await ReloadAPIData().ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
IsConnected = false;
|
||||
await _API.Disconnect().ConfigureAwait(false);
|
||||
_API.UnbindAllEvents();
|
||||
}
|
||||
break;
|
||||
case ConnectionStatusChange.ConnectionLoss:
|
||||
try
|
||||
{
|
||||
await _API.Reconnect().ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
IsConnected = false;
|
||||
await _API.Disconnect().ConfigureAwait(false);
|
||||
_API.UnbindAllEvents();
|
||||
}
|
||||
break;
|
||||
case ConnectionStatusChange.Disconnected:
|
||||
case ConnectionStatusChanged.ConnectionLoss:
|
||||
IsConnected = false;
|
||||
IsConnecting = true;
|
||||
break;
|
||||
case ConnectionStatusChanged.Disconnected:
|
||||
IsConnected = false;
|
||||
IsConnecting = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -81,22 +62,28 @@ namespace Borepin.Base
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
public virtual Task ReloadAPIData()
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
/// <summary>
|
||||
/// PageModel is Connected
|
||||
/// </summary>
|
||||
private bool _IsConnected = true;
|
||||
private bool _IsConnected = false;
|
||||
public bool IsConnected
|
||||
{
|
||||
get => _IsConnected;
|
||||
set => SetProperty(ref _IsConnected, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// PageModel is Connecting
|
||||
/// </summary>
|
||||
private bool _IsConnecting = false;
|
||||
public bool IsConnecting
|
||||
{
|
||||
get => _IsConnecting;
|
||||
set => SetProperty(ref _IsConnecting, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region INavigationAware
|
||||
@ -110,11 +97,9 @@ namespace Borepin.Base
|
||||
{
|
||||
await LoadAPIData().ConfigureAwait(false);
|
||||
}
|
||||
catch(Exception ex)
|
||||
catch(Exception exception)
|
||||
{
|
||||
IsConnected = false;
|
||||
await _API.Disconnect().ConfigureAwait(false);
|
||||
_API.UnbindAllEvents();
|
||||
Log.Warn("Load API Data failed", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
11
Borepin/Borepin/Base/ErrorMessages.cs
Normal file
11
Borepin/Borepin/Base/ErrorMessages.cs
Normal file
@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Borepin.Base
|
||||
{
|
||||
public static class ErrorMessages
|
||||
{
|
||||
|
||||
}
|
||||
}
|
35
Borepin/Borepin/Converter/AllTrueBoolConverter.cs
Normal file
35
Borepin/Borepin/Converter/AllTrueBoolConverter.cs
Normal file
@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Borepin.Converter
|
||||
{
|
||||
public class AllTrueBoolConverter : IMultiValueConverter
|
||||
{
|
||||
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (values == null || !targetType.IsAssignableFrom(typeof(bool)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var value in values)
|
||||
{
|
||||
if (!(value is bool b))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (!b)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
@ -3,29 +3,30 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
x:Class="Borepin.Page.AddUserPage"
|
||||
xmlns:converters="clr-namespace:Borepin.Converter"
|
||||
xmlns:resource_text="clr-namespace:Borepin.Resources.Text"
|
||||
xmlns:resource_text="clr-namespace:Borepin.Resources.Text"
|
||||
xmlns:views="clr-namespace:Borepin.View"
|
||||
Title="{x:Static resource_text:TextResource.TITLE_AddUser}">
|
||||
<ContentPage.Resources>
|
||||
<ResourceDictionary>
|
||||
<converters:AllTrueBoolConverter x:Key="AllTrueBoolConverter"/>
|
||||
<converters:InvertBoolConverter x:Key="InvertBoolConverter"/>
|
||||
</ResourceDictionary>
|
||||
</ContentPage.Resources>
|
||||
<ContentPage.Content>
|
||||
<StackLayout Padding="20">
|
||||
<StackLayout IsVisible="{Binding IsBusy}">
|
||||
<ActivityIndicator IsRunning="{Binding IsBusy}"></ActivityIndicator>
|
||||
</StackLayout>
|
||||
<StackLayout IsVisible="{Binding IsBusy, Converter={StaticResource InvertBoolConverter}}">
|
||||
<StackLayout IsVisible="{Binding IsConnected}">
|
||||
<Label Text="{x:Static resource_text:TextResource.USERNAME}" Style="{StaticResource Style_Label_Property_Title}"></Label>
|
||||
<views:ConnectionStateView/>
|
||||
<StackLayout>
|
||||
<StackLayout.IsVisible>
|
||||
<MultiBinding Converter="{StaticResource AllTrueBoolConverter}">
|
||||
<Binding Path="IsBusy" Converter="{StaticResource InvertBoolConverter}"/>
|
||||
<Binding Path="IsConnected" />
|
||||
</MultiBinding>
|
||||
</StackLayout.IsVisible>
|
||||
<Label Text="{x:Static resource_text:TextResource.USERNAME}" Style="{StaticResource Style_Label_Property_Title}"></Label>
|
||||
<Entry Text="{Binding Username}" Keyboard="Url" IsSpellCheckEnabled="false"/>
|
||||
<Label Text="{x:Static resource_text:TextResource.PASSWORD}" Style="{StaticResource Style_Label_Property_Title}"></Label>
|
||||
<Entry Text="{Binding Password}" Keyboard="Url" IsSpellCheckEnabled="false"/>
|
||||
<Button Text="{x:Static resource_text:TextResource.AddUserPage_AddUser}" Command="{Binding AddUserCommand}" Style="{StaticResource Style_Button_Primary}"/>
|
||||
</StackLayout>
|
||||
<StackLayout IsVisible="{Binding IsConnected, Converter={StaticResource InvertBoolConverter}}">
|
||||
<Label Text="{x:Static resource_text:TextResource.PLEASECONNECTTOSERVER}"/>
|
||||
</StackLayout>
|
||||
<Button Text="{x:Static resource_text:TextResource.AddUserPage_AddUser}" Command="{Binding AddUserCommand}" Style="{StaticResource Style_Button_Primary}"/>
|
||||
</StackLayout>
|
||||
</StackLayout>
|
||||
</ContentPage.Content>
|
||||
|
@ -1,56 +1,58 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
xmlns:views="clr-namespace:Borepin.View"
|
||||
x:Class="Borepin.Page.MachineListPage"
|
||||
xmlns:converters="clr-namespace:Borepin.Converter"
|
||||
xmlns:resource_text="clr-namespace:Borepin.Resources.Text"
|
||||
Title="{x:Static resource_text:TextResource.TITLE_Machines}">
|
||||
<ContentPage
|
||||
xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
xmlns:views="clr-namespace:Borepin.View"
|
||||
x:Class="Borepin.Page.MachineListPage"
|
||||
xmlns:converters="clr-namespace:Borepin.Converter"
|
||||
xmlns:resource_text="clr-namespace:Borepin.Resources.Text"
|
||||
Title="{x:Static resource_text:TextResource.TITLE_Machines}"
|
||||
>
|
||||
<NavigationPage.TitleView>
|
||||
<Button Text="Refresh" HorizontalOptions="End" BackgroundColor="{StaticResource SecondColor}" TextColor="{StaticResource FirstColor}" Command="{Binding RefreshCommand}"/>
|
||||
</NavigationPage.TitleView>
|
||||
<ContentPage.Resources>
|
||||
<ResourceDictionary>
|
||||
<converters:AllTrueBoolConverter x:Key="AllTrueBoolConverter"/>
|
||||
<converters:InvertBoolConverter x:Key="InvertBoolConverter"/>
|
||||
<converters:ListNotEmptyConverter x:Key="ListNotEmptyConverter"/>
|
||||
</ResourceDictionary>
|
||||
</ContentPage.Resources>
|
||||
<ContentPage.Content>
|
||||
<StackLayout Padding="20">
|
||||
<StackLayout IsVisible="{Binding IsBusy}">
|
||||
<ActivityIndicator IsRunning="{Binding IsBusy}"></ActivityIndicator>
|
||||
</StackLayout>
|
||||
<StackLayout IsVisible="{Binding IsBusy, Converter={StaticResource InvertBoolConverter}}">
|
||||
<StackLayout IsVisible="{Binding IsConnected}">
|
||||
<Button Text="{x:Static resource_text:TextResource.SCANQR}" Command="{Binding ScanCodeCommand}" Style="{StaticResource Style_Button_Primary}">
|
||||
<views:ConnectionStateView/>
|
||||
<StackLayout>
|
||||
<StackLayout.IsVisible>
|
||||
<MultiBinding Converter="{StaticResource AllTrueBoolConverter}">
|
||||
<Binding Path="IsBusy" Converter="{StaticResource InvertBoolConverter}"/>
|
||||
<Binding Path="IsConnected"/>
|
||||
</MultiBinding>
|
||||
</StackLayout.IsVisible>
|
||||
<Button Text="{x:Static resource_text:TextResource.SCANQR}" Command="{Binding ScanCodeCommand}" Style="{StaticResource Style_Button_Primary}">
|
||||
<Button.IsVisible>
|
||||
<OnPlatform x:TypeArguments="x:Boolean"
|
||||
iOS="True"
|
||||
Android="True"/>
|
||||
</Button.IsVisible>
|
||||
</Button>
|
||||
<ListView ItemsSource="{Binding MachineListItemViewModel_List}" SelectionMode="None" SeparatorColor="Transparent" IsGroupingEnabled="True" GroupDisplayBinding="{Binding Category}">
|
||||
<ListView.GroupHeaderTemplate>
|
||||
<DataTemplate>
|
||||
<ViewCell>
|
||||
<StackLayout Margin="0, 10, 0, 0">
|
||||
<Label Text="{Binding Category}"/>
|
||||
</StackLayout>
|
||||
</ViewCell>
|
||||
</DataTemplate>
|
||||
</ListView.GroupHeaderTemplate>
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<ViewCell>
|
||||
<views:MachineListItemView />
|
||||
</ViewCell>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</StackLayout>
|
||||
<StackLayout IsVisible="{Binding IsConnected, Converter={StaticResource InvertBoolConverter}}">
|
||||
<Label Text="{x:Static resource_text:TextResource.PLEASECONNECTTOSERVER}" ></Label>
|
||||
</StackLayout>
|
||||
<ListView ItemsSource="{Binding MachineListItemViewModel_List}" SelectionMode="None" SeparatorColor="Transparent" IsGroupingEnabled="True" GroupDisplayBinding="{Binding Category}">
|
||||
<ListView.GroupHeaderTemplate>
|
||||
<DataTemplate>
|
||||
<ViewCell>
|
||||
<StackLayout Margin="0, 10, 0, 0">
|
||||
<Label Text="{Binding Category}"/>
|
||||
</StackLayout>
|
||||
</ViewCell>
|
||||
</DataTemplate>
|
||||
</ListView.GroupHeaderTemplate>
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<ViewCell>
|
||||
<views:MachineListItemView />
|
||||
</ViewCell>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</StackLayout>
|
||||
</StackLayout>
|
||||
</ContentPage.Content>
|
||||
|
@ -3,51 +3,61 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
x:Class="Borepin.Page.MachinePage"
|
||||
xmlns:converters="clr-namespace:Borepin.Converter"
|
||||
xmlns:resource_text="clr-namespace:Borepin.Resources.Text"
|
||||
xmlns:resource_text="clr-namespace:Borepin.Resources.Text"
|
||||
xmlns:views="clr-namespace:Borepin.View"
|
||||
Title="{x:Static resource_text:TextResource.TITLE_Machine}">
|
||||
<NavigationPage.TitleView>
|
||||
<Label Text="{Binding MachineItem.State, Converter={StaticResource MachineStateStringConverter}}" FontAttributes="Bold" HorizontalOptions="End" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" WidthRequest="150" Margin="7.5" VerticalOptions="FillAndExpand" FontSize="Small" BackgroundColor="{Binding MachineItem.State, Converter={StaticResource MachineStateColorConverter}}"/>
|
||||
</NavigationPage.TitleView>
|
||||
<ContentPage.Resources>
|
||||
<ResourceDictionary>
|
||||
<converters:AllTrueBoolConverter x:Key="AllTrueBoolConverter"/>
|
||||
<converters:InvertBoolConverter x:Key="InvertBoolConverter"/>
|
||||
<converters:MachineStateColorConverter x:Key="MachineStateColorConverter"/>
|
||||
<converters:MachineStateStringConverter x:Key="MachineStateStringConverter"/>
|
||||
<converters:IsNotNullBoolConverter x:Key="IsNotNullBoolConverter"/>
|
||||
<converters:InvertBoolConverter x:Key="InvertBoolConverter"/>
|
||||
</ResourceDictionary>
|
||||
</ContentPage.Resources>
|
||||
<NavigationPage.TitleView>
|
||||
<Label Text="{Binding MachineItem.State, Converter={StaticResource MachineStateStringConverter}}" FontAttributes="Bold" HorizontalOptions="End" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" WidthRequest="150" Margin="7.5" VerticalOptions="FillAndExpand" FontSize="Small" BackgroundColor="{Binding MachineItem.State, Converter={StaticResource MachineStateColorConverter}}">
|
||||
<Label.IsVisible>
|
||||
<MultiBinding Converter="{StaticResource AllTrueBoolConverter}">
|
||||
<Binding Path="IsBusy" Converter="{StaticResource InvertBoolConverter}"/>
|
||||
<Binding Path="IsConnected" />
|
||||
</MultiBinding>
|
||||
</Label.IsVisible>
|
||||
</Label>
|
||||
</NavigationPage.TitleView>
|
||||
<ContentPage.Content>
|
||||
<ScrollView>
|
||||
<StackLayout Padding="20">
|
||||
<StackLayout IsVisible="{Binding IsConnected}">
|
||||
<StackLayout IsVisible="{Binding IsBusy}">
|
||||
<ActivityIndicator IsRunning="{Binding IsBusy}"></ActivityIndicator>
|
||||
<views:ConnectionStateView/>
|
||||
<StackLayout>
|
||||
<StackLayout.IsVisible>
|
||||
<MultiBinding Converter="{StaticResource AllTrueBoolConverter}">
|
||||
<Binding Path="IsBusy" Converter="{StaticResource InvertBoolConverter}"/>
|
||||
<Binding Path="IsConnected" />
|
||||
</MultiBinding>
|
||||
</StackLayout.IsVisible>
|
||||
<Label Text="{Binding MachineItem.Name}" Style="{StaticResource LabelStyle_Title}"/>
|
||||
<Label Text="{Binding MachineItem.Description}" Style="{StaticResource Style_Label_Text_Center}"/>
|
||||
<StackLayout Orientation="Horizontal" IsVisible="{Binding MachineItem.CurrentUser, Converter={StaticResource IsNotNullBoolConverter}}">
|
||||
<Label Text="{x:Static resource_text:TextResource.MachinePage_CurrentUser}" Style="{StaticResource Style_Label_Property_Title}"/>
|
||||
<Label Text="{Binding MachineItem.CurrentUser.Username}" Style="{StaticResource Style_Label_Property_Text}"/>
|
||||
</StackLayout>
|
||||
<StackLayout IsVisible="{Binding IsBusy, Converter={StaticResource InvertBoolConverter}}">
|
||||
<Label Text="{Binding MachineItem.Name}" Style="{StaticResource LabelStyle_Title}"/>
|
||||
<Label Text="{Binding MachineItem.Description}" Style="{StaticResource Style_Label_Text_Center}"/>
|
||||
<StackLayout Orientation="Horizontal" IsVisible="{Binding MachineItem.CurrentUser, Converter={StaticResource IsNotNullBoolConverter}}">
|
||||
<Label Text="{x:Static resource_text:TextResource.MachinePage_CurrentUser}" Style="{StaticResource Style_Label_Property_Title}"/>
|
||||
<Label Text="{Binding MachineItem.CurrentUser.Username}" Style="{StaticResource Style_Label_Property_Text}"/>
|
||||
</StackLayout>
|
||||
|
||||
<Button Text="{x:Static resource_text:TextResource.MachinePage_Use}" IsVisible="{Binding MachineItem.CanUse}" Command="{Binding UseMachineCommand}" Style="{StaticResource Style_Button_Primary}"/>
|
||||
<Label Text="{x:Static resource_text:TextResource.MachinePage_CanNotUseByPermission}" IsVisible="{Binding MachineItem.CanNotUseByPermission}" Style="{StaticResource Style_Label_Text_Center}"/>
|
||||
<Button Text="{x:Static resource_text:TextResource.MachinePage_GiveBack}" IsVisible="{Binding MachineItem.CanInUse}" Command="{Binding GiveBackMachineCommand}" Style="{StaticResource Style_Button_Primary}"/>
|
||||
<Button VerticalOptions="End" Text="{x:Static resource_text:TextResource.MachinePage_OpenWiki}" IsVisible="{Binding MachineItem.Wiki, Converter={StaticResource IsNotNullBoolConverter}}" Command="{Binding OpenWikiCommand}" Style="{StaticResource Style_Button_Primary}"/>
|
||||
<StackLayout Grid.Row="2" VerticalOptions="End" IsVisible="{Binding MachineItem.CanManage}">
|
||||
<Label Text="{x:Static resource_text:TextResource.MachinePage_ManageMachine}" Style="{StaticResource Style_Label_Property_Title}"/>
|
||||
<StackLayout Margin="10, 0, 0, 20" Orientation="Horizontal" IsVisible="{Binding MachineItem.LastUser, Converter={StaticResource IsNotNullBoolConverter}}">
|
||||
<Label Text="{x:Static resource_text:TextResource.MachinePage_LastUser}" Style="{StaticResource Style_Label_Property_Title}"/>
|
||||
<Label Text="{Binding MachineItem.LastUser.Username}" Style="{StaticResource Style_Label_Property_Text}" Margin="10, 0, 0, 0"/>
|
||||
</StackLayout>
|
||||
<Button Text="{x:Static resource_text:TextResource.MachinePage_ForceFree}" Command="{Binding ForceFreeMachineCommand}" Style="{StaticResource Style_Button_Primary}"/>
|
||||
<Button Text="{x:Static resource_text:TextResource.MachinePage_ForceBlock}" Command="{Binding ForceBlockMachineCommand}" Style="{StaticResource Style_Button_Admin}"/>
|
||||
<Button Text="{x:Static resource_text:TextResource.MachinePage_ForceDisable}" Command="{Binding ForceDisableMachineCommand}" Style="{StaticResource Style_Button_Admin}"/>
|
||||
<Button Text="{x:Static resource_text:TextResource.MachinePage_Use}" IsVisible="{Binding MachineItem.CanUse}" Command="{Binding UseMachineCommand}" Style="{StaticResource Style_Button_Primary}"/>
|
||||
<Label Text="{x:Static resource_text:TextResource.MachinePage_CanNotUseByPermission}" IsVisible="{Binding MachineItem.CanNotUseByPermission}" Style="{StaticResource Style_Label_Text_Center}"/>
|
||||
<Button Text="{x:Static resource_text:TextResource.MachinePage_GiveBack}" IsVisible="{Binding MachineItem.CanInUse}" Command="{Binding GiveBackMachineCommand}" Style="{StaticResource Style_Button_Primary}"/>
|
||||
<Button VerticalOptions="End" Text="{x:Static resource_text:TextResource.MachinePage_OpenWiki}" IsVisible="{Binding MachineItem.Wiki, Converter={StaticResource IsNotNullBoolConverter}}" Command="{Binding OpenWikiCommand}" Style="{StaticResource Style_Button_Primary}"/>
|
||||
<StackLayout Grid.Row="2" VerticalOptions="End" IsVisible="{Binding MachineItem.CanManage}">
|
||||
<Label Text="{x:Static resource_text:TextResource.MachinePage_ManageMachine}" Style="{StaticResource Style_Label_Property_Title}"/>
|
||||
<StackLayout Margin="10, 0, 0, 20" Orientation="Horizontal" IsVisible="{Binding MachineItem.LastUser, Converter={StaticResource IsNotNullBoolConverter}}">
|
||||
<Label Text="{x:Static resource_text:TextResource.MachinePage_LastUser}" Style="{StaticResource Style_Label_Property_Title}"/>
|
||||
<Label Text="{Binding MachineItem.LastUser.Username}" Style="{StaticResource Style_Label_Property_Text}" Margin="10, 0, 0, 0"/>
|
||||
</StackLayout>
|
||||
<Button Text="{x:Static resource_text:TextResource.MachinePage_ForceFree}" Command="{Binding ForceFreeMachineCommand}" Style="{StaticResource Style_Button_Primary}"/>
|
||||
<Button Text="{x:Static resource_text:TextResource.MachinePage_ForceBlock}" Command="{Binding ForceBlockMachineCommand}" Style="{StaticResource Style_Button_Admin}"/>
|
||||
<Button Text="{x:Static resource_text:TextResource.MachinePage_ForceDisable}" Command="{Binding ForceDisableMachineCommand}" Style="{StaticResource Style_Button_Admin}"/>
|
||||
</StackLayout>
|
||||
</StackLayout>
|
||||
<Label Text="{x:Static resource_text:TextResource.PLEASECONNECTTOSERVER}" IsVisible="{Binding IsConnected, Converter={StaticResource InvertBoolConverter}}"></Label>
|
||||
</StackLayout>
|
||||
</ScrollView>
|
||||
</ContentPage.Content>
|
||||
|
@ -3,7 +3,7 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
x:Class="Borepin.Page.ProfilePage"
|
||||
xmlns:converters="clr-namespace:Borepin.Converter"
|
||||
xmlns:resource_text="clr-namespace:Borepin.Resources.Text"
|
||||
xmlns:resource_text="clr-namespace:Borepin.Resources.Text" xmlns:views="clr-namespace:Borepin.View"
|
||||
Title="{x:Static resource_text:TextResource.TITLE_Profile}">
|
||||
<ContentPage.Resources>
|
||||
<ResourceDictionary>
|
||||
@ -12,21 +12,20 @@
|
||||
</ContentPage.Resources>
|
||||
<ContentPage.Content>
|
||||
<StackLayout Padding="20">
|
||||
<StackLayout IsVisible="{Binding IsBusy}">
|
||||
<ActivityIndicator IsRunning="{Binding IsBusy}"></ActivityIndicator>
|
||||
</StackLayout>
|
||||
<StackLayout IsVisible="{Binding IsBusy, Converter={StaticResource InvertBoolConverter}}">
|
||||
<StackLayout IsVisible="{Binding IsConnected}">
|
||||
<Label Text="{Binding Username}" Style="{StaticResource LabelStyle_Title}"/>
|
||||
<StackLayout IsVisible="{Binding CanManage}">
|
||||
<Label Text="{x:Static resource_text:TextResource.ProfilePage_ChangePassword}" Style="{StaticResource Style_Label_Property_Title}"/>
|
||||
<Entry Placeholder="{x:Static resource_text:TextResource.ProfilePage_OldPassword}" Text="{Binding OldPassword}"/>
|
||||
<Entry Placeholder="{x:Static resource_text:TextResource.ProfilePage_NewPassword}" Text="{Binding NewPassword}"/>
|
||||
<Button Text="{x:Static resource_text:TextResource.ProfilePage_UpdatePassword}" Command="{Binding UpdatePasswordCommand}" Style="{StaticResource Style_Button_Primary}"/>
|
||||
</StackLayout>
|
||||
</StackLayout>
|
||||
<StackLayout IsVisible="{Binding IsConnected, Converter={StaticResource InvertBoolConverter}}">
|
||||
<Label Text="{x:Static resource_text:TextResource.PLEASECONNECTTOSERVER}"/>
|
||||
<views:ConnectionStateView/>
|
||||
<StackLayout>
|
||||
<StackLayout.IsVisible>
|
||||
<MultiBinding Converter="{StaticResource AllTrueBoolConverter}">
|
||||
<Binding Path="IsBusy" Converter="{StaticResource InvertBoolConverter}"/>
|
||||
<Binding Path="IsConnected" />
|
||||
</MultiBinding>
|
||||
</StackLayout.IsVisible>
|
||||
<Label Text="{Binding Username}" Style="{StaticResource LabelStyle_Title}"/>
|
||||
<StackLayout IsVisible="{Binding CanManage}">
|
||||
<Label Text="{x:Static resource_text:TextResource.ProfilePage_ChangePassword}" Style="{StaticResource Style_Label_Property_Title}"/>
|
||||
<Entry Placeholder="{x:Static resource_text:TextResource.ProfilePage_OldPassword}" Text="{Binding OldPassword}"/>
|
||||
<Entry Placeholder="{x:Static resource_text:TextResource.ProfilePage_NewPassword}" Text="{Binding NewPassword}"/>
|
||||
<Button Text="{x:Static resource_text:TextResource.ProfilePage_UpdatePassword}" Command="{Binding UpdatePasswordCommand}" Style="{StaticResource Style_Button_Primary}"/>
|
||||
</StackLayout>
|
||||
</StackLayout>
|
||||
</StackLayout>
|
||||
|
@ -11,36 +11,36 @@
|
||||
</NavigationPage.TitleView>
|
||||
<ContentPage.Resources>
|
||||
<ResourceDictionary>
|
||||
<converters:AllTrueBoolConverter x:Key="AllTrueBoolConverter"/>
|
||||
<converters:InvertBoolConverter x:Key="InvertBoolConverter"/>
|
||||
<converters:ListNotEmptyConverter x:Key="ListNotEmptyConverter"/>
|
||||
</ResourceDictionary>
|
||||
</ContentPage.Resources>
|
||||
<ContentPage.Content>
|
||||
<StackLayout Padding="20">
|
||||
<StackLayout IsVisible="{Binding IsBusy}">
|
||||
<ActivityIndicator IsRunning="{Binding IsBusy}"></ActivityIndicator>
|
||||
</StackLayout>
|
||||
<StackLayout IsVisible="{Binding IsBusy, Converter={StaticResource InvertBoolConverter}}">
|
||||
<StackLayout IsVisible="{Binding IsConnected}">
|
||||
<Button Text="{x:Static resource_text:TextResource.UserListPage_AddUser}" Command="{Binding AddUserCommand}" Style="{StaticResource Style_Button_Primary}"/>
|
||||
<SearchBar Text="{Binding SearchUsername}" SearchCommand="{Binding SearchUserCommand}">
|
||||
<SearchBar.Behaviors>
|
||||
<prism:EventToCommandBehavior EventName="TextChanged" Command="{Binding SearchUserCommand}"/>
|
||||
</SearchBar.Behaviors>
|
||||
</SearchBar>
|
||||
<ListView ItemsSource="{Binding FilteredUserListItemViewModel_List}" SelectionMode="None" SeparatorColor="Transparent">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<ViewCell>
|
||||
<views:UserListItemView />
|
||||
</ViewCell>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</StackLayout>
|
||||
<StackLayout IsVisible="{Binding IsConnected, Converter={StaticResource InvertBoolConverter}}">
|
||||
<Label Text="{x:Static resource_text:TextResource.PLEASECONNECTTOSERVER}"/>
|
||||
</StackLayout>
|
||||
<views:ConnectionStateView/>
|
||||
<StackLayout>
|
||||
<StackLayout.IsVisible>
|
||||
<MultiBinding Converter="{StaticResource AllTrueBoolConverter}">
|
||||
<Binding Path="IsBusy" Converter="{StaticResource InvertBoolConverter}"/>
|
||||
<Binding Path="IsConnected" />
|
||||
</MultiBinding>
|
||||
</StackLayout.IsVisible>
|
||||
<Button Text="{x:Static resource_text:TextResource.UserListPage_AddUser}" Command="{Binding AddUserCommand}" Style="{StaticResource Style_Button_Primary}"/>
|
||||
<SearchBar Text="{Binding SearchUsername}" SearchCommand="{Binding SearchUserCommand}">
|
||||
<SearchBar.Behaviors>
|
||||
<prism:EventToCommandBehavior EventName="TextChanged" Command="{Binding SearchUserCommand}"/>
|
||||
</SearchBar.Behaviors>
|
||||
</SearchBar>
|
||||
<ListView ItemsSource="{Binding FilteredUserListItemViewModel_List}" SelectionMode="None" SeparatorColor="Transparent">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<ViewCell>
|
||||
<views:UserListItemView />
|
||||
</ViewCell>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</StackLayout>
|
||||
</StackLayout>
|
||||
</ContentPage.Content>
|
||||
|
@ -8,39 +8,39 @@
|
||||
Title="{x:Static resource_text:TextResource.TITLE_User}">
|
||||
<ContentPage.Resources>
|
||||
<ResourceDictionary>
|
||||
<converters:AllTrueBoolConverter x:Key="AllTrueBoolConverter"/>
|
||||
<converters:InvertBoolConverter x:Key="InvertBoolConverter"/>
|
||||
<converters:MachineStateColorConverter x:Key="MachineStateColorConverter"/>
|
||||
<converters:MachineStateStringConverter x:Key="MachineStateStringConverter"/>
|
||||
<converters:IsNotNullBoolConverter x:Key="IsNotNullBoolConverter"/>
|
||||
<converters:InvertBoolConverter x:Key="InvertBoolConverter"/>
|
||||
</ResourceDictionary>
|
||||
</ContentPage.Resources>
|
||||
<ContentPage.Content>
|
||||
<StackLayout Padding="20">
|
||||
<StackLayout IsVisible="{Binding IsBusy}">
|
||||
<ActivityIndicator IsRunning="{Binding IsBusy}"></ActivityIndicator>
|
||||
</StackLayout>
|
||||
<StackLayout IsVisible="{Binding IsBusy, Converter={StaticResource InvertBoolConverter}}">
|
||||
<StackLayout IsVisible="{Binding IsConnected}">
|
||||
<Label Text="{Binding UserItem.Username}" Style="{StaticResource LabelStyle_Title}"/>
|
||||
<ListView ItemsSource="{Binding PermissionSelectViewModel_List}" SelectionMode="None" SeparatorColor="Transparent">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<ViewCell>
|
||||
<views:PermissionSelectView />
|
||||
</ViewCell>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
<StackLayout IsVisible="{Binding CanAdmin}">
|
||||
<Label Text="{x:Static resource_text:TextResource.UserPage_ChangePassword}" Style="{StaticResource Style_Label_Property_Title}"/>
|
||||
<Entry Placeholder="{x:Static resource_text:TextResource.UserPage_NewPassword}" Text="{Binding NewPassword}"/>
|
||||
<Button Text="{x:Static resource_text:TextResource.UserPage_UpdatePassword}" Command="{Binding UpdatePasswordCommand}" Style="{StaticResource Style_Button_Primary}"/>
|
||||
</StackLayout>
|
||||
<Button Grid.Row="1" Text="{x:Static resource_text:TextResource.DELETE}" Command="{Binding DeleteCommand}" Style="{StaticResource Style_Button_Admin}" VerticalOptions="End"/>
|
||||
</StackLayout>
|
||||
<StackLayout IsVisible="{Binding IsConnected, Converter={StaticResource InvertBoolConverter}}">
|
||||
<Label Text="{x:Static resource_text:TextResource.PLEASECONNECTTOSERVER}"/>
|
||||
<views:ConnectionStateView/>
|
||||
<StackLayout>
|
||||
<StackLayout.IsVisible>
|
||||
<MultiBinding Converter="{StaticResource AllTrueBoolConverter}">
|
||||
<Binding Path="IsBusy" Converter="{StaticResource InvertBoolConverter}"/>
|
||||
<Binding Path="IsConnected" />
|
||||
</MultiBinding>
|
||||
</StackLayout.IsVisible>
|
||||
<Label Text="{Binding UserItem.Username}" Style="{StaticResource LabelStyle_Title}"/>
|
||||
<ListView ItemsSource="{Binding PermissionSelectViewModel_List}" SelectionMode="None" SeparatorColor="Transparent">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<ViewCell>
|
||||
<views:PermissionSelectView />
|
||||
</ViewCell>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
<StackLayout IsVisible="{Binding CanAdmin}">
|
||||
<Label Text="{x:Static resource_text:TextResource.UserPage_ChangePassword}" Style="{StaticResource Style_Label_Property_Title}"/>
|
||||
<Entry Placeholder="{x:Static resource_text:TextResource.UserPage_NewPassword}" Text="{Binding NewPassword}"/>
|
||||
<Button Text="{x:Static resource_text:TextResource.UserPage_UpdatePassword}" Command="{Binding UpdatePasswordCommand}" Style="{StaticResource Style_Button_Primary}"/>
|
||||
</StackLayout>
|
||||
<Button Grid.Row="1" Text="{x:Static resource_text:TextResource.DELETE}" Command="{Binding DeleteCommand}" Style="{StaticResource Style_Button_Admin}" VerticalOptions="End"/>
|
||||
</StackLayout>
|
||||
</StackLayout>
|
||||
</ContentPage.Content>
|
||||
|
@ -8,6 +8,7 @@ using Borepin.Service;
|
||||
using Borepin.Service.Storage;
|
||||
using FabAccessAPI;
|
||||
using FabAccessAPI.Exceptions;
|
||||
using FabAccessAPI.Exceptions.SASL;
|
||||
using Prism.Commands;
|
||||
using Prism.Navigation;
|
||||
using Prism.Services;
|
||||
@ -79,12 +80,18 @@ namespace Borepin.PageModel.AddServerProcess
|
||||
public async Task AuthenticateCommandExecute()
|
||||
{
|
||||
IsBusy = true;
|
||||
if(Username == null || Username == String.Empty || Password == null || Password == String.Empty)
|
||||
{
|
||||
IsBusy = false;
|
||||
return;
|
||||
}
|
||||
|
||||
Username = Username.Trim();
|
||||
|
||||
_ConnectionData = new ConnectionData()
|
||||
{
|
||||
Host = _ConnectionData.Host,
|
||||
Mechanism = Mechanism.PLAIN,
|
||||
Mechanism = SASLMechanismEnum.PLAIN,
|
||||
Username = Username,
|
||||
Properties = new Dictionary<string, object>(StringComparer.Ordinal)
|
||||
{
|
||||
@ -98,7 +105,7 @@ namespace Borepin.PageModel.AddServerProcess
|
||||
if (_API.IsConnected)
|
||||
{
|
||||
await _API.Disconnect().ConfigureAwait(true);
|
||||
_API.UnbindAllEvents();
|
||||
_API.UnbindEventHandler();
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,7 +113,7 @@ namespace Borepin.PageModel.AddServerProcess
|
||||
{
|
||||
await _API.Connect(_ConnectionData).ConfigureAwait(false);
|
||||
}
|
||||
catch (ConnectingFailedException)
|
||||
catch (ConnectionException)
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(async () =>
|
||||
{
|
||||
@ -116,6 +123,16 @@ namespace Borepin.PageModel.AddServerProcess
|
||||
});
|
||||
return;
|
||||
}
|
||||
catch (InvalidCredentialsException)
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(async () =>
|
||||
{
|
||||
await _PageDialogService.DisplayAlertAsync(Resources.Text.TextResource.ALERT_ConnectionFailed, Resources.Text.TextResource.ALERT_CredentialsInvalid, Resources.Text.TextResource.OK).ConfigureAwait(false);
|
||||
|
||||
IsBusy = false;
|
||||
});
|
||||
return;
|
||||
}
|
||||
catch (AuthenticationFailedException)
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(async () =>
|
||||
@ -126,7 +143,7 @@ namespace Borepin.PageModel.AddServerProcess
|
||||
});
|
||||
return;
|
||||
}
|
||||
catch(Exception)
|
||||
catch(Exception ex)
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(async () =>
|
||||
{
|
||||
|
@ -51,7 +51,7 @@ namespace Borepin.PageModel.AddServerProcess
|
||||
}
|
||||
public async void AuthPlainCommandExecute()
|
||||
{
|
||||
_ConnectionData.Mechanism = Mechanism.PLAIN;
|
||||
_ConnectionData.Mechanism = SASLMechanismEnum.PLAIN;
|
||||
|
||||
INavigationResult result = await _NavigationService.NavigateAsync("AddServerProcess_AuthPlainPage").ConfigureAwait(false);
|
||||
if(result.Exception != null)
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Borepin.Base;
|
||||
using Borepin.Service.ErrorMessage;
|
||||
using FabAccessAPI;
|
||||
using FabAccessAPI.Exceptions;
|
||||
using Prism.Commands;
|
||||
@ -16,11 +17,14 @@ namespace Borepin.PageModel.AddServerProcess
|
||||
{
|
||||
#region Private Fields
|
||||
private ConnectionData _ConnectionData;
|
||||
private IErrorMessageService _ErrorMessageService;
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
public SelectServerPageModel(INavigationService navigationService, IPageDialogService pageDialogService) : base(navigationService, pageDialogService)
|
||||
public SelectServerPageModel(INavigationService navigationService, IPageDialogService pageDialogService, IErrorMessageService errorMessageService) : base(navigationService, pageDialogService)
|
||||
{
|
||||
_ErrorMessageService = errorMessageService;
|
||||
|
||||
ConnectToServerCommand = new DelegateCommand(async () => await ConnectToServerExecute().ConfigureAwait(false));
|
||||
DetectLocalServerCommand = new DelegateCommand(DetectHostCommandExecute);
|
||||
ScanCodeCommand = new DelegateCommand(ScanCodeCommandExecute);
|
||||
@ -118,17 +122,12 @@ namespace Borepin.PageModel.AddServerProcess
|
||||
try
|
||||
{
|
||||
API api = new API();
|
||||
await api.TestConnection(_ConnectionData).ConfigureAwait(false);
|
||||
await api.TryToConnect(_ConnectionData).ConfigureAwait(false);
|
||||
}
|
||||
catch(ConnectingFailedException)
|
||||
catch(Exception exception)
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(async () =>
|
||||
{
|
||||
await _PageDialogService.DisplayAlertAsync(Resources.Text.TextResource.ALERT_ConnectionFailed, Resources.Text.TextResource.ALERT_UnableServer, Resources.Text.TextResource.OK).ConfigureAwait(false);
|
||||
|
||||
IsBusy = false;
|
||||
});
|
||||
|
||||
_ErrorMessageService.DisplayConnectFailedMessage(exception);
|
||||
IsBusy = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -145,7 +145,15 @@ namespace Borepin.PageModel
|
||||
{
|
||||
if(_API.IsConnected)
|
||||
{
|
||||
await LoadAPIData().ConfigureAwait(true);
|
||||
try
|
||||
{
|
||||
await LoadAPIData().ConfigureAwait(true);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
IsRefreshing = false;
|
||||
|
@ -109,7 +109,7 @@ namespace Borepin.PageModel
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(async () =>
|
||||
{
|
||||
await _PageDialogService.DisplayAlertAsync(Resources.Text.TextResource.ALERT, Resources.Text.TextResource.Alert_QRInvalid, Resources.Text.TextResource.OK).ConfigureAwait(false);
|
||||
await _PageDialogService.DisplayAlertAsync(Resources.Text.TextResource.ALERT, Resources.Text.TextResource.ALERT_QRInvalid, Resources.Text.TextResource.OK).ConfigureAwait(false);
|
||||
IsScanning = true;
|
||||
});
|
||||
return;
|
||||
|
@ -36,7 +36,7 @@ namespace Borepin.PageModel
|
||||
List<Task> tasks = new List<Task>();
|
||||
|
||||
IList<ConnectionData> list = await _LoginStorageService.GetList().ConfigureAwait(false);
|
||||
if (_API.IsConnected)
|
||||
if (_API.IsConnected || _API.IsConnecting)
|
||||
{
|
||||
ActiveConnection = new ServerListItemViewModel(_NavigationService, _PageDialogService);
|
||||
await ActiveConnection.LoadInstance(_API.ConnectionData).ConfigureAwait(false);
|
||||
|
@ -1,9 +1,11 @@
|
||||
using Borepin.Base;
|
||||
using Borepin.Base.Exceptions;
|
||||
using Borepin.Service;
|
||||
using Borepin.Service.ErrorMessage;
|
||||
using Borepin.Service.Storage;
|
||||
using FabAccessAPI;
|
||||
using FabAccessAPI.Exceptions;
|
||||
using FabAccessAPI.Exceptions.SASL;
|
||||
using Prism.Commands;
|
||||
using Prism.Navigation;
|
||||
using Prism.Services;
|
||||
@ -21,14 +23,16 @@ namespace Borepin.PageModel
|
||||
#region Private Fields
|
||||
private readonly IDialogService _DialogService;
|
||||
private readonly ILoginStorageService _LoginStorageService;
|
||||
private readonly IErrorMessageService _ErrorMessageService;
|
||||
private bool IsDialog = false;
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
public ServerPageModel(INavigationService navigationService, IPageDialogService pageDialogService, IAPIService apiService, IDialogService dialogService, ILoginStorageService loginStorageService) : base(navigationService, pageDialogService, apiService)
|
||||
public ServerPageModel(INavigationService navigationService, IPageDialogService pageDialogService, IAPIService apiService, IDialogService dialogService, ILoginStorageService loginStorageService, IErrorMessageService errorMessageService) : base(navigationService, pageDialogService, apiService)
|
||||
{
|
||||
_DialogService = dialogService;
|
||||
_LoginStorageService = loginStorageService;
|
||||
_ErrorMessageService = errorMessageService;
|
||||
|
||||
ConnectCommand = new DelegateCommand(async () => await ConnectCommandExecute().ConfigureAwait(false));
|
||||
DisconnectCommand = new DelegateCommand(async () => await DisonnectCommandExecute().ConfigureAwait(false));
|
||||
@ -51,7 +55,7 @@ namespace Borepin.PageModel
|
||||
throw new InstanceIncorrectException();
|
||||
}
|
||||
|
||||
if(_API.ConnectionData != null && Connection_Item != null)
|
||||
if((_API.IsConnected || _API.IsConnecting) && Connection_Item != null)
|
||||
{
|
||||
InstanceIsActiveConnection = Connection_Item.Equals(_API.ConnectionData);
|
||||
}
|
||||
@ -124,31 +128,16 @@ namespace Borepin.PageModel
|
||||
if(_API.IsConnected)
|
||||
{
|
||||
await _API.Disconnect().ConfigureAwait(true);
|
||||
_API.UnbindAllEvents();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await _API.Connect(Connection_Item).ConfigureAwait(false);
|
||||
}
|
||||
catch(ConnectingFailedException)
|
||||
catch(Exception exception)
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(async () =>
|
||||
{
|
||||
await _PageDialogService.DisplayAlertAsync(Resources.Text.TextResource.ALERT_ConnectionFailed, Resources.Text.TextResource.ALERT_UnableServer, Resources.Text.TextResource.OK).ConfigureAwait(false);
|
||||
|
||||
IsBusy = false;
|
||||
});
|
||||
return;
|
||||
}
|
||||
catch(AuthenticationFailedException)
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(async () =>
|
||||
{
|
||||
await _PageDialogService.DisplayAlertAsync(Resources.Text.TextResource.ALERT_ConnectionFailed, Resources.Text.TextResource.ALERT_AuthServer, Resources.Text.TextResource.OK).ConfigureAwait(false);
|
||||
|
||||
IsBusy = false;
|
||||
});
|
||||
_ErrorMessageService.DisplayConnectFailedMessage(exception);
|
||||
IsBusy = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -184,7 +173,7 @@ namespace Borepin.PageModel
|
||||
}
|
||||
public async Task DisonnectCommandExecute()
|
||||
{
|
||||
_API.UnbindAllEvents();
|
||||
_API.UnbindEventHandler();
|
||||
await _API.Disconnect().ConfigureAwait(false);
|
||||
|
||||
await LoadInstance(Connection_Item).ConfigureAwait(false);
|
||||
@ -215,7 +204,7 @@ namespace Borepin.PageModel
|
||||
if(string.Equals(result.Parameters.GetValue<string>("result"), "confirm", StringComparison.Ordinal))
|
||||
{
|
||||
await _API.Disconnect().ConfigureAwait(false);
|
||||
_API.UnbindAllEvents();
|
||||
_API.UnbindEventHandler();
|
||||
await _LoginStorageService.Remove(result.Parameters.GetValue<ConnectionData>("instance")).ConfigureAwait(false);
|
||||
|
||||
Device.BeginInvokeOnMainThread(async () =>
|
||||
|
@ -1,8 +1,10 @@
|
||||
using Borepin.Base;
|
||||
using Borepin.Service;
|
||||
using Borepin.Service.ErrorMessage;
|
||||
using Borepin.Service.Storage;
|
||||
using FabAccessAPI;
|
||||
using FabAccessAPI.Exceptions;
|
||||
using FabAccessAPI.Exceptions.SASL;
|
||||
using Prism.AppModel;
|
||||
using Prism.Navigation;
|
||||
using Prism.Services;
|
||||
@ -18,12 +20,14 @@ namespace Borepin.PageModel
|
||||
{
|
||||
#region Private Fields
|
||||
private readonly ILoginStorageService _LoginStorageService;
|
||||
private readonly IErrorMessageService _ErrorMessageService;
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
public StartPageModel(INavigationService navigationService, IPageDialogService pageDialogService, IAPIService apiService, ILoginStorageService loginStorageService) : base(navigationService, pageDialogService, apiService)
|
||||
public StartPageModel(INavigationService navigationService, IPageDialogService pageDialogService, IAPIService apiService, ILoginStorageService loginStorageService, IErrorMessageService errorMessageService) : base(navigationService, pageDialogService, apiService)
|
||||
{
|
||||
_LoginStorageService = loginStorageService;
|
||||
_ErrorMessageService = errorMessageService;
|
||||
}
|
||||
#endregion
|
||||
|
||||
@ -69,23 +73,13 @@ namespace Borepin.PageModel
|
||||
}
|
||||
});
|
||||
}
|
||||
catch(Exception ex)
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (ex is AuthenticationFailedException)
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(async () =>
|
||||
{
|
||||
await _PageDialogService.DisplayAlertAsync(Resources.Text.TextResource.ALERT_ConnectionFailed, Resources.Text.TextResource.ALERT_AuthServer, Resources.Text.TextResource.OK).ConfigureAwait(false);
|
||||
});
|
||||
}
|
||||
else if (ex is ConnectingFailedException)
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(async () =>
|
||||
{
|
||||
await _PageDialogService.DisplayAlertAsync(Resources.Text.TextResource.ALERT_ConnectionFailed, Resources.Text.TextResource.ALERT_UnableServer, Resources.Text.TextResource.OK).ConfigureAwait(false);
|
||||
});
|
||||
}
|
||||
_ErrorMessageService.DisplayConnectFailedMessage(exception);
|
||||
}
|
||||
|
||||
if (_API.IsConnected == false)
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(async () =>
|
||||
{
|
||||
INavigationResult result = await _NavigationService.NavigateAsync("/MainPage/NavigationPage/ServerListPage").ConfigureAwait(false);
|
||||
|
@ -90,7 +90,14 @@ namespace Borepin.PageModel
|
||||
{
|
||||
if(_API.IsConnected)
|
||||
{
|
||||
await LoadAPIData().ConfigureAwait(true);
|
||||
try
|
||||
{
|
||||
await LoadAPIData().ConfigureAwait(true);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
IsRefreshing = false;
|
||||
|
103
Borepin/Borepin/Resources/Text/TextResource.Designer.cs
generated
103
Borepin/Borepin/Resources/Text/TextResource.Designer.cs
generated
@ -170,6 +170,15 @@ namespace Borepin.Resources.Text {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Authentication failed ähnelt.
|
||||
/// </summary>
|
||||
internal static string ALERT_AuthFailed {
|
||||
get {
|
||||
return ResourceManager.GetString("ALERT_AuthFailed", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Unable to authenticate to server. ähnelt.
|
||||
/// </summary>
|
||||
@ -179,6 +188,15 @@ namespace Borepin.Resources.Text {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die SASL Mechanism is not supported- ähnelt.
|
||||
/// </summary>
|
||||
internal static string ALERT_BadMechanism {
|
||||
get {
|
||||
return ResourceManager.GetString("ALERT_BadMechanism", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Connection failed ähnelt.
|
||||
/// </summary>
|
||||
@ -188,6 +206,24 @@ namespace Borepin.Resources.Text {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Connection time exceeded. ähnelt.
|
||||
/// </summary>
|
||||
internal static string ALERT_ConnectionTimeout {
|
||||
get {
|
||||
return ResourceManager.GetString("ALERT_ConnectionTimeout", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Credentials are invalid. ähnelt.
|
||||
/// </summary>
|
||||
internal static string ALERT_CredentialsInvalid {
|
||||
get {
|
||||
return ResourceManager.GetString("ALERT_CredentialsInvalid", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Connection already exist. Please delete old Connection before adding the new Connection. ähnelt.
|
||||
/// </summary>
|
||||
@ -209,9 +245,27 @@ namespace Borepin.Resources.Text {
|
||||
/// <summary>
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die QR Code is invalid ähnelt.
|
||||
/// </summary>
|
||||
internal static string Alert_QRInvalid {
|
||||
internal static string ALERT_QRInvalid {
|
||||
get {
|
||||
return ResourceManager.GetString("Alert_QRInvalid", resourceCulture);
|
||||
return ResourceManager.GetString("ALERT_QRInvalid", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die SASL Authenticaiton failed ähnelt.
|
||||
/// </summary>
|
||||
internal static string ALERT_SASLAuth {
|
||||
get {
|
||||
return ResourceManager.GetString("ALERT_SASLAuth", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die TLS certificate is invalid. ähnelt.
|
||||
/// </summary>
|
||||
internal static string ALERT_TLSInvalid {
|
||||
get {
|
||||
return ResourceManager.GetString("ALERT_TLSInvalid", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
@ -287,6 +341,33 @@ namespace Borepin.Resources.Text {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Connecting to Server ... ähnelt.
|
||||
/// </summary>
|
||||
internal static string ConnectionStatus_Connecting {
|
||||
get {
|
||||
return ResourceManager.GetString("ConnectionStatus_Connecting", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die No Connection to Server ähnelt.
|
||||
/// </summary>
|
||||
internal static string ConnectionStatus_NoConnection {
|
||||
get {
|
||||
return ResourceManager.GetString("ConnectionStatus_NoConnection", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Please select a Server. ähnelt.
|
||||
/// </summary>
|
||||
internal static string ConnectionStatus_NotConnected {
|
||||
get {
|
||||
return ResourceManager.GetString("ConnectionStatus_NotConnected", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Delete ähnelt.
|
||||
/// </summary>
|
||||
@ -450,6 +531,15 @@ namespace Borepin.Resources.Text {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Unlock ähnelt.
|
||||
/// </summary>
|
||||
internal static string MachinePage_Unlock {
|
||||
get {
|
||||
return ResourceManager.GetString("MachinePage_Unlock", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Use ähnelt.
|
||||
/// </summary>
|
||||
@ -540,15 +630,6 @@ namespace Borepin.Resources.Text {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Please connect to Server ähnelt.
|
||||
/// </summary>
|
||||
internal static string PLEASECONNECTTOSERVER {
|
||||
get {
|
||||
return ResourceManager.GetString("PLEASECONNECTTOSERVER", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Change Password ähnelt.
|
||||
/// </summary>
|
||||
|
@ -151,36 +151,71 @@ You can also put down several servers and then connect to the desired server.</v
|
||||
</data>
|
||||
<data name="ALERT_AddressInvalid" xml:space="preserve">
|
||||
<value>Server address is invaild.</value>
|
||||
<comment>Message Content</comment>
|
||||
</data>
|
||||
<data name="ALERT_AddUserFailed" xml:space="preserve">
|
||||
<value>Add User failed</value>
|
||||
<comment>Message Content</comment>
|
||||
</data>
|
||||
<data name="ALERT_AuthFailed" xml:space="preserve">
|
||||
<value>Authentication failed</value>
|
||||
<comment>Message Title</comment>
|
||||
</data>
|
||||
<data name="ALERT_AuthServer" xml:space="preserve">
|
||||
<value>Unable to authenticate to server.</value>
|
||||
<comment>Message Content</comment>
|
||||
</data>
|
||||
<data name="ALERT_BadMechanism" xml:space="preserve">
|
||||
<value>SASL Mechanism is not supported-</value>
|
||||
<comment>Message Content</comment>
|
||||
</data>
|
||||
<data name="ALERT_ConnectionFailed" xml:space="preserve">
|
||||
<value>Connection failed</value>
|
||||
<comment>Message Title</comment>
|
||||
</data>
|
||||
<data name="ALERT_ConnectionTimeout" xml:space="preserve">
|
||||
<value>Connection time exceeded.</value>
|
||||
<comment>Message Content</comment>
|
||||
</data>
|
||||
<data name="ALERT_CredentialsInvalid" xml:space="preserve">
|
||||
<value>Credentials are invalid.</value>
|
||||
<comment>Message Content</comment>
|
||||
</data>
|
||||
<data name="ALERT_DuplicateConnection" xml:space="preserve">
|
||||
<value>Connection already exist. Please delete old Connection before adding the new Connection.</value>
|
||||
<comment>Message Content</comment>
|
||||
</data>
|
||||
<data name="ALERT_PasswordInvalid" xml:space="preserve">
|
||||
<value>Password is invalid.</value>
|
||||
<comment>Message Content</comment>
|
||||
</data>
|
||||
<data name="Alert_QRInvalid" xml:space="preserve">
|
||||
<data name="ALERT_QRInvalid" xml:space="preserve">
|
||||
<value>QR Code is invalid</value>
|
||||
<comment>Message Content</comment>
|
||||
</data>
|
||||
<data name="ALERT_SASLAuth" xml:space="preserve">
|
||||
<value>SASL Authenticaiton failed</value>
|
||||
<comment>Message Content</comment>
|
||||
</data>
|
||||
<data name="ALERT_TLSInvalid" xml:space="preserve">
|
||||
<value>TLS certificate is invalid.</value>
|
||||
<comment>Message Content</comment>
|
||||
</data>
|
||||
<data name="ALERT_UnableServer" xml:space="preserve">
|
||||
<value>Unable to connect to server.</value>
|
||||
<comment>Message Content</comment>
|
||||
</data>
|
||||
<data name="ALERT_UnexpectedError" xml:space="preserve">
|
||||
<value>Unexpected Error.</value>
|
||||
<comment>Message Content</comment>
|
||||
</data>
|
||||
<data name="ALERT_UserExist" xml:space="preserve">
|
||||
<value>User allready exist.</value>
|
||||
<comment>Message Content</comment>
|
||||
</data>
|
||||
<data name="ALERT_UsernameInvalid" xml:space="preserve">
|
||||
<value>Username is invalid.</value>
|
||||
<comment>Message Content</comment>
|
||||
</data>
|
||||
<data name="Bionade" xml:space="preserve">
|
||||
<value>It's Bionade</value>
|
||||
@ -194,6 +229,15 @@ You can also put down several servers and then connect to the desired server.</v
|
||||
<data name="CONFIRM" xml:space="preserve">
|
||||
<value>Confirm</value>
|
||||
</data>
|
||||
<data name="ConnectionStatus_Connecting" xml:space="preserve">
|
||||
<value>Connecting to Server ...</value>
|
||||
</data>
|
||||
<data name="ConnectionStatus_NoConnection" xml:space="preserve">
|
||||
<value>No Connection to Server</value>
|
||||
</data>
|
||||
<data name="ConnectionStatus_NotConnected" xml:space="preserve">
|
||||
<value>Please select a Server.</value>
|
||||
</data>
|
||||
<data name="DELETE" xml:space="preserve">
|
||||
<value>Delete</value>
|
||||
</data>
|
||||
@ -249,6 +293,9 @@ Ask in your Space if you can be trained on the machine to be unlocked for the ma
|
||||
<data name="MachinePage_OpenWiki" xml:space="preserve">
|
||||
<value>Open Wiki</value>
|
||||
</data>
|
||||
<data name="MachinePage_Unlock" xml:space="preserve">
|
||||
<value>Unlock</value>
|
||||
</data>
|
||||
<data name="MachinePage_Use" xml:space="preserve">
|
||||
<value>Use</value>
|
||||
</data>
|
||||
@ -279,9 +326,6 @@ Ask in your Space if you can be trained on the machine to be unlocked for the ma
|
||||
<data name="PASSWORD" xml:space="preserve">
|
||||
<value>Password</value>
|
||||
</data>
|
||||
<data name="PLEASECONNECTTOSERVER" xml:space="preserve">
|
||||
<value>Please connect to Server</value>
|
||||
</data>
|
||||
<data name="ProfilePage_ChangePassword" xml:space="preserve">
|
||||
<value>Change Password</value>
|
||||
</data>
|
||||
|
108
Borepin/Borepin/Service/ErrorMessage/ErrorMessageService.cs
Normal file
108
Borepin/Borepin/Service/ErrorMessage/ErrorMessageService.cs
Normal file
@ -0,0 +1,108 @@
|
||||
using Capnp.Rpc;
|
||||
using FabAccessAPI.Exceptions;
|
||||
using FabAccessAPI.Exceptions.SASL;
|
||||
using Prism.Services;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Borepin.Service.ErrorMessage
|
||||
{
|
||||
public class ErrorMessageService : IErrorMessageService
|
||||
{
|
||||
#region Private Members
|
||||
private IPageDialogService _PageDialogService;
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
public ErrorMessageService(IPageDialogService pageDialogService)
|
||||
{
|
||||
_PageDialogService = pageDialogService;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods to Display Connection Error
|
||||
|
||||
public void DisplayConnectFailedMessage(System.Exception exception)
|
||||
{
|
||||
if(exception is ConnectionException)
|
||||
{
|
||||
DisplayConnectionMessage(exception as ConnectionException);
|
||||
}
|
||||
else if(exception is AuthenticationException)
|
||||
{
|
||||
DisplayAuthenticationMessage(exception as AuthenticationException);
|
||||
}
|
||||
else
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(async () =>
|
||||
{
|
||||
await _PageDialogService.DisplayAlertAsync(Resources.Text.TextResource.ALERT_ConnectionFailed, Resources.Text.TextResource.ALERT_UnexpectedError, Resources.Text.TextResource.OK).ConfigureAwait(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void DisplayAuthenticationMessage(AuthenticationException exception)
|
||||
{
|
||||
if (exception.InnerException is BadMechanismException)
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(async () =>
|
||||
{
|
||||
await _PageDialogService.DisplayAlertAsync(Resources.Text.TextResource.ALERT_AuthFailed, Resources.Text.TextResource.ALERT_BadMechanism, Resources.Text.TextResource.OK).ConfigureAwait(false);
|
||||
});
|
||||
}
|
||||
else if (exception.InnerException is InvalidCredentialsException)
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(async () =>
|
||||
{
|
||||
await _PageDialogService.DisplayAlertAsync(Resources.Text.TextResource.ALERT_AuthFailed, Resources.Text.TextResource.ALERT_CredentialsInvalid, Resources.Text.TextResource.OK).ConfigureAwait(false);
|
||||
});
|
||||
}
|
||||
else if (exception.InnerException is AuthenticationFailedException)
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(async () =>
|
||||
{
|
||||
await _PageDialogService.DisplayAlertAsync(Resources.Text.TextResource.ALERT_AuthFailed, Resources.Text.TextResource.ALERT_SASLAuth, Resources.Text.TextResource.OK).ConfigureAwait(false);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(async () =>
|
||||
{
|
||||
await _PageDialogService.DisplayAlertAsync(Resources.Text.TextResource.ALERT_AuthFailed, Resources.Text.TextResource.ALERT_UnexpectedError, Resources.Text.TextResource.OK).ConfigureAwait(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void DisplayConnectionMessage(ConnectionException exception)
|
||||
{
|
||||
if(exception.InnerException is System.Security.Authentication.AuthenticationException)
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(async () =>
|
||||
{
|
||||
await _PageDialogService.DisplayAlertAsync(Resources.Text.TextResource.ALERT_ConnectionFailed, Resources.Text.TextResource.ALERT_TLSInvalid, Resources.Text.TextResource.OK).ConfigureAwait(false);
|
||||
});
|
||||
}
|
||||
else if(exception.InnerException is FabAccessAPI.Exceptions.TimeoutException)
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(async () =>
|
||||
{
|
||||
await _PageDialogService.DisplayAlertAsync(Resources.Text.TextResource.ALERT_ConnectionFailed, Resources.Text.TextResource.ALERT_ConnectionTimeout, Resources.Text.TextResource.OK).ConfigureAwait(false);
|
||||
});
|
||||
}
|
||||
else if(exception.InnerException is RpcException)
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(async () =>
|
||||
{
|
||||
await _PageDialogService.DisplayAlertAsync(Resources.Text.TextResource.ALERT_ConnectionFailed, Resources.Text.TextResource.ALERT_UnableServer, Resources.Text.TextResource.OK).ConfigureAwait(false);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(async () =>
|
||||
{
|
||||
await _PageDialogService.DisplayAlertAsync(Resources.Text.TextResource.ALERT_ConnectionFailed, Resources.Text.TextResource.ALERT_UnexpectedError, Resources.Text.TextResource.OK).ConfigureAwait(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
14
Borepin/Borepin/Service/ErrorMessage/IErrorMessageService.cs
Normal file
14
Borepin/Borepin/Service/ErrorMessage/IErrorMessageService.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using FabAccessAPI.Exceptions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Borepin.Service.ErrorMessage
|
||||
{
|
||||
public interface IErrorMessageService
|
||||
{
|
||||
void DisplayConnectFailedMessage(Exception exception);
|
||||
void DisplayConnectionMessage(ConnectionException exception);
|
||||
void DisplayAuthenticationMessage(AuthenticationException exception);
|
||||
}
|
||||
}
|
32
Borepin/Borepin/View/ConnectionStateView.xaml
Normal file
32
Borepin/Borepin/View/ConnectionStateView.xaml
Normal file
@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
x:Class="Borepin.View.ConnectionStateView"
|
||||
xmlns:converters="clr-namespace:Borepin.Converter"
|
||||
xmlns:resource_text="clr-namespace:Borepin.Resources.Text">
|
||||
<ContentView.Resources>
|
||||
<ResourceDictionary>
|
||||
<converters:AllTrueBoolConverter x:Key="AllTrueBoolConverter"/>
|
||||
<converters:InvertBoolConverter x:Key="InvertBoolConverter"/>
|
||||
</ResourceDictionary>
|
||||
</ContentView.Resources>
|
||||
<ContentView.Content>
|
||||
<StackLayout>
|
||||
<StackLayout IsVisible="{Binding IsBusy}">
|
||||
<ActivityIndicator IsRunning="{Binding IsBusy}"></ActivityIndicator>
|
||||
</StackLayout>
|
||||
|
||||
<StackLayout>
|
||||
<StackLayout.IsVisible>
|
||||
<MultiBinding Converter="{StaticResource AllTrueBoolConverter}">
|
||||
<Binding Path="IsBusy" Converter="{StaticResource InvertBoolConverter}"/>
|
||||
<Binding Path="IsConnected" Converter="{StaticResource InvertBoolConverter}"/>
|
||||
</MultiBinding>
|
||||
</StackLayout.IsVisible>
|
||||
<Label Text="{x:Static resource_text:TextResource.ConnectionStatus_NoConnection}"/>
|
||||
<Label Text="{x:Static resource_text:TextResource.ConnectionStatus_Connecting}" IsVisible="{Binding IsConnecting}"/>
|
||||
<Label Text="{x:Static resource_text:TextResource.ConnectionStatus_NotConnected}" IsVisible="{Binding IsConnecting, Converter={StaticResource InvertBoolConverter}}"/>
|
||||
</StackLayout>
|
||||
</StackLayout>
|
||||
</ContentView.Content>
|
||||
</ContentView>
|
20
Borepin/Borepin/View/ConnectionStateView.xaml.cs
Normal file
20
Borepin/Borepin/View/ConnectionStateView.xaml.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Xaml;
|
||||
|
||||
namespace Borepin.View
|
||||
{
|
||||
[XamlCompilation(XamlCompilationOptions.Compile)]
|
||||
public partial class ConnectionStateView : ContentView
|
||||
{
|
||||
public ConnectionStateView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
using Capnp.Rpc;
|
||||
using FabAccessAPI.Exceptions;
|
||||
using FabAccessAPI.Exceptions.SASL;
|
||||
using FabAccessAPI.Schema;
|
||||
using NLog;
|
||||
using S22.Sasl;
|
||||
@ -7,7 +8,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Security;
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@ -21,49 +21,76 @@ namespace FabAccessAPI
|
||||
#endregion
|
||||
|
||||
#region Private Members
|
||||
/// <summary>
|
||||
/// Internal client to connect to a server with TCP and RPC
|
||||
/// </summary>
|
||||
private TcpRpcClient _TcpRpcClient;
|
||||
|
||||
/// <summary>
|
||||
/// Private ConnectionData
|
||||
/// </summary>
|
||||
private ConnectionData _ConnectionData;
|
||||
|
||||
/// <summary>
|
||||
/// Private ServerData
|
||||
/// </summary>
|
||||
private ServerData _ServerData;
|
||||
|
||||
/// <summary>
|
||||
/// Private Session
|
||||
/// </summary>
|
||||
private Session _Session;
|
||||
|
||||
/// <summary>
|
||||
/// Private Bootstrap
|
||||
/// </summary>
|
||||
private IBootstrap _Bootstrap;
|
||||
|
||||
/// <summary>
|
||||
/// Timer to check connection status
|
||||
/// </summary>
|
||||
private readonly Timer _ConnectionHeatbeat;
|
||||
|
||||
/// <summary>
|
||||
/// Semaphore to connect only once
|
||||
/// </summary>
|
||||
private static SemaphoreSlim _ConnectSemaphore = new SemaphoreSlim(1, 1);
|
||||
private static SemaphoreSlim _ReconnectSemaphore = new SemaphoreSlim(1, 1);
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public API()
|
||||
{
|
||||
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
public event EventHandler<ConnectionStatusChange> ConnectionStatusChanged;
|
||||
|
||||
public void OnTcpRpcConnectionChanged(object sender, ConnectionStateChange args)
|
||||
{
|
||||
if (args.LastState == ConnectionState.Active && args.NewState == ConnectionState.Down)
|
||||
{
|
||||
Log.Trace("TcpRpcClient Event ConnectionLoss");
|
||||
ConnectionStatusChanged?.Invoke(this, ConnectionStatusChange.ConnectionLoss);
|
||||
_TcpRpcClient = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void UnbindAllEvents()
|
||||
{
|
||||
if(ConnectionStatusChanged != null)
|
||||
{
|
||||
foreach (Delegate d in ConnectionStatusChanged.GetInvocationList())
|
||||
{
|
||||
ConnectionStatusChanged -= (EventHandler<ConnectionStatusChange>)d;
|
||||
}
|
||||
}
|
||||
_ConnectionHeatbeat = new Timer(Heartbeat, null, 1000, 1000);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Members
|
||||
public ConnectionData ConnectionData { get; private set; }
|
||||
/// <summary>
|
||||
/// State of the conneciton, can the API-Service connect to a server
|
||||
/// </summary>
|
||||
public bool CanConnect
|
||||
{
|
||||
get
|
||||
{
|
||||
return _ConnectionData != null;
|
||||
}
|
||||
}
|
||||
|
||||
public ConnectionInfo ConnectionInfo { get; private set; }
|
||||
/// <summary>
|
||||
/// State of the conneciton, is the API-Service connecting to a server
|
||||
/// </summary>
|
||||
public bool IsConnecting
|
||||
{
|
||||
get
|
||||
{
|
||||
return _TcpRpcClient != null && _ConnectionData != null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// State of the conneciton, is the API-Service connected to a server
|
||||
/// </summary>
|
||||
public bool IsConnected
|
||||
{
|
||||
get
|
||||
@ -72,15 +99,124 @@ namespace FabAccessAPI
|
||||
}
|
||||
}
|
||||
|
||||
public Session Session { get; private set; }
|
||||
/// <summary>
|
||||
/// Information about the connection
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException"> When API-Service is not connected or trying to connected to a server </exception>
|
||||
public ConnectionData ConnectionData
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_ConnectionData == null || !IsConnecting)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
else
|
||||
{
|
||||
return _ConnectionData;
|
||||
}
|
||||
}
|
||||
private set
|
||||
{
|
||||
_ConnectionData = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Information about the server
|
||||
/// Is only avalible if the API-Service is connected
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException"> When API-Service is not connected </exception>
|
||||
public ServerData ServerData
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_ServerData == null || !IsConnected)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
else
|
||||
{
|
||||
return _ServerData;
|
||||
}
|
||||
}
|
||||
private set
|
||||
{
|
||||
_ServerData = value;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
/// <summary>
|
||||
/// Event on changes in connection status
|
||||
/// </summary>
|
||||
public event EventHandler<ConnectionStatusChanged> ConnectionStatusChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Unbind all handlers from EventHandler<ConnectionStatusChanged>
|
||||
/// </summary>
|
||||
public void UnbindEventHandler()
|
||||
{
|
||||
if (ConnectionStatusChanged != null)
|
||||
{
|
||||
Log.Trace("Eventhandlers unbinded");
|
||||
foreach (Delegate d in ConnectionStatusChanged.GetInvocationList())
|
||||
{
|
||||
ConnectionStatusChanged -= (EventHandler<ConnectionStatusChanged>)d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Eventhandler for TcpRpcConnectionChanged
|
||||
/// Track connection loss and publish i in ConnectionStatusChanged
|
||||
/// </summary>
|
||||
public void OnTcpRpcConnectionChanged(object sender, ConnectionStateChange args)
|
||||
{
|
||||
if (args.LastState == ConnectionState.Active && args.NewState == ConnectionState.Down)
|
||||
{
|
||||
Log.Trace("TcpRpcClient Event ConnectionLoss");
|
||||
ConnectionStatusChanged?.Invoke(this, FabAccessAPI.ConnectionStatusChanged.ConnectionLoss);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Session
|
||||
/// <summary>
|
||||
/// Get session after connection
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException"> When API-Service is not connected </exception>
|
||||
public Session Session
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_Session == null || !IsConnected)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
else
|
||||
{
|
||||
return _Session;
|
||||
}
|
||||
}
|
||||
private set
|
||||
{
|
||||
_Session = value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// Connect to server with ConnectionData
|
||||
/// If connection lost, the API-Server will try to reconnect
|
||||
/// </summary>
|
||||
/// <exception cref="AuthenticationException"></exception>
|
||||
/// <exception cref="ConnectingFailedException"></exception>
|
||||
/// <param name="connectionData"> Data to establish a connection to a server </param>
|
||||
/// <exception cref="ConnectionException"> When API-Service can not connect to a server </exception>
|
||||
/// <exception cref="AuthenticationException"> When API-Service can connect to a server but can not authenticate </exception>
|
||||
/// <exception cref="InvalidOperationException"> When API-Service is allready connected </exception>
|
||||
public async Task Connect(ConnectionData connectionData, TcpRpcClient tcpRpcClient = null)
|
||||
{
|
||||
await _ConnectSemaphore.WaitAsync();
|
||||
@ -88,7 +224,8 @@ namespace FabAccessAPI
|
||||
{
|
||||
if (IsConnected)
|
||||
{
|
||||
await Disconnect();
|
||||
Log.Warn("API already connected");
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
if (tcpRpcClient == null)
|
||||
@ -101,20 +238,19 @@ namespace FabAccessAPI
|
||||
await _ConnectAsync(tcpRpcClient, connectionData).ConfigureAwait(false);
|
||||
|
||||
_Bootstrap = tcpRpcClient.GetMain<IBootstrap>();
|
||||
ConnectionInfo = await _GetConnectionInfo(_Bootstrap);
|
||||
ServerData = await _GetServerData(_Bootstrap);
|
||||
|
||||
Session = await _Authenticate(connectionData).ConfigureAwait(false);
|
||||
ConnectionData = connectionData;
|
||||
|
||||
_TcpRpcClient = tcpRpcClient;
|
||||
tcpRpcClient.ConnectionStateChanged += OnTcpRpcConnectionChanged;
|
||||
ConnectionStatusChanged?.Invoke(this, ConnectionStatusChange.Connected);
|
||||
ConnectionStatusChanged?.Invoke(this, FabAccessAPI.ConnectionStatusChanged.Connected);
|
||||
Log.Info("API connected");
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
await Disconnect().ConfigureAwait(false);
|
||||
Log.Warn(ex, "API connecting failed");
|
||||
Log.Warn(ex, "API connect failed");
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
@ -124,91 +260,89 @@ namespace FabAccessAPI
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disconnect from a server
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException"> When API-Service is not connected or trying to connect </exception>
|
||||
public Task Disconnect()
|
||||
{
|
||||
if (IsConnected)
|
||||
{
|
||||
_TcpRpcClient.Dispose();
|
||||
}
|
||||
|
||||
_Bootstrap = null;
|
||||
Session = null;
|
||||
_TcpRpcClient = null;
|
||||
ConnectionData = null;
|
||||
ConnectionInfo = null;
|
||||
|
||||
ConnectionStatusChanged?.Invoke(this, ConnectionStatusChange.Disconnected);
|
||||
_Bootstrap = null;
|
||||
_TcpRpcClient = null;
|
||||
|
||||
Session = null;
|
||||
ConnectionData = null;
|
||||
ServerData = null;
|
||||
|
||||
ConnectionStatusChanged?.Invoke(this, FabAccessAPI.ConnectionStatusChanged.Disconnected);
|
||||
|
||||
Log.Info("API disconnected");
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task Reconnect()
|
||||
/// <summary>
|
||||
/// Try to connect to a server and get ServerData
|
||||
/// The connection is not maintained
|
||||
/// </summary>
|
||||
/// <exception cref="ConnectionException"> When API-Service can not connect to a server </exception>
|
||||
public async Task<ServerData> TryToConnect(ConnectionData connectionData, TcpRpcClient tcpRpcClient = null)
|
||||
{
|
||||
await _ReconnectSemaphore.WaitAsync();
|
||||
try
|
||||
if (tcpRpcClient == null)
|
||||
{
|
||||
if (ConnectionData != null && IsConnected == false)
|
||||
{
|
||||
await Connect(ConnectionData);
|
||||
}
|
||||
tcpRpcClient = new TcpRpcClient();
|
||||
}
|
||||
|
||||
ConnectionStatusChanged?.Invoke(this, ConnectionStatusChange.Reconnected);
|
||||
Log.Info("API reconnected");
|
||||
}
|
||||
finally
|
||||
{
|
||||
_ReconnectSemaphore.Release();
|
||||
}
|
||||
|
||||
await _ConnectAsync(tcpRpcClient, connectionData).ConfigureAwait(false);
|
||||
IBootstrap bootstrap = tcpRpcClient.GetMain<IBootstrap>();
|
||||
|
||||
ServerData serverData = await _GetServerData(bootstrap).ConfigureAwait(false);
|
||||
|
||||
tcpRpcClient.Dispose();
|
||||
|
||||
return serverData;
|
||||
}
|
||||
|
||||
public async Task<ConnectionInfo> TestConnection(ConnectionData connectionData, TcpRpcClient tcpRpcClient = null)
|
||||
/// <summary>
|
||||
/// Public Wrapper to run HeartbeatAsync
|
||||
/// </summary>
|
||||
public void Heartbeat(object state)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (tcpRpcClient == null)
|
||||
{
|
||||
tcpRpcClient = new TcpRpcClient();
|
||||
}
|
||||
|
||||
await _ConnectAsync(tcpRpcClient, connectionData).ConfigureAwait(false);
|
||||
IBootstrap testBootstrap = tcpRpcClient.GetMain<IBootstrap>();
|
||||
|
||||
ConnectionInfo connectionInfo = await _GetConnectionInfo(testBootstrap).ConfigureAwait(false);
|
||||
|
||||
tcpRpcClient.Dispose();
|
||||
|
||||
return connectionInfo;
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw new ConnectingFailedException();
|
||||
}
|
||||
_ = HeartbeatAsync();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
|
||||
private async Task HeartbeatAsync()
|
||||
{
|
||||
if(!IsConnected && CanConnect)
|
||||
{
|
||||
await Connect(ConnectionData).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validate Certificate
|
||||
/// TODO: Do some validation
|
||||
/// </summary>
|
||||
private static bool _RemoteCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
|
||||
private bool _RemoteCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
|
||||
{
|
||||
// TODO Cert Check
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Connect to server async with ConnectionData
|
||||
/// Connect async to a server with ConnectionData
|
||||
/// </summary>
|
||||
/// <exception cref="AuthenticationException">TLS Error</exception>
|
||||
/// <exception cref="ConnectingFailedException">Based on RPC Exception</exception>
|
||||
///
|
||||
private async Task _ConnectAsync(TcpRpcClient rpcClient, ConnectionData connectionData)
|
||||
/// <exception cref="ConnectionException">Based on RPC Exception</exception>
|
||||
private async Task _ConnectAsync(TcpRpcClient tcprpcClient, ConnectionData connectionData)
|
||||
{
|
||||
rpcClient.InjectMidlayer((tcpstream) =>
|
||||
tcprpcClient.InjectMidlayer((tcpstream) =>
|
||||
{
|
||||
var sslStream = new SslStream(tcpstream, false, new RemoteCertificateValidationCallback(_RemoteCertificateValidationCallback));
|
||||
try
|
||||
@ -216,61 +350,58 @@ namespace FabAccessAPI
|
||||
sslStream.AuthenticateAsClient("bffhd");
|
||||
return sslStream;
|
||||
}
|
||||
catch (AuthenticationException)
|
||||
catch (System.Security.Authentication.AuthenticationException exception)
|
||||
{
|
||||
sslStream.Close();
|
||||
throw;
|
||||
Log.Warn(exception);
|
||||
throw new ConnectionException("TLS failed", exception);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
Task timeoutTask = Task.Delay(3000);
|
||||
rpcClient.Connect(connectionData.Host.Host, connectionData.Host.Port);
|
||||
await await Task.WhenAny(rpcClient.WhenConnected, timeoutTask);
|
||||
|
||||
if(timeoutTask.IsCompleted)
|
||||
tcprpcClient.Connect(connectionData.Host.Host, connectionData.Host.Port);
|
||||
await await Task.WhenAny(tcprpcClient.WhenConnected, timeoutTask);
|
||||
|
||||
if (timeoutTask.IsCompleted)
|
||||
{
|
||||
throw new ConnectingFailedException("Connection timeout");
|
||||
Exceptions.TimeoutException timeoutException = new Exceptions.TimeoutException();
|
||||
Log.Warn(timeoutException);
|
||||
throw new ConnectionException("Connection timeout", timeoutException);
|
||||
}
|
||||
}
|
||||
catch (RpcException exception) when (string.Equals(exception.Message, "TcpRpcClient is unable to connect", StringComparison.Ordinal))
|
||||
{
|
||||
throw new ConnectingFailedException("RPC Connecting failed", exception);
|
||||
Log.Warn(exception);
|
||||
throw new ConnectionException("RPC Connecting failed", exception);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create ConnectionInfo from Bootstrap
|
||||
/// </summary>
|
||||
private async Task<ConnectionInfo> _GetConnectionInfo(IBootstrap bootstrap)
|
||||
{
|
||||
ConnectionInfo connectionInfo = new ConnectionInfo()
|
||||
{
|
||||
APIVersion = await bootstrap.GetAPIVersion().ConfigureAwait(false),
|
||||
Mechanisms = new List<string>(await bootstrap.Mechanisms().ConfigureAwait(false)),
|
||||
ServerName = (await bootstrap.GetServerRelease().ConfigureAwait(false)).Item1,
|
||||
ServerRelease = (await bootstrap.GetServerRelease().ConfigureAwait(false)).Item2,
|
||||
};
|
||||
|
||||
return connectionInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Authenticate connection with ConnectionData
|
||||
/// </summary>
|
||||
/// <exception cref="UnsupportedMechanismException"></exception>
|
||||
/// <exception cref="InvalidCredentialsException"></exception>
|
||||
/// <exception cref="AuthenticationFailedException"></exception>
|
||||
/// <param name="connectionData"> Data to establish a connection to a server </param>
|
||||
/// <exception cref="AuthenticationException"></exception>
|
||||
private async Task<Session> _Authenticate(ConnectionData connectionData)
|
||||
{
|
||||
IAuthentication? authentication = await _Bootstrap.CreateSession(MechanismString.ToString(connectionData.Mechanism)).ConfigureAwait(false);
|
||||
IAuthentication? authentication = await _Bootstrap.CreateSession(SASLMechanism.ToString(connectionData.Mechanism)).ConfigureAwait(false);
|
||||
|
||||
return await _SASLAuthenticate(authentication, MechanismString.ToString(connectionData.Mechanism), connectionData.Properties).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
return await _SASLAuthenticate(authentication, SASLMechanism.ToString(connectionData.Mechanism), connectionData.Properties).ConfigureAwait(false);
|
||||
}
|
||||
catch (System.Exception exception)
|
||||
{
|
||||
Log.Warn(exception, "API authenticating failed");
|
||||
AuthenticationException authenticationException = new AuthenticationException("Authentication failed", exception);
|
||||
|
||||
throw authenticationException;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Authenticate Connection to get Session
|
||||
/// Authenticate with SASL
|
||||
/// </summary>
|
||||
/// <exception cref="BadMechanismException"></exception>
|
||||
/// <exception cref="InvalidCredentialsException"></exception>
|
||||
@ -293,11 +424,11 @@ namespace FabAccessAPI
|
||||
Response? response = await authentication.Step(data);
|
||||
while (!saslMechanism.IsCompleted)
|
||||
{
|
||||
if(response.Failed != null)
|
||||
if (response.Failed != null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if(response.Challenge != null)
|
||||
if (response.Challenge != null)
|
||||
{
|
||||
byte[]? additional = saslMechanism.GetResponse(response.Challenge.ToArray());
|
||||
response = await authentication.Step(additional);
|
||||
@ -331,6 +462,22 @@ namespace FabAccessAPI
|
||||
throw new AuthenticationFailedException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get ServerData from server with tcprpcconnection
|
||||
/// </summary>
|
||||
private async Task<ServerData> _GetServerData(IBootstrap bootstrap)
|
||||
{
|
||||
ServerData serverData = new ServerData()
|
||||
{
|
||||
APIVersion = await bootstrap.GetAPIVersion().ConfigureAwait(false),
|
||||
Mechanisms = new List<string>(await bootstrap.Mechanisms().ConfigureAwait(false)),
|
||||
ServerName = (await bootstrap.GetServerRelease().ConfigureAwait(false)).Item1,
|
||||
ServerRelease = (await bootstrap.GetServerRelease().ConfigureAwait(false)).Item2,
|
||||
};
|
||||
|
||||
return serverData;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
@ -3,12 +3,18 @@ using System.Collections.Generic;
|
||||
|
||||
namespace FabAccessAPI
|
||||
{
|
||||
/// <summary>
|
||||
/// Data to establish a connection to a server
|
||||
/// With Data for Authentication
|
||||
/// </summary>
|
||||
public class ConnectionData
|
||||
{
|
||||
public Uri Host;
|
||||
public Mechanism Mechanism;
|
||||
public string Username;
|
||||
|
||||
public SASLMechanismEnum Mechanism;
|
||||
public Dictionary<string, object> Properties;
|
||||
|
||||
public DateTime LastTime;
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
|
@ -1,23 +0,0 @@
|
||||
namespace FabAccessAPI
|
||||
{
|
||||
public enum ConnectionStatusChange
|
||||
{
|
||||
/// <summary>
|
||||
/// Client has established connection to server.
|
||||
/// </summary>
|
||||
Connected,
|
||||
/// <summary>
|
||||
/// Client has closed connection to server.
|
||||
/// </summary>
|
||||
Disconnected,
|
||||
/// <summary>
|
||||
/// Connection was lost and Client has reestablished connection to server.
|
||||
/// </summary>
|
||||
Reconnected,
|
||||
/// <summary>
|
||||
/// Connection was lost and can be reestablished with reconnect
|
||||
/// Connection should be closed if reconnecting fails.
|
||||
/// </summary>
|
||||
ConnectionLoss
|
||||
}
|
||||
}
|
20
FabAccessAPI/ConnectionStatusChanged.cs
Normal file
20
FabAccessAPI/ConnectionStatusChanged.cs
Normal file
@ -0,0 +1,20 @@
|
||||
namespace FabAccessAPI
|
||||
{
|
||||
public enum ConnectionStatusChanged
|
||||
{
|
||||
/// <summary>
|
||||
/// API-Service has established connection to server
|
||||
/// </summary>
|
||||
Connected,
|
||||
|
||||
/// <summary>
|
||||
/// API-Service has closed the connection to a server
|
||||
/// </summary>
|
||||
Disconnected,
|
||||
|
||||
/// <summary>
|
||||
/// Connection was lost and the API-Service will try to reconnect automatically
|
||||
/// </summary>
|
||||
ConnectionLoss
|
||||
}
|
||||
}
|
26
FabAccessAPI/Exceptions/AuthenticationException.cs
Normal file
26
FabAccessAPI/Exceptions/AuthenticationException.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using System;
|
||||
|
||||
namespace FabAccessAPI.Exceptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Authenticating to a server has failed
|
||||
/// InnerException will provide more information
|
||||
/// </summary>
|
||||
public class AuthenticationException : Exception
|
||||
{
|
||||
public AuthenticationException()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public AuthenticationException(string message) : base(message)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public AuthenticationException(string message, Exception inner) : base(message, inner)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace FabAccessAPI.Exceptions
|
||||
{
|
||||
public class ConnectingFailedException : Exception
|
||||
{
|
||||
public ConnectingFailedException()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public ConnectingFailedException(string message) : base(message)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public ConnectingFailedException(string message, Exception inner) : base(message, inner)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
26
FabAccessAPI/Exceptions/ConnectionException.cs
Normal file
26
FabAccessAPI/Exceptions/ConnectionException.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using System;
|
||||
|
||||
namespace FabAccessAPI.Exceptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Connecting to a server has failed
|
||||
/// InnerException will provide more information
|
||||
/// </summary>
|
||||
public class ConnectionException : Exception
|
||||
{
|
||||
public ConnectionException()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public ConnectionException(string message) : base(message)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public ConnectionException(string message, Exception inner) : base(message, inner)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
using System;
|
||||
|
||||
namespace FabAccessAPI.Exceptions
|
||||
namespace FabAccessAPI.Exceptions.SASL
|
||||
{
|
||||
public class AuthenticationFailedException : Exception
|
||||
{
|
@ -1,6 +1,6 @@
|
||||
using System;
|
||||
|
||||
namespace FabAccessAPI.Exceptions
|
||||
namespace FabAccessAPI.Exceptions.SASL
|
||||
{
|
||||
public class BadMechanismException : Exception
|
||||
{
|
@ -1,6 +1,6 @@
|
||||
using System;
|
||||
|
||||
namespace FabAccessAPI.Exceptions
|
||||
namespace FabAccessAPI.Exceptions.SASL
|
||||
{
|
||||
public class InvalidCredentialsException : Exception
|
||||
{
|
25
FabAccessAPI/Exceptions/TimeoutException.cs
Normal file
25
FabAccessAPI/Exceptions/TimeoutException.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using System;
|
||||
|
||||
namespace FabAccessAPI.Exceptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Timeout on Connection
|
||||
/// </summary>
|
||||
public class TimeoutException : Exception
|
||||
{
|
||||
public TimeoutException()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public TimeoutException(string message) : base(message)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public TimeoutException(string message, Exception inner) : base(message, inner)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -1,63 +1,84 @@
|
||||
using Capnp.Rpc;
|
||||
using FabAccessAPI.Schema;
|
||||
using System;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Capnp.Rpc;
|
||||
using FabAccessAPI.Exceptions;
|
||||
using FabAccessAPI.Schema;
|
||||
|
||||
namespace FabAccessAPI
|
||||
{
|
||||
/// <summary>
|
||||
/// Service to connect to a server and maintain the connection
|
||||
/// </summary>
|
||||
public interface IAPI
|
||||
{
|
||||
#region Information about a connection and the server
|
||||
/// <summary>
|
||||
/// Data to establish connection.
|
||||
/// State of the conneciton, is the API-Service connecting to a server
|
||||
/// </summary>
|
||||
/// Without SecretProperties
|
||||
ConnectionData ConnectionData { get; }
|
||||
bool IsConnecting { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Information about the established connection.
|
||||
/// </summary>
|
||||
ConnectionInfo ConnectionInfo { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Is API connected?
|
||||
/// State of the conneciton, is the API-Service connected to a server
|
||||
/// </summary>
|
||||
bool IsConnected { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get session when connection is established
|
||||
/// Information about the connection
|
||||
/// </summary>
|
||||
Session Session { get; }
|
||||
/// <exception cref="InvalidOperationException"> When API-Service is not connected or trying to connected to a server </exception>
|
||||
ConnectionData ConnectionData { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Event on changes in connection state.
|
||||
/// Information about the server
|
||||
/// Is only avalible if the API-Service is connected
|
||||
/// </summary>
|
||||
event EventHandler<ConnectionStatusChange> ConnectionStatusChanged;
|
||||
/// <exception cref="InvalidOperationException"> When API-Service is not connected </exception>
|
||||
ServerData ServerData { get; }
|
||||
#endregion
|
||||
|
||||
#region Methods to connect to server
|
||||
/// <summary>
|
||||
/// Unbind all Events from ConnectionStatus Change
|
||||
/// Connect to server with ConnectionData
|
||||
/// If connection lost, the API-Server will try to reconnect
|
||||
/// </summary>
|
||||
void UnbindAllEvents();
|
||||
|
||||
/// <summary>
|
||||
/// Connect to BFFH Server
|
||||
/// </summary>
|
||||
/// <param name="connectionData"></param>
|
||||
/// <param name="connectionData"> Data to establish a connection to a server </param>
|
||||
/// <exception cref="ConnectionException"> When API-Service can not connect to a server </exception>
|
||||
/// <exception cref="AuthenticationException"> When API-Service can connect to a server but can not authenticate </exception>
|
||||
/// <exception cref="InvalidOperationException"> When API-Service is allready connected </exception>
|
||||
Task Connect(ConnectionData connectionData, TcpRpcClient tcpRpcClient = null);
|
||||
|
||||
/// <summary>
|
||||
/// Disconnect from BFFH Server
|
||||
/// Disconnect from a server
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException"> When API-Service is not connected or trying to connect </exception>
|
||||
Task Disconnect();
|
||||
|
||||
/// <summary>
|
||||
/// Reconnect after connection loss with the last ConnectionData
|
||||
/// Try to connect to a server and get ServerData
|
||||
/// The connection is not maintained
|
||||
/// </summary>
|
||||
Task Reconnect();
|
||||
/// <exception cref="ConnectionException"> When API-Service can not connect to a server </exception>
|
||||
Task<ServerData> TryToConnect(ConnectionData connectionData, TcpRpcClient tcpRpcClient = null);
|
||||
#endregion
|
||||
|
||||
#region Session
|
||||
/// <summary>
|
||||
/// Get session after connection
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException"> When API-Service is not connected </exception>
|
||||
Session Session { get; }
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
/// <summary>
|
||||
/// Event on changes in connection status
|
||||
/// </summary>
|
||||
event EventHandler<ConnectionStatusChanged> ConnectionStatusChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Connect to Server and get ConnectionInfo.
|
||||
/// The Connection is not maintained.
|
||||
/// Unbind all handlers from EventHandler<ConnectionStatusChanged>
|
||||
/// </summary>
|
||||
Task<ConnectionInfo> TestConnection(ConnectionData connectionData, TcpRpcClient tcpRpcClient = null);
|
||||
void UnbindEventHandler();
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
@ -1,24 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace FabAccessAPI
|
||||
{
|
||||
public enum Mechanism
|
||||
{
|
||||
PLAIN,
|
||||
}
|
||||
|
||||
public static class MechanismString
|
||||
{
|
||||
public static string ToString(Mechanism mechanism)
|
||||
{
|
||||
switch(mechanism)
|
||||
{
|
||||
case Mechanism.PLAIN:
|
||||
return "PLAIN";
|
||||
default:
|
||||
throw new ArgumentException("Mechanism not known.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
24
FabAccessAPI/SASLMechanismEnum.cs
Normal file
24
FabAccessAPI/SASLMechanismEnum.cs
Normal file
@ -0,0 +1,24 @@
|
||||
using System;
|
||||
|
||||
namespace FabAccessAPI
|
||||
{
|
||||
public enum SASLMechanismEnum
|
||||
{
|
||||
PLAIN,
|
||||
}
|
||||
|
||||
public static class SASLMechanism
|
||||
{
|
||||
public static string ToString(SASLMechanismEnum mechanism)
|
||||
{
|
||||
switch(mechanism)
|
||||
{
|
||||
case SASLMechanismEnum.PLAIN:
|
||||
return "PLAIN";
|
||||
default:
|
||||
throw new ArgumentException("Mechanism unknown.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -3,7 +3,7 @@ using System.Collections.Generic;
|
||||
|
||||
namespace FabAccessAPI
|
||||
{
|
||||
public class ConnectionInfo
|
||||
public class ServerData
|
||||
{
|
||||
public Schema.Version APIVersion;
|
||||
public string ServerName;
|
@ -1,6 +1,7 @@
|
||||
using Capnp.Rpc;
|
||||
using FabAccessAPI;
|
||||
using FabAccessAPI.Exceptions;
|
||||
using FabAccessAPI.Exceptions.SASL;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@ -19,7 +20,7 @@ namespace FabAccessAPI_Test
|
||||
ConnectionData connectionData = new ConnectionData()
|
||||
{
|
||||
Host = new UriBuilder(TestEnv.SCHEMA, "UnkownHost" + TestEnv.TESTSERVER, TestEnv.TESTSERVER_PORT).Uri,
|
||||
Mechanism = Mechanism.PLAIN,
|
||||
Mechanism = SASLMechanismEnum.PLAIN,
|
||||
Username = "UnkownUser",
|
||||
Properties = new Dictionary<string, object>()
|
||||
{
|
||||
@ -32,14 +33,14 @@ namespace FabAccessAPI_Test
|
||||
{
|
||||
await api.Connect(connectionData);
|
||||
}
|
||||
catch (ConnectingFailedException)
|
||||
catch (ConnectionException)
|
||||
{
|
||||
Assert.Pass();
|
||||
}
|
||||
Assert.Fail();
|
||||
}
|
||||
|
||||
[Test, Ignore("")]
|
||||
[Test]
|
||||
public async Task Connect_InvalidCredentials()
|
||||
{
|
||||
API api = new API();
|
||||
@ -50,7 +51,7 @@ namespace FabAccessAPI_Test
|
||||
{
|
||||
await api.Connect(connectionData);
|
||||
}
|
||||
catch(InvalidCredentialsException)
|
||||
catch(AuthenticationException exception) when (exception.InnerException is InvalidCredentialsException)
|
||||
{
|
||||
Assert.Pass();
|
||||
}
|
||||
@ -68,11 +69,11 @@ namespace FabAccessAPI_Test
|
||||
bool event_Disconnected = false;
|
||||
api.ConnectionStatusChanged += (sender, eventArgs) =>
|
||||
{
|
||||
if (eventArgs == ConnectionStatusChange.Connected)
|
||||
if (eventArgs == ConnectionStatusChanged.Connected)
|
||||
{
|
||||
event_Connected = true;
|
||||
}
|
||||
if(eventArgs == ConnectionStatusChange.Disconnected)
|
||||
if(eventArgs == ConnectionStatusChanged.Disconnected)
|
||||
{
|
||||
event_Disconnected = true;
|
||||
}
|
||||
@ -82,7 +83,7 @@ namespace FabAccessAPI_Test
|
||||
|
||||
bool HasSesion = api.Session != null;
|
||||
bool HasConnectionData = api.ConnectionData != null;
|
||||
bool HasConnectionInfo = api.ConnectionInfo != null;
|
||||
bool HasServerData = api.ServerData != null;
|
||||
bool IsConnected = api.IsConnected;
|
||||
|
||||
await api.Disconnect();
|
||||
@ -95,7 +96,7 @@ namespace FabAccessAPI_Test
|
||||
|
||||
Assert.IsTrue(HasSesion, "HasSesion");
|
||||
Assert.IsTrue(HasConnectionData, "HasConnectionData");
|
||||
Assert.IsTrue(HasConnectionInfo, "HasConnectionInfo");
|
||||
Assert.IsTrue(HasServerData, "HasServerData");
|
||||
Assert.IsTrue(IsConnected, "IsConnected");
|
||||
|
||||
Assert.IsFalse(api.IsConnected, "api.IsConnected");
|
||||
@ -109,9 +110,9 @@ namespace FabAccessAPI_Test
|
||||
|
||||
ConnectionData connectionData = TestEnv.CreateConnetionData(username);
|
||||
|
||||
ConnectionInfo connectionInfo = await api.TestConnection(connectionData);
|
||||
ServerData serverData = await api.TryToConnect(connectionData);
|
||||
|
||||
Assert.IsNotNull(connectionInfo);
|
||||
Assert.IsNotNull(serverData);
|
||||
}
|
||||
|
||||
[TestCase("Admin1"), Explicit]
|
||||
@ -121,28 +122,23 @@ namespace FabAccessAPI_Test
|
||||
|
||||
ConnectionData connectionData = TestEnv.CreateConnetionData(username);
|
||||
|
||||
bool event_Connected = false;
|
||||
bool event_ConnectionLoss = false;
|
||||
bool event_Reconnect = false;
|
||||
bool event_Disconnected = false;
|
||||
int event_Connected = 0;
|
||||
int event_ConnectionLoss = 0;
|
||||
int event_Disconnected = 0;
|
||||
|
||||
api.ConnectionStatusChanged += (sender, eventArgs) =>
|
||||
{
|
||||
if (eventArgs == ConnectionStatusChange.Connected)
|
||||
if (eventArgs == ConnectionStatusChanged.Connected)
|
||||
{
|
||||
event_Connected = true;
|
||||
event_Connected++;
|
||||
}
|
||||
if (eventArgs == ConnectionStatusChange.ConnectionLoss)
|
||||
if (eventArgs == ConnectionStatusChanged.ConnectionLoss)
|
||||
{
|
||||
event_ConnectionLoss = true;
|
||||
event_ConnectionLoss++;
|
||||
}
|
||||
if (eventArgs == ConnectionStatusChange.Reconnected)
|
||||
if (eventArgs == ConnectionStatusChanged.Disconnected)
|
||||
{
|
||||
event_Reconnect = true;
|
||||
}
|
||||
if (eventArgs == ConnectionStatusChange.Disconnected)
|
||||
{
|
||||
event_Disconnected = true;
|
||||
event_Disconnected++;
|
||||
}
|
||||
};
|
||||
|
||||
@ -161,16 +157,14 @@ namespace FabAccessAPI_Test
|
||||
Thread.Sleep(3000);
|
||||
|
||||
// Stop here and connect with internet again
|
||||
await api.Reconnect();
|
||||
await api.Disconnect();
|
||||
|
||||
Thread.Sleep(3000);
|
||||
Thread.Sleep(1000);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.IsTrue(event_Connected, "event_Connected");
|
||||
Assert.IsTrue(event_ConnectionLoss, "event_ConnectionLoss");
|
||||
Assert.IsTrue(event_Reconnect, "event_Reconnect");
|
||||
Assert.IsTrue(event_Disconnected, "event_Disconnected");
|
||||
Assert.AreEqual(2, event_Connected, "event_Connected");
|
||||
Assert.AreEqual(1, event_ConnectionLoss, "event_ConnectionLoss");
|
||||
Assert.AreEqual(1, event_Disconnected, "event_Disconnected");
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,7 @@ namespace FabAccessAPI_Test.API_TestEnv
|
||||
|
||||
[TestCase("Admin1", "NewMakerA1", "UseA", "ReadA", "DiscloseA")]
|
||||
[Order(1)]
|
||||
[Ignore("Deprecated")]
|
||||
public async Task AddRoles(string username, string username2, params string[] roles)
|
||||
{
|
||||
API api = new API();
|
||||
|
@ -7,8 +7,8 @@ namespace FabAccessAPI_Test
|
||||
public static class TestEnv
|
||||
{
|
||||
public const string SCHEMA = "fabaccess";
|
||||
public const string TESTSERVER = "127.0.0.1";//"bffh.lab.bln.kjknet.de";
|
||||
public const int TESTSERVER_PORT = 59666;
|
||||
public const string TESTSERVER = "127.0.0.1";//"test.fab-access.org";
|
||||
public const int TESTSERVER_PORT = 59661;
|
||||
public const string PASSWORD = "secret";
|
||||
|
||||
public static ConnectionData CreateConnetionData(string username)
|
||||
@ -16,7 +16,7 @@ namespace FabAccessAPI_Test
|
||||
ConnectionData connectionData = new ConnectionData()
|
||||
{
|
||||
Host = new UriBuilder(TestEnv.SCHEMA, TestEnv.TESTSERVER, TestEnv.TESTSERVER_PORT).Uri,
|
||||
Mechanism = Mechanism.PLAIN,
|
||||
Mechanism = SASLMechanismEnum.PLAIN,
|
||||
Username = username,
|
||||
Properties = new Dictionary<string, object>()
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user