using System; using System.IO; using System.IO.Ports; using System.Text; using Microsoft.SPOT; //using Microsoft.SPOT.IO; //using GHI.Utilities; //using GHI.IO; namespace Zmodem { 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); } /// /// Run a repeat task for zmodem transmission /// /// /// Status of the loop: /// 0 = OK /// 1 = DONE, SUCCESS /// -1 = FAILURE IN TRANSMISSION /// -2 = FILE DOES NOT EXIST /// public int 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."); inTransfer = true; // optional response 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 -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 -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 1; } else { zrqinits_sent++; } } return 0; } private int sendZRQInit() { TimeSpan tsNow = DateTime.Now.TimeOfDay; //test if its time if ((tsNow - tsStart) < tsZRQTimeout) return -1; else tsStart = tsNow; Debug.Print("Sending ZRQINIT..."); zshhdr(sPort, ZRQINIT, ref Txhdr); zrqinits_sent++; if (zrqinits_sent == ZRQ_MAX) Debug.Print("Too Many ZRQinits 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 } }