VB Magic

2012/12/19

Plumbing in Place

Filed under: .NET, Learning, VB.NET — Tags: , , , , , — vbmagic @ 1:54 pm

Spent last weekend and some of the start of my Xmas holiday trying to finalise the plumbing between my three tiers.

There are now two types of services in the middle tier.

  1. OData read-only service. This is where things like the web client can get access to the game entities that get generated and allow for the game codex to be built. These will remain open and available to anyone that wants to view them.
  2. Updates and membership information. This will be a WCF based service mainly due to requiring the “Updates” to be sent over the service bus to the back end part of the system. And this is what will end up being secured at a later date when I’ve managed to research it more.

The only part of the feed currently available is the Latest News feed which is shown on the front page of the client web site currently.

There is also a hidden part of the client website which handles adding new news articles.

This is basically how adding a new news article works…

  • Client Website sends a news article to the middle tier WCF Service AddNewsArticle
  • The middle tier web service generates a GUID (Checking job result list in the DB to make sure it doesn’t already exists) and then creates a Service Bus “Job” which contains the GUID and passes GUID back to client.
  • The back end tier receives the news article job and then adds the article to the database and writes an entry in the job result list saying that it successfully created or it failed to create the news article using the GUID as a key.
  • While the back end is processing this job the web client has requested the job result from the middle service tier using the GUID key it recieved earlier. This service will wait up to thirty seconds for the job result to be generated (Checking every second) and then return the result back to the web client. It will also send a job to the back end to delete the job result.
  • The web client will either go back to the front page and the news article will then be displayed, or if there was an error, it will display the error on the add news article page so the user can decided what to do (I.e. me try to fix it 😉 )

The upshot all this recent work on the plumbing side of things means that I can now proceed and create the more interesting bits of the game and hopefully make very few changes to the way the plumbing works.

The heart of this system is the Job that passes from the Service Tier to the Back end Tier. All the code required to do a “Job of work” is held in these jobs. All the service tier has to do is create a job of a specific type and populate the required information, then send this job over the service bus to the back end. All the back end has to the is “Process” the job which makes the back end very small and simple in the way of code. All the work is done in the Job Process function. Hence most of the future development will be in these jobs and the client.

Onwards and upwards.

2012/12/03

Decisions Decisions – Solution Structure and how to get things done

Filed under: .NET, Azure, Learning — Tags: , , , , , , , — vbmagic @ 2:44 pm

I spent most of this weekend, researching different ways to achieve my project. There are certain things that I know I want to do:

Adopt a Three Tier Approach.

  1. Multiple Clients (Including a Website)
  2. A middle service layer that all the clients talk to which can read from the database but not update.
  3. A back end which is the only part of the solution allowed to update the database

The communication between the middle tier and the back end tier will be via the Azure Service bus.

I want the user of the application to be able to log in to the various client using their Windows Account/Google/Facebook ID’s.

With this in mind I was looking into ways to achieve these goals. I was mainly concentrating on what the middle service tier should be.

I wanted something that will make it easy to implement clients on non Microsoft platforms which drew me towards using the new ASP.Net MVC Web API. The downside of this is there is no metadata to describe the service which makes the client much harder to write. After a few discussions with a colleague, they suggested using WCF OData service which should allow making clients easy on the windows side. (Automatic generation of model classes when adding a service reference)

But I’m still open to suggestions from anyone reading this if they think there may be a better way.

The solution structure for the project as it stands now will be:

Tyrannt.Model (Holds the Code First EF Classes)

Tyrannt.Backoffice (Windows Azure Worker Role – The back end tier)

Tyrannt.OData (Web Role – the Middle WCF OData tier)

Tyrannt.Website (First client – will consume the OData service)

I would also like to make use of database migrations that are in Entity Framework 5. Which also brings up another question which it’s been hard to answer. Where is the best place to put the Database context. An easy solution would be to put one in both the Backoffice and OData projects but that means maintaining two separate classes that basically do the same job)

I’ve seen some posts about not having the database context in the same project as the Model classes. I’m trying to work out the best way to do this and still allow database migrations.

Feel free to comment this posts with suggestions ideas or even telling me that I’ve got it all wrong ;-).

