dinsdag 3 oktober 2017

Signature Dataviewer

Following are some (random) notes regarding the creation of a WPF program that is used for reading, logging and displaying data from a 'Signature' series doppler based current profiling sensor ( http://www.nortek-as.com/en/products/current-profilers/signature1000-signature500-en).

Charts

The best solution for displaying charts in both WindowsForms and WPF applications seems to be the OxyPlot package. This is free and open source, available for multiple platforms. It can be installed using NuGet. Note that if you are not using the pre-release versions it might be necessary to switch the .NET target version.
After installing, add the OxyPlot to the toolbox:
Copy OxyPlot.dll to the OxyPlot.WindowsForms.xxx Folder.
  • Right click in the toolbox
  • Select "Choose items..."
  • Press "Browse..." under ".NET framework components
  • Select the OxyPlot.WindowsForms.dll assembly (note that there are versions for both .NET 4.0 and .NET 4.5)
  • The "Plot" control should appear under "OxyPlot.WindowsForms Components" in the ToolBox 
 Do not install 'Oxyplot-Extensions', since that is not using the latest version of OxyPlot.

The is no scrolling Spectrogram type in OxyPlot, although the heatmap comes close.
Scrolling requires fast bitmap blitting, which can be achieved using the 'WriteableBitmap' class.
For creating the palette as required for a spectrograph we can use the 'OxyPalette' function from the OxyPlot package:

var palette = OxyPalette.Interpolate(256, OxyColors.Green, OxyColors.White, OxyColors.Red);

This will return a 256 long array of colors, going from green to white to red, and for every color there is a A,R,G and B component. (Alpha, Red, Green and Blue) 

Theming

WPF Theming is relatively simple if you use the ThemeManager from the WPFToolkit.
Just add the WPF.Themes project to the solution, and create a reference to WPF.Themes.  Then in the Window_Loaded() event handler, set the theme:

private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            ThemeManager.ApplyTheme(this, "ExpressionDark");

        }


Next add the the Background to the XAML file for the window:

        Background="{DynamicResource WindowBackgroundBrush}"

This will change the window background color according to the theme.

Function Keys

Using function keys in WPF seems difficult. But the easiest solution I have found so far is the following.
Add these lines to the XAML file:
   
<Window.Resources>
   <RoutedUICommand x:Key="cmd1"></RoutedUICommand>
</Window.Resources>
<Window.CommandBindings>
   <CommandBinding Command="{StaticResource cmd1}" Executed="PressButtonCommand"></CommandBinding>
</Window.CommandBindings>
<Window.InputBindings>
  <KeyBinding  Key="F12" Command="{StaticResource cmd1}"/>
</Window.InputBindings>

And in the code:
private void PressButtonCommand(object sender,RoutedEventArgs e)
        {
                Console.WriteLine("Button Clicked from F12");
        }


It's explained in this question on StackOverflow. The only problem with this solution is that I don't fully understand what is going on, but it works like a charm.

maandag 15 februari 2016

Data on the Map

Part of showing measurement data is also showing where it comes from. So a map is required, with some markers and maybe some additional information.
Using Google Maps is probably the easiest way to show just a map with some markers. From the past I remembered that it was not allowed to use Google maps in commercial applications, but this has changed and currently its free for something like 2500 page-views per day. So that will not be problem.



Other options may be OpenStreetmap. This data is definitely free forever. When looking for a library to display OpenStreetmap data the first option is usually OpenLayers. This is a very feature-rich library that all kinds of chart manipulation. But the version 3 is also more than 500 K of javascript, which seems a bit overkill to me for just showing some positions. Looking bit further I found 'Leaflet', a lightweight library for showing maps. On their main page they claim it's only 33 KB, but the version I downloaded is 123 KB, so it probably grown a bit since the initial release.


donderdag 27 augustus 2015

The Visual Studio Way

 In Visual Studio 2015 it is more convenient to use the ASP.NET framework. Basics of this type of application are nicely described in 'Understanding MVC in ASP.NET' . The article is a little outdated, but the basics are still the same.
The app will be based on a Web API:

  

This will create 90% of the full project..:
Data will be stored in the database, and for interaction with it we will use the Entity Framework. If that is not installed yet, use the NuGet package manager to install it.

The basis of this app is described in  Using WEB API2 with Entity Framework article on MSDN.


Updating and configuring the database.

Requires Entity Framework. Make sure that in Configuration.cs, AutomaticMigrations is enabled:

 public Configuration()
        {
            AutomaticMigrationsEnabled = true;
        }


The connection string for the database is in Web.config:

    <connectionStrings>
        <add name="ScourViewContext" connectionString="Data Source=localhost\SQLEXPRESS;  Database=Scour;User Id=sa;Password=ceespass" providerName="System.Data.SqlClient" />
    </connectionStrings>


