接入基本设备

This commit is contained in:
suntao 2021-06-04 10:35:58 +08:00
parent 2f84f70a5f
commit 7729332f41
62 changed files with 2880 additions and 86 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

@ -0,0 +1,104 @@
fileFormatVersion: 2
guid: 05133d14d67abd743954cf1d04471e99
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: -1
mipBias: -100
wrapU: 1
wrapV: 1
wrapW: -1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -0,0 +1,104 @@
fileFormatVersion: 2
guid: 4990d472dee7f3b428ae030c3a2b936f
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: -1
mipBias: -100
wrapU: 1
wrapV: 1
wrapW: -1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@ -34,7 +34,7 @@ RectTransform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 124, y: -40}
m_AnchoredPosition: {x: 60, y: -40}
m_SizeDelta: {x: 62, y: 64}
m_Pivot: {x: 0, y: 1}
--- !u!1 &2745425216517653314
@ -111,6 +111,43 @@ MonoBehaviour:
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!1 &2777627832895409916
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 2165555432541117810}
m_Layer: 5
m_Name: AntIcon
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &2165555432541117810
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2777627832895409916}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children:
- {fileID: 540031521672414432}
- {fileID: 4177612200037049886}
m_Father: {fileID: 6430829710355291381}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 24.4, y: 1}
m_SizeDelta: {x: 44, y: 44}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!1 &3159732546393567601
GameObject:
m_ObjectHideFlags: 0
@ -140,15 +177,15 @@ RectTransform:
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children:
- {fileID: 540031521672414432}
- {fileID: 4177612200037049886}
- {fileID: 2165555432541117810}
- {fileID: 7669984967702354254}
m_Father: {fileID: 2716191576886074694}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 64, y: 64}
m_SizeDelta: {x: 118, y: 64}
m_Pivot: {x: 0, y: 1}
--- !u!222 &6268486626046139400
CanvasRenderer:
@ -705,6 +742,80 @@ CanvasGroup:
m_Interactable: 1
m_BlocksRaycasts: 1
m_IgnoreParentGroups: 0
--- !u!1 &5162363420470615978
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 652708983550405284}
- component: {fileID: 2795585007386087676}
- component: {fileID: 6254887144258792832}
m_Layer: 5
m_Name: Image
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &652708983550405284
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5162363420470615978}
m_LocalRotation: {x: 0.08715578, y: -0, z: -0, w: 0.9961947}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 7669984967702354254}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 10, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 44, y: 44}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &2795585007386087676
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5162363420470615978}
m_CullTransparentMesh: 0
--- !u!114 &6254887144258792832
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5162363420470615978}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 21300000, guid: 407dc13d102a8d842bbaa773773df936, type: 3}
m_Type: 0
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!1 &5440549946619805752
GameObject:
m_ObjectHideFlags: 0
@ -741,7 +852,7 @@ RectTransform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 1}
m_AnchorMax: {x: 0.5, y: 1}
m_AnchoredPosition: {x: 0, y: -90}
m_AnchoredPosition: {x: 35.2, y: -90}
m_SizeDelta: {x: 134, y: 34}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &3527654833021650121
@ -974,11 +1085,11 @@ RectTransform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6502171739230510504}
m_LocalRotation: {x: 0.08715578, y: 0, z: 0, w: 0.9961947}
m_LocalRotation: {x: 0.08715578, y: -0, z: -0, w: 0.9961947}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 6430829710355291381}
m_Father: {fileID: 2165555432541117810}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 10, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
@ -1048,11 +1159,11 @@ RectTransform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7066476020851680862}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 6430829710355291381}
m_Father: {fileID: 2165555432541117810}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
@ -1175,6 +1286,117 @@ MonoBehaviour:
m_VerticalOverflow: 0
m_LineSpacing: 1
m_Text: Searching...
--- !u!1 &8591994605769169618
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 6521823677394231837}
- component: {fileID: 743327725710735410}
- component: {fileID: 1839312524778472856}
m_Layer: 5
m_Name: Ble
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &6521823677394231837
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8591994605769169618}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 7669984967702354254}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 44, y: 44}
m_Pivot: {x: 0, y: 1}
--- !u!222 &743327725710735410
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8591994605769169618}
m_CullTransparentMesh: 0
--- !u!114 &1839312524778472856
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8591994605769169618}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 21300000, guid: 05133d14d67abd743954cf1d04471e99, type: 3}
m_Type: 0
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!1 &8808031170711301744
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 7669984967702354254}
m_Layer: 5
m_Name: BleIcon
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &7669984967702354254
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8808031170711301744}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children:
- {fileID: 6521823677394231837}
- {fileID: 652708983550405284}
m_Father: {fileID: 6430829710355291381}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: -28.5, y: 1.4}
m_SizeDelta: {x: 44, y: 44}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!1001 &564608893206712879
PrefabInstance:
m_ObjectHideFlags: 0

View File

@ -141,6 +141,7 @@ namespace Assets.Scripts.Apis
public async Task<JsonResult<object>> GetEarthData(double lat, double lon)
{
//CultureInfo.InvariantCulture
var result = await GetAsync<JsonResult<object>>($"Map/GetEarthData?lat={ lat }&lon={ lon }");
return result;

View File

@ -26,6 +26,20 @@ namespace Assets.Scripts.Devices
get; protected set;
}
public NetworkType Network { get; internal set; }
/// <summary>
/// 信号强度
/// 信号从0dbm~97dbm个人建议是0~30是强30~70是中70~97是弱。
/// </summary>
public int SignalStrength { get; internal set; }
protected AbstractDevice(NetworkType networkType)
{
this.Network = networkType;
}
public abstract void Connect();
public abstract void Disconnect(bool save = true);

View File

@ -26,7 +26,7 @@ namespace Assets.Scripts.Devices.Ant
public readonly byte uid;
public DataSourceBase(racerSportType sportType, bool isHuman)
public DataSourceBase(racerSportType sportType, bool isHuman):base(NetworkType.ANT)
{
this.sportType = sportType;
this.isHuman = isHuman;

View File

@ -9,7 +9,7 @@ using UnityEngine;
namespace Assets.Scripts.Devices.Ant
{
public class FitDevice : AbstractAntDevice, ISpeedDevice, IPowerDevice, ICadenceDevice, IRequiresRiderWeight
public class FitDevice : AbstractAntDevice, ISpeedDevice, IPowerDevice, ICadenceDevice, IRequiresRiderWeight, ITrainerDevice
{
public const int MAX_NO_EVENT_STOP_COUNT = 12;

View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Assets.Scripts.Devices.Ant.Interfaces
{
public interface ITrainerDevice
{
void SetErgMode(int targetPower);
void SetResistanceMode(double value);
void SetWindResistance(double? height = null);
void SetTrackResistance(double grade);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 362cacfc69d803d478112e7c293cfa17
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -39,4 +39,10 @@ namespace Assets.Scripts.Devices.Ant
Trainer,
VirtualPower,
}
public enum NetworkType
{
BLE,
ANT
}
}

View File

@ -2,6 +2,8 @@
using Assets.Scripts.Ble.HeartRate;
using Assets.Scripts.Ble.Service;
using Assets.Scripts.Devices.Ant;
using Assets.Scripts.Devices.Ble.Characteristic;
using Assets.Scripts.Devices.Ble.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
@ -22,17 +24,23 @@ namespace Assets.Scripts.Devices.Ble
public override string Name { get => peripheralInfo.Name; protected set => base.Name = value; }
public BleDevice(BlePeripheralInfo peripheralInfo, BleWinHwInterface bleWinHwInterface, SensorType sensor) //: base(peripheralInfo)
public List<ICharacteristic> Characteristics { get; protected set; } = new List<ICharacteristic>();
public BleDevice(BlePeripheralInfo peripheralInfo, BleWinHwInterface bleWinHwInterface, SensorType sensor) : base(NetworkType.BLE)
{
this.hwInterface = bleWinHwInterface;
this.hwInterface.BluetoothStateChangedEvent += BluetoothStateChangedEvent;
this.peripheralInfo = peripheralInfo;
base.Sensor = sensor;
//base.Name = this.peripheralInfo.Name;
//Debug.Log(base.Name + "," + sensor.ToString());
Characteristics.Add(new BatteryLevel());
Characteristics.Add(new ManufacturerNameCharacteristic());
Characteristics.Add(new ModelNumberCharacteristic());
}
public void ConnectToPeripheralIfPossible()
@ -230,7 +238,7 @@ namespace Assets.Scripts.Devices.Ble
public override void Disconnect(bool save = true)
{
//throw new NotImplementedException();
this.hwInterface.DisconnectPeripheral(this.peripheralInfo, null);
}
}
}

View File

@ -43,11 +43,19 @@ namespace Assets.Scripts.Devices.Ble
{
if (!discoveredDevices.ContainsKey(device.Peripheral.Address))
{
Debug.Log("发现设备" + device.Peripheral.Address + device.Peripheral.Name + ", type:" + device.SensorType);
Debug.Log($"发现设备{ device.Peripheral.Address }, \t\tName:{ device.Peripheral.Name }, type:{ device.SensorType }");
if (string.IsNullOrWhiteSpace(device.Peripheral.Name))
{
return;
}
if(device.SensorType == Ant.SensorType.Trainer)
{
//var device1 = new Ftms(device.Peripheral, hwInterface);
//discoveredDevices.Add(device.Peripheral.Address, device1);
var device1 = new Tacx(device.Peripheral, hwInterface);
discoveredDevices.Add(device.Peripheral.Address, device1);
}
else if(device.SensorType == Ant.SensorType.HeartRate)
{
@ -59,8 +67,18 @@ namespace Assets.Scripts.Devices.Ble
var device1 = new CyclingPower(device.Peripheral, hwInterface);
discoveredDevices.Add(device.Peripheral.Address, device1);
}
else if(device.SensorType == Ant.SensorType.SpeedCadence)
{
var device1 = new SpeedCadence(device.Peripheral, hwInterface);
discoveredDevices.Add(device.Peripheral.Address, device1);
}
//discoveredDevices.Add(device.Peripheral.Address, new BleDevice(device.Peripheral, hwInterface, device.SensorType));
}
if (discoveredDevices.ContainsKey(device.Peripheral.Address))
{
discoveredDevices[device.Peripheral.Address].SignalStrength = device.Rssi;
//Debug.Log($"设备{ device.Peripheral.Name }信号量:{ device.Rssi }");
}
});
}

View File