2012/11/28

Adding in an Extra Tier

Filed under: .NET, ASP.NET MVC, Learning — Tags: — vbmagic @ 10:16 am

After thinking about making clients to access the game and the fact that it is probably easier to create a generator tool on a desktop client (Thinking Windows 8 here); I thought it would probably be best to create an Web API web role now rather than later. The website after all is basically a Client as well.
So new way of thinking. All clients including the website don’t know anything about Entity Framework or Azure Service bus. All they need to know about is how to access the API. Having this as an extra Tier will also allow easier scaling in future.
Looks like I’ll be hitting the Pluralsight ASP.NET Web API course this weekend.

2012/11/07

Windows Azure, Service Bus Queue between Webrole and Worker role

In a previous post I showed Azure Storage Queues being used to send messages between a Webrole and a Worker role.

I’ve got back onto this project to learn more about MCV 4 and Windows Azure Service Bus Queues. So I modified the original classes to use the SB Queues instead of Azure Storage queues. Behind the scenes Entity Framework Code First is being used to access the database. I’m loving the ability to change the classes and then run update-database to modify the SQL Azure database. Back to the queues.

First you need to create a queue which this article can walk you you through.

Once the queue is created put the connection string into the Azure Service Configuration file.

Below is the base class for a Job for this application.

Imports System
Imports System.Collections.Generic
Imports System.Diagnostics
Imports System.Linq
Imports System.Net
Imports System.Threading
Imports Microsoft.ServiceBus
Imports Microsoft.ServiceBus.Messaging
Imports Microsoft.WindowsAzure
Imports Microsoft.WindowsAzure.Diagnostics
Imports Microsoft.WindowsAzure.ServiceRuntime
Imports Microsoft.WindowsAzure.Storage

Public MustInherit Class tjob


    ' Enable database access
    Protected Friend _db As New TyranntDb()
    ' A place holder for the user information
    Protected Friend _usr As Member
    ' setup variables to allow for access to Azure Queues
    Protected Friend _jobQueue As QueueClient

    ' A sample of a job entry in the Queue
    Private sample = <job type="email" userid="102">
                         <type/>
                     </job>

    ' the user ID which is used to pull the user information
    Private _userID As Int64

    ' Initialise the job from the XML String
    Public Sub New(jobStr As String)
        Dim jobXML As XElement = XElement.Parse(jobStr)
        _JobType = jobXML.@type
        Dim usrstr As String = jobXML.@userid

        Try
            UserID = Convert.ToInt64(usrstr)
        Catch ex As Exception
            ErrorMessage = ex.Message
            ReportError("tJob New Convert int64")
        End Try

    End Sub

    ' Create a blank job, this is used for creating a job to
    ' put onto the queue.
    Public Sub New()
        _JobType = ""
        _userID = -1
    End Sub

    ' Job type. Used to create the correct object.
    Public Property JobType As String

    ' The user ID. If this is being set then it
    ' will look up the user from the database
    Public Property UserID As Integer
        Get
            Return _userID
        End Get
        Set(value As Integer)
            _userID = value
            If _userID > 0 Then
                GetUserDetails()
            End If
        End Set
    End Property

    ' This is the code that "Processes" the job. Each job type must
    ' implement this code.
    Public MustOverride Function Process() As Boolean

    ' A general variable for storing any errors that
    ' occur. If it's empty then no errors are assumed.
    Public Property ErrorMessage As String

    ' This will generate an XML element that describes the job.
    Public MustOverride Function ToXML() As XElement

    ' This will generate a string version of the XML
    ' which describes this job.
    Public Overrides Function ToString() As String
        Return ToXML.ToString
    End Function

    ' This routine will pull the user information from the
    ' database and store the user detals in the _usr object.
    Protected Friend Sub GetUserDetails()
        Dim q = From u In _db.Members
                Where u.ID = _userID
                Select u
        If q.Count > 0 Then
            _usr = q.Single
        End If
    End Sub

    ' If the job is being created. This function will add the job
    ' to the Azure Queue.
    Public Sub AddJobToQueue()
        ' Get the azure storage account object.
        _jobQueue = QueueClient.CreateFromConnectionString(CloudConfigurationManager.GetSetting("Microsoft.ServiceBus.ConnectionString"), "standard_queue")
        Try
            ' Now add the job details to the queue.
            Dim msg As New BrokeredMessage(Me.ToString)
            _jobQueue.Send(msg)
        Catch ex As Exception
            _ErrorMessage = ex.Message
            ReportError("AddJobToQueue")
        End Try
    End Sub

    Public Sub ReportError(location As String)
        Dim err As New ErrorMessage
        err.ErrorTime = DateTime.Now
        err.Location = location
        err.Message = Me.ErrorMessage
        _db.ErrorMessages.Add(err)
        _db.SaveChanges()
    End Sub
