C2课程同步问题

This commit is contained in:
lishuo 2022-06-23 18:55:08 +08:00
parent 84b3e1e7a4
commit 701ec3069b
8 changed files with 202 additions and 36 deletions

View File

@ -1,4 +1,5 @@
using Assets.Scripts.Devices.Ble.Characteristic; using Assets.Scripts.Ble.Commands;
using Assets.Scripts.Devices.Ble.Characteristic;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -14,5 +15,6 @@ namespace Assets.Scripts.Devices.Ant.Interfaces
void Reset(); void Reset();
void SetResistanceLevel(ushort v); void SetResistanceLevel(ushort v);
void C2GetStatus(byte[] bs); void C2GetStatus(byte[] bs);
void SendCommand(RowerCommand command,int data = 0);
} }
} }

View File

@ -48,5 +48,13 @@ namespace Assets.Scripts
{ {
return (uint)((int)bytes[startIndex] | (int)bytes[startIndex + 1] << 8 | (int)bytes[startIndex + 2] << 16); return (uint)((int)bytes[startIndex] | (int)bytes[startIndex + 1] << 8 | (int)bytes[startIndex + 2] << 16);
} }
public static byte[] HexToByteArray(string hex)
{
hex=hex.Replace(" ", "");
return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
}
} }
} }

View File

