diff --git a/Assets/Resources/Images/Bluetooth_0.png b/Assets/Resources/Images/Bluetooth_0.png new file mode 100644 index 00000000..791ebc96 Binary files /dev/null and b/Assets/Resources/Images/Bluetooth_0.png differ diff --git a/Assets/Resources/Images/Bluetooth_0.png.meta b/Assets/Resources/Images/Bluetooth_0.png.meta new file mode 100644 index 00000000..eef33745 --- /dev/null +++ b/Assets/Resources/Images/Bluetooth_0.png.meta @@ -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: diff --git a/Assets/Resources/Images/Bluetooth_2.png b/Assets/Resources/Images/Bluetooth_2.png new file mode 100644 index 00000000..2d7e336c Binary files /dev/null and b/Assets/Resources/Images/Bluetooth_2.png differ diff --git a/Assets/Resources/Images/Bluetooth_2.png.meta b/Assets/Resources/Images/Bluetooth_2.png.meta new file mode 100644 index 00000000..fb42216f --- /dev/null +++ b/Assets/Resources/Images/Bluetooth_2.png.meta @@ -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: diff --git a/Assets/Resources/UI/Prefab/Panel/DevicePanel.prefab b/Assets/Resources/UI/Prefab/Panel/DevicePanel.prefab index 9440650e..eab71612 100644 --- a/Assets/Resources/UI/Prefab/Panel/DevicePanel.prefab +++ b/Assets/Resources/UI/Prefab/Panel/DevicePanel.prefab @@ -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 diff --git a/Assets/Scripts/Apis/MapApi.cs b/Assets/Scripts/Apis/MapApi.cs index ee431127..1bddc2eb 100644 --- a/Assets/Scripts/Apis/MapApi.cs +++ b/Assets/Scripts/Apis/MapApi.cs @@ -141,6 +141,7 @@ namespace Assets.Scripts.Apis public async Task> GetEarthData(double lat, double lon) { + //CultureInfo.InvariantCulture var result = await GetAsync>($"Map/GetEarthData?lat={ lat }&lon={ lon }"); return result; diff --git a/Assets/Scripts/Devices/AbstractDevice.cs b/Assets/Scripts/Devices/AbstractDevice.cs index 0ecc01ea..4fccfd2e 100644 --- a/Assets/Scripts/Devices/AbstractDevice.cs +++ b/Assets/Scripts/Devices/AbstractDevice.cs @@ -26,6 +26,20 @@ namespace Assets.Scripts.Devices get; protected set; } + public NetworkType Network { get; internal set; } + + /// + /// 信号强度 + /// 信号从0dbm~97dbm,个人建议是0~30是强,30~70是中,70~97是弱。 + /// + public int SignalStrength { get; internal set; } + + protected AbstractDevice(NetworkType networkType) + { + this.Network = networkType; + } + + public abstract void Connect(); public abstract void Disconnect(bool save = true); diff --git a/Assets/Scripts/Devices/Ant/DataSourceBase.cs b/Assets/Scripts/Devices/Ant/DataSourceBase.cs index 54098f96..978be736 100644 --- a/Assets/Scripts/Devices/Ant/DataSourceBase.cs +++ b/Assets/Scripts/Devices/Ant/DataSourceBase.cs @@ -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; diff --git a/Assets/Scripts/Devices/Ant/FitDevice.cs b/Assets/Scripts/Devices/Ant/FitDevice.cs index 947ed54d..08382a5b 100644 --- a/Assets/Scripts/Devices/Ant/FitDevice.cs +++ b/Assets/Scripts/Devices/Ant/FitDevice.cs @@ -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; diff --git a/Assets/Scripts/Devices/Ant/Interfaces/ITrainerDevice.cs b/Assets/Scripts/Devices/Ant/Interfaces/ITrainerDevice.cs new file mode 100644 index 00000000..1a5df313 --- /dev/null +++ b/Assets/Scripts/Devices/Ant/Interfaces/ITrainerDevice.cs @@ -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); + } +} diff --git a/Assets/Scripts/Devices/Ant/Interfaces/ITrainerDevice.cs.meta b/Assets/Scripts/Devices/Ant/Interfaces/ITrainerDevice.cs.meta new file mode 100644 index 00000000..4d738a92 --- /dev/null +++ b/Assets/Scripts/Devices/Ant/Interfaces/ITrainerDevice.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 362cacfc69d803d478112e7c293cfa17 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Devices/Ant/SensorType.cs b/Assets/Scripts/Devices/Ant/SensorType.cs index 7b8c6469..259aca9f 100644 --- a/Assets/Scripts/Devices/Ant/SensorType.cs +++ b/Assets/Scripts/Devices/Ant/SensorType.cs @@ -39,4 +39,10 @@ namespace Assets.Scripts.Devices.Ant Trainer, VirtualPower, } + + public enum NetworkType + { + BLE, + ANT + } } diff --git a/Assets/Scripts/Devices/Ble/BleDevice.cs b/Assets/Scripts/Devices/Ble/BleDevice.cs index 6041daef..133543d5 100644 --- a/Assets/Scripts/Devices/Ble/BleDevice.cs +++ b/Assets/Scripts/Devices/Ble/BleDevice.cs @@ -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 Characteristics { get; protected set; } = new List(); + + 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); } } } diff --git a/Assets/Scripts/Devices/Ble/BleDeviceAdapter.cs b/Assets/Scripts/Devices/Ble/BleDeviceAdapter.cs index d4be45c5..de47e3eb 100644 --- a/Assets/Scripts/Devices/Ble/BleDeviceAdapter.cs +++ b/Assets/Scripts/Devices/Ble/BleDeviceAdapter.cs @@ -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 }"); + } }); } diff --git a/Assets/Scripts/Devices/Ble/Characteristic/BatteryLevel.cs b/Assets/Scripts/Devices/Ble/Characteristic/BatteryLevel.cs new file mode 100644 index 00000000..64a15fb0 --- /dev/null +++ b/Assets/Scripts/Devices/Ble/Characteristic/BatteryLevel.cs @@ -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; + } + } +} diff --git a/Assets/Scripts/Devices/Ble/Characteristic/BatteryLevel.cs.meta b/Assets/Scripts/Devices/Ble/Characteristic/BatteryLevel.cs.meta new file mode 100644 index 00000000..29b9995a --- /dev/null +++ b/Assets/Scripts/Devices/Ble/Characteristic/BatteryLevel.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b654b3a52c37e6f47b9823953c8fd5d1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Devices/Ble/Characteristic/CyclingPowerMeasurement.cs b/Assets/Scripts/Devices/Ble/Characteristic/CyclingPowerMeasurement.cs new file mode 100644 index 00000000..5015ddec --- /dev/null +++ b/Assets/Scripts/Devices/Ble/Characteristic/CyclingPowerMeasurement.cs @@ -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() + { + + } + } +} diff --git a/Assets/Scripts/Devices/Ble/Characteristic/CyclingPowerMeasurement.cs.meta b/Assets/Scripts/Devices/Ble/Characteristic/CyclingPowerMeasurement.cs.meta new file mode 100644 index 00000000..90ee0d7c --- /dev/null +++ b/Assets/Scripts/Devices/Ble/Characteristic/CyclingPowerMeasurement.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9b0646295dbaa0a4e951de9bd391e475 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Devices/Ble/Characteristic/CyclingSpeedCadenceMeasurement.cs b/Assets/Scripts/Devices/Ble/Characteristic/CyclingSpeedCadenceMeasurement.cs new file mode 100644 index 00000000..2a9d6ee2 --- /dev/null +++ b/Assets/Scripts/Devices/Ble/Characteristic/CyclingSpeedCadenceMeasurement.cs @@ -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; + } + } + } + } +} diff --git a/Assets/Scripts/Devices/Ble/Characteristic/CyclingSpeedCadenceMeasurement.cs.meta b/Assets/Scripts/Devices/Ble/Characteristic/CyclingSpeedCadenceMeasurement.cs.meta new file mode 100644 index 00000000..0f2c2161 --- /dev/null +++ b/Assets/Scripts/Devices/Ble/Characteristic/CyclingSpeedCadenceMeasurement.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0ea4d73295288ad4a9f561a37246110a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Devices/Ble/Characteristic/FtmsFitnessMachineFeature.cs b/Assets/Scripts/Devices/Ble/Characteristic/FtmsFitnessMachineFeature.cs new file mode 100644 index 00000000..a0417bb4 --- /dev/null +++ b/Assets/Scripts/Devices/Ble/Characteristic/FtmsFitnessMachineFeature.cs @@ -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(); + } + } +} diff --git a/Assets/Scripts/Devices/Ble/Characteristic/FtmsFitnessMachineFeature.cs.meta b/Assets/Scripts/Devices/Ble/Characteristic/FtmsFitnessMachineFeature.cs.meta new file mode 100644 index 00000000..da4dfc4f --- /dev/null +++ b/Assets/Scripts/Devices/Ble/Characteristic/FtmsFitnessMachineFeature.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c6095c96152da4c47870708061e1d5ac +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Devices/Ble/Characteristic/FtmsIndoorBikeData.cs b/Assets/Scripts/Devices/Ble/Characteristic/FtmsIndoorBikeData.cs new file mode 100644 index 00000000..f9f2b0d8 --- /dev/null +++ b/Assets/Scripts/Devices/Ble/Characteristic/FtmsIndoorBikeData.cs @@ -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 list = new List(); + 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, + } + } +} diff --git a/Assets/Scripts/Devices/Ble/Characteristic/FtmsIndoorBikeData.cs.meta b/Assets/Scripts/Devices/Ble/Characteristic/FtmsIndoorBikeData.cs.meta new file mode 100644 index 00000000..13929831 --- /dev/null +++ b/Assets/Scripts/Devices/Ble/Characteristic/FtmsIndoorBikeData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e3577d5802131984c9463ba280183935 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Devices/Ble/Characteristic/HeartRateMeasurement.cs b/Assets/Scripts/Devices/Ble/Characteristic/HeartRateMeasurement.cs index 00985c01..aefdb328 100644 --- a/Assets/Scripts/Devices/Ble/Characteristic/HeartRateMeasurement.cs +++ b/Assets/Scripts/Devices/Ble/Characteristic/HeartRateMeasurement.cs @@ -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 { diff --git a/Assets/Scripts/Devices/Ble/Characteristic/ICharacteristicExtensions.cs b/Assets/Scripts/Devices/Ble/Characteristic/ICharacteristicExtensions.cs new file mode 100644 index 00000000..1f96eca3 --- /dev/null +++ b/Assets/Scripts/Devices/Ble/Characteristic/ICharacteristicExtensions.cs @@ -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)); + } + } +} diff --git a/Assets/Scripts/Devices/Ble/Characteristic/ICharacteristicExtensions.cs.meta b/Assets/Scripts/Devices/Ble/Characteristic/ICharacteristicExtensions.cs.meta new file mode 100644 index 00000000..00c122d5 --- /dev/null +++ b/Assets/Scripts/Devices/Ble/Characteristic/ICharacteristicExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 238bc68320174c9429b5958b5207b2c5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Devices/Ble/Characteristic/ManufacturerNameCharacteristic.cs b/Assets/Scripts/Devices/Ble/Characteristic/ManufacturerNameCharacteristic.cs new file mode 100644 index 00000000..cd7aa54a --- /dev/null +++ b/Assets/Scripts/Devices/Ble/Characteristic/ManufacturerNameCharacteristic.cs @@ -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(); + } + } +} diff --git a/Assets/Scripts/Devices/Ble/Characteristic/ManufacturerNameCharacteristic.cs.meta b/Assets/Scripts/Devices/Ble/Characteristic/ManufacturerNameCharacteristic.cs.meta new file mode 100644 index 00000000..04d9c4e0 --- /dev/null +++ b/Assets/Scripts/Devices/Ble/Characteristic/ManufacturerNameCharacteristic.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4f0a2af64ce8fe84cbdf0e5a14bbc8ae +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Devices/Ble/Characteristic/ModelNumberCharacteristic.cs b/Assets/Scripts/Devices/Ble/Characteristic/ModelNumberCharacteristic.cs new file mode 100644 index 00000000..4f9080be --- /dev/null +++ b/Assets/Scripts/Devices/Ble/Characteristic/ModelNumberCharacteristic.cs @@ -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(); + } + } +} diff --git a/Assets/Scripts/Devices/Ble/Characteristic/ModelNumberCharacteristic.cs.meta b/Assets/Scripts/Devices/Ble/Characteristic/ModelNumberCharacteristic.cs.meta new file mode 100644 index 00000000..5a6c87f0 --- /dev/null +++ b/Assets/Scripts/Devices/Ble/Characteristic/ModelNumberCharacteristic.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ef489f56607ba2540b8954b67e1a4d3e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Devices/Ble/Characteristic/TacxFecNotify.cs b/Assets/Scripts/Devices/Ble/Characteristic/TacxFecNotify.cs new file mode 100644 index 00000000..40e5d446 --- /dev/null +++ b/Assets/Scripts/Devices/Ble/Characteristic/TacxFecNotify.cs @@ -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() + { + } + } +} diff --git a/Assets/Scripts/Devices/Ble/Characteristic/TacxFecNotify.cs.meta b/Assets/Scripts/Devices/Ble/Characteristic/TacxFecNotify.cs.meta new file mode 100644 index 00000000..da2a81db --- /dev/null +++ b/Assets/Scripts/Devices/Ble/Characteristic/TacxFecNotify.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 640f21041d0bf6347b1e48669c9a8691 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Devices/Ble/Characteristic/TacxFecWrite.cs b/Assets/Scripts/Devices/Ble/Characteristic/TacxFecWrite.cs new file mode 100644 index 00000000..a27a2d56 --- /dev/null +++ b/Assets/Scripts/Devices/Ble/Characteristic/TacxFecWrite.cs @@ -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() + { + } + } +} diff --git a/Assets/Scripts/Devices/Ble/Characteristic/TacxFecWrite.cs.meta b/Assets/Scripts/Devices/Ble/Characteristic/TacxFecWrite.cs.meta new file mode 100644 index 00000000..d2f85994 --- /dev/null +++ b/Assets/Scripts/Devices/Ble/Characteristic/TacxFecWrite.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6c9564a0124aa5946b25bdd1a97ade2c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Devices/Ble/Devices/CyclingPower.cs b/Assets/Scripts/Devices/Ble/Devices/CyclingPower.cs index bbfafbd1..3be1eb54 100644 --- a/Assets/Scripts/Devices/Ble/Devices/CyclingPower.cs +++ b/Assets/Scripts/Devices/Ble/Devices/CyclingPower.cs @@ -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 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 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 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); + } + } } } } diff --git a/Assets/Scripts/Devices/Ble/Devices/Ftms.cs b/Assets/Scripts/Devices/Ble/Devices/Ftms.cs index 141fbcfc..5cc33535 100644 --- a/Assets/Scripts/Devices/Ble/Devices/Ftms.cs +++ b/Assets/Scripts/Devices/Ble/Devices/Ftms.cs @@ -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 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 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 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(); } + + /// + /// 轨道阻力 + /// + /// + 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();// { 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()); + } } } diff --git a/Assets/Scripts/Devices/Ble/Devices/HeartRate.cs b/Assets/Scripts/Devices/Ble/Devices/HeartRate.cs index 437e3798..3f1faf04 100644 --- a/Assets/Scripts/Devices/Ble/Devices/HeartRate.cs +++ b/Assets/Scripts/Devices/Ble/Devices/HeartRate.cs @@ -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 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 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); } } diff --git a/Assets/Scripts/Devices/Ble/Devices/SpeedCadence.cs b/Assets/Scripts/Devices/Ble/Devices/SpeedCadence.cs new file mode 100644 index 00000000..61453909 --- /dev/null +++ b/Assets/Scripts/Devices/Ble/Devices/SpeedCadence.cs @@ -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 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 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 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); + } + } + } + } +} diff --git a/Assets/Scripts/Devices/Ble/Devices/SpeedCadence.cs.meta b/Assets/Scripts/Devices/Ble/Devices/SpeedCadence.cs.meta new file mode 100644 index 00000000..9dce6618 --- /dev/null +++ b/Assets/Scripts/Devices/Ble/Devices/SpeedCadence.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3fc83bb07e841d04bb491332cdfeb669 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Devices/Ble/Devices/Tacx.cs b/Assets/Scripts/Devices/Ble/Devices/Tacx.cs new file mode 100644 index 00000000..e995f866 --- /dev/null +++ b/Assets/Scripts/Devices/Ble/Devices/Tacx.cs @@ -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 Services; + + private TacxFecNotify tacxFecNotify; + private BleCharacteristicInfo tacxFecWriteCharacteristic; + + private TacxFecWrite tacxFecWrite; + + /// + /// 当前坡度,当切换到其他模式的时候,需要把坡度设置为0 + /// + 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 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 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 data = new List { 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()); + } + + /// + /// 阻力模式 + /// + /// + 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 data = new List { 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()); + } + + /// + /// 风阻 + /// + /// 海拔高度,单位米 + 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 data = new List { 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()); + } + + /// + /// 轨道阻力 + /// + /// 坡度百分比的值,单位是% + 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 data = new List { 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 data = new List { 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()); + } + } +} diff --git a/Assets/Scripts/Devices/Ble/Devices/Tacx.cs.meta b/Assets/Scripts/Devices/Ble/Devices/Tacx.cs.meta new file mode 100644 index 00000000..5cbe2c59 --- /dev/null +++ b/Assets/Scripts/Devices/Ble/Devices/Tacx.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d133d5406fc475841a781b88d91dc139 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Devices/Ble/Extension/PrimitiveExtensions.cs b/Assets/Scripts/Devices/Ble/Extension/PrimitiveExtensions.cs new file mode 100644 index 00000000..e2454026 --- /dev/null +++ b/Assets/Scripts/Devices/Ble/Extension/PrimitiveExtensions.cs @@ -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; + } + } +} diff --git a/Assets/Scripts/Devices/Ble/Extension/PrimitiveExtensions.cs.meta b/Assets/Scripts/Devices/Ble/Extension/PrimitiveExtensions.cs.meta new file mode 100644 index 00000000..bdc5bb7d --- /dev/null +++ b/Assets/Scripts/Devices/Ble/Extension/PrimitiveExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d49278e3c0c3a8a43bc1145abf67a5c0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Devices/Ble/Interfaces.meta b/Assets/Scripts/Devices/Ble/Interfaces.meta new file mode 100644 index 00000000..b33d5ffc --- /dev/null +++ b/Assets/Scripts/Devices/Ble/Interfaces.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c3f0cb374472e52419b47f6e6d582341 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Devices/Ble/Interfaces/ICharacteristic.cs b/Assets/Scripts/Devices/Ble/Interfaces/ICharacteristic.cs new file mode 100644 index 00000000..99678344 --- /dev/null +++ b/Assets/Scripts/Devices/Ble/Interfaces/ICharacteristic.cs @@ -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(); + } +} diff --git a/Assets/Scripts/Devices/Ble/Interfaces/ICharacteristic.cs.meta b/Assets/Scripts/Devices/Ble/Interfaces/ICharacteristic.cs.meta new file mode 100644 index 00000000..305813bf --- /dev/null +++ b/Assets/Scripts/Devices/Ble/Interfaces/ICharacteristic.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e333c9c3812a8654c87353be9e87e84e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Devices/Ble/Scan/BleAdvertisementInfo.cs b/Assets/Scripts/Devices/Ble/Scan/BleAdvertisementInfo.cs index b66dcdf9..b2436be2 100644 --- a/Assets/Scripts/Devices/Ble/Scan/BleAdvertisementInfo.cs +++ b/Assets/Scripts/Devices/Ble/Scan/BleAdvertisementInfo.cs @@ -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 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 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; } } } diff --git a/Assets/Scripts/Devices/Ble/Win/BleDeviceProxy.cs b/Assets/Scripts/Devices/Ble/Win/BleDeviceProxy.cs index 498a4540..d52e6bbe 100644 --- a/Assets/Scripts/Devices/Ble/Win/BleDeviceProxy.cs +++ b/Assets/Scripts/Devices/Ble/Win/BleDeviceProxy.cs @@ -7,7 +7,7 @@ using System.Threading.Tasks; namespace Assets.Scripts.Ble { /// - /// rouvy里的 WinBlePeripheralInfo类 + /// r里的 WinBlePeripheralInfo类 /// public class BleDeviceProxy { diff --git a/Assets/Scripts/Devices/Ble/Win/BleWinHwInterface.cs b/Assets/Scripts/Devices/Ble/Win/BleWinHwInterface.cs index e9a623e5..8f606167 100644 --- a/Assets/Scripts/Devices/Ble/Win/BleWinHwInterface.cs +++ b/Assets/Scripts/Devices/Ble/Win/BleWinHwInterface.cs @@ -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 services = null; + if (service != null) { - SensorType sensor = SensorType.None; - List services = null; - if (service != null) - { - services = new List { service.Value }; - foreach(var item in ServiceUuids.Services) + services = new List { 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) diff --git a/Assets/Scripts/Devices/Ble/Win/GattDisconnected.cs b/Assets/Scripts/Devices/Ble/Win/GattDisconnected.cs index 1d54c17a..3a473f73 100644 --- a/Assets/Scripts/Devices/Ble/Win/GattDisconnected.cs +++ b/Assets/Scripts/Devices/Ble/Win/GattDisconnected.cs @@ -1,4 +1,5 @@ using Assets.Scripts.Devices.Ble; +using Assets.Scripts.Devices.Ble.Win; using System; using System.Collections.Generic; using System.Linq; diff --git a/Assets/Scripts/Devices/Ble/Win/GattServicesDiscovered.cs b/Assets/Scripts/Devices/Ble/Win/GattServicesDiscovered.cs index c39ed4dd..f25cfbfb 100644 --- a/Assets/Scripts/Devices/Ble/Win/GattServicesDiscovered.cs +++ b/Assets/Scripts/Devices/Ble/Win/GattServicesDiscovered.cs @@ -1,4 +1,5 @@ using Assets.Scripts.Devices.Ble; +using Assets.Scripts.Devices.Ble.Win; using System; using System.Collections.Generic; using System.Linq; diff --git a/Assets/Scripts/Devices/Ble/Win/WclAlertableWait.cs b/Assets/Scripts/Devices/Ble/Win/WclAlertableWait.cs new file mode 100644 index 00000000..3b1b1664 --- /dev/null +++ b/Assets/Scripts/Devices/Ble/Win/WclAlertableWait.cs @@ -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"; + } +} diff --git a/Assets/Scripts/Devices/Ble/Win/WclAlertableWait.cs.meta b/Assets/Scripts/Devices/Ble/Win/WclAlertableWait.cs.meta new file mode 100644 index 00000000..d0af7774 --- /dev/null +++ b/Assets/Scripts/Devices/Ble/Win/WclAlertableWait.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4ec3942a1d98efc4eb36cd00001109b1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Devices/Ble/Win/WclBleGattThread.cs b/Assets/Scripts/Devices/Ble/Win/WclBleGattThread.cs index 869a7cb1..ffcb5561 100644 --- a/Assets/Scripts/Devices/Ble/Win/WclBleGattThread.cs +++ b/Assets/Scripts/Devices/Ble/Win/WclBleGattThread.cs @@ -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 actions = new ConcurrentQueue(); + 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 diff --git a/Assets/Scripts/Devices/Ble/Win/WclBleMainThread.cs b/Assets/Scripts/Devices/Ble/Win/WclBleMainThread.cs index 11f9535e..4eb35b05 100644 --- a/Assets/Scripts/Devices/Ble/Win/WclBleMainThread.cs +++ b/Assets/Scripts/Devices/Ble/Win/WclBleMainThread.cs @@ -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(); } diff --git a/Assets/Scripts/Devices/Ble/Win/WclBleThread.cs b/Assets/Scripts/Devices/Ble/Win/WclBleThread.cs new file mode 100644 index 00000000..08532eee --- /dev/null +++ b/Assets/Scripts/Devices/Ble/Win/WclBleThread.cs @@ -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 aQueue = new ConcurrentQueue(); + + // 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; + } +} diff --git a/Assets/Scripts/Devices/Ble/Win/WclBleThread.cs.meta b/Assets/Scripts/Devices/Ble/Win/WclBleThread.cs.meta new file mode 100644 index 00000000..bcce7694 --- /dev/null +++ b/Assets/Scripts/Devices/Ble/Win/WclBleThread.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 25f702c985f2a1b41990cc8fcdcb0450 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Devices/MainDeviceAdapter.cs b/Assets/Scripts/Devices/MainDeviceAdapter.cs index c1e93101..fc256c4f 100644 --- a/Assets/Scripts/Devices/MainDeviceAdapter.cs +++ b/Assets/Scripts/Devices/MainDeviceAdapter.cs @@ -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; diff --git a/Assets/Scripts/UI/Prefab/Device/DeviceItem.cs b/Assets/Scripts/UI/Prefab/Device/DeviceItem.cs index ccee8601..db9ee92f 100644 --- a/Assets/Scripts/UI/Prefab/Device/DeviceItem.cs +++ b/Assets/Scripts/UI/Prefab/Device/DeviceItem.cs @@ -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(); + mType = this.transform.Find("Type").GetComponent(); } // 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(); } diff --git a/Assets/Scripts/UI/Prefab/MainNav.cs b/Assets/Scripts/UI/Prefab/MainNav.cs index da54177f..6e4dd060 100644 --- a/Assets/Scripts/UI/Prefab/MainNav.cs +++ b/Assets/Scripts/UI/Prefab/MainNav.cs @@ -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(); }); diff --git a/Assets/Scripts/UI/Prefab/Panel/DeviceController.cs b/Assets/Scripts/UI/Prefab/Panel/DeviceController.cs index 6ff744fc..c49235f8 100644 --- a/Assets/Scripts/UI/Prefab/Panel/DeviceController.cs +++ b/Assets/Scripts/UI/Prefab/Panel/DeviceController.cs @@ -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().sprite = circuloCompleto; + } + else + { + bleStatus.sprite = ble0; + bleCirculo.GetComponent().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(); + var bg = this.transform.Find("Status").Find("Bg"); statusText = this.transform.Find("Status").Find("GameObject").gameObject; statusText.SetActive(false); - - circulo = bg.Find("Image").GetComponent(); + antStatus = bg.Find("AntIcon/Ant+").GetComponent(); + circulo = bg.Find("AntIcon/Image").GetComponent(); //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("Images/icon-circulo-carga"); circuloCompleto = Resources.Load("Images/icon-circulo-completo"); + + bleStatus = bg.Find("BleIcon/Ble").GetComponent(); + bleCirculo = bg.Find("BleIcon/Image").GetComponent(); + bleCirculo.DOLocalRotate(new Vector3(0, 0, 360), 1f, RotateMode.FastBeyond360).SetEase(Ease.Linear).SetLoops(-1); + ble0 = Resources.Load("Images/Bluetooth_0"); + ble1 = Resources.Load("Images/Bluetooth_2"); + base.SetRounded(bg, 64); + + + //var text = this.transform.Find("InputField").GetComponent(); + + //var btn = this.transform.Find("Button").GetComponent