End Class

As you can see, this class must be inherited by another class which will perform the actual job. The base class will handle getting a users information from the database represented by the TyranntDB context class. I’ve added an error reporting system which will store any errors into a table in the database. The job information is stored in an XML format (The message queues allow for 64kb message which should be plenty for our purposes), so the inheriting class must implement a ToXml method. Also the inheriting job class must know how to do the job it is intended to perform so it must implement a Process method as well. Finally the class must be able to add its self to the message queue. So this base class has an AddJobToQueue method which makes use of the ToXml method the inheriting class has implemented to generate the message body to be added onto the queue. The service bus Imports at the top of the class will expose the QueueClient and the BrokeredMessage which is all that is required to add a message to the queue. You can see in the code how simple it is.

Next we need to add a new class that is derived from this base class to add a new member to the database. Below is the code that achieves this.

Public Class tjNewUser
    Inherits tjob

    ' an example of a new user job
    Private sample = <job type="newuser" userid="-1">
                         <user name="{username}" email="{user)@{domain}">Full Name</user>
                     </job>

    ' Extra data required by this class
    Public Property userName As String
    Public Property email As String
    Public Property fullName As String

    Public Sub New(jobStr As String)
        ' initialise basic information
        MyBase.New(jobStr)
        Dim jobXML As XElement = XElement.Parse(jobStr)
        ' now initialise new user information
        userName = jobXML...<user>.@name
        email = jobXML...<user>.@email
        fullName = jobXML...<user>.Value
    End Sub

    Public Sub New()
        ' initialise the base information
        MyBase.New()
        JobType = "newuser"
        userName = ""
        email = ""
        fullName = ""
    End Sub

    ' Create the new user in the database
    Public Overrides Function Process() As Boolean
        ' first check to see if the user already exists

        Try
            Dim r = From m In _db.Members
                          Where m.MemberAlias = _userName
                          Select m

            If r.Count > 0 Then
                ' User already exists so do not continue
                ' return true in this case as request
                ' has been processed more than one.
                ErrorMessage = "User " & _userName & " Already exists"
                ReportError("tjNewUser Process")
                Return True
            End If
        Catch ex As Exception
            ErrorMessage = ex.Message
            ReportError("tjNewUser lynq query to get member ID")
        End Try

        ' create a new user
        Dim usr As New Member
        ' populate the generic information
        usr.EmailAddress = _email
        usr.Name = _fullName
        usr.MemberAlias = _userName
        usr.LastLogin = DateTime.Now
        ' now set the user group to be member
        Dim userType As MemberType

        Try
            userType = (From m In _db.MemberTypes
                     Where m.Name = "Member"
                     Select m).Single
        Catch ex As Exception
            ErrorMessage = ex.Message
            ReportError("tjNewUser Lynq query to get 'Member' member type")
            Return False
        End Try
        usr.Type = userType
        ' now save the user
        Try
            _db.Members.Add(usr)
            _db.SaveChanges()
        Catch ex As Exception
            ErrorMessage = ex.Message
            ReportError("tjNewUser Memebers.Add Save Changes")
            Return False
        End Try

        ' Now that the user was sucessfully created,
        ' generate a new user email job
        Dim jb As New tjEmail
        jb.EmailType = "NewAccount"
        jb.UserID = usr.ID
        ' Add the job to the Azure job queue
        jb.AddJobToQueue()
        If jb.ErrorMessage = "" Then
            Return True
        Else
            ErrorMessage = jb.ErrorMessage
            ReportError("tjNewUser Add Job to Queue produced error")
            Return False
        End If
    End Function

    Public Overrides Function ToXML() As XElement
        Return <job type="newuser" userid=<%= UserID %>>
                   <user name=<%= _userName %> email=<%= _email %>><%= _fullName %></user>
               </job>
    End Function

