Monday, March 4, 2013

Adding async/await support to WP7 projects

When porting changes made to a Windows Phone 8 app back to the Windows Phone 7.x version I ran into an issue due to the lack of async/await support.

Turns out you can add the Microsoft.Bcl.Async Nuget package to add support for Async in Windows Phone 7.x. Note that at this time it is prerelease, so you may need to indicate as such to nuget to install it.

Once added I could then create some extension methods for the Live SDK to convert the event pattern to the async pattern:

    public static class LiveConnectClientExtension
    {
        public static Task GetAsyncTask(this LiveConnectClient client, string path)
        {
            var tcs = new TaskCompletionSource();
            client.GetCompleted += (s,e) => 
            {
                if (e.Error != null) tcs.TrySetException(e.Error);
                else if (e.Cancelled) tcs.TrySetCanceled();
                else tcs.TrySetResult( new LiveOperationResult(e.Result, e.RawResult));
            };
            client.GetAsync(path);
            return tcs.Task;
        }
    }

Critical Sections

Now that things are occurring asynchronously their is the possibility of the user triggering multiple threads. See Awaitable critical section for a tidy solution.

See Also:

Tuesday, February 26, 2013

Windows Phone - NZ Tides

About NZ Tides for Windows Phone

Tidal predictions for New Zealand ports using data from LINZ.

Values are for standard barometric pressure.

Features:

  • View High and Low tide times and heights for a given day between 2011 and 2013 inclusive.
  • Use the graph and slider to calculate tide heights for a given time of day.
  • No network connection is required, all data is embedded within the app.
  • Select port by name or from a map
  • Automatically adjusts times for daylight savings
  • View the tides levels for the current month.

Versions:

  • 2.1
    • Pin individual ports
  • 2.0
    • Locations can now have aerial maps (optional)
    • Reformatted date and time selection. The slider can be toggled in.
  • 1.2
    • Added secondary ports.
    • Pin a location to the live tile.
    • Hide unused Locations.
    • Calculate sunrise and sunset times for location.
  • 1.1
    • Fix for issue with tide graph on year transition.
    • Tapping the rise or set times in the compass view will toggle to showing the time until the event occurs.

Tips and FAQ

Customising locations

The "add locations" application bar button can be used to show and hide primary ports and to remove any secondary ports that have been added to the locations.

Tuesday, February 19, 2013

Making a Salesforce Id case insensitive in Excel

I have an Excel spreadsheet with a large number of rows. Each row includes the 15 character case sensitive version of a Salesforce Id. I need to find the rows that contain duplicate Salesforce Ids.

With the 15 character Salesforce Ids being case sensitive, and Excel by and large being case insensitive this presents an issue.

Possible Solutions

  1. Export the data from Salesforce using the 18 character Salesforce Ids. These are specifically designed to be case insensitive.
  2. Use an Excel formula to create another column containing a case insensitive value.
    =CD2&CODE(MID(CD2,12,1))&CODE(MID(CD2,13,1))&CODE(MID(CD2,14,1))&CODE(MID(CD2,15,1))
    Here the cell CD2 contains the case sensitive Salesforce Id.
    This formula is appending the ASCII character value for each of the last 4 characters in the ID.
    For example, an ID that ends with "yBSe" will have "1216683101" appended.
    You may need to extend the number of checked characters if you have a large number of records. As it is only the last 4 characters of the input Id will be case checked.
  3. Use VBA to calculate the 18 character case insensitive values.
    Place the following into a VBA module.
    Function FixID(InID As String) As String
    ' Converts a 15 character ID to an 18 character, case-insensitive one ...
    Dim InChars As String, InI As Integer, InUpper As String
    Dim InCnt As Integer
    InChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345"
    InUpper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    
    InCnt = 0
    For InI = 15 To 1 Step -1
       InCnt = 2 * InCnt + Sgn(InStr(1, InUpper, Mid(InID, InI, 1), vbBinaryCompare))
       If InI Mod 5 = 1 Then
           FixID = Mid(InChars, InCnt + 1, 1) + FixID
           InCnt = 0
           End If
        Next InI
        FixID = InID + FixID
    End Function
    

    Then reference the function directly in a formula.

