537 lines
17 KiB
C#
537 lines
17 KiB
C#
using Common;
|
||
using DotNext;
|
||
using Newtonsoft.Json;
|
||
|
||
namespace WebProtocol
|
||
{
|
||
|
||
/// <summary> The Sign of Package </summary>
|
||
public enum PackSign
|
||
{
|
||
/// <summary> Package: Send Read or Write Address to Device </summary>
|
||
SendAddr = 0x00,
|
||
/// <summary> Package: Send Data Which Update the flash of Device</summary>
|
||
SendData = 0xFF,
|
||
/// <summary> </summary>
|
||
RecvData = 0x0F,
|
||
/// <summary> </summary>
|
||
RecvResp = 0xF0,
|
||
}
|
||
|
||
/// <summary> Package Burst Type </summary>
|
||
public enum BurstType
|
||
{
|
||
/// <summary> Fixed Type </summary>
|
||
FixedBurst = 0b00,
|
||
/// <summary> Extended Type </summary>
|
||
ExtendBurst = 0b01,
|
||
}
|
||
|
||
|
||
/// <summary> Package options which to send address to read or write </summary>
|
||
public class SendAddrPackOptions
|
||
{
|
||
|
||
/// <summary>
|
||
/// 突发类型
|
||
/// </summary>
|
||
/// <example>0</example>
|
||
public required BurstType BurstType { get; set; }
|
||
|
||
/// <summary>
|
||
/// 任务ID
|
||
/// </summary>
|
||
/// <example>1</example>
|
||
public required byte CommandID { get; set; }
|
||
|
||
/// <summary>
|
||
/// 标识写入还是读取
|
||
/// </summary>
|
||
/// <example>true</example>
|
||
public required bool IsWrite { get; set; }
|
||
|
||
/// <summary>
|
||
/// 突发长度:0是32bits,255是32bits x 256
|
||
/// </summary>
|
||
/// <example>255</example>
|
||
public required byte BurstLength { get; set; }
|
||
|
||
|
||
/// <summary>
|
||
/// 目标地址
|
||
/// </summary>
|
||
/// <example>0</example>
|
||
public required UInt32 Address { get; set; }
|
||
|
||
/// <summary>
|
||
/// 转换为Json格式字符串
|
||
/// </summary>
|
||
/// <returns>Json String</returns>
|
||
public override string ToString()
|
||
{
|
||
return JsonConvert.SerializeObject(this);
|
||
}
|
||
}
|
||
|
||
/// <summary> Package Options which to receive from boards </summary>
|
||
public class RecvPackOptions
|
||
{
|
||
/// <summary> 数据包类型 </summary>
|
||
public enum PackType
|
||
{
|
||
/// <summary> 读响应包 </summary>
|
||
ReadResp,
|
||
/// <summary> 写响应包 </summary>
|
||
WriteResp
|
||
};
|
||
|
||
/// <summary>
|
||
/// 时间戳
|
||
/// </summary>
|
||
/// <example>1234567</example>
|
||
public required UInt32 Timestamp { get; set; }
|
||
|
||
/// <summary>
|
||
/// 数据包类型
|
||
/// </summary>
|
||
/// <example>0</example>
|
||
public required PackType Type { get; set; }
|
||
|
||
/// <summary>
|
||
/// Task ID
|
||
/// </summary>
|
||
/// <example>0</example>
|
||
public required byte CommandID { get; set; }
|
||
|
||
/// <summary>
|
||
/// Whether is succeed to finish command
|
||
/// </summary>
|
||
/// <example>true</example>
|
||
public required bool IsSuccess { get; set; }
|
||
|
||
/// <summary>
|
||
/// Return Data
|
||
/// </summary>
|
||
/// <example>[]</example>
|
||
public byte[]? Data { get; set; }
|
||
|
||
/// <summary>
|
||
/// Convert to Json String
|
||
/// </summary>
|
||
/// <returns> Json String </returns>
|
||
public override string ToString()
|
||
{
|
||
return JsonConvert.SerializeObject(this);
|
||
}
|
||
}
|
||
|
||
/// <summary> Server->FPGA 地址包 </summary>
|
||
public class SendAddrPackage
|
||
{
|
||
readonly byte sign = (byte)PackSign.SendAddr;
|
||
readonly byte commandType;
|
||
readonly byte burstLength;
|
||
readonly byte _reserved = 0;
|
||
readonly UInt32 address;
|
||
|
||
/// <summary>
|
||
/// 使用地址包选项构造地址包
|
||
/// </summary>
|
||
/// <param name="opts"> 地址包选项 </param>
|
||
public SendAddrPackage(SendAddrPackOptions opts)
|
||
{
|
||
byte byteBurstType = Convert.ToByte((byte)opts.BurstType << 6);
|
||
byte byteCommandID = Convert.ToByte((opts.CommandID & 0x03) << 4);
|
||
byte byteIsWrite = (opts.IsWrite ? (byte)0x01 : (byte)0x00);
|
||
this.commandType = Convert.ToByte(byteBurstType | byteCommandID | byteIsWrite);
|
||
this.burstLength = opts.BurstLength;
|
||
this.address = opts.Address;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 使用完整参数构造地址包
|
||
/// </summary>
|
||
/// <param name="burstType"> 突发类型 </param>
|
||
/// <param name="commandID"> 任务ID </param>
|
||
/// <param name="isWrite"> 是否是写数据 </param>
|
||
/// <param name="burstLength"> 突发长度 </param>
|
||
/// <param name="address"> 设备地址 </param>
|
||
public SendAddrPackage(BurstType burstType, byte commandID, bool isWrite, byte burstLength, UInt32 address)
|
||
{
|
||
byte byteBurstType = Convert.ToByte((byte)burstType << 6);
|
||
byte byteCommandID = Convert.ToByte((commandID & 0x03) << 4);
|
||
byte byteIsWrite = (isWrite ? (byte)0x01 : (byte)0x00);
|
||
this.commandType = Convert.ToByte(byteBurstType | byteCommandID | byteIsWrite);
|
||
this.burstLength = burstLength;
|
||
this.address = address;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 使用二进制参数构建地址包
|
||
/// </summary>
|
||
/// <param name="commandType">二进制命令类型</param>
|
||
/// <param name="burstLength">突发长度</param>
|
||
/// <param name="address">写入或读取的地址</param>
|
||
public SendAddrPackage(byte commandType, byte burstLength, UInt32 address)
|
||
{
|
||
this.commandType = commandType;
|
||
this.burstLength = burstLength;
|
||
this.address = address;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取对应地址包选项
|
||
/// </summary>
|
||
public SendAddrPackOptions Options
|
||
{
|
||
get
|
||
{
|
||
return new SendAddrPackOptions()
|
||
{
|
||
Address = this.address,
|
||
BurstLength = this.burstLength,
|
||
BurstType = (BurstType)(this.commandType >> 6),
|
||
CommandID = Convert.ToByte((this.commandType >> 4) & 0b11),
|
||
IsWrite = Convert.ToBoolean(this.commandType & 1)
|
||
};
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将地址包转化为字节数组
|
||
/// </summary>
|
||
/// <returns> 字节数组 </returns>
|
||
public byte[] ToBytes()
|
||
{
|
||
var arr = new byte[8];
|
||
arr[0] = sign;
|
||
arr[1] = commandType;
|
||
arr[2] = burstLength;
|
||
arr[3] = _reserved;
|
||
|
||
var bytesAddr = Common.Number.NumberToBytes(address, 4).Value;
|
||
Array.Copy(bytesAddr, 0, arr, 4, bytesAddr.Length);
|
||
|
||
return arr;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 讲地址包转化为Json格式的地址包选项
|
||
/// </summary>
|
||
/// <returns> 字符串 </returns>
|
||
public override string ToString()
|
||
{
|
||
var opts = new SendAddrPackOptions()
|
||
{
|
||
BurstType = (BurstType)(commandType >> 6),
|
||
CommandID = Convert.ToByte((commandType >> 4) & 0b0011),
|
||
IsWrite = Convert.ToBoolean(commandType & 0x01),
|
||
BurstLength = burstLength,
|
||
Address = address,
|
||
};
|
||
|
||
return JsonConvert.SerializeObject(opts);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据字节数组构建地址包
|
||
/// </summary>
|
||
/// <param name="bytes">字节数组</param>
|
||
/// <param name="checkSign">是否校验地址包包头</param>
|
||
/// <returns>地址包</returns>
|
||
public static Result<SendAddrPackage> FromBytes(byte[] bytes, bool checkSign = true)
|
||
{
|
||
if (bytes.Length != 8)
|
||
{
|
||
return new(new ArgumentException(
|
||
"Bytes are not equal to 8 bytes.",
|
||
nameof(bytes)
|
||
));
|
||
}
|
||
|
||
if (checkSign && bytes[0] != (byte)PackSign.SendAddr)
|
||
{
|
||
return new(new ArgumentException(
|
||
"The sign of bytes is not SendAddr Package, but you can disable it by set checkSign false.",
|
||
nameof(bytes)
|
||
));
|
||
}
|
||
|
||
var address = Common.Number.BytesToUInt64(bytes[4..]).Value;
|
||
return new SendAddrPackage(bytes[1], bytes[2], Convert.ToUInt32(address));
|
||
}
|
||
}
|
||
|
||
/// <summary> 数据包 </summary>
|
||
public struct SendDataPackage
|
||
{
|
||
readonly byte sign = (byte)PackSign.SendData;
|
||
readonly byte[] _reserved = new byte[3];
|
||
readonly byte[] bodyData;
|
||
|
||
/// <summary>
|
||
/// 根据数据内容构建数据包
|
||
/// </summary>
|
||
/// <param name="bodyData">数据</param>
|
||
public SendDataPackage(byte[] bodyData)
|
||
{
|
||
if (bodyData.Length > 256 * (32 / 8))
|
||
throw new Exception("The data of SendDataPackage can't over 256 * 32bits");
|
||
|
||
if (bodyData.Length % 4 != 0)
|
||
throw new Exception("The data of SendDataPackage should be divided by 4");
|
||
|
||
this.bodyData = bodyData;
|
||
|
||
_ = _reserved;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将数据包转化为字节数组
|
||
/// </summary>
|
||
/// <returns>字节数组</returns>
|
||
public byte[] ToBytes()
|
||
{
|
||
var bodyDataLen = bodyData.Length;
|
||
var arr = new byte[4 + bodyDataLen];
|
||
|
||
arr[0] = sign;
|
||
|
||
Array.Copy(bodyData, 0, arr, 4, bodyDataLen);
|
||
|
||
return arr;
|
||
}
|
||
|
||
|
||
}
|
||
|
||
/// <summary> FPGA->Server 读响应包 </summary>
|
||
public class RecvDataPackage
|
||
{
|
||
readonly UInt32 timestamp;
|
||
readonly byte sign = (byte)PackSign.RecvData;
|
||
readonly byte commandID;
|
||
readonly byte resp;
|
||
readonly byte _reserved = 0;
|
||
readonly byte[] bodyData;
|
||
|
||
/// <summary>
|
||
/// FPGA->Server 读响应包
|
||
/// 构造函数
|
||
/// </summary>
|
||
/// <param name="timestamp"> 时间戳 </param>
|
||
/// <param name="commandID"> 任务ID号 </param>
|
||
/// <param name="resp"> 读响应包响应 </param>
|
||
/// <param name="bodyData"> 数据 </param>
|
||
public RecvDataPackage(UInt32 timestamp, byte commandID, byte resp, byte[] bodyData)
|
||
{
|
||
this.timestamp = timestamp;
|
||
this.commandID = commandID;
|
||
this.resp = resp;
|
||
this.bodyData = bodyData;
|
||
|
||
_ = this.sign;
|
||
_ = this._reserved;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 通过接受包选项构建读响应包
|
||
/// </summary>
|
||
/// <param name="opts">接收包(读响应包和写响应包)选项</param>
|
||
public RecvDataPackage(RecvPackOptions opts)
|
||
{
|
||
this.timestamp = opts.Timestamp;
|
||
this.commandID = opts.CommandID;
|
||
this.resp = Convert.ToByte(opts.IsSuccess ? 0b10 : 0b00);
|
||
this.bodyData = opts.Data ?? (byte[])[0, 0, 0, 0];
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取读响应包选项
|
||
/// </summary>
|
||
public RecvPackOptions Options
|
||
{
|
||
get
|
||
{
|
||
var opts = new RecvPackOptions()
|
||
{
|
||
Timestamp = this.timestamp,
|
||
Type = RecvPackOptions.PackType.ReadResp,
|
||
CommandID = this.commandID,
|
||
IsSuccess = Convert.ToBoolean((resp >> 1) == 0b01 ? false : true),
|
||
Data = this.bodyData,
|
||
};
|
||
|
||
return opts;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取是否成功
|
||
/// </summary>
|
||
public bool IsSuccessful
|
||
{
|
||
get { return Convert.ToBoolean((this.resp >> 1) == 0b01 ? false : true); }
|
||
}
|
||
|
||
/// <summary>
|
||
/// 从字节数组构建读响应包
|
||
/// </summary>
|
||
/// <param name="bytes">字节数组</param>
|
||
/// <returns>读响应包</returns>
|
||
public static Result<RecvDataPackage> FromBytes(byte[] bytes)
|
||
{
|
||
if (bytes[4] != (byte)PackSign.RecvData)
|
||
return new(new ArgumentException(
|
||
$"The sign of bytes is not RecvData Package, Sign: 0x{BitConverter.ToString([bytes[0]])}",
|
||
nameof(bytes)
|
||
));
|
||
return new RecvDataPackage(
|
||
Number.BytesToUInt32(bytes[..4]).Value,
|
||
bytes[5],
|
||
bytes[6],
|
||
bytes[8..]);
|
||
}
|
||
|
||
/// <summary>
|
||
/// [TODO:description]
|
||
/// </summary>
|
||
/// <param name="bytes">[TODO:parameter]</param>
|
||
/// <returns>[TODO:return]</returns>
|
||
public static bool IsRecvDataPackage(byte[] bytes)
|
||
{
|
||
return bytes[4] == (byte)PackSign.RecvData;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将数据包转化为字节数组
|
||
/// </summary>
|
||
/// <returns>字节数组</returns>
|
||
public byte[] ToBytes()
|
||
{
|
||
var bodyDataLen = bodyData.Length;
|
||
var arr = new byte[8 + bodyDataLen];
|
||
|
||
Buffer.BlockCopy(
|
||
Number.UInt32ArrayToBytes([this.timestamp]).Value, 0, arr, 0, 4);
|
||
arr[4] = this.sign;
|
||
arr[5] = this.commandID;
|
||
arr[6] = this.resp;
|
||
arr[7] = this.resp;
|
||
|
||
Array.Copy(bodyData, 0, arr, 8, bodyDataLen);
|
||
|
||
return arr;
|
||
}
|
||
|
||
}
|
||
|
||
/// <summary> 写响应包 </summary>
|
||
public class RecvRespPackage
|
||
{
|
||
readonly UInt32 timestamp;
|
||
readonly byte sign = (byte)PackSign.RecvResp;
|
||
readonly byte commandID;
|
||
readonly byte resp;
|
||
readonly byte _reserved = 0;
|
||
|
||
/// <summary>
|
||
/// 构建写响应包
|
||
/// </summary>
|
||
/// <param name="timestamp">时间戳</param>
|
||
/// <param name="commandID">任务ID</param>
|
||
/// <param name="resp">写响应</param>
|
||
public RecvRespPackage(UInt32 timestamp, byte commandID, byte resp)
|
||
{
|
||
this.timestamp = timestamp;
|
||
this.commandID = commandID;
|
||
this.resp = resp;
|
||
|
||
_ = this.sign;
|
||
_ = this._reserved;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 通过接受包选项构建写响应包
|
||
/// </summary>
|
||
/// <param name="opts">接收包(读响应包和写响应包)选项</param>
|
||
public RecvRespPackage(RecvPackOptions opts)
|
||
{
|
||
this.timestamp = opts.Timestamp;
|
||
this.commandID = opts.CommandID;
|
||
this.resp = Convert.ToByte(opts.IsSuccess ? 0b10 : 0b00);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取写响应包选项
|
||
/// </summary>
|
||
public RecvPackOptions Options
|
||
{
|
||
get
|
||
{
|
||
var opts = new RecvPackOptions()
|
||
{
|
||
Timestamp = this.timestamp,
|
||
Type = RecvPackOptions.PackType.WriteResp,
|
||
CommandID = commandID,
|
||
IsSuccess = Convert.ToBoolean((resp >> 1) == 0b01 ? false : true),
|
||
Data = null,
|
||
};
|
||
|
||
return opts;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 写入是否成功
|
||
/// </summary>
|
||
public bool IsSuccessful
|
||
{
|
||
get { return Convert.ToBoolean((this.resp >> 1) == 0b01 ? false : true); }
|
||
}
|
||
|
||
/// <summary>
|
||
/// 从字节数组构建写响应包
|
||
/// </summary>
|
||
/// <param name="bytes">字节数组</param>
|
||
/// <returns>写响应包</returns>
|
||
public static Result<RecvRespPackage> FromBytes(byte[] bytes)
|
||
{
|
||
if (bytes[4] != (byte)PackSign.RecvResp)
|
||
return new(new ArgumentException(
|
||
$"The sign of bytes is not RecvResp Package, Sign: 0x{BitConverter.ToString([bytes[4]])}",
|
||
nameof(bytes)
|
||
));
|
||
var timestamp = Number.BytesToUInt32(bytes[..4]).Value;
|
||
return new RecvRespPackage(timestamp, bytes[5], bytes[6]);
|
||
}
|
||
|
||
/// <summary>
|
||
/// [TODO:description]
|
||
/// </summary>
|
||
/// <param name="bytes">[TODO:parameter]</param>
|
||
/// <returns>[TODO:return]</returns>
|
||
public static bool IsRecvRespPackage(byte[] bytes)
|
||
{
|
||
return bytes[4] == (byte)PackSign.RecvResp;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将数据包转化为字节数组
|
||
/// </summary>
|
||
/// <returns>字节数组</returns>
|
||
public byte[] ToBytes()
|
||
{
|
||
var arr = new byte[8];
|
||
Buffer.BlockCopy(
|
||
Number.UInt32ArrayToBytes([this.timestamp]).Value, 0, arr, 0, 4);
|
||
arr[4] = this.sign;
|
||
arr[5] = this.commandID;
|
||
arr[6] = this.resp;
|
||
arr[7] = this._reserved;
|
||
|
||
return arr;
|
||
}
|
||
}
|
||
}
|