End Class

As you can see, this class has overrode the ToXML function and the Process Function which has the code that actually adds the user to the Members database. In the last part of the process function it creates a new job which will be used to send an email to the user about their newly created account. This job class is shown below.

Imports System.Net.Mail
Imports Microsoft.WindowsAzure

Public Class tjEmail
    Inherits tjob

    ' a sample email job
    Private sample = <job type="email" userid="102">
                         <email from="noreply@tyranntrpg.org" type="newuser"/>
                     </job>

    ' setup extra information required by this job
    Private _from As String
    Private _emailType As String

    ' The is the from email address
    Public WriteOnly Property From As String
        Set(value As String)
            _from = value
        End Set
    End Property

    ' This will be the email type e.g. newuser
    Public WriteOnly Property EmailType As String
        Set(value As String)
            _emailType = value
        End Set
    End Property

    ' If the job XML already exists this will set up
    ' the information automatically
    Public Sub New(jobStr As String)
        MyBase.new(jobStr)
        Dim jobXML As XElement = XElement.Parse(jobStr)
        _from = jobXML...<email>.@from
        _emailType = jobXML...<email>.@type
    End Sub

    ' Create an empty email job if creating a new job
    Public Sub New()
        MyBase.New()
        JobType = "email"
        _from = "noreply@tyranntrpg.org"
        _emailType = ""
    End Sub

    '' Send the email
    Public Overrides Function Process() As Boolean
        Dim email As MailMessage
        ' Generate the correct body of the email
        Select Case _emailType
            Case "NewAccount"
                email = GenerateNewUserEmail()
            Case Else
                ErrorMessage = String.Format("Email Type [{0}] not recognised", _emailType)
                ReportError("tjEmail Process")
                Return False
        End Select

        Dim smtp = New SmtpClient(CloudConfigurationManager.GetSetting("SMTPAddress"), Integer.Parse(CloudConfigurationManager.GetSetting("SMTPPort")))
        smtp.Credentials = New Net.NetworkCredential(CloudConfigurationManager.GetSetting("SMTPUser"), CloudConfigurationManager.GetSetting("SMTPPassword"))
        smtp.EnableSsl = True

        Try

            smtp.Send(email)

        Catch ex As Exception
            Me.ErrorMessage = ex.Message
            ReportError("tjEmail Send()")
            Return False
        End Try
        Return True
    End Function

    ' This will generate the subject and body of the newuser email
    Private Function GenerateNewUserEmail() As MailMessage
        If _usr Is Nothing Then
            ErrorMessage = "_usr is null"
            ReportError("GenerateNewUserEmail()")
            Return Nothing
        End If
        Dim email As New MailMessage(_from, _usr.EmailAddress)
        Dim subject As String = ""
        Dim body As String = ""
        Try
            Dim emailMsg = (From e In _db.EmailMessages
                            Where e.Name = "NewAccount"
                            Select e).Single
            subject = emailMsg.Subject
            body = String.Format(emailMsg.Body, _usr.Name)
        Catch ex As Exception
            ErrorMessage = ex.Message
            ReportError("GenerateNewUserEmail(), Lynq Query to _db.EmailMessages")
            Return Nothing
        End Try
        ErrorMessage = body
        email.Subject = subject
        email.Body = body
        Return email
    End Function

    Public Overrides Function ToXML() As XElement
        Return <job type="email" userid=<%= UserID %>>
                   <email from=<%= _from %> type=<%= _emailType %>/>
               </job>
    End Function

    Private Sub Smtp_SendCompleted(sender As Object, e As ComponentModel.AsyncCompletedEventArgs)
        If e.Error Is Nothing Then
            ErrorMessage = "Mail sent correctly"
            Me.ReportError("tjEmail SendCompleted")
        Else
            ErrorMessage = e.Error.Message
            Me.ReportError("tjEmail SendCompleted")
        End If
    End Sub

