| /* |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| using System; |
| using System.Net; |
| using System.Windows; |
| using System.Windows.Controls; |
| using System.Windows.Documents; |
| using System.Windows.Ink; |
| using System.Windows.Input; |
| using System.Windows.Media; |
| using System.Windows.Media.Animation; |
| using System.Windows.Shapes; |
| using DeviceCompass = Microsoft.Devices.Sensors.Compass; |
| using System.Windows.Threading; |
| using System.Runtime.Serialization; |
| using System.Collections.Generic; |
| using System.Diagnostics; |
| using System.Globalization; |
| using System.Threading; |
| using Microsoft.Devices.Sensors; |
| |
| namespace WPCordovaClassLib.Cordova.Commands |
| { |
| |
| public class Compass : BaseCommand |
| { |
| #region Static members |
| |
| /// <summary> |
| /// Status of listener |
| /// </summary> |
| private static int currentStatus; |
| |
| /// <summary> |
| /// Id for get getCompass method |
| /// </summary> |
| private static string getCompassId = "getCompassId"; |
| |
| /// <summary> |
| /// Compass |
| /// </summary> |
| private static DeviceCompass compass = new DeviceCompass(); |
| |
| /// <summary> |
| /// Listeners for callbacks |
| /// </summary> |
| private static Dictionary<string, Compass> watchers = new Dictionary<string, Compass>(); |
| |
| #endregion |
| |
| #region Status codes |
| |
| public const int Stopped = 0; |
| public const int Starting = 1; |
| public const int Running = 2; |
| public const int ErrorFailedToStart = 4; |
| public const int Not_Supported = 20; |
| |
| /* |
| * // Capture error codes |
| CompassError.COMPASS_INTERNAL_ERR = 0; |
| CompassError.COMPASS_NOT_SUPPORTED = 20; |
| * */ |
| |
| #endregion |
| |
| #region CompassOptions class |
| /// <summary> |
| /// Represents Accelerometer options. |
| /// </summary> |
| [DataContract] |
| public class CompassOptions |
| { |
| /// <summary> |
| /// How often to retrieve the Acceleration in milliseconds |
| /// </summary> |
| [DataMember(IsRequired = false, Name = "frequency")] |
| public int Frequency { get; set; } |
| |
| /// <summary> |
| /// The change in degrees required to initiate a watchHeadingFilter success callback. |
| /// </summary> |
| [DataMember(IsRequired = false, Name = "filter")] |
| public int Filter { get; set; } |
| |
| /// <summary> |
| /// Watcher id |
| /// </summary> |
| [DataMember(IsRequired = false, Name = "id")] |
| public string Id { get; set; } |
| |
| } |
| #endregion |
| |
| |
| /// <summary> |
| /// Time the value was last changed |
| /// </summary> |
| //private DateTime lastValueChangedTime; |
| |
| /// <summary> |
| /// Accelerometer options |
| /// </summary> |
| private CompassOptions compassOptions; |
| |
| //bool isDataValid; |
| |
| //bool calibrating = false; |
| |
| public Compass() |
| { |
| |
| } |
| |
| /// <summary> |
| /// Formats current coordinates into JSON format |
| /// </summary> |
| /// <returns>Coordinates in JSON format</returns> |
| private string GetHeadingFormatted(CompassReading reading) |
| { |
| // NOTE: timestamp is generated on the JS side, to avoid issues with format conversions |
| string result = String.Format("\"magneticHeading\":{0},\"headingAccuracy\":{1},\"trueHeading\":{2}", |
| reading.MagneticHeading.ToString("0.0", CultureInfo.InvariantCulture), |
| reading.HeadingAccuracy.ToString("0.0", CultureInfo.InvariantCulture), |
| reading.TrueHeading.ToString("0.0", CultureInfo.InvariantCulture)); |
| return "{" + result + "}"; |
| } |
| |
| public void getHeading(string options) |
| { |
| if (!DeviceCompass.IsSupported) |
| { |
| DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, Not_Supported)); |
| } |
| else |
| { |
| //if (compass == null) |
| //{ |
| // // Instantiate the compass. |
| // compass = new DeviceCompass(); |
| // compass.TimeBetweenUpdates = TimeSpan.FromMilliseconds(40); |
| // compass.CurrentValueChanged += new EventHandler<Microsoft.Devices.Sensors.SensorReadingEventArgs<Microsoft.Devices.Sensors.CompassReading>>(compass_CurrentValueChanged); |
| // compass.Calibrate += new EventHandler<Microsoft.Devices.Sensors.CalibrationEventArgs>(compass_Calibrate); |
| //} |
| |
| |
| //compass.Start(); |
| |
| } |
| |
| try |
| { |
| if (currentStatus != Running) |
| { |
| lock (compass) |
| { |
| compass.CurrentValueChanged += compass_SingleHeadingValueChanged; |
| compass.Start(); |
| this.SetStatus(Starting); |
| } |
| |
| long timeout = 2000; |
| while ((currentStatus == Starting) && (timeout > 0)) |
| { |
| timeout = timeout - 100; |
| Thread.Sleep(100); |
| } |
| |
| if (currentStatus != Running) |
| { |
| DispatchCommandResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, ErrorFailedToStart)); |
| return; |
| } |
| } |
| lock (compass) |
| { |
| compass.CurrentValueChanged -= compass_SingleHeadingValueChanged; |
| if (watchers.Count < 1) |
| { |
| compass.Stop(); |
| this.SetStatus(Stopped); |
| } |
| } |
| } |
| catch (UnauthorizedAccessException) |
| { |
| DispatchCommandResult(new PluginResult(PluginResult.Status.ILLEGAL_ACCESS_EXCEPTION, ErrorFailedToStart)); |
| } |
| catch (Exception) |
| { |
| DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, ErrorFailedToStart)); |
| } |
| } |
| |
| void compass_SingleHeadingValueChanged(object sender, Microsoft.Devices.Sensors.SensorReadingEventArgs<CompassReading> e) |
| { |
| this.SetStatus(Running); |
| if (compass.IsDataValid) |
| { |
| // trueHeading :: The heading in degrees from 0 - 359.99 at a single moment in time. |
| // magneticHeading:: The heading relative to the geographic North Pole in degrees 0 - 359.99 at a single moment in time. |
| // A negative value indicates that the true heading could not be determined. |
| // headingAccuracy :: The deviation in degrees between the reported heading and the true heading. |
| //rawMagnetometerReading = e.SensorReading.MagnetometerReading; |
| |
| //Debug.WriteLine("Compass Result :: " + GetHeadingFormatted(e.SensorReading)); |
| |
| PluginResult result = new PluginResult(PluginResult.Status.OK, GetHeadingFormatted(e.SensorReading)); |
| |
| DispatchCommandResult(result); |
| } |
| } |
| |
| /// <summary> |
| /// Starts listening for compass sensor |
| /// </summary> |
| /// <returns>status of listener</returns> |
| private int start() |
| { |
| if ((currentStatus == Running) || (currentStatus == Starting)) |
| { |
| return currentStatus; |
| } |
| try |
| { |
| lock (compass) |
| { |
| watchers.Add(getCompassId, this); |
| compass.CurrentValueChanged += watchers[getCompassId].compass_CurrentValueChanged; |
| compass.Start(); |
| this.SetStatus(Starting); |
| } |
| } |
| catch (Exception) |
| { |
| this.SetStatus(ErrorFailedToStart); |
| } |
| return currentStatus; |
| } |
| |
| public void startWatch(string options) |
| { |
| if (!DeviceCompass.IsSupported) |
| { |
| DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, Not_Supported)); |
| } |
| |
| try |
| { |
| compassOptions = JSON.JsonHelper.Deserialize<CompassOptions>(options); |
| } |
| catch (Exception ex) |
| { |
| this.DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, ex.Message)); |
| return; |
| } |
| |
| if (string.IsNullOrEmpty(compassOptions.Id)) |
| { |
| this.DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION)); |
| return; |
| } |
| |
| try |
| { |
| lock (compass) |
| { |
| watchers.Add(compassOptions.Id, this); |
| compass.CurrentValueChanged += watchers[compassOptions.Id].compass_CurrentValueChanged; |
| compass.Start(); |
| this.SetStatus(Starting); |
| } |
| } |
| catch (Exception) |
| { |
| this.DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, ErrorFailedToStart)); |
| return; |
| } |
| } |
| |
| public void stopWatch(string options) |
| { |
| try |
| { |
| compassOptions = JSON.JsonHelper.Deserialize<CompassOptions>(options); |
| } |
| catch (Exception ex) |
| { |
| this.DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, ex.Message)); |
| return; |
| } |
| |
| if (string.IsNullOrEmpty(compassOptions.Id)) |
| { |
| this.DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION)); |
| return; |
| } |
| |
| if (currentStatus != Stopped) |
| { |
| lock (compass) |
| { |
| Compass watcher = watchers[compassOptions.Id]; |
| compass.CurrentValueChanged -= watcher.compass_CurrentValueChanged; |
| watchers.Remove(compassOptions.Id); |
| watcher.Dispose(); |
| } |
| } |
| this.SetStatus(Stopped); |
| |
| this.DispatchCommandResult(); |
| } |
| |
| void compass_Calibrate(object sender, Microsoft.Devices.Sensors.CalibrationEventArgs e) |
| { |
| //throw new NotImplementedException(); |
| // TODO: pass calibration error to JS |
| } |
| |
| void compass_CurrentValueChanged(object sender, Microsoft.Devices.Sensors.SensorReadingEventArgs<CompassReading> e) |
| { |
| this.SetStatus(Running); |
| if (compass.IsDataValid) |
| { |
| // trueHeading :: The heading in degrees from 0 - 359.99 at a single moment in time. |
| // magneticHeading:: The heading relative to the geographic North Pole in degrees 0 - 359.99 at a single moment in time. |
| // A negative value indicates that the true heading could not be determined. |
| // headingAccuracy :: The deviation in degrees between the reported heading and the true heading. |
| //rawMagnetometerReading = e.SensorReading.MagnetometerReading; |
| |
| //Debug.WriteLine("Compass Result :: " + GetHeadingFormatted(e.SensorReading)); |
| |
| PluginResult result = new PluginResult(PluginResult.Status.OK, GetHeadingFormatted(e.SensorReading)); |
| result.KeepCallback = true; |
| |
| DispatchCommandResult(result); |
| } |
| } |
| |
| /// <summary> |
| /// Sets current status |
| /// </summary> |
| /// <param name="status">current status</param> |
| private void SetStatus(int status) |
| { |
| currentStatus = status; |
| } |
| |
| } |
| } |