See Also:

Wednesday, January 23, 2013

Using the Burp Suite to test a Web Service that is consumed in a Salesforce app

The following steps can be used to run the Burp Suite scanner against a Web service that is consumed in a Salesforce app via callouts.

The basic idea is to:

  1. import the Web Service WSDL into SOAP UI,
  2. configure SOAP UI to use the Burp Proxy,
  3. use SOAP UI to simulate the SOAP requests for typical use cases, This will require updating the sample requests generated in SOAP UI to represent those made from Salesforce under normal usage.
  4. select the requests to scan from the Burp Target, Site map tabs

Get a Burp Suite License

ISV partners can submit a Burp License Request.

Install and run Burp

I put the Burp jar file and license txt file in a directory and started it with the following in a batch file:

java -jar -Xmx1024m burpsuite_pro_v1.5.04.jar

See Also: Getting Started With Burp Suite

Configure Burp

Turn “Intercept” (Proxy->Intercept) off within Burp.

Configure SOAP UI to use the Burp Proxy

File > Preferences > Proxy Settings:

127.0.0.1:8080
You can find the Burp Proxy details Under Proxy > Options > Proxy Listeners

You may need to installed Burp's generated CA Certificate into the Trusted Root Certification Authorities tab if using SSL. See Installing Burp's CA Certificate. If not configured you get the following error message in Soap UI "Error getting response; javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated."

For SoapUI to pickup the new cert:

  1. Go to the path: C:\Program Files (x86)\SmartBear\SoapUI-5.0.0\jre\lib\security
  2. Export the Burp CA Certificate using Proxy > Options > CA certificate... > Export: Certificate in DER format.
  3. Use the Java keytool executable to import the certificate:
    "C:\Program Files (x86)\Java\jdk1.7.0_67\bin\keytool.exe" -import -alias burp -file "C:\WhereYouExportedTheDerCertificate\PortSwiggerCA.cer" -keystore cacerts
  4. The keystore password will be:
    changeit

Use SOAP UI to simulate the web requests that Salesforce would make to the web service

This will require you to update the sample requests that SOAP UI generates for each of the web methods with realistic request data. Try to mimic the calls that Salesforce will be making.

When the SOAP UI requests are submitted Burp will record them under the Target > Site map tab.

Start the Burp Scanner

Under the Target > Site map tab select the request nodes or host/branch that you want to scan. If it was a website you would usually do an "Spider this branch" at this point. Start the Scanner for the branches by selecting "Actively scan this branch".

Under the Scanner > Scan Queue tab the requests will appear and be processed. The output will start appearing under the Scanner > Results tab.

Export the Burp Scanner Results

Under the Scanner > Scan Queue tab select the results of interest then right click and select "Report selected issues"

The Printer-friendly version with hyperlinks works well for both screen reading and printing. Defaults can be used for the remaining steps. Ensure you save the report in a file with the ".html" extension.


See Also:

Tuesday, December 25, 2012

Windows Phone 8 project update fails with "reference cannot be removed from the project because it is always referenced by the compiler"

When using Visual Studio 2012 to upgrade an existing WP7.1 project to WP8 the process failed with the message:

Upgrade to the project could not be completed. This reference cannot be removed from the project because it is always referenced by the compiler.

Followed by:

Upgrade to the project could not be completed. Error HRESULT E_FAIL has been returned from a call to a COM component.

I found the solution in the post Upgrade Windows Phone 7.1 project to Windows Phone 8?

My project was missing the reference to System.Core.dll before attempting the upgrade. The was a reference to mscorlib.dll.

For some unknown reason attempting to add the System.Core reference to the project from Visual Studio didn't work. It would check in the add dialog but wouldn't then be present in the list of references.

In the end I needed to manually add the reference to the project via notepad.