End Class

All the SMTP server information is held in the azure service configuration file. Again the overrode functions do all the work in this class.

Now onto the back end. This is uses the Worker Role With Service Bus Queue template (Shown Below)
New Worker Role Dialogue

As the job classes actually do all the work required, this is a very small piece of code.

Imports System
Imports System.Collections.Generic
Imports System.Diagnostics
Imports System.Linq
Imports System.Net
Imports System.Threading
Imports Microsoft.ServiceBus
Imports Microsoft.ServiceBus.Messaging
Imports Microsoft.WindowsAzure
Imports Microsoft.WindowsAzure.Diagnostics
Imports Microsoft.WindowsAzure.ServiceRuntime
Imports Microsoft.WindowsAzure.Storage
Imports Tyrannt.Domain

Public Class WorkerRole
    Inherits RoleEntryPoint

    ' The name of your queue
    Const QueueName As String = "standard_queue"

    Private _db As New TyranntDb

    ' QueueClient is Thread-safe. Recommended that you cache 
    ' rather than recreating it on every request
    Dim Client As QueueClient
    Dim IsStopped As Boolean

    Public Overrides Sub Run()

        ' This is a sample implementation for Tyrannt.Backoffice. Replace with your logic.

        While (Not IsStopped)
            Try
                ' Receive the message
                Dim receivedMessage As BrokeredMessage = Client.Receive()

                If (Not receivedMessage Is Nothing) Then
                    ' Process the message
                    Trace.WriteLine("Procesing", receivedMessage.SequenceNumber.ToString())
                    ProcessMessage(receivedMessage)
                    receivedMessage.Complete()
                End If
            Catch ex As MessagingException
                If (Not ex.IsTransient) Then
                    Trace.WriteLine(ex.Message)
                    Throw ex
                End If
                Thread.Sleep(10000)
            Catch ex As OperationCanceledException
                If (Not IsStopped) Then
                    Trace.WriteLine(ex.Message)
                    Throw ex
                End If
            End Try
        End While

    End Sub

    Private Sub ProcessMessage(msg As BrokeredMessage)

        Dim msgBody As String = "msgBody not available"
        Dim errMsg As String = ""
        Try
            msgBody = msg.GetBody(Of String)()
            ' Turn the message into an XML element
            Dim xmlMsg As XElement = XElement.Parse(msgBody)
            ' Extract the message type from the element
            Dim type As String = xmlMsg.@type

            ' Now we create a job
            Dim job As tjob
            'ReportError("Processing job [" & type & "]", "WorkerRole.vb")
            Select Case type
                ' Use the message type to see what kind of job is required
                Case "newuser"
                    job = New tjNewUser(xmlMsg.ToString)
                Case "email"
                    job = New tjEmail(xmlMsg.ToString)
                Case Else
                    ReportError("job type [" + type + "] Not recognised", "WorkerRole.ProcessMessage()")
                    Exit Sub
            End Select
            ' Process the job.
            If job.Process() = True Then
                ' The job succeeded so write a trace message to say this and
                ' delete the message from the queue.
                Trace.WriteLine(String.Format("{0} succeeded", type), "Information")
            Else
                ' The job failed so write a trace error message saying why the job failed.
                ' This will leave the job on the queue to be processed again.
                Trace.WriteLine(String.Format("{0} failed: {1} ", type, job.ErrorMessage), "Error")
            End If
        Catch ex As Exception
            ' something big has gone wrong so write this out as an error trace message.
            Trace.WriteLine(String.Format("Failed to parse xml message: [{0}]", msgBody, "Error"))
            ReportError(ex.Message, "WorkerRole.ProcessMessage()")
            Exit Sub
        End Try
    End Sub

    Public Overrides Function OnStart() As Boolean

        ' Set the maximum number of concurrent connections 
        ServicePointManager.DefaultConnectionLimit = 12

        ' Create the queue if it does not exist already
        Dim connectionString As String = CloudConfigurationManager.GetSetting("Microsoft.ServiceBus.ConnectionString")
        Dim namespaceManager As NamespaceManager = namespaceManager.CreateFromConnectionString(connectionString)
        If (Not namespaceManager.QueueExists(QueueName)) Then
            namespaceManager.CreateQueue(QueueName)
        End If

        ' Get a client to use the queue
        Client = QueueClient.CreateFromConnectionString(connectionString, QueueName)
        IsStopped = False
        Return MyBase.OnStart()

    End Function

    Public Overrides Sub OnStop()

        ' Close the connection to Service Bus Queue
        IsStopped = True
        Client.Close()
        MyBase.OnStop()

    End Sub

    Private Sub ReportError(msg As String, location As String)
        Dim err As New ErrorMessage
        err.ErrorTime = DateTime.Now
        err.Location = location
        err.Message = msg
        _db.ErrorMessages.Add(err)
        _db.SaveChanges()
    End Sub

