teotestbluetooth / TestBLE / TestBLE.Android / Services / BluetoothService.cs @ 4fda4a87
Historique | Voir | Annoter | Télécharger (8,644 ko)
1 |
using Android.App; |
---|---|
2 |
using Android.Bluetooth; |
3 |
using Android.Content; |
4 |
using Android.OS; |
5 |
using Android.Runtime; |
6 |
using Android.Views; |
7 |
using Android.Widget; |
8 |
using Java.IO; |
9 |
using Java.Util; |
10 |
using System; |
11 |
using System.Collections.Generic; |
12 |
using System.Collections.ObjectModel; |
13 |
using System.Linq; |
14 |
using System.Text; |
15 |
using System.Threading; |
16 |
using System.Threading.Tasks; |
17 |
using TestBLE.Droid.Receivers; |
18 |
using TestBLE.Droid.Utils; |
19 |
using TestBLE.Models; |
20 |
using TestBLE.Services; |
21 |
|
22 |
namespace TestBLE.Droid.Services |
23 |
{ |
24 |
public class BluetoothService : IBluetoothService |
25 |
{ |
26 |
|
27 |
private List<BluetoothDevice> discoveredDevices = new List<BluetoothDevice>(); |
28 |
private TaskCompletionSource<bool> scanCompletionSource; |
29 |
private BluetoothManager bluetoothManager; |
30 |
private BluetoothAdapter bluetoothAdapter; |
31 |
private BluetoothReceiver bluetoothReceiver; |
32 |
|
33 |
private Thread listeningThread; |
34 |
private BluetoothSocket socket; |
35 |
private readonly string sppUUID = "00001101-0000-1000-8000-00805f9b34fb"; |
36 |
private string bufferedData; |
37 |
|
38 |
public ObservableCollection<BTDevice> FoundDevices = new ObservableCollection<BTDevice>(); |
39 |
public BluetoothService() |
40 |
{ |
41 |
FoundDevices = new ObservableCollection<BTDevice>(); |
42 |
bluetoothAdapter = BluetoothAdapter.DefaultAdapter; |
43 |
bluetoothReceiver = new BluetoothReceiver(); |
44 |
bluetoothReceiver.OnBluetoothDeviceDiscovered += DeviceDiscovered; |
45 |
bluetoothReceiver.OnScanFinished += ScanFinished; |
46 |
|
47 |
// abonnements du BluetoothReceiver au intent ActionFound Et ActionDiscoveryFinished |
48 |
var intentFilter = new IntentFilter(); |
49 |
intentFilter.AddAction(BluetoothDevice.ActionFound); |
50 |
intentFilter.AddAction(BluetoothAdapter.ActionDiscoveryFinished); |
51 |
intentFilter.AddAction(BluetoothDevice.ActionBondStateChanged); |
52 |
Application.Context.RegisterReceiver(bluetoothReceiver, intentFilter); |
53 |
} |
54 |
|
55 |
public async Task<bool> Connect(BTDevice btDevice) |
56 |
{ |
57 |
try |
58 |
{ |
59 |
// Si jamais on est en train de scanner |
60 |
// On annule le scan |
61 |
if (bluetoothAdapter.IsDiscovering) |
62 |
{ |
63 |
bluetoothAdapter.CancelDiscovery(); |
64 |
} |
65 |
|
66 |
var droidBtDevice = bluetoothAdapter.GetRemoteDevice(btDevice.MACAddress); |
67 |
//Android.Bluetooth.BluetoothDevice droidBtDevice = adapter.BondedDevices.FirstOrDefault(x => (x.Address == bluetoothDevice.Address)); |
68 |
if (droidBtDevice != null) |
69 |
{ |
70 |
// Si le socket est occupé pour une autre connexion |
71 |
// On ferme la connexion existante |
72 |
if (socket != null) |
73 |
{ |
74 |
await Disconnect(btDevice); |
75 |
} |
76 |
|
77 |
// Creation du canal RFCOMM (non sécurisé) |
78 |
socket = droidBtDevice.CreateInsecureRfcommSocketToServiceRecord(UUID.FromString(sppUUID)); |
79 |
|
80 |
var cts = new CancellationTokenSource(); |
81 |
|
82 |
cts.CancelAfter(50000); |
83 |
await socket.ConnectAsync().WithCancellation(cts.Token); |
84 |
|
85 |
if (listeningThread != null) listeningThread.Abort(); |
86 |
listeningThread = new Thread(async delagate => await ListeningAsync(btDevice)); |
87 |
listeningThread.Start(); |
88 |
|
89 |
return true; |
90 |
} |
91 |
} |
92 |
catch (Exception ex) |
93 |
{ |
94 |
System.Diagnostics.Debug.WriteLine(ex); |
95 |
} |
96 |
|
97 |
return false; |
98 |
} |
99 |
|
100 |
// Fonction d'écoute pour le Thread d'écoute |
101 |
private async Task ListeningAsync(BTDevice btDevice) |
102 |
{ |
103 |
try |
104 |
{ |
105 |
var buffer = new byte[1024]; |
106 |
while (socket.IsConnected) |
107 |
{ |
108 |
var byteAvailable = await socket.InputStream.ReadAsync(buffer, 0, buffer.Length); |
109 |
// Resize the byte array |
110 |
var filledBuffer = buffer.Take(byteAvailable).ToArray(); |
111 |
Application.SynchronizationContext.Post(_ => { OnDataReceived(filledBuffer); }, null); |
112 |
} |
113 |
} |
114 |
catch (IOException ex) |
115 |
{ |
116 |
System.Diagnostics.Debug.WriteLine(ex); |
117 |
} |
118 |
catch (ThreadAbortException taEx) |
119 |
{ |
120 |
System.Diagnostics.Debug.WriteLine(taEx); |
121 |
} |
122 |
catch (Exception ex) |
123 |
{ |
124 |
System.Diagnostics.Debug.WriteLine(ex); |
125 |
} |
126 |
} |
127 |
|
128 |
public EventHandler<string> DataReceivedEvent { get; set; } |
129 |
private string _bufferedData; |
130 |
|
131 |
/* |
132 |
* Fonction appelée à la reception de données |
133 |
* Trigger l'evenement DataReceivedEvent |
134 |
* |
135 |
* IMPORTANT: Les trames peuvent arriver découpées, |
136 |
* Donc les données sont gardées dans un buffer d'envoi |
137 |
* nommé _bufferedData |
138 |
*/ |
139 |
private void OnDataReceived(byte[] buffer) |
140 |
{ |
141 |
// Si le premier character est 2 (ASCII: STX) |
142 |
// Alors c'est le début de la trame. |
143 |
if (buffer[0] == 2) |
144 |
{ |
145 |
_bufferedData = ""; |
146 |
// Si character seul on quitte |
147 |
if (buffer.Length == 1) |
148 |
{ |
149 |
return; |
150 |
} |
151 |
// On enlève STX |
152 |
buffer = buffer.Skip(1).ToArray(); |
153 |
// On mets le début de la trame dans buffer d'envoi |
154 |
_bufferedData += Encoding.ASCII.GetString(buffer); |
155 |
} |
156 |
|
157 |
// Si le dernier character est 13 (ASCII: CR) |
158 |
// Alors la trame est terminée |
159 |
if (buffer[^1] == 13) |
160 |
{ |
161 |
// On enlève CR |
162 |
buffer = buffer.SkipLast(1).ToArray(); |
163 |
// Conversion en chaîne de caractères |
164 |
// Et on complète le buffer d'envoi |
165 |
_bufferedData += Encoding.ASCII.GetString(buffer); |
166 |
// Trigger evènement |
167 |
DataReceivedEvent.Invoke(this, _bufferedData); |
168 |
} |
169 |
} |
170 |
|
171 |
private void ScanFinished(object sender, string e) |
172 |
{ |
173 |
scanCompletionSource?.SetResult(true); |
174 |
} |
175 |
|
176 |
private void DeviceDiscovered(object sender, BluetoothDevice e) |
177 |
{ |
178 |
if (discoveredDevices.FirstOrDefault(x => x.Address == e.Address) is null) |
179 |
{ |
180 |
discoveredDevices.Add(e); |
181 |
FoundDevices.Add(new BTDevice() { Name = e.Name, MACAddress = e.Address }); |
182 |
} |
183 |
} |
184 |
|
185 |
public ObservableCollection<BTDevice> DeviceList { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } |
186 |
|
187 |
public Task StartScan() |
188 |
{ |
189 |
if (bluetoothAdapter.IsDiscovering) |
190 |
{ |
191 |
return scanCompletionSource.Task; |
192 |
} |
193 |
|
194 |
scanCompletionSource = new TaskCompletionSource<bool>(); |
195 |
discoveredDevices.Clear(); |
196 |
FoundDevices.Clear(); |
197 |
|
198 |
// Lancement du scan |
199 |
bluetoothAdapter.StartDiscovery(); |
200 |
|
201 |
return scanCompletionSource.Task; |
202 |
} |
203 |
|
204 |
public Task<bool> Pair(BTDevice bluetoothDevice) |
205 |
{ |
206 |
throw new NotImplementedException(); |
207 |
} |
208 |
|
209 |
public Task<bool> Disconnect(BTDevice bluetoothDevice) |
210 |
{ |
211 |
while (socket.IsConnected) socket.Close(); |
212 |
return Task.FromResult(true); |
213 |
} |
214 |
|
215 |
private byte[] hexValuesToByteArray(string[] hexValues) |
216 |
{ |
217 |
byte[] ret = hexValues.Select(value => Convert.ToByte(value, 16)).ToArray(); |
218 |
return ret; |
219 |
} |
220 |
|
221 |
public Task<bool> SendHexValues(byte[] hexValues) |
222 |
{ |
223 |
if (socket.IsConnected == false) |
224 |
{ |
225 |
return Task.FromResult(false); |
226 |
} else |
227 |
{ |
228 |
// Envoi de la commande |
229 |
socket.OutputStream.Write(hexValues, 0, hexValues.Length); |
230 |
socket.OutputStream.Flush(); |
231 |
} |
232 |
|
233 |
return Task.FromResult(true); |
234 |
} |
235 |
|
236 |
public ICollection<BTDevice> GetBondedDevices() |
237 |
{ |
238 |
ICollection<BTDevice> ret = new ObservableCollection<BTDevice>(); |
239 |
foreach (BluetoothDevice bondedDevice in bluetoothAdapter.BondedDevices) |
240 |
{ |
241 |
BTDevice toAddItem = new BTDevice |
242 |
{ |
243 |
MACAddress = bondedDevice.Address, |
244 |
Name = bondedDevice.Name |
245 |
}; |
246 |
ret.Add(toAddItem); |
247 |
} |
248 |
return ret; |
249 |
} |
250 |
} |
251 |
} |