After that the reference appeared as required in Visual Studio and the upgrade to the Windows Phone 8 project succeeded.

Saturday, December 15, 2012

A mock Windows Phone Microsoft.Devices.Sensors.Motion API for emulator support

The Windows Phone 7/8 emulators provide accelerometer support, but not support for the Microsoft.Devices.Sensors.Motion API.

I've created MotionWrapper and MockMotionReading classes that can provide fake data when using the emulator in debug mode. When you switch to a real device or a release build it reverts to an underlying Motion instance.

At this stage the MockMotionReading only varies the pitch, roll, and yaw and then calculates the corresponding RotationMatrix and Quaternion. The Gravity vector is left unchanged.

Ideally I'd like to use the accelerometer data to determine a Gravity vector so it could be varied during testing.

MotionWrapper

/// <summary>
    /// Provides Windows Phone applications information about the device’s orientation and motion.
    /// </summary>
    public class MotionWrapper //: SensorBase<MotionReading> // No public constructors, nice one.
    {
        private Motion motion;

        public event EventHandler<SensorReadingEventArgs<MockMotionReading>> CurrentValueChanged;

        #region Properties
        /// <summary>
        /// Gets or sets the preferred time between Microsoft.Devices.Sensors.SensorBase<TSensorReading>.CurrentValueChanged events.
        /// </summary>
        public virtual TimeSpan TimeBetweenUpdates
        {
            get
            {
                return motion.TimeBetweenUpdates;
            }
            set
            {
                motion.TimeBetweenUpdates = value;
            }
        }

        /// <summary>
        /// Gets or sets whether the device on which the application is running supports the sensors required by the Microsoft.Devices.Sensors.Motion class.
        /// </summary>
        public static bool IsSupported
        {
            get
            {
#if(DEBUG)
                return true;
#else
                return Motion.IsSupported;
#endif

            }
        }
        #endregion

        #region Constructors
        protected MotionWrapper()
        {
        }

        protected MotionWrapper(Motion motion)
        {
            this.motion = motion;
            this.motion.CurrentValueChanged += motion_CurrentValueChanged;
        }
        #endregion

        /// <summary>
        /// Get an instance of the MotionWrappper that supports the Motion API
        /// </summary>
        /// <returns></returns>
        public static MotionWrapper Instance()
        {
#if(DEBUG)
            if (!Motion.IsSupported)
            {
                return new MockMotionWrapper();
            }
#endif
            return new MotionWrapper(new Motion());
        }

        /// <summary>
        /// The value from the underlying Motion API has changed. Relay it on within a MockMotionReading.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void motion_CurrentValueChanged(object sender, SensorReadingEventArgs<MotionReading> e)
        {
            var f = new SensorReadingEventArgs<MockMotionReading>();
            f.SensorReading = new MockMotionReading(e.SensorReading);
   RaiseValueChangedEvent(sender, f);
        }

        protected void RaiseValueChangedEvent(object sender, SensorReadingEventArgs<MockMotionReading> e)
        {
            if (CurrentValueChanged != null)
            {
                CurrentValueChanged(this, e);
            }
        }

        /// <summary>
        /// Starts acquisition of data from the sensor.
        /// </summary>
        public virtual void Start()
        {
            motion.Start();
        }

        /// <summary>
        /// Stops acquisition of data from the sensor.
        /// </summary>
        public virtual void Stop()
        {
            motion.Stop();
        }
    }

MockMotionWrapper

