Subversion Repositories HomeAutomation

Rev

Rev 770 | Rev 877 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | SVN | RSS feed

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

//this class should probably be integrated into canNMT instead

public class Downloader {
    private HexFile hf;
    private DaemonConnection dc;
    private bool isBiosUpdate;
    private ulong dlOffset = 0;
    //private Thread down;
    private byte receiveID;
    private uint hwid;
    private bool HWID_INUSE;
    
    private enum dState { SEND_START, WAIT_ACK_PRG, SEND_PGM_DATA, WAIT_ACK_DATA, RESEND_ADDR, WAIT_DONE, SEND_DONE, SEND_BIOS_UPDATE, SEND_RESET, DONE, DEBUG_STATE2, WAIT_BOOT, SEND_EMPTY_START, WAIT_ACK_PRG_EMPTY, SEND_EMPTY_DATA, WAIT_ACK_EMPTY_DATA, SEND_DONE_EMPTY, WAIT_DONE_EMPTY};
    
    private const byte CAN_NMT              = 0x00;
    
    private const byte CAN_NMT_RESET        = 0x04;
    private const byte CAN_NMT_BIOS_START   = 0x08;
    private const byte CAN_NMT_PGM_START    = 0x0C;
    private const byte CAN_NMT_PGM_DATA     = 0x10;
    private const byte CAN_NMT_PGM_END      = 0x14;
    private const byte CAN_NMT_PGM_COPY     = 0x18;
    private const byte CAN_NMT_PGM_ACK      = 0x1C;
    private const byte CAN_NMT_PGM_NACK     = 0x20;
    private const byte CAN_NMT_START_APP    = 0x24;
    private const byte CAN_NMT_APP_START    = 0x28;
    private const byte CAN_NMT_HEARTBEAT    = 0x2C;
    
    private const byte MY_ID = 0x00;
    //private const byte TARGET_ID = 0x78;
    private const byte MY_NID = 0x0;
    private const byte ADDR0_INDEX = 0;
    private const byte ADDR1_INDEX = 1;
    private const byte ADDR2_INDEX = 2;
    private const byte ADDR3_INDEX = 3;

    private const byte HWID0_INDEX = 4;
    private const byte HWID1_INDEX = 5;
    private const byte HWID2_INDEX = 6;
    private const byte HWID3_INDEX = 7;
    
    private const int TIMEOUT_MS = 1000;
    private const int TIMEOUT_SHORT_MS = 200;
    
    private long timeStart = 0;
    
    public Downloader(HexFile hf, DaemonConnection dc, byte receiveID, bool isBiosUpdate, bool HWID_INUSE, uint hwid) {
        this.hf = hf;
        this.dc = dc;
        this.receiveID = receiveID;
        this.isBiosUpdate = isBiosUpdate;
        this.hwid = hwid;
        this.HWID_INUSE = HWID_INUSE;
    }
    
    public int calcCRC(HexFile hf) {
        int crc=0;
        for (ulong i=hf.getAddrLower(); i <= hf.getAddrUpper(); i++) {
            crc = crc16_update(crc, hf.getByte(i));
        }
        return crc;
    }
    
    public int crc16_update(int crc, byte a) {
        int i;
        crc ^= a;
        for (i = 0; i < 8; ++i) {
            if ((crc & 1) != 0)
                crc = (crc >> 1) ^ 0xA001;
            else
                crc = (crc >> 1);
        }
        return crc;
    }
    
    public bool go() {
        if (dc==null || hf==null || hf.getLength()==0) return false;
        return downloader();
    }
    
    public void abort() {
        //if (down != null && down.IsAlive) down.Abort();
    }
    