The 'name' is set to 'ScourViewContext'   This name is used in the 'ScourViewContext.cs'file to actually set the context:

        public ScourViewContext() : base("name=ScourViewContext")         
        {
            this.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);
        }

The 'this.Database.Log...' section was added manually to enable logging.

Entity framework database maintenance is done in the Package Manage Console (Tools-> NuGet Package Manager->Package Manager Console)

Required commands: Add-Migration and Update-database.


Note that in this sample the command fails first because the 'Default Project' was set to 'ScourDataWriter'. Since no database is specified the commands use the connection string from the web.config

To start a database from scratch, use the SQL Server Management Studio to delete the tables 'dbo._MigrationHistory' and 'dbo.MyData'. Also remove all the files form the 'Migrations' folder in Visual Studio, except the 'Configuration.cs' . Running 'Add-Migration InitialCreate' and 'Update-Database' will now create the xxxxxx_InitialCreate.cs file and a new table in the database.

Adding a column
To add an extra column to the database, modify the 'model' first. In this case I added the Depth_Span_m field to the ScourSensor model:






Now we can start the migration by typing 'Add-Migration' this will ask for a name, which will just be the name for this specific migration. You can type anything here.




This will automatically create the AddDepthSpan Migration functions as shown above.
Now send the migration to the database using the Update-Database command.

Sending data to the database using the http  'POST'

With the ASP.NET application set-up as shown in the example and the database initialised it will run and await http commands from the browser or any other service that sends http commands to the right URL.
To send a new record to the database using the http 'POST' we will use the following code:
(Taken from StackOverflow question 'How to post JSON to the server' )


string json = JsonConvert.SerializeObject(s);
string URL = "localhost:12345/api/ScourData";
var request = (HttpWebRequest)WebRequest.Create(URL);
request.Credentials = CredentialCache.DefaultNetworkCredentials;
request.Method = "POST"

request.ContentType = "application/json";
using (var streamWriter = new StreamWriter(request.GetRequestStream()))
      {
      streamWriter.Write(json);
      streamWriter.Flush();
      streamWriter.Close();
      }


var httpResponse = (HttpWebResponse)request.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
            {
                var result = streamReader.ReadToEnd();
            }



's' is an instance of the the previously defined datamodel (the one we used to generate the database) filled with the actual numbers. A new WebRequest is created that is cast to a HttpWebRequest since we will need the Http protocol for our 'POST' action.
In this example the URL is set to 'localhost:12345'. This obviously has to be replaced with the url of the server that is serving the ASP.NET app.

Security in ASP.NET Web API2

This blogpost by Martin Kearn on MSDN describes how to access the web api using a secure connection. When using the standard authentication / security setup as created in the 'WEB-API' template app it is a matter of requesting a security token (by providing user-name and password) and then add this token to the web request.
To build a login/register page start from the sample app by Mike Wasson .
This app requires the Knockoutjs framework, so we'll have to install that first using NuGet. (Hint: look for 'knockoutjs'. If you look for Knockout, or knockout.js it will not be found...)
Now add the 'app.js' to the project. 
In App_Start/BundleConfig.cs, add a new [script bundle]
(http://www.asp.net/mvc/tutorials/mvc-4/bundling-and-minification). 

        bundles.Add(new ScriptBundle("~/bundles/app").Include(
                  "~/Scripts/knockout-{version}.js",
                  "~/Scripts/app.js"));

NOTE: Later in the project I found that using 'knockout.js' is really overkill if you just want to show a few numbers from a database so I removed it completely from the project.

When securing the website it is obviously best to work with users that have different 'roles'. Most obvious there should be some way to log in as 'Adminstrator' with all required privileges to modify the critical sections. Now protecting specific functionality in the application is simple: just  decorate the controller function with some Authorization :
[Authorize(Roles = "Administrator")]
What is generally overlooked in all example projects is how to create the initial Adminstrator in the database.
The ASP.NET template already created the dbo.AspNetUsers and dbo.AspNetUserRoles database, and it's easy to add users, but how do you create the initial user that has Administrator rights ?
Finally I just added the following lines to the Create() function in the IdentityConfig.cs:

 
var UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));  
var RoleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(context.Get<ApplicationDbContext>()));
string name = "Administrator";
string password = "Total2Confusion!"; 
 
 //Create Role Admin if it does not exist
if (!RoleManager.RoleExists(name))
{
  var roleresult = RoleManager.Create(new IdentityRole(name));
}
//Create User=Administrator with password
var user = new ApplicationUser();
user.UserName = name;
var adminresult = UserManager.Create(user, password); 
 //Add User Admin to Role Admin