/// <summary>
    /// Provides Windows Phone applications mock information about the device’s orientation and motion.
    /// </summary>
    public class MockMotionWrapper : MotionWrapper
    {
        /// <summary>
        /// Use a timer to trigger simulated data updates.
        /// </summary>
        private DispatcherTimer timer;

        private MockMotionReading lastCompassReading = new MockMotionReading(true);

        #region Properties
        /// <summary>
        /// Gets or sets the preferred time between Microsoft.Devices.Sensors.SensorBase<TSensorReading>.CurrentValueChanged events.
        /// </summary>
        public override TimeSpan TimeBetweenUpdates
        {
            get
            {
                return timer.Interval;
            }
            set
            {
                timer.Interval = value;
            }
        }
        #endregion

        #region Constructors
        public MockMotionWrapper()
        {
            timer = new DispatcherTimer();
            timer.Interval = TimeSpan.FromMilliseconds(30);
            timer.Tick += new EventHandler(timer_Tick);
        }
        #endregion

        void timer_Tick(object sender, EventArgs e)
        {
            var reading = new Microsoft.Devices.Sensors.SensorReadingEventArgs<MockMotionReading>();
            lastCompassReading = new MockMotionReading(lastCompassReading);
            reading.SensorReading = lastCompassReading;

            //if (lastCompassReading.HeadingAccuracy > 20)
            //{
            //    RaiseValueChangedEvent(this, new CalibrationEventArgs());
            //}

            RaiseValueChangedEvent(this, reading);
        }

        /// <summary>
        /// Starts acquisition of data from the sensor.
        /// </summary>
        public override void Start()
        {
            timer.Start();
        }

        /// <summary>
        /// Stops acquisition of data from the sensor.
        /// </summary>
        public override void Stop()
        {
            timer.Stop();
        }

    }

MockMotionReading

//Microsoft.Devices.Sensors.MotionReading
    /// <summary>
    /// Contains information about the orientation and movement of the device.
    /// </summary>
    public struct MockMotionReading : Microsoft.Devices.Sensors.ISensorReading
    {
        public static bool RequiresCalibration = false;

        #region Properties
        /// <summary>
        /// Gets the attitude (yaw, pitch, and roll) of the device, in radians.
        /// </summary>
        public MockAttitudeReading Attitude { get; internal set; }

        /// <summary>
        ///  Gets the linear acceleration of the device, in gravitational units.
        /// </summary>
        public Vector3 DeviceAcceleration { get; internal set; }

        /// <summary>
        /// Gets the rotational velocity of the device, in radians per second.
        /// </summary>
        public Vector3 DeviceRotationRate { get; internal set; }

        /// <summary>
        /// Gets the gravity vector associated with the Microsoft.Devices.Sensors.MotionReading.
        /// </summary>
        public Vector3 Gravity { get; internal set; }

        /// <summary>
        /// Gets a timestamp indicating the time at which the accelerometer reading was
        ///     taken. This can be used to correlate readings across sensors and provide
        ///     additional input to algorithms that process raw sensor data.
        /// </summary>
        public DateTimeOffset Timestamp { get; internal set; }
        #endregion

        #region Constructors

        /// <summary>
        /// Initialize an instance from an actual MotionReading
        /// </summary>
        /// <param name="cr"></param>
        public MockMotionReading(MotionReading cr)
            : this()
        {
            this.Attitude = new MockAttitudeReading(cr.Attitude);
            this.DeviceAcceleration = cr.DeviceAcceleration;
            this.DeviceRotationRate = cr.DeviceRotationRate;
            this.Gravity = cr.Gravity;
            this.Timestamp = cr.Timestamp;
        }

        /// <summary>
        /// Create an instance initialized with testing data
        /// </summary>
        /// <param name="test"></param>
        public MockMotionReading(bool test) 
            : this()
        {
            float pitch = 0.01f;
            float roll = 0.02f;
            float yaw = 0.03f;

            this.Attitude = new MockAttitudeReading()
            {
                Pitch = pitch,
                Roll = roll,
                Yaw = yaw,
                RotationMatrix = Matrix.CreateFromYawPitchRoll(yaw, pitch, roll),  
                Quaternion = Quaternion.CreateFromYawPitchRoll(yaw, pitch, roll), 

                Timestamp = DateTimeOffset.Now
            };

            // TODO: pull data from the Accelerometer
            this.Gravity = new Vector3(0, 0, 1f);
        }

        /// <summary>
        /// Create a new mock instance based on the previous mock instance
        /// </summary>
        /// <param name="lastCompassReading"></param>
        public MockMotionReading(MockMotionReading lastCompassReading)
            : this()
        {
            // Adjust the pitch, roll, and yaw as required.

            // -90 to 90 deg
            float pitchDegrees = MathHelper.ToDegrees(lastCompassReading.Attitude.Pitch) - 0.5f;
            //pitchDegrees = ((pitchDegrees + 90) % 180) - 90;

            // -90 to 90 deg
            float rollDegrees = MathHelper.ToDegrees(lastCompassReading.Attitude.Roll);
            //rollDegrees = ((rollDegrees + 90) % 180) - 90;

            // 0 to 360 deg
            float yawDegrees = MathHelper.ToDegrees(lastCompassReading.Attitude.Yaw) + 0.5f;
            //yawDegrees = yawDegrees % 360;

            float pitch = MathHelper.ToRadians(pitchDegrees);
            float roll = MathHelper.ToRadians(rollDegrees);
            float yaw = MathHelper.ToRadians(yawDegrees);

            this.Attitude = new MockAttitudeReading()
            {
                Pitch = pitch,
                Roll = roll,
                Yaw = yaw,
                RotationMatrix = Matrix.CreateFromYawPitchRoll(yaw, pitch, roll),
                Quaternion = Quaternion.CreateFromYawPitchRoll(yaw, pitch, roll),
                
                Timestamp = DateTimeOffset.Now
            };
            
            this.DeviceAcceleration = lastCompassReading.DeviceAcceleration;
            this.DeviceRotationRate = lastCompassReading.DeviceRotationRate;
            this.Gravity = lastCompassReading.Gravity;
            Timestamp = DateTime.Now;

        }
        #endregion



    }

