Condividi tramite


Harnessing Chatbots via Xamarin.Forms to Automate Disaster Recovery Resource Obtainment

Recently Microsoft partnered with Robust Choice Cloud Solutions to create an intelligent solution that would enable two-way mass communication with citizens affected by natural disasters. Robust Choice Cloud Solutions Inc., is a Canadian startup company formed out of the University of British Columbia’s entrepreneurship center in 2013. Robust Choice emerged from years of cutting-edge academic research on the use of machine learning and visual analytics to support urban and regional decision making projects.

This March, the team and I were provided the opportunity to participate in a joint project with Robust Choice Cloud Solution to develop a prototype to automate resource obtainment in global disaster recovery situations. The team decided to develop a bot to address this need as mobile data connectivity is resilient to most natural disasters and can provide an more available source of information gathering when 911 calls have clogged voice communication abilities. As an added bonus, modern bots can be used as a two way communication mechanism collecting data from users providing while simultaneously providing needed information.

The following is a high level diagram detailing data flows of the proposed bot solution:

Many different components were implemented however this post will cover how the custom client bot application was created.

How to configure Direct Line Channel

With the requirement to develop a rich client for our bot, we decided to use Xamarin.Forms as it would allow us to create a universal interface for all modern mobile platforms and integrate some platform-specific code to it if needed.

Developing a third-party client is possible thanks to Direct Line API. To start working with Direct Line API we had to activate it first. It’s possible to do using Bot Framework portal and Channel widget there:

clip_image004

To avoid any security issues in the case of several client applications, the Bot Framework portal allows developers to generate unique credentials per application. To accomplish this, simply click Edit link and add a new application to the list below:

clip_image006

You can see that there are 1.1 and 3.0 versions for Direct Line API. This project utilized version 3.0 however Microsoft will continue to support version 1.1.

Share Project VS Portable Class Library VS .NET Standard

Next we can start working on the Xamarin client. Creating a Cross-Platform client application using Xamarin is an easy process, however there are a couple of items to be aware of. Once you create a new project, you will be able to see a way to select one of two Code Sharing Strategies between Shared Project and Portable Class Library (PCL):

clip_image008

Xamarin supports three methods to share code amongst Android, iOS and UWP applications. One additional strategy is to use .NET Standard class libraries however there is no option for it initially. Simply select PCL and convert your PCL to .NET Standard later. Do keep in mind though that there is no easy way to convert Share Project to PCL and back. Be aware of the selected sharing strategy as you will be able to use different libraries and coding practices in your applications.

Thanks to the Shared Project enablement, you can easily mix platform-independent and platform-specific code using preprocessor directives or partial classes. In fact, using this template will enable you to create three platform-specific projects (Android, iOS, UWP) and a folder that will be precompiled to each of these projects in a shared folder.

When designing a Xamarin.Forms interface, developers spend most of the time working out of the shared folder creating XAML, resources, models and so on. It is easy to use preprocessor directives or even better design pattern based on partial classes when you are ready to implement any platform-specific code.

A second way to share code requires utilization of the Portable Class Library (PCL). This approach allows developers to create one more projects as a portable library that can be used with many different technologies. The main advantage is the ability to share business logic between multiple different .NET applications. It also allows developers to create a better design by using design patterns such as Service Locator. It is recommended to build Portable Class Libraries on a .NET Profile that contains list of APIs are available for selected platforms. Knowing that a profile has several platforms in the list with its own subset of APIs, PCLs require less access to .NET Framework classes than any other Xamarin project in your solution.

A third solution stems from last year’s Xamarin announcement for support of .NET Standard libraries. These .NET Standard libraries are similar to PCLs, but they don’t require any profile and provide better access to .NET Class Library. At the same time, when developing a .NET Standard library, developers should not care about Xamarin or any other developer platform. A developer can simply develop a library, publish it and all platforms that the library will support. A good example of a library that is based on .NET Standard is the DirectLine client SDK as shown in the following example:

clip_image010

You can see that this library supports .NET Standard 1.3 that is good enough for Xamarin and developers can add it to Android, iOS and UWP projects.

As discussed earlier, there are three ways to share code. This is what makes Xamarin unique as it enables developers to have the flexibility to mix their approach as required to achieve their desired outcome.

Let’s take a look at the Shared Project approach. Xamarin supports referencing PCLs and .NET Standard. Developers can select the Shared Project approach and use PCLs and .NET Standard libraries together. In this scenario, the Xamarin.Forms implementation will be added to each project as a PCL and DirectLine package as a .NET Standard making the Shared Project approach very universal.

At the same time if a developer wishes to stay with PCL approach, they still can mix different packages by converting PCL to .NET Standard. This was the method used for our natural disaster recovery project.

Direct Line SDK and .NET Standard

Microsoft.Bot.Connector.DirectLine cannot be added to Portable Class Library directly, but Xamarin allows us to transform PCL to .NET Standard library. In order to do it you can open project properties and change target to .NET Platform Standard clicking appropriate link:

clip_image012

This will not work because the Xamarin.Forms library in PCL is not supported by .NET Standard.

clip_image014