End Class

I’ve added the ability to access the database to this class only for error reporting which helps tracking down any issues during development. The only subroutine aside from the error routines is the ProcessMessage. This function will get the message body. Work out what type of message it is and then generate the correct class and run the Process method. (Note: You can only use the GetBody method once, if you try more than once it will crash out.) In this example I complete the received message even if it fails to process (Failure gets added to the error table). In future I may end up doing more elaborate things.

I hope some people find this helpful.

2012/07/19

Taking a IIS pre-generated web service and putting into Azure

Filed under: Azure, Learning, SQL Azure, VB.NET — Tags: , , , , — vbmagic @ 3:11 pm

I had, what I thought, was a simple job to do; but it took over two weeks of discovery to find out it actually was easy, but not quite in the way I was planning to do it.

I had two web services that I needed to host in Azure. For a temporary measure, I create a single Azure instance and this had a simple elevated start up task that installed required run-times and then downloaded 7zip file from Azure storage and then extracted this file.

I then connected to the instance via remote desktop, launched IIS Manager and created the web applications there. I also manually started a back end process.

The next step was to try and automate the deployment of this process using two instances. I moved the required database into SQL Azure which worked fine.

Next I created a new C# Azure project with an ASP.net web role and added the tasks to install the runtime and also looked up the commands required to add the website applications.

I hit a problem that I never really managed to solve where the installation of the C++ 10 runtime would just hang the start-up task. After spending around three days trying to diagnose what had happened I decided to start again from scratch.

I created a new VB Azure project with an ASP.net web role. I added the same task which ran a batch file. This time round the run-times installed with no problem. (I guess I’ll never know what went wrong with that one)