    public bool downloader() {
        dState pgs = dState.SEND_START;
        int t = 0;
        int t2 = 0;
        CanPacket outCm = null;
        CanPacket cm = null;
        byte[] data = new byte[8];
        uint byteSent = 0;
        ulong currentAddress = 0;
        bool done = false;
        bool errorOccured = false;
        
        //int rotstate=0;
        int dldisplaystate=0;
        
        try {
            if (isBiosUpdate) {
                dlOffset = hf.getAddrLower();
                //Console.WriteLine(dlOffset);
                //dlOffset = 200;
            }

            CanNMT cpn = new CanNMT(HWID_INUSE);
            cpn.setReceiver(receiveID);
            bool hasMessage = false;
            
            while (!done) {
                Thread.Sleep(1);
                
                switch (pgs) {
                    case dState.SEND_START:
                        // Send boot start packet to target.
                        // and wait for ack.
                        currentAddress = hf.getAddrLower();
                        //data[RID_INDEX] = TARGET_ID;
                        data[ADDR0_INDEX] = (byte)((currentAddress-dlOffset) & 0x0000FF);
                        data[ADDR1_INDEX] = (byte)(((currentAddress-dlOffset) & 0x00FF00) >> 8);
                        data[ADDR2_INDEX] = (byte)(((currentAddress-dlOffset) & 0xFF0000) >> 16);
                        data[ADDR3_INDEX] = 0;
                        if (HWID_INUSE) {
                            data[HWID0_INDEX] = (byte)((hwid) & 0xFF);
                            data[HWID1_INDEX] = (byte)((hwid>>8) & 0xFF);
                            data[HWID2_INDEX] = (byte)((hwid>>16) & 0xFF);
                            data[HWID3_INDEX] = (byte)((hwid>>24) & 0xFF);
                        }
                        //outCm = new CanPacket(CAN_NMT, CAN_NMT_PGM_START, MY_ID, receiveID, 4, data);
                        outCm = cpn.getPgmStartPacket(data);
                        dc.sendCanPacket(outCm);
                        t = Environment.TickCount;
                        t2 = Environment.TickCount;
                        pgs = dState.WAIT_ACK_PRG;
                        timeStart = Environment.TickCount;
                        Console.WriteLine("Downloading...");
                        break;
                    
                    
                    case dState.WAIT_ACK_PRG:
                        // Check for timeout, resend start packet in that case..
                        if ((Environment.TickCount - t) > TIMEOUT_MS) {
                            Console.WriteLine("Timeout while waiting for start prg ack.");
                            errorOccured = true;
                            pgs = dState.SEND_RESET;
                        }
                        //Thread.Sleep(1);
                        // If received ack.
                        hasMessage = dc.getData(out cm);
                        if (hasMessage) {
                            CanNMT cpnin = new CanNMT(cm, HWID_INUSE);
                            if (cpnin.isNMTPacket()) {
                            //if (cpnin.isNMTPacket() && (cpnin.getSender() == receiveID)) {
                                // If no error
                                if (cpnin.getType() == CAN_NMT_PGM_ACK) {
                                    // Start sending program data..
                                    byteSent = 0;
                                    pgs = dState.SEND_PGM_DATA;
                                }
                                else if (cpnin.getType() == CAN_NMT_PGM_NACK ) { 
                                    // else ..
                                    Console.WriteLine("Nack on CAN_NMT_PGM_START.");
                                    errorOccured = true;
                                    pgs = dState.SEND_RESET;
                                }
                            }
                        }
                        break;
                        
                    case dState.SEND_PGM_DATA:
                        //Console.WriteLine("byteSent = "+byteSent);
//disable                       Console.Write(".");
//Console.Write("\b");
//int curleft = Console.CursorLeft;
//int curtop = Console.CursorTop;
//Console.SetCursorPosition(curleft, curtop-1);

//if (rotstate==0) {
//   Console.Write("-");
//   rotstate=1;
//} else if (rotstate==1) {
//   Console.Write("\\");
//   rotstate=2;
//} else if (rotstate==2) {
//   Console.Write("|");
//   rotstate=3;
//} else if (rotstate==3) {
//   Console.Write("/");
//   rotstate=0;
//}
//Console.Write("\r");
//dldisplaystate

//FULHACK deulx
                        ulong percent = byteSent*100/(hf.getAddrUpper()-currentAddress);
                        
                        if (percent>9 && dldisplaystate==0) {
                           Console.Write("\r");
                           Console.Write(percent+"%");
                           dldisplaystate=1;
                        } else if (percent>19 && dldisplaystate==1) {
                           Console.Write("\r");
                           Console.Write(percent+"%");
                           dldisplaystate=2;
                        } else if (percent>29 && dldisplaystate==2) {
                           Console.Write("\r");
                           Console.Write(percent+"%");
                           dldisplaystate=3;
                        } else if (percent>39 && dldisplaystate==3) {
                           Console.Write("\r");
                           Console.Write(percent+"%");
                           dldisplaystate=4;
                        } else if (percent>49 && dldisplaystate==4) {
                           Console.Write("\r");
                           Console.Write(percent+"%");
                           dldisplaystate=5;
                        } else if (percent>59 && dldisplaystate==5) {
                           Console.Write("\r");
                           Console.Write(percent+"%");
                           dldisplaystate=6;
                        } else if (percent>69 && dldisplaystate==6) {
                           Console.Write("\r");
                           Console.Write(percent+"%");
                           dldisplaystate=7;
                        } else if (percent>79 && dldisplaystate==7) {
                           Console.Write("\r");
                           Console.Write(percent+"%");
                           dldisplaystate=8;
                        } else if (percent>89 && dldisplaystate==8) {
                           Console.Write("\r");
                           Console.Write(percent+"%");
                           dldisplaystate=9;
                        } else if (percent>99 && dldisplaystate==9) {
                           Console.Write("\r");
                           Console.Write(percent+"%");
                           dldisplaystate=10;
                        }

                        // Send program data.
                        byte datalength = 2;
                        for (ulong i = 0; i < 6; i++) { 
                            // Populate data.
                            data[i+2] = hf.getByte(currentAddress + i + byteSent);
                            if (currentAddress + i + byteSent > hf.getAddrUpper()) {
                                break;  //then done
                            }
                            datalength++;
                        }
                        data[0] = (byte)(byteSent & 0x00FF);
                        data[1] = (byte)((byteSent & 0xFF00) >> 8);

                        outCm = cpn.getPgmDataPacket(data, datalength);
                        dc.sendCanPacket(outCm);
                        
                        t = Environment.TickCount;
                        t2 = Environment.TickCount;
                        byteSent += 6;
                        pgs = dState.WAIT_ACK_DATA;
                        break;
                        
                        
                    case dState.WAIT_ACK_DATA:
                        // Wait for pgm ack.
                        if ((Environment.TickCount - t) > TIMEOUT_MS) {
                            // Woops, error. 
                            Console.WriteLine("Timeout while waiting for data ack.");
                            errorOccured = true;
                            pgs = dState.SEND_RESET;
                        }
                        else if ((Environment.TickCount - t2) > TIMEOUT_SHORT_MS) {
                            // Woops, error. 
//disable                           Console.Write(":");
                            byteSent -= 6;
                            pgs = dState.SEND_PGM_DATA;
                            break;
                        }
                        //Thread.Sleep(1);
                        // If received ack.
                        hasMessage = dc.getData(out cm);
                        if (hasMessage) {
                            CanNMT cpnin = new CanNMT(cm, HWID_INUSE);
                            if (cpnin.isNMTPacket()) {
                            //if (cpnin.isNMTPacket() && (cpnin.getSender() == receiveID)) {
                                // If no error
                                if (cpnin.getType() == CAN_NMT_PGM_ACK) {
                                    //currentAddress += 2;
                                    // Check if end.
                                    if (currentAddress + byteSent > hf.getAddrUpper()) {
                                        // Yes, we are done, send done.
                                        pgs = dState.SEND_DONE;
                                    }
                                    else { 
                                        // More to write.
                                        //byteSent = 0;
                                        pgs = dState.SEND_PGM_DATA;
                                    }
                                }
                                else if (cpnin.getType() == CAN_NMT_PGM_NACK ) { 
                                    // else ..
                                    Console.WriteLine("Nack on CAN_NMT_PGM_DATA.");
                                    errorOccured = true;
                                    pgs = dState.SEND_RESET;
                                }
                            }
                        }
                        break;
                        
                    case dState.SEND_DONE:
                       Console.Write("\r");
                       Console.Write("100%");
                        // Send done
                        outCm = cpn.getPgmEndPacket();
                        dc.sendCanPacket(outCm);

                        t = Environment.TickCount;
                        t2 = Environment.TickCount;
                        pgs = dState.WAIT_DONE;
                        break;
                        
                        
                    case dState.WAIT_DONE:
                        // Check for timeout, resend done packet in that case..
                        if ((Environment.TickCount - t) > TIMEOUT_MS) {
                            Console.WriteLine("Timeout while waiting for end prg ack.");
                            errorOccured = true;
                            pgs = dState.SEND_RESET;
                        }
                        //Thread.Sleep(1);
                        
                        hasMessage = dc.getData(out cm);
                        if (hasMessage) {
                            CanNMT cpnin = new CanNMT(cm, HWID_INUSE);
                            if (cpnin.isNMTPacket()) {
                            //if (cpnin.isNMTPacket() && (cpnin.getSender() == receiveID)) {
                                // If no error
                                if (cpnin.getType() == CAN_NMT_PGM_ACK) {
                                    Console.WriteLine("");
                                    //
                                    int crc = calcCRC(hf);
                                
                                    if (cm.getDataLength() == 2) {
                                        byte[] cmdata = cm.getData();
                                        if ((cmdata[0] == (crc & 0xFF)) && (cmdata[1] == (crc>>8))) {
                                            if (!isBiosUpdate) {
                                                Console.WriteLine("Done, successfully programmed application.");
                                                pgs = dState.SEND_RESET;
                                            } else {
                                                Console.WriteLine("Done, successfully programmed bios to application space.");
                                                pgs = dState.SEND_BIOS_UPDATE;
                                            }
                                        } else {
                                            string nodecrc = String.Format("{0:x4}", (int)cmdata[0] + (int)(cmdata[1]<<8));
                                            nodecrc = "0x"+nodecrc;
                                            string realcrc = String.Format("{0:x4}", (int)(crc & 0xFF) + (int)(crc & 0xFF00));
                                            realcrc = "0x"+realcrc;
                                            Console.WriteLine("CRC failed. Node sent CRC "+nodecrc+" but should be "+realcrc+".");
                                            errorOccured = true;
                                            pgs = dState.SEND_RESET;
                                        }
                                        
                                        byteSent = 0;
                                    }
                                    //Console.WriteLine("crc: " + crc + " " + (crc>>8) + " " + (crc & 0xFF));
                                    //Console.WriteLine("ACKpkg with CRC: " + cm.ToString());
                                    // 
                                }
                                else if (cpnin.getType() == CAN_NMT_PGM_NACK) {
                                    // else resend addr..
                                    Console.WriteLine("Nack on CAN_NMT_PGM_END.");
                                    errorOccured = true;
                                    pgs = dState.SEND_RESET;
                                }
                            }
                        }
                        break;
                        
                    case dState.SEND_BIOS_UPDATE:
                        data[0] = (byte)(0 & 0x00FF);
                        data[1] = (byte)((0 & 0xFF00) >> 8);
                        data[2] = (byte)(dlOffset & 0x00FF);
                        data[3] = (byte)((dlOffset & 0xFF00) >> 8);
                        data[4] = (byte)((hf.getAddrUpper()-hf.getAddrLower()+1) & 0x00FF);
                        data[5] = (byte)(((hf.getAddrUpper()-hf.getAddrLower()+1) & 0xFF00) >> 8);
                        
                        outCm = cpn.getPgmCopyPacket(data);
                        Console.WriteLine("Now say a prayer, moving bios");
                        dc.sendCanPacket(outCm);
                        t = Environment.TickCount;
                        t2 = Environment.TickCount;
                        pgs = dState.WAIT_BOOT;
                        //pgs = dState.DONE;
                        break;

                    case dState.WAIT_BOOT:
                        // Wait for node reset answer (boot msg).
                        if ((Environment.TickCount - t) > 3*TIMEOUT_MS) {
                            // Woops, error. 
                            Console.WriteLine("Timeout while waiting for node to boot.");
                            errorOccured = true;
                            pgs = dState.SEND_DONE;
                        }
                        //Thread.Sleep(1);
                        // If received boot msg.
                        hasMessage = dc.getData(out cm);
                        if (hasMessage) {
                            CanNMT cpnin = new CanNMT(cm, HWID_INUSE);
                            if (cpnin.isNMTPacket()) {
                            //if (cpnin.isNMTPacket() && (cpnin.getSender() == receiveID)) {
                                // If no error
                                if (cpnin.getType() == CAN_NMT_BIOS_START) {
                                    pgs = dState.SEND_EMPTY_START;
                                }
                                else { 
                                    // else ..
                                }
                            }
                        }
                        break;
                        
                    case dState.SEND_EMPTY_START:
                        // Send boot start packet to target.
                        // and wait for ack.
                        
                        //data[RID_INDEX] = TARGET_ID;
                        data[ADDR0_INDEX] = (byte)((0) & 0x0000FF);
                        data[ADDR1_INDEX] = (byte)(((0) & 0x00FF00) >> 8);
                        data[ADDR2_INDEX] = (byte)(((0) & 0xFF0000) >> 16);
                        data[ADDR3_INDEX] = 0;
                        if (HWID_INUSE) {
                            data[HWID0_INDEX] = (byte)((hwid) & 0xFF);
                            data[HWID1_INDEX] = (byte)((hwid>>8) & 0xFF);
                            data[HWID2_INDEX] = (byte)((hwid>>16) & 0xFF);
                            data[HWID3_INDEX] = (byte)((hwid>>24) & 0xFF);
                        }
                        //outCm = new CanPacket(CAN_NMT, CAN_NMT_PGM_START, MY_ID, receiveID, 4, data);
                        outCm = cpn.getPgmStartPacket(data);
                        dc.sendCanPacket(outCm);
                        t = Environment.TickCount;
                        t2 = Environment.TickCount;
                        pgs = dState.WAIT_ACK_PRG_EMPTY;
                        break;
                    
                    
                    case dState.WAIT_ACK_PRG_EMPTY:
                        // Check for timeout, resend start packet in that case..
                        if ((Environment.TickCount - t) > 3*TIMEOUT_MS) {
                            Console.WriteLine("Timeout while waiting for start prg ack during empty-first-block-cycle.");
                            errorOccured = true;
                            pgs = dState.SEND_RESET;
                        }
                        //Thread.Sleep(1);
                        // If received ack.
                        hasMessage = dc.getData(out cm);
                        if (hasMessage) {
                            CanNMT cpnin = new CanNMT(cm, HWID_INUSE);
                            if (cpnin.isNMTPacket()) {
                            //if (cpnin.isNMTPacket() && (cpnin.getSender() == receiveID)) {
                                // If no error
                                if (cpnin.getType() == CAN_NMT_PGM_ACK) {
                                    // Start sending program data..
                                    byteSent = 0;
                                    pgs = dState.SEND_EMPTY_DATA;
                                }
                                else if (cpnin.getType() == CAN_NMT_PGM_NACK ) { 
                                    // else ..
                                    Console.WriteLine("Nack on CAN_NMT_PGM_START.");
                                    errorOccured = true;
                                    pgs = dState.SEND_RESET;
                                }
                            }
                        }
                        break;
                        
                    case dState.SEND_EMPTY_DATA:
                        // Send program data.
                        data[0] = 0;
                        data[1] = 0;
                        data[2] = 0xff;
                        data[3] = 0xff;

                        outCm = cpn.getPgmDataPacket(data, 4);
                        dc.sendCanPacket(outCm);
                        
                        t = Environment.TickCount;
                        t2 = Environment.TickCount;
                        byteSent += 6;
                        pgs = dState.WAIT_ACK_EMPTY_DATA;
                        break;
                        
                        
                    case dState.WAIT_ACK_EMPTY_DATA:
                        // Wait for pgm ack.
                        if ((Environment.TickCount - t) > 2*TIMEOUT_MS) {
                            // Woops, error. 
                            Console.WriteLine("Timeout while waiting for data ack.");
                            errorOccured = true;
                            pgs = dState.SEND_RESET;
                        }
                        //Thread.Sleep(1);
                        // If received ack.
                        hasMessage = dc.getData(out cm);
                        if (hasMessage) {
                            CanNMT cpnin = new CanNMT(cm, HWID_INUSE);
                            if (cpnin.isNMTPacket()) {
                            //if (cpnin.isNMTPacket() && (cpnin.getSender() == receiveID)) {
                                // If no error
                                if (cpnin.getType() == CAN_NMT_PGM_ACK) {
                                    pgs = dState.SEND_DONE_EMPTY;
                                }
                                else if (cpnin.getType() == CAN_NMT_PGM_NACK ) { 
                                    // else ..
                                    Console.WriteLine("Nack on CAN_NMT_PGM_DATA.");
                                    errorOccured = true;
                                    pgs = dState.SEND_RESET;
                                }
                            }
                        }
                        break;
                        
                    case dState.SEND_DONE_EMPTY:
                        // Send done
                        outCm = cpn.getPgmEndPacket();
                        dc.sendCanPacket(outCm);

                        t = Environment.TickCount;
                        t2 = Environment.TickCount;
                        pgs = dState.WAIT_DONE_EMPTY;
                        break;
                        
                        
                    case dState.WAIT_DONE_EMPTY:
                        // Check for timeout, resend done packet in that case..
                        if ((Environment.TickCount - t) > TIMEOUT_MS) {
                            Console.WriteLine("Timeout while waiting for end prg ack.");
                            errorOccured = true;
                            pgs = dState.SEND_RESET;
                        }
                        //Thread.Sleep(1);
                        
                        hasMessage = dc.getData(out cm);
                        if (hasMessage) {
                            CanNMT cpnin = new CanNMT(cm, HWID_INUSE);
                            if (cpnin.isNMTPacket()) {
                            //if (cpnin.isNMTPacket() && (cpnin.getSender() == receiveID)) {
                                // If no error
                                if (cpnin.getType() == CAN_NMT_PGM_ACK) {
                                    Console.WriteLine("");
                                    //
                                    Console.WriteLine("Done, successfully cleaned application flash.");
                                    pgs = dState.SEND_RESET;
                                }
                                else if (cpnin.getType() == CAN_NMT_PGM_NACK) {
                                    // else resend addr..
                                    Console.WriteLine("Nack on CAN_NMT_PGM_END.");
                                    errorOccured = true;
                                    pgs = dState.SEND_RESET;
                                }
                            }
                        }
                        break;                      
                    case dState.SEND_RESET:
                        // Send reset
                        cpn.doReset(dc, receiveID, hwid);
                        t = Environment.TickCount;
                        t2 = Environment.TickCount;
                        pgs = dState.DONE;
                        break;
                        
                        
                    case dState.DONE:
                        done = true;
                        //down.Abort();
                        if (!errorOccured) {
                            long tsecs = ((Environment.TickCount - timeStart) / 1000);
                            double bitRate = hf.getLength() / ((double)(Environment.TickCount - timeStart) / 1000.0);
                            
                            if (tsecs <= 0) tsecs = 1;
                            long mins = tsecs / 60;
                            long secs = tsecs % 60;
                            string pad = "";
                            if (secs < 10) {
                                pad = "0";
                            }
                            Console.WriteLine("Time spent: " + mins + ":" + pad + secs + ", " + Math.Round(bitRate, 2) + " bytes/s");
                        }
                        //Console.Write("> ");
                        break;
                }
            }
            
        } catch(Exception) {
            Console.WriteLine("Exception occured");
            //Console.WriteLine(e);
            //skriva ut antal bytes skrivna vore bra här, kanske nån annan form av hjälp för debug, något gick snett liksom
            
        }
        
        return !errorOccured;
    }
}