To remedy this, you would need to remove Xamarin.Forms from PCL and retarget it again.

You will be able to select a version of .NET Standard as detailed below once retargeting is completed:

clip_image016

Next, it is important to re-add Xamarin.Forms. The following project.json needs to be modified in order to import the element:

 {
"supports": {},
"dependencies": {
"Microsoft.NETCore.Portable.Compatibility": "1.0.1",
"NETStandard.Library": "1.6.0"
},
"frameworks": {
"netstandard1.7": {
"imports": "portable-net45+win8+wpa81"
}
}
}

You will then be able to install Xamarin.Forms package using NuGet package manager once the changes are completed.

Visual Studio 2017 doesn’t use project.json for .NET Standard libraries. Instead you have to edit csproj file adding PackageTargetFallback element, but migration from old PCL to .NET Standard doesn’t change old approach to new one, but if you create a new .NET Standard from scratch, you will need to work with csproj:

 <PropertyGroup>
<TargetFramework>netstandard1.6</TargetFramework>
<PackageTargetFallback>$(PackageTargetFallback);portable-win+net45+wp8+win81+wpa8</PackageTargetFallback>
</PropertyGroup>

Once completed, you can go ahead with DirectLine client package adding it to all projects.

Own implementation for Direct Line API

Direct Live API 3.0 uses bidirectional communication with its clients which includes http requests that a client should initiate itself based on timer or web sockets. This was mainly the reason reason why our natural disaster project didn’t use Microsoft.Bot.Connector.DirectLine. The solution doesn’t support web sockets and we needed to use web sockets to establish communications with the bot.

The natural disaster project used just basic PCL and we created our own code for Direct Line API. To accomplish this we used System.Net.Http package (preinstalled for many projects) and Websockets.Pcl package.

clip_image018

Websockets.Pcl allows us to use Web Sockets to receive new messages from the bot.

In order to implement Direct Line client API in C# we simply created set of classes with all possible messages and DirectLine class as the core:

 

 public class DirectLine
{
private string DirectLineKey;
Websockets.IWebSocketConnection connection;

public delegate void NewMessageDelegate(ActivitySet args);

public event NewMessageDelegate OnNewMessage;

public DirectLine(string key)
{
DirectLineKey = key;

connection = Websockets.WebSocketFactory.Create();
connection.OnOpened += Connection_OnOpened;
connection.OnMessage += Connection_OnMessage;
}

private void Connection_OnMessage(string obj)
{
var result=JsonConvert.DeserializeObject(obj);
if (OnNewMessage != null) OnNewMessage(result);
}

private void Connection_OnOpened()
{
Debug.WriteLine("WebSocket is opened");
}

public async Task StartConversationAsync()
{
var _client = new System.Net.Http.HttpClient();
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer",
DirectLineKey);
var response = await _client.PostAsync("https://directline.botframework.com/v3/directline/conversations", new StringContent(String.Empty));
if (response.IsSuccessStatusCode)
{
string content=await response.Content.ReadAsStringAsync();
var conversation=JsonConvert.DeserializeObject(content);

connection.Open(conversation.streamUrl);

return conversation;
}
return null;
}

public async Task SendMessageAsync(string conversationId, Activity activity)
{
var _client = new System.Net.Http.HttpClient();
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer",
DirectLineKey);
_client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

string jsonText = JsonConvert.SerializeObject(activity);

var response = await _client.PostAsync($"https://directline.botframework.com/v3/directline/conversations/{conversationId}/activities",
new StringContent(jsonText,Encoding.UTF8, "application/json"));
}
}

 

We used the event model to notify our UI about some messages that we received by sockets. All other classes are just wrappers for json messages that we developed based on REST API specification.

To start a conversation using our class you simply need to call StartConversationAsync and use OnNewMessage event to receive new messages and update interface:

 

 public MainPage()
{
InitializeComponent();

bot = new DirectLine(DirectLineKey);
bot.OnNewMessage += Bot_OnNewMessage;
}

protected async override void OnAppearing()
{
conv=await bot.StartConversationAsync();
base.OnAppearing();
}

 

We have several visuals for our bot like images, text and buttons and because of this we were unable to use ListView for the UI. StackLayout and ScrollViewer were used instead as follows:

 <Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<ScrollView x:Name="scroll">
<StackLayout x:Name="stack" Margin="20" Orientation="Vertical">
</StackLayout>
</ScrollView>
<Entry x:Name="entry" Grid.Row="1" HeightRequest="200" Margin="20"></Entry>
<Button Text="Send" HorizontalOptions="Center" x:Name="button" Grid.Row="2" Margin="20" Clicked="button_Clicked"></Button>
</Grid>

Several ContentView controls were developed to visualize different kind of content (user text, bot text, image, buttons (yes, no)) to complete the solution.

Here are couple screenshots of the completed UI:

clip_image020 clip_image022

Of course, there is enough room for UI improvement, but basic interface works fine and all challenges were resolved in the proof of concept offering.

Further details on the natural disaster recovery solution can be found in the following GitHub repository: https://github.com/sbaidachni/CityPlusIO/tree/master/CrossPlatformBotClient.