2015-08-30 42 views
1

我需要在Xamarin.Android/iOS/Mono上通過藍牙使用SPP。這是我想對Xamarin.Android的代碼,但該行爲是相同的,如果我去到iOS和Mono在Linux/Mac上:如何通過藍牙使用Xamarin.Android/iOS/Mono和SPP?

using System; 
using System.Collections.Generic; 
using System.Text; 
using Android.Bluetooth; 
using Java.Util; 
using System.IO; 
using System.Threading; 
using PI.SDK.Devices.BC.Responses; 
using System.Threading.Tasks; 

namespace PI.SDK.Devices.BC 
{ 
    public class BluetoothDeviceConnectionChannel : IBCDeviceConnectionChannel 
    { 
     private Queue<ResponseBase> _dispatcher; 
     private bool _abort = false; 

     private BluetoothAdapter _adapter; 
     private BluetoothSocket _socket; 
     private BluetoothDevice _device; 
     private static UUID _uuid = UUID.FromString("00001101-0000-1000-8000-00805F9B34FB"); 
     private StreamReader _reader; 
     private StreamWriter _writer; 
     private string _deviceAddress; 

     public event Action<string> Notify; 
     public bool IsOpen { get { return _socket.IsConnected; } } 

     public BluetoothDeviceConnectionChannel(string deviceAddress) 
     { 
      _adapter = BluetoothAdapter.DefaultAdapter; 
      if (_adapter == null) 
       throw new PIDeviceManagerException("Bluetooth is not supported on this Android device"); 

      _deviceAddress = deviceAddress; 
     } 

     public BluetoothDeviceConnectionChannel(BluetoothDevice device) : this(device.Address) { } 

     public void Close() 
     { 
      _socket.Close(); 
     } 

     public bool Open() 
     { 
      if (!_adapter.IsEnabled) 
      { 
       throw new PIDeviceManagerException("Bluetooth is not enabled"); 
      } 

      _adapter.CancelDiscovery(); 

      _device = _adapter.GetRemoteDevice(_deviceAddress); 
      _socket = _device.CreateRfcommSocketToServiceRecord(_uuid); 
      _socket.Connect(); 

      if (_socket.IsConnected) 
      { 
       _reader = new StreamReader(_socket.InputStream, Encoding.GetEncoding("Windows-1252")); 
       _writer = new StreamWriter(_socket.OutputStream, Encoding.GetEncoding("Windows-1252")); 
       _dispatcher = new Queue<ResponseBase>(); 
       Task.Factory.StartNew(() => ReceiveData()); 
       return true; 
      } 

      return false; 
     } 

     public void ReceiveData() 
     { 
      while (_socket != null && _socket.IsConnected) 
      { 
       var data = _reader.ReadToEnd(); 

       if (string.IsNullOrWhiteSpace(data)) 
        continue; 

       var dataBuffer = data.ToCharArray(); 
       var synBuilder = new StringBuilder(); 

       foreach (var c in dataBuffer) 
       { 
        switch (c) 
        { 
         case ControlChars.NACK: 
         case ControlChars.EOT: 
#if DEBUG 
          System.Diagnostics.Debug.WriteLine($"[PINPAD -> APP] {c.ToString().Dump()}"); 
#endif 
          _abort = true; 
          return; 
         case ControlChars.ACK: 
#if DEBUG 
          System.Diagnostics.Debug.WriteLine($"[PINPAD -> APP] {c.ToString().Dump()}"); 
#endif 
          continue; 
         case ControlChars.SYN: 
          synBuilder.Append(c); 
          break; 
         case ControlChars.ETB: 
          synBuilder.Append(c); 
          var cmdResponse = synBuilder.ToString(); 
#if DEBUG 
          System.Diagnostics.Debug.WriteLine($"[PINPAD -> APP] {cmdResponse.Dump()}"); 
#endif 
          var response = CommandResponseParser.Parse(cmdResponse); 
          if (response != null) 
          { 
           _dispatcher.Enqueue(response); 
          } 
          return; 
         default: 
          synBuilder.Append(c); 
          break; 
        } 
       } 
      } 
     } 

     public ResponseBase SendData(string data) 
     { 
      _abort = false; 
      try 
      { 
       _writer.Write(data); 
      } 
      catch 
      { 
       throw new PIException("Unable to send data to device"); 
      } 
#if DEBUG 
      System.Diagnostics.Debug.WriteLine($"[APP -> PINPAD] {data.Dump()}"); 
#endif 

      if (data[0] == ControlChars.CAN) 
      { 
       Thread.Sleep(100); 
       return null; 
      } 

      while (!_abort) 
      { 
       if (_dispatcher.Count > 0) 
       { 
        var response = _dispatcher.Dequeue(); 
        if (response != null) 
        { 
         if (response is PPNotifyResponse) 
         { 
          if (Notify != null && Notify.GetInvocationList().Length > 0) 
           Notify(response.Message); 

          continue; 
         } 

         return response; 
        } 
       } 
      } 
      throw new InvalidOperationException("invalidData"); 
     } 