@ -11,6 +11,11 @@ namespace Assets.Scripts.Devices.Ble.Characteristic
{ {
public class C2RowerData : ICharacteristic, IRowerCommonData public class C2RowerData : ICharacteristic, IRowerCommonData
{ {
public static string JUSTROW_HEX = "F1 76 07 01 01 01 13 02 01 01 61 F2";
public static string TERMINATEWORKOUT_HEX = "F1 76 04 13 02 01 02 62 F2";
public static string FIXEDDISTANCE_HEX = "F1 76 18 01 01 03 03 05 80 00 00 07 D0 05 05 80 00 00 01 90 14 01 01 13 02 01 01 28 F2";
public static string FIXEDTIME_HEX = "F1 76 07 01 01 01 13 02 01 01 61 F2";
public Guid Uuid => ServiceUuids.Characteristics.C2RowerData35; public Guid Uuid => ServiceUuids.Characteristics.C2RowerData35;
public Guid ServiceUuid => ServiceUuids.Characteristics.C2Service; public Guid ServiceUuid => ServiceUuids.Characteristics.C2Service;
@ -133,17 +138,19 @@ namespace Assets.Scripts.Devices.Ble.Characteristic
InstantaneousPower = BitConvertHelper.ToUInt16(data, 4); InstantaneousPower = BitConvertHelper.ToUInt16(data, 4);
//TotalEnergy = BitConvertHelper.ToUInt16(data, 6); //TotalEnergy = BitConvertHelper.ToUInt16(data, 6);
} }
//else if (data[0] == 0x39) else if (data[0] == 0x39)
//{ {
// AverageStrokeRate = (int)data[11]; ElapsedTime = (int)(Convert.ToDouble(BitConvertHelper.ToUInt24(data, 1)) / 100);
// ResistanceLevel = (int)data[16]; TotalDistance = (uint)(Convert.ToDouble(BitConvertHelper.ToUInt24(data, 4)) / 10);
//} CompleteEvent?.Invoke(this, null);
}
//else if (data[0] == 34) //else if (data[0] == 34)
//{ //{
// isReadyStatus = data[2] == 1 || data[2] == 129; // isReadyStatus = data[2] == 1 || data[2] == 129;
//} //}
} }
public event EventHandler StartEvent; public event EventHandler StartEvent;
public event EventHandler CompleteEvent;
private double LbsToNewton(double lbs,bool isKg = false) => 4.4482216 * lbs * (isKg ? (1f / 9.8f) : 1f); private double LbsToNewton(double lbs,bool isKg = false) => 4.4482216 * lbs * (isKg ? (1f / 9.8f) : 1f);
public void SetUnavailable() public void SetUnavailable()
{ {

View File

@ -94,6 +94,8 @@ namespace Assets.Scripts.Devices.Ble.Characteristic
public event EventHandler PullChanged; public event EventHandler PullChanged;
//开始事件 //开始事件
public event EventHandler StartEvent; public event EventHandler StartEvent;
//结束事件
public event EventHandler CompleteEvent;
//日志事件 //日志事件
public event EventHandler LogEvent; public event EventHandler LogEvent;
private ushort _pullValue; private ushort _pullValue;

View File

@ -6,7 +6,14 @@ using System.Threading.Tasks;
namespace Assets.Scripts.Ble.Commands namespace Assets.Scripts.Ble.Commands
{ {
public enum CommandResponseCode public enum RowerCommand
{
JustRow = 1,
FixedDistance=2,
FixedTime = 3,
}
public enum CommandResponseCode
{ {
// Token: 0x04000FEB RID: 4075 // Token: 0x04000FEB RID: 4075
Success = 1, Success = 1,

View File

@ -1,4 +1,5 @@
using Assets.Scripts.Ble; using Assets.Scripts.Ble;
using Assets.Scripts.Ble.Commands;
using Assets.Scripts.Devices.Ant.Interfaces; using Assets.Scripts.Devices.Ant.Interfaces;
using Assets.Scripts.Devices.Ble.Characteristic; using Assets.Scripts.Devices.Ble.Characteristic;
using Assets.Scripts.Devices.Ble.Interfaces; using Assets.Scripts.Devices.Ble.Interfaces;
@ -21,7 +22,7 @@ namespace Assets.Scripts.Devices.Ble.Devices
public C2RowerData c2RowerData { get => _c2RowerData; } public C2RowerData c2RowerData { get => _c2RowerData; }
private List<BleServiceInfo> Services; private List<BleServiceInfo> Services;
private BleCharacteristicInfo controlPointCharacteristic; private BleCharacteristicInfo controlPointCharacteristic;
private BleCharacteristicInfo c2Control; private BleCharacteristicInfo c2Control { get; set; }
public FtmsRower(BlePeripheralInfo peripheralInfo, IBleWinHwInterface bleWinHwInterface) : base(peripheralInfo, bleWinHwInterface, Ant.SensorType.Rower) public FtmsRower(BlePeripheralInfo peripheralInfo, IBleWinHwInterface bleWinHwInterface) : base(peripheralInfo, bleWinHwInterface, Ant.SensorType.Rower)
{ {
@ -103,7 +104,7 @@ namespace Assets.Scripts.Devices.Ble.Devices
} }
else if (character.MatchGuid(ServiceUuids.Characteristics.C2RowerControl)) else if (character.MatchGuid(ServiceUuids.Characteristics.C2RowerControl))
{ {
Debug.Log("c2划船机控制台"); Debug.Log($"c2划船机控制台{character.Id}");
this.c2Control = character; this.c2Control = character;
} }
} }
@ -173,6 +174,26 @@ namespace Assets.Scripts.Devices.Ble.Devices
} }
} }
public void SendCommand(RowerCommand command,int data = 0)
{
if (!C2RowerData.IsEnabled)
return;
switch (command)
{
case RowerCommand.JustRow:
SetJustRow();
break;
case RowerCommand.FixedDistance:
SetFixedDistance(data);
break;
case RowerCommand.FixedTime:
SetFixedTime(data);
break;
default:
break;
}
}
public void Reset() public void Reset()
{ {
if (C2RowerData.IsEnabled == true) if (C2RowerData.IsEnabled == true)
@ -196,18 +217,97 @@ namespace Assets.Scripts.Devices.Ble.Devices
} }
} }
/// <summary>
///
/// </summary>
/// <param name="workoutType">训练类型012里程不分段3里程分段4时间不分段5678~13</param>
public void SetWorkoutType(int workoutType)
{
var typeHex = workoutType.ToString("X2");
var checkSum = "00";
var data = BitConvertHelper.HexToByteArray($"F1 76 03 01 01 {typeHex} {checkSum} F2");//"F1 76 03 01 01 03 76 F2"//03?
checkSum = GetChecksumHexString(data);
data = BitConvertHelper.HexToByteArray($"F1 76 03 01 01 {typeHex} {checkSum} F2");
hwInterface.WriteCharacteristic(this.c2Control, data);
}
public void SetConfigureWorkout()
{
var data = BitConvertHelper.HexToByteArray("F1 76 03 14 01 01 61 F2"); //03?
hwInterface.WriteCharacteristic(this.c2Control, data);
}
public void SetScreenState()
{
var data = BitConvertHelper.HexToByteArray("F1 76 04 13 02 01 01 63 F2"); //04?
hwInterface.WriteCharacteristic(this.c2Control, data);
}
public void SetPoll()
{
var data = BitConvertHelper.HexToByteArray($"F1 76 07 8D 93 A3 A0 A8 B3 B6 C1 F2");
hwInterface.WriteCharacteristic(this.c2Control, data);
}
public void SetJustRow()
{
var data = BitConvertHelper.HexToByteArray("F1 76 07 01 01 01 13 02 01 01 61 F2");
Debug.Log($"SetJustRow:{string.Join(",", data)}");
hwInterface.WriteCharacteristic(this.c2Control, data);
SetScreenState();
}
public void SetFixedDistance(int distance)
{
if (distance < 100)
return;
SetWorkoutType(3);
var hexditance = distance.ToString("X4");
var checkSum = "00";
var data = BitConvertHelper.HexToByteArray($"F1 76 07 03 05 80 00 00 {hexditance} {checkSum} F2");//F1 76 07 03 05 80 00 00 {hexditance} {checkSum} F2
checkSum = GetChecksumHexString(data);
data = BitConvertHelper.HexToByteArray($"F1 76 07 03 05 80 00 00 {hexditance} {checkSum} F2");//07?
hwInterface.WriteCharacteristic(this.c2Control, data);
SetConfigureWorkout();
SetScreenState();
}
private string GetChecksumHexString(byte[] data)
{
var res = data[1];
for (int i = 2; i < data.Length-2; i++)
{
res ^= data[i];
}
return res.ToString("X2");
}
public void SetFixedTime(int mins)
{
SetWorkoutType(5);
var timeHex = (mins * 100).ToString("X6");
var checkNum = "00";
var data = BitConvertHelper.HexToByteArray($"F1 76 07 03 05 00 00 {timeHex} {checkNum} F2");
checkNum = GetChecksumHexString(data);
data = BitConvertHelper.HexToByteArray($"F1 76 07 03 05 00 00 {timeHex} {checkNum} F2");
hwInterface.WriteCharacteristic(this.c2Control, data);
SetConfigureWorkout();
SetScreenState();
}
public void SetResistanceLevel(ushort v) public void SetResistanceLevel(ushort v)
{ {
if (C2RowerData.IsEnabled == true) if (C2RowerData.IsEnabled == true)
{ {
//等对csafe协议研究透彻后写 ////等对csafe协议研究透彻后写
//if (this.c2Control != null) //if (this.c2Control != null)
//{ //{
// var data = new byte[] { 0x29 }.Concat(BitConverter.GetBytes(v)).ToArray(); //02312131 = > 03010103
// byte checksum = (byte)((byte)(data[0] ^ data[1]) ^ data[2]); //var data = new byte[] { 0x76 }.Concat(BitConverter.GetBytes(v)).ToArray();
// var r = new byte[] { 0xF1 }.Concat(data).Concat(new byte[] { checksum, 0xf2 }) .ToArray(); //byte checksum = (byte)((byte)(data[0] ^ data[1]) ^ data[2]);
// Debug.Log($"设置阻力{v}, {string.Join(",", r)}"); //var r = new byte[] { 0xF1 }.Concat(data).Concat(new byte[] { checksum, 0xf2 }).ToArray();
// hwInterface.WriteCharacteristic(this.c2Control, r); //Debug.Log($"设置阻力{v}, {string.Join(",", r)}");
//
// var data = BitConvertHelper.HexToByteArray("F1 76 07 01 01 01 13 02 01 01 61 F2");
// hwInterface.WriteCharacteristic(this.c2Control, data);
//} //}
} }
else else

View File

@ -45,6 +45,7 @@ namespace Assets.Scripts.Devices.Ble.Interfaces
int ResistanceLevel { get; set; } int ResistanceLevel { get; set; }
void Reset(); void Reset();
event EventHandler StartEvent; event EventHandler StartEvent;
event EventHandler CompleteEvent;//结束事件
event EventHandler RowerResChanged; event EventHandler RowerResChanged;
} }
} }

