675 lines
19 KiB
C#
675 lines
19 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Collections;
|
|
|
|
namespace XMX.FileTransmit
|
|
{
|
|
|
|
public enum XModemCheckMode
|
|
{
|
|
CheckSum,
|
|
CRC16
|
|
}
|
|
|
|
public enum XModemType
|
|
{
|
|
XModem,
|
|
XModem_1K
|
|
}
|
|
|
|
/// <summary>
|
|
/// XModem接收到的消息类型
|
|
/// </summary>
|
|
enum XmodemMessageType : int
|
|
{
|
|
KEY_C,
|
|
ACK,
|
|
NAK,
|
|
EOT,
|
|
PACKET,
|
|
PACKET_ERROR,
|
|
CAN
|
|
}
|
|
|
|
/// <summary>
|
|
/// XModem接收到的消息内容
|
|
/// </summary>
|
|
internal class XmodemMessage
|
|
{
|
|
private XmodemMessageType _MessageType;
|
|
private object _Value;
|
|
|
|
|
|
public XmodemMessage(XmodemMessageType type)
|
|
: this(type, null)
|
|
{
|
|
|
|
}
|
|
|
|
public XmodemMessage(XmodemMessageType type, object value)
|
|
{
|
|
_MessageType = type;
|
|
_Value = value;
|
|
}
|
|
|
|
|
|
public XmodemMessageType MessageType
|
|
{
|
|
get { return _MessageType; }
|
|
}
|
|
|
|
public object Value
|
|
{
|
|
get { return _Value; }
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Xmodem发送步骤
|
|
/// </summary>
|
|
internal enum XmodemSendStage : int
|
|
{
|
|
WaitReceiveRequest, //等待接收端请求
|
|
PacketSending,
|
|
WaitReceiveAnswerEndTransmit
|
|
}
|
|
|
|
/// <summary>
|
|
/// Xmodem接收步骤
|
|
/// </summary>
|
|
internal enum XmodemReceiveStage : int
|
|
{
|
|
WaitForFirstPacket,
|
|
PacketReceiving,
|
|
}
|
|
|
|
public class XModemInfo
|
|
{
|
|
private XModemType _XModemType;
|
|
private TransmitMode _TransType;
|
|
private XModemCheckMode _CheckMode;
|
|
|
|
public XModemInfo()
|
|
: this(XModemType.XModem, TransmitMode.Send, XModemCheckMode.CheckSum)
|
|
{
|
|
|
|
}
|
|
|
|
public XModemInfo(XModemType type, TransmitMode transType, XModemCheckMode checkType)
|
|
{
|
|
_XModemType = type;
|
|
_TransType = transType;
|
|
_CheckMode = checkType;
|
|
}
|
|
|
|
|
|
public XModemType Type
|
|
{
|
|
get { return _XModemType; }
|
|
set { _XModemType = value; }
|
|
}
|
|
|
|
public TransmitMode TransMode
|
|
{
|
|
get { return _TransType; }
|
|
set { _TransType = value; }
|
|
}
|
|
|
|
public XModemCheckMode CheckMode
|
|
{
|
|
get { return _CheckMode; }
|
|
set { _CheckMode = value; }
|
|
}
|
|
|
|
}
|
|
|
|
public class XModem : IFileTramsmit, ITransmitUart
|
|
{
|
|
private readonly byte SOH = 0x01;
|
|
private readonly byte EOT = 0x04;
|
|
private readonly byte ACK = 0x06;
|
|
private readonly byte NAK = 0x15;
|
|
private readonly byte CAN = 0x18;
|
|
|
|
private readonly byte STX = 0x02;
|
|
private readonly byte KEY_C = 0x43; //'C';
|
|
|
|
private int RetryMax = 6;
|
|
XModemInfo xmodemInfo = new XModemInfo();
|
|
|
|
|
|
private Thread TransThread;
|
|
private bool IsStart = false;
|
|
private int reTryCount;
|
|
private ManualResetEvent waitReceiveEvent = new ManualResetEvent(false);
|
|
|
|
|
|
private XmodemReceiveStage ReceiveStage;
|
|
private XmodemSendStage SendStage;
|
|
private Queue<XmodemMessage> msgQueue = new Queue<XmodemMessage>();
|
|
|
|
public XModem(TransmitMode transType, XModemType xmodemType, int reTryCount)
|
|
{
|
|
RetryMax = reTryCount;
|
|
|
|
xmodemInfo.CheckMode = XModemCheckMode.CheckSum;
|
|
xmodemInfo.Type = xmodemType;
|
|
xmodemInfo.TransMode = transType;
|
|
}
|
|
|
|
public void Start()
|
|
{
|
|
IsStart = true;
|
|
reTryCount = 0;
|
|
|
|
|
|
ReceiveStage = XmodemReceiveStage.WaitForFirstPacket;
|
|
SendStage = XmodemSendStage.WaitReceiveRequest;
|
|
msgQueue.Clear();
|
|
|
|
TransThread = new Thread(new ThreadStart(TransThreadHandler));
|
|
TransThread.IsBackground = true;
|
|
TransThread.Name = "XmodemTransThread";
|
|
TransThread.Start();
|
|
if (xmodemInfo.TransMode == TransmitMode.Receive)
|
|
{
|
|
if (StartReceive != null)
|
|
{
|
|
StartReceive(xmodemInfo, null);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Stop()
|
|
{
|
|
if (xmodemInfo.TransMode == TransmitMode.Receive)
|
|
{
|
|
Abort();
|
|
}
|
|
else
|
|
{
|
|
SendEOT();
|
|
}
|
|
|
|
if (EndOfTransmit != null)
|
|
{
|
|
EndOfTransmit(xmodemInfo, null);
|
|
}
|
|
}
|
|
|
|
public void Abort()
|
|
{
|
|
IsStart = false;
|
|
SendCAN();
|
|
|
|
if (EndOfTransmit != null)
|
|
{
|
|
EndOfTransmit(xmodemInfo, null);
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// 解析数据
|
|
/// </summary>
|
|
/// <param name="data"></param>
|
|
private void ParseReceivedMessage(byte[] data)
|
|
{
|
|
XmodemMessage ReceivedMessage = null;
|
|
|
|
if (data == null)
|
|
{
|
|
ReceivedMessage = null;
|
|
}
|
|
else
|
|
{
|
|
if (data[0] == STX || data[0] == SOH)
|
|
{
|
|
ReceivedMessage = new XmodemMessage(XmodemMessageType.PACKET_ERROR);
|
|
int packetLen = 0;
|
|
if (data[0] == STX)
|
|
{
|
|
packetLen = 1024;
|
|
}
|
|
else if (data[0] == SOH)
|
|
{
|
|
packetLen = 128;
|
|
}
|
|
|
|
int checkDataLen = xmodemInfo.CheckMode == XModemCheckMode.CheckSum ? 1 : 2;
|
|
if (packetLen + 3 + checkDataLen == data.Length)
|
|
{
|
|
int packetNo = 0;
|
|
if (data[1] == Convert.ToByte((~data[2]) & 0xFF))
|
|
{
|
|
packetNo = data[1];
|
|
}
|
|
|
|
int frameCheckCode = 0;
|
|
int calCheckCode = -1;
|
|
byte[] packet = new byte[packetLen];
|
|
|
|
Array.Copy(data, 3, packet, 0, packetLen);
|
|
if (xmodemInfo.CheckMode == XModemCheckMode.CheckSum)
|
|
{
|
|
frameCheckCode = data[3 + packetLen];
|
|
calCheckCode = Convert.ToByte(DataCheck.GetCheckSum(packet) & 0xFF);
|
|
}
|
|
else
|
|
{
|
|
frameCheckCode = (data[3 + packetLen] << 8) + data[3 + packetLen + 1];
|
|
calCheckCode = Convert.ToUInt16(DataCheck.GetCRC(CRCType.CRC16_XMODEM, packet) & 0xFFFF);
|
|
}
|
|
|
|
if (frameCheckCode == calCheckCode)
|
|
{
|
|
ReceivedMessage = new XmodemMessage(XmodemMessageType.PACKET, new PacketEventArgs(packetNo, packet));
|
|
}
|
|
|
|
}
|
|
msgQueue.Enqueue(ReceivedMessage);
|
|
}
|
|
else
|
|
{
|
|
foreach (byte b in data)
|
|
{
|
|
ReceivedMessage = null;
|
|
if (b == EOT)
|
|
{
|
|
ReceivedMessage = new XmodemMessage(XmodemMessageType.EOT);
|
|
}
|
|
else if (b == CAN)
|
|
{
|
|
ReceivedMessage = new XmodemMessage(XmodemMessageType.CAN);
|
|
}
|
|
else if (b == NAK)
|
|
{
|
|
ReceivedMessage = new XmodemMessage(XmodemMessageType.NAK);
|
|
}
|
|
else if (b == ACK)
|
|
{
|
|
ReceivedMessage = new XmodemMessage(XmodemMessageType.ACK);
|
|
}
|
|
else if (b == KEY_C)
|
|
{
|
|
ReceivedMessage = new XmodemMessage(XmodemMessageType.KEY_C);
|
|
}
|
|
|
|
if (ReceivedMessage != null)
|
|
{
|
|
msgQueue.Enqueue(ReceivedMessage);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
waitReceiveEvent.Set();
|
|
|
|
}
|
|
|
|
|
|
private void SendFrameToUart(byte data)
|
|
{
|
|
byte[] bytes = new byte[1];
|
|
bytes[0] = data;
|
|
SendFrameToUart(bytes);
|
|
}
|
|
private void SendFrameToUart(byte[] data)
|
|
{
|
|
if (SendToUartEvent != null)
|
|
{
|
|
SendToUartEvent(xmodemInfo, new SendToUartEventArgs(data));
|
|
}
|
|
}
|
|
|
|
private void SendACK()
|
|
{
|
|
SendFrameToUart(ACK);
|
|
|
|
}
|
|
|
|
private void SendNAK()
|
|
{
|
|
SendFrameToUart(NAK);
|
|
}
|
|
|
|
private void SendKEYC()
|
|
{
|
|
SendFrameToUart(KEY_C);
|
|
}
|
|
|
|
private void SendCAN()
|
|
{
|
|
byte[] bytes = new byte[5];
|
|
for (int i = 0; i < 5; i++)
|
|
{
|
|
bytes[i] = CAN;
|
|
}
|
|
SendFrameToUart(bytes);
|
|
}
|
|
|
|
private void SendEOT()
|
|
{
|
|
SendFrameToUart(EOT);
|
|
SendStage = XmodemSendStage.WaitReceiveAnswerEndTransmit;
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransThreadHandler()
|
|
{
|
|
while (IsStart)
|
|
{
|
|
if (xmodemInfo.TransMode == TransmitMode.Send)
|
|
{
|
|
SendHandler();
|
|
}
|
|
else if (xmodemInfo.TransMode == TransmitMode.Receive)
|
|
{
|
|
ReceiveHandler();
|
|
}
|
|
}
|
|
}
|
|
|
|
void SendHandler()
|
|
{
|
|
XmodemMessage msg = null;
|
|
lock (msgQueue)
|
|
{
|
|
if (msgQueue.Count > 0)
|
|
{
|
|
msg = msgQueue.Dequeue();
|
|
}
|
|
}
|
|
|
|
if (msg != null)
|
|
{
|
|
reTryCount = 0;
|
|
|
|
switch (msg.MessageType)
|
|
{
|
|
case XmodemMessageType.NAK:
|
|
if (SendStage == XmodemSendStage.WaitReceiveRequest)
|
|
{
|
|
SendStage = XmodemSendStage.PacketSending;
|
|
|
|
xmodemInfo.CheckMode = XModemCheckMode.CheckSum;
|
|
if (StartSend != null)
|
|
{
|
|
StartSend(xmodemInfo, null);
|
|
}
|
|
}
|
|
else if (SendStage == XmodemSendStage.WaitReceiveAnswerEndTransmit)
|
|
{
|
|
SendEOT();
|
|
}
|
|
else
|
|
{
|
|
// 通知重发或发头一包
|
|
if (ReSendPacket != null)
|
|
{
|
|
ReSendPacket(xmodemInfo, null);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case XmodemMessageType.KEY_C:
|
|
if (SendStage == XmodemSendStage.WaitReceiveRequest)
|
|
{
|
|
SendStage = XmodemSendStage.PacketSending;
|
|
// 通知发头一包CRC
|
|
xmodemInfo.CheckMode = XModemCheckMode.CRC16;
|
|
if (StartSend != null)
|
|
{
|
|
StartSend(xmodemInfo, null);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case XmodemMessageType.ACK:
|
|
if (SendStage == XmodemSendStage.PacketSending)
|
|
{
|
|
// 通知发下一包
|
|
if (SendNextPacket != null)
|
|
{
|
|
SendNextPacket(xmodemInfo, null);
|
|
}
|
|
}
|
|
else if (SendStage == XmodemSendStage.WaitReceiveAnswerEndTransmit)
|
|
{
|
|
// 通知中止
|
|
//if (AbortTransmit != null)
|
|
//{
|
|
// AbortTransmit(xmodemInfo, null);
|
|
//}
|
|
if (EndOfTransmit != null)
|
|
{
|
|
EndOfTransmit(xmodemInfo, null);
|
|
}
|
|
IsStart = false;
|
|
}
|
|
break;
|
|
|
|
case XmodemMessageType.CAN:
|
|
// 通知中止
|
|
if (AbortTransmit != null)
|
|
{
|
|
AbortTransmit(xmodemInfo, null);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (waitReceiveEvent.WaitOne(3000))
|
|
{
|
|
waitReceiveEvent.Reset();
|
|
|
|
}
|
|
else
|
|
{
|
|
reTryCount++;
|
|
if (reTryCount > RetryMax)
|
|
{
|
|
IsStart = false;
|
|
//通知接收超时
|
|
if (TransmitTimeOut != null)
|
|
{
|
|
TransmitTimeOut(xmodemInfo, null);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void ReceiveHandler()
|
|
{
|
|
if (ReceiveStage == XmodemReceiveStage.WaitForFirstPacket)
|
|
{
|
|
if (reTryCount % 2 == 0)
|
|
{
|
|
xmodemInfo.CheckMode = XModemCheckMode.CheckSum;
|
|
SendKEYC();
|
|
}
|
|
else
|
|
{
|
|
xmodemInfo.CheckMode = XModemCheckMode.CRC16;
|
|
SendNAK();
|
|
}
|
|
}
|
|
|
|
|
|
XmodemMessage msg = null;
|
|
lock (msgQueue)
|
|
{
|
|
if (msgQueue.Count > 0)
|
|
{
|
|
msg = msgQueue.Dequeue();
|
|
}
|
|
|
|
}
|
|
if (msg != null)
|
|
{
|
|
reTryCount = 0;
|
|
|
|
switch (msg.MessageType)
|
|
{
|
|
case XmodemMessageType.PACKET:
|
|
ReceiveStage = XmodemReceiveStage.PacketReceiving;
|
|
SendACK();
|
|
if (ReceivedPacket != null)
|
|
{
|
|
PacketEventArgs e = msg.Value as PacketEventArgs;
|
|
ReceivedPacket(xmodemInfo, new PacketEventArgs(e.PacketNo, e.Packet));
|
|
}
|
|
|
|
// 通知发下一包
|
|
if (SendNextPacket != null)
|
|
{
|
|
SendNextPacket(xmodemInfo, null);
|
|
}
|
|
break;
|
|
case XmodemMessageType.PACKET_ERROR:
|
|
SendNAK();
|
|
// 通知重发
|
|
if (ReSendPacket != null)
|
|
{
|
|
ReSendPacket(xmodemInfo, null);
|
|
}
|
|
break;
|
|
case XmodemMessageType.EOT:
|
|
SendACK();
|
|
// 通知完成
|
|
if (EndOfTransmit != null)
|
|
{
|
|
EndOfTransmit(xmodemInfo, null);
|
|
}
|
|
break;
|
|
case XmodemMessageType.CAN:
|
|
SendACK();
|
|
// 通知中止
|
|
if (AbortTransmit != null)
|
|
{
|
|
AbortTransmit(xmodemInfo, null);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (waitReceiveEvent.WaitOne(3000))
|
|
{
|
|
waitReceiveEvent.Reset();
|
|
}
|
|
else
|
|
{
|
|
reTryCount++;
|
|
if (reTryCount > RetryMax)
|
|
{
|
|
IsStart = false;
|
|
//通知接收超时
|
|
if (TransmitTimeOut != null)
|
|
{
|
|
TransmitTimeOut(xmodemInfo, null);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
#region IFileTramsmit 成员
|
|
|
|
public event EventHandler StartSend;
|
|
|
|
public event EventHandler StartReceive;
|
|
|
|
public event EventHandler SendNextPacket;
|
|
|
|
public event EventHandler ReSendPacket;
|
|
|
|
public event EventHandler AbortTransmit;
|
|
|
|
public event EventHandler EndOfTransmit;
|
|
|
|
public event EventHandler TransmitTimeOut;
|
|
|
|
public event PacketEventHandler ReceivedPacket;
|
|
|
|
public void SendPacket(PacketEventArgs packet)
|
|
{
|
|
int packetLen = 0;
|
|
int checkLen = 0;
|
|
byte[] data;
|
|
|
|
if (xmodemInfo.CheckMode == XModemCheckMode.CheckSum)
|
|
{
|
|
checkLen = 1;
|
|
}
|
|
else
|
|
{
|
|
checkLen = 2;
|
|
}
|
|
|
|
if (xmodemInfo.Type == XModemType.XModem_1K)
|
|
{
|
|
packetLen = 1024;
|
|
}
|
|
else
|
|
{
|
|
packetLen = 128;
|
|
}
|
|
|
|
data = new byte[3 + packetLen + checkLen];
|
|
|
|
data[0] = SOH;
|
|
if (xmodemInfo.Type == XModemType.XModem_1K)
|
|
{
|
|
data[0] = STX;
|
|
}
|
|
|
|
data[1] = Convert.ToByte(packet.PacketNo & 0xFF);
|
|
data[2] = Convert.ToByte((~data[1]) & 0xFF);
|
|
Array.Copy(packet.Packet, 0, data, 3, packetLen);
|
|
|
|
if (xmodemInfo.CheckMode == XModemCheckMode.CheckSum)
|
|
{
|
|
data[3 + packetLen] = Convert.ToByte(DataCheck.GetCheckSum(packet.Packet) & 0xFF);
|
|
}
|
|
else
|
|
{
|
|
UInt16 crc = Convert.ToUInt16(DataCheck.GetCRC(CRCType.CRC16_XMODEM, packet.Packet) & 0xFFFF);
|
|
data[3 + packetLen] = Convert.ToByte(crc >> 8);
|
|
data[3 + packetLen + 1] = Convert.ToByte(crc & 0xFF);
|
|
}
|
|
|
|
SendFrameToUart(data);
|
|
}
|
|
|
|
#endregion
|
|
|
|
|
|
#region ITransmitUart 成员
|
|
|
|
public event SendToUartEventHandler SendToUartEvent;
|
|
public void ReceivedFromUart(byte[] data)
|
|
{
|
|
ParseReceivedMessage(data);
|
|
|
|
}
|
|
#endregion
|
|
}
|
|
}
|