     public ResponseBase SendData(CommandBase data) 
     { 
      var cmd = data.ToBCCommandString(); 
      return SendData(cmd); 
     } 
    } 
} 

我想要實現的代碼波紋管用於Windows的他相同的行爲使用SerialPort類和一個COMxxx端口,這個端口只不過是一個串口藍牙COM與目標設備。

using PI.SDK.Devices.BC.Responses; 
using System; 
using System.Collections.Generic; 
using System.IO.Ports; 
using System.Text; 
using System.Threading; 

namespace PI.SDK.Devices.BC 
{ 
    public class SerialDeviceConnectionChannel : IBCDeviceConnectionChannel 
    { 
     private SerialPort _port; 
     private Queue<ResponseBase> _dispatcher; 
     private bool _abort = false; 

     public event Action<string> Notify; 
     public bool IsOpen { get { return _port.IsOpen; } } 

     public SerialDeviceConnectionChannel(string port) 
     { 
      _port = new SerialPort(port, 19200, Parity.None, 8, StopBits.One); 
      _port.ReadTimeout = 3 * 1000; 
      _port.WriteTimeout = 3 * 1000; 
      _port.Encoding = Encoding.GetEncoding(1252); 
      _port.DataReceived += DataReceived; 
     } 
     public void Close() 
     { 
      _port.Close(); 
     } 

     public bool Open() 
     { 
      while (true) 
      { 
       try 
       { 
        _port.Open(); 
        _port.DiscardInBuffer(); 
        _port.DiscardInBuffer(); 

        break; 
       } 
       catch { Console.WriteLine($"Trying to connect to {_port}"); } 
      } 
      _dispatcher = new Queue<ResponseBase>(); 
      return _port.IsOpen; 
     } 

     private void DataReceived(object sender, SerialDataReceivedEventArgs e) 
     { 
      var data = _port.ReadExisting(); 

      var dataBuffer = data.ToCharArray(); 
      var synBuilder = new StringBuilder(); 
      foreach (var c in dataBuffer) 
      { 
       switch (c) 
       { 
        case ControlChars.NACK: 
        case ControlChars.EOT: 
#if DEBUG 
         Console.WriteLine($"[PINPAD -> APP] {c.ToString().Dump()}"); 
#endif 
         _abort = true; 
         return; 
        case ControlChars.ACK: 
#if DEBUG 
         Console.WriteLine($"[PINPAD -> APP] {c.ToString().Dump()}"); 
#endif 
         continue; 
        case ControlChars.SYN: 
         synBuilder.Append(c); 
         break; 
        case ControlChars.ETB: 
         synBuilder.Append(c); 
         var cmdResponse = synBuilder.ToString(); 
#if DEBUG 
         Console.WriteLine($"[PINPAD -> APP] {cmdResponse.Dump()}"); 
#endif 
         var response = CommandResponseParser.Parse(cmdResponse); 
         if (response != null) 
         { 
          _dispatcher.Enqueue(response); 
         } 
         return; 
        default: 
         synBuilder.Append(c); 
         break; 
       } 
      }  
     } 

     public ResponseBase SendData(string data) 
     { 
      _abort = false; 
      try 
      { 
       _port.Write(data); 
      } 
      catch 
      { 
       throw new PIException("Unable to send data to device"); 
      } 
#if DEBUG 
      Console.WriteLine($"[APP -> PINPAD] {data.Dump()}"); 
#endif 

      if (data[0] == ControlChars.CAN) 
      { 
       Thread.Sleep(100); 
       return null; 
      } 

      while (!_abort) 
      { 
       if (_dispatcher.Count > 0) 
       { 
        var response = _dispatcher.Dequeue(); 
        if (response != null) 
        { 
         if (response is PPNotifyResponse) 
         { 
          if (Notify != null && Notify.GetInvocationList().Length > 0) 
           Notify(response.Message); 

          continue; 
         } 

         return response; 
        } 
       } 
      } 
      throw new InvalidOperationException("invalidData"); 
     } 

     public ResponseBase SendData(CommandBase data) 
     { 
      var cmd = data.ToBCCommandString(); 
      return SendData(cmd); 
     } 
    } 
} 

此代碼在調用_reader.ReadToEnd()時掛起。除Windows以外的所有其他平臺上。看起來我沒有得到迴應。

請注意,Android/iOS/Mono版本必須遵守Windows classe ctor上所述的串行連接配置,以及消息和串行通信的編碼必須爲Windows-1252。

任何幫助指出的錯誤或如何讓它的工作方式與在Windows上相同的方式將不勝感激,因爲沒有Seri​​alPort類,我有點在這些設備上丟失,看起來像藍牙通信是晦澀的時說話關於Xamarin /移動設備。

謝謝!

最好的問候, Gutemberg

回答

0

發現問題。在Windows中,對serialPort.Read()的調用可以是異步的,並且在Android/iOS/Mono的其他線程中,它不能。

如果我剛剛開始閱讀_writer.Write(),換句話說,在同一個線程上,我可以讓它工作得很好。