MockAttitudeReading

public struct MockAttitudeReading : ISensorReading
    {
        public MockAttitudeReading(AttitudeReading attitudeReading) : this()
        {
            Pitch = attitudeReading.Pitch;
            Quaternion = attitudeReading.Quaternion;
            Roll = attitudeReading.Roll;
            RotationMatrix = attitudeReading.RotationMatrix;
            Timestamp = attitudeReading.Timestamp;
            Yaw = attitudeReading.Yaw;
        }

        /// <summary>
        /// Gets the pitch of the attitude reading in radians.
        /// </summary>
        public float Pitch { get; set; }
        
        /// <summary>
        /// Gets the quaternion representation of the attitude reading.
        /// </summary>
        public Quaternion Quaternion { get; set; }
        
        /// <summary>
        /// Gets the roll of the attitude reading in radians.
        /// </summary>
        public float Roll { get; set; }
        
        /// <summary>
        /// Gets the matrix representation of the attitude reading.
        /// </summary>
        public Matrix RotationMatrix { get; set; }

        /// <summary>
        /// Gets a timestamp indicating the time at which the accelerometer reading was
        ///     taken. This can be used to correlate readings across sensors and provide
        ///     additional input to algorithms that process raw sensor data.
        /// </summary>
        public DateTimeOffset Timestamp { get; set; }
        
        /// <summary>
        /// Gets the yaw of the attitude reading in radians.
        /// </summary>
        public float Yaw { get; set; }
    }

See Also:

Wednesday, December 12, 2012

Days between two DateTime values excluding weekends

Given two DateTime values in Apex, how many weekdays are there between the two?

Getting the total number of days is straight forward using Date.daysBetween(). Excluding the weekends requires a bit more work.

One possible solution is to just loop through all the dates between the two values and count any that aren't Saturday or Sunday. It's a bit crude and will be expensive if the dates are far apart. It should however work through pure brute force.

    public static Integer daysBetweenExcludingWeekends(Datetime startDate, Datetime endDate) {
        Integer i = 0;

        while (startDate < endDate) {
            if (startDate.format('EEE') != 'Sat' && startDate.format('EEE') != 'Sun') {
                i = i + 1;
            }
            startDate = startDate.addDays(1);
        }
          
        return i;
    }

See also: