using System;
using System.Text;
using Microsoft.SPOT;
//using Microsoft.SPOT.IO;
using System.IO;
using System.IO.Ports;
using System.Diagnostics;
using System.Threading;
//using GHI.Utilities;
//using GHI.IO;
namespace CPF.FileTransfer
{
public class ZmodemSender : Zmodem
{
//Constants
private const int ZRQ_MAX = 10;
private const int ZRQ_TIMEOUT = 2;
private static TimeSpan tsZRQTimeout = new TimeSpan(0, 0, ZRQ_TIMEOUT);
//Variables
private string fileName;
private SerialPort sPort;
private bool inTransfer;
private bool allDone;
private TimeSpan tsStart;
private int zrqinits_sent;
private byte[] txbuff;
private byte[] rxbuff;
private int count;
private UInt16 MAX_BLOCK = 8192;
private UInt16 MAX_TXFRAME = 1024;
private bool Wantfcs32; /*we want to send 32bit fcs*/
private UInt16 Rxbuflen; /* Receiver's max buffer length (from ZRINIT) */
private UInt16 Txwindow; /* Control the size of the transmitted windows */
private UInt16 Tframlen;
//private UInt16 Txwspac; /*spacing between zcrcq requests */
private UInt16 blklen; /*subpacket block lenghth*/
private UInt16 blkopt; /*override value for zmodem blklen */
//Transfer settings
private byte Lzconv; /* Local ZMODEM file conversion request */
private byte Lzmanag; /* Loval ZMODEM file management request */
private byte Lztrans;
private byte Lskipnocor;
//Constructor
///
/// Constructor in which is specified the serialport object to operate on, and the filename.
///
/// SerialPort Object reference, port is assumed to be open
/// Absolute path filename or director for multiple file transfer
///
/// This sample shows you how to implement the class for basic use.
///
/// SerialPort sp = new SerialPort("COM1", 115200, Parity.None, 8, StopBits.One);
/// ZmodemSender zs = new ZmodemSender(sp, "\\SD\\sample.txt");
/// while (zs.doTasks()) {
/// //Do other things here. We only return here between major transfer
/// //stage steps.
/// }
///
///
public ZmodemSender(SerialPort port, string filename)
{
this.sPort = port;
this.fileName = filename;
this.inTransfer = false;
this.allDone = false;
this.zrqinits_sent = 0;
this.txbuff = new byte[MAX_BLOCK];//MAX_BLOCK
this.rxbuff = new byte[128];
this.count = 0;
this.tsStart = DateTime.Now.TimeOfDay;
this.Rxbuflen = 16384;
this.Tframlen = MAX_TXFRAME;
this.blklen = 128;
this.blkopt = 0;
this.Wantfcs32 = true;
//init file xfer settings
this.Lzconv = 0;
this.Lzmanag = 0;
this.Lztrans = 0;
this.Lskipnocor = 0;
//init the static Class
Init(false);
}
public bool doTasks()
{
//first read port
//read();
//
if (!inTransfer & zrqinits_sent < ZRQ_MAX) {
//Guess we should send another zrqinit, it will check if its time.
if (sendZRQInit() > 0) {
//Wait for a reply
if (getZRInit()) {
Debug.Print("Got ZRINIT Message.");
byte[] myattn = Encoding.UTF8.GetBytes("0");
if (sendZSInit(ref myattn, (UInt16)myattn.Length) == OK) {
inTransfer = true;
Debug.Print("Got ACK to our ZSINIT");
}
}
}
}
if (zrqinits_sent >= ZRQ_MAX) {
Debug.Print("Too Many Attempts, quitting.");
return false;
//System.Environment.Exit(-1);
}
if (inTransfer && !allDone) {
//figure out what file we're on....
//send file info
if (!File.Exists(fileName)) {
Debug.Print("File Does Not Exist, exiting");
return false;
//System.Environment.Exit(-2);
}
FileInfo fi = new FileInfo(fileName);
if (sendZFile(fi) == OK) {
//Transfer successful.
allDone = true;
}
else { zrqinits_sent++; }
}
if (allDone) {
if (sendZFIN() == OK) {
Debug.Print("OVER AND OUT");
return false;
//System.Environment.Exit(0);
}
else { zrqinits_sent++; }
}
return true;
}
private int sendZRQInit()
{
TimeSpan tsNow = DateTime.Now.TimeOfDay;
//test if its time
if ((tsNow - tsStart) < tsZRQTimeout)
return -1;
else
tsStart = tsNow;
Debug.Print("Sending init...");
zshhdr(sPort, ZRQINIT, ref Txhdr);
zrqinits_sent++;
if (zrqinits_sent == ZRQ_MAX)
Debug.Print("Too Many ZRinits issued, aborting zmodem transfer");
return 1;
}
int Rxflags = 0;
int Rxflags2 = 0;
private bool rxBuffIsSet = false;
private bool getZRInit()
{
UInt32 rxpos = 0;
switch (zgethdr(sPort, ref Rxhdr, 0, ref rxpos)) {
case ZCHALLENGE: /* Echo receiver's challenge number */
stohdr(rxpos);
zshhdr(sPort, ZACK, ref Txhdr);
return false;
case ZCOMMAND:
return false;
case ZRINIT: /*this is what we want*/
Rxflags = Rxhdr[ZF0];
Rxflags2 = Rxhdr[ZF1];
Txfcs32 = Wantfcs32 && ((Rxflags & CANFC32) == CANFC32);
bool oldZctl = Zctlesc;
Zctlesc = (Rxflags & TESCCTL) == TESCCTL;
/* update table, we initialized with a different escape method */
if (Zctlesc != oldZctl)
zsendline_init();
/* Receive buffer size flags */
Rxbuflen = (UInt16)(Rxhdr[ZP0] + (Rxhdr[ZP1] << 8));
if (Rxbuflen > 0) rxBuffIsSet = true;
/* Full Duplex */
if ((Rxflags & CANFDX) != CANFDX) //cannot fdx
Txwindow = 0;
if (Rxbuflen < 32 || Rxbuflen > MAX_BLOCK) {
Rxbuflen = MAX_BLOCK;
}
if (Rxbuflen > Tframlen)
Rxbuflen = Tframlen;
if (Rxbuflen == 0)
Rxbuflen = MAX_TXFRAME;
Debug.Print("getZRinit(): Rxbuflen= " + Rxbuflen.ToString());
if (blklen < 1024) {
if (sPort.BaudRate > 300)
blklen = 256;
if (sPort.BaudRate > 1200)
blklen = 512;
if (sPort.BaudRate > 2400)
blklen = 1024;
}
if (blklen > Rxbuflen)
blklen = Rxbuflen;
if (blkopt > 0 && blklen > blkopt)
blklen = blkopt;
Debug.Print("getZRinit(): blklen= " + blklen.ToString());
Debug.Print("getZRinit(): Txwindow=" + Txwindow.ToString());
return true;
case ZCAN:
case TIMEOUT:
case ZRQINIT: /* we got an echo back*/
return false;
default: /*something totally scrobbled came in */
zshhdr(sPort, ZNAK, ref Txhdr);
return false;
}
}
private int errors;
private int sendZSInit(ref byte[] fname, UInt16 fname_len)
{
int c;
UInt32 rxpos = 0;
errors = 0;
for (; ; ) {
stohdr(0); // reset the flag header
if (Zctlesc) {
Txhdr[ZF0] |= TESCCTL;
zshhdr(sPort, ZSINIT, ref Txhdr);
}
else {
zsbhdr(sPort, ZSINIT, ref Txhdr);
}
//Send data subpacket
ZSDATA(ref fname, fname_len, ZCRCW);
c = zgethdr(sPort, ref Rxhdr, 0, ref rxpos);
switch (c) {
case ZCAN:
Debug.Print("sendZSInit(): GOT ZCAN");
return ERROR;
case ZACK:
Debug.Print("sendZSInit(): GOT ZACK");
return OK;
case ZRINIT:
Debug.Print("sendZSInit(): GOT ZRINIT");
return ERROR;
default:
Debug.Print("sendZSInit(): IN DEFAULT" + c.ToString());
if (++errors > 5) {
Debug.Print("sendZSInit(): GOT TOO MANY ERROR");
return ERROR;
}
continue;
}
}
}
private void ZSDATA(ref byte[] buff, UInt16 length, byte frameend)
{
if (Txfcs32) {
zsda32(sPort, ref buff, length, frameend);
}
else {
zsdata(sPort, ref buff, length, frameend);
}
}
private string makeOctalString(UInt32 num)
{
StringBuilder sb = new StringBuilder();
UInt32 mod = 0;
while (num > 0) {
mod = num % 8;
sb.Insert(0, mod.ToString(), 1);
num /= 8;
}
return (sb.ToString());
}
private DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, 0);
private int sendZFile(FileInfo fi)
{
int c;
UInt32 rxpos = 0;
StringBuilder sb = new StringBuilder();
StringBuilder sbdata = new StringBuilder();
UInt32 unixTimeStamp = (UInt32)(fi.LastWriteTimeUtc.Subtract(epoch).Ticks / TimeSpan.TicksPerSecond);
/*
* we are going to send a Zfile
*/
for (; ; ) {
stohdr(0);
Txhdr[ZF0] = Lzconv;
Txhdr[ZF1] = Lzmanag;
if (Lskipnocor > 0)
Txhdr[ZF1] |= ZF1_ZMSKNOLOC;
Txhdr[ZF2] = Lztrans;
Txhdr[ZF3] = 0;
// Send binary header
zsbhdr(sPort, ZFILE, ref Txhdr);
//Assemble File info
sb.Append(fi.Name+ "\0\0");
/*sb.Append('\0');
sb.Append(" ");
sb.Append(fi.Length.ToString() + " ");
sb.Append(makeOctalString(unixTimeStamp) + " ");//seconds from epoch mod date octal rep
sb.Append("0 ");//file mode
sb.Append("0 ");// Serial number
sb.Append("1 ");//number of files left
sb.Append(fi.Length.ToString());
sb.Append(" \0");
*/
//Copy into the buffer
byte[] fdata = new byte[sb.Length + 2];
Array.Copy(Encoding.UTF8.GetBytes(sb.ToString()), fdata, Encoding.UTF8.GetBytes(sb.ToString()).Length);
Debug.Print("Sending ZDATA with string: " + sb.ToString());
if (fdata.Length > 1024) {
//Filename is too long, close it down.
Debug.Print("sendZFile(): File string is too long. Aborting");
return ERROR;
}
//Transmit file info
ZSDATA(ref fdata, (ushort)fdata.Length, ZCRCW);
again:
c = zgethdr(sPort, ref Rxhdr, 0, ref rxpos);
switch (c) {
case ZRINIT://this is what we get when we finish the file...
Debug.Print("sendZFile(): got ZRINIT time to move on.");
try {
if (fs != null)
fs.Close();
}
catch (IOException e) {
Debug.Print("Problem Closing file after transfer. " + e.ToString());
}
fsOpen = false;
return OK;
default: continue;
case ZRQINIT:
Debug.Print("sendZFile(): got ZRQINIT remote site is sender!");
return ERROR;
case ZCAN:
Debug.Print("sendZFile(): got ZCAN");
return ERROR;
case TIMEOUT:
Debug.Print("sendZFile(): got TIMEOUT");
return ERROR;
case ZABORT:
Debug.Print("sendZFile(): got ABORT");
return ERROR;
case ZFIN:
Debug.Print("sendZFile(): got ZFIN");
return ERROR;
case ZCRC:
Debug.Print("sendZFile(): got ZCRC");
/*
* The receiver has a file with the same name and length, and they
* responded with a ZCRC header with a a byte count, which requires
* us, the sender, to perform a 32bit crc on the number of btes in
* the file and transmit complement of the CRC in an answering the
* ZCRC header.
*/
goto again;
case ZSKIP:
Debug.Print("sendZFile(): got ZSKIP");
return OK;
case ZACK:
Debug.Print("sendZFile(): got ZACK " + c.ToString() + " with offset " + rxpos.ToString());
if (sendFData(fi, rxpos, ZACK) == OK)
goto again;
else
return ERROR;
case ZRPOS://this is what we need to get started
Debug.Print("sendZFile(): got ZRPOS " + c.ToString() + " with offset " + rxpos.ToString());
if (sendFData(fi, rxpos, ZRPOS) == OK)
goto again;
else
return ERROR;
}
}
}
private FileStream fs;
private bool fsOpen = false;
private UInt32 bytes_sent;
private int sendFData(FileInfo fi, UInt32 rxpos, byte lastHdr)
{
int n;
if (!fsOpen) {
try {
fs = File.OpenRead(fi.FullName);
}
catch (Exception e) {
fsOpen = false;
Debug.Print(e.ToString());
return ERROR;
}
fsOpen = true;
bytes_sent = 0;
}
fs.Seek((long)rxpos, SeekOrigin.Begin);
if (rxpos != bytes_sent) {// looks like we missed a step
Debug.Print("sendFData(): Jumping (missed pckt or resume)");
bytes_sent = rxpos;
}
//write the block to the txbuff
n = fs.Read(txbuff, 0, Rxbuflen);
if (n == 0) { //
Debug.Print("N bytes returned.. is this an empty file?");
//return ERROR;
}
// determine terminating character
byte endl;
if (n < Rxbuflen) { //eof encountered
endl = ZCRCE;
Debug.Print("End of file seen");
}
else if (((Rxflags & CANOVIO) != CANOVIO) || rxBuffIsSet)
endl = ZCRCW;
else if ((Rxflags & CANFDX) == CANFDX)
endl = ZCRCQ;
else
endl = ZCRCG;
//Debug.Print("Line Ending is: " + endl.ToString());
// We are in a streaming mode don't resend header
if (!((endl == ZCRCQ) && (lastHdr == ZACK))) {
stohdr(bytes_sent);
zsbhdr(sPort, ZDATA, ref Txhdr);
}
ZSDATA(ref txbuff, (ushort)n, endl);
bytes_sent += (UInt16)n;
if (endl == ZCRCE) {
stohdr(bytes_sent);
zsbhdr(sPort, ZEOF, ref Txhdr);
}
return OK;
}
private int sendZFIN()
{
stohdr(0);
zshhdr(sPort, ZFIN, ref Txhdr);
UInt32 rxpos = 0;
switch (zgethdr(sPort, ref Rxhdr, 0, ref rxpos)) {
case ZFIN: /* This is what we want */
sPort.WriteByte(0x4F); //O
sPort.WriteByte(0x4F); //O
return OK;
case ZCAN:
Debug.Print("sendZFIN(): got zcan instead of zfin");
goto default;
case TIMEOUT:
Debug.Print("sendZFIN(): got TIMEOUT instead of zfin");
goto default;
default:
return ERROR;
}
}
~ZmodemSender()
{
try {
fs.Close();
}
catch (Exception e) {
Debug.Print("Closing object exception in File Close.");
}
}
//public int zsendfile(FileInfo zm_fileinfo, byte[] buf, UInt16 blen)
//{
//}
//public int getnak(){
//}
//public int wtxpn(FileInfo zm_fileinfo) {
//}
//public int wcs(string oname, string remotename) {
//}
//public int zfilbuff(FileInfo zm_fileinfo) {
//}
//public int filbuf(byte[] buf, UInt16 count) {
//}
//public int getzrxinit (void) {
//}
//public int calc_blklen (long int total_sent) {
//}
//public int sendzsinit (void) {
//}
//public int wctx(FileInfo zm_fileinfo) {
//}
//public int zsendfdata(FileInfo zm_fileinfo) {
//}
//public int getinsync (FileInfo zm_fileinfo) {
//}
//public void
}
}