@ -0,0 +1,49 @@
using Assets.Scripts.Ble;
using Assets.Scripts.Devices.Ble.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace Assets.Scripts.Devices.Ble.Characteristic
{
public class BatteryLevel : ICharacteristic
{
public Guid Uuid => ServiceUuids.Characteristics.BatteryLevel;
public Guid ServiceUuid => ServiceUuids.Get(ServiceUuids.Battery).IdGuid;
private ICharacteristic _characteristicUnavailable = null;
public ICharacteristic CharacteristicUnavailable
{
get
{
return this._characteristicUnavailable;
}
}
public virtual bool IsOptional
{
get
{
return true;
}
}
public int BatteryLevelValue { get; private set; }
public void HandleAttributeReceived(byte[] data)
{
BatteryLevelValue = (int)data[0];
Debug.Log($"电量:{ BatteryLevelValue }");
}
public void SetUnavailable()
{
this._characteristicUnavailable = this;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b654b3a52c37e6f47b9823953c8fd5d1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,57 @@
using Assets.Scripts.Ble;
using Assets.Scripts.Devices.Ble.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Assets.Scripts.Devices.Ble.Characteristic
{
public class CyclingPowerMeasurement: ICharacteristic
{
public Guid Uuid
{
get
{
return ServiceUuids.Characteristics.CyclingPowerMeasurement;
}
}
public Guid ServiceUuid
{
get
{
return ServiceUuids.Get(ServiceUuids.CyclingPower).IdGuid;
}
}
public virtual bool IsOptional
{
get
{
return false;
}
}
private double wheelCircumference;
public int Power { get; private set; }
public CyclingPowerMeasurement(double wheelCircumference)
{
this.wheelCircumference = wheelCircumference;
}
public void HandleAttributeReceived(byte[] data)
{
this.Power = BitConverter.ToInt16(data, 2);
}
public void SetUnavailable()
{
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9b0646295dbaa0a4e951de9bd391e475
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,271 @@
using Assets.Scripts.Ble;
using Assets.Scripts.Devices.Ble.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using Assets.Scripts.Devices.Ble.Extension;
namespace Assets.Scripts.Devices.Ble.Characteristic
{
public class CyclingSpeedCadenceMeasurement : ICharacteristic
{
private CyclingSpeedCadenceMeasurement.IUpdate _currentUpdate;
private CyclingSpeedCadenceMeasurement.IUpdate _previousUpdate = new CyclingSpeedCadenceMeasurement.NullUpdate();
private const byte MaxNoEvents = (byte)4;
private byte _noCadenceEventCount;
private byte _noSpeedEventCount;
public Guid Uuid => ServiceUuids.Characteristics.CscMeasurement;
public Guid ServiceUuid => ServiceUuids.Get(ServiceUuids.CyclingSpeedCadence).IdGuid;
public bool IsOptional => false;
public double Speed { get; private set; }
public int Cadence { get; private set; }
public double WheelCircumference { private get; set; }
public CyclingSpeedCadenceMeasurement(double wheelCircumference)
{
this.WheelCircumference = wheelCircumference;
}
public void HandleAttributeReceived(byte[] data)
{
//throw new NotImplementedException();
//Debug.Log(string.Join(",", data));
this._currentUpdate = new CyclingSpeedCadenceMeasurement.Update(data);
this.HandleCadenceUpdate();
this.HandleSpeedUpdate();
this._previousUpdate = this._currentUpdate;
}
private void HandleCadenceUpdate()
{
if (this._currentUpdate.HasCadence && (int)this._currentUpdate.CadenceEventCount != (int)this._previousUpdate.CadenceEventCount)
{
this._noCadenceEventCount = (byte)0;
if (this.CadenceTimeDifference() <= 0)
return;
this.Cadence = this.CalculateCadence();
}
else
{
this._noCadenceEventCount = (byte)((uint)this._noCadenceEventCount + 1U);
if ((int)this._noCadenceEventCount < 4)
return;
this.Cadence = 0;
}
}
private void HandleSpeedUpdate()
{
if (this._currentUpdate.HasSpeed && (int)this._currentUpdate.SpeedEventCount != (int)this._previousUpdate.SpeedEventCount)
{
this._noSpeedEventCount = (byte)0;
double num = this.CalculateSpeed();
if (num >= 150.0)
return;
this.Speed = num;
}
else
{
this._noSpeedEventCount = (byte)((uint)this._noSpeedEventCount + 1U);
if ((int)this._noSpeedEventCount < 4)
return;
this.Speed = 0.0;
}
}
private int CalculateCadence()
{
int num = this.CadenceEventDifference() * 61440 / this.CadenceTimeDifference();
if (num > (int)byte.MaxValue)
return (int)byte.MaxValue;
if (num < 0)
return 0;
return num;
}
private int CadenceTimeDifference()
{
if ((int)this._currentUpdate.CadenceTime1024 > (int)this._previousUpdate.CadenceTime1024)
return (int)this._currentUpdate.CadenceTime1024 - (int)this._previousUpdate.CadenceTime1024;
return (int)ushort.MaxValue - (int)this._previousUpdate.CadenceTime1024 + (int)this._currentUpdate.CadenceTime1024 + 1;
}
private int CadenceEventDifference()
{
if ((int)this._currentUpdate.CadenceEventCount > (int)this._previousUpdate.CadenceEventCount)
return (int)this._currentUpdate.CadenceEventCount - (int)this._previousUpdate.CadenceEventCount;
return (int)ushort.MaxValue - (int)this._previousUpdate.CadenceEventCount + (int)this._currentUpdate.CadenceEventCount + 1;
}
private double CalculateSpeed()
{
double num = (double)(ulong)(this.WheelCircumference / 10.0 * 36864.0 * (double)this.SpeedEventDifference() / (double)this.SpeedTimeDifference()) / 1000.0;
if (num >= 0.0)
return num;
return 0.0;
}
private int SpeedTimeDifference()
{
if ((int)this._currentUpdate.SpeedTime1024 > (int)this._previousUpdate.SpeedTime1024)
return (int)this._currentUpdate.SpeedTime1024 - (int)this._previousUpdate.SpeedTime1024;
return (int)ushort.MaxValue - (int)this._previousUpdate.SpeedTime1024 + (int)this._currentUpdate.SpeedTime1024 + 1;
}
private uint SpeedEventDifference()
{
if (this._currentUpdate.SpeedEventCount > this._previousUpdate.SpeedEventCount)
return this._currentUpdate.SpeedEventCount - this._previousUpdate.SpeedEventCount;
return (uint)(-1 - (int)this._previousUpdate.SpeedEventCount + (int)this._currentUpdate.SpeedEventCount + 1);
}
public void SetUnavailable()
{
//throw new NotImplementedException();
}
private interface IUpdate
{
bool HasCadence { get; }
bool HasSpeed { get; }
uint SpeedEventCount { get; }
ushort SpeedTime1024 { get; }
ushort CadenceEventCount { get; }
ushort CadenceTime1024 { get; }
}
private class Update : CyclingSpeedCadenceMeasurement.IUpdate
{
private static readonly byte WheelRevolutionsFlagBit = (byte)0;
private static readonly byte CrankRevolutionsFlagBit = (byte)1;
private readonly byte[] _data;
private byte Flags
{
get
{
return this._data[0];
}
}
public bool HasCadence
{
get
{
return PrimitiveExtensions.IsBitSet(this.Flags, (int)CyclingSpeedCadenceMeasurement.Update.CrankRevolutionsFlagBit);
}
}
public bool HasSpeed
{
get
{
return PrimitiveExtensions.IsBitSet(this.Flags, (int)CyclingSpeedCadenceMeasurement.Update.WheelRevolutionsFlagBit);
}
}
public uint SpeedEventCount
{
get
{
return BitConvertHelper.ToUInt32(this._data, 1);
}
}
public ushort SpeedTime1024
{
get
{
return BitConvertHelper.ToUInt16(this._data, 5);
}
}
public ushort CadenceEventCount
{
get
{
return BitConvertHelper.ToUInt16(this._data, this.HasSpeed ? 7 : 1);
}
}
public ushort CadenceTime1024
{
get
{
return BitConvertHelper.ToUInt16(this._data, this.HasSpeed ? 9 : 3);
}
}
public Update(byte[] data)
{
this._data = data;
}
}
private class NullUpdate : CyclingSpeedCadenceMeasurement.IUpdate
{
public bool HasCadence
{
get
{
return false;
}
}
public bool HasSpeed
{
get
{
return false;
}
}
public uint SpeedEventCount
{
get
{
return 0U;
}
}
public ushort SpeedTime1024
{
get
{
return (ushort)0;
}
}
public ushort CadenceEventCount
{
get
{
return (ushort)0;
}
}
public ushort CadenceTime1024
{
get
{
return (ushort)0;
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0ea4d73295288ad4a9f561a37246110a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,45 @@
using Assets.Scripts.Ble;
using Assets.Scripts.Devices.Ble.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Assets.Scripts.Devices.Ble.Characteristic
{
public class FtmsFitnessMachineFeature : ICharacteristic
{
public Guid Uuid => ServiceUuids.Characteristics.FitnessMachineFeature;
public Guid ServiceUuid => ServiceUuids.Get(ServiceUuids.Ftms).IdGuid;
public bool IsOptional => false;
public bool IsInclinationSupported
{
get; private set;
}
public bool IsInclinationTargetSettingSupported
{
get; private set;
}
public void HandleAttributeReceived(byte[] data)
{
//throw new NotImplementedException();
if (data.Length != 8)
return;
this.IsInclinationSupported = ByteExtensions.IsFlagSet(data[0], (byte)8);
//this.ResistanceLevelSupported = ByteExtensions.IsFlagSet(data[0], sbyte.MinValue);
this.IsInclinationTargetSettingSupported = ByteExtensions.IsFlagSet(data[4], 2);
}
public void SetUnavailable()
{
//throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c6095c96152da4c47870708061e1d5ac
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,220 @@
using Assets.Scripts.Ble;
using Assets.Scripts.Devices.Ble.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Assets.Scripts.Devices.Ble.Characteristic
{
public class FtmsIndoorBikeData : ICharacteristic
{
private readonly ushort? _instantSpeedSubject = new ushort?();
private readonly ushort? _averageSpeedSubject = new ushort?();
private readonly ushort? _instantCadenceSubject = new ushort?();
private readonly ushort? _averageCadenceSubject = (new ushort?());
private readonly uint? _totalDistanceSubject = (new uint?());
private readonly short? _resistanceLevelSubject = (new short?());
private readonly short? _instantPowerSubject = (new short?());
private readonly short? _averagePowerSubject = (new short?());
private readonly ushort? _totalEnergySubject = (new ushort?());
private readonly ushort? _energyPerHourSubject = (new ushort?());
private readonly byte? _energyPerMinuteSubject = (new byte?());
private readonly byte? _heartRateSubject = (new byte?());
private readonly byte? _metabolicEquivalentSubject = (new byte?());
private readonly ushort? _elapsedTimeSubject = (new ushort?());
private readonly ushort? _remainingTimeSubject = (new ushort?());
public short? InstantPower
{
get
{
return this._instantPowerSubject;
}
}
public ushort? InstantCadence
{
get
{
return this._instantCadenceSubject;
}
}
public ushort? InstantSpeed
{
get
{
return this._instantSpeedSubject;
}
}
public Guid Uuid => ServiceUuids.Characteristics.IndoorBikeData;
public Guid ServiceUuid => ServiceUuids.Get(ServiceUuids.Ftms).IdGuid;
public bool IsOptional => false;
public void HandleAttributeReceived(byte[] data)
{
if(data.Length < 2)
{
return;
}
List<FtmsIndoorBikeData.IndoorBikeDataField> list = new List<FtmsIndoorBikeData.IndoorBikeDataField>();
if (!ByteExtensions.IsFlagSetAtPosition(data[0], (byte)0))
list.Add(FtmsIndoorBikeData.IndoorBikeDataField.InstantaneousSpeed);
if (ByteExtensions.IsFlagSetAtPosition(data[0], (byte)1))
list.Add(FtmsIndoorBikeData.IndoorBikeDataField.AverageSpeed);
if (ByteExtensions.IsFlagSetAtPosition(data[0], (byte)2))
list.Add(FtmsIndoorBikeData.IndoorBikeDataField.InstantaneousCadence);
if (ByteExtensions.IsFlagSetAtPosition(data[0], (byte)3))
list.Add(FtmsIndoorBikeData.IndoorBikeDataField.AverageCadence);
if (ByteExtensions.IsFlagSetAtPosition(data[0], (byte)4))
list.Add(FtmsIndoorBikeData.IndoorBikeDataField.TotalDistance);
if (ByteExtensions.IsFlagSetAtPosition(data[0], (byte)5))
list.Add(FtmsIndoorBikeData.IndoorBikeDataField.ResistanceLevel);
if (ByteExtensions.IsFlagSetAtPosition(data[0], (byte)6))
list.Add(FtmsIndoorBikeData.IndoorBikeDataField.InstantaneousPower);
if (ByteExtensions.IsFlagSetAtPosition(data[0], (byte)7))
list.Add(FtmsIndoorBikeData.IndoorBikeDataField.AveragePower);
if (ByteExtensions.IsFlagSetAtPosition(data[1], (byte)0))
list.Add(FtmsIndoorBikeData.IndoorBikeDataField.ExpendedEnergy);
if (ByteExtensions.IsFlagSetAtPosition(data[1], (byte)1))
list.Add(FtmsIndoorBikeData.IndoorBikeDataField.HeartRate);
if (ByteExtensions.IsFlagSetAtPosition(data[1], (byte)2))
list.Add(FtmsIndoorBikeData.IndoorBikeDataField.MetabolicEquivalent);
if (ByteExtensions.IsFlagSetAtPosition(data[1], (byte)3))
list.Add(FtmsIndoorBikeData.IndoorBikeDataField.ElapsedTime);
if (ByteExtensions.IsFlagSetAtPosition(data[1], (byte)4))
list.Add(FtmsIndoorBikeData.IndoorBikeDataField.RemainingTime);
int offset = 2;
foreach (FtmsIndoorBikeData.IndoorBikeDataField field in list)
offset += this.ParseField(data, offset, field);
}
public void SetUnavailable()
{
//throw new NotImplementedException();
}
private int ParseField(byte[] attribute, int offset, FtmsIndoorBikeData.IndoorBikeDataField field)
{
int fieldSize = FtmsIndoorBikeData.GetFieldSize(field);
if (attribute.Length < fieldSize + offset)
throw new ArgumentException("attribute");
switch (field)
{
case FtmsIndoorBikeData.IndoorBikeDataField.InstantaneousSpeed:
ICharacteristicExtensions.HandleUshortAttributeValue((ICharacteristic)this, attribute, offset, this._instantSpeedSubject);
break;
case FtmsIndoorBikeData.IndoorBikeDataField.AverageSpeed:
ICharacteristicExtensions.HandleUshortAttributeValue((ICharacteristic)this, attribute, offset, this._averageSpeedSubject);
break;
case FtmsIndoorBikeData.IndoorBikeDataField.InstantaneousCadence:
ICharacteristicExtensions.HandleUshortAttributeValue((ICharacteristic)this, attribute, offset, this._instantCadenceSubject);
break;
case FtmsIndoorBikeData.IndoorBikeDataField.AverageCadence:
ICharacteristicExtensions.HandleUshortAttributeValue((ICharacteristic)this, attribute, offset, this._averageCadenceSubject);
break;
case FtmsIndoorBikeData.IndoorBikeDataField.TotalDistance:
ICharacteristicExtensions.HandleUint24AttributeValue((ICharacteristic)this, attribute, offset, this._totalDistanceSubject);
break;
case FtmsIndoorBikeData.IndoorBikeDataField.ResistanceLevel:
ICharacteristicExtensions.HandleShortAttributeValue((ICharacteristic)this, attribute, offset, this._resistanceLevelSubject);
break;
case FtmsIndoorBikeData.IndoorBikeDataField.InstantaneousPower:
ICharacteristicExtensions.HandleShortAttributeValue((ICharacteristic)this, attribute, offset, this._instantPowerSubject);
break;
case FtmsIndoorBikeData.IndoorBikeDataField.AveragePower:
ICharacteristicExtensions.HandleShortAttributeValue((ICharacteristic)this, attribute, offset, this._averagePowerSubject);
break;
case FtmsIndoorBikeData.IndoorBikeDataField.ExpendedEnergy:
ICharacteristicExtensions.HandleUshortAttributeValue((ICharacteristic)this, attribute, offset, this._totalEnergySubject);
ICharacteristicExtensions.HandleUshortAttributeValue((ICharacteristic)this, attribute, offset + 2, this._energyPerHourSubject);
ICharacteristicExtensions.HandleByteAttributeValue((ICharacteristic)this, attribute, offset + 4, this._energyPerMinuteSubject);
break;
case FtmsIndoorBikeData.IndoorBikeDataField.HeartRate:
ICharacteristicExtensions.HandleByteAttributeValue((ICharacteristic)this, attribute, offset, this._heartRateSubject);
break;
case FtmsIndoorBikeData.IndoorBikeDataField.MetabolicEquivalent:
ICharacteristicExtensions.HandleByteAttributeValue((ICharacteristic)this, attribute, offset, this._metabolicEquivalentSubject);
break;
case FtmsIndoorBikeData.IndoorBikeDataField.ElapsedTime:
ICharacteristicExtensions.HandleUshortAttributeValue((ICharacteristic)this, attribute, offset, this._elapsedTimeSubject);
break;
case FtmsIndoorBikeData.IndoorBikeDataField.RemainingTime:
ICharacteristicExtensions.HandleUshortAttributeValue((ICharacteristic)this, attribute, offset, this._remainingTimeSubject);
break;
}
return fieldSize;
}
private static int GetFieldSize(FtmsIndoorBikeData.IndoorBikeDataField field)
{
int num = 0;
switch (field)
{
case FtmsIndoorBikeData.IndoorBikeDataField.InstantaneousSpeed:
num = 2;
break;
case FtmsIndoorBikeData.IndoorBikeDataField.AverageSpeed:
num = 2;
break;
case FtmsIndoorBikeData.IndoorBikeDataField.InstantaneousCadence:
num = 2;
break;
case FtmsIndoorBikeData.IndoorBikeDataField.AverageCadence:
num = 2;
break;
case FtmsIndoorBikeData.IndoorBikeDataField.TotalDistance:
num = 3;
break;
case FtmsIndoorBikeData.IndoorBikeDataField.ResistanceLevel:
num = 2;
break;
case FtmsIndoorBikeData.IndoorBikeDataField.InstantaneousPower:
num = 2;
break;
case FtmsIndoorBikeData.IndoorBikeDataField.AveragePower:
num = 2;
break;
case FtmsIndoorBikeData.IndoorBikeDataField.ExpendedEnergy:
num = 5;
break;
case FtmsIndoorBikeData.IndoorBikeDataField.HeartRate:
num = 1;
break;
case FtmsIndoorBikeData.IndoorBikeDataField.MetabolicEquivalent:
num = 1;
break;
case FtmsIndoorBikeData.IndoorBikeDataField.ElapsedTime:
num = 2;
break;
case FtmsIndoorBikeData.IndoorBikeDataField.RemainingTime:
num = 2;
break;
}
return num;
}
private enum IndoorBikeDataField
{
InstantaneousSpeed,
AverageSpeed,
InstantaneousCadence,
AverageCadence,
TotalDistance,
ResistanceLevel,
InstantaneousPower,
AveragePower,
ExpendedEnergy,
HeartRate,
MetabolicEquivalent,
ElapsedTime,
RemainingTime,
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e3577d5802131984c9463ba280183935
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,4 +1,5 @@
using Assets.Scripts.Ble;
using Assets.Scripts.Devices.Ble.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
@ -8,7 +9,7 @@ using UnityEngine;
namespace Assets.Scripts.Devices.Ble.Characteristic
{
public class HeartRateMeasurement
public class HeartRateMeasurement : ICharacteristic
{
public Guid Uuid
{
@ -26,6 +27,13 @@ namespace Assets.Scripts.Devices.Ble.Characteristic
}
}
public bool IsOptional
{
get
{
return false;
}
}
private HrmFormat Format { get; set; }
// Token: 0x170005EB RID: 1515
@ -39,7 +47,7 @@ namespace Assets.Scripts.Devices.Ble.Characteristic
if (this.Format == HrmFormat.Short)
{
this.BeatsPerMinute = BitConvertHelper.ToUInt8(data, 1);
Debug.Log("心率:" + this.BeatsPerMinute);
//Debug.Log("心率:" + this.BeatsPerMinute);
return;
}
if (data.Length < 3)
@ -49,6 +57,10 @@ namespace Assets.Scripts.Devices.Ble.Characteristic
this.BeatsPerMinute = BitConvertHelper.ToUInt16(data, 1);
}
public void SetUnavailable()
{
}
[Flags]
private enum HrmFlags
{

View File

@ -0,0 +1,46 @@
using Assets.Scripts.Devices.Ble.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Assets.Scripts.Devices.Ble.Characteristic
{
public static class ICharacteristicExtensions
{
public static void HandleByteAttributeValue(this ICharacteristic characteristic, byte[] attribute, int offset, byte? subject)
{
if (attribute.Length < 1 + offset)
throw new ArgumentException("attribute");
byte num = attribute[offset];
subject =new byte?(num);
}
public static void HandleShortAttributeValue(this ICharacteristic characteristic, byte[] attribute, int offset, short? subject)
{
if (attribute.Length < 2 + offset)
throw new ArgumentException("attribute");
short num = BitConverter.ToInt16(attribute, offset);
subject= new short?(num);
}
public static void HandleUshortAttributeValue(this ICharacteristic characteristic, byte[] attribute, int offset, ushort? subject)
{
if (attribute.Length < 2 + offset)
throw new ArgumentException("attribute");
ushort num = BitConverter.ToUInt16(attribute, offset);
subject =new ushort?(num);
}
public static void HandleUint24AttributeValue(this ICharacteristic characteristic, byte[] attribute, int offset, uint? subject)
{
if (attribute.Length < 4 + offset - 1)
throw new ArgumentException("attribute");
byte[] numArray = new byte[4];
Buffer.BlockCopy((Array)attribute, offset, (Array)numArray, 0, 3);
uint num = BitConverter.ToUInt32(numArray, 0);
subject = (new uint?(num));
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 238bc68320174c9429b5958b5207b2c5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,31 @@
using Assets.Scripts.Ble;
using Assets.Scripts.Devices.Ble.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace Assets.Scripts.Devices.Ble.Characteristic
{
public class ManufacturerNameCharacteristic : ICharacteristic
{
public Guid Uuid => ServiceUuids.Characteristics.ManufactureNameString;
public Guid ServiceUuid => ServiceUuids.Get(ServiceUuids.DeviceInformation).IdGuid;
public bool IsOptional => true;
public void HandleAttributeReceived(byte[] data)
{
//throw new NotImplementedException();
Debug.Log(Encoding.UTF8.GetString(data));
}
public void SetUnavailable()
{
//throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4f0a2af64ce8fe84cbdf0e5a14bbc8ae
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,31 @@
using Assets.Scripts.Ble;
using Assets.Scripts.Devices.Ble.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace Assets.Scripts.Devices.Ble.Characteristic
{
public class ModelNumberCharacteristic : ICharacteristic
{
public Guid Uuid => ServiceUuids.Characteristics.ModelNumber;
public Guid ServiceUuid => ServiceUuids.Get(ServiceUuids.DeviceInformation).IdGuid;
public bool IsOptional => true;
public void HandleAttributeReceived(byte[] data)
{
//throw new NotImplementedException();
Debug.Log($"model number:\t{ Encoding.UTF8.GetString(data) }");
}
public void SetUnavailable()
{
//throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ef489f56607ba2540b8954b67e1a4d3e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,205 @@
using Assets.Scripts.Ble;
using Assets.Scripts.Devices.Ble.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Assets.Scripts.Devices.Ble.Characteristic
{
public class TacxFecNotify : ICharacteristic
{
public Guid Uuid => ServiceUuids.Characteristics.TacxFecRead;
public Guid ServiceUuid => ServiceUuids.Get(ServiceUuids.TacxBle).IdGuid;
public virtual bool IsOptional
{
get
{
return false;
}
}
public double Speed { get; set; }
private int _Power;
public int Power
{
get
{
return _Power;
}
set
{
_Power = DeviceValueFilter.Power(value);
//Debug.Log(_Power);
}
}
private int _Cadence;
public int Cadence
{
get
{
return _Cadence;
}
set
{
_Cadence = DeviceValueFilter.Cadence(value);
}
}
public void HandleAttributeReceived(byte[] data)
{
//throw new NotImplementedException();
var responseId = data[2];
if (responseId != (byte)ANT_Managed_Library.ANT_ReferenceLibrary.ANTMessageID.BROADCAST_DATA_0x4E)
{
return;
}
var data1 = data.Skip(3).ToArray();
var pageNumber = data1[1];
switch (pageNumber)
{
case 16:
{
HandleGeneralDataPage(data1.Skip(1).ToArray());
}
break;
case 19:
{
//lastInstCadence = data1[5];
}
break;
case 21: //bike data
case 22: //Row data, same format as bike for cad and power
case 20: //Elliptical, same format as bike for cad and power
case 24: //Nordic Skier, same format as bike for cad and power
{
//lastInstCadence = data1[5];
//lastInstPower = (ushort)(data1[6] + (data1[7] << 8));
//System.Console.Out.WriteLine("Cadence:" + response.messageContents[5] + ",Power:" + (response.messageContents[6] + (response.messageContents[7] << 8)));
//System.Console.Out.WriteLine("SPM:"+response.messageContents[5]+"Power:"+(response.messageContents[6] + (response.messageContents[7] << 8)));
}
break;
case 25:
HandleTrainerDataPage(data1.Skip(1).ToArray());
break;
default:
break;
}
}
private void HandleGeneralDataPage(byte[] dataPayload)
{
try
{
byte equipmentType = dataPayload[1];
byte elapsedTime = dataPayload[2];
byte distanceTraveled = dataPayload[3];
//byte heartRate = dataPayload[6];
//FecPageHandler.HeartRateDataSource heartRateDataSource = (FecPageHandler.HeartRateDataSource)(dataPayload[7] & 3);
//bool traveledEnabled = (dataPayload[7] & 4) == 1;
//bool distanceTraveledEnabled = (dataPayload[7] & 8) == 1;
//FecPageHandler.FeState feState = (FecPageHandler.FeState)(dataPayload[7] & 128);
//bool lapToggle = (dataPayload[7] & 128) == 1;
//this._fecGeneralData = new FecGeneralData(equipmentType, elapsedTime, distanceTraveled, (short)num, feState, lapToggle, heartRate, heartRateDataSource, traveledEnabled, distanceTraveledEnabled);
bool traveledEnabled = ((int)dataPayload[7] & 4) == 1;
bool distanceTraveledEnabled = ((int)dataPayload[7] & 8) == 1;
int num1 = (int)dataPayload[4] | (int)dataPayload[5] << 8; // Instantaneous speed, m/s
Speed = (double)num1 / 1000.0 * 60.0 * 60.0 / 1000.0; //转换成公里/小时
//Console.WriteLine(num1 +"-----"+Speed);
//fecDevice.UpdateSpeed(new double?(value));
//PubCommData.Speed = num2;
//byte? b2 = (heartRate == 255) ? null : new byte?(heartRate);
//int? xl = b2.HasValue ? new int?((int)b2.GetValueOrDefault()) : 0;
//Console.WriteLine("最新速度" + Speed);
//Console.WriteLine("最新心率" + xl);
//string str = "";
//for (int i = 0; i < response.messageContents.Length; i++)
//{
// str += "," + response.messageContents[i];
//}
//PubCommData.sp = "速度:" + str;
}
catch (Exception)
{
}
}
private void HandleTrainerDataPage(byte[] dataPayload)
{
//if (response.messageContents.Length < 11) break;
//double value = 0;
//for (int i = 0; i < response.messageContents.Length; i++)
//{
// if (i < 8)
// {
// value += response.messageContents[i];
// }
//}
//double value1 = response.messageContents[10] * 256 + response.messageContents[9];
//double power = response.messageContents[7] * 256 + response.messageContents[6];
//Console.WriteLine("power:" + power);
//PubCommData.power = power;
try
{
if (dataPayload[5] > 0)
{
//Console.WriteLine(dataPayload[5] + "," + dataPayload[6]);//瞬时功率
}
byte num1 = dataPayload[1];
byte num2 = dataPayload[2];
int accumulatedPower = (int)dataPayload[3] | (int)dataPayload[4] << 8;
int instantaneousPower = (int)dataPayload[5] | ((int)dataPayload[6] & 15) << 8; //Watts
bool bikePowerCalibrationRequired = ((int)dataPayload[6] >> 4 & 1) == 1;
bool resistanceCalibrationRequired = ((int)dataPayload[6] >> 5 & 1) == 1;
bool userConfigurationRequired = ((int)dataPayload[6] >> 6 & 1) == 1;
bool speedIsTooLow = ((int)dataPayload[7] & 1) == 1;
bool speedIsTooHigh = ((int)dataPayload[7] & 2) == 1;
//PubCommData.power = instantaneousPower;
Power = instantaneousPower;
//bool bikePowerCalibrationRequired = (response.messageContents[6] >> 4 & 1) == 1;
//bool resistanceCalibrationRequired = (response.messageContents[6] >> 5 & 1) == 1;
//bool flag = (response.messageContents[6] >> 6 & 1) == 1;
//bool speedIsTooLow = (response.messageContents[7] & 1) == 1;
//bool speedIsTooHigh = (response.messageContents[7] & 2) == 1;
//FecPageHandler.FeState feState = (FecPageHandler.FeState)(dataPayload[7] & 112);
//bool lapToggle = (dataPayload[7] & 128) == 1;
//this._fecTrainerData = new FecTrainerData((int)eventCount, (int)b, accumulatedPower, num, bikePowerCalibrationRequired, resistanceCalibrationRequired, flag, speedIsTooLow, speedIsTooHigh, feState, lapToggle);
//device.UpdatePower(new int?(num));
byte? nullable = (int)num2 == (int)byte.MaxValue ? new byte?() : new byte?(num2);
int? cadence = nullable.HasValue ? new int?((int)nullable.GetValueOrDefault()) : new int?();
this.Cadence = cadence.GetValueOrDefault();
//Console.WriteLine($"power:{ instantaneousPower },Cadence:{ cadence }, data:{ string.Join(",", dataPayload) }");
if (!userConfigurationRequired)
return;
//UpdateUserConfiguration();
}
catch (Exception)
{
}
}
public virtual void SetUnavailable()
{
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 640f21041d0bf6347b1e48669c9a8691
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,39 @@
using Assets.Scripts.Ble;
using Assets.Scripts.Devices.Ble.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Assets.Scripts.Devices.Ble.Characteristic
{
public class TacxFecWrite : ICharacteristic
{
public virtual bool IsOptional
{
get
{
return false;
}
}
public Guid Uuid => ServiceUuids.Characteristics.TacxFecRead;
public Guid ServiceUuid => ServiceUuids.Get(ServiceUuids.TacxBle).IdGuid;
public void HandleAttributeReceived(byte[] data)
{
}
public void WriteMessageToControlPoint()
{
}
public virtual void SetUnavailable()
{
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6c9564a0124aa5946b25bdd1a97ade2c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,5 +1,6 @@
using Assets.Scripts.Ble;
using Assets.Scripts.Devices.Ant.Interfaces;
using Assets.Scripts.Devices.Ble.Characteristic;
using System;
using System.Collections.Generic;
using System.Linq;
@ -12,12 +13,16 @@ namespace Assets.Scripts.Devices.Ble.Devices
public class CyclingPower : BleDevice, IPowerDevice
{
private List<BleServiceInfo> Services;
private CyclingPowerMeasurement cyclingPowerMeasurement;
public CyclingPower(BlePeripheralInfo peripheralInfo, BleWinHwInterface bleWinHwInterface) : base(peripheralInfo, bleWinHwInterface, Ant.SensorType.Power)
{
cyclingPowerMeasurement = new CyclingPowerMeasurement(1920);
base.Characteristics.Add(cyclingPowerMeasurement);
bleWinHwInterface.CharacteristicReadEvent += CharacteristicReadMainCallback;
}
public int Power { get => 999; set => throw new NotImplementedException(); }
public int Power { get => cyclingPowerMeasurement.Power; set => throw new NotImplementedException(); }
protected override void CreateServices(List<BleServiceInfo> discoveredServices)
{
@ -35,7 +40,7 @@ namespace Assets.Scripts.Devices.Ble.Devices
this.hwInterface.SubscribeCharacteristic(character, (hw, cha, res) =>
{
Debug.Log("1111111111111111111111");
//Debug.Log("1111111111111111111111");
});
}
}
@ -46,7 +51,22 @@ namespace Assets.Scripts.Devices.Ble.Devices
private void CharacteristicReadMainCallback(BleWinHwInterface hwInterface, BleCharacteristicInfo characteristic, BleResponse<byte[]> response)
{
Debug.Log("main call" + string.Join(",", response.Data));
//Debug.Log("power main call" + string.Join(",", response.Data));
//Debug.Log(characteristic.MatchGuid(cyclingPowerMeasurement.Uuid));
//Debug.Log(characteristic.Service.MatchGuid(cyclingPowerMeasurement.ServiceUuid));
//if (characteristic.MatchGuid(cyclingPowerMeasurement.Uuid))
//{
// cyclingPowerMeasurement.HandleAttributeReceived(response.Data);
//}
foreach (var item in base.Characteristics)
{
if (characteristic.MatchGuid(item.Uuid))
{
item.HandleAttributeReceived(response.Data);
}
}
}
}
}

View File

@ -1,5 +1,6 @@
using Assets.Scripts.Ble;
using Assets.Scripts.Devices.Ant.Interfaces;
using Assets.Scripts.Devices.Ble.Characteristic;
using System;
using System.Collections.Generic;
using System.Linq;
@ -8,19 +9,119 @@ using System.Threading.Tasks;
namespace Assets.Scripts.Devices.Ble.Devices
{
public class Ftms : BleDevice, IPowerDevice
public class Ftms : BleDevice, ISpeedDevice, IPowerDevice, ICadenceDevice, ITrainerDevice
{
public int Power { get => _ftmsIndoorBikeData.InstantPower.GetValueOrDefault(0); set => throw new NotImplementedException(); }
public double Speed { get => _ftmsIndoorBikeData.InstantSpeed.GetValueOrDefault(0); set => throw new NotImplementedException(); }
public int Cadence { get => _ftmsIndoorBikeData.InstantCadence.GetValueOrDefault(0); set => throw new NotImplementedException(); }
private FtmsIndoorBikeData _ftmsIndoorBikeData;
private List<BleServiceInfo> Services;
private BleCharacteristicInfo controlPointCharacteristic;
public Ftms(BlePeripheralInfo peripheralInfo, BleWinHwInterface bleWinHwInterface) :base(peripheralInfo, bleWinHwInterface, Ant.SensorType.Trainer)
{
this._ftmsIndoorBikeData = new FtmsIndoorBikeData();
base.Characteristics.Add(this._ftmsIndoorBikeData);
base.Characteristics.Add(new FtmsFitnessMachineFeature());
bleWinHwInterface.CharacteristicReadEvent += CharacteristicReadMainCallback;
}
public int Power { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
protected override void CreateServices(List<BleServiceInfo> discoveredServices)
{
this.Services = discoveredServices;
foreach (var service in this.Services)
{
hwInterface.DiscoverCharacteristic(service, (hwInterface, service1, response) =>
{
foreach (var character in response.Data)
{
if (character.MatchGuid(ServiceUuids.Characteristics.IndoorBikeData))
{
//Debug.Log("功率功能");
this.hwInterface.SubscribeCharacteristic(character, (hw, cha, res) =>
{
//Debug.Log("1111111111111111111111");
});
}
else if (character.MatchGuid(ServiceUuids.Characteristics.FitnessMachineControlPoint))
{
this.controlPointCharacteristic = character;
}
}
});
}
}
private void CharacteristicReadMainCallback(BleWinHwInterface hwInterface, BleCharacteristicInfo characteristic, BleResponse<byte[]> response)
{
foreach (var item in base.Characteristics)
{
if (characteristic.MatchGuid(item.Uuid))
{
//Debug.Log(string.Join(",", response.Data));
item.HandleAttributeReceived(response.Data);
}
}
}
public void SetErgMode(int targetPower)
{
//throw new NotImplementedException();
byte[] bytes = BitConverter.GetBytes(targetPower);
this.hwInterface.WriteCharacteristic(this.controlPointCharacteristic, new byte[3] {
(byte)5,
bytes[0],
bytes[1]
});
}
public void SetResistanceMode(double value)
{
//throw new NotImplementedException();
var data = new byte[]
{
(byte)4,
(byte)0.1
};
}
public void SetWindResistance(double? height = null)
{
throw new NotImplementedException();
}
/// <summary>
/// 轨道阻力
/// </summary>
/// <param name="grade"></param>
public void SetTrackResistance(double grade)
{
if (this.State != Ant.DeviceState.Connected)
return;
if (controlPointCharacteristic == null)
return;
short windSpeed = 0;
short value2 = (short)(grade);
byte rollingResistanceCoefficient = (byte)(0.004 * 10000);
byte windResistanceCoefficient = 0;
var data = new List<byte>();// { 17, (byte)windSpeed, (byte)value2, rollingResistanceCoefficient, windResistanceCoefficient };
data.Add(17);
data.AddRange(BitConverter.GetBytes(windSpeed));
data.AddRange(BitConverter.GetBytes(value2));
data.AddRange(BitConverter.GetBytes(rollingResistanceCoefficient));
data.AddRange(BitConverter.GetBytes(windResistanceCoefficient));
this.hwInterface.WriteCharacteristic(this.controlPointCharacteristic, data.ToArray());
}
}
}

View File

@ -18,14 +18,13 @@ namespace Assets.Scripts.Devices.Ble.Devices
private HeartRateMeasurement heartRateMeasurement;
public HeartRate(BlePeripheralInfo peripheralInfo, BleWinHwInterface bleWinHwInterface) : base(peripheralInfo, bleWinHwInterface, Ant.SensorType.HeartRate)
{
Debug.Log("创建心率设备");
heartRateMeasurement = new HeartRateMeasurement();
base.Characteristics.Add(heartRateMeasurement);
bleWinHwInterface.CharacteristicReadEvent += CharacteristicReadMainCallback;
}
protected override void CreateServices(List<BleServiceInfo> discoveredServices)
{
//throw new NotImplementedException();
@ -33,18 +32,33 @@ namespace Assets.Scripts.Devices.Ble.Devices
foreach (var service in this.Services)
{
//Debug.Log($"11111 "+ service.Id.ToString());
hwInterface.DiscoverCharacteristic(service, (hwInterface, service1, response) =>
{
foreach (var character in response.Data)
{
if (character.MatchGuid(heartRateMeasurement.Uuid))
{
Debug.Log("心率功能");
this.hwInterface.SubscribeCharacteristic(character, (hw, cha, res) =>
{
Debug.Log("1111111111111111111111");
//Debug.Log($"心率计subscribe char:{ }");
});
continue;
}
}
foreach (var item in Characteristics.Where(c=>c.IsOptional))
{
//Debug.Log(item.GetType() + "服务可用"+ item.Uuid.ToString() +", service:" + item.ServiceUuid);
var ccc = response.Data.FirstOrDefault(r => r.MatchGuid(item.Uuid));
if (ccc == null)
{
item.SetUnavailable();
}
else
{
Debug.Log(item.GetType() + "服务可用");
GetBatteryLevel(ccc);
}
}
});
@ -52,10 +66,26 @@ namespace Assets.Scripts.Devices.Ble.Devices
}
public void GetBatteryLevel(BleCharacteristicInfo bbbb)
{
this.hwInterface.ReadCharacteristic(bbbb, (hwInterface1, characteristic1, response1) => {
Debug.Log("read收到消息" + string.Join(",", response1));
});
}
private void CharacteristicReadMainCallback(BleWinHwInterface hwInterface, BleCharacteristicInfo characteristic, BleResponse<byte[]> response)
{
Debug.Log("main call" + string.Join(",", response.Data));
heartRateMeasurement.HandleAttributeReceived(response.Data);
//Debug.Log("heart rate main call" + string.Join(",", response.Data));
foreach (var item in base.Characteristics)
{
if (characteristic.MatchGuid(item.Uuid))
{
item.HandleAttributeReceived(response.Data);
}
}
//heartRateMeasurement.HandleAttributeReceived(response.Data);
}
}

View File

@ -0,0 +1,103 @@
using Assets.Scripts.Ble;
using Assets.Scripts.Devices.Ant.Interfaces;
using Assets.Scripts.Devices.Ble.Characteristic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace Assets.Scripts.Devices.Ble.Devices
{
public class SpeedCadence : BleDevice, ISpeedDevice, ICadenceDevice
{
private CyclingSpeedCadenceMeasurement _cyclingSpeedCadenceMeasurement;
public int Cadence { get => _cyclingSpeedCadenceMeasurement.Cadence; set => throw new NotImplementedException(); }
public double Speed { get => _cyclingSpeedCadenceMeasurement.Speed; set => throw new NotImplementedException(); }
private double _wheelCircumference;
private List<BleServiceInfo> Services;
public SpeedCadence(BlePeripheralInfo peripheralInfo, BleWinHwInterface bleWinHwInterface) : base(peripheralInfo, bleWinHwInterface, Ant.SensorType.SpeedCadence)
{
Debug.Log("创建速度踏频设备");
_cyclingSpeedCadenceMeasurement = new CyclingSpeedCadenceMeasurement(this._wheelCircumference);
base.Characteristics.Add(_cyclingSpeedCadenceMeasurement);
bleWinHwInterface.CharacteristicReadEvent += CharacteristicReadMainCallback;
}
public void SetWheelCircumference(double value)
{
}
protected override void CreateServices(List<BleServiceInfo> discoveredServices)
{
this.Services = discoveredServices;
foreach (var service in this.Services)
{
//Debug.Log($"11111 "+ service.Id.ToString());
hwInterface.DiscoverCharacteristic(service, (hwInterface, service1, response) =>
{
Debug.Log($"设备{ this.Name }的char: { string.Join("\r\n", response.Data.Select(d=>d.Id)) }");
foreach (var character in response.Data)
{
if (character.MatchGuid(_cyclingSpeedCadenceMeasurement.Uuid))
{
this.hwInterface.SubscribeCharacteristic(character, (hw, cha, res) =>
{
//Debug.Log("1111111111111111111111");
});
continue;
}
}
foreach (var item in Characteristics.Where(c => c.IsOptional))
{
//Debug.Log(item.GetType() + "服务可用"+ item.Uuid.ToString() +", service:" + item.ServiceUuid);
var ccc = response.Data.FirstOrDefault(r => r.MatchGuid(item.Uuid));
if (ccc == null)
{
item.SetUnavailable();
}
else
{
Debug.Log(item.GetType() + "服务可用");
GetBatteryLevel(ccc);
}
}
});
}
}
public void GetBatteryLevel(BleCharacteristicInfo bbbb)
{
this.hwInterface.ReadCharacteristic(bbbb, (hwInterface1, characteristic1, response1) => {
Debug.Log("read收到消息" + string.Join(",", response1));
});
}
private void CharacteristicReadMainCallback(BleWinHwInterface hwInterface, BleCharacteristicInfo characteristic, BleResponse<byte[]> response)
{
//Debug.Log("power main call" + string.Join(",", response.Data));
//Debug.Log(characteristic.MatchGuid(cyclingPowerMeasurement.Uuid));
//Debug.Log(characteristic.Service.MatchGuid(cyclingPowerMeasurement.ServiceUuid));
//if (characteristic.MatchGuid(cyclingPowerMeasurement.Uuid))
//{
// cyclingPowerMeasurement.HandleAttributeReceived(response.Data);
//}
foreach (var item in base.Characteristics)
{
if (characteristic.MatchGuid(item.Uuid))
{
item.HandleAttributeReceived(response.Data);
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3fc83bb07e841d04bb491332cdfeb669
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,262 @@
using Assets.Scripts.Ble;
using Assets.Scripts.Devices.Ant;
using Assets.Scripts.Devices.Ant.Interfaces;
using Assets.Scripts.Devices.Ble.Characteristic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace Assets.Scripts.Devices.Ble.Devices
{
public class Tacx : BleDevice, IPowerDevice, ICadenceDevice, ISpeedDevice, ITrainerDevice
{
public int Power { get => tacxFecNotify.Power; set => throw new NotImplementedException(); }
public int Cadence { get => tacxFecNotify.Cadence; set => throw new NotImplementedException(); }
public double Speed { get => tacxFecNotify.Speed; set => throw new NotImplementedException(); }
private List<BleServiceInfo> Services;
private TacxFecNotify tacxFecNotify;
private BleCharacteristicInfo tacxFecWriteCharacteristic;
private TacxFecWrite tacxFecWrite;
/// <summary>
/// 当前坡度当切换到其他模式的时候需要把坡度设置为0
/// </summary>
private double _grade = 0;
public Tacx(BlePeripheralInfo peripheralInfo, BleWinHwInterface bleWinHwInterface) : base(peripheralInfo, bleWinHwInterface, Ant.SensorType.Trainer)
{
tacxFecNotify = new TacxFecNotify();
base.Characteristics.Add(tacxFecNotify);
tacxFecWrite = new TacxFecWrite();
base.Characteristics.Add(tacxFecWrite);
bleWinHwInterface.CharacteristicReadEvent += CharacteristicReadMainCallback;
//bleWinHwInterface.WriteCharacteristic()
}
protected override void CreateServices(List<BleServiceInfo> discoveredServices)
{
this.Services = discoveredServices;
foreach (var service in this.Services)
{
hwInterface.DiscoverCharacteristic(service, (hwInterface, service1, response) =>
{
foreach (var character in response.Data)
{
if (character.MatchGuid(ServiceUuids.Characteristics.TacxFecRead))
{
//Debug.Log("功率功能");
this.hwInterface.SubscribeCharacteristic(character, (hw, cha, res) =>
{
//Debug.Log("1111111111111111111111");
});
}
else if (character.MatchGuid(ServiceUuids.Characteristics.TacxFecWrite))
{
this.tacxFecWriteCharacteristic = character;
}
}
});
}
}
private void CharacteristicReadMainCallback(BleWinHwInterface hwInterface, BleCharacteristicInfo characteristic, BleResponse<byte[]> response)
{
foreach (var item in base.Characteristics)
{
if (characteristic.MatchGuid(item.Uuid))
{
//Debug.Log(string.Join(",", response.Data));
item.HandleAttributeReceived(response.Data);
}
}
}
public void SetErgMode(int targetPower)
{
Debug.Log("目标功率1");
if (this.State != Ant.DeviceState.Connected)
return;
if (tacxFecWriteCharacteristic == null)
return;
Debug.Log($"目标功率:{ targetPower }");
List<byte> data = new List<byte> { 164, 9, 79, 5, 49,
byte.MaxValue,
byte.MaxValue,
byte.MaxValue,
byte.MaxValue,
byte.MaxValue,
(byte)(targetPower & byte.MaxValue),
(byte)(targetPower >> 8 & byte.MaxValue)
};
byte checksum = (byte)0;
data.ForEach(b => checksum ^= b);
data.Add(checksum);
this.hwInterface.WriteCharacteristic(this.tacxFecWriteCharacteristic, data.ToArray());
}
/// <summary>
/// 阻力模式
/// </summary>
/// <param name="value"></param>
public void SetResistanceMode(double value)
{
if (this.State != Ant.DeviceState.Connected)
return;
if (tacxFecWriteCharacteristic == null)
return;
if(_grade > 0)
{
this.SetTrackResistance(0);
}
double resistance = value / 100 * 200;
List<byte> data = new List<byte> { 164, 9, 79, 5, 48,
byte.MaxValue,
byte.MaxValue,
byte.MaxValue,
byte.MaxValue,
byte.MaxValue,
byte.MaxValue,
(byte)resistance
};
byte checksum = (byte)0;
data.ForEach(b => checksum ^= b);
data.Add(checksum);
this.hwInterface.WriteCharacteristic(this.tacxFecWriteCharacteristic, data.ToArray());
}
/// <summary>
/// 风阻
/// </summary>
/// <param name="height">海拔高度,单位米</param>
public void SetWindResistance(double? height = null)
{
if (this.State != Ant.DeviceState.Connected)
return;
if (tacxFecWriteCharacteristic == null)
return;
if (_grade > 0)
{
this.SetTrackResistance(0);
}
byte windResistance = 0;
if (height.HasValue)
{
//Wind Resistance Coefficient [kg/m] = Frontal Surface Area [m2] x Drag Coefficient x Air Density[kg / m3]
var wr = 0.40 * 1.0 * AirDensity.GetAirDensity(height.Value);
if (wr > 1.86)
{
wr = 1.86;
}
windResistance = (byte)(Convert.ToInt32(Math.Round(wr, 2) / 0.01));
Console.WriteLine($"风阻系数:{ windResistance }");
}
byte id = 79;
byte size = 9;
byte channel = 5;
List<byte> data = new List<byte> { 164, size, id, channel, 50,
byte.MaxValue,
byte.MaxValue,
byte.MaxValue,
byte.MaxValue,
windResistance,
byte.MaxValue,
byte.MaxValue,
};
byte checksum = (byte)0;
data.ForEach(b => checksum ^= b);
data.Add(checksum);
this.hwInterface.WriteCharacteristic(this.tacxFecWriteCharacteristic, data.ToArray());
}
/// <summary>
/// 轨道阻力
/// </summary>
/// <param name="grade">坡度百分比的值,单位是%</param>
public void SetTrackResistance(double grade)
{
if (this.State != Ant.DeviceState.Connected)
return;
if (tacxFecWriteCharacteristic == null)
return;
_grade = grade;
if (_grade > 15)
{
_grade = 15;
}
else if (_grade < -5)
{
_grade = -5;
}
var gradeValue = Convert.ToInt32((grade + 200) * 100);
var gradeBytes = BitConverter.GetBytes(gradeValue);
byte id = 79;
byte size = 9;
byte channel = 5;
List<byte> data = new List<byte> { 164, size, id, channel, 51,
byte.MaxValue,
byte.MaxValue,
byte.MaxValue,
byte.MaxValue,
gradeBytes[0],
gradeBytes[1],
byte.MaxValue,
};
byte checksum = (byte)0;
data.ForEach(b => checksum ^= b);
data.Add(checksum);
this.hwInterface.WriteCharacteristic(this.tacxFecWriteCharacteristic, data.ToArray());
}
private void UpdateUserConfiguration(double weight)
{
var _weight = (int)(weight * 100);
if (this.State != Ant.DeviceState.Connected)
return;
if (tacxFecWriteCharacteristic == null)
return;
byte id = 79;
byte size = 4;
byte channel = 5;
List<byte> data = new List<byte> { 164, size, id, channel, 55,
(byte) (_weight & (int) byte.MaxValue),
(byte) (_weight >> 8 & (int) byte.MaxValue),
byte.MaxValue,
(byte) 143,
(byte) 12,
(byte) 70,
(byte) 0
};
byte checksum = (byte)0;
data.ForEach(b => checksum ^= b);
data.Add(checksum);
this.hwInterface.WriteCharacteristic(this.tacxFecWriteCharacteristic, data.ToArray());
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d133d5406fc475841a781b88d91dc139
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Assets.Scripts.Devices.Ble.Extension
{
public static class PrimitiveExtensions
{
public static bool IsBitSet(this byte value, int position)
{
return ((uint)value & (uint)(1 << position)) > 0U;
}
public static bool IsBitSet(this ushort value, int position)
{
return ((uint)value & (uint)(1 << position)) > 0U;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d49278e3c0c3a8a43bc1145abf67a5c0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c3f0cb374472e52419b47f6e6d582341
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Assets.Scripts.Devices.Ble.Interfaces
{
public interface ICharacteristic
{
Guid Uuid { get; }
Guid ServiceUuid { get; }
bool IsOptional { get; }
void HandleAttributeReceived(byte[] data);
void SetUnavailable();
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e333c9c3812a8654c87353be9e87e84e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -16,7 +16,7 @@ namespace Assets.Scripts.Ble.Scan
public BlePeripheralInfo Peripheral { get; }
public int Rssi { get; }
public SensorType SensorType { get; }
public SensorType SensorType { get; internal set; }
public BleAdvertisementInfo(BlePeripheralInfo peripheral, int rssi, bool connectible, List<Guid> services, byte[] manufactureData, SensorType sensor)
{
this.Peripheral = peripheral;
@ -26,6 +26,8 @@ namespace Assets.Scripts.Ble.Scan
//this.ManufactureData = manufactureData;
this.SensorType = sensor;
Index = 1;
}
private readonly List<Guid> services;
@ -36,5 +38,14 @@ namespace Assets.Scripts.Ble.Scan
return this.services;
}
}
public void TryAddService(Guid service)
{
if (this.services.Contains(service))
return;
this.services.Add(service);
}
public int Index { get; set; }
}
}

View File

@ -7,7 +7,7 @@ using System.Threading.Tasks;
namespace Assets.Scripts.Ble
{
/// <summary>
/// rouvy里的 WinBlePeripheralInfo类
/// r里的 WinBlePeripheralInfo类
/// </summary>
public class BleDeviceProxy
{

View File

@ -2,6 +2,7 @@
using Assets.Scripts.Ble.Win;
using Assets.Scripts.Devices.Ant;
using Assets.Scripts.Devices.Ble;
using Assets.Scripts.Devices.Ble.Win;
using System;
using System.Collections.Generic;
using System.Linq;
@ -96,53 +97,90 @@ namespace Assets.Scripts.Ble
private void WatcherScanInfoReceived(WclBleMainThread sender, long address, string name, int rssi, CPPBridge.WclBleAdvertisementType packetType, Guid? service)
{
//Debug.Log($"address:{ address }, name:{ name }, service:{ (service == null ? "" : service.Value.ToString()) }");
//if(address != 224160707349234)
//{
//return;
//}
if (service.HasValue)
{
//Debug.Log($"address:{ address }, name:{ name }, service:{ (service == null ? "" : service.Value.ToString()) }");
}
if (!string.IsNullOrWhiteSpace(name))
{
if (pCache.ContainsKey(address.ToString()))
{
((pCache[address.ToString()].Peripheral) as WinBlePeripheralInfo).SetName(name);
(pCache[address.ToString()].Peripheral as WinBlePeripheralInfo).SetName(name);
}
}
//Debug.Log("service:" + service.ToString()+",name:" + name);
if (service.HasValue && ServiceUuids.Services.Select(s => s.IdGuid).Any(x => x.Equals(service.Value)))
if (!service.HasValue || ServiceUuids.Services.Select(s => s.IdGuid).All(x => !x.Equals(service.Value)))
{
return;
}
SensorType sensor = SensorType.None;
List<Guid> services = null;
if (service != null)
{
SensorType sensor = SensorType.None;
List<Guid> services = null;
if (service != null)
{
services = new List<Guid> { service.Value };
foreach(var item in ServiceUuids.Services)
services = new List<Guid> { service.Value };
foreach(var item in ServiceUuids.Services)
{
if(item.IdGuid != service.Value)
{
if(item.IdGuid != service.Value)
{
continue;
}
if(item.IdByteArray == ServiceUuids.Ftms)
{
sensor = SensorType.Trainer;
}
else if(item.IdByteArray == ServiceUuids.HeartRate)
{
sensor = SensorType.HeartRate;
}
else if(item.IdByteArray == ServiceUuids.CyclingPower)
{
sensor = SensorType.Power;
}
continue;
}
};
if(item.IdByteArray == ServiceUuids.Ftms)
{
sensor = SensorType.Trainer;
}
else if(item.IdByteArray == ServiceUuids.HeartRate)
{
sensor = SensorType.HeartRate;
}
else if(item.IdByteArray == ServiceUuids.CyclingPower)
{
sensor = SensorType.Power;
//sensor = SensorType.Trainer;
}
else if(item.IdByteArray == ServiceUuids.CyclingSpeedCadence)
{
sensor = SensorType.SpeedCadence;
}
else if(item.IdByteArray == ServiceUuids.TacxBle)
{
sensor = SensorType.Trainer;
}
}
};
if (!pCache.ContainsKey(address.ToString())) {
var device = new BleAdvertisementInfo(new WinBlePeripheralInfo(address.ToString(), name), rssi, true, services, null, sensor);
pCache.Add(address.ToString(), device);
if (!pCache.ContainsKey(address.ToString()))
{
var device = new BleAdvertisementInfo(new WinBlePeripheralInfo(address.ToString(), name), rssi, true, services, null, sensor);
pCache.Add(address.ToString(), device);
WclBleGattThread gattClient = this.SetUpGattClient(device.Peripheral);
//this.ConnectInternal(gattClient);
WclBleGattThread gattClient = this.SetUpGattClient(device.Peripheral);
//this.ConnectInternal(gattClient);
}
else
{
//Debug.Log(sensor);
//pCache[address.ToString()].SensorType = sensor;
foreach (var item in services)
{
pCache[address.ToString()].TryAddService(item);
}
pCache[address.ToString()].Index++;
if(pCache[address.ToString()].SensorType == SensorType.Power && pCache[address.ToString()].Services.Any(s=> s.Equals(ServiceUuids.Get(ServiceUuids.TacxBle).IdGuid)))
{
pCache[address.ToString()].SensorType = SensorType.Trainer;
//Debug.Log("纠正为trainer, "+ pCache[address.ToString()].Index);
}
_discoveredCallback?.Invoke(pCache[address.ToString()]);
if (pCache[address.ToString()].Index > 4 || pCache[address.ToString()].SensorType != SensorType.Power)
{
_discoveredCallback?.Invoke(pCache[address.ToString()]);
}
}
}
@ -179,8 +217,11 @@ namespace Assets.Scripts.Ble
private void ConnectInternal(WclBleGattThread gattClient)
{
int num = gattClient.Connect();
Debug.Log("连接设备返回" + num);
//Task.Run(() =>
//{
int num = gattClient.Connect();
Debug.Log("连接设备返回" + num);
//});
}
internal void DisconnectPeripheral(BlePeripheralInfo peripheral, Action callback)
@ -323,7 +364,10 @@ namespace Assets.Scripts.Ble
{
return;
}
this.characteristicsDiscoveredCallbacks.Add(service, callback);
if (!this.characteristicsDiscoveredCallbacks.ContainsKey(service))
{
this.characteristicsDiscoveredCallbacks.Add(service, callback);
}
int num = gattThread.DiscoverCharacteristics(service);
if (WclBleErrors.IsSuccessCode(num))
{
@ -355,11 +399,29 @@ namespace Assets.Scripts.Ble
this.GattCharacteristicSubscribed(gattThread, characteristic, response);
}
public void WriteCharacteristic(BleCharacteristicInfo characteristic, byte[] data)
{
var gattThread = this.wclBleMainThread.GetGattThread(characteristic.Peripheral);
if(gattThread == null)
{
return;
}
int num = gattThread.WriteCharacteristic(characteristic, data);
if (WclBleErrors.IsSuccessCode(num))
{
Debug.Log("设置命令成功");
return;
}
}
public void Dispose()
{
this.wclBleMainThread.Dispose();
this.wclBleMainThread = null;
hwInterface = null;
pCache.Clear();
}
public void ReadCharacteristic(BleCharacteristicInfo characteristic, CharacteristicReadCallback callback)

View File

@ -1,4 +1,5 @@
using Assets.Scripts.Devices.Ble;
using Assets.Scripts.Devices.Ble.Win;
using System;
using System.Collections.Generic;
using System.Linq;

View File

@ -1,4 +1,5 @@
using Assets.Scripts.Devices.Ble;
using Assets.Scripts.Devices.Ble.Win;
using System;
using System.Collections.Generic;
using System.Linq;

View File

@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace Assets.Scripts.Devices.Ble.Win
{
internal class WclAlertableWait
{
[DllImport("WclBlePluginCPP.dll", CallingConvention = CallingConvention.StdCall, EntryPoint = "WCLWait")]
[return: MarshalAs(UnmanagedType.U4)]
public static extern uint Wait([MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.SysInt, SizeParamIndex = 1)][In] IntPtr[] Handle, [MarshalAs(UnmanagedType.U4)][In] uint Cnt, [MarshalAs(UnmanagedType.U4)][In] uint Timeout);
// Token: 0x0600207E RID: 8318
[DllImport("WclBlePluginCPP.dll", CallingConvention = CallingConvention.StdCall, EntryPoint = "WCLFlushApc")]
[return: MarshalAs(UnmanagedType.U4)]
public static extern uint FlushApc();
// Token: 0x0600207F RID: 8319
[DllImport("WclBlePluginCPP.dll", CallingConvention = CallingConvention.StdCall, EntryPoint = "WCLSetApcSync")]
public static extern void SetApcSync();
// Token: 0x04001189 RID: 4489
public const uint WAIT_OBJECT_0 = 0U;
// Token: 0x0400118A RID: 4490
public const uint WAIT_IO_COMPLETION = 192U;
// Token: 0x0400118B RID: 4491
public const uint WAIT_TIMEOUT = 258U;
// Token: 0x0400118C RID: 4492
public const uint WAIT_FAILED = 4294967295U;
// Token: 0x0400118D RID: 4493
public const uint INFINITE = 4294967295U;
// Token: 0x0400118E RID: 4494
public const string GuidTemplate = "0000XXXX-0000-1000-8000-00805F9B34FB";
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4ec3942a1d98efc4eb36cd00001109b1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,15 +1,16 @@
using Assets.Scripts.Ble.Win;
using Assets.Scripts.Ble;
using Assets.Scripts.Ble.Win;
using Assets.Scripts.Ble.Win.CPPBridge;
using Assets.Scripts.Devices.Ble;
using Assets.Scripts.Devices.Ble.Win;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
namespace Assets.Scripts.Ble
namespace Assets.Scripts.Devices.Ble.Win
{
internal class WclBleGattThread
{
@ -163,7 +164,12 @@ namespace Assets.Scripts.Ble
private readonly long address;
public BlePeripheralInfo Peripheral { get; }
private Thread thread;
private ConcurrentQueue<Action> actions = new ConcurrentQueue<Action>();
private bool start = false;
private ManualResetEvent sEvent;
private AutoResetEvent aEvent;
private ManualResetEvent tEvent;
internal WclBleGattThread(BlePeripheralInfo bleDevice, IntPtr radio)
{
this.Peripheral = bleDevice;
@ -175,10 +181,84 @@ namespace Assets.Scripts.Ble
public bool Start()
{
this.SetUpWorkerThread();
if (this.start)
{
return false;
}
this.start = true;
this.sEvent = new ManualResetEvent(false);
this.tEvent = new ManualResetEvent(false);
this.aEvent = new AutoResetEvent(false);
//thread = new Thread(new ThreadStart(ThreadProc));
// this.sEvent.Set();
// IntPtr[] handle = new IntPtr[]
// {
// this.tEvent.SafeWaitHandle.DangerousGetHandle(),
// this.aEvent.SafeWaitHandle.DangerousGetHandle()
// };
// while (WclAlertableWait.Wait(handle, 2U, WclAlertableWait.INFINITE) != WclAlertableWait.WAIT_OBJECT_0)
// {
// Action action;
// if (this.actions.TryDequeue(out action))
// {
// if (action != null)
// {
// Debug.Log("连接设备");
// action();
// }
// }
// }
// }));
//thread.Start();
//this.sEvent.WaitOne();
ThreadProc();
return true;
}
public void Stop()
{
Debug.Log("停止");
start = false;
thread?.Abort();
}
private void ThreadProc()
{
this.SetUpWorkerThread();
//this.sEvent.Set();
//this.DoWorkerLoop();
//this.CleanUpWorkerThread();
//this.InternalCleanUp();
}
private void CleanUpWorkerThread()
{
this.gatt.Disconnect();
this.gatt.Dispose();
this.gatt = null;
}
private void InternalCleanUp()
{
this.sEvent.Close();
this.sEvent.Dispose();
this.sEvent = null;
this.aEvent.Close();
this.aEvent.Dispose();
this.aEvent = null;
this.tEvent.Close();
this.tEvent.Dispose();
this.tEvent = null;
}
private void SetUpWorkerThread()
{
this.gatt = new WclBleGattClient(this.address, this.rPtr)
@ -189,6 +269,28 @@ namespace Assets.Scripts.Ble
};
}
private void DoWorkerLoop()
{
IntPtr[] handle = new IntPtr[]
{
this.tEvent.SafeWaitHandle.DangerousGetHandle(),
this.aEvent.SafeWaitHandle.DangerousGetHandle()
};
while (WclAlertableWait.Wait(handle, 2U, 4294967295U) != 0U)
{
//this.InvokeQueuedActions();
Action action;
if (this.actions.TryDequeue(out action))
{
if (action != null)
{
Debug.Log("连接设备");
action();
}
}
}
}
private void OnCharacteristicChanged(WclBleGattClient connection, ushort handle, byte[] value)
{
if (!this.subscribedCharHandles.ContainsKey(handle)) {
@ -260,6 +362,10 @@ namespace Assets.Scripts.Ble
public int Connect()
{
this.gatt.Connect();
//this.actions.Enqueue(() =>
//{
// this.gatt.Connect();
//});
return WclBleErrors.WCL_E_SUCCESS;
}
@ -381,6 +487,21 @@ namespace Assets.Scripts.Ble
this.gattCharacteristicsSubscribed(this, characteristic, response);
}
public int WriteCharacteristic(BleCharacteristicInfo characteristicInfo, byte[] data)
{
if (!this.charMapping.ContainsKey(characteristicInfo))
{
return WclBleErrors.WCL_E_CONNECTION_NOT_ACTIVE;
}
GattCharacteristic gCh = this.charMapping[characteristicInfo];
int result = this.gatt.WriteCharacteristic(gCh, data);
return WclBleErrors.WCL_E_SUCCESS;
}
private class WinBleCharacteristicInfo : BleCharacteristicInfo
{
// Token: 0x06003F85 RID: 16261 RVA: 0x000EA274 File Offset: 0x000E8474

View File

@ -1,4 +1,5 @@
using Assets.Scripts.Ble.CPPBridge;
using Assets.Scripts.Ble;
using Assets.Scripts.Ble.CPPBridge;
using Assets.Scripts.Devices.Ble;
using System;
using System.Collections.Generic;
@ -8,7 +9,7 @@ using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
namespace Assets.Scripts.Ble
namespace Assets.Scripts.Devices.Ble.Win
{
internal class WclBleMainThread
{
@ -99,6 +100,12 @@ namespace Assets.Scripts.Ble
public void Dispose()
{
Debug.Log("停止thread");
foreach (var item in this.gattClients.Values)
{
item.Stop();
}
wclBleManager.Dispose();
}

View File

@ -0,0 +1,215 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.Concurrent;
using System.Threading;
namespace Assets.Scripts.Devices.Ble.Win
{
internal abstract class WclBleThread
{
// Token: 0x170005E2 RID: 1506
// (get) Token: 0x06002114 RID: 8468 RVA: 0x000890FE File Offset: 0x000872FE
public bool IsRunning
{
get
{
return this.started || this.IsTerminating;
}
}
// Token: 0x170005E3 RID: 1507
// (get) Token: 0x06002115 RID: 8469 RVA: 0x00089112 File Offset: 0x00087312
public bool IsTerminating
{
get
{
return this.terminating;
}
}
// Token: 0x170005E4 RID: 1508
// (get) Token: 0x06002116 RID: 8470 RVA: 0x0008911C File Offset: 0x0008731C
public bool CanLoadWork
{
get
{
return this.IsRunning && !this.IsTerminating;
}
}
// Token: 0x06002117 RID: 8471
protected abstract void SetUpWorkerThread();
// Token: 0x06002118 RID: 8472
protected abstract void CleanUpWorkerThread();
// Token: 0x06002119 RID: 8473 RVA: 0x00089131 File Offset: 0x00087331
protected void TreadProcInitialized()
{
this.sEvent.Set();
}
// Token: 0x0600211A RID: 8474 RVA: 0x0008913F File Offset: 0x0008733F
protected void ProcessPendingAPCMessages()
{
while (WclAlertableWait.FlushApc() != 0U)
{
}
}
// Token: 0x0600211B RID: 8475 RVA: 0x00089148 File Offset: 0x00087348
protected void InternalCleanUp()
{
this.sEvent.Close();
this.sEvent.Dispose();
this.sEvent = null;
this.aEvent.Close();
this.aEvent.Dispose();
this.aEvent = null;
this.tEvent.Close();
this.tEvent.Dispose();
this.tEvent = null;
this.terminating = false;
}
// Token: 0x0600211C RID: 8476 RVA: 0x000891B5 File Offset: 0x000873B5
private void ThreadProc()
{
this.SetUpWorkerThread();
this.sEvent.Set();
this.DoWorkerLoop();
this.CleanUpWorkerThread();
this.InternalCleanUp();
}
// Token: 0x0600211D RID: 8477 RVA: 0x000891DC File Offset: 0x000873DC
private void DoWorkerLoop()
{
IntPtr[] handle = new IntPtr[]
{
this.tEvent.SafeWaitHandle.DangerousGetHandle(),
this.aEvent.SafeWaitHandle.DangerousGetHandle()
};
while (WclAlertableWait.Wait(handle, 2U, 4294967295U) != 0U && !this.terminating)
{
this.InvokeQueuedActions();
if (this.terminating)
{
break;
}
}
}
// Token: 0x0600211E RID: 8478 RVA: 0x0008923C File Offset: 0x0008743C
public virtual bool Start()
{
if (this.IsRunning)
{
return false;
}
this.sEvent = new ManualResetEvent(false);
this.tEvent = new ManualResetEvent(false);
this.aEvent = new AutoResetEvent(false);
this.wThread = new Thread(new ThreadStart(this.ThreadProc));
this.wThread.Start();
this.sEvent.WaitOne();
this.started = true;
return true;
}
// Token: 0x0600211F RID: 8479 RVA: 0x000892B0 File Offset: 0x000874B0
public virtual bool Stop()
{
if (!this.started || this.IsTerminating)
{
return false;
}
this.terminating = true;
this.tEvent.Set();
this.wThread = null;
this.ClearActionQueue();
this.started = false;
return true;
}
// Token: 0x06002120 RID: 8480 RVA: 0x000892FD File Offset: 0x000874FD
protected bool EnqueueAction(Action action)
{
if (this.CanLoadWork)
{
this.aQueue.Enqueue(action);
this.aEvent.Set();
return true;
}
return false;
}
// Token: 0x06002121 RID: 8481 RVA: 0x00089324 File Offset: 0x00087524
protected void ClearActionQueue()
{
Action action;
while (this.aQueue.TryDequeue(out action))
{
}
}
// Token: 0x06002122 RID: 8482 RVA: 0x00089340 File Offset: 0x00087540
protected void InvokeQueuedActions()
{
while (this.DequeueAdnInvokeAction())
{
}
}
// Token: 0x06002123 RID: 8483 RVA: 0x0008934C File Offset: 0x0008754C
protected bool DequeueAdnInvokeAction()
{
Action action = this.TryDequActione();
if (action == null)
{
return false;
}
action();
return true;
}
// Token: 0x06002124 RID: 8484 RVA: 0x0008936C File Offset: 0x0008756C
private Action TryDequActione()
{
if (!this.CanLoadWork)
{
return null;
}
Action result;
if (!this.aQueue.TryDequeue(out result))
{
return null;
}
return result;
}
// Token: 0x04001341 RID: 4929
protected ManualResetEvent tEvent;
// Token: 0x04001342 RID: 4930
protected AutoResetEvent aEvent;
// Token: 0x04001343 RID: 4931
private readonly ConcurrentQueue<Action> aQueue = new ConcurrentQueue<Action>();
// Token: 0x04001344 RID: 4932
protected ManualResetEvent sEvent;
// Token: 0x04001345 RID: 4933
protected Thread wThread;
// Token: 0x04001346 RID: 4934
protected volatile bool terminating;
// Token: 0x04001347 RID: 4935
private volatile bool started;
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 25f702c985f2a1b41990cc8fcdcb0450
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace Assets.Scripts.Devices
{
@ -45,6 +46,7 @@ namespace Assets.Scripts.Devices
var adapter = adapters.FirstOrDefault(a => a.Interface == connectionInterface);
if(adapter != null)
{
Debug.Log("bbbbbb " + (adapter.GetState().ToString()));
return adapter.GetState();
}
return DeviceAdapterState.Unavailable;

View File

@ -10,6 +10,7 @@ public class DeviceItem : Selectable, IEventSystemHandler, IPointerClickHandler
{
private bool isOn;
private Text mText;
private Text mType;
public AbstractDevice DeviceInfo
{
get;set;
@ -18,6 +19,7 @@ public class DeviceItem : Selectable, IEventSystemHandler, IPointerClickHandler
protected override void Awake()
{
mText = this.transform.Find("Name").GetComponent<Text>();
mType = this.transform.Find("Type").GetComponent<Text>();
}
// Start is called before the first frame update
@ -25,11 +27,17 @@ public class DeviceItem : Selectable, IEventSystemHandler, IPointerClickHandler
{
//this.currentSelectionState = SelectionState.Selected
mText.text = DeviceInfo.Name + "-" + DeviceInfo.DeviceNumber;
mText.text = DeviceInfo.Name;// + "-" + DeviceInfo.DeviceNumber;
if(DeviceInfo.Network == NetworkType.ANT)
{
mText.text += "-" + DeviceInfo.DeviceNumber;
}
//if(DeviceInfo.State == DeviceState.Connected)
{
// this.SetSelectedStyle();
}
mType.text = DeviceInfo.Network.ToString();
}

View File

@ -19,7 +19,7 @@ public class MainNav : MonoBehaviour
var device = this.transform.Find("Device");
UIManager.AddEvent(device.gameObject, EventTriggerType.PointerClick, x =>
{
Debug.Log("click device");
//Debug.Log("click device");
UIManager.ShowDevicePanel();
});

View File

@ -8,6 +8,7 @@ using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using DG.Tweening;
using Assets.Scripts.Devices.Ble.Devices;
public class DeviceController : PFUIPanel
{
@ -21,6 +22,11 @@ public class DeviceController : PFUIPanel
RectTransform circulo;
private GameObject statusText;
private Image bleStatus;
private Sprite ble0;
private Sprite ble1;
RectTransform bleCirculo;
private bool _available = false;
private bool Available
{
@ -54,11 +60,38 @@ public class DeviceController : PFUIPanel
}
}
private bool _bleAvailable = false;
private bool BleAvailable
{
get
{
return _bleAvailable;
}
set
{
if (value == _bleAvailable)
return;
_bleAvailable = value;
if (_bleAvailable)
{
bleStatus.sprite = ble1;
DOTween.Kill(bleCirculo, false);
bleCirculo.GetComponent<Image>().sprite = circuloCompleto;
}
else
{
bleStatus.sprite = ble0;
bleCirculo.GetComponent<Image>().sprite = circuloCarga;
bleCirculo.DOLocalRotate(new Vector3(0, 0, 360), 1f, RotateMode.FastBeyond360).SetEase(Ease.Linear).SetLoops(-1);
}
}
}
protected override void Awake()
{
base.Awake();
Debug.Log("device awake");
//Debug.Log("device awake");
}
// Start is called before the first frame update
@ -83,14 +116,13 @@ public class DeviceController : PFUIPanel
// Debug.Log("aaaaaaaaaaaaaa");
//});
var bg = this.transform.Find("Status").Find("Bg");
antStatus = bg.Find("Ant+").GetComponent<Image>();
var bg = this.transform.Find("Status").Find("Bg");
statusText = this.transform.Find("Status").Find("GameObject").gameObject;
statusText.SetActive(false);
circulo = bg.Find("Image").GetComponent<RectTransform>();
antStatus = bg.Find("AntIcon/Ant+").GetComponent<Image>();
circulo = bg.Find("AntIcon/Image").GetComponent<RectTransform>();
//circulo.DORotate(new Vector3(0, 0, 360), 1.5f, RotateMode.Fast).SetLoops(-1);
circulo.DOLocalRotate(new Vector3(0, 0, 360), 1f, RotateMode.FastBeyond360).SetEase(Ease.Linear).SetLoops(-1);
@ -99,7 +131,42 @@ public class DeviceController : PFUIPanel
circuloCarga = Resources.Load<Sprite>("Images/icon-circulo-carga");
circuloCompleto = Resources.Load<Sprite>("Images/icon-circulo-completo");
bleStatus = bg.Find("BleIcon/Ble").GetComponent<Image>();
bleCirculo = bg.Find("BleIcon/Image").GetComponent<RectTransform>();
bleCirculo.DOLocalRotate(new Vector3(0, 0, 360), 1f, RotateMode.FastBeyond360).SetEase(Ease.Linear).SetLoops(-1);
ble0 = Resources.Load<Sprite>("Images/Bluetooth_0");
ble1 = Resources.Load<Sprite>("Images/Bluetooth_2");
base.SetRounded(bg, 64);
//var text = this.transform.Find("InputField").GetComponent<InputField>();
//var btn = this.transform.Find("Button").GetComponent<Button>();
//btn.onClick.AddListener(() =>
//{
// //Debug.Log(text.text);
// var device = App.MainDeviceAdapter.GetDevices().FirstOrDefault(d => d.State == DeviceState.Connected && d.Sensor == SensorType.HeartRate);
// if(device == null)
// {
// return;
// }
// //(device as HeartRate).GetBatteryLevel();
// //(device as Tacx).SetResistanceMode(int.Parse(text.text));
// //if (device is Tacx)
// //{
// // (device as Tacx).SetTrackResistance(double.Parse(text.text));
// //}
// //else if(device is FitDevice)
// //{
// // (device as FitDevice).SetErgMode(100* 4);
// //}
//});
}
@ -109,9 +176,11 @@ public class DeviceController : PFUIPanel
timer -= Time.deltaTime;
if (timer <= 0)
{
//Available = AntConnector.Instance().IsAvailable;
Available = App.MainDeviceAdapter.GetState(Assets.Scripts.Devices.ConnectionInterface.ANT) == Assets.Scripts.Devices.DeviceAdapterState.On;
BleAvailable = App.MainDeviceAdapter.GetState(Assets.Scripts.Devices.ConnectionInterface.BLE) == Assets.Scripts.Devices.DeviceAdapterState.On;
//Debug.Log("ble available:" + App.MainDeviceAdapter.GetState(Assets.Scripts.Devices.ConnectionInterface.BLE).ToString());
timer = 1.0f;
}
}