if (adminresult.Succeeded)
 {
 var result = UserManager.AddToRole(user.Id, name);
 }
  

This may not be the most elegant way to do this, but it definitely works. After running the app for the first time there will be a user 'Administrator' in the AspNetUsers table, and the ID of this user is also entered in the AspNetUserRoles table with the required 'RoleID', as shown here in MSQL Management Studio:


Redirecting to a login page on unauthorized access

The sample application as used here has one start page with the Login/Register section on it. In our application we want  the site to open with the login page if the user is not authorized yet.  Almost all references to his problem mention overriding or implementing the 'HandleUnauthorizedRequest()' method. But it appears that there is a different way, using the UseCookieAuthentication() method options and providing a 'LoginPath':

            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType =                             DefaultAuthenticationTypes.ApplicationCookie,
                LoginPath = new PathString("/Account/Login")
            });


The usage is explained in the LoginPath Help:

 // Summary:
        //     The LoginPath property informs the middleware that it should change an outgoing
        //     401 Unauthorized status code into a 302 redirection onto the given login path.
        //     The current url which generated the 401 is added to the LoginPath as a query
        //     string parameter named by the ReturnUrlParameter. Once a request to the LoginPath
        //     grants a new SignIn identity, the ReturnUrlParameter value is used to redirect
        //     the browser back to the url which caused the original unauthorized status code.
        //     If the LoginPath is null or empty, the middleware will not look for 401 Unauthorized
        //     status codes, and it will not redirect automatically when a login occurs.


 

Adding additional data to the UserInfo

It would be convenient to have an 'organizationID' for each user so we can see what organization this belongs to. Based on the Customizing profile information article I've done the following:
Added the  OrganizationID member to ApplicationUsermodel:

   public class ApplicationUser : IdentityUser
    {
        public int OrganizationID { get; set; }
        :

Added the field to the RegisterBindingModel:

        [Required]
        [Display(Name = "OrganizationID")]
        public int OrganizationID { get; set; }


Added code to get the current user ID and current User in the AccountController->GetUserInfo()

      // GET api/Account/UserInfo
        [HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)]
        [Route("UserInfo")]
        public UserInfoViewModel GetUserInfo()
        {
            var currentUserId = User.Identity.GetUserId();
            var currentUser = UserManager.FindById(User.Identity.GetUserId());

            ExternalLoginData externalLogin = ExternalLoginData.FromIdentity(User.Identity as ClaimsIdentity);
            return new UserInfoViewModel
            {
                Email = User.Identity.GetUserName(),
                HasRegistered = externalLogin == null,
                LoginProvider = externalLogin != null ? externalLogin.LoginProvider : null,
                OrganizationID = currentUser.OrganizationID
            };
        }


Chaining the API calls in JavaScript

On the initial start of the page we always have to log in, get details of the logged in user and then get a list of stations assigned to this user. This means our web page will have to execute 3 api calls which all depend on the result of the previous one.
This article about writing better Ajax describes this very well.

Speeding up

Because the FLOT library requires time-tags as milliseconds, the ASP controller that returns the data also uses milliseconds. This however means that every point basically contains three zeroes '000' that are not used.
Of course compression would take care of that, but I noticed this is not enabled on IIS by default. Scott Hanselman explains how to do this.  After enabling this I noticed that the site indeed felt much more responsive.


Deploying the website to an IIS server

So far the site has only been running from within Visual Studio, which is super convenient, but it also hides the actual  setup required to get it running on any other location. The next article describes the process of transferring the site to a different computer, running IIS:

http://www.asp.net/mvc/overview/deployment/visual-studio-web-deployment/deploying-to-iis 

The article describes a totally automatic installation of the website to IIS, including initialising the database and setting all required permissions.

Error handling

The default setup of the ASP.NET application just throws an exception when for example the database is not found, which, if unhandled just results in a automatically generated default error page.
The solution to this is to add the 'Application_Error' function to Global.asax.cs file, just after the 'Application_Start()' function. Here you can check what type of exception was thrown by calling the Server.GetLastError() function and handle it.

void Application_Error(object sender, EventArgs e)
        {
            // Code that runs when an unhandled error occurs
            // Get the exception object.
            Exception exc = Server.GetLastError();

            // Handle HTTP errors
            if (exc.GetType() == typeof(HttpException))
            {
                if (exc.Message.Contains("NoCatch") || exc.Message.Contains("maxUrlLength"))
                    return;

                //Redirect HTTP errors to HttpError page
                Server.Transfer("HttpErrorPage.aspx");
            }
        }

Show and Edit data in the database

So far I only displayed data from the sensors and edited all details in the SQL Server Management Studio. It would obviously be better to have all this editing on the web page itself.
The 'Getting Started with ASP.NET MVC5' series of articles shows the basics of how to do this.