View File

@ -20,6 +20,7 @@ using UnityEngine;
using UnityEngine.Android; using UnityEngine.Android;
using UnityEngine.UI; using UnityEngine.UI;
using static RowerTaskPanel; using static RowerTaskPanel;
using Assets.Scripts.Ble.Commands;
public class RowerHomeScript : PFUIPanel public class RowerHomeScript : PFUIPanel
{ {
@ -57,6 +58,7 @@ public class RowerHomeScript : PFUIPanel
{ {
RowerData.PullChanged -= PaintPullCurve; RowerData.PullChanged -= PaintPullCurve;
RowerData.StartEvent -= StartFunc; RowerData.StartEvent -= StartFunc;
RowerData.CompleteEvent -= CompelteFunc;
RowerData.RowerResChanged -= ResChanged; RowerData.RowerResChanged -= ResChanged;
} }
C2RowerData.EnableChanged -= ModeChanged; C2RowerData.EnableChanged -= ModeChanged;
@ -256,10 +258,12 @@ public class RowerHomeScript : PFUIPanel
//UIManager.ShowRowerWelldone("C0F81E83-120B-4A2C-AD0E-8BC1B8EB3E74", Init); //UIManager.ShowRowerWelldone("C0F81E83-120B-4A2C-AD0E-8BC1B8EB3E74", Init);
//return; //return;
if (checkRowing()) return; if (checkRowing()) return;
if (C2RowerData.IsEnabled == true && C2RowerData.rowerType != null) return; //if (C2RowerData.IsEnabled == true && C2RowerData.rowerType != null) return;
UIManager.ShowRowerTaskPanel(type => UIManager.ShowRowerTaskPanel(type =>
{ {
rowerType = type; rowerType = type;
if(C2RowerData.IsEnabled)
HandleC2RowerTaskPanel(type);//处理app自定义课程同步到c2划船机
HandleSelectType(); HandleSelectType();
}, rowerType); }, rowerType);
}, false); }, false);
@ -292,12 +296,33 @@ public class RowerHomeScript : PFUIPanel
RowerData.PullChanged += PaintPullCurve; RowerData.PullChanged += PaintPullCurve;
RowerData.StartEvent -= StartFunc; RowerData.StartEvent -= StartFunc;
RowerData.StartEvent += StartFunc; RowerData.StartEvent += StartFunc;
RowerData.CompleteEvent -= CompelteFunc;
RowerData.CompleteEvent += CompelteFunc;
} }
rowerType = new RowerType { type = 1, value = 500 }; rowerType = new RowerType { type = 1, value = 500 };
HandleSelectType(); HandleSelectType();
Init(); Init();
isFirstReset = false; isFirstReset = false;
} }
void HandleC2RowerTaskPanel(RowerType rowerType)
{
if (!C2RowerData.IsEnabled)
return;
RowerCommand currentCommand = RowerCommand.JustRow;
if (rowerType.type == 0 && rowerType.value == 0)
{
currentCommand = RowerCommand.JustRow;
}
if (rowerType.type == 1 && rowerType.value >= 100)
{
currentCommand = RowerCommand.FixedDistance;
}
if (rowerType.type == 2 && rowerType.value > 0)
{
currentCommand = RowerCommand.FixedTime;
}
Rower?.SendCommand(currentCommand, (int)rowerType.value);
}
IEnumerator SetResistanceLevel(ushort res) IEnumerator SetResistanceLevel(ushort res)
{ {
@ -523,6 +548,26 @@ public class RowerHomeScript : PFUIPanel
btnStart.GetComponent<Image>().sprite = spriteDict["Untagged"]; btnStart.GetComponent<Image>().sprite = spriteDict["Untagged"];
btnStart.tag = "Untagged"; btnStart.tag = "Untagged";
} }
private void CompelteFunc(object sender, EventArgs e)
{
var rowdata = (IRowerCommonData)sender;
if (rowdata != RowerData)
return;
var heartRate = HeartRate ?? 0;
var energy = RowerData.TotalEnergy;
var strokeCount = RowerData.StrokeCount;
var power = RowerData.InstantaneousPower;
var rate = RowerData.StrokeRate;
KMText.text = "0";
records.Add($"{strokeCount},{RowerData.ElapsedTime},{rowerType.value},{RowerData.InstantaneousPower},{RowerData.InstantaneousPace},{RowerData.StrokeRate},{RowerData.ResistanceLevel},{heartRate},{energy},{RowerData.AveragePower},{RowerData.ElapsedTime}");
var tmpdata = new TempRowerCalc() { strokeCount = strokeCount, pace = RowerData.InstantaneousPace, power = power, rate = rate, heartRate = heartRate, distance = (int)rowerType.value, energy = energy };
values.Add(tmpdata);
SendDataToRace(tmpdata);
HandleSaveDirect();
}
private bool SaveFunc(RowerRecordModel model, List<string> files) private bool SaveFunc(RowerRecordModel model, List<string> files)
{ {
if (Application.internetReachability == NetworkReachability.NotReachable) if (Application.internetReachability == NetworkReachability.NotReachable)
@ -764,6 +809,8 @@ public class RowerHomeScript : PFUIPanel
RowerData.PullChanged += PaintPullCurve; RowerData.PullChanged += PaintPullCurve;
RowerData.StartEvent -= StartFunc; RowerData.StartEvent -= StartFunc;
RowerData.StartEvent += StartFunc; RowerData.StartEvent += StartFunc;
RowerData.CompleteEvent -= CompelteFunc;
RowerData.CompleteEvent += CompelteFunc;
RowerData.RowerResChanged -= ResChanged; RowerData.RowerResChanged -= ResChanged;
RowerData.RowerResChanged += ResChanged; RowerData.RowerResChanged += ResChanged;
} }
@ -868,23 +915,12 @@ public class RowerHomeScript : PFUIPanel
var power = RowerData.InstantaneousPower; var power = RowerData.InstantaneousPower;
var rate = RowerData.StrokeRate; var rate = RowerData.StrokeRate;
truelyTime++;
Debug.Log($"ElapsedTime :{RowerData.ElapsedTime} : {truelyTime}-{RowerData.TotalDistance}"); Debug.Log($"ElapsedTime :{RowerData.ElapsedTime} : {truelyTime}-{RowerData.TotalDistance}");
TempRowerCalc tmpdata = null; TempRowerCalc tmpdata = null;
//解决里程训练无法结束的问题 //解决C2里程训练无法结束的问题
var c2notStop = rowerType.type == 1 && (rowerType.value-totalDistance) <= 5; var c2notStop = C2RowerData.IsEnabled && rowerType.type == 1 && (rowerType.value-totalDistance) <= 5;
if (c2notStop)
{
distance = (int)rowerType.value;
KMText.text = "0";
records.Add($"{strokeCount},{RowerData.ElapsedTime},{rowerType.value},{RowerData.InstantaneousPower},{RowerData.InstantaneousPace},{RowerData.StrokeRate},{RowerData.ResistanceLevel},{heartRate},{energy},{RowerData.AveragePower},{truelyTime}");
tmpdata = new TempRowerCalc() { strokeCount = strokeCount, pace = RowerData.InstantaneousPace, power = power, rate = rate, heartRate = heartRate, distance = (int)rowerType.value, energy = energy };
values.Add(tmpdata);
SendDataToRace(tmpdata);
HandleSaveDirect();
return;
}
//里程停止逻辑 //里程停止逻辑
if (totalDistance == RowerData.TotalDistance && !c2notStop) if (totalDistance == RowerData.TotalDistance && !c2notStop)
{ {
@ -1024,15 +1060,19 @@ public class RowerHomeScript : PFUIPanel
KMText.text = totalDistance.ToString(); KMText.text = totalDistance.ToString();
} }
records.Add($"{strokeCount},{RowerData.ElapsedTime},{distance},{RowerData.InstantaneousPower},{RowerData.InstantaneousPace},{RowerData.StrokeRate},{RowerData.ResistanceLevel},{heartRate},{energy},{RowerData.AveragePower},{truelyTime}"); if (truelyTime > 0)
tmpdata = new TempRowerCalc() { strokeCount = strokeCount, pace = pace, power = power, rate = rate, heartRate = heartRate, distance = distance, energy = energy }; {
values.Add(tmpdata); records.Add($"{strokeCount},{RowerData.ElapsedTime},{distance},{RowerData.InstantaneousPower},{RowerData.InstantaneousPace},{RowerData.StrokeRate},{RowerData.ResistanceLevel},{heartRate},{energy},{RowerData.AveragePower},{truelyTime}");
SendDataToRace(tmpdata); tmpdata = new TempRowerCalc() { strokeCount = strokeCount, pace = pace, power = power, rate = rate, heartRate = heartRate, distance = distance, energy = energy };
values.Add(tmpdata);
SendDataToRace(tmpdata);
}
if (ticks % 5 == 0) if (ticks % 5 == 0)
{ {
SaveRealTimes();//实时保存数据 SaveRealTimes();//实时保存数据
} }
truelyTime++;
} }
//检查本地数据 //检查本地数据
int historyDistance = 0,historyStrokeCount = 0,historyEnergy = 0; int historyDistance = 0,historyStrokeCount = 0,historyEnergy = 0;
@ -1337,10 +1377,9 @@ public class RowerHomeScript : PFUIPanel
var rate = RowerData.StrokeRate; var rate = RowerData.StrokeRate;
var pace = RowerData.InstantaneousPace; var pace = RowerData.InstantaneousPace;
var strokeCount = RowerData.StrokeCount + historyStrokeCount; var strokeCount = RowerData.StrokeCount + historyStrokeCount;
var distance = (int)RowerData.TotalDistance + historyDistance;
var energy = RowerData.TotalEnergy + historyEnergy; var energy = RowerData.TotalEnergy + historyEnergy;
var heartRate = HeartRate ?? 0; var heartRate = HeartRate ?? 0;
if (rowerType.type == 1 && (rowerType.value - totalDistance) <= 5 && !createTime.HasValue) if (rowerType.type == 1 && totalDistance >= rowerType.value && !createTime.HasValue)
{ {
openTimer = false; openTimer = false;
KMText.text = "0"; KMText.text = "0";