The next part of the script was to download and extract the 7zip archive file. (To download from storage I used the Azure Command Line tools from Rob  Blackwell/Two10 Degrees: https://github.com/RobBlackwell/AzureCommandLineTools)

I then extracted the archive using 7zip command line tools (http://www.7-zip.org/download.html)

It was when I came to run AppCmd.exe to add the extracted web applications, I learned about the order that Tasks etc. are run in Azure. (After a day or so of tracking things down)

The following article helped clarify this a lot. (I’ve borrowed the picture from the article below) http://msdn.microsoft.com/en-us/library/windowsazure/hh127476.aspx

Azure Startup flow chart

Azure Startup flow chart

 

So basically, any start-up tasks would not be able to run scripts that try to modify the website (Like adding the web applications). I abandoned that idea.

Now there is an Elevated Simple task which installs the required run-times and downloads and extracts the files required.

Then there is an Elevated Foreground task which starts the back end process.

All that was left to do was get the web applications (Which were extracted by the Simple Task onto the C: drive)

After a lot more web searching I decided to modify the service definition file for the website and added the virtual applications to this file. (As described in this article: http://msdn.microsoft.com/en-us/library/windowsazure/gg433110.aspx )

I pointed the physical directory to where the application will be extracted to on the C drive of the instance. This caused a build error saying it couldn’t find the directories. I created these directories on the C drive of the machine I was publishing from which allowed the publish to complete.

Unfortunately the deployment got stuck in busy until I deleted it. (Waited a good few hours and tried a couple of times just in case of a “Glitch”).

As a last resort, I extracted the web applications and added the files to the web role’s project folder and then included them into the project. Next I modified the physical directory to a relative directory pointing to the included application directories.

Published and to my great surprise it actually work. Phew!

Took a few weeks but was a great learning process (If a bit frustrating at times 😉 )

Jas

2012/04/20

Free 1 month subscription to Pluralsight Training Course.

Filed under: .NET, ASP.NET MVC, Learning — vbmagic @ 3:28 pm

If you wanted to learn ASP.net MVC with a bit of Entity Framework Code First, HTML 5 and jQuery thrown in Pluralsight are offering a free months subscription to this course:

http://www.pluralsight-training.net/microsoft/Courses/TableOfContents?courseName=web-development

All you need to do is follow them on twitter https://twitter.com/#!/pluralsight

And then go to this web page on their web site: http://www.pluralsight-training.net/microsoft/TwitterOffer

And put in your twitter name.

I’ve been a subscriber to their courses for a while now and find them invaluable.

2012/02/29

Pluralsight offer 1 day free access to their entire catalogue of training videos

Filed under: .NET, Learning — Tags: , , , — vbmagic @ 8:07 am

If anyone is after some Training in .net, c++, java etc. Pluralsight have free training for a day!

2012/02/20

Fez Spider Talks

Actually got my soldering iron out at the weekend and soldered pins onto the Gadgeteer Extender module.

While rummaging around in my old electronics last week, I came across an old SP03 Text to Speech module:

SP03 Text to Speech module

SP03 Text to Speech module

Info on this device can be found here: SP03 Documentation

So with a breadboard the new soldered Extender module and some connector wires and pull up resistors; it was all connected together and powered on. No magic blue smoke meant that things may actually be working ;-).

Fez Spider with SP03

Fez Spider with SP03

The device uses either serial or I2C communication to communicate which is supported by the FEZ Spider. It took a lot of looking around and a couple of questions on the Tiny CLR forum but I managed to make a class that allowed the communication between the two and managed to make it speak for the first time. Below is that class:

using System;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
 
namespace FEZ_Speak
{
    class SP03
    {
        // initialse the device object
        private I2CDevice _sp03;
 
        // setup constants
        private const byte SP03ADDRESS = 0x62;
        private const int SP03CLOCKRATE = 100;
 
        // setup default speaking paramaters
        private byte _volume = 0x00;
        private byte _speed = 0x03;
        private byte _pitch = 0x05;
 
        // Initialise the hardware
        public SP03()
        {
            I2CDevice.Configuration config = new I2CDevice.Configuration(SP03ADDRESS, SP03CLOCKRATE);
            _sp03 = new I2CDevice(config);
        }
 
        // Speech properties
        public byte Volume
        {
            get { return _volume; }
            set { _volume = value; }
        }
 
        public byte Speed
        {
            get { return _speed; }
            set { _speed = value; }
        }
 
        public byte Pitch
        {
            get { return _pitch; }
            set { _pitch = value; }
        }
 
        // Methods
        // Say something
        public void Say(string speech)
        {
            WaitForSpeechFinish();
            I2CDevice.I2CTransaction[] xActions = new I2CDevice.I2CTransaction[3];
            xActions[0] = I2CDevice.CreateWriteTransaction(GetSettings());
            xActions[1] = I2CDevice.CreateWriteTransaction(ConvertText(speech));
            xActions[2] = I2CDevice.CreateWriteTransaction(SayIt());
            if (_sp03.Execute(xActions, 1000) == 0)
            {
                Debug.Print("Failed to perform I2C transaction");
            }
        }
 
        private byte[] ConvertText(string text)
        {
            System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
            byte[] buffer = encoding.GetBytes(text);
            byte[] result = new byte[buffer.Length + 2];
            result[0] = 0;
            result[1] = 0;
            buffer.CopyTo(result, 2);
            return result;
        }
 
        private byte[] GetSettings()
        {
            byte[] speechConfig = new byte[] { 0, 0, _volume, _pitch, _speed };
            return speechConfig;
        }
 
        private byte[] SayIt()
        {
            return new byte[] { 0, 0x40 };
        }
 
        private void WaitForSpeechFinish()
        {
            bool speaking = true;
 
            byte[] request = new byte[1] { 0 };
 
            while (speaking)
            {
                byte[] response = new byte[1];
                I2CDevice.I2CTransaction[] xActions = new I2CDevice.I2CTransaction[2];
                xActions[0] = I2CDevice.CreateWriteTransaction(request);
                xActions[1] = I2CDevice.CreateReadTransaction(response);
                if (response[0] == 0)
                    speaking = false;
            }
        }
    }
}

And here is the code that consumed that class:

using System;
using System.Collections;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Presentation;
using Microsoft.SPOT.Presentation.Controls;
using Microsoft.SPOT.Presentation.Media;
using Microsoft.SPOT.Touch;
using Microsoft.SPOT.Hardware;
 
using Gadgeteer.Networking;
using GT = Gadgeteer;
using GTM = Gadgeteer.Modules;
using Gadgeteer.Modules.GHIElectronics;
 
namespace FEZ_Speak
{
    public partial class Program
    {
        // This method is run when the mainboard is powered up or reset.   
        void ProgramStarted()
        {
            SP03 speechUnit = new SP03();
 
            speechUnit.Say("Hello Tiny C L R.");
        }
    }
}

After running this a growly computer voice spoke the words. In case anyone doesn’t believe me, here is the evidence 😉

Jas

2012/02/16

Eeek, my first C# post! Multi-Dimensional Arrays in Micro Framework failure

Filed under: .NET, .Net Micro Framework, C#, Learning — Tags: , — vbmagic @ 1:42 pm

Hi,

As I’m using the Gadgeteer/.Net Micro Framework, I’ve been forced to learn some more c# ;-).

I came across an issue where Multi-Dimensional arrays are not compatible with the micro framework.

Thanks to the help of Architect from the TinyCLR.com forums (See his blog link “Break Continue” in the blogroll to the right of the post), I’ve now been introduced to jagged arrays in C# which solved the issue.

This failed:

private char[,] maze = new char[20,20];

So I now use the following code to achieve the same thing:

 private char[][] maze = new char[20][];
 
// Stuff
 
        private void InitialiseMaze()
        {
            for (int lp = 0; lp <= 19; lp++)
            {
                maze[lp] = new char[20];
            }
        }

To see it in use, Here is a section of code used to populate the array from the code:

// Stuff
 
        private void LoadMaze()
        {
            // read in the text file and convert to a string.
            string path = _mazePath + @"\store\" + _currentLevel + ".map";
            byte[] rawBytes = _storageDev.ReadFile(path);
            char[] rawCharacters = System.Text.UTF8Encoding.UTF8.GetChars(rawBytes);
            string tmpMap = new string(rawCharacters);
            // Now split the text file into lines using the \r character from the text file
            // note this will leave the \n character at the end of each line
            string[] lines = tmpMap.Split('\r');
            // Get the header, removing the \n
            string header = lines[0].Trim('\n');
            // check maze version and the dimensions of the maze
            string[] bits = header.Split('|');
            string ver = bits[0];
            int width = int.Parse(bits[1]);
            int height = int.Parse(bits[2]);
            if (ver != "1.0")
            {
                throw new Exception("Maze version is incorrect");
            }
            for (int y = 1; y <= height; y++)
            {
                char[] row = lines[y].Trim('\n').ToCharArray();
                for (int x = 0; x < width; x++)
                {
                    maze[x][y-1] = row[x];
                }
            }
        }

2012/02/10

Added a generator tool (Windows) in VB.net for TyranntMicro

Filed under: .NET, Fez Spider, Gadgeteer, Learning, VB.NET, Windows Forms — Tags: , , , — vbmagic @ 7:56 pm

Hi,

Added a new generator tool. So far it can just generate items:

This tool will store the items in “|” delimited text files which can easily be turned into objects using the split(“|”c) or split(‘|’) string function.

An example of the above “Arrows” item is:

1.0|Arrows||A Quiver of arrows. Holds a maximum of 10 arrows|arrowico.gif|arrow.gif|ammo|10|0|False|False|False

I was planning to do this on the Spider but it just makes more sense to do this on a desktop machine.

So Character Generator is almost finished. (Starting items are all that is required)
Item generator done
Next when I get spare time I’ll start working on the Maze Generator in the generator tool and a “Play Game” screen system on the Spider.

Older Posts »

